summaryrefslogtreecommitdiff
path: root/src/idle-handles.c
diff options
context:
space:
mode:
authorOlli Salli <olli.salli@collabora.co.uk>2007-01-29 15:47:36 +0000
committerOlli Salli <olli.salli@collabora.co.uk>2007-01-29 15:47:36 +0000
commit7a897b917168505139de0c726e96663e2c7ed977 (patch)
treeae8ec0815c2f66152812bdc85439c44bc708cd67 /src/idle-handles.c
parent36e7ba52d4397763aca609c368bc6f2472b644a1 (diff)
downloadtelepathy-idle-7a897b917168505139de0c726e96663e2c7ed977.tar.gz
Initial import (migration from SF.net SVN)
20070129154736-9db4d-be80727a61507e6581870228122d0d2a7c12995e.gz
Diffstat (limited to 'src/idle-handles.c')
-rw-r--r--src/idle-handles.c589
1 files changed, 589 insertions, 0 deletions
diff --git a/src/idle-handles.c b/src/idle-handles.c
new file mode 100644
index 0000000..b7efb8c
--- /dev/null
+++ b/src/idle-handles.c
@@ -0,0 +1,589 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "idle-handles.h"
+#include "idle-handles-private.h"
+
+#include "idle-connection.h"
+
+#include "gintset.h"
+
+#define idle_handle_priv_new() (g_slice_new0(IdleHandlePriv))
+
+gboolean idle_nickname_is_valid(const gchar *nickname)
+{
+ gsize len;
+ gunichar ucs4char;
+ const gchar *char_pos = nickname;
+
+ len = g_utf8_strlen(nickname, -1);
+
+ if (!len)
+ {
+ return FALSE;
+ }
+
+ while (char_pos != NULL)
+ {
+ ucs4char = g_utf8_get_char_validated(char_pos, -1);
+
+ switch (*char_pos)
+ {
+ case '[':
+ case ']':
+ case '\\':
+ case '`':
+ case '_':
+ case '^':
+ case '{':
+ case '|':
+ case '}':
+ case '-':
+ break;
+ case '\0':
+ {
+ return TRUE;
+ }
+ break;
+ default:
+ {
+ if (!(g_unichar_isalpha(ucs4char) || ((char_pos != nickname) && g_unichar_isdigit(ucs4char))))
+ {
+ return FALSE;
+ }
+ }
+ break;
+ }
+
+ char_pos = g_utf8_find_next_char(char_pos, NULL);
+ }
+
+ return TRUE;
+}
+
+gboolean idle_channelname_is_valid(const gchar *channel)
+{
+ const static gchar not_allowed_chars[6] = {' ', '\007', ',', '\r', '\n', ':'};
+ const gchar *tmp, *tmp2;
+ int i;
+ gsize len;
+
+ if ((channel[0] != '#') && (channel[0] != '!') && (channel[0] != '&') && (channel[0] != '+'))
+ {
+ return FALSE;
+ }
+
+ len = strlen(channel);
+
+ if ((len < 2) || (len > 50))
+ {
+ return FALSE;
+ }
+
+ if (channel[0] == '!')
+ {
+ for (i=0; i<5; i++)
+ {
+ if (!g_ascii_isupper(channel[i+1]) && !isdigit(channel[i+1]))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ tmp = strchr(channel+1, ':');
+
+ if (tmp != NULL)
+ {
+ for (tmp2 = channel+1; tmp2 != tmp; tmp2++)
+ {
+ for (i=0; i<6; i++)
+ {
+ if (*tmp2 == not_allowed_chars[i])
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ for (tmp2 = tmp+1; tmp2 != channel+len; tmp2++)
+ {
+ for (i=0; i<6; i++)
+ {
+ if (*tmp2 == not_allowed_chars[i])
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (tmp2 = channel+1; tmp2 != channel+len; tmp2++)
+ {
+ for (i=0; i<6; i++)
+ {
+ if (*tmp2 == not_allowed_chars[i])
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ }
+
+ return TRUE;
+}
+
+static void handle_priv_free(IdleHandlePriv *priv)
+{
+ g_assert(priv != NULL);
+ g_free(priv->string);
+
+ if (priv->cp != NULL)
+ {
+ idle_contact_presence_free(priv->cp);
+ }
+
+ g_slice_free(IdleHandlePriv, priv);
+}
+
+static IdleHandle idle_handle_alloc(IdleHandleStorage *storage, TpHandleType type)
+{
+ IdleHandle ret;
+
+ g_assert(storage != NULL);
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ ret = GPOINTER_TO_INT(g_heap_extract_first(storage->contact_unused));
+
+ if (ret == 0)
+ {
+ ret = storage->contact_serial++;
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ ret = GPOINTER_TO_INT(g_heap_extract_first(storage->room_unused));
+
+ if (ret == 0)
+ {
+ ret = storage->room_serial++;
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ g_debug("%s: unsupported handle type %u", G_STRFUNC, type);
+ ret = 0;
+ }
+ break;
+ }
+
+/* g_debug("%s: returning %u (type %u)", G_STRFUNC, ret, type);*/
+
+ return ret;
+}
+
+IdleHandlePriv *handle_priv_lookup(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ g_assert(storage != NULL);
+
+ if (handle == 0)
+ {
+ return NULL;
+ }
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ priv = g_hash_table_lookup(storage->contact_handles, GINT_TO_POINTER(handle));
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ priv = g_hash_table_lookup(storage->room_handles, GINT_TO_POINTER(handle));
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ g_critical("%s: Only TP_HANDLE_TYPE_CONTACT and TP_HANDLE_TYPE_ROOM supported!", G_STRFUNC);
+ return NULL;
+ }
+ break;
+ }
+
+ return priv;
+}
+
+void handle_priv_remove(IdleHandleStorage *storage, TpHandleType type, IdleHandlePriv *priv, IdleHandle handle)
+{
+ g_assert(storage != NULL);
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ g_hash_table_remove(storage->contact_strings, priv->string);
+ g_hash_table_remove(storage->contact_handles, GINT_TO_POINTER(handle));
+
+ if (handle == storage->contact_serial-1)
+ {
+ /* take advantage of good luck ;) */
+ storage->contact_serial--;
+ }
+ else
+ {
+ g_heap_add(storage->contact_unused, GINT_TO_POINTER(handle));
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ g_hash_table_remove(storage->room_strings, priv->string);
+ g_hash_table_remove(storage->room_handles, GINT_TO_POINTER(handle));
+
+ if (handle == storage->room_serial-1)
+ {
+ storage->room_serial--;
+ }
+ else
+ {
+ g_heap_add(storage->room_unused, GINT_TO_POINTER(handle));
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ g_critical("%s: Only TP_HANDLE_TYPE_CONTACT and TP_HANDLE_TYPE_ROOM supported!", G_STRFUNC);
+ return;
+ }
+ break;
+ }
+}
+
+gboolean idle_handle_type_is_valid(TpHandleType type)
+{
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ return TRUE;
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ return FALSE;
+ }
+ break;
+ }
+}
+
+static guint g_strncase_hash(gconstpointer key)
+{
+ guint ret;
+ gchar *tmp;
+
+ tmp = g_utf8_strdown(key, 32);
+ ret = g_str_hash(tmp);
+
+ g_free(tmp);
+
+ return ret;
+}
+
+static gboolean g_strncase_equal(gconstpointer a, gconstpointer b)
+{
+ gchar *s1, *s2;
+ gboolean ret;
+
+ s1 = g_utf8_casefold(a, -1);
+ s2 = g_utf8_casefold(b, -1);
+
+ ret = (strcmp(s1, s2) == 0);
+
+ g_free(s1);
+ g_free(s2);
+
+ return ret;
+}
+
+static gint idle_handle_compare(gconstpointer a, gconstpointer b)
+{
+ return (a < b) ? -1 : (a == b) ? 0 : 1;
+}
+
+IdleHandleStorage *idle_handle_storage_new()
+{
+ IdleHandleStorage *ret;
+
+ ret = g_slice_new0(IdleHandleStorage);
+
+ ret->contact_handles = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)(handle_priv_free));
+ ret->room_handles = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)(handle_priv_free));
+
+ ret->contact_strings = g_hash_table_new_full(g_strncase_hash, g_strncase_equal, NULL, NULL);
+ ret->room_strings = g_hash_table_new_full(g_strncase_hash, g_strncase_equal, NULL, NULL);
+
+ ret->contact_unused = g_heap_new(idle_handle_compare);
+ ret->room_unused = g_heap_new(idle_handle_compare);
+
+ ret->contact_serial = 1;
+ ret->room_serial = 1;
+
+ return ret;
+}
+
+void idle_handle_storage_destroy(IdleHandleStorage *storage)
+{
+ g_assert(storage != NULL);
+ g_assert(storage->contact_handles != NULL);
+ g_assert(storage->room_handles != NULL);
+
+ g_hash_table_destroy(storage->contact_handles);
+ g_hash_table_destroy(storage->room_handles);
+
+ g_hash_table_destroy(storage->contact_strings);
+ g_hash_table_destroy(storage->room_strings);
+
+ g_heap_destroy(storage->contact_unused);
+ g_heap_destroy(storage->room_unused);
+
+ g_slice_free(IdleHandleStorage, storage);
+}
+
+gboolean idle_handle_is_valid(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ return (handle_priv_lookup(storage, type, handle) != NULL);
+}
+
+gboolean idle_handle_ref(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ priv = handle_priv_lookup(storage, type, handle);
+
+ if (priv == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ priv->refcount++;
+ return TRUE;
+ }
+}
+
+gboolean idle_handle_unref(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ priv = handle_priv_lookup(storage, type, handle);
+
+ if (priv == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ g_assert(priv->refcount > 0);
+
+ priv->refcount--;
+
+ if (priv->refcount == 0)
+ {
+ handle_priv_remove(storage, type, priv, handle);
+ }
+
+ return TRUE;
+ }
+}
+
+const char *idle_handle_inspect(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ priv = handle_priv_lookup(storage, type, handle);
+
+ if (priv == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return priv->string;
+ }
+}
+
+IdleHandle idle_handle_for_contact(IdleHandleStorage *storage, const char *nickname)
+{
+ IdleHandle handle;
+
+ g_assert(storage != NULL);
+
+ if ((!nickname) || (nickname[0] == '\0'))
+ {
+ g_debug("%s: handle for invalid nickname requested", G_STRFUNC);
+ return 0;
+ }
+
+ if (!idle_nickname_is_valid(nickname))
+ {
+ g_debug("%s: nickname (%s) isn't valid!", G_STRFUNC, nickname);
+ return 0;
+ }
+
+ handle = GPOINTER_TO_INT(g_hash_table_lookup(storage->contact_strings, nickname));
+
+ if (handle == 0)
+ {
+ handle = idle_handle_alloc(storage, TP_HANDLE_TYPE_CONTACT);
+ }
+
+ if (handle_priv_lookup(storage, TP_HANDLE_TYPE_CONTACT, handle) == NULL)
+ {
+ IdleHandlePriv *priv;
+ priv = idle_handle_priv_new();
+ priv->string = g_strdup(nickname);
+
+ g_hash_table_insert(storage->contact_handles, GINT_TO_POINTER(handle), priv);
+ g_hash_table_insert(storage->contact_strings, priv->string, GINT_TO_POINTER(handle));
+ }
+
+ return handle;
+}
+
+gboolean idle_handle_for_room_exists(IdleHandleStorage *storage, const char *channel_up)
+{
+ IdleHandle handle;
+ gchar *channel;
+
+ g_assert(storage != NULL);
+
+ channel = g_ascii_strdown(channel_up, -1);
+
+ if ((channel == NULL) || (channel[0] == '\0'))
+ {
+ return FALSE;
+ }
+ else
+ {
+ handle = GPOINTER_TO_INT(g_hash_table_lookup(storage->room_strings, channel));
+ }
+
+ g_free(channel);
+
+ return handle_priv_lookup(storage, TP_HANDLE_TYPE_ROOM, handle) != NULL;
+}
+
+IdleHandle idle_handle_for_room(IdleHandleStorage *storage, const char *channel)
+{
+ IdleHandle handle;
+
+ g_assert(storage != NULL);
+
+ if ((channel == NULL) || (channel[0] == '\0'))
+ {
+ g_debug("%s: handle for a invalid channel requested", G_STRFUNC);
+ return 0;
+ }
+
+ if (!idle_channelname_is_valid(channel))
+ {
+ g_debug("%s: channel name (%s) not valid!", G_STRFUNC, channel);
+ return 0;
+ }
+
+ handle = GPOINTER_TO_INT(g_hash_table_lookup(storage->room_strings, channel));
+
+ if (handle == 0)
+ {
+ handle = idle_handle_alloc(storage, TP_HANDLE_TYPE_ROOM);
+ }
+
+ if (handle_priv_lookup(storage, TP_HANDLE_TYPE_ROOM, handle) == NULL)
+ {
+ IdleHandlePriv *priv;
+ priv = idle_handle_priv_new();
+ priv->string = g_strdup(channel);
+
+ g_hash_table_insert(storage->room_handles, GINT_TO_POINTER(handle), priv);
+ g_hash_table_insert(storage->room_strings, priv->string, GINT_TO_POINTER(handle));
+ }
+
+ return handle;
+}
+
+gboolean idle_handle_set_presence(IdleHandleStorage *storage, IdleHandle handle, IdleContactPresence *cp)
+{
+ IdleHandlePriv *priv;
+
+ g_assert(storage != NULL);
+ g_assert(handle != 0);
+
+ priv = handle_priv_lookup(storage, TP_HANDLE_TYPE_CONTACT, handle);
+
+ if (priv == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ priv->cp = cp;
+ }
+
+ return TRUE;
+}
+
+IdleContactPresence *idle_handle_get_presence(IdleHandleStorage *storage, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ g_assert(storage != NULL);
+ g_assert(handle != 0);
+
+ priv = handle_priv_lookup(storage, TP_HANDLE_TYPE_CONTACT, handle);
+
+ if (priv == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return priv->cp;
+ }
+}
+