diff options
Diffstat (limited to 'libempathy')
34 files changed, 7519 insertions, 0 deletions
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am new file mode 100644 index 000000000..30d40c595 --- /dev/null +++ b/libempathy/Makefile.am @@ -0,0 +1,54 @@ +AM_CPPFLAGS = \ + -I. \ + -I$(top_srcdir) \ + -DDATADIR=\""$(datadir)"\" \ + -DLOCALEDIR=\""$(datadir)/locale"\" \ + $(EMPATHY_CFLAGS) \ + $(WARN_CFLAGS) + +BUILT_SOURCES = \ + empathy-marshal.h \ + empathy-marshal.c \ + empathy-chandler-glue.h + +noinst_LTLIBRARIES = libempathy.la + +libempathy_la_SOURCES = \ + gossip-conf.c gossip-conf.h \ + gossip-contact.c gossip-contact.h \ + gossip-avatar.c gossip-avatar.h \ + gossip-time.c gossip-time.h \ + gossip-presence.c gossip-presence.h \ + gossip-telepathy-group.c gossip-telepathy-group.h \ + gossip-paths.c gossip-paths.h \ + gossip-debug.c gossip-debug.h \ + gossip-utils.c gossip-utils.h \ + gossip-message.c gossip-message.h \ + empathy-session.c empathy-session.h \ + empathy-contact-list.c empathy-contact-list.h \ + empathy-contact-manager.c empathy-contact-manager.h \ + empathy-tp-chat.c empathy-tp-chat.h \ + empathy-chandler.c empathy-chandler.h \ + empathy-marshal-main.c + +libempathy_la_LIBADD = \ + $(EMPATHY_LIBS) + +libempathy_includedir = $(includedir)/empathy/ + +%-marshal.h: %-marshal.list Makefile.am + $(GLIB_GENMARSHAL) --header --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.h + +%-marshal.c: %-marshal.list Makefile.am + $(GLIB_GENMARSHAL) --body --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.c + +%-marshal-main.c: %-marshal.c %-marshal.h + +empathy-chandler-glue.h: empathy-chandler.xml + $(LIBTOOL) --mode=execute $(DBUS_BINDING_TOOL) --prefix=empathy_chandler --mode=glib-server --output=$@ $< + +EXTRA_DIST = \ + empathy-marshal.list \ + empathy-chandler.xml + +CLEANFILES = $(BUILT_SOURCES) diff --git a/libempathy/empathy-chandler.c b/libempathy/empathy-chandler.c new file mode 100644 index 000000000..adc415a93 --- /dev/null +++ b/libempathy/empathy-chandler.c @@ -0,0 +1,150 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + * + */ + +#include <config.h> + +#include <dbus/dbus-glib.h> + +#include <libtelepathy/tp-helpers.h> +#include <libtelepathy/tp-conn.h> +#include <libtelepathy/tp-chan.h> + +#include "empathy-chandler.h" +#include "gossip-debug.h" +#include "empathy-marshal.h" + +#define DEBUG_DOMAIN "EmpathyChandler" + +static gboolean empathy_chandler_handle_channel (EmpathyChandler *chandler, + const gchar *bus_name, + const gchar *connection, + const gchar *channel_type, + const gchar *channel, + guint handle_type, + guint handle, + GError **error); + +#include "empathy-chandler-glue.h" + +enum { + NEW_CHANNEL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (EmpathyChandler, empathy_chandler, G_TYPE_OBJECT) + +static void +empathy_chandler_class_init (EmpathyChandlerClass *klass) +{ + signals[NEW_CHANNEL] = + g_signal_new ("new-channel", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + empathy_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, + 2, TELEPATHY_CONN_TYPE, TELEPATHY_CHAN_TYPE); +} + +static void +empathy_chandler_init (EmpathyChandler *chandler) +{ +} + +EmpathyChandler * +empathy_chandler_new (const gchar *bus_name, + const gchar *object_path) +{ + static gboolean initialized = FALSE; + EmpathyChandler *chandler; + DBusGProxy *proxy; + guint result; + GError *error = NULL; + + if (!initialized) { + dbus_g_object_type_install_info (EMPATHY_TYPE_CHANDLER, + &dbus_glib_empathy_chandler_object_info); + initialized = TRUE; + } + + proxy = dbus_g_proxy_new_for_name (tp_get_bus (), + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + if (!dbus_g_proxy_call (proxy, "RequestName", &error, + G_TYPE_STRING, bus_name, + G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID)) { + gossip_debug (DEBUG_DOMAIN, + "Failed to request name: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + + return NULL; + } + g_object_unref (proxy); + + chandler = g_object_new (EMPATHY_TYPE_CHANDLER, NULL); + dbus_g_connection_register_g_object (tp_get_bus (), + object_path, + G_OBJECT (chandler)); + + return chandler; +} + +static gboolean +empathy_chandler_handle_channel (EmpathyChandler *chandler, + const gchar *bus_name, + const gchar *connection, + const gchar *channel_type, + const gchar *channel, + guint handle_type, + guint handle, + GError **error) +{ + TpChan *tp_chan; + TpConn *tp_conn; + + tp_conn = tp_conn_new (tp_get_bus (), + bus_name, + connection); + + tp_chan = tp_chan_new (tp_get_bus(), + bus_name, + channel, + channel_type, + handle_type, + handle); + + g_signal_emit (chandler, signals[NEW_CHANNEL], 0, tp_conn, tp_chan); + + g_object_unref (tp_chan); + g_object_unref (tp_conn); + + return TRUE; +} + diff --git a/libempathy/empathy-chandler.h b/libempathy/empathy-chandler.h new file mode 100644 index 000000000..ca271613c --- /dev/null +++ b/libempathy/empathy-chandler.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + * + */ + +#ifndef __EMPATHY_CHANDLER_H__ +#define __EMPATHY_CHANDLER_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CHANDLER (empathy_chandler_get_type ()) +#define EMPATHY_CHANDLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CHANDLER, EmpathyChandler)) +#define EMPATHY_CHANDLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_CHANDLER, EmpathyChandlerClass)) +#define EMPATHY_IS_CHANDLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CHANDLER)) +#define EMPATHY_IS_CHANDLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CHANDLER)) +#define EMPATHY_CHANDLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CHANDLER, EmpathyChandlerClass)) + +typedef struct _EmpathyChandler EmpathyChandler; +typedef struct _EmpathyChandlerClass EmpathyChandlerClass; + +struct _EmpathyChandler { + GObject parent; +}; + +struct _EmpathyChandlerClass { + GObjectClass parent_class; +}; + +GType empathy_chandler_get_type (void) G_GNUC_CONST; +EmpathyChandler *empathy_chandler_new (const gchar *bus_name, + const gchar *object_path); + +G_END_DECLS + +#endif /* __EMPATHY_CHANDLER_H__ */ diff --git a/libempathy/empathy-chandler.xml b/libempathy/empathy-chandler.xml new file mode 100644 index 000000000..0fb264e5f --- /dev/null +++ b/libempathy/empathy-chandler.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<node name="/"> + <interface name="org.freedesktop.Telepathy.ChannelHandler"> + <method name="HandleChannel"> + <arg direction="in" type="s" name="bus_name" /> + <arg direction="in" type="o" name="connection" /> + <arg direction="in" type="s" name="channel_type" /> + <arg direction="in" type="o" name="channel" /> + <arg direction="in" type="u" name="handle_type" /> + <arg direction="in" type="u" name="handle" /> + </method> + </interface> +</node> diff --git a/libempathy/empathy-contact-list.c b/libempathy/empathy-contact-list.c new file mode 100644 index 000000000..2cc486de9 --- /dev/null +++ b/libempathy/empathy-contact-list.c @@ -0,0 +1,1793 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#include <config.h> + +#include <string.h> + +#include <libtelepathy/tp-conn.h> +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-helpers.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-contact-list.h" +#include "empathy-session.h" +#include "gossip-debug.h" +#include "gossip-telepathy-group.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; + GossipContact *own_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 { + 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 GossipPresenceState contact_list_presence_state_from_str (const gchar *str); + +enum { + CONTACT_ADDED, + CONTACT_REMOVED, + DESTROY, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; +static guint n_avatar_requests = 0; + +G_DEFINE_TYPE (EmpathyContactList, empathy_contact_list, G_TYPE_OBJECT); + +static void +empathy_contact_list_class_init (EmpathyContactListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = contact_list_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); + + 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); + + if (priv->tp_conn) { + contact_list_finalize_proxies (list); + 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->own_contact); + 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; + TpConn *tp_conn; + guint handle; + GError *error = NULL; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + + mc = empathy_session_get_mission_control (); + + if (mission_control_get_connection_status (mc, account, NULL) != 0) { + /* The account is not connected, nothing to do. */ + return NULL; + } + + tp_conn = mission_control_get_connection (mc, account, NULL); + g_return_val_if_fail (tp_conn != NULL, NULL); + + list = g_object_new (EMPATHY_TYPE_CONTACT_LIST, NULL); + priv = GET_PRIV (list); + + priv->tp_conn = tp_conn; + priv->account = g_object_ref (account); + + g_signal_connect (priv->tp_conn, "destroy", + G_CALLBACK (contact_list_destroy_cb), + list); + + 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); + + 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); + } + + 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) +{ + 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); + + return NULL; +} + +void +empathy_contact_list_add (EmpathyContactList *list, + guint handle, + 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; + } + + /* 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) +{ + 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, ""); + } +} + +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); + + 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); + } + + 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)", + gossip_contact_get_name (contact), + handle); + + contact_list_block_contact (list, contact); + if (presence) { + gossip_contact_add_presence (contact, presence); + g_object_unref (presence); + } else { + g_object_set (contact, "presences", NULL, NULL); + } + contact_list_unblock_contact (list, contact); +} + +static void +contact_list_presences_table_foreach (const gchar *state_str, + GHashTable *presences_table, + GossipPresence **presence) +{ + GossipPresenceState state; + const GValue *message; + + if (*presence) { + g_object_unref (*presence); + *presence = NULL; + } + + state = contact_list_presence_state_from_str (state_str); + if (state == GOSSIP_PRESENCE_STATE_UNAVAILABLE) { + return; + } + + *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)); + } + + gossip_presence_set_resource (*presence, ""); +} + +static GossipPresenceState +contact_list_presence_state_from_str (const gchar *str) +{ + if (strcmp (str, "available") == 0) { + return GOSSIP_PRESENCE_STATE_AVAILABLE; + } else if ((strcmp (str, "dnd") == 0) || (strcmp (str, "busy") == 0)) { + return GOSSIP_PRESENCE_STATE_BUSY; + } else if ((strcmp (str, "away") == 0) || (strcmp (str, "brb") == 0)) { + return GOSSIP_PRESENCE_STATE_AWAY; + } else if (strcmp (str, "xa") == 0) { + return GOSSIP_PRESENCE_STATE_EXT_AWAY; + } else if (strcmp (str, "hidden") == 0) { + return GOSSIP_PRESENCE_STATE_HIDDEN; + } else if (strcmp (str, "offline") == 0) { + return GOSSIP_PRESENCE_STATE_UNAVAILABLE; + } else if (strcmp (str, "chat") == 0) { + /* We don't support chat, so treat it like available. */ + return GOSSIP_PRESENCE_STATE_AVAILABLE; + } + + return GOSSIP_PRESENCE_STATE_AVAILABLE; +} + diff --git a/libempathy/empathy-contact-list.h b/libempathy/empathy-contact-list.h new file mode 100644 index 000000000..db8b36e39 --- /dev/null +++ b/libempathy/empathy-contact-list.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#ifndef __EMPATHY_CONTACT_LIST_H__ +#define __EMPATHY_CONTACT_LIST_H__ + +#include <glib.h> +#include <libmissioncontrol/mc-account.h> + +#include "gossip-contact.h" + +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)) + +typedef struct _EmpathyContactList EmpathyContactList; +typedef struct _EmpathyContactListClass EmpathyContactListClass; +typedef struct _EmpathyContactListPriv EmpathyContactListPriv; + +struct _EmpathyContactList { + GObject parent; +}; + +struct _EmpathyContactListClass { + GObjectClass parent_class; +}; + +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); + +G_END_DECLS + +#endif /* __EMPATHY_CONTACT_LIST_H__ */ diff --git a/libempathy/empathy-contact-manager.c b/libempathy/empathy-contact-manager.c new file mode 100644 index 000000000..15fb82eed --- /dev/null +++ b/libempathy/empathy-contact-manager.c @@ -0,0 +1,550 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#include <config.h> + +#include <string.h> + +#include <libtelepathy/tp-constants.h> + +#include "empathy-contact-manager.h" +#include "empathy-session.h" +#include "gossip-utils.h" +#include "gossip-debug.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv)) + +#define DEBUG_DOMAIN "ContactManager" + +struct _EmpathyContactManagerPriv { + GHashTable *lists; + gboolean setup; +}; + +typedef struct { + const gchar *old_group; + const gchar *new_group; +} ContactManagerRenameGroupData; + +typedef struct { + GossipContact *contact; + 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) +{ + GObjectClass *object_class = G_OBJECT_CLASS (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 +empathy_contact_manager_init (EmpathyContactManager *manager) +{ + EmpathyContactManagerPriv *priv; + MissionControl *mc; + GSList *accounts, *l; + + priv = GET_PRIV (manager); + + priv->lists = g_hash_table_new_full (gossip_account_hash, + gossip_account_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) g_object_unref); + + mc = empathy_session_get_mission_control (); + + dbus_g_proxy_connect_signal (DBUS_G_PROXY (mc), "AccountStatusChanged", + G_CALLBACK (contact_manager_status_changed_cb), + manager, NULL); + + /* Get ContactList for existing connections */ + accounts = mission_control_get_online_connections (mc, NULL); + for (l = accounts; l; l = l->next) { + McAccount *account; + + account = l->data; + contact_manager_add_account (manager, account); + + g_object_unref (account); + } + g_slist_free (accounts); +} + +static void +contact_manager_finalize (GObject *object) +{ + EmpathyContactManagerPriv *priv; + + priv = GET_PRIV (object); + + g_hash_table_destroy (priv->lists); +} + +EmpathyContactManager * +empathy_contact_manager_new (void) +{ + return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL); +} + +void +empathy_contact_manager_setup (EmpathyContactManager *manager) +{ + EmpathyContactManagerPriv *priv; + + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); + + priv = GET_PRIV (manager); + + if (priv->setup) { + /* Already done */ + return; + } + + g_hash_table_foreach (priv->lists, + (GHFunc) contact_manager_setup_foreach, + manager); + + priv->setup = TRUE; +} + +EmpathyContactList * +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); +} + +GossipContact * +empathy_contact_manager_get_own (EmpathyContactManager *manager, + McAccount *account) +{ + EmpathyContactManagerPriv *priv; + EmpathyContactList *list; + + 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); + + list = g_hash_table_lookup (priv->lists, account); + + if (!list) { + return NULL; + } + + return empathy_contact_list_get_own (list); +} + +GossipContact * +empathy_contact_manager_create (EmpathyContactManager *manager, + McAccount *account, + const gchar *id) +{ + EmpathyContactManagerPriv *priv; + EmpathyContactList *list; + + 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); + + list = g_hash_table_lookup (priv->lists, account); + + if (!list) { + return NULL; + } + + return empathy_contact_list_get_from_id (list, id); +} + +GossipContact * +empathy_contact_manager_find (EmpathyContactManager *manager, + const gchar *id) +{ + EmpathyContactManagerPriv *priv; + ContactManagerFindData data; + + 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_find (priv->lists, + (GHRFunc) contact_manager_find_foreach, + &data); + + return data.contact; +} + +void +empathy_contact_manager_add (EmpathyContactManager *manager, + GossipContact *contact, + const gchar *message) +{ + EmpathyContactManagerPriv *priv; + EmpathyContactList *list; + McAccount *account; + guint handle; + + 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); + handle = gossip_contact_get_handle (contact); + list = g_hash_table_lookup (priv->lists, account); + + if (list) { + empathy_contact_list_add (list, handle, message); + } +} + +void +empathy_contact_manager_remove (EmpathyContactManager *manager, + GossipContact *contact) +{ + EmpathyContactManagerPriv *priv; + EmpathyContactList *list; + McAccount *account; + guint handle; + + 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); + handle = gossip_contact_get_handle (contact); + list = g_hash_table_lookup (priv->lists, account); + + if (list) { + empathy_contact_list_remove (list, handle); + } +} + +void +empathy_contact_manager_rename_group (EmpathyContactManager *manager, + const gchar *old_group, + const gchar *new_group) +{ + EmpathyContactManagerPriv *priv; + ContactManagerRenameGroupData data; + + g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager)); + g_return_if_fail (old_group != NULL); + g_return_if_fail (new_group != NULL); + + priv = GET_PRIV (manager); + + data.old_group = old_group; + data.new_group = new_group; + + g_hash_table_foreach (priv->lists, + (GHFunc) contact_manager_rename_group_foreach, + &data); +} + +GList * +empathy_contact_manager_get_groups (EmpathyContactManager *manager) +{ + EmpathyContactManagerPriv *priv; + GList *groups = 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_groups_foreach, + &groups); + + 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, + EmpathyContactManager *manager) +{ + empathy_contact_list_setup (list); +} + +static gboolean +contact_manager_find_foreach (McAccount *account, + EmpathyContactList *list, + ContactManagerFindData *data) +{ + data->contact = empathy_contact_list_find (list, data->id); + + if (data->contact) { + return TRUE; + } + + return FALSE; +} + +static void +contact_manager_add_account (EmpathyContactManager *manager, + McAccount *account) +{ + EmpathyContactManagerPriv *priv; + EmpathyContactList *list; + + priv = GET_PRIV (manager); + + if (g_hash_table_lookup (priv->lists, account)) { + return; + } + + gossip_debug (DEBUG_DOMAIN, "Adding new account: %s", + mc_account_get_display_name (account)); + + list = empathy_contact_list_new (account); + if (!list) { + return; + } + + g_hash_table_insert (priv->lists, g_object_ref (account), list); + + /* Connect signals */ + g_signal_connect (list, "contact-added", + G_CALLBACK (contact_manager_added_cb), + manager); + g_signal_connect (list, "contact-removed", + G_CALLBACK (contact_manager_removed_cb), + manager); + g_signal_connect (list, "destroy", + G_CALLBACK (contact_manager_destroy_cb), + manager); + + if (priv->setup) { + empathy_contact_list_setup (list); + } +} + +static void +contact_manager_added_cb (EmpathyContactList *list, + GossipContact *contact, + EmpathyContactManager *manager) +{ + g_signal_emit (manager, signals[CONTACT_ADDED], 0, contact); +} + +static void +contact_manager_removed_cb (EmpathyContactList *list, + GossipContact *contact, + EmpathyContactManager *manager) +{ + g_signal_emit (manager, signals[CONTACT_REMOVED], 0, contact); +} + +static void +contact_manager_destroy_cb (EmpathyContactList *list, + EmpathyContactManager *manager) +{ + EmpathyContactManagerPriv *priv; + McAccount *account; + + priv = GET_PRIV (manager); + + account = empathy_contact_list_get_account (list); + + gossip_debug (DEBUG_DOMAIN, "Removing account: %s", + mc_account_get_display_name (account)); + + /* Disconnect signals from the list */ + g_signal_handlers_disconnect_by_func (list, + contact_manager_added_cb, + manager); + g_signal_handlers_disconnect_by_func (list, + contact_manager_removed_cb, + manager); + g_signal_handlers_disconnect_by_func (list, + contact_manager_destroy_cb, + manager); + + g_hash_table_remove (priv->lists, account); +} + +static void +contact_manager_rename_group_foreach (McAccount *account, + EmpathyContactList *list, + ContactManagerRenameGroupData *data) +{ + empathy_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) +{ + GList *groups, *l; + + groups = empathy_contact_list_get_groups (list); + for (l = groups; l; l = l->next) { + if (!g_list_find_custom (*all_groups, + l->data, + (GCompareFunc) strcmp)) { + *all_groups = g_list_append (*all_groups, + g_strdup (l->data)); + } + g_free (l->data); + } + + g_list_free (groups); +} + +static void +contact_manager_get_contacts_foreach (McAccount *account, + EmpathyContactList *list, + GList **contacts) +{ + GList *l; + + l = empathy_contact_list_get_contacts (list); + *contacts = g_list_concat (*contacts, l); +} + +static void +contact_manager_status_changed_cb (MissionControl *mc, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *unique_name, + EmpathyContactManager *manager) +{ + EmpathyContactManagerPriv *priv; + McAccount *account; + + priv = GET_PRIV (manager); + + if (status != TP_CONN_STATUS_CONNECTED) { + /* We only care about newly connected accounts */ + return; + } + + account = mc_account_lookup (unique_name); + contact_manager_add_account (manager, account); + + g_object_unref (account); +} + diff --git a/libempathy/empathy-contact-manager.h b/libempathy/empathy-contact-manager.h new file mode 100644 index 000000000..ca6cb3fe5 --- /dev/null +++ b/libempathy/empathy-contact-manager.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#ifndef __EMPATHY_CONTACT_MANAGER_H__ +#define __EMPATHY_CONTACT_MANAGER_H__ + +#include <glib.h> + +#include <libmissioncontrol/mc-account.h> + +#include "gossip-contact.h" +#include "empathy-contact-list.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CONTACT_MANAGER (empathy_contact_manager_get_type ()) +#define EMPATHY_CONTACT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManager)) +#define EMPATHY_CONTACT_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerClass)) +#define EMPATHY_IS_CONTACT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_MANAGER)) +#define EMPATHY_IS_CONTACT_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_MANAGER)) +#define EMPATHY_CONTACT_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerClass)) + +typedef struct _EmpathyContactManager EmpathyContactManager; +typedef struct _EmpathyContactManagerClass EmpathyContactManagerClass; +typedef struct _EmpathyContactManagerPriv EmpathyContactManagerPriv; + +struct _EmpathyContactManager { + GObject parent; +}; + +struct _EmpathyContactManagerClass { + GObjectClass parent_class; +}; + +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, + McAccount *account); +GossipContact * empathy_contact_manager_get_own (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 + +#endif /* __EMPATHY_CONTACT_MANAGER_H__ */ diff --git a/libempathy/empathy-marshal-main.c b/libempathy/empathy-marshal-main.c new file mode 100644 index 000000000..f3ab95f54 --- /dev/null +++ b/libempathy/empathy-marshal-main.c @@ -0,0 +1,2 @@ +#include "empathy-marshal.h" +#include "empathy-marshal.c" diff --git a/libempathy/empathy-marshal.list b/libempathy/empathy-marshal.list new file mode 100644 index 000000000..6035b9451 --- /dev/null +++ b/libempathy/empathy-marshal.list @@ -0,0 +1,3 @@ +VOID:POINTER,UINT,UINT,STRING +VOID:OBJECT,UINT +VOID:OBJECT,OBJECT diff --git a/libempathy/empathy-session.c b/libempathy/empathy-session.c new file mode 100644 index 000000000..fefda7694 --- /dev/null +++ b/libempathy/empathy-session.c @@ -0,0 +1,161 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <glib.h> + +#include <libtelepathy/tp-helpers.h> + +#include <libmissioncontrol/mc-account-monitor.h> + +#include "empathy-session.h" +#include "gossip-debug.h" + +#define DEBUG_DOMAIN "Session" + +static void session_start_mission_control (void); +static void session_error_cb (MissionControl *mc, + GError *error, + gpointer data); +static void session_account_enabled_cb (McAccountMonitor *monitor, + gchar *unique_name, + gpointer user_data); +static void session_service_ended_cb (MissionControl *mc, + gpointer user_data); + +static MissionControl *mission_control = NULL; +static EmpathyContactManager *contact_manager = NULL; + +void +empathy_session_connect (void) +{ + MissionControl *mc; + McAccountMonitor *monitor; + static gboolean started = FALSE; + + if (started) { + return; + } + + mc = empathy_session_get_mission_control (); + monitor = mc_account_monitor_new (); + + g_signal_connect (monitor, "account-enabled", + G_CALLBACK (session_account_enabled_cb), + NULL); + g_signal_connect (mc, "ServiceEnded", + G_CALLBACK (session_service_ended_cb), + NULL); + + g_object_unref (monitor); + session_start_mission_control (); + + started = TRUE; +} + +void +empathy_session_finalize (void) +{ + if (mission_control) { + g_object_unref (mission_control); + mission_control = NULL; + } + + if (contact_manager) { + g_object_unref (contact_manager); + contact_manager = NULL; + } +} + +MissionControl * +empathy_session_get_mission_control (void) +{ + if (!mission_control) { + mission_control = mission_control_new (tp_get_bus ()); + } + + return mission_control; +} + +EmpathyContactManager * +empathy_session_get_contact_manager (void) +{ + if (!contact_manager) { + contact_manager = empathy_contact_manager_new (); + } + + return contact_manager; +} + +static void +session_start_mission_control (void) +{ + MissionControl *mc; + McPresence presence; + + mc = empathy_session_get_mission_control (); + presence = mission_control_get_presence_actual (mc, NULL); + + if (presence != MC_PRESENCE_UNSET && + presence != MC_PRESENCE_OFFLINE) { + /* MC is already running and online, nothing to do */ + return; + } + + gossip_debug (DEBUG_DOMAIN, "Starting Mission Control..."); + + /* FIXME: Save/Restore status message */ + mission_control_set_presence (mc, MC_PRESENCE_AVAILABLE, + NULL, + (McCallback) session_error_cb, + NULL); + + mission_control_connect_all_with_default_presence (mc, + (McCallback) session_error_cb, + NULL); +} + +static void +session_error_cb (MissionControl *mc, + GError *error, + gpointer data) +{ + if (error) { + gossip_debug (DEBUG_DOMAIN, "Error: %s", error->message); + } +} + +static void +session_account_enabled_cb (McAccountMonitor *monitor, + gchar *unique_name, + gpointer user_data) +{ + gossip_debug (DEBUG_DOMAIN, "Account enabled: %s", unique_name); + session_start_mission_control (); +} + +static void +session_service_ended_cb (MissionControl *mc, + gpointer user_data) +{ + gossip_debug (DEBUG_DOMAIN, "Mission Control stopped"); +} + diff --git a/libempathy/empathy-session.h b/libempathy/empathy-session.h new file mode 100644 index 000000000..0d3a2dce1 --- /dev/null +++ b/libempathy/empathy-session.h @@ -0,0 +1,38 @@ +#include <libtelepathy/tp-helpers.h>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EMPATHY_SESSION_H__ +#define __EMPATHY_SESSION_H__ + +#include <glib.h> + +#include <libmissioncontrol/mission-control.h> +#include "empathy-contact-manager.h" + +G_BEGIN_DECLS + +void empathy_session_connect (void); +void empathy_session_finalize (void); +MissionControl * empathy_session_get_mission_control (void); +EmpathyContactManager *empathy_session_get_contact_manager (void); + +G_END_DECLS + +#endif /* __EMPATHY_MISSION_CONTROL_H__ */ diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c new file mode 100644 index 000000000..ad00711d9 --- /dev/null +++ b/libempathy/empathy-tp-chat.c @@ -0,0 +1,474 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#include <config.h> + +#include <libtelepathy/tp-helpers.h> +#include <libtelepathy/tp-chan-type-text-gen.h> +#include <libtelepathy/tp-chan-iface-chat-state-gen.h> + +#include "empathy-tp-chat.h" +#include "empathy-contact-manager.h" +#include "empathy-contact-list.h" +#include "empathy-session.h" +#include "empathy-marshal.h" +#include "gossip-debug.h" +#include "gossip-time.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv)) + +#define DEBUG_DOMAIN "TpChat" + +struct _EmpathyTpChatPriv { + EmpathyContactList *list; + TpChan *tp_chan; + DBusGProxy *text_iface; + 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_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, + EmpathyTpChatState state, + EmpathyTpChat *chat); +static void tp_chat_emit_message (EmpathyTpChat *chat, + guint type, + guint timestamp, + guint from_handle, + const gchar *message_body); + +enum { + MESSAGE_RECEIVED, + CHAT_STATE_CHANGED, + DESTROY, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT); + +static void +empathy_tp_chat_class_init (EmpathyTpChatClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = tp_chat_finalize; + + signals[MESSAGE_RECEIVED] = + g_signal_new ("message-received", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, GOSSIP_TYPE_MESSAGE); + + signals[CHAT_STATE_CHANGED] = + g_signal_new ("chat-state-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + empathy_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, + 2, GOSSIP_TYPE_CONTACT, G_TYPE_UINT); + + 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 (EmpathyTpChatPriv)); +} + +static void +empathy_tp_chat_init (EmpathyTpChat *chat) +{ +} + + +static void +tp_chat_finalize (GObject *object) +{ + EmpathyTpChatPriv *priv; + EmpathyTpChat *chat; + GError *error = NULL; + + chat = EMPATHY_TP_CHAT (object); + priv = GET_PRIV (chat); + + if (priv->tp_chan) { + gossip_debug (DEBUG_DOMAIN, "Closing channel..."); + + g_signal_handlers_disconnect_by_func (priv->tp_chan, + tp_chat_destroy_cb, + object); + + if (!tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error)) { + gossip_debug (DEBUG_DOMAIN, + "Error closing text channel: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + g_object_unref (priv->tp_chan); + } + + if (priv->list) { + g_object_unref (priv->list); + } + + G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object); +} + +EmpathyTpChat * +empathy_tp_chat_new (McAccount *account, + TpChan *tp_chan) +{ + EmpathyTpChatPriv *priv; + EmpathyTpChat *chat; + EmpathyContactManager *manager; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL); + + chat = g_object_new (EMPATHY_TYPE_TP_CHAT, NULL); + priv = GET_PRIV (chat); + + manager = empathy_session_get_contact_manager (); + priv->list = empathy_contact_manager_get_list (manager, account); + priv->tp_chan = g_object_ref (tp_chan); + g_object_ref (priv->list); + + priv->text_iface = tp_chan_get_interface (tp_chan, + TELEPATHY_CHAN_IFACE_TEXT_QUARK); + priv->chat_state_iface = tp_chan_get_interface (tp_chan, + TELEPATHY_CHAN_IFACE_CHAT_STATE_QUARK); + + g_signal_connect (priv->tp_chan, "destroy", + G_CALLBACK (tp_chat_destroy_cb), + chat); + dbus_g_proxy_connect_signal (priv->text_iface, "Received", + G_CALLBACK (tp_chat_received_cb), + chat, NULL); + dbus_g_proxy_connect_signal (priv->text_iface, "Sent", + G_CALLBACK (tp_chat_sent_cb), + chat, NULL); + + if (priv->chat_state_iface != NULL) { + dbus_g_proxy_connect_signal (priv->chat_state_iface, + "ChatStateChanged", + G_CALLBACK (tp_chat_state_changed_cb), + chat, NULL); + } + + return chat; +} + +EmpathyTpChat * +empathy_tp_chat_new_with_contact (GossipContact *contact) +{ + EmpathyTpChat *chat; + MissionControl *mc; + McAccount *account; + TpConn *tp_conn; + TpChan *text_chan; + const gchar *bus_name; + guint handle; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + mc = empathy_session_get_mission_control (); + account = gossip_contact_get_account (contact); + + if (mission_control_get_connection_status (mc, account, NULL) != 0) { + /* The account is not connected, nothing to do. */ + return NULL; + } + + tp_conn = mission_control_get_connection (mc, account, NULL); + g_return_val_if_fail (tp_conn != NULL, NULL); + bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn)); + handle = gossip_contact_get_handle (contact); + + text_chan = tp_conn_new_channel (tp_get_bus (), + tp_conn, + bus_name, + TP_IFACE_CHANNEL_TYPE_TEXT, + TP_HANDLE_TYPE_CONTACT, + handle, + TRUE); + + chat = empathy_tp_chat_new (account, text_chan); + + g_object_unref (tp_conn); + g_object_unref (text_chan); + + return chat; +} + +void +empathy_tp_chat_request_pending (EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv; + GPtrArray *messages_list; + guint i; + GError *error = NULL; + + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + + priv = GET_PRIV (chat); + + /* If we do this call async, don't forget to ignore Received signal + * until we get the answer */ + if (!tp_chan_type_text_list_pending_messages (priv->text_iface, + TRUE, + &messages_list, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Error retrieving pending messages: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return; + } + + for (i = 0; i < messages_list->len; i++) { + GValueArray *message_struct; + const gchar *message_body; + guint message_id; + guint timestamp; + guint from_handle; + guint message_type; + guint message_flags; + + message_struct = g_ptr_array_index (messages_list, i); + + message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0)); + timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1)); + from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2)); + message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3)); + message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4)); + message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5)); + + gossip_debug (DEBUG_DOMAIN, "Message pending: %s", message_body); + + tp_chat_emit_message (chat, + message_type, + timestamp, + from_handle, + message_body); + + g_value_array_free (message_struct); + } + + g_ptr_array_free (messages_list, TRUE); +} + +void +empathy_tp_chat_send (EmpathyTpChat *chat, + GossipMessage *message) +{ + EmpathyTpChatPriv *priv; + const gchar *message_body; + GossipMessageType message_type; + GError *error = NULL; + + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + g_return_if_fail (GOSSIP_IS_MESSAGE (message)); + + priv = GET_PRIV (chat); + + message_body = gossip_message_get_body (message); + message_type = gossip_message_get_type (message); + + gossip_debug (DEBUG_DOMAIN, "Sending message: %s", message_body); + if (!tp_chan_type_text_send (priv->text_iface, + message_type, + message_body, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Send Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } +} + +void +empathy_tp_chat_send_state (EmpathyTpChat *chat, + EmpathyTpChatState state) +{ + EmpathyTpChatPriv *priv; + GError *error = NULL; + + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + + priv = GET_PRIV (chat); + + if (priv->chat_state_iface) { + gossip_debug (DEBUG_DOMAIN, "Set state: %d", state); + if (!tp_chan_iface_chat_state_set_chat_state (priv->chat_state_iface, + state, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Set Chat State Error: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + } +} + +static void +tp_chat_destroy_cb (TpChan *text_chan, + EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv; + + priv = GET_PRIV (chat); + + gossip_debug (DEBUG_DOMAIN, "Channel destroyed"); + + g_object_unref (priv->tp_chan); + priv->tp_chan = NULL; + priv->text_iface = NULL; + priv->chat_state_iface = NULL; + + g_signal_emit (chat, signals[DESTROY], 0); +} + +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) +{ + EmpathyTpChatPriv *priv; + GArray *message_ids; + + priv = GET_PRIV (chat); + + gossip_debug (DEBUG_DOMAIN, "Message received: %s", message_body); + + tp_chat_emit_message (chat, + message_type, + timestamp, + from_handle, + message_body); + + message_ids = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (message_ids, message_id); + tp_chan_type_text_acknowledge_pending_messages (priv->text_iface, + message_ids, NULL); + g_array_free (message_ids, TRUE); +} + +static void +tp_chat_sent_cb (DBusGProxy *text_iface, + guint timestamp, + guint message_type, + gchar *message_body, + EmpathyTpChat *chat) +{ + gossip_debug (DEBUG_DOMAIN, "Message sent: %s", message_body); + + tp_chat_emit_message (chat, + message_type, + timestamp, + 0, + message_body); +} + +static void +tp_chat_state_changed_cb (DBusGProxy *chat_state_iface, + guint handle, + EmpathyTpChatState state, + EmpathyTpChat *chat) +{ + EmpathyTpChatPriv *priv; + GossipContact *contact; + + priv = GET_PRIV (chat); + + contact = empathy_contact_list_get_from_handle (priv->list, handle); + + g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state); + + g_object_unref (contact); +} + +static void +tp_chat_emit_message (EmpathyTpChat *chat, + guint type, + guint timestamp, + guint from_handle, + const gchar *message_body) +{ + EmpathyTpChatPriv *priv; + GossipMessage *message; + GossipContact *sender; + + priv = GET_PRIV (chat); + + if (from_handle == 0) { + sender = empathy_contact_list_get_own (priv->list); + g_object_ref (sender); + } else { + sender = empathy_contact_list_get_from_handle (priv->list, + from_handle); + } + + message = gossip_message_new (message_body); + gossip_message_set_type (message, type); + gossip_message_set_sender (message, sender); + gossip_message_set_timestamp (message, (GossipTime) timestamp); + + g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message); + + g_object_unref (message); + g_object_unref (sender); +} + diff --git a/libempathy/empathy-tp-chat.h b/libempathy/empathy-tp-chat.h new file mode 100644 index 000000000..c64bbd738 --- /dev/null +++ b/libempathy/empathy-tp-chat.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#ifndef __EMPATHY_TP_CHAT_H__ +#define __EMPATHY_TP_CHAT_H__ + +#include <glib.h> + +#include <libtelepathy/tp-chan.h> + +#include <libmissioncontrol/mc-account.h> + +#include "gossip-message.h" +#include "gossip-contact.h" + + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_TP_CHAT (empathy_tp_chat_get_type ()) +#define EMPATHY_TP_CHAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_TP_CHAT, EmpathyTpChat)) +#define EMPATHY_TP_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_TP_CHAT, EmpathyTpChatClass)) +#define EMPATHY_IS_TP_CHAT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_TP_CHAT)) +#define EMPATHY_IS_TP_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_TP_CHAT)) +#define EMPATHY_TP_CHAT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_TP_CHAT, EmpathyTpChatClass)) + +typedef struct _EmpathyTpChat EmpathyTpChat; +typedef struct _EmpathyTpChatClass EmpathyTpChatClass; +typedef struct _EmpathyTpChatPriv EmpathyTpChatPriv; + +struct _EmpathyTpChat { + GObject parent; +}; + +struct _EmpathyTpChatClass { + GObjectClass parent_class; +}; + +typedef enum { + EMPATHY_TP_CHAT_STATE_GONE, + EMPATHY_TP_CHAT_STATE_INACTIVE, + EMPATHY_TP_CHAT_STATE_ACTIVE, + EMPATHY_TP_CHAT_STATE_PAUSED, + EMPATHY_TP_CHAT_STATE_COMPOSING +} EmpathyTpChatState; + +GType empathy_tp_chat_get_type (void) G_GNUC_CONST; +EmpathyTpChat *empathy_tp_chat_new (McAccount *account, + TpChan *tp_chan); +EmpathyTpChat *empathy_tp_chat_new_with_contact (GossipContact *contact); +void empathy_tp_chat_request_pending (EmpathyTpChat *chat); +void empathy_tp_chat_send (EmpathyTpChat *chat, + GossipMessage *message); +void empathy_tp_chat_send_state (EmpathyTpChat *chat, + EmpathyTpChatState state); + +G_END_DECLS + +#endif /* __EMPATHY_TP_CHAT_H__ */ diff --git a/libempathy/gossip-avatar.c b/libempathy/gossip-avatar.c new file mode 100644 index 000000000..5c17a5176 --- /dev/null +++ b/libempathy/gossip-avatar.c @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * + * 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: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include "gossip-avatar.h" + +#define DEBUG_DOMAIN "Avatar" + +GType +gossip_avatar_get_gtype (void) +{ + static GType type_id = 0; + + if (!type_id) { + type_id = g_boxed_type_register_static ("GossipAvatar", + (GBoxedCopyFunc) gossip_avatar_ref, + (GBoxedFreeFunc) gossip_avatar_unref); + } + + return type_id; +} + +GossipAvatar * +gossip_avatar_new (guchar *data, + gsize len, + gchar *format) +{ + GossipAvatar *avatar; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + g_return_val_if_fail (format != NULL, NULL); + + avatar = g_slice_new0 (GossipAvatar); + avatar->data = g_memdup (data, len); + avatar->len = len; + avatar->format = g_strdup (format); + avatar->refcount = 1; + + return avatar; +} + +void +gossip_avatar_unref (GossipAvatar *avatar) +{ + g_return_if_fail (avatar != NULL); + + avatar->refcount--; + if (avatar->refcount == 0) { + g_free (avatar->data); + g_free (avatar->format); + g_slice_free (GossipAvatar, avatar); + } +} + +GossipAvatar * +gossip_avatar_ref (GossipAvatar *avatar) +{ + g_return_val_if_fail (avatar != NULL, NULL); + + avatar->refcount++; + + return avatar; +} + diff --git a/libempathy/gossip-avatar.h b/libempathy/gossip-avatar.h new file mode 100644 index 000000000..44fa9aba3 --- /dev/null +++ b/libempathy/gossip-avatar.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#ifndef __GOSSIP_AVATAR_H__ +#define __GOSSIP_AVATAR_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GOSSIP_TYPE_AVATAR (gossip_avatar_get_gtype ()) + +typedef struct _GossipAvatar GossipAvatar; + +struct _GossipAvatar { + guchar *data; + gsize len; + gchar *format; + guint refcount; +}; + +GType gossip_avatar_get_gtype (void) G_GNUC_CONST; +GossipAvatar * gossip_avatar_new (guchar *avatar, + gsize len, + gchar *format); +GossipAvatar * gossip_avatar_ref (GossipAvatar *avatar); +void gossip_avatar_unref (GossipAvatar *avatar); + +G_END_DECLS + +#endif /* __GOSSIP_AVATAR_H__ */ diff --git a/libempathy/gossip-conf.c b/libempathy/gossip-conf.c new file mode 100644 index 000000000..9625a700d --- /dev/null +++ b/libempathy/gossip-conf.c @@ -0,0 +1,382 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Imendio AB + * + * 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: Richard Hult <richard@imendio.com> + */ + +#include "config.h" + +#include <string.h> + +#include <gconf/gconf-client.h> + +#include "gossip-conf.h" +#include "gossip-debug.h" + +#define DEBUG_DOMAIN "Config" + +#define GOSSIP_CONF_ROOT "/apps/empathy" +#define DESKTOP_INTERFACE_ROOT "/desktop/gnome/interface" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONF, GossipConfPriv)) + +typedef struct { + GConfClient *gconf_client; +} GossipConfPriv; + +typedef struct { + GossipConf *conf; + GossipConfNotifyFunc func; + gpointer user_data; +} GossipConfNotifyData; + +static void conf_finalize (GObject *object); + +G_DEFINE_TYPE (GossipConf, gossip_conf, G_TYPE_OBJECT); + +static GossipConf *global_conf = NULL; + +static void +gossip_conf_class_init (GossipConfClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = conf_finalize; + + g_type_class_add_private (object_class, sizeof (GossipConfPriv)); +} + +static void +gossip_conf_init (GossipConf *conf) +{ + GossipConfPriv *priv; + + priv = GET_PRIV (conf); + + priv->gconf_client = gconf_client_get_default (); + + gconf_client_add_dir (priv->gconf_client, + GOSSIP_CONF_ROOT, + GCONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + gconf_client_add_dir (priv->gconf_client, + DESKTOP_INTERFACE_ROOT, + GCONF_CLIENT_PRELOAD_NONE, + NULL); +} + +static void +conf_finalize (GObject *object) +{ + GossipConfPriv *priv; + + priv = GET_PRIV (object); + + gconf_client_remove_dir (priv->gconf_client, + GOSSIP_CONF_ROOT, + NULL); + gconf_client_remove_dir (priv->gconf_client, + DESKTOP_INTERFACE_ROOT, + NULL); + + g_object_unref (priv->gconf_client); + + G_OBJECT_CLASS (gossip_conf_parent_class)->finalize (object); +} + +GossipConf * +gossip_conf_get (void) +{ + if (!global_conf) { + global_conf = g_object_new (GOSSIP_TYPE_CONF, NULL); + } + + return global_conf; +} + +void +gossip_conf_shutdown (void) +{ + if (global_conf) { + g_object_unref (global_conf); + global_conf = NULL; + } +} + +gboolean +gossip_conf_set_int (GossipConf *conf, + const gchar *key, + gint value) +{ + GossipConfPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + gossip_debug (DEBUG_DOMAIN, "Setting int:'%s' to %d", key, value); + + priv = GET_PRIV (conf); + + return gconf_client_set_int (priv->gconf_client, + key, + value, + NULL); +} + +gboolean +gossip_conf_get_int (GossipConf *conf, + const gchar *key, + gint *value) +{ + GossipConfPriv *priv; + GError *error = NULL; + + *value = 0; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_int (priv->gconf_client, + key, + &error); + + gossip_debug (DEBUG_DOMAIN, "Getting int:'%s' (=%d), error:'%s'", + key, *value, error ? error->message : "None"); + + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gboolean +gossip_conf_set_bool (GossipConf *conf, + const gchar *key, + gboolean value) +{ + GossipConfPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + gossip_debug (DEBUG_DOMAIN, "Setting bool:'%s' to %d ---> %s", + key, value, value ? "true" : "false"); + + priv = GET_PRIV (conf); + + return gconf_client_set_bool (priv->gconf_client, + key, + value, + NULL); +} + +gboolean +gossip_conf_get_bool (GossipConf *conf, + const gchar *key, + gboolean *value) +{ + GossipConfPriv *priv; + GError *error = NULL; + + *value = FALSE; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_bool (priv->gconf_client, + key, + &error); + + gossip_debug (DEBUG_DOMAIN, "Getting bool:'%s' (=%d ---> %s), error:'%s'", + key, *value, *value ? "true" : "false", + error ? error->message : "None"); + + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gboolean +gossip_conf_set_string (GossipConf *conf, + const gchar *key, + const gchar *value) +{ + GossipConfPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + gossip_debug (DEBUG_DOMAIN, "Setting string:'%s' to '%s'", + key, value); + + priv = GET_PRIV (conf); + + return gconf_client_set_string (priv->gconf_client, + key, + value, + NULL); +} + +gboolean +gossip_conf_get_string (GossipConf *conf, + const gchar *key, + gchar **value) +{ + GossipConfPriv *priv; + GError *error = NULL; + + *value = NULL; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_string (priv->gconf_client, + key, + &error); + + gossip_debug (DEBUG_DOMAIN, "Getting string:'%s' (='%s'), error:'%s'", + key, *value, error ? error->message : "None"); + + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gboolean +gossip_conf_set_string_list (GossipConf *conf, + const gchar *key, + GSList *value) +{ + GossipConfPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + return gconf_client_set_list (priv->gconf_client, + key, + GCONF_VALUE_STRING, + value, + NULL); +} + +gboolean +gossip_conf_get_string_list (GossipConf *conf, + const gchar *key, + GSList **value) +{ + GossipConfPriv *priv; + GError *error = NULL; + + *value = NULL; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_list (priv->gconf_client, + key, + GCONF_VALUE_STRING, + &error); + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +static void +conf_notify_data_free (GossipConfNotifyData *data) +{ + g_object_unref (data->conf); + g_slice_free (GossipConfNotifyData, data); +} + +static void +conf_notify_func (GConfClient *client, + guint id, + GConfEntry *entry, + gpointer user_data) +{ + GossipConfNotifyData *data; + + data = user_data; + + data->func (data->conf, + gconf_entry_get_key (entry), + data->user_data); +} + +guint +gossip_conf_notify_add (GossipConf *conf, + const gchar *key, + GossipConfNotifyFunc func, + gpointer user_data) +{ + GossipConfPriv *priv; + guint id; + GossipConfNotifyData *data; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), 0); + + priv = GET_PRIV (conf); + + data = g_slice_new (GossipConfNotifyData); + data->func = func; + data->user_data = user_data; + data->conf = g_object_ref (conf); + + id = gconf_client_notify_add (priv->gconf_client, + key, + conf_notify_func, + data, + (GFreeFunc) conf_notify_data_free, + NULL); + + return id; +} + +gboolean +gossip_conf_notify_remove (GossipConf *conf, + guint id) +{ + GossipConfPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + gconf_client_notify_remove (priv->gconf_client, id); + + return TRUE; +} + diff --git a/libempathy/gossip-conf.h b/libempathy/gossip-conf.h new file mode 100644 index 000000000..35fdfb902 --- /dev/null +++ b/libempathy/gossip-conf.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_CONF_H__ +#define __GOSSIP_CONF_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GOSSIP_TYPE_CONF (gossip_conf_get_type ()) +#define GOSSIP_CONF(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_CONF, GossipConf)) +#define GOSSIP_CONF_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_CONF, GossipConfClass)) +#define GOSSIP_IS_CONF(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_CONF)) +#define GOSSIP_IS_CONF_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_CONF)) +#define GOSSIP_CONF_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_CONF, GossipConfClass)) + +typedef struct _GossipConf GossipConf; +typedef struct _GossipConfClass GossipConfClass; + +struct _GossipConf { + GObject parent; +}; + +struct _GossipConfClass { + GObjectClass parent_class; +}; + +typedef void (*GossipConfNotifyFunc) (GossipConf *conf, + const gchar *key, + gpointer user_data); + +GType gossip_conf_get_type (void) G_GNUC_CONST; +GossipConf *gossip_conf_get (void); +void gossip_conf_shutdown (void); +guint gossip_conf_notify_add (GossipConf *conf, + const gchar *key, + GossipConfNotifyFunc func, + gpointer data); +gboolean gossip_conf_notify_remove (GossipConf *conf, + guint id); +gboolean gossip_conf_set_int (GossipConf *conf, + const gchar *key, + gint value); +gboolean gossip_conf_get_int (GossipConf *conf, + const gchar *key, + gint *value); +gboolean gossip_conf_set_bool (GossipConf *conf, + const gchar *key, + gboolean value); +gboolean gossip_conf_get_bool (GossipConf *conf, + const gchar *key, + gboolean *value); +gboolean gossip_conf_set_string (GossipConf *conf, + const gchar *key, + const gchar *value); +gboolean gossip_conf_get_string (GossipConf *conf, + const gchar *key, + gchar **value); +gboolean gossip_conf_set_string_list (GossipConf *conf, + const gchar *key, + GSList *value); +gboolean gossip_conf_get_string_list (GossipConf *conf, + const gchar *key, + GSList **value); + +G_END_DECLS + +#endif /* __GOSSIP_CONF_H__ */ + diff --git a/libempathy/gossip-contact.c b/libempathy/gossip-contact.c new file mode 100644 index 000000000..6950c89b7 --- /dev/null +++ b/libempathy/gossip-contact.c @@ -0,0 +1,815 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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: Mikael Hallendal <micke@imendio.com> + * Martyn Russell <martyn@imendio.com> + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> + +#include "gossip-contact.h" +#include "gossip-utils.h" +#include "gossip-debug.h" + +#define DEBUG_DOMAIN "Contact" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT, GossipContactPriv)) + +typedef struct _GossipContactPriv GossipContactPriv; + +struct _GossipContactPriv { + gchar *id; + gchar *name; + guint handle; + GList *presences; + GList *groups; + GossipSubscription subscription; + GossipAvatar *avatar; + McAccount *account; +}; + +static void contact_class_init (GossipContactClass *class); +static void contact_init (GossipContact *contact); +static void contact_finalize (GObject *object); +static void contact_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void contact_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void contact_set_presences (GossipContact *contact, + GList *presences); + +enum { + PROP_0, + PROP_NAME, + PROP_ID, + PROP_PRESENCES, + PROP_GROUPS, + PROP_SUBSCRIPTION, + PROP_AVATAR, + PROP_HANDLE, + PROP_ACCOUNT +}; + +static gpointer parent_class = NULL; + +GType +gossip_contact_get_gtype (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (GossipContactClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) contact_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GossipContact), + 0, /* n_preallocs */ + (GInstanceInitFunc) contact_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "GossipContact", + &info, 0); + } + + return type; +} + +static void +contact_class_init (GossipContactClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + parent_class = g_type_class_peek_parent (class); + + object_class->finalize = contact_finalize; + object_class->get_property = contact_get_property; + object_class->set_property = contact_set_property; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "Contact Name", + "The name of the contact", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "Contact id", + "String identifying contact", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_PRESENCES, + g_param_spec_pointer ("presences", + "Contact presences", + "Presences of contact", + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_GROUPS, + g_param_spec_pointer ("groups", + "Contact groups", + "Groups of contact", + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_SUBSCRIPTION, + g_param_spec_int ("subscription", + "Contact Subscription", + "The subscription status of the contact", + GOSSIP_SUBSCRIPTION_NONE, + GOSSIP_SUBSCRIPTION_BOTH, + GOSSIP_SUBSCRIPTION_NONE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_AVATAR, + g_param_spec_boxed ("avatar", + "Avatar image", + "The avatar image", + GOSSIP_TYPE_AVATAR, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ACCOUNT, + g_param_spec_object ("account", + "Contact Account", + "The account associated with the contact", + MC_TYPE_ACCOUNT, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_HANDLE, + g_param_spec_uint ("handle", + "Contact Handle", + "The handle of the contact", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GossipContactPriv)); +} + +static void +contact_init (GossipContact *contact) +{ + GossipContactPriv *priv; + + priv = GET_PRIV (contact); + + priv->name = NULL; + priv->id = NULL; + priv->presences = NULL; + priv->account = NULL; + priv->groups = NULL; + priv->avatar = NULL; + priv->handle = 0; +} + +static void +contact_finalize (GObject *object) +{ + GossipContactPriv *priv; + + priv = GET_PRIV (object); + + gossip_debug (DEBUG_DOMAIN, "finalize: %p", object); + + g_free (priv->name); + g_free (priv->id); + + if (priv->avatar) { + gossip_avatar_unref (priv->avatar); + } + + if (priv->presences) { + g_list_foreach (priv->presences, (GFunc) g_object_unref, NULL); + g_list_free (priv->presences); + } + + if (priv->groups) { + g_list_foreach (priv->groups, (GFunc) g_free, NULL); + g_list_free (priv->groups); + } + + if (priv->account) { + g_object_unref (priv->account); + } + + (G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +contact_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GossipContactPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, + gossip_contact_get_name (GOSSIP_CONTACT (object))); + break; + case PROP_ID: + g_value_set_string (value, + gossip_contact_get_id (GOSSIP_CONTACT (object))); + break; + case PROP_PRESENCES: + g_value_set_pointer (value, priv->presences); + break; + case PROP_GROUPS: + g_value_set_pointer (value, priv->groups); + break; + case PROP_SUBSCRIPTION: + g_value_set_int (value, priv->subscription); + break; + case PROP_AVATAR: + g_value_set_boxed (value, priv->avatar); + break; + case PROP_ACCOUNT: + g_value_set_object (value, priv->account); + break; + case PROP_HANDLE: + g_value_set_uint (value, priv->handle); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +contact_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GossipContactPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_NAME: + gossip_contact_set_name (GOSSIP_CONTACT (object), + g_value_get_string (value)); + break; + case PROP_ID: + gossip_contact_set_id (GOSSIP_CONTACT (object), + g_value_get_string (value)); + break; + case PROP_PRESENCES: + contact_set_presences (GOSSIP_CONTACT (object), + g_value_get_pointer (value)); + break; + case PROP_GROUPS: + gossip_contact_set_groups (GOSSIP_CONTACT (object), + g_value_get_pointer (value)); + break; + case PROP_SUBSCRIPTION: + gossip_contact_set_subscription (GOSSIP_CONTACT (object), + g_value_get_int (value)); + break; + case PROP_AVATAR: + gossip_contact_set_avatar (GOSSIP_CONTACT (object), + g_value_get_boxed (value)); + break; + case PROP_ACCOUNT: + gossip_contact_set_account (GOSSIP_CONTACT (object), + MC_ACCOUNT (g_value_get_object (value))); + break; + case PROP_HANDLE: + gossip_contact_set_handle (GOSSIP_CONTACT (object), + g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +GossipContact * +gossip_contact_new (McAccount *account) +{ + return g_object_new (GOSSIP_TYPE_CONTACT, + "account", account, + NULL); +} + +GossipContact * +gossip_contact_new_full (McAccount *account, + const gchar *id, + const gchar *name) +{ + return g_object_new (GOSSIP_TYPE_CONTACT, + "account", account, + "name", name, + "id", id, + NULL); +} + +const gchar * +gossip_contact_get_id (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), ""); + + priv = GET_PRIV (contact); + + if (priv->id) { + return priv->id; + } + + return ""; +} + +const gchar * +gossip_contact_get_name (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), ""); + + priv = GET_PRIV (contact); + + if (priv->name == NULL) { + return gossip_contact_get_id (contact); + } + + return priv->name; +} + +GossipAvatar * +gossip_contact_get_avatar (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->avatar; +} + +McAccount * +gossip_contact_get_account (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->account; +} + +GossipPresence * +gossip_contact_get_active_presence (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + if (priv->presences) { + /* Highest priority of the presences is first */ + return GOSSIP_PRESENCE (priv->presences->data); + } + + return NULL; +} + +GossipPresence * +gossip_contact_get_presence_for_resource (GossipContact *contact, + const gchar *resource) +{ + GossipContactPriv *priv; + GList *l; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + g_return_val_if_fail (resource != NULL, NULL); + + priv = GET_PRIV (contact); + + for (l = priv->presences; l; l = l->next) { + const gchar *p_res; + + p_res = gossip_presence_get_resource (GOSSIP_PRESENCE (l->data)); + if (p_res && strcmp (resource, p_res) == 0) { + return GOSSIP_PRESENCE (l->data); + } + } + + return NULL; +} + +GList * +gossip_contact_get_presence_list (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->presences; +} + +GList * +gossip_contact_get_groups (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + return priv->groups; +} + +GossipSubscription +gossip_contact_get_subscription (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), + GOSSIP_SUBSCRIPTION_NONE); + + priv = GET_PRIV (contact); + + return priv->subscription; +} + +guint +gossip_contact_get_handle (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), 0); + + priv = GET_PRIV (contact); + + return priv->handle; +} + +void +gossip_contact_set_id (GossipContact *contact, + const gchar *id) +{ + GossipContactPriv *priv; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_if_fail (id != NULL); + + priv = GET_PRIV (contact); + + g_free (priv->id); + priv->id = g_strdup (id); + + g_object_notify (G_OBJECT (contact), "id"); +} + +void +gossip_contact_set_name (GossipContact *contact, + const gchar *name) +{ + GossipContactPriv *priv; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_if_fail (name != NULL); + + priv = GET_PRIV (contact); + + g_free (priv->name); + priv->name = g_strdup (name); + + g_object_notify (G_OBJECT (contact), "name"); +} + +static void +contact_set_presences (GossipContact *contact, + GList *presences) +{ + GossipContactPriv *priv; + + priv = GET_PRIV (contact); + + if (priv->presences) { + g_list_foreach (priv->presences, (GFunc) g_object_unref, NULL); + g_list_free (priv->presences); + } + + priv->presences = g_list_copy (presences); + g_list_foreach (priv->presences, (GFunc) g_object_ref, NULL); + + g_object_notify (G_OBJECT (contact), "presences"); +} + +void +gossip_contact_set_avatar (GossipContact *contact, + GossipAvatar *avatar) +{ + GossipContactPriv *priv; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + + priv = GET_PRIV (contact); + + if (priv->avatar) { + gossip_avatar_unref (priv->avatar); + priv->avatar = NULL; + } + + if (avatar) { + priv->avatar = gossip_avatar_ref (avatar); + } + + g_object_notify (G_OBJECT (contact), "avatar"); +} + +void +gossip_contact_set_account (GossipContact *contact, + McAccount *account) +{ + GossipContactPriv *priv; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_if_fail (MC_IS_ACCOUNT (account)); + + priv = GET_PRIV (contact); + + if (priv->account) { + g_object_unref (priv->account); + } + + if (account) { + priv->account = g_object_ref (account); + } else { + priv->account = NULL; + } + + g_object_notify (G_OBJECT (contact), "account"); +} + +void +gossip_contact_add_presence (GossipContact *contact, + GossipPresence *presence) +{ + GossipContactPriv *priv; + GossipPresence *this_presence; + GList *l; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_if_fail (GOSSIP_IS_PRESENCE (presence)); + + priv = GET_PRIV (contact); + + for (l = priv->presences; l && presence; l = l->next) { + this_presence = l->data; + + if (gossip_presence_resource_equal (this_presence, presence)) { + gint ref_count; + + ref_count = G_OBJECT (presence)->ref_count; + + /* Remove old presence for this resource, we + * would use g_list_remove_all() here but we + * want to make sure we unref for each + * instance we find it in the list. + */ + priv->presences = g_list_remove (priv->presences, this_presence); + g_object_unref (this_presence); + + if (!priv->presences || ref_count <= 1) { + break; + } + + /* Reset list to beginnging to make sure we + * didn't miss any duplicates. + */ + l = priv->presences; + } + } + + /* Add new presence */ + priv->presences = g_list_insert_sorted (priv->presences, + g_object_ref (presence), + gossip_presence_sort_func); + + g_object_notify (G_OBJECT (contact), "presences"); +} + +void +gossip_contact_remove_presence (GossipContact *contact, + GossipPresence *presence) +{ + GossipContactPriv *priv; + GossipPresence *this_presence; + GList *l; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + g_return_if_fail (GOSSIP_IS_PRESENCE (presence)); + + priv = GET_PRIV (contact); + + for (l = priv->presences; l; l = l->next) { + this_presence = l->data; + + if (gossip_presence_resource_equal (this_presence, presence)) { + gint ref_count; + + ref_count = G_OBJECT (presence)->ref_count; + + /* Remove old presence for this resource, we + * would use g_list_remove_all() here but we + * want to make sure we unref for each + * instance we find it in the list. + */ + priv->presences = g_list_remove (priv->presences, this_presence); + g_object_unref (this_presence); + + if (!priv->presences || ref_count <= 1) { + break; + } + + /* Reset list to beginnging to make sure we + * didn't miss any duplicates. + */ + l = priv->presences; + } + } + + priv->presences = g_list_sort (priv->presences, + gossip_presence_sort_func); + + g_object_notify (G_OBJECT (contact), "presences"); +} + +void +gossip_contact_set_groups (GossipContact *contact, + GList *groups) +{ + GossipContactPriv *priv; + GList *old_groups, *l; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + + priv = GET_PRIV (contact); + + old_groups = priv->groups; + priv->groups = NULL; + + for (l = groups; l; l = l->next) { + priv->groups = g_list_append (priv->groups, + g_strdup (l->data)); + } + + g_list_foreach (old_groups, (GFunc) g_free, NULL); + g_list_free (old_groups); + + g_object_notify (G_OBJECT (contact), "groups"); +} + +void +gossip_contact_set_subscription (GossipContact *contact, + GossipSubscription subscription) +{ + GossipContactPriv *priv; + + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + + priv = GET_PRIV (contact); + + priv->subscription = subscription; + + g_object_notify (G_OBJECT (contact), "subscription"); +} + +void +gossip_contact_set_handle (GossipContact *contact, + guint handle) +{ + GossipContactPriv *priv; + + priv = GET_PRIV (contact); + + priv->handle = handle; + + g_object_notify (G_OBJECT (contact), "handle"); +} + +gboolean +gossip_contact_is_online (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), FALSE); + + priv = GET_PRIV (contact); + + if (priv->presences == NULL) { + return FALSE; + } + + return TRUE; +} + +const gchar * +gossip_contact_get_status (GossipContact *contact) +{ + GossipContactPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), ""); + + priv = GET_PRIV (contact); + + if (priv->presences) { + GossipPresence *p; + const gchar *status; + + p = GOSSIP_PRESENCE (priv->presences->data); + status = gossip_presence_get_status (p); + if (!status) { + status = gossip_presence_state_get_default_status (gossip_presence_get_state (p)); + } + return status; + } else { + return _("Offline"); + } +} + +gboolean +gossip_contact_equal (gconstpointer v1, + gconstpointer v2) +{ + McAccount *account_a; + McAccount *account_b; + const gchar *id_a; + const gchar *id_b; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (v1), FALSE); + g_return_val_if_fail (GOSSIP_IS_CONTACT (v2), FALSE); + + account_a = gossip_contact_get_account (GOSSIP_CONTACT (v1)); + account_b = gossip_contact_get_account (GOSSIP_CONTACT (v2)); + + id_a = gossip_contact_get_id (GOSSIP_CONTACT (v1)); + id_b = gossip_contact_get_id (GOSSIP_CONTACT (v2)); + + return gossip_account_equal (account_a, account_b) && g_str_equal (id_a, id_b); +} + +guint +gossip_contact_hash (gconstpointer key) +{ + GossipContactPriv *priv; + guint hash; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (key), +1); + + priv = GET_PRIV (GOSSIP_CONTACT (key)); + + hash = gossip_account_hash (gossip_contact_get_account (GOSSIP_CONTACT (key))); + hash += g_str_hash (gossip_contact_get_id (GOSSIP_CONTACT (key))); + + return hash; +} + diff --git a/libempathy/gossip-contact.h b/libempathy/gossip-contact.h new file mode 100644 index 000000000..92caee9ea --- /dev/null +++ b/libempathy/gossip-contact.h @@ -0,0 +1,102 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_CONTACT_H__ +#define __GOSSIP_CONTACT_H__ + +#include <glib-object.h> + +#include <libmissioncontrol/mc-account.h> + +#include "gossip-avatar.h" +#include "gossip-presence.h" + +G_BEGIN_DECLS + +#define GOSSIP_TYPE_CONTACT (gossip_contact_get_gtype ()) +#define GOSSIP_CONTACT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_CONTACT, GossipContact)) +#define GOSSIP_CONTACT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_CONTACT, GossipContactClass)) +#define GOSSIP_IS_CONTACT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_CONTACT)) +#define GOSSIP_IS_CONTACT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_CONTACT)) +#define GOSSIP_CONTACT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_CONTACT, GossipContactClass)) + +typedef struct _GossipContact GossipContact; +typedef struct _GossipContactClass GossipContactClass; + +struct _GossipContact { + GObject parent; +}; + +struct _GossipContactClass { + GObjectClass parent_class; +}; + +typedef enum { + GOSSIP_SUBSCRIPTION_NONE = 0, + GOSSIP_SUBSCRIPTION_TO = 1 << 0, /* We send our presence to that contact */ + GOSSIP_SUBSCRIPTION_FROM = 1 << 1, /* That contact sends his presence to us */ + GOSSIP_SUBSCRIPTION_BOTH = GOSSIP_SUBSCRIPTION_TO | GOSSIP_SUBSCRIPTION_FROM +} GossipSubscription; + +GType gossip_contact_get_gtype (void) G_GNUC_CONST; + +GossipContact * gossip_contact_new (McAccount *account); +GossipContact * gossip_contact_new_full (McAccount *account, + const gchar *id, + const gchar *name); +const gchar * gossip_contact_get_id (GossipContact *contact); +const gchar * gossip_contact_get_name (GossipContact *contact); +GossipAvatar * gossip_contact_get_avatar (GossipContact *contact); +McAccount * gossip_contact_get_account (GossipContact *contact); +void gossip_contact_add_presence (GossipContact *contact, + GossipPresence *presence); +void gossip_contact_remove_presence (GossipContact *contact, + GossipPresence *presence); +GossipPresence * gossip_contact_get_presence_for_resource (GossipContact *contact, + const gchar *resource); +GossipPresence * gossip_contact_get_active_presence (GossipContact *contact); +GList * gossip_contact_get_presence_list (GossipContact *contact); +GList * gossip_contact_get_groups (GossipContact *contact); +GossipSubscription gossip_contact_get_subscription (GossipContact *contact); +guint gossip_contact_get_handle (GossipContact *contact); +void gossip_contact_set_id (GossipContact *contact, + const gchar *id); +void gossip_contact_set_name (GossipContact *contact, + const gchar *name); +void gossip_contact_set_avatar (GossipContact *contact, + GossipAvatar *avatar); +void gossip_contact_set_account (GossipContact *contact, + McAccount *account); +void gossip_contact_set_groups (GossipContact *contact, + GList *categories); +void gossip_contact_set_subscription (GossipContact *contact, + GossipSubscription subscription); +void gossip_contact_set_handle (GossipContact *contact, + guint handle); +gboolean gossip_contact_is_online (GossipContact *contact); +const gchar * gossip_contact_get_status (GossipContact *contact); +gboolean gossip_contact_equal (gconstpointer v1, + gconstpointer v2); +guint gossip_contact_hash (gconstpointer key); + +G_END_DECLS + +#endif /* __GOSSIP_CONTACT_H__ */ + diff --git a/libempathy/gossip-debug.c b/libempathy/gossip-debug.c new file mode 100644 index 000000000..a6bbeea2a --- /dev/null +++ b/libempathy/gossip-debug.c @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * + * 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: Richard Hult <richard@imendio.com> + */ + +#include "config.h" + +#include <stdarg.h> +#include <string.h> + +#include <glib.h> +#include <glib/gprintf.h> + +/* Set EMPATHY_DEBUG to a colon/comma/space separated list of domains, or "all" + * to get all debug output. + */ + +#include "gossip-debug.h" + +static gchar **debug_strv; +static gboolean all_domains = FALSE; + +static void +debug_init (void) +{ + static gboolean inited = FALSE; + + if (!inited) { + const gchar *env; + gint i; + + env = g_getenv ("EMPATHY_DEBUG"); + + if (env) { + debug_strv = g_strsplit_set (env, ":, ", 0); + } else { + debug_strv = NULL; + } + + for (i = 0; debug_strv && debug_strv[i]; i++) { + if (strcmp ("all", debug_strv[i]) == 0) { + all_domains = TRUE; + } + } + + inited = TRUE; + } +} + +void +gossip_debug_impl (const gchar *domain, const gchar *msg, ...) +{ + gint i; + + g_return_if_fail (domain != NULL); + g_return_if_fail (msg != NULL); + + debug_init (); + + for (i = 0; debug_strv && debug_strv[i]; i++) { + if (all_domains || strcmp (domain, debug_strv[i]) == 0) { + va_list args; + + g_print ("%s: ", domain); + + va_start (args, msg); + g_vprintf (msg, args); + va_end (args); + + g_print ("\n"); + break; + } + } +} + diff --git a/libempathy/gossip-debug.h b/libempathy/gossip-debug.h new file mode 100644 index 000000000..39dae0f1c --- /dev/null +++ b/libempathy/gossip-debug.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_DEBUG_H__ +#define __GOSSIP_DEBUG_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +#ifdef G_HAVE_ISO_VARARGS +# ifdef GOSSIP_DISABLE_DEBUG +# define gossip_debug(...) +# else +# define gossip_debug(...) gossip_debug_impl (__VA_ARGS__) +# endif +#elif defined(G_HAVE_GNUC_VARARGS) +# if GOSSIP_DISABLE_DEBUG +# define gossip_debug(fmt...) +# else +# define gossip_debug(fmt...) gossip_debug_impl(fmt) +# endif +#else +# if GOSSIP_DISABLE_DEBUG +# define gossip_debug(x) +# else +# define gossip_debug gossip_debug_impl +# endif +#endif + +void gossip_debug_impl (const gchar *domain, const gchar *msg, ...); + +G_END_DECLS + +#endif /* __GOSSIP_DEBUG_H__ */ + diff --git a/libempathy/gossip-message.c b/libempathy/gossip-message.c new file mode 100644 index 000000000..85889e7da --- /dev/null +++ b/libempathy/gossip-message.c @@ -0,0 +1,365 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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: Mikael Hallendal <micke@imendio.com> + */ + +#include "config.h" + +#include "gossip-message.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_MESSAGE, GossipMessagePriv)) + +typedef struct _GossipMessagePriv GossipMessagePriv; + +struct _GossipMessagePriv { + GossipMessageType type; + GossipContact *sender; + gchar *body; + GossipTime timestamp; + +}; + +static void gossip_message_class_init (GossipMessageClass *class); +static void gossip_message_init (GossipMessage *message); +static void gossip_message_finalize (GObject *object); +static void message_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void message_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +enum { + PROP_0, + PROP_TYPE, + PROP_SENDER, + PROP_BODY, + PROP_TIMESTAMP, +}; + +static gpointer parent_class = NULL; + +GType +gossip_message_get_gtype (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (GossipMessageClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gossip_message_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GossipMessage), + 0, /* n_preallocs */ + (GInstanceInitFunc) gossip_message_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "GossipMessage", + &info, 0); + } + + return type; +} + +static void +gossip_message_class_init (GossipMessageClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + parent_class = g_type_class_peek_parent (class); + + object_class->finalize = gossip_message_finalize; + object_class->get_property = message_get_property; + object_class->set_property = message_set_property; + + g_object_class_install_property (object_class, + PROP_TYPE, + g_param_spec_int ("type", + "Message Type", + "The type of message", + GOSSIP_MESSAGE_TYPE_NORMAL, + GOSSIP_MESSAGE_TYPE_LAST, + GOSSIP_MESSAGE_TYPE_NORMAL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SENDER, + g_param_spec_object ("sender", + "Message Sender", + "The sender of the message", + GOSSIP_TYPE_CONTACT, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_BODY, + g_param_spec_string ("body", + "Message Body", + "The content of the message", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TIMESTAMP, + g_param_spec_long ("timestamp", + "timestamp", + "timestamp", + -1, + G_MAXLONG, + -1, + G_PARAM_READWRITE)); + + + g_type_class_add_private (object_class, sizeof (GossipMessagePriv)); + +} + +static void +gossip_message_init (GossipMessage *message) +{ + GossipMessagePriv *priv; + + priv = GET_PRIV (message); + + priv->type = GOSSIP_MESSAGE_TYPE_NORMAL; + priv->sender = NULL; + priv->body = NULL; + priv->timestamp = gossip_time_get_current (); +} + +static void +gossip_message_finalize (GObject *object) +{ + GossipMessagePriv *priv; + + priv = GET_PRIV (object); + + if (priv->sender) { + g_object_unref (priv->sender); + } + + g_free (priv->body); + + (G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +message_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GossipMessagePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_TYPE: + g_value_set_int (value, priv->type); + break; + case PROP_SENDER: + g_value_set_object (value, priv->sender); + break; + case PROP_BODY: + g_value_set_string (value, priv->body); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +message_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GossipMessagePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_TYPE: + gossip_message_set_type (GOSSIP_MESSAGE (object), + g_value_get_int (value)); + break; + case PROP_SENDER: + gossip_message_set_sender (GOSSIP_MESSAGE (object), + GOSSIP_CONTACT (g_value_get_object (value))); + break; + case PROP_BODY: + gossip_message_set_body (GOSSIP_MESSAGE (object), + g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +GossipMessage * +gossip_message_new (const gchar *body) +{ + return g_object_new (GOSSIP_TYPE_MESSAGE, + "body", body, + NULL); +} + +GossipMessageType +gossip_message_get_type (GossipMessage *message) +{ + GossipMessagePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_MESSAGE (message), + GOSSIP_MESSAGE_TYPE_NORMAL); + + priv = GET_PRIV (message); + + return priv->type; +} + +void +gossip_message_set_type (GossipMessage *message, + GossipMessageType type) +{ + GossipMessagePriv *priv; + + g_return_if_fail (GOSSIP_IS_MESSAGE (message)); + + priv = GET_PRIV (message); + + priv->type = type; + + g_object_notify (G_OBJECT (message), "type"); +} + +GossipContact * +gossip_message_get_sender (GossipMessage *message) +{ + GossipMessagePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_MESSAGE (message), NULL); + + priv = GET_PRIV (message); + + return priv->sender; +} + +void +gossip_message_set_sender (GossipMessage *message, GossipContact *contact) +{ + GossipMessagePriv *priv; + GossipContact *old_sender; + + g_return_if_fail (GOSSIP_IS_MESSAGE (message)); + g_return_if_fail (GOSSIP_IS_CONTACT (contact)); + + priv = GET_PRIV (message); + + old_sender = priv->sender; + priv->sender = g_object_ref (contact); + + if (old_sender) { + g_object_unref (old_sender); + } + + g_object_notify (G_OBJECT (message), "sender"); +} + +const gchar * +gossip_message_get_body (GossipMessage *message) +{ + GossipMessagePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_MESSAGE (message), NULL); + + priv = GET_PRIV (message); + + return priv->body; +} + +void +gossip_message_set_body (GossipMessage *message, + const gchar *body) +{ + GossipMessagePriv *priv; + GossipMessageType type; + + g_return_if_fail (GOSSIP_IS_MESSAGE (message)); + + priv = GET_PRIV (message); + + g_free (priv->body); + priv->body = NULL; + + type = GOSSIP_MESSAGE_TYPE_NORMAL; + if (g_str_has_prefix (body, "/me")) { + type = GOSSIP_MESSAGE_TYPE_ACTION; + body += 4; + } + + if (body) { + priv->body = g_strdup (body); + } + + if (type != priv->type) { + gossip_message_set_type (message, type); + } + + g_object_notify (G_OBJECT (message), "body"); +} + +GossipTime +gossip_message_get_timestamp (GossipMessage *message) +{ + GossipMessagePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_MESSAGE (message), -1); + + priv = GET_PRIV (message); + + return priv->timestamp; +} + +void +gossip_message_set_timestamp (GossipMessage *message, + GossipTime timestamp) +{ + GossipMessagePriv *priv; + + g_return_if_fail (GOSSIP_IS_MESSAGE (message)); + g_return_if_fail (timestamp >= -1); + + priv = GET_PRIV (message); + + if (timestamp <= 0) { + priv->timestamp = gossip_time_get_current (); + } else { + priv->timestamp = timestamp; + } + + g_object_notify (G_OBJECT (message), "timestamp"); +} + diff --git a/libempathy/gossip-message.h b/libempathy/gossip-message.h new file mode 100644 index 000000000..8defba7e7 --- /dev/null +++ b/libempathy/gossip-message.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_MESSAGE_H__ +#define __GOSSIP_MESSAGE_H__ + +#include <glib-object.h> + +#include "gossip-contact.h" +#include "gossip-time.h" + +G_BEGIN_DECLS + +#define GOSSIP_TYPE_MESSAGE (gossip_message_get_gtype ()) +#define GOSSIP_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_MESSAGE, GossipMessage)) +#define GOSSIP_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_MESSAGE, GossipMessageClass)) +#define GOSSIP_IS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_MESSAGE)) +#define GOSSIP_IS_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_MESSAGE)) +#define GOSSIP_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_MESSAGE, GossipMessageClass)) + +typedef struct _GossipMessage GossipMessage; +typedef struct _GossipMessageClass GossipMessageClass; + +struct _GossipMessage { + GObject parent; +}; + +struct _GossipMessageClass { + GObjectClass parent_class; +}; + +typedef enum { + GOSSIP_MESSAGE_TYPE_NORMAL, + GOSSIP_MESSAGE_TYPE_ACTION, + GOSSIP_MESSAGE_TYPE_NOTICE, + GOSSIP_MESSAGE_TYPE_AUTO_REPLY, + GOSSIP_MESSAGE_TYPE_LAST +} GossipMessageType; + +GType gossip_message_get_gtype (void) G_GNUC_CONST; +GossipMessage * gossip_message_new (const gchar *body); +GossipMessageType gossip_message_get_type (GossipMessage *message); +void gossip_message_set_type (GossipMessage *message, + GossipMessageType type); +GossipContact * gossip_message_get_sender (GossipMessage *message); +void gossip_message_set_sender (GossipMessage *message, + GossipContact *contact); +const gchar * gossip_message_get_body (GossipMessage *message); +void gossip_message_set_body (GossipMessage *message, + const gchar *body); +/* What return value should we have here? */ +GossipTime gossip_message_get_timestamp (GossipMessage *message); +void gossip_message_set_timestamp (GossipMessage *message, + GossipTime timestamp); + +G_END_DECLS + +#endif /* __GOSSIP_MESSAGE_H__ */ diff --git a/libempathy/gossip-paths.c b/libempathy/gossip-paths.c new file mode 100644 index 000000000..aa97bdcc7 --- /dev/null +++ b/libempathy/gossip-paths.c @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Richard Hult <richard@imendio.com> + */ + +#include "config.h" + +#include "gossip-paths.h" + +#define EMPATHY "empathy" + +gchar * +gossip_paths_get_glade_path (const gchar *filename) +{ + return g_build_filename (DATADIR, EMPATHY, filename, NULL); +} + +gchar * +gossip_paths_get_image_path (const gchar *filename) +{ + return g_build_filename (DATADIR, EMPATHY, filename, NULL); +} + +gchar * +gossip_paths_get_dtd_path (const gchar *filename) +{ + return g_build_filename (DATADIR, EMPATHY, filename, NULL); +} + +gchar * +gossip_paths_get_locale_path () +{ + return g_strdup (LOCALEDIR); +} diff --git a/libempathy/gossip-paths.h b/libempathy/gossip-paths.h new file mode 100644 index 000000000..e00a33e28 --- /dev/null +++ b/libempathy/gossip-paths.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Imendio AB + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GOSSIP_PATHS_H__ +#define __GOSSIP_PATHS_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +gchar *gossip_paths_get_glade_path (const gchar *filename); +gchar *gossip_paths_get_image_path (const gchar *filename); +gchar *gossip_paths_get_dtd_path (const gchar *filename); +gchar *gossip_paths_get_sound_path (const gchar *filename); +gchar *gossip_paths_get_locale_path (void); + +G_END_DECLS + +#endif /* __GOSSIP_PATHS_H__ */ diff --git a/libempathy/gossip-presence.c b/libempathy/gossip-presence.c new file mode 100644 index 000000000..e41ae5548 --- /dev/null +++ b/libempathy/gossip-presence.c @@ -0,0 +1,441 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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: Mikael Hallendal <micke@imendio.com> + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> + +#include "gossip-presence.h" +#include "gossip-time.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_PRESENCE, GossipPresencePriv)) + +typedef struct _GossipPresencePriv GossipPresencePriv; + +struct _GossipPresencePriv { + GossipPresenceState state; + + gchar *status; + gchar *resource; + + gint priority; + GossipTime timestamp; +}; + +static void presence_finalize (GObject *object); +static void presence_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void presence_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +enum { + PROP_0, + PROP_STATE, + PROP_STATUS, + PROP_RESOURCE, + PROP_PRIORITY +}; + +G_DEFINE_TYPE (GossipPresence, gossip_presence, G_TYPE_OBJECT); + +static void +gossip_presence_class_init (GossipPresenceClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = presence_finalize; + object_class->get_property = presence_get_property; + object_class->set_property = presence_set_property; + + g_object_class_install_property (object_class, + PROP_STATE, + g_param_spec_int ("state", + "Presence State", + "The current state of the presence", + GOSSIP_PRESENCE_STATE_AVAILABLE, + GOSSIP_PRESENCE_STATE_EXT_AWAY, + GOSSIP_PRESENCE_STATE_AVAILABLE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_STATUS, + g_param_spec_string ("status", + "Presence Status", + "Status string set on presence", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_RESOURCE, + g_param_spec_string ("resource", + "Presence Resource", + "Resource that this presence is for", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_PRIORITY, + g_param_spec_int ("priority", + "Presence Priority", + "Priority value of presence", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GossipPresencePriv)); +} + +static void +gossip_presence_init (GossipPresence *presence) +{ + GossipPresencePriv *priv; + + priv = GET_PRIV (presence); + + priv->state = GOSSIP_PRESENCE_STATE_AVAILABLE; + + priv->status = NULL; + priv->resource = NULL; + + priv->priority = 0; + + priv->timestamp = gossip_time_get_current (); +} + +static void +presence_finalize (GObject *object) +{ + GossipPresencePriv *priv; + + priv = GET_PRIV (object); + + g_free (priv->status); + g_free (priv->resource); + + (G_OBJECT_CLASS (gossip_presence_parent_class)->finalize) (object); +} + +static void +presence_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GossipPresencePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_STATE: + g_value_set_int (value, priv->state); + break; + case PROP_STATUS: + g_value_set_string (value, + gossip_presence_get_status (GOSSIP_PRESENCE (object))); + break; + case PROP_RESOURCE: + g_value_set_string (value, + gossip_presence_get_resource (GOSSIP_PRESENCE (object))); + break; + case PROP_PRIORITY: + g_value_set_int (value, priv->priority); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} +static void +presence_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GossipPresencePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_STATE: + priv->state = g_value_get_int (value); + break; + case PROP_STATUS: + gossip_presence_set_status (GOSSIP_PRESENCE (object), + g_value_get_string (value)); + break; + case PROP_RESOURCE: + gossip_presence_set_resource (GOSSIP_PRESENCE (object), + g_value_get_string (value)); + break; + case PROP_PRIORITY: + priv->priority = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +GossipPresence * +gossip_presence_new (void) +{ + return g_object_new (GOSSIP_TYPE_PRESENCE, NULL); +} + +GossipPresence * +gossip_presence_new_full (GossipPresenceState state, + const gchar *status) +{ + return g_object_new (GOSSIP_TYPE_PRESENCE, + "state", state, + "status", status, + NULL); +} + +const gchar * +gossip_presence_get_resource (GossipPresence *presence) +{ + GossipPresencePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence), NULL); + + priv = GET_PRIV (presence); + + if (priv->resource) { + return priv->resource; + } + + return NULL; +} + +const gchar * +gossip_presence_get_status (GossipPresence *presence) +{ + GossipPresencePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence), + _("Offline")); + + priv = GET_PRIV (presence); + + return priv->status; +} + +gint +gossip_presence_get_priority (GossipPresence *presence) +{ + GossipPresencePriv *priv; + + priv = GET_PRIV (presence); + g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence), 0); + + return priv->priority; +} + +void +gossip_presence_set_resource (GossipPresence *presence, + const gchar *resource) +{ + GossipPresencePriv *priv; + + g_return_if_fail (GOSSIP_IS_PRESENCE (presence)); + g_return_if_fail (resource != NULL); + + priv = GET_PRIV (presence); + + g_free (priv->resource); + priv->resource = g_strdup (resource); + + g_object_notify (G_OBJECT (presence), "resource"); +} + +GossipPresenceState +gossip_presence_get_state (GossipPresence *presence) +{ + GossipPresencePriv *priv; + + g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence), + GOSSIP_PRESENCE_STATE_AVAILABLE); + + priv = GET_PRIV (presence); + + return priv->state; +} + +void +gossip_presence_set_state (GossipPresence *presence, + GossipPresenceState state) +{ + GossipPresencePriv *priv; + + g_return_if_fail (GOSSIP_IS_PRESENCE (presence)); + + priv = GET_PRIV (presence); + + priv->state = state; + + g_object_notify (G_OBJECT (presence), "state"); +} + +void +gossip_presence_set_status (GossipPresence *presence, + const gchar *status) +{ + GossipPresencePriv *priv; + + priv = GET_PRIV (presence); + g_return_if_fail (GOSSIP_IS_PRESENCE (presence)); + + g_free (priv->status); + + if (status) { + priv->status = g_strdup (status); + } else { + priv->status = NULL; + } + + g_object_notify (G_OBJECT (presence), "status"); +} + +void +gossip_presence_set_priority (GossipPresence *presence, + gint priority) +{ + GossipPresencePriv *priv; + + g_return_if_fail (GOSSIP_IS_PRESENCE (presence)); + + priv = GET_PRIV (presence); + + priv->priority = priority; + + g_object_notify (G_OBJECT (presence), "priority"); +} + +gboolean +gossip_presence_resource_equal (gconstpointer a, + gconstpointer b) +{ + GossipPresencePriv *priv1; + GossipPresencePriv *priv2; + + g_return_val_if_fail (GOSSIP_IS_PRESENCE (a), FALSE); + g_return_val_if_fail (GOSSIP_IS_PRESENCE (b), FALSE); + + priv1 = GET_PRIV (a); + priv2 = GET_PRIV (b); + + if (!priv1->resource) { + if (!priv2->resource) { + return TRUE; + } + + return FALSE; + } + + if (!priv2->resource) { + return FALSE; + } + + if (strcmp (priv1->resource, priv2->resource) == 0) { + return TRUE; + } + + return FALSE; +} + +gint +gossip_presence_sort_func (gconstpointer a, + gconstpointer b) +{ + GossipPresencePriv *priv_a; + GossipPresencePriv *priv_b; + gint diff; + + g_return_val_if_fail (GOSSIP_IS_PRESENCE (a), 0); + g_return_val_if_fail (GOSSIP_IS_PRESENCE (b), 0); + + /* We sort here by priority AND status, in theory, the + * priority would be enough for JUST Jabber contacts which + * actually abide to the protocol, but for other protocols and + * dodgy clients, we will sort by: + * + * 1. State + * 2. Priority + * 3. Time it was set (most recent first). + */ + + priv_a = GET_PRIV (a); + priv_b = GET_PRIV (b); + + /* 1. State */ + diff = priv_a->state - priv_b->state; + if (diff != 0) { + return diff < 1 ? -1 : +1; + } + + /* 2. Priority */ + diff = priv_a->priority - priv_b->priority; + if (diff != 0) { + return diff < 1 ? -1 : +1; + } + + /* 3. Time (newest first) */ + diff = priv_b->timestamp - priv_a->timestamp; + if (diff != 0) { + return diff < 1 ? -1 : +1; + } + + /* No real difference, except maybe resource */ + return 0; +} + +const gchar * +gossip_presence_state_get_default_status (GossipPresenceState state) +{ + switch (state) { + case GOSSIP_PRESENCE_STATE_AVAILABLE: + return _("Available"); + break; + + case GOSSIP_PRESENCE_STATE_BUSY: + return _("Busy"); + break; + + case GOSSIP_PRESENCE_STATE_AWAY: + case GOSSIP_PRESENCE_STATE_EXT_AWAY: + return _("Away"); + break; + + case GOSSIP_PRESENCE_STATE_HIDDEN: + case GOSSIP_PRESENCE_STATE_UNAVAILABLE: + return _("Unavailable"); + } + + return _("Available"); +} diff --git a/libempathy/gossip-presence.h b/libempathy/gossip-presence.h new file mode 100644 index 000000000..64a0b8ec3 --- /dev/null +++ b/libempathy/gossip-presence.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_PRESENCE_H__ +#define __GOSSIP_PRESENCE_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GOSSIP_TYPE_PRESENCE (gossip_presence_get_type ()) +#define GOSSIP_PRESENCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_PRESENCE, GossipPresence)) +#define GOSSIP_PRESENCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_PRESENCE, GossipPresenceClass)) +#define GOSSIP_IS_PRESENCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_PRESENCE)) +#define GOSSIP_IS_PRESENCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_PRESENCE)) +#define GOSSIP_PRESENCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_PRESENCE, GossipPresenceClass)) + +typedef struct _GossipPresence GossipPresence; +typedef struct _GossipPresenceClass GossipPresenceClass; + +struct _GossipPresence { + GObject parent; +}; + +struct _GossipPresenceClass { + GObjectClass parent_class; +}; + +typedef enum { + GOSSIP_PRESENCE_STATE_AVAILABLE, + GOSSIP_PRESENCE_STATE_BUSY, + GOSSIP_PRESENCE_STATE_AWAY, + GOSSIP_PRESENCE_STATE_EXT_AWAY, + GOSSIP_PRESENCE_STATE_HIDDEN, /* When you appear offline to others */ + GOSSIP_PRESENCE_STATE_UNAVAILABLE, +} GossipPresenceState; + +GType gossip_presence_get_type (void) G_GNUC_CONST; + +GossipPresence * gossip_presence_new (void); +GossipPresence * gossip_presence_new_full (GossipPresenceState state, + const gchar *status); + +const gchar * gossip_presence_get_resource (GossipPresence *presence); +GossipPresenceState gossip_presence_get_state (GossipPresence *presence); +const gchar * gossip_presence_get_status (GossipPresence *presence); +gint gossip_presence_get_priority (GossipPresence *presence); + +void gossip_presence_set_resource (GossipPresence *presence, + const gchar *resource); +void gossip_presence_set_state (GossipPresence *presence, + GossipPresenceState state); +void gossip_presence_set_status (GossipPresence *presence, + const gchar *status); +void gossip_presence_set_priority (GossipPresence *presence, + gint priority); +gboolean gossip_presence_resource_equal (gconstpointer a, + gconstpointer b); +gint gossip_presence_sort_func (gconstpointer a, + gconstpointer b); + +const gchar * gossip_presence_state_get_default_status (GossipPresenceState state); + +G_END_DECLS + +#endif /* __GOSSIP_PRESENCE_H__ */ + diff --git a/libempathy/gossip-telepathy-group.c b/libempathy/gossip-telepathy-group.c new file mode 100644 index 000000000..4b04ac42f --- /dev/null +++ b/libempathy/gossip-telepathy-group.c @@ -0,0 +1,496 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#include <config.h> + +#include <dbus/dbus-glib.h> +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-chan-iface-group-gen.h> +#include <libtelepathy/tp-constants.h> +#include <libtelepathy/tp-conn.h> + +#include "gossip-debug.h" +#include "gossip-telepathy-group.h" +#include "empathy-marshal.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + GOSSIP_TYPE_TELEPATHY_GROUP, GossipTelepathyGroupPriv)) + +#define DEBUG_DOMAIN "TelepathyGroup" + +struct _GossipTelepathyGroupPriv { + DBusGProxy *group_iface; + TpConn *tp_conn; + TpChan *tp_chan; + gchar *group_name; +}; + +static void gossip_telepathy_group_class_init (GossipTelepathyGroupClass *klass); +static void gossip_telepathy_group_init (GossipTelepathyGroup *group); +static void telepathy_group_finalize (GObject *object); +static void telepathy_group_destroy_cb (DBusGProxy *proxy, + GossipTelepathyGroup *group); +static void telepathy_group_members_changed_cb (DBusGProxy *group_iface, + gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + guint actor, + guint reason, + GossipTelepathyGroup *group); + +enum { + MEMBERS_ADDED, + MEMBERS_REMOVED, + LOCAL_PENDING, + REMOTE_PENDING, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (GossipTelepathyGroup, gossip_telepathy_group, G_TYPE_OBJECT); + +static void +gossip_telepathy_group_class_init (GossipTelepathyGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = telepathy_group_finalize; + + signals[MEMBERS_ADDED] = + g_signal_new ("members-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + G_TYPE_NONE, + 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + + signals[MEMBERS_REMOVED] = + g_signal_new ("members-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + G_TYPE_NONE, + 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + + signals[LOCAL_PENDING] = + g_signal_new ("local-pending", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + G_TYPE_NONE, + 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + + signals[REMOTE_PENDING] = + g_signal_new ("remote-pending", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + empathy_marshal_VOID__POINTER_UINT_UINT_STRING, + G_TYPE_NONE, + 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (GossipTelepathyGroupPriv)); +} + +static void +gossip_telepathy_group_init (GossipTelepathyGroup *group) +{ +} + +static void +telepathy_group_finalize (GObject *object) +{ + GossipTelepathyGroupPriv *priv; + + priv = GET_PRIV (object); + + if (priv->group_iface) { + g_signal_handlers_disconnect_by_func (priv->group_iface, + telepathy_group_destroy_cb, + object); + dbus_g_proxy_disconnect_signal (priv->group_iface, "MembersChanged", + G_CALLBACK (telepathy_group_members_changed_cb), + object); + g_object_unref (priv->group_iface); + } + + if (priv->tp_conn) { + g_object_unref (priv->tp_conn); + } + + if (priv->tp_chan) { + g_object_unref (priv->tp_chan); + } + + g_free (priv->group_name); + + G_OBJECT_CLASS (gossip_telepathy_group_parent_class)->finalize (object); +} + +GossipTelepathyGroup * +gossip_telepathy_group_new (TpChan *tp_chan, + TpConn *tp_conn) +{ + GossipTelepathyGroup *group; + GossipTelepathyGroupPriv *priv; + DBusGProxy *group_iface; + + g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL); + + group_iface = tp_chan_get_interface (tp_chan, + TELEPATHY_CHAN_IFACE_GROUP_QUARK); + g_return_val_if_fail (group_iface != NULL, NULL); + + group = g_object_new (GOSSIP_TYPE_TELEPATHY_GROUP, NULL); + priv = GET_PRIV (group); + + priv->tp_conn = g_object_ref (tp_conn); + priv->tp_chan = g_object_ref (tp_chan); + priv->group_iface = g_object_ref (group_iface); + + dbus_g_proxy_connect_signal (priv->group_iface, "MembersChanged", + G_CALLBACK (telepathy_group_members_changed_cb), + group, NULL); + g_signal_connect (group_iface, "destroy", + G_CALLBACK (telepathy_group_destroy_cb), + group); + + + return group; +} + +void +gossip_telepathy_group_add_members (GossipTelepathyGroup *group, + GArray *handles, + const gchar *message) +{ + GossipTelepathyGroupPriv *priv; + GError *error = NULL; + + g_return_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group)); + g_return_if_fail (handles != NULL); + + priv = GET_PRIV (group); + + if (!tp_chan_iface_group_add_members (priv->group_iface, + handles, + message, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Failed to add members: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } +} + +void +gossip_telepathy_group_add_member (GossipTelepathyGroup *group, + guint handle, + const gchar *message) +{ + GArray *handles; + + handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (handles, handle); + + gossip_telepathy_group_add_members (group, handles, message); + + g_array_free (handles, TRUE); +} + +void +gossip_telepathy_group_remove_members (GossipTelepathyGroup *group, + GArray *handles, + const gchar *message) +{ + GossipTelepathyGroupPriv *priv; + GError *error = NULL; + + g_return_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group)); + + priv = GET_PRIV (group); + + if (!tp_chan_iface_group_remove_members (priv->group_iface, + handles, + message, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Failed to remove members: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } +} + +void +gossip_telepathy_group_remove_member (GossipTelepathyGroup *group, + guint handle, + const gchar *message) +{ + GArray *handles; + + g_return_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group)); + + handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (handles, handle); + + gossip_telepathy_group_remove_members (group, handles, message); + + g_array_free (handles, TRUE); +} + +GArray * +gossip_telepathy_group_get_members (GossipTelepathyGroup *group) +{ + GossipTelepathyGroupPriv *priv; + GArray *members; + GError *error = NULL; + + g_return_val_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group), NULL); + + priv = GET_PRIV (group); + + if (!tp_chan_iface_group_get_members (priv->group_iface, + &members, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't get members: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return NULL; + } + + return members; +} + +void +gossip_telepathy_group_get_all_members (GossipTelepathyGroup *group, + GArray **members, + GArray **local_pending, + GArray **remote_pending) +{ + GossipTelepathyGroupPriv *priv; + GError *error = NULL; + + g_return_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group)); + + priv = GET_PRIV (group); + + if (!tp_chan_iface_group_get_all_members (priv->group_iface, + members, + local_pending, + remote_pending, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't get all members: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } +} + +GPtrArray * +gossip_telepathy_group_get_local_pending_members_with_info (GossipTelepathyGroup *group) +{ + GossipTelepathyGroupPriv *priv; + GPtrArray *info = NULL; + GError *error = NULL; + + g_return_val_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group), NULL); + + priv = GET_PRIV (group); + + if (!tp_chan_iface_group_get_local_pending_members_with_info (priv->group_iface, + &info, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "GetLocalPendingMembersWithInfo failed: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + + return info; +} + +static void +telepathy_group_destroy_cb (DBusGProxy *proxy, + GossipTelepathyGroup *group) +{ + GossipTelepathyGroupPriv *priv; + + priv = GET_PRIV (group); + + g_object_unref (priv->group_iface); + g_object_unref (priv->tp_conn); + g_object_unref (priv->tp_chan); + priv->group_iface = NULL; + priv->tp_chan = NULL; + priv->tp_conn = NULL; +} + +static void +telepathy_group_members_changed_cb (DBusGProxy *group_iface, + gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + guint actor, + guint reason, + GossipTelepathyGroup *group) +{ + GossipTelepathyGroupPriv *priv; + + priv = GET_PRIV (group); + + /* emit signals */ + if (added->len > 0) { + g_signal_emit (group, signals[MEMBERS_ADDED], 0, + added, actor, reason, message); + } + if (removed->len > 0) { + g_signal_emit (group, signals[MEMBERS_REMOVED], 0, + removed, actor, reason, message); + } + if (local_pending->len > 0) { + g_signal_emit (group, signals[LOCAL_PENDING], 0, + local_pending, actor, reason, message); + } + if (remote_pending->len > 0) { + g_signal_emit (group, signals[REMOTE_PENDING], 0, + remote_pending, actor, reason, message); + } +} + +const gchar * +gossip_telepathy_group_get_name (GossipTelepathyGroup *group) +{ + TelepathyHandleType handle_type; + guint channel_handle; + GArray *group_handles; + gchar **group_names; + GError *error = NULL; + + GossipTelepathyGroupPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group), NULL); + + priv = GET_PRIV (group); + + /* Lazy initialisation */ + if (priv->group_name) { + return priv->group_name; + } + + if (!tp_chan_get_handle (DBUS_G_PROXY (priv->tp_chan), + &handle_type, + &channel_handle, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't retreive channel handle for group: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return NULL; + } + + group_handles = g_array_new (FALSE, FALSE, sizeof (gint)); + g_array_append_val (group_handles, channel_handle); + if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), + handle_type, + group_handles, + &group_names, + &error)) { + gossip_debug (DEBUG_DOMAIN, + "Couldn't get group name: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_array_free (group_handles, TRUE); + return NULL; + } + + priv->group_name = *group_names; + g_array_free (group_handles, TRUE); + g_free (group_names); + + return priv->group_name; +} + +guint +gossip_telepathy_group_get_self_handle (GossipTelepathyGroup *group) +{ + GossipTelepathyGroupPriv *priv; + guint handle; + GError *error = NULL; + + g_return_val_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group), 0 ); + + priv = GET_PRIV (group); + + if (!tp_chan_iface_group_get_self_handle (priv->group_iface, &handle, &error)) { + gossip_debug (DEBUG_DOMAIN, + "Failed to get self handle: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + return 0; + } + + return handle; +} + +const gchar * +gossip_telepathy_group_get_object_path (GossipTelepathyGroup *group) +{ + GossipTelepathyGroupPriv *priv; + + g_return_val_if_fail (GOSSIP_IS_TELEPATHY_GROUP (group), NULL); + + priv = GET_PRIV (group); + + return dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)); +} + +gboolean +gossip_telepathy_group_is_member (GossipTelepathyGroup *group, + guint handle) +{ + GArray *members; + guint i; + gboolean found = FALSE; + + members = gossip_telepathy_group_get_members (group); + for (i = 0; i < members->len; i++) { + if (g_array_index (members, guint, i) == handle) { + found = TRUE; + break; + } + } + g_array_free (members, TRUE); + + return found; +} + diff --git a/libempathy/gossip-telepathy-group.h b/libempathy/gossip-telepathy-group.h new file mode 100644 index 000000000..9c61bdbc4 --- /dev/null +++ b/libempathy/gossip-telepathy-group.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Xavier Claessens <xclaesse@gmail.com> + * + * 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. + */ + +#ifndef __GOSSIP_TELEPATHY_GROUP_H__ +#define __GOSSIP_TELEPATHY_GROUP_H__ + +#include <glib.h> + +#include <libtelepathy/tp-chan.h> + +G_BEGIN_DECLS + +#define GOSSIP_TYPE_TELEPATHY_GROUP (gossip_telepathy_group_get_type ()) +#define GOSSIP_TELEPATHY_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_TELEPATHY_GROUP, GossipTelepathyGroup)) +#define GOSSIP_TELEPATHY_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GOSSIP_TYPE_TELEPATHY_GROUP, GossipTelepathyGroupClass)) +#define GOSSIP_IS_TELEPATHY_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_TELEPATHY_GROUP)) +#define GOSSIP_IS_TELEPATHY_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_TELEPATHY_GROUP)) +#define GOSSIP_TELEPATHY_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_TELEPATHY_GROUP, GossipTelepathyGroupClass)) + +typedef struct _GossipTelepathyGroup GossipTelepathyGroup; +typedef struct _GossipTelepathyGroupClass GossipTelepathyGroupClass; +typedef struct _GossipTelepathyGroupPriv GossipTelepathyGroupPriv; + +struct _GossipTelepathyGroup { + GObject parent; +}; + +struct _GossipTelepathyGroupClass { + GObjectClass parent_class; +}; + +GType gossip_telepathy_group_get_type (void) G_GNUC_CONST; +GossipTelepathyGroup *gossip_telepathy_group_new (TpChan *tp_chan, + TpConn *tp_conn); +void gossip_telepathy_group_add_members (GossipTelepathyGroup *group, + GArray *handles, + const gchar *message); +void gossip_telepathy_group_add_member (GossipTelepathyGroup *group, + guint handle, + const gchar *message); +void gossip_telepathy_group_remove_members (GossipTelepathyGroup *group, + GArray *handle, + const gchar *message); +void gossip_telepathy_group_remove_member (GossipTelepathyGroup *group, + guint handle, + const gchar *message); +GArray * gossip_telepathy_group_get_members (GossipTelepathyGroup *group); +void gossip_telepathy_group_get_all_members (GossipTelepathyGroup *group, + GArray **members, + GArray **local_pending, + GArray **remote_pending); +GPtrArray * gossip_telepathy_group_get_local_pending_members_with_info + (GossipTelepathyGroup *group); +const gchar * gossip_telepathy_group_get_name (GossipTelepathyGroup *group); +guint gossip_telepathy_group_get_self_handle (GossipTelepathyGroup *group); +const gchar * gossip_telepathy_group_get_object_path (GossipTelepathyGroup *group); +gboolean gossip_telepathy_group_is_member (GossipTelepathyGroup *group, + guint handle); + +G_END_DECLS + +#endif /* __GOSSIP_TELEPATHY_GROUP_H__ */ diff --git a/libempathy/gossip-time.c b/libempathy/gossip-time.c new file mode 100644 index 000000000..a1956354e --- /dev/null +++ b/libempathy/gossip-time.c @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * + * 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: Richard Hult <richard@imendio.com> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gossip-time.h" + +/* Note: GossipTime is always in UTC. */ + +GossipTime +gossip_time_get_current (void) +{ + return time (NULL); +} + +time_t +gossip_time_get_local_time (struct tm *tm) +{ + const gchar *timezone; + time_t t; + + timezone = g_getenv ("TZ"); + g_setenv ("TZ", "", TRUE); + + tzset (); + + t = mktime (tm); + + if (timezone) { + g_setenv ("TZ", timezone, TRUE); + } else { + g_unsetenv ("TZ"); + } + + tzset (); + + return t; +} + +/* The format is: "20021209T23:51:30" and is in UTC. 0 is returned on + * failure. The alternative format "20021209" is also accepted. + */ +GossipTime +gossip_time_parse (const gchar *str) +{ + struct tm tm; + gint year, month; + gint n_parsed; + + memset (&tm, 0, sizeof (struct tm)); + + n_parsed = sscanf (str, "%4d%2d%2dT%2d:%2d:%2d", + &year, &month, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec); + if (n_parsed != 3 && n_parsed != 6) { + return 0; + } + + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_isdst = -1; + + return gossip_time_get_local_time (&tm); +} + +/* Converts the UTC timestamp to a string, also in UTC. Returns NULL on failure. */ +gchar * +gossip_time_to_string_utc (GossipTime t, + const gchar *format) +{ + gchar stamp[128]; + struct tm *tm; + + g_return_val_if_fail (format != NULL, NULL); + + tm = gmtime (&t); + if (strftime (stamp, sizeof (stamp), format, tm) == 0) { + return NULL; + } + + return g_strdup (stamp); +} + +/* Converts the UTC timestamp to a string, in local time. Returns NULL on failure. */ +gchar * +gossip_time_to_string_local (GossipTime t, + const gchar *format) +{ + gchar stamp[128]; + struct tm *tm; + + g_return_val_if_fail (format != NULL, NULL); + + tm = localtime (&t); + if (strftime (stamp, sizeof (stamp), format, tm) == 0) { + return NULL; + } + + return g_strdup (stamp); +} + diff --git a/libempathy/gossip-time.h b/libempathy/gossip-time.h new file mode 100644 index 000000000..06057aa52 --- /dev/null +++ b/libempathy/gossip-time.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_TIME_H__ +#define __GOSSIP_TIME_H__ + +#define __USE_XOPEN +#include <time.h> + +#include <glib.h> + +G_BEGIN_DECLS + +#define GOSSIP_TIME_FORMAT_DISPLAY_SHORT "%H:%M" +#define GOSSIP_TIME_FORMAT_DISPLAY_LONG "%a %d %b %Y" + +/* Note: Always in UTC. */ +typedef long GossipTime; + +GossipTime gossip_time_get_current (void); +time_t gossip_time_get_local_time (struct tm *tm); +GossipTime gossip_time_parse (const gchar *str); +GossipTime gossip_time_parse_format (const gchar *str, + const gchar *format); +gchar *gossip_time_to_string_utc (GossipTime t, + const gchar *format); +gchar *gossip_time_to_string_local (GossipTime t, + const gchar *format); + +G_END_DECLS + +#endif /* __GOSSIP_TIME_H__ */ + diff --git a/libempathy/gossip-utils.c b/libempathy/gossip-utils.c new file mode 100644 index 000000000..24f5cd432 --- /dev/null +++ b/libempathy/gossip-utils.c @@ -0,0 +1,447 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * + * 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: Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + */ + +#include "config.h" + +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <regex.h> + +#include <glib/gi18n.h> + +#include <libxml/uri.h> +#include <libmissioncontrol/mc-account.h> + +#include "gossip-debug.h" +#include "gossip-utils.h" +#include "gossip-paths.h" +#include "empathy-session.h" +#include "empathy-contact-manager.h" + +#define DEBUG_DOMAIN "Utils" + +static void regex_init (void); + +gchar * +gossip_substring (const gchar *str, + gint start, + gint end) +{ + return g_strndup (str + start, end - start); +} + +/* + * Regular Expression code to match urls. + */ +#define USERCHARS "-A-Za-z0-9" +#define PASSCHARS "-A-Za-z0-9,?;.:/!%$^*&~\"#'" +#define HOSTCHARS "-A-Za-z0-9" +#define PATHCHARS "-A-Za-z0-9_$.+!*(),;:@&=?/~#%" +#define SCHEME "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)" +#define USER "[" USERCHARS "]+(:["PASSCHARS "]+)?" +#define URLPATH "/[" PATHCHARS "]*[^]'.}>) \t\r\n,\\\"]" + +static regex_t dingus[GOSSIP_REGEX_ALL]; + +static void +regex_init (void) +{ + static gboolean inited = FALSE; + const gchar *expression; + gint i; + + if (inited) { + return; + } + + for (i = 0; i < GOSSIP_REGEX_ALL; i++) { + switch (i) { + case GOSSIP_REGEX_AS_IS: + expression = + SCHEME "//(" USER "@)?[" HOSTCHARS ".]+" + "(:[0-9]+)?(" URLPATH ")?"; + break; + case GOSSIP_REGEX_BROWSER: + expression = + "(www|ftp)[" HOSTCHARS "]*\\.[" HOSTCHARS ".]+" + "(:[0-9]+)?(" URLPATH ")?"; + break; + case GOSSIP_REGEX_EMAIL: + expression = + "(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9]" + "[a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+"; + break; + case GOSSIP_REGEX_OTHER: + expression = + "news:[-A-Z\\^_a-z{|}~!\"#$%&'()*+,./0-9;:=?`]+" + "@[" HOSTCHARS ".]+(:[0-9]+)?"; + break; + default: + /* Silence the compiler. */ + expression = NULL; + continue; + } + + memset (&dingus[i], 0, sizeof (regex_t)); + regcomp (&dingus[i], expression, REG_EXTENDED | REG_ICASE); + } + + inited = TRUE; +} + +gint +gossip_regex_match (GossipRegExType type, + const gchar *msg, + GArray *start, + GArray *end) +{ + regmatch_t matches[1]; + gint ret = 0; + gint num_matches = 0; + gint offset = 0; + gint i; + + g_return_val_if_fail (type >= 0 || type <= GOSSIP_REGEX_ALL, 0); + + regex_init (); + + while (!ret && type != GOSSIP_REGEX_ALL) { + ret = regexec (&dingus[type], msg + offset, 1, matches, 0); + if (ret == 0) { + gint s; + + num_matches++; + + s = matches[0].rm_so + offset; + offset = matches[0].rm_eo + offset; + + g_array_append_val (start, s); + g_array_append_val (end, offset); + } + } + + if (type != GOSSIP_REGEX_ALL) { + gossip_debug (DEBUG_DOMAIN, + "Found %d matches for regex type:%d", + num_matches, type); + return num_matches; + } + + /* If GOSSIP_REGEX_ALL then we run ALL regex's on the string. */ + for (i = 0; i < GOSSIP_REGEX_ALL; i++, ret = 0) { + while (!ret) { + ret = regexec (&dingus[i], msg + offset, 1, matches, 0); + if (ret == 0) { + gint s; + + num_matches++; + + s = matches[0].rm_so + offset; + offset = matches[0].rm_eo + offset; + + g_array_append_val (start, s); + g_array_append_val (end, offset); + } + } + } + + gossip_debug (DEBUG_DOMAIN, + "Found %d matches for ALL regex types", + num_matches); + + return num_matches; +} + +gint +gossip_strcasecmp (const gchar *s1, + const gchar *s2) +{ + return gossip_strncasecmp (s1, s2, -1); +} + +gint +gossip_strncasecmp (const gchar *s1, + const gchar *s2, + gsize n) +{ + gchar *u1, *u2; + gint ret_val; + + u1 = g_utf8_casefold (s1, n); + u2 = g_utf8_casefold (s2, n); + + ret_val = g_utf8_collate (u1, u2); + g_free (u1); + g_free (u2); + + return ret_val; +} + +gboolean +gossip_xml_validate (xmlDoc *doc, + const gchar *dtd_filename) +{ + gchar *path, *escaped; + xmlValidCtxt cvp; + xmlDtd *dtd; + gboolean ret; + + path = gossip_paths_get_dtd_path (dtd_filename); + + /* The list of valid chars is taken from libxml. */ + escaped = xmlURIEscapeStr (path, ":@&=+$,/?;"); + + g_free (path); + + memset (&cvp, 0, sizeof (cvp)); + dtd = xmlParseDTD (NULL, escaped); + ret = xmlValidateDtd (&cvp, doc, dtd); + + xmlFree (escaped); + xmlFreeDtd (dtd); + + return ret; +} + +xmlNodePtr +gossip_xml_node_get_child (xmlNodePtr node, + const gchar *child_name) +{ + xmlNodePtr l; + + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (child_name != NULL, NULL); + + for (l = node->children; l; l = l->next) { + if (l->name && strcmp (l->name, child_name) == 0) { + return l; + } + } + + return NULL; +} + +xmlChar * +gossip_xml_node_get_child_content (xmlNodePtr node, + const gchar *child_name) +{ + xmlNodePtr l; + + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (child_name != NULL, NULL); + + l = gossip_xml_node_get_child (node, child_name); + if (l) { + return xmlNodeGetContent (l); + } + + return NULL; +} + +xmlNodePtr +gossip_xml_node_find_child_prop_value (xmlNodePtr node, + const gchar *prop_name, + const gchar *prop_value) +{ + xmlNodePtr l; + xmlNodePtr found = NULL; + + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (prop_name != NULL, NULL); + g_return_val_if_fail (prop_value != NULL, NULL); + + for (l = node->children; l && !found; l = l->next) { + xmlChar *prop; + + if (!xmlHasProp (l, prop_name)) { + continue; + } + + prop = xmlGetProp (l, prop_name); + if (prop && strcmp (prop, prop_value) == 0) { + found = l; + } + + xmlFree (prop); + } + + return found; +} + +GType +gossip_dbus_type_to_g_type (const gchar *dbus_type_string) +{ + if (dbus_type_string == NULL) + return G_TYPE_NONE; + + if (dbus_type_string[0] == 's') { + return G_TYPE_STRING; + } + else if (dbus_type_string[0] == 'b') { + return G_TYPE_BOOLEAN; + } + else if (dbus_type_string[0] == 'q') { + return G_TYPE_UINT; + } + else if (dbus_type_string[0] == 'n') { + return G_TYPE_INT; + } + + g_assert_not_reached (); + return G_TYPE_NONE; +} + +const gchar * +gossip_g_type_to_dbus_type (GType g_type) +{ + switch (g_type) + { + case G_TYPE_STRING: + return "s"; + case G_TYPE_BOOLEAN: + return "b"; + case G_TYPE_UINT: + return "q"; + case G_TYPE_INT: + return "n"; + default: + g_assert_not_reached (); + } + + return NULL; +} + +gchar * +gossip_g_value_to_string (const GValue *value) +{ + gchar *return_string = NULL; + GValue string_g_value = {0, }; + + g_value_init (&string_g_value, G_TYPE_STRING); + g_value_transform (value, &string_g_value); + return_string = g_value_dup_string (&string_g_value); + g_value_unset (&string_g_value); + + return return_string; +} + +GValue * +gossip_string_to_g_value (const gchar *str, GType type) +{ + GValue *g_value; + + g_value = g_new0 (GValue, 1); + g_value_init (g_value, type); + + switch (type) { + case G_TYPE_STRING: + g_value_set_string (g_value, str); + break; + case G_TYPE_BOOLEAN: + g_value_set_boolean (g_value, (str[0] == 'y' || str[0] == 'T')); + break; + case G_TYPE_UINT: + g_value_set_uint (g_value, atoi (str)); + break; + case G_TYPE_INT: + g_value_set_int (g_value, atoi (str)); + break; + default: + g_assert_not_reached (); + } + + return g_value; +} + +gboolean +gossip_g_value_equal (const GValue *value1, + const GValue *value2) +{ + GType type; + + g_return_val_if_fail (value1 != NULL, FALSE); + g_return_val_if_fail (value2 != NULL, FALSE); + + type = G_VALUE_TYPE (value1); + if (type != G_VALUE_TYPE (value2)) { + return FALSE; + } + + switch (type) + { + case G_TYPE_STRING: { + const gchar *str1; + const gchar *str2; + + str1 = g_value_get_string (value1); + str2 = g_value_get_string (value2); + return (str1 && str2 && strcmp (str1, str2) == 0) || + (G_STR_EMPTY (str1) && G_STR_EMPTY (str2)); + } + case G_TYPE_BOOLEAN: + return g_value_get_boolean (value1) == g_value_get_boolean (value2); + case G_TYPE_UINT: + return g_value_get_uint (value1) == g_value_get_uint (value2); + case G_TYPE_INT: + return g_value_get_int (value1) == g_value_get_int (value2); + default: + g_warning ("Unsupported GType in value comparaison"); + } + + return FALSE; +} + +guint +gossip_account_hash (gconstpointer key) +{ + return g_str_hash (mc_account_get_unique_name (MC_ACCOUNT (key))); +} + +gboolean +gossip_account_equal (gconstpointer a, + gconstpointer b) +{ + const gchar *name_a; + const gchar *name_b; + + name_a = mc_account_get_unique_name (MC_ACCOUNT (a)); + name_b = mc_account_get_unique_name (MC_ACCOUNT (b)); + + return g_str_equal (name_a, name_b); +} + +GossipContact * +gossip_get_own_contact_from_contact (GossipContact *contact) +{ + EmpathyContactManager *manager; + McAccount *account; + + g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL); + + manager = empathy_session_get_contact_manager (); + account = gossip_contact_get_account (contact); + + return empathy_contact_manager_get_own (manager, account); +} + diff --git a/libempathy/gossip-utils.h b/libempathy/gossip-utils.h new file mode 100644 index 000000000..7f0ab90ef --- /dev/null +++ b/libempathy/gossip-utils.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2005 Imendio AB + * + * 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. + */ + +#ifndef __GOSSIP_UTILS_H__ +#define __GOSSIP_UTILS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> + +#include "gossip-contact.h" + +G_BEGIN_DECLS + +#define G_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0') + +typedef enum { + GOSSIP_REGEX_AS_IS, + GOSSIP_REGEX_BROWSER, + GOSSIP_REGEX_EMAIL, + GOSSIP_REGEX_OTHER, + GOSSIP_REGEX_ALL, +} GossipRegExType; + +/* Regular expressions */ +gchar * gossip_substring (const gchar *str, + gint start, + gint end); +gint gossip_regex_match (GossipRegExType type, + const gchar *msg, + GArray *start, + GArray *end); + +/* Strings */ +gint gossip_strcasecmp (const gchar *s1, + const gchar *s2); +gint gossip_strncasecmp (const gchar *s1, + const gchar *s2, + gsize n); + +/* XML */ +gboolean gossip_xml_validate (xmlDoc *doc, + const gchar *dtd_filename); +xmlNodePtr gossip_xml_node_get_child (xmlNodePtr node, + const gchar *child_name); +xmlChar * gossip_xml_node_get_child_content (xmlNodePtr node, + const gchar *child_name); +xmlNodePtr gossip_xml_node_find_child_prop_value (xmlNodePtr node, + const gchar *prop_name, + const gchar *prop_value); + + +/* GValue/GType */ +GType gossip_dbus_type_to_g_type (const gchar *dbus_type_string); +const gchar *gossip_g_type_to_dbus_type (GType g_type); +gchar * gossip_g_value_to_string (const GValue *value); +GValue * gossip_string_to_g_value (const gchar *str, + GType type); +gboolean gossip_g_value_equal (const GValue *value1, + const GValue *value2); + +guint gossip_account_hash (gconstpointer key); +gboolean gossip_account_equal (gconstpointer a, + gconstpointer b); +GossipContact * gossip_get_own_contact_from_contact (GossipContact *contact); +G_END_DECLS + +#endif /* __GOSSIP_UTILS_H__ */ |