diff -r d0f3a028347a -r 59927b2d3b75 telepathygabble/src/roster.c --- a/telepathygabble/src/roster.c Tue Feb 02 01:10:06 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1653 +0,0 @@ -/* - * roster.c - Source for Gabble roster helper - * - * Copyright (C) 2006 Collabora Ltd. - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#include -#include - -#include "telepathy-interfaces.h" -#include "tp-channel-factory-iface.h" - -#include "debug.h" -#include "gabble-connection.h" -#include "gabble-roster-channel.h" -#include "namespaces.h" -#include "roster.h" -#include "util.h" - -#include "gabble_enums.h" - -#define DBUS_API_SUBJECT_TO_CHANGE -#define DEBUG_FLAG GABBLE_DEBUG_ROSTER -#define GOOGLE_ROSTER_VERSION "2" - -#ifdef DEBUG_FLAG -//#define DEBUG(format, ...) -#define DEBUGGING 0 -#define NODE_DEBUG(n, s) -#endif /* DEBUG_FLAG */ - -/* Properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -/* signal enum */ -enum -{ - NICKNAME_UPDATE, - LAST_SIGNAL -#ifdef EMULATOR - = LAST_SIGNAL_ROSTER -#endif - -}; - - -#ifdef EMULATOR -#include "libgabble_wsd_solution.h" - - GET_STATIC_ARRAY_FROM_TLS(signals,gabble_roster,guint) - #define signals (GET_WSD_VAR_NAME(signals,gabble_roster, s)()) - -#else - - static guint signals[LAST_SIGNAL] = {0}; - -#endif - - -typedef struct _GabbleRosterPrivate GabbleRosterPrivate; -struct _GabbleRosterPrivate -{ - GabbleConnection *conn; - - LmMessageHandler *iq_cb; - LmMessageHandler *presence_cb; - - GHashTable *channels; - GHashTable *items; - - gboolean roster_received; - gboolean dispose_has_run; -}; - -typedef enum -{ - GOOGLE_ITEM_TYPE_NORMAL = 0, - GOOGLE_ITEM_TYPE_BLOCKED, - GOOGLE_ITEM_TYPE_HIDDEN, - GOOGLE_ITEM_TYPE_PINNED -} GoogleItemType; - -typedef struct _GabbleRosterItem GabbleRosterItem; -struct _GabbleRosterItem -{ - GabbleRosterSubscription subscription; - gboolean ask_subscribe; - GoogleItemType google_type; - gchar *name; - gchar **groups; -}; - -static void gabble_roster_factory_iface_init (); -static void gabble_roster_init (GabbleRoster *roster); -static GObject * gabble_roster_constructor (GType type, guint n_props, GObjectConstructParam *props); -static void gabble_roster_dispose (GObject *object); -static void gabble_roster_finalize (GObject *object); -static void gabble_roster_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec); -static void gabble_roster_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec); - -static void _gabble_roster_item_free (GabbleRosterItem *item); - -#ifndef EMULATOR -G_DEFINE_TYPE_WITH_CODE (GabbleRoster, gabble_roster, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, gabble_roster_factory_iface_init)); -#else - GET_STATIC_VAR_FROM_TLS(gabble_roster_parent_class,gabble_roster,gpointer) - #define gabble_roster_parent_class (*GET_WSD_VAR_NAME(gabble_roster_parent_class,gabble_roster,s)()) - - GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_roster,GType) - #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_roster,s)()) - - - static void gabble_roster_init (GabbleRoster *self); - static void gabble_roster_class_init (GabbleRosterClass *klass); - static void gabble_roster_class_intern_init (gpointer klass) { gabble_roster_parent_class = g_type_class_peek_parent (klass); gabble_roster_class_init ((GabbleRosterClass*) klass); } EXPORT_C GType gabble_roster_get_type (void) { if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleRosterClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_roster_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleRoster), 0, (GInstanceInitFunc) gabble_roster_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleRoster"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) gabble_roster_factory_iface_init }; g_type_add_interface_static (g_define_type_id, tp_channel_factory_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id; } ; - -#endif - -#define GABBLE_ROSTER_GET_PRIVATE(o) ((GabbleRosterPrivate*) ((o)->priv)); - -static void -gabble_roster_class_init (GabbleRosterClass *gabble_roster_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_roster_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_roster_class, sizeof (GabbleRosterPrivate)); - - object_class->constructor = gabble_roster_constructor; - - object_class->dispose = gabble_roster_dispose; - object_class->finalize = gabble_roster_finalize; - - object_class->get_property = gabble_roster_get_property; - object_class->set_property = gabble_roster_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "XMPP roster object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, - PROP_CONNECTION, - param_spec); - - signals[NICKNAME_UPDATE] = g_signal_new ( - "nickname-update", - G_TYPE_FROM_CLASS (gabble_roster_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); -} - -static void -gabble_roster_init (GabbleRoster *obj) -{ - GabbleRosterPrivate *priv = - G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_ROSTER, GabbleRosterPrivate); - - obj->priv = priv; - - priv->channels = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - g_object_unref); - - priv->items = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) _gabble_roster_item_free); -} - -static GObject * -gabble_roster_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - /* GabbleRosterPrivate *priv; */ - - obj = G_OBJECT_CLASS (gabble_roster_parent_class)-> - constructor (type, n_props, props); - /* priv = GABBLE_ROSTER_GET_PRIVATE (GABBLE_ROSTER (obj)); */ - - return obj; -} - -void -gabble_roster_dispose (GObject *object) -{ - GabbleRoster *self = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - gabble_debug (DEBUG_FLAG, "dispose called"); - - priv->dispose_has_run = TRUE; - - g_assert (priv->iq_cb == NULL); - g_assert (priv->presence_cb == NULL); - - tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); - g_assert (priv->channels == NULL); - - if (G_OBJECT_CLASS (gabble_roster_parent_class)->dispose) - G_OBJECT_CLASS (gabble_roster_parent_class)->dispose (object); -} - -static void -item_handle_unref_foreach (gpointer key, gpointer data, gpointer user_data) -{ - GabbleHandle handle = (GabbleHandle) key; - GabbleRosterPrivate *priv = (GabbleRosterPrivate *) user_data; - - gabble_handle_unref (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, handle); -} - -void -gabble_roster_finalize (GObject *object) -{ - GabbleRoster *self = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (self); - - gabble_debug (DEBUG_FLAG, "called with %p", object); - - g_hash_table_foreach (priv->items, item_handle_unref_foreach, priv); - g_hash_table_destroy (priv->items); - - G_OBJECT_CLASS (gabble_roster_parent_class)->finalize (object); -} - -static void -gabble_roster_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleRoster *roster = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_roster_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleRoster *roster = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -_gabble_roster_item_free (GabbleRosterItem *item) -{ - g_assert (item != NULL); - - g_strfreev (item->groups); - g_free (item->name); - g_free (item); -} - -static const gchar * -_subscription_to_string (GabbleRosterSubscription subscription) -{ - switch (subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - return "none"; - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - return "from"; - case GABBLE_ROSTER_SUBSCRIPTION_TO: - return "to"; - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - return "both"; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - return "remove"; - default: - g_assert_not_reached (); - return NULL; - } -} - -static GabbleRosterSubscription -_parse_item_subscription (LmMessageNode *item_node) -{ - const gchar *subscription; - - g_assert (item_node != NULL); - - subscription = lm_message_node_get_attribute (item_node, "subscription"); - - if (NULL == subscription || 0 == strcmp (subscription, "none")) - return GABBLE_ROSTER_SUBSCRIPTION_NONE; - else if (0 == strcmp (subscription, "from")) - return GABBLE_ROSTER_SUBSCRIPTION_FROM; - else if (0 == strcmp (subscription, "to")) - return GABBLE_ROSTER_SUBSCRIPTION_TO; - else if (0 == strcmp (subscription, "both")) - return GABBLE_ROSTER_SUBSCRIPTION_BOTH; - else if (0 == strcmp (subscription, "remove")) - return GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - else - { - NODE_DEBUG (item_node, "got unexpected subscription value"); - return GABBLE_ROSTER_SUBSCRIPTION_NONE; - } -} - -static gchar ** -_parse_item_groups (LmMessageNode *item_node) -{ - LmMessageNode *group_node; - GPtrArray *strv; - - strv = g_ptr_array_new (); - - for (group_node = item_node->children; - NULL != group_node; - group_node = group_node->next) - { - if (0 != strcmp (group_node->name, "group")) - continue; - - if (NULL == group_node->value) - continue; - - g_ptr_array_add (strv, g_strdup (group_node->value)); - } - - g_ptr_array_add (strv, NULL); - - return (gchar **) g_ptr_array_free (strv, FALSE); -} - -static const gchar * -_google_item_type_to_string (GoogleItemType google_type) -{ - switch (google_type) - { - case GOOGLE_ITEM_TYPE_NORMAL: - return NULL; - case GOOGLE_ITEM_TYPE_BLOCKED: - return "B"; - case GOOGLE_ITEM_TYPE_HIDDEN: - return "H"; - case GOOGLE_ITEM_TYPE_PINNED: - return "P"; - } - - g_assert_not_reached (); - - return NULL; -} - -static GoogleItemType -_parse_google_item_type (LmMessageNode *item_node) -{ - const gchar *google_type; - - g_assert (item_node != NULL); - - google_type = lm_message_node_get_attribute (item_node, "gr:t"); - - if (NULL == google_type) - return GOOGLE_ITEM_TYPE_NORMAL; - else if (!g_strdiff (google_type, "B")) - return GOOGLE_ITEM_TYPE_BLOCKED; - else if (!g_strdiff (google_type, "H")) - return GOOGLE_ITEM_TYPE_HIDDEN; - else if (!g_strdiff (google_type, "P")) - return GOOGLE_ITEM_TYPE_PINNED; - - NODE_DEBUG (item_node, "got unexpected google contact type value"); - - return GOOGLE_ITEM_TYPE_NORMAL; -} - -static gboolean -_google_roster_item_should_keep (LmMessageNode *item_node, - GabbleRosterItem *item) -{ - const gchar *attr; - - /* skip automatically subscribed Google roster items */ - attr = lm_message_node_get_attribute (item_node, "gr:autosub"); - - if (!g_strdiff (attr, "true")) - return FALSE; - - /* skip email addresses that replied to an invite */ - attr = lm_message_node_get_attribute (item_node, "gr:alias-for"); - - if (attr != NULL) - return FALSE; - - /* allow items that we've requested a subscription from */ - if (item->ask_subscribe) - return TRUE; - - if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE) - return TRUE; - - /* discard anything else */ - return FALSE; -} - -static GabbleRosterItem * -_gabble_roster_item_get (GabbleRoster *roster, - GabbleHandle handle) -{ - GabbleRosterItem *item; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL)); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - if (NULL == item) - { - item = g_new0 (GabbleRosterItem, 1); - gabble_handle_ref (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, handle); - g_hash_table_insert (priv->items, GINT_TO_POINTER (handle), item); - } - - return item; -} - -static void -_gabble_roster_item_remove (GabbleRoster *roster, - GabbleHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL)); - - g_hash_table_remove (priv->items, GINT_TO_POINTER (handle)); - gabble_handle_unref (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, handle); -} - -static GabbleRosterItem * -_gabble_roster_item_update (GabbleRoster *roster, - GabbleHandle handle, - LmMessageNode *node, - gboolean google_roster_mode) -{ - GabbleRosterItem *item; - const gchar *ask, *name; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL)); - g_assert (node != NULL); - - item = _gabble_roster_item_get (roster, handle); - - item->subscription = _parse_item_subscription (node); - - ask = lm_message_node_get_attribute (node, "ask"); - if (NULL != ask && 0 == strcmp (ask, "subscribe")) - item->ask_subscribe = TRUE; - else - item->ask_subscribe = FALSE; - - if (google_roster_mode) - { - item->google_type = _parse_google_item_type (node); - - /* discard roster item if strange, just hide it if it's hidden */ - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - { - gabble_debug (DEBUG_FLAG, "Google roster: caching hidden contact %d (%s)", handle, - lm_message_node_get_attribute (node, "jid")); - item->subscription = GABBLE_ROSTER_SUBSCRIPTION_NONE; - } - else if (!_google_roster_item_should_keep (node, item)) - { - gabble_debug (DEBUG_FLAG, "Google roster: discarding odd contact %d (%s)", handle, - lm_message_node_get_attribute (node, "jid")); - item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - } - } - - if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) - name = NULL; - else - name = lm_message_node_get_attribute (node, "name"); - - if (g_strdiff (item->name, name)) - { - g_free (item->name); - item->name = g_strdup (name); - - gabble_debug (DEBUG_FLAG, "name for handle %d changed to %s", handle, name); - g_signal_emit (G_OBJECT (roster), signals[NICKNAME_UPDATE], 0, handle); - } - - g_strfreev (item->groups); - item->groups = _parse_item_groups (node); - - return item; -} - - -#ifdef ENABLE_DEBUG -static gchar * -_gabble_roster_item_dump (GabbleRosterItem *item) -{ - GString *str; - - g_assert (item != NULL); - - str = g_string_new ("subscription: "); - - g_string_append (str, _subscription_to_string (item->subscription)); - - if (item->ask_subscribe) - g_string_append (str, ", ask: subscribe"); - - if (item->google_type != GOOGLE_ITEM_TYPE_NORMAL) - g_string_append_printf (str, ", google_type: %s", - _google_item_type_to_string (item->google_type)); - - if (item->name) - g_string_append_printf (str, ", name: %s", item->name); - - if (item->groups) - { - gchar **tmp; - g_string_append (str, ", groups: { "); - for (tmp = item->groups; *tmp; tmp++) - { - g_string_append (str, *tmp); - g_string_append_c (str, ' '); - } - g_string_append (str, "}"); - } - - return g_string_free (str, FALSE); -} -#endif /* ENABLE_DEBUG */ - - -static LmMessage * -_gabble_roster_message_new (GabbleRoster *roster, - LmMessageSubType sub_type, - LmMessageNode **query_return) -{ - LmMessage *message; - LmMessageNode *query_node; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - - message = lm_message_new_with_sub_type (NULL, - LM_MESSAGE_TYPE_IQ, - sub_type); - - query_node = lm_message_node_add_child (message->node, "query", NULL); - - if (NULL != query_return) - *query_return = query_node; - - lm_message_node_set_attribute (query_node, "xmlns", NS_ROSTER); - - if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) - { - lm_message_node_set_attributes (query_node, - "xmlns:gr", NS_GOOGLE_ROSTER, - "gr:ext", GOOGLE_ROSTER_VERSION, - "gr:include", "all", - NULL); - } - - return message; -} - - -static LmMessage * -_gabble_roster_item_to_message (GabbleRoster *roster, - GabbleHandle handle, - LmMessageNode **item_return) -{ - const gchar *jid; - - LmMessage *message; - LmMessageNode *query_node, *item_node; - GabbleRosterItem *item; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL)); - - item = _gabble_roster_item_get (roster, handle); - - message = _gabble_roster_message_new (roster, LM_MESSAGE_SUB_TYPE_SET, - &query_node); - - item_node = lm_message_node_add_child (query_node, "item", NULL); - - if (NULL != item_return) - *item_return = item_node; - - jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, - handle); - lm_message_node_set_attribute (item_node, "jid", jid); - - if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE) - { - const gchar *subscription = _subscription_to_string (item->subscription); - lm_message_node_set_attribute (item_node, "subscription", subscription); - } - - if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) - goto DONE; - - if ((priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) && - item->google_type != GOOGLE_ITEM_TYPE_NORMAL) - lm_message_node_set_attribute (item_node, "gr:t", - _google_item_type_to_string (item->google_type)); - - if (item->ask_subscribe) - lm_message_node_set_attribute (item_node, "ask", "subscribe"); - - if (item->name) - lm_message_node_set_attribute (item_node, "name", item->name); - - if (item->groups) - { - gchar **tmp; - - for (tmp = item->groups; *tmp; tmp++) - { - lm_message_node_add_child (item_node, "group", *tmp); - } - } - -DONE: - return message; -} - -static GabbleRosterChannel * -_gabble_roster_create_channel (GabbleRoster *roster, - GabbleHandle handle) -{ - const char *name; - char *object_path; - - GabbleRosterChannel *chan; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (priv->channels != NULL); - g_assert (g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)) == NULL); - - name = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_LIST, handle); - object_path = g_strdup_printf ("%s/RosterChannel/%s", priv->conn->object_path, name); - chan = g_object_new (GABBLE_TYPE_ROSTER_CHANNEL, - "connection", priv->conn, - "object-path", object_path, - "handle", handle, - NULL); - - gabble_debug (DEBUG_FLAG, "created %s", object_path); - g_free (object_path); - - g_hash_table_insert (priv->channels, GINT_TO_POINTER (handle), chan); - - if (priv->roster_received) - { - gabble_debug (DEBUG_FLAG, "roster already received, emitting signal for %s list channel", - name); - - g_signal_emit_by_name (roster, "new-channel", chan); - } - else - { - gabble_debug (DEBUG_FLAG, "roster not yet received, not emitting signal for %s list channel", - name); - } - - return chan; -} - -static GabbleRosterChannel * -_gabble_roster_get_channel (GabbleRoster *roster, - GabbleHandle handle) -{ - GabbleRosterChannel *chan; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (priv->channels != NULL); - g_assert (gabble_handle_is_valid (priv->conn->handles, TP_HANDLE_TYPE_LIST, handle, NULL)); - - chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)); - - if (chan == NULL) - chan = _gabble_roster_create_channel (roster, handle); - - return chan; -} - -static void -_gabble_roster_emit_one (gpointer key, - gpointer value, - gpointer data) -{ - GabbleRoster *roster = GABBLE_ROSTER (data); - GabbleRosterChannel *chan = GABBLE_ROSTER_CHANNEL (value); -#ifdef ENABLE_DEBUG - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - GabbleHandle handle = GPOINTER_TO_INT (key); - const gchar *name = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_LIST, handle); - - gabble_debug (DEBUG_FLAG, "roster now received, emitting signal signal for %s list channel", - name); -#endif - - g_signal_emit_by_name (roster, "new-channel", chan); -} - -static void -_gabble_roster_received (GabbleRoster *roster) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (priv->channels != NULL); - - if (!priv->roster_received) - { - priv->roster_received = TRUE; - - g_hash_table_foreach (priv->channels, _gabble_roster_emit_one, roster); - } -} - -/** - * gabble_roster_iq_cb - * - * Called by loudmouth when we get an incoming . This handler - * is concerned only with roster queries, and allows other handlers - * if queries other than rosters are received. - */ -static LmHandlerResult -gabble_roster_iq_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - const gchar *from; - gboolean google_roster = FALSE; - LmMessageNode *iq_node, *query_node; - LmMessageSubType sub_type; - - GabbleRoster *roster = GABBLE_ROSTER (user_data); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (lmconn == priv->conn->lmconn); - - if (priv->channels == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - iq_node = lm_message_get_node (message); - query_node = lm_message_node_get_child_with_namespace (iq_node, "query", - NS_ROSTER); - - if (query_node == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - from = lm_message_node_get_attribute (message->node, "from"); - - if (from != NULL) - { - GabbleHandle sender; - - sender = gabble_handle_for_contact (priv->conn->handles, - from, FALSE); - - if (sender != priv->conn->self_handle) - { - NODE_DEBUG (iq_node, "discarding roster IQ which is not from " - "ourselves or the server"); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - } - - if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) - { - const char *gr_ext; - - gr_ext = lm_message_node_get_attribute (query_node, "gr:ext"); - - if (!g_strdiff (gr_ext, GOOGLE_ROSTER_VERSION)) - google_roster = TRUE; - } - - sub_type = lm_message_get_sub_type (message); - - /* if this is a result, it's from our initial query. if it's a set, - * it's a roster push. either way, parse the items. */ - switch (sub_type) - { - LmMessageNode *item_node; - GIntSet *pub_add, *pub_rem, - *sub_add, *sub_rem, *sub_rp, - *known_add, *known_rem, - *deny_add, *deny_rem; - GArray *removed; - GabbleHandle handle; - GabbleRosterChannel *chan; - guint i; - - case LM_MESSAGE_SUB_TYPE_RESULT: - case LM_MESSAGE_SUB_TYPE_SET: - /* asymmetry is because we don't get locally pending subscription - * requests via , we get it via */ - pub_add = g_intset_new (); - pub_rem = g_intset_new (); - sub_add = g_intset_new (); - sub_rem = g_intset_new (); - sub_rp = g_intset_new (); - known_add = g_intset_new (); - known_rem = g_intset_new (); - removed = g_array_new (FALSE, FALSE, sizeof (GabbleHandle)); - - if (google_roster) - { - deny_add = g_intset_new (); - deny_rem = g_intset_new (); - } - else - { - deny_add = NULL; - deny_rem = NULL; - } - - /* get the publish channel first because we need it when processing */ - handle = GABBLE_LIST_HANDLE_PUBLISH; - chan = _gabble_roster_get_channel (roster, handle); - - /* iterate every sub-node, which we expect to be s */ - for (item_node = query_node->children; - item_node; - item_node = item_node->next) - { - const char *jid; - GabbleRosterItem *item; - - if (strcmp (item_node->name, "item")) - { - NODE_DEBUG (item_node, "query sub-node is not item, skipping"); - continue; - } - - jid = lm_message_node_get_attribute (item_node, "jid"); - if (!jid) - { - NODE_DEBUG (item_node, "item node has no jid, skipping"); - continue; - } - - handle = gabble_handle_for_contact (priv->conn->handles, jid, FALSE); - if (handle == 0) - { - NODE_DEBUG (item_node, "item jid is malformed, skipping"); - continue; - } - - item = _gabble_roster_item_update (roster, handle, item_node, - google_roster); - -#ifdef ENABLE_DEBUG - if (DEBUGGING) - { - gchar *dump = _gabble_roster_item_dump (item); - gabble_debug (DEBUG_FLAG, "jid: %s, %s", jid, dump); - g_free (dump); - } -#endif - - /* handle publish list changes */ - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - g_intset_add (pub_add, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - /* publish channel is a bit odd, the roster item doesn't tell us - * if someone is awaiting our approval - we get this via presence - * type=subscribe, so we have to not remove them if they're - * already local_pending in our publish channel */ - if (!handle_set_is_member (chan->group.local_pending, handle)) - { - g_intset_add (pub_rem, handle); - } - break; - default: - g_assert_not_reached (); - } - - /* handle subscribe list changes */ - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - g_intset_add (sub_add, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - if (item->ask_subscribe) - g_intset_add (sub_rp, handle); - else - g_intset_add (sub_rem, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - g_intset_add (sub_rem, handle); - break; - default: - g_assert_not_reached (); - } - - /* handle known list changes */ - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - g_intset_add (known_rem, handle); - else - g_intset_add (known_add, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - g_intset_add (known_rem, handle); - break; - default: - g_assert_not_reached (); - } - - /* handle deny list changes */ - if (google_roster) - { - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - if (item->google_type == GOOGLE_ITEM_TYPE_BLOCKED) - g_intset_add (deny_add, handle); - else - g_intset_add (deny_rem, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - g_intset_add (deny_rem, handle); - break; - default: - g_assert_not_reached (); - } - } - - /* delay removing items from roster until signals have been emitted; - * otherwise handles go out of scope! */ - if (GABBLE_ROSTER_SUBSCRIPTION_REMOVE == item->subscription) - g_array_append_val (removed, handle); - } - - /* chan was initialised to the publish channel before the for loop */ - - gabble_debug (DEBUG_FLAG, "calling change members on publish channel"); - gabble_group_mixin_change_members (G_OBJECT (chan), - "", pub_add, pub_rem, NULL, NULL, 0, 0); - - handle = GABBLE_LIST_HANDLE_SUBSCRIBE; - chan = _gabble_roster_get_channel (roster, handle); - - gabble_debug (DEBUG_FLAG, "calling change members on subscribe channel"); - gabble_group_mixin_change_members (G_OBJECT (chan), - "", sub_add, sub_rem, NULL, sub_rp, 0, 0); - - handle = GABBLE_LIST_HANDLE_KNOWN; - chan = _gabble_roster_get_channel (roster, handle); - - gabble_debug (DEBUG_FLAG, "calling change members on known channel"); - gabble_group_mixin_change_members (G_OBJECT (chan), - "", known_add, known_rem, NULL, NULL, 0, 0); - - if (google_roster) - { - handle = GABBLE_LIST_HANDLE_DENY; - chan = _gabble_roster_get_channel (roster, handle); - - gabble_debug (DEBUG_FLAG, "calling change members on deny channel"); - gabble_group_mixin_change_members (G_OBJECT (chan), - "", deny_add, deny_rem, NULL, NULL, 0, 0); - - g_intset_destroy (deny_add); - g_intset_destroy (deny_rem); - } - - for (i = 0; i < removed->len; i++) - _gabble_roster_item_remove (roster, - g_array_index (removed, GabbleHandle, i)); - - g_intset_destroy (pub_add); - g_intset_destroy (pub_rem); - g_intset_destroy (sub_add); - g_intset_destroy (sub_rem); - g_intset_destroy (sub_rp); - g_intset_destroy (known_add); - g_intset_destroy (known_rem); - g_array_free (removed, TRUE); - break; - default: - NODE_DEBUG (iq_node, "unhandled roster IQ"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - switch (sub_type) - { - case LM_MESSAGE_SUB_TYPE_RESULT: - /* result means it's a roster push, so the roster is now complete and we - * can emit signals */ - _gabble_roster_received (roster); - break; - case LM_MESSAGE_SUB_TYPE_SET: - /* acknowledge roster */ - _gabble_connection_acknowledge_set_iq (priv->conn, message); - break; - default: - break; - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - - -static void -_gabble_roster_send_presence_ack (GabbleRoster *roster, - const gchar *from, - LmMessageSubType sub_type, - gboolean changed) -{ - LmMessage *reply; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - if (!changed) - { - gabble_debug (DEBUG_FLAG, "not sending ack to avoid loop with buggy server"); - return; - } - - switch (sub_type) - { - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE: - sub_type = LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED; - break; - case LM_MESSAGE_SUB_TYPE_SUBSCRIBED: - sub_type = LM_MESSAGE_SUB_TYPE_SUBSCRIBE; - break; - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED: - sub_type = LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE; - break; - default: - g_assert_not_reached(); - return; - } - - reply = lm_message_new_with_sub_type (from, - LM_MESSAGE_TYPE_PRESENCE, - sub_type); - - _gabble_connection_send (priv->conn, reply, NULL); - - lm_message_unref (reply); -} - - -/** - * connection_presence_roster_cb: - * @handler: #LmMessageHandler for this message - * @connection: #LmConnection that originated the message - * @message: the presence message - * @user_data: callback data - * - * Called by loudmouth when we get an incoming . - */ -static LmHandlerResult -gabble_roster_presence_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - LmMessageNode *pres_node, *child_node; - const char *from; - LmMessageSubType sub_type; - GIntSet *tmp; - GabbleHandle handle; - const gchar *status_message = NULL; - GabbleRosterChannel *chan = NULL; - gboolean changed; - GabbleRoster *roster = GABBLE_ROSTER (user_data); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (lmconn == priv->conn->lmconn); - - if (priv->channels == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - pres_node = lm_message_get_node (message); - - from = lm_message_node_get_attribute (pres_node, "from"); - - if (from == NULL) - { - NODE_DEBUG (pres_node, "presence stanza without from attribute, ignoring"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - sub_type = lm_message_get_sub_type (message); - - handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE); - - if (handle == 0) - { - NODE_DEBUG (pres_node, "ignoring presence from malformed jid"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - if (handle == priv->conn->self_handle) - { - NODE_DEBUG (pres_node, "ignoring presence from ourselves on another resource"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - g_assert (handle != 0); - - child_node = lm_message_node_get_child (pres_node, "status"); - if (child_node) - status_message = lm_message_node_get_value (child_node); - - switch (sub_type) - { - case LM_MESSAGE_SUB_TYPE_SUBSCRIBE: - gabble_debug (DEBUG_FLAG, "making %s (handle %u) local pending on the publish channel", - from, handle); - - tmp = g_intset_new (); - g_intset_add (tmp, handle); - - handle = GABBLE_LIST_HANDLE_PUBLISH; - chan = _gabble_roster_get_channel (roster, handle); - gabble_group_mixin_change_members (G_OBJECT (chan), status_message, - NULL, NULL, tmp, NULL, 0, 0); - - g_intset_destroy (tmp); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE: - gabble_debug (DEBUG_FLAG, "removing %s (handle %u) from the publish channel", - from, handle); - - tmp = g_intset_new (); - g_intset_add (tmp, handle); - - handle = GABBLE_LIST_HANDLE_PUBLISH; - chan = _gabble_roster_get_channel (roster, handle); - changed = gabble_group_mixin_change_members (G_OBJECT (chan), - status_message, NULL, tmp, NULL, NULL, 0, 0); - - _gabble_roster_send_presence_ack (roster, from, sub_type, changed); - - g_intset_destroy (tmp); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - case LM_MESSAGE_SUB_TYPE_SUBSCRIBED: - gabble_debug (DEBUG_FLAG, "adding %s (handle %u) to the subscribe channel", - from, handle); - - tmp = g_intset_new (); - g_intset_add (tmp, handle); - - handle = GABBLE_LIST_HANDLE_SUBSCRIBE; - chan = _gabble_roster_get_channel (roster, handle); - changed = gabble_group_mixin_change_members (G_OBJECT (chan), - status_message, tmp, NULL, NULL, NULL, 0, 0); - - _gabble_roster_send_presence_ack (roster, from, sub_type, changed); - - g_intset_destroy (tmp); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED: - gabble_debug (DEBUG_FLAG, "removing %s (handle %u) from the subscribe channel", - from, handle); - - tmp = g_intset_new (); - g_intset_add (tmp, handle); - - handle = GABBLE_LIST_HANDLE_SUBSCRIBE; - chan = _gabble_roster_get_channel (roster, handle); - changed = gabble_group_mixin_change_members (G_OBJECT (chan), - status_message, NULL, tmp, NULL, NULL, 0, 0); - - _gabble_roster_send_presence_ack (roster, from, sub_type, changed); - - g_intset_destroy (tmp); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - default: - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } -} - -static void -gabble_roster_factory_iface_close_all (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - gabble_debug (DEBUG_FLAG, "closing channels"); - - if (priv->channels) - { - g_hash_table_destroy (priv->channels); - priv->channels = NULL; - } -} - -static void -gabble_roster_factory_iface_connecting (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - gabble_debug (DEBUG_FLAG, "adding callbacks"); - - g_assert (priv->iq_cb == NULL); - g_assert (priv->presence_cb == NULL); - - priv->iq_cb = lm_message_handler_new (gabble_roster_iq_cb, - roster, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->iq_cb, - LM_MESSAGE_TYPE_IQ, - LM_HANDLER_PRIORITY_NORMAL); - - priv->presence_cb = lm_message_handler_new (gabble_roster_presence_cb, - roster, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->presence_cb, - LM_MESSAGE_TYPE_PRESENCE, - LM_HANDLER_PRIORITY_LAST); -} - -static void -gabble_roster_factory_iface_connected (TpChannelFactoryIface *iface) -{ - LmMessage *message; - - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - gabble_debug (DEBUG_FLAG, "requesting roster"); - - message = _gabble_roster_message_new (roster, LM_MESSAGE_SUB_TYPE_GET, NULL); - - _gabble_connection_send (priv->conn, message, NULL); - - lm_message_unref (message); -} - -static void -gabble_roster_factory_iface_disconnected (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - gabble_debug (DEBUG_FLAG, "removing callbacks"); - - g_assert (priv->iq_cb != NULL); - g_assert (priv->presence_cb != NULL); - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->iq_cb, - LM_MESSAGE_TYPE_IQ); - lm_message_handler_unref (priv->iq_cb); - priv->iq_cb = NULL; - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->presence_cb, - LM_MESSAGE_TYPE_PRESENCE); - lm_message_handler_unref (priv->presence_cb); - priv->presence_cb = NULL; -} - -struct foreach_data { - TpChannelFunc func; - gpointer data; -}; - -static void -_gabble_roster_factory_iface_foreach_one (gpointer key, - gpointer value, - gpointer data) -{ - TpChannelIface *chan = TP_CHANNEL_IFACE (value); - struct foreach_data *foreach = (struct foreach_data *) data; - - foreach->func (chan, foreach->data); -} - -static void -gabble_roster_factory_iface_foreach (TpChannelFactoryIface *iface, - TpChannelFunc func, - gpointer data) -{ - struct foreach_data foreach; - - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - foreach.func = func; - foreach.data = data; - - g_hash_table_foreach (priv->channels, - _gabble_roster_factory_iface_foreach_one, &foreach); -} - -static TpChannelFactoryRequestStatus -gabble_roster_factory_iface_request (TpChannelFactoryIface *iface, - const gchar *chan_type, - TpHandleType handle_type, - guint handle, - TpChannelIface **ret, - GError **error) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; - - if (handle_type != TP_HANDLE_TYPE_LIST) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - - if (!gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_LIST, - handle, - NULL)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE; - - /* disallow "deny" channels if we don't have google:roster support */ - if (handle == GABBLE_LIST_HANDLE_DENY && - !(priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - - if (priv->roster_received) - { - GabbleRosterChannel *chan; - chan = _gabble_roster_get_channel (roster, handle); - *ret = TP_CHANNEL_IFACE (chan); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE; - } - else - { - return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED; - } -} - -static void -gabble_roster_factory_iface_init (gpointer g_iface, - gpointer iface_data) -{ - TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; - - klass->close_all = gabble_roster_factory_iface_close_all; - klass->connecting = gabble_roster_factory_iface_connecting; - klass->connected = gabble_roster_factory_iface_connected; - klass->disconnected = gabble_roster_factory_iface_disconnected; - klass->foreach = gabble_roster_factory_iface_foreach; - klass->request = gabble_roster_factory_iface_request; -} - -GabbleRoster * -gabble_roster_new (GabbleConnection *conn) -{ - g_return_val_if_fail (conn != NULL, NULL); - - return g_object_new (GABBLE_TYPE_ROSTER, - "connection", conn, - NULL); -} - -GabbleRosterSubscription -gabble_roster_handle_get_subscription (GabbleRoster *roster, - GabbleHandle handle) -{ - GabbleRosterItem *item; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, GABBLE_ROSTER_SUBSCRIPTION_NONE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), - GABBLE_ROSTER_SUBSCRIPTION_NONE); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), - GABBLE_ROSTER_SUBSCRIPTION_NONE); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - if (NULL == item) - return GABBLE_ROSTER_SUBSCRIPTION_NONE; - - return item->subscription; -} - -gboolean -gabble_roster_handle_set_blocked (GabbleRoster *roster, - GabbleHandle handle, - gboolean blocked, - GError **error) -{ - GabbleRosterItem *item; - GoogleItemType orig_type; - LmMessage *message; - gboolean ret; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE); - g_return_val_if_fail (priv->conn->features & - GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER, FALSE); - - item = _gabble_roster_item_get (roster, handle); - orig_type = item->google_type; - - if (blocked == (orig_type == GOOGLE_ITEM_TYPE_BLOCKED)) - return TRUE; - - /* temporarily set the desired block state and generate a message */ - if (blocked) - item->google_type = GOOGLE_ITEM_TYPE_BLOCKED; - else - item->google_type = GOOGLE_ITEM_TYPE_NORMAL; - message = _gabble_roster_item_to_message (roster, handle, NULL); - item->google_type = orig_type; - - ret = _gabble_connection_send (priv->conn, message, error); - - lm_message_unref (message); - - return ret; -} - -gboolean -gabble_roster_handle_has_entry (GabbleRoster *roster, - GabbleHandle handle) -{ - GabbleRosterItem *item; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - return (NULL != item); -} - -const gchar * -gabble_roster_handle_get_name (GabbleRoster *roster, - GabbleHandle handle) -{ - GabbleRosterItem *item; - - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, NULL); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), NULL); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), NULL); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - if (NULL == item) - return NULL; - - return item->name; -} - -gboolean -gabble_roster_handle_set_name (GabbleRoster *roster, - GabbleHandle handle, - const gchar *name, - GError **error) -{ - LmMessage *message; - LmMessageNode *item_node; - gboolean ret; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE); - g_return_val_if_fail (name != NULL, FALSE); - - message = _gabble_roster_item_to_message (roster, handle, &item_node); - - lm_message_node_set_attribute (item_node, "name", name); - - ret = _gabble_connection_send (priv->conn, message, error); - - lm_message_unref (message); - - return ret; -} - -gboolean -gabble_roster_handle_remove (GabbleRoster *roster, - GabbleHandle handle, - GError **error) -{ - GabbleRosterItem *item; - GabbleRosterSubscription subscription; - LmMessage *message; - gboolean ret; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE); - - item = _gabble_roster_item_get (roster, handle); - subscription = item->subscription; - item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - - message = _gabble_roster_item_to_message (roster, handle, NULL); - ret = _gabble_connection_send (priv->conn, message, error); - lm_message_unref (message); - - item->subscription = subscription; - - return ret; -} - -gboolean -gabble_roster_handle_add (GabbleRoster *roster, - GabbleHandle handle, - GError **error) -{ - GabbleRosterItem *item; - LmMessage *message; - gboolean do_add = FALSE; - gboolean ret; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles, - TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE); - - if (!gabble_roster_handle_has_entry (roster, handle)) - do_add = TRUE; - - item = _gabble_roster_item_get (roster, handle); - - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - { - item->google_type = GOOGLE_ITEM_TYPE_NORMAL; - do_add = TRUE; - } - - if (!do_add) - return TRUE; - - message = _gabble_roster_item_to_message (roster, handle, NULL); - ret = _gabble_connection_send (priv->conn, message, error); - lm_message_unref (message); - - return ret; -}