diff options
Diffstat (limited to 'libempathy')
-rw-r--r-- | libempathy/Makefile.am | 2 | ||||
-rw-r--r-- | libempathy/empathy-contact-list.c | 1760 | ||||
-rw-r--r-- | libempathy/empathy-contact-list.h | 66 | ||||
-rw-r--r-- | libempathy/empathy-contact-manager.c | 340 | ||||
-rw-r--r-- | libempathy/empathy-contact-manager.h | 15 | ||||
-rw-r--r-- | libempathy/empathy-tp-chat.c | 190 | ||||
-rw-r--r-- | libempathy/empathy-tp-chatroom.c | 167 | ||||
-rw-r--r-- | libempathy/empathy-tp-chatroom.h | 61 | ||||
-rw-r--r-- | libempathy/empathy-tp-contact-list.c | 1822 | ||||
-rw-r--r-- | libempathy/empathy-tp-contact-list.h | 70 | ||||
-rw-r--r-- | libempathy/gossip-contact.c | 19 | ||||
-rw-r--r-- | libempathy/gossip-contact.h | 1 | ||||
-rw-r--r-- | libempathy/gossip-utils.c | 17 | ||||
-rw-r--r-- | libempathy/gossip-utils.h | 1 |
14 files changed, 2522 insertions, 2009 deletions
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am index 4f1f1aadc..684d4e64b 100644 --- a/libempathy/Makefile.am +++ b/libempathy/Makefile.am @@ -26,7 +26,9 @@ libempathy_la_SOURCES = \ gossip-message.c gossip-message.h \ empathy-contact-list.c empathy-contact-list.h \ empathy-contact-manager.c empathy-contact-manager.h \ + empathy-tp-contact-list.c empathy-tp-contact-list.h \ empathy-tp-chat.c empathy-tp-chat.h \ + empathy-tp-chatroom.c empathy-tp-chatroom.h \ empathy-chandler.c empathy-chandler.h \ empathy-idle.c empathy-idle.h \ empathy-marshal-main.c diff --git a/libempathy/empathy-contact-list.c b/libempathy/empathy-contact-list.c index 6966ba23f..f763cdb60 100644 --- a/libempathy/empathy-contact-list.c +++ b/libempathy/empathy-contact-list.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> * Copyright (C) 2007 Collabora Ltd. * * This program is free software; you can redistribute it and/or @@ -17,209 +16,42 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. - * + * * Authors: Xavier Claessens <xclaesse@gmail.com> */ -#include <config.h> - -#include <string.h> - -#include <libtelepathy/tp-helpers.h> -#include <libtelepathy/tp-conn.h> -#include <libtelepathy/tp-chan.h> -#include <libtelepathy/tp-chan-type-contact-list-gen.h> -#include <libtelepathy/tp-conn-iface-aliasing-gen.h> -#include <libtelepathy/tp-conn-iface-presence-gen.h> -#include <libtelepathy/tp-conn-iface-avatars-gen.h> +#include "config.h" #include "empathy-contact-list.h" -#include "gossip-telepathy-group.h" -#include "gossip-debug.h" -#include "gossip-utils.h" - -#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListPriv)) - -#define DEBUG_DOMAIN "ContactList" -#define MAX_AVATAR_REQUESTS 10 - -struct _EmpathyContactListPriv { - TpConn *tp_conn; - McAccount *account; - MissionControl *mc; - GossipContact *own_contact; - - GossipTelepathyGroup *known; - GossipTelepathyGroup *publish; - GossipTelepathyGroup *subscribe; - - GHashTable *groups; - GHashTable *contacts; - DBusGProxy *aliasing_iface; - DBusGProxy *avatars_iface; - DBusGProxy *presence_iface; +static void contact_list_base_init (gpointer klass); - GList *avatar_requests_queue; -}; - -typedef enum { - CONTACT_LIST_TYPE_KNOWN, - CONTACT_LIST_TYPE_PUBLISH, - CONTACT_LIST_TYPE_SUBSCRIBE, - CONTACT_LIST_TYPE_UNKNOWN, - CONTACT_LIST_TYPE_COUNT -} ContactListType; - -typedef struct { - guint handle; - GList *new_groups; -} ContactListData; - -typedef struct { - EmpathyContactList *list; - guint handle; -} ContactListAvatarRequestData; - -typedef struct { - EmpathyContactList *list; - guint *handles; -} ContactListAliasesRequestData; - -static void empathy_contact_list_class_init (EmpathyContactListClass *klass); -static void empathy_contact_list_init (EmpathyContactList *list); -static void contact_list_finalize (GObject *object); -static void contact_list_finalize_proxies (EmpathyContactList *list); -static void contact_list_contact_removed_foreach (guint handle, - GossipContact *contact, - EmpathyContactList *list); -static void contact_list_destroy_cb (DBusGProxy *proxy, - EmpathyContactList *list); -static gboolean contact_list_find_foreach (guint handle, - GossipContact *contact, - gchar *id); -static void contact_list_newchannel_cb (DBusGProxy *proxy, - const gchar *object_path, - const gchar *channel_type, - TelepathyHandleType handle_type, - guint channel_handle, - gboolean suppress_handle, - EmpathyContactList *list); -static ContactListType contact_list_get_type (EmpathyContactList *list, - TpChan *list_chan); -static void contact_list_contact_added_cb (GossipTelepathyGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list); -static void contact_list_contact_removed_cb (GossipTelepathyGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list); -static void contact_list_local_pending_cb (GossipTelepathyGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list); -static void contact_list_groups_updated_cb (GossipContact *contact, - GParamSpec *param, - EmpathyContactList *list); -static void contact_list_subscription_updated_cb (GossipContact *contact, - GParamSpec *param, - EmpathyContactList *list); -static void contact_list_name_updated_cb (GossipContact *contact, - GParamSpec *param, - EmpathyContactList *list); -static void contact_list_update_groups_foreach (gchar *object_path, - GossipTelepathyGroup *group, - ContactListData *data); -static GossipTelepathyGroup * contact_list_get_group (EmpathyContactList *list, - const gchar *name); -static gboolean contact_list_find_group (gchar *key, - GossipTelepathyGroup *group, - gchar *group_name); -static void contact_list_get_groups_foreach (gchar *key, - GossipTelepathyGroup *group, - GList **groups); -static void contact_list_group_channel_closed_cb (TpChan *channel, - EmpathyContactList *list); -static void contact_list_group_members_added_cb (GossipTelepathyGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list); -static void contact_list_group_members_removed_cb (GossipTelepathyGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list); -static void contact_list_get_contacts_foreach (guint handle, - GossipContact *contact, - GList **contacts); -static void contact_list_get_info (EmpathyContactList *list, - GArray *handles); -static void contact_list_request_avatar (EmpathyContactList *list, - guint handle); -static void contact_list_start_avatar_requests (EmpathyContactList *list); -static void contact_list_avatar_update_cb (DBusGProxy *proxy, - guint handle, - gchar *new_token, - EmpathyContactList *list); -static void contact_list_request_avatar_cb (DBusGProxy *proxy, - GArray *avatar_data, - gchar *mime_type, - GError *error, - ContactListAvatarRequestData *data); -static void contact_list_aliases_update_cb (DBusGProxy *proxy, - GPtrArray *handlers, - EmpathyContactList *list); -static void contact_list_request_aliases_cb (DBusGProxy *proxy, - gchar **contact_names, - GError *error, - ContactListAliasesRequestData *data); -static void contact_list_presence_update_cb (DBusGProxy *proxy, - GHashTable *handle_table, - EmpathyContactList *list); -static void contact_list_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyContactList *list); -static void contact_list_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - GossipPresence **presence); -static void contact_list_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyContactList *list); +GType +empathy_contact_list_get_type (void) +{ + static GType type = 0; -enum { - CONTACT_ADDED, - CONTACT_REMOVED, - DESTROY, - LAST_SIGNAL -}; + if (!type) { + static const GTypeInfo type_info = { + sizeof (EmpathyContactListIface), + contact_list_base_init, + NULL, + }; -static guint signals[LAST_SIGNAL]; -static guint n_avatar_requests = 0; + type = g_type_register_static (G_TYPE_INTERFACE, + "EmpathyContactList", + &type_info, 0); + } -G_DEFINE_TYPE (EmpathyContactList, empathy_contact_list, G_TYPE_OBJECT); + return type; +} static void -empathy_contact_list_class_init (EmpathyContactListClass *klass) +contact_list_base_init (gpointer klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + static gboolean initialized = FALSE; - object_class->finalize = contact_list_finalize; - - signals[CONTACT_ADDED] = + if (!initialized) { g_signal_new ("contact-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -229,7 +61,6 @@ empathy_contact_list_class_init (EmpathyContactListClass *klass) G_TYPE_NONE, 1, GOSSIP_TYPE_CONTACT); - signals[CONTACT_REMOVED] = g_signal_new ("contact-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -239,1577 +70,66 @@ empathy_contact_list_class_init (EmpathyContactListClass *klass) G_TYPE_NONE, 1, GOSSIP_TYPE_CONTACT); - signals[DESTROY] = - g_signal_new ("destroy", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - g_type_class_add_private (object_class, sizeof (EmpathyContactListPriv)); -} - -static void -empathy_contact_list_init (EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - - priv = GET_PRIV (list); - - priv->groups = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_object_unref); - priv->contacts = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_object_unref); -} - - -static void -contact_list_finalize (GObject *object) -{ - EmpathyContactListPriv *priv; - EmpathyContactList *list; - - list = EMPATHY_CONTACT_LIST (object); - priv = GET_PRIV (list); - - gossip_debug (DEBUG_DOMAIN, "finalize: %p", object); - - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (contact_list_status_changed_cb), - list); - - contact_list_finalize_proxies (list); - - if (priv->tp_conn) { - g_object_unref (priv->tp_conn); + initialized = TRUE; } - - if (priv->known) { - g_object_unref (priv->known); - } - - if (priv->subscribe) { - g_object_unref (priv->subscribe); - } - - if (priv->publish) { - g_object_unref (priv->publish); - } - - g_object_unref (priv->account); - g_object_unref (priv->own_contact); - g_object_unref (priv->mc); - g_hash_table_destroy (priv->groups); - g_hash_table_destroy (priv->contacts); - - G_OBJECT_CLASS (empathy_contact_list_parent_class)->finalize (object); -} - -EmpathyContactList * -empathy_contact_list_new (McAccount *account) -{ - EmpathyContactListPriv *priv; - EmpathyContactList *list; - MissionControl *mc; - guint handle; - GError *error = NULL; - - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - - mc = gossip_mission_control_new (); - - if (mission_control_get_connection_status (mc, account, NULL) != 0) { - /* The account is not connected, nothing to do. */ - return NULL; - } - - list = g_object_new (EMPATHY_TYPE_CONTACT_LIST, NULL); - priv = GET_PRIV (list); - - priv->tp_conn = mission_control_get_connection (mc, account, NULL); - priv->account = g_object_ref (account); - priv->mc = mc; - - g_signal_connect (priv->tp_conn, "destroy", - G_CALLBACK (contact_list_destroy_cb), - list); - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (contact_list_status_changed_cb), - list, NULL); - - priv->aliasing_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_ALIASING_QUARK); - priv->avatars_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_AVATARS_QUARK); - priv->presence_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_PRESENCE_QUARK); - - if (priv->aliasing_iface) { - dbus_g_proxy_connect_signal (priv->aliasing_iface, - "AliasesChanged", - G_CALLBACK (contact_list_aliases_update_cb), - list, NULL); - } - - if (priv->avatars_iface) { - dbus_g_proxy_connect_signal (priv->avatars_iface, - "AvatarUpdated", - G_CALLBACK (contact_list_avatar_update_cb), - list, NULL); - } - - if (priv->presence_iface) { - dbus_g_proxy_connect_signal (priv->presence_iface, - "PresenceUpdate", - G_CALLBACK (contact_list_presence_update_cb), - list, NULL); - } - - /* Get our own handle and contact */ - if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn), - &handle, &error)) { - gossip_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } else { - priv->own_contact = empathy_contact_list_get_from_handle (list, handle); - } - - return list; } void empathy_contact_list_setup (EmpathyContactList *list) { - EmpathyContactListPriv *priv; - GPtrArray *channels; - GError *error = NULL; - guint i; - g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); - priv = GET_PRIV (list); - - gossip_debug (DEBUG_DOMAIN, "setup contact list: %p", list); - - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (contact_list_newchannel_cb), - list, NULL); - - /* Get existing channels */ - if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn), - &channels, - &error)) { - gossip_debug (DEBUG_DOMAIN, - "Failed to get list of open channels: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return; - } - - for (i = 0; channels->len > i; i++) { - GValueArray *chan_struct; - const gchar *object_path; - const gchar *chan_iface; - TelepathyHandleType handle_type; - guint handle; - - chan_struct = g_ptr_array_index (channels, i); - object_path = g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); - chan_iface = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); - handle_type = g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); - handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); - - contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), - object_path, chan_iface, - handle_type, handle, - FALSE, list); - - g_value_array_free (chan_struct); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->setup) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->setup (list); } - - g_ptr_array_free (channels, TRUE); -} - -McAccount * -empathy_contact_list_get_account (EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return priv->account; -} - -GossipContact * -empathy_contact_list_get_own (EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return priv->own_contact; } GossipContact * empathy_contact_list_find (EmpathyContactList *list, - const gchar *id) + const gchar *id) { - EmpathyContactListPriv *priv; - GossipContact *contact; - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - priv = GET_PRIV (list); - - contact = g_hash_table_find (priv->contacts, - (GHRFunc) contact_list_find_foreach, - (gchar*) id); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->find) { + return EMPATHY_CONTACT_LIST_GET_IFACE (list)->find (list, id); + } return NULL; } void empathy_contact_list_add (EmpathyContactList *list, - guint handle, + GossipContact *contact, const gchar *message) { - EmpathyContactListPriv *priv; - g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); - priv = GET_PRIV (list); - - gossip_telepathy_group_add_member (priv->subscribe, handle, message); -} - -void -empathy_contact_list_remove (EmpathyContactList *list, - guint handle) -{ - EmpathyContactListPriv *priv; - - g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - gossip_telepathy_group_remove_member (priv->subscribe, handle, ""); - gossip_telepathy_group_remove_member (priv->publish, handle, ""); - gossip_telepathy_group_remove_member (priv->known, handle, ""); -} - -GossipContact * -empathy_contact_list_get_from_id (EmpathyContactList *list, - const gchar *id) -{ - EmpathyContactListPriv *priv; - GossipContact *contact; - const gchar *contact_ids[] = {id, NULL}; - GArray *handles; - guint handle; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - g_return_val_if_fail (id != NULL, NULL); - - priv = GET_PRIV (list); - - contact = empathy_contact_list_find (list, id); - if (contact) { - return contact; + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->add) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->add (list, contact, message); } - - /* The id is unknown, requests a new handle */ - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - contact_ids, - &handles, &error)) { - gossip_debug (DEBUG_DOMAIN, - "RequestHandle for %s failed: %s", id, - error ? error->message : "No error given"); - g_clear_error (&error); - return 0; - } - - handle = g_array_index(handles, guint, 0); - g_array_free (handles, TRUE); - - return empathy_contact_list_get_from_handle (list, handle); -} - -GossipContact * -empathy_contact_list_get_from_handle (EmpathyContactList *list, - guint handle) -{ - GossipContact *contact; - GArray *handles; - GList *contacts; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - - handles = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (handles, handle); - - contacts = empathy_contact_list_get_from_handles (list, handles); - g_array_free (handles, TRUE); - - if (!contacts) { - return NULL; - } - - contact = contacts->data; - g_list_free (contacts); - - return contact; -} - -GList * -empathy_contact_list_get_from_handles (EmpathyContactList *list, - GArray *handles) -{ - EmpathyContactListPriv *priv; - gchar **handles_names; - gchar **id; - GArray *new_handles; - GList *contacts = NULL; - guint i; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - g_return_val_if_fail (handles != NULL, NULL); - - priv = GET_PRIV (list); - - /* Search all handles we already have */ - new_handles = g_array_new (FALSE, FALSE, sizeof (guint)); - for (i = 0; i < handles->len; i++) { - GossipContact *contact; - guint handle; - - handle = g_array_index (handles, guint, i); - contact = g_hash_table_lookup (priv->contacts, - GUINT_TO_POINTER (handle)); - - if (contact) { - contacts = g_list_prepend (contacts, - g_object_ref (contact)); - } else { - g_array_append_val (new_handles, handle); - } - } - - if (new_handles->len == 0) { - return contacts; - } - - /* Holds all handles we don't have yet. - * FIXME: We should release them at some point. */ - if (!tp_conn_hold_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - new_handles, &error)) { - gossip_debug (DEBUG_DOMAIN, - "HoldHandles Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Get the IDs of all new handles */ - if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - new_handles, - &handles_names, - &error)) { - gossip_debug (DEBUG_DOMAIN, - "InspectHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Create contact objects */ - for (i = 0, id = handles_names; *id && i < new_handles->len; id++, i++) { - GossipContact *contact; - guint handle; - - handle = g_array_index (new_handles, guint, i); - contact = g_object_new (GOSSIP_TYPE_CONTACT, - "account", priv->account, - "id", *id, - "handle", handle, - NULL); - - g_signal_connect (contact, "notify::groups", - G_CALLBACK (contact_list_groups_updated_cb), - list); - g_signal_connect (contact, "notify::subscription", - G_CALLBACK (contact_list_subscription_updated_cb), - list); - g_signal_connect (contact, "notify::name", - G_CALLBACK (contact_list_name_updated_cb), - list); - - gossip_debug (DEBUG_DOMAIN, "new contact created: %s (%d)", - *id, handle); - - g_hash_table_insert (priv->contacts, - GUINT_TO_POINTER (handle), - contact); - - contacts = g_list_prepend (contacts, g_object_ref (contact)); - } - - contact_list_get_info (list, new_handles); - - g_array_free (new_handles, TRUE); - g_strfreev (handles_names); - - return contacts; } void -empathy_contact_list_rename_group (EmpathyContactList *list, - const gchar *old_group, - const gchar *new_group) +empathy_contact_list_remove (EmpathyContactList *list, + GossipContact *contact, + const gchar *message) { - EmpathyContactListPriv *priv; - GossipTelepathyGroup *group; - GArray *members; - g_return_if_fail (EMPATHY_IS_CONTACT_LIST (list)); - g_return_if_fail (old_group != NULL); - g_return_if_fail (new_group != NULL); - - priv = GET_PRIV (list); - - group = g_hash_table_find (priv->groups, - (GHRFunc) contact_list_find_group, - (gchar*) old_group); - if (!group) { - /* The group doesn't exists on this account */ - return; - } - - gossip_debug (DEBUG_DOMAIN, "rename group %s to %s", group, new_group); - - /* Remove all members from the old group */ - members = gossip_telepathy_group_get_members (group); - gossip_telepathy_group_remove_members (group, members, ""); - contact_list_group_members_removed_cb (group, members, - 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_hash_table_remove (priv->groups, - gossip_telepathy_group_get_object_path (group)); - /* Add all members to the new group */ - group = contact_list_get_group (list, new_group); - if (group) { - gossip_telepathy_group_add_members (group, members, ""); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove) { + EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove (list, contact, message); } } GList * -empathy_contact_list_get_groups (EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GList *groups = NULL; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - g_hash_table_foreach (priv->groups, - (GHFunc) contact_list_get_groups_foreach, - &groups); - - groups = g_list_sort (groups, (GCompareFunc) strcmp); - - return groups; -} - -GList * empathy_contact_list_get_contacts (EmpathyContactList *list) { - EmpathyContactListPriv *priv; - GList *contacts = NULL; - g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list), NULL); - priv = GET_PRIV (list); - - /* FIXME: we should only return contacts that are in the contact list */ - g_hash_table_foreach (priv->contacts, - (GHFunc) contact_list_get_contacts_foreach, - &contacts); - - return contacts; -} - -static void -contact_list_finalize_proxies (EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - - priv = GET_PRIV (list); - - if (priv->tp_conn) { - g_signal_handlers_disconnect_by_func (priv->tp_conn, - contact_list_destroy_cb, - list); - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (contact_list_newchannel_cb), - list); - } - - if (priv->aliasing_iface) { - dbus_g_proxy_disconnect_signal (priv->aliasing_iface, - "AliasesChanged", - G_CALLBACK (contact_list_aliases_update_cb), - list); - } - - if (priv->avatars_iface) { - dbus_g_proxy_disconnect_signal (priv->avatars_iface, - "AvatarUpdated", - G_CALLBACK (contact_list_avatar_update_cb), - list); - } - - if (priv->presence_iface) { - dbus_g_proxy_disconnect_signal (priv->presence_iface, - "PresenceUpdate", - G_CALLBACK (contact_list_presence_update_cb), - list); - } -} - -static void -contact_list_destroy_cb (DBusGProxy *proxy, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - - priv = GET_PRIV (list); - - gossip_debug (DEBUG_DOMAIN, "Connection destroyed... " - "Account disconnected or CM crashed"); - - /* DBus proxies should NOT be used anymore */ - g_object_unref (priv->tp_conn); - priv->tp_conn = NULL; - priv->aliasing_iface = NULL; - priv->avatars_iface = NULL; - priv->presence_iface = NULL; - - /* Remove all contacts */ - g_hash_table_foreach (priv->contacts, - (GHFunc) contact_list_contact_removed_foreach, - list); - g_hash_table_remove_all (priv->contacts); - - /* Tell the world to not use us anymore */ - g_signal_emit (list, signals[DESTROY], 0); -} - -static void -contact_list_contact_removed_foreach (guint handle, - GossipContact *contact, - EmpathyContactList *list) -{ - g_signal_handlers_disconnect_by_func (contact, - contact_list_groups_updated_cb, - list); - g_signal_handlers_disconnect_by_func (contact, - contact_list_subscription_updated_cb, - list); - g_signal_handlers_disconnect_by_func (contact, - contact_list_name_updated_cb, - list); - - g_signal_emit (list, signals[CONTACT_REMOVED], 0, contact); -} - -static void -contact_list_block_contact (EmpathyContactList *list, - GossipContact *contact) -{ - g_signal_handlers_block_by_func (contact, - contact_list_groups_updated_cb, - list); - g_signal_handlers_block_by_func (contact, - contact_list_subscription_updated_cb, - list); - g_signal_handlers_block_by_func (contact, - contact_list_name_updated_cb, - list); -} - -static void -contact_list_unblock_contact (EmpathyContactList *list, - GossipContact *contact) -{ - g_signal_handlers_unblock_by_func (contact, - contact_list_groups_updated_cb, - list); - g_signal_handlers_unblock_by_func (contact, - contact_list_subscription_updated_cb, - list); - g_signal_handlers_unblock_by_func (contact, - contact_list_name_updated_cb, - list); -} - -static gboolean -contact_list_find_foreach (guint handle, - GossipContact *contact, - gchar *id) -{ - if (strcmp (gossip_contact_get_id (contact), id) == 0) { - return TRUE; - } - - return FALSE; -} - -static void -contact_list_newchannel_cb (DBusGProxy *proxy, - const gchar *object_path, - const gchar *channel_type, - TelepathyHandleType handle_type, - guint channel_handle, - gboolean suppress_handle, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GossipTelepathyGroup *group; - TpChan *new_chan; - const gchar *bus_name; - GArray *members; - - priv = GET_PRIV (list); - - if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) != 0 || - suppress_handle) { - return; - } - - bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)); - new_chan = tp_chan_new (tp_get_bus (), - bus_name, - object_path, - channel_type, handle_type, channel_handle); - - if (handle_type == TP_HANDLE_TYPE_LIST) { - ContactListType list_type; - - list_type = contact_list_get_type (list, new_chan); - if (list_type == CONTACT_LIST_TYPE_UNKNOWN) { - gossip_debug (DEBUG_DOMAIN, "Unknown contact list channel"); - g_object_unref (new_chan); - return; - } - - gossip_debug (DEBUG_DOMAIN, "New contact list channel of type: %d", - list_type); - - group = gossip_telepathy_group_new (new_chan, priv->tp_conn); - - switch (list_type) { - case CONTACT_LIST_TYPE_KNOWN: - if (priv->known) { - g_object_unref (priv->known); - } - priv->known = group; - break; - case CONTACT_LIST_TYPE_PUBLISH: - if (priv->publish) { - g_object_unref (priv->publish); - } - priv->publish = group; - break; - case CONTACT_LIST_TYPE_SUBSCRIBE: - if (priv->subscribe) { - g_object_unref (priv->subscribe); - } - priv->subscribe = group; - break; - default: - g_assert_not_reached (); - } - - /* Connect and setup the new contact-list group */ - if (list_type == CONTACT_LIST_TYPE_KNOWN || - list_type == CONTACT_LIST_TYPE_SUBSCRIBE) { - g_signal_connect (group, "members-added", - G_CALLBACK (contact_list_contact_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (contact_list_contact_removed_cb), - list); - - members = gossip_telepathy_group_get_members (group); - contact_list_contact_added_cb (group, members, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_array_free (members, TRUE); - } - if (list_type == CONTACT_LIST_TYPE_PUBLISH) { - GPtrArray *info; - GArray *pending; - guint i; - - g_signal_connect (group, "local-pending", - G_CALLBACK (contact_list_local_pending_cb), - list); - - info = gossip_telepathy_group_get_local_pending_members_with_info (group); - - if (!info) { - /* This happens with butterfly because - * GetLocalPendingMembersWithInfo is not - * implemented */ - g_object_unref (new_chan); - return; - } - - pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); - for (i = 0; info->len > i; i++) { - GValueArray *pending_struct; - guint member; - guint invitor; - guint reason; - const gchar *message; - - pending_struct = g_ptr_array_index (info, i); - member = g_value_get_uint (g_value_array_get_nth (pending_struct, 0)); - invitor = g_value_get_uint (g_value_array_get_nth (pending_struct, 1)); - reason = g_value_get_uint (g_value_array_get_nth (pending_struct, 2)); - message = g_value_get_string (g_value_array_get_nth (pending_struct, 3)); - - g_array_insert_val (pending, 0, member); - - contact_list_local_pending_cb (group, pending, - invitor, - reason, - message, list); - - g_value_array_free (pending_struct); - } - - g_ptr_array_free (info, TRUE); - g_array_free (pending, TRUE); - } - } - else if (handle_type == TP_HANDLE_TYPE_GROUP) { - const gchar *object_path; - - object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (new_chan)); - if (g_hash_table_lookup (priv->groups, object_path)) { - g_object_unref (new_chan); - return; - } - - group = gossip_telepathy_group_new (new_chan, priv->tp_conn); - - gossip_debug (DEBUG_DOMAIN, "New server-side group channel: %s", - gossip_telepathy_group_get_name (group)); - - dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed", - G_CALLBACK - (contact_list_group_channel_closed_cb), - list, NULL); - - g_hash_table_insert (priv->groups, g_strdup (object_path), group); - g_signal_connect (group, "members-added", - G_CALLBACK (contact_list_group_members_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (contact_list_group_members_removed_cb), - list); - - members = gossip_telepathy_group_get_members (group); - contact_list_group_members_added_cb (group, members, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_array_free (members, TRUE); - } - - g_object_unref (new_chan); -} - -static ContactListType -contact_list_get_type (EmpathyContactList *list, - TpChan *list_chan) -{ - EmpathyContactListPriv *priv; - GArray *handles; - gchar **handle_name; - ContactListType list_type; - GError *error = NULL; - - priv = GET_PRIV (list); - - handles = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (handles, list_chan->handle); - - if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_LIST, - handles, - &handle_name, - &error)) { - gossip_debug (DEBUG_DOMAIN, - "InspectHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (handles, TRUE); - return CONTACT_LIST_TYPE_UNKNOWN; - } - - if (strcmp (*handle_name, "subscribe") == 0) { - list_type = CONTACT_LIST_TYPE_SUBSCRIBE; - } else if (strcmp (*handle_name, "publish") == 0) { - list_type = CONTACT_LIST_TYPE_PUBLISH; - } else if (strcmp (*handle_name, "known") == 0) { - list_type = CONTACT_LIST_TYPE_KNOWN; - } else { - list_type = CONTACT_LIST_TYPE_UNKNOWN; - } - - g_strfreev (handle_name); - g_array_free (handles, TRUE); - - return list_type; -} - -static void -contact_list_contact_added_cb (GossipTelepathyGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GList *added_list, *l; - - priv = GET_PRIV (list); - - added_list = empathy_contact_list_get_from_handles (list, handles); - - for (l = added_list; l; l = l->next) { - GossipContact *contact; - - contact = GOSSIP_CONTACT (l->data); - contact_list_block_contact (list, contact); - gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_BOTH); - contact_list_unblock_contact (list, contact); - - g_signal_emit (list, signals[CONTACT_ADDED], 0, contact); - - g_object_unref (contact); - } - - g_list_free (added_list); -} - -static void -contact_list_contact_removed_cb (GossipTelepathyGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GList *removed_list, *l; - - priv = GET_PRIV (list); - - removed_list = empathy_contact_list_get_from_handles (list, handles); - - for (l = removed_list; l; l = l->next) { - GossipContact *contact; - guint handle; - - contact = GOSSIP_CONTACT (l->data); - - handle = gossip_contact_get_handle (contact); - g_hash_table_remove (priv->contacts, GUINT_TO_POINTER (handle)); - - g_signal_emit (list, signals[CONTACT_REMOVED], 0, contact); - - g_object_unref (contact); + if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_contacts) { + return EMPATHY_CONTACT_LIST_GET_IFACE (list)->get_contacts (list); } - g_list_free (removed_list); -} - -static void -contact_list_local_pending_cb (GossipTelepathyGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GList *pending_list, *l; - - priv = GET_PRIV (list); - - pending_list = empathy_contact_list_get_from_handles (list, handles); - - for (l = pending_list; l; l = l->next) { - GossipContact *contact; - - contact = GOSSIP_CONTACT (l->data); - - /* FIXME: Is that the correct way ? */ - contact_list_block_contact (list, contact); - gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_FROM); - contact_list_unblock_contact (list, contact); - g_signal_emit (list, signals[CONTACT_ADDED], 0, contact); - - g_object_unref (contact); - } - - g_list_free (pending_list); -} - -static void -contact_list_groups_updated_cb (GossipContact *contact, - GParamSpec *param, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - ContactListData data; - GList *groups, *l; - - priv = GET_PRIV (list); - - /* Make sure all groups are created */ - groups = gossip_contact_get_groups (contact); - for (l = groups; l; l = l->next) { - contact_list_get_group (list, l->data); - } - - data.handle = gossip_contact_get_handle (contact); - data.new_groups = groups; - - g_hash_table_foreach (priv->groups, - (GHFunc) contact_list_update_groups_foreach, - &data); -} - -static void -contact_list_subscription_updated_cb (GossipContact *contact, - GParamSpec *param, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GossipSubscription subscription; - guint handle; - - priv = GET_PRIV (list); - - subscription = gossip_contact_get_subscription (contact); - handle = gossip_contact_get_handle (contact); - - /* FIXME: what to do here, I'm a bit lost... */ - if (subscription) { - gossip_telepathy_group_add_member (priv->publish, handle, ""); - } else { - gossip_telepathy_group_remove_member (priv->publish, handle, ""); - } -} - -static void -contact_list_name_updated_cb (GossipContact *contact, - GParamSpec *param, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GHashTable *new_alias; - const gchar *new_name; - guint handle; - GError *error = NULL; - - priv = GET_PRIV (list); - - handle = gossip_contact_get_handle (contact); - new_name = gossip_contact_get_name (contact); - - gossip_debug (DEBUG_DOMAIN, "renaming handle %d to %s", - handle, new_name); - - new_alias = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - g_free); - - g_hash_table_insert (new_alias, - GUINT_TO_POINTER (handle), - g_strdup (new_name)); - - if (!tp_conn_iface_aliasing_set_aliases (priv->aliasing_iface, - new_alias, - &error)) { - gossip_debug (DEBUG_DOMAIN, - "Couldn't rename contact: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } - - g_hash_table_destroy (new_alias); -} - -static void -contact_list_update_groups_foreach (gchar *object_path, - GossipTelepathyGroup *group, - ContactListData *data) -{ - gboolean is_member; - gboolean found = FALSE; - const gchar *group_name; - GList *l; - - is_member = gossip_telepathy_group_is_member (group, data->handle); - group_name = gossip_telepathy_group_get_name (group); - - for (l = data->new_groups; l; l = l->next) { - if (strcmp (group_name, l->data) == 0) { - found = TRUE; - break; - } - } - - if (is_member && !found) { - /* We are no longer member of this group */ - gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'", - data->handle, group_name); - gossip_telepathy_group_remove_member (group, data->handle, ""); - } - - if (!is_member && found) { - /* We are now member of this group */ - gossip_debug (DEBUG_DOMAIN, "Contact %d added to group '%s'", - data->handle, group_name); - gossip_telepathy_group_add_member (group, data->handle, ""); - } -} - -static GossipTelepathyGroup * -contact_list_get_group (EmpathyContactList *list, - const gchar *name) -{ - EmpathyContactListPriv *priv; - GossipTelepathyGroup *group; - TpChan *group_channel; - GArray *handles; - guint group_handle; - char *group_object_path; - const char *names[2] = {name, NULL}; - GError *error = NULL; - - priv = GET_PRIV (list); - - group = g_hash_table_find (priv->groups, - (GHRFunc) contact_list_find_group, - (gchar*) name); - if (group) { - return group; - } - - gossip_debug (DEBUG_DOMAIN, "creating new group: %s", name); - - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_GROUP, - names, - &handles, - &error)) { - gossip_debug (DEBUG_DOMAIN, - "Couldn't request the creation of a new handle for group: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - group_handle = g_array_index (handles, guint, 0); - g_array_free (handles, TRUE); - - if (!tp_conn_request_channel (DBUS_G_PROXY (priv->tp_conn), - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - group_handle, - FALSE, - &group_object_path, - &error)) { - gossip_debug (DEBUG_DOMAIN, - "Couldn't request the creation of a new group channel: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - - group_channel = tp_chan_new (tp_get_bus (), - dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)), - group_object_path, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - group_handle); - - dbus_g_proxy_connect_signal (DBUS_G_PROXY (group_channel), - "Closed", - G_CALLBACK - (contact_list_group_channel_closed_cb), - list, - NULL); - - group = gossip_telepathy_group_new (group_channel, priv->tp_conn); - g_hash_table_insert (priv->groups, group_object_path, group); - g_signal_connect (group, "members-added", - G_CALLBACK (contact_list_group_members_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (contact_list_group_members_removed_cb), - list); - - return group; -} - -static gboolean -contact_list_find_group (gchar *key, - GossipTelepathyGroup *group, - gchar *group_name) -{ - if (strcmp (group_name, gossip_telepathy_group_get_name (group)) == 0) { - return TRUE; - } - - return FALSE; -} - -static void -contact_list_get_groups_foreach (gchar *key, - GossipTelepathyGroup *group, - GList **groups) -{ - const gchar *name; - - name = gossip_telepathy_group_get_name (group); - *groups = g_list_append (*groups, g_strdup (name)); -} - -static void -contact_list_group_channel_closed_cb (TpChan *channel, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - - priv = GET_PRIV (list); - - g_hash_table_remove (priv->groups, - dbus_g_proxy_get_path (DBUS_G_PROXY (channel))); -} - -static void -contact_list_group_members_added_cb (GossipTelepathyGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GList *added_list, *l; - const gchar *group_name; - - priv = GET_PRIV (list); - - group_name = gossip_telepathy_group_get_name (group); - added_list = empathy_contact_list_get_from_handles (list, members); - - for (l = added_list; l; l = l->next) { - GossipContact *contact; - GList *contact_groups; - - contact = GOSSIP_CONTACT (l->data); - contact_groups = gossip_contact_get_groups (contact); - - if (!g_list_find_custom (contact_groups, - group_name, - (GCompareFunc) strcmp)) { - gossip_debug (DEBUG_DOMAIN, "Contact %s added to group '%s'", - gossip_contact_get_name (contact), - group_name); - contact_groups = g_list_append (contact_groups, - g_strdup (group_name)); - contact_list_block_contact (list, contact); - gossip_contact_set_groups (contact, contact_groups); - contact_list_unblock_contact (list, contact); - } - - g_object_unref (contact); - } - - g_list_free (added_list); -} - -static void -contact_list_group_members_removed_cb (GossipTelepathyGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - GList *removed_list, *l; - const gchar *group_name; - - priv = GET_PRIV (list); - - group_name = gossip_telepathy_group_get_name (group); - removed_list = empathy_contact_list_get_from_handles (list, members); - - for (l = removed_list; l; l = l->next) { - GossipContact *contact; - GList *contact_groups; - GList *to_remove; - - /* FIXME: Does it leak ? */ - contact = GOSSIP_CONTACT (l->data); - contact_groups = gossip_contact_get_groups (contact); - contact_groups = g_list_copy (contact_groups); - - to_remove = g_list_find_custom (contact_groups, - group_name, - (GCompareFunc) strcmp); - if (to_remove) { - gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'", - gossip_contact_get_handle (contact), - group_name); - contact_groups = g_list_remove_link (contact_groups, - to_remove); - contact_list_block_contact (list, contact); - gossip_contact_set_groups (contact, contact_groups); - contact_list_unblock_contact (list, contact); - } - - g_list_free (contact_groups); - - g_object_unref (contact); - } - - g_list_free (removed_list); -} - -static void -contact_list_get_contacts_foreach (guint handle, - GossipContact *contact, - GList **contacts) -{ - *contacts = g_list_append (*contacts, g_object_ref (contact)); -} - -static void -contact_list_get_info (EmpathyContactList *list, - GArray *handles) -{ - EmpathyContactListPriv *priv; - GError *error = NULL; - - priv = GET_PRIV (list); - - if (priv->presence_iface) { - /* FIXME: We should use GetPresence instead */ - if (!tp_conn_iface_presence_request_presence (priv->presence_iface, - handles, &error)) { - gossip_debug (DEBUG_DOMAIN, - "Could not request presences: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } - } - - if (priv->aliasing_iface) { - ContactListAliasesRequestData *data; - - data = g_slice_new (ContactListAliasesRequestData); - data->list = list; - data->handles = g_memdup (handles->data, handles->len * sizeof (guint)); - - tp_conn_iface_aliasing_request_aliases_async (priv->aliasing_iface, - handles, - (tp_conn_iface_aliasing_request_aliases_reply) - contact_list_request_aliases_cb, - data); - } - - if (priv->avatars_iface) { - guint i; - - for (i = 0; i < handles->len; i++) { - guint handle; - - handle = g_array_index (handles, gint, i); - contact_list_request_avatar (list, handle); - } - } -} - -static void -contact_list_request_avatar (EmpathyContactList *list, - guint handle) -{ - EmpathyContactListPriv *priv; - - priv = GET_PRIV (list); - - /* We queue avatar requests to not send too many dbus async - * calls at once. If we don't we reach the dbus's limit of - * pending calls */ - priv->avatar_requests_queue = g_list_append (priv->avatar_requests_queue, - GUINT_TO_POINTER (handle)); - contact_list_start_avatar_requests (list); -} - -static void -contact_list_start_avatar_requests (EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - ContactListAvatarRequestData *data; - - priv = GET_PRIV (list); - - while (n_avatar_requests < MAX_AVATAR_REQUESTS && - priv->avatar_requests_queue) { - data = g_slice_new (ContactListAvatarRequestData); - data->list = list; - data->handle = GPOINTER_TO_UINT (priv->avatar_requests_queue->data); - - n_avatar_requests++; - priv->avatar_requests_queue = g_list_remove (priv->avatar_requests_queue, - priv->avatar_requests_queue->data); - - tp_conn_iface_avatars_request_avatar_async (priv->avatars_iface, - data->handle, - (tp_conn_iface_avatars_request_avatar_reply) - contact_list_request_avatar_cb, - data); - } -} - -static void -contact_list_avatar_update_cb (DBusGProxy *proxy, - guint handle, - gchar *new_token, - EmpathyContactList *list) -{ - gossip_debug (DEBUG_DOMAIN, "Changing avatar for %d to %s", - handle, new_token); - - contact_list_request_avatar (list, handle); -} - -static void -contact_list_request_avatar_cb (DBusGProxy *proxy, - GArray *avatar_data, - gchar *mime_type, - GError *error, - ContactListAvatarRequestData *data) -{ - GossipContact *contact; - - contact = empathy_contact_list_get_from_handle (data->list, data->handle); - - if (error) { - gossip_debug (DEBUG_DOMAIN, "Error requesting avatar for %s: %s", - gossip_contact_get_name (contact), - error ? error->message : "No error given"); - } else { - GossipAvatar *avatar; - - avatar = gossip_avatar_new (avatar_data->data, - avatar_data->len, - mime_type); - contact_list_block_contact (data->list, contact); - gossip_contact_set_avatar (contact, avatar); - contact_list_unblock_contact (data->list, contact); - gossip_avatar_unref (avatar); - } - - n_avatar_requests--; - contact_list_start_avatar_requests (data->list); - - g_slice_free (ContactListAvatarRequestData, data); -} - -static void -contact_list_aliases_update_cb (DBusGProxy *proxy, - GPtrArray *renamed_handlers, - EmpathyContactList *list) -{ - gint i; - - for (i = 0; renamed_handlers->len > i; i++) { - guint handle; - const gchar *alias; - GValueArray *renamed_struct; - GossipContact *contact; - - renamed_struct = g_ptr_array_index (renamed_handlers, i); - handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0)); - alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1)); - - if (alias && *alias == '\0') { - alias = NULL; - } - - contact = empathy_contact_list_get_from_handle (list, handle); - contact_list_block_contact (list, contact); - gossip_contact_set_name (contact, alias); - contact_list_unblock_contact (list, contact); - - gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (update cb)", - handle, alias); - } -} - -static void -contact_list_request_aliases_cb (DBusGProxy *proxy, - gchar **contact_names, - GError *error, - ContactListAliasesRequestData *data) -{ - guint i = 0; - gchar **name; - - for (name = contact_names; *name && !error; name++) { - GossipContact *contact; - - contact = empathy_contact_list_get_from_handle (data->list, - data->handles[i]); - contact_list_block_contact (data->list, contact); - gossip_contact_set_name (contact, *name); - contact_list_unblock_contact (data->list, contact); - - gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (request cb)", - data->handles[i], *name); - - i++; - } - - g_free (data->handles); - g_slice_free (ContactListAliasesRequestData, data); -} - -static void -contact_list_presence_update_cb (DBusGProxy *proxy, - GHashTable *handle_table, - EmpathyContactList *list) -{ - g_hash_table_foreach (handle_table, - (GHFunc) contact_list_parse_presence_foreach, - list); -} - -static void -contact_list_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyContactList *list) -{ - GHashTable *presences_table; - GossipContact *contact; - GossipPresence *presence = NULL; - - contact = empathy_contact_list_get_from_handle (list, handle); - presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1)); - - g_hash_table_foreach (presences_table, - (GHFunc) contact_list_presences_table_foreach, - &presence); - - gossip_debug (DEBUG_DOMAIN, "Presence changed for %s (%d) to %s (%d)", - gossip_contact_get_name (contact), - handle, - presence ? gossip_presence_get_status (presence) : "unset", - presence ? gossip_presence_get_state (presence) : MC_PRESENCE_UNSET); - - contact_list_block_contact (list, contact); - gossip_contact_set_presence (contact, presence); - contact_list_unblock_contact (list, contact); -} - -static void -contact_list_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - GossipPresence **presence) -{ - McPresence state; - const GValue *message; - - state = gossip_presence_state_from_str (state_str); - if ((state == MC_PRESENCE_UNSET) || (state == MC_PRESENCE_OFFLINE)) { - return; - } - - if (*presence) { - g_object_unref (*presence); - *presence = NULL; - } - - *presence = gossip_presence_new (); - gossip_presence_set_state (*presence, state); - - message = g_hash_table_lookup (presences_table, "message"); - if (message != NULL) { - gossip_presence_set_status (*presence, - g_value_get_string (message)); - } -} - -static void -contact_list_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyContactList *list) -{ - EmpathyContactListPriv *priv; - McAccount *account; - - priv = GET_PRIV (list); - - account = mc_account_lookup (unique_name); - if (status != TP_CONN_STATUS_DISCONNECTED || - !gossip_account_equal (account, priv->account)) { - g_object_unref (account); - return; - } - - /* We are disconnected, do just like if the connection was destroyed */ - g_signal_handlers_disconnect_by_func (priv->tp_conn, - contact_list_destroy_cb, - list); - contact_list_destroy_cb (DBUS_G_PROXY (priv->tp_conn), list); - - g_object_unref (account); + return NULL; } diff --git a/libempathy/empathy-contact-list.h b/libempathy/empathy-contact-list.h index 60c56b25a..02ec3186d 100644 --- a/libempathy/empathy-contact-list.h +++ b/libempathy/empathy-contact-list.h @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> * Copyright (C) 2007 Collabora Ltd. * * This program is free software; you can redistribute it and/or @@ -17,15 +16,14 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. - * + * * Authors: Xavier Claessens <xclaesse@gmail.com> */ #ifndef __EMPATHY_CONTACT_LIST_H__ #define __EMPATHY_CONTACT_LIST_H__ -#include <glib.h> -#include <libmissioncontrol/mc-account.h> +#include <glib-object.h> #include "gossip-contact.h" @@ -33,47 +31,41 @@ G_BEGIN_DECLS #define EMPATHY_TYPE_CONTACT_LIST (empathy_contact_list_get_type ()) #define EMPATHY_CONTACT_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactList)) -#define EMPATHY_CONTACT_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListClass)) #define EMPATHY_IS_CONTACT_LIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_LIST)) -#define EMPATHY_IS_CONTACT_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_LIST)) -#define EMPATHY_CONTACT_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListClass)) +#define EMPATHY_CONTACT_LIST_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyContactListIface)) typedef struct _EmpathyContactList EmpathyContactList; -typedef struct _EmpathyContactListClass EmpathyContactListClass; -typedef struct _EmpathyContactListPriv EmpathyContactListPriv; +typedef struct _EmpathyContactListIface EmpathyContactListIface; -struct _EmpathyContactList { - GObject parent; -}; +struct _EmpathyContactListIface { + GTypeInterface base_iface; -struct _EmpathyContactListClass { - GObjectClass parent_class; + /* VTabled */ + void (*setup) (EmpathyContactList *list); + GossipContact * (*find) (EmpathyContactList *list, + const gchar *id); + void (*add) (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); + void (*remove) (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); + GList * (*get_contacts) (EmpathyContactList *list); }; -GType empathy_contact_list_get_type (void) G_GNUC_CONST; -EmpathyContactList * empathy_contact_list_new (McAccount *account); -void empathy_contact_list_setup (EmpathyContactList *list); -McAccount * empathy_contact_list_get_account (EmpathyContactList *list); -GossipContact * empathy_contact_list_get_own (EmpathyContactList *list); -GossipContact * empathy_contact_list_find (EmpathyContactList *list, - const gchar *id); -void empathy_contact_list_add (EmpathyContactList *list, - guint handle, - const gchar *message); -void empathy_contact_list_remove (EmpathyContactList *list, - guint handle); -GossipContact * empathy_contact_list_get_from_id (EmpathyContactList *list, - const gchar *id); -GossipContact * empathy_contact_list_get_from_handle (EmpathyContactList *list, - guint handle); -GList * empathy_contact_list_get_from_handles (EmpathyContactList *list, - GArray *handles); -void empathy_contact_list_rename_group (EmpathyContactList *list, - const gchar *old_group, - const gchar *new_group); -GList * empathy_contact_list_get_groups (EmpathyContactList *list); -GList * empathy_contact_list_get_contacts (EmpathyContactList *list); +GType empathy_contact_list_get_type (void) G_GNUC_CONST; +void empathy_contact_list_setup (EmpathyContactList *list); +GossipContact * empathy_contact_list_find (EmpathyContactList *list, + const gchar *id); +void empathy_contact_list_add (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); +void empathy_contact_list_remove (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); +GList * empathy_contact_list_get_contacts (EmpathyContactList *list); G_END_DECLS #endif /* __EMPATHY_CONTACT_LIST_H__ */ + diff --git a/libempathy/empathy-contact-manager.c b/libempathy/empathy-contact-manager.c index ebc6b0573..a4cae35c7 100644 --- a/libempathy/empathy-contact-manager.c +++ b/libempathy/empathy-contact-manager.c @@ -27,6 +27,7 @@ #include <libtelepathy/tp-constants.h> #include "empathy-contact-manager.h" +#include "empathy-contact-list.h" #include "gossip-utils.h" #include "gossip-debug.h" @@ -51,50 +52,55 @@ typedef struct { const gchar *id; } ContactManagerFindData; -static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass); -static void empathy_contact_manager_init (EmpathyContactManager *manager); -static void contact_manager_finalize (GObject *object); -static void contact_manager_setup_foreach (McAccount *account, - EmpathyContactList *list, - EmpathyContactManager *manager); -static gboolean contact_manager_find_foreach (McAccount *account, - EmpathyContactList *list, - ContactManagerFindData *data); -static void contact_manager_add_account (EmpathyContactManager *manager, - McAccount *account); -static void contact_manager_added_cb (EmpathyContactList *list, - GossipContact *contact, - EmpathyContactManager *manager); -static void contact_manager_removed_cb (EmpathyContactList *list, - GossipContact *contact, - EmpathyContactManager *manager); -static void contact_manager_destroy_cb (EmpathyContactList *list, - EmpathyContactManager *manager); -static void contact_manager_rename_group_foreach (McAccount *account, - EmpathyContactList *list, - ContactManagerRenameGroupData *data); -static void contact_manager_get_groups_foreach (McAccount *account, - EmpathyContactList *list, - GList **all_groups); -static void contact_manager_get_contacts_foreach (McAccount *account, - EmpathyContactList *list, - GList **contacts); -static void contact_manager_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyContactManager *manager); - -enum { - CONTACT_ADDED, - CONTACT_REMOVED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL]; - -G_DEFINE_TYPE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT); +static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass); +static void contact_manager_iface_init (EmpathyContactListIface *iface); +static void empathy_contact_manager_init (EmpathyContactManager *manager); +static void contact_manager_finalize (GObject *object); +static void contact_manager_setup (EmpathyContactList *manager); +static GossipContact *contact_manager_find (EmpathyContactList *manager, + const gchar *id); +static void contact_manager_add (EmpathyContactList *manager, + GossipContact *contact, + const gchar *message); +static void contact_manager_remove (EmpathyContactList *manager, + GossipContact *contact, + const gchar *message); +static GList * contact_manager_get_contacts (EmpathyContactList *manager); +static void contact_manager_setup_foreach (McAccount *account, + EmpathyTpContactList *list, + EmpathyContactManager *manager); +static gboolean contact_manager_find_foreach (McAccount *account, + EmpathyTpContactList *list, + ContactManagerFindData *data); +static void contact_manager_add_account (EmpathyContactManager *manager, + McAccount *account); +static void contact_manager_added_cb (EmpathyTpContactList *list, + GossipContact *contact, + EmpathyContactManager *manager); +static void contact_manager_removed_cb (EmpathyTpContactList *list, + GossipContact *contact, + EmpathyContactManager *manager); +static void contact_manager_destroy_cb (EmpathyTpContactList *list, + EmpathyContactManager *manager); +static void contact_manager_rename_group_foreach (McAccount *account, + EmpathyTpContactList *list, + ContactManagerRenameGroupData *data); +static void contact_manager_get_groups_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **all_groups); +static void contact_manager_get_contacts_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **contacts); +static void contact_manager_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyContactManager *manager); + +G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, + contact_manager_iface_init)); static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass) @@ -103,30 +109,20 @@ empathy_contact_manager_class_init (EmpathyContactManagerClass *klass) object_class->finalize = contact_manager_finalize; - signals[CONTACT_ADDED] = - g_signal_new ("contact-added", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, GOSSIP_TYPE_CONTACT); - - signals[CONTACT_REMOVED] = - g_signal_new ("contact-removed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, GOSSIP_TYPE_CONTACT); - g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv)); } static void +contact_manager_iface_init (EmpathyContactListIface *iface) +{ + iface->setup = contact_manager_setup; + iface->find = contact_manager_find; + iface->add = contact_manager_add; + iface->remove = contact_manager_remove; + iface->get_contacts = contact_manager_get_contacts; +} + +static void empathy_contact_manager_init (EmpathyContactManager *manager) { EmpathyContactManagerPriv *priv; @@ -185,8 +181,8 @@ empathy_contact_manager_new (void) return manager; } -void -empathy_contact_manager_setup (EmpathyContactManager *manager) +static void +contact_manager_setup (EmpathyContactList *manager) { EmpathyContactManagerPriv *priv; @@ -206,131 +202,145 @@ empathy_contact_manager_setup (EmpathyContactManager *manager) priv->setup = TRUE; } -EmpathyContactList * -empathy_contact_manager_get_list (EmpathyContactManager *manager, - McAccount *account) +static GossipContact * +contact_manager_find (EmpathyContactList *manager, + const gchar *id) { EmpathyContactManagerPriv *priv; + ContactManagerFindData data; g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (id != NULL, NULL); priv = GET_PRIV (manager); - return g_hash_table_lookup (priv->lists, account); + data.contact = NULL; + data.id = id; + + g_hash_table_find (priv->lists, + (GHRFunc) contact_manager_find_foreach, + &data); + + return data.contact; } -GossipContact * -empathy_contact_manager_get_own (EmpathyContactManager *manager, - McAccount *account) +static void +contact_manager_add (EmpathyContactList *manager, + GossipContact *contact, + const gchar *message) { EmpathyContactManagerPriv *priv; EmpathyContactList *list; + McAccount *account; - g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); priv = GET_PRIV (manager); + account = gossip_contact_get_account (contact); list = g_hash_table_lookup (priv->lists, account); - - if (!list) { - return NULL; - } - return empathy_contact_list_get_own (list); + if (list) { + empathy_contact_list_add (list, contact, message); + } } -GossipContact * -empathy_contact_manager_create (EmpathyContactManager *manager, - McAccount *account, - const gchar *id) +static void +contact_manager_remove (EmpathyContactList *manager, + GossipContact *contact, + const gchar *message) { EmpathyContactManagerPriv *priv; EmpathyContactList *list; + McAccount *account; - g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - g_return_val_if_fail (id != NULL, NULL); + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); priv = GET_PRIV (manager); + account = gossip_contact_get_account (contact); list = g_hash_table_lookup (priv->lists, account); - - if (!list) { - return NULL; - } - return empathy_contact_list_get_from_id (list, id); + if (list) { + empathy_contact_list_remove (list, contact, message); + } } -GossipContact * -empathy_contact_manager_find (EmpathyContactManager *manager, - const gchar *id) +static GList * +contact_manager_get_contacts (EmpathyContactList *manager) { EmpathyContactManagerPriv *priv; - ContactManagerFindData data; + GList *contacts = NULL; g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - g_return_val_if_fail (id != NULL, NULL); priv = GET_PRIV (manager); - data.contact = NULL; - data.id = id; + g_hash_table_foreach (priv->lists, + (GHFunc) contact_manager_get_contacts_foreach, + &contacts); - g_hash_table_find (priv->lists, - (GHRFunc) contact_manager_find_foreach, - &data); + return contacts; +} - return data.contact; +EmpathyTpContactList * +empathy_contact_manager_get_list (EmpathyContactManager *manager, + McAccount *account) +{ + EmpathyContactManagerPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + + priv = GET_PRIV (manager); + + return g_hash_table_lookup (priv->lists, account); } -void -empathy_contact_manager_add (EmpathyContactManager *manager, - GossipContact *contact, - const gchar *message) +GossipContact * +empathy_contact_manager_get_user (EmpathyContactManager *manager, + McAccount *account) { EmpathyContactManagerPriv *priv; - EmpathyContactList *list; - McAccount *account; - guint handle; + EmpathyTpContactList *list; - g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); priv = GET_PRIV (manager); - account = gossip_contact_get_account (contact); - handle = gossip_contact_get_handle (contact); list = g_hash_table_lookup (priv->lists, account); - - if (list) { - empathy_contact_list_add (list, handle, message); + + if (!list) { + return NULL; } + + return empathy_tp_contact_list_get_user (list); } -void -empathy_contact_manager_remove (EmpathyContactManager *manager, - GossipContact *contact) +GossipContact * +empathy_contact_manager_create (EmpathyContactManager *manager, + McAccount *account, + const gchar *id) { EmpathyContactManagerPriv *priv; - EmpathyContactList *list; - McAccount *account; - guint handle; + EmpathyTpContactList *list; - g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); - g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (id != NULL, NULL); priv = GET_PRIV (manager); - account = gossip_contact_get_account (contact); - handle = gossip_contact_get_handle (contact); list = g_hash_table_lookup (priv->lists, account); - - if (list) { - empathy_contact_list_remove (list, handle); + + if (!list) { + return NULL; } + + return empathy_tp_contact_list_get_from_id (list, id); } void @@ -372,37 +382,21 @@ empathy_contact_manager_get_groups (EmpathyContactManager *manager) return groups; } -GList * -empathy_contact_manager_get_contacts (EmpathyContactManager *manager) -{ - EmpathyContactManagerPriv *priv; - GList *contacts = NULL; - - g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL); - - priv = GET_PRIV (manager); - - g_hash_table_foreach (priv->lists, - (GHFunc) contact_manager_get_contacts_foreach, - &contacts); - - return contacts; -} - static void contact_manager_setup_foreach (McAccount *account, - EmpathyContactList *list, + EmpathyTpContactList *list, EmpathyContactManager *manager) { - empathy_contact_list_setup (list); + empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list)); } static gboolean contact_manager_find_foreach (McAccount *account, - EmpathyContactList *list, + EmpathyTpContactList *list, ContactManagerFindData *data) { - data->contact = empathy_contact_list_find (list, data->id); + data->contact = empathy_contact_list_find (EMPATHY_CONTACT_LIST (list), + data->id); if (data->contact) { return TRUE; @@ -416,7 +410,7 @@ contact_manager_add_account (EmpathyContactManager *manager, McAccount *account) { EmpathyContactManagerPriv *priv; - EmpathyContactList *list; + EmpathyTpContactList *list; priv = GET_PRIV (manager); @@ -427,7 +421,7 @@ contact_manager_add_account (EmpathyContactManager *manager, gossip_debug (DEBUG_DOMAIN, "Adding new account: %s", mc_account_get_display_name (account)); - list = empathy_contact_list_new (account); + list = empathy_tp_contact_list_new (account); if (!list) { return; } @@ -446,28 +440,28 @@ contact_manager_add_account (EmpathyContactManager *manager, manager); if (priv->setup) { - empathy_contact_list_setup (list); + empathy_contact_list_setup (EMPATHY_CONTACT_LIST (list)); } } static void -contact_manager_added_cb (EmpathyContactList *list, +contact_manager_added_cb (EmpathyTpContactList *list, GossipContact *contact, EmpathyContactManager *manager) { - g_signal_emit (manager, signals[CONTACT_ADDED], 0, contact); + g_signal_emit_by_name (manager, "contact-added", contact); } static void -contact_manager_removed_cb (EmpathyContactList *list, +contact_manager_removed_cb (EmpathyTpContactList *list, GossipContact *contact, EmpathyContactManager *manager) { - g_signal_emit (manager, signals[CONTACT_REMOVED], 0, contact); + g_signal_emit_by_name (manager, "contact-removed", contact); } static void -contact_manager_destroy_cb (EmpathyContactList *list, +contact_manager_destroy_cb (EmpathyTpContactList *list, EmpathyContactManager *manager) { EmpathyContactManagerPriv *priv; @@ -475,7 +469,7 @@ contact_manager_destroy_cb (EmpathyContactList *list, priv = GET_PRIV (manager); - account = empathy_contact_list_get_account (list); + account = empathy_tp_contact_list_get_account (list); gossip_debug (DEBUG_DOMAIN, "Removing account: %s", mc_account_get_display_name (account)); @@ -496,22 +490,22 @@ contact_manager_destroy_cb (EmpathyContactList *list, static void contact_manager_rename_group_foreach (McAccount *account, - EmpathyContactList *list, + EmpathyTpContactList *list, ContactManagerRenameGroupData *data) { - empathy_contact_list_rename_group (list, - data->old_group, - data->new_group); + empathy_tp_contact_list_rename_group (list, + data->old_group, + data->new_group); } static void -contact_manager_get_groups_foreach (McAccount *account, - EmpathyContactList *list, - GList **all_groups) +contact_manager_get_groups_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **all_groups) { GList *groups, *l; - groups = empathy_contact_list_get_groups (list); + groups = empathy_tp_contact_list_get_groups (list); for (l = groups; l; l = l->next) { if (!g_list_find_custom (*all_groups, l->data, @@ -526,13 +520,13 @@ contact_manager_get_groups_foreach (McAccount *account, } static void -contact_manager_get_contacts_foreach (McAccount *account, - EmpathyContactList *list, - GList **contacts) +contact_manager_get_contacts_foreach (McAccount *account, + EmpathyTpContactList *list, + GList **contacts) { GList *l; - l = empathy_contact_list_get_contacts (list); + l = empathy_contact_list_get_contacts (EMPATHY_CONTACT_LIST (list)); *contacts = g_list_concat (*contacts, l); } diff --git a/libempathy/empathy-contact-manager.h b/libempathy/empathy-contact-manager.h index 95876dbf3..db893b486 100644 --- a/libempathy/empathy-contact-manager.h +++ b/libempathy/empathy-contact-manager.h @@ -28,7 +28,7 @@ #include <libmissioncontrol/mc-account.h> #include "gossip-contact.h" -#include "empathy-contact-list.h" +#include "empathy-tp-contact-list.h" G_BEGIN_DECLS @@ -53,26 +53,17 @@ struct _EmpathyContactManagerClass { GType empathy_contact_manager_get_type (void) G_GNUC_CONST; EmpathyContactManager *empathy_contact_manager_new (void); -void empathy_contact_manager_setup (EmpathyContactManager *manager); -EmpathyContactList * empathy_contact_manager_get_list (EmpathyContactManager *manager, +EmpathyTpContactList * empathy_contact_manager_get_list (EmpathyContactManager *manager, McAccount *account); -GossipContact * empathy_contact_manager_get_own (EmpathyContactManager *manager, +GossipContact * empathy_contact_manager_get_user (EmpathyContactManager *manager, McAccount *account); -GossipContact * empathy_contact_manager_find (EmpathyContactManager *manager, - const gchar *id); GossipContact * empathy_contact_manager_create (EmpathyContactManager *manager, McAccount *account, const gchar *id); -void empathy_contact_manager_add (EmpathyContactManager *manager, - GossipContact *contact, - const gchar *message); -void empathy_contact_manager_remove (EmpathyContactManager *manager, - GossipContact *contact); void empathy_contact_manager_rename_group (EmpathyContactManager *manager, const gchar *old_group, const gchar *new_group); GList * empathy_contact_manager_get_groups (EmpathyContactManager *manager); -GList * empathy_contact_manager_get_contacts (EmpathyContactManager *manager); G_END_DECLS diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c index 58950f606..73f60491c 100644 --- a/libempathy/empathy-tp-chat.c +++ b/libempathy/empathy-tp-chat.c @@ -22,6 +22,8 @@ #include <config.h> +#include <string.h> + #include <libtelepathy/tp-chan-type-text-gen.h> #include <libtelepathy/tp-chan-iface-chat-state-gen.h> #include <libtelepathy/tp-conn.h> @@ -29,7 +31,7 @@ #include "empathy-tp-chat.h" #include "empathy-contact-manager.h" -#include "empathy-contact-list.h" +#include "empathy-tp-contact-list.h" #include "empathy-marshal.h" #include "gossip-debug.h" #include "gossip-time.h" @@ -41,7 +43,7 @@ #define DEBUG_DOMAIN "TpChat" struct _EmpathyTpChatPriv { - EmpathyContactList *list; + EmpathyTpContactList *list; EmpathyContactManager *manager; McAccount *account; gchar *id; @@ -52,35 +54,51 @@ struct _EmpathyTpChatPriv { DBusGProxy *chat_state_iface; }; -static void empathy_tp_chat_class_init (EmpathyTpChatClass *klass); -static void empathy_tp_chat_init (EmpathyTpChat *chat); -static void tp_chat_finalize (GObject *object); -static void tp_chat_destroy_cb (TpChan *text_chan, - EmpathyTpChat *chat); -static void tp_chat_closed_cb (TpChan *text_chan, - EmpathyTpChat *chat); -static void tp_chat_received_cb (DBusGProxy *text_iface, - guint message_id, - guint timestamp, - guint from_handle, - guint message_type, - guint message_flags, - gchar *message_body, - EmpathyTpChat *chat); -static void tp_chat_sent_cb (DBusGProxy *text_iface, - guint timestamp, - guint message_type, - gchar *message_body, - EmpathyTpChat *chat); -static void tp_chat_state_changed_cb (DBusGProxy *chat_state_iface, - guint handle, - TelepathyChannelChatState state, - EmpathyTpChat *chat); -static void tp_chat_emit_message (EmpathyTpChat *chat, - guint type, - guint timestamp, - guint from_handle, - const gchar *message_body); +static void empathy_tp_chat_class_init (EmpathyTpChatClass *klass); +static void empathy_tp_chat_init (EmpathyTpChat *chat); +static void tp_chat_finalize (GObject *object); +static GObject * tp_chat_constructor (GType type, + guint n_props, + GObjectConstructParam *props); +static void tp_chat_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void tp_chat_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void tp_chat_destroy_cb (TpChan *text_chan, + EmpathyTpChat *chat); +static void tp_chat_closed_cb (TpChan *text_chan, + EmpathyTpChat *chat); +static void tp_chat_received_cb (DBusGProxy *text_iface, + guint message_id, + guint timestamp, + guint from_handle, + guint message_type, + guint message_flags, + gchar *message_body, + EmpathyTpChat *chat); +static void tp_chat_sent_cb (DBusGProxy *text_iface, + guint timestamp, + guint message_type, + gchar *message_body, + EmpathyTpChat *chat); +static void tp_chat_state_changed_cb (DBusGProxy *chat_state_iface, + guint handle, + TelepathyChannelChatState state, + EmpathyTpChat *chat); +static void tp_chat_emit_message (EmpathyTpChat *chat, + guint type, + guint timestamp, + guint from_handle, + const gchar *message_body); +enum { + PROP_0, + PROP_ACCOUNT, + PROP_TP_CHAN +}; enum { MESSAGE_RECEIVED, @@ -99,6 +117,27 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = tp_chat_finalize; + object_class->constructor = tp_chat_constructor; + object_class->get_property = tp_chat_get_property; + object_class->set_property = tp_chat_set_property; + + g_object_class_install_property (object_class, + PROP_ACCOUNT, + g_param_spec_object ("account", + "channel Account", + "The account associated with the channel", + MC_TYPE_ACCOUNT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_TP_CHAN, + g_param_spec_object ("tp-chan", + "telepathy channel", + "The text channel for the chat", + TELEPATHY_CHAN_TYPE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); signals[MESSAGE_RECEIVED] = g_signal_new ("message-received", @@ -182,29 +221,26 @@ tp_chat_finalize (GObject *object) G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object); } -EmpathyTpChat * -empathy_tp_chat_new (McAccount *account, - TpChan *tp_chan) +static GObject * +tp_chat_constructor (GType type, + guint n_props, + GObjectConstructParam *props) { - EmpathyTpChatPriv *priv; - EmpathyTpChat *chat; + GObject *chat; + EmpathyTpChatPriv *priv; - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL); + chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props); - chat = g_object_new (EMPATHY_TYPE_TP_CHAT, NULL); priv = GET_PRIV (chat); - +g_print ("**********tp_chat_constructor"); priv->manager = empathy_contact_manager_new (); - priv->list = empathy_contact_manager_get_list (priv->manager, account); - priv->tp_chan = g_object_ref (tp_chan); - priv->account = g_object_ref (account); + priv->list = empathy_contact_manager_get_list (priv->manager, priv->account); priv->mc = gossip_mission_control_new (); g_object_ref (priv->list); - priv->text_iface = tp_chan_get_interface (tp_chan, + priv->text_iface = tp_chan_get_interface (priv->tp_chan, TELEPATHY_CHAN_IFACE_TEXT_QUARK); - priv->chat_state_iface = tp_chan_get_interface (tp_chan, + priv->chat_state_iface = tp_chan_get_interface (priv->tp_chan, TELEPATHY_CHAN_IFACE_CHAT_STATE_QUARK); g_signal_connect (priv->tp_chan, "destroy", @@ -230,6 +266,62 @@ empathy_tp_chat_new (McAccount *account, return chat; } +static void +tp_chat_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyTpChatPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_ACCOUNT: + g_value_set_object (value, priv->account); + break; + case PROP_TP_CHAN: + g_value_set_object (value, priv->tp_chan); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +tp_chat_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyTpChatPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_ACCOUNT: + priv->account = g_object_ref (g_value_get_object (value)); + break; + case PROP_TP_CHAN: + priv->tp_chan = g_object_ref (g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +EmpathyTpChat * +empathy_tp_chat_new (McAccount *account, + TpChan *tp_chan) +{ + return g_object_new (EMPATHY_TYPE_TP_CHAT, + "account", account, + "tp-chan", tp_chan, + NULL); +} + EmpathyTpChat * empathy_tp_chat_new_with_contact (GossipContact *contact) { @@ -538,7 +630,7 @@ tp_chat_state_changed_cb (DBusGProxy *chat_state_iface, priv = GET_PRIV (chat); - contact = empathy_contact_list_get_from_handle (priv->list, handle); + contact = empathy_tp_contact_list_get_from_handle (priv->list, handle); gossip_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d", gossip_contact_get_name (contact), @@ -564,11 +656,11 @@ tp_chat_emit_message (EmpathyTpChat *chat, priv = GET_PRIV (chat); if (from_handle == 0) { - sender = empathy_contact_list_get_own (priv->list); + sender = empathy_tp_contact_list_get_user (priv->list); g_object_ref (sender); } else { - sender = empathy_contact_list_get_from_handle (priv->list, - from_handle); + sender = empathy_tp_contact_list_get_from_handle (priv->list, + from_handle); } message = gossip_message_new (message_body); diff --git a/libempathy/empathy-tp-chatroom.c b/libempathy/empathy-tp-chatroom.c new file mode 100644 index 000000000..6feef549d --- /dev/null +++ b/libempathy/empathy-tp-chatroom.c @@ -0,0 +1,167 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <libmissioncontrol/mission-control.h> + +#include "empathy-tp-chatroom.h" +#include "empathy-tp-contact-list.h" +#include "empathy-contact-list.h" +#include "empathy-contact-manager.h" +#include "gossip-telepathy-group.h" +#include "gossip-utils.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroomPriv)) + +#define DEBUG_DOMAIN "TpChatroom" + +struct _EmpathyTpChatroomPriv { + EmpathyContactManager *manager; + EmpathyTpContactList *list; + GossipTelepathyGroup *group; +}; + +static void empathy_tp_chatroom_class_init (EmpathyTpChatroomClass *klass); +static void tp_chatroom_iface_init (EmpathyContactListIface *iface); +static void empathy_tp_chatroom_init (EmpathyTpChatroom *chatroom); +static void tp_chatroom_finalize (GObject *object); +static void tp_chatroom_setup (EmpathyContactList *list); +static GossipContact * tp_chatroom_find (EmpathyContactList *list, + const gchar *id); +static void tp_chatroom_add (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); +static void tp_chatroom_remove (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); +static GList * tp_chatroom_get_contacts (EmpathyContactList *list); + +G_DEFINE_TYPE_WITH_CODE (EmpathyTpChatroom, empathy_tp_chatroom, EMPATHY_TYPE_TP_CHAT, + G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, + tp_chatroom_iface_init)); + +static void +empathy_tp_chatroom_class_init (EmpathyTpChatroomClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = tp_chatroom_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyTpChatroomPriv)); +} + +static void +tp_chatroom_iface_init (EmpathyContactListIface *iface) +{ + iface->setup = tp_chatroom_setup; + iface->find = tp_chatroom_find; + iface->add = tp_chatroom_add; + iface->remove = tp_chatroom_remove; + iface->get_contacts = tp_chatroom_get_contacts; +} + +static void +empathy_tp_chatroom_init (EmpathyTpChatroom *chatroom) +{ +} + +static void +tp_chatroom_finalize (GObject *object) +{ + EmpathyTpChatroomPriv *priv; + EmpathyTpChatroom *chatroom; + + chatroom = EMPATHY_TP_CHATROOM (object); + priv = GET_PRIV (chatroom); + + g_object_unref (priv->group); + g_object_unref (priv->manager); + g_object_unref (priv->list); + + G_OBJECT_CLASS (empathy_tp_chatroom_parent_class)->finalize (object); +} + +EmpathyTpChatroom * +empathy_tp_chatroom_new (McAccount *account, + TpChan *tp_chan) +{ + EmpathyTpChatroomPriv *priv; + EmpathyTpChatroom *chatroom; + TpConn *tp_conn; + MissionControl *mc; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL); + + chatroom = g_object_new (EMPATHY_TYPE_TP_CHATROOM, + "account", account, + "tp-chan", tp_chan, + NULL); + + priv = GET_PRIV (chatroom); + + mc = gossip_mission_control_new (); + tp_conn = mission_control_get_connection (mc, account, NULL); + priv->manager = empathy_contact_manager_new (); + priv->group = gossip_telepathy_group_new (tp_chan, tp_conn); + priv->list = empathy_contact_manager_get_list (priv->manager, account); + + g_object_unref (mc); + g_object_unref (tp_conn); + + return chatroom; +} + +static void +tp_chatroom_setup (EmpathyContactList *list) +{ +} + +static GossipContact * +tp_chatroom_find (EmpathyContactList *list, + const gchar *id) +{ + return NULL; +} + +static void +tp_chatroom_add (EmpathyContactList *list, + GossipContact *contact, + const gchar *message) +{ +} + +static void +tp_chatroom_remove (EmpathyContactList *list, + GossipContact *contact, + const gchar *message) +{ +} + +static GList * +tp_chatroom_get_contacts (EmpathyContactList *list) +{ + return NULL; +} + diff --git a/libempathy/empathy-tp-chatroom.h b/libempathy/empathy-tp-chatroom.h new file mode 100644 index 000000000..75ccc58c1 --- /dev/null +++ b/libempathy/empathy-tp-chatroom.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_TP_CHATROOM_H__ +#define __EMPATHY_TP_CHATROOM_H__ + +#include <glib.h> + +#include <libtelepathy/tp-chan.h> + +#include <libmissioncontrol/mc-account.h> + +#include "empathy-tp-chat.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_TP_CHATROOM (empathy_tp_chatroom_get_type ()) +#define EMPATHY_TP_CHATROOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroom)) +#define EMPATHY_TP_CHATROOM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroomClass)) +#define EMPATHY_IS_TP_CHATROOM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_TP_CHATROOM)) +#define EMPATHY_IS_TP_CHATROOM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_TP_CHATROOM)) +#define EMPATHY_TP_CHATROOM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_TP_CHATROOM, EmpathyTpChatroomClass)) + +typedef struct _EmpathyTpChatroom EmpathyTpChatroom; +typedef struct _EmpathyTpChatroomClass EmpathyTpChatroomClass; +typedef struct _EmpathyTpChatroomPriv EmpathyTpChatroomPriv; + +struct _EmpathyTpChatroom { + EmpathyTpChat parent; +}; + +struct _EmpathyTpChatroomClass { + EmpathyTpChatClass parent_class; +}; + +GType empathy_tp_chatroom_get_type (void) G_GNUC_CONST; +EmpathyTpChatroom *empathy_tp_chatroom_new (McAccount *account, + TpChan *tp_chan); + +G_END_DECLS + +#endif /* __EMPATHY_TP_CHATROOM_H__ */ diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c new file mode 100644 index 000000000..43c317e00 --- /dev/null +++ b/libempathy/empathy-tp-contact-list.c @@ -0,0 +1,1822 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * Copyright (C) 2007 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> + +#include <libtelepathy/tp-helpers.h> +#include <libtelepathy/tp-conn.h> +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-chan-type-contact-list-gen.h> +#include <libtelepathy/tp-conn-iface-aliasing-gen.h> +#include <libtelepathy/tp-conn-iface-presence-gen.h> +#include <libtelepathy/tp-conn-iface-avatars-gen.h> + +#include "empathy-tp-contact-list.h" +#include "empathy-contact-list.h" +#include "gossip-telepathy-group.h" +#include "gossip-debug.h" +#include "gossip-utils.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_TP_CONTACT_LIST, EmpathyTpContactListPriv)) + +#define DEBUG_DOMAIN "TpContactList" +#define MAX_AVATAR_REQUESTS 10 + +struct _EmpathyTpContactListPriv { + TpConn *tp_conn; + McAccount *account; + MissionControl *mc; + GossipContact *user_contact; + + GossipTelepathyGroup *known; + GossipTelepathyGroup *publish; + GossipTelepathyGroup *subscribe; + + GHashTable *groups; + GHashTable *contacts; + + DBusGProxy *aliasing_iface; + DBusGProxy *avatars_iface; + DBusGProxy *presence_iface; + + GList *avatar_requests_queue; +}; + +typedef enum { + TP_CONTACT_LIST_TYPE_KNOWN, + TP_CONTACT_LIST_TYPE_PUBLISH, + TP_CONTACT_LIST_TYPE_SUBSCRIBE, + TP_CONTACT_LIST_TYPE_UNKNOWN, + TP_CONTACT_LIST_TYPE_COUNT +} TpContactListType; + +typedef struct { + guint handle; + GList *new_groups; +} TpContactListData; + +typedef struct { + EmpathyTpContactList *list; + guint handle; +} TpContactListAvatarRequestData; + +typedef struct { + EmpathyTpContactList *list; + guint *handles; +} TpContactListAliasesRequestData; + +static void empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass); +static void tp_contact_list_iface_init (EmpathyContactListIface *iface); +static void empathy_tp_contact_list_init (EmpathyTpContactList *list); +static void tp_contact_list_finalize (GObject *object); +static void tp_contact_list_finalize_proxies (EmpathyTpContactList *list); +static void tp_contact_list_setup (EmpathyContactList *list); +static GossipContact * tp_contact_list_find (EmpathyContactList *list, + const gchar *id); +static void tp_contact_list_add (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); +static void tp_contact_list_remove (EmpathyContactList *list, + GossipContact *contact, + const gchar *message); +static GList * tp_contact_list_get_contacts (EmpathyContactList *list); +static void tp_contact_list_contact_removed_foreach (guint handle, + GossipContact *contact, + EmpathyTpContactList *list); +static void tp_contact_list_destroy_cb (DBusGProxy *proxy, + EmpathyTpContactList *list); +static gboolean tp_contact_list_find_foreach (guint handle, + GossipContact *contact, + gchar *id); +static void tp_contact_list_newchannel_cb (DBusGProxy *proxy, + const gchar *object_path, + const gchar *channel_type, + TelepathyHandleType handle_type, + guint channel_handle, + gboolean suppress_handle, + EmpathyTpContactList *list); +static TpContactListType tp_contact_list_get_type (EmpathyTpContactList *list, + TpChan *list_chan); +static void tp_contact_list_contact_added_cb (GossipTelepathyGroup *group, + GArray *handles, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list); +static void tp_contact_list_contact_removed_cb (GossipTelepathyGroup *group, + GArray *handles, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list); +static void tp_contact_list_local_pending_cb (GossipTelepathyGroup *group, + GArray *handles, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list); +static void tp_contact_list_groups_updated_cb (GossipContact *contact, + GParamSpec *param, + EmpathyTpContactList *list); +static void tp_contact_list_subscription_updated_cb (GossipContact *contact, + GParamSpec *param, + EmpathyTpContactList *list); +static void tp_contact_list_name_updated_cb (GossipContact *contact, + GParamSpec *param, + EmpathyTpContactList *list); +static void tp_contact_list_update_groups_foreach (gchar *object_path, + GossipTelepathyGroup *group, + TpContactListData *data); +static GossipTelepathyGroup * tp_contact_list_get_group (EmpathyTpContactList *list, + const gchar *name); +static gboolean tp_contact_list_find_group (gchar *key, + GossipTelepathyGroup *group, + gchar *group_name); +static void tp_contact_list_get_groups_foreach (gchar *key, + GossipTelepathyGroup *group, + GList **groups); +static void tp_contact_list_group_channel_closed_cb (TpChan *channel, + EmpathyTpContactList *list); +static void tp_contact_list_group_members_added_cb (GossipTelepathyGroup *group, + GArray *members, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list); +static void tp_contact_list_group_members_removed_cb (GossipTelepathyGroup *group, + GArray *members, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list); +static void tp_contact_list_get_contacts_foreach (guint handle, + GossipContact *contact, + GList **contacts); +static void tp_contact_list_get_info (EmpathyTpContactList *list, + GArray *handles); +static void tp_contact_list_request_avatar (EmpathyTpContactList *list, + guint handle); +static void tp_contact_list_start_avatar_requests (EmpathyTpContactList *list); +static void tp_contact_list_avatar_update_cb (DBusGProxy *proxy, + guint handle, + gchar *new_token, + EmpathyTpContactList *list); +static void tp_contact_list_request_avatar_cb (DBusGProxy *proxy, + GArray *avatar_data, + gchar *mime_type, + GError *error, + TpContactListAvatarRequestData *data); +static void tp_contact_list_aliases_update_cb (DBusGProxy *proxy, + GPtrArray *handlers, + EmpathyTpContactList *list); +static void tp_contact_list_request_aliases_cb (DBusGProxy *proxy, + gchar **contact_names, + GError *error, + TpContactListAliasesRequestData *data); +static void tp_contact_list_presence_update_cb (DBusGProxy *proxy, + GHashTable *handle_table, + EmpathyTpContactList *list); +static void tp_contact_list_parse_presence_foreach (guint handle, + GValueArray *presence_struct, + EmpathyTpContactList *list); +static void tp_contact_list_presences_table_foreach (const gchar *state_str, + GHashTable *presences_table, + GossipPresence **presence); +static void tp_contact_list_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyTpContactList *list); + +enum { + DESTROY, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; +static guint n_avatar_requests = 0; + +G_DEFINE_TYPE_WITH_CODE (EmpathyTpContactList, empathy_tp_contact_list, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, + tp_contact_list_iface_init)); + +static void +empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = tp_contact_list_finalize; + + signals[DESTROY] = + g_signal_new ("destroy", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); +} + +static void +tp_contact_list_iface_init (EmpathyContactListIface *iface) +{ + iface->setup = tp_contact_list_setup; + iface->find = tp_contact_list_find; + iface->add = tp_contact_list_add; + iface->remove = tp_contact_list_remove; + iface->get_contacts = tp_contact_list_get_contacts; +} + +static void +empathy_tp_contact_list_init (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + + priv = GET_PRIV (list); + + priv->groups = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + priv->contacts = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) g_object_unref); +} + +static void +tp_contact_list_finalize (GObject *object) +{ + EmpathyTpContactListPriv *priv; + EmpathyTpContactList *list; + + list = EMPATHY_TP_CONTACT_LIST (object); + priv = GET_PRIV (list); + + gossip_debug (DEBUG_DOMAIN, "finalize: %p", object); + + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (tp_contact_list_status_changed_cb), + list); + + tp_contact_list_finalize_proxies (list); + + if (priv->tp_conn) { + g_object_unref (priv->tp_conn); + } + + if (priv->known) { + g_object_unref (priv->known); + } + + if (priv->subscribe) { + g_object_unref (priv->subscribe); + } + + if (priv->publish) { + g_object_unref (priv->publish); + } + + g_object_unref (priv->account); + g_object_unref (priv->user_contact); + g_object_unref (priv->mc); + g_hash_table_destroy (priv->groups); + g_hash_table_destroy (priv->contacts); + + G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); +} + +EmpathyTpContactList * +empathy_tp_contact_list_new (McAccount *account) +{ + EmpathyTpContactListPriv *priv; + EmpathyTpContactList *list; + MissionControl *mc; + guint handle; + GError *error = NULL; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + + mc = gossip_mission_control_new (); + + if (mission_control_get_connection_status (mc, account, NULL) != 0) { + /* The account is not connected, nothing to do. */ + return NULL; + } + + list = g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, NULL); + priv = GET_PRIV (list); + + priv->tp_conn = mission_control_get_connection (mc, account, NULL); + priv->account = g_object_ref (account); + priv->mc = mc; + + g_signal_connect (priv->tp_conn, "destroy", + G_CALLBACK (tp_contact_list_destroy_cb), + list); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (tp_contact_list_status_changed_cb), + list, NULL); + + priv->aliasing_iface = tp_conn_get_interface (priv->tp_conn, + TELEPATHY_CONN_IFACE_ALIASING_QUARK); + priv->avatars_iface = tp_conn_get_interface (priv->tp_conn, + TELEPATHY_CONN_IFACE_AVATARS_QUARK); + priv->presence_iface = tp_conn_get_interface (priv->tp_conn, + TELEPATHY_CONN_IFACE_PRESENCE_QUARK); + + if (priv->aliasing_iface) { + dbus_g_proxy_connect_signal (priv->aliasing_iface, + "AliasesChanged", + G_CALLBACK (tp_contact_list_aliases_update_cb), + list, NULL); + } + + if (priv->avatars_iface) { + dbus_g_proxy_connect_signal (priv->avatars_iface, + "AvatarUpdated", + G_CALLBACK (tp_contact_list_avatar_update_cb), + list, NULL); + } + + if (priv->presence_iface) { + dbus_g_proxy_connect_signal (priv->presence_iface, + "PresenceUpdate", + G_CALLBACK (tp_contact_list_presence_update_cb), + list, NULL); + } + + /* Get our own handle and contact */ + if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn), + &handle, &error)) { + gossip_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } else { + priv->user_contact = empathy_tp_contact_list_get_from_handle (list, handle); + } + + return list; +} + +static void +tp_contact_list_setup (EmpathyContactList *list) +{ + EmpathyTpContactListPriv *priv; + GPtrArray *channels; + GError *error = NULL; + guint i; + + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + + priv = GET_PRIV (list); + + gossip_debug (DEBUG_DOMAIN, "setup contact list: %p", list); + + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", + G_CALLBACK (tp_contact_list_newchannel_cb), + list, NULL); + + /* Get existing channels */ + if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn), + &channels, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Failed to get list of open channels: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return; + } + + for (i = 0; channels->len > i; i++) { + GValueArray *chan_struct; + const gchar *object_path; + const gchar *chan_iface; + TelepathyHandleType handle_type; + guint handle; + + chan_struct = g_ptr_array_index (channels, i); + object_path = g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); + chan_iface = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); + handle_type = g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); + handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); + + tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), + object_path, chan_iface, + handle_type, handle, + FALSE, + EMPATHY_TP_CONTACT_LIST (list)); + + g_value_array_free (chan_struct); + } + + g_ptr_array_free (channels, TRUE); +} + +static GossipContact * +tp_contact_list_find (EmpathyContactList *list, + const gchar *id) +{ + EmpathyTpContactListPriv *priv; + GossipContact *contact; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + + priv = GET_PRIV (list); + + contact = g_hash_table_find (priv->contacts, + (GHRFunc) tp_contact_list_find_foreach, + (gchar*) id); + + return NULL; +} + +static void +tp_contact_list_add (EmpathyContactList *list, + GossipContact *contact, + const gchar *message) +{ + EmpathyTpContactListPriv *priv; + guint handle; + + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + + priv = GET_PRIV (list); + + handle = gossip_contact_get_handle (contact); + gossip_telepathy_group_add_member (priv->subscribe, handle, message); +} + +static void +tp_contact_list_remove (EmpathyContactList *list, + GossipContact *contact, + const gchar *message) +{ + EmpathyTpContactListPriv *priv; + guint handle; + + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + + priv = GET_PRIV (list); + + handle = gossip_contact_get_handle (contact); + gossip_telepathy_group_remove_member (priv->subscribe, handle, message); + gossip_telepathy_group_remove_member (priv->publish, handle, message); + gossip_telepathy_group_remove_member (priv->known, handle, message); +} + +static GList * +tp_contact_list_get_contacts (EmpathyContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *contacts = NULL; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + + priv = GET_PRIV (list); + + /* FIXME: we should only return contacts that are in the contact list */ + g_hash_table_foreach (priv->contacts, + (GHFunc) tp_contact_list_get_contacts_foreach, + &contacts); + + return contacts; +} + +McAccount * +empathy_tp_contact_list_get_account (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + + priv = GET_PRIV (list); + + return priv->account; +} + +GossipContact * +empathy_tp_contact_list_get_user (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + + priv = GET_PRIV (list); + + return priv->user_contact; +} + +GossipContact * +empathy_tp_contact_list_get_from_id (EmpathyTpContactList *list, + const gchar *id) +{ + EmpathyTpContactListPriv *priv; + GossipContact *contact; + const gchar *contact_ids[] = {id, NULL}; + GArray *handles; + guint handle; + GError *error = NULL; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + g_return_val_if_fail (id != NULL, NULL); + + priv = GET_PRIV (list); + + contact = tp_contact_list_find (EMPATHY_CONTACT_LIST (list), id); + if (contact) { + return contact; + } + + /* The id is unknown, requests a new handle */ + if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), + TP_HANDLE_TYPE_CONTACT, + contact_ids, + &handles, &error)) { + gossip_debug (DEBUG_DOMAIN, + "RequestHandle for %s failed: %s", id, + error ? error->message : "No error given"); + g_clear_error (&error); + return 0; + } + + handle = g_array_index(handles, guint, 0); + g_array_free (handles, TRUE); + + return empathy_tp_contact_list_get_from_handle (list, handle); +} + +GossipContact * +empathy_tp_contact_list_get_from_handle (EmpathyTpContactList *list, + guint handle) +{ + GossipContact *contact; + GArray *handles; + GList *contacts; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + + handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (handles, handle); + + contacts = empathy_tp_contact_list_get_from_handles (list, handles); + g_array_free (handles, TRUE); + + if (!contacts) { + return NULL; + } + + contact = contacts->data; + g_list_free (contacts); + + return contact; +} + +GList * +empathy_tp_contact_list_get_from_handles (EmpathyTpContactList *list, + GArray *handles) +{ + EmpathyTpContactListPriv *priv; + gchar **handles_names; + gchar **id; + GArray *new_handles; + GList *contacts = NULL; + guint i; + GError *error = NULL; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + g_return_val_if_fail (handles != NULL, NULL); + + priv = GET_PRIV (list); + + /* Search all handles we already have */ + new_handles = g_array_new (FALSE, FALSE, sizeof (guint)); + for (i = 0; i < handles->len; i++) { + GossipContact *contact; + guint handle; + + handle = g_array_index (handles, guint, i); + contact = g_hash_table_lookup (priv->contacts, + GUINT_TO_POINTER (handle)); + + if (contact) { + contacts = g_list_prepend (contacts, + g_object_ref (contact)); + } else { + g_array_append_val (new_handles, handle); + } + } + + if (new_handles->len == 0) { + return contacts; + } + + /* Holds all handles we don't have yet. + * FIXME: We should release them at some point. */ + if (!tp_conn_hold_handles (DBUS_G_PROXY (priv->tp_conn), + TP_HANDLE_TYPE_CONTACT, + new_handles, &error)) { + gossip_debug (DEBUG_DOMAIN, + "HoldHandles Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_array_free (new_handles, TRUE); + return contacts; + } + + /* Get the IDs of all new handles */ + if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), + TP_HANDLE_TYPE_CONTACT, + new_handles, + &handles_names, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "InspectHandle Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_array_free (new_handles, TRUE); + return contacts; + } + + /* Create contact objects */ + for (i = 0, id = handles_names; *id && i < new_handles->len; id++, i++) { + GossipContact *contact; + guint handle; + + handle = g_array_index (new_handles, guint, i); + contact = g_object_new (GOSSIP_TYPE_CONTACT, + "account", priv->account, + "id", *id, + "handle", handle, + NULL); + + g_signal_connect (contact, "notify::groups", + G_CALLBACK (tp_contact_list_groups_updated_cb), + list); + g_signal_connect (contact, "notify::subscription", + G_CALLBACK (tp_contact_list_subscription_updated_cb), + list); + g_signal_connect (contact, "notify::name", + G_CALLBACK (tp_contact_list_name_updated_cb), + list); + + gossip_debug (DEBUG_DOMAIN, "new contact created: %s (%d)", + *id, handle); + + g_hash_table_insert (priv->contacts, + GUINT_TO_POINTER (handle), + contact); + + contacts = g_list_prepend (contacts, g_object_ref (contact)); + } + + tp_contact_list_get_info (list, new_handles); + + g_array_free (new_handles, TRUE); + g_strfreev (handles_names); + + return contacts; +} + +void +empathy_tp_contact_list_rename_group (EmpathyTpContactList *list, + const gchar *old_group, + const gchar *new_group) +{ + EmpathyTpContactListPriv *priv; + GossipTelepathyGroup *group; + GArray *members; + + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + g_return_if_fail (old_group != NULL); + g_return_if_fail (new_group != NULL); + + priv = GET_PRIV (list); + + group = g_hash_table_find (priv->groups, + (GHRFunc) tp_contact_list_find_group, + (gchar*) old_group); + if (!group) { + /* The group doesn't exists on this account */ + return; + } + + gossip_debug (DEBUG_DOMAIN, "rename group %s to %s", group, new_group); + + /* Remove all members from the old group */ + members = gossip_telepathy_group_get_members (group); + gossip_telepathy_group_remove_members (group, members, ""); + tp_contact_list_group_members_removed_cb (group, members, + 0, + TP_CHANNEL_GROUP_CHANGE_REASON_NONE, + NULL, list); + g_hash_table_remove (priv->groups, + gossip_telepathy_group_get_object_path (group)); + + /* Add all members to the new group */ + group = tp_contact_list_get_group (list, new_group); + if (group) { + gossip_telepathy_group_add_members (group, members, ""); + } +} + +GList * +empathy_tp_contact_list_get_groups (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *groups = NULL; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + + priv = GET_PRIV (list); + + g_hash_table_foreach (priv->groups, + (GHFunc) tp_contact_list_get_groups_foreach, + &groups); + + groups = g_list_sort (groups, (GCompareFunc) strcmp); + + return groups; +} + +static void +tp_contact_list_finalize_proxies (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + + priv = GET_PRIV (list); + + if (priv->tp_conn) { + g_signal_handlers_disconnect_by_func (priv->tp_conn, + tp_contact_list_destroy_cb, + list); + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", + G_CALLBACK (tp_contact_list_newchannel_cb), + list); + } + + if (priv->aliasing_iface) { + dbus_g_proxy_disconnect_signal (priv->aliasing_iface, + "AliasesChanged", + G_CALLBACK (tp_contact_list_aliases_update_cb), + list); + } + + if (priv->avatars_iface) { + dbus_g_proxy_disconnect_signal (priv->avatars_iface, + "AvatarUpdated", + G_CALLBACK (tp_contact_list_avatar_update_cb), + list); + } + + if (priv->presence_iface) { + dbus_g_proxy_disconnect_signal (priv->presence_iface, + "PresenceUpdate", + G_CALLBACK (tp_contact_list_presence_update_cb), + list); + } +} + +static void +tp_contact_list_destroy_cb (DBusGProxy *proxy, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + + priv = GET_PRIV (list); + + gossip_debug (DEBUG_DOMAIN, "Connection destroyed... " + "Account disconnected or CM crashed"); + + /* DBus proxies should NOT be used anymore */ + g_object_unref (priv->tp_conn); + priv->tp_conn = NULL; + priv->aliasing_iface = NULL; + priv->avatars_iface = NULL; + priv->presence_iface = NULL; + + /* Remove all contacts */ + g_hash_table_foreach (priv->contacts, + (GHFunc) tp_contact_list_contact_removed_foreach, + list); + g_hash_table_remove_all (priv->contacts); + + /* Tell the world to not use us anymore */ + g_signal_emit (list, signals[DESTROY], 0); +} + +static void +tp_contact_list_contact_removed_foreach (guint handle, + GossipContact *contact, + EmpathyTpContactList *list) +{ + g_signal_handlers_disconnect_by_func (contact, + tp_contact_list_groups_updated_cb, + list); + g_signal_handlers_disconnect_by_func (contact, + tp_contact_list_subscription_updated_cb, + list); + g_signal_handlers_disconnect_by_func (contact, + tp_contact_list_name_updated_cb, + list); + + g_signal_emit_by_name (list, "contact-removed", contact); +} + +static void +tp_contact_list_block_contact (EmpathyTpContactList *list, + GossipContact *contact) +{ + g_signal_handlers_block_by_func (contact, + tp_contact_list_groups_updated_cb, + list); + g_signal_handlers_block_by_func (contact, + tp_contact_list_subscription_updated_cb, + list); + g_signal_handlers_block_by_func (contact, + tp_contact_list_name_updated_cb, + list); +} + +static void +tp_contact_list_unblock_contact (EmpathyTpContactList *list, + GossipContact *contact) +{ + g_signal_handlers_unblock_by_func (contact, + tp_contact_list_groups_updated_cb, + list); + g_signal_handlers_unblock_by_func (contact, + tp_contact_list_subscription_updated_cb, + list); + g_signal_handlers_unblock_by_func (contact, + tp_contact_list_name_updated_cb, + list); +} + +static gboolean +tp_contact_list_find_foreach (guint handle, + GossipContact *contact, + gchar *id) +{ + if (strcmp (gossip_contact_get_id (contact), id) == 0) { + return TRUE; + } + + return FALSE; +} + +static void +tp_contact_list_newchannel_cb (DBusGProxy *proxy, + const gchar *object_path, + const gchar *channel_type, + TelepathyHandleType handle_type, + guint channel_handle, + gboolean suppress_handle, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GossipTelepathyGroup *group; + TpChan *new_chan; + const gchar *bus_name; + GArray *members; + + priv = GET_PRIV (list); + + if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) != 0 || + suppress_handle) { + return; + } + + bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)); + new_chan = tp_chan_new (tp_get_bus (), + bus_name, + object_path, + channel_type, handle_type, channel_handle); + + if (handle_type == TP_HANDLE_TYPE_LIST) { + TpContactListType list_type; + + list_type = tp_contact_list_get_type (list, new_chan); + if (list_type == TP_CONTACT_LIST_TYPE_UNKNOWN) { + gossip_debug (DEBUG_DOMAIN, "Unknown contact list channel"); + g_object_unref (new_chan); + return; + } + + gossip_debug (DEBUG_DOMAIN, "New contact list channel of type: %d", + list_type); + + group = gossip_telepathy_group_new (new_chan, priv->tp_conn); + + switch (list_type) { + case TP_CONTACT_LIST_TYPE_KNOWN: + if (priv->known) { + g_object_unref (priv->known); + } + priv->known = group; + break; + case TP_CONTACT_LIST_TYPE_PUBLISH: + if (priv->publish) { + g_object_unref (priv->publish); + } + priv->publish = group; + break; + case TP_CONTACT_LIST_TYPE_SUBSCRIBE: + if (priv->subscribe) { + g_object_unref (priv->subscribe); + } + priv->subscribe = group; + break; + default: + g_assert_not_reached (); + } + + /* Connect and setup the new contact-list group */ + if (list_type == TP_CONTACT_LIST_TYPE_KNOWN || + list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { + g_signal_connect (group, "members-added", + G_CALLBACK (tp_contact_list_contact_added_cb), + list); + g_signal_connect (group, "members-removed", + G_CALLBACK (tp_contact_list_contact_removed_cb), + list); + + members = gossip_telepathy_group_get_members (group); + tp_contact_list_contact_added_cb (group, members, 0, + TP_CHANNEL_GROUP_CHANGE_REASON_NONE, + NULL, list); + g_array_free (members, TRUE); + } + if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { + GPtrArray *info; + GArray *pending; + guint i; + + g_signal_connect (group, "local-pending", + G_CALLBACK (tp_contact_list_local_pending_cb), + list); + + info = gossip_telepathy_group_get_local_pending_members_with_info (group); + + if (!info) { + /* This happens with butterfly because + * GetLocalPendingMembersWithInfo is not + * implemented */ + g_object_unref (new_chan); + return; + } + + pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); + for (i = 0; info->len > i; i++) { + GValueArray *pending_struct; + guint member; + guint invitor; + guint reason; + const gchar *message; + + pending_struct = g_ptr_array_index (info, i); + member = g_value_get_uint (g_value_array_get_nth (pending_struct, 0)); + invitor = g_value_get_uint (g_value_array_get_nth (pending_struct, 1)); + reason = g_value_get_uint (g_value_array_get_nth (pending_struct, 2)); + message = g_value_get_string (g_value_array_get_nth (pending_struct, 3)); + + g_array_insert_val (pending, 0, member); + + tp_contact_list_local_pending_cb (group, pending, + invitor, + reason, + message, list); + + g_value_array_free (pending_struct); + } + + g_ptr_array_free (info, TRUE); + g_array_free (pending, TRUE); + } + } + else if (handle_type == TP_HANDLE_TYPE_GROUP) { + const gchar *object_path; + + object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (new_chan)); + if (g_hash_table_lookup (priv->groups, object_path)) { + g_object_unref (new_chan); + return; + } + + group = gossip_telepathy_group_new (new_chan, priv->tp_conn); + + gossip_debug (DEBUG_DOMAIN, "New server-side group channel: %s", + gossip_telepathy_group_get_name (group)); + + dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed", + G_CALLBACK + (tp_contact_list_group_channel_closed_cb), + list, NULL); + + g_hash_table_insert (priv->groups, g_strdup (object_path), group); + g_signal_connect (group, "members-added", + G_CALLBACK (tp_contact_list_group_members_added_cb), + list); + g_signal_connect (group, "members-removed", + G_CALLBACK (tp_contact_list_group_members_removed_cb), + list); + + members = gossip_telepathy_group_get_members (group); + tp_contact_list_group_members_added_cb (group, members, 0, + TP_CHANNEL_GROUP_CHANGE_REASON_NONE, + NULL, list); + g_array_free (members, TRUE); + } + + g_object_unref (new_chan); +} + +static TpContactListType +tp_contact_list_get_type (EmpathyTpContactList *list, + TpChan *list_chan) +{ + EmpathyTpContactListPriv *priv; + GArray *handles; + gchar **handle_name; + TpContactListType list_type; + GError *error = NULL; + + priv = GET_PRIV (list); + + handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (handles, list_chan->handle); + + if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), + TP_HANDLE_TYPE_LIST, + handles, + &handle_name, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "InspectHandle Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_array_free (handles, TRUE); + return TP_CONTACT_LIST_TYPE_UNKNOWN; + } + + if (strcmp (*handle_name, "subscribe") == 0) { + list_type = TP_CONTACT_LIST_TYPE_SUBSCRIBE; + } else if (strcmp (*handle_name, "publish") == 0) { + list_type = TP_CONTACT_LIST_TYPE_PUBLISH; + } else if (strcmp (*handle_name, "known") == 0) { + list_type = TP_CONTACT_LIST_TYPE_KNOWN; + } else { + list_type = TP_CONTACT_LIST_TYPE_UNKNOWN; + } + + g_strfreev (handle_name); + g_array_free (handles, TRUE); + + return list_type; +} + +static void +tp_contact_list_contact_added_cb (GossipTelepathyGroup *group, + GArray *handles, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *added_list, *l; + + priv = GET_PRIV (list); + + added_list = empathy_tp_contact_list_get_from_handles (list, handles); + + for (l = added_list; l; l = l->next) { + GossipContact *contact; + + contact = GOSSIP_CONTACT (l->data); + tp_contact_list_block_contact (list, contact); + gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_BOTH); + tp_contact_list_unblock_contact (list, contact); + + g_signal_emit_by_name (list, "contact-added", contact); + + g_object_unref (contact); + } + + g_list_free (added_list); +} + +static void +tp_contact_list_contact_removed_cb (GossipTelepathyGroup *group, + GArray *handles, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *removed_list, *l; + + priv = GET_PRIV (list); + + removed_list = empathy_tp_contact_list_get_from_handles (list, handles); + + for (l = removed_list; l; l = l->next) { + GossipContact *contact; + guint handle; + + contact = GOSSIP_CONTACT (l->data); + + handle = gossip_contact_get_handle (contact); + g_hash_table_remove (priv->contacts, GUINT_TO_POINTER (handle)); + + g_signal_emit_by_name (list, "contact-removed", contact); + + g_object_unref (contact); + } + + g_list_free (removed_list); +} + +static void +tp_contact_list_local_pending_cb (GossipTelepathyGroup *group, + GArray *handles, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *pending_list, *l; + + priv = GET_PRIV (list); + + pending_list = empathy_tp_contact_list_get_from_handles (list, handles); + + for (l = pending_list; l; l = l->next) { + GossipContact *contact; + + contact = GOSSIP_CONTACT (l->data); + + /* FIXME: Is that the correct way ? */ + tp_contact_list_block_contact (list, contact); + gossip_contact_set_subscription (contact, GOSSIP_SUBSCRIPTION_FROM); + tp_contact_list_unblock_contact (list, contact); + g_signal_emit_by_name (list, "contact-added", contact); + + g_object_unref (contact); + } + + g_list_free (pending_list); +} + +static void +tp_contact_list_groups_updated_cb (GossipContact *contact, + GParamSpec *param, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + TpContactListData data; + GList *groups, *l; + + priv = GET_PRIV (list); + + /* Make sure all groups are created */ + groups = gossip_contact_get_groups (contact); + for (l = groups; l; l = l->next) { + tp_contact_list_get_group (list, l->data); + } + + data.handle = gossip_contact_get_handle (contact); + data.new_groups = groups; + + g_hash_table_foreach (priv->groups, + (GHFunc) tp_contact_list_update_groups_foreach, + &data); +} + +static void +tp_contact_list_subscription_updated_cb (GossipContact *contact, + GParamSpec *param, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GossipSubscription subscription; + guint handle; + + priv = GET_PRIV (list); + + subscription = gossip_contact_get_subscription (contact); + handle = gossip_contact_get_handle (contact); + + /* FIXME: what to do here, I'm a bit lost... */ + if (subscription) { + gossip_telepathy_group_add_member (priv->publish, handle, ""); + } else { + gossip_telepathy_group_remove_member (priv->publish, handle, ""); + } +} + +static void +tp_contact_list_name_updated_cb (GossipContact *contact, + GParamSpec *param, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GHashTable *new_alias; + const gchar *new_name; + guint handle; + GError *error = NULL; + + priv = GET_PRIV (list); + + handle = gossip_contact_get_handle (contact); + new_name = gossip_contact_get_name (contact); + + gossip_debug (DEBUG_DOMAIN, "renaming handle %d to %s", + handle, new_name); + + new_alias = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_free); + + g_hash_table_insert (new_alias, + GUINT_TO_POINTER (handle), + g_strdup (new_name)); + + if (!tp_conn_iface_aliasing_set_aliases (priv->aliasing_iface, + new_alias, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't rename contact: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + + g_hash_table_destroy (new_alias); +} + +static void +tp_contact_list_update_groups_foreach (gchar *object_path, + GossipTelepathyGroup *group, + TpContactListData *data) +{ + gboolean is_member; + gboolean found = FALSE; + const gchar *group_name; + GList *l; + + is_member = gossip_telepathy_group_is_member (group, data->handle); + group_name = gossip_telepathy_group_get_name (group); + + for (l = data->new_groups; l; l = l->next) { + if (strcmp (group_name, l->data) == 0) { + found = TRUE; + break; + } + } + + if (is_member && !found) { + /* We are no longer member of this group */ + gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'", + data->handle, group_name); + gossip_telepathy_group_remove_member (group, data->handle, ""); + } + + if (!is_member && found) { + /* We are now member of this group */ + gossip_debug (DEBUG_DOMAIN, "Contact %d added to group '%s'", + data->handle, group_name); + gossip_telepathy_group_add_member (group, data->handle, ""); + } +} + +static GossipTelepathyGroup * +tp_contact_list_get_group (EmpathyTpContactList *list, + const gchar *name) +{ + EmpathyTpContactListPriv *priv; + GossipTelepathyGroup *group; + TpChan *group_channel; + GArray *handles; + guint group_handle; + char *group_object_path; + const char *names[2] = {name, NULL}; + GError *error = NULL; + + priv = GET_PRIV (list); + + group = g_hash_table_find (priv->groups, + (GHRFunc) tp_contact_list_find_group, + (gchar*) name); + if (group) { + return group; + } + + gossip_debug (DEBUG_DOMAIN, "creating new group: %s", name); + + if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), + TP_HANDLE_TYPE_GROUP, + names, + &handles, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't request the creation of a new handle for group: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return NULL; + } + group_handle = g_array_index (handles, guint, 0); + g_array_free (handles, TRUE); + + if (!tp_conn_request_channel (DBUS_G_PROXY (priv->tp_conn), + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_GROUP, + group_handle, + FALSE, + &group_object_path, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't request the creation of a new group channel: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return NULL; + } + + group_channel = tp_chan_new (tp_get_bus (), + dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)), + group_object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_GROUP, + group_handle); + + dbus_g_proxy_connect_signal (DBUS_G_PROXY (group_channel), + "Closed", + G_CALLBACK + (tp_contact_list_group_channel_closed_cb), + list, + NULL); + + group = gossip_telepathy_group_new (group_channel, priv->tp_conn); + g_hash_table_insert (priv->groups, group_object_path, group); + g_signal_connect (group, "members-added", + G_CALLBACK (tp_contact_list_group_members_added_cb), + list); + g_signal_connect (group, "members-removed", + G_CALLBACK (tp_contact_list_group_members_removed_cb), + list); + + return group; +} + +static gboolean +tp_contact_list_find_group (gchar *key, + GossipTelepathyGroup *group, + gchar *group_name) +{ + if (strcmp (group_name, gossip_telepathy_group_get_name (group)) == 0) { + return TRUE; + } + + return FALSE; +} + +static void +tp_contact_list_get_groups_foreach (gchar *key, + GossipTelepathyGroup *group, + GList **groups) +{ + const gchar *name; + + name = gossip_telepathy_group_get_name (group); + *groups = g_list_append (*groups, g_strdup (name)); +} + +static void +tp_contact_list_group_channel_closed_cb (TpChan *channel, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + + priv = GET_PRIV (list); + + g_hash_table_remove (priv->groups, + dbus_g_proxy_get_path (DBUS_G_PROXY (channel))); +} + +static void +tp_contact_list_group_members_added_cb (GossipTelepathyGroup *group, + GArray *members, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *added_list, *l; + const gchar *group_name; + + priv = GET_PRIV (list); + + group_name = gossip_telepathy_group_get_name (group); + added_list = empathy_tp_contact_list_get_from_handles (list, members); + + for (l = added_list; l; l = l->next) { + GossipContact *contact; + GList *contact_groups; + + contact = GOSSIP_CONTACT (l->data); + contact_groups = gossip_contact_get_groups (contact); + + if (!g_list_find_custom (contact_groups, + group_name, + (GCompareFunc) strcmp)) { + gossip_debug (DEBUG_DOMAIN, "Contact %s added to group '%s'", + gossip_contact_get_name (contact), + group_name); + contact_groups = g_list_append (contact_groups, + g_strdup (group_name)); + tp_contact_list_block_contact (list, contact); + gossip_contact_set_groups (contact, contact_groups); + tp_contact_list_unblock_contact (list, contact); + } + + g_object_unref (contact); + } + + g_list_free (added_list); +} + +static void +tp_contact_list_group_members_removed_cb (GossipTelepathyGroup *group, + GArray *members, + guint actor_handle, + guint reason, + const gchar *message, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + GList *removed_list, *l; + const gchar *group_name; + + priv = GET_PRIV (list); + + group_name = gossip_telepathy_group_get_name (group); + removed_list = empathy_tp_contact_list_get_from_handles (list, members); + + for (l = removed_list; l; l = l->next) { + GossipContact *contact; + GList *contact_groups; + GList *to_remove; + + /* FIXME: Does it leak ? */ + contact = GOSSIP_CONTACT (l->data); + contact_groups = gossip_contact_get_groups (contact); + contact_groups = g_list_copy (contact_groups); + + to_remove = g_list_find_custom (contact_groups, + group_name, + (GCompareFunc) strcmp); + if (to_remove) { + gossip_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'", + gossip_contact_get_handle (contact), + group_name); + contact_groups = g_list_remove_link (contact_groups, + to_remove); + tp_contact_list_block_contact (list, contact); + gossip_contact_set_groups (contact, contact_groups); + tp_contact_list_unblock_contact (list, contact); + } + + g_list_free (contact_groups); + + g_object_unref (contact); + } + + g_list_free (removed_list); +} + +static void +tp_contact_list_get_contacts_foreach (guint handle, + GossipContact *contact, + GList **contacts) +{ + *contacts = g_list_append (*contacts, g_object_ref (contact)); +} + +static void +tp_contact_list_get_info (EmpathyTpContactList *list, + GArray *handles) +{ + EmpathyTpContactListPriv *priv; + GError *error = NULL; + + priv = GET_PRIV (list); + + if (priv->presence_iface) { + /* FIXME: We should use GetPresence instead */ + if (!tp_conn_iface_presence_request_presence (priv->presence_iface, + handles, &error)) { + gossip_debug (DEBUG_DOMAIN, + "Could not request presences: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + } + + if (priv->aliasing_iface) { + TpContactListAliasesRequestData *data; + + data = g_slice_new (TpContactListAliasesRequestData); + data->list = list; + data->handles = g_memdup (handles->data, handles->len * sizeof (guint)); + + tp_conn_iface_aliasing_request_aliases_async (priv->aliasing_iface, + handles, + (tp_conn_iface_aliasing_request_aliases_reply) + tp_contact_list_request_aliases_cb, + data); + } + + if (priv->avatars_iface) { + guint i; + + for (i = 0; i < handles->len; i++) { + guint handle; + + handle = g_array_index (handles, gint, i); + tp_contact_list_request_avatar (list, handle); + } + } +} + +static void +tp_contact_list_request_avatar (EmpathyTpContactList *list, + guint handle) +{ + EmpathyTpContactListPriv *priv; + + priv = GET_PRIV (list); + + /* We queue avatar requests to not send too many dbus async + * calls at once. If we don't we reach the dbus's limit of + * pending calls */ + priv->avatar_requests_queue = g_list_append (priv->avatar_requests_queue, + GUINT_TO_POINTER (handle)); + tp_contact_list_start_avatar_requests (list); +} + +static void +tp_contact_list_start_avatar_requests (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + TpContactListAvatarRequestData *data; + + priv = GET_PRIV (list); + + while (n_avatar_requests < MAX_AVATAR_REQUESTS && + priv->avatar_requests_queue) { + data = g_slice_new (TpContactListAvatarRequestData); + data->list = list; + data->handle = GPOINTER_TO_UINT (priv->avatar_requests_queue->data); + + n_avatar_requests++; + priv->avatar_requests_queue = g_list_remove (priv->avatar_requests_queue, + priv->avatar_requests_queue->data); + + tp_conn_iface_avatars_request_avatar_async (priv->avatars_iface, + data->handle, + (tp_conn_iface_avatars_request_avatar_reply) + tp_contact_list_request_avatar_cb, + data); + } +} + +static void +tp_contact_list_avatar_update_cb (DBusGProxy *proxy, + guint handle, + gchar *new_token, + EmpathyTpContactList *list) +{ + gossip_debug (DEBUG_DOMAIN, "Changing avatar for %d to %s", + handle, new_token); + + tp_contact_list_request_avatar (list, handle); +} + +static void +tp_contact_list_request_avatar_cb (DBusGProxy *proxy, + GArray *avatar_data, + gchar *mime_type, + GError *error, + TpContactListAvatarRequestData *data) +{ + GossipContact *contact; + + contact = empathy_tp_contact_list_get_from_handle (data->list, data->handle); + + if (error) { + gossip_debug (DEBUG_DOMAIN, "Error requesting avatar for %s: %s", + gossip_contact_get_name (contact), + error ? error->message : "No error given"); + } else { + GossipAvatar *avatar; + + avatar = gossip_avatar_new (avatar_data->data, + avatar_data->len, + mime_type); + tp_contact_list_block_contact (data->list, contact); + gossip_contact_set_avatar (contact, avatar); + tp_contact_list_unblock_contact (data->list, contact); + gossip_avatar_unref (avatar); + } + + n_avatar_requests--; + tp_contact_list_start_avatar_requests (data->list); + + g_slice_free (TpContactListAvatarRequestData, data); +} + +static void +tp_contact_list_aliases_update_cb (DBusGProxy *proxy, + GPtrArray *renamed_handlers, + EmpathyTpContactList *list) +{ + gint i; + + for (i = 0; renamed_handlers->len > i; i++) { + guint handle; + const gchar *alias; + GValueArray *renamed_struct; + GossipContact *contact; + + renamed_struct = g_ptr_array_index (renamed_handlers, i); + handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0)); + alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1)); + + if (alias && *alias == '\0') { + alias = NULL; + } + + contact = empathy_tp_contact_list_get_from_handle (list, handle); + tp_contact_list_block_contact (list, contact); + gossip_contact_set_name (contact, alias); + tp_contact_list_unblock_contact (list, contact); + + gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (update cb)", + handle, alias); + } +} + +static void +tp_contact_list_request_aliases_cb (DBusGProxy *proxy, + gchar **contact_names, + GError *error, + TpContactListAliasesRequestData *data) +{ + guint i = 0; + gchar **name; + + for (name = contact_names; *name && !error; name++) { + GossipContact *contact; + + contact = empathy_tp_contact_list_get_from_handle (data->list, + data->handles[i]); + tp_contact_list_block_contact (data->list, contact); + gossip_contact_set_name (contact, *name); + tp_contact_list_unblock_contact (data->list, contact); + + gossip_debug (DEBUG_DOMAIN, "contact %d renamed to %s (request cb)", + data->handles[i], *name); + + i++; + } + + g_free (data->handles); + g_slice_free (TpContactListAliasesRequestData, data); +} + +static void +tp_contact_list_presence_update_cb (DBusGProxy *proxy, + GHashTable *handle_table, + EmpathyTpContactList *list) +{ + g_hash_table_foreach (handle_table, + (GHFunc) tp_contact_list_parse_presence_foreach, + list); +} + +static void +tp_contact_list_parse_presence_foreach (guint handle, + GValueArray *presence_struct, + EmpathyTpContactList *list) +{ + GHashTable *presences_table; + GossipContact *contact; + GossipPresence *presence = NULL; + + contact = empathy_tp_contact_list_get_from_handle (list, handle); + presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1)); + + g_hash_table_foreach (presences_table, + (GHFunc) tp_contact_list_presences_table_foreach, + &presence); + + gossip_debug (DEBUG_DOMAIN, "Presence changed for %s (%d) to %s (%d)", + gossip_contact_get_name (contact), + handle, + presence ? gossip_presence_get_status (presence) : "unset", + presence ? gossip_presence_get_state (presence) : MC_PRESENCE_UNSET); + + tp_contact_list_block_contact (list, contact); + gossip_contact_set_presence (contact, presence); + tp_contact_list_unblock_contact (list, contact); +} + +static void +tp_contact_list_presences_table_foreach (const gchar *state_str, + GHashTable *presences_table, + GossipPresence **presence) +{ + McPresence state; + const GValue *message; + + state = gossip_presence_state_from_str (state_str); + if ((state == MC_PRESENCE_UNSET) || (state == MC_PRESENCE_OFFLINE)) { + return; + } + + if (*presence) { + g_object_unref (*presence); + *presence = NULL; + } + + *presence = gossip_presence_new (); + gossip_presence_set_state (*presence, state); + + message = g_hash_table_lookup (presences_table, "message"); + if (message != NULL) { + gossip_presence_set_status (*presence, + g_value_get_string (message)); + } +} + +static void +tp_contact_list_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + McAccount *account; + + priv = GET_PRIV (list); + + account = mc_account_lookup (unique_name); + if (status != TP_CONN_STATUS_DISCONNECTED || + !gossip_account_equal (account, priv->account)) { + g_object_unref (account); + return; + } + + /* We are disconnected, do just like if the connection was destroyed */ + g_signal_handlers_disconnect_by_func (priv->tp_conn, + tp_contact_list_destroy_cb, + list); + tp_contact_list_destroy_cb (DBUS_G_PROXY (priv->tp_conn), list); + + g_object_unref (account); +} + diff --git a/libempathy/empathy-tp-contact-list.h b/libempathy/empathy-tp-contact-list.h new file mode 100644 index 000000000..580741328 --- /dev/null +++ b/libempathy/empathy-tp-contact-list.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * Copyright (C) 2007 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_TP_CONTACT_LIST_H__ +#define __EMPATHY_TP_CONTACT_LIST_H__ + +#include <glib.h> +#include <libmissioncontrol/mc-account.h> + +#include "gossip-contact.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_TP_CONTACT_LIST (empathy_tp_contact_list_get_type ()) +#define EMPATHY_TP_CONTACT_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyTpContactList)) +#define EMPATHY_TP_CONTACT_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_LIST, EmpathyTpContactListClass)) +#define EMPATHY_IS_TP_CONTACT_LIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_LIST)) +#define EMPATHY_IS_TP_CONTACT_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_LIST)) +#define EMPATHY_TP_CONTACT_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_LIST, EmpathyTpContactListClass)) + +typedef struct _EmpathyTpContactList EmpathyTpContactList; +typedef struct _EmpathyTpContactListClass EmpathyTpContactListClass; +typedef struct _EmpathyTpContactListPriv EmpathyTpContactListPriv; + +struct _EmpathyTpContactList { + GObject parent; +}; + +struct _EmpathyTpContactListClass { + GObjectClass parent_class; +}; + +GType empathy_tp_contact_list_get_type (void) G_GNUC_CONST; +EmpathyTpContactList * empathy_tp_contact_list_new (McAccount *account); +McAccount * empathy_tp_contact_list_get_account (EmpathyTpContactList *list); +GossipContact * empathy_tp_contact_list_get_user (EmpathyTpContactList *list); +GossipContact * empathy_tp_contact_list_get_from_id (EmpathyTpContactList *list, + const gchar *id); +GossipContact * empathy_tp_contact_list_get_from_handle (EmpathyTpContactList *list, + guint handle); +GList * empathy_tp_contact_list_get_from_handles (EmpathyTpContactList *list, + GArray *handles); +void empathy_tp_contact_list_rename_group (EmpathyTpContactList *list, + const gchar *old_group, + const gchar *new_group); +GList * empathy_tp_contact_list_get_groups (EmpathyTpContactList *list); + +G_END_DECLS + +#endif /* __EMPATHY_TP_CONTACT_LIST_H__ */ diff --git a/libempathy/gossip-contact.c b/libempathy/gossip-contact.c index 68be90cd9..51015f365 100644 --- a/libempathy/gossip-contact.c +++ b/libempathy/gossip-contact.c @@ -30,6 +30,7 @@ #include "gossip-contact.h" #include "gossip-utils.h" #include "gossip-debug.h" +#include "empathy-contact-manager.h" #define DEBUG_DOMAIN "Contact" @@ -654,6 +655,24 @@ gossip_contact_get_status (GossipContact *contact) return _("Offline"); } +GossipContact * +gossip_contact_get_user (GossipContact *contact) +{ + GossipContactPriv *priv; + EmpathyContactManager *manager; + GossipContact *user_contact; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + manager = empathy_contact_manager_new (); + user_contact = empathy_contact_manager_get_user (manager, priv->account); + g_object_unref (manager); + + return user_contact; +} + gboolean gossip_contact_equal (gconstpointer v1, gconstpointer v2) diff --git a/libempathy/gossip-contact.h b/libempathy/gossip-contact.h index 0b2032949..3dc9bc76d 100644 --- a/libempathy/gossip-contact.h +++ b/libempathy/gossip-contact.h @@ -89,6 +89,7 @@ gboolean gossip_contact_is_online (GossipContact gboolean gossip_contact_is_in_group (GossipContact *contact, const gchar *group); const gchar * gossip_contact_get_status (GossipContact *contact); +GossipContact * gossip_contact_get_user (GossipContact *contact); gboolean gossip_contact_equal (gconstpointer v1, gconstpointer v2); guint gossip_contact_hash (gconstpointer key); diff --git a/libempathy/gossip-utils.c b/libempathy/gossip-utils.c index b8cc59ab7..6ab70ce4a 100644 --- a/libempathy/gossip-utils.c +++ b/libempathy/gossip-utils.c @@ -432,23 +432,6 @@ gossip_account_equal (gconstpointer a, return g_str_equal (name_a, name_b); } -GossipContact * -gossip_get_own_contact_from_contact (GossipContact *contact) -{ - EmpathyContactManager *manager; - McAccount *account; - GossipContact *own_contact; - - g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); - - manager = empathy_contact_manager_new (); - account = gossip_contact_get_account (contact); - own_contact = empathy_contact_manager_get_own (manager, account); - g_object_unref (manager); - - return own_contact; -} - MissionControl * gossip_mission_control_new (void) { diff --git a/libempathy/gossip-utils.h b/libempathy/gossip-utils.h index e1e9baeed..052dfb781 100644 --- a/libempathy/gossip-utils.h +++ b/libempathy/gossip-utils.h @@ -88,7 +88,6 @@ gboolean gossip_g_value_equal (const GValue *value1, guint gossip_account_hash (gconstpointer key); gboolean gossip_account_equal (gconstpointer a, gconstpointer b); -GossipContact * gossip_get_own_contact_from_contact (GossipContact *contact); MissionControl *gossip_mission_control_new (void); G_END_DECLS |