[Telepathy-commits] [telepathy-glib/master] TpContact: implement upgrading and getting by ID
Simon McVittie
simon.mcvittie at collabora.co.uk
Mon Oct 20 10:21:54 PDT 2008
---
docs/reference/telepathy-glib-sections.txt | 4 +
telepathy-glib/contact.c | 492 +++++++++++++++++++++++++---
telepathy-glib/contact.h | 21 ++
3 files changed, 479 insertions(+), 38 deletions(-)
diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index 5503b56..01a7324 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -2397,6 +2397,10 @@ TpContact
TpContactFeature
TpConnectionContactsByHandleCb
tp_connection_get_contacts_by_handle
+TpConnectionContactsByIdCb
+tp_connection_get_contacts_by_id
+TpConnectionUpgradeContactsCb
+tp_connection_upgrade_contacts
tp_contact_get_alias
tp_contact_get_avatar_token
tp_contact_get_connection
diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c
index 82c5ccc..6c7263a 100644
--- a/telepathy-glib/contact.c
+++ b/telepathy-glib/contact.c
@@ -633,6 +633,7 @@ tp_contact_init (TpContact *self)
typedef struct _ContactsContext ContactsContext;
typedef void (*ContactsProc) (ContactsContext *self);
+typedef enum { CB_BY_HANDLE, CB_BY_ID, CB_UPGRADE } ContactsSignature;
struct _ContactsContext {
gsize refcount;
@@ -645,18 +646,26 @@ struct _ContactsContext {
/* 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;
+ /* strv of IDs; NULL unless we started from IDs */
+ GPtrArray *request_ids;
+ /* ID => GError, NULL unless we started from IDs */
+ GHashTable *request_errors;
+
/* features we need before this request can finish */
ContactFeatureFlags wanted;
/* callback for when we've finished, plus the usual misc */
- TpConnectionContactsByHandleCb callback;
+ ContactsSignature signature;
+ union {
+ GCallback generic;
+ TpConnectionContactsByHandleCb by_handle;
+ TpConnectionContactsByIdCb by_id;
+ TpConnectionUpgradeContactsCb upgrade;
+ } callback;
gpointer user_data;
GDestroyNotify destroy;
GObject *weak_object;
@@ -675,7 +684,8 @@ static ContactsContext *
contacts_context_new (TpConnection *connection,
guint n_contacts,
ContactFeatureFlags want_features,
- TpConnectionContactsByHandleCb callback,
+ ContactsSignature signature,
+ GCallback callback,
gpointer user_data,
GDestroyNotify destroy,
GObject *weak_object)
@@ -687,10 +697,10 @@ contacts_context_new (TpConnection *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->signature = signature;
+ c->callback.generic = callback;
c->user_data = user_data;
c->destroy = destroy;
c->weak_object = weak_object;
@@ -734,6 +744,16 @@ contacts_context_unref (gpointer p)
g_array_free (c->invalid, TRUE);
c->invalid = NULL;
+ if (c->request_ids != NULL)
+ g_strfreev ((gchar **) g_ptr_array_free (c->request_ids, FALSE));
+
+ c->request_ids = NULL;
+
+ if (c->request_errors != NULL)
+ g_hash_table_destroy (c->request_errors);
+
+ c->request_errors = NULL;
+
if (c->destroy != NULL)
c->destroy (c->user_data);
@@ -750,8 +770,23 @@ 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);
+ switch (c->signature)
+ {
+ case CB_BY_HANDLE:
+ c->callback.by_handle (c->connection, 0, NULL, 0, NULL, error,
+ c->user_data, c->weak_object);
+ return;
+ case CB_BY_ID:
+ c->callback.by_id (c->connection, 0, NULL, NULL, NULL, error,
+ c->user_data, c->weak_object);
+ return;
+ case CB_UPGRADE:
+ c->callback.upgrade (c->connection, 0, NULL, error,
+ c->user_data, c->weak_object);
+ return;
+ default:
+ g_assert_not_reached ();
+ }
}
@@ -790,6 +825,67 @@ contacts_context_fail (ContactsContext *c,
* that were not valid.
*/
+/**
+ * TpConnectionContactsByIdCb:
+ * @connection: The connection
+ * @n_contacts: The number of TpContact objects successfully created
+ * (one per valid ID), 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
+ * @requested_ids: An array of @n_contacts valid IDs (JIDs, SIP URIs etc.)
+ * that were passed to tp_connection_get_contacts_by_id(), in an order
+ * corresponding to @contacts, or %NULL on unrecoverable errors
+ * @invalid_id_errors: A hash table in which the keys are IDs that were
+ * passed to tp_connection_get_contacts_by_id() but turned out to be invalid,
+ * and the values are the corresponding #GError; %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_id()
+ * @weak_object: the @weak_object that was passed to
+ * tp_connection_get_contacts_by_id()
+ *
+ * Signature of a callback used to receive the result of
+ * tp_connection_get_contacts_by_id().
+ *
+ * If an unrecoverable error occurs (for instance, if @connection
+ * becomes disconnected) the whole operation fails, and no contacts,
+ * requested IDs or invalid IDs are returned.
+ *
+ * If some or even all of the @ids passed to
+ * tp_connection_get_contacts_by_id() 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 IDs that were
+ * valid (possibly none of them), and @invalid_id_errors will map the IDs
+ * that were not valid to a corresponding #GError.
+ *
+ * @requested_ids contains the IDs that were requested. The
+ * normalized form of requested_ids[i] can be obtained by calling
+ * tp_contact_get_identifier (contacts[i]).
+ */
+
+/**
+ * TpConnectionUpgradeContactsCb:
+ * @connection: The connection
+ * @n_contacts: The number of TpContact objects for which an upgrade was
+ * requested, 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
+ * @error: An unrecoverable error, or %NULL if the connection remains valid
+ * @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_upgrade_contacts().
+ *
+ * If an unrecoverable error occurs (for instance, if @connection becomes
+ * disconnected) the whole operation fails, and no contacts are returned.
+ */
+
static void
contacts_context_continue (ContactsContext *c)
@@ -811,12 +907,32 @@ contacts_context_continue (ContactsContext *c)
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);
+ switch (c->signature)
+ {
+ case CB_BY_HANDLE:
+ c->callback.by_handle (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);
+ break;
+ case CB_BY_ID:
+ c->callback.by_id (c->connection,
+ c->contacts->len, (TpContact * const *) c->contacts->pdata,
+ (const gchar * const *) c->request_ids->pdata,
+ c->request_errors, NULL, c->user_data, c->weak_object);
+ break;
+ case CB_UPGRADE:
+ c->callback.upgrade (c->connection,
+ c->contacts->len, (TpContact * const *) c->contacts->pdata,
+ NULL, c->user_data, c->weak_object);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
/* we've given the TpContact refs to the callback, so: */
- g_ptr_array_remove_range (c->contacts, 0, c->contacts->len);
+ if (c->contacts->len > 0)
+ g_ptr_array_remove_range (c->contacts, 0, c->contacts->len);
}
else
{
@@ -1397,6 +1513,44 @@ contacts_get_avatar_tokens (ContactsContext *c)
}
+static void
+contacts_context_queue_features (ContactsContext *context,
+ ContactFeatureFlags feature_flags)
+{
+ if ((feature_flags & CONTACT_FEATURE_FLAG_ALIAS) != 0 &&
+ tp_proxy_has_interface_by_id (context->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING))
+ {
+ g_queue_push_tail (&context->todo, contacts_get_aliases);
+ }
+
+ if ((feature_flags & CONTACT_FEATURE_FLAG_PRESENCE) != 0)
+ {
+ if (tp_proxy_has_interface_by_id (context->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE))
+ {
+ g_queue_push_tail (&context->todo, contacts_get_simple_presence);
+ }
+#if 0
+ /* FIXME: Before doing this for the first time, we'd need to download
+ * from the CM the definition of what each status actually *means* */
+ else if (tp_proxy_has_interface_by_id (context->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_PRESENCE))
+ {
+ g_queue_push_tail (&context->todo, contacts_get_complex_presence);
+ }
+#endif
+ }
+
+ if ((feature_flags & CONTACT_FEATURE_FLAG_AVATAR_TOKEN) != 0 &&
+ tp_proxy_has_interface_by_id (context->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS))
+ {
+ g_queue_push_tail (&context->todo, contacts_get_avatar_tokens);
+ }
+}
+
+
/**
* tp_connection_get_contacts_by_handle:
* @self: A connection, which must be ready (#TpConnection:connection-ready
@@ -1452,47 +1606,309 @@ tp_connection_get_contacts_by_handle (TpConnection *self,
}
context = contacts_context_new (self, n_handles, feature_flags,
- callback, user_data, destroy, weak_object);
+ CB_BY_HANDLE, G_CALLBACK (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);
- if ((feature_flags & CONTACT_FEATURE_FLAG_ALIAS) != 0 &&
- tp_proxy_has_interface_by_id (self,
- TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING))
+ /* After that we'll get the features */
+ contacts_context_queue_features (context, feature_flags);
+
+ /* 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);
+}
+
+
+static gboolean
+contacts_context_idle_continue (gpointer data)
+{
+ contacts_context_continue (data);
+ return FALSE;
+}
+
+
+/**
+ * tp_connection_upgrade_contacts:
+ * @self: A connection, which must be ready (#TpConnection:connection-ready
+ * must be %TRUE)
+ * @n_contacts: The number of contacts in @contacts (must be at least 1)
+ * @contacts: An array of #TpContact objects associated with @self
+ * @n_features: The number of features in @features (must be at least 1)
+ * @features: An array of features that must be ready for use (if supported)
+ * before the callback is called
+ * @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
+ *
+ * Given several #TpContact objects, make asynchronous method calls
+ * 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_upgrade_contacts (TpConnection *self,
+ guint n_contacts,
+ TpContact * const *contacts,
+ guint n_features,
+ const TpContactFeature *features,
+ TpConnectionUpgradeContactsCb 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_contacts >= 1);
+ g_return_if_fail (contacts != NULL);
+ g_return_if_fail (n_features == 0 || features != NULL);
+ g_return_if_fail (callback != NULL);
+
+ for (i = 0; i < n_contacts; i++)
{
- g_queue_push_tail (&context->todo, contacts_get_aliases);
+ g_return_if_fail (contacts[i]->priv->connection == self);
}
- if ((feature_flags & CONTACT_FEATURE_FLAG_PRESENCE) != 0)
+ for (i = 0; i < n_features; i++)
{
- if (tp_proxy_has_interface_by_id (self,
- TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE))
+ g_return_if_fail (features[i] < NUM_TP_CONTACT_FEATURES);
+ feature_flags |= (1 << features[i]);
+ }
+
+ context = contacts_context_new (self, n_contacts, feature_flags,
+ CB_UPGRADE, G_CALLBACK (callback), user_data, destroy, weak_object);
+
+ for (i = 0; i < n_contacts; i++)
+ {
+ g_ptr_array_add (context->contacts, g_object_ref (contacts[i]));
+ g_array_append_val (context->handles, contacts[i]->priv->handle);
+ }
+
+ g_assert (context->handles->len == n_contacts);
+
+ contacts_context_queue_features (context, feature_flags);
+
+ /* use an idle to make sure the callback is called after we return,
+ * even if all the contacts actually have all the features, just to be
+ * consistent */
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ contacts_context_idle_continue, context, contacts_context_unref);
+}
+
+
+static void
+contacts_requested_one (TpConnection *connection,
+ TpHandleType handle_type,
+ guint n_handles,
+ const TpHandle *handles,
+ const gchar * const *ids,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ ContactsContext *c = user_data;
+
+ if (error == NULL)
+ {
+ TpContact *contact;
+
+ g_assert (handle_type == TP_HANDLE_TYPE_CONTACT);
+ /* -1 because NULL terminator is explicit */
+ g_assert (c->next_index < c->request_ids->len - 1);
+
+ g_assert (n_handles == 1);
+ g_assert (handles[0] != 0);
+
+ contact = tp_contact_ensure (connection, handles[0]);
+ g_array_append_val (c->handles, handles[0]);
+ g_ptr_array_add (c->contacts, contact);
+ c->next_index++;
+ }
+ else if (error->domain == TP_ERRORS &&
+ (error->code == TP_ERROR_NOT_AVAILABLE ||
+ error->code == TP_ERROR_INVALID_ARGUMENT))
+ {
+ g_hash_table_insert (c->request_errors,
+ g_ptr_array_index (c->request_ids, c->next_index),
+ g_error_copy (error));
+ /* shift the rest of the IDs down one and do not increment next_index */
+ g_ptr_array_remove_index (c->request_ids, c->next_index);
+ }
+ else
+ {
+ contacts_context_fail (c, error);
+ return;
+ }
+
+ contacts_context_continue (c);
+}
+
+
+static void
+contacts_request_one (ContactsContext *c)
+{
+ const gchar *ids[] = { NULL, NULL };
+
+ ids[0] = g_ptr_array_index (c->request_ids, c->next_index);
+ g_assert (ids[0] != NULL);
+
+ c->refcount++;
+ tp_connection_request_handles (c->connection, -1,
+ TP_HANDLE_TYPE_CONTACT, ids,
+ contacts_requested_one, c, contacts_context_unref, c->weak_object);
+}
+
+
+static void
+contacts_requested_handles (TpConnection *connection,
+ TpHandleType handle_type,
+ guint n_handles,
+ const TpHandle *handles,
+ const gchar * const *ids,
+ 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)
+ {
+ guint i;
+
+ for (i = 0; i < n_handles; i++)
{
- g_queue_push_tail (&context->todo, contacts_get_simple_presence);
+ TpContact *contact = tp_contact_ensure (connection, handles[i]);
+
+ g_array_append_val (c->handles, handles[i]);
+ g_ptr_array_add (c->contacts, contact);
}
-#if 0
- /* FIXME: Before doing this for the first time, we'd need to download
- * from the CM the definition of what each status actually *means* */
- else if (tp_proxy_has_interface_by_id (self,
- TP_IFACE_QUARK_CONNECTION_INTERFACE_PRESENCE))
+ }
+ else if (error->domain == TP_ERRORS &&
+ (error->code == TP_ERROR_NOT_AVAILABLE ||
+ error->code == TP_ERROR_INVALID_ARGUMENT))
+ {
+ /* One of the strings is bad. We don't know which, so split them. */
+ guint i;
+
+ DEBUG ("A handle was bad, trying to recover: %s %u: %s",
+ g_quark_to_string (error->domain), error->code, error->message);
+
+ /* -1 because NULL terminator is explicit */
+ for (i = 0; i < c->request_ids->len - 1; i++)
{
- g_queue_push_tail (&context->todo, contacts_get_complex_presence);
+ g_queue_push_head (&c->todo, contacts_request_one);
}
-#endif
+
+ g_assert (c->next_index == 0);
+ }
+ else
+ {
+ DEBUG ("RequestHandles failed: %s %u: %s",
+ g_quark_to_string (error->domain), error->code, error->message);
+ contacts_context_fail (c, error);
+ return;
}
- if ((feature_flags & CONTACT_FEATURE_FLAG_AVATAR_TOKEN) != 0 &&
- tp_proxy_has_interface_by_id (self,
- TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS))
+ contacts_context_continue (c);
+}
+
+
+/**
+ * tp_connection_get_contacts_by_id:
+ * @self: A connection, which must be ready (#TpConnection:connection-ready
+ * must be %TRUE)
+ * @n_ids: The number of IDs in @ids (must be at least 1)
+ * @ids: An array of strings representing the desired contacts by their
+ * identifiers in the IM protocol (XMPP JIDs, SIP URIs, MSN Passports,
+ * AOL screen-names etc.)
+ * @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 obtain 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_id (TpConnection *self,
+ guint n_ids,
+ const gchar * const *ids,
+ guint n_features,
+ const TpContactFeature *features,
+ TpConnectionContactsByIdCb 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_ids >= 1);
+ g_return_if_fail (ids != NULL);
+ g_return_if_fail (ids[0] != NULL);
+ g_return_if_fail (n_features == 0 || features != NULL);
+ g_return_if_fail (callback != NULL);
+
+ for (i = 0; i < n_features; i++)
{
- g_queue_push_tail (&context->todo, contacts_get_avatar_tokens);
+ g_return_if_fail (features[i] < NUM_TP_CONTACT_FEATURES);
+ feature_flags |= (1 << features[i]);
}
- /* 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);
+ context = contacts_context_new (self, n_ids, feature_flags,
+ CB_BY_ID, G_CALLBACK (callback), user_data, destroy, weak_object);
+ context->request_errors = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_error_free);
+
+ context->request_ids = g_ptr_array_sized_new (n_ids);
+
+ for (i = 0; i < n_ids; i++)
+ {
+ g_return_if_fail (ids[i] != NULL);
+ g_ptr_array_add (context->request_ids, g_strdup (ids[i]));
+ }
+
+ g_ptr_array_add (context->request_ids, NULL);
+
+ /* set up the queue of feature introspection */
+ 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 */
+ tp_connection_request_handles (self, -1,
+ TP_HANDLE_TYPE_CONTACT,
+ (const gchar * const *) context->request_ids->pdata,
+ contacts_requested_handles, context, contacts_context_unref,
+ weak_object);
}
diff --git a/telepathy-glib/contact.h b/telepathy-glib/contact.h
index 996578a..35b8440 100644
--- a/telepathy-glib/contact.h
+++ b/telepathy-glib/contact.h
@@ -97,6 +97,27 @@ void tp_connection_get_contacts_by_handle (TpConnection *self,
TpConnectionContactsByHandleCb callback,
gpointer user_data, GDestroyNotify destroy, GObject *weak_object);
+typedef void (*TpConnectionUpgradeContactsCb) (TpConnection *connection,
+ guint n_contacts, TpContact * const *contacts,
+ const GError *error, gpointer user_data, GObject *weak_object);
+
+void tp_connection_upgrade_contacts (TpConnection *self,
+ guint n_contacts, TpContact * const *contacts,
+ guint n_features, const TpContactFeature *features,
+ TpConnectionUpgradeContactsCb callback,
+ gpointer user_data, GDestroyNotify destroy, GObject *weak_object);
+
+typedef void (*TpConnectionContactsByIdCb) (TpConnection *connection,
+ guint n_contacts, TpContact * const *contacts,
+ const gchar * const *requested_ids, GHashTable *invalid_id_errors,
+ const GError *error, gpointer user_data, GObject *weak_object);
+
+void tp_connection_get_contacts_by_id (TpConnection *self,
+ guint n_ids, const gchar * const *ids,
+ guint n_features, const TpContactFeature *features,
+ TpConnectionContactsByIdCb 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