telepathygabble/src/handles.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * handles.c - mechanism to store and retrieve handles on a connection
       
     3  * Copyright (C) 2005 Collabora Ltd.
       
     4  * 
       
     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 <glib.h>
       
    22 #include <dbus/dbus-glib.h>
       
    23 #include <string.h>
       
    24 
       
    25 #include "gheap.h"
       
    26 #include "handles.h"
       
    27 #include "handle-set.h"
       
    28 #include "telepathy-errors.h"
       
    29 #include "telepathy-helpers.h"
       
    30 #include "util.h"
       
    31 
       
    32 #include "config.h"
       
    33 
       
    34 #ifdef ENABLE_HANDLE_LEAK_DEBUG
       
    35 #include <stdlib.h>
       
    36 #include <stdio.h>
       
    37 #include <execinfo.h>
       
    38 
       
    39 	
       
    40 typedef struct _HandleLeakTrace HandleLeakTrace;
       
    41 
       
    42 struct _HandleLeakTrace
       
    43 {
       
    44   char **trace;
       
    45   int len;
       
    46 };
       
    47 
       
    48 static void
       
    49 handle_leak_trace_free (HandleLeakTrace *hltrace)
       
    50 {
       
    51   free (hltrace->trace);
       
    52   g_free (hltrace);
       
    53 }
       
    54 
       
    55 static void
       
    56 handle_leak_trace_free_gfunc (gpointer data, gpointer user_data)
       
    57 {
       
    58   return handle_leak_trace_free ((HandleLeakTrace *) data);
       
    59 }
       
    60 
       
    61 #endif /* ENABLE_HANDLE_LEAK_DEBUG */
       
    62 
       
    63 /*#ifdef EMULATOR
       
    64 #include "libgabble_wsd_solution.h"
       
    65 	
       
    66 	gchar** _s_handles_list_handle_strings() { return (gchar**)((libgabble_ImpurePtr()->_s_handles_list_handle_strings)); }
       
    67 
       
    68 	#define list_handle_strings (GET_WSD_VAR_NAME(list_handle_strings,handles, s)())	
       
    69 	
       
    70 #endif*/
       
    71 	
       
    72 	
       
    73 typedef struct _GabbleHandlePriv GabbleHandlePriv;
       
    74 
       
    75 struct _GabbleHandlePriv
       
    76 {
       
    77   guint refcount;
       
    78   gchar *string;
       
    79 #ifdef ENABLE_HANDLE_LEAK_DEBUG
       
    80   GSList *traces;
       
    81 #endif /* ENABLE_HANDLE_LEAK_DEBUG */
       
    82   GData *datalist;
       
    83 };
       
    84 
       
    85 struct _GabbleHandleRepo
       
    86 {
       
    87   GHashTable *contact_handles;
       
    88   GHashTable *room_handles;
       
    89   GData *list_handles;
       
    90   GHashTable *contact_strings;
       
    91   GHashTable *room_strings;
       
    92   GHeap *free_contact_handles;
       
    93   GHeap *free_room_handles;
       
    94   guint contact_serial;
       
    95   guint room_serial;
       
    96   GData *client_contact_handle_sets;
       
    97   GData *client_room_handle_sets;
       
    98   DBusGProxy *bus_service_proxy;
       
    99 };
       
   100 
       
   101 //#ifndef EMULATOR
       
   102 static const char *list_handle_strings[GABBLE_LIST_HANDLE_DENY] =
       
   103 {
       
   104     "publish",      /* GABBLE_LIST_HANDLE_PUBLISH */
       
   105     "subscribe",    /* GABBLE_LIST_HANDLE_SUBSCRIBE */
       
   106     "known",        /* GABBLE_LIST_HANDLE_KNOWN */
       
   107     "deny"          /* GABBLE_LIST_HANDLE_DENY */
       
   108 };
       
   109 //#endif
       
   110 
       
   111 /* private functions */
       
   112 
       
   113 static GabbleHandlePriv *
       
   114 handle_priv_new ()
       
   115 {
       
   116   GabbleHandlePriv *priv;
       
   117 
       
   118   priv = g_new0 (GabbleHandlePriv, 1);
       
   119 
       
   120   g_datalist_init (&(priv->datalist));
       
   121   return priv;
       
   122 }
       
   123 
       
   124 static void
       
   125 handle_priv_free (GabbleHandlePriv *priv)
       
   126 {
       
   127   g_assert (priv != NULL);
       
   128 
       
   129   g_free(priv->string);
       
   130   g_datalist_clear (&(priv->datalist));
       
   131 #ifdef ENABLE_HANDLE_LEAK_DEBUG
       
   132   g_slist_foreach (priv->traces, handle_leak_trace_free_gfunc, NULL);
       
   133   g_slist_free (priv->traces);
       
   134 #endif /* ENABLE_HANDLE_LEAK_DEBUG */
       
   135   g_free (priv);
       
   136 }
       
   137 
       
   138 static GabbleHandlePriv *
       
   139 handle_priv_lookup (GabbleHandleRepo *repo,
       
   140                     TpHandleType type,
       
   141                     GabbleHandle handle)
       
   142 {
       
   143   GabbleHandlePriv *priv = NULL;
       
   144 
       
   145   g_assert (repo != NULL);
       
   146   g_assert (gabble_handle_type_is_valid (type, NULL));
       
   147   g_assert (handle != 0);
       
   148 
       
   149   switch (type) {
       
   150     case TP_HANDLE_TYPE_CONTACT:
       
   151       priv = g_hash_table_lookup (repo->contact_handles, GINT_TO_POINTER (handle));
       
   152       break;
       
   153     case TP_HANDLE_TYPE_ROOM:
       
   154       priv = g_hash_table_lookup (repo->room_handles, GINT_TO_POINTER (handle));
       
   155       break;
       
   156     case TP_HANDLE_TYPE_LIST:
       
   157       priv = g_datalist_id_get_data (&repo->list_handles, handle);
       
   158       break;
       
   159     default:
       
   160       g_assert_not_reached();
       
   161     }
       
   162 
       
   163   return priv;
       
   164 }
       
   165 
       
   166 static GabbleHandle
       
   167 gabble_handle_alloc (GabbleHandleRepo *repo, TpHandleType type)
       
   168 {
       
   169   GabbleHandle ret = 0;
       
   170 
       
   171   g_assert (repo != NULL);
       
   172   g_assert (gabble_handle_type_is_valid (type, NULL));
       
   173 
       
   174   switch (type) {
       
   175     case TP_HANDLE_TYPE_CONTACT:
       
   176       if (g_heap_size (repo->free_contact_handles))
       
   177         ret = GPOINTER_TO_UINT (g_heap_extract_first (repo->free_contact_handles));
       
   178       else
       
   179         ret = repo->contact_serial++;
       
   180       break;
       
   181     case TP_HANDLE_TYPE_ROOM:
       
   182       if (g_heap_size (repo->free_room_handles))
       
   183         ret = GPOINTER_TO_UINT (g_heap_extract_first (repo->free_room_handles));
       
   184       else
       
   185         ret = repo->room_serial++;
       
   186       break;
       
   187     default:
       
   188       g_assert_not_reached();
       
   189     }
       
   190 
       
   191   return ret;
       
   192 }
       
   193 
       
   194 static gint
       
   195 handle_compare_func (gconstpointer a, gconstpointer b)
       
   196 {
       
   197   GabbleHandle first = GPOINTER_TO_UINT (a);
       
   198   GabbleHandle second = GPOINTER_TO_UINT (b);
       
   199 
       
   200   return (first == second) ? 0 : ((first < second) ? -1 : 1);
       
   201 }
       
   202 
       
   203 static void
       
   204 handle_priv_remove (GabbleHandleRepo *repo,
       
   205                     TpHandleType type,
       
   206                     GabbleHandle handle)
       
   207 {
       
   208   GabbleHandlePriv *priv;
       
   209   const gchar *string;
       
   210 
       
   211   g_assert (gabble_handle_type_is_valid (type, NULL));
       
   212   g_assert (handle != 0);
       
   213   g_assert (repo != NULL);
       
   214 
       
   215   priv = handle_priv_lookup (repo, type, handle);
       
   216 
       
   217   g_assert (priv != NULL);
       
   218 
       
   219   string = priv->string;
       
   220 
       
   221   switch (type) {
       
   222     case TP_HANDLE_TYPE_CONTACT:
       
   223       g_hash_table_remove (repo->contact_strings, string);
       
   224       g_hash_table_remove (repo->contact_handles, GINT_TO_POINTER (handle));
       
   225       if (handle == repo->contact_serial-1)
       
   226         repo->contact_serial--;
       
   227       else
       
   228         g_heap_add (repo->free_contact_handles, GUINT_TO_POINTER (handle));
       
   229       break;
       
   230     case TP_HANDLE_TYPE_ROOM:
       
   231       g_hash_table_remove (repo->room_strings, string);
       
   232       g_hash_table_remove (repo->room_handles, GINT_TO_POINTER (handle));
       
   233       if (handle == repo->room_serial-1)
       
   234         repo->room_serial--;
       
   235       else
       
   236         g_heap_add (repo->free_room_handles, GUINT_TO_POINTER (handle));
       
   237       break;
       
   238     case TP_HANDLE_TYPE_LIST:
       
   239       g_dataset_id_remove_data (&repo->list_handles, handle);
       
   240       break;
       
   241     default:
       
   242       g_assert_not_reached ();
       
   243     }
       
   244 }
       
   245 
       
   246 static void
       
   247 handles_name_owner_changed_cb (DBusGProxy *proxy,
       
   248                                const gchar *name,
       
   249                                const gchar *old_owner,
       
   250                                const gchar *new_owner,
       
   251                                gpointer data)
       
   252 {
       
   253   GabbleHandleRepo *repo = (GabbleHandleRepo *) data;
       
   254 
       
   255   if (old_owner && strlen (old_owner))
       
   256     {
       
   257       if (!new_owner || !strlen (new_owner))
       
   258         {
       
   259           g_datalist_remove_data (&repo->client_contact_handle_sets, old_owner);
       
   260           g_datalist_remove_data (&repo->client_room_handle_sets, old_owner);
       
   261         }
       
   262     }
       
   263 }
       
   264 
       
   265 /* public API */
       
   266 
       
   267 /**
       
   268  * gabble_handle_jid_is_valid
       
   269  *
       
   270  * Validates a jid for given handle type and returns TRUE/FALSE
       
   271  * on success/failure. In the latter case further information is
       
   272  * provided through error if set.
       
   273  */
       
   274 gboolean
       
   275 gabble_handle_jid_is_valid (TpHandleType type, const gchar *jid, GError **error)
       
   276 {
       
   277   if (type == TP_HANDLE_TYPE_CONTACT || type == TP_HANDLE_TYPE_ROOM)
       
   278     {
       
   279       if (!strchr (jid, '@'))
       
   280         {
       
   281           g_debug ("%s: jid %s has no @", G_STRFUNC, jid);
       
   282 
       
   283           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   284               "jid %s has no @", jid);
       
   285 
       
   286           return FALSE;
       
   287         }
       
   288 
       
   289       /* FIXME: do more extensive checking */
       
   290     }
       
   291   else
       
   292     {
       
   293       g_assert_not_reached ();
       
   294       /* FIXME: add checking for other types here */
       
   295     }
       
   296 
       
   297   return TRUE;
       
   298 }
       
   299 
       
   300 gboolean
       
   301 gabble_handle_type_is_valid (TpHandleType type, GError **error)
       
   302 {
       
   303   gboolean ret;
       
   304 
       
   305   if (type > TP_HANDLE_TYPE_NONE && type <= TP_HANDLE_TYPE_LIST)
       
   306     {
       
   307       ret = TRUE;
       
   308     }
       
   309   else
       
   310     {
       
   311       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   312           "invalid handle type %u", type);
       
   313       ret = FALSE;
       
   314     }
       
   315 
       
   316   return ret;
       
   317 }
       
   318 
       
   319 
       
   320 GabbleHandleRepo *
       
   321 gabble_handle_repo_new ()
       
   322 {
       
   323   GabbleHandleRepo *repo;
       
   324   GabbleHandle publish, subscribe, known, deny;
       
   325 
       
   326   repo = g_new0 (GabbleHandleRepo, 1);
       
   327 
       
   328   repo->contact_handles = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) handle_priv_free);
       
   329 
       
   330   repo->room_handles = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) handle_priv_free);
       
   331 
       
   332   repo->contact_strings = g_hash_table_new (g_str_hash, g_str_equal);
       
   333   repo->room_strings = g_hash_table_new (g_str_hash, g_str_equal);
       
   334 
       
   335   repo->free_contact_handles = g_heap_new (handle_compare_func);
       
   336   repo->free_room_handles = g_heap_new (handle_compare_func);
       
   337 
       
   338   repo->contact_serial = 1;
       
   339   repo->room_serial = 1;
       
   340 
       
   341   g_datalist_init (&repo->list_handles);
       
   342 
       
   343   publish = GABBLE_LIST_HANDLE_PUBLISH;
       
   344   g_datalist_id_set_data_full (&repo->list_handles, (GQuark) publish,
       
   345       handle_priv_new(), (GDestroyNotify) handle_priv_free);
       
   346 
       
   347   subscribe = GABBLE_LIST_HANDLE_SUBSCRIBE;
       
   348   g_datalist_id_set_data_full (&repo->list_handles, (GQuark) subscribe,
       
   349       handle_priv_new(), (GDestroyNotify) handle_priv_free);
       
   350 
       
   351   known = GABBLE_LIST_HANDLE_KNOWN;
       
   352   g_datalist_id_set_data_full (&repo->list_handles, (GQuark) known,
       
   353       handle_priv_new(), (GDestroyNotify) handle_priv_free);
       
   354 
       
   355   deny = GABBLE_LIST_HANDLE_DENY;
       
   356   g_datalist_id_set_data_full (&repo->list_handles, (GQuark) deny,
       
   357       handle_priv_new(), (GDestroyNotify) handle_priv_free);
       
   358 
       
   359   g_datalist_init (&repo->client_contact_handle_sets);
       
   360   g_datalist_init (&repo->client_room_handle_sets);
       
   361 
       
   362   repo->bus_service_proxy = dbus_g_proxy_new_for_name (tp_get_bus(),
       
   363                                                        DBUS_SERVICE_DBUS,
       
   364                                                        DBUS_PATH_DBUS,
       
   365                                                        DBUS_INTERFACE_DBUS);
       
   366 
       
   367   dbus_g_proxy_add_signal (repo->bus_service_proxy,
       
   368                            "NameOwnerChanged",
       
   369                            G_TYPE_STRING,
       
   370                            G_TYPE_STRING,
       
   371                            G_TYPE_STRING,
       
   372                            G_TYPE_INVALID);
       
   373   dbus_g_proxy_connect_signal (repo->bus_service_proxy,
       
   374                                "NameOwnerChanged",
       
   375                                G_CALLBACK (handles_name_owner_changed_cb),
       
   376                                repo,
       
   377                                NULL);
       
   378 
       
   379   return repo;
       
   380 }
       
   381 
       
   382 #ifdef ENABLE_HANDLE_LEAK_DEBUG
       
   383 
       
   384 static void
       
   385 handle_leak_debug_printbt_foreach (gpointer data, gpointer user_data)
       
   386 {
       
   387   HandleLeakTrace *hltrace = (HandleLeakTrace *) data;
       
   388   int i;
       
   389 
       
   390   for (i = 1; i < hltrace->len; i++)
       
   391     {
       
   392       g_message ("\t\t%s\n", hltrace->trace[i]);
       
   393     }
       
   394 
       
   395   g_message ("\n");
       
   396 }
       
   397 
       
   398 static void
       
   399 handle_leak_debug_printhandles_foreach (gpointer key, gpointer value, gpointer ignore)
       
   400 {
       
   401   GabbleHandle handle = GPOINTER_TO_UINT (key);
       
   402   GabbleHandlePriv *priv = (GabbleHandlePriv *) value;
       
   403 
       
   404   g_message ("\t%5u: %s (%u refs), traces:\n", handle, priv->string, priv->refcount);
       
   405   
       
   406   g_slist_foreach (priv->traces, handle_leak_debug_printbt_foreach, NULL);
       
   407 }
       
   408 
       
   409 static void
       
   410 handle_leak_debug_print_report (GabbleHandleRepo *repo)
       
   411 {
       
   412   g_assert (repo != NULL);
       
   413 
       
   414   g_message ("The following contact handles were not freed:\n");
       
   415   g_hash_table_foreach (repo->contact_handles, handle_leak_debug_printhandles_foreach, NULL);
       
   416   g_message ("The following room handles were not freed:\n");
       
   417   g_hash_table_foreach (repo->room_handles, handle_leak_debug_printhandles_foreach, NULL);
       
   418 }
       
   419 
       
   420 static HandleLeakTrace *
       
   421 handle_leak_debug_bt ()
       
   422 {
       
   423   void *bt_addresses[16];
       
   424   HandleLeakTrace *ret = g_new0 (HandleLeakTrace, 1);
       
   425   
       
   426   ret->len = backtrace (bt_addresses, 16);
       
   427   ret->trace = backtrace_symbols (bt_addresses, ret->len);
       
   428 
       
   429   return ret;
       
   430 }
       
   431 
       
   432 #define HANDLE_LEAK_DEBUG_DO(traces_slist) \
       
   433   { (traces_slist) =  g_slist_append ((traces_slist), handle_leak_debug_bt ()); }
       
   434 
       
   435 #else /* !ENABLE_HANDLE_LEAK_DEBUG */
       
   436 
       
   437 #define HANDLE_LEAK_DEBUG_DO(traces_slist) {}
       
   438 
       
   439 #endif /* ENABLE_HANDLE_LEAK_DEBUG */
       
   440 
       
   441 
       
   442 void
       
   443 gabble_handle_repo_destroy (GabbleHandleRepo *repo)
       
   444 {
       
   445   g_assert (repo != NULL);
       
   446   g_assert (repo->contact_handles);
       
   447   g_assert (repo->room_handles);
       
   448   g_assert (repo->contact_strings);
       
   449   g_assert (repo->room_strings);
       
   450 
       
   451   g_datalist_clear (&repo->client_contact_handle_sets);
       
   452   g_datalist_clear (&repo->client_room_handle_sets);
       
   453 
       
   454 #ifdef ENABLE_HANDLE_LEAK_DEBUG
       
   455   handle_leak_debug_print_report (repo);
       
   456 #endif /* ENABLE_HANDLE_LEAK_DEBUG */
       
   457 
       
   458   g_hash_table_destroy (repo->contact_handles);
       
   459   g_hash_table_destroy (repo->room_handles);
       
   460   g_hash_table_destroy (repo->contact_strings);
       
   461   g_hash_table_destroy (repo->room_strings);
       
   462   g_heap_destroy (repo->free_contact_handles);
       
   463   g_heap_destroy (repo->free_room_handles);
       
   464   g_datalist_clear (&repo->list_handles);
       
   465 
       
   466   dbus_g_proxy_disconnect_signal (repo->bus_service_proxy,
       
   467                                   "NameOwnerChanged",
       
   468                                   G_CALLBACK (handles_name_owner_changed_cb),
       
   469                                   repo);
       
   470   g_object_unref (G_OBJECT (repo->bus_service_proxy));
       
   471 
       
   472   g_free (repo);
       
   473 }
       
   474 
       
   475 
       
   476 gboolean
       
   477 gabble_handle_is_valid (GabbleHandleRepo *repo, TpHandleType type, GabbleHandle handle, GError **error)
       
   478 {
       
   479   GArray *arr;
       
   480   gboolean ret;
       
   481 
       
   482   arr = g_array_new (FALSE, FALSE, sizeof (GabbleHandle));
       
   483   g_array_insert_val (arr, 0, handle);
       
   484 
       
   485   ret = gabble_handles_are_valid (repo, type, arr, FALSE, error);
       
   486 
       
   487   g_array_free (arr, TRUE);
       
   488 
       
   489   return ret;
       
   490 }
       
   491 
       
   492 gboolean
       
   493 gabble_handles_are_valid (GabbleHandleRepo *repo,
       
   494                           TpHandleType type,
       
   495                           const GArray *array,
       
   496                           gboolean allow_zero,
       
   497                           GError **error)
       
   498 {
       
   499   guint i;
       
   500 
       
   501   g_return_val_if_fail (repo != NULL, FALSE);
       
   502   g_return_val_if_fail (array != NULL, FALSE);
       
   503 
       
   504   if (!gabble_handle_type_is_valid (type, error))
       
   505     return FALSE;
       
   506 
       
   507   for (i = 0; i < array->len; i++)
       
   508     {
       
   509       GabbleHandle handle = g_array_index (array, GabbleHandle, i);
       
   510 
       
   511       if (handle == 0)
       
   512         {
       
   513           if (allow_zero)
       
   514               continue;
       
   515 
       
   516           g_debug ("someone tried to validate handle zero");
       
   517 
       
   518           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   519               "invalid handle %u", handle);
       
   520           return FALSE;
       
   521         }
       
   522 
       
   523       if (handle_priv_lookup (repo, type, handle) == NULL)
       
   524         {
       
   525           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   526               "invalid handle %u", handle);
       
   527           return FALSE;
       
   528         }
       
   529     }
       
   530 
       
   531   return TRUE;
       
   532 }
       
   533 
       
   534 
       
   535 gboolean
       
   536 gabble_handle_ref (GabbleHandleRepo *repo,
       
   537                    TpHandleType type,
       
   538                    GabbleHandle handle)
       
   539 {
       
   540   GabbleHandlePriv *priv;
       
   541 
       
   542   if (type == TP_HANDLE_TYPE_LIST)
       
   543     {
       
   544       if (handle >= GABBLE_LIST_HANDLE_PUBLISH && handle <= GABBLE_LIST_HANDLE_DENY)
       
   545         return TRUE;
       
   546       else
       
   547         return FALSE;
       
   548     }
       
   549 
       
   550   priv = handle_priv_lookup (repo, type, handle);
       
   551 
       
   552   if (priv == NULL)
       
   553     return FALSE;
       
   554 
       
   555   priv->refcount++;
       
   556 
       
   557   HANDLE_LEAK_DEBUG_DO (priv->traces);
       
   558 
       
   559   return TRUE;
       
   560 }
       
   561 
       
   562 
       
   563 gboolean
       
   564 gabble_handle_unref (GabbleHandleRepo *repo,
       
   565                      TpHandleType type,
       
   566                      GabbleHandle handle)
       
   567 {
       
   568   GabbleHandlePriv *priv;
       
   569 
       
   570   if (type == TP_HANDLE_TYPE_LIST)
       
   571     {
       
   572       if (handle >= GABBLE_LIST_HANDLE_PUBLISH && handle <= GABBLE_LIST_HANDLE_DENY)
       
   573         return TRUE;
       
   574       else
       
   575         return FALSE;
       
   576     }
       
   577 
       
   578   priv = handle_priv_lookup (repo, type, handle);
       
   579 
       
   580   if (priv == NULL)
       
   581     return FALSE;
       
   582 
       
   583   HANDLE_LEAK_DEBUG_DO (priv->traces);
       
   584 
       
   585   g_assert (priv->refcount > 0);
       
   586 
       
   587   priv->refcount--;
       
   588 
       
   589   if (priv->refcount == 0)
       
   590     handle_priv_remove (repo, type, handle);
       
   591 
       
   592   return TRUE;
       
   593 }
       
   594 
       
   595 
       
   596 const char *
       
   597 gabble_handle_inspect (GabbleHandleRepo *repo,
       
   598                        TpHandleType type,
       
   599                        GabbleHandle handle)
       
   600 {
       
   601   GabbleHandlePriv *priv;
       
   602 
       
   603   if (type == TP_HANDLE_TYPE_LIST)
       
   604     {
       
   605       g_assert (handle >= GABBLE_LIST_HANDLE_PUBLISH
       
   606                   && handle <= GABBLE_LIST_HANDLE_DENY);
       
   607       return list_handle_strings[handle-1];
       
   608     }
       
   609 
       
   610   priv = handle_priv_lookup (repo, type, handle);
       
   611 
       
   612   if (priv == NULL)
       
   613     return NULL;
       
   614   else
       
   615     return priv->string;
       
   616 }
       
   617 
       
   618 static GabbleHandle
       
   619 _handle_lookup_by_jid (GabbleHandleRepo *repo,
       
   620                        const gchar *jid)
       
   621 {
       
   622   GabbleHandle handle;
       
   623 
       
   624   handle = GPOINTER_TO_UINT (g_hash_table_lookup (repo->contact_strings, jid));
       
   625 
       
   626   if (0 == handle)
       
   627     return 0;
       
   628 
       
   629   return handle;
       
   630 }
       
   631 
       
   632 
       
   633 GabbleHandle
       
   634 gabble_handle_for_contact (GabbleHandleRepo *repo,
       
   635                            const char *jid,
       
   636                            gboolean with_resource)
       
   637 {
       
   638   char *username = NULL;
       
   639   char *server = NULL;
       
   640   char *resource = NULL;
       
   641   char *clean_jid = NULL;
       
   642   GabbleHandle handle = 0;
       
   643   GabbleHandlePriv *priv;
       
   644 
       
   645   g_assert (repo != NULL);
       
   646   g_assert (jid != NULL);
       
   647   g_assert (*jid != '\0');
       
   648 
       
   649   gabble_decode_jid (jid, &username, &server, &resource);
       
   650 
       
   651   if (NULL == username || '\0' == *username)
       
   652     goto OUT;
       
   653 
       
   654   if (NULL == resource && with_resource)
       
   655     goto OUT;
       
   656 
       
   657   if (NULL != resource)
       
   658     {
       
   659       clean_jid = g_strdup_printf ("%s@%s/%s", username, server, resource);
       
   660       handle = _handle_lookup_by_jid (repo, clean_jid);
       
   661 
       
   662       if (0 != handle)
       
   663         goto OUT;
       
   664     }
       
   665 
       
   666   if (!with_resource)
       
   667     {
       
   668       g_free (clean_jid);
       
   669       clean_jid = g_strdup_printf ("%s@%s", username, server);
       
   670       handle = _handle_lookup_by_jid (repo, clean_jid);
       
   671 
       
   672       if (0 != handle)
       
   673         goto OUT;
       
   674     }
       
   675 
       
   676   handle = gabble_handle_alloc (repo, TP_HANDLE_TYPE_CONTACT);
       
   677   priv = handle_priv_new ();
       
   678   priv->string = clean_jid;
       
   679   clean_jid = NULL;
       
   680   g_hash_table_insert (repo->contact_handles, GINT_TO_POINTER (handle), priv);
       
   681   g_hash_table_insert (repo->contact_strings, priv->string, GUINT_TO_POINTER (handle));
       
   682 
       
   683   HANDLE_LEAK_DEBUG_DO (priv->traces);
       
   684 
       
   685 OUT:
       
   686 
       
   687   g_free (clean_jid);
       
   688   g_free (username);
       
   689   g_free (server);
       
   690   g_free (resource);
       
   691   return handle;
       
   692 }
       
   693 
       
   694 gboolean
       
   695 gabble_handle_for_room_exists (GabbleHandleRepo *repo,
       
   696                                const gchar *jid,
       
   697                                gboolean ignore_nick)
       
   698 {
       
   699   GabbleHandle handle;
       
   700   gchar *room, *service, *nick;
       
   701   gchar *clean_jid;
       
   702 
       
   703   gabble_decode_jid (jid, &room, &service, &nick);
       
   704 
       
   705   if (!room || !service || room[0] == '\0')
       
   706     return FALSE;
       
   707 
       
   708   if (ignore_nick || !nick)
       
   709     clean_jid = g_strdup_printf ("%s@%s", room, service);
       
   710   else
       
   711     clean_jid = g_strdup_printf ("%s@%s/%s", room, service, nick);
       
   712 
       
   713   handle = GPOINTER_TO_UINT (g_hash_table_lookup (repo->room_strings,
       
   714                                                   clean_jid));
       
   715   
       
   716   g_free (clean_jid);
       
   717   g_free (room);
       
   718   g_free (service);
       
   719   g_free (nick);
       
   720 
       
   721   if (handle == 0)
       
   722     return FALSE;
       
   723 
       
   724   return (handle_priv_lookup (repo, TP_HANDLE_TYPE_ROOM, handle) != NULL);
       
   725 }
       
   726 
       
   727 
       
   728 GabbleHandle
       
   729 gabble_handle_for_room (GabbleHandleRepo *repo,
       
   730                         const gchar *jid)
       
   731 {
       
   732   GabbleHandle handle;
       
   733   gchar *room, *service, *clean_jid;
       
   734 
       
   735   g_assert (repo != NULL);
       
   736   g_assert (jid != NULL);
       
   737   g_assert (*jid != '\0');
       
   738 
       
   739   handle = 0;
       
   740 
       
   741   room = service = NULL;
       
   742   gabble_decode_jid (jid, &room, &service, NULL);
       
   743 
       
   744   if (room && service && *room != '\0')
       
   745     {
       
   746       clean_jid = g_strdup_printf ("%s@%s", room, service);
       
   747 
       
   748       handle = GPOINTER_TO_UINT (g_hash_table_lookup (repo->room_strings, clean_jid));
       
   749 
       
   750       if (handle == 0)
       
   751         {
       
   752           GabbleHandlePriv *priv;
       
   753           handle = gabble_handle_alloc (repo, TP_HANDLE_TYPE_ROOM);
       
   754           priv = handle_priv_new ();
       
   755           priv->string = clean_jid;
       
   756           g_hash_table_insert (repo->room_handles, GUINT_TO_POINTER (handle), priv);
       
   757           g_hash_table_insert (repo->room_strings, clean_jid, GUINT_TO_POINTER (handle));
       
   758           HANDLE_LEAK_DEBUG_DO (priv->traces);
       
   759         }
       
   760       else
       
   761         {
       
   762           g_free (clean_jid);
       
   763         }
       
   764     }
       
   765 
       
   766   g_free (room);
       
   767   g_free (service);
       
   768 
       
   769   return handle;
       
   770 }
       
   771 
       
   772 
       
   773 GabbleHandle
       
   774 gabble_handle_for_list (GabbleHandleRepo *repo,
       
   775                         const gchar *list)
       
   776 {
       
   777   GabbleHandle handle = 0;
       
   778   int i;
       
   779 
       
   780   g_assert (repo != NULL);
       
   781   g_assert (list != NULL);
       
   782 
       
   783   for (i = 0; i < GABBLE_LIST_HANDLE_DENY; i++)
       
   784     {
       
   785       if (0 == strcmp (list_handle_strings[i], list))
       
   786         handle = (GabbleHandle) i + 1;
       
   787     }
       
   788 
       
   789   return handle;
       
   790 }
       
   791 
       
   792 /**
       
   793  * gabble_handle_set_qdata:
       
   794  * @repo: A #GabbleHandleRepo
       
   795  * @type: The handle type
       
   796  * @handle: A handle to set data on
       
   797  * @key_id: Key id to associate data with
       
   798  * @data: data to associate with handle
       
   799  * @destroy: A #GDestroyNotify to call to detroy the data,
       
   800  *           or NULL if not needed.
       
   801  *
       
   802  * Associates a blob of data with a given handle and a given key
       
   803  *
       
   804  * If @destroy is set, then the data is freed when the handle is freed.
       
   805  */
       
   806 
       
   807 gboolean
       
   808 gabble_handle_set_qdata (GabbleHandleRepo *repo,
       
   809                          TpHandleType type, GabbleHandle handle,
       
   810                          GQuark key_id, gpointer data, GDestroyNotify destroy)
       
   811 {
       
   812   GabbleHandlePriv *priv;
       
   813   priv = handle_priv_lookup (repo, type, handle);
       
   814 
       
   815   if (!priv)
       
   816     return FALSE;
       
   817 
       
   818   g_datalist_id_set_data_full (&priv->datalist, key_id, data, destroy);
       
   819   return TRUE;
       
   820 }
       
   821 
       
   822 /**
       
   823  * gabble_handle_get_qdata:
       
   824  * @repo: A #GabbleHandleRepo
       
   825  * @type: The handle type
       
   826  * @handle: A handle to get data from
       
   827  * @key_id: Key id of data to fetch
       
   828  *
       
   829  * Gets the data associated with a given key on a given handle
       
   830  */
       
   831 gpointer
       
   832 gabble_handle_get_qdata (GabbleHandleRepo *repo,
       
   833                          TpHandleType type, GabbleHandle handle,
       
   834                          GQuark key_id)
       
   835 {
       
   836   GabbleHandlePriv *priv;
       
   837   priv = handle_priv_lookup (repo, type, handle);
       
   838 
       
   839   if (!priv)
       
   840     return NULL;
       
   841 
       
   842   return g_datalist_id_get_data(&priv->datalist, key_id);
       
   843 }
       
   844 
       
   845 /**
       
   846  * gabble_handle_client_hold:
       
   847  * @repo: a #GabbleHandleRepo
       
   848  * @client_name: D-Bus bus name of client to hold the handle for
       
   849  * @handle: the handle to hold
       
   850  * @type: type of handle to hold
       
   851  * @error: used to return a pointer to a GError detailing any error that occurred
       
   852  *
       
   853  * Marks a handle as held by a given client.
       
   854  *
       
   855  * Returns: Whether the handle was succesfully marked as held or an error occurred.
       
   856  */
       
   857 
       
   858 gboolean
       
   859 gabble_handle_client_hold (GabbleHandleRepo *repo,
       
   860                            const gchar *client_name,
       
   861                            GabbleHandle handle,
       
   862                            TpHandleType type,
       
   863                            GError **error)
       
   864 {
       
   865   GData **handle_set_list;
       
   866   GabbleHandleSet *handle_set;
       
   867 
       
   868   g_assert (repo != NULL);
       
   869 
       
   870   switch (type)
       
   871     {
       
   872     case TP_HANDLE_TYPE_CONTACT:
       
   873       handle_set_list = &repo->client_contact_handle_sets;
       
   874       break;
       
   875     case TP_HANDLE_TYPE_ROOM:
       
   876       handle_set_list = &repo->client_room_handle_sets;
       
   877       break;
       
   878     case TP_HANDLE_TYPE_LIST:
       
   879       /* no-op */
       
   880       return TRUE;
       
   881     default:
       
   882       g_critical ("%s: called with invalid handle type %u", G_STRFUNC, type);
       
   883       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   884           "invalid handle type %u", type);
       
   885       return FALSE;
       
   886     }
       
   887 
       
   888   if (!client_name || *client_name == '\0')
       
   889     {
       
   890       g_critical ("%s: called with invalid client name", G_STRFUNC);
       
   891       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   892           "invalid client name");
       
   893       return FALSE;
       
   894     }
       
   895 
       
   896   handle_set = (GabbleHandleSet*) g_datalist_get_data (handle_set_list, client_name);
       
   897 
       
   898   if (!handle_set)
       
   899     {
       
   900       handle_set = handle_set_new (repo, type);
       
   901       g_datalist_set_data_full (handle_set_list,
       
   902                                 client_name,
       
   903                                 handle_set,
       
   904                                 (GDestroyNotify) handle_set_destroy);
       
   905     }
       
   906 
       
   907   handle_set_add (handle_set, handle);
       
   908 
       
   909   return TRUE;
       
   910 }
       
   911 
       
   912 /**
       
   913  * gabble_handle_client_release:
       
   914  * @repo: a #GabbleHandleRepo
       
   915  * @client_name: D-Bus bus name of client to release the handle for
       
   916  * @handle: the handle to release
       
   917  * @type: type of handle to release
       
   918  * @error: used to return a pointer to a GError detailing any error that occurred
       
   919  *
       
   920  * Unmarks a handle as held by a given client.
       
   921  *
       
   922  * Returns: Whether the handle had been marked as held by the given client and now unmarked or not.
       
   923  */
       
   924 
       
   925 gboolean
       
   926 gabble_handle_client_release (GabbleHandleRepo *repo,
       
   927                            const gchar *client_name,
       
   928                            GabbleHandle handle,
       
   929                            TpHandleType type,
       
   930                            GError **error)
       
   931 {
       
   932   GData **handle_set_list;
       
   933   GabbleHandleSet *handle_set;
       
   934 
       
   935   g_assert (repo != NULL);
       
   936 
       
   937   switch (type)
       
   938     {
       
   939     case TP_HANDLE_TYPE_CONTACT:
       
   940       handle_set_list = &repo->client_contact_handle_sets;
       
   941       break;
       
   942     case TP_HANDLE_TYPE_ROOM:
       
   943       handle_set_list = &repo->client_room_handle_sets;
       
   944       break;
       
   945     case TP_HANDLE_TYPE_LIST:
       
   946       /* no-op */
       
   947       return TRUE;
       
   948     default:
       
   949       g_critical ("%s: called with invalid handle type %u", G_STRFUNC, type);
       
   950       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   951           "invalid handle type %u", type);
       
   952       return FALSE;
       
   953     }
       
   954 
       
   955   if (!client_name || *client_name == '\0')
       
   956     {
       
   957       g_critical ("%s: called with invalid client name", G_STRFUNC);
       
   958       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   959           "invalid client name");
       
   960       return FALSE;
       
   961     }
       
   962 
       
   963   handle_set = (GabbleHandleSet*) g_datalist_get_data (handle_set_list, client_name);
       
   964 
       
   965   if (!handle_set)
       
   966     {
       
   967       g_critical ("%s: no handle set found for the given client %s", G_STRFUNC, client_name);
       
   968       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
   969           "the given client %s wasn't holding any handles", client_name);
       
   970       return FALSE;
       
   971     }
       
   972 
       
   973   if (!handle_set_remove (handle_set, handle))
       
   974     {
       
   975       g_critical ("%s: the client %s wasn't holding the handle %u", G_STRFUNC,
       
   976           client_name, handle);
       
   977       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
   978           "the given client %s wasn't holding the handle %u", client_name,
       
   979           handle);
       
   980       return FALSE;
       
   981     }
       
   982 
       
   983   return TRUE;
       
   984 }
       
   985