summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2009-09-11 18:17:37 (GMT)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2009-09-11 18:17:40 (GMT)
commit5b9a37b84cd79f09f9a31d12f096d0f42943eb75 (patch)
treef12dc2ce4a4a0d3e364d1da4ee8773637ed7b1a6
parentd06e06461a782e5e6c17b7dbe8f0684a92cf39f9 (diff)
parentaadbac38cb0f7ccf19c1cd3e5b944b66d1b97b88 (diff)
downloadtelepathy-mission-control-5b9a37b84cd79f09f9a31d12f096d0f42943eb75.tar.gz
telepathy-mission-control-5b9a37b84cd79f09f9a31d12f096d0f42943eb75.tar.xz
Merge branch 'contact-caps-undraft'
Reviewed-by: Alberto Mardegan <alberto.mardegan@nokia.com> (partially) Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
-rw-r--r--src/Makefile.am2
-rw-r--r--src/mcd-connection-priv.h5
-rw-r--r--src/mcd-connection.c84
-rw-r--r--src/mcd-dispatcher-priv.h3
-rw-r--r--src/mcd-dispatcher.c307
-rw-r--r--src/mcd.xml1
-rw-r--r--test/twisted/Makefile.am1
-rw-r--r--test/twisted/capabilities/contact-caps.py168
-rw-r--r--test/twisted/capabilities/legacy-caps.py4
-rw-r--r--test/twisted/constants.py2
-rw-r--r--test/twisted/mctest.py12
-rw-r--r--test/twisted/telepathy/clients/AbiWord.client4
-rw-r--r--xml/Connection_Interface_Contact_Capabilities.xml145
-rw-r--r--xml/Connection_Interface_Contact_Capabilities_Draft1.xml166
-rw-r--r--xml/Makefile.am3
-rw-r--r--xml/all.xml2
-rw-r--r--xml/nmc5.xml1
17 files changed, 830 insertions, 80 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 39db972..4f2afa1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,7 @@ mc_headers = \
mcd-provisioning-factory.h
mc_gen_headers = \
+ _gen/cli-Connection_Interface_Contact_Capabilities_Draft1.h \
_gen/cli-Connection_Interface_Contact_Capabilities.h \
_gen/enums.h \
_gen/svc-Account_Interface_ChannelRequests.h \
@@ -41,6 +42,7 @@ mc_gen_headers = \
_gen/svc-Account_Manager_Interface_Query.h
nodist_libmcd_convenience_la_SOURCES = \
+ _gen/cli-Connection_Interface_Contact_Capabilities_Draft1-body.h \
_gen/cli-Connection_Interface_Contact_Capabilities-body.h \
_gen/register-dbus-glib-marshallers-body.h \
_gen/signals-marshal.c \
diff --git a/src/mcd-connection-priv.h b/src/mcd-connection-priv.h
index 0a89103..9894a65 100644
--- a/src/mcd-connection-priv.h
+++ b/src/mcd-connection-priv.h
@@ -44,7 +44,8 @@ void _mcd_connection_set_tp_connection (McdConnection *connection,
const gchar *bus_name,
const gchar *obj_path, GError **error);
-G_GNUC_INTERNAL void _mcd_connection_start_dispatching (McdConnection *self);
+G_GNUC_INTERNAL void _mcd_connection_start_dispatching (McdConnection *self,
+ GPtrArray *client_caps);
G_GNUC_INTERNAL gboolean _mcd_connection_is_ready (McdConnection *self);
@@ -54,6 +55,8 @@ G_GNUC_INTERNAL void _mcd_connection_set_nickname (McdConnection *self,
G_GNUC_INTERNAL void _mcd_connection_set_avatar (McdConnection *self,
const GArray *avatar,
const gchar *mime_type);
+G_GNUC_INTERNAL void _mcd_connection_update_client_caps (McdConnection *self,
+ GPtrArray *client_caps);
G_END_DECLS
diff --git a/src/mcd-connection.c b/src/mcd-connection.c
index 4e4f262..7adaaf9 100644
--- a/src/mcd-connection.c
+++ b/src/mcd-connection.c
@@ -70,7 +70,9 @@
#include "sp_timestamp.h"
#include "mcd-signals-marshal.h"
+#include "_gen/cli-Connection_Interface_Contact_Capabilities_Draft1.h"
#include "_gen/cli-Connection_Interface_Contact_Capabilities.h"
+#include "_gen/cli-Connection_Interface_Contact_Capabilities_Draft1-body.h"
#include "_gen/cli-Connection_Interface_Contact_Capabilities-body.h"
#define INITIAL_RECONNECTION_TIME 1 /* 1 second */
@@ -116,6 +118,7 @@ struct _McdConnectionPrivate
guint has_avatars_if : 1;
guint has_alias_if : 1;
guint has_capabilities_if : 1;
+ guint has_contact_capabilities_draft1_if : 1;
guint has_contact_capabilities_if : 1;
guint has_requests_if : 1;
@@ -582,6 +585,12 @@ _mcd_connection_setup_capabilities (McdConnection *connection)
GType type;
guint i;
+ if (priv->has_contact_capabilities_if)
+ {
+ DEBUG ("ContactCapabilities in use, avoiding Capabilities");
+ return;
+ }
+
if (!priv->has_capabilities_if)
{
DEBUG ("connection does not support capabilities interface");
@@ -612,7 +621,7 @@ _mcd_connection_setup_contact_capabilities (McdConnection *connection)
McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection);
GPtrArray *contact_capabilities;
- if (!priv->has_contact_capabilities_if)
+ if (!priv->has_contact_capabilities_draft1_if)
{
DEBUG ("connection does not support contact capabilities interface");
priv->got_contact_capabilities = TRUE;
@@ -623,7 +632,7 @@ _mcd_connection_setup_contact_capabilities (McdConnection *connection)
DEBUG ("advertising capabilities");
- mc_cli_connection_interface_contact_capabilities_call_set_self_capabilities
+ mc_cli_connection_interface_contact_capabilities_draft1_call_set_self_capabilities
(priv->tp_conn, -1, contact_capabilities, NULL, NULL, NULL, NULL);
DEBUG ("SetSelfCapabilities: Called.");
@@ -1392,6 +1401,8 @@ on_connection_ready (TpConnection *tp_conn, const GError *error,
TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING);
priv->has_capabilities_if = tp_proxy_has_interface_by_id (tp_conn,
TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES);
+ priv->has_contact_capabilities_draft1_if = tp_proxy_has_interface_by_id (tp_conn,
+ MC_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_CAPABILITIES_DRAFT1);
priv->has_contact_capabilities_if = tp_proxy_has_interface_by_id (tp_conn,
MC_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_CAPABILITIES);
priv->has_requests_if = tp_proxy_has_interface_by_id (tp_conn,
@@ -1403,7 +1414,7 @@ on_connection_ready (TpConnection *tp_conn, const GError *error,
if (priv->has_capabilities_if)
_mcd_connection_setup_capabilities (connection);
- if (priv->has_contact_capabilities_if)
+ if (priv->has_contact_capabilities_draft1_if)
_mcd_connection_setup_contact_capabilities (connection);
if (priv->has_avatars_if)
@@ -1418,7 +1429,8 @@ on_connection_ready (TpConnection *tp_conn, const GError *error,
}
void
-_mcd_connection_start_dispatching (McdConnection *self)
+_mcd_connection_start_dispatching (McdConnection *self,
+ GPtrArray *client_caps)
{
g_return_if_fail (MCD_IS_CONNECTION (self));
g_return_if_fail (!self->priv->dispatching_started);
@@ -1432,10 +1444,29 @@ _mcd_connection_start_dispatching (McdConnection *self)
else
mcd_connection_setup_pre_requests (self);
+ _mcd_connection_update_client_caps (self, client_caps);
+
/* and request all channels */
request_unrequested_channels (self);
}
+void
+_mcd_connection_update_client_caps (McdConnection *self,
+ GPtrArray *client_caps)
+{
+ g_return_if_fail (MCD_IS_CONNECTION (self));
+
+ if (!self->priv->has_contact_capabilities_if)
+ {
+ DEBUG ("ContactCapabilities unsupported");
+ return;
+ }
+
+ DEBUG ("Sending client caps to connection");
+ mc_cli_connection_interface_contact_capabilities_call_update_capabilities
+ (self->priv->tp_conn, -1, client_caps, NULL, NULL, NULL, NULL);
+}
+
static void
mcd_connection_done_task_before_connect (McdConnection *self)
{
@@ -1539,6 +1570,32 @@ mcd_connection_early_get_interfaces_cb (TpConnection *tp_conn,
mcd_connection_early_get_statuses_cb, NULL, NULL,
(GObject *) self);
}
+ else if (q == MC_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_CAPABILITIES)
+ {
+ GPtrArray *client_caps;
+
+ /* nail on the interface (TpConnection will eventually know
+ * how to do this for itself) */
+ tp_proxy_add_interface_by_id ((TpProxy *) tp_conn, q);
+ self->priv->has_contact_capabilities_if = TRUE;
+
+ /* we don't need to delay Connect for this, it can be
+ * fire-and-forget */
+
+ client_caps = _mcd_dispatcher_dup_client_caps (
+ self->priv->dispatcher);
+
+ if (client_caps != NULL)
+ {
+ _mcd_connection_update_client_caps (self, client_caps);
+ g_ptr_array_foreach (client_caps,
+ (GFunc) g_value_array_free, NULL);
+ g_ptr_array_free (client_caps, TRUE);
+ }
+ /* else the McdDispatcher hasn't sorted itself out yet, so
+ * we can't usefully pre-load capabilities - we'll be told
+ * the real capabilities as soon as it has worked them out */
+ }
}
}
@@ -1957,6 +2014,20 @@ _mcd_connection_request_channel (McdConnection *connection,
}
static void
+mcd_connection_add_signals (TpProxy *self,
+ guint quark,
+ DBusGProxy *proxy,
+ gpointer data)
+{
+ mc_cli_Connection_Interface_Contact_Capabilities_Draft1_add_signals (self,
+ quark,
+ proxy,
+ data);
+ mc_cli_Connection_Interface_Contact_Capabilities_add_signals (self, quark,
+ proxy, data);
+}
+
+static void
mcd_connection_class_init (McdConnectionClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1973,9 +2044,8 @@ mcd_connection_class_init (McdConnectionClass * klass)
_mc_ext_register_dbus_glib_marshallers ();
tp_connection_init_known_interfaces ();
- tp_proxy_or_subclass_hook_on_interface_add
- (TP_TYPE_CONNECTION,
- mc_cli_Connection_Interface_Contact_Capabilities_add_signals);
+ tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_CONNECTION,
+ mcd_connection_add_signals);
/* Properties */
g_object_class_install_property
diff --git a/src/mcd-dispatcher-priv.h b/src/mcd-dispatcher-priv.h
index 9b1fc7d..23f492f 100644
--- a/src/mcd-dispatcher-priv.h
+++ b/src/mcd-dispatcher-priv.h
@@ -58,6 +58,9 @@ void _mcd_dispatcher_recover_channel (McdDispatcher *dispatcher,
G_GNUC_INTERNAL void _mcd_dispatcher_add_connection (McdDispatcher *self,
McdConnection *connection);
+G_GNUC_INTERNAL GPtrArray *_mcd_dispatcher_dup_client_caps (
+ McdDispatcher *self);
+
G_END_DECLS
#endif /* MCD_DISPATCHER_H */
diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c
index fbb09d7..477b6b9 100644
--- a/src/mcd-dispatcher.c
+++ b/src/mcd-dispatcher.c
@@ -59,6 +59,8 @@
#include <telepathy-glib/defs.h>
#include <telepathy-glib/gtypes.h>
+#include <telepathy-glib/handle-repo.h>
+#include <telepathy-glib/handle-repo-dynamic.h>
#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/proxy-subclass.h>
#include <telepathy-glib/svc-channel-dispatcher.h>
@@ -196,6 +198,10 @@ typedef struct _McdClient
GList *approver_filters;
GList *handler_filters;
GList *observer_filters;
+
+ /* Handler.Capabilities, represented as handles taken from
+ * dispatcher->priv->string_pool */
+ TpHandleSet *capability_tokens;
} McdClient;
struct _McdDispatcherPrivate
@@ -242,8 +248,11 @@ struct _McdDispatcherPrivate
* property. */
gboolean operation_list_active;
+ /* Not really handles as such, but TpHandleRepoIface gives us a convenient
+ * reference-counted string pool */
+ TpHandleRepoIface *string_pool;
+
gboolean is_disposed;
-
};
struct cancel_call_data
@@ -338,20 +347,8 @@ mcd_dispatcher_context_handler_done (McdDispatcherContext *context)
}
static void
-mcd_client_free (McdClient *client)
+mcd_client_become_incapable (McdClient *client)
{
- if (client->proxy)
- {
- GError error = { TP_DBUS_ERRORS,
- TP_DBUS_ERROR_NAME_OWNER_LOST, "Client disappeared" };
-
- _mcd_object_ready (client->proxy, client_ready_quark, &error);
-
- g_object_unref (client->proxy);
- }
-
- g_free (client->name);
-
g_list_foreach (client->approver_filters,
(GFunc)g_hash_table_destroy, NULL);
g_list_free (client->approver_filters);
@@ -367,6 +364,30 @@ mcd_client_free (McdClient *client)
g_list_free (client->observer_filters);
client->observer_filters = NULL;
+ if (client->capability_tokens != NULL)
+ {
+ tp_handle_set_destroy (client->capability_tokens);
+ client->capability_tokens = NULL;
+ }
+}
+
+static void
+mcd_client_free (McdClient *client)
+{
+ if (client->proxy)
+ {
+ GError error = { TP_DBUS_ERRORS,
+ TP_DBUS_ERROR_NAME_OWNER_LOST, "Client disappeared" };
+
+ _mcd_object_ready (client->proxy, client_ready_quark, &error);
+
+ g_object_unref (client->proxy);
+ }
+
+ mcd_client_become_incapable (client);
+
+ g_free (client->name);
+
g_slice_free (McdClient, client);
}
@@ -1792,6 +1813,12 @@ _mcd_dispatcher_dispose (GObject * object)
priv->dbus_daemon = NULL;
}
+ if (priv->string_pool != NULL)
+ {
+ g_object_unref (priv->string_pool);
+ priv->string_pool = NULL;
+ }
+
G_OBJECT_CLASS (mcd_dispatcher_parent_class)->dispose (object);
}
@@ -2013,6 +2040,96 @@ mcd_client_set_filters (McdClient *client,
}
}
+typedef struct {
+ TpHandleRepoIface *repo;
+ GPtrArray *array;
+} TokenAppendContext;
+
+static void
+append_token_to_ptrs (TpHandleSet *unused G_GNUC_UNUSED,
+ TpHandle handle,
+ gpointer data)
+{
+ TokenAppendContext *context = data;
+
+ g_ptr_array_add (context->array,
+ g_strdup (tp_handle_inspect (context->repo, handle)));
+}
+
+static void
+mcd_dispatcher_append_client_caps (McdDispatcher *self,
+ McdClient *client,
+ GPtrArray *vas)
+{
+ GPtrArray *filters = g_ptr_array_sized_new (
+ g_list_length (client->handler_filters));
+ GPtrArray *cap_tokens;
+ GValueArray *va;
+ GList *list;
+
+ for (list = client->handler_filters; list != NULL; list = list->next)
+ {
+ GHashTable *copy = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) tp_g_value_slice_free);
+
+ tp_g_hash_table_update (copy, list->data,
+ (GBoxedCopyFunc) g_strdup,
+ (GBoxedCopyFunc) tp_g_value_slice_dup);
+ g_ptr_array_add (filters, copy);
+ }
+
+ if (client->capability_tokens == NULL)
+ {
+ cap_tokens = g_ptr_array_sized_new (1);
+ }
+ else
+ {
+ TokenAppendContext context = { self->priv->string_pool, NULL };
+
+ cap_tokens = g_ptr_array_sized_new (
+ tp_handle_set_size (client->capability_tokens) + 1);
+ context.array = cap_tokens;
+ tp_handle_set_foreach (client->capability_tokens, append_token_to_ptrs,
+ &context);
+ }
+
+ g_ptr_array_add (cap_tokens, NULL);
+
+ if (DEBUGGING)
+ {
+ guint i;
+
+ DEBUG ("%s%s:", TP_CLIENT_BUS_NAME_BASE, client->name);
+
+ DEBUG ("- %u channel filters", filters->len);
+ DEBUG ("- %u capability tokens:", cap_tokens->len - 1);
+
+ for (i = 0; i < cap_tokens->len - 1; i++)
+ {
+ DEBUG (" %s", (gchar *) g_ptr_array_index (cap_tokens, i));
+ }
+
+ DEBUG ("-end-");
+ }
+
+ va = g_value_array_new (3);
+ g_value_array_append (va, NULL);
+ g_value_array_append (va, NULL);
+ g_value_array_append (va, NULL);
+
+ g_value_init (va->values + 0, G_TYPE_STRING);
+ g_value_init (va->values + 1, TP_ARRAY_TYPE_CHANNEL_CLASS_LIST);
+ g_value_init (va->values + 2, G_TYPE_STRV);
+
+ g_value_take_string (va->values + 0,
+ g_strconcat (TP_CLIENT_BUS_NAME_BASE, client->name,
+ NULL));
+ g_value_take_boxed (va->values + 1, filters);
+ g_value_take_boxed (va->values + 2, g_ptr_array_free (cap_tokens, FALSE));
+
+ g_ptr_array_add (vas, va);
+}
+
static void
mcd_dispatcher_release_startup_lock (McdDispatcher *self)
{
@@ -2029,17 +2146,23 @@ mcd_dispatcher_release_startup_lock (McdDispatcher *self)
if (self->priv->startup_lock == 0)
{
GHashTableIter iter;
- gpointer k;
+ gpointer p;
+ GPtrArray *vas;
DEBUG ("All initial clients have been inspected");
self->priv->startup_completed = TRUE;
+ vas = _mcd_dispatcher_dup_client_caps (self);
+
g_hash_table_iter_init (&iter, self->priv->connections);
- while (g_hash_table_iter_next (&iter, &k, NULL))
+ while (g_hash_table_iter_next (&iter, &p, NULL))
{
- _mcd_connection_start_dispatching (k);
+ _mcd_connection_start_dispatching (p, vas);
}
+
+ g_ptr_array_foreach (vas, (GFunc) g_value_array_free, NULL);
+ g_ptr_array_free (vas, TRUE);
}
}
@@ -2084,6 +2207,61 @@ finally:
mcd_dispatcher_release_startup_lock (self);
}
+/* This is NULL-safe for the last argument, for ease of use with
+ * tp_asv_get_boxed */
+static void
+mcd_client_add_cap_tokens (McdClient *client,
+ McdDispatcher *dispatcher,
+ const gchar * const *cap_tokens)
+{
+ guint i;
+
+ if (cap_tokens == NULL)
+ return;
+
+ for (i = 0; cap_tokens[i] != NULL; i++)
+ {
+ TpHandle handle = tp_handle_ensure (dispatcher->priv->string_pool,
+ cap_tokens[i], NULL, NULL);
+
+ tp_handle_set_add (client->capability_tokens, handle);
+ tp_handle_unref (dispatcher->priv->string_pool, handle);
+ }
+}
+
+static void
+mcd_dispatcher_update_client_caps (McdDispatcher *self,
+ McdClient *client)
+{
+ GPtrArray *vas;
+ GHashTableIter iter;
+ gpointer k;
+
+ /* If we haven't finished inspecting initial clients yet, we'll push all
+ * the client caps into all connections when we do, so do nothing.
+ *
+ * If we don't have any connections, on the other hand, then there's
+ * nothing to do. */
+ if (!self->priv->startup_completed
+ || g_hash_table_size (self->priv->connections) == 0)
+ {
+ return;
+ }
+
+ vas = g_ptr_array_sized_new (1);
+ mcd_dispatcher_append_client_caps (self, client, vas);
+
+ g_hash_table_iter_init (&iter, self->priv->connections);
+
+ while (g_hash_table_iter_next (&iter, &k, NULL))
+ {
+ _mcd_connection_update_client_caps (k, vas);
+ }
+
+ g_ptr_array_foreach (vas, (GFunc) g_value_array_free, NULL);
+ g_ptr_array_free (vas, TRUE);
+}
+
static void
handler_get_all_cb (TpProxy *proxy,
GHashTable *properties,
@@ -2136,6 +2314,10 @@ handler_get_all_cb (TpProxy *proxy,
DEBUG ("%s has BypassApproval=%c", client->name,
client->bypass_approver ? 'T' : 'F');
+ mcd_client_add_cap_tokens (client, self,
+ tp_asv_get_boxed (properties, "Capabilities",
+ G_TYPE_STRV));
+
channels = tp_asv_get_boxed (properties, "HandledChannels",
MC_ARRAY_TYPE_OBJECT);
@@ -2169,6 +2351,8 @@ handler_get_all_cb (TpProxy *proxy,
}
}
+ mcd_dispatcher_update_client_caps (self, client);
+
finally:
mcd_dispatcher_release_startup_lock (self);
}
@@ -2288,9 +2472,11 @@ finally:
}
static void
-parse_client_file (McdClient *client, GKeyFile *file)
+parse_client_file (McdDispatcher *self,
+ McdClient *client,
+ GKeyFile *file)
{
- gchar **iface_names, **groups;
+ gchar **iface_names, **groups, **cap_tokens;
guint i;
gsize len = 0;
@@ -2347,6 +2533,14 @@ parse_client_file (McdClient *client, GKeyFile *file)
client->bypass_approver =
g_key_file_get_boolean (file, TP_IFACE_CLIENT_HANDLER,
"BypassApproval", NULL);
+
+ cap_tokens = g_key_file_get_keys (file,
+ TP_IFACE_CLIENT_HANDLER ".Capabilities",
+ NULL,
+ NULL);
+ mcd_client_add_cap_tokens (client, self,
+ (const gchar * const *) cap_tokens);
+ g_strfreev (cap_tokens);
}
static gchar *
@@ -2416,6 +2610,8 @@ create_mcd_client (McdDispatcher *self,
if (!activatable)
client->active = TRUE;
+ client->capability_tokens = tp_handle_set_new (self->priv->string_pool);
+
client->proxy = (TpClient *) _mcd_client_proxy_new (
self->priv->dbus_daemon, client->name, owner);
@@ -2457,7 +2653,7 @@ mcd_client_start_introspection (McdClientProxy *proxy,
if (G_LIKELY (!error))
{
DEBUG ("File found for %s: %s", client->name, filename);
- parse_client_file (client, file);
+ parse_client_file (dispatcher, client, file);
file_found = TRUE;
}
else
@@ -2484,21 +2680,28 @@ mcd_client_start_introspection (McdClientProxy *proxy,
{
client_add_interface_by_id (client);
- if ((client->interfaces & MCD_CLIENT_HANDLER) != 0 &&
- _mcd_client_proxy_is_active (proxy))
+ if ((client->interfaces & MCD_CLIENT_HANDLER) != 0)
{
- DEBUG ("%s is an active, activatable Handler", client->name);
+ if (_mcd_client_proxy_is_active (proxy))
+ {
+ DEBUG ("%s is an active, activatable Handler", client->name);
- /* We need to investigate whether it is handling any channels */
+ /* We need to investigate whether it is handling any channels */
- if (!dispatcher->priv->startup_completed)
- dispatcher->priv->startup_lock++;
+ if (!dispatcher->priv->startup_completed)
+ dispatcher->priv->startup_lock++;
- tp_cli_dbus_properties_call_get_all (client->proxy, -1,
- TP_IFACE_CLIENT_HANDLER,
- handler_get_all_cb,
- NULL, NULL,
- G_OBJECT (dispatcher));
+ tp_cli_dbus_properties_call_get_all (client->proxy, -1,
+ TP_IFACE_CLIENT_HANDLER,
+ handler_get_all_cb,
+ NULL, NULL,
+ G_OBJECT (dispatcher));
+ }
+ else
+ {
+ DEBUG ("%s is a Handler but not active", client->name);
+ mcd_dispatcher_update_client_caps (dispatcher, client);
+ }
}
}
@@ -2672,6 +2875,11 @@ name_owner_changed_cb (TpDBusDaemon *proxy,
if (!client->activatable)
{
+ /* in ContactCapabilities we indicate the disappearance
+ * of a client by giving it an empty set of capabilities and
+ * filters */
+ mcd_client_become_incapable (client);
+ mcd_dispatcher_update_client_caps (self, client);
g_hash_table_remove (priv->clients, name);
}
}
@@ -2890,6 +3098,10 @@ mcd_dispatcher_init (McdDispatcher * dispatcher)
priv->handler_map = _mcd_handler_map_new ();
priv->connections = g_hash_table_new (NULL, NULL);
+
+ /* Dummy handle type, we're just using this as a string pool */
+ priv->string_pool = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT,
+ NULL, NULL);
}
McdDispatcher *
@@ -3879,6 +4091,32 @@ mcd_dispatcher_lost_connection (gpointer data,
g_object_unref (self);
}
+GPtrArray *
+_mcd_dispatcher_dup_client_caps (McdDispatcher *self)
+{
+ GPtrArray *vas;
+ GHashTableIter iter;
+ gpointer p;
+
+ g_return_val_if_fail (MCD_IS_DISPATCHER (self), NULL);
+
+ vas = g_ptr_array_sized_new (g_hash_table_size (self->priv->clients));
+
+ if (!self->priv->startup_completed)
+ {
+ return NULL;
+ }
+
+ g_hash_table_iter_init (&iter, self->priv->clients);
+
+ while (g_hash_table_iter_next (&iter, NULL, &p))
+ {
+ mcd_dispatcher_append_client_caps (self, p, vas);
+ }
+
+ return vas;
+}
+
void
_mcd_dispatcher_add_connection (McdDispatcher *self,
McdConnection *connection)
@@ -3894,7 +4132,12 @@ _mcd_dispatcher_add_connection (McdDispatcher *self,
if (self->priv->startup_completed)
{
- _mcd_connection_start_dispatching (connection);
+ GPtrArray *vas = _mcd_dispatcher_dup_client_caps (self);
+
+ _mcd_connection_start_dispatching (connection, vas);
+
+ g_ptr_array_foreach (vas, (GFunc) g_value_array_free, NULL);
+ g_ptr_array_free (vas, TRUE);
}
/* else _mcd_connection_start_dispatching will be called when we're ready
* for it */
diff --git a/src/mcd.xml b/src/mcd.xml
index a324381..6e8e657 100644
--- a/src/mcd.xml
+++ b/src/mcd.xml
@@ -12,6 +12,7 @@
<xi:include href="../xml/Account_Manager_Interface_Query.xml"/>
<xi:include href="../xml/Account_Manager_Interface_Creation.xml"/>
+<xi:include href="../xml/Connection_Interface_Contact_Capabilities_Draft1.xml"/>
<xi:include href="../xml/Connection_Interface_Contact_Capabilities.xml"/>
</tp:spec>
diff --git a/test/twisted/Makefile.am b/test/twisted/Makefile.am
index 2dcd18d..0b24a93 100644
--- a/test/twisted/Makefile.am
+++ b/test/twisted/Makefile.am
@@ -20,6 +20,7 @@ TWISTED_BASIC_TESTS = \
account-requests/create-text.py \
account-requests/delete-account-during-request.py \
capabilities/draft-1.py \
+ capabilities/contact-caps.py \
capabilities/legacy-caps.py \
dispatcher/approver-fails.py \
dispatcher/already-has-channel.py \
diff --git a/test/twisted/capabilities/contact-caps.py b/test/twisted/capabilities/contact-caps.py
new file mode 100644
index 0000000..712028c
--- /dev/null
+++ b/test/twisted/capabilities/contact-caps.py
@@ -0,0 +1,168 @@
+# Copyright (C) 2009 Nokia Corporation
+# Copyright (C) 2009 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
+
+import dbus
+
+"""Regression test for pushing clients' capabilities into a CM with
+ContactCapabilities (final version, which is the same as draft 2).
+"""
+
+import dbus
+import dbus.service
+
+from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \
+ call_async
+from mctest import exec_test, SimulatedConnection, SimulatedClient, \
+ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \
+ expect_client_setup
+import constants as cs
+
+def test(q, bus, mc):
+ forbidden = [
+ EventPattern('dbus-method-call', handled=False,
+ interface=cs.CONN_IFACE_CAPS,
+ method='AdvertiseCapabilities'),
+ ]
+ q.forbid_events(forbidden)
+
+ # Two clients want to handle channels: MediaCall is running, and AbiWord
+ # is activatable.
+
+ # this must match the .client file
+ abi_contact_fixed_properties = dbus.Dictionary({
+ cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAM_TUBE,
+ cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
+ cs.CHANNEL_TYPE_STREAM_TUBE + '.Service': 'x-abiword',
+ }, signature='sv')
+ abi_room_fixed_properties = dbus.Dictionary({
+ cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAM_TUBE,
+ cs.CHANNEL + '.TargetHandleType': cs.HT_ROOM,
+ cs.CHANNEL_TYPE_STREAM_TUBE + '.Service': 'x-abiword',
+ }, signature='sv')
+
+ media_fixed_properties = dbus.Dictionary({
+ cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAMED_MEDIA,
+ }, signature='sv')
+ media_call = SimulatedClient(q, bus, 'MediaCall',
+ observe=[], approve=[], handle=[media_fixed_properties],
+ cap_tokens=[cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/ice-udp',
+ cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/audio/speex',
+ cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/video/theora'],
+ bypass_approval=False)
+
+ # wait for MC to download the properties
+ expect_client_setup(q, [media_call])
+
+ def check_contact_caps(e):
+ structs = e.args[0]
+
+ filters = {}
+ tokens = {}
+
+ assert len(structs) == 2
+
+ for struct in structs:
+ assert struct[0] not in filters
+ filters[struct[0]] = sorted(struct[1])
+ tokens[struct[0]] = sorted(struct[2])
+
+ assert media_fixed_properties in filters[cs.CLIENT + '.MediaCall']
+ assert len(filters[cs.CLIENT + '.MediaCall']) == 1
+
+ assert abi_room_fixed_properties in filters[cs.CLIENT + '.AbiWord']
+ assert abi_contact_fixed_properties in filters[cs.CLIENT + '.AbiWord']
+ assert len(filters[cs.CLIENT + '.AbiWord']) == 2
+
+ assert len(tokens[cs.CLIENT + '.MediaCall']) == 3
+ assert cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/ice-udp' in \
+ tokens[cs.CLIENT + '.MediaCall']
+ assert cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/audio/speex' in \
+ tokens[cs.CLIENT + '.MediaCall']
+ assert cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/video/theora' in \
+ tokens[cs.CLIENT + '.MediaCall']
+
+ assert len(tokens[cs.CLIENT + '.AbiWord']) == 2
+ assert 'com.example.Foo' in tokens[cs.CLIENT + '.AbiWord']
+ assert 'com.example.Bar' in tokens[cs.CLIENT + '.AbiWord']
+
+ return True
+
+ params = dbus.Dictionary({"account": "someguy@example.com",
+ "password": "secrecy"}, signature='sv')
+ cm_name_ref, account = create_fakecm_account(q, bus, mc, params)
+ conn, before, after = enable_fakecm_account(q, bus, mc, account, params,
+ extra_interfaces=[cs.CONN_IFACE_CONTACT_CAPS,
+ cs.CONN_IFACE_CAPS],
+ expect_before_connect=[
+ EventPattern('dbus-method-call', handled=False,
+ interface=cs.CONN_IFACE_CONTACT_CAPS,
+ method='UpdateCapabilities',
+ predicate=check_contact_caps),
+ ],
+ expect_after_connect=[
+ EventPattern('dbus-method-call', handled=False,
+ interface=cs.CONN_IFACE_CONTACT_CAPS,
+ method='UpdateCapabilities',
+ predicate=check_contact_caps),
+ ])
+ q.dbus_return(before.message, signature='')
+ q.dbus_return(after.message, signature='')
+
+ irssi_bus = dbus.bus.BusConnection()
+ irssi_bus.set_exit_on_disconnect(False) # we'll disconnect later
+ q.attach_to_bus(irssi_bus)
+ irssi_fixed_properties = dbus.Dictionary({
+ cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
+ cs.CHANNEL + '.TargetHandleType': cs.HT_ROOM,
+ }, signature='sv')
+ irssi = SimulatedClient(q, irssi_bus, 'Irssi',
+ observe=[], approve=[], handle=[irssi_fixed_properties],
+ cap_tokens=[],
+ bypass_approval=False)
+
+ # wait for MC to download the properties
+ expect_client_setup(q, [irssi])
+
+ e = q.expect('dbus-method-call', handled=False,
+ interface=cs.CONN_IFACE_CONTACT_CAPS,
+ method='UpdateCapabilities')
+
+ assert len(e.args[0]) == 1
+ struct = e.args[0][0]
+ assert struct[0] == cs.CLIENT + '.Irssi'
+ assert struct[1] == [irssi_fixed_properties]
+ assert struct[2] == []
+
+ # When Irssi exits, the CM is told it has gone
+ irssi.release_name()
+ del irssi
+ irssi_bus.flush()
+ irssi_bus.close()
+
+ e = q.expect('dbus-method-call', handled=False,
+ interface=cs.CONN_IFACE_CONTACT_CAPS,
+ method='UpdateCapabilities')
+
+ assert len(e.args[0]) == 1
+ struct = e.args[0][0]
+ assert struct[0] == cs.CLIENT + '.Irssi'
+ assert struct[1] == []
+ assert struct[2] == []
+
+if __name__ == '__main__':
+ exec_test(test, {})
diff --git a/test/twisted/capabilities/legacy-caps.py b/test/twisted/capabilities/legacy-caps.py
index aad9c80..81d99ab 100644
--- a/test/twisted/capabilities/legacy-caps.py
+++ b/test/twisted/capabilities/legacy-caps.py
@@ -20,10 +20,6 @@ import dbus
"""Regression test for pushing clients' capabilities into an old CM, with only
the old Capabilities interface.
-
-Until MC supports ContactCapabilities draft 2, this test will also apply to
-versions of Gabble that implement old Capabilities, and ContactCapabilities
-draft 1, but not draft 2.
"""
import dbus
diff --git a/test/twisted/constants.py b/test/twisted/constants.py
index f55ea7f..7013cf2 100644
--- a/test/twisted/constants.py
+++ b/test/twisted/constants.py
@@ -60,7 +60,7 @@ CONN_IFACE_AVATARS = CONN + '.Interface.Avatars'
CONN_IFACE_CAPS = CONN + '.Interface.Capabilities'
CONN_IFACE_CONTACTS = CONN + '.Interface.Contacts'
CONN_IFACE_CONTACT_CAPS_DRAFT1 = CONN + '.Interface.ContactCapabilities.DRAFT'
-CONN_IFACE_CONTACT_CAPS_DRAFT2 = CONN + '.Interface.ContactCapabilities.DRAFT2'
+CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities'
CONN_IFACE_REQUESTS = CONN + '.Interface.Requests'
CONN_IFACE_SIMPLE_PRESENCE = CONN + '.Interface.SimplePresence'
diff --git a/test/twisted/mctest.py b/test/twisted/mctest.py
index 5aa015a..2d62358 100644
--- a/test/twisted/mctest.py
+++ b/test/twisted/mctest.py
@@ -577,7 +577,8 @@ def aasv(x):
class SimulatedClient(object):
def __init__(self, q, bus, clientname,
- observe=[], approve=[], handle=[], bypass_approval=False,
+ observe=[], approve=[], handle=[],
+ cap_tokens=[], bypass_approval=False,
request_notification=True, implement_get_interfaces=True):
self.q = q
self.bus = bus
@@ -590,6 +591,7 @@ class SimulatedClient(object):
self.bypass_approval = bool(bypass_approval)
self.request_notification = bool(request_notification)
self.handled_channels = dbus.Array([], signature='o')
+ self.cap_tokens = dbus.Array(cap_tokens, signature='s')
if implement_get_interfaces:
q.add_dbus_method_impl(self.Get_Interfaces,
@@ -619,6 +621,9 @@ class SimulatedClient(object):
q.add_dbus_method_impl(self.Get_HandlerChannelFilter,
path=self.object_path, interface=cs.PROPERTIES_IFACE,
method='Get', args=[cs.HANDLER, 'HandlerChannelFilter'])
+ q.add_dbus_method_impl(self.Get_Capabilities,
+ path=self.object_path, interface=cs.PROPERTIES_IFACE,
+ method='Get', args=[cs.HANDLER, 'Capabilities'])
q.add_dbus_method_impl(self.Get_HandledChannels,
path=self.object_path, interface=cs.PROPERTIES_IFACE,
method='Get', args=[cs.HANDLER, 'HandledChannels'])
@@ -684,9 +689,14 @@ class SimulatedClient(object):
'HandlerChannelFilter': self.handle,
'BypassApproval': self.bypass_approval,
'HandledChannels': self.handled_channels,
+ 'Capabilities': self.cap_tokens,
},
signature='a{sv}', bus=self.bus)
+ def Get_Capabilities(self, e):
+ self.q.dbus_return(e.message, self.cap_tokens, signature='v',
+ bus=self.bus)
+
def Get_HandledChannels(self, e):
self.q.dbus_return(e.message, self.handled_channels, signature='v',
bus=self.bus)
diff --git a/test/twisted/telepathy/clients/AbiWord.client b/test/twisted/telepathy/clients/AbiWord.client
index 4022f43..2ec8494 100644
--- a/test/twisted/telepathy/clients/AbiWord.client
+++ b/test/twisted/telepathy/clients/AbiWord.client
@@ -10,3 +10,7 @@ org.freedesktop.Telepathy.Channel.Type.StreamTube.Service s=x-abiword
org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.StreamTube
org.freedesktop.Telepathy.Channel.TargetHandleType u=2
org.freedesktop.Telepathy.Channel.Type.StreamTube.Service s=x-abiword
+
+[org.freedesktop.Telepathy.Client.Handler.Capabilities]
+com.example.Foo=true
+com.example.Bar=true
diff --git a/xml/Connection_Interface_Contact_Capabilities.xml b/xml/Connection_Interface_Contact_Capabilities.xml
index 042b24b..50f8b78 100644
--- a/xml/Connection_Interface_Contact_Capabilities.xml
+++ b/xml/Connection_Interface_Contact_Capabilities.xml
@@ -18,10 +18,9 @@ Lesser General Public License for more details.</p>
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>
</tp:license>
- <interface name="org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities.DRAFT"
- tp:causes-havoc="experimental">
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities">
<tp:requires interface="org.freedesktop.Telepathy.Connection"/>
- <tp:added version="0.17.16">(as a draft)</tp:added>
+ <tp:added version="0.17.UNRELEASED">(as stable API)</tp:added>
<tp:docstring xmlns="http://www.w3.org/1999/xhtml">
<p>Contact capabilities describe the channel classes which may be
@@ -38,38 +37,77 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
<p>This interface also enables user interfaces to notify the connection
manager what capabilities to advertise for the user to other contacts.
This is done by using the
- <tp:member-ref>SetSelfCapabilities</tp:member-ref> method, and deals
- with channel property values pertaining to them which are implemented
- by available client processes.</p>
+ <tp:member-ref>UpdateCapabilities</tp:member-ref> method.</p>
+ <tp:rationale>
+ <p>XMPP is a major user of this interface: XMPP contacts will not,
+ in general, be callable using VoIP unless they advertise suitable
+ Jingle capabilities.</p>
+
+ <p>Many other protocols also have some concept of capability flags,
+ which this interface exposes in a protocol-independent way.</p>
+ </tp:rationale>
</tp:docstring>
- <method name="SetSelfCapabilities"
- tp:name-for-bindings="Set_Self_Capabilities">
- <arg direction="in" name="caps" type="aa{sv}"
+ <tp:struct name="Handler_Capabilities"
+ array-name="Handler_Capabilities_List">
+ <tp:docstring>
+ A structure representing the capabilities of a single client.
+ </tp:docstring>
+
+ <tp:member name="Well_Known_Name" type="s" tp:type="DBus_Well_Known_Name">
+ <tp:docstring>
+ For implementations of the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy">Client</tp:dbus-ref>
+ interface, the well-known bus name name of the client; for any other
+ process, any other reversed domain name that uniquely identifies it.
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member name="Channel_Classes" type="aa{sv}"
tp:type="String_Variant_Map[]">
<tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- An array of channel classes to replace to the list of what the
- connection can handle. If you include optional properties, they
- may not get advertised. The given properties are matched to the
- mandatory properties.
+ An array of channel classes that can be handled by this client.
+ This will usually be a copy of the client's <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Client.Handler">HandlerChannelFilter</tp:dbus-ref>
+ property.
</tp:docstring>
- </arg>
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>Used by user interfaces to indicate which channel classes they are
- able to handle on this connection. It replaces the previous advertised
- channel classes by the set given as parameter.</p>
+ </tp:member>
- <p>If a channel class is unknown by the connection manager, it is just
- ignored. No error are returned in this case, and other known channel
- class are added.</p>
+ <tp:member name="Capabilities"
+ type="as" tp:type="Handler_Capability_Token[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ An array of client capabilities supported by this client, to be
+ used by the connection manager to determine what capabilities to
+ advertise. This will usually be a copy of the client's <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Client.Handler">Capabilities</tp:dbus-ref>
+ property.
+ </tp:docstring>
+ </tp:member>
+ </tp:struct>
- <p>Upon a successful invocation of this method, the
- <tp:member-ref>ContactCapabilitiesChanged</tp:member-ref> signal
- will only be emitted for the user's own
- handle (as returned by GetSelfHandle) by the connection manager if, in
- the given protocol, the given capabilities are distinct from the
- previous state.</p>
+ <method name="UpdateCapabilities" tp:name-for-bindings="Update_Capabilities">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Alter the connection's advertised capabilities to include
+ the intersection of the given clients' capabilities with what the
+ connection manager is able to implement.</p>
+
+ <p>On connections managed by the ChannelDispatcher, processes other
+ than the ChannelDispatcher SHOULD NOT call this method, and the
+ ChannelDispatcher SHOULD use this method to advertise the
+ capabilities of all the registered <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy">Client.Handler</tp:dbus-ref>
+ implementations.On connections not managed by the ChannelDispatcher,
+ clients MAY use this method directly, to indicate the channels they
+ will handle and the extra capabilities they have.</p>
+
+ <p>Upon a successful invocation of this method, the connection manager
+ will only emit the
+ <tp:member-ref>ContactCapabilitiesChanged</tp:member-ref> signal
+ for the user's <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Connection">SelfHandle</tp:dbus-ref>
+ if, in the underlying protocol, the new capabilities are distinct
+ from the previous state.</p>
<tp:rationale>
<p>The connection manager will essentially intersect the provided
@@ -79,10 +117,45 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
channel) will almost certainly not be advertised.</p>
</tp:rationale>
+ <p>This method MAY be called on a newly-created connection while it
+ is still in the DISCONNECTED state, to request that when the
+ connection connects, it will do so with the appropriate
+ capabilities. Doing so MUST NOT fail.</p>
</tp:docstring>
+
+ <arg direction="in" name="Handler_Capabilities" type="a(saa{sv}as)"
+ tp:type="Handler_Capabilities[]">
+ <tp:docstring>
+ <p>The capabilities of one or more clients.</p>
+
+ <p>For each client in the given list, any capabilities previously
+ advertised for the same client name are discarded, then replaced by
+ the capabilities indicated.</p>
+
+ <p>As a result, if a client becomes unavailable, this method SHOULD
+ be called with a <tp:type>Handler_Capabilities</tp:type> structure
+ containing its name, an empty list of channel classes, and an
+ empty list of capabilities. When this is done, the connection
+ manager SHOULD free all memory associated with that client name.</p>
+
+ <tp:rationale>
+ <p>This method takes a list of clients so that
+ when the channel dispatcher first calls it (with a list of all
+ the Handlers that are initially available), the changes can be
+ made atomically, with only one transmission of updated
+ capabilities to the network. Afterwards, the channel dispatcher
+ will call this method with a single-element list every time
+ a Handler becomes available or unavailable.</p>
+ </tp:rationale>
+
+ <p>The connection manager MUST ignore any channel classes and client
+ capabilities for which there is no representation in the protocol
+ or no support in the connection manager.</p>
+ </tp:docstring>
+ </arg>
+
<tp:possible-errors>
<tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
- <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
</tp:possible-errors>
</method>
@@ -95,11 +168,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
<p>The handle zero MUST NOT be included in the request.</p>
</tp:docstring>
</arg>
- <!-- There was a bug in dbus-glib that prevent to use the right type:
- Instead of a{ua(a{sv}as)}, we used a(ua{sv}as) as a workaround.
- See http://bugs.freedesktop.org/show_bug.cgi?id=17329
- Now there is a fix, so we don't use the workaround anymore.
- -->
<arg direction="out" type="a{ua(a{sv}as)}"
tp:type="Contact_Capabilities_Map" name="Contact_Capabilities">
<tp:docstring xmlns="http://www.w3.org/1999/xhtml">
@@ -161,6 +229,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
</tp:member>
</tp:mapping>
+ <tp:contact-attribute name="capabilities"
+ type="a(a{sv}as)" tp:type="Requestable_Channel_Class[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The same structs that would be returned by
+ <tp:member-ref>GetContactCapabilities</tp:member-ref>.
+ Omitted from the result if the contact's capabilities
+ are not known; present in the result as an empty array if the
+ contact is known to have no capabilities at all.</p>
+ </tp:docstring>
+ </tp:contact-attribute>
+
</interface>
</node>
<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/xml/Connection_Interface_Contact_Capabilities_Draft1.xml b/xml/Connection_Interface_Contact_Capabilities_Draft1.xml
new file mode 100644
index 0000000..18e78ca
--- /dev/null
+++ b/xml/Connection_Interface_Contact_Capabilities_Draft1.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" ?>
+<node name="/Connection_Interface_Contact_Capabilities_Draft1" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright> Copyright (C) 2005, 2006, 2008 Collabora Limited </tp:copyright>
+ <tp:copyright> Copyright (C) 2005, 2006, 2008 Nokia Corporation </tp:copyright>
+ <tp:copyright> Copyright (C) 2006 INdT </tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+<p>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.</p>
+
+<p>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 Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>
+ </tp:license>
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:requires interface="org.freedesktop.Telepathy.Connection"/>
+ <tp:added version="0.17.16">(as a draft)</tp:added>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Contact capabilities describe the channel classes which may be
+ created with a given contact in advance of attempting to create a
+ channel. Each capability represents a commitment by the
+ connection manager that it will ordinarily be able to create a channel
+ with a contact when given a request with the properties defined by the
+ channel class.</p>
+
+ <p>Capabilities pertain to particular contact handles, and represent
+ activities such as having a text chat, a voice call with the user or a
+ stream tube of a defined type.</p>
+
+ <p>This interface also enables user interfaces to notify the connection
+ manager what capabilities to advertise for the user to other contacts.
+ This is done by using the
+ <tp:member-ref>SetSelfCapabilities</tp:member-ref> method, and deals
+ with channel property values pertaining to them which are implemented
+ by available client processes.</p>
+
+ </tp:docstring>
+
+ <method name="SetSelfCapabilities"
+ tp:name-for-bindings="Set_Self_Capabilities">
+ <arg direction="in" name="caps" type="aa{sv}"
+ tp:type="String_Variant_Map[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ An array of channel classes to replace to the list of what the
+ connection can handle. If you include optional properties, they
+ may not get advertised. The given properties are matched to the
+ mandatory properties.
+ </tp:docstring>
+ </arg>
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Used by user interfaces to indicate which channel classes they are
+ able to handle on this connection. It replaces the previous advertised
+ channel classes by the set given as parameter.</p>
+
+ <p>If a channel class is unknown by the connection manager, it is just
+ ignored. No error are returned in this case, and other known channel
+ class are added.</p>
+
+ <p>Upon a successful invocation of this method, the
+ <tp:member-ref>ContactCapabilitiesChanged</tp:member-ref> signal
+ will only be emitted for the user's own
+ handle (as returned by GetSelfHandle) by the connection manager if, in
+ the given protocol, the given capabilities are distinct from the
+ previous state.</p>
+
+ <tp:rationale>
+ <p>The connection manager will essentially intersect the provided
+ capabilities and the channel classes it implements. Therefore,
+ certain properties which are never fixed for a channel class
+ (such as the target handle, or the Parameters property of a tube
+ channel) will almost certainly not be advertised.</p>
+ </tp:rationale>
+
+ </tp:docstring>
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
+ </tp:possible-errors>
+ </method>
+
+ <method name="GetContactCapabilities"
+ tp:name-for-bindings="Get_Contact_Capabilities">
+ <arg direction="in" name="handles" type="au" tp:type="Contact_Handle[]">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>An array of contact handles for this connection.</p>
+
+ <p>The handle zero MUST NOT be included in the request.</p>
+ </tp:docstring>
+ </arg>
+ <!-- There was a bug in dbus-glib that prevent to use the right type:
+ Instead of a{ua(a{sv}as)}, we used a(ua{sv}as) as a workaround.
+ See http://bugs.freedesktop.org/show_bug.cgi?id=17329
+ Now there is a fix, so we don't use the workaround anymore.
+ -->
+ <arg direction="out" type="a{ua(a{sv}as)}"
+ tp:type="Contact_Capabilities_Map_1" name="Contact_Capabilities">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ An array of structures containing:
+ <ul>
+ <li>a dictionary mapping the channel properties to their values.</li>
+ <li>an array of additional allowed properties</li>
+ </ul>
+ </tp:docstring>
+ </arg>
+ <tp:docstring>
+ Returns an array of enhanced capabilities for the given contact handles.
+ </tp:docstring>
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.InvalidHandle">
+ <tp:docstring>
+ The handle does not represent a contact. Zero is always invalid.
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <signal name="ContactCapabilitiesChanged"
+ tp:name-for-bindings="Contact_Capabilities_Changed">
+ <arg name="caps" type="a{ua(a{sv}as)}"
+ tp:type="Contact_Capabilities_Map_1">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ All the capabilities of the contacts
+ </tp:docstring>
+ </arg>
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Announce that there has been a change of capabilities on the
+ given handles. A single signal can be emitted for several
+ contacts.</p>
+
+ <tp:rationale>
+ <p>The underlying protocol can get several contacts' capabilities at
+ the same time.</p>
+ </tp:rationale>
+
+ </tp:docstring>
+ </signal>
+
+ <tp:mapping name="Contact_Capabilities_Map_1"
+ array-name="Contact_Capabilities_Map_1_List">
+ <tp:docstring>A mapping from contact handle to their capabilities.
+ </tp:docstring>
+ <tp:member type="u" name="Key" tp:type="Contact_Handle">
+ <tp:docstring>
+ A contact handle.
+ </tp:docstring>
+ </tp:member>
+ <tp:member type="a(a{sv}as)" name="Value"
+ tp:type="Requestable_Channel_Class[]">
+ <tp:docstring>
+ The contact capabilities.
+ </tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/xml/Makefile.am b/xml/Makefile.am
index ef83840..873db25 100644
--- a/xml/Makefile.am
+++ b/xml/Makefile.am
@@ -23,7 +23,8 @@ SPECS = \
Client_Handler.xml \
Client_Interface_Requests.xml \
Client_Observer.xml \
- Connection_Interface_Contact_Capabilities.xml
+ Connection_Interface_Contact_Capabilities.xml \
+ Connection_Interface_Contact_Capabilities_Draft1.xml
SPECS_GEN = ${SPECS:%.xml=_gen/introspect-%.xml}
diff --git a/xml/all.xml b/xml/all.xml
index 96dc6c4..cb7661f 100644
--- a/xml/all.xml
+++ b/xml/all.xml
@@ -25,6 +25,8 @@
from="Telepathy specification (ConnectionManager)"/>
<tp:external-type name="Requestable_Channel_Class" type="(a{sv}as)"
from="Telepathy specification (Requests)"/>
+ <tp:external-type name="Handler_Capability_Token" type="s"
+ from="Telepathy specification (Handler)"/>
</tp:generic-types>
<xi:include href="generic-types.xml"/>
diff --git a/xml/nmc5.xml b/xml/nmc5.xml
index 3c09ca7..973081e 100644
--- a/xml/nmc5.xml
+++ b/xml/nmc5.xml
@@ -25,6 +25,7 @@
<xi:include href="Channel_Request.xml"/>
<xi:include href="Channel_Dispatch_Operation.xml"/>
+<xi:include href="Connection_Interface_Contact_Capabilities_Draft1.xml"/>
<xi:include href="Connection_Interface_Contact_Capabilities.xml"/>
</tp:spec>