diff -r d0f3a028347a -r 59927b2d3b75 telepathygabble/src/gabble-presence-cache.c --- a/telepathygabble/src/gabble-presence-cache.c Tue Feb 02 01:10:06 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1267 +0,0 @@ -/* - * gabble-presence-cache.c - Gabble's contact presence cache - * Copyright (C) 2005 Collabora Ltd. - * and/or its subsidiaries. All rights reserved. - * - * 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 "debug.h" -#include "disco.h" /* \o\ \o/ /o/ */ -#include "gabble-presence.h" -#include "namespaces.h" -#include "util.h" -#include "handle-set.h" -#include "gintset.h" - -#include "gabble-presence-cache.h" - -#include "gabble-presence-cache-signals-marshal.h" - -#include "gabble_enums.h" - -#ifndef EMULATOR -G_DEFINE_TYPE (GabblePresenceCache, gabble_presence_cache, G_TYPE_OBJECT); -#endif - -/* when five DIFFERENT guys report the same caps for a given bundle, it'll be enough */ -#define CAPABILITY_BUNDLE_ENOUGH_TRUST 5 -#define DEBUG_FLAG GABBLE_DEBUG_PRESENCE - -#ifdef DEBUG_FLAG -//#define DEBUG(format, ...) -#define DEBUGGING 0 -#define NODE_DEBUG(n, s) -#endif /* DEBUG_FLAG */ - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -/* signal enum */ -enum -{ - PRESENCE_UPDATE, - NICKNAME_UPDATE, - CAPABILITIES_UPDATE, - LAST_SIGNAL -#ifdef EMULATOR - = LAST_SIGNAL_PRE_CACHE -#endif - -}; - - -#ifdef EMULATOR -#include "libgabble_wsd_solution.h" - - GET_STATIC_ARRAY_FROM_TLS(signals,gabble_pre_cache,guint) - #define signals (GET_WSD_VAR_NAME(signals,gabble_pre_cache, s)()) - - GET_STATIC_VAR_FROM_TLS(gabble_presence_cache_parent_class,gabble_pre_cache,gpointer) - #define gabble_presence_cache_parent_class (*GET_WSD_VAR_NAME(gabble_presence_cache_parent_class,gabble_pre_cache,s)()) - - GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_pre_cache,GType) - #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_pre_cache,s)()) - - -static void gabble_presence_cache_init (GabblePresenceCache *self); -static void gabble_presence_cache_class_init (GabblePresenceCacheClass *klass); -static void gabble_presence_cache_class_intern_init (gpointer klass) - { - gabble_presence_cache_parent_class = g_type_class_peek_parent (klass); - gabble_presence_cache_class_init ((GabblePresenceCacheClass*) klass); - } - EXPORT_C GType gabble_presence_cache_get_type (void) - { - if ((g_define_type_id == 0)) - { static const GTypeInfo g_define_type_info = { sizeof (GabblePresenceCacheClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_presence_cache_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabblePresenceCache), 0, (GInstanceInitFunc) gabble_presence_cache_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabblePresenceCache"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; - }; - - -#else - - static guint signals[LAST_SIGNAL] = {0}; - -#endif - - -#define GABBLE_PRESENCE_CACHE_PRIV(account) ((GabblePresenceCachePrivate *)account->priv) - -typedef struct _GabblePresenceCachePrivate GabblePresenceCachePrivate; - -struct _GabblePresenceCachePrivate -{ - GabbleConnection *conn; - - gulong status_changed_cb; - LmMessageHandler *lm_message_cb; - - GHashTable *presence; - GabbleHandleSet *presence_handles; - - GHashTable *capabilities; - GHashTable *disco_pending; - guint caps_serial; - - gboolean dispose_has_run; -}; - -typedef struct _DiscoWaiter DiscoWaiter; - -struct _DiscoWaiter -{ - GabbleHandleRepo *repo; - GabbleHandle handle; - gchar *resource; - guint serial; - gboolean disco_requested; -}; - -/** - * disco_waiter_new () - */ -static DiscoWaiter * -disco_waiter_new (GabbleHandleRepo *repo, GabbleHandle handle, const gchar *resource, guint serial) -{ - DiscoWaiter *waiter; - - g_assert (repo); - gabble_handle_ref (repo, TP_HANDLE_TYPE_CONTACT, handle); - - waiter = g_new0 (DiscoWaiter, 1); - waiter->repo = repo; - waiter->handle = handle; - waiter->resource = g_strdup (resource); - waiter->serial = serial; - - gabble_debug (DEBUG_FLAG, "created waiter %p for handle %u with serial %u", waiter, handle, serial); - - return waiter; -} - -static void -disco_waiter_free (DiscoWaiter *waiter) -{ - g_assert (NULL != waiter); - - gabble_debug (DEBUG_FLAG, "freeing waiter %p for handle %u with serial %u", waiter, waiter->handle, waiter->serial); - - gabble_handle_unref (waiter->repo, TP_HANDLE_TYPE_CONTACT, waiter->handle); - - g_free (waiter->resource); - g_free (waiter); -} - -static void -disco_waiter_list_free (GSList *list) -{ - GSList *i; - - gabble_debug (DEBUG_FLAG, "list %p", list); - - for (i = list; NULL != i; i = i->next) - disco_waiter_free ((DiscoWaiter *) i->data); - - g_slist_free (list); -} - -static guint -disco_waiter_list_get_request_count (GSList *list) -{ - guint c = 0; - GSList *i; - - for (i = list; i; i = i->next) - { - DiscoWaiter *waiter = (DiscoWaiter *) i->data; - - if (waiter->disco_requested) - c++; - } - - return c; -} - -typedef struct _CapabilityInfo CapabilityInfo; - -struct _CapabilityInfo -{ - GabblePresenceCapabilities caps; - GIntSet *guys; - guint trust; -}; - -static CapabilityInfo * -capability_info_get (GabblePresenceCache *cache, const gchar *node, - GabblePresenceCapabilities caps) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - CapabilityInfo *info = g_hash_table_lookup (priv->capabilities, node); - - if (NULL == info) - { - info = g_new0 (CapabilityInfo, 1); - info->caps = caps; - info->guys = g_intset_new (); - g_hash_table_insert (priv->capabilities, g_strdup (node), info); - } - - return info; -} - -static guint -capability_info_recvd (GabblePresenceCache *cache, const gchar *node, - GabbleHandle handle, GabblePresenceCapabilities caps) -{ - CapabilityInfo *info = capability_info_get (cache, node, caps); - - /* Detect inconsistency in reported caps */ - if (info->caps != caps) - { - g_intset_clear (info->guys); - info->caps = caps; - info->trust = 0; - } - - if (!g_intset_is_member (info->guys, handle)) - { - g_intset_add (info->guys, handle); - info->trust++; - } - - return info->trust; -} - -static void gabble_presence_cache_init (GabblePresenceCache *presence_cache); -static GObject * gabble_presence_cache_constructor (GType type, guint n_props, - GObjectConstructParam *props); -static void gabble_presence_cache_dispose (GObject *object); -static void gabble_presence_cache_finalize (GObject *object); -static void gabble_presence_cache_set_property (GObject *object, guint - property_id, const GValue *value, GParamSpec *pspec); -static void gabble_presence_cache_get_property (GObject *object, guint - property_id, GValue *value, GParamSpec *pspec); -static GabblePresence *_cache_insert (GabblePresenceCache *cache, - GabbleHandle handle); - -static void gabble_presence_cache_status_changed_cb (GabbleConnection *, - TpConnectionStatus, TpConnectionStatusReason, gpointer); -static LmHandlerResult gabble_presence_cache_lm_message_cb (LmMessageHandler*, - LmConnection*, LmMessage*, gpointer); - -static void -gabble_presence_cache_class_init (GabblePresenceCacheClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *param_spec; - - g_type_class_add_private (object_class, sizeof (GabblePresenceCachePrivate)); - - object_class->constructor = gabble_presence_cache_constructor; - - object_class->dispose = gabble_presence_cache_dispose; - object_class->finalize = gabble_presence_cache_finalize; - - object_class->get_property = gabble_presence_cache_get_property; - object_class->set_property = gabble_presence_cache_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "presence cache.", - 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); - - signals[PRESENCE_UPDATE] = g_signal_new ( - "presence-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - signals[NICKNAME_UPDATE] = g_signal_new ( - "nickname-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - signals[CAPABILITIES_UPDATE] = g_signal_new ( - "capabilities-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - gabble_presence_cache_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); -} - -static void -gabble_presence_cache_init (GabblePresenceCache *cache) -{ - GabblePresenceCachePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (cache, - GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCachePrivate); - - cache->priv = priv; - - priv->presence = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); - priv->capabilities = g_hash_table_new (g_str_hash, g_str_equal); - priv->disco_pending = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) disco_waiter_list_free); - priv->caps_serial = 1; -} - -static GObject * -gabble_presence_cache_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabblePresenceCachePrivate *priv; - - obj = G_OBJECT_CLASS (gabble_presence_cache_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_PRESENCE_CACHE_PRIV (GABBLE_PRESENCE_CACHE (obj)); - - priv->status_changed_cb = g_signal_connect (priv->conn, "status-changed", - G_CALLBACK (gabble_presence_cache_status_changed_cb), obj); - - return obj; -} - -static void -gabble_presence_cache_dispose (GObject *object) -{ - GabblePresenceCache *self = GABBLE_PRESENCE_CACHE (object); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (self); - - if (priv->dispose_has_run) - return; - - gabble_debug (DEBUG_FLAG, "dispose called"); - - priv->dispose_has_run = TRUE; - - g_assert (priv->lm_message_cb == NULL); - - g_signal_handler_disconnect (priv->conn, priv->status_changed_cb); - - g_hash_table_destroy (priv->presence); - priv->presence = NULL; - - handle_set_destroy (priv->presence_handles); - priv->presence_handles = NULL; - - if (G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose) - G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose (object); -} - -static void -gabble_presence_cache_finalize (GObject *object) -{ - gabble_debug (DEBUG_FLAG, "called with %p", object); - - G_OBJECT_CLASS (gabble_presence_cache_parent_class)->finalize (object); -} - -static void -gabble_presence_cache_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_presence_cache_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - GabbleHandleSet *new_presence_handles; - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - new_presence_handles = handle_set_new (priv->conn->handles, TP_HANDLE_TYPE_CONTACT); - - if (priv->presence_handles) - { - const GIntSet *add; - GIntSet *tmp; - add = handle_set_peek (priv->presence_handles); - tmp = handle_set_update (new_presence_handles, add); - handle_set_destroy (priv->presence_handles); - g_intset_destroy (tmp); - } - priv->presence_handles = new_presence_handles; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -#if 0 -static gboolean -_presence_node_has_google_voice (LmMessageNode *pres_node) -{ - LmMessageNode *node; - const gchar *cap_ext; - gchar **features, **tmp; - gboolean found = FALSE; - - node = lm_message_node_get_child_with_namespace (pres_node, "c", NS_CAPS); - - if (node == NULL); - return FALSE; - - cap_ext = lm_message_node_get_attribute (node, "ext"); - - if (cap_ext == NULL); - return FALSE; - - features = g_strsplit (cap_ext, " ", 0); - - for (tmp = features; *tmp; tmp++) - { - if (!g_strdiff (tmp, "voice-v1")) - { - found = TRUE; - break; - } - } - - g_strfreev (features); - - return found; -} -#endif - -static void -gabble_presence_cache_status_changed_cb (GabbleConnection *conn, - TpConnectionStatus status, - TpConnectionStatusReason reason, - gpointer data) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (data); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - - g_assert (conn == priv->conn); - - switch (status) - { - case TP_CONN_STATUS_CONNECTING: - g_assert (priv->lm_message_cb == NULL); - - priv->lm_message_cb = lm_message_handler_new (gabble_presence_cache_lm_message_cb, - cache, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_PRESENCE, - LM_HANDLER_PRIORITY_LAST); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_MESSAGE, - LM_HANDLER_PRIORITY_FIRST); - break; - case TP_CONN_STATUS_CONNECTED: - /* TODO: emit self presence */ - break; - case TP_CONN_STATUS_DISCONNECTED: - g_assert (priv->lm_message_cb != NULL); - - lm_connection_unregister_message_handler (conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_PRESENCE); - lm_connection_unregister_message_handler (conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_MESSAGE); - lm_message_handler_unref (priv->lm_message_cb); - priv->lm_message_cb = NULL; - break; - default: - g_assert_not_reached (); - } -} - -static GabblePresenceId -_presence_node_get_status (LmMessageNode *pres_node) -{ - const gchar *presence_show; - LmMessageNode *child_node = lm_message_node_get_child (pres_node, "show"); - - if (!child_node) - { - /* - NODE_DEBUG (pres_node, - " without received from server, " - "setting presence to available"); - */ - return GABBLE_PRESENCE_AVAILABLE; - } - - presence_show = lm_message_node_get_value (child_node); - - if (!presence_show) - { - /* - NODE_DEBUG (pres_node, - "empty tag received from server, " - "setting presence to available"); - */ - return GABBLE_PRESENCE_AVAILABLE; - } - - if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_AWAY)) - return GABBLE_PRESENCE_AWAY; - else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_CHAT)) - return GABBLE_PRESENCE_CHAT; - else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_DND)) - return GABBLE_PRESENCE_DND; - else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_XA)) - return GABBLE_PRESENCE_XA; - else - { - NODE_DEBUG (pres_node, - "unrecognised value received from server, " - "setting presence to available"); - return GABBLE_PRESENCE_AVAILABLE; - } -} - -static void -_grab_nickname (GabblePresenceCache *cache, - GabbleHandle handle, - const gchar *from, - LmMessageNode *node) -{ - const gchar *nickname; - GabblePresence *presence; - - node = lm_message_node_get_child_with_namespace (node, "nick", NS_NICK); - - if (NULL == node) - return; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - return; - - nickname = lm_message_node_get_value (node); - gabble_debug (DEBUG_FLAG, "got nickname \"%s\" for %s", nickname, from); - - if (g_strdiff (presence->nickname, nickname)) - { - if (NULL != presence->nickname) - g_free (presence->nickname); - - presence->nickname = g_strdup (nickname); - g_signal_emit (cache, signals[NICKNAME_UPDATE], 0, handle); - } -} - -static GSList * -_extract_cap_bundles (LmMessageNode *lm_node) -{ - const gchar *node, *ver, *ext; - GSList *uris = NULL; - LmMessageNode *cap_node; - - cap_node = lm_message_node_get_child_with_namespace (lm_node, "c", NS_CAPS); - - if (NULL == cap_node) - return NULL; - - node = lm_message_node_get_attribute (cap_node, "node"); - - if (NULL == node) - return NULL; - - ver = lm_message_node_get_attribute (cap_node, "ver"); - - if (NULL != ver) - uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, ver)); - - ext = lm_message_node_get_attribute (cap_node, "ext"); - - if (NULL != ext) - { - gchar **exts, **i; - - exts = g_strsplit (ext, " ", 0); - - for (i = exts; NULL != *i; i++) - uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, *i)); - - g_strfreev (exts); - } - - return uris; -} - -static void -_caps_disco_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *query_result, - GError *error, - gpointer user_data) -{ - GSList *waiters, *i; - LmMessageNode *child; - GabblePresenceCache *cache; - GabblePresenceCachePrivate *priv; - gchar *full_jid = NULL; - GabblePresenceCapabilities caps = 0; - guint trust; - GabbleHandle handle; - - cache = GABBLE_PRESENCE_CACHE (user_data); - priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - - if (NULL == node) - { - g_warning ("got disco response with NULL node, ignoring"); - return; - } - - waiters = g_hash_table_lookup (priv->disco_pending, node); - - if (NULL != error) - { - DiscoWaiter *waiter = NULL; - - gabble_debug (DEBUG_FLAG, "disco query failed: %s", error->message); - - for (i = waiters; NULL != i; i = i->next) - { - waiter = (DiscoWaiter *) i->data; - - if (!waiter->disco_requested) - { - const gchar *jid; - - jid = gabble_handle_inspect (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, - waiter->handle); - full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource); - - gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, node, - _caps_disco_cb, cache, G_OBJECT(cache), NULL); - waiter->disco_requested = TRUE; - break; - } - } - - if (NULL != i) - { - gabble_debug (DEBUG_FLAG, "sent a retry disco request to %s for URI %s", full_jid, node); - } - else - { - gabble_debug (DEBUG_FLAG, "failed to find a suitable candidate to retry disco request for URI %s", node); - /* FIXME do something very clever here? */ - g_hash_table_remove (priv->disco_pending, node); - } - - goto OUT; - } - - for (child = query_result->children; NULL != child; child = child->next) - { - const gchar *var; - - if (0 != strcmp (child->name, "feature")) - continue; - - var = lm_message_node_get_attribute (child, "var"); - - if (NULL == var) - continue; - - /* TODO: use a table that equates disco features to caps */ - if (0 == strcmp (var, NS_GOOGLE_TRANSPORT_P2P)) - caps |= PRESENCE_CAP_GOOGLE_TRANSPORT_P2P; - else if (0 == strcmp (var, NS_GOOGLE_FEAT_VOICE)) - caps |= PRESENCE_CAP_GOOGLE_VOICE; - else if (0 == strcmp (var, NS_JINGLE)) - caps |= PRESENCE_CAP_JINGLE; - else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_AUDIO)) - caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO; - else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_VIDEO)) - caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO; - } - - handle = gabble_handle_for_contact (priv->conn->handles, jid, FALSE); - trust = capability_info_recvd (cache, node, handle, caps); - - for (i = waiters; NULL != i;) - { - DiscoWaiter *waiter; - GabblePresence *presence; - - waiter = (DiscoWaiter *) i->data; - - if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST || waiter->handle == handle) - { - GSList *tmp; - /* trusted reply */ - presence = gabble_presence_cache_get (cache, waiter->handle); - - if (presence) - { - GabblePresenceCapabilities save_caps = presence->caps; - gabble_debug (DEBUG_FLAG, "setting caps for %d (%s) to %d", handle, jid, caps); - gabble_presence_set_capabilities (presence, waiter->resource,caps, - waiter->serial); - gabble_debug (DEBUG_FLAG, "caps for %d (%s) now %d", handle, jid, presence->caps); - g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, - waiter->handle, save_caps, presence->caps); - } - - tmp = i; - i = i->next; - - waiters = g_slist_delete_link (waiters, tmp); - - g_hash_table_steal (priv->disco_pending, node); - g_hash_table_insert (priv->disco_pending, g_strdup (node), waiters); - - disco_waiter_free (waiter); - } - else if (trust + disco_waiter_list_get_request_count (waiters) - 1 - < CAPABILITY_BUNDLE_ENOUGH_TRUST) - { - /* if the possible trust, not counting this guy, is too low, - * we have been poisoned and reset our trust meters - disco - * anybody we still haven't to be able to get more trusted replies */ - - if (!waiter->disco_requested) - { - const gchar *jid; - - jid = gabble_handle_inspect (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, waiter->handle); - full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource); - - gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, - node, _caps_disco_cb, cache, G_OBJECT(cache), NULL); - waiter->disco_requested = TRUE; - - g_free (full_jid); - full_jid = NULL; - } - - i = i->next; - } - else - { - /* trust level still uncertain, don't do nothing */ - i = i->next; - } - } - - if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST) - g_hash_table_remove (priv->disco_pending, node); - -OUT: - - g_free (full_jid); -} - -static void -_process_caps_uri (GabblePresenceCache *cache, - const gchar *from, - const gchar *uri, - GabbleHandle handle, - const gchar *resource, - guint serial) -{ - CapabilityInfo *info; - gpointer value; - GabblePresenceCachePrivate *priv; - - priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - info = capability_info_get (cache, uri, 0); - - if (info->trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST - || g_intset_is_member (info->guys, handle)) - { - /* we already have enough trust for this node; apply the cached value to - * the (handle, resource) */ - - GabblePresence *presence = gabble_presence_cache_get (cache, handle); - gabble_debug (DEBUG_FLAG, "enough trust for URI %s, setting caps for %u (%s) to %u", - uri, handle, from, info->caps); - - if (presence) - { - GabblePresenceCapabilities save_caps = presence->caps; - gabble_presence_set_capabilities (presence, resource, info->caps, serial); - g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, - handle, save_caps, presence->caps); - gabble_debug (DEBUG_FLAG, "caps for %d (%s) now %d", handle, from, presence->caps); - } - else - { - gabble_debug (DEBUG_FLAG, "presence not found"); - } - } - else - { - /* Append the (handle, resource) pair to the list of such pairs - * waiting for capabilities for this uri, and send a disco request - * if we don't have enough possible trust yet */ - - GSList *waiters; - DiscoWaiter *waiter; - guint possible_trust; - - gabble_debug (DEBUG_FLAG, "not enough trust for URI %s", uri); - value = g_hash_table_lookup (priv->disco_pending, uri); - - if (value) - g_hash_table_steal (priv->disco_pending, uri); - - waiters = (GSList *) value; - waiter = disco_waiter_new (priv->conn->handles, handle, resource, serial); - waiters = g_slist_prepend (waiters, waiter); - g_hash_table_insert (priv->disco_pending, g_strdup (uri), waiters); - - possible_trust = disco_waiter_list_get_request_count (waiters); - - if (!value || info->trust + possible_trust < CAPABILITY_BUNDLE_ENOUGH_TRUST) - { - /* DISCO */ - gabble_debug (DEBUG_FLAG, "only %u trust out of %u possible thus far, sending disco for URI %s", - info->trust + possible_trust, CAPABILITY_BUNDLE_ENOUGH_TRUST, uri); - gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, - from, uri, _caps_disco_cb, cache, G_OBJECT (cache), NULL); - /* enough DISCO for you, buddy */ - waiter->disco_requested = TRUE; - } - } -} - -static void -_process_caps (GabblePresenceCache *cache, - GabbleHandle handle, - const gchar *from, - LmMessageNode *lm_node) -{ - gchar *resource; - GSList *uris, *i; - GabblePresenceCachePrivate *priv; - guint serial; - - priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - serial = priv->caps_serial++; - - gabble_decode_jid (from, NULL, NULL, &resource); - - if (NULL == resource) - return; - - uris = _extract_cap_bundles (lm_node); - - for (i = uris; NULL != i; i = i->next) - { - _process_caps_uri (cache, from, (gchar *) i->data, handle, resource, serial); - g_free (i->data); - } - - g_free (resource); - g_slist_free (uris); -} - -static void -_grab_avatar_sha1 (GabblePresenceCache *cache, - GabbleHandle handle, - const gchar *from, - LmMessageNode *node) -{ - const gchar *sha1; - LmMessageNode *x_node, *photo_node; - GabblePresence *presence; - //LIB_TRACE( ELibTraceTypeInfo, "%s", "Out _grab_avatar_sha1" ); - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - return; - - x_node = lm_message_node_get_child_with_namespace (node, "x", - NS_VCARD_TEMP_UPDATE); - - if (NULL == x_node) - { -#if 0 - if (handle == priv->conn->parent.self_handle) - { - /* One of my other resources does not support XEP-0153. As per that - * XEP, I MUST stop advertising the image hash, at least until all - * instances of non-conforming resources have gone offline. - * However, we're going to ignore this requirement and hope that - * non-conforming clients won't alter the , which should - * in practice be true. - */ - presence->avatar_sha1 = NULL; - } -#endif - return; - } - - photo_node = lm_message_node_get_child (x_node, "photo"); - - /* If there is no photo node, the resource supports XEP-0153, but has - * nothing in particular to say about the avatar. */ - if (NULL == photo_node) - return; - - sha1 = lm_message_node_get_value (photo_node); - - if (g_strdiff (presence->avatar_sha1, sha1)) - { - g_free (presence->avatar_sha1); - presence->avatar_sha1 = g_strdup (sha1); - -#if 0 - if (handle == priv->conn->parent.self_handle) - { - /* that would be us, then. According to XEP-0153, we MUST - * immediately send a presence update with an empty update child - * element (no photo node), then re-download our own vCard; - * when that arrives, we may start setting the photo node in our - * presence again. - * - * For the moment I'm going to ignore that requirement and - * trust that our other resource is getting its sha1 right! - */ - /* TODO: I don't trust anyone to get XMPP right, so let's do - * this. :D */ - } -#endif - //LIB_TRACE( ELibTraceTypeInfo, "%s", "AVATAR_UPDATE _grab_avatar_sha1" ); - //g_signal_emit (cache, signals[AVATAR_UPDATE], 0, handle); - } - //LIB_TRACE( ELibTraceTypeInfo, "%s", "Out _grab_avatar_sha1" ); -} - -static LmHandlerResult -_parse_presence_message (GabblePresenceCache *cache, - GabbleHandle handle, - const gchar *from, - LmMessage *message) -{ - gint8 priority = 0; - gchar *resource = NULL; - const gchar *status_message = NULL; - LmMessageNode *presence_node, *child_node; - LmHandlerResult ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - GabblePresenceId presence_id; - GabblePresence *presence; - - presence_node = message->node; - g_assert (0 == strcmp (presence_node->name, "presence")); - - gabble_decode_jid (from, NULL, NULL, &resource); - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL != presence) - presence->keep_unavailable = FALSE; - - child_node = lm_message_node_get_child (presence_node, "status"); - - if (child_node) - status_message = lm_message_node_get_value (child_node); - - child_node = lm_message_node_get_child (presence_node, "priority"); - - if (child_node) - { - const gchar *prio = lm_message_node_get_value (child_node); - - if (prio != NULL) - priority = CLAMP (atoi (prio), G_MININT8, G_MAXINT8); - } - - switch (lm_message_get_sub_type (message)) - { - case LM_MESSAGE_SUB_TYPE_NOT_SET: - case LM_MESSAGE_SUB_TYPE_AVAILABLE: - presence_id = _presence_node_get_status (presence_node); - gabble_presence_cache_update (cache, handle, resource, presence_id, - status_message, priority); - -#if 0 - if (_presence_node_has_google_voice (presence_node)) - { - presence = gabble_presence_cache_get (cache, handle); - g_assert (NULL != presence); - gabble_debug (DEBUG_FLAG, "%s has voice-v1 support", from); - gabble_presence_set_capabilities (presence, resource, - PRESENCE_CAP_GOOGLE_VOICE); - } -#endif - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - - case LM_MESSAGE_SUB_TYPE_ERROR: - NODE_DEBUG (presence_node, "setting contact offline due to error"); - /* fall through */ - - case LM_MESSAGE_SUB_TYPE_UNAVAILABLE: - gabble_presence_cache_update (cache, handle, resource, - GABBLE_PRESENCE_OFFLINE, status_message, priority); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - - default: - break; - } - - _grab_avatar_sha1 (cache, handle, from, presence_node); - _grab_nickname (cache, handle, from, presence_node); - _process_caps (cache, handle, from, presence_node); - - g_free (resource); - - return ret; -} - -static LmHandlerResult -_parse_message_message (GabblePresenceCache *cache, - GabbleHandle handle, - const gchar *from, - LmMessage *message) -{ - LmMessageNode *node; - GabblePresence *presence; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - { - presence = _cache_insert (cache, handle); - presence->keep_unavailable = TRUE; - } - - node = lm_message_get_node (message); - - _grab_nickname (cache, handle, from, node); - _process_caps (cache, handle, from, node); - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; -} - - -/** - * gabble_presence_cache_lm_message_cb: - * @handler: #LmMessageHandler for this message - * @connection: #LmConnection that originated the message - * @message: the presence message - * @user_data: callback data - * - * Called by loudmouth when we get an incoming . - */ -static LmHandlerResult -gabble_presence_cache_lm_message_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - const char *from; - GabbleHandle handle; - - g_assert (lmconn == priv->conn->lmconn); - - from = lm_message_node_get_attribute (message->node, "from"); - - if (NULL == from) - { - NODE_DEBUG (message->node, "message without from attribute, ignoring"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE); - - if (0 == handle) - { - NODE_DEBUG (message->node, "ignoring message from malformed jid"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - if (handle == priv->conn->self_handle) - { - NODE_DEBUG (message->node, - "ignoring message from ourselves on another resource"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - switch (lm_message_get_type (message)) - { - case LM_MESSAGE_TYPE_PRESENCE: - return _parse_presence_message (cache, handle, from, message); - case LM_MESSAGE_TYPE_MESSAGE: - return _parse_message_message (cache, handle, from, message); - default: - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } -} - - -GabblePresenceCache * -gabble_presence_cache_new (GabbleConnection *conn) -{ - return g_object_new (GABBLE_TYPE_PRESENCE_CACHE, - "connection", conn, - NULL); -} - -GabblePresence * -gabble_presence_cache_get (GabblePresenceCache *cache, GabbleHandle handle) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - - -// g_assert (gabble_handle_is_valid (priv->conn->handles, -// TP_HANDLE_TYPE_CONTACT, handle, NULL)); - if(priv) - if(priv->conn) - if(priv->conn->handles){ - if ( gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL) ) - { - return g_hash_table_lookup (priv->presence, GINT_TO_POINTER (handle)); - } - else - { - return NULL; - } - } - return NULL; -} - -void -gabble_presence_cache_maybe_remove ( - GabblePresenceCache *cache, - GabbleHandle handle) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - GabblePresence *presence; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - return; - - if (presence->status == GABBLE_PRESENCE_OFFLINE && - presence->status_message == NULL && - !presence->keep_unavailable) - { - const gchar *jid; - - jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, - handle); - gabble_debug (DEBUG_FLAG, "discarding cached presence for unavailable jid %s", jid); - g_hash_table_remove (priv->presence, GINT_TO_POINTER (handle)); - handle_set_remove (priv->presence_handles, handle); - } -} - -static GabblePresence * -_cache_insert ( - GabblePresenceCache *cache, - GabbleHandle handle) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - GabblePresence *presence; - - presence = gabble_presence_new (); - g_hash_table_insert (priv->presence, GINT_TO_POINTER (handle), presence); - handle_set_add (priv->presence_handles, handle); - return presence; -} - -void -gabble_presence_cache_update ( - GabblePresenceCache *cache, - GabbleHandle handle, - const gchar *resource, - GabblePresenceId presence_id, - const gchar *status_message, - gint8 priority) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - const gchar *jid; - GabblePresence *presence; - - jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, - handle); - gabble_debug (DEBUG_FLAG, "%s (%d) resource %s prio %d presence %d message \"%s\"", - jid, handle, resource, priority, presence_id, status_message); - - presence = gabble_presence_cache_get (cache, handle); - - if (presence == NULL) - presence = _cache_insert (cache, handle); - - if (gabble_presence_update (presence, resource, presence_id, status_message, - priority)) - g_signal_emit (cache, signals[PRESENCE_UPDATE], 0, handle); - - gabble_presence_cache_maybe_remove (cache, handle); -} - -void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache, - const gchar *node, GabblePresenceCapabilities new_caps) -{ - CapabilityInfo *info; - - info = capability_info_get (cache, node, 0); - info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST; - info->caps |= new_caps; -} -