diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 1f14239..f50df56 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -27,6 +27,7 @@ noinst_LTLIBRARIES = libempathy.la
 
 libempathy_headers =				\
 	empathy-account-settings.h		\
+	empathy-approver.h		\
 	empathy-call-factory.h			\
 	empathy-call-handler.h			\
 	empathy-chatroom-manager.h		\
@@ -72,6 +73,7 @@ endif
 libempathy_la_SOURCES =					\
 	$(libempathy_headers)				\
 	empathy-account-settings.c			\
+	empathy-approver.c		\
 	empathy-call-factory.c				\
 	empathy-call-handler.c				\
 	empathy-chatroom-manager.c			\
diff --git a/libempathy/empathy-approver.c b/libempathy/empathy-approver.c
new file mode 100644
index 0000000..f46f367
--- /dev/null
+++ b/libempathy/empathy-approver.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
+ */
+
+#include <config.h>
+
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/proxy-subclass.h>
+#include <telepathy-glib/gtypes.h>
+#include <telepathy-glib/defs.h>
+#include <telepathy-glib/svc-client.h>
+#include <telepathy-glib/svc-generic.h>
+#include <telepathy-glib/interfaces.h>
+
+#include "empathy-approver.h"
+#include "empathy-utils.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
+#include <libempathy/empathy-debug.h>
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyApprover)
+
+typedef struct
+{
+  EmpathyApproverApproveChannelsFunc *approve_channels;
+  gpointer approve_channels_user_data;
+
+  gchar *name;
+  gchar *busname;
+
+  GPtrArray *filters;
+
+  gboolean dispose_run;
+} EmpathyApproverPriv;
+
+static void empathy_approver_client_approver_iface_init (gpointer g_iface,
+    gpointer g_iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (EmpathyApprover,
+    empathy_approver,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+      tp_dbus_properties_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT, NULL);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT_APPROVER,
+      empathy_approver_client_approver_iface_init);
+  );
+
+static const gchar *empathy_approver_interfaces[] = {
+  TP_IFACE_CLIENT_APPROVER,
+  NULL
+};
+
+enum
+{
+  PROP_INTERFACES = 1,
+  PROP_APPROVER_CHANNEL_FILTER,
+  PROP_NAME,
+};
+
+static GObject *
+approver_constructor (GType type,
+    guint n_construct_params,
+    GObjectConstructParam *construct_params)
+{
+  GObject *obj =
+    G_OBJECT_CLASS (empathy_approver_parent_class)->constructor
+      (type, n_construct_params, construct_params);
+  EmpathyApprover *approver = EMPATHY_APPROVER (obj);
+  EmpathyApproverPriv *priv = GET_PRIV (approver);
+  TpDBusDaemon *dbus;
+  gchar *object_path;
+
+  priv = GET_PRIV (approver);
+
+  priv->busname = g_strdup_printf (TP_CLIENT_BUS_NAME_BASE"%s", priv->name);
+  object_path = g_strdup_printf (TP_CLIENT_OBJECT_PATH_BASE"%s",
+    priv->name);
+
+  dbus = tp_dbus_daemon_dup (NULL);
+
+  g_assert (tp_dbus_daemon_request_name (dbus,
+    priv->busname, TRUE, NULL));
+  dbus_g_connection_register_g_object (tp_proxy_get_dbus_connection (dbus),
+    object_path, obj);
+
+  DEBUG ("Registered at '%s'", object_path);
+
+  g_free (object_path);
+  g_object_unref (dbus);
+
+  return G_OBJECT (approver);
+}
+
+static void
+approver_dispose (GObject *object)
+{
+  EmpathyApproverPriv *priv = GET_PRIV (object);
+  TpDBusDaemon *dbus;
+
+  if (priv->dispose_run)
+    return;
+
+  priv->dispose_run = TRUE;
+
+  dbus = tp_dbus_daemon_dup (NULL);
+
+  tp_dbus_daemon_release_name (dbus, priv->busname, NULL);
+
+  g_object_unref (dbus);
+
+  if (G_OBJECT_CLASS (empathy_approver_parent_class)->dispose != NULL)
+    G_OBJECT_CLASS (empathy_approver_parent_class)->dispose (object);
+}
+
+static void
+approver_finalize (GObject *object)
+{
+  EmpathyApproverPriv *priv = GET_PRIV (object);
+
+  if (priv->filters != NULL)
+    g_boxed_free (TP_ARRAY_TYPE_CHANNEL_CLASS_LIST, priv->filters);
+
+  g_free (priv->name);
+  g_free (priv->busname);
+
+  if (G_OBJECT_CLASS (empathy_approver_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (empathy_approver_parent_class)->finalize (object);
+}
+
+static void
+approver_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyApprover *approver = EMPATHY_APPROVER (object);
+  EmpathyApproverPriv *priv = GET_PRIV (approver);
+
+  switch (property_id)
+    {
+      case PROP_APPROVER_CHANNEL_FILTER:
+        priv->filters = g_value_dup_boxed (value);
+        if (priv->filters == NULL)
+          priv->filters = g_ptr_array_new ();
+        break;
+      case PROP_NAME:
+        priv->name = g_value_dup_string (value);
+        if (EMP_STR_EMPTY (priv->name))
+          {
+            TpDBusDaemon *bus;
+
+            bus = tp_dbus_daemon_dup (NULL);
+            priv->name = g_strdup_printf ("badger_%s_%p",
+                tp_dbus_daemon_get_unique_name (bus), object);
+            g_strdelimit (priv->name, ":.", '_');
+            g_object_unref (bus);
+          }
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+approver_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyApprover *self = EMPATHY_APPROVER (object);
+  EmpathyApproverPriv *priv = GET_PRIV (self);
+
+  switch (property_id)
+    {
+      case PROP_INTERFACES:
+        g_value_set_boxed (value, empathy_approver_interfaces);
+        break;
+      case PROP_APPROVER_CHANNEL_FILTER:
+        g_value_set_boxed (value, priv->filters);
+        break;
+      case PROP_NAME:
+        g_value_set_string (value, priv->name);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+empathy_approver_class_init (EmpathyApproverClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *param_spec;
+
+  static TpDBusPropertiesMixinPropImpl client_props[] = {
+    { "Interfaces", "interfaces", NULL },
+    { NULL }
+  };
+  static TpDBusPropertiesMixinPropImpl client_approver_props[] = {
+    { "ApproverChannelFilter", "approver-channel-filter", NULL },
+    { NULL }
+  };
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+    { TP_IFACE_CLIENT,
+      tp_dbus_properties_mixin_getter_gobject_properties,
+      NULL,
+      client_props
+    },
+    { TP_IFACE_CLIENT_APPROVER,
+      tp_dbus_properties_mixin_getter_gobject_properties,
+      NULL,
+      client_approver_props
+    },
+    { NULL }
+  };
+
+  object_class->finalize = approver_finalize;
+  object_class->dispose = approver_dispose;
+  object_class->constructor = approver_constructor;
+
+  object_class->get_property = approver_get_property;
+  object_class->set_property = approver_set_property;
+
+  param_spec = g_param_spec_boxed ("interfaces", "interfaces",
+    "Available D-Bus interfaces",
+    G_TYPE_STRV,
+    G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+  param_spec = g_param_spec_boxed ("approver-channel-filter",
+    "approver-channel-filter",
+    "Filter for channels this approver",
+    TP_ARRAY_TYPE_CHANNEL_CLASS_LIST,
+    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+  g_object_class_install_property (object_class,
+    PROP_APPROVER_CHANNEL_FILTER, param_spec);
+
+  param_spec = g_param_spec_string ("name", "name",
+    "The local name of the approver",
+    NULL,
+    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+  g_object_class_install_property (object_class,
+    PROP_NAME, param_spec);
+
+  g_type_class_add_private (object_class, sizeof (EmpathyApproverPriv));
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+    G_STRUCT_OFFSET (EmpathyApproverClass, dbus_props_class));
+}
+
+static void
+empathy_approver_init (EmpathyApprover *approver)
+{
+  EmpathyApproverPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (approver,
+    EMPATHY_TYPE_APPROVER, EmpathyApproverPriv);
+
+  approver->priv = priv;
+}
+
+EmpathyApprover *
+empathy_approver_new (const gchar *name,
+    GPtrArray *filters)
+{
+  return EMPATHY_APPROVER (
+    g_object_new (EMPATHY_TYPE_APPROVER,
+      "name", name,
+      "approver-channel-filter", filters,
+      NULL));
+}
+
+static void
+empathy_approver_add_dispatch_operation (TpSvcClientApprover *approver,
+    const GPtrArray *channels,
+    const gchar *path,
+    GHashTable *properties,
+    DBusGMethodInvocation *context)
+{
+  EmpathyApprover *self = EMPATHY_APPROVER (approver);
+  EmpathyApproverPriv *priv = GET_PRIV (self);
+  TpDBusDaemon *bus;
+  TpChannelDispatchOperation *operation;
+  gboolean result;
+  GError *error = NULL;
+
+  if (priv->approve_channels == NULL)
+    {
+      error = g_error_new_literal (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+        "No approve function setup");
+      goto error;
+    }
+
+  bus = tp_dbus_daemon_dup (&error);
+  if (bus == NULL)
+    goto error;
+
+  operation = tp_channel_dispatch_operation_new (bus, path, properties, &error);
+  g_object_unref (bus);
+
+  if (operation == NULL)
+    goto error;
+
+  result = priv->approve_channels (self, channels, operation, properties,
+        priv->approve_channels_user_data, &error);
+  g_object_unref (operation);
+
+  if (!result)
+    goto error;
+
+  tp_svc_client_approver_return_from_add_dispatch_operation (context);
+  return;
+
+error:
+  dbus_g_method_return_error (context, error);
+  g_error_free (error);
+}
+
+static void
+empathy_approver_client_approver_iface_init (gpointer g_iface,
+    gpointer g_iface_data)
+{
+  TpSvcClientApproverClass *klass = (TpSvcClientApproverClass *) g_iface;
+
+  tp_svc_client_approver_implement_add_dispatch_operation (klass,
+    empathy_approver_add_dispatch_operation);
+}
+
+void
+empathy_approver_set_approve_channels_func (EmpathyApprover *approver,
+    EmpathyApproverApproveChannelsFunc *func,
+    gpointer user_data)
+{
+  EmpathyApproverPriv *priv = GET_PRIV (approver);
+
+  priv->approve_channels = func;
+  priv->approve_channels_user_data = user_data;
+}
diff --git a/libempathy/empathy-approver.h b/libempathy/empathy-approver.h
new file mode 100644
index 0000000..7845c90
--- /dev/null
+++ b/libempathy/empathy-approver.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
+ */
+
+#ifndef __EMPATHY_APPROVER_H__
+#define __EMPATHY_APPROVER_H__
+
+#include <glib.h>
+
+#include <telepathy-glib/channel.h>
+#include <telepathy-glib/dbus-properties-mixin.h>
+#include <telepathy-glib/channel-dispatch-operation.h>
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_APPROVER         (empathy_approver_get_type ())
+#define EMPATHY_APPROVER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), \
+  EMPATHY_TYPE_APPROVER, EmpathyApprover))
+#define EMPATHY_APPROVER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), \
+  EMPATHY_TYPE_APPROVER, EmpathyApproverClass))
+#define EMPATHY_IS_APPROVER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), \
+  EMPATHY_TYPE_APPROVER))
+#define EMPATHY_IS_APPROVER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), \
+  EMPATHY_TYPE_APPROVER))
+#define EMPATHY_APPROVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), \
+  EMPATHY_TYPE_APPROVER, EmpathyApproverClass))
+
+GType empathy_approver_get_type (void) G_GNUC_CONST;
+
+typedef struct _EmpathyApprover      EmpathyApprover;
+typedef struct _EmpathyApproverClass EmpathyApproverClass;
+
+struct _EmpathyApprover
+{
+  GObject parent;
+  gpointer priv;
+};
+
+struct _EmpathyApproverClass
+{
+  GObjectClass parent_class;
+  TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+
+EmpathyApprover * empathy_approver_new (const gchar *name,
+    GPtrArray *filters);
+
+typedef gboolean (EmpathyApproverApproveChannelsFunc) (
+    EmpathyApprover *approver,
+    const GPtrArray *channels,
+    TpChannelDispatchOperation *channel_dispatch_operation,
+    GHashTable *properties,
+    gpointer user_data,
+    GError **error);
+
+void empathy_approver_set_approve_channels_func (EmpathyApprover *approver,
+    EmpathyApproverApproveChannelsFunc *func,
+    gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_APPROVER_H__ */
diff --git a/libempathy/empathy-dispatch-operation.c b/libempathy/empathy-dispatch-operation.c
index 20ebadb..253825a 100644
--- a/libempathy/empathy-dispatch-operation.c
+++ b/libempathy/empathy-dispatch-operation.c
@@ -41,8 +41,6 @@
 G_DEFINE_TYPE(EmpathyDispatchOperation, empathy_dispatch_operation,
   G_TYPE_OBJECT)
 
-static void empathy_dispatch_operation_set_status (
-  EmpathyDispatchOperation *self, EmpathyDispatchOperationState status);
 static void empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
   const GError *error, gpointer user_data);
 
@@ -397,7 +395,7 @@ empathy_dispatch_operation_finalize (GObject *object)
   G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->finalize (object);
 }
 
-static void
+void
 empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
   EmpathyDispatchOperationState status)
 {
@@ -529,11 +527,7 @@ empathy_dispatch_operation_start (EmpathyDispatchOperation *operation)
   g_return_if_fail (
     priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
 
-  if (priv->incoming && !priv->approved)
-    empathy_dispatch_operation_set_status (operation,
-      EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
-  else
-    empathy_dispatch_operation_set_status (operation,
+  empathy_dispatch_operation_set_status (operation,
       EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
 }
 
diff --git a/libempathy/empathy-dispatch-operation.h b/libempathy/empathy-dispatch-operation.h
index 148adec..c2a038f 100644
--- a/libempathy/empathy-dispatch-operation.h
+++ b/libempathy/empathy-dispatch-operation.h
@@ -115,6 +115,9 @@ EmpathyDispatchOperationState empathy_dispatch_operation_get_status (
 gboolean empathy_dispatch_operation_is_incoming (
   EmpathyDispatchOperation *operation);
 
+void empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
+  EmpathyDispatchOperationState status);
+
 G_END_DECLS
 
 #endif /* #ifndef __EMPATHY_DISPATCH_OPERATION_H__*/
diff --git a/src/empathy-event-manager.c b/src/empathy-event-manager.c
index c4ab4ad..3115b6f 100644
--- a/src/empathy-event-manager.c
+++ b/src/empathy-event-manager.c
@@ -28,7 +28,6 @@
 #include <telepathy-glib/util.h>
 #include <telepathy-glib/interfaces.h>
 
-#include <libempathy/empathy-dispatcher.h>
 #include <libempathy/empathy-idle.h>
 #include <libempathy/empathy-tp-contact-factory.h>
 #include <libempathy/empathy-contact-manager.h>
@@ -37,6 +36,7 @@
 #include <libempathy/empathy-tp-file.h>
 #include <libempathy/empathy-utils.h>
 #include <libempathy/empathy-call-factory.h>
+#include <libempathy/empathy-approver.h>
 
 #include <extensions/extensions.h>
 
@@ -61,9 +61,6 @@
 typedef struct {
   EmpathyEventManager *manager;
   EmpathyDispatchOperation *operation;
-  gulong approved_handler;
-  gulong claimed_handler;
-  gulong invalidated_handler;
   /* Remove contact if applicable */
   EmpathyContact *contact;
   /* option signal handler and it's instance */
@@ -71,14 +68,27 @@ typedef struct {
   GObject *handler_instance;
   /* optional accept widget */
   GtkWidget *dialog;
+  gulong ready_id;
 } EventManagerApproval;
 
+/* Wrapper around a MC ChannelDispatchOperation containing one
+ * EventManagerApproval per channel. */
+typedef struct {
+  EmpathyEventManager *self;
+  TpChannelDispatchOperation *cd_operation;
+  /* List of EventManagerApproval */
+  GSList *approvals;
+
+  TpProxySignalConnection *channel_lost_sig;
+  gulong invalidated_sig;
+} EventManagerCDOperation;
+
 typedef struct {
-  EmpathyDispatcher *dispatcher;
   EmpathyContactManager *contact_manager;
+  EmpathyApprover *approver;
   GSList *events;
-  /* Ongoing approvals */
-  GSList *approvals;
+  /* Ongoing operations; list of EventManagerCDOperation */
+  GSList *ongoing_operations;
 
   gint ringing;
 } EmpathyEventManagerPriv;
@@ -123,18 +133,16 @@ event_manager_approval_new (EmpathyEventManager *manager,
 static void
 event_manager_approval_free (EventManagerApproval *approval)
 {
-  g_signal_handler_disconnect (approval->operation,
-    approval->approved_handler);
-  g_signal_handler_disconnect (approval->operation,
-    approval->claimed_handler);
-  g_signal_handler_disconnect (approval->operation,
-    approval->invalidated_handler);
   g_object_unref (approval->operation);
 
   if (approval->handler != 0)
     g_signal_handler_disconnect (approval->handler_instance,
       approval->handler);
 
+  if (approval->ready_id != 0)
+    g_signal_handler_disconnect (approval->operation,
+      approval->ready_id);
+
   if (approval->contact != NULL)
     g_object_unref (approval->contact);
 
@@ -146,7 +154,39 @@ event_manager_approval_free (EventManagerApproval *approval)
   g_slice_free (EventManagerApproval, approval);
 }
 
-static void event_remove (EventPriv *event);
+static EventManagerCDOperation *
+event_manager_cd_operation_new (EmpathyEventManager *self,
+    TpChannelDispatchOperation *cd_operation)
+{
+  EventManagerCDOperation *result = g_slice_new0 (EventManagerCDOperation);
+  result->self = self;
+  result->cd_operation = g_object_ref (cd_operation);
+  result->approvals = NULL;
+
+  return result;
+}
+
+static void
+event_manager_cd_operation_free (EventManagerCDOperation *op)
+{
+  GSList *l;
+
+  if (tp_proxy_get_invalidated (op->cd_operation) == NULL)
+    {
+      /* It's not safe to disconnect signals on an invalidated proxy */
+      tp_proxy_signal_connection_disconnect (op->channel_lost_sig);
+    }
+
+  g_signal_handler_disconnect (op->cd_operation, op->invalidated_sig);
+
+  g_object_unref (op->cd_operation);
+
+  for (l = op->approvals; l != NULL; l = g_slist_next (l))
+    event_manager_approval_free (l->data);
+  g_slist_free (op->approvals);
+
+  g_slice_free (EventManagerCDOperation, op);
+}
 
 static void
 event_free (EventPriv *event)
@@ -415,61 +455,145 @@ event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
 }
 
 static void
-event_manager_approval_done (EventManagerApproval *approval)
+remove_event_for_approval (EmpathyEventManager *self,
+    EventManagerApproval *approval)
 {
-  EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
-  GSList                  *l;
+  EmpathyEventManagerPriv *priv = GET_PRIV (self);
+  GSList *l;
 
-  if (approval->operation != NULL)
+  for (l = priv->events; l; l = l->next)
     {
-      GQuark channel_type;
+      EventPriv *event = l->data;
 
-      channel_type = empathy_dispatch_operation_get_channel_type_id (
-          approval->operation);
-      if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
+      if (event->approval == approval)
         {
-          priv->ringing--;
-          if (priv->ringing == 0)
-            empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
+          event_remove (event);
+          return;
         }
     }
+}
 
-  priv->approvals = g_slist_remove (priv->approvals, approval);
+static void
+event_manager_approval_done (EmpathyEventManager *self,
+    EventManagerCDOperation *cd_op)
+{
+  EmpathyEventManagerPriv *priv = GET_PRIV (self);
+  GSList *it;
 
-  for (l = priv->events; l; l = l->next)
+  priv->ongoing_operations = g_slist_remove (priv->ongoing_operations, cd_op);
+
+  for (it = cd_op->approvals; it != NULL; it = g_slist_next (it))
     {
-      EventPriv *event = l->data;
+      EventManagerApproval *approval = it->data;
 
-      if (event->approval == approval)
+      if (approval->operation != NULL)
         {
-          event_remove (event);
-          break;
+          GQuark channel_type;
+
+          channel_type = empathy_dispatch_operation_get_channel_type_id (
+              approval->operation);
+          if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
+            {
+              priv->ringing--;
+              if (priv->ringing == 0)
+                empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
+            }
         }
+
+      remove_event_for_approval (self, approval);
     }
 
-  event_manager_approval_free (approval);
+  event_manager_cd_operation_free (cd_op);
+}
+
+static void
+handle_with_cb (TpChannelDispatchOperation *operation,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  if (error != NULL)
+    DEBUG ("HandleWith() failed: %s", error->message);
 }
 
 static void
 event_manager_operation_approved_cb (EmpathyDispatchOperation *operation,
-  EventManagerApproval *approval)
+  EventManagerCDOperation *cd_op)
+{
+  /* Approve the whole ChannelDispatchOperation when one of its
+   * EmpathyDispatchOperation has been approved */
+  DEBUG ("Operation has been approved");
+
+  tp_cli_channel_dispatch_operation_call_handle_with (cd_op->cd_operation, -1,
+      "", handle_with_cb, NULL, NULL, G_OBJECT (cd_op->self));
+
+  event_manager_approval_done (cd_op->self, cd_op);
+}
+
+static void
+claim_cb (TpChannelDispatchOperation *operation,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
 {
-  event_manager_approval_done (approval);
+  if (error != NULL)
+    DEBUG ("Claim() failed: %s", error->message);
 }
 
 static void
 event_manager_operation_claimed_cb (EmpathyDispatchOperation *operation,
-  EventManagerApproval *approval)
+  EventManagerCDOperation *cd_op)
 {
-  event_manager_approval_done (approval);
+  DEBUG ("Operation has been claimed");
+
+  tp_cli_channel_dispatch_operation_call_claim (cd_op->cd_operation, -1,
+      claim_cb, NULL, NULL, G_OBJECT (cd_op->self));
+
+  event_manager_approval_done (cd_op->self, cd_op);
 }
 
 static void
-event_manager_operation_invalidated_cb (EmpathyDispatchOperation *operation,
-  guint domain, gint code, gchar *message,
-  EventManagerApproval *approval)
+dispatch_operation_channel_lost_sig_cb (TpChannelDispatchOperation *proxy,
+    const gchar *channel,
+    const gchar *error,
+    const gchar *msg,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  EmpathyEventManager *self = EMPATHY_EVENT_MANAGER (weak_object);
+  EventManagerCDOperation *cd_op = user_data;
+  GSList *l;
+
+  DEBUG ("Lost channel %s", channel);
+  for (l = cd_op->approvals; l != NULL; l = g_slist_next (l))
+    {
+      EventManagerApproval *approval = l->data;
+      TpChannel *chan;
+      const gchar *path;
+
+      chan = empathy_dispatch_operation_get_channel (approval->operation);
+      path = tp_proxy_get_object_path (chan);
+
+      if (!tp_strdiff (path, channel))
+        {
+          /* Remove the EventManagerApproval associated with the channel */
+          cd_op->approvals = g_slist_remove (cd_op->approvals, approval);
+          remove_event_for_approval (self, approval);
+          event_manager_approval_free (approval);
+          break;
+        }
+    }
+}
+
+static void
+dispatch_operation_invalidated_cb (TpChannelDispatchOperation *proxy,
+  guint domain,
+  gint code,
+  gchar *message,
+  EventManagerCDOperation *cd_op)
 {
-  event_manager_approval_done (approval);
+  DEBUG ("ChannelDispatchOperation invalidated");
+  event_manager_approval_done (cd_op->self, cd_op);
 }
 
 static void
@@ -672,26 +796,16 @@ event_manager_ft_got_contact_cb (EmpathyTpContactFactory *factory,
 }
 
 static void
-event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
-  EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
+dispatch_operation_ready_cb (EmpathyDispatchOperation *operation,
+    EventManagerApproval *approval)
 {
+  EmpathyEventManager *manager = approval->manager;
   const gchar *channel_type;
-  EventManagerApproval *approval;
-  EmpathyEventManagerPriv *priv = GET_PRIV (manager);
-
-  channel_type = empathy_dispatch_operation_get_channel_type (operation);
 
-  approval = event_manager_approval_new (manager, operation);
-  priv->approvals = g_slist_prepend (priv->approvals, approval);
+  empathy_dispatch_operation_set_status (operation,
+      EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
 
-  approval->approved_handler = g_signal_connect (operation, "approved",
-    G_CALLBACK (event_manager_operation_approved_cb), approval);
-
-  approval->claimed_handler = g_signal_connect (operation, "claimed",
-     G_CALLBACK (event_manager_operation_claimed_cb), approval);
-
-  approval->invalidated_handler = g_signal_connect (operation, "invalidated",
-     G_CALLBACK (event_manager_operation_invalidated_cb), approval);
+  channel_type = empathy_dispatch_operation_get_channel_type (operation);
 
   if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
     {
@@ -780,6 +894,107 @@ event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
     }
 }
 
+static gboolean
+approve_channels (EmpathyApprover *approver,
+    const GPtrArray *channels,
+    TpChannelDispatchOperation *tp_cd_operation,
+    GHashTable *properties,
+    gpointer user_data,
+    GError **error)
+{
+  EmpathyEventManager *self = EMPATHY_EVENT_MANAGER (user_data);
+  EmpathyEventManagerPriv *priv = GET_PRIV (self);
+  guint i;
+  const gchar *conn_path;
+  TpDBusDaemon *bus;
+  TpConnection *conn;
+  EventManagerCDOperation *cd_operation;
+
+  conn_path = tp_asv_get_object_path (properties,
+      TP_IFACE_CHANNEL_DISPATCH_OPERATION ".Connection");
+  if (conn_path == NULL)
+    {
+      DEBUG ("Missing Connection property");
+
+      g_set_error_literal (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+          "Missing Connection property");
+      return FALSE;
+    }
+
+  bus = tp_dbus_daemon_dup (error);
+  if (bus == NULL)
+    return FALSE;
+
+  conn = tp_connection_new (bus, NULL, conn_path, error);
+  g_object_unref (bus);
+  if (conn == NULL)
+    return FALSE;
+
+  DEBUG ("New ChannelDispatchOperation");
+
+  cd_operation = event_manager_cd_operation_new (self, tp_cd_operation);
+
+  cd_operation->channel_lost_sig =
+    tp_cli_channel_dispatch_operation_connect_to_channel_lost (tp_cd_operation,
+        dispatch_operation_channel_lost_sig_cb, cd_operation, NULL,
+        G_OBJECT (self), NULL);
+
+  /* No need to connect the 'Finished' D-Bus signal, 'invalidated' is enough
+   * as the spec garantees that the ChannelDispatchOperation is removed from
+   * the bus. */
+  cd_operation->invalidated_sig = g_signal_connect (tp_cd_operation,
+      "invalidated", G_CALLBACK (dispatch_operation_invalidated_cb),
+      cd_operation);
+
+  priv->ongoing_operations = g_slist_prepend (priv->ongoing_operations,
+      cd_operation);
+
+  for (i = 0; i < channels->len; i++)
+    {
+      GValueArray *arr = g_ptr_array_index (channels, i);
+      const gchar *chan_path;
+      EmpathyDispatchOperation *operation;
+      EventManagerApproval *approval;
+      GHashTable *props;
+      TpChannel *channel;
+
+      chan_path = g_value_get_boxed (g_value_array_get_nth (arr, 0));
+      props = g_value_get_boxed (g_value_array_get_nth (arr, 1));
+
+      channel = tp_channel_new_from_properties (conn, chan_path, props, error);
+      if (channel == NULL)
+        return FALSE;
+
+      /* TODO: at some point it would be good to get rid of
+       * EmpathyDispatchOperation */
+      operation = empathy_dispatch_operation_new (conn, channel, NULL, TRUE);
+
+      approval = event_manager_approval_new (self, operation);
+      cd_operation->approvals = g_slist_prepend (cd_operation->approvals,
+          approval);
+
+      g_signal_connect (operation, "approved",
+          G_CALLBACK (event_manager_operation_approved_cb), cd_operation);
+      g_signal_connect (operation, "claimed",
+          G_CALLBACK (event_manager_operation_claimed_cb), cd_operation);
+
+      switch (empathy_dispatch_operation_get_status (operation))
+        {
+          case EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING:
+            approval->ready_id = g_signal_connect (operation, "ready",
+              G_CALLBACK (dispatch_operation_ready_cb), approval);
+            break;
+          case EMPATHY_DISPATCHER_OPERATION_STATE_PENDING:
+            dispatch_operation_ready_cb (operation, approval);
+            break;
+          default:
+            g_assert_not_reached ();
+        }
+    }
+
+  return TRUE;
+}
+
 static void
 event_pending_subscribe_func (EventPriv *event)
 {
@@ -919,10 +1134,11 @@ event_manager_finalize (GObject *object)
 
   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
   g_slist_free (priv->events);
-  g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
-  g_slist_free (priv->approvals);
+  g_slist_foreach (priv->ongoing_operations,
+      (GFunc) event_manager_cd_operation_free, NULL);
+  g_slist_free (priv->ongoing_operations);
   g_object_unref (priv->contact_manager);
-  g_object_unref (priv->dispatcher);
+  g_object_unref (priv->approver);
 }
 
 static void
@@ -972,6 +1188,15 @@ empathy_event_manager_init (EmpathyEventManager *manager)
     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
   EmpathyContactMonitor *monitor;
   EmpathyContactList *list_iface;
+  GPtrArray *filters;
+  /* Channel types filtered by the approver */
+  const gchar *types[] = { TP_IFACE_CHANNEL_TYPE_TEXT,
+      TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
+      TP_IFACE_CHANNEL_TYPE_STREAM_TUBE,
+      TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
+      NULL };
+  guint i;
 
   list_iface = EMPATHY_CONTACT_LIST (empathy_contact_manager_dup_singleton ());
   monitor = empathy_contact_list_get_monitor (list_iface);
@@ -979,10 +1204,26 @@ empathy_event_manager_init (EmpathyEventManager *manager)
 
   manager->priv = priv;
 
-  priv->dispatcher = empathy_dispatcher_dup_singleton ();
   priv->contact_manager = empathy_contact_manager_dup_singleton ();
-  g_signal_connect (priv->dispatcher, "approve",
-    G_CALLBACK (event_manager_approve_channel_cb), manager);
+
+  /* Create the Approver */
+  filters = g_ptr_array_new_with_free_func (
+      (GDestroyNotify) g_hash_table_destroy);
+
+  for (i = 0; types[i] != NULL; i++)
+    {
+      g_ptr_array_add (filters, tp_asv_new (
+          TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING, types[i],
+          NULL));
+    }
+
+  priv->approver = empathy_approver_new ("EmpathyApprover", filters);
+
+  empathy_approver_set_approve_channels_func (priv->approver, approve_channels,
+      manager);
+
+  g_ptr_array_free (filters, TRUE);
+
   g_signal_connect (priv->contact_manager, "pendings-changed",
     G_CALLBACK (event_manager_pendings_changed_cb), manager);
   g_signal_connect (monitor, "contact-presence-changed",
diff --git a/tests/interactive/.gitignore b/tests/interactive/.gitignore
index f6f87d8..a4e3beb 100644
--- a/tests/interactive/.gitignore
+++ b/tests/interactive/.gitignore
@@ -7,3 +7,4 @@ test-empathy-account-assistant
 test-empathy-presence-chooser
 test-empathy-status-preset-dialog
 test-empathy-protocol-chooser
+test-empathy-approver
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 2f9696e..c2fee81 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -18,7 +18,8 @@ noinst_PROGRAMS =			\
 	test-empathy-account-assistant \
 	test-empathy-presence-chooser	\
 	test-empathy-status-preset-dialog \
-	test-empathy-protocol-chooser
+	test-empathy-protocol-chooser \
+	test-empathy-approver
 
 contact_manager_SOURCES = contact-manager.c
 empathy_logs_SOURCES = empathy-logs.c
@@ -27,6 +28,7 @@ test_empathy_presence_chooser_SOURCES = test-empathy-presence-chooser.c
 test_empathy_status_preset_dialog_SOURCES = test-empathy-status-preset-dialog.c
 test_empathy_protocol_chooser_SOURCES = test-empathy-protocol-chooser.c
 test_empathy_account_assistant_SOURCES = test-empathy-account-assistant.c
+test_empathy_approver_SOURCES = test-empathy-approver.c
 
 test_empathy_account_assistant_CFLAGS = -I$(top_srcdir)/src
 test_empathy_account_assistant_LDADD = 			\
diff --git a/tests/interactive/test-empathy-approver.c b/tests/interactive/test-empathy-approver.c
new file mode 100644
index 0000000..8b62092
--- /dev/null
+++ b/tests/interactive/test-empathy-approver.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
+ */
+
+#include <libempathy/empathy-approver.h>
+
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/dbus.h>
+
+static void
+handle_with_cb (TpChannelDispatchOperation *operation,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  GMainLoop *mainloop = user_data;
+
+  if (error != NULL)
+    g_print ("HandleWith() failed: %s\n", error->message);
+  else
+    g_print ("HandleWith() succeed\n");
+
+  g_main_loop_quit (mainloop);
+}
+
+static gboolean
+approve_channels (EmpathyApprover *approver,
+    const GPtrArray *channels,
+    TpChannelDispatchOperation *operation,
+    GHashTable *properties,
+    gpointer user_data,
+    GError **error)
+{
+  guint i;
+
+  g_print ("received %u channels:\n", channels->len);
+
+  for (i = 0; i < channels->len; i++)
+    {
+      GValueArray *arr = g_ptr_array_index (channels, i);
+      const gchar *object_path;
+
+      object_path = g_value_get_boxed (g_value_array_get_nth (arr, 0));
+
+      g_print ("\t%s\n", object_path);
+    }
+
+  tp_cli_channel_dispatch_operation_call_handle_with (operation, -1,
+      "", handle_with_cb, user_data, NULL, NULL);
+
+  return TRUE;
+}
+
+int
+main (int argc,
+    char **argv)
+{
+  GMainLoop *mainloop;
+  GPtrArray *filters;
+  GHashTable *asv;
+  EmpathyApprover *approver;
+
+  g_type_init ();
+
+  /* Create ApproverChannelFilter */
+  filters = g_ptr_array_new ();
+  asv = g_hash_table_new (g_str_hash, g_str_equal);
+  g_ptr_array_add (filters, asv);
+
+  approver = empathy_approver_new ("EmpathyTestApprover", filters);
+  mainloop = g_main_loop_new (NULL, FALSE);
+
+  empathy_approver_set_approve_channels_func (approver, approve_channels,
+      mainloop);
+
+  g_main_loop_run (mainloop);
+
+  g_main_loop_unref (mainloop);
+  g_ptr_array_foreach (filters, (GFunc) g_hash_table_destroy, NULL);
+  g_ptr_array_free (filters, TRUE);
+  g_object_unref (approver);
+
+  return 0;
+}
