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 ();
+}