telepathygabble/src/gabble-presence-cache.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * gabble-presence-cache.c - Gabble's contact presence cache
       
     3  * Copyright (C) 2005 Collabora Ltd.
       
     4  *  and/or its subsidiaries. All rights reserved.
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Lesser General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2.1 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Lesser General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Lesser General Public
       
    17  * License along with this library; if not, write to the Free Software
       
    18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    19  */
       
    20 
       
    21 #include <stdlib.h>
       
    22 #include <string.h>
       
    23 
       
    24 #include "debug.h"
       
    25 #include "disco.h" /* \o\ \o/ /o/ */
       
    26 #include "gabble-presence.h"
       
    27 #include "namespaces.h"
       
    28 #include "util.h"
       
    29 #include "handle-set.h"
       
    30 #include "gintset.h"
       
    31 
       
    32 #include "gabble-presence-cache.h"
       
    33 
       
    34 #include "gabble-presence-cache-signals-marshal.h"
       
    35 
       
    36 #include "gabble_enums.h"
       
    37 
       
    38 #ifndef EMULATOR
       
    39 G_DEFINE_TYPE (GabblePresenceCache, gabble_presence_cache, G_TYPE_OBJECT);
       
    40 #endif
       
    41 
       
    42 /* when five DIFFERENT guys report the same caps for a given bundle, it'll be enough */
       
    43 #define CAPABILITY_BUNDLE_ENOUGH_TRUST 5
       
    44 #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE
       
    45 
       
    46 #ifdef DEBUG_FLAG
       
    47 //#define DEBUG(format, ...)
       
    48 #define DEBUGGING 0
       
    49 #define NODE_DEBUG(n, s)
       
    50 #endif /* DEBUG_FLAG */
       
    51 
       
    52 /* properties */
       
    53 enum
       
    54 {
       
    55   PROP_CONNECTION = 1,
       
    56   LAST_PROPERTY
       
    57 };
       
    58 
       
    59 /* signal enum */
       
    60 enum
       
    61 {
       
    62   PRESENCE_UPDATE,
       
    63   NICKNAME_UPDATE,
       
    64   CAPABILITIES_UPDATE,
       
    65   LAST_SIGNAL 
       
    66 #ifdef EMULATOR  
       
    67   = LAST_SIGNAL_PRE_CACHE
       
    68 #endif
       
    69   
       
    70 };
       
    71 
       
    72 
       
    73 #ifdef EMULATOR
       
    74 #include "libgabble_wsd_solution.h"
       
    75 
       
    76 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_pre_cache,guint)
       
    77 	#define signals (GET_WSD_VAR_NAME(signals,gabble_pre_cache, s)())
       
    78 	
       
    79     GET_STATIC_VAR_FROM_TLS(gabble_presence_cache_parent_class,gabble_pre_cache,gpointer)
       
    80 	#define gabble_presence_cache_parent_class (*GET_WSD_VAR_NAME(gabble_presence_cache_parent_class,gabble_pre_cache,s)())
       
    81 	
       
    82 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_pre_cache,GType)
       
    83 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_pre_cache,s)())
       
    84 
       
    85 		
       
    86 static void gabble_presence_cache_init (GabblePresenceCache *self); 
       
    87 static void gabble_presence_cache_class_init (GabblePresenceCacheClass *klass);
       
    88 static void gabble_presence_cache_class_intern_init (gpointer klass)
       
    89  { 
       
    90  gabble_presence_cache_parent_class = g_type_class_peek_parent (klass); 
       
    91  gabble_presence_cache_class_init ((GabblePresenceCacheClass*) klass);
       
    92  } 
       
    93  EXPORT_C GType gabble_presence_cache_get_type (void) 
       
    94  { 
       
    95  if ((g_define_type_id == 0))
       
    96  	{ 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; 
       
    97  };
       
    98 	
       
    99 		
       
   100 #else
       
   101 
       
   102 	static guint signals[LAST_SIGNAL] = {0};
       
   103 
       
   104 #endif
       
   105 
       
   106 
       
   107 #define GABBLE_PRESENCE_CACHE_PRIV(account) ((GabblePresenceCachePrivate *)account->priv)
       
   108 
       
   109 typedef struct _GabblePresenceCachePrivate GabblePresenceCachePrivate;
       
   110 
       
   111 struct _GabblePresenceCachePrivate
       
   112 {
       
   113   GabbleConnection *conn;
       
   114 
       
   115   gulong status_changed_cb;
       
   116   LmMessageHandler *lm_message_cb;
       
   117 
       
   118   GHashTable *presence;
       
   119   GabbleHandleSet *presence_handles;
       
   120 
       
   121   GHashTable *capabilities;
       
   122   GHashTable *disco_pending;
       
   123   guint caps_serial;
       
   124 
       
   125   gboolean dispose_has_run;
       
   126 };
       
   127 
       
   128 typedef struct _DiscoWaiter DiscoWaiter;
       
   129 
       
   130 struct _DiscoWaiter
       
   131 {
       
   132   GabbleHandleRepo *repo;
       
   133   GabbleHandle handle;
       
   134   gchar *resource;
       
   135   guint serial;
       
   136   gboolean disco_requested;
       
   137 };
       
   138 
       
   139 /**
       
   140  * disco_waiter_new ()
       
   141  */
       
   142 static DiscoWaiter *
       
   143 disco_waiter_new (GabbleHandleRepo *repo, GabbleHandle handle, const gchar *resource, guint serial)
       
   144 {
       
   145   DiscoWaiter *waiter;
       
   146 
       
   147   g_assert (repo);
       
   148   gabble_handle_ref (repo, TP_HANDLE_TYPE_CONTACT, handle);
       
   149 
       
   150   waiter = g_new0 (DiscoWaiter, 1);
       
   151   waiter->repo = repo;
       
   152   waiter->handle = handle;
       
   153   waiter->resource = g_strdup (resource);
       
   154   waiter->serial = serial;
       
   155 
       
   156   gabble_debug (DEBUG_FLAG, "created waiter %p for handle %u with serial %u", waiter, handle, serial);
       
   157 
       
   158   return waiter;
       
   159 }
       
   160 
       
   161 static void
       
   162 disco_waiter_free (DiscoWaiter *waiter)
       
   163 {
       
   164   g_assert (NULL != waiter);
       
   165 
       
   166   gabble_debug (DEBUG_FLAG, "freeing waiter %p for handle %u with serial %u", waiter, waiter->handle, waiter->serial);
       
   167 
       
   168   gabble_handle_unref (waiter->repo, TP_HANDLE_TYPE_CONTACT, waiter->handle);
       
   169 
       
   170   g_free (waiter->resource);
       
   171   g_free (waiter);
       
   172 }
       
   173 
       
   174 static void
       
   175 disco_waiter_list_free (GSList *list)
       
   176 {
       
   177   GSList *i;
       
   178 
       
   179   gabble_debug (DEBUG_FLAG, "list %p", list);
       
   180 
       
   181   for (i = list; NULL != i; i = i->next)
       
   182     disco_waiter_free ((DiscoWaiter *) i->data);
       
   183 
       
   184   g_slist_free (list);
       
   185 }
       
   186 
       
   187 static guint
       
   188 disco_waiter_list_get_request_count (GSList *list)
       
   189 {
       
   190   guint c = 0;
       
   191   GSList *i;
       
   192 
       
   193   for (i = list; i; i = i->next)
       
   194     {
       
   195       DiscoWaiter *waiter = (DiscoWaiter *) i->data;
       
   196 
       
   197       if (waiter->disco_requested)
       
   198         c++;
       
   199     }
       
   200 
       
   201   return c;
       
   202 }
       
   203 
       
   204 typedef struct _CapabilityInfo CapabilityInfo;
       
   205 
       
   206 struct _CapabilityInfo
       
   207 {
       
   208   GabblePresenceCapabilities caps;
       
   209   GIntSet *guys;
       
   210   guint trust;
       
   211 };
       
   212 
       
   213 static CapabilityInfo *
       
   214 capability_info_get (GabblePresenceCache *cache, const gchar *node,
       
   215     GabblePresenceCapabilities caps)
       
   216 {
       
   217   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   218   CapabilityInfo *info = g_hash_table_lookup (priv->capabilities, node);
       
   219 
       
   220   if (NULL == info)
       
   221     {
       
   222       info = g_new0 (CapabilityInfo, 1);
       
   223       info->caps = caps;
       
   224       info->guys = g_intset_new ();
       
   225       g_hash_table_insert (priv->capabilities, g_strdup (node), info);
       
   226     }
       
   227 
       
   228   return info;
       
   229 }
       
   230 
       
   231 static guint
       
   232 capability_info_recvd (GabblePresenceCache *cache, const gchar *node,
       
   233         GabbleHandle handle, GabblePresenceCapabilities caps)
       
   234 {
       
   235   CapabilityInfo *info = capability_info_get (cache, node, caps);
       
   236 
       
   237   /* Detect inconsistency in reported caps */
       
   238   if (info->caps != caps)
       
   239     {
       
   240       g_intset_clear (info->guys);
       
   241       info->caps = caps;
       
   242       info->trust = 0;
       
   243     }
       
   244 
       
   245   if (!g_intset_is_member (info->guys, handle))
       
   246     {
       
   247       g_intset_add (info->guys, handle);
       
   248       info->trust++;
       
   249     }
       
   250 
       
   251   return info->trust;
       
   252 }
       
   253 
       
   254 static void gabble_presence_cache_init (GabblePresenceCache *presence_cache);
       
   255 static GObject * gabble_presence_cache_constructor (GType type, guint n_props,
       
   256     GObjectConstructParam *props);
       
   257 static void gabble_presence_cache_dispose (GObject *object);
       
   258 static void gabble_presence_cache_finalize (GObject *object);
       
   259 static void gabble_presence_cache_set_property (GObject *object, guint
       
   260     property_id, const GValue *value, GParamSpec *pspec);
       
   261 static void gabble_presence_cache_get_property (GObject *object, guint
       
   262     property_id, GValue *value, GParamSpec *pspec);
       
   263 static GabblePresence *_cache_insert (GabblePresenceCache *cache,
       
   264     GabbleHandle handle);
       
   265 
       
   266 static void gabble_presence_cache_status_changed_cb (GabbleConnection *,
       
   267     TpConnectionStatus, TpConnectionStatusReason, gpointer);
       
   268 static LmHandlerResult gabble_presence_cache_lm_message_cb (LmMessageHandler*,
       
   269     LmConnection*, LmMessage*, gpointer);
       
   270 
       
   271 static void
       
   272 gabble_presence_cache_class_init (GabblePresenceCacheClass *klass)
       
   273 {
       
   274   GObjectClass *object_class = G_OBJECT_CLASS (klass);
       
   275   GParamSpec *param_spec;
       
   276 
       
   277   g_type_class_add_private (object_class, sizeof (GabblePresenceCachePrivate));
       
   278 
       
   279   object_class->constructor = gabble_presence_cache_constructor;
       
   280 
       
   281   object_class->dispose = gabble_presence_cache_dispose;
       
   282   object_class->finalize = gabble_presence_cache_finalize;
       
   283 
       
   284   object_class->get_property = gabble_presence_cache_get_property;
       
   285   object_class->set_property = gabble_presence_cache_set_property;
       
   286 
       
   287   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   288                                     "Gabble connection object that owns this "
       
   289                                     "presence cache.",
       
   290                                     GABBLE_TYPE_CONNECTION,
       
   291                                     G_PARAM_CONSTRUCT_ONLY |
       
   292                                     G_PARAM_READWRITE |
       
   293                                     G_PARAM_STATIC_NICK |
       
   294                                     G_PARAM_STATIC_BLURB);
       
   295   g_object_class_install_property (object_class,
       
   296                                    PROP_CONNECTION,
       
   297                                    param_spec);
       
   298 
       
   299   signals[PRESENCE_UPDATE] = g_signal_new (
       
   300     "presence-update",
       
   301     G_TYPE_FROM_CLASS (klass),
       
   302     G_SIGNAL_RUN_LAST,
       
   303     0,
       
   304     NULL, NULL,
       
   305     g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
       
   306   signals[NICKNAME_UPDATE] = g_signal_new (
       
   307     "nickname-update",
       
   308     G_TYPE_FROM_CLASS (klass),
       
   309     G_SIGNAL_RUN_LAST,
       
   310     0,
       
   311     NULL, NULL,
       
   312     g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
       
   313   signals[CAPABILITIES_UPDATE] = g_signal_new (
       
   314     "capabilities-update",
       
   315     G_TYPE_FROM_CLASS (klass),
       
   316     G_SIGNAL_RUN_LAST,
       
   317     0,
       
   318     NULL, NULL,
       
   319     gabble_presence_cache_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
       
   320 }
       
   321 
       
   322 static void
       
   323 gabble_presence_cache_init (GabblePresenceCache *cache)
       
   324 {
       
   325   GabblePresenceCachePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (cache,
       
   326       GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCachePrivate);
       
   327 
       
   328   cache->priv = priv;
       
   329 
       
   330   priv->presence = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
       
   331   priv->capabilities = g_hash_table_new (g_str_hash, g_str_equal);
       
   332   priv->disco_pending = g_hash_table_new_full (g_str_hash, g_str_equal,
       
   333     g_free, (GDestroyNotify) disco_waiter_list_free);
       
   334   priv->caps_serial = 1;
       
   335 }
       
   336 
       
   337 static GObject *
       
   338 gabble_presence_cache_constructor (GType type, guint n_props,
       
   339                                    GObjectConstructParam *props)
       
   340 {
       
   341   GObject *obj;
       
   342   GabblePresenceCachePrivate *priv;
       
   343 
       
   344   obj = G_OBJECT_CLASS (gabble_presence_cache_parent_class)->
       
   345            constructor (type, n_props, props);
       
   346   priv = GABBLE_PRESENCE_CACHE_PRIV (GABBLE_PRESENCE_CACHE (obj));
       
   347 
       
   348   priv->status_changed_cb = g_signal_connect (priv->conn, "status-changed",
       
   349       G_CALLBACK (gabble_presence_cache_status_changed_cb), obj);
       
   350 
       
   351   return obj;
       
   352 }
       
   353 
       
   354 static void
       
   355 gabble_presence_cache_dispose (GObject *object)
       
   356 {
       
   357   GabblePresenceCache *self = GABBLE_PRESENCE_CACHE (object);
       
   358   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (self);
       
   359 
       
   360   if (priv->dispose_has_run)
       
   361     return;
       
   362 
       
   363   gabble_debug (DEBUG_FLAG, "dispose called");
       
   364 
       
   365   priv->dispose_has_run = TRUE;
       
   366 
       
   367   g_assert (priv->lm_message_cb == NULL);
       
   368 
       
   369   g_signal_handler_disconnect (priv->conn, priv->status_changed_cb);
       
   370 
       
   371   g_hash_table_destroy (priv->presence);
       
   372   priv->presence = NULL;
       
   373 
       
   374   handle_set_destroy (priv->presence_handles);
       
   375   priv->presence_handles = NULL;
       
   376 
       
   377   if (G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose)
       
   378     G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose (object);
       
   379 }
       
   380 
       
   381 static void
       
   382 gabble_presence_cache_finalize (GObject *object)
       
   383 {
       
   384   gabble_debug (DEBUG_FLAG, "called with %p", object);
       
   385 
       
   386   G_OBJECT_CLASS (gabble_presence_cache_parent_class)->finalize (object);
       
   387 }
       
   388 
       
   389 static void
       
   390 gabble_presence_cache_get_property (GObject    *object,
       
   391                                     guint       property_id,
       
   392                                     GValue     *value,
       
   393                                     GParamSpec *pspec)
       
   394 {
       
   395   GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object);
       
   396   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   397 
       
   398   switch (property_id) {
       
   399     case PROP_CONNECTION:
       
   400       g_value_set_object (value, priv->conn);
       
   401       break;
       
   402     default:
       
   403       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   404       break;
       
   405   }
       
   406 }
       
   407 
       
   408 static void
       
   409 gabble_presence_cache_set_property (GObject     *object,
       
   410                                     guint        property_id,
       
   411                                     const GValue *value,
       
   412                                     GParamSpec   *pspec)
       
   413 {
       
   414   GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object);
       
   415   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   416   GabbleHandleSet *new_presence_handles;
       
   417 
       
   418   switch (property_id) {
       
   419     case PROP_CONNECTION:
       
   420       priv->conn = g_value_get_object (value);
       
   421       new_presence_handles = handle_set_new (priv->conn->handles, TP_HANDLE_TYPE_CONTACT);
       
   422 
       
   423       if (priv->presence_handles)
       
   424         {
       
   425           const GIntSet *add;
       
   426           GIntSet *tmp;
       
   427           add = handle_set_peek (priv->presence_handles);
       
   428           tmp = handle_set_update (new_presence_handles, add);
       
   429           handle_set_destroy (priv->presence_handles);
       
   430           g_intset_destroy (tmp);
       
   431         }
       
   432       priv->presence_handles = new_presence_handles;
       
   433       break;
       
   434     default:
       
   435       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   436       break;
       
   437   }
       
   438 }
       
   439 
       
   440 #if 0
       
   441 static gboolean
       
   442 _presence_node_has_google_voice (LmMessageNode *pres_node)
       
   443 {
       
   444   LmMessageNode *node;
       
   445   const gchar *cap_ext;
       
   446   gchar **features, **tmp;
       
   447   gboolean found = FALSE;
       
   448 
       
   449   node = lm_message_node_get_child_with_namespace (pres_node, "c", NS_CAPS);
       
   450 
       
   451   if (node == NULL);
       
   452     return FALSE;
       
   453 
       
   454   cap_ext = lm_message_node_get_attribute (node, "ext");
       
   455 
       
   456   if (cap_ext == NULL);
       
   457     return FALSE;
       
   458 
       
   459   features = g_strsplit (cap_ext, " ", 0);
       
   460 
       
   461   for (tmp = features; *tmp; tmp++)
       
   462     {
       
   463       if (!g_strdiff (tmp, "voice-v1"))
       
   464         {
       
   465           found = TRUE;
       
   466           break;
       
   467         }
       
   468     }
       
   469 
       
   470   g_strfreev (features);
       
   471 
       
   472   return found;
       
   473 }
       
   474 #endif
       
   475 
       
   476 static void
       
   477 gabble_presence_cache_status_changed_cb (GabbleConnection *conn,
       
   478                                          TpConnectionStatus status,
       
   479                                          TpConnectionStatusReason reason,
       
   480                                          gpointer data)
       
   481 {
       
   482   GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (data);
       
   483   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   484 
       
   485   g_assert (conn == priv->conn);
       
   486 
       
   487   switch (status)
       
   488     {
       
   489     case TP_CONN_STATUS_CONNECTING:
       
   490       g_assert (priv->lm_message_cb == NULL);
       
   491 
       
   492       priv->lm_message_cb = lm_message_handler_new (gabble_presence_cache_lm_message_cb,
       
   493                                                     cache, NULL);
       
   494       lm_connection_register_message_handler (priv->conn->lmconn,
       
   495                                               priv->lm_message_cb,
       
   496                                               LM_MESSAGE_TYPE_PRESENCE,
       
   497                                               LM_HANDLER_PRIORITY_LAST);
       
   498       lm_connection_register_message_handler (priv->conn->lmconn,
       
   499                                               priv->lm_message_cb,
       
   500                                               LM_MESSAGE_TYPE_MESSAGE,
       
   501                                               LM_HANDLER_PRIORITY_FIRST);
       
   502       break;
       
   503     case TP_CONN_STATUS_CONNECTED:
       
   504       /* TODO: emit self presence */
       
   505       break;
       
   506     case TP_CONN_STATUS_DISCONNECTED:
       
   507       g_assert (priv->lm_message_cb != NULL);
       
   508 
       
   509       lm_connection_unregister_message_handler (conn->lmconn,
       
   510                                                 priv->lm_message_cb,
       
   511                                                 LM_MESSAGE_TYPE_PRESENCE);
       
   512       lm_connection_unregister_message_handler (conn->lmconn,
       
   513                                                 priv->lm_message_cb,
       
   514                                                 LM_MESSAGE_TYPE_MESSAGE);
       
   515       lm_message_handler_unref (priv->lm_message_cb);
       
   516       priv->lm_message_cb = NULL;
       
   517       break;
       
   518     default:
       
   519       g_assert_not_reached ();
       
   520     }
       
   521 }
       
   522 
       
   523 static GabblePresenceId
       
   524 _presence_node_get_status (LmMessageNode *pres_node)
       
   525 {
       
   526   const gchar *presence_show;
       
   527   LmMessageNode *child_node = lm_message_node_get_child (pres_node, "show");
       
   528 
       
   529   if (!child_node)
       
   530     {
       
   531       /*
       
   532       NODE_DEBUG (pres_node,
       
   533         "<presence> without <show> received from server, "
       
   534         "setting presence to available");
       
   535       */
       
   536       return GABBLE_PRESENCE_AVAILABLE;
       
   537     }
       
   538 
       
   539   presence_show = lm_message_node_get_value (child_node);
       
   540 
       
   541   if (!presence_show)
       
   542     {
       
   543       /*
       
   544       NODE_DEBUG (pres_node,
       
   545         "empty <show> tag received from server, "
       
   546         "setting presence to available");
       
   547       */
       
   548       return GABBLE_PRESENCE_AVAILABLE;
       
   549     }
       
   550 
       
   551   if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_AWAY))
       
   552     return GABBLE_PRESENCE_AWAY;
       
   553   else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_CHAT))
       
   554     return GABBLE_PRESENCE_CHAT;
       
   555   else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_DND))
       
   556     return GABBLE_PRESENCE_DND;
       
   557   else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_XA))
       
   558     return GABBLE_PRESENCE_XA;
       
   559   else
       
   560     {
       
   561        NODE_DEBUG (pres_node,
       
   562         "unrecognised <show/> value received from server, "
       
   563         "setting presence to available");
       
   564       return GABBLE_PRESENCE_AVAILABLE;
       
   565     }
       
   566 }
       
   567 
       
   568 static void
       
   569 _grab_nickname (GabblePresenceCache *cache,
       
   570                 GabbleHandle handle,
       
   571                 const gchar *from,
       
   572                 LmMessageNode *node)
       
   573 {
       
   574   const gchar *nickname;
       
   575   GabblePresence *presence;
       
   576 
       
   577   node = lm_message_node_get_child_with_namespace (node, "nick", NS_NICK);
       
   578 
       
   579   if (NULL == node)
       
   580     return;
       
   581 
       
   582   presence = gabble_presence_cache_get (cache, handle);
       
   583 
       
   584   if (NULL == presence)
       
   585     return;
       
   586 
       
   587   nickname = lm_message_node_get_value (node);
       
   588   gabble_debug (DEBUG_FLAG, "got nickname \"%s\" for %s", nickname, from);
       
   589 
       
   590   if (g_strdiff (presence->nickname, nickname))
       
   591     {
       
   592       if (NULL != presence->nickname)
       
   593         g_free (presence->nickname);
       
   594 
       
   595       presence->nickname = g_strdup (nickname);
       
   596       g_signal_emit (cache, signals[NICKNAME_UPDATE], 0, handle);
       
   597     }
       
   598 }
       
   599 
       
   600 static GSList *
       
   601 _extract_cap_bundles (LmMessageNode *lm_node)
       
   602 {
       
   603   const gchar *node, *ver, *ext;
       
   604   GSList *uris = NULL;
       
   605   LmMessageNode *cap_node;
       
   606 
       
   607   cap_node = lm_message_node_get_child_with_namespace (lm_node, "c", NS_CAPS);
       
   608 
       
   609   if (NULL == cap_node)
       
   610     return NULL;
       
   611 
       
   612   node = lm_message_node_get_attribute (cap_node, "node");
       
   613 
       
   614   if (NULL == node)
       
   615     return NULL;
       
   616 
       
   617   ver = lm_message_node_get_attribute (cap_node, "ver");
       
   618 
       
   619   if (NULL != ver)
       
   620     uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, ver));
       
   621 
       
   622   ext = lm_message_node_get_attribute (cap_node, "ext");
       
   623 
       
   624   if (NULL != ext)
       
   625     {
       
   626       gchar **exts, **i;
       
   627 
       
   628       exts = g_strsplit (ext, " ", 0);
       
   629 
       
   630       for (i = exts; NULL != *i; i++)
       
   631         uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, *i));
       
   632 
       
   633       g_strfreev (exts);
       
   634     }
       
   635 
       
   636   return uris;
       
   637 }
       
   638 
       
   639 static void
       
   640 _caps_disco_cb (GabbleDisco *disco,
       
   641                 GabbleDiscoRequest *request,
       
   642                 const gchar *jid,
       
   643                 const gchar *node,
       
   644                 LmMessageNode *query_result,
       
   645                 GError *error,
       
   646                 gpointer user_data)
       
   647 {
       
   648   GSList *waiters, *i;
       
   649   LmMessageNode *child;
       
   650   GabblePresenceCache *cache;
       
   651   GabblePresenceCachePrivate *priv;
       
   652   gchar *full_jid = NULL;
       
   653   GabblePresenceCapabilities caps = 0;
       
   654   guint trust;
       
   655   GabbleHandle handle;
       
   656 
       
   657   cache = GABBLE_PRESENCE_CACHE (user_data);
       
   658   priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   659 
       
   660   if (NULL == node)
       
   661     {
       
   662       g_warning ("got disco response with NULL node, ignoring");
       
   663       return;
       
   664     }
       
   665 
       
   666   waiters = g_hash_table_lookup (priv->disco_pending, node);
       
   667 
       
   668   if (NULL != error)
       
   669     {
       
   670       DiscoWaiter *waiter = NULL;
       
   671 
       
   672       gabble_debug (DEBUG_FLAG, "disco query failed: %s", error->message);
       
   673 
       
   674       for (i = waiters; NULL != i; i = i->next)
       
   675         {
       
   676           waiter = (DiscoWaiter *) i->data;
       
   677 
       
   678           if (!waiter->disco_requested)
       
   679             {
       
   680               const gchar *jid;
       
   681 
       
   682               jid = gabble_handle_inspect (priv->conn->handles,
       
   683                                            TP_HANDLE_TYPE_CONTACT,
       
   684                                            waiter->handle);
       
   685               full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource);
       
   686 
       
   687               gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, node,
       
   688                                     _caps_disco_cb, cache, G_OBJECT(cache), NULL);
       
   689               waiter->disco_requested = TRUE;
       
   690               break;
       
   691             }
       
   692         }
       
   693 
       
   694       if (NULL != i)
       
   695         {
       
   696           gabble_debug (DEBUG_FLAG, "sent a retry disco request to %s for URI %s", full_jid, node);
       
   697         }
       
   698       else
       
   699         {
       
   700           gabble_debug (DEBUG_FLAG, "failed to find a suitable candidate to retry disco request for URI %s", node);
       
   701           /* FIXME do something very clever here? */
       
   702           g_hash_table_remove (priv->disco_pending, node);
       
   703         }
       
   704 
       
   705       goto OUT;
       
   706     }
       
   707 
       
   708   for (child = query_result->children; NULL != child; child = child->next)
       
   709     {
       
   710       const gchar *var;
       
   711 
       
   712       if (0 != strcmp (child->name, "feature"))
       
   713         continue;
       
   714 
       
   715       var = lm_message_node_get_attribute (child, "var");
       
   716 
       
   717       if (NULL == var)
       
   718         continue;
       
   719 
       
   720       /* TODO: use a table that equates disco features to caps */
       
   721       if (0 == strcmp (var, NS_GOOGLE_TRANSPORT_P2P))
       
   722         caps |= PRESENCE_CAP_GOOGLE_TRANSPORT_P2P;
       
   723       else if (0 == strcmp (var, NS_GOOGLE_FEAT_VOICE))
       
   724         caps |= PRESENCE_CAP_GOOGLE_VOICE;
       
   725       else if (0 == strcmp (var, NS_JINGLE))
       
   726         caps |= PRESENCE_CAP_JINGLE;
       
   727       else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_AUDIO))
       
   728         caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO;
       
   729       else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_VIDEO))
       
   730         caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO;
       
   731     }
       
   732 
       
   733   handle = gabble_handle_for_contact (priv->conn->handles, jid, FALSE);
       
   734   trust = capability_info_recvd (cache, node, handle, caps);
       
   735 
       
   736   for (i = waiters; NULL != i;)
       
   737     {
       
   738       DiscoWaiter *waiter;
       
   739       GabblePresence *presence;
       
   740 
       
   741       waiter = (DiscoWaiter *) i->data;
       
   742 
       
   743       if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST || waiter->handle == handle)
       
   744         {
       
   745           GSList *tmp;
       
   746           /* trusted reply */
       
   747           presence = gabble_presence_cache_get (cache, waiter->handle);
       
   748 
       
   749           if (presence)
       
   750           {
       
   751             GabblePresenceCapabilities save_caps = presence->caps;
       
   752             gabble_debug (DEBUG_FLAG, "setting caps for %d (%s) to %d", handle, jid, caps);
       
   753             gabble_presence_set_capabilities (presence, waiter->resource,caps,
       
   754               waiter->serial);
       
   755             gabble_debug (DEBUG_FLAG, "caps for %d (%s) now %d", handle, jid, presence->caps);
       
   756             g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0,
       
   757               waiter->handle, save_caps, presence->caps);
       
   758           }
       
   759 
       
   760           tmp = i;
       
   761           i = i->next;
       
   762 
       
   763           waiters = g_slist_delete_link (waiters, tmp);
       
   764 
       
   765           g_hash_table_steal (priv->disco_pending, node);
       
   766           g_hash_table_insert (priv->disco_pending, g_strdup (node), waiters);
       
   767 
       
   768           disco_waiter_free (waiter);
       
   769         }
       
   770       else if (trust + disco_waiter_list_get_request_count (waiters) - 1
       
   771           < CAPABILITY_BUNDLE_ENOUGH_TRUST)
       
   772         {
       
   773           /* if the possible trust, not counting this guy, is too low,
       
   774            * we have been poisoned and reset our trust meters - disco
       
   775            * anybody we still haven't to be able to get more trusted replies */
       
   776 
       
   777           if (!waiter->disco_requested)
       
   778             {
       
   779               const gchar *jid;
       
   780 
       
   781               jid = gabble_handle_inspect (priv->conn->handles,
       
   782                 TP_HANDLE_TYPE_CONTACT, waiter->handle);
       
   783               full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource);
       
   784 
       
   785               gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid,
       
   786                   node, _caps_disco_cb, cache, G_OBJECT(cache), NULL);
       
   787               waiter->disco_requested = TRUE;
       
   788 
       
   789               g_free (full_jid);
       
   790               full_jid = NULL;
       
   791             }
       
   792 
       
   793           i = i->next;
       
   794         }
       
   795       else
       
   796         {
       
   797           /* trust level still uncertain, don't do nothing */
       
   798           i = i->next;
       
   799         }
       
   800     }
       
   801 
       
   802   if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST)
       
   803     g_hash_table_remove (priv->disco_pending, node);
       
   804 
       
   805 OUT:
       
   806 
       
   807   g_free (full_jid);
       
   808 }
       
   809 
       
   810 static void
       
   811 _process_caps_uri (GabblePresenceCache *cache,
       
   812                    const gchar *from,
       
   813                    const gchar *uri,
       
   814                    GabbleHandle handle,
       
   815                    const gchar *resource,
       
   816                    guint serial)
       
   817 {
       
   818   CapabilityInfo *info;
       
   819   gpointer value;
       
   820   GabblePresenceCachePrivate *priv;
       
   821 
       
   822   priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   823   info = capability_info_get (cache, uri, 0);
       
   824 
       
   825   if (info->trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST
       
   826       || g_intset_is_member (info->guys, handle))
       
   827     {
       
   828       /* we already have enough trust for this node; apply the cached value to
       
   829        * the (handle, resource) */
       
   830 
       
   831       GabblePresence *presence = gabble_presence_cache_get (cache, handle);
       
   832       gabble_debug (DEBUG_FLAG, "enough trust for URI %s, setting caps for %u (%s) to %u",
       
   833           uri, handle, from, info->caps);
       
   834 
       
   835       if (presence)
       
   836         {
       
   837           GabblePresenceCapabilities save_caps = presence->caps;
       
   838           gabble_presence_set_capabilities (presence, resource, info->caps, serial);
       
   839           g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0,
       
   840               handle, save_caps, presence->caps);
       
   841           gabble_debug (DEBUG_FLAG, "caps for %d (%s) now %d", handle, from, presence->caps);
       
   842         }
       
   843       else
       
   844         {
       
   845           gabble_debug (DEBUG_FLAG, "presence not found");
       
   846         }
       
   847     }
       
   848   else
       
   849     {
       
   850       /* Append the (handle, resource) pair to the list of such pairs
       
   851        * waiting for capabilities for this uri, and send a disco request
       
   852        * if we don't have enough possible trust yet */
       
   853 
       
   854       GSList *waiters;
       
   855       DiscoWaiter *waiter;
       
   856       guint possible_trust;
       
   857 
       
   858       gabble_debug (DEBUG_FLAG, "not enough trust for URI %s", uri);
       
   859       value = g_hash_table_lookup (priv->disco_pending, uri);
       
   860 
       
   861       if (value)
       
   862         g_hash_table_steal (priv->disco_pending, uri);
       
   863 
       
   864       waiters = (GSList *) value;
       
   865       waiter = disco_waiter_new (priv->conn->handles, handle, resource, serial);
       
   866       waiters = g_slist_prepend (waiters, waiter);
       
   867       g_hash_table_insert (priv->disco_pending, g_strdup (uri), waiters);
       
   868 
       
   869       possible_trust = disco_waiter_list_get_request_count (waiters);
       
   870 
       
   871       if (!value || info->trust + possible_trust < CAPABILITY_BUNDLE_ENOUGH_TRUST)
       
   872         {
       
   873           /* DISCO */
       
   874           gabble_debug (DEBUG_FLAG, "only %u trust out of %u possible thus far, sending disco for URI %s",
       
   875               info->trust + possible_trust, CAPABILITY_BUNDLE_ENOUGH_TRUST, uri);
       
   876           gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO,
       
   877               from, uri, _caps_disco_cb, cache, G_OBJECT (cache), NULL);
       
   878           /* enough DISCO for you, buddy */
       
   879           waiter->disco_requested = TRUE;
       
   880         }
       
   881     }
       
   882 }
       
   883 
       
   884 static void
       
   885 _process_caps (GabblePresenceCache *cache,
       
   886                GabbleHandle handle,
       
   887                const gchar *from,
       
   888                LmMessageNode *lm_node)
       
   889 {
       
   890   gchar *resource;
       
   891   GSList *uris, *i;
       
   892   GabblePresenceCachePrivate *priv;
       
   893   guint serial;
       
   894 
       
   895   priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
   896   serial = priv->caps_serial++;
       
   897 
       
   898   gabble_decode_jid (from, NULL, NULL, &resource);
       
   899 
       
   900   if (NULL == resource)
       
   901     return;
       
   902 
       
   903   uris = _extract_cap_bundles (lm_node);
       
   904 
       
   905   for (i = uris; NULL != i; i = i->next)
       
   906     {
       
   907       _process_caps_uri (cache, from, (gchar *) i->data, handle, resource, serial);
       
   908       g_free (i->data);
       
   909     }
       
   910 
       
   911   g_free (resource);
       
   912   g_slist_free (uris);
       
   913 }
       
   914 
       
   915 static void
       
   916 _grab_avatar_sha1 (GabblePresenceCache *cache,
       
   917                    GabbleHandle handle,
       
   918                    const gchar *from,
       
   919                    LmMessageNode *node)
       
   920 {
       
   921   const gchar *sha1;
       
   922   LmMessageNode *x_node, *photo_node;
       
   923   GabblePresence *presence;
       
   924   //LIB_TRACE( ELibTraceTypeInfo, "%s",  "Out _grab_avatar_sha1" );
       
   925   presence = gabble_presence_cache_get (cache, handle);
       
   926 
       
   927   if (NULL == presence)
       
   928     return;
       
   929 
       
   930   x_node = lm_message_node_get_child_with_namespace (node, "x",
       
   931       NS_VCARD_TEMP_UPDATE);
       
   932 
       
   933   if (NULL == x_node)
       
   934     {
       
   935 #if 0
       
   936       if (handle == priv->conn->parent.self_handle)
       
   937         {
       
   938           /* One of my other resources does not support XEP-0153. As per that
       
   939            * XEP, I MUST stop advertising the image hash, at least until all
       
   940            * instances of non-conforming resources have gone offline.
       
   941            * However, we're going to ignore this requirement and hope that
       
   942            * non-conforming clients won't alter the <PHOTO>, which should
       
   943            * in practice be true.
       
   944            */
       
   945           presence->avatar_sha1 = NULL;
       
   946         }
       
   947 #endif
       
   948       return;
       
   949     }
       
   950 
       
   951   photo_node = lm_message_node_get_child (x_node, "photo");
       
   952 
       
   953   /* If there is no photo node, the resource supports XEP-0153, but has
       
   954    * nothing in particular to say about the avatar. */
       
   955   if (NULL == photo_node)
       
   956     return;
       
   957 
       
   958   sha1 = lm_message_node_get_value (photo_node);
       
   959 
       
   960   if (g_strdiff (presence->avatar_sha1, sha1))
       
   961     {
       
   962       g_free (presence->avatar_sha1);
       
   963       presence->avatar_sha1 = g_strdup (sha1);
       
   964 
       
   965 #if 0
       
   966       if (handle == priv->conn->parent.self_handle)
       
   967         {
       
   968           /* that would be us, then. According to XEP-0153, we MUST
       
   969            * immediately send a presence update with an empty update child
       
   970            * element (no photo node), then re-download our own vCard;
       
   971            * when that arrives, we may start setting the photo node in our
       
   972            * presence again.
       
   973            *
       
   974            * For the moment I'm going to ignore that requirement and
       
   975            * trust that our other resource is getting its sha1 right!
       
   976            */
       
   977           /* TODO: I don't trust anyone to get XMPP right, so let's do
       
   978            * this. :D */
       
   979         }
       
   980 #endif
       
   981       //LIB_TRACE( ELibTraceTypeInfo, "%s",  "AVATAR_UPDATE _grab_avatar_sha1" );
       
   982       //g_signal_emit (cache, signals[AVATAR_UPDATE], 0, handle);
       
   983     }
       
   984   //LIB_TRACE( ELibTraceTypeInfo, "%s",  "Out _grab_avatar_sha1" );
       
   985 }
       
   986 
       
   987 static LmHandlerResult
       
   988 _parse_presence_message (GabblePresenceCache *cache,
       
   989                          GabbleHandle handle,
       
   990                          const gchar *from,
       
   991                          LmMessage *message)
       
   992 {
       
   993   gint8 priority = 0;
       
   994   gchar *resource = NULL;
       
   995   const gchar *status_message = NULL;
       
   996   LmMessageNode *presence_node, *child_node;
       
   997   LmHandlerResult ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   998   GabblePresenceId presence_id;
       
   999   GabblePresence *presence;
       
  1000 
       
  1001   presence_node = message->node;
       
  1002   g_assert (0 == strcmp (presence_node->name, "presence"));
       
  1003 
       
  1004   gabble_decode_jid (from, NULL, NULL, &resource);
       
  1005 
       
  1006   presence = gabble_presence_cache_get (cache, handle);
       
  1007 
       
  1008   if (NULL != presence)
       
  1009       presence->keep_unavailable = FALSE;
       
  1010 
       
  1011   child_node = lm_message_node_get_child (presence_node, "status");
       
  1012 
       
  1013   if (child_node)
       
  1014     status_message = lm_message_node_get_value (child_node);
       
  1015 
       
  1016   child_node = lm_message_node_get_child (presence_node, "priority");
       
  1017 
       
  1018   if (child_node)
       
  1019     {
       
  1020       const gchar *prio = lm_message_node_get_value (child_node);
       
  1021 
       
  1022       if (prio != NULL)
       
  1023         priority = CLAMP (atoi (prio), G_MININT8, G_MAXINT8);
       
  1024     }
       
  1025 
       
  1026   switch (lm_message_get_sub_type (message))
       
  1027     {
       
  1028     case LM_MESSAGE_SUB_TYPE_NOT_SET:
       
  1029     case LM_MESSAGE_SUB_TYPE_AVAILABLE:
       
  1030       presence_id = _presence_node_get_status (presence_node);
       
  1031       gabble_presence_cache_update (cache, handle, resource, presence_id,
       
  1032           status_message, priority);
       
  1033 
       
  1034 #if 0
       
  1035       if (_presence_node_has_google_voice (presence_node))
       
  1036         {
       
  1037           presence = gabble_presence_cache_get (cache, handle);
       
  1038           g_assert (NULL != presence);
       
  1039           gabble_debug (DEBUG_FLAG, "%s has voice-v1 support", from);
       
  1040           gabble_presence_set_capabilities (presence, resource,
       
  1041               PRESENCE_CAP_GOOGLE_VOICE);
       
  1042         }
       
  1043 #endif
       
  1044 
       
  1045       ret = LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1046       break;
       
  1047 
       
  1048     case LM_MESSAGE_SUB_TYPE_ERROR:
       
  1049       NODE_DEBUG (presence_node, "setting contact offline due to error");
       
  1050       /* fall through */
       
  1051 
       
  1052     case LM_MESSAGE_SUB_TYPE_UNAVAILABLE:
       
  1053       gabble_presence_cache_update (cache, handle, resource,
       
  1054           GABBLE_PRESENCE_OFFLINE, status_message, priority);
       
  1055 
       
  1056       ret = LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1057       break;
       
  1058 
       
  1059     default:
       
  1060       break;
       
  1061     }
       
  1062 
       
  1063   _grab_avatar_sha1 (cache, handle, from, presence_node);
       
  1064   _grab_nickname (cache, handle, from, presence_node);
       
  1065   _process_caps (cache, handle, from, presence_node);
       
  1066 
       
  1067   g_free (resource);
       
  1068 
       
  1069   return ret;
       
  1070 }
       
  1071 
       
  1072 static LmHandlerResult
       
  1073 _parse_message_message (GabblePresenceCache *cache,
       
  1074                         GabbleHandle handle,
       
  1075                         const gchar *from,
       
  1076                         LmMessage *message)
       
  1077 {
       
  1078   LmMessageNode *node;
       
  1079   GabblePresence *presence;
       
  1080 
       
  1081   presence = gabble_presence_cache_get (cache, handle);
       
  1082 
       
  1083   if (NULL == presence)
       
  1084     {
       
  1085       presence = _cache_insert (cache, handle);
       
  1086       presence->keep_unavailable = TRUE;
       
  1087     }
       
  1088 
       
  1089   node = lm_message_get_node (message);
       
  1090 
       
  1091   _grab_nickname (cache, handle, from, node);
       
  1092   _process_caps (cache, handle, from, node);
       
  1093 
       
  1094   return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1095 }
       
  1096 
       
  1097 
       
  1098 /**
       
  1099  * gabble_presence_cache_lm_message_cb:
       
  1100  * @handler: #LmMessageHandler for this message
       
  1101  * @connection: #LmConnection that originated the message
       
  1102  * @message: the presence message
       
  1103  * @user_data: callback data
       
  1104  *
       
  1105  * Called by loudmouth when we get an incoming <presence>.
       
  1106  */
       
  1107 static LmHandlerResult
       
  1108 gabble_presence_cache_lm_message_cb (LmMessageHandler *handler,
       
  1109                                      LmConnection *lmconn,
       
  1110                                      LmMessage *message,
       
  1111                                      gpointer user_data)
       
  1112 {
       
  1113   GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data);
       
  1114   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
  1115   const char *from;
       
  1116   GabbleHandle handle;
       
  1117 
       
  1118   g_assert (lmconn == priv->conn->lmconn);
       
  1119 
       
  1120   from = lm_message_node_get_attribute (message->node, "from");
       
  1121 
       
  1122   if (NULL == from)
       
  1123     {
       
  1124      NODE_DEBUG (message->node, "message without from attribute, ignoring");
       
  1125       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1126     }
       
  1127 
       
  1128   handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE);
       
  1129 
       
  1130   if (0 == handle)
       
  1131     {
       
  1132       NODE_DEBUG (message->node, "ignoring message from malformed jid");
       
  1133       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1134     }
       
  1135 
       
  1136   if (handle == priv->conn->self_handle)
       
  1137     {
       
  1138       NODE_DEBUG (message->node,
       
  1139         "ignoring message from ourselves on another resource");
       
  1140       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1141     }
       
  1142 
       
  1143   switch (lm_message_get_type (message))
       
  1144     {
       
  1145     case LM_MESSAGE_TYPE_PRESENCE:
       
  1146       return _parse_presence_message (cache, handle, from, message);
       
  1147     case LM_MESSAGE_TYPE_MESSAGE:
       
  1148       return _parse_message_message (cache, handle, from, message);
       
  1149     default:
       
  1150       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1151     }
       
  1152 }
       
  1153 
       
  1154 
       
  1155 GabblePresenceCache *
       
  1156 gabble_presence_cache_new (GabbleConnection *conn)
       
  1157 {
       
  1158   return g_object_new (GABBLE_TYPE_PRESENCE_CACHE,
       
  1159                        "connection", conn,
       
  1160                        NULL);
       
  1161 }
       
  1162 
       
  1163 GabblePresence *
       
  1164 gabble_presence_cache_get (GabblePresenceCache *cache, GabbleHandle handle)
       
  1165 {
       
  1166   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
  1167 
       
  1168   
       
  1169 //  g_assert (gabble_handle_is_valid (priv->conn->handles,
       
  1170 //        TP_HANDLE_TYPE_CONTACT, handle, NULL));
       
  1171   if(priv)
       
  1172     if(priv->conn)
       
  1173       if(priv->conn->handles){
       
  1174   if ( gabble_handle_is_valid (priv->conn->handles,
       
  1175           TP_HANDLE_TYPE_CONTACT, handle, NULL) )
       
  1176       {
       
  1177         return g_hash_table_lookup (priv->presence, GINT_TO_POINTER (handle));  
       
  1178       }
       
  1179   else
       
  1180       {
       
  1181       return NULL;
       
  1182       }
       
  1183   }
       
  1184   return NULL;
       
  1185 }
       
  1186 
       
  1187 void
       
  1188 gabble_presence_cache_maybe_remove (
       
  1189     GabblePresenceCache *cache,
       
  1190     GabbleHandle handle)
       
  1191 {
       
  1192   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
  1193   GabblePresence *presence;
       
  1194 
       
  1195   presence = gabble_presence_cache_get (cache, handle);
       
  1196 
       
  1197   if (NULL == presence)
       
  1198     return;
       
  1199 
       
  1200   if (presence->status == GABBLE_PRESENCE_OFFLINE &&
       
  1201       presence->status_message == NULL &&
       
  1202       !presence->keep_unavailable)
       
  1203     {
       
  1204       const gchar *jid;
       
  1205 
       
  1206       jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT,
       
  1207           handle);
       
  1208       gabble_debug (DEBUG_FLAG, "discarding cached presence for unavailable jid %s", jid);
       
  1209       g_hash_table_remove (priv->presence, GINT_TO_POINTER (handle));
       
  1210       handle_set_remove (priv->presence_handles, handle);
       
  1211     }
       
  1212 }
       
  1213 
       
  1214 static GabblePresence *
       
  1215 _cache_insert (
       
  1216     GabblePresenceCache *cache,
       
  1217     GabbleHandle handle)
       
  1218 {
       
  1219   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
  1220   GabblePresence *presence;
       
  1221 
       
  1222   presence = gabble_presence_new ();
       
  1223   g_hash_table_insert (priv->presence, GINT_TO_POINTER (handle), presence);
       
  1224   handle_set_add (priv->presence_handles, handle);
       
  1225   return presence;
       
  1226 }
       
  1227 
       
  1228 void
       
  1229 gabble_presence_cache_update (
       
  1230     GabblePresenceCache *cache,
       
  1231     GabbleHandle handle,
       
  1232     const gchar *resource,
       
  1233     GabblePresenceId presence_id,
       
  1234     const gchar *status_message,
       
  1235     gint8 priority)
       
  1236 {
       
  1237   GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache);
       
  1238   const gchar *jid;
       
  1239   GabblePresence *presence;
       
  1240 
       
  1241   jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT,
       
  1242       handle);
       
  1243   gabble_debug (DEBUG_FLAG, "%s (%d) resource %s prio %d presence %d message \"%s\"",
       
  1244       jid, handle, resource, priority, presence_id, status_message);
       
  1245 
       
  1246   presence = gabble_presence_cache_get (cache, handle);
       
  1247 
       
  1248   if (presence == NULL)
       
  1249     presence = _cache_insert (cache, handle);
       
  1250 
       
  1251   if (gabble_presence_update (presence, resource, presence_id, status_message,
       
  1252         priority))
       
  1253     g_signal_emit (cache, signals[PRESENCE_UPDATE], 0, handle);
       
  1254 
       
  1255   gabble_presence_cache_maybe_remove (cache, handle);
       
  1256 }
       
  1257 
       
  1258 void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache,
       
  1259     const gchar *node, GabblePresenceCapabilities new_caps)
       
  1260 {
       
  1261   CapabilityInfo *info;
       
  1262 
       
  1263   info = capability_info_get (cache, node, 0);
       
  1264   info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST;
       
  1265   info->caps |= new_caps;
       
  1266 }
       
  1267