--- a/telepathygabble/src/gabble-presence-cache.c Wed Mar 31 22:32:38 2010 +0300
+++ /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 <stdlib.h>
-#include <string.h>
-
-#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,
- "<presence> without <show> 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 <show> 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 <show/> 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 <PHOTO>, 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 <presence>.
- */
-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;
-}
-