summaryrefslogtreecommitdiff
path: root/src/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/connection.c')
-rw-r--r--src/connection.c1702
1 files changed, 1652 insertions, 50 deletions
diff --git a/src/connection.c b/src/connection.c
index 2ab791a0..a0e19e4d 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -61,11 +61,31 @@
#include "plugin-loader.h"
+#ifdef ENABLE_OLPC
+#include "olpc-activity-manager.h"
+#endif
+
#include <extensions/extensions.h>
#define DEBUG_FLAG DEBUG_CONNECTION
#include "debug.h"
+#ifdef ENABLE_OLPC
+
+#define ACTIVITY_PAIR_TYPE \
+ (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, \
+ G_TYPE_INVALID))
+
+static void
+salut_connection_olpc_buddy_info_iface_init (gpointer g_iface,
+ gpointer iface_data);
+
+static void
+salut_connection_olpc_activity_properties_iface_init (gpointer g_iface,
+ gpointer iface_data);
+
+#endif
+
static void
salut_connection_aliasing_service_iface_init (gpointer g_iface,
gpointer iface_data);
@@ -96,8 +116,8 @@ G_DEFINE_TYPE_WITH_CODE(SalutConnection,
tp_contacts_mixin_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST,
tp_base_contact_list_mixin_list_iface_init);
- G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
- tp_presence_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+ tp_presence_mixin_simple_presence_iface_init);
G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS,
salut_connection_avatar_service_iface_init);
G_IMPLEMENT_INTERFACE
@@ -109,8 +129,19 @@ G_DEFINE_TYPE_WITH_CODE(SalutConnection,
salut_conn_future_iface_init);
G_IMPLEMENT_INTERFACE (SALUT_TYPE_PLUGIN_CONNECTION,
salut_plugin_connection_iface_init);
+#ifdef ENABLE_OLPC
+ G_IMPLEMENT_INTERFACE (SALUT_TYPE_SVC_OLPC_BUDDY_INFO,
+ salut_connection_olpc_buddy_info_iface_init);
+ G_IMPLEMENT_INTERFACE (SALUT_TYPE_SVC_OLPC_ACTIVITY_PROPERTIES,
+ salut_connection_olpc_activity_properties_iface_init);
+#endif
)
+#ifdef ENABLE_OLPC
+static gboolean uninvite_stanza_callback (WockyPorter *porter,
+ WockyStanza *stanza, gpointer user_data);
+#endif
+
/* properties */
enum {
PROP_NICKNAME = 1,
@@ -127,6 +158,9 @@ enum {
PROP_SELF,
PROP_XCM,
PROP_SI_BYTESTREAM_MANAGER,
+#ifdef ENABLE_OLPC
+ PROP_OLPC_ACTIVITY_MANAGER,
+#endif
PROP_BACKEND,
PROP_DNSSD_NAME,
LAST_PROP
@@ -143,6 +177,10 @@ struct _SalutConnectionPrivate
gchar *last_name;
gchar *jid;
gchar *email;
+#ifdef ENABLE_OLPC
+ gchar *olpc_color;
+ GArray *olpc_key;
+#endif
/* Discovery client for browsing and resolving */
SalutDiscoveryClient *discovery_client;
@@ -185,6 +223,11 @@ struct _SalutConnectionPrivate
/* gchar *interface → GList<DBusGMethodInvocation> */
GHashTable *pending_sidecars;
+#ifdef ENABLE_OLPC
+ SalutOlpcActivityManager *olpc_activity_manager;
+ guint uninvite_handler_id;
+#endif
+
/* timer used when trying to properly disconnect */
guint disconnect_timer;
@@ -213,6 +256,9 @@ salut_connection_create_handle_repos (TpBaseConnection *self,
TpHandleRepoIface *repos[TP_NUM_HANDLE_TYPES]);
static GPtrArray *
+salut_connection_create_channel_factories (TpBaseConnection *self);
+
+static GPtrArray *
salut_connection_create_channel_managers (TpBaseConnection *self);
static gchar *
@@ -261,21 +307,6 @@ static TpDBusPropertiesMixinPropImpl conn_avatars_properties[] = {
{ NULL }
};
-static TpDBusPropertiesMixinPropImpl conn_aliasing_properties[] = {
- { "AliasFlags", GUINT_TO_POINTER (0), NULL },
- { NULL }
-};
-
-static void
-conn_aliasing_properties_getter (GObject *object,
- GQuark interface,
- GQuark name,
- GValue *value,
- gpointer getter_data)
-{
- g_value_set_uint (value, GPOINTER_TO_UINT (getter_data));
-}
-
static void
salut_connection_init (SalutConnection *obj)
{
@@ -303,6 +334,10 @@ salut_connection_init (SalutConnection *obj)
priv->last_name = NULL;
priv->jid = NULL;
priv->email = NULL;
+#ifdef ENABLE_OLPC
+ priv->olpc_color = NULL;
+ priv->olpc_key = NULL;
+#endif
priv->discovery_client = NULL;
priv->self = NULL;
@@ -319,9 +354,6 @@ static void
sidecars_conn_status_changed_cb (SalutConnection *conn,
guint status, guint reason, gpointer unused);
-static void _contact_manager_contact_change_cb (SalutContactManager *mgr,
- SalutContact *contact, int changes, gpointer data);
-
static void
salut_connection_constructed (GObject *obj)
{
@@ -337,9 +369,8 @@ salut_connection_constructed (GObject *obj)
G_STRUCT_OFFSET (SalutConnection, contacts_mixin));
tp_base_connection_register_with_contacts_mixin (base);
- tp_presence_mixin_register_with_contacts_mixin (obj);
- tp_base_contact_list_mixin_register_with_contacts_mixin (
- TP_BASE_CONTACT_LIST (self->priv->contact_manager), base);
+ tp_presence_mixin_simple_presence_register_with_contacts_mixin (obj);
+ tp_base_contact_list_mixin_register_with_contacts_mixin (base);
tp_contacts_mixin_add_contact_attributes_iface (obj,
TP_IFACE_CONNECTION_INTERFACE_AVATARS,
@@ -421,6 +452,11 @@ salut_connection_get_property (GObject *object,
g_value_set_object (value, priv->si_bytestream_manager);
break;
#endif
+#ifdef ENABLE_OLPC
+ case PROP_OLPC_ACTIVITY_MANAGER:
+ g_value_set_object (value, priv->olpc_activity_manager);
+ break;
+#endif
case PROP_BACKEND:
g_value_set_gtype (value, priv->backend_type);
break;
@@ -547,15 +583,23 @@ make_presence_opt_args (SalutPresenceId presence, const gchar *message)
static GHashTable *
get_contact_statuses (GObject *obj,
- const GArray *handles)
+ const GArray *handles,
+ GError **error)
{
SalutConnection *self = SALUT_CONNECTION (obj);
SalutConnectionPrivate *priv = self->priv;
TpBaseConnection *base = (TpBaseConnection *) self;
TpHandle self_handle = tp_base_connection_get_self_handle (base);
+ TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
GHashTable *ret;
guint i;
+ if (!tp_handles_are_valid (handle_repo, handles, FALSE, error))
+ {
+ return NULL;
+ }
+
ret = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) tp_presence_status_free);
@@ -672,12 +716,16 @@ static const gchar *interfaces [] = {
TP_IFACE_CONNECTION_INTERFACE_ALIASING,
TP_IFACE_CONNECTION_INTERFACE_AVATARS,
TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
- TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
+ TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST,
SALUT_IFACE_CONNECTION_FUTURE,
+#ifdef ENABLE_OLPC
+ SALUT_IFACE_OLPC_BUDDY_INFO,
+ SALUT_IFACE_OLPC_ACTIVITY_PROPERTIES,
+#endif
NULL };
static GPtrArray *
@@ -714,11 +762,6 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class)
NULL,
conn_avatars_properties,
},
- { TP_IFACE_CONNECTION_INTERFACE_ALIASING,
- conn_aliasing_properties_getter,
- NULL,
- conn_aliasing_properties,
- },
{ NULL }
};
@@ -735,6 +778,8 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class)
tp_connection_class->create_handle_repos =
salut_connection_create_handle_repos;
+ tp_connection_class->create_channel_factories =
+ salut_connection_create_channel_factories;
tp_connection_class->create_channel_managers =
salut_connection_create_channel_managers;
tp_connection_class->get_unique_connection_name =
@@ -754,7 +799,7 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class)
is_presence_status_available, get_contact_statuses, set_own_status,
presence_statuses);
- tp_presence_mixin_init_dbus_properties (object_class);
+ tp_presence_mixin_simple_presence_init_dbus_properties (object_class);
tp_contacts_mixin_class_init (object_class,
G_STRUCT_OFFSET (SalutConnectionClass, contacts_mixin));
@@ -857,6 +902,17 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class)
g_object_class_install_property (object_class, PROP_SI_BYTESTREAM_MANAGER,
param_spec);
+#ifdef ENABLE_OLPC
+ param_spec = g_param_spec_object (
+ "olpc-activity-manager",
+ "SalutOlpcActivityManager object",
+ "The OLPC activity Manager associated with this Salut Connection",
+ SALUT_TYPE_OLPC_ACTIVITY_MANAGER,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_OLPC_ACTIVITY_MANAGER,
+ param_spec);
+#endif
+
param_spec = g_param_spec_gtype (
"backend-type",
"backend type",
@@ -921,6 +977,19 @@ salut_connection_dispose (GObject *object)
priv->self = NULL;
}
+#ifdef ENABLE_OLPC
+ {
+ wocky_porter_unregister_handler (self->porter, priv->uninvite_handler_id);
+ priv->uninvite_handler_id = 0;
+ }
+
+ if (priv->olpc_activity_manager != NULL)
+ {
+ g_object_unref (priv->olpc_activity_manager);
+ priv->olpc_activity_manager = NULL;
+ }
+#endif
+
if (self->session != NULL)
{
g_object_unref (self->session);
@@ -969,6 +1038,11 @@ salut_connection_finalize (GObject *object)
g_free (priv->last_name);
g_free (priv->email);
g_free (priv->jid);
+#ifdef ENABLE_OLPC
+ if (priv->olpc_key != NULL)
+ g_array_unref (priv->olpc_key);
+ g_free (priv->olpc_color);
+#endif
g_free (priv->dnssd_name);
tp_contacts_mixin_finalize (G_OBJECT(self));
@@ -1074,6 +1148,19 @@ _self_established_cb (SalutSelf *s, gpointer data)
}
#endif
+#ifdef ENABLE_OLPC
+ if (!salut_olpc_activity_manager_start (priv->olpc_activity_manager, &error))
+ {
+ DEBUG ("failed to start olpc activity manager: %s", error->message);
+ g_clear_error (&error);
+
+ tp_base_connection_change_status ( TP_BASE_CONNECTION (base),
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+ return;
+ }
+#endif
+
tp_base_connection_change_status (base, TP_CONNECTION_STATUS_CONNECTED,
TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED);
}
@@ -1099,7 +1186,13 @@ discovery_client_running (SalutConnection *self)
priv->self = salut_discovery_client_create_self (priv->discovery_client,
self, priv->nickname, priv->first_name, priv->last_name, priv->jid,
- priv->email, priv->published_name);
+ priv->email, priv->published_name,
+#ifdef ENABLE_OLPC
+ priv->olpc_key, priv->olpc_color
+#else
+ NULL, NULL
+#endif
+ );
if (priv->pre_connect_caps != NULL)
{
@@ -1196,6 +1289,23 @@ _salut_connection_disconnect (SalutConnection *self)
/* Aliasing interface */
+/**
+ * salut_connection_get_alias_flags
+ *
+ * Implements D-Bus method GetAliasFlags
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing
+ *
+ */
+static void
+salut_connection_get_alias_flags (TpSvcConnectionInterfaceAliasing *self,
+ DBusGMethodInvocation *context)
+{
+ /* Aliases are set by the contacts
+ * Actually we concat the first and lastname property */
+
+ tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context,
+ 0);
+}
static const gchar *
salut_connection_get_alias (SalutConnection *self, TpHandle handle)
@@ -1234,7 +1344,7 @@ salut_connection_get_alias (SalutConnection *self, TpHandle handle)
* salut_connection_request_aliases
*
* Implements D-Bus method RequestAliases
- * on interface im.telepathy1.Connection.Interface.Aliasing
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing
*
*/
static void
@@ -1276,6 +1386,40 @@ salut_connection_request_aliases (TpSvcConnectionInterfaceAliasing *iface,
}
static void
+salut_connection_get_aliases (TpSvcConnectionInterfaceAliasing *iface,
+ const GArray *contacts, DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ TpBaseConnection *base = TP_BASE_CONNECTION (self);
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ guint i;
+ GError *error = NULL;
+ GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, NULL);
+
+ if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ for (i = 0; i < contacts->len; i++)
+ {
+ TpHandle handle = g_array_index (contacts, TpHandle, i);
+
+ g_hash_table_insert (result, GUINT_TO_POINTER (handle),
+ (gchar *) salut_connection_get_alias (self, handle));
+ }
+
+ tp_svc_connection_interface_aliasing_return_from_get_aliases (context,
+ result);
+
+ g_hash_table_unref (result);
+}
+
+static void
salut_connection_aliasing_fill_contact_attributes (GObject *obj,
const GArray *contacts, GHashTable *attributes_hash)
{
@@ -1415,18 +1559,24 @@ static void
_contact_manager_contact_alias_changed (SalutConnection *self,
SalutContact *contact, TpHandle handle)
{
- GHashTable *aliases;
+ GPtrArray *aliases;
+ GValue entry = {0, };
- DEBUG("Emitting AliasesChanged");
+ g_value_init (&entry, TP_STRUCT_TYPE_ALIAS_PAIR);
+ g_value_take_boxed (&entry,
+ dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ALIAS_PAIR));
+
+ dbus_g_type_struct_set (&entry,
+ 0, handle, 1, salut_contact_get_alias (contact), G_MAXUINT);
+ aliases = g_ptr_array_sized_new (1);
+ g_ptr_array_add (aliases, g_value_get_boxed (&entry));
- aliases = g_hash_table_new (NULL, NULL);
- g_hash_table_insert (aliases,
- GUINT_TO_POINTER (handle),
- (gchar *) salut_contact_get_alias (contact));
+ DEBUG("Emitting AliasesChanged");
tp_svc_connection_interface_aliasing_emit_aliases_changed (self, aliases);
- g_hash_table_unref (aliases);
+ g_value_unset (&entry);
+ g_ptr_array_unref (aliases);
}
static void
@@ -1438,7 +1588,9 @@ salut_connection_aliasing_service_iface_init (gpointer g_iface,
#define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x \
(klass, salut_connection_##x)
+ IMPLEMENT (get_alias_flags);
IMPLEMENT (request_aliases);
+ IMPLEMENT (get_aliases);
IMPLEMENT (set_aliases);
#undef IMPLEMENT
}
@@ -1501,6 +1653,62 @@ salut_connection_set_avatar (TpSvcConnectionInterfaceAvatars *iface,
static void
+salut_connection_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *iface,
+ const GArray *contacts, DBusGMethodInvocation *context)
+{
+ guint i;
+ gchar **ret;
+ GError *err = NULL;
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = TP_BASE_CONNECTION (self);
+ TpHandle self_handle = tp_base_connection_get_self_handle (base);
+ TpHandleRepoIface *handle_repo;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ handle_repo = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+
+ if (!tp_handles_are_valid (handle_repo, contacts, FALSE, &err))
+ {
+ dbus_g_method_return_error (context, err);
+ g_error_free (err);
+ return;
+ }
+
+ ret = g_new0(gchar *, contacts->len + 1);
+
+ for (i = 0; i < contacts->len ; i++)
+ {
+ TpHandle handle = g_array_index (contacts, TpHandle, i);
+ if (self_handle == handle)
+ {
+ ret[i] = priv->self->avatar_token;
+ }
+ else
+ {
+ SalutContact *contact;
+
+ contact = salut_contact_manager_get_contact (priv->contact_manager,
+ handle);
+ if (contact != NULL)
+ {
+ ret[i] = contact->avatar_token;
+ g_object_unref (contact);
+ }
+ }
+ if (ret[i] == NULL)
+ ret[i] = "";
+ }
+
+ tp_svc_connection_interface_avatars_return_from_get_avatar_tokens (context,
+ (const gchar **)ret);
+
+ g_free (ret);
+}
+
+static void
salut_connection_get_known_avatar_tokens (
TpSvcConnectionInterfaceAvatars *iface, const GArray *contacts,
DBusGMethodInvocation *context)
@@ -1694,6 +1902,78 @@ salut_connection_request_avatars (
}
static void
+_request_avatar_cb (SalutContact *contact, guint8 *avatar, gsize size,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ GError *err = NULL;
+ GArray *arr;
+
+ if (size == 0)
+ {
+ err = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+ "Unable to get avatar");
+ dbus_g_method_return_error (context, err);
+ g_error_free (err);
+ return;
+ }
+
+ arr = g_array_sized_new (FALSE, FALSE, sizeof (guint8), size);
+ arr = g_array_append_vals (arr, avatar, size);
+ tp_svc_connection_interface_avatars_return_from_request_avatar (context,
+ arr, "");
+ g_array_unref (arr);
+}
+
+static void
+salut_connection_request_avatar (TpSvcConnectionInterfaceAvatars *iface,
+ guint handle, DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = TP_BASE_CONNECTION (self);
+ TpHandle self_handle = tp_base_connection_get_self_handle (base);
+ SalutContact *contact;
+ GError *err = NULL;
+ TpHandleRepoIface *handle_repo;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ handle_repo = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+
+ if (!tp_handle_is_valid (handle_repo, handle, &err))
+ {
+ dbus_g_method_return_error (context, err);
+ g_error_free (err);
+ return;
+ }
+
+ if (handle == self_handle)
+ {
+ _request_avatar_cb (NULL, priv->self->avatar, priv->self->avatar_size,
+ context);
+ return;
+ }
+
+ contact = salut_contact_manager_get_contact (priv->contact_manager, handle);
+ if (contact == NULL || contact->avatar_token == NULL)
+ {
+ err = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, "No known avatar");
+ dbus_g_method_return_error (context, err);
+ g_error_free (err);
+ if (contact != NULL)
+ {
+ g_object_unref (contact);
+ }
+ return;
+ }
+ salut_contact_get_avatar (contact, _request_avatar_cb, context);
+ g_object_unref (contact);
+}
+
+static void
conn_avatars_properties_getter (GObject *object,
GQuark interface,
GQuark name,
@@ -1714,6 +1994,15 @@ conn_avatars_properties_getter (GObject *object,
}
static void
+salut_connection_get_avatar_requirements (
+ TpSvcConnectionInterfaceAvatars *iface, DBusGMethodInvocation *context)
+{
+ tp_svc_connection_interface_avatars_return_from_get_avatar_requirements (
+ context, mimetypes, AVATAR_MIN_PX, AVATAR_MIN_PX, AVATAR_MAX_PX,
+ AVATAR_MAX_PX, AVATAR_MAX_BYTES);
+}
+
+static void
salut_connection_avatar_service_iface_init (gpointer g_iface,
gpointer iface_data)
{
@@ -1722,7 +2011,10 @@ salut_connection_avatar_service_iface_init (gpointer g_iface,
#define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x \
(klass, salut_connection_##x)
+ IMPLEMENT (get_avatar_requirements);
+ IMPLEMENT (get_avatar_tokens);
IMPLEMENT (get_known_avatar_tokens);
+ IMPLEMENT (request_avatar);
IMPLEMENT (request_avatars);
IMPLEMENT (set_avatar);
IMPLEMENT (clear_avatar);
@@ -1746,6 +2038,56 @@ salut_free_enhanced_contact_capabilities (GPtrArray *caps)
g_ptr_array_unref (caps);
}
+/**
+ * salut_connection_get_contact_capabilities
+ *
+ * Implements D-Bus method GetContactCapabilities
+ * on interface
+ * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities
+ */
+static void
+salut_connection_get_contact_capabilities (
+ TpSvcConnectionInterfaceContactCapabilities *iface,
+ const GArray *handles,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ guint i;
+ GHashTable *ret;
+ GError *error = NULL;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (!tp_handles_are_valid (contact_handles, handles, FALSE, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ ret = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) salut_free_enhanced_contact_capabilities);
+
+ for (i = 0; i < handles->len; i++)
+ {
+ GPtrArray *arr = g_ptr_array_new ();
+ TpHandle handle = g_array_index (handles, TpHandle, i);
+
+ salut_connection_get_handle_contact_capabilities (self, handle, arr);
+
+ g_hash_table_insert (ret, GINT_TO_POINTER (handle), arr);
+ }
+
+ tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities
+ (context, ret);
+
+ g_hash_table_unref (ret);
+}
+
+
static void
_emit_contact_capabilities_changed (SalutConnection *conn,
TpHandle handle)
@@ -1827,7 +2169,7 @@ data_forms_equal (GPtrArray *one,
*
* Implements D-Bus method UpdateCapabilities
* on interface
- * im.telepathy1.Connection.Interface.ContactCapabilities
+ * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities
*/
static void
salut_connection_update_capabilities (
@@ -1959,10 +2301,1041 @@ salut_conn_contact_caps_iface_init (gpointer g_iface, gpointer iface_data)
#define IMPLEMENT(x) \
tp_svc_connection_interface_contact_capabilities_implement_##x (\
klass, salut_connection_##x)
+ IMPLEMENT(get_contact_capabilities);
IMPLEMENT(update_capabilities);
#undef IMPLEMENT
}
+
+#ifdef ENABLE_OLPC
+static GValue *
+new_gvalue (GType type)
+{
+ GValue *result = g_slice_new0 (GValue);
+ g_value_init (result, type);
+ return result;
+}
+
+static GHashTable *
+get_properties_hash (const GArray *key, const gchar *color, const gchar *jid,
+ const gchar *ip4, const gchar *ip6)
+{
+ GHashTable *properties;
+ GValue *gvalue;
+
+ properties = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) tp_g_value_slice_free);
+ if (key != NULL)
+ {
+ gvalue = new_gvalue (DBUS_TYPE_G_UCHAR_ARRAY);
+ g_value_set_boxed (gvalue, key);
+ g_hash_table_insert (properties, "key", gvalue);
+ }
+
+ if (color != NULL)
+ {
+ gvalue = new_gvalue (G_TYPE_STRING);
+ g_value_set_string (gvalue, color);
+ g_hash_table_insert (properties, "color", gvalue);
+ }
+
+ if (jid != NULL)
+ {
+ gvalue = new_gvalue (G_TYPE_STRING);
+ g_value_set_string (gvalue, jid);
+ g_hash_table_insert (properties, "jid", gvalue);
+ }
+
+ if (ip4 != NULL)
+ {
+ gvalue = new_gvalue (G_TYPE_STRING);
+ g_value_set_string (gvalue, ip4);
+ g_hash_table_insert (properties, "ip4-address", gvalue);
+ }
+
+ if (ip6 != NULL)
+ {
+ gvalue = new_gvalue (G_TYPE_STRING);
+ g_value_set_string (gvalue, ip6);
+ g_hash_table_insert (properties, "ip6-address", gvalue);
+ }
+
+ return properties;
+}
+
+static void
+emit_properties_changed (SalutConnection *connection,
+ TpHandle handle,
+ const GArray *key,
+ const gchar *color,
+ const gchar *jid,
+ const gchar *ip4,
+ const gchar *ip6)
+{
+ GHashTable *properties;
+ properties = get_properties_hash (key, color, jid, ip4, ip6);
+
+ salut_svc_olpc_buddy_info_emit_properties_changed (connection,
+ handle, properties);
+
+ g_hash_table_unref (properties);
+}
+
+static void
+append_activity (SalutOlpcActivity *activity,
+ gpointer user_data)
+{
+ GPtrArray *arr = user_data;
+ GType type = ACTIVITY_PAIR_TYPE;
+ GValue gvalue = {0};
+
+ g_value_init (&gvalue, type);
+ g_value_take_boxed (&gvalue,
+ dbus_g_type_specialized_construct (type));
+
+ dbus_g_type_struct_set (&gvalue,
+ 0, activity->id,
+ 1, activity->room,
+ G_MAXUINT);
+ g_ptr_array_add (arr, g_value_get_boxed (&gvalue));
+}
+
+static void
+free_olpc_activities (GPtrArray *arr)
+{
+ GType type = ACTIVITY_PAIR_TYPE;
+ guint i;
+
+ for (i = 0; i < arr->len; i++)
+ g_boxed_free (type, arr->pdata[i]);
+
+ g_ptr_array_unref (arr);
+}
+
+static void
+_contact_manager_contact_olpc_activities_changed (SalutConnection *self,
+ SalutContact *contact,
+ TpHandle handle)
+{
+ GPtrArray *activities = g_ptr_array_new ();
+
+ DEBUG ("called for %u", handle);
+
+ salut_contact_foreach_olpc_activity (contact, append_activity, activities);
+ salut_svc_olpc_buddy_info_emit_activities_changed (self,
+ handle, activities);
+ free_olpc_activities (activities);
+}
+
+static void
+_contact_manager_contact_olpc_properties_changed (SalutConnection *self,
+ SalutContact *contact,
+ TpHandle handle)
+{
+ emit_properties_changed (self, handle, contact->olpc_key,
+ contact->olpc_color, contact->jid, contact->olpc_ip4, contact->olpc_ip6);
+}
+
+static gboolean
+check_handle (TpHandleRepoIface *handle_repo,
+ TpHandle handle,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ if (!tp_handle_is_valid (handle_repo, handle, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_contact (TpBaseConnection *base,
+ TpHandle contact,
+ DBusGMethodInvocation *context)
+{
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ base, TP_HANDLE_TYPE_CONTACT);
+
+ return check_handle (contact_repo, contact, context);
+}
+
+static gboolean
+check_room (TpBaseConnection *base,
+ TpHandle contact,
+ DBusGMethodInvocation *context)
+{
+ TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
+ base, TP_HANDLE_TYPE_ROOM);
+
+ return check_handle (room_repo, contact, context);
+}
+
+static void
+salut_connection_olpc_get_properties (SalutSvcOLPCBuddyInfo *iface,
+ TpHandle handle,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = TP_BASE_CONNECTION (self);
+ TpHandle self_handle = tp_base_connection_get_self_handle (base);
+ GHashTable *properties = NULL;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (!check_contact (base, handle, context))
+ return;
+
+ if (handle == self_handle)
+ {
+ properties = get_properties_hash (priv->self->olpc_key,
+ priv->self->olpc_color, priv->self->jid, NULL, NULL);
+ }
+ else
+ {
+ SalutContact *contact;
+ contact = salut_contact_manager_get_contact (priv->contact_manager,
+ handle);
+ if (contact == NULL)
+ {
+ /* FIXME: should this be InvalidHandle? */
+ GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unknown contact" };
+ dbus_g_method_return_error (context, &e);
+ return;
+ }
+ properties = get_properties_hash (contact->olpc_key, contact->olpc_color,
+ contact->jid, contact->olpc_ip4, contact->olpc_ip6);
+ g_object_unref (contact);
+ }
+
+ salut_svc_olpc_buddy_info_return_from_get_properties (context, properties);
+ g_hash_table_unref (properties);
+}
+
+
+static gboolean
+find_unknown_properties (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ gchar **valid_props = (gchar **) user_data;
+ int i;
+ for (i = 0; valid_props[i] != NULL; i++)
+ {
+ if (!tp_strdiff (key, valid_props[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+salut_connection_olpc_set_properties (SalutSvcOLPCBuddyInfo *iface,
+ GHashTable *properties,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+
+ GError *error = NULL;
+ /* Only a few known properties, so handle it quite naively */
+ const gchar *known_properties[] = { "color", "key", "jid", "ip4-address",
+ "ip6-address", NULL };
+ const gchar *color = NULL;
+ const GArray *key = NULL;
+ const gchar *jid = NULL;
+ const GValue *val;
+
+ /* this function explicitly supports being called when DISCONNECTED
+ * or CONNECTING */
+
+ if (g_hash_table_find (properties, find_unknown_properties, known_properties)
+ != NULL)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Unknown property given");
+ goto error;
+ }
+
+ val = (const GValue *) g_hash_table_lookup (properties, "color");
+ if (val != NULL)
+ {
+ if (G_VALUE_TYPE (val) != G_TYPE_STRING)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Color value should be of type s");
+ goto error;
+ }
+ else
+ {
+ int len;
+ gboolean correct = TRUE;
+
+ color = g_value_get_string (val);
+
+ /* be very anal about the color format */
+ len = strlen (color);
+ if (len != 15)
+ {
+ correct = FALSE;
+ }
+ else
+ {
+ int i;
+ for (i = 0 ; i < len ; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ case 8:
+ correct = (color[i] == '#');
+ break;
+ case 7:
+ correct = (color[i] == ',');
+ break;
+ default:
+ correct = isxdigit (color[i]);
+ break;
+ }
+ }
+ }
+
+ if (!correct)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Color value has an incorrect format");
+ goto error;
+ }
+ }
+ }
+
+ if ((val = (const GValue *) g_hash_table_lookup (properties, "key")) != NULL)
+ {
+ if (G_VALUE_TYPE (val) != DBUS_TYPE_G_UCHAR_ARRAY)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Key value should be of type ay");
+ goto error;
+ }
+ else
+ {
+ key = g_value_get_boxed (val);
+ if (key->len == 0)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Key value of length 0 not allowed");
+ goto error;
+ }
+ }
+ }
+
+ val = g_hash_table_lookup (properties, "jid");
+ if (val != NULL)
+ {
+ if (G_VALUE_TYPE (val) != G_TYPE_STRING)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "JID value should be of type s");
+ goto error;
+ }
+
+ jid = g_value_get_string (val);
+
+ if (strchr (jid, '@') == NULL)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "JID value has an incorrect format");
+ goto error;
+ }
+ }
+
+ if (priv->self)
+ {
+ if (!salut_self_set_olpc_properties (priv->self, key, color, jid,
+ &error))
+ goto error;
+ }
+ else
+ {
+ /* queue it up for later */
+ if (key)
+ {
+ if (priv->olpc_key == NULL)
+ {
+ priv->olpc_key = g_array_sized_new (FALSE, FALSE, sizeof (guint8),
+ key->len);
+ }
+ else
+ {
+ g_array_remove_range (priv->olpc_key, 0, priv->olpc_key->len);
+ }
+ g_array_append_vals (priv->olpc_key, key->data, key->len);
+ }
+ if (color)
+ {
+ g_free (priv->olpc_color);
+ priv->olpc_color = g_strdup (color);
+ }
+ if (jid)
+ {
+ g_free (priv->jid);
+ priv->jid = g_strdup (jid);
+ }
+ }
+
+ salut_svc_olpc_buddy_info_return_from_set_properties (context);
+ return;
+
+error:
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+}
+
+static void
+salut_connection_olpc_get_current_activity (SalutSvcOLPCBuddyInfo *iface,
+ TpHandle handle,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ TpHandle self_handle = tp_base_connection_get_self_handle (base);
+ SalutConnectionPrivate *priv = self->priv;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ DEBUG ("called for %u", handle);
+
+ if (!check_contact (base, handle, context))
+ return;
+
+ if (handle == self_handle)
+ {
+ DEBUG ("Returning my own cur.act.: %s -> %u",
+ priv->self->olpc_cur_act ? priv->self->olpc_cur_act : "",
+ priv->self->olpc_cur_act_room);
+ salut_svc_olpc_buddy_info_return_from_get_current_activity (context,
+ priv->self->olpc_cur_act ? priv->self->olpc_cur_act : "",
+ priv->self->olpc_cur_act_room);
+ }
+ else
+ {
+ SalutContact *contact = salut_contact_manager_get_contact
+ (priv->contact_manager, handle);
+
+ if (contact == NULL)
+ {
+ /* FIXME: should this be InvalidHandle? */
+ GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unknown contact" };
+ DEBUG ("Returning error: unknown contact");
+ dbus_g_method_return_error (context, &e);
+ return;
+ }
+
+ DEBUG ("Returning buddy %u cur.act.: %s -> %u", handle,
+ contact->olpc_cur_act ? contact->olpc_cur_act : "",
+ contact->olpc_cur_act_room);
+ salut_svc_olpc_buddy_info_return_from_get_current_activity (context,
+ contact->olpc_cur_act ? contact->olpc_cur_act : "",
+ contact->olpc_cur_act_room);
+ g_object_unref (contact);
+ }
+}
+
+static void
+salut_connection_olpc_set_current_activity (SalutSvcOLPCBuddyInfo *iface,
+ const gchar *activity_id,
+ TpHandle room_handle,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ GError *error = NULL;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ DEBUG ("called");
+
+ if (activity_id[0] == '\0')
+ {
+ if (room_handle != 0)
+ {
+ GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "If activity ID is empty, room handle must be 0" };
+
+ dbus_g_method_return_error (context, &e);
+ return;
+ }
+ }
+ else
+ {
+ if (!check_room (base, room_handle, context))
+ return;
+ }
+
+ if (!salut_self_set_olpc_current_activity (priv->self, activity_id,
+ room_handle, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ salut_svc_olpc_buddy_info_return_from_set_current_activity (context);
+}
+
+static void
+salut_connection_olpc_get_activities (SalutSvcOLPCBuddyInfo *iface,
+ TpHandle handle,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ TpHandle self_handle = tp_base_connection_get_self_handle (base);
+ GPtrArray *arr;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ DEBUG ("called for %u", handle);
+
+ if (!check_contact (base, handle, context))
+ return;
+
+ if (handle == self_handle)
+ {
+ arr = g_ptr_array_new ();
+ salut_self_foreach_olpc_activity (priv->self, append_activity, arr);
+ }
+ else
+ {
+ SalutContact *contact = salut_contact_manager_get_contact
+ (priv->contact_manager, handle);
+
+ if (contact == NULL)
+ {
+ /* FIXME: should this be InvalidHandle? */
+ GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unknown contact" };
+ DEBUG ("Returning error: unknown contact");
+ dbus_g_method_return_error (context, &e);
+ return;
+ }
+
+ arr = g_ptr_array_new ();
+ salut_contact_foreach_olpc_activity (contact, append_activity, arr);
+ g_object_unref (contact);
+ }
+
+ salut_svc_olpc_buddy_info_return_from_get_activities (context, arr);
+ free_olpc_activities (arr);
+}
+
+static void
+salut_connection_olpc_set_activities (SalutSvcOLPCBuddyInfo *iface,
+ const GPtrArray *activities,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_ROOM);
+ GHashTable *room_to_act_id = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal, NULL, (GDestroyNotify) g_free);
+ GError *error = NULL;
+ guint i;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ for (i = 0; i < activities->len; i++)
+ {
+ GValue pair = {0};
+ gchar *activity;
+ guint room_handle;
+
+ g_value_init (&pair, ACTIVITY_PAIR_TYPE);
+ g_value_set_static_boxed (&pair, g_ptr_array_index (activities, i));
+ dbus_g_type_struct_get (&pair,
+ 0, &activity,
+ 1, &room_handle,
+ G_MAXUINT);
+
+ if (activity[0] == '\0')
+ {
+ GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Invalid empty activity ID" };
+
+ DEBUG ("%s", e.message);
+ dbus_g_method_return_error (context, &e);
+ g_free (activity);
+ goto finally;
+ }
+
+ if (!tp_handle_is_valid (room_repo, room_handle, &error))
+ {
+ DEBUG ("Invalid room handle %u: %s", room_handle, error->message);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ g_free (activity);
+ goto finally;
+ }
+
+ g_hash_table_insert (room_to_act_id, GUINT_TO_POINTER (room_handle),
+ activity);
+ }
+
+ if (!salut_self_set_olpc_activities (priv->self, room_to_act_id, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ }
+ else
+ {
+ salut_svc_olpc_buddy_info_return_from_set_activities (context);
+ }
+
+finally:
+ g_hash_table_unref (room_to_act_id);
+}
+
+static void
+salut_connection_olpc_add_activity (SalutSvcOLPCBuddyInfo *iface,
+ const gchar *id,
+ TpHandle handle,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ GError *error = NULL;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (!salut_self_add_olpc_activity (priv->self, id, handle, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ }
+ else
+ {
+ salut_svc_olpc_buddy_info_return_from_set_activities (context);
+ }
+}
+
+static void
+salut_connection_olpc_buddy_info_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ SalutSvcOLPCBuddyInfoClass *klass =
+ (SalutSvcOLPCBuddyInfoClass *) g_iface;
+#define IMPLEMENT(x) salut_svc_olpc_buddy_info_implement_##x (klass, \
+ salut_connection_olpc_##x)
+ IMPLEMENT(set_properties);
+ IMPLEMENT(get_properties);
+ IMPLEMENT(set_activities);
+ IMPLEMENT(add_activity);
+ IMPLEMENT(get_activities);
+ IMPLEMENT(set_current_activity);
+ IMPLEMENT(get_current_activity);
+#undef IMPLEMENT
+}
+
+static void
+salut_connection_act_get_properties (SalutSvcOLPCActivityProperties *iface,
+ TpHandle handle,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_ROOM);
+ GHashTable *properties = NULL;
+ GError *error = NULL;
+ SalutOlpcActivity *activity;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (!tp_handle_is_valid (room_repo, handle, &error))
+ goto error;
+
+ activity = salut_olpc_activity_manager_get_activity_by_room (
+ priv->olpc_activity_manager, handle);
+ if (activity == NULL)
+ {
+ g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+ "Activity unknown: %u", handle);
+ goto error;
+ }
+
+ properties = salut_olpc_activity_create_properties_table (activity);
+
+ salut_svc_olpc_buddy_info_return_from_get_properties (context, properties);
+ g_hash_table_unref (properties);
+
+ return;
+
+error:
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+}
+
+static gboolean
+check_color (const gchar *color)
+{
+ int len, i;
+
+ /* be very anal about the color format */
+ len = strlen (color);
+ if (len != 15)
+ return FALSE;
+
+ for (i = 0 ; i < len ; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ case 8:
+ if (color[i] != '#')
+ return FALSE;
+ break;
+ case 7:
+ if (color[i] != ',')
+ return FALSE;
+ break;
+ default:
+ if (!isxdigit (color[i]))
+ return FALSE;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+/* returned strings are only valid as long as the hash table isn't modified */
+static gboolean
+extract_properties_from_hash (GHashTable *properties,
+ const gchar **id,
+ const gchar **color,
+ const gchar **name,
+ const gchar **type,
+ const gchar **tags,
+ gboolean *is_private,
+ GError **error)
+{
+ GValue *activity_id_val, *color_val, *activity_name_val, *activity_type_val,
+ *tags_val, *is_private_val;
+
+ /* activity ID */
+ activity_id_val = g_hash_table_lookup (properties, "id");
+ if (activity_id_val != NULL)
+ {
+ if (G_VALUE_TYPE (activity_id_val) != G_TYPE_STRING)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Activity ID value should be of type s");
+ return FALSE;
+ }
+
+ if (id != NULL)
+ *id = g_value_get_string (activity_id_val);
+ }
+
+ /* color */
+ color_val = g_hash_table_lookup (properties, "color");
+ if (color_val != NULL)
+ {
+ if (G_VALUE_TYPE (color_val) != G_TYPE_STRING)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Color value should be of type s");
+ return FALSE;
+ }
+
+ if (color != NULL)
+ {
+ *color = g_value_get_string (color_val);
+
+ if (!check_color (*color))
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Color value has an incorrect format");
+ return FALSE;
+ }
+ }
+ }
+
+ /* name */
+ activity_name_val = g_hash_table_lookup (properties, "name");
+ if (activity_name_val != NULL)
+ {
+ if (G_VALUE_TYPE (activity_name_val) != G_TYPE_STRING)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "name value should be of type s");
+ return FALSE;
+ }
+
+ if (name != NULL)
+ *name = g_value_get_string (activity_name_val);
+ }
+
+ /* type */
+ activity_type_val = g_hash_table_lookup (properties, "type");
+ if (activity_type_val != NULL)
+ {
+ if (G_VALUE_TYPE (activity_type_val) != G_TYPE_STRING)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "type value should be of type s");
+ return FALSE;
+ }
+
+ if (type != NULL)
+ {
+ *type = g_value_get_string (activity_type_val);
+
+ if (*type[0] == '\0')
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "type value must be non-empty");
+ return FALSE;
+ }
+ }
+ }
+
+ /* tags */
+ tags_val = g_hash_table_lookup (properties, "tags");
+ if (tags_val != NULL)
+ {
+ if (G_VALUE_TYPE (activity_type_val) != G_TYPE_STRING)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "tags value should be of type s");
+ return FALSE;
+ }
+
+ if (type != NULL)
+ *tags = g_value_get_string (tags_val);
+ }
+
+ /* is_private */
+ is_private_val = g_hash_table_lookup (properties, "private");
+ if (is_private_val != NULL)
+ {
+ if (G_VALUE_TYPE (is_private_val) != G_TYPE_BOOLEAN)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "private value should be of type b");
+ return FALSE;
+ }
+
+ if (is_private != NULL)
+ *is_private = g_value_get_boolean (is_private_val);
+ }
+
+ return TRUE;
+}
+
+static void
+salut_connection_act_set_properties (SalutSvcOLPCActivityProperties *iface,
+ TpHandle handle,
+ GHashTable *properties,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ GError *error = NULL;
+ const gchar *known_properties[] = { "color", "name", "type", "private",
+ "tags", NULL };
+ const gchar *color = NULL, *name = NULL, *type = NULL, *tags = NULL;
+ gboolean is_private = TRUE;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (!check_room (base, handle, context))
+ return;
+
+ if (g_hash_table_find (properties, find_unknown_properties, known_properties)
+ != NULL)
+ {
+ error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Unknown property given");
+ goto error;
+ }
+
+ if (!extract_properties_from_hash (properties, NULL, &color, &name, &type,
+ &tags, &is_private, &error))
+ goto error;
+
+ if (!salut_self_set_olpc_activity_properties (priv->self, handle, color,
+ name, type, tags, is_private, &error))
+ goto error;
+
+ salut_svc_olpc_activity_properties_return_from_set_properties (context);
+ return;
+
+error:
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+}
+
+typedef struct
+{
+ SalutContact *inviter;
+ SalutOlpcActivity *activity;
+} muc_ready_ctx;
+
+static muc_ready_ctx *
+muc_ready_ctx_new (SalutContact *inviter,
+ SalutOlpcActivity *activity)
+{
+ muc_ready_ctx *ctx = g_slice_new (muc_ready_ctx);
+ ctx->inviter = inviter;
+ g_object_ref (inviter);
+ ctx->activity = activity;
+ g_object_ref (activity);
+ return ctx;
+}
+
+static void
+muc_ready_ctx_free (muc_ready_ctx *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ g_object_unref (ctx->inviter);
+ g_object_unref (ctx->activity);
+ g_slice_free (muc_ready_ctx, ctx);
+}
+
+static void
+muc_ready_cb (SalutMucChannel *muc,
+ muc_ready_ctx *ctx)
+{
+ /* We joined the muc so have to forget about invites */
+ salut_contact_left_activity (ctx->inviter, ctx->activity);
+
+ DEBUG ("forget invite received from %s", ctx->inviter->name);
+ g_signal_handlers_disconnect_matched (muc, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+ NULL, ctx);
+ muc_ready_ctx_free (ctx);
+}
+
+static void
+muc_closed_cb (SalutMucChannel *muc,
+ muc_ready_ctx *ctx)
+{
+ /* FIXME: should we call left_private_activity here too ? */
+
+ g_signal_handlers_disconnect_matched (muc, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+ NULL, ctx);
+ muc_ready_ctx_free (ctx);
+}
+
+void
+salut_connection_olpc_observe_invitation (SalutConnection *self,
+ TpHandle room,
+ TpHandle inviter_handle,
+ WockyNode *invite_node)
+{
+ SalutConnectionPrivate *priv = self->priv;
+ WockyNode *props_node;
+ GHashTable *properties;
+ const gchar *activity_id, *color = NULL, *activity_name = NULL,
+ *activity_type = NULL, *tags = NULL;
+ SalutContact *inviter;
+ SalutOlpcActivity *activity;
+ SalutMucChannel *muc;
+ muc_ready_ctx *ctx;
+
+ props_node = wocky_node_get_child_ns (invite_node, "properties",
+ NS_OLPC_ACTIVITY_PROPS);
+
+ if (props_node == NULL)
+ return;
+
+ inviter = salut_contact_manager_get_contact (priv->contact_manager,
+ inviter_handle);
+ if (inviter == NULL)
+ return;
+
+ properties = salut_wocky_node_extract_properties (props_node,
+ "property");
+
+ if (!extract_properties_from_hash (properties, &activity_id, &color,
+ &activity_name, &activity_type, &tags, NULL, NULL))
+ return;
+
+ activity = salut_olpc_activity_manager_got_invitation (
+ priv->olpc_activity_manager,
+ room, inviter, activity_id, activity_name, activity_type,
+ color, tags);
+#ifndef USE_BACKEND_BONJOUR
+ muc = salut_muc_manager_get_text_channel (priv->muc_manager, room);
+ g_assert (muc != NULL);
+
+ ctx = muc_ready_ctx_new (inviter, activity);
+ g_signal_connect (muc, "ready", G_CALLBACK (muc_ready_cb), ctx);
+ g_signal_connect (muc, "closed", G_CALLBACK (muc_closed_cb), ctx);
+
+ g_object_unref (muc);
+#endif
+ g_hash_table_unref (properties);
+ g_object_unref (inviter);
+}
+
+static void
+salut_connection_act_get_activity (SalutSvcOLPCActivityProperties *iface,
+ const gchar *activity_id,
+ DBusGMethodInvocation *context)
+{
+ SalutConnection *self = SALUT_CONNECTION (iface);
+ SalutConnectionPrivate *priv = self->priv;
+ TpBaseConnection *base = (TpBaseConnection *) self;
+ GError *error = NULL;
+ SalutOlpcActivity *activity;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ activity = salut_olpc_activity_manager_get_activity_by_id (
+ priv->olpc_activity_manager, activity_id);
+ if (activity == NULL)
+ {
+ g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+ "Activity unknown: %s", activity_id);
+ goto error;
+ }
+
+ salut_svc_olpc_activity_properties_return_from_get_activity (context,
+ activity->room);
+
+ return;
+
+error:
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+}
+
+static void
+salut_connection_olpc_activity_properties_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ SalutSvcOLPCActivityPropertiesClass *klass =
+ (SalutSvcOLPCActivityPropertiesClass *) g_iface;
+#define IMPLEMENT(x) salut_svc_olpc_activity_properties_implement_##x \
+ (klass, salut_connection_act_##x)
+ IMPLEMENT(set_properties);
+ IMPLEMENT(get_properties);
+ IMPLEMENT(get_activity);
+#undef IMPLEMENT
+}
+#endif
+
gchar *
salut_normalize_non_empty (const gchar *id,
GError **error)
@@ -2033,7 +3406,234 @@ _contact_manager_contact_change_cb (SalutContactManager *mgr,
{
salut_conn_contact_info_changed (self, contact, handle);
}
+
+#ifdef ENABLE_OLPC
+ if (changes & SALUT_CONTACT_OLPC_PROPERTIES)
+ _contact_manager_contact_olpc_properties_changed (self, contact, handle);
+
+ if (changes & SALUT_CONTACT_OLPC_CURRENT_ACTIVITY)
+ salut_svc_olpc_buddy_info_emit_current_activity_changed (self,
+ handle, contact->olpc_cur_act, contact->olpc_cur_act_room);
+
+ if (changes & SALUT_CONTACT_OLPC_ACTIVITIES)
+ _contact_manager_contact_olpc_activities_changed (self, contact, handle);
+#endif
+}
+
+#ifdef ENABLE_OLPC
+static void
+_olpc_activity_manager_activity_modified_cb (SalutOlpcActivityManager *mgr,
+ SalutOlpcActivity *activity, SalutConnection *self)
+{
+ GHashTable *properties;
+
+ properties = salut_olpc_activity_create_properties_table (activity);
+ salut_svc_olpc_activity_properties_emit_activity_properties_changed (
+ self, activity->room, properties);
+
+ g_hash_table_unref (properties);
+}
+
+gboolean
+salut_connection_olpc_observe_muc_stanza (SalutConnection *self,
+ TpHandle room, TpHandle sender, WockyStanza *stanza)
+{
+ WockyNode *node = wocky_stanza_get_top_node (stanza);
+ SalutConnectionPrivate *priv = self->priv;
+ WockyNode *props_node;
+ GHashTable *properties;
+ const gchar *activity_id, *color = NULL, *activity_name = NULL,
+ *activity_type = NULL, *tags = NULL;
+ gboolean is_private = FALSE;
+ SalutOlpcActivity *activity;
+
+ props_node = wocky_node_get_child_ns (node, "properties",
+ NS_OLPC_ACTIVITY_PROPS);
+
+ if (props_node == NULL)
+ return FALSE;
+
+ activity = salut_olpc_activity_manager_get_activity_by_room (
+ priv->olpc_activity_manager, room);
+
+ if (activity == NULL)
+ {
+ DEBUG ("no activity in room %d", room);
+ return FALSE;
+ }
+
+ properties = salut_wocky_node_extract_properties (props_node,
+ "property");
+
+ if (!extract_properties_from_hash (properties, &activity_id, &color,
+ &activity_name, &activity_type, &tags, &is_private, NULL))
+ return TRUE;
+
+ salut_olpc_activity_update (activity, room, activity_id, activity_name,
+ activity_type, color, tags, is_private);
+
+ g_hash_table_unref (properties);
+
+ return TRUE;
+}
+
+static gboolean
+uninvite_stanza_callback (WockyPorter *porter,
+ WockyStanza *stanza,
+ gpointer user_data)
+{
+ SalutConnection *self = SALUT_CONNECTION (user_data);
+ SalutConnectionPrivate *priv = self->priv;
+ TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) self, TP_HANDLE_TYPE_ROOM);
+ WockyNode *node;
+ TpHandle room_handle;
+ const gchar *room, *activity_id;
+ SalutOlpcActivity *activity;
+ WockyNode *top_node = wocky_stanza_get_top_node (stanza);
+ SalutContact *contact = SALUT_CONTACT (wocky_stanza_get_from_contact (stanza));
+
+ node = wocky_node_get_child_ns (top_node, "uninvite",
+ NS_OLPC_ACTIVITY_PROPS);
+ g_assert (node != NULL);
+
+ room = wocky_node_get_attribute (node, "room");
+ if (room == NULL)
+ {
+ DEBUG ("No room attribute");
+ return FALSE;
+ }
+
+ room_handle = tp_handle_lookup (room_repo, room, NULL, NULL);
+ if (room_handle == 0)
+ {
+ DEBUG ("room %s unknown", room);
+ return FALSE;
+ }
+
+ activity_id = wocky_node_get_attribute (node, "id");
+ if (activity_id == NULL)
+ {
+ DEBUG ("No id attribute");
+ return FALSE;
+ }
+
+ DEBUG ("received uninvite from %s", contact->name);
+
+ activity = salut_olpc_activity_manager_get_activity_by_room (
+ priv->olpc_activity_manager, room_handle);
+
+ if (activity == NULL)
+ return FALSE;
+
+ salut_contact_left_activity (contact, activity);
+
+ return TRUE;
+}
+
+#endif
+
+static GPtrArray *
+salut_connection_create_channel_factories (TpBaseConnection *base)
+{
+ SalutConnection *self = SALUT_CONNECTION (base);
+ SalutConnectionPrivate *priv = self->priv;
+ GPtrArray *factories = g_ptr_array_sized_new (4);
+
+ /* Create the contact manager */
+ priv->contact_manager = salut_discovery_client_create_contact_manager (
+ priv->discovery_client, self);
+ g_signal_connect (priv->contact_manager, "contact-change",
+ G_CALLBACK (_contact_manager_contact_change_cb), self);
+
+#ifdef ENABLE_OLPC
+ priv->uninvite_handler_id = wocky_porter_register_handler_from_anyone (
+ self->porter,
+ WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE,
+ WOCKY_PORTER_HANDLER_PRIORITY_NORMAL,
+ uninvite_stanza_callback, self,
+ '(', "uninvite",
+ ':', NS_OLPC_ACTIVITY_PROPS,
+ ')', NULL);
+
+ /* create the OLPC activity manager */
+ priv->olpc_activity_manager =
+ salut_discovery_client_create_olpc_activity_manager (
+ priv->discovery_client, self);
+ g_signal_connect (priv->olpc_activity_manager, "activity-modified",
+ G_CALLBACK (_olpc_activity_manager_activity_modified_cb), self);
+#endif
+
+ return factories;
+}
+
+#ifdef ENABLE_OLPC
+static void
+muc_channel_closed_cb (SalutMucChannel *chan,
+ SalutOlpcActivity *activity)
+{
+ SalutConnection *conn;
+ SalutConnectionPrivate *priv;
+ TpBaseConnection *base;
+ TpHandle self_handle;
+ GPtrArray *activities = g_ptr_array_new ();
+
+ g_signal_handlers_disconnect_by_func (chan,
+ G_CALLBACK (muc_channel_closed_cb), activity);
+
+ g_object_get (activity,
+ "connection", &conn,
+ NULL);
+
+ priv = conn->priv;
+ base = (TpBaseConnection *) conn;
+ self_handle = tp_base_connection_get_self_handle (base);
+
+ salut_self_remove_olpc_activity (priv->self, activity);
+
+ salut_self_foreach_olpc_activity (priv->self, append_activity, activities);
+ salut_svc_olpc_buddy_info_emit_activities_changed (conn, self_handle,
+ activities);
+ free_olpc_activities (activities);
+
+ /* we were holding a ref since the channel was opened */
+ g_object_unref (activity);
+
+ g_object_unref (conn);
+}
+
+static void
+muc_manager_new_channels_cb (TpChannelManager *channel_manager,
+ GHashTable *channels,
+ SalutConnection *conn)
+{
+ SalutConnectionPrivate *priv = conn->priv;
+ GHashTableIter iter;
+ gpointer chan;
+
+ g_hash_table_iter_init (&iter, channels);
+ while (g_hash_table_iter_next (&iter, &chan, NULL))
+ {
+ SalutOlpcActivity *activity;
+ TpHandle room_handle;
+
+ if (!SALUT_IS_MUC_CHANNEL (chan))
+ return;
+
+ g_object_get (chan,
+ "handle", &room_handle,
+ NULL);
+
+ /* ref the activity as long as we have a channel open */
+ activity = salut_olpc_activity_manager_ensure_activity_by_room (
+ priv->olpc_activity_manager,
+ room_handle);
+
+ g_signal_connect (chan, "closed", G_CALLBACK (muc_channel_closed_cb),
+ activity);
+ }
}
+#endif
static void
add_to_array (gpointer data,
@@ -2052,16 +3652,12 @@ salut_connection_create_channel_managers (TpBaseConnection *base)
GPtrArray *tmp;
SalutPluginLoader *loader;
- /* Create the contact manager. This is not a channel manager anymore,
- * but still needs to be created from here because others channel managers use
- * it and TpBaseConnection calls ::create_channel_managers() before
- * ::constructed() */
- self->priv->contact_manager = salut_discovery_client_create_contact_manager (
- self->priv->discovery_client, self);
- g_signal_connect (self->priv->contact_manager, "contact-change",
- G_CALLBACK (_contact_manager_contact_change_cb), self);
-
+ /* FIXME: The second and third arguments depend on create_channel_factories
+ * being called before this; should telepathy-glib guarantee that or
+ * should we be defensive?
+ */
priv->im_manager = salut_im_manager_new (self, priv->contact_manager);
+
priv->ft_manager = salut_ft_manager_new (self, priv->contact_manager);
#ifndef USE_BACKEND_BONJOUR
@@ -2077,6 +3673,7 @@ salut_connection_create_channel_managers (TpBaseConnection *base)
#endif
g_ptr_array_add (managers, priv->im_manager);
+ g_ptr_array_add (managers, priv->contact_manager);
g_ptr_array_add (managers, priv->ft_manager);
#ifndef USE_BACKEND_BONJOUR
g_ptr_array_add (managers, priv->muc_manager);
@@ -2087,6 +3684,11 @@ salut_connection_create_channel_managers (TpBaseConnection *base)
g_ptr_array_add (managers, priv->tubes_manager);
#endif
+#ifdef ENABLE_OLPC
+ g_signal_connect (TP_CHANNEL_MANAGER (priv->muc_manager), "new-channels",
+ G_CALLBACK (muc_manager_new_channels_cb), self);
+#endif
+
/* plugin channel managers */
loader = salut_plugin_loader_dup ();
tmp = salut_plugin_loader_create_channel_managers (loader, plugin_connection);