diff --git a/docs/reference/telepathy-glib-docs.sgml b/docs/reference/telepathy-glib-docs.sgml index 7cb2350..42168eb 100644 --- a/docs/reference/telepathy-glib-docs.sgml +++ b/docs/reference/telepathy-glib-docs.sgml @@ -54,6 +54,8 @@ + + diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index aa83aeb..b1fa096 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -3901,6 +3901,52 @@ tp_cli_connection_manager_connect_to_new_connection
+contact-search +contact-search +telepathy-glib/telepathy-glib.h +TpContactSearch +TpContactSearchClass +tp_contact_search_new_async +tp_contact_search_new_finish +tp_contact_search_reset_async +tp_contact_search_reset_finish +tp_contact_search_start +tp_contact_search_get_search_keys +tp_contact_search_get_account +tp_contact_search_get_limit +tp_contact_search_get_server + +tp_contact_search_get_type +TP_CONTACT_SEARCH +TP_CONTACT_SEARCH_CLASS +TP_CONTACT_SEARCH_GET_CLASS +TP_IS_CONTACT_SEARCH +TP_IS_CONTACT_SEARCH_CLASS +TP_TYPE_CONTACT_SEARCH +TpContactSearchPrivate +
+ +
+contact-search-result +contact-search-result +telepathy-glib/telepathy-glib.h +TpContactSearchResult +TpContactSearchResultClass +tp_contact_search_result_get_field +tp_contact_search_result_get_fields +tp_contact_search_result_get_identifier + +tp_contact_search_result_get_type +TP_CONTACT_SEARCH_RESULT +TP_CONTACT_SEARCH_RESULT_CLASS +TP_CONTACT_SEARCH_RESULT_GET_CLASS +TP_IS_CONTACT_SEARCH_RESULT +TP_IS_CONTACT_SEARCH_RESULT_CLASS +TP_TYPE_CONTACT_SEARCH_RESULT +TpContactSearchResultPrivate +
+ +
media-interfaces media-interfaces telepathy-glib/media-interfaces.h @@ -4065,6 +4111,7 @@ tp_capabilities_supports_text_chatrooms tp_capabilities_supports_text_chats tp_capabilities_supports_stream_tubes tp_capabilities_supports_dbus_tubes +tp_capabilities_supports_contact_search tp_capabilities_get_type TP_CAPABILITIES diff --git a/telepathy-glib/Makefile.am b/telepathy-glib/Makefile.am index d458b60..25d2030 100644 --- a/telepathy-glib/Makefile.am +++ b/telepathy-glib/Makefile.am @@ -47,6 +47,8 @@ our_headers = \ cm-message.h \ connection.h \ connection-manager.h \ + contact-search.h \ + contact-search-result.h \ capabilities.h \ contact.h \ base-contact-list.h \ @@ -174,6 +176,9 @@ libtelepathy_glib_internal_la_SOURCES = \ contact.c \ contact-list-channel-internal.h \ contact-list-channel.c \ + contact-search.c \ + contact-search-internal.h \ + contact-search-result.c \ base-contact-list.c \ base-contact-list-internal.h \ cm-message.c \ diff --git a/telepathy-glib/capabilities.c b/telepathy-glib/capabilities.c index 2cf9523..c99e046 100644 --- a/telepathy-glib/capabilities.c +++ b/telepathy-glib/capabilities.c @@ -476,3 +476,78 @@ tp_capabilities_supports_dbus_tubes (TpCapabilities *self, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, handle_type, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, service_name); } + +/** + * tp_capabilities_supports_contact_search: + * @self: a #TpCapabilities object + * @with_limit: (out): if not %NULL, used to return %TRUE if the @limit + * parameter to tp_contact_search_new_async() and + * tp_contact_search_reset_async() can be nonzero + * @with_server: (out): if not %NULL, used to return %TRUE if the @server + * parameter to tp_contact_search_new_async() and + * tp_contact_search_reset_async() can be non-%NULL + * + * Return whether this protocol or connection can perform contact + * searches. Optionally, also return whether a limited number of + * results can be specified, and whether alternative servers can be + * searched. + * + * Returns: %TRUE if #TpContactSearch can be used. + * + * Since: 0.13.UNRELEASED + */ +gboolean +tp_capabilities_supports_contact_search (TpCapabilities *self, + gboolean *with_limit, + gboolean *with_server) +{ + gboolean ret = FALSE; + guint i, j; + + g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); + + if (with_limit) + *with_limit = FALSE; + + if (with_server) + *with_server = FALSE; + + for (i = 0; i < self->priv->classes->len; i++) + { + GValueArray *arr = g_ptr_array_index (self->priv->classes, i); + GHashTable *fixed; + const gchar *chan_type; + const gchar **allowed_properties; + + tp_value_array_unpack (arr, 2, &fixed, &allowed_properties); + + if (g_hash_table_size (fixed) != 1) + continue; + + chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); + + if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH)) + continue; + + ret = TRUE; + + for (j = 0; allowed_properties[j] != NULL; j++) + { + if (with_limit) + { + if (!tp_strdiff (allowed_properties[j], + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_LIMIT)) + *with_limit = TRUE; + } + + if (with_server) + { + if (!tp_strdiff (allowed_properties[j], + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_SERVER)) + *with_server = TRUE; + } + } + } + + return ret; +} diff --git a/telepathy-glib/capabilities.h b/telepathy-glib/capabilities.h index 499a239..7e06748 100644 --- a/telepathy-glib/capabilities.h +++ b/telepathy-glib/capabilities.h @@ -63,6 +63,10 @@ gboolean tp_capabilities_supports_dbus_tubes (TpCapabilities *self, TpHandleType handle_type, const gchar *service_name); +gboolean tp_capabilities_supports_contact_search (TpCapabilities *self, + gboolean *with_limit, + gboolean *with_server); + G_END_DECLS #endif diff --git a/telepathy-glib/contact-search-internal.h b/telepathy-glib/contact-search-internal.h new file mode 100644 index 0000000..bb933b3 --- /dev/null +++ b/telepathy-glib/contact-search-internal.h @@ -0,0 +1,37 @@ +/* + * contact-search-internal.h - private functions for contact searches + * + * Copyright (C) 2010 Collabora Ltd. + * + * The code contained in this file 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 file 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 code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __TP_CONTACT_SEARCH_INTERNAL_H__ +#define __TP_CONTACT_SEARCH_INTERNAL_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +TpContactSearchResult *_tp_contact_search_result_new (const gchar *identifier); + +void _tp_contact_search_result_insert_field (TpContactSearchResult *self, + TpContactInfoField *field); + +G_END_DECLS + +#endif diff --git a/telepathy-glib/contact-search-result.c b/telepathy-glib/contact-search-result.c new file mode 100644 index 0000000..26a74b0 --- /dev/null +++ b/telepathy-glib/contact-search-result.c @@ -0,0 +1,259 @@ +/* + * contact-search-result.c - a result for a contact search + * + * Copyright (C) 2010 Collabora Ltd. + * + * The code contained in this file 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 file 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 code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "telepathy-glib/contact-search-result.h" +#include "telepathy-glib/contact-search-internal.h" + +#include +#include +#include + +#define DEBUG_FLAG TP_DEBUG_CHANNEL +#include "telepathy-glib/debug-internal.h" + +#include "_gen/telepathy-interfaces.h" + +/** + * SECTION:contact-search-result + * @title: TpContactSearchResult + * @short_description: a result of a contact search + * @see_also: #TpContactSearch + * + * #TpContactSearchResult objects represent results for + * #TpContactSearch. + * + * Since: 0.13.UNRELEASED + */ + +/** + * TpContactSearchResultClass: + * + * The class of a #TpContactSearchResult. + * + * Since: 0.13.UNRELEASED + */ + +/** + * TpContactSearchResult: + * + * An object representing the results of a Telepathy contact + * search channel. + * There are no interesting public struct fields. + * + * Since: 0.13.UNRELEASED + */ + +G_DEFINE_TYPE (TpContactSearchResult, + tp_contact_search_result, + G_TYPE_OBJECT); + +struct _TpContactSearchResultPrivate +{ + gchar *identifier; + /* List of TpContactInfoField. The list and its contents are owned by us. */ + GList *fields; +}; + +enum /* properties */ +{ + PROP_0, + PROP_IDENTIFIER, +}; + +static gint +find_tp_contact_info_field (gconstpointer f, + gconstpointer n) +{ + TpContactInfoField *field = (TpContactInfoField *) f; + gchar *name = (gchar *) n; + + return g_strcmp0 (field->field_name, name); +} + +static void +tp_contact_search_result_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TpContactSearchResult *self = TP_CONTACT_SEARCH_RESULT (object); + + switch (prop_id) + { + case PROP_IDENTIFIER: + g_assert (self->priv->identifier == NULL); /* construct-only */ + self->priv->identifier = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +static void +tp_contact_search_result_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TpContactSearchResult *self = TP_CONTACT_SEARCH_RESULT (object); + + switch (prop_id) + { + case PROP_IDENTIFIER: + g_value_set_string (value, self->priv->identifier); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tp_contact_search_result_dispose (GObject *object) +{ + TpContactSearchResult *self = TP_CONTACT_SEARCH_RESULT (object); + + g_free (self->priv->identifier); + g_list_free_full (self->priv->fields, + (GDestroyNotify) tp_contact_info_field_free); + + G_OBJECT_CLASS (tp_contact_search_result_parent_class)->dispose (object); +} + +static void +tp_contact_search_result_class_init (TpContactSearchResultClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = tp_contact_search_result_set_property; + gobject_class->get_property = tp_contact_search_result_get_property; + gobject_class->dispose = tp_contact_search_result_dispose; + + /** + * TpContactSearch:identifier: + * + * The contact identifier. + * + * Since: 0.13.UNRELEASED + */ + g_object_class_install_property (gobject_class, + PROP_IDENTIFIER, + g_param_spec_string ("identifier", + "Identifier", + "The contact identifier", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (gobject_class, + sizeof (TpContactSearchResultPrivate)); +} + +static void +tp_contact_search_result_init (TpContactSearchResult *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TYPE_CONTACT_SEARCH_RESULT, + TpContactSearchResultPrivate); +} + +TpContactSearchResult * +_tp_contact_search_result_new (const gchar *identifier) +{ + g_return_val_if_fail (identifier != NULL, NULL); + + return g_object_new (TP_TYPE_CONTACT_SEARCH_RESULT, + "identifier", identifier, + NULL); +} + +void +_tp_contact_search_result_insert_field (TpContactSearchResult *self, + TpContactInfoField *field) +{ + g_return_if_fail (TP_IS_CONTACT_SEARCH_RESULT (self)); + + self->priv->fields = g_list_append (self->priv->fields, field); +} + +/** + * tp_contact_search_result_get_identifier: + * @self: a #TpContactSearchResult + * + * + * + * Returns: the contact identifier. + * + * Since: 0.13.UNRELEASED + */ +const gchar * +tp_contact_search_result_get_identifier (TpContactSearchResult *self) +{ + g_return_val_if_fail (TP_IS_CONTACT_SEARCH_RESULT (self), NULL); + + return self->priv->identifier; +} + +/** + * tp_contact_search_result_get_field: + * @self: a #TpContactSearchResult + * @field: the name of the field + * + * + * + * Returns: (transfer none): the specified field, or %NULL if the + * result doesn't have it. + * + * Since: 0.13.UNRELEASED + */ +TpContactInfoField * +tp_contact_search_result_get_field (TpContactSearchResult *self, + const gchar *field) +{ + GList *l; + + g_return_val_if_fail (TP_IS_CONTACT_SEARCH_RESULT (self), NULL); + + l = g_list_find_custom (self->priv->fields, + field, + find_tp_contact_info_field); + + return (l ? l->data : NULL); +} + +/** + * tp_contact_search_result_get_fields: + * @self: a search result + * + * + * + * Returns: (transfer container) (element-type TelepathyGLib.ContactInfoField): + * a #GList of #TpContactInfoField for the specified contact. You should free + * it when you're done with g_list_free(). + */ +GList * +tp_contact_search_result_get_fields (TpContactSearchResult *self) +{ + g_return_val_if_fail (TP_IS_CONTACT_SEARCH_RESULT (self), NULL); + + return g_list_copy (self->priv->fields); +} diff --git a/telepathy-glib/contact-search-result.h b/telepathy-glib/contact-search-result.h new file mode 100644 index 0000000..4d5ad93 --- /dev/null +++ b/telepathy-glib/contact-search-result.h @@ -0,0 +1,73 @@ +/* + * contact-search-result.h - a result from a contact search + * + * Copyright (C) 2010 Collabora Ltd. + * + * The code contained in this file 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 file 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 code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __TP_CONTACT_SEARCH_RESULT_H__ +#define __TP_CONTACT_SEARCH_RESULT_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define TP_TYPE_CONTACT_SEARCH_RESULT \ + (tp_contact_search_result_get_type ()) +#define TP_CONTACT_SEARCH_RESULT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TYPE_CONTACT_SEARCH_RESULT, \ + TpContactSearchResult)) +#define TP_CONTACT_SEARCH_RESULT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TYPE_CONTACT_SEARCH_RESULT, \ + TpContactSearchResultClass)) +#define TP_IS_CONTACT_SEARCH_RESULT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TYPE_CONTACT_SEARCH_RESULT)) +#define TP_IS_CONTACT_SEARCH_RESULT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TYPE_CONTACT_SEARCH_RESULT)) +#define TP_CONTACT_SEARCH_RESULT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_CONTACT_SEARCH_RESULT, \ + TpContactSearchResultClass)) + +typedef struct _TpContactSearchResult TpContactSearchResult; +typedef struct _TpContactSearchResultPrivate TpContactSearchResultPrivate; +typedef struct _TpContactSearchResultClass TpContactSearchResultClass; + +struct _TpContactSearchResult +{ + /**/ + GObject parent; + TpContactSearchResultPrivate *priv; +}; + +struct _TpContactSearchResultClass +{ + /**/ + GObjectClass parent_class; + GCallback _padding[7]; +}; + +GType tp_contact_search_result_get_type (void); + +const gchar *tp_contact_search_result_get_identifier (TpContactSearchResult *self); +TpContactInfoField *tp_contact_search_result_get_field (TpContactSearchResult *self, + const gchar *field); +GList *tp_contact_search_result_get_fields (TpContactSearchResult *self); + +G_END_DECLS + +#endif diff --git a/telepathy-glib/contact-search.c b/telepathy-glib/contact-search.c new file mode 100644 index 0000000..0181e03 --- /dev/null +++ b/telepathy-glib/contact-search.c @@ -0,0 +1,783 @@ +/* + * contact-search.c - a representation for an ongoing search for contacts + * + * Copyright (C) 2010 Collabora Ltd. + * + * The code contained in this file 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 file 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 code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "telepathy-glib/contact-search.h" + +#include +#include +#include +#include +#include +#include + +#define DEBUG_FLAG TP_DEBUG_CHANNEL +#include "telepathy-glib/debug-internal.h" + +#include "_gen/telepathy-interfaces.h" + +/** + * SECTION:contact-search + * @title: TpContactSearch + * @short_description: object for a Telepathy contact search channel + * @see_also: #TpChannel + * + * #TpContactSearch objects represent ongoing searches for contacts. They + * implement the #GAsyncInitable interface, so the initialization may fail. + * + * In normal circumstances, after creating a #TpContactSearch object, you + * would connect to the #TpContactSearch::search-results-received signal + * to get search results when a search happens. You would then call + * tp_contact_search_get_search_keys() to get the search keys, and then + * do a search using tp_contact_search_start(). When results are found, + * the #TpContactSearch::search-results-received callback will be called. + * + * You can check the search state by looking at the + * #TpContactSearch:state property. If you want to be notified about + * changes, connect to the notify::state signal, see + * #GObject::notify for details. + * + * You can search as many times as you want on a #TpContactSearch object, + * but you need to call tp_contact_search_reset_async() between searches. + * + * Since: 0.13.UNRELEASED + */ + +/** + * TpContactSearchClass: + * + * The class of a #TpContactSearch. + * + * Since: 0.13.UNRELEASED + */ + +/** + * TpContactSearch: + * + * An object for Telepathy contact searches. + * There are no interesting public struct fields. + * + * Since: 0.13.UNRELEASED + */ + +static void async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (TpContactSearch, + tp_contact_search, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)) + +struct _TpContactSearchPrivate +{ + TpAccount *account; + TpChannel *channel; + TpChannelContactSearchState state; + gchar *server; + guint limit; + const gchar * const *keys; + + GCancellable *cancellable; + GSimpleAsyncResult *async_res; +}; + +enum /* properties */ +{ + PROP_0, + PROP_ACCOUNT, + PROP_SERVER, + PROP_LIMIT, + PROP_STATE, +}; + +enum /* signals */ +{ + SEARCH_RESULTS_RECEIVED, + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL] = { 0, }; + +static void +close_search_channel (TpContactSearch *self) +{ + if (self->priv->channel != NULL) + { + DEBUG ("Closing existing search channel"); + + tp_cli_channel_call_close (self->priv->channel, + -1, NULL, NULL, NULL, NULL); + tp_clear_object (&self->priv->channel); + } +} + +static void +_search_state_changed (TpChannel *channel, + guint state, + const gchar *error, + GHashTable *details, + gpointer user_data, + GObject *weak_object) +{ + TpContactSearch *self = TP_CONTACT_SEARCH (weak_object); + + if (self->priv->state != state) + { + DEBUG ("SearchStateChanged: %u", state); + self->priv->state = state; + g_object_notify (weak_object, "state"); + } +} + +static void +_search_results_received (TpChannel *channel, + GHashTable *result, + gpointer user_data, + GObject *object) +{ + GHashTableIter iter; + gchar *contact; + GPtrArray *info; + GList *results = NULL; + + g_hash_table_iter_init (&iter, result); + while (g_hash_table_iter_next (&iter, (gpointer) &contact, (gpointer) &info)) + { + TpContactSearchResult *search_result; + char *field; + char **parameters; + char **values; + gint i; + + search_result = _tp_contact_search_result_new (contact); + + for (i = info->len - 1; i >= 0; i--) + { + TpContactInfoField *contact_field; + tp_value_array_unpack (g_ptr_array_index (info, i), 3, + &field, ¶meters, &values); + + contact_field = tp_contact_info_field_new (field, parameters, values); + _tp_contact_search_result_insert_field (search_result, contact_field); + } + results = g_list_prepend (results, search_result); + } + + DEBUG ("SearchResultsReceived (%i results)", g_hash_table_size (result)); + g_signal_emit (object, _signals[SEARCH_RESULTS_RECEIVED], 0, results); + + g_list_free_full (results, g_object_unref); +} + +static void +_create_search_channel_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + TpAccountChannelRequest *channel_request; + TpContactSearch *self = TP_CONTACT_SEARCH (user_data); + GHashTable *properties; + GError *error = NULL; + const gchar *server; + guint limit; + gboolean valid; + + channel_request = TP_ACCOUNT_CHANNEL_REQUEST (source_object); + + self->priv->channel = + tp_account_channel_request_create_and_handle_channel_finish ( + channel_request, + result, + NULL, + &error); + + if (error != NULL) + { + DEBUG ("Failed to create search channel: %s", error->message); + g_simple_async_result_take_error (self->priv->async_res, error); + goto out; + } + + DEBUG ("Got channel: %s", tp_proxy_get_object_path (self->priv->channel)); + + tp_cli_channel_type_contact_search_connect_to_search_result_received ( + self->priv->channel, _search_results_received, + NULL, NULL, G_OBJECT (self), &error); + if (error != NULL) + { + DEBUG ("Failed to connect SearchResultReceived: %s", error->message); + close_search_channel (self); + g_simple_async_result_take_error (self->priv->async_res, error); + goto out; + } + + tp_cli_channel_type_contact_search_connect_to_search_state_changed ( + self->priv->channel, _search_state_changed, + NULL, NULL, G_OBJECT (self), &error); + if (error != NULL) + { + DEBUG ("Failed to connect SearchStateChanged: %s", error->message); + close_search_channel (self); + g_simple_async_result_take_error (self->priv->async_res, error); + goto out; + } + + properties = tp_channel_borrow_immutable_properties (self->priv->channel); + + self->priv->keys = tp_asv_get_strv (properties, + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_AVAILABLE_SEARCH_KEYS); + server = tp_asv_get_string (properties, + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_SERVER); + if (g_strcmp0 (server, self->priv->server) != 0) + { + g_free (self->priv->server); + self->priv->server = g_strdup (server); + g_object_notify (G_OBJECT (self), "server"); + } + limit = tp_asv_get_uint32 (properties, + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_LIMIT, &valid); + if (valid && limit != self->priv->limit) + { + self->priv->limit = limit; + g_object_notify (G_OBJECT (self), "limit"); + } + + self->priv->state = TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED; + g_object_notify (G_OBJECT (self), "state"); + + out: + g_simple_async_result_complete (self->priv->async_res); + tp_clear_object (&self->priv->async_res); +} + +static void +tp_contact_search_open_new_channel (TpContactSearch *self) +{ + GHashTable *request; + TpAccountChannelRequest *channel_request; + + close_search_channel (self); + + DEBUG ("Requesting new search channel"); + + request = tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, + G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH, + NULL); + + if (self->priv->server != NULL) + tp_asv_set_string (request, + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_SERVER, + self->priv->server); + + if (self->priv->limit != 0) + tp_asv_set_uint32 (request, + TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_LIMIT, + self->priv->limit); + + channel_request = tp_account_channel_request_new (self->priv->account, + request, + TP_USER_ACTION_TIME_NOT_USER_ACTION); + + tp_account_channel_request_create_and_handle_channel_async ( + channel_request, + self->priv->cancellable, + _create_search_channel_cb, + G_OBJECT (self)); + g_object_unref (channel_request); + + g_hash_table_unref (request); +} + +static void +tp_contact_search_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TpContactSearch *self = TP_CONTACT_SEARCH (object); + + switch (prop_id) + { + case PROP_ACCOUNT: + g_assert (self->priv->account == NULL); /* construct-only */ + self->priv->account = g_value_dup_object (value); + break; + + case PROP_SERVER: + g_assert (self->priv->server == NULL); /* construct-only */ + self->priv->server = g_value_dup_string (value); + break; + + case PROP_LIMIT: + self->priv->limit = g_value_get_uint (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +static void +tp_contact_search_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TpContactSearch *self = TP_CONTACT_SEARCH (object); + + switch (prop_id) + { + case PROP_ACCOUNT: + g_value_set_object (value, self->priv->account); + break; + + case PROP_SERVER: + g_value_set_string (value, self->priv->server); + break; + + case PROP_LIMIT: + g_value_set_uint (value, self->priv->limit); + break; + + case PROP_STATE: + g_value_set_uint (value, self->priv->state); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tp_contact_search_dispose (GObject *object) +{ + TpContactSearch *self = TP_CONTACT_SEARCH (object); + + close_search_channel (self); + g_object_unref (self->priv->account); + g_object_unref (self->priv->cancellable); + + G_OBJECT_CLASS (tp_contact_search_parent_class)->dispose (object); +} + +static void +tp_contact_search_class_init (TpContactSearchClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = tp_contact_search_set_property; + gobject_class->get_property = tp_contact_search_get_property; + gobject_class->dispose = tp_contact_search_dispose; + + /** + * TpContactSearch:account: + * + * This search's account. + * + * Since: 0.13.UNRELEASED + */ + g_object_class_install_property (gobject_class, + PROP_ACCOUNT, + g_param_spec_object ("account", + "Account", + "A #TpAccount used to create search channels", + TP_TYPE_ACCOUNT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * TpContactSearch:server: + * + * The search server. This is only supported by some protocols, + * use tp_capabilities_supports_contact_search() to check if it's + * supported. + * + * To change the server after the object has been constructed, + * use tp_contact_search_reset_async(). + * + * Since: 0.13.UNRELEASED + */ + g_object_class_install_property (gobject_class, + PROP_SERVER, + g_param_spec_string ("server", + "Server", + "The server on which to search for contacts", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * TpContactSearch:limit: + * + * The maximum number of results that the server should return. + * This is only supported by some protocols, use + * tp_capabilities_supports_contact_search() to check if it's + * supported. + * + * To change the limit after the object has been constructed, + * use tp_contact_search_reset_async(). + * + * Since: 0.13.UNRELEASED + */ + g_object_class_install_property (gobject_class, + PROP_LIMIT, + g_param_spec_uint ("limit", + "Limit", + "The maximum number of results to be returned by the server", + 0, + G_MAXUINT32, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * TpContactSearch:state: + * + * This search's state, as a %TpChannelContactSearchState. + * + * Since: 0.13.UNRELEASED + */ + g_object_class_install_property (gobject_class, + PROP_STATE, + g_param_spec_uint ("state", + "State", + "The search's state", + 0, + G_MAXUINT32, + TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * TpContactSearch::search-results-received: + * @self: a contact search + * @results: (element-type TelepathyGLib.ContactSearchResult): + * a #GList with the search results + * + * Emitted when search results are received. Note that this signal may + * be called multiple times for the same search. + * + * Since: 0.13.UNRELEASED + */ + _signals[SEARCH_RESULTS_RECEIVED] = g_signal_new ("search-results-received", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + + g_type_class_add_private (gobject_class, + sizeof (TpContactSearchPrivate)); +} + +static void +tp_contact_search_init (TpContactSearch *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TYPE_CONTACT_SEARCH, + TpContactSearchPrivate); + + self->priv->cancellable = g_cancellable_new (); +} + +/** + * tp_contact_search_new_async: + * @account: an account for the contact search + * @server: the server on which to search for contacts, or %NULL + * @limit: The maximum number of results the server should return, + * or 0 for the server default. + * @callback: a #GAsyncReadyCallback to call when the initialization + * is finished + * @user_data: data to pass to the callback function + * + * + * + * Since: 0.13.UNRELEASED + */ +void +tp_contact_search_new_async (TpAccount *account, + const gchar *server, + guint limit, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (TP_IS_ACCOUNT (account)); + + g_async_initable_new_async (TP_TYPE_CONTACT_SEARCH, + G_PRIORITY_DEFAULT, + NULL, + callback, + user_data, + "account", account, + "server", server, + "limit", limit, + NULL); +} + +/** + * tp_contact_search_new_finish: + * @result: the #GAsyncResult from the callback + * @error: a #GError location to store an error, or %NULL + * + * + * + * Returns: (transfer full): a new contact search object, or %NULL + * in case of error. + * + * Since: 0.13.UNRELEASED + */ +TpContactSearch * +tp_contact_search_new_finish (GAsyncResult *result, + GError **error) +{ + GObject *object, *source_object; + + source_object = g_async_result_get_source_object (result); + + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + result, error); + g_object_unref (source_object); + + if (object != NULL) + return TP_CONTACT_SEARCH (object); + else + return NULL; +} + +/** + * tp_contact_search_reset_async: + * @self: the #TpContactSearch to reset + * @server: the server on which to search for contacts, or %NULL + * @limit: The maximum number of results the server should return, + * or 0 for the server default. + * @callback: a #GAsyncReadyCallback to call when the initialization + * is finished + * @user_data: data to pass to the callback function + * + * Resets the contact search object so a new search can be performed. + * If another tp_contact_search_reset_async() call is in progress, + * it will be cancelled and tp_contact_search_reset_finish() will + * return an appropriate error. + * + * Since: 0.13.UNRELEASED + */ +void +tp_contact_search_reset_async (TpContactSearch *self, + const gchar *server, + guint limit, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (TP_IS_CONTACT_SEARCH (self)); + + if (self->priv->async_res != NULL) + { + g_cancellable_cancel (self->priv->cancellable); + g_object_unref (self->priv->cancellable); + + self->priv->cancellable = g_cancellable_new (); + } + + g_return_if_fail (self->priv->async_res == NULL); + + g_free (self->priv->server); + self->priv->server = g_strdup (server); + self->priv->limit = limit; + + self->priv->async_res = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + tp_contact_search_reset_async); + + tp_contact_search_open_new_channel (self); +} + +/** + * tp_contact_search_reset_finish: + * @self: the #TpContactSearch that is being reset + * @result: the #GAsyncResult from the callback + * @error: a #GError location to store an error, or %NULL + * + * + * + * Returns: (transfer none): the new search keys, or %NULL + * in case of error. + * + * Since: 0.13.UNRELEASED + */ +const gchar * const * +tp_contact_search_reset_finish (TpContactSearch *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (self), + tp_contact_search_reset_async), + FALSE); + + simple = (GSimpleAsyncResult *) result; + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + return self->priv->keys; +} + +/** + * tp_contact_search_start: + * @self: a #TpContactSearch + * @criteria: (transfer none) (element-type utf8 utf8): a map + * from keys returned by tp_contact_search_get_search_keys() + * to values to search for + * + * Starts a search for the keys specified in @criteria. Connect + * to the #TpContactSearch::search-results-received signal + * before calling this function. + * + * Before searching again on the same #TpContactSearch, you must + * call tp_contact_search_reset_async(). + * + * Since: 0.13.UNRELEASED + */ +void +tp_contact_search_start (TpContactSearch *self, + GHashTable *criteria) +{ + g_return_if_fail (TP_IS_CONTACT_SEARCH (self)); + g_return_if_fail (TP_IS_CHANNEL (self->priv->channel)); + g_return_if_fail (self->priv->state == + TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED); + + tp_cli_channel_type_contact_search_call_search (self->priv->channel, + -1, criteria, NULL, NULL, NULL, NULL); +} + +/** + * tp_contact_search_get_search_keys: + * @self: the contact search object to get the keys from + * + * Get the search keys for a contact search. + * The keys are vCard field names in lower case, except when + * they're one of the special cases from telepathy-spec like + * "tell;cell" or "x-n-given". See the Channel.Type.ContactSearch + * interface in the Telepathy D-Bus Interface Specification for + * a list of the special cases. + * + * Returns: (transfer none): the new search keys, or %NULL. + * + * Since: 0.13.UNRELEASED + */ +const gchar * const * +tp_contact_search_get_search_keys (TpContactSearch *self) +{ + g_return_val_if_fail (TP_IS_CONTACT_SEARCH (self), NULL); + + return self->priv->keys; +} + +/** + * tp_contact_search_get_account: + * @self: a contact search object + * + * + * + * Returns: The TpContactSearch:account property + * + * Since: 0.13.UNRELEASED + */ +TpAccount * +tp_contact_search_get_account (TpContactSearch *self) +{ + g_return_val_if_fail (TP_IS_CONTACT_SEARCH (self), NULL); + + return self->priv->account; +} + +/** + * tp_contact_search_get_server: + * @self: a contact search object + * + * + * + * Returns: The TpContactSearch:server property + * + * Since: 0.13.UNRELEASED + */ +const gchar * +tp_contact_search_get_server (TpContactSearch *self) +{ + g_return_val_if_fail (TP_IS_CONTACT_SEARCH (self), NULL); + + return self->priv->server; +} + +/** + * tp_contact_search_get_limit: + * @self: a contact search object + * + * + * + * Returns: The TpContactSearch:limit property + * + * Since: 0.13.UNRELEASED + */ +guint +tp_contact_search_get_limit (TpContactSearch *self) +{ + g_return_val_if_fail (TP_IS_CONTACT_SEARCH (self), 0); + + return self->priv->limit; +} + +static void +contact_search_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpContactSearch *self = TP_CONTACT_SEARCH (initable); + + self->priv->async_res = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, tp_contact_search_new_async); + + tp_contact_search_open_new_channel (self); +} + +static gboolean +contact_search_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), + error)) + return FALSE; + + return TRUE; +} + +static void +async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = contact_search_init_async; + iface->init_finish = contact_search_init_finish; +} diff --git a/telepathy-glib/contact-search.h b/telepathy-glib/contact-search.h new file mode 100644 index 0000000..c77c804 --- /dev/null +++ b/telepathy-glib/contact-search.h @@ -0,0 +1,101 @@ +/* + * contact-search.h - a representation for an ongoing search for contacts + * + * Copyright (C) 2010 Collabora Ltd. + * + * The code contained in this file 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 file 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 code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __TP_CONTACT_SEARCH_H__ +#define __TP_CONTACT_SEARCH_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define TP_TYPE_CONTACT_SEARCH \ + (tp_contact_search_get_type ()) +#define TP_CONTACT_SEARCH(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TYPE_CONTACT_SEARCH, \ + TpContactSearch)) +#define TP_CONTACT_SEARCH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TYPE_CONTACT_SEARCH, \ + TpContactSearchClass)) +#define TP_IS_CONTACT_SEARCH(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TYPE_CONTACT_SEARCH)) +#define TP_IS_CONTACT_SEARCH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TYPE_CONTACT_SEARCH)) +#define TP_CONTACT_SEARCH_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_CONTACT_SEARCH, \ + TpContactSearchClass)) + +typedef struct _TpContactSearch TpContactSearch; +typedef struct _TpContactSearchPrivate TpContactSearchPrivate; +typedef struct _TpContactSearchClass TpContactSearchClass; + +struct _TpContactSearch +{ + /**/ + GObject parent; + TpContactSearchPrivate *priv; +}; + +struct _TpContactSearchClass +{ + /**/ + GObjectClass parent_class; + GCallback _padding[7]; +}; + +GType tp_contact_search_get_type (void); + +void tp_contact_search_new_async (TpAccount *account, + const gchar *server, + guint limit, + GAsyncReadyCallback callback, + gpointer user_data); +TpContactSearch *tp_contact_search_new_finish (GAsyncResult *result, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +void tp_contact_search_reset_async (TpContactSearch *self, + const gchar *server, + guint limit, + GAsyncReadyCallback callback, + gpointer user_data); + +const gchar * const * +/* this comment stops gtkdoc denying that this function exists */ +tp_contact_search_reset_finish (TpContactSearch *self, + GAsyncResult *result, + GError **error); + +void tp_contact_search_start (TpContactSearch *self, + GHashTable *criteria); + +const gchar * const * +/* this comment stops gtkdoc denying that this function exists */ +tp_contact_search_get_search_keys (TpContactSearch *self); + +TpAccount * tp_contact_search_get_account (TpContactSearch *self); +const gchar * tp_contact_search_get_server (TpContactSearch *self); +guint tp_contact_search_get_limit (TpContactSearch *self); + +G_END_DECLS + +#endif diff --git a/telepathy-glib/introspection.am b/telepathy-glib/introspection.am index b960c9d..44ba270 100644 --- a/telepathy-glib/introspection.am +++ b/telepathy-glib/introspection.am @@ -28,6 +28,8 @@ INTROSPECTION_FILES = \ $(srcdir)/dbus.c $(srcdir)/dbus.h \ $(srcdir)/capabilities.c $(srcdir)/capabilities.h \ $(srcdir)/contact.c $(srcdir)/contact.h \ + $(srcdir)/contact-search.c $(srcdir)/contact-search.h \ + $(srcdir)/contact-search-result.c $(srcdir)/contact-search-result.h \ $(srcdir)/defs.h \ $(srcdir)/debug.c $(srcdir)/debug.h \ $(srcdir)/base-client.c $(srcdir)/base-client.h \ diff --git a/telepathy-glib/telepathy-glib.h b/telepathy-glib/telepathy-glib.h index 52b7f00..a2c4765 100644 --- a/telepathy-glib/telepathy-glib.h +++ b/telepathy-glib/telepathy-glib.h @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/tests/Makefile.am b/tests/Makefile.am index ec52a94..8f211a4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,7 +16,8 @@ noinst_PROGRAMS = \ test-intset \ test-signal-connect-object \ test-util \ - test-debug-domain + test-debug-domain \ + test-contact-search-result TESTS = $(noinst_PROGRAMS) \ all-errors-documented.py @@ -62,6 +63,14 @@ test_capabilities_LDADD = \ $(top_builddir)/telepathy-glib/libtelepathy-glib-internal.la \ $(GLIB_LIBS) +# this one uses internal ABI +test_contact_search_result_SOURCES = \ + contact-search-result.c +test_contact_search_result_LDADD = \ + $(top_builddir)/tests/lib/libtp-glib-tests.la \ + $(top_builddir)/telepathy-glib/libtelepathy-glib-internal.la \ + $(GLIB_LIBS) + test_dtmf_player_SOURCES = dtmf-player.c test_dtmf_player_LDADD = \ $(top_builddir)/tests/lib/libtp-glib-tests.la \ diff --git a/tests/contact-search-result.c b/tests/contact-search-result.c new file mode 100644 index 0000000..bcb1e91 --- /dev/null +++ b/tests/contact-search-result.c @@ -0,0 +1,73 @@ +/* Tests of TpContactSearchResult + * + * Copyright © 2010 Collabora Ltd. + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#include + +#include "telepathy-glib/contact-search-internal.h" + +#include +#include +#include +#include +#include +#include + +#include "tests/lib/util.h" + +static void +test_contact_search_result (void) +{ + TpContactSearchResult *result; + TpContactInfoField *field; + const gchar *identifier; + GList *fields; + gchar *field_value = "Joe"; + + result = _tp_contact_search_result_new ("id"); + g_assert (TP_IS_CONTACT_SEARCH_RESULT (result)); + + identifier = tp_contact_search_result_get_identifier (result); + g_assert_cmpstr (identifier, ==, "id"); + + fields = tp_contact_search_result_get_fields (result); + g_assert (fields == NULL); + + field = tp_contact_search_result_get_field (result, "fn"); + g_assert (field == NULL); + + field = tp_contact_info_field_new ("fn", NULL, &field_value); + g_assert (field != NULL); + + _tp_contact_search_result_insert_field (result, field); + fields = tp_contact_search_result_get_fields (result); + g_assert (fields != NULL); + g_list_free (fields); + + field = tp_contact_search_result_get_field (result, "fn"); + g_assert (field != NULL); + g_assert_cmpstr (field->field_value[0], ==, field_value); + + g_object_unref (result); +} + +int +main (int argc, + char **argv) +{ + g_type_init (); + tp_debug_set_flags ("all"); + + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add_func ("/contact-search/contact-search-result", + test_contact_search_result); + + return g_test_run (); +} diff --git a/tests/dbus/Makefile.am b/tests/dbus/Makefile.am index 93df1ce..36a353b 100644 --- a/tests/dbus/Makefile.am +++ b/tests/dbus/Makefile.am @@ -24,6 +24,7 @@ noinst_PROGRAMS = \ test-connection-interests \ test-connection-getinterfaces-failure \ test-contact-lists \ + test-contact-search \ test-contacts \ test-contacts-bug-19101 \ test-contacts-mixin \ @@ -119,6 +120,14 @@ test_connection_interests_SOURCES = connection-interests.c test_connection_getinterfaces_failure_SOURCES = \ connection-getinterfaces-failure.c +# this one uses internal ABI +test_contact_search_SOURCES = \ + contact-search.c +test_contact_search_LDADD = \ + $(top_builddir)/tests/lib/libtp-glib-tests.la \ + $(top_builddir)/telepathy-glib/libtelepathy-glib-internal.la \ + $(GLIB_LIBS) + test_contacts_SOURCES = contacts.c test_contacts_bug_19101_SOURCES = contacts-bug-19101.c diff --git a/tests/dbus/contact-search.c b/tests/dbus/contact-search.c new file mode 100644 index 0000000..8f95c0f --- /dev/null +++ b/tests/dbus/contact-search.c @@ -0,0 +1,70 @@ +/* Tests of TpContactSearch and TpContactSearchResult + * + * Copyright © 2010 Collabora Ltd. + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#include + +#include "telepathy-glib/contact-search-internal.h" + +#include +#include +#include +#include +#include +#include + +#include "tests/lib/util.h" + +static TpContactSearch *search; + +static void +on_searcher_created (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + search = tp_contact_search_new_finish (result, &error); + g_assert (TP_IS_CONTACT_SEARCH (search)); + g_assert_no_error (error); +} + +static void +test_contact_search (void) +{ + TpDBusDaemon *dbus = tp_tests_dbus_daemon_dup_or_die (); + TpAccount *account = tp_account_new (dbus, + "/org/freedesktop/Telepathy/Account/what/ev/er", NULL); + + tp_contact_search_new_async (account, + NULL, + 0, + on_searcher_created, + NULL); + + while (search == NULL) + g_main_context_iteration (NULL, TRUE); + + g_assert (TP_IS_CONTACT_SEARCH (search)); +} + +int +main (int argc, + char **argv) +{ + g_type_init (); + tp_debug_set_flags ("all"); + + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add_func ("/contact-search/contact-search", + test_contact_search); + + return g_test_run (); +}