summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2008-12-01 11:12:21 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2008-12-01 11:12:21 +0000
commitc6582df2f2f83786e37ecd25488671e5dec642a5 (patch)
treec04f0236eb3e3e0692cadbee7d72922a78b48580
parent2b649e3dbf2a7a77c54637dc1fd16c25817a53b3 (diff)
parenta786cf24c5228fe1e675f484db066bfd82f297bf (diff)
Merge branch 'contacts-fast-path'
-rw-r--r--NEWS15
-rw-r--r--docs/reference/telepathy-glib-sections.txt3
-rw-r--r--telepathy-glib/connection-handles.c146
-rw-r--r--telepathy-glib/connection.h13
-rw-r--r--telepathy-glib/contact.c360
-rw-r--r--telepathy-glib/contacts-mixin.h6
-rw-r--r--telepathy-glib/group-mixin.h7
-rw-r--r--telepathy-glib/presence-mixin.h6
-rw-r--r--telepathy-glib/properties-mixin.h6
-rw-r--r--telepathy-glib/text-mixin.h6
-rw-r--r--telepathy-glib/util.c67
-rw-r--r--telepathy-glib/util.h2
-rw-r--r--tests/dbus/contacts.c48
13 files changed, 620 insertions, 65 deletions
diff --git a/NEWS b/NEWS
index 661567c08..a54854b95 100644
--- a/NEWS
+++ b/NEWS
@@ -5,8 +5,23 @@ Dependencies:
Enhancements:
+* TpContact now has a fast path using the Contacts interface to reduce
+ D-Bus round-trips, if supported
+
+* tp_connection_get_contact_attributes integrates the Contacts interface
+ with TpConnection's handle reference tracking at a lower level
+
+* TpChannel now tracks its immutable properties (as provided by
+ Requests.NewChannels, Requests.CreateChannel and Requests.EnsureChannel),
+ can be constructed from a dictionary of immutable properties
+ (tp_channel_new_from_properties), and has a fast path using GetAll to reduce
+ round trips (if supported); when constructed from a dictionary of immutable
+ properties, non-Group channels should become 'ready' in a single round-trip
+
Fixes:
+* fd.o #15092: mixins in a superclass should now work correctly in subclasses
+
telepathy-glib 0.7.18 (2008-11-03)
==================================
diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index 25ec1680c..2a748aa9a 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -930,6 +930,8 @@ tp_g_value_slice_free
tp_g_value_slice_dup
tp_strdiff
tp_mixin_offset_cast
+tp_mixin_class_get_offset
+tp_mixin_instance_get_offset
tp_escape_as_identifier
tp_strv_contains
</SECTION>
@@ -2078,6 +2080,7 @@ tp_connection_get_status
TpConnectionRequestHandlesCb
tp_connection_request_handles
TpConnectionHoldHandlesCb
+tp_connection_get_contact_attributes
tp_connection_hold_handles
tp_connection_unref_handles
tp_connection_init_known_interfaces
diff --git a/telepathy-glib/connection-handles.c b/telepathy-glib/connection-handles.c
index 6d1ff10b6..3d8647a05 100644
--- a/telepathy-glib/connection-handles.c
+++ b/telepathy-glib/connection-handles.c
@@ -268,6 +268,17 @@ _tp_connection_clean_up_handle_refs (TpConnection *self)
* Release the reference to the handles in @handles that was obtained by
* calling tp_connection_hold_handles() or tp_connection_request_handles().
*
+ * This function might release any references held by calling
+ * tp_cli_connection_call_request_handles(),
+ * tp_cli_connection_run_request_handles(),
+ * tp_cli_connection_call_hold_handles(),
+ * tp_cli_connection_run_hold_handles(),
+ * tp_cli_connection_interface_contacts_call_get_contact_attributes() or
+ * tp_cli_connection_interface_contacts_run_get_contact_attributes() directly.
+ * Those functions should be avoided in favour of using #TpContact,
+ * tp_connection_hold_handles(), tp_connection_request_handles() and
+ * tp_connection_get_contact_attributes().
+ *
* If @self has already become invalid, this function does nothing.
*/
void
@@ -649,3 +660,138 @@ tp_connection_request_handles (TpConnection *self,
(const gchar **) context->ids, connection_requested_handles,
context, request_handles_context_free, weak_object);
}
+
+
+typedef struct {
+ tp_cli_connection_interface_contacts_callback_for_get_contact_attributes
+ callback;
+ gpointer user_data;
+ GDestroyNotify destroy;
+ gboolean hold;
+} GetContactAttributesContext;
+
+
+static void
+get_contact_attributes_context_free (gpointer p)
+{
+ GetContactAttributesContext *c = p;
+
+ if (c->destroy != NULL)
+ c->destroy (c->user_data);
+
+ g_slice_free (GetContactAttributesContext, c);
+}
+
+
+static void
+connection_got_contact_attributes (TpConnection *self,
+ GHashTable *attributes,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ GetContactAttributesContext *c = user_data;
+
+ DEBUG ("%u handles, hold=%c", g_hash_table_size (attributes),
+ c->hold ? 'T' : 'F');
+
+ if (c->hold)
+ {
+ GHashTableIter iter;
+ gpointer key, value;
+ GArray *handles = g_array_sized_new (FALSE, FALSE, sizeof (guint),
+ g_hash_table_size (attributes));
+
+ g_hash_table_iter_init (&iter, attributes);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ TpHandle handle = GPOINTER_TO_UINT (key);
+
+ DEBUG ("- %u", handle);
+
+ g_array_append_val (handles, handle);
+ }
+
+ /* remember that we have a ref */
+ _tp_connection_ref_handles (self, TP_HANDLE_TYPE_CONTACT, handles);
+ g_array_free (handles, TRUE);
+ }
+
+ c->callback (self, attributes, error, c->user_data, weak_object);
+}
+
+
+/**
+ * tp_connection_get_contact_attributes:
+ * @self: a connection
+ * @timeout_ms: the timeout in milliseconds, or -1 to use the default
+ * @n_handles: the number of handles in @handles (must be at least 1)
+ * @handles: an array of handles
+ * @interfaces: a #GStrv of interfaces
+ * @hold: if %TRUE, the callback will hold one reference to each valid handle
+ * @callback: called on success or failure (unless @weak_object has become
+ * unreferenced)
+ * @user_data: arbitrary user-supplied data
+ * @destroy: called to destroy @user_data after calling @callback, or when
+ * @weak_object becomes unreferenced (whichever occurs sooner)
+ * @weak_object: if not %NULL, an object to be weakly referenced: if it is
+ * destroyed, @callback will not be called
+ *
+ * Return (via a callback) any number of attributes of the given handles, and
+ * if they are valid and @hold is TRUE, hold a reference to them.
+ *
+ * This is a thin wrapper around the GetContactAttributes D-Bus method, and
+ * should be used in preference to
+ * tp_cli_connection_interface_contacts_get_contact_attributes().
+ * The #TpContact API provides a higher-level abstraction which should
+ * usually be used instead.
+ *
+ * @callback will later be called with the attributes of those of the given
+ * handles that were valid. Invalid handles are simply omitted from the
+ * parameter to the callback.
+ *
+ * If @hold is %TRUE, the @callback is given one reference to each handle
+ * that appears as a key in the callback's @attributes parameter.
+ */
+void
+tp_connection_get_contact_attributes (TpConnection *self,
+ gint timeout_ms,
+ guint n_handles,
+ const TpHandle *handles,
+ const gchar * const *interfaces,
+ gboolean hold,
+ tp_cli_connection_interface_contacts_callback_for_get_contact_attributes callback,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GObject *weak_object)
+{
+ GetContactAttributesContext *c;
+ GArray *a;
+ guint i;
+
+ DEBUG ("%u handles, hold=%c", n_handles, hold ? 'T' : 'F');
+
+ for (i = 0; i < n_handles; i++)
+ DEBUG ("- %u", handles[i]);
+
+ g_return_if_fail (TP_IS_CONNECTION (self));
+ g_return_if_fail (n_handles >= 1);
+ g_return_if_fail (handles != NULL);
+ g_return_if_fail (callback != NULL);
+
+ c = g_slice_new0 (GetContactAttributesContext);
+ a = g_array_sized_new (FALSE, FALSE, sizeof (guint), n_handles);
+
+ g_array_append_vals (a, handles, n_handles);
+
+ c->destroy = destroy;
+ c->user_data = user_data;
+ c->callback = callback;
+ c->hold = hold;
+
+ tp_cli_connection_interface_contacts_call_get_contact_attributes (self, -1,
+ a, (const gchar **) interfaces, hold, connection_got_contact_attributes,
+ c, get_contact_attributes_context_free, weak_object);
+ g_array_free (a, TRUE);
+}
diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h
index 557b464d5..248316f46 100644
--- a/telepathy-glib/connection.h
+++ b/telepathy-glib/connection.h
@@ -131,4 +131,17 @@ G_END_DECLS
#include <telepathy-glib/_gen/tp-cli-connection.h>
+G_BEGIN_DECLS
+
+/* connection-handles.c again - this has to come after the auto-generated
+ * stuff because it uses an auto-generated typedef */
+
+void tp_connection_get_contact_attributes (TpConnection *self,
+ gint timeout_ms, guint n_handles, const TpHandle *handles,
+ const gchar * const *interfaces, gboolean hold,
+ tp_cli_connection_interface_contacts_callback_for_get_contact_attributes callback,
+ gpointer user_data, GDestroyNotify destroy, GObject *weak_object);
+
+G_END_DECLS
+
#endif
diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c
index 77360c6c3..ceae418dd 100644
--- a/telepathy-glib/contact.c
+++ b/telepathy-glib/contact.c
@@ -20,6 +20,8 @@
#include <telepathy-glib/contact.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/util.h>
@@ -45,6 +47,17 @@
* Contact objects are instantiated using
* tp_connection_get_contacts_by_handle() or
* tp_connection_get_contacts_by_id().
+ *
+ * Note that releasing a #TpContact object might release handle references
+ * held by calling tp_cli_connection_call_request_handles(),
+ * tp_cli_connection_run_request_handles(),
+ * tp_cli_connection_call_hold_handles(),
+ * tp_cli_connection_run_hold_handles(),
+ * tp_cli_connection_interface_contacts_call_get_contact_attributes() or
+ * tp_cli_connection_interface_contacts_run_get_contact_attributes() directly.
+ * Those functions should be avoided in favour of using #TpContact,
+ * tp_connection_hold_handles(), tp_connection_request_handles() and
+ * tp_connection_get_contact_attributes().
*/
struct _TpContactClass {
@@ -1288,20 +1301,26 @@ contacts_aliases_changed (TpConnection *connection,
static void
+contacts_bind_to_aliases_changed (TpConnection *connection)
+{
+ if (!connection->priv->tracking_aliases_changed)
+ {
+ connection->priv->tracking_aliases_changed = TRUE;
+
+ tp_cli_connection_interface_aliasing_connect_to_aliases_changed (
+ connection, contacts_aliases_changed, NULL, NULL, NULL, NULL);
+ }
+}
+
+
+static void
contacts_get_aliases (ContactsContext *c)
{
guint i;
g_assert (c->handles->len == c->contacts->len);
- /* ensure we'll get told about alias changes */
- if (!c->connection->priv->tracking_aliases_changed)
- {
- c->connection->priv->tracking_aliases_changed = TRUE;
-
- tp_cli_connection_interface_aliasing_connect_to_aliases_changed (
- c->connection, contacts_aliases_changed, NULL, NULL, NULL, NULL);
- }
+ contacts_bind_to_aliases_changed (c->connection);
for (i = 0; i < c->contacts->len; i++)
{
@@ -1324,6 +1343,28 @@ contacts_get_aliases (ContactsContext *c)
static void
+contact_maybe_set_simple_presence (TpContact *contact,
+ GValueArray *presence)
+{
+ if (contact == NULL || presence == NULL)
+ return;
+
+ contact->priv->has_features |= CONTACT_FEATURE_FLAG_PRESENCE;
+ contact->priv->presence_type = g_value_get_uint (presence->values + 0);
+ g_free (contact->priv->presence_status);
+ contact->priv->presence_status = g_value_dup_string (
+ presence->values + 1);
+ g_free (contact->priv->presence_message);
+ contact->priv->presence_message = g_value_dup_string (
+ presence->values + 2);
+
+ g_object_notify ((GObject *) contact, "presence-type");
+ g_object_notify ((GObject *) contact, "presence-status");
+ g_object_notify ((GObject *) contact, "presence-message");
+}
+
+
+static void
contacts_presences_changed (TpConnection *connection,
GHashTable *presences,
gpointer user_data G_GNUC_UNUSED,
@@ -1338,23 +1379,8 @@ contacts_presences_changed (TpConnection *connection,
{
TpContact *contact = _tp_connection_lookup_contact (connection,
GPOINTER_TO_UINT (key));
- GValueArray *presence = value;
-
- if (contact == NULL)
- continue;
-
- contact->priv->has_features |= CONTACT_FEATURE_FLAG_PRESENCE;
- contact->priv->presence_type = g_value_get_uint (presence->values + 0);
- g_free (contact->priv->presence_status);
- contact->priv->presence_status = g_value_dup_string (
- presence->values + 1);
- g_free (contact->priv->presence_message);
- contact->priv->presence_message = g_value_dup_string (
- presence->values + 2);
-
- g_object_notify ((GObject *) contact, "presence-type");
- g_object_notify ((GObject *) contact, "presence-status");
- g_object_notify ((GObject *) contact, "presence-message");
+
+ contact_maybe_set_simple_presence (contact, value);
}
}
@@ -1384,19 +1410,25 @@ contacts_got_simple_presence (TpConnection *connection,
static void
+contacts_bind_to_presences_changed (TpConnection *connection)
+{
+ if (!connection->priv->tracking_presences_changed)
+ {
+ connection->priv->tracking_presences_changed = TRUE;
+
+ tp_cli_connection_interface_simple_presence_connect_to_presences_changed
+ (connection, contacts_presences_changed, NULL, NULL, NULL, NULL);
+ }
+}
+
+static void
contacts_get_simple_presence (ContactsContext *c)
{
guint i;
g_assert (c->handles->len == c->contacts->len);
- if (!c->connection->priv->tracking_presences_changed)
- {
- c->connection->priv->tracking_presences_changed = TRUE;
-
- tp_cli_connection_interface_simple_presence_connect_to_presences_changed
- (c->connection, contacts_presences_changed, NULL, NULL, NULL, NULL);
- }
+ contacts_bind_to_presences_changed (c->connection);
for (i = 0; i < c->contacts->len; i++)
{
@@ -1475,19 +1507,26 @@ contacts_got_known_avatar_tokens (TpConnection *connection,
static void
+contacts_bind_to_avatar_updated (TpConnection *connection)
+{
+ if (!connection->priv->tracking_avatar_updated)
+ {
+ connection->priv->tracking_avatar_updated = TRUE;
+
+ tp_cli_connection_interface_avatars_connect_to_avatar_updated
+ (connection, contacts_avatar_updated, NULL, NULL, NULL, NULL);
+ }
+}
+
+
+static void
contacts_get_avatar_tokens (ContactsContext *c)
{
guint i;
g_assert (c->handles->len == c->contacts->len);
- if (!c->connection->priv->tracking_avatar_updated)
- {
- c->connection->priv->tracking_avatar_updated = TRUE;
-
- tp_cli_connection_interface_avatars_connect_to_avatar_updated
- (c->connection, contacts_avatar_updated, NULL, NULL, NULL, NULL);
- }
+ contacts_bind_to_avatar_updated (c->connection);
for (i = 0; i < c->contacts->len; i++)
{
@@ -1547,6 +1586,214 @@ contacts_context_queue_features (ContactsContext *context,
}
+static void
+contacts_got_attributes (TpConnection *connection,
+ GHashTable *attributes,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ ContactsContext *c = user_data;
+ guint i;
+
+ if (error != NULL)
+ {
+ contacts_context_fail (c, error);
+ return;
+ }
+
+ i = 0;
+
+ if (c->signature == CB_BY_HANDLE)
+ {
+ g_assert (c->contacts->len == 0);
+
+ while (i < c->handles->len)
+ {
+ TpHandle handle = g_array_index (c->handles, guint, i);
+ GHashTable *asv = g_hash_table_lookup (attributes,
+ GUINT_TO_POINTER (handle));
+
+ if (asv == NULL)
+ {
+ /* not in the hash table => not valid */
+ g_array_append_val (c->invalid, handle);
+ g_array_remove_index_fast (c->handles, i);
+ }
+ else
+ {
+ TpContact *contact = NULL;
+ guint j;
+
+ /* we might already have consumed the only reference we have to
+ * the handle - if we have, we must recycle the same object
+ * rather than calling tp_contact_ensure again */
+ for (j = 0; j < i; j++)
+ {
+ if (handle == g_array_index (c->handles, guint, j))
+ {
+ contact = g_object_ref (g_ptr_array_index (c->contacts,
+ j));
+ }
+ }
+
+ if (contact == NULL)
+ contact = tp_contact_ensure (connection, handle);
+
+ g_ptr_array_add (c->contacts, contact);
+
+ /* save the contact and move on to the next handle */
+ i++;
+ }
+ }
+ }
+
+ g_assert (c->contacts->len == c->handles->len);
+
+ for (i = 0; i < c->handles->len; i++)
+ {
+ TpContact *contact = g_ptr_array_index (c->contacts, i);
+ const gchar *s;
+ gpointer boxed;
+ GHashTable *asv = g_hash_table_lookup (attributes,
+ GUINT_TO_POINTER (contact->priv->handle));
+
+ if (asv == NULL)
+ {
+ GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
+ "We hold a ref to handle #%u but it appears to be invalid",
+ contact->priv->handle);
+
+ contacts_context_fail (c, e);
+ g_error_free (e);
+ return;
+ }
+
+ /* set up the contact with its attributes */
+
+ s = tp_asv_get_string (asv, TP_IFACE_CONNECTION "/contact-id");
+
+ if (s == NULL)
+ {
+ GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
+ "Connection manager %s is broken: contact #%u in the "
+ "GetContactAttributes result has no contact-id",
+ tp_proxy_get_bus_name (connection), contact->priv->handle);
+
+ contacts_context_fail (c, e);
+ g_error_free (e);
+ return;
+ }
+
+ if (contact->priv->identifier == NULL)
+ {
+ contact->priv->identifier = g_strdup (s);
+ }
+ else if (tp_strdiff (contact->priv->identifier, s))
+ {
+ GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
+ "Connection manager %s is broken: contact #%u identifier "
+ "changed from %s to %s",
+ tp_proxy_get_bus_name (connection), contact->priv->handle,
+ contact->priv->identifier, s);
+
+ contacts_context_fail (c, e);
+ g_error_free (e);
+ return;
+ }
+
+ s = tp_asv_get_string (asv,
+ TP_IFACE_CONNECTION_INTERFACE_ALIASING "/alias");
+
+ if (s != NULL)
+ {
+ contact->priv->has_features |= CONTACT_FEATURE_FLAG_ALIAS;
+ g_free (contact->priv->alias);
+ contact->priv->alias = g_strdup (s);
+ g_object_notify ((GObject *) contact, "alias");
+ }
+
+ s = tp_asv_get_string (asv,
+ TP_IFACE_CONNECTION_INTERFACE_AVATARS "/token");
+
+ if (s != NULL)
+ contacts_avatar_updated (connection, contact->priv->handle, s,
+ NULL, NULL);
+
+ boxed = tp_asv_get_boxed (asv,
+ TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE "/presence",
+ TP_STRUCT_TYPE_SIMPLE_PRESENCE);
+ contact_maybe_set_simple_presence (contact, boxed);
+
+ /* FIXME: TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES "/caps" */
+ }
+
+ contacts_context_continue (c);
+}
+
+
+static void
+contacts_get_attributes (ContactsContext *context)
+{
+ GArray *contact_attribute_interfaces =
+ context->connection->priv->contact_attribute_interfaces;
+ GPtrArray *array = g_ptr_array_sized_new (
+ contact_attribute_interfaces->len);
+ const gchar **supported_interfaces;
+ guint i;
+
+ g_assert (tp_proxy_has_interface_by_id (context->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS));
+ g_assert (contact_attribute_interfaces != NULL);
+
+ for (i = 0; i < contact_attribute_interfaces->len; i++)
+ {
+ GQuark q = g_array_index (contact_attribute_interfaces, GQuark, i);
+
+ if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING)
+ {
+ if ((context->wanted & CONTACT_FEATURE_FLAG_ALIAS) != 0)
+ {
+ g_ptr_array_add (array,
+ TP_IFACE_CONNECTION_INTERFACE_ALIASING);
+ contacts_bind_to_aliases_changed (context->connection);
+ }
+ }
+ else if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS)
+ {
+ if ((context->wanted & CONTACT_FEATURE_FLAG_AVATAR_TOKEN) != 0)
+ {
+ g_ptr_array_add (array,
+ TP_IFACE_CONNECTION_INTERFACE_AVATARS);
+ contacts_bind_to_avatar_updated (context->connection);
+ }
+ }
+ else if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE)
+ {
+ if ((context->wanted & CONTACT_FEATURE_FLAG_PRESENCE) != 0)
+ {
+ g_ptr_array_add (array,
+ TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE);
+ contacts_bind_to_presences_changed (context->connection);
+ }
+ }
+ }
+
+ g_ptr_array_add (array, NULL);
+ supported_interfaces = (const gchar **) g_ptr_array_free (array, FALSE);
+
+ /* we want to hold the handles if and only if the call is by_handle -
+ * for the other modes, we already have handles */
+ context->refcount++;
+ tp_connection_get_contact_attributes (context->connection, -1,
+ context->handles->len, (const TpHandle *) context->handles->data,
+ supported_interfaces, (context->signature == CB_BY_HANDLE),
+ contacts_got_attributes,
+ context, contacts_context_unref, context->weak_object);
+ g_free (supported_interfaces);
+}
+
+
/**
* tp_connection_get_contacts_by_handle:
* @self: A connection, which must be ready (#TpConnection:connection-ready
@@ -1607,6 +1854,23 @@ tp_connection_get_contacts_by_handle (TpConnection *self,
g_array_append_vals (context->handles, handles, n_handles);
+ if (tp_proxy_has_interface_by_id (self,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS))
+ {
+ /* we support the Contacts interface, so we can hold the handles and
+ * simultaneously inspect them. After that, we'll fill in any
+ * features that are necessary (this becomes a no-op if Contacts
+ * gave us everything). */
+ contacts_get_attributes (context);
+ contacts_context_queue_features (context, feature_flags);
+ /* we have one excess ref to the context because we create it,
+ * and then contacts_get_attributes refs it */
+ contacts_context_unref (context);
+ return;
+ }
+
+ /* if we haven't already returned, we're on the slow path */
+
/* Before we return anything we'll want to inspect the handles */
g_queue_push_head (&context->todo, contacts_inspect);
@@ -1698,6 +1962,12 @@ tp_connection_upgrade_contacts (TpConnection *self,
g_assert (context->handles->len == n_contacts);
+ if (tp_proxy_has_interface_by_id (self,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS))
+ {
+ g_queue_push_head (&context->todo, contacts_get_attributes);
+ }
+
contacts_context_queue_features (context, feature_flags);
/* use an idle to make sure the callback is called after we return,
@@ -1902,7 +2172,17 @@ tp_connection_get_contacts_by_id (TpConnection *self,
g_ptr_array_add (context->request_ids, NULL);
/* set up the queue of feature introspection */
- g_queue_push_head (&context->todo, contacts_inspect);
+
+ if (tp_proxy_has_interface_by_id (self,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS))
+ {
+ g_queue_push_head (&context->todo, contacts_get_attributes);
+ }
+ else
+ {
+ g_queue_push_head (&context->todo, contacts_inspect);
+ }
+
contacts_context_queue_features (context, feature_flags);
/* but first, we need to get the handles in the first place */
diff --git a/telepathy-glib/contacts-mixin.h b/telepathy-glib/contacts-mixin.h
index f374932f9..2aad0cc99 100644
--- a/telepathy-glib/contacts-mixin.h
+++ b/telepathy-glib/contacts-mixin.h
@@ -76,16 +76,14 @@ struct _TpContactsMixin {
#define TP_CONTACTS_MIXIN_CLASS_OFFSET_QUARK \
(tp_contacts_mixin_class_get_offset_quark ())
#define TP_CONTACTS_MIXIN_CLASS_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
- TP_CONTACTS_MIXIN_CLASS_OFFSET_QUARK)))
+ tp_mixin_class_get_offset (o, TP_CONTACTS_MIXIN_CLASS_OFFSET_QUARK)
#define TP_CONTACTS_MIXIN_CLASS(o) \
((TpContactsMixinClass *) tp_mixin_offset_cast (o, \
TP_CONTACTS_MIXIN_CLASS_OFFSET (o)))
#define TP_CONTACTS_MIXIN_OFFSET_QUARK (tp_contacts_mixin_get_offset_quark ())
#define TP_CONTACTS_MIXIN_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \
- TP_CONTACTS_MIXIN_OFFSET_QUARK)))
+ tp_mixin_instance_get_offset (o, TP_CONTACTS_MIXIN_OFFSET_QUARK)
#define TP_CONTACTS_MIXIN(o) \
((TpContactsMixin *) tp_mixin_offset_cast (o, TP_CONTACTS_MIXIN_OFFSET (o)))
diff --git a/telepathy-glib/group-mixin.h b/telepathy-glib/group-mixin.h
index eda9542a3..0ed90b563 100644
--- a/telepathy-glib/group-mixin.h
+++ b/telepathy-glib/group-mixin.h
@@ -148,15 +148,14 @@ struct _TpGroupMixin {
#define TP_GROUP_MIXIN_CLASS_OFFSET_QUARK \
(tp_group_mixin_class_get_offset_quark ())
#define TP_GROUP_MIXIN_CLASS_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
- TP_GROUP_MIXIN_CLASS_OFFSET_QUARK)))
+ tp_mixin_class_get_offset (o, TP_GROUP_MIXIN_CLASS_OFFSET_QUARK)
#define TP_GROUP_MIXIN_CLASS(o) \
((TpGroupMixinClass *) tp_mixin_offset_cast (o, \
TP_GROUP_MIXIN_CLASS_OFFSET (o)))
#define TP_GROUP_MIXIN_OFFSET_QUARK (tp_group_mixin_get_offset_quark ())
-#define TP_GROUP_MIXIN_OFFSET(o) (GPOINTER_TO_UINT (g_type_get_qdata (\
- G_OBJECT_TYPE (o), TP_GROUP_MIXIN_OFFSET_QUARK)))
+#define TP_GROUP_MIXIN_OFFSET(o) \
+ tp_mixin_instance_get_offset (o, TP_GROUP_MIXIN_OFFSET_QUARK)
#define TP_GROUP_MIXIN(o) ((TpGroupMixin *) tp_mixin_offset_cast (o, \
TP_GROUP_MIXIN_OFFSET(o)))
diff --git a/telepathy-glib/presence-mixin.h b/telepathy-glib/presence-mixin.h
index 37ad7ded9..93ad88b58 100644
--- a/telepathy-glib/presence-mixin.h
+++ b/telepathy-glib/presence-mixin.h
@@ -225,16 +225,14 @@ struct _TpPresenceMixin {
#define TP_PRESENCE_MIXIN_CLASS_OFFSET_QUARK \
(tp_presence_mixin_class_get_offset_quark ())
#define TP_PRESENCE_MIXIN_CLASS_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
- TP_PRESENCE_MIXIN_CLASS_OFFSET_QUARK)))
+ tp_mixin_class_get_offset (o, TP_PRESENCE_MIXIN_CLASS_OFFSET_QUARK)
#define TP_PRESENCE_MIXIN_CLASS(o) \
((TpPresenceMixinClass *) tp_mixin_offset_cast (o, \
TP_PRESENCE_MIXIN_CLASS_OFFSET (o)))
#define TP_PRESENCE_MIXIN_OFFSET_QUARK (tp_presence_mixin_get_offset_quark ())
#define TP_PRESENCE_MIXIN_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \
- TP_PRESENCE_MIXIN_OFFSET_QUARK)))
+ tp_mixin_instance_get_offset (o, TP_PRESENCE_MIXIN_OFFSET_QUARK)
#define TP_PRESENCE_MIXIN(o) \
((TpPresenceMixin *) tp_mixin_offset_cast (o, TP_PRESENCE_MIXIN_OFFSET (o)))
diff --git a/telepathy-glib/properties-mixin.h b/telepathy-glib/properties-mixin.h
index a6069b397..e3faa578a 100644
--- a/telepathy-glib/properties-mixin.h
+++ b/telepathy-glib/properties-mixin.h
@@ -149,8 +149,7 @@ typedef struct _TpPropertiesMixin TpPropertiesMixin;
#define TP_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK \
(tp_properties_mixin_class_get_offset_quark ())
#define TP_PROPERTIES_MIXIN_CLASS_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
- TP_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK)))
+ tp_mixin_class_get_offset (o, TP_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK)
#define TP_PROPERTIES_MIXIN_CLASS(o) \
((TpPropertiesMixinClass *) tp_mixin_offset_cast (o,\
TP_PROPERTIES_MIXIN_CLASS_OFFSET (o)))
@@ -158,8 +157,7 @@ typedef struct _TpPropertiesMixin TpPropertiesMixin;
#define TP_PROPERTIES_MIXIN_OFFSET_QUARK \
(tp_properties_mixin_get_offset_quark ())
#define TP_PROPERTIES_MIXIN_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \
- TP_PROPERTIES_MIXIN_OFFSET_QUARK)))
+ tp_mixin_instance_get_offset (o, TP_PROPERTIES_MIXIN_OFFSET_QUARK)
#define TP_PROPERTIES_MIXIN(o) \
((TpPropertiesMixin *) tp_mixin_offset_cast (o, \
TP_PROPERTIES_MIXIN_OFFSET (o)))
diff --git a/telepathy-glib/text-mixin.h b/telepathy-glib/text-mixin.h
index 60f25524e..297087ebd 100644
--- a/telepathy-glib/text-mixin.h
+++ b/telepathy-glib/text-mixin.h
@@ -64,16 +64,14 @@ struct _TpTextMixin {
#define TP_TEXT_MIXIN_CLASS_OFFSET_QUARK \
(tp_text_mixin_class_get_offset_quark ())
#define TP_TEXT_MIXIN_CLASS_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
- TP_TEXT_MIXIN_CLASS_OFFSET_QUARK)))
+ tp_mixin_class_get_offset (o, TP_TEXT_MIXIN_CLASS_OFFSET_QUARK)
#define TP_TEXT_MIXIN_CLASS(o) \
((TpTextMixinClass *) tp_mixin_offset_cast (o, \
TP_TEXT_MIXIN_CLASS_OFFSET (o)))
#define TP_TEXT_MIXIN_OFFSET_QUARK (tp_text_mixin_get_offset_quark ())
#define TP_TEXT_MIXIN_OFFSET(o) \
- (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \
- TP_TEXT_MIXIN_OFFSET_QUARK)))
+ tp_mixin_instance_get_offset (o, TP_TEXT_MIXIN_OFFSET_QUARK)
#define TP_TEXT_MIXIN(o) \
((TpTextMixin *) tp_mixin_offset_cast (o, TP_TEXT_MIXIN_OFFSET (o)))
diff --git a/telepathy-glib/util.c b/telepathy-glib/util.c
index f1da86a69..f05963ac4 100644
--- a/telepathy-glib/util.c
+++ b/telepathy-glib/util.c
@@ -211,6 +211,73 @@ tp_mixin_offset_cast (gpointer instance, guint offset)
return ((guchar *) instance + offset);
}
+
+/**
+ * tp_mixin_instance_get_offset:
+ * @instance: A pointer to a GObject-derived instance structure
+ * @quark: A quark that was used to store the offset with g_type_set_qdata()
+ *
+ * If the type of @instance, or any of its ancestor types, has had an offset
+ * attached using qdata with the given @quark, return that offset. If not,
+ * this indicates a programming error and results are undefined.
+ *
+ * This is used to implement the telepathy-glib mixin classes.
+ *
+ * Returns: the offset of the mixin
+ */
+guint
+tp_mixin_instance_get_offset (gpointer instance,
+ GQuark quark)
+{
+ GType t;
+
+ for (t = G_OBJECT_TYPE (instance);
+ t != 0;
+ t = g_type_parent (t))
+ {
+ gpointer qdata = g_type_get_qdata (t, quark);
+
+ if (qdata != NULL)
+ return GPOINTER_TO_UINT (qdata);
+ }
+
+ g_return_val_if_reached (0);
+}
+
+
+/**
+ * tp_mixin_class_get_offset:
+ * @klass: A pointer to a GObjectClass-derived class structure
+ * @quark: A quark that was used to store the offset with g_type_set_qdata()
+ *
+ * If the type of @klass, or any of its ancestor types, has had an offset
+ * attached using qdata with the given @quark, return that offset. If not,
+ * this indicates a programming error and results are undefined.
+ *
+ * This is used to implement the telepathy-glib mixin classes.
+ *
+ * Returns: the offset of the mixin class
+ */
+guint
+tp_mixin_class_get_offset (gpointer klass,
+ GQuark quark)
+{
+ GType t;
+
+ for (t = G_OBJECT_CLASS_TYPE (klass);
+ t != 0;
+ t = g_type_parent (t))
+ {
+ gpointer qdata = g_type_get_qdata (t, quark);
+
+ if (qdata != NULL)
+ return GPOINTER_TO_UINT (qdata);
+ }
+
+ g_return_val_if_reached (0);
+}
+
+
static inline gboolean
_esc_ident_bad (gchar c, gboolean is_first)
{
diff --git a/telepathy-glib/util.h b/telepathy-glib/util.h
index bb659783a..dcc07638a 100644
--- a/telepathy-glib/util.h
+++ b/telepathy-glib/util.h
@@ -41,6 +41,8 @@ void tp_g_hash_table_update (GHashTable *target, GHashTable *source,
gboolean tp_strdiff (const gchar *left, const gchar *right);
gpointer tp_mixin_offset_cast (gpointer instance, guint offset);
+guint tp_mixin_instance_get_offset (gpointer instance, GQuark quark);
+guint tp_mixin_class_get_offset (gpointer klass, GQuark quark);
gchar *tp_escape_as_identifier (const gchar *name);
diff --git a/tests/dbus/contacts.c b/tests/dbus/contacts.c
index 47da72b35..b2399a8b3 100644
--- a/tests/dbus/contacts.c
+++ b/tests/dbus/contacts.c
@@ -872,12 +872,12 @@ main (int argc,
char **argv)
{
TpDBusDaemon *dbus;
- ContactsConnection *service_conn;
- TpBaseConnection *service_conn_as_base;
- gchar *name;
- gchar *conn_path;
+ ContactsConnection *service_conn, *legacy_service_conn;
+ TpBaseConnection *service_conn_as_base, *legacy_service_conn_as_base;
+ gchar *name, *legacy_name;
+ gchar *conn_path, *legacy_conn_path;
GError *error = NULL;
- TpConnection *client_conn;
+ TpConnection *client_conn, *legacy_client_conn;
/* Setup */
@@ -894,10 +894,23 @@ main (int argc,
MYASSERT (service_conn != NULL, "");
MYASSERT (service_conn_as_base != NULL, "");
+ legacy_service_conn = CONTACTS_CONNECTION (g_object_new (
+ LEGACY_CONTACTS_TYPE_CONNECTION,
+ "account", "legacy@example.com",
+ "protocol", "simple",
+ NULL));
+ legacy_service_conn_as_base = TP_BASE_CONNECTION (legacy_service_conn);
+ MYASSERT (legacy_service_conn != NULL, "");
+ MYASSERT (legacy_service_conn_as_base != NULL, "");
+
MYASSERT (tp_base_connection_register (service_conn_as_base, "simple",
&name, &conn_path, &error), "");
MYASSERT_NO_ERROR (error);
+ MYASSERT (tp_base_connection_register (legacy_service_conn_as_base, "simple",
+ &legacy_name, &legacy_conn_path, &error), "");
+ MYASSERT_NO_ERROR (error);
+
client_conn = tp_connection_new (dbus, name, conn_path, &error);
MYASSERT (client_conn != NULL, "");
MYASSERT_NO_ERROR (error);
@@ -905,6 +918,14 @@ main (int argc,
"");
MYASSERT_NO_ERROR (error);
+ legacy_client_conn = tp_connection_new (dbus, legacy_name, legacy_conn_path,
+ &error);
+ MYASSERT (legacy_client_conn != NULL, "");
+ MYASSERT_NO_ERROR (error);
+ MYASSERT (tp_connection_run_until_ready (legacy_client_conn, TRUE, &error,
+ NULL), "");
+ MYASSERT_NO_ERROR (error);
+
/* Tests */
test_by_handle (service_conn, client_conn);
@@ -913,17 +934,34 @@ main (int argc,
test_upgrade (service_conn, client_conn);
test_by_id (client_conn);
+ test_by_handle (legacy_service_conn, legacy_client_conn);
+ test_no_features (legacy_service_conn, legacy_client_conn);
+ test_features (legacy_service_conn, legacy_client_conn);
+ test_upgrade (legacy_service_conn, legacy_client_conn);
+ test_by_id (legacy_client_conn);
+
/* Teardown */
MYASSERT (tp_cli_connection_run_disconnect (client_conn, -1, &error, NULL),
"");
MYASSERT_NO_ERROR (error);
+ g_object_unref (client_conn);
+
+ MYASSERT (tp_cli_connection_run_disconnect (legacy_client_conn, -1, &error,
+ NULL), "");
+ MYASSERT_NO_ERROR (error);
+ g_object_unref (legacy_client_conn);
service_conn_as_base = NULL;
g_object_unref (service_conn);
g_free (name);
g_free (conn_path);
+ legacy_service_conn_as_base = NULL;
+ g_object_unref (legacy_service_conn);
+ g_free (legacy_name);
+ g_free (legacy_conn_path);
+
g_object_unref (dbus);
return fail;