diff -r 46cc8e302e43 -r 3404599e4dda telepathygabble/src/disco.c --- a/telepathygabble/src/disco.c Wed Mar 31 22:32:38 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,981 +0,0 @@ -/* - * gabble-media-stream-glue.h - * - * Copyright (C) 2006 Collabora Ltd. - * - * - * This library 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 library 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 library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - - */ - - - -#include -#include -#include -#include -#include "loudmouth/loudmouth.h" - -#include "debug.h" -#include "disco.h" -#include "gabble-connection.h" -#include "gabble-error.h" -#include "namespaces.h" -#include "telepathy-helpers.h" -#include "util.h" -#include "search-keys-info.h" - - -#ifdef EMULATOR -#include "libgabble_wsd_solution.h" - - GET_STATIC_VAR_FROM_TLS(quark,gabble_disco,GQuark) - #define quark (*GET_WSD_VAR_NAME(quark,gabble_disco,s)()) - - GET_STATIC_VAR_FROM_TLS(gabble_disco_parent_class,gabble_disco,gpointer) - #define gabble_disco_parent_class (*GET_WSD_VAR_NAME(gabble_disco_parent_class,gabble_disco,s)()) - - GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_disco,GType) - #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_disco,s)()) - - - -static void gabble_disco_init (GabbleDisco *self); -static void gabble_disco_class_init (GabbleDiscoClass *klass); -static void gabble_disco_class_intern_init (gpointer klass) -{ -gabble_disco_parent_class = g_type_class_peek_parent (klass); -gabble_disco_class_init ((GabbleDiscoClass*) klass); -} -EXPORT_C GType gabble_disco_get_type (void) -{ - -if ((g_define_type_id == 0)) -{ -static const GTypeInfo g_define_type_info = - { sizeof (GabbleDiscoClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_disco_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleDisco), 0, (GInstanceInitFunc) gabble_disco_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleDisco"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; - } ; - - -#endif - - -#define DBUS_API_SUBJECT_TO_CHANGE -#define DEBUG_FLAG GABBLE_DEBUG_DISCO -#define DEFAULT_REQUEST_TIMEOUT 20000 -#define DISCO_PIPELINE_SIZE 10 - - -/* Properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -#ifndef EMULATOR -G_DEFINE_TYPE(GabbleDisco, gabble_disco, G_TYPE_OBJECT); -#endif - -typedef struct _GabbleDiscoPrivate GabbleDiscoPrivate; -struct _GabbleDiscoPrivate -{ - GabbleConnection *connection; - GSList *service_cache; - GList *requests; - gboolean dispose_has_run; -}; - -struct _GabbleDiscoRequest -{ - GabbleDisco *disco; - guint timer_id; - - GabbleDiscoType type; - gchar *jid; - gchar *node; - GabbleDiscoCb callback; - gpointer user_data; - GObject *bound_object; -}; - -GQuark -gabble_disco_error_quark (void) -{ - -#ifndef EMULATOR - static GQuark quark = 0; -#endif - - if (!quark) - quark = g_quark_from_static_string ("gabble-disco-error"); - return quark; -} - -#define GABBLE_DISCO_GET_PRIVATE(o) ((GabbleDiscoPrivate*)((o)->priv)); - - - - - - -static void -gabble_disco_init (GabbleDisco *obj) -{ - GabbleDiscoPrivate *priv = - G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_DISCO, GabbleDiscoPrivate); - obj->priv = priv; -} - - - - -static GObject *gabble_disco_constructor (GType type, guint n_props, - GObjectConstructParam *props); -static void gabble_disco_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec); -static void gabble_disco_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec); -static void gabble_disco_dispose (GObject *object); -static void gabble_disco_finalize (GObject *object); - -static void -gabble_disco_class_init (GabbleDiscoClass *gabble_disco_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_disco_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_disco_class, sizeof (GabbleDiscoPrivate)); - - object_class->constructor = gabble_disco_constructor; - - object_class->get_property = gabble_disco_get_property; - object_class->set_property = gabble_disco_set_property; - - object_class->dispose = gabble_disco_dispose; - object_class->finalize = gabble_disco_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "XMPP Discovery object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); -} - -static void -gabble_disco_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleDisco *chan = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->connection); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_disco_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleDisco *chan = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - priv->connection = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_disco_conn_status_changed_cb (GabbleConnection *conn, - TpConnectionStatus status, TpConnectionStatusReason reason, gpointer data); - -static GObject * -gabble_disco_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleDisco *disco; - GabbleDiscoPrivate *priv; - - obj = G_OBJECT_CLASS (gabble_disco_parent_class)-> constructor (type, - n_props, props); - disco = GABBLE_DISCO (obj); - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_signal_connect (priv->connection, "status-changed", - G_CALLBACK (gabble_disco_conn_status_changed_cb), disco); - - return obj; -} - -static void cancel_request (GabbleDiscoRequest *request); - -void -gabble_disco_dispose (GObject *object) -{ - GSList *l; - DBusGProxy *bus_proxy; - - GabbleDisco *self = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (self); - bus_proxy = tp_get_bus_proxy (); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - gabble_debug (DEBUG_FLAG, "dispose called"); - - /* cancel request removes the element from the list after cancelling */ - while (priv->requests) - cancel_request (priv->requests->data); - - for (l = priv->service_cache; l; l = g_slist_next (l)) - { - GabbleDiscoItem *item = (GabbleDiscoItem *) l->data; - g_free ((char *) item->jid); - g_free ((char *) item->name); - g_free ((char *) item->type); - g_free ((char *) item->category); - g_hash_table_destroy (item->features); - g_free (item); - } - - g_slist_free (priv->service_cache); - priv->service_cache = NULL; - - if (G_OBJECT_CLASS (gabble_disco_parent_class)->dispose) - G_OBJECT_CLASS (gabble_disco_parent_class)->dispose (object); -} - -void -gabble_disco_finalize (GObject *object) -{ - gabble_debug (DEBUG_FLAG, "called with %p", object); - - G_OBJECT_CLASS (gabble_disco_parent_class)->finalize (object); -} - -/** - * gabble_disco_new: - * @conn: The #GabbleConnection to use for service discovery - * - * Creates an object to use for Jabber service discovery (DISCO) - * There should be one of these per connection - */ -GabbleDisco * -gabble_disco_new (GabbleConnection *conn) -{ - GabbleDisco *disco; - - g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); - - disco = GABBLE_DISCO (g_object_new (GABBLE_TYPE_DISCO, - "connection", conn, - NULL)); - - return disco; -} - - -static void notify_delete_request (gpointer data, GObject *obj); - -static void -delete_request (GabbleDiscoRequest *request) -{ - GabbleDisco *disco = request->disco; - GabbleDiscoPrivate *priv; - - g_assert (NULL != request); - /* fix: Application Crashed: Main */ - if ( disco == NULL ) - { - g_debug ("%s: accesing after dereferenced connection", G_STRFUNC); - return; - } - g_assert (GABBLE_IS_DISCO (disco)); - - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_assert (NULL != g_list_find (priv->requests, request)); - - priv->requests = g_list_remove (priv->requests, request); - - if (NULL != request->bound_object) - { - g_object_weak_unref (request->bound_object, notify_delete_request, request); - } - - if (0 != request->timer_id) - { - g_source_remove (request->timer_id); - } - - g_free (request->jid); - g_free (request); -} - -static gboolean -timeout_request (gpointer data) -{ - GabbleDiscoRequest *request = (GabbleDiscoRequest*) data; - GError *err; - g_return_val_if_fail (data != NULL, FALSE); - - err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_TIMEOUT, - "Request for %s on %s timed out", - (request->type == GABBLE_DISCO_TYPE_INFO)?"info":"items", - request->jid); - (request->callback)(request->disco, request, request->jid, request->node, - NULL, err, request->user_data); - g_error_free (err); - - request->timer_id = 0; - delete_request (request); - return FALSE; -} - -static void -cancel_request (GabbleDiscoRequest *request) -{ - GError *err; - - g_assert (request != NULL); - - err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_CANCELLED, - "Request for %s on %s cancelled", - (request->type == GABBLE_DISCO_TYPE_INFO)?"info":"items", - request->jid); - (request->callback)(request->disco, request, request->jid, request->node, - NULL, err, request->user_data); - g_error_free (err); - - delete_request (request); -} - -static LmHandlerResult -request_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, gpointer user_data) -{ - LmMessageNode *query_node; - GError *err = NULL; - - GabbleDiscoRequest *request = (GabbleDiscoRequest*) user_data; - GabbleDisco *disco = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_assert (request); - - if (!g_list_find (priv->requests, request)) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - query_node = lm_message_node_get_child (reply_msg->node, "query"); - - if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR) - { - LmMessageNode *error_node; - - error_node = lm_message_node_get_child (reply_msg->node, "error"); - if (error_node) - { - err = gabble_xmpp_error_to_g_error ( - gabble_xmpp_error_from_node (error_node)); - } - - if (err == NULL) - { - err = g_error_new (GABBLE_DISCO_ERROR, - GABBLE_DISCO_ERROR_UNKNOWN, - "an unknown error occurred"); - } - } - else if (NULL == query_node) - { - err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_UNKNOWN, - "disco response contained no node"); - } - - request->callback (request->disco, request, request->jid, request->node, - query_node, err, request->user_data); - delete_request (request); - - if (err) - g_error_free (err); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -notify_delete_request (gpointer data, GObject *obj) -{ - GabbleDiscoRequest *request = (GabbleDiscoRequest *) data; - request->bound_object = NULL; - delete_request (request); -} - -/** - * gabble_disco_request: - * @self: #GabbleDisco object to use for request - * @type: type of request - * @jid: Jabber ID to request on - * @node: node to request on @jid, or NULL - * @callback: #GabbleDiscoCb to call on request fullfilment - * @object: GObject to bind request to. the callback will not be - * called if this object has been unrefed. NULL if not needed - * @error: #GError to return a telepathy error in if unable to make - * request, NULL if unneeded. - * - * Make a disco request on the given jid with the default timeout. - */ -GabbleDiscoRequest * -gabble_disco_request (GabbleDisco *self, GabbleDiscoType type, - const gchar *jid, const char *node, - GabbleDiscoCb callback, gpointer user_data, - GObject *object, GError **error) -{ - return gabble_disco_request_with_timeout (self, type, jid, node, - DEFAULT_REQUEST_TIMEOUT, - callback, user_data, - object, error); -} - -/** - * gabble_disco_request_with_timeout: - * @self: #GabbleDisco object to use for request - * @type: type of request - * @jid: Jabber ID to request on - * @node: node to request on @jid, or NULL - * @timeout: the time until the request fails, in milliseconds (1/1000ths of a second) - * @callback: #GabbleDiscoCb to call on request fullfilment - * @object: GObject to bind request to. the callback will not be - * called if this object has been unrefed. NULL if not needed - * @error: #GError to return a telepathy error in if unable to make - * request, NULL if unneeded. - * - * Make a disco request on the given jid, which will fail unless a reply - * is received within the given timeout interval. - */ -GabbleDiscoRequest * -gabble_disco_request_with_timeout (GabbleDisco *self, GabbleDiscoType type, - const gchar *jid, const char *node, - guint timeout, GabbleDiscoCb callback, - gpointer user_data, GObject *object, - GError **error) -{ - const gchar *xmlns; - - LmMessage *msg; - LmMessageNode *lm_node; - GabbleDiscoRequest *request; - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (self); - - - request = g_new0 (GabbleDiscoRequest, 1); - request->disco = self; - request->type = type; - request->jid = g_strdup (jid); - if (node) - request->node = g_strdup (node); - request->callback = callback; - request->user_data = user_data; - request->bound_object = object; - - if (NULL != object) - g_object_weak_ref (object, notify_delete_request, request); - - gabble_debug (DEBUG_FLAG, "Creating disco request %p for %s", - request, request->jid); - - priv->requests = g_list_prepend (priv->requests, request); - msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_GET); - lm_node = lm_message_node_add_child (msg->node, "query", NULL); - - switch (type) { - case GABBLE_DISCO_TYPE_INFO: - xmlns = NS_DISCO_INFO; - break; - case GABBLE_DISCO_TYPE_ITEMS: - xmlns = NS_DISCO_ITEMS; - break; - default: - g_assert_not_reached (); - return NULL; - } - - lm_message_node_set_attribute (lm_node, "xmlns", xmlns); - - if (node) - { - lm_message_node_set_attribute (lm_node, "node", node); - } - - if (! _gabble_connection_send_with_reply (priv->connection, msg, - request_reply_cb, G_OBJECT(self), request, error)) - { - delete_request (request); - lm_message_unref (msg); - return NULL; - } - else - { - request->timer_id = - g_timeout_add (timeout, timeout_request, request); - lm_message_unref (msg); - return request; - } -} - -void -gabble_disco_cancel_request (GabbleDisco *disco, GabbleDiscoRequest *request) -{ - GabbleDiscoPrivate *priv; - - g_return_if_fail (GABBLE_IS_DISCO (disco)); - g_return_if_fail (NULL != request); - - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_return_if_fail (NULL != g_list_find (priv->requests, request)); - - cancel_request (request); -} - -/* Disco pipeline */ - - -typedef struct _GabbleDiscoPipeline GabbleDiscoPipeline; -struct _GabbleDiscoPipeline { - GabbleDisco *disco; - gpointer user_data; - GabbleDiscoPipelineCb callback; - GabbleDiscoEndCb end_callback; - GPtrArray *disco_pipeline; - GHashTable *remaining_items; - GabbleDiscoRequest *list_request; - gboolean running; -}; - -static void -gabble_disco_fill_pipeline (GabbleDisco *disco, GabbleDiscoPipeline *pipeline); - -static void -item_info_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *result, - GError *error, - gpointer user_data) -{ - LmMessageNode *identity, *feature, *field, *value_node; - const char *category, *type, *var, *name, *value; - GHashTable *keys; - GabbleDiscoItem item; - - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) user_data; - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_ptr_array_remove_fast (pipeline->disco_pipeline, request); - - if (error) - { - gabble_debug (DEBUG_FLAG, "got error %s", error->message); - goto done; - } - - identity = lm_message_node_get_child (result, "identity"); - if (NULL == identity) - goto done; - - name = lm_message_node_get_attribute (identity, "name"); - if (NULL == name) - goto done; - - category = lm_message_node_get_attribute (identity, "category"); - if (NULL == category) - goto done; - - type = lm_message_node_get_attribute (identity, "type"); - if (NULL == type) - goto done; - - gabble_debug (DEBUG_FLAG, "got item identity, jid=%s, name=%s, category=%s, type=%s", - jid, name, category, type); - - keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - for (feature = result->children; feature; feature = feature->next) - { - if (0 == strcmp (feature->name, "feature")) - { - var = lm_message_node_get_attribute (feature, "var"); - if (0 == strcmp (var, NS_SEARCH)) - { - priv->connection->features |= GABBLE_CONNECTION_FEATURES_SEARCH; - get_search_keys_info(priv->connection,jid); - } - if (var) - g_hash_table_insert (keys, g_strdup (var), NULL); - } - else if (0 == strcmp (feature->name, "x")) - { - if (lm_message_node_has_namespace (feature, NS_X_DATA, NULL)) - { - for (field = feature->children; - field; field = field->next) - { - if (0 != strcmp (field->name, "field")) - continue; - - var = lm_message_node_get_attribute (field, "var"); - if (NULL == var) - continue; - - value_node = lm_message_node_get_child (field, "value"); - if (NULL == value_node) - continue; - - value = lm_message_node_get_value (value_node); - if (NULL == value) - continue; - - g_hash_table_insert (keys, g_strdup (var), g_strdup (value)); - } - } - } - } - - item.jid = jid; - item.name = name; - item.category = category; - item.type = type; - item.features = keys; - - pipeline->callback (pipeline, &item, pipeline->user_data); - g_hash_table_destroy (keys); - -done: - gabble_disco_fill_pipeline (disco, pipeline); - - return; -} - - -static gboolean -return_true (gpointer key, gpointer value, gpointer data) -{ - return TRUE; -} - -static void -gabble_disco_fill_pipeline (GabbleDisco *disco, GabbleDiscoPipeline *pipeline) -{ - if (!pipeline->running) - { - gabble_debug (DEBUG_FLAG, "pipeline not running, not refilling"); - } - else - { - /* send disco requests for the JIDs in the remaining_items hash table - * until there are DISCO_PIPELINE_SIZE requests in progress */ - while (pipeline->disco_pipeline->len < DISCO_PIPELINE_SIZE) - { - gchar *jid; - GabbleDiscoRequest *request; - - jid = (gchar *) g_hash_table_find (pipeline->remaining_items, - return_true, NULL); - if (NULL == jid) - break; - - request = gabble_disco_request (disco, - GABBLE_DISCO_TYPE_INFO, jid, NULL, item_info_cb, pipeline, - G_OBJECT(disco), NULL); - - g_ptr_array_add (pipeline->disco_pipeline, request); - - /* frees jid */ - g_hash_table_remove (pipeline->remaining_items, jid); - } - - if (0 == pipeline->disco_pipeline->len) - { - /* signal that the pipeline has finished */ - pipeline->running = FALSE; - pipeline->end_callback (pipeline, pipeline->user_data); - } - } -} - - -static void -disco_items_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *result, - GError *error, - gpointer user_data) -{ - LmMessageNode *iter; - const char *item_jid; - gpointer key, value; - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) user_data; - - pipeline->list_request = NULL; - - if (error) - { - gabble_debug (DEBUG_FLAG, "Got error on items request: %s", error->message); - goto out; - } - - iter = result->children; - - for (; iter; iter = iter->next) - { - if (0 != strcmp (iter->name, "item")) - continue; - - item_jid = lm_message_node_get_attribute (iter, "jid"); - - if (NULL != item_jid && - !g_hash_table_lookup_extended (pipeline->remaining_items, item_jid, &key, &value)) - { - gchar *tmp = g_strdup (item_jid); - gabble_debug (DEBUG_FLAG, "discovered service item: %s", tmp); - g_hash_table_insert (pipeline->remaining_items, tmp, tmp); - } - } - -out: - gabble_disco_fill_pipeline (disco, pipeline); -} - -/** - * gabble_disco_pipeline_init: - * @disco: disco object to use in the pipeline - * @callback: GFunc to call on request fullfilment - * @user_data: the usual - * - * Prepares the pipeline for making the ITEM request on the server and - * subsequent INFO elements on returned items. - * - * GabbleDiscoPipeline is opaque structure for the user. - */ -gpointer gabble_disco_pipeline_init (GabbleDisco *disco, - GabbleDiscoPipelineCb callback, - GabbleDiscoEndCb end_callback, - gpointer user_data) -{ - GabbleDiscoPipeline *pipeline = g_new (GabbleDiscoPipeline, 1); - pipeline->user_data = user_data; - pipeline->callback = callback; - pipeline->end_callback = end_callback; - pipeline->disco_pipeline = g_ptr_array_sized_new (DISCO_PIPELINE_SIZE); - pipeline->remaining_items = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - pipeline->running = TRUE; - pipeline->disco = disco; - - return pipeline; -} - -/** - * gabble_disco_pipeline_run: - * @self: reference to the pipeline structure - * @server: server to query - * - * Makes ITEMS request on the server, and afterwards queries for INFO - * on each item. INFO queries are pipelined. The item properties are stored - * in hash table parameter to the callback function. The user is responsible - * for destroying the hash table after it's done with. - * - * Upon returning all the results, the end_callback is called with - * reference to the pipeline. - */ -void -gabble_disco_pipeline_run (gpointer self, const char *server) -{ - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) self; - - pipeline->running = TRUE; - - pipeline->list_request = gabble_disco_request (pipeline->disco, - GABBLE_DISCO_TYPE_ITEMS, server, NULL, disco_items_cb, pipeline, - G_OBJECT (pipeline->disco), NULL); -} - - -/** - * gabble_disco_pipeline_cancel: - * @pipeline: pipeline to cancel - * - * Flushes the pipeline (cancels all pending disco requests) and - * destroys it. - */ -void -gabble_disco_pipeline_destroy (gpointer self) -{ - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) self; - - pipeline->running = FALSE; - - if (pipeline->list_request != NULL) - { - gabble_disco_cancel_request (pipeline->disco, pipeline->list_request); - pipeline->list_request = NULL; - } - - /* iterate using a while loop otherwise we're modifying - * the array as we iterate it, and miss things! */ - while (pipeline->disco_pipeline->len > 0) - { - GabbleDiscoRequest *request = - g_ptr_array_index (pipeline->disco_pipeline, 0); - gabble_disco_cancel_request (pipeline->disco, request); - } - - g_hash_table_destroy (pipeline->remaining_items); - g_ptr_array_free (pipeline->disco_pipeline, TRUE); - g_free (pipeline); -} - - -static void -service_feature_copy_one (gpointer k, gpointer v, gpointer user_data) -{ - char *key = (char *) k; - char *value = (char *) v; - - GHashTable *target = (GHashTable *) user_data; - g_hash_table_insert (target, g_strdup (key), g_strdup (value)); -} - -/* Service discovery */ -static void -services_cb (gpointer pipeline, GabbleDiscoItem *item, gpointer user_data) -{ - GabbleDiscoItem *my_item = g_new0 (GabbleDiscoItem, 1); - - GabbleDisco *disco = GABBLE_DISCO (user_data); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - my_item->jid = g_strdup (item->jid); - my_item->name = g_strdup (item->name); - my_item->type = g_strdup (item->type); - my_item->category = g_strdup (item->category); - - my_item->features = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - g_hash_table_foreach (item->features, service_feature_copy_one, my_item->features); - - priv->service_cache = g_slist_prepend (priv->service_cache, my_item); -} - -static void -end_cb (gpointer pipeline, gpointer user_data) -{ - GabbleDisco *disco = GABBLE_DISCO (user_data); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - gabble_disco_pipeline_destroy (pipeline); - priv->service_cache = g_slist_reverse (priv->service_cache); - - /* FIXME - service discovery done - signal that somehow */ -} - -static void -gabble_disco_conn_status_changed_cb (GabbleConnection *conn, - TpConnectionStatus status, - TpConnectionStatusReason reason, - gpointer data) -{ - GabbleDisco *disco = GABBLE_DISCO (data); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - if (status == TP_CONN_STATUS_CONNECTED) - { - char *server; - gpointer pipeline = gabble_disco_pipeline_init (disco, services_cb, - end_cb, disco); - - g_object_get (priv->connection, "stream-server", &server, NULL); - - g_assert (server != NULL); - - gabble_debug (DEBUG_FLAG, "connected, initiating service discovery on %s", server); - gabble_disco_pipeline_run (pipeline, server); - - g_free (server); - } -} - -const GabbleDiscoItem * -gabble_disco_service_find (GabbleDisco *disco, - const char *type, - const char *category, - const char *feature) -{ - GabbleDiscoPrivate *priv; - GSList *l; - - g_assert (GABBLE_IS_DISCO (disco)); - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - for (l = priv->service_cache; l; l = g_slist_next (l)) - { - GabbleDiscoItem *item = (GabbleDiscoItem *) l->data; - gboolean selected = TRUE; - - if (type != NULL && g_strdiff (type, item->type)) - selected = FALSE; - - if (category != NULL && g_strdiff (category, item->category)) - selected = FALSE; - - if (feature != NULL) - { - gpointer k, v; - if (!g_hash_table_lookup_extended (item->features, feature, &k, &v)) - selected = FALSE; - } - - if (selected) - return item; - } - - return NULL; -}