[Telepathy-commits] [telepathy-glib/master] TpContact: hook up factory functions etc.

Simon McVittie simon.mcvittie at collabora.co.uk
Tue Oct 28 10:47:10 PDT 2008


---
 docs/reference/telepathy-glib-sections.txt |    2 +
 telepathy-glib/connection-internal.h       |   11 +
 telepathy-glib/connection.c                |   68 ++++
 telepathy-glib/contact.c                   |  556 ++++++++++++++++++++++++++++
 telepathy-glib/contact.h                   |   12 +
 5 files changed, 649 insertions(+), 0 deletions(-)

diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index 80dd4fc..5503b56 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -2395,6 +2395,8 @@ tp_cli_media_stream_handler_signal_callback_set_stream_held
 <TITLE>contact</TITLE>
 TpContact
 TpContactFeature
+TpConnectionContactsByHandleCb
+tp_connection_get_contacts_by_handle
 tp_contact_get_alias
 tp_contact_get_avatar_token
 tp_contact_get_connection
diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h
index eb77464..00f274d 100644
--- a/telepathy-glib/connection-internal.h
+++ b/telepathy-glib/connection-internal.h
@@ -23,6 +23,7 @@
 #define TP_CONNECTION_INTERNAL_H
 
 #include <telepathy-glib/connection.h>
+#include <telepathy-glib/contact.h>
 
 G_BEGIN_DECLS
 
@@ -32,6 +33,16 @@ const GArray *_tp_connection_get_contact_attribute_interfaces (
 void _tp_connection_init_handle_refs (TpConnection *self);
 void _tp_connection_clean_up_handle_refs (TpConnection *self);
 
+void _tp_connection_add_contact (TpConnection *self, TpHandle handle,
+    TpContact *contact);
+void _tp_connection_remove_contact (TpConnection *self, TpHandle handle,
+    TpContact *contact);
+TpContact *_tp_connection_lookup_contact (TpConnection *self, TpHandle handle);
+
+/* Actually implemented in contact.c, but having a contact-internal header
+ * just for this would be overkill */
+void _tp_contact_connection_invalidated (TpContact *contact);
+
 G_END_DECLS
 
 #endif
diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c
index aba7f9f..3271ab1 100644
--- a/telepathy-glib/connection.c
+++ b/telepathy-glib/connection.c
@@ -124,6 +124,9 @@ struct _TpConnectionPrivate {
     /* GArray of GQuark */
     GArray *contact_attribute_interfaces;
 
+    /* TpHandle => weak ref to TpContact */
+    GHashTable *contacts;
+
     unsigned ready:1;
 };
 
@@ -479,6 +482,7 @@ tp_connection_init (TpConnection *self)
 
   self->priv->status = TP_UNKNOWN_CONNECTION_STATUS;
   self->priv->status_reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED;
+  self->priv->contacts = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
@@ -504,11 +508,31 @@ tp_connection_finalize (GObject *object)
   ((GObjectClass *) tp_connection_parent_class)->finalize (object);
 }
 
+
+static void
+contact_notify_invalidated (gpointer k G_GNUC_UNUSED,
+                            gpointer v,
+                            gpointer d G_GNUC_UNUSED)
+{
+  _tp_contact_connection_invalidated (v);
+}
+
+
 static void
 tp_connection_dispose (GObject *object)
 {
+  TpConnection *self = TP_CONNECTION (object);
+
   DEBUG ("%p", object);
 
+  if (self->priv->contacts != NULL)
+    {
+      g_hash_table_foreach (self->priv->contacts, contact_notify_invalidated,
+          NULL);
+      g_hash_table_destroy (self->priv->contacts);
+      self->priv->contacts = NULL;
+    }
+
   ((GObjectClass *) tp_connection_parent_class)->dispose (object);
 }
 
@@ -1209,6 +1233,50 @@ _tp_connection_get_contact_attribute_interfaces (TpConnection *self)
 }
 
 
+TpContact *
+_tp_connection_lookup_contact (TpConnection *self,
+                               TpHandle handle)
+{
+  g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
+
+  return g_hash_table_lookup (self->priv->contacts, GUINT_TO_POINTER (handle));
+}
+
+
+/* this could be done with proper weak references, but we know that every
+ * connection will weakly reference all its contacts, so we can just do this
+ * explicitly in tp_contact_dispose */
+void
+_tp_connection_remove_contact (TpConnection *self,
+                               TpHandle handle,
+                               TpContact *contact)
+{
+  TpContact *mine;
+
+  g_return_if_fail (TP_IS_CONNECTION (self));
+  g_return_if_fail (TP_IS_CONTACT (contact));
+
+  mine = g_hash_table_lookup (self->priv->contacts, GUINT_TO_POINTER (handle));
+  g_return_if_fail (mine == contact);
+  g_hash_table_remove (self->priv->contacts, GUINT_TO_POINTER (handle));
+}
+
+
+void
+_tp_connection_add_contact (TpConnection *self,
+                            TpHandle handle,
+                            TpContact *contact)
+{
+  g_return_if_fail (TP_IS_CONNECTION (self));
+  g_return_if_fail (TP_IS_CONTACT (contact));
+  g_return_if_fail (g_hash_table_lookup (self->priv->contacts,
+        GUINT_TO_POINTER (handle)) == NULL);
+
+  g_hash_table_insert (self->priv->contacts, GUINT_TO_POINTER (handle),
+      contact);
+}
+
+
 /**
  * tp_connection_is_ready:
  * @self: a connection
diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c
index 3d8a267..5aa4ff7 100644
--- a/telepathy-glib/contact.c
+++ b/telepathy-glib/contact.c
@@ -20,6 +20,10 @@
 
 #include <telepathy-glib/contact.h>
 
+#include <telepathy-glib/util.h>
+
+#include "telepathy-glib/connection-internal.h"
+
 
 /**
  * SECTION:contact
@@ -291,6 +295,17 @@ tp_contact_get_presence_message (TpContact *self)
 }
 
 
+void
+_tp_contact_connection_invalidated (TpContact *contact)
+{
+  /* The connection has gone away, so we no longer have a meaningful handle,
+   * and will never have one again. */
+  g_assert (contact->priv->handle != 0);
+  contact->priv->handle = 0;
+  g_object_notify ((GObject *) contact, "handle");
+}
+
+
 static void
 tp_contact_dispose (GObject *object)
 {
@@ -300,6 +315,8 @@ tp_contact_dispose (GObject *object)
     {
       g_assert (self->priv->connection != NULL);
 
+      _tp_connection_remove_contact (self->priv->connection,
+          self->priv->handle, self);
       tp_connection_unref_handles (self->priv->connection,
           TP_HANDLE_TYPE_CONTACT, 1, &self->priv->handle);
 
@@ -540,9 +557,548 @@ tp_contact_class_init (TpContactClass *klass)
 }
 
 
+/* Consumes one reference to @handle. */
+static TpContact *
+tp_contact_ensure (TpConnection *connection,
+                   TpHandle handle)
+{
+  TpContact *self = _tp_connection_lookup_contact (connection, handle);
+
+  if (self != NULL)
+    {
+      g_assert (self->priv->handle == handle);
+
+      /* we have one ref to this handle more than we need, so consume it */
+      tp_connection_unref_handles (self->priv->connection,
+          TP_HANDLE_TYPE_CONTACT, 1, &self->priv->handle);
+
+      return g_object_ref (self);
+    }
+
+  self = TP_CONTACT (g_object_new (TP_TYPE_CONTACT, NULL));
+
+  self->priv->handle = handle;
+  _tp_connection_add_contact (connection, handle, self);
+  self->priv->connection = g_object_ref (connection);
+
+  return self;
+}
+
+
 static void
 tp_contact_init (TpContact *self)
 {
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_CONTACT,
       TpContactPrivate);
 }
+
+/* Here's what needs to happen when we create contacts, in pseudocode:
+ *
+ * if we started from IDs:
+ *    request all the handles
+ *    if it fails with NotAvailable, at least one of them is invalid:
+ *        request the first handle
+ *        request the second handle
+ *        ...
+ *    else if it fails for any other reason:
+ *        abort
+ *
+ * (by now, ->handles is populated)
+ *
+ * if contact attributes are supported:
+ *    (the fast path)
+ *    get the contact attributes (and simultaneously hold the handles)
+ *    if it failed, goto abort
+ *    if none are missing, goto done
+ * else if we started from handles:
+ *    try to hold all the handles
+ *    if it fails with InvalidHandle:
+ *        hold the first handle
+ *        hold the second handle
+ *        ...
+ *    else if it fails for any other reason:
+ *        abort
+ *
+ * (the slow path)
+ * get the avatar tokens if we want them - if it fails, goto abort
+ * get the aliases if we want them - if it fails, goto abort
+ * ...
+ *
+ * Most of this is actually implemented by popping callbacks (ContactsProc)
+ * from a queue.
+ */
+
+typedef struct _ContactsContext ContactsContext;
+typedef void (*ContactsProc) (ContactsContext *self);
+
+struct _ContactsContext {
+    gsize refcount;
+
+    /* owned */
+    TpConnection *connection;
+    /* array of owned TpContact; preallocated but empty until handles have
+     * been held or requested */
+    GPtrArray *contacts;
+    /* array of handles; empty until RequestHandles has returned, if we
+     * started from IDs */
+    GArray *handles;
+    /* array of IDs; empty until handles have been inspected, if we started
+     * from handles */
+    GPtrArray *ids;
+    /* array of handles; empty until RequestHandles has returned, if we
+     * started from IDs */
+    GArray *invalid;
+
+    /* features we need before this request can finish */
+    ContactFeatureFlags wanted;
+
+    /* callback for when we've finished, plus the usual misc */
+    TpConnectionContactsByHandleCb callback;
+    gpointer user_data;
+    GDestroyNotify destroy;
+    GObject *weak_object;
+
+    /* queue of ContactsProc */
+    GQueue todo;
+
+    /* index into handles or ids, only used when the first HoldHandles call
+     * failed with InvalidHandle, or the RequestHandles call failed with
+     * NotAvailable */
+    guint next_index;
+};
+
+
+static ContactsContext *
+contacts_context_new (TpConnection *connection,
+                      guint n_contacts,
+                      ContactFeatureFlags want_features,
+                      TpConnectionContactsByHandleCb callback,
+                      gpointer user_data,
+                      GDestroyNotify destroy,
+                      GObject *weak_object)
+{
+  ContactsContext *c = g_slice_new0 (ContactsContext);
+
+  c->refcount = 1;
+  c->connection = g_object_ref (connection);
+  c->contacts = g_ptr_array_sized_new (n_contacts);
+  c->handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), n_contacts);
+  c->invalid = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), n_contacts);
+  c->ids = g_ptr_array_sized_new (n_contacts);
+
+  c->wanted = want_features;
+  c->callback = callback;
+  c->user_data = user_data;
+  c->destroy = destroy;
+  c->weak_object = weak_object;
+
+  /* This code (and lots of telepathy-glib, really) won't work if this
+   * assertion fails, because we put function pointers in a GQueue. If anyone
+   * cares about platforms where this fails, fixing this would involve
+   * slice-allocating sizeof (GCallback) bytes repeatedly, and putting *those*
+   * in the queue. */
+  g_assert (sizeof (GCallback) == sizeof (gpointer));
+  g_queue_init (&c->todo);
+
+  return c;
+}
+
+
+static void
+contacts_context_unref (gpointer p)
+{
+  ContactsContext *c = p;
+
+  if ((--c->refcount) > 0)
+    return;
+
+  g_assert (c->connection != NULL);
+  g_object_unref (c->connection);
+  c->connection = NULL;
+
+  g_queue_clear (&c->todo);
+
+  g_assert (c->contacts != NULL);
+  g_ptr_array_foreach (c->contacts, (GFunc) g_object_unref, NULL);
+  g_ptr_array_free (c->contacts, TRUE);
+  c->contacts = NULL;
+
+  g_assert (c->handles != NULL);
+  g_array_free (c->handles, TRUE);
+  c->handles = NULL;
+
+  g_assert (c->invalid != NULL);
+  g_array_free (c->invalid, TRUE);
+  c->invalid = NULL;
+
+  if (c->destroy != NULL)
+    c->destroy (c->user_data);
+
+  c->destroy = NULL;
+  c->user_data = NULL;
+
+  c->weak_object = NULL;
+
+  g_slice_free (ContactsContext, c);
+}
+
+
+static void
+contacts_context_fail (ContactsContext *c,
+                       const GError *error)
+{
+  c->callback (c->connection, 0, NULL, 0, NULL, error, c->user_data,
+      c->weak_object);
+}
+
+
+/**
+ * TpConnectionContactsByHandleCb:
+ * @connection: The connection
+ * @n_contacts: The number of TpContact objects successfully created
+ *  (one per valid handle), or 0 on unrecoverable errors
+ * @contacts: An array of @n_contacts TpContact objects (this callback is
+ *  given one reference to each of these objects, and must unref any that
+ *  are not needed with g_object_unref()), or %NULL on unrecoverable errors
+ * @n_invalid: The number of invalid handles that were passed to
+ *  tp_connection_get_contacts_by_handle(), or 0 on unrecoverable errors
+ * @invalid: An array of @n_invalid handles that were passed to
+ *  tp_connection_get_contacts_by_handle() but turned out to be invalid,
+ *  or %NULL on unrecoverable errors
+ * @error: %NULL on success, or an unrecoverable error that caused everything
+ *  to fail
+ * @user_data: the @user_data that was passed to
+ *  tp_connection_get_contacts_by_handle()
+ * @weak_object: the @weak_object that was passed to
+ *  tp_connection_get_contacts_by_handle()
+ *
+ * Signature of a callback used to receive the result of
+ * tp_connection_get_contacts_by_handle().
+ *
+ * If an unrecoverable error occurs (for instance, if @connection
+ * becomes disconnected) the whole operation fails, and no contacts or
+ * invalid handles are returned.
+ *
+ * If some or even all of the @handles passed to
+ * tp_connection_get_contacts_by_handle() were not valid, this is not
+ * considered to be a failure. @error will be %NULL in this situation,
+ * @contacts will contain contact objects for those handles that were
+ * valid (possibly none of them), and @invalid will contain the handles
+ * that were not valid.
+ */
+
+
+static void
+contacts_context_continue (ContactsContext *c)
+{
+  if (g_queue_is_empty (&c->todo))
+    {
+      /* do some final sanity checking then hand over the contacts to the
+       * library user */
+      guint i;
+
+      g_assert (c->contacts != NULL);
+      g_assert (c->invalid != NULL);
+
+      for (i = 0; i < c->contacts->len; i++)
+        {
+          TpContact *contact = TP_CONTACT (g_ptr_array_index (c->contacts, i));
+
+          g_assert (contact->priv->identifier != NULL);
+          g_assert (contact->priv->handle != 0);
+        }
+
+      c->callback (c->connection,
+          c->contacts->len, (TpContact * const *) c->contacts->pdata,
+          c->invalid->len, (const TpHandle *) c->invalid->data,
+          NULL, c->user_data, c->weak_object);
+      /* we've given the TpContact refs to the callback, so: */
+      g_ptr_array_remove_range (c->contacts, 0, c->contacts->len);
+    }
+  else
+    {
+      /* bah! */
+      ContactsProc next = g_queue_pop_head (&c->todo);
+
+      next (c);
+    }
+}
+
+
+static void
+contacts_held_one (TpConnection *connection,
+                   TpHandleType handle_type,
+                   guint n_handles,
+                   const TpHandle *handles,
+                   const GError *error,
+                   gpointer user_data,
+                   GObject *weak_object)
+{
+  ContactsContext *c = user_data;
+
+  g_assert (handle_type == TP_HANDLE_TYPE_CONTACT);
+  g_assert (c->next_index < c->handles->len);
+
+  if (error == NULL)
+    {
+      /* I have a handle of my very own. Just what I always wanted! */
+      TpContact *contact;
+
+      g_assert (n_handles == 1);
+      g_assert (handles[0] != 0);
+      g_debug ("%u vs %u", g_array_index (c->handles, TpHandle, c->next_index),
+          handles[0]);
+      g_assert (g_array_index (c->handles, TpHandle, c->next_index)
+          == handles[0]);
+
+      contact = tp_contact_ensure (connection, handles[0]);
+      g_ptr_array_add (c->contacts, contact);
+      c->next_index++;
+    }
+  else if (error->domain == TP_ERRORS &&
+      error->code == TP_ERROR_INVALID_HANDLE)
+    {
+      g_array_append_val (c->invalid,
+          g_array_index (c->handles, TpHandle, c->next_index));
+      /* ignore the bad handle - we just won't return a TpContact for it */
+      g_array_remove_index_fast (c->handles, c->next_index);
+      /* do not increment next_index - another handle has been moved into that
+       * position */
+    }
+  else
+    {
+      /* the connection fell down a well or something */
+      contacts_context_fail (c, error);
+      return;
+    }
+
+  /* Either continue to hold handles, or proceed along the slow path. */
+  contacts_context_continue (c);
+}
+
+
+static void
+contacts_hold_one (ContactsContext *c)
+{
+  c->refcount++;
+  tp_connection_hold_handles (c->connection, -1,
+      TP_HANDLE_TYPE_CONTACT, 1,
+      &g_array_index (c->handles, TpHandle, c->next_index),
+      contacts_held_one, c, contacts_context_unref, c->weak_object);
+}
+
+
+static void
+contacts_held_handles (TpConnection *connection,
+                       TpHandleType handle_type,
+                       guint n_handles,
+                       const TpHandle *handles,
+                       const GError *error,
+                       gpointer user_data,
+                       GObject *weak_object)
+{
+  ContactsContext *c = user_data;
+
+  g_assert (handle_type == TP_HANDLE_TYPE_CONTACT);
+  g_assert (weak_object == c->weak_object);
+
+  if (error == NULL)
+    {
+      /* I now own all n handles. It's like Christmas morning! */
+      guint i;
+
+      g_assert (n_handles == c->handles->len);
+
+      for (i = 0; i < c->handles->len; i++)
+        {
+          g_ptr_array_add (c->contacts,
+              tp_contact_ensure (connection,
+                g_array_index (c->handles, TpHandle, i)));
+        }
+    }
+  else if (error->domain == TP_ERRORS &&
+      error->code == TP_ERROR_INVALID_HANDLE)
+    {
+      /* One of the handles is bad. We don't know which one :-( so split
+       * the batch into a chain of calls. */
+      guint i;
+
+      for (i = 0; i < c->handles->len; i++)
+        {
+          g_queue_push_head (&c->todo, contacts_hold_one);
+        }
+
+      g_assert (c->next_index == 0);
+    }
+  else
+    {
+      /* the connection fell down a well or something */
+      contacts_context_fail (c, error);
+      return;
+    }
+
+  /* Either hold the handles individually, or proceed along the slow path. */
+  contacts_context_continue (c);
+}
+
+
+static void
+contacts_inspected (TpConnection *connection,
+                    const gchar **ids,
+                    const GError *error,
+                    gpointer user_data,
+                    GObject *weak_object)
+{
+  ContactsContext *c = user_data;
+
+  g_assert (weak_object == c->weak_object);
+  g_assert (c->handles->len == c->contacts->len);
+
+  if (error != NULL)
+    {
+      /* the connection fell down a well or something */
+      contacts_context_fail (c, error);
+      return;
+    }
+  else if (G_UNLIKELY (g_strv_length ((GStrv) ids) != c->handles->len))
+    {
+      GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
+          "Connection manager %s is broken: we inspected %u "
+          "handles but InspectHandles returned %u strings",
+          tp_proxy_get_bus_name (connection), c->handles->len,
+          g_strv_length ((GStrv) ids));
+
+      g_warning ("%s", e->message);
+      contacts_context_fail (c, e);
+      g_error_free (e);
+      return;
+    }
+  else
+    {
+      guint i;
+
+      for (i = 0; i < c->contacts->len; i++)
+        {
+          TpContact *contact = g_ptr_array_index (c->contacts, i);
+
+          g_assert (ids[i] != NULL);
+
+          if (contact->priv->identifier == NULL)
+            {
+              contact->priv->identifier = g_strdup (ids[i]);
+            }
+          else if (tp_strdiff (contact->priv->identifier, ids[i]))
+            {
+              GError *e = g_error_new (TP_DBUS_ERRORS,
+                  TP_DBUS_ERROR_INCONSISTENT,
+                  "Connection manager %s is broken: contact handle %u "
+                  "identifier changed from %s to %s",
+                  tp_proxy_get_bus_name (connection), contact->priv->handle,
+                  contact->priv->identifier, ids[i]);
+
+              g_warning ("%s", e->message);
+              contacts_context_fail (c, e);
+              g_error_free (e);
+              return;
+            }
+        }
+    }
+
+  contacts_context_continue (c);
+}
+
+
+static void
+contacts_inspect (ContactsContext *c)
+{
+  guint i;
+
+  g_assert (c->handles->len == c->contacts->len);
+
+  for (i = 0; i < c->contacts->len; i++)
+    {
+      TpContact *contact = g_ptr_array_index (c->contacts, i);
+
+      if (contact->priv->identifier == NULL)
+        {
+          c->refcount++;
+          tp_cli_connection_call_inspect_handles (c->connection, -1,
+              TP_HANDLE_TYPE_CONTACT, c->handles, contacts_inspected,
+              c, contacts_context_unref, c->weak_object);
+          return;
+        }
+    }
+
+  /* else there's no need to inspect the contacts' handles, because we already
+   * know all their identifiers */
+  contacts_context_continue (c);
+}
+
+
+/**
+ * tp_connection_get_contacts_by_handle:
+ * @self: A connection, which must be ready (#TpConnection:connection-ready
+ *  must be %TRUE)
+ * @n_handles: The number of handles in @handles (must be at least 1)
+ * @handles: An array of handles of type %TP_HANDLE_TYPE_CONTACT representing
+ *  the desired contacts
+ * @n_features: The number of features in @features (may be 0)
+ * @features: An array of features that must be ready for use (if supported)
+ *  before the callback is called (may be %NULL if @n_features is 0)
+ * @callback: A user callback to call when the contacts are ready
+ * @user_data: Data to pass to the callback
+ * @destroy: Called to destroy @user_data either after @callback has been
+ *  called, or if the operation is cancelled
+ * @weak_object: An object to pass to the callback, which will be weakly
+ *  referenced; if this object is destroyed, the operation will be cancelled
+ *
+ * Create a number of #TpContact objects and make asynchronous method calls
+ * to hold their handles and ensure that all the features specified in
+ * @features are ready for use (if they are supported at all).
+ *
+ * It is not an error to put features in @features even if the connection
+ * manager doesn't support them - users of this method should have a static
+ * list of features they would like to use if possible, and use it for all
+ * connection managers.
+ */
+void
+tp_connection_get_contacts_by_handle (TpConnection *self,
+                                      guint n_handles,
+                                      const TpHandle *handles,
+                                      guint n_features,
+                                      const TpContactFeature *features,
+                                      TpConnectionContactsByHandleCb callback,
+                                      gpointer user_data,
+                                      GDestroyNotify destroy,
+                                      GObject *weak_object)
+{
+  ContactFeatureFlags feature_flags = 0;
+  ContactsContext *context;
+  guint i;
+
+  g_return_if_fail (tp_connection_is_ready (self));
+  g_return_if_fail (tp_proxy_get_invalidated (self) == NULL);
+  g_return_if_fail (n_handles >= 1);
+  g_return_if_fail (handles != NULL);
+  g_return_if_fail (n_features == 0 || features != NULL);
+  g_return_if_fail (callback != NULL);
+
+  for (i = 0; i < n_features; i++)
+    {
+      g_return_if_fail (features[i] < NUM_TP_CONTACT_FEATURES);
+      feature_flags |= (1 << features[i]);
+    }
+
+  context = contacts_context_new (self, n_handles, feature_flags,
+      callback, user_data, destroy, weak_object);
+
+  g_array_append_vals (context->handles, handles, n_handles);
+
+  /* Before we return anything we'll want to inspect the handles */
+  g_queue_push_head (&context->todo, contacts_inspect);
+
+  /* but first, we need to hold onto them */
+  tp_connection_hold_handles (self, -1,
+      TP_HANDLE_TYPE_CONTACT, n_handles, handles,
+      contacts_held_handles, context, contacts_context_unref, weak_object);
+}
diff --git a/telepathy-glib/contact.h b/telepathy-glib/contact.h
index d8c3533..996578a 100644
--- a/telepathy-glib/contact.h
+++ b/telepathy-glib/contact.h
@@ -85,6 +85,18 @@ TpConnectionPresenceType tp_contact_get_presence_type (TpContact *self);
 const gchar *tp_contact_get_presence_status (TpContact *self);
 const gchar *tp_contact_get_presence_message (TpContact *self);
 
+
+typedef void (*TpConnectionContactsByHandleCb) (TpConnection *connection,
+    guint n_contacts, TpContact * const *contacts,
+    guint n_invalid, const TpHandle *invalid,
+    const GError *error, gpointer user_data, GObject *weak_object);
+
+void tp_connection_get_contacts_by_handle (TpConnection *self,
+    guint n_handles, const TpHandle *handles,
+    guint n_features, const TpContactFeature *features,
+    TpConnectionContactsByHandleCb callback,
+    gpointer user_data, GDestroyNotify destroy, GObject *weak_object);
+
 G_END_DECLS
 
 #endif
-- 
1.5.6.5




More information about the Telepathy-commits mailing list