Index: protocols/telepathy/gossip-telepathy-group.h
===================================================================
--- protocols/telepathy/gossip-telepathy-group.h	(revision 2310)
+++ protocols/telepathy/gossip-telepathy-group.h	(working copy)
@@ -48,28 +48,29 @@ struct _GossipTelepathyGroupClass {
 	GObjectClass       parent_class;
 };
 
-GType                 gossip_telepathy_group_get_type        (void) G_GNUC_CONST;
-GossipTelepathyGroup *gossip_telepathy_group_new             (GossipTelepathy      *telepathy,
-							      TpChan               *tp_chan);
-void                  gossip_telepathy_group_setup           (GossipTelepathyGroup *group);
-void                  gossip_telepathy_group_add_members     (GossipTelepathyGroup *group,
-							      GArray               *handles,
-							      const gchar          *message);
-void                  gossip_telepathy_group_add_member      (GossipTelepathyGroup *group,
-							      guint                 handle,
-							      const gchar          *message);
-void                  gossip_telepathy_group_remove_members  (GossipTelepathyGroup *group,
-							      GArray               *handle,
-							      const gchar          *message);
-void                  gossip_telepathy_group_remove_member   (GossipTelepathyGroup *group,
-							      guint                 handle,
-							      const gchar          *message);
-GArray *              gossip_telepathy_group_get_members     (GossipTelepathyGroup *group);
-const gchar *         gossip_telepathy_group_get_name        (GossipTelepathyGroup *group);
-guint                 gossip_telepathy_group_get_self_handle (GossipTelepathyGroup *group);
-const gchar *         gossip_telepathy_group_get_object_path (GossipTelepathyGroup *group);
-gboolean              gossip_telepathy_group_is_member       (GossipTelepathyGroup *group,
-							      guint                 handle);
+GType                 gossip_telepathy_group_get_type          (void) G_GNUC_CONST;
+GossipTelepathyGroup *gossip_telepathy_group_new               (GossipTelepathy      *telepathy,
+								TpChan               *tp_chan);
+void                  gossip_telepathy_group_setup             (GossipTelepathyGroup *group);
+void                  gossip_telepathy_group_add_members       (GossipTelepathyGroup *group,
+								GArray               *handles,
+								const gchar          *message);
+void                  gossip_telepathy_group_add_member        (GossipTelepathyGroup *group,
+								guint                 handle,
+								const gchar          *message);
+void                  gossip_telepathy_group_remove_members    (GossipTelepathyGroup *group,
+								GArray               *handle,
+								const gchar          *message);
+void                  gossip_telepathy_group_remove_member     (GossipTelepathyGroup *group,
+								guint                 handle,
+								const gchar          *message);
+GArray *              gossip_telepathy_group_get_local_pending (GossipTelepathyGroup *group);
+GArray *              gossip_telepathy_group_get_members       (GossipTelepathyGroup *group);
+const gchar *         gossip_telepathy_group_get_name          (GossipTelepathyGroup *group);
+guint                 gossip_telepathy_group_get_self_handle   (GossipTelepathyGroup *group);
+const gchar *         gossip_telepathy_group_get_object_path   (GossipTelepathyGroup *group);
+gboolean              gossip_telepathy_group_is_member         (GossipTelepathyGroup *group,
+								guint                 handle);
 
 G_END_DECLS
 
Index: protocols/telepathy/gossip-telepathy.c
===================================================================
--- protocols/telepathy/gossip-telepathy.c	(revision 2310)
+++ protocols/telepathy/gossip-telepathy.c	(working copy)
@@ -38,6 +38,8 @@
 #include <libgossip/gossip-chatroom.h>
 #include <libgossip/gossip-presence.h>
 #include <libgossip/gossip-chatroom-provider.h>
+#include <libgossip/gossip-ft-provider.h>
+#include <libgossip/gossip-voip-provider.h>
 #include <libgossip/gossip-debug.h>
 #include <libgossip/gossip-message.h>
 #include <libgossip/gossip-chatroom.h>
@@ -51,6 +53,7 @@
 #include "gossip-telepathy-contacts.h"
 #include "gossip-telepathy-message.h"
 #include "gossip-telepathy-chatrooms.h"
+#include "gossip-telepathy-voip.h"
 #include "gossip-telepathy-contact-list.h"
 #include "gossip-telepathy-private.h"
 #include "gossip-telepathy-cmgr.h"
@@ -71,6 +74,7 @@ struct _GossipTelepathyPriv {
 	GossipTelepathyContactList *contact_list;
 	GossipTelepathyChatrooms   *chatrooms;
 	GossipTelepathyMessage     *message;
+	GossipTelepathyVoip        *voip;
 };
 
 static void             gossip_telepathy_class_init             (GossipTelepathyClass         *klass);
@@ -238,9 +242,36 @@ static void             telepathy_ft_acc
 static void             telepathy_ft_decline                    (GossipFTProvider             *provider,
 								 GossipFTId                    id);
 
+
+/* voip */
+static void             telepathy_voip_init                      (GossipVoipProviderIface      *iface);
+static void             telepathy_voip_call                      (GossipVoipProvider           *provider,
+								  GossipContact                *contact);
+static void             telepathy_voip_accept                    (GossipVoipProvider           *provider,
+								  GossipContact                *contact);
+static void             telepathy_voip_end                       (GossipVoipProvider           *provider,
+								  GossipContact                *contact);
+static void             telepathy_voip_set_video_output          (GossipVoipProvider           *provider,
+								  GossipContact                *contact,
+								  guint                         window_id);
+static void             telepathy_voip_set_video_preview         (GossipVoipProvider           *provider,
+								  gboolean                      active,
+								  guint                         window_id);
+static void             telepathy_voip_set_output_volume         (GossipVoipProvider           *provider,
+								  GossipContact                *contact,
+								  guint                         volume);
+static void             telepathy_voip_mute_input                (GossipVoipProvider           *provider,
+								  GossipContact                *contact,
+								  gboolean                      muted);
+static void             telepathy_voip_mute_output               (GossipVoipProvider           *provider,
+								  GossipContact                *contact,
+								  gboolean                      muted);
+
 G_DEFINE_TYPE_WITH_CODE (GossipTelepathy, gossip_telepathy, GOSSIP_TYPE_PROTOCOL,
 			 G_IMPLEMENT_INTERFACE (GOSSIP_TYPE_CHATROOM_PROVIDER,
 						telepathy_chatroom_init);
+			 G_IMPLEMENT_INTERFACE (GOSSIP_TYPE_VOIP_PROVIDER,
+						telepathy_voip_init);
 			 G_IMPLEMENT_INTERFACE (GOSSIP_TYPE_FT_PROVIDER,
 						telepathy_ft_init));
 
@@ -306,6 +337,7 @@ gossip_telepathy_init (GossipTelepathy *
 	priv->chatrooms = gossip_telepathy_chatrooms_init (telepathy);
 	priv->contacts = gossip_telepathy_contacts_init (telepathy);
 	priv->message = gossip_telepathy_message_init (telepathy);
+	priv->voip = gossip_telepathy_voip_init (telepathy);
 }
 
 static void
@@ -331,6 +363,7 @@ telepathy_finalize (GObject *object)
 	gossip_telepathy_chatrooms_finalize (priv->chatrooms);
 	gossip_telepathy_contacts_finalize (priv->contacts);
 	gossip_telepathy_message_finalize (priv->message);
+	gossip_telepathy_voip_finalize (priv->voip);
 
 	if (priv->tp_conn) {
 		g_signal_handlers_disconnect_by_func (priv->tp_conn,
@@ -1494,6 +1527,9 @@ telepathy_newchannel_cb (DBusGProxy     
 			default:
 				gossip_debug (DEBUG_DOMAIN, "Text channel Handle type unsupported");
 		}
+	}
+	else if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) == 0) {
+		gossip_telepathy_voip_newchannel (priv->voip, new_chan, 0);
 	} else {
 		gossip_debug (DEBUG_DOMAIN, "Channel type unsupported");
 	}
@@ -1776,6 +1812,190 @@ telepathy_ft_decline (GossipFTProvider *
 }
 
 /*
+ * Voip
+ */
+
+static void
+telepathy_voip_init (GossipVoipProviderIface *iface)
+{
+	iface->call              = telepathy_voip_call;
+	iface->accept            = telepathy_voip_accept;
+	iface->end               = telepathy_voip_end;
+	iface->set_video_output  = telepathy_voip_set_video_output;
+	iface->set_video_preview = telepathy_voip_set_video_preview;
+	iface->set_output_volume = telepathy_voip_set_output_volume;
+	iface->mute_input        = telepathy_voip_mute_input;
+	iface->mute_output       = telepathy_voip_mute_output;
+}
+
+static void
+telepathy_voip_call (GossipVoipProvider *provider,
+		     GossipContact      *contact)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_call (priv->voip, handle);
+}
+
+static void
+telepathy_voip_accept (GossipVoipProvider *provider,
+		       GossipContact      *contact)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_accept (priv->voip, handle);
+}
+
+static void
+telepathy_voip_end (GossipVoipProvider *provider,
+		    GossipContact      *contact)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_end (priv->voip, handle);
+}
+
+void
+telepathy_voip_set_video_output (GossipVoipProvider *provider,
+				 GossipContact      *contact,
+				 guint               window_id)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_set_video_output (priv->voip, handle, window_id);
+}
+
+void
+telepathy_voip_set_video_preview (GossipVoipProvider *provider,
+				  gboolean            active,
+				  guint               window_id)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	gossip_telepathy_voip_set_video_preview (priv->voip, active, window_id);
+}
+
+void
+telepathy_voip_set_output_volume (GossipVoipProvider *provider,
+				  GossipContact      *contact,
+				  guint               volume)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_set_output_volume (priv->voip, handle, volume);
+}
+
+void
+telepathy_voip_mute_input (GossipVoipProvider *provider,
+			   GossipContact      *contact,
+			   gboolean            muted)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_mute_input (priv->voip, handle, muted);
+}
+
+void
+telepathy_voip_mute_output (GossipVoipProvider *provider,
+			    GossipContact      *contact,
+			    gboolean            muted)
+{
+	GossipTelepathy     *telepathy;
+	GossipTelepathyPriv *priv;
+	guint                handle;
+	const gchar         *id;
+
+	g_return_if_fail (GOSSIP_IS_TELEPATHY (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	telepathy = GOSSIP_TELEPATHY (provider);
+	priv = GET_PRIV (telepathy);
+
+	id = gossip_contact_get_id (contact);
+	handle = gossip_telepathy_contacts_get_handle (priv->contacts, id);
+
+	gossip_telepathy_voip_mute_output (priv->voip, handle, muted);
+}
+
+/*
  * External functions
  */
 
Index: protocols/telepathy/gossip-telepathy-voip.c
===================================================================
--- protocols/telepathy/gossip-telepathy-voip.c	(revision 0)
+++ protocols/telepathy/gossip-telepathy-voip.c	(revision 0)
@@ -0,0 +1,698 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006 Raphael Slinckx <raphael@slinckx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include <libtelepathy/tp-constants.h>
+#include <libtelepathy/tp-helpers.h>
+#include <libtelepathy/tp-chan-type-streamed-media-gen.h>
+
+#include <libgossip/gossip-account.h>
+#include <libgossip/gossip-contact.h>
+#include <libgossip/gossip-debug.h>
+#include <libgossip/gossip-voip-provider.h>
+
+#include "gossip-telepathy.h"
+#include "gossip-telepathy-private.h"
+#include "gossip-telepathy-voip.h"
+#include "gossip-telepathy-group.h"
+#include "gossip-telepathy-marshal.h"
+
+#include "stream-engine-glue.h"
+
+#define DEBUG_DOMAIN "TelepathyVoip"
+
+#define STREAM_ENGINE_BUS_NAME     "org.freedesktop.Telepathy.StreamEngine"
+#define STREAM_ENGINE_OBJECT_PATH  "/org/freedesktop/Telepathy/StreamEngine"
+#define STREAM_ENGINE_IFACE        "org.freedesktop.Telepathy.StreamEngine"
+#define CHANNEL_HANDLER_INTERFACE  "org.freedesktop.Telepathy.ChannelHandler"
+
+struct _GossipTelepathyVoip
+{
+	GossipTelepathy *telepathy;
+	GHashTable      *stream_channels;
+	DBusGProxy      *se_proxy;
+};
+
+typedef struct
+{
+	TpChan                   *stream_channel;
+	GossipTelepathyVoip      *voip;
+	GossipTelepathyGroup     *group;
+	guint                     other_handle;
+	GossipVoipProviderState   state;
+	GossipVoipProviderOrigin  origin;
+	guint                     audio_stream_id;
+	guint                     video_stream_id;
+	guint                     video_window_id;
+} TelepathyStreamChannelData;
+
+static void     telepathy_voip_channel_data_free       (TelepathyStreamChannelData *channel_data);
+static void     telepathy_voip_receiving_cb            (DBusGProxy                 *stream_engine,
+							const char                 *channel_path,
+							guint                       stream_id,
+							gboolean                    receiving,
+							GossipTelepathyVoip        *voip);
+static void     telepathy_voip_streams_changed         (GossipTelepathyVoip        *voip,
+							TelepathyStreamChannelData *channel_data);
+static void     telepathy_voip_stream_state_changed_cb (DBusGProxy                 *channel,
+							guint                       stream_id,
+							guint                       stream_state,
+							TelepathyStreamChannelData *channel_data);
+static void     telepathy_voip_channel_closed_cb       (DBusGProxy                 *channel,
+							GossipTelepathyVoip        *voip);
+
+GossipTelepathyVoip *
+gossip_telepathy_voip_init (GossipTelepathy *telepathy)
+{
+	GossipTelepathyVoip *voip;
+
+	g_return_val_if_fail (GOSSIP_IS_TELEPATHY (telepathy), NULL);
+
+	voip = g_slice_new0 (GossipTelepathyVoip);
+	voip->telepathy = telepathy;
+	voip->stream_channels = g_hash_table_new_full (g_str_hash,
+						       g_str_equal,
+						       (GDestroyNotify) g_free,
+						       (GDestroyNotify) telepathy_voip_channel_data_free);
+
+	voip->se_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
+						    STREAM_ENGINE_BUS_NAME,
+						    STREAM_ENGINE_OBJECT_PATH,
+						    STREAM_ENGINE_IFACE);
+
+	dbus_g_object_register_marshaller(gossip_telepathy_marshal_VOID__STRING_UINT_BOOLEAN,
+					  G_TYPE_NONE,
+					  DBUS_TYPE_G_OBJECT_PATH,
+					  G_TYPE_UINT,
+					  G_TYPE_BOOLEAN,
+					  G_TYPE_INVALID);
+
+	dbus_g_proxy_add_signal (voip->se_proxy, "Receiving",
+				 DBUS_TYPE_G_OBJECT_PATH,
+				 G_TYPE_UINT,
+				 G_TYPE_BOOLEAN,
+				 G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal (voip->se_proxy, "Receiving",
+				     G_CALLBACK (telepathy_voip_receiving_cb),
+				     voip, NULL);
+
+	return voip;
+}
+
+void
+gossip_telepathy_voip_finalize (GossipTelepathyVoip *voip)
+{
+	g_return_if_fail (voip != NULL);
+
+	g_hash_table_destroy (voip->stream_channels);
+	g_object_unref (voip->se_proxy);
+
+	g_slice_free (GossipTelepathyVoip, voip);
+}
+
+static void
+telepathy_voip_channel_data_free (TelepathyStreamChannelData *channel_data)
+{
+	g_object_unref (channel_data->group);
+	g_object_unref (channel_data->stream_channel);
+	g_slice_free (TelepathyStreamChannelData, channel_data);
+}
+
+static GossipContact*
+telepathy_voip_get_contact (GossipTelepathyVoip *voip,
+			    guint handle)
+{
+	GossipTelepathyContacts *contacts;
+
+	contacts = gossip_telepathy_get_contacts (voip->telepathy);
+
+	return gossip_telepathy_contacts_get_from_handle (contacts, handle);
+}
+
+static void
+telepathy_voip_state_changed (GossipTelepathyVoip        *voip,
+			      TelepathyStreamChannelData *channel_data)
+{
+	g_signal_emit_by_name (voip->telepathy, "voip-state-changed",
+			       telepathy_voip_get_contact (voip, channel_data->other_handle),
+			       channel_data->state,
+			       channel_data->origin);
+}
+
+static void
+telepathy_voip_detect_streams (GossipTelepathyVoip        *voip,
+			       TelepathyStreamChannelData *channel_data,
+			       GPtrArray                  *stream_infos)
+{
+	guint i;
+
+	for (i = 0; i < stream_infos->len; i++) {
+		GValueArray *values;
+		guint        stream_id;
+		guint        stream_handle;
+		guint        stream_type;
+
+		values = g_ptr_array_index (stream_infos, i);
+		stream_id     = g_value_get_uint (g_value_array_get_nth (values, 0));
+		stream_handle = g_value_get_uint (g_value_array_get_nth (values, 1));
+		stream_type   = g_value_get_uint (g_value_array_get_nth (values, 2));
+
+		if (stream_handle != channel_data->other_handle) {
+			continue;
+		}
+
+		switch (stream_type) {
+			case TP_MEDIA_STREAM_TYPE_AUDIO:
+				gossip_debug (DEBUG_DOMAIN, "Got Audio stream ID: %d", stream_id);
+				channel_data->audio_stream_id = stream_id;
+				break;
+			case TP_MEDIA_STREAM_TYPE_VIDEO:
+				gossip_debug (DEBUG_DOMAIN, "Got Video stream ID: %d", stream_id);
+				channel_data->video_stream_id = stream_id;
+				break;
+			default:
+				gossip_debug (DEBUG_DOMAIN, "Unknown stream type: %d", stream_type);
+		}
+	}
+}
+
+static void
+telepathy_voip_request_streams (GossipTelepathyVoip        *voip,
+				TelepathyStreamChannelData *channel_data)
+{
+	DBusGProxy                 *streamed_iface;
+	guint                       stream_type;
+	GArray                     *stream_types;
+	GPtrArray                  *stream_infos;
+	GossipContact              *contact;
+	GossipContactCapabilities   capabilities;
+	GError                     *error = NULL;
+
+	/* Request audio stream */
+	gossip_debug (DEBUG_DOMAIN, "Requesting Streams");
+
+	streamed_iface = tp_chan_get_interface (channel_data->stream_channel,
+			TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
+	stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
+
+	/* Get the capabilities of the contact */
+	contact = telepathy_voip_get_contact (voip, channel_data->other_handle);
+	capabilities = gossip_contact_get_capabilities (contact);
+
+	if (capabilities & GOSSIP_CONTACT_CAPABILITY_AUDIO) {
+		/* garray wants a variable for the append_val macro */
+		stream_type = TP_MEDIA_STREAM_TYPE_AUDIO;
+		g_array_append_val (stream_types, stream_type);
+	}
+
+	if (capabilities & GOSSIP_CONTACT_CAPABILITY_VIDEO) {
+		/* garray wants a variable for the append_val macro */
+		stream_type = TP_MEDIA_STREAM_TYPE_VIDEO;
+		g_array_append_val (stream_types, stream_type);
+	}
+
+	if (!tp_chan_type_streamed_media_request_streams (streamed_iface,
+							  channel_data->other_handle,
+							  stream_types,
+							  &stream_infos,
+							  &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't request audio/video streams: %s",
+			      error->message);
+		g_clear_error (&error);
+		g_array_free (stream_types, TRUE);
+		return;
+	}
+
+	telepathy_voip_detect_streams (voip, channel_data, stream_infos);
+
+	g_array_free (stream_types, TRUE);
+	/* FIXME: We leak the GValueArray for each element of stream_infos */
+	g_ptr_array_free (stream_infos, TRUE);
+
+	channel_data->state = GOSSIP_VOIP_PROVIDER_STATE_CONNECTING;
+	telepathy_voip_state_changed (voip, channel_data);
+}
+
+void
+gossip_telepathy_voip_newchannel (GossipTelepathyVoip *voip,
+				  TpChan              *new_chan,
+				  guint                outgoing_handle)
+{
+	TelepathyStreamChannelData *channel_data;
+	DBusGProxy                 *ch_proxy;
+	DBusGProxy                 *streamed_iface;
+	GError                     *error = NULL;
+	GArray                     *members;
+	GossipContact              *contact;
+	TpConn                     *tp_conn;
+	const gchar                *object_path;
+	GossipAccount              *account;
+
+	g_return_if_fail (voip != NULL);
+	g_return_if_fail (TELEPATHY_IS_CHAN (new_chan));
+
+	object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (new_chan));
+	account = gossip_telepathy_get_account (voip->telepathy);
+
+	if (g_hash_table_lookup (voip->stream_channels, object_path)) {
+		return;
+	}
+
+	channel_data = g_slice_new0 (TelepathyStreamChannelData);
+	channel_data->voip = voip;
+	channel_data->stream_channel = g_object_ref (new_chan);
+	channel_data->other_handle = outgoing_handle;
+	channel_data->group = gossip_telepathy_group_new (voip->telepathy,
+							  new_chan);
+
+	g_hash_table_insert (voip->stream_channels,
+			     g_strdup (object_path),
+			     channel_data);
+
+	dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed",
+				     G_CALLBACK (telepathy_voip_channel_closed_cb),
+				     voip, NULL);
+
+	streamed_iface = tp_chan_get_interface (channel_data->stream_channel,
+						TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
+
+	dbus_g_proxy_connect_signal (streamed_iface, "StreamStateChanged",
+				     G_CALLBACK (telepathy_voip_stream_state_changed_cb),
+				     channel_data, NULL);
+
+	if (outgoing_handle == 0) {
+		GPtrArray *stream_infos;
+
+		/* This is an incoming call. Get the caller handle and signal
+		 * the UI of an incoming call request
+		 */
+		gossip_debug (DEBUG_DOMAIN, "Incoming Call");
+		members = gossip_telepathy_group_get_members (channel_data->group);
+		if (members == NULL) {
+			return;
+		}
+
+		channel_data->other_handle = g_array_index (members, guint, 0);
+		channel_data->origin = GOSSIP_VOIP_PROVIDER_ORIGIN_INCOMING;
+		channel_data->state = GOSSIP_VOIP_PROVIDER_STATE_WAITING_ACCEPT;
+		g_array_free (members, TRUE);
+
+		contact = telepathy_voip_get_contact (voip, channel_data->other_handle);
+
+		/* We get the incoming stream information */
+		if (!tp_chan_type_streamed_media_list_streams (streamed_iface,
+							       &stream_infos,
+							       &error)) {
+			gossip_debug (DEBUG_DOMAIN, "Couldn't list existing streams on incoming call: %s",
+				      error->message);
+			g_clear_error (&error);
+			return;
+		}
+
+		telepathy_voip_detect_streams (voip, channel_data, stream_infos);
+		/* FIXME: We leak the GValueArray for each element of stream_infos */
+		g_ptr_array_free (stream_infos, TRUE);
+
+		/* Tell there is an incoming call */
+		g_signal_emit_by_name (voip->telepathy, "voip-incoming",
+				       contact);
+	} else {
+		/* This is an outgoing call. Request the streams now */
+		gossip_debug (DEBUG_DOMAIN, "Outgoing Call");
+		channel_data->origin = GOSSIP_VOIP_PROVIDER_ORIGIN_OUTGOING;
+		channel_data->state = GOSSIP_VOIP_PROVIDER_STATE_CALLING;
+
+		contact = telepathy_voip_get_contact (voip, channel_data->other_handle);
+
+		telepathy_voip_request_streams (voip, channel_data);
+	}
+
+	/* Tell we have a new call */
+	g_signal_emit_by_name (voip->telepathy, "voip-created", contact);
+
+	/* Signal the streams we got, so the UI can adapt */
+	telepathy_voip_streams_changed (voip, channel_data);
+
+	/* Signal the state of the new call */
+	telepathy_voip_state_changed (voip, channel_data);
+
+	ch_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
+					      STREAM_ENGINE_BUS_NAME,
+					      STREAM_ENGINE_OBJECT_PATH,
+					      CHANNEL_HANDLER_INTERFACE);
+
+	tp_conn = gossip_telepathy_get_connection (voip->telepathy);
+
+	/* Invoke Stream Engine now */
+	if (!org_freedesktop_Telepathy_ChannelHandler_handle_channel (
+				ch_proxy,
+				dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn)),
+				dbus_g_proxy_get_path (DBUS_G_PROXY (tp_conn)),
+				new_chan->type,
+				object_path,
+				new_chan->handle_type,
+				new_chan->handle,
+				&error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't start stream-engine: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	g_object_unref (ch_proxy);
+}
+
+static void
+telepathy_voip_streams_changed (GossipTelepathyVoip        *voip,
+				TelepathyStreamChannelData *channel_data)
+{
+	GossipVoipProviderStreamType  stream_types = 0;
+	GossipContact                *contact;
+
+	if (channel_data->audio_stream_id != 0) {
+		stream_types |= GOSSIP_VOIP_PROVIDER_STREAM_TYPE_AUDIO;
+	}
+	if (channel_data->video_stream_id != 0) {
+		stream_types |= GOSSIP_VOIP_PROVIDER_STREAM_TYPE_VIDEO;
+	}
+
+	contact = telepathy_voip_get_contact (voip, channel_data->other_handle);
+	g_signal_emit_by_name (voip->telepathy, "voip-streams-changed",
+			       contact,
+			       stream_types);
+}
+
+static void
+telepathy_voip_stream_state_changed_cb (DBusGProxy                 *channel,
+					guint                       stream_id,
+					guint                       stream_state,
+					TelepathyStreamChannelData *channel_data)
+{
+	const gchar *object_path;
+	GError      *error = NULL;
+
+	gossip_debug (DEBUG_DOMAIN, "StreamStateChanged: %d now in state",
+		      stream_id, stream_state);
+
+	if (stream_state != TP_MEDIA_STREAM_STATE_CONNECTING ||
+	    stream_id != channel_data->video_stream_id ||
+	    channel_data->video_window_id == 0) {
+		return;
+	}
+
+	object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (channel_data->stream_channel));
+	if (!org_freedesktop_Telepathy_StreamEngine_set_output_window (channel_data->voip->se_proxy,
+								       object_path,
+								       stream_id,
+								       channel_data->video_window_id,
+								       &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't set video_output: %s",
+			      error->message);
+		g_clear_error (&error);
+	}
+}
+
+static void
+telepathy_voip_receiving_cb (DBusGProxy          *stream_engine,
+			     const char          *channel_path,
+			     guint                stream_id,
+			     gboolean             receiving,
+			     GossipTelepathyVoip *voip)
+{
+	TelepathyStreamChannelData *channel_data;
+
+	gossip_debug (DEBUG_DOMAIN, "Receiving from s-e. Path: '%s'", channel_path);
+
+	channel_data = g_hash_table_lookup (voip->stream_channels, channel_path);
+
+	if (channel_data == NULL) {
+		return;
+	}
+
+	if (channel_data->audio_stream_id == stream_id ||
+	    channel_data->video_stream_id == stream_id) {
+	    	if (receiving) {
+	    		channel_data->state = GOSSIP_VOIP_PROVIDER_STATE_TALKING;
+	    	}
+		telepathy_voip_state_changed (voip, channel_data);
+	}
+}
+
+static void
+telepathy_voip_channel_closed_cb (DBusGProxy          *channel,
+				  GossipTelepathyVoip *voip)
+{
+	TelepathyStreamChannelData *channel_data;
+	const gchar                *object_path;
+
+	object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (channel));
+	channel_data = g_hash_table_lookup (voip->stream_channels,
+					    object_path);
+
+	if (channel_data == NULL) {
+		return;
+	}
+
+	gossip_debug (DEBUG_DOMAIN, "Channel closed removing path: %s",
+		      object_path);
+
+	channel_data->state = GOSSIP_VOIP_PROVIDER_STATE_CLOSED;
+	telepathy_voip_state_changed (voip, channel_data);
+
+	g_hash_table_remove (voip->stream_channels, object_path);
+}
+
+void
+gossip_telepathy_voip_call (GossipTelepathyVoip *voip,
+			    guint                handle)
+{
+	TpConn        *tp_conn;
+	const gchar   *bus_name;
+	TpChan        *new_chan;
+	char          *stream_object_path;
+	GError        *error = NULL;
+
+	tp_conn = gossip_telepathy_get_connection (voip->telepathy);
+
+	if (!tp_conn_request_channel (DBUS_G_PROXY (tp_conn),
+				      TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+				      TP_CONN_HANDLE_TYPE_CONTACT,
+				      handle,
+				      TRUE,
+				      &stream_object_path,
+				      &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't request the creation of a new stream channel: %s",
+			      error->message);
+		g_clear_error (&error);
+		return;
+	}
+
+	bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
+	new_chan = tp_chan_new (tp_get_bus (),
+				bus_name,
+				stream_object_path,
+				TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+				TP_CONN_HANDLE_TYPE_NONE, 0);
+
+	gossip_telepathy_voip_newchannel (voip, new_chan, handle);
+
+	g_object_unref (new_chan);
+	g_free (stream_object_path);
+}
+
+static gboolean
+telepathy_voip_find_data (gchar                      *key,
+			  TelepathyStreamChannelData *data,
+			  guint                       handle)
+{
+	return data->other_handle == handle;
+}
+
+static TelepathyStreamChannelData *
+telepathy_voip_data_for_handle (GossipTelepathyVoip *voip,
+				guint                handle)
+{
+	return g_hash_table_find (voip->stream_channels,
+				  (GHRFunc) telepathy_voip_find_data,
+				  GUINT_TO_POINTER (handle));
+}
+
+void
+gossip_telepathy_voip_accept (GossipTelepathyVoip *voip,
+			      guint                handle)
+{
+	TelepathyStreamChannelData *channel_data;
+	GArray                     *local_pending;
+
+	channel_data = telepathy_voip_data_for_handle (voip, handle);
+	if (!channel_data) {
+		return;
+	}
+
+	local_pending = gossip_telepathy_group_get_local_pending (channel_data->group);
+	if (local_pending == NULL) {
+		return;
+	}
+
+	gossip_telepathy_group_add_members (channel_data->group, local_pending, "");
+	g_array_free (local_pending, TRUE);
+}
+
+void
+gossip_telepathy_voip_end (GossipTelepathyVoip *voip,
+			   guint                handle)
+{
+	TelepathyStreamChannelData *channel_data;
+	GError                     *error = NULL;
+
+	channel_data = telepathy_voip_data_for_handle (voip, handle);
+	if (!channel_data) {
+		return;
+	}
+
+	if (!tp_chan_close (DBUS_G_PROXY (channel_data->stream_channel), &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Could not close stream channel: %s",
+			      error->message);
+		g_clear_error (&error);
+	}
+}
+
+void
+gossip_telepathy_voip_set_video_output (GossipTelepathyVoip *voip,
+					guint                handle,
+					guint                window_id)
+{
+	TelepathyStreamChannelData *channel_data;
+
+	channel_data = telepathy_voip_data_for_handle (voip, handle);
+	if (!channel_data) {
+		return;
+	}
+
+	channel_data->video_window_id = window_id;
+}
+
+void
+gossip_telepathy_voip_set_video_preview (GossipTelepathyVoip *voip,
+					 gboolean             active,
+					 guint                window_id)
+{
+	GError *error = NULL;
+
+	if (active) {
+		if (!org_freedesktop_Telepathy_StreamEngine_add_preview_window (voip->se_proxy,
+										window_id,
+										&error)) {
+			gossip_debug (DEBUG_DOMAIN, "Couldn't set video preview: %s",
+				      error->message);
+			g_clear_error (&error);
+		}
+	} else {
+		if (!org_freedesktop_Telepathy_StreamEngine_remove_preview_window (voip->se_proxy,
+										   window_id,
+										   &error)) {
+			gossip_debug (DEBUG_DOMAIN, "Couldn't set video preview: %s",
+				      error->message);
+			g_clear_error (&error);
+		}
+	}
+}
+
+void
+gossip_telepathy_voip_set_output_volume (GossipTelepathyVoip *voip,
+					 guint                handle,
+					 guint                volume)
+{
+	TelepathyStreamChannelData *channel_data;
+	const gchar                *object_path;
+	GError                     *error = NULL;
+
+	channel_data = telepathy_voip_data_for_handle (voip, handle);
+	if (!channel_data) {
+		return;
+	}
+
+	object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (channel_data->stream_channel));
+	if (!org_freedesktop_Telepathy_StreamEngine_set_output_volume (voip->se_proxy,
+								       object_path,
+								       channel_data->audio_stream_id,
+								       volume,
+								       &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't set volume: %s",
+			      error->message);
+		g_clear_error (&error);
+	}
+}
+
+void
+gossip_telepathy_voip_mute_input (GossipTelepathyVoip *voip,
+				  guint                handle,
+				  gboolean             muted)
+{
+	TelepathyStreamChannelData *channel_data;
+	const gchar                *object_path;
+	GError                     *error = NULL;
+
+	channel_data = telepathy_voip_data_for_handle (voip, handle);
+	if (!channel_data) {
+		return;
+	}
+
+	object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (channel_data->stream_channel));
+	if (!org_freedesktop_Telepathy_StreamEngine_mute_input (voip->se_proxy,
+								object_path,
+								channel_data->audio_stream_id,
+								muted,
+								&error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't mute input: %s",
+			      error->message);
+		g_clear_error (&error);
+	}
+}
+
+void
+gossip_telepathy_voip_mute_output (GossipTelepathyVoip *voip,
+				   guint                handle,
+				   gboolean             muted)
+{
+	TelepathyStreamChannelData *channel_data;
+	const gchar                *object_path;
+	GError                     *error = NULL;
+
+	channel_data = telepathy_voip_data_for_handle (voip, handle);
+	if (!channel_data) {
+		return;
+	}
+
+	object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (channel_data->stream_channel));
+	if (!org_freedesktop_Telepathy_StreamEngine_mute_output (voip->se_proxy,
+								 object_path,
+								 channel_data->audio_stream_id,
+								 muted,
+								 &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't mute output: %s",
+			      error->message);
+		g_clear_error (&error);
+	}
+}
+
Index: protocols/telepathy/stream-engine.xml
===================================================================
--- protocols/telepathy/stream-engine.xml	(revision 0)
+++ protocols/telepathy/stream-engine.xml	(revision 0)
@@ -0,0 +1,48 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/TpMediaEngine">
+  <interface name="org.freedesktop.Telepathy.ChannelHandler">
+    <method name="HandleChannel">
+      <arg direction="in" type="s" name="bus_name" />
+      <arg direction="in" type="o" name="connection" />
+      <arg direction="in" type="s" name="channel_type" />
+      <arg direction="in" type="o" name="channel" />
+      <arg direction="in" type="u" name="handle_type" />
+      <arg direction="in" type="u" name="handle" />
+    </method>
+  </interface>
+  <interface name="org.freedesktop.Telepathy.StreamEngine">
+    <method name="SetOutputVolume">
+      <arg direction="in" type="o" name="channel_path" />
+      <arg direction="in" type="u" name="stream_id" />
+      <arg direction="in" type="u" name="volume" />
+    </method>
+    <method name="MuteInput">
+      <arg direction="in" type="o" name="channel_path" />
+      <arg direction="in" type="u" name="stream_id" />
+      <arg direction="in" type="b" name="mute_state" />
+    </method>
+    <method name="MuteOutput">
+      <arg direction="in" type="o" name="channel_path" />
+      <arg direction="in" type="u" name="stream_id" />
+      <arg direction="in" type="b" name="mute_state" />
+    </method>
+    <method name="SetOutputWindow">
+      <arg direction="in" type="o" name="channel_path" />
+      <arg direction="in" type="u" name="stream_id" />
+      <arg direction="in" type="u" name="window" />
+    </method>
+    <method name="AddPreviewWindow">
+      <arg direction="in" type="u" name="window" />
+    </method>
+    <method name="RemovePreviewWindow">
+      <arg direction="in" type="u" name="window" />
+    </method>
+    <signal name="Receiving">
+      <arg type="o" name="channel_path" />
+      <arg type="u" name="stream_id" />
+      <arg type="b" name="state" />
+    </signal>
+    <method name="Shutdown">
+    </method>
+  </interface>
+</node>
Index: protocols/telepathy/gossip-telepathy-contacts.c
===================================================================
--- protocols/telepathy/gossip-telepathy-contacts.c	(revision 2310)
+++ protocols/telepathy/gossip-telepathy-contacts.c	(working copy)
@@ -26,6 +26,7 @@
 #include <libtelepathy/tp-conn-iface-aliasing-gen.h>
 #include <libtelepathy/tp-conn-iface-presence-gen.h>
 #include <libtelepathy/tp-conn-iface-avatars-gen.h>
+#include <libtelepathy/tp-conn-iface-capabilities-gen.h>
 
 #include <libgossip/gossip-account.h>
 #include <libgossip/gossip-debug.h>
@@ -43,6 +44,7 @@ struct _GossipTelepathyContacts {
 	DBusGProxy      *aliasing_iface;
 	DBusGProxy      *avatars_iface;
 	DBusGProxy      *presence_iface;
+	DBusGProxy      *capabilities_iface;
 
 	GHashTable      *contacts;
 
@@ -59,58 +61,70 @@ typedef struct {
 	guint                   *handles;
 } TelepathyContactsAliasesRequestData;
 
-static void     telepathy_contacts_disconnecting_cb        (GossipProtocol                       *telepathy,
-							    GossipAccount                        *account,
-							    GossipTelepathyContacts              *contacts);
-void            telepathy_contacts_connected_cb            (GossipProtocol                       *telepathy,
-							    GossipAccount                        *account,
-							    GossipTelepathyContacts              *contacts);
-static void     telepathy_contacts_disconnecting_foreach   (gchar                                *id,
-							    GossipContact                        *contact,
-							    GossipTelepathyContacts              *contacts);
-static gboolean telepathy_contacts_find_foreach            (gchar                                *id,
-							    GossipContact                        *contact,
-							    guint                                 handle);
+static void     telepathy_contacts_disconnecting_cb            (GossipProtocol                       *telepathy,
+								GossipAccount                        *account,
+								GossipTelepathyContacts              *contacts);
+void            telepathy_contacts_connected_cb                (GossipProtocol                       *telepathy,
+								GossipAccount                        *account,
+								GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_disconnecting_foreach       (gchar                                *id,
+								GossipContact                        *contact,
+								GossipTelepathyContacts              *contacts);
+static gboolean telepathy_contacts_find_foreach                (gchar                                *id,
+								GossipContact                        *contact,
+								guint                                 handle);
 static GossipContact *
-                telepathy_contacts_new_contact             (GossipTelepathyContacts              *contacts,
-							    const gchar                          *id);
-static void     telepathy_contacts_get_info                (GossipTelepathyContacts              *contacts,
-							    GArray                               *handles);
-static void     telepathy_contacts_request_avatar          (GossipTelepathyContacts              *contacts,
-							    guint                                 handle);
-static void     telepathy_contacts_start_avatar_requests   (GossipTelepathyContacts              *contacts);
-static void     telepathy_contacts_avatar_update_cb        (DBusGProxy                           *proxy,
-							    guint                                 handle,
-							    gchar                                *new_token,
-							    GossipTelepathyContacts              *contacts);
-static void     telepathy_contacts_request_avatar_cb       (DBusGProxy                           *proxy,
-							    GArray                               *avatar_data,
-							    char                                 *mime_type,
-							    GError                               *error,
-							    TelepathyContactsAvatarRequestData   *data);
-static void     telepathy_contacts_set_avatar_cb           (DBusGProxy                           *proxy,
-							    char                                 *token,
-							    GError                               *error,
-							    GossipCallbackData                   *data);
-static void     telepathy_contacts_aliases_update_cb       (DBusGProxy                           *proxy,
-							    GPtrArray                            *renamed_handlers,
-							    GossipTelepathyContacts              *contacts);
-static void     telepathy_contacts_request_aliases_cb      (DBusGProxy                           *proxy,
-							    gchar                               **contact_names,
-							    GError                               *error,
-							    TelepathyContactsAliasesRequestData  *data);
-static void     telepathy_contacts_presence_update_cb      (DBusGProxy                           *proxy,
-							    GHashTable                           *handle_table,
-							    GossipTelepathyContacts              *contacts);
-static void     telepathy_contacts_parse_presence_foreach  (guint                                 contact_handle,
-							    GValueArray                          *presence_struct,
-							    GossipTelepathyContacts              *contacts);
-static void     telepathy_contacts_presences_table_foreach (const gchar                          *state_str,
-							    GHashTable                           *presences_table,
-							    GossipPresence                      **presence);
+                telepathy_contacts_new_contact                 (GossipTelepathyContacts              *contacts,
+								const gchar                          *id);
+static void     telepathy_contacts_get_info                    (GossipTelepathyContacts              *contacts,
+								GArray                               *handles);
+static void     telepathy_contacts_request_avatar              (GossipTelepathyContacts              *contacts,
+								guint                                 handle);
+static void     telepathy_contacts_start_avatar_requests       (GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_avatar_update_cb            (DBusGProxy                           *proxy,
+								guint                                 handle,
+								gchar                                *new_token,
+								GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_request_avatar_cb           (DBusGProxy                           *proxy,
+								GArray                               *avatar_data,
+								gchar                                *mime_type,
+								GError                               *error,
+								TelepathyContactsAvatarRequestData   *data);
+static void     telepathy_contacts_set_avatar_cb               (DBusGProxy                           *proxy,
+								gchar                                *token,
+								GError                               *error,
+								GossipCallbackData                   *data);
+static void     telepathy_contacts_aliases_update_cb           (DBusGProxy                           *proxy,
+								GPtrArray                            *renamed_handlers,
+								GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_request_aliases_cb          (DBusGProxy                           *proxy,
+								gchar                               **contact_names,
+								GError                               *error,
+								TelepathyContactsAliasesRequestData  *data);
+static void     telepathy_contacts_presence_update_cb          (DBusGProxy                           *proxy,
+								GHashTable                           *handle_table,
+								GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_parse_presence_foreach      (guint                                 contact_handle,
+								GValueArray                          *presence_struct,
+								GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_presences_table_foreach     (const gchar                          *state_str,
+								GHashTable                           *presences_table,
+								GossipPresence                      **presence);
+static void     telepathy_contacts_capabilities_update_contact (GossipTelepathyContacts              *contacts,
+								guint                                 handle,
+								const gchar                          *channel_type,
+								guint                                 generic,
+								guint                                 specific);
+static void     telepathy_contacts_capabilities_changed_cb     (DBusGProxy                           *proxy,
+								GPtrArray                            *capabilities,
+								GossipTelepathyContacts              *contacts);
+static void     telepathy_contacts_get_capabilities_cb         (DBusGProxy                           *proxy,
+								GPtrArray                            *capabilities,
+								GError                               *error,
+								GossipTelepathyContacts              *contacts);
 static GossipPresenceState
-		    telepathy_presence_state_from_str      (const gchar                          *str);
-static const gchar *telepathy_presence_state_to_str        (GossipPresenceState                   presence_state);
+		    telepathy_presence_state_from_str          (const gchar                          *str);
+static const gchar *telepathy_presence_state_to_str            (GossipPresenceState                   presence_state);
 
 static guint n_avatar_requests = 0;
 
@@ -159,6 +173,7 @@ telepathy_contacts_disconnecting_cb (Gos
 	contacts->aliasing_iface = NULL;
 	contacts->avatars_iface = NULL;
 	contacts->presence_iface = NULL;
+	contacts->capabilities_iface = NULL;
 }
 
 void
@@ -175,6 +190,8 @@ telepathy_contacts_connected_cb (GossipP
 							 TELEPATHY_CONN_IFACE_AVATARS_QUARK);
 	contacts->presence_iface = tp_conn_get_interface (tp_conn,
 							  TELEPATHY_CONN_IFACE_PRESENCE_QUARK);
+	contacts->capabilities_iface = tp_conn_get_interface (tp_conn,
+							      TELEPATHY_CONN_IFACE_CAPABILITIES_QUARK);
 
 	if (contacts->aliasing_iface) {
 		dbus_g_proxy_connect_signal (contacts->aliasing_iface,
@@ -196,6 +213,50 @@ telepathy_contacts_connected_cb (GossipP
 					     G_CALLBACK (telepathy_contacts_presence_update_cb),
 					     contacts, NULL);
 	}
+
+	if (contacts->capabilities_iface) {
+		GPtrArray   *adv_add;
+		GValueArray *adv_add_values;
+		GValue       adv_chan_type = {0, };
+		GValue       adv_chan_capabilities = {0, };
+		const char  *adv_rem[1] = {NULL};
+		GError      *error = NULL;
+
+		dbus_g_proxy_connect_signal (contacts->capabilities_iface,
+					     "CapabilitiesChanged",
+					     G_CALLBACK (telepathy_contacts_capabilities_changed_cb),
+					     contacts, NULL);
+
+		/* Tells the world we support audio and video */
+		g_value_init (&adv_chan_type, G_TYPE_STRING);
+		g_value_init (&adv_chan_capabilities, G_TYPE_UINT);
+
+		g_value_set_string (&adv_chan_type,
+				    TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+		g_value_set_uint (&adv_chan_capabilities,
+				  TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
+				  TP_CHANNEL_MEDIA_CAPABILITY_VIDEO);
+
+		adv_add_values = g_value_array_new (2);
+		g_value_array_append (adv_add_values, &adv_chan_type);
+		g_value_array_append (adv_add_values, &adv_chan_capabilities);
+
+		adv_add = g_ptr_array_new ();
+		g_ptr_array_add (adv_add, adv_add_values);
+
+		if (!tp_conn_iface_capabilities_advertise_capabilities (contacts->capabilities_iface,
+									adv_add,
+									adv_rem,
+									NULL,
+									&error)) {
+			gossip_debug (DEBUG_DOMAIN, "Couldn't advertise capabilities: %s",
+				      error->message);
+			g_clear_error (&error);
+		}
+
+		g_value_array_free (adv_add_values);
+		g_ptr_array_free (adv_add, TRUE);
+	}
 }
 
 GossipContact *
@@ -689,6 +750,14 @@ telepathy_contacts_get_info (GossipTelep
 			telepathy_contacts_request_avatar (contacts, handle);
 		}
 	}
+
+	if (contacts->capabilities_iface) {
+		tp_conn_iface_capabilities_get_capabilities_async (contacts->capabilities_iface,
+								   handles,
+								   (tp_conn_iface_capabilities_get_capabilities_reply)
+								   telepathy_contacts_get_capabilities_cb,
+								   contacts);
+	}
 }
 
 static void
@@ -913,6 +982,97 @@ telepathy_contacts_presences_table_forea
 	gossip_presence_set_resource (*presence, "");
 }
 
+static void
+telepathy_contacts_capabilities_update_contact (GossipTelepathyContacts *contacts,
+						guint                    handle,
+						const gchar             *channel_type,
+						guint                    generic,
+						guint                    specific)
+{
+	GossipContactCapabilities  capabilities = 0;
+	GossipContact             *contact;
+
+	contact = gossip_telepathy_contacts_get_from_handle (contacts, handle);
+
+	if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) == 0) {
+		if (specific & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) {
+			capabilities |= GOSSIP_CONTACT_CAPABILITY_AUDIO;
+		}
+		if (specific & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) {
+			capabilities |= GOSSIP_CONTACT_CAPABILITY_VIDEO;
+		}
+
+		gossip_debug (DEBUG_DOMAIN, "Set contact capabilities for '%s' to %d",
+			      gossip_contact_get_name (contact),
+			      capabilities);
+
+		gossip_contact_set_capabilities (contact, capabilities);
+	}
+}
+
+static void
+telepathy_contacts_capabilities_changed_cb (DBusGProxy              *proxy,
+					    GPtrArray               *capabilities,
+					    GossipTelepathyContacts *contacts)
+{
+	guint i;
+
+	for (i = 0; i < capabilities->len; i++) {
+		GValueArray *values;
+		guint        handle;
+		const gchar *channel_type;
+		guint        new_generic;
+		guint        new_specific;
+
+		values = g_ptr_array_index (capabilities, i);
+		handle = g_value_get_uint (g_value_array_get_nth (values, 0));
+		channel_type = g_value_get_string (g_value_array_get_nth (values, 1));
+		new_generic = g_value_get_uint (g_value_array_get_nth (values, 3));
+		new_specific = g_value_get_uint (g_value_array_get_nth (values, 5));
+
+		telepathy_contacts_capabilities_update_contact (contacts,
+								handle,
+								channel_type,
+								new_generic,
+								new_specific);
+	}
+}
+
+static void
+telepathy_contacts_get_capabilities_cb (DBusGProxy              *proxy,
+					GPtrArray               *capabilities,
+					GError                  *error,
+					GossipTelepathyContacts *contacts)
+{
+	guint i;
+
+	if (error) {
+		gossip_debug (DEBUG_DOMAIN, "Error while getting capabilites: %s",
+			      error->message);
+		return;
+	}
+
+	for (i = 0; i < capabilities->len; i++)	{
+		GValueArray *values;
+		guint        handle;
+		const gchar *channel_type;
+		guint        generic;
+		guint        specific;
+
+		values = g_ptr_array_index (capabilities, i);
+		handle = g_value_get_uint (g_value_array_get_nth (values, 0));
+		channel_type = g_value_get_string (g_value_array_get_nth (values, 1));
+		generic = g_value_get_uint (g_value_array_get_nth (values, 2));
+		specific = g_value_get_uint (g_value_array_get_nth (values, 3));
+
+		telepathy_contacts_capabilities_update_contact (contacts,
+								handle,
+								channel_type,
+								generic,
+								specific);
+	}
+}
+
 static GossipPresenceState
 telepathy_presence_state_from_str (const gchar *str)
 {
Index: protocols/telepathy/gossip-telepathy-marshal.list
===================================================================
--- protocols/telepathy/gossip-telepathy-marshal.list	(revision 2310)
+++ protocols/telepathy/gossip-telepathy-marshal.list	(working copy)
@@ -1 +1,2 @@
 VOID:POINTER,UINT,UINT,STRING
+VOID:STRING,UINT,BOOLEAN
Index: protocols/telepathy/gossip-telepathy-voip.h
===================================================================
--- protocols/telepathy/gossip-telepathy-voip.h	(revision 0)
+++ protocols/telepathy/gossip-telepathy-voip.h	(revision 0)
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006 Raphael Slinckx <raphael@slinckx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GOSSIP_TELEPATHY_VOIP_H__
+#define __GOSSIP_TELEPATHY_VOIP_H__
+
+#include "gossip-telepathy.h"
+
+typedef struct _GossipTelepathyVoip GossipTelepathyVoip;
+
+GossipTelepathyVoip *  gossip_telepathy_voip_init             (GossipTelepathy       *telepathy);
+void                   gossip_telepathy_voip_finalize         (GossipTelepathyVoip   *voip);
+void                   gossip_telepathy_voip_newchannel       (GossipTelepathyVoip   *voip,
+							       TpChan                *new_chan,
+							       guint                  outgoing_handle);
+void                   gossip_telepathy_voip_call             (GossipTelepathyVoip   *voip,
+							       guint                  handle);
+void                   gossip_telepathy_voip_accept           (GossipTelepathyVoip   *voip,
+							       guint                  handle);
+void                   gossip_telepathy_voip_end              (GossipTelepathyVoip   *voip,
+							       guint                  handle);
+void                   gossip_telepathy_voip_set_video_output (GossipTelepathyVoip   *voip,
+							       guint                  handle,
+							       guint                  window_id);
+void                   gossip_telepathy_voip_set_video_preview(GossipTelepathyVoip   *voip,
+							       gboolean               active,
+							       guint                  window_id);
+void                   gossip_telepathy_voip_set_output_volume(GossipTelepathyVoip   *voip,
+							       guint                  handle,
+							       guint                  volume);
+void                   gossip_telepathy_voip_mute_input       (GossipTelepathyVoip   *voip,
+							       guint                  handle,
+							       gboolean               muted);
+void                   gossip_telepathy_voip_mute_output      (GossipTelepathyVoip   *voip,
+							       guint                  handle,
+							       gboolean               muted);
+
+#endif /* __GOSSIP_TELEPATHY_VOIP_H__ */
Index: protocols/telepathy/gossip-telepathy-marshal.c
===================================================================
--- protocols/telepathy/gossip-telepathy-marshal.c	(revision 0)
+++ protocols/telepathy/gossip-telepathy-marshal.c	(revision 0)
@@ -0,0 +1,129 @@
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:POINTER,UINT,UINT,STRING (gossip-telepathy-marshal.list:1) */
+void
+gossip_telepathy_marshal_VOID__POINTER_UINT_UINT_STRING (GClosure     *closure,
+                                                         GValue       *return_value,
+                                                         guint         n_param_values,
+                                                         const GValue *param_values,
+                                                         gpointer      invocation_hint,
+                                                         gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_UINT_UINT_STRING) (gpointer     data1,
+                                                               gpointer     arg_1,
+                                                               guint        arg_2,
+                                                               guint        arg_3,
+                                                               gpointer     arg_4,
+                                                               gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_UINT_UINT_STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__POINTER_UINT_UINT_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_uint (param_values + 2),
+            g_marshal_value_peek_uint (param_values + 3),
+            g_marshal_value_peek_string (param_values + 4),
+            data2);
+}
+
+/* VOID:STRING,UINT,BOOLEAN (gossip-telepathy-marshal.list:2) */
+void
+gossip_telepathy_marshal_VOID__STRING_UINT_BOOLEAN (GClosure     *closure,
+                                                    GValue       *return_value,
+                                                    guint         n_param_values,
+                                                    const GValue *param_values,
+                                                    gpointer      invocation_hint,
+                                                    gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_UINT_BOOLEAN) (gpointer     data1,
+                                                          gpointer     arg_1,
+                                                          guint        arg_2,
+                                                          gboolean     arg_3,
+                                                          gpointer     data2);
+  register GMarshalFunc_VOID__STRING_UINT_BOOLEAN callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 4);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__STRING_UINT_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_uint (param_values + 2),
+            g_marshal_value_peek_boolean (param_values + 3),
+            data2);
+}
+
Index: protocols/telepathy/Makefile.am
===================================================================
--- protocols/telepathy/Makefile.am	(revision 2310)
+++ protocols/telepathy/Makefile.am	(working copy)
@@ -5,6 +5,14 @@ AM_CPPFLAGS =                           
 	$(GOSSIP_TELEPATHY_CFLAGS) 		\
 	$(WARN_CFLAGS)
 
+dbus_binding_sources = stream-engine.xml
+
+stream-engine-glue.h: $(dbus_binding_sources)
+	$(LIBTOOL) --mode=execute $(DBUSBINDINGTOOL) --prefix=stream_engine \
+	--mode=glib-client --output=stream-engine-glue.h $(dbus_binding_sources)
+
+dbus_generated = stream-engine-glue.h
+
 noinst_LTLIBRARIES = libgossip-telepathy.la
 
 libgossip_telepathy_la_LIBADD = $(GOSSIP_TELEPATHY_LIBS)
@@ -17,6 +25,7 @@ libgossip_telepathy_la_SOURCES =						\
 	gossip-telepathy-chatrooms.c		gossip-telepathy-chatrooms.h	\
 	gossip-telepathy-contact-list.c		gossip-telepathy-contact-list.h	\
 	gossip-telepathy-cmgr.c			gossip-telepathy-cmgr.h		\
+	gossip-telepathy-voip.c			gossip-telepathy-voip.h		\
 	gossip-telepathy-private.h						\
 	gossip-telepathy-marshal-main.c
 
@@ -34,11 +43,13 @@ gossip-telepathy-marshal.c: gossip-telep
 
 gossip-telepathy-marshal-main.c: gossip-telepathy-marshal.c gossip-telepathy-marshal.h
 
-BUILT_SOURCES = \
-	gossip-telepathy-marshal.h \
-	gossip-telepathy-marshal.c
+BUILT_SOURCES = 				\
+	gossip-telepathy-marshal.h		\
+	gossip-telepathy-marshal.c		\
+	$(dbus_generated)
 
-EXTRA_DIST = \
+EXTRA_DIST = 					\
+	$(dbus_binding_sources)			\
 	gossip-telepathy-marshal.list
 
 CLEANFILES = $(BUILT_SOURCES)
Index: protocols/telepathy/gossip-telepathy-group.c
===================================================================
--- protocols/telepathy/gossip-telepathy-group.c	(revision 2310)
+++ protocols/telepathy/gossip-telepathy-group.c	(working copy)
@@ -367,6 +367,29 @@ gossip_telepathy_group_get_members (Goss
 	return members;
 }
 
+GArray *
+gossip_telepathy_group_get_local_pending (GossipTelepathyGroup *group)
+{
+	GossipTelepathyGroupPriv *priv;
+	GArray                   *members;
+	GError                   *error = NULL;
+
+	g_return_val_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group), NULL);
+
+	priv = GET_PRIV (group);
+
+	if (!tp_chan_iface_group_get_local_pending_members (priv->group_iface,
+							    &members,
+							    &error)) {
+		gossip_debug (DEBUG_DOMAIN, "Couldn't get local pending members: %s",
+			      error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	return members;
+}
+
 static void
 telepathy_group_destroy_cb (DBusGProxy           *proxy,
 			    GossipTelepathyGroup *group)
Index: protocols/telepathy/gossip-telepathy-marshal.h
===================================================================
--- protocols/telepathy/gossip-telepathy-marshal.h	(revision 0)
+++ protocols/telepathy/gossip-telepathy-marshal.h	(revision 0)
@@ -0,0 +1,28 @@
+
+#ifndef __gossip_telepathy_marshal_MARSHAL_H__
+#define __gossip_telepathy_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:POINTER,UINT,UINT,STRING (gossip-telepathy-marshal.list:1) */
+extern void gossip_telepathy_marshal_VOID__POINTER_UINT_UINT_STRING (GClosure     *closure,
+                                                                     GValue       *return_value,
+                                                                     guint         n_param_values,
+                                                                     const GValue *param_values,
+                                                                     gpointer      invocation_hint,
+                                                                     gpointer      marshal_data);
+
+/* VOID:STRING,UINT,BOOLEAN (gossip-telepathy-marshal.list:2) */
+extern void gossip_telepathy_marshal_VOID__STRING_UINT_BOOLEAN (GClosure     *closure,
+                                                                GValue       *return_value,
+                                                                guint         n_param_values,
+                                                                const GValue *param_values,
+                                                                gpointer      invocation_hint,
+                                                                gpointer      marshal_data);
+
+G_END_DECLS
+
+#endif /* __gossip_telepathy_marshal_MARSHAL_H__ */
+
Index: protocols/telepathy/stream-engine-glue.h
===================================================================
--- protocols/telepathy/stream-engine-glue.h	(revision 0)
+++ protocols/telepathy/stream-engine-glue.h	(revision 0)
@@ -0,0 +1,315 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+#include <glib/gtypes.h>
+#include <glib/gerror.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#ifndef DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_Telepathy_ChannelHandler
+#define DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_Telepathy_ChannelHandler
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_ChannelHandler_handle_channel (DBusGProxy *proxy, const char * IN_bus_name, const char* IN_connection, const char * IN_channel_type, const char* IN_channel, const guint IN_handle_type, const guint IN_handle, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "HandleChannel", error, G_TYPE_STRING, IN_bus_name, DBUS_TYPE_G_OBJECT_PATH, IN_connection, G_TYPE_STRING, IN_channel_type, DBUS_TYPE_G_OBJECT_PATH, IN_channel, G_TYPE_UINT, IN_handle_type, G_TYPE_UINT, IN_handle, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_ChannelHandler_handle_channel_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_ChannelHandler_handle_channel_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_ChannelHandler_handle_channel_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_ChannelHandler_handle_channel_async (DBusGProxy *proxy, const char * IN_bus_name, const char* IN_connection, const char * IN_channel_type, const char* IN_channel, const guint IN_handle_type, const guint IN_handle, org_freedesktop_Telepathy_ChannelHandler_handle_channel_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "HandleChannel", org_freedesktop_Telepathy_ChannelHandler_handle_channel_async_callback, stuff, g_free, G_TYPE_STRING, IN_bus_name, DBUS_TYPE_G_OBJECT_PATH, IN_connection, G_TYPE_STRING, IN_channel_type, DBUS_TYPE_G_OBJECT_PATH, IN_channel, G_TYPE_UINT, IN_handle_type, G_TYPE_UINT, IN_handle, G_TYPE_INVALID);
+}
+#endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_Telepathy_ChannelHandler */
+
+#ifndef DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_Telepathy_StreamEngine
+#define DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_Telepathy_StreamEngine
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_set_output_volume (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const guint IN_volume, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "SetOutputVolume", error, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_UINT, IN_volume, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_set_output_volume_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_set_output_volume_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_set_output_volume_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_set_output_volume_async (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const guint IN_volume, org_freedesktop_Telepathy_StreamEngine_set_output_volume_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "SetOutputVolume", org_freedesktop_Telepathy_StreamEngine_set_output_volume_async_callback, stuff, g_free, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_UINT, IN_volume, G_TYPE_INVALID);
+}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_mute_input (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const gboolean IN_mute_state, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "MuteInput", error, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_BOOLEAN, IN_mute_state, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_mute_input_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_mute_input_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_mute_input_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_mute_input_async (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const gboolean IN_mute_state, org_freedesktop_Telepathy_StreamEngine_mute_input_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "MuteInput", org_freedesktop_Telepathy_StreamEngine_mute_input_async_callback, stuff, g_free, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_BOOLEAN, IN_mute_state, G_TYPE_INVALID);
+}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_mute_output (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const gboolean IN_mute_state, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "MuteOutput", error, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_BOOLEAN, IN_mute_state, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_mute_output_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_mute_output_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_mute_output_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_mute_output_async (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const gboolean IN_mute_state, org_freedesktop_Telepathy_StreamEngine_mute_output_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "MuteOutput", org_freedesktop_Telepathy_StreamEngine_mute_output_async_callback, stuff, g_free, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_BOOLEAN, IN_mute_state, G_TYPE_INVALID);
+}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_set_output_window (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const guint IN_window, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "SetOutputWindow", error, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_UINT, IN_window, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_set_output_window_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_set_output_window_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_set_output_window_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_set_output_window_async (DBusGProxy *proxy, const char* IN_channel_path, const guint IN_stream_id, const guint IN_window, org_freedesktop_Telepathy_StreamEngine_set_output_window_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "SetOutputWindow", org_freedesktop_Telepathy_StreamEngine_set_output_window_async_callback, stuff, g_free, DBUS_TYPE_G_OBJECT_PATH, IN_channel_path, G_TYPE_UINT, IN_stream_id, G_TYPE_UINT, IN_window, G_TYPE_INVALID);
+}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_add_preview_window (DBusGProxy *proxy, const guint IN_window, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "AddPreviewWindow", error, G_TYPE_UINT, IN_window, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_add_preview_window_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_add_preview_window_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_add_preview_window_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_add_preview_window_async (DBusGProxy *proxy, const guint IN_window, org_freedesktop_Telepathy_StreamEngine_add_preview_window_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "AddPreviewWindow", org_freedesktop_Telepathy_StreamEngine_add_preview_window_async_callback, stuff, g_free, G_TYPE_UINT, IN_window, G_TYPE_INVALID);
+}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_remove_preview_window (DBusGProxy *proxy, const guint IN_window, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "RemovePreviewWindow", error, G_TYPE_UINT, IN_window, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_remove_preview_window_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_remove_preview_window_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_remove_preview_window_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_remove_preview_window_async (DBusGProxy *proxy, const guint IN_window, org_freedesktop_Telepathy_StreamEngine_remove_preview_window_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "RemovePreviewWindow", org_freedesktop_Telepathy_StreamEngine_remove_preview_window_async_callback, stuff, g_free, G_TYPE_UINT, IN_window, G_TYPE_INVALID);
+}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_freedesktop_Telepathy_StreamEngine_shutdown (DBusGProxy *proxy, GError **error)
+
+{
+  return dbus_g_proxy_call (proxy, "Shutdown", error, G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
+typedef void (*org_freedesktop_Telepathy_StreamEngine_shutdown_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
+
+static void
+org_freedesktop_Telepathy_StreamEngine_shutdown_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+  DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+  GError *error = NULL;
+  dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+  (*(org_freedesktop_Telepathy_StreamEngine_shutdown_reply)data->cb) (proxy, error, data->userdata);
+  return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_freedesktop_Telepathy_StreamEngine_shutdown_async (DBusGProxy *proxy, org_freedesktop_Telepathy_StreamEngine_shutdown_reply callback, gpointer userdata)
+
+{
+  DBusGAsyncData *stuff;
+  stuff = g_new (DBusGAsyncData, 1);
+  stuff->cb = G_CALLBACK (callback);
+  stuff->userdata = userdata;
+  return dbus_g_proxy_begin_call (proxy, "Shutdown", org_freedesktop_Telepathy_StreamEngine_shutdown_async_callback, stuff, g_free, G_TYPE_INVALID);
+}
+#endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_Telepathy_StreamEngine */
+
+G_END_DECLS
Index: src/gossip-contact-list.c
===================================================================
--- src/gossip-contact-list.c	(revision 2310)
+++ src/gossip-contact-list.c	(working copy)
@@ -40,12 +40,14 @@
 #include "gossip-app.h"
 #include "gossip-cell-renderer-expander.h"
 #include "gossip-cell-renderer-text.h"
+#include "gossip-cell-renderer-activatable.h"
 #include "gossip-chat-invite.h"
 #include "gossip-contact-groups.h"
 #include "gossip-contact-info-dialog.h"
 #include "gossip-contact-list.h"
 #include "gossip-edit-contact-dialog.h"
 #include "gossip-ft-window.h"
+#include "gossip-voip-window.h"
 #include "gossip-log-window.h"
 #include "gossip-marshal.h"
 #include "gossip-sound.h"
@@ -67,6 +69,8 @@
 /* Time after connecting which we wait before active users are enabled */
 #define ACTIVE_USER_WAIT_TO_ENABLE_TIME 5000
 
+#define GOSSIP_STOCK_MIC "gnome-stock-mic"
+
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT_LIST, GossipContactListPriv))
 
 struct _GossipContactListPriv {
@@ -230,6 +234,14 @@ static void     contact_list_pixbuf_cell
 							      GtkTreeModel           *model,
 							      GtkTreeIter            *iter,
 							      GossipContactList      *list);
+static void     contact_list_voip_activated_cb               (GossipCellRendererActivatable *cell,
+                                  const gchar                   *path_string,
+                                  GossipContactList             *list);
+static void     contact_list_voip_cell_data_func             (GtkTreeViewColumn             *tree_column,
+                                  GtkCellRenderer               *cell,
+                                  GtkTreeModel                  *model,
+                                  GtkTreeIter                   *iter,
+                                  GossipContactList             *list);
 static void     contact_list_avatar_cell_data_func           (GtkTreeViewColumn      *tree_column,
 							      GtkCellRenderer        *cell,
 							      GtkTreeModel           *model,
@@ -247,7 +259,8 @@ static void     contact_list_expander_ce
 							      GossipContactList      *list);
 static GtkWidget *contact_list_get_contact_menu              (GossipContactList      *list,
 							      gboolean                can_send_file,
-							      gboolean                can_show_log);
+							      gboolean                can_show_log,
+							      gboolean                can_voip);
 static gboolean contact_list_button_press_event_cb           (GossipContactList      *list,
 							      GdkEventButton         *event,
 							      gpointer                user_data);
@@ -318,6 +331,7 @@ enum {
 	COL_IS_ONLINE,
 	COL_IS_COMPOSING,
 	COL_IS_SEPARATOR,
+	COL_CAN_VOIP,
 	COL_COUNT
 };
 
@@ -370,6 +384,10 @@ static const GtkActionEntry entries[] = 
 	  N_("_View Previous Conversations"), NULL, N_("View previous conversations with this contact"),
 	  G_CALLBACK (contact_list_action_cb)
 	},
+	{ "Call", GOSSIP_STOCK_MIC,
+	  N_("_Call"), NULL, N_("Start a voice or video conversation with this contact"),
+	  G_CALLBACK (contact_list_action_cb)
+	},
 };
 
 static guint n_entries = G_N_ELEMENTS (entries);
@@ -378,6 +396,7 @@ static const gchar *ui_info =
 	"<ui>"
 	"  <popup name='Contact'>"
 	"    <menuitem action='Chat'/>"
+	"    <menuitem action='Call'/>"
 	"    <menuitem action='Log'/>"
 	"    <menuitem action='SendFile'/>"
 	"    <separator/>"
@@ -863,6 +882,7 @@ contact_list_contact_update (GossipConta
 				    COL_IS_ONLINE, now_online,
 				    COL_NAME, gossip_contact_get_name (contact),
 				    COL_PIXBUF_AVATAR, pixbuf_avatar,
+				    COL_CAN_VOIP, gossip_contact_can_voip (contact),
 				    -1);
 	}
 
@@ -922,6 +942,9 @@ contact_list_contact_added_cb (GossipSes
 	g_signal_connect (contact, "notify::type",
 			  G_CALLBACK (contact_list_contact_updated_cb),
 			  list);
+	g_signal_connect (contact, "notify::capabilities",
+			  G_CALLBACK (contact_list_contact_updated_cb),
+			  list);
 
 	has_events = g_hash_table_lookup (priv->flash_table, contact) != NULL;
 
@@ -1361,6 +1384,7 @@ contact_list_add_contact (GossipContactL
 				    COL_IS_ONLINE, gossip_contact_is_online (contact),
 				    COL_IS_COMPOSING, FALSE,
 				    COL_IS_SEPARATOR, FALSE,
+				    COL_CAN_VOIP, gossip_contact_can_voip (contact),
 				    -1);
 
 		gossip_debug (DEBUG_DOMAIN, 
@@ -1423,6 +1447,7 @@ contact_list_add_contact (GossipContactL
 				    COL_IS_ONLINE, gossip_contact_is_online (contact),
 				    COL_IS_COMPOSING, FALSE,
 				    COL_IS_SEPARATOR, FALSE,
+				    COL_CAN_VOIP, gossip_contact_can_voip (contact),
 				    -1);
 
 		gossip_debug (DEBUG_DOMAIN, 
@@ -1543,7 +1568,8 @@ contact_list_create_model (GossipContact
 					  G_TYPE_BOOLEAN,      /* Is active */
 					  G_TYPE_BOOLEAN,      /* Is online */
 					  G_TYPE_BOOLEAN,      /* Is composing */
-					  G_TYPE_BOOLEAN);     /* Is separator */
+					  G_TYPE_BOOLEAN,      /* Is separator */
+					  G_TYPE_BOOLEAN);    /* Can do Voip */
 
 	/* Save normal model */
 	model = GTK_TREE_MODEL (priv->store);
@@ -1655,6 +1681,21 @@ contact_list_setup_view (GossipContactLi
 					    "status", COL_STATUS);
 	gtk_tree_view_column_add_attribute (col, cell,
 					    "is_group", COL_IS_GROUP);
+	/* Voip Capability Icon */
+	cell = gossip_cell_renderer_activatable_new ();
+	gtk_tree_view_column_pack_start (col, cell, FALSE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_voip_cell_data_func,
+		list, NULL);
+
+	g_object_set (cell,
+		      "visible", FALSE,
+		      NULL);
+
+	g_signal_connect (cell, "path-activated",
+			  G_CALLBACK (contact_list_voip_activated_cb),
+			  list);
 
 	/* Avatar */
 	cell = gtk_cell_renderer_pixbuf_new ();
@@ -2127,6 +2168,64 @@ contact_list_pixbuf_cell_data_func (GtkT
 }
 
 static void
+contact_list_voip_activated_cb (GossipCellRendererActivatable *cell,
+				const gchar                   *path_string,
+				GossipContactList             *list)
+{
+	GtkTreeModel  *model;
+	GtkTreeIter    iter;
+	GossipContact *contact;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
+	if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string)) {
+		g_object_unref (model);
+		return;
+	}
+
+	gtk_tree_model_get (model, &iter,
+			    COL_CONTACT, &contact,
+			    -1);
+
+	if (contact) {
+		gossip_voip_window_present (contact, FALSE);
+		g_object_unref (contact);
+	}
+
+	g_object_unref (model);
+}
+
+static void
+contact_list_voip_cell_data_func (GtkTreeViewColumn *tree_column,
+				  GtkCellRenderer   *cell,
+				  GtkTreeModel      *model,
+				  GtkTreeIter       *iter,
+				  GossipContactList *list)
+{
+	gboolean is_group;
+	gboolean is_active;
+	gboolean can_voip;
+
+	gtk_tree_model_get (model, iter,
+			    COL_IS_GROUP, &is_group,
+			    COL_IS_ACTIVE, &is_active,
+			    COL_CAN_VOIP, &can_voip,
+			    -1);
+
+	g_object_set (cell,
+		      "visible", !is_group && can_voip,
+		      NULL);
+
+	if (can_voip) {
+		g_object_set (cell,
+			      "stock-id", GOSSIP_STOCK_MIC,
+			      "stock-size", GTK_ICON_SIZE_BUTTON,
+			      NULL);
+	}
+
+	contact_list_cell_set_background (list, cell, is_group, is_active);
+}
+
+static void
 contact_list_avatar_cell_data_func (GtkTreeViewColumn *tree_column,
 				    GtkCellRenderer   *cell,
 				    GtkTreeModel      *model,
@@ -2218,7 +2317,8 @@ contact_list_expander_cell_data_func (Gt
 static GtkWidget *
 contact_list_get_contact_menu (GossipContactList *list,
 			       gboolean           can_send_file,
-			       gboolean           can_show_log)
+			       gboolean           can_show_log,
+			       gboolean           can_voip)
 {
 	GossipContactListPriv *priv;
 	GtkAction             *action;
@@ -2230,6 +2330,9 @@ contact_list_get_contact_menu (GossipCon
 	action = gtk_ui_manager_get_action (priv->ui, "/Contact/Log");
 	gtk_action_set_sensitive (action, can_show_log);
 
+	action = gtk_ui_manager_get_action (priv->ui, "/Contact/Call");
+	gtk_action_set_sensitive (action, can_voip);
+
 	/* FIXME: disable file transfer until finished. */
 	action = gtk_ui_manager_get_action (priv->ui, "/Contact/SendFile");
 	gtk_action_set_visible (action, can_send_file);
@@ -2259,18 +2362,21 @@ gossip_contact_list_get_contact_menu (Go
 				      GossipContact     *contact)
 {
 	GtkWidget *menu;
+	gboolean   can_voip;
 	gboolean   can_show_log;
 	gboolean   can_send_file;
 
 	g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), NULL);
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 
+	can_voip = gossip_contact_can_voip (contact);
 	can_show_log = gossip_log_exists_for_contact (contact);
 	can_send_file = FALSE;
 
 	menu = contact_list_get_contact_menu (list,
 					      can_send_file,
-					      can_show_log);
+					      can_show_log,
+					      can_voip);
 	return menu;
 }
 
@@ -2633,6 +2739,17 @@ contact_list_action_cb (GtkAction       
 		contact = gossip_contact_list_get_selected (list);
 		contact_list_action_activated (list, contact);
 		g_object_unref (contact);
+		return;
+	}
+
+	if (strcmp (name, "Call") == 0) {
+		GossipContact *contact;
+
+		contact = gossip_contact_list_get_selected (list);
+		if (contact) {
+			gossip_voip_window_present (contact, FALSE);
+			g_object_unref (contact);
+		}
 
 		return;
 	}
Index: src/gossip-app.c
===================================================================
--- src/gossip-app.c	(revision 2310)
+++ src/gossip-app.c	(working copy)
@@ -53,6 +53,7 @@
 #include "gossip-chatrooms-window.h"
 #include "gossip-contact-list.h"
 #include "gossip-ft-window.h"
+#include "gossip-voip-window.h"
 #include "gossip-geometry.h"
 #include "gossip-group-chat.h"
 #include "gossip-idle.h"
@@ -277,6 +278,9 @@ static void            app_session_proto
 static gchar *         app_session_get_password_cb            (GossipSession            *session,
 							       GossipAccount            *account,
 							       gpointer                  user_data);
+static void            app_session_voip_new_call_cb           (GossipSession            *session,
+							       GossipContact            *contact,
+							       gpointer                  user_data);
 static void            app_accels_load                        (void);
 static void            app_accels_save                        (void);
 static void            app_show_hide_list_cb                  (GtkWidget                *widget,
@@ -451,6 +455,10 @@ app_finalize (GObject *object)
 					      app_session_chatroom_auto_connect_cb,
 					      NULL);
 
+	g_signal_handlers_disconnect_by_func (priv->session,
+					      app_session_voip_new_call_cb,
+					      NULL);
+
 	g_signal_handlers_disconnect_by_func (priv->event_manager,
 					      app_event_added_cb,
 					      NULL);
@@ -558,6 +566,10 @@ app_setup (GossipSession *session)
 			  G_CALLBACK (app_session_chatroom_auto_connect_cb),
 			  NULL);
 
+	g_signal_connect (priv->session, "voip-incoming",
+			  G_CALLBACK (app_session_voip_new_call_cb),
+			  NULL);
+
 	g_signal_connect (priv->event_manager, "event-added",
 			  G_CALLBACK (app_event_added_cb),
 			  NULL);
@@ -1602,6 +1614,14 @@ app_session_get_password_cb (GossipSessi
 	return password;
 }
 
+static void
+app_session_voip_new_call_cb (GossipSession *session,
+			      GossipContact *contact,
+			      gpointer       user_data)
+{
+	gossip_voip_window_present (contact, TRUE);
+}
+
 /*
  * Accels
  */
Index: src/gossip-voip-window.c
===================================================================
--- src/gossip-voip-window.c	(revision 0)
+++ src/gossip-voip-window.c	(revision 0)
@@ -0,0 +1,599 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006 Raphael Slinckx <raphael@slinckx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <glib/gi18n.h>
+
+#include <libgossip/gossip-debug.h>
+#include <libgossip/gossip-protocol.h>
+#include <libgossip/gossip-contact.h>
+#include <libgossip/gossip-voip-provider.h>
+
+#include "gossip-app.h"
+#include "gossip-stock.h"
+#include "gossip-ui-utils.h"
+
+#include "gossip-voip-window.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_VOIP_WINDOW, GossipVoipWindowPriv))
+
+#define DEBUG_DOMAIN "VoipWindow"
+
+struct _GossipVoipWindowPriv {
+	GtkWidget     *window;
+	GtkWidget     *monitor_container;
+	GtkWidget     *call_status_label;
+	GtkWidget     *call_toggle_button;
+	GtkWidget     *video_container;
+	GtkWidget     *video_output_frame;
+	GtkWidget     *video_preview_frame;
+	GtkWidget     *video_output;
+	GtkWidget     *video_preview;
+	GtkWidget     *audio_input_container;
+	GtkWidget     *audio_output_container;
+
+	GossipContact *contact;
+	gchar         *name;
+
+	guint          voip_state;
+	guint          origin;
+};
+
+static void       gossip_voip_window_class_init         (GossipVoipWindowClass *klass);
+static void       gossip_voip_window_init               (GossipVoipWindow      *window);
+static void       gossip_voip_window_finalize           (GObject               *object);
+
+static GossipVoipProvider *voip_window_get_voip_provider (GossipVoipWindow *window);
+
+static void           voip_window_destroy_cb            (GtkWidget              *widget,
+								 GossipVoipWindow      *window);
+static void           voip_window_contact_updated              (GossipContact          *contact,
+								 GParamSpec             *param,
+								 GossipVoipWindow       *window);
+static void           voip_window_voip_created          (GossipVoipProvider *provider,
+								GossipContact    *contact,
+								GossipVoipWindow *window);
+static void           voip_window_voip_streams_changed  (GossipVoipProvider *provider,
+								GossipContact               *contact,
+								GossipVoipProviderStreamType streams,
+								GossipVoipWindow            *window);
+static void           voip_window_voip_state_changed           (GossipVoipProvider *provider,
+								GossipContact           *contact,
+								GossipVoipProviderState  state,
+								GossipVoipProviderOrigin origin,
+								GossipVoipWindow        *window);
+static void           voip_window_call_toggled_cb              (GtkWidget              *button,
+								GossipVoipWindow *window);
+static void           voip_window_in_mute_toggled_cb           (GtkWidget              *button,
+								GossipVoipWindow *window);
+static void           voip_window_out_mute_toggled_cb          (GtkWidget              *button,
+								GossipVoipWindow *window);
+static void           voip_window_out_volume_changed_cb          (GtkWidget              *button,
+								GossipVoipWindow *window);
+
+static GossipVoipWindow *window_instance;
+
+G_DEFINE_TYPE (GossipVoipWindow, gossip_voip_window, G_TYPE_OBJECT);
+
+static void
+gossip_voip_window_class_init (GossipVoipWindowClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = gossip_voip_window_finalize;
+	window_instance = NULL;
+
+	g_type_class_add_private (object_class, sizeof (GossipVoipWindowPriv));
+}
+
+static void
+gossip_voip_window_init (GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	GladeXML             *glade;
+
+	priv = GET_PRIV (window);
+
+	glade = gossip_glade_get_file ("chat.glade",
+				       "call_window",
+				       NULL,
+				       "call_window", &priv->window,
+				       "call_status_label", &priv->call_status_label,
+				       "call_toggle_button", &priv->call_toggle_button,
+				       "monitor_container", &priv->monitor_container,
+				       "audio_input_container", &priv->audio_input_container,
+				       "audio_output_container", &priv->audio_output_container,
+				       /* FIXME: When these actually work, uncomment and implement
+				       "audio_input_monitor", ,
+				       "audio_input_monitor_image", ,
+				       "audio_output_monitor", ,
+				       "audio_output_monitor_image", ,
+				       "in_mute_toggle_button", ,
+				       "out_mute_toggle_button", ,
+				       "in_volume_scale", ,
+				       "out_volume_scale", ,
+				       */
+				       NULL);
+
+	gossip_glade_connect (glade,
+			      window,
+			      "call_window", "destroy", voip_window_destroy_cb,
+			      "call_toggle_button", "toggled", voip_window_call_toggled_cb,
+			      "in_mute_toggle_button", "toggled", voip_window_in_mute_toggled_cb,
+			      "out_mute_toggle_button", "toggled", voip_window_out_mute_toggled_cb,
+			      "out_volume_scale", "value-changed", voip_window_out_volume_changed_cb,
+			      NULL);
+
+	g_object_unref (glade);
+
+	/* FIXME: Aspect ratio should be computed automatically */
+	priv->video_output_frame = gtk_aspect_frame_new (NULL,
+			0.5, 0.5,
+			(gfloat) 4.0/3.0,
+			FALSE);
+	priv->video_preview_frame = gtk_aspect_frame_new (NULL,
+			0.5, 0.5,
+			(gfloat) 4.0/3.0,
+			FALSE);
+
+	gtk_frame_set_shadow_type (GTK_FRAME (priv->video_output_frame),
+			GTK_SHADOW_ETCHED_IN);
+	gtk_frame_set_shadow_type (GTK_FRAME (priv->video_preview_frame),
+			GTK_SHADOW_ETCHED_IN);
+
+	priv->video_container = gtk_layout_new (NULL, NULL);
+	gtk_widget_set_size_request (priv->video_container, 640, 480);
+	g_object_set (priv->video_container,
+			"width", 640,
+			"height", 480,
+			NULL);
+	gtk_widget_show (priv->video_container);
+
+	gtk_table_attach (GTK_TABLE (priv->monitor_container),
+			priv->video_container,
+			0, 2,
+			0, 1,
+			GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL,
+			0, 0);
+
+	priv->video_output = gtk_socket_new ();
+	priv->video_preview = gtk_socket_new ();
+
+	gtk_container_add (GTK_CONTAINER (priv->video_output_frame),
+			priv->video_output);
+	gtk_container_add (GTK_CONTAINER (priv->video_preview_frame),
+			priv->video_preview);
+
+	gtk_widget_set_size_request (priv->video_output, 640, 480);
+	gtk_widget_set_size_request (priv->video_preview, 160, 120);
+	gtk_widget_set_size_request (priv->video_output_frame, 640, 480);
+	gtk_widget_set_size_request (priv->video_preview_frame, 160, 120);
+
+	gtk_layout_put (GTK_LAYOUT (priv->video_container),
+			priv->video_output_frame,
+			0, 0);
+
+	gtk_layout_put (GTK_LAYOUT (priv->video_container),
+			priv->video_preview_frame,
+			640-160-3, 3);
+
+	gtk_widget_show_all (priv->video_output_frame);
+	gtk_widget_show_all (priv->video_preview_frame);
+
+	g_object_set (priv->audio_input_container, "visible", FALSE, NULL);
+	g_object_set (priv->audio_output_container, "visible", FALSE, NULL);
+	g_object_set (priv->video_container, "visible", FALSE, NULL);
+
+	/* connect to signals outside glade */
+
+	/* Set up private details */
+	priv->origin = GOSSIP_VOIP_PROVIDER_ORIGIN_UNKNOWN;
+	priv->voip_state = GOSSIP_VOIP_PROVIDER_STATE_NONE;
+}
+
+static void
+gossip_voip_window_finalize (GObject *object)
+{
+	GossipVoipWindow     *window;
+	GossipVoipWindowPriv *priv;
+	GossipVoipProvider    *provider;
+
+	window = GOSSIP_VOIP_WINDOW (object);
+	priv = GET_PRIV (window);
+
+	gossip_debug (DEBUG_DOMAIN, "Finalizing");
+
+	/* Disconnect signals to session */
+	g_signal_handlers_disconnect_by_func (priv->contact,
+					      voip_window_contact_updated,
+					      window);
+
+	provider = voip_window_get_voip_provider (window);
+
+	/* Remove the video preview */
+	gossip_voip_provider_set_video_preview (provider,
+			FALSE,
+			gtk_socket_get_id (GTK_SOCKET (priv->video_preview)));
+
+	/* Stop any active conversation and unref voip-related stuff */
+	gossip_voip_provider_end (provider, priv->contact);
+
+	gtk_widget_destroy (priv->window);
+	g_object_unref (priv->contact);
+	g_free (priv->name);
+
+	G_OBJECT_CLASS (gossip_voip_window_parent_class)->finalize (object);
+}
+
+GossipVoipWindow *
+gossip_voip_window_present (GossipContact *contact, gboolean incoming)
+{
+	GossipVoipWindowPriv *priv;
+	GossipVoipProvider   *provider;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
+
+	if (window_instance != NULL)
+	{
+		/* There is already an existing voip window */
+		priv = GET_PRIV (window_instance);
+		if (gossip_contact_equal (priv->contact, contact))
+		{
+			/* It's the correct one, let's present it */
+			gtk_window_present (GTK_WINDOW (priv->window));
+			return window_instance;
+		}
+		else if (incoming)
+		{
+			GtkWidget *dialog;
+			/* It's not the correct window, and the call is incoming.
+			 * We decline the incoming call
+			 * FIXME: Signal the denial to the UI
+			 */
+			dialog = gtk_message_dialog_new (NULL, 0,
+					GTK_MESSAGE_INFO,
+					GTK_BUTTONS_CLOSE,
+					_("'%s' tried to call you while you where talking.\nThe call has been declined."),
+					gossip_contact_get_name (contact));
+
+			/* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+			g_signal_connect_swapped (dialog, "response",
+					G_CALLBACK (gtk_widget_destroy),
+					dialog);
+
+			provider = voip_window_get_voip_provider (window_instance);
+			gossip_voip_provider_end (provider, contact);
+
+			return NULL;
+		}
+		else
+		{
+			/* Outgoing call. We override any existing conversation.
+			 * The exiting window need to be destroyed first.
+			 * FIXME: Maybe ask the user before closing the existing conv.
+			 */
+			g_object_unref (window_instance);
+			window_instance = NULL;
+		}
+	}
+
+	window_instance = GOSSIP_VOIP_WINDOW (g_object_new (GOSSIP_TYPE_VOIP_WINDOW, NULL));
+	priv = GET_PRIV (window_instance);
+	priv->contact = g_object_ref (contact);
+	priv->name = g_strdup (gossip_contact_get_name (contact));
+
+	g_signal_connect (priv->contact,
+			"notify::name",
+			 G_CALLBACK (voip_window_contact_updated),
+			 window_instance);
+
+	/* Setup the voip provider things */
+	provider = voip_window_get_voip_provider (window_instance);
+	g_signal_connect_object (provider,
+			"voip-created",
+			G_CALLBACK (voip_window_voip_created),
+			window_instance, 0);
+
+	g_signal_connect_object (provider,
+			"voip-state-changed",
+			G_CALLBACK (voip_window_voip_state_changed),
+			window_instance, 0);
+
+	g_signal_connect_object (provider,
+			"voip-streams-changed",
+			G_CALLBACK (voip_window_voip_streams_changed),
+			window_instance, 0);
+
+
+	/* Show the preview window */
+	gossip_voip_provider_set_video_preview (provider,
+			TRUE,
+			gtk_socket_get_id (GTK_SOCKET (priv->video_preview)));
+
+	gtk_window_present (GTK_WINDOW (priv->window));
+
+	/* Start the call if requested by the user */
+	if (!incoming)
+	{
+		/* This is an outgoing call, the user asked for it.
+		 * Initiate the calling.
+		 * When the call comes from outside, we wait because the
+		 * user need to click on accept before
+		 */
+		gtk_toggle_button_set_active (
+				GTK_TOGGLE_BUTTON (priv->call_toggle_button),
+				TRUE);
+	}
+
+	return window_instance;
+}
+
+static void
+voip_window_destroy_cb (GtkWidget         *widget,
+				GossipVoipWindow *window)
+{
+	gossip_debug (DEBUG_DOMAIN, "Destroyed");
+	g_return_if_fail (window == window_instance);
+
+	g_object_unref (window_instance);
+	window_instance = NULL;
+}
+
+static void
+voip_window_contact_updated (GossipContact     *contact,
+			      GParamSpec        *param,
+			      GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	gchar *title;
+
+	priv = GET_PRIV (window);
+	if (strcmp (priv->name, gossip_contact_get_name (contact)) != 0) {
+		g_free (priv->name);
+		priv->name = g_strdup (gossip_contact_get_name (contact));
+
+		title = g_strdup_printf ("Conversation with %s", priv->name);
+		gtk_window_set_title (GTK_WINDOW (priv->window), title);
+		g_free (title);
+	}
+}
+
+static void
+voip_window_voip_created (GossipVoipProvider *provider,
+			GossipContact *contact,
+			GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	priv = GET_PRIV (window);
+
+	if (!gossip_contact_equal (priv->contact, contact)) {
+		return;
+	}
+
+	/* Setup the video output window */
+	gossip_debug (DEBUG_DOMAIN, "Voip Created, setting output window");
+	gossip_voip_provider_set_video_output (provider,
+			priv->contact,
+			gtk_socket_get_id (GTK_SOCKET (priv->video_output)));
+}
+
+static void
+voip_window_voip_streams_changed  (GossipVoipProvider *provider,
+			GossipContact               *contact,
+			GossipVoipProviderStreamType streams,
+			GossipVoipWindow            *window)
+{
+	GossipVoipWindowPriv *priv;
+	gboolean              audio_present = FALSE;
+	gboolean              video_present = FALSE;
+
+	priv = GET_PRIV (window);
+
+	if (!gossip_contact_equal (priv->contact, contact)) {
+		return;
+	}
+
+	/* FIXME: Show/Hide UI elements according to the present stream types */
+	if (streams & GOSSIP_VOIP_PROVIDER_STREAM_TYPE_AUDIO) {
+		audio_present = TRUE;
+	}
+	if (streams & GOSSIP_VOIP_PROVIDER_STREAM_TYPE_VIDEO) {
+		video_present = TRUE;
+	}
+
+	gossip_debug (DEBUG_DOMAIN, "Showing audio? %d : Showing video? %d", audio_present, video_present);
+	g_object_set (priv->audio_input_container, "visible", audio_present, NULL);
+	g_object_set (priv->audio_output_container, "visible", audio_present, NULL);
+	g_object_set (priv->video_container, "visible", video_present, NULL);
+}
+
+static void
+voip_window_voip_state_changed (GossipVoipProvider *provider,
+			GossipContact           *contact,
+			GossipVoipProviderState  state,
+			GossipVoipProviderOrigin origin,
+			GossipVoipWindow        *window)
+{
+	GossipVoipWindowPriv *priv;
+	const gchar *origin_string;
+	const gchar *state_string;
+	const gchar *button_string;
+	gchar *status;
+
+	priv = GET_PRIV (window);
+
+	if (!gossip_contact_equal (priv->contact, contact)) {
+		return;
+	}
+
+	switch (origin)
+	{
+		case GOSSIP_VOIP_PROVIDER_ORIGIN_INCOMING:
+			origin_string = _("Incoming Call");
+			break;
+		case GOSSIP_VOIP_PROVIDER_ORIGIN_OUTGOING:
+			origin_string = _("Outgoing Call");
+			break;
+		default:
+			origin_string = _("Not Talking");
+			break;
+	}
+
+	switch (state)
+	{
+		case GOSSIP_VOIP_PROVIDER_STATE_CALLING:
+			state_string = _("Calling...");
+			button_string = _("Hang Up");
+			break;
+		case GOSSIP_VOIP_PROVIDER_STATE_WAITING_ACCEPT:
+			state_string =  _("Waiting for authorisation...");
+			button_string = _("Accept");
+			break;
+		case GOSSIP_VOIP_PROVIDER_STATE_CONNECTING:
+			state_string = _("Connecting...");
+			button_string = _("Hang Up");
+			break;
+		case GOSSIP_VOIP_PROVIDER_STATE_TALKING:
+			state_string = _("Talking");
+			button_string = _("Hang Up");
+			break;
+		case GOSSIP_VOIP_PROVIDER_STATE_CLOSED:
+			state_string = _("Ended");
+			button_string = _("Call");
+			break;
+		default:
+			state_string = _("Unknown");
+			button_string = _("Call");
+			break;
+	}
+
+	status = g_strdup_printf ("%s\n<span size='smaller' foreground='#555'>%s</span>",
+			origin_string, state_string);
+	gossip_debug (DEBUG_DOMAIN, "Voip State Changed in UI: %s", status);
+	gtk_label_set_markup (GTK_LABEL (priv->call_status_label), status);
+	g_free (status);
+
+	gtk_button_set_label (GTK_BUTTON (priv->call_toggle_button), button_string);
+
+	priv->voip_state = state;
+	priv->origin = origin;
+
+	if (state == GOSSIP_VOIP_PROVIDER_STATE_CLOSED)
+	{
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->call_toggle_button), FALSE);
+
+		/* Force a refresh of the video frames */
+		gtk_widget_queue_draw (priv->video_output_frame);
+
+		/* Hide the active ui elements */
+		g_object_set (priv->audio_input_container, "visible", FALSE, NULL);
+		g_object_set (priv->audio_output_container, "visible", FALSE, NULL);
+		g_object_set (priv->video_container, "visible", FALSE, NULL);
+	}
+}
+
+static void
+voip_window_call_toggled_cb (GtkWidget        *button,
+			GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	GossipVoipProvider    *provider;
+	gboolean               active;
+
+	priv = GET_PRIV (window);
+	provider = voip_window_get_voip_provider (window);
+
+	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+	gossip_debug (DEBUG_DOMAIN, "Call toggled: %d %d", active, priv->voip_state);
+	if (active && priv->voip_state != GOSSIP_VOIP_PROVIDER_STATE_WAITING_ACCEPT)
+	{
+		gossip_voip_provider_call (provider, priv->contact);
+	}
+	else if (active && priv->voip_state == GOSSIP_VOIP_PROVIDER_STATE_WAITING_ACCEPT)
+	{
+		gossip_voip_provider_accept (provider, priv->contact);
+	}
+	else if (!active && priv->voip_state != GOSSIP_VOIP_PROVIDER_STATE_CLOSED)
+	{
+		gossip_voip_provider_end (provider, priv->contact);
+	}
+}
+
+static void
+voip_window_in_mute_toggled_cb (GtkWidget        *button,
+			GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	GossipVoipProvider    *provider;
+	gboolean               active;
+
+	priv = GET_PRIV (window);
+	provider = voip_window_get_voip_provider (window);
+
+	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+	gossip_voip_provider_mute_input (provider, priv->contact, !active);
+}
+
+static void
+voip_window_out_mute_toggled_cb (GtkWidget        *button,
+			GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	GossipVoipProvider    *provider;
+	gboolean               active;
+
+	priv = GET_PRIV (window);
+	provider = voip_window_get_voip_provider (window);
+
+	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+	gossip_voip_provider_mute_output (provider, priv->contact, !active);
+}
+
+static void
+voip_window_out_volume_changed_cb (GtkWidget        *scale,
+			GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	GossipVoipProvider    *provider;
+	guint                  volume;
+
+	priv = GET_PRIV (window);
+	provider = voip_window_get_voip_provider (window);
+
+	volume = (guint) gtk_range_get_value (GTK_RANGE (scale));
+	gossip_voip_provider_set_output_volume (provider, priv->contact, volume);
+}
+
+static GossipVoipProvider *
+voip_window_get_voip_provider (GossipVoipWindow *window)
+{
+	GossipVoipWindowPriv *priv;
+	GossipAccount         *account;
+
+	priv = GET_PRIV (window);
+	account = gossip_contact_get_account (priv->contact);
+	return gossip_session_get_voip_provider (
+			gossip_app_get_session (),
+			account);
+}
+
Index: src/gossip-voip-window.h
===================================================================
--- src/gossip-voip-window.h	(revision 0)
+++ src/gossip-voip-window.h	(revision 0)
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006 Raphael Slinckx <raphael@slinckx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GOSSIP_VOIP_WINDOW_H__
+#define __GOSSIP_VOIP_WINDOW_H__
+
+#include <glib-object.h>
+
+#include <libgossip/gossip-contact.h>
+
+#define GOSSIP_TYPE_VOIP_WINDOW         (gossip_voip_window_get_type ())
+#define GOSSIP_VOIP_WINDOW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_VOIP_WINDOW, GossipVoipWindow))
+#define GOSSIP_VOIP_WINDOW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_VOIP_WINDOW, GossipVoipWindowClass))
+#define GOSSIP_IS_VOIP_WINDOW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_VOIP_WINDOW))
+#define GOSSIP_IS_VOIP_WINDOW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_VOIP_WINDOW))
+#define GOSSIP_VOIP_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_VOIP_WINDOW, GossipVoipWindowClass))
+
+typedef struct _GossipVoipWindow      GossipVoipWindow;
+typedef struct _GossipVoipWindowClass GossipVoipWindowClass;
+typedef struct _GossipVoipWindowPriv  GossipVoipWindowPriv;
+
+struct _GossipVoipWindow {
+	GObject parent;
+};
+
+struct _GossipVoipWindowClass {
+	GObjectClass parent_class;
+};
+
+GType             gossip_voip_window_get_type (void);
+GossipVoipWindow *gossip_voip_window_present  (GossipContact *contact,
+					       gboolean       incoming);
+
+#endif /* __GOSSIP_VOIP_WINDOW_H__ */
Index: src/gossip-cell-renderer-activatable.c
===================================================================
--- src/gossip-cell-renderer-activatable.c	(revision 0)
+++ src/gossip-cell-renderer-activatable.c	(revision 0)
@@ -0,0 +1,118 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007  Raphael Slinckx <raphael@slinckx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtktreeview.h>
+
+#include <libgossip/gossip-debug.h>
+
+#include "gossip-cell-renderer-activatable.h"
+
+#define DEBUG_DOMAIN "GossipCellRendererActivatable"
+
+enum {
+	PATH_ACTIVATED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void     gossip_cell_renderer_activatable_init       (GossipCellRendererActivatable      *cell);
+static void     gossip_cell_renderer_activatable_class_init (GossipCellRendererActivatableClass *klass);
+static gboolean cell_renderer_activatable_activate          (GtkCellRenderer                    *cell,
+							     GdkEvent                           *event,
+							     GtkWidget                          *widget,
+							     const gchar                        *path,
+							     GdkRectangle                       *background_area,
+							     GdkRectangle                       *cell_area,
+							     GtkCellRendererState                flags);
+
+G_DEFINE_TYPE (GossipCellRendererActivatable, gossip_cell_renderer_activatable, GTK_TYPE_CELL_RENDERER_PIXBUF)
+
+static void
+gossip_cell_renderer_activatable_init (GossipCellRendererActivatable *cell)
+{
+	g_object_set (cell,
+		      "xpad", 0,
+		      "ypad", 0,
+		      "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+		      "follow-state", TRUE,
+		      NULL);
+}
+
+static void
+gossip_cell_renderer_activatable_class_init (GossipCellRendererActivatableClass *klass)
+{
+	GtkCellRendererClass *cell_class;
+
+	cell_class = GTK_CELL_RENDERER_CLASS (klass);
+	cell_class->activate = cell_renderer_activatable_activate;
+
+	signals[PATH_ACTIVATED] =
+		g_signal_new ("path-activated",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1, G_TYPE_STRING);
+}
+
+GtkCellRenderer *
+gossip_cell_renderer_activatable_new (void)
+{
+	return g_object_new (GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE, NULL);
+}
+
+static gboolean
+cell_renderer_activatable_activate (GtkCellRenderer      *cell,
+				    GdkEvent             *event,
+				    GtkWidget            *widget,
+				    const gchar          *path_string,
+				    GdkRectangle         *background_area,
+				    GdkRectangle         *cell_area,
+				    GtkCellRendererState  flags)
+{
+	GossipCellRendererActivatable *activatable;
+	gint                           ex, ey, bx, by, bw, bh;
+
+	activatable = GOSSIP_CELL_RENDERER_ACTIVATABLE (cell);
+
+	if (!GTK_IS_TREE_VIEW (widget) ||
+	    event->type != GDK_BUTTON_PRESS) {
+		return FALSE;
+	}
+
+	ex  = (gint) ((GdkEventButton *) event)->x;
+	ey  = (gint) ((GdkEventButton *) event)->y;
+	bx = background_area->x;
+	by = background_area->y;
+	bw = background_area->width;
+	bh = background_area->height;
+
+	if (ex < bx || ex > (bx+bw) || ey < by || ey > (by+bh)){
+		/* Click wasn't on the icon */
+		return TRUE;
+	}
+
+	g_signal_emit (activatable, signals[PATH_ACTIVATED], 0, path_string);
+
+	return FALSE;
+}
Index: src/gossip-cell-renderer-activatable.h
===================================================================
--- src/gossip-cell-renderer-activatable.h	(revision 0)
+++ src/gossip-cell-renderer-activatable.h	(revision 0)
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007  Raphael Slinckx <raphael@slinckx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GOSSIP_CELL_RENDERER_ACTIVATABLE_H__
+#define __GOSSIP_CELL_RENDERER_ACTIVATABLE_H__
+
+#include <gtk/gtkcellrendererpixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE            (gossip_cell_renderer_activatable_get_type ())
+#define GOSSIP_CELL_RENDERER_ACTIVATABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE, GossipCellRendererActivatable))
+#define GOSSIP_CELL_RENDERER_ACTIVATABLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE, GossipCellRendererActivatableClass))
+#define GOSSIP_IS_CELL_RENDERER_ACTIVATABLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE))
+#define GOSSIP_IS_CELL_RENDERER_ACTIVATABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE))
+#define GOSSIP_CELL_RENDERER_ACTIVATABLE_GET_CLASS(obj)	 (G_TYPE_INSTANCE_GET_CLASS ((obj), GOSSIP_TYPE_CELL_RENDERER_ACTIVATABLE, GossipCellRendererActivatableClass))
+
+typedef struct _GossipCellRendererActivatable      GossipCellRendererActivatable;
+typedef struct _GossipCellRendererActivatableClass GossipCellRendererActivatableClass;
+
+struct _GossipCellRendererActivatable {
+  GtkCellRendererPixbuf parent;
+};
+
+struct _GossipCellRendererActivatableClass {
+  GtkCellRendererPixbufClass parent_class;
+};
+
+GType            gossip_cell_renderer_activatable_get_type (void) G_GNUC_CONST;
+GtkCellRenderer *gossip_cell_renderer_activatable_new      (void);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_CELL_RENDERER_ACTIVATABLE_H__ */
Index: src/gossip-chat-window.c
===================================================================
--- src/gossip-chat-window.c	(revision 2310)
+++ src/gossip-chat-window.c	(working copy)
@@ -43,6 +43,7 @@
 #include "gossip-app.h"
 #include "gossip-chat-invite.h"
 #include "gossip-chat-window.h"
+#include "gossip-voip-window.h"
 #include "gossip-contact-info-dialog.h"
 #include "gossip-log-window.h"
 #include "gossip-new-chatroom-dialog.h"
@@ -78,6 +79,8 @@ struct _GossipChatWindowPriv {
 	/* Menu items. */
 	GtkWidget   *menu_conv_clear;
 	GtkWidget   *menu_conv_insert_smiley;
+	GtkWidget   *menu_conv_call;
+	GtkWidget   *menu_conv_call_separator;
 	GtkWidget   *menu_conv_log;
 	GtkWidget   *menu_conv_separator;
 	GtkWidget   *menu_conv_add_contact;
@@ -138,6 +141,8 @@ static void       chat_window_info_activ
 							 GossipChatWindow      *window);
 static void       chat_window_add_contact_activate_cb   (GtkWidget             *menuitem,
 							 GossipChatWindow      *window);
+static void       chat_window_call_activate_cb          (GtkWidget             *menuitem,
+							 GossipChatWindow      *window);
 static void       chat_window_log_activate_cb           (GtkWidget             *menuitem,
 							 GossipChatWindow      *window);
 static void       chat_window_show_contacts_toggled_cb  (GtkWidget             *menuitem,
@@ -289,6 +294,8 @@ gossip_chat_window_init (GossipChatWindo
 				       "menu_conv", &menu_conv,
 				       "menu_conv_clear", &priv->menu_conv_clear,
 				       "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
+				       "menu_conv_call", &priv->menu_conv_call,
+				       "menu_conv_call_separator", &priv->menu_conv_call_separator,
 				       "menu_conv_log", &priv->menu_conv_log,
 				       "menu_conv_separator", &priv->menu_conv_separator,
 				       "menu_conv_add_contact", &priv->menu_conv_add_contact,
@@ -315,6 +322,7 @@ gossip_chat_window_init (GossipChatWindo
 			      "chat_window", "configure-event", chat_window_configure_event_cb,
 			      "menu_conv", "activate", chat_window_conv_activate_cb,
 			      "menu_conv_clear", "activate", chat_window_clear_activate_cb,
+			      "menu_conv_call", "activate", chat_window_call_activate_cb,
 			      "menu_conv_log", "activate", chat_window_log_activate_cb,
 			      "menu_conv_add_contact", "activate", chat_window_add_contact_activate_cb,
 			      "menu_conv_info", "activate", chat_window_info_activate_cb,
@@ -940,6 +948,22 @@ chat_window_add_contact_activate_cb (Gtk
 }
 
 static void
+chat_window_call_activate_cb (GtkWidget        *menuitem,
+			      GossipChatWindow *window)
+{
+	GossipChatWindowPriv *priv;
+
+	priv = GET_PRIV (window);
+
+	if (!gossip_chat_is_group_chat (priv->current_chat)) {
+		GossipContact *contact;
+
+		contact = gossip_chat_get_contact (priv->current_chat);
+		gossip_voip_window_present (contact, FALSE);
+	}
+}
+
+static void
 chat_window_log_activate_cb (GtkWidget        *menuitem,
 			     GossipChatWindow *window)
 {
@@ -1027,6 +1051,7 @@ chat_window_conv_activate_cb (GtkWidget 
 {
 	GossipChatWindowPriv *priv;
 	gboolean              log_exists = FALSE;
+	gboolean              can_voip = FALSE;
 
 	priv = GET_PRIV (window);
 
@@ -1046,9 +1071,13 @@ chat_window_conv_activate_cb (GtkWidget 
 		if (contact) {
 			log_exists = gossip_log_exists_for_contact (contact);
 		}
+		can_voip = gossip_contact_can_voip (contact);
 	}
 
 	gtk_widget_set_sensitive (priv->menu_conv_log, log_exists);
+	gossip_debug (DEBUG_DOMAIN, "Show voip menu items: %d", can_voip);
+	g_object_set (priv->menu_conv_call, "visible", can_voip, NULL);
+	g_object_set (priv->menu_conv_call_separator, "visible", can_voip, NULL);
 }
 
 static void
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision 2310)
+++ src/Makefile.am	(working copy)
@@ -52,6 +52,7 @@ gossip_SOURCES = 				                   
 	gossip-avatar-image.c		   gossip-avatar-image.h		\
 	gossip-cell-renderer-expander.c    gossip-cell-renderer-expander.h      \
 	gossip-cell-renderer-text.c        gossip-cell-renderer-text.h          \
+	gossip-cell-renderer-activatable.c gossip-cell-renderer-activatable.h   \
 	gossip-chat.c		           gossip-chat.h			\
 	gossip-chat-invite.c	           gossip-chat-invite.h			\
 	gossip-chat-manager.c              gossip-chat-manager.h               	\
@@ -78,6 +79,7 @@ gossip_SOURCES = 				                   
 	gossip-presence-chooser.c          gossip-presence-chooser.h        	\
 	gossip-preferences.c               gossip-preferences.h	        	\
 	gossip-private-chat.c              gossip-private-chat.h               	\
+	gossip-voip-window.c               gossip-voip-window.h               	\
 	gossip-sound.c		           gossip-sound.h			\
 	gossip-stock.c		           gossip-stock.h			\
 	gossip-spell.c                     gossip-spell.h                       \
Index: libgossip/gossip-contact.h
===================================================================
--- libgossip/gossip-contact.h	(revision 2310)
+++ libgossip/gossip-contact.h	(working copy)
@@ -61,6 +61,11 @@ typedef enum {
 	GOSSIP_SUBSCRIPTION_BOTH
 } GossipSubscription;
 
+typedef enum {
+       GOSSIP_CONTACT_CAPABILITY_AUDIO = 1 << 0,
+       GOSSIP_CONTACT_CAPABILITY_VIDEO = 1 << 1,
+} GossipContactCapabilities;
+
 GType              gossip_contact_get_gtype                 (void) G_GNUC_CONST;
 
 GossipContact *    gossip_contact_new                       (GossipContactType   type,
@@ -87,6 +92,8 @@ GossipPresence *   gossip_contact_get_ac
 GList *            gossip_contact_get_presence_list         (GossipContact      *contact);
 GList *            gossip_contact_get_groups                (GossipContact      *contact);
 GossipSubscription gossip_contact_get_subscription          (GossipContact      *contact);
+GossipContactCapabilities  gossip_contact_get_capabilities  (GossipContact	*contact);
+gboolean           gossip_contact_can_voip                  (GossipContact      *contact);
 void               gossip_contact_set_id                    (GossipContact      *contact,
 							     const gchar        *id);
 void               gossip_contact_set_name                  (GossipContact      *contact,
@@ -99,6 +106,9 @@ void               gossip_contact_set_gr
 							     GList              *categories);
 void               gossip_contact_set_subscription          (GossipContact      *contact,
 							     GossipSubscription  subscription);
+void               gossip_contact_set_capabilities          (GossipContact      *contact,
+                                                             GossipContactCapabilities
+  capabilities);
 gint               gossip_contact_name_compare              (gconstpointer       a,
 							     gconstpointer       b);
 gint               gossip_contact_name_case_compare         (gconstpointer       a,
Index: libgossip/gossip-utils.c
===================================================================
--- libgossip/gossip-utils.c	(revision 2310)
+++ libgossip/gossip-utils.c	(working copy)
@@ -304,6 +304,9 @@ gossip_dbus_type_to_g_type (const gchar 
 	else if (dbus_type_string[0] == 'n') {
 		return G_TYPE_INT;
 	}
+	else if (dbus_type_string[0] == 'i') {
+		return G_TYPE_INT;
+	}
 
 	g_assert_not_reached ();
 	return G_TYPE_NONE;
Index: libgossip/libgossip-marshal.list
===================================================================
--- libgossip/libgossip-marshal.list	(revision 2310)
+++ libgossip/libgossip-marshal.list	(working copy)
@@ -10,4 +10,6 @@ VOID:OBJECT,BOOLEAN
 VOID:OBJECT,POINTER
 VOID:OBJECT,INT
 VOID:OBJECT,OBJECT,INT
+VOID:OBJECT,UINT
+VOID:OBJECT,UINT,UINT
 STRING:OBJECT
Index: libgossip/gossip-voip-provider.c
===================================================================
--- libgossip/gossip-voip-provider.c	(revision 0)
+++ libgossip/gossip-voip-provider.c	(revision 0)
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006 Raphael Slinckx
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "libgossip-marshal.h"
+#include "gossip-voip-provider.h"
+
+static void  voip_provider_base_init (gpointer g_class);
+
+enum {
+	VOIP_INCOMING,
+	VOIP_CREATED,
+	VOIP_STATE_CHANGED,
+	VOIP_STREAMS_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GType
+gossip_voip_provider_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static const GTypeInfo info =
+		{
+			sizeof (GossipVoipProviderIface),
+			voip_provider_base_init,
+			NULL,
+			NULL,
+			NULL,
+			NULL,
+			0,
+			0,
+			NULL
+		};
+
+		type = g_type_register_static (G_TYPE_INTERFACE,
+					       "GossipVoipProvider",
+					       &info, 0);
+
+		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+	}
+
+	return type;
+}
+
+static void
+voip_provider_base_init (gpointer g_class)
+{
+	static gboolean initialized = FALSE;
+
+	if (!initialized) {
+		signals[VOIP_STREAMS_CHANGED] =
+			g_signal_new ("voip-streams-changed",
+				      G_TYPE_FROM_CLASS (g_class),
+				      G_SIGNAL_RUN_LAST,
+				      0,
+				      NULL, NULL,
+				      libgossip_marshal_VOID__OBJECT_UINT,
+				      G_TYPE_NONE,
+				      2, GOSSIP_TYPE_CONTACT, G_TYPE_UINT);
+
+		signals[VOIP_STATE_CHANGED] =
+			g_signal_new ("voip-state-changed",
+				      G_TYPE_FROM_CLASS (g_class),
+				      G_SIGNAL_RUN_LAST,
+				      0,
+				      NULL, NULL,
+				      libgossip_marshal_VOID__OBJECT_UINT_UINT,
+				      G_TYPE_NONE,
+				      3, GOSSIP_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_UINT);
+
+		signals[VOIP_INCOMING] =
+			g_signal_new ("voip-incoming",
+				      G_TYPE_FROM_CLASS (g_class),
+				      G_SIGNAL_RUN_LAST,
+				      0,
+				      NULL, NULL,
+				      g_cclosure_marshal_VOID__OBJECT,
+				      G_TYPE_NONE,
+				      1, GOSSIP_TYPE_CONTACT);
+
+		signals[VOIP_CREATED] =
+			g_signal_new ("voip-created",
+				      G_TYPE_FROM_CLASS (g_class),
+				      G_SIGNAL_RUN_LAST,
+				      0,
+				      NULL, NULL,
+				      g_cclosure_marshal_VOID__OBJECT,
+				      G_TYPE_NONE,
+				      1, GOSSIP_TYPE_CONTACT);
+
+		initialized = TRUE;
+	}
+}
+
+void
+gossip_voip_provider_call (GossipVoipProvider *provider,
+			   GossipContact      *contact)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->call) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->call (provider,
+								 contact);
+	}
+}
+
+void
+gossip_voip_provider_accept (GossipVoipProvider *provider,
+			     GossipContact      *contact)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->accept) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->accept (provider,
+								   contact);
+	}
+}
+
+void
+gossip_voip_provider_end (GossipVoipProvider *provider,
+			  GossipContact      *contact)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->end) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->end (provider,
+								contact);
+	}
+}
+
+void
+gossip_voip_provider_set_video_output (GossipVoipProvider *provider,
+				       GossipContact      *contact,
+				       guint               window_id)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->set_video_output) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->set_video_output (provider,
+									     contact,
+									     window_id);
+	}
+}
+
+void
+gossip_voip_provider_set_video_preview (GossipVoipProvider *provider,
+					gboolean            active,
+					guint               window_id)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->set_video_preview) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->set_video_preview (provider,
+									      active,
+									      window_id);
+	}
+}
+
+void
+gossip_voip_provider_set_output_volume (GossipVoipProvider *provider,
+					GossipContact      *contact,
+					guint               volume)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->set_output_volume) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->set_output_volume (provider,
+									      contact,
+									      volume);
+	}
+}
+
+void
+gossip_voip_provider_mute_input (GossipVoipProvider *provider,
+				 GossipContact      *contact,
+				 gboolean            muted)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->mute_input) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->mute_input (provider,
+								       contact,
+								       muted);
+	}
+}
+
+void
+gossip_voip_provider_mute_output (GossipVoipProvider *provider,
+				  GossipContact      *contact,
+				  gboolean            muted)
+{
+	g_return_if_fail (GOSSIP_IS_VOIP_PROVIDER (provider));
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	if (GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->mute_output) {
+		GOSSIP_VOIP_PROVIDER_GET_IFACE (provider)->mute_output (provider,
+								        contact,
+								        muted);
+	}
+}
+
Index: libgossip/gossip-voip-provider.h
===================================================================
--- libgossip/gossip-voip-provider.h	(revision 0)
+++ libgossip/gossip-voip-provider.h	(revision 0)
@@ -0,0 +1,110 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006 Raphael Slinckx
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GOSSIP_VOIP_PROVIDER_H__
+#define __GOSSIP_VOIP_PROVIDER_H__
+
+#include <glib-object.h>
+
+#include "gossip-contact.h"
+
+G_BEGIN_DECLS
+
+#define GOSSIP_TYPE_VOIP_PROVIDER           (gossip_voip_provider_get_type ())
+#define GOSSIP_VOIP_PROVIDER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOSSIP_TYPE_VOIP_PROVIDER, GossipVoipProvider))
+#define GOSSIP_IS_VOIP_PROVIDER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOSSIP_TYPE_VOIP_PROVIDER))
+#define GOSSIP_VOIP_PROVIDER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GOSSIP_TYPE_VOIP_PROVIDER, GossipVoipProviderIface))
+
+typedef enum {
+	GOSSIP_VOIP_PROVIDER_STATE_NONE,
+	GOSSIP_VOIP_PROVIDER_STATE_CALLING,
+	GOSSIP_VOIP_PROVIDER_STATE_WAITING_ACCEPT,
+	GOSSIP_VOIP_PROVIDER_STATE_CONNECTING,
+	GOSSIP_VOIP_PROVIDER_STATE_TALKING,
+	GOSSIP_VOIP_PROVIDER_STATE_CLOSED,
+} GossipVoipProviderState;
+
+typedef enum {
+	GOSSIP_VOIP_PROVIDER_ORIGIN_UNKNOWN,
+	GOSSIP_VOIP_PROVIDER_ORIGIN_INCOMING,
+	GOSSIP_VOIP_PROVIDER_ORIGIN_OUTGOING,
+} GossipVoipProviderOrigin;
+
+typedef enum {
+	GOSSIP_VOIP_PROVIDER_STREAM_TYPE_AUDIO = 1 << 0,
+	GOSSIP_VOIP_PROVIDER_STREAM_TYPE_VIDEO = 1 << 1,
+} GossipVoipProviderStreamType;
+
+typedef struct _GossipVoipProvider      GossipVoipProvider;
+typedef struct _GossipVoipProviderIface GossipVoipProviderIface;
+
+struct _GossipVoipProviderIface {
+	GTypeInterface g_iface;
+
+	/* Virtual Table */
+	void (*call)              (GossipVoipProvider *provider,
+				   GossipContact      *contact);
+	void (*accept)            (GossipVoipProvider *provider,
+				   GossipContact      *contact);
+	void (*end)               (GossipVoipProvider *provider,
+				   GossipContact      *contact);
+	void (*set_video_output)  (GossipVoipProvider *provider,
+				   GossipContact      *contact,
+				   guint               window_id);
+	void (*set_video_preview) (GossipVoipProvider *provider,
+				   gboolean            active,
+				   guint               window_id);
+	void (*set_output_volume) (GossipVoipProvider *provider,
+				   GossipContact      *contact,
+				   guint               volume);
+	void (*mute_input)        (GossipVoipProvider *provider,
+				   GossipContact      *contact,
+				   gboolean            muted);
+	void (*mute_output)       (GossipVoipProvider *provider,
+				   GossipContact      *contact,
+				   gboolean            muted);
+};
+
+GType gossip_voip_provider_get_type          (void) G_GNUC_CONST;
+void  gossip_voip_provider_call              (GossipVoipProvider *provider,
+					      GossipContact      *contact);
+void  gossip_voip_provider_accept            (GossipVoipProvider *provider,
+					      GossipContact      *contact);
+void  gossip_voip_provider_end               (GossipVoipProvider *provider,
+					      GossipContact      *contact);
+void  gossip_voip_provider_set_video_output  (GossipVoipProvider *provider,
+					      GossipContact      *contact,
+					      guint               window_id);
+void  gossip_voip_provider_set_video_preview (GossipVoipProvider *provider,
+					      gboolean            active,
+					      guint               window_id);
+void  gossip_voip_provider_set_output_volume (GossipVoipProvider *provider,
+					      GossipContact      *contact,
+					      guint               volume);
+void  gossip_voip_provider_mute_input        (GossipVoipProvider *provider,
+					      GossipContact      *contact,
+					      gboolean            muted);
+void  gossip_voip_provider_mute_output       (GossipVoipProvider *provider,
+					      GossipContact      *contact,
+					      gboolean            muted);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_VOIP_PROVIDER_H__ */
Index: libgossip/gossip-session.c
===================================================================
--- libgossip/gossip-session.c	(revision 2310)
+++ libgossip/gossip-session.c	(working copy)
@@ -106,6 +106,9 @@ static void            session_protocol_
 static void            session_protocol_new_message              (GossipProtocol       *protocol,
 								  GossipMessage        *message,
 								  GossipSession        *session);
+static void            session_protocol_voip_incoming            (GossipProtocol       *protocol,
+								  GossipContact        *contact,
+								  GossipSession        *session);
 static void            session_protocol_contact_added            (GossipProtocol       *protocol,
 								  GossipContact        *contact,
 								  GossipSession        *session);
@@ -147,6 +150,7 @@ enum {
 	PROTOCOL_DISCONNECTING,
 	PROTOCOL_ERROR,
 	NEW_MESSAGE,
+	VOIP_INCOMING,
 	CONTACT_ADDED,
 	CONTACT_REMOVED,
 	COMPOSING,
@@ -266,6 +270,16 @@ gossip_session_class_init (GossipSession
 			      G_TYPE_NONE,
 			      1, GOSSIP_TYPE_MESSAGE);
 
+	signals[VOIP_INCOMING] =
+		g_signal_new ("voip-incoming",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      libgossip_marshal_VOID__OBJECT,
+			      G_TYPE_NONE,
+			      1, GOSSIP_TYPE_CONTACT);
+
 	signals[CONTACT_ADDED] =
 		g_signal_new ("contact-added",
 			      G_TYPE_FROM_CLASS (klass),
@@ -400,6 +414,9 @@ session_protocol_signals_setup (GossipSe
 	g_signal_connect (protocol, "new_message",
 			  G_CALLBACK (session_protocol_new_message),
 			  session);
+	g_signal_connect (protocol, "voip-incoming",
+			  G_CALLBACK (session_protocol_voip_incoming),
+			  session);
 	g_signal_connect (protocol, "contact-added",
 			  G_CALLBACK (session_protocol_contact_added),
 			  session);
@@ -555,6 +572,15 @@ session_protocol_new_message (GossipProt
 }
 
 static void
+session_protocol_voip_incoming (GossipProtocol *protocol,
+				GossipContact  *contact,
+				GossipSession  *session)
+{
+	/* Just relay the signal */
+	g_signal_emit (session, signals[VOIP_INCOMING], 0, contact);
+}
+
+static void
 session_protocol_contact_added (GossipProtocol *protocol,
 				GossipContact  *contact,
 				GossipSession  *session)
@@ -1474,6 +1500,25 @@ gossip_session_get_ft_provider (GossipSe
 	return GOSSIP_FT_PROVIDER (protocol);
 }
 
+GossipVoipProvider *
+gossip_session_get_voip_provider (GossipSession *session,
+				  GossipAccount *account)
+{
+	GossipSessionPriv *priv;
+	GossipProtocol    *protocol;
+
+	g_return_val_if_fail (GOSSIP_IS_SESSION (session), NULL);
+	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
+
+	priv = GET_PRIV (session);
+
+	protocol = g_hash_table_lookup (priv->accounts, account);
+
+	g_return_val_if_fail (GOSSIP_IS_PROTOCOL (protocol), NULL);
+
+	return GOSSIP_VOIP_PROVIDER (protocol);
+}
+
 GossipContact *
 gossip_session_find_contact (GossipSession *session,
 			     const gchar   *id)
Index: libgossip/Makefile.am
===================================================================
--- libgossip/Makefile.am	(revision 2310)
+++ libgossip/Makefile.am	(working copy)
@@ -60,6 +60,8 @@ libgossip_la_SOURCES =             			\
 	gossip-ft-provider.h 				\
 	gossip-log.c					\
 	gossip-log.h					\
+	gossip-voip-provider.c	 			\
+	gossip-voip-provider.h 				\
 	gossip-message.c           			\
 	gossip-message.h           			\
 	gossip-conf.h          				\
Index: libgossip/gossip-session.h
===================================================================
--- libgossip/gossip-session.h	(revision 2310)
+++ libgossip/gossip-session.h	(working copy)
@@ -36,6 +36,7 @@
 #include "gossip-contact-manager.h"
 #include "gossip-chatroom-manager.h"
 #include "gossip-ft-provider.h"
+#include "gossip-voip-provider.h"
 
 G_BEGIN_DECLS
 
@@ -59,13 +60,13 @@ struct _GossipSessionClass {
 
 GType           gossip_session_get_type                (void) G_GNUC_CONST;
 
-GossipSession * gossip_session_new                     (const gchar             *accounts_file,
-							const gchar             *contacts_file,
-							const gchar             *chatrooms_file);
+GossipSession * gossip_session_new                     (const gchar            *accounts_file,
+							const gchar            *contacts_file,
+							const gchar            *chatrooms_file);
 
 /* Get protocol */
-GossipProtocol *gossip_session_get_protocol            (GossipSession           *session,
-							GossipAccount           *account);
+GossipProtocol *gossip_session_get_protocol            (GossipSession          *session,
+							GossipAccount          *account);
 
 /* Providers */
 GossipChatroomProvider *
@@ -74,6 +75,9 @@ GossipChatroomProvider *
 GossipFTProvider *
 		gossip_session_get_ft_provider         (GossipSession          *session,
 							GossipAccount          *account);
+GossipVoipProvider *
+		gossip_session_get_voip_provider       (GossipSession          *session,
+							GossipAccount          *account);
 
 /* Accounts */
 GossipAccountManager *
Index: libgossip/gossip-contact.c
===================================================================
--- libgossip/gossip-contact.c	(revision 2310)
+++ libgossip/gossip-contact.c	(working copy)
@@ -29,6 +29,7 @@
 
 #include "gossip-contact.h"
 #include "gossip-utils.h"
+#include "libgossip-marshal.h"
 
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT, GossipContactPriv))
 
@@ -38,17 +39,15 @@
 typedef struct _GossipContactPriv GossipContactPriv;
 
 struct _GossipContactPriv {
-	GossipContactType   type;
-
-	gchar              *id;
-	gchar              *name;
-
-	GList              *presences;
-	GList              *groups;
-
-	GossipSubscription  subscription;
-	GossipAvatar       *avatar;
-	GossipAccount      *account;
+	GossipContactType          type;
+	gchar                     *id;
+	gchar                     *name;
+	GList                     *presences;
+	GList                     *groups;
+	GossipSubscription         subscription;
+	GossipAvatar              *avatar;
+	GossipAccount             *account;
+	GossipContactCapabilities  capabilities;
 };
 
 static void contact_class_init    (GossipContactClass *class);
@@ -76,7 +75,8 @@ enum {
 	PROP_GROUPS,
 	PROP_SUBSCRIPTION,
 	PROP_AVATAR,
-	PROP_ACCOUNT
+	PROP_ACCOUNT,
+	PROP_CAPABILITIES,
 };
 
 static gpointer parent_class = NULL;
@@ -110,7 +110,13 @@ gossip_contact_get_gtype (void)
 static void
 contact_class_init (GossipContactClass *class)
 {
-	GObjectClass *object_class;
+	GObjectClass      *object_class;
+	GType              capabilities_type;
+	static GFlagsValue capabilities_flags[] = {
+		{GOSSIP_CONTACT_CAPABILITY_AUDIO, "GOSSIP_CONTACT_CAPABILITY_AUDIO", NULL},
+		{GOSSIP_CONTACT_CAPABILITY_VIDEO, "GOSSIP_CONTACT_CAPABILITY_VIDEO", NULL},
+		{0, NULL, NULL}
+	};
 
 	object_class = G_OBJECT_CLASS (class);
 	parent_class = g_type_class_peek_parent (class);
@@ -119,6 +125,9 @@ contact_class_init (GossipContactClass *
 	object_class->get_property = contact_get_property;
 	object_class->set_property = contact_set_property;
 
+	capabilities_type = g_flags_register_static ("GossipContactCapabilities",
+						     capabilities_flags);
+
 	g_object_class_install_property (object_class,
 					 PROP_TYPE,
 					 g_param_spec_int ("type",
@@ -185,6 +194,15 @@ contact_class_init (GossipContactClass *
 							      GOSSIP_TYPE_ACCOUNT,
 							      G_PARAM_READWRITE));
 
+	g_object_class_install_property (object_class,
+					PROP_CAPABILITIES,
+					g_param_spec_flags ("capabilities",
+							    "Contact Capabilities",
+							    "The capabilities (audio/video/...) of the contact",
+							    capabilities_type,
+							    0,
+							    G_PARAM_READWRITE));
+
 	g_type_class_add_private (object_class, sizeof (GossipContactPriv));
 }
 
@@ -195,13 +213,14 @@ contact_init (GossipContact *contact)
 
 	priv = GET_PRIV (contact);
 
-	priv->type        = GOSSIP_CONTACT_TYPE_TEMPORARY;
-	priv->name        = NULL;
-	priv->id          = NULL;
-	priv->presences   = NULL;
-	priv->account     = NULL;
-	priv->groups      = NULL;
-	priv->avatar      = NULL;
+	priv->type         = GOSSIP_CONTACT_TYPE_TEMPORARY;
+	priv->name         = NULL;
+	priv->id           = NULL;
+	priv->presences    = NULL;
+	priv->account      = NULL;
+	priv->groups       = NULL;
+	priv->avatar       = NULL;
+	priv->capabilities = 0;
 }
 
 static void
@@ -272,6 +291,9 @@ contact_get_property (GObject    *object
 	case PROP_ACCOUNT:
 		g_value_set_object (value, priv->account);
 		break;
+	case PROP_CAPABILITIES:
+		g_value_set_flags (value, priv->capabilities);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -321,6 +343,10 @@ contact_set_property (GObject      *obje
 		gossip_contact_set_account (GOSSIP_CONTACT (object),
 					    GOSSIP_ACCOUNT (g_value_get_object (value)));
 		break;
+	case PROP_CAPABILITIES:
+		gossip_contact_set_capabilities (GOSSIP_CONTACT (object),
+						 g_value_get_flags (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -529,6 +555,34 @@ gossip_contact_get_subscription (GossipC
 	return priv->subscription;
 }
 
+GossipContactCapabilities
+gossip_contact_get_capabilities (GossipContact *contact)
+{
+	GossipContactPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), 0);
+
+	priv = GET_PRIV (contact);
+
+	return priv->capabilities;
+}
+
+gboolean
+gossip_contact_can_voip (GossipContact *contact)
+{
+	GossipContactPriv *priv;
+	gboolean           can_voip;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), 0);
+
+	priv = GET_PRIV (contact);
+
+	can_voip = priv->capabilities & (GOSSIP_CONTACT_CAPABILITY_AUDIO |
+					 GOSSIP_CONTACT_CAPABILITY_VIDEO);
+
+	return can_voip;
+}
+
 static void
 contact_set_type (GossipContact      *contact,
 		  GossipContactType   type)
@@ -775,6 +829,21 @@ gossip_contact_set_subscription (GossipC
 	g_object_notify (G_OBJECT (contact), "subscription");
 }
 
+void
+gossip_contact_set_capabilities (GossipContact             *contact,
+				 GossipContactCapabilities  capabilities)
+{
+	GossipContactPriv *priv;
+
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	priv = GET_PRIV (contact);
+
+	priv->capabilities = capabilities;
+
+	g_object_notify (G_OBJECT (contact), "capabilities");
+}
+
 gint
 gossip_contact_name_compare (gconstpointer a,
 			     gconstpointer b)
Index: ui/chat.glade
===================================================================
--- ui/chat.glade	(revision 2310)
+++ ui/chat.glade	(working copy)
@@ -116,7 +116,7 @@
 		      <property name="use_underline">True</property>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image235">
+			<widget class="GtkImage" id="image200">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-clear</property>
 			  <property name="icon_size">1</property>
@@ -138,7 +138,34 @@
 		  </child>
 
 		  <child>
-		    <widget class="GtkSeparatorMenuItem" id="separator11">
+		    <widget class="GtkSeparatorMenuItem" id="menu_conv_call_separator">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="menu_conv_call">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Start a voice or video conversation with this contact</property>
+		      <property name="label" translatable="yes">Call...</property>
+		      <property name="use_underline">True</property>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image201">
+			  <property name="visible">True</property>
+			  <property name="stock">gnome-stock-mic</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkSeparatorMenuItem" id="separator12">
 		      <property name="visible">True</property>
 		    </widget>
 		  </child>
@@ -151,7 +178,7 @@
 		      <accelerator key="F3" modifiers="0" signal="activate"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image236">
+			<widget class="GtkImage" id="image202">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-justify-left</property>
 			  <property name="icon_size">1</property>
@@ -176,7 +203,7 @@
 		      <property name="use_underline">True</property>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image237">
+			<widget class="GtkImage" id="image203">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-add</property>
 			  <property name="icon_size">1</property>
@@ -196,7 +223,7 @@
 		      <property name="use_underline">True</property>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image238">
+			<widget class="GtkImage" id="image204">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-info</property>
 			  <property name="icon_size">1</property>
@@ -210,7 +237,7 @@
 		  </child>
 
 		  <child>
-		    <widget class="GtkSeparatorMenuItem" id="separator7">
+		    <widget class="GtkSeparatorMenuItem" id="separator11">
 		      <property name="visible">True</property>
 		    </widget>
 		  </child>
@@ -223,7 +250,7 @@
 		      <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image239">
+			<widget class="GtkImage" id="image205">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-close</property>
 			  <property name="icon_size">1</property>
@@ -697,4 +724,429 @@
   </child>
 </widget>
 
+<widget class="GtkWindow" id="call_window">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Conversation with Foobar</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">True</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkVBox" id="vbox10">
+      <property name="border_width">4</property>
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">3</property>
+
+      <child>
+	<widget class="GtkHBox" id="hbox1">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkToggleButton" id="call_toggle_button">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">False</property>
+	      <property name="inconsistent">False</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment1">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">0</property>
+		  <property name="yscale">0</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">0</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkHBox" id="hbox2">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">2</property>
+
+		      <child>
+			<widget class="GtkImage" id="image182">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-connect</property>
+			  <property name="icon_size">4</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label7">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">_Call</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="call_status_label">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Not Talking
+&lt;span size='smaller' &gt;Press Talk to connect&lt;/span&gt;</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkTable" id="monitor_container">
+	  <property name="visible">True</property>
+	  <property name="n_rows">2</property>
+	  <property name="n_columns">2</property>
+	  <property name="homogeneous">False</property>
+	  <property name="row_spacing">3</property>
+	  <property name="column_spacing">3</property>
+
+	  <child>
+	    <widget class="GtkHBox" id="audio_output_container">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">3</property>
+
+	      <child>
+		<widget class="GtkImage" id="audio_input_monitor_image">
+		  <property name="visible">True</property>
+		  <property name="sensitive">False</property>
+		  <property name="stock">gnome-stock-volume</property>
+		  <property name="icon_size">4</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkProgressBar" id="audio_input_monitor">
+		  <property name="visible">True</property>
+		  <property name="sensitive">False</property>
+		  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+		  <property name="fraction">0</property>
+		  <property name="pulse_step">0.10000000149</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="left_attach">0</property>
+	      <property name="right_attach">1</property>
+	      <property name="top_attach">1</property>
+	      <property name="bottom_attach">2</property>
+	      <property name="y_options">fill</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkHBox" id="audio_input_container">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">3</property>
+
+	      <child>
+		<widget class="GtkImage" id="audio_output_monitor_image">
+		  <property name="visible">True</property>
+		  <property name="sensitive">False</property>
+		  <property name="stock">gnome-stock-mic</property>
+		  <property name="icon_size">4</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkProgressBar" id="audio_output_monitor">
+		  <property name="visible">True</property>
+		  <property name="sensitive">False</property>
+		  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+		  <property name="fraction">0</property>
+		  <property name="pulse_step">0.10000000149</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="left_attach">1</property>
+	      <property name="right_attach">2</property>
+	      <property name="top_attach">1</property>
+	      <property name="bottom_attach">2</property>
+	      <property name="y_options">fill</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkExpander" id="expander1">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="expanded">False</property>
+	  <property name="spacing">3</property>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment2">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">12</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkTable" id="table1">
+		  <property name="visible">True</property>
+		  <property name="n_rows">2</property>
+		  <property name="n_columns">2</property>
+		  <property name="homogeneous">False</property>
+		  <property name="row_spacing">3</property>
+		  <property name="column_spacing">3</property>
+
+		  <child>
+		    <widget class="GtkToggleButton" id="in_mute_toggle_button">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">True</property>
+		      <property name="inconsistent">False</property>
+
+		      <child>
+			<widget class="GtkImage" id="image180">
+			  <property name="visible">True</property>
+			  <property name="stock">gnome-stock-mic</property>
+			  <property name="icon_size">4</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">0</property>
+		      <property name="bottom_attach">1</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkToggleButton" id="out_mute_toggle_button">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">True</property>
+		      <property name="inconsistent">False</property>
+
+		      <child>
+			<widget class="GtkImage" id="image181">
+			  <property name="visible">True</property>
+			  <property name="stock">gnome-stock-volume</property>
+			  <property name="icon_size">4</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">0</property>
+		      <property name="right_attach">1</property>
+		      <property name="top_attach">1</property>
+		      <property name="bottom_attach">2</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkHScale" id="in_volume_scale">
+		      <property name="visible">True</property>
+		      <property name="sensitive">False</property>
+		      <property name="can_focus">True</property>
+		      <property name="draw_value">False</property>
+		      <property name="value_pos">GTK_POS_TOP</property>
+		      <property name="digits">1</property>
+		      <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+		      <property name="inverted">False</property>
+		      <property name="adjustment">100 0 100 0 0 0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">0</property>
+		      <property name="bottom_attach">1</property>
+		      <property name="y_options">fill</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkHScale" id="out_volume_scale">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="draw_value">False</property>
+		      <property name="value_pos">GTK_POS_TOP</property>
+		      <property name="digits">1</property>
+		      <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+		      <property name="inverted">False</property>
+		      <property name="adjustment">100 0 100 0 0 0</property>
+		    </widget>
+		    <packing>
+		      <property name="left_attach">1</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">1</property>
+		      <property name="bottom_attach">2</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options">fill</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="label8">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Volume Settings</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">True</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="type">label_item</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
 </glade-interface>
