/*
* conn.c - an example connection
*
* Copyright © 2007-2009 Collabora Ltd.
* Copyright © 2007-2009 Nokia Corporation
*
* Copying and distribution of this file, with or without modification,
* are permitted in any medium without royalty provided the copyright
* notice and this notice are preserved.
*/
#include "conn.h"
#include
#include
#include
#include
#include
#include "contact-list-manager.h"
static void init_aliasing (gpointer, gpointer);
G_DEFINE_TYPE_WITH_CODE (ExampleContactListConnection,
example_contact_list_connection,
TP_TYPE_BASE_CONNECTION,
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING,
init_aliasing);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
tp_contacts_mixin_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))
enum
{
PROP_ACCOUNT = 1,
PROP_SIMULATION_DELAY,
N_PROPS
};
struct _ExampleContactListConnectionPrivate
{
gchar *account;
guint simulation_delay;
ExampleContactListManager *list_manager;
gboolean away;
};
static void
example_contact_list_connection_init (ExampleContactListConnection *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
EXAMPLE_TYPE_CONTACT_LIST_CONNECTION,
ExampleContactListConnectionPrivate);
}
static void
get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *spec)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (object);
switch (property_id)
{
case PROP_ACCOUNT:
g_value_set_string (value, self->priv->account);
break;
case PROP_SIMULATION_DELAY:
g_value_set_uint (value, self->priv->simulation_delay);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
}
}
static void
set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *spec)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (object);
switch (property_id)
{
case PROP_ACCOUNT:
g_free (self->priv->account);
self->priv->account = g_value_dup_string (value);
break;
case PROP_SIMULATION_DELAY:
self->priv->simulation_delay = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
}
}
static void
finalize (GObject *object)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (object);
tp_contacts_mixin_finalize (object);
g_free (self->priv->account);
G_OBJECT_CLASS (example_contact_list_connection_parent_class)->finalize (
object);
}
static gchar *
get_unique_connection_name (TpBaseConnection *conn)
{
ExampleContactListConnection *self = EXAMPLE_CONTACT_LIST_CONNECTION (conn);
return g_strdup_printf ("%s@%p", self->priv->account, self);
}
gchar *
example_contact_list_normalize_contact (TpHandleRepoIface *repo,
const gchar *id,
gpointer context,
GError **error)
{
if (id[0] == '\0')
{
g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE,
"Contact ID must not be empty");
return NULL;
}
return g_utf8_normalize (id, -1, G_NORMALIZE_ALL_COMPOSE);
}
static gchar *
example_contact_list_normalize_group (TpHandleRepoIface *repo,
const gchar *id,
gpointer context,
GError **error)
{
if (id[0] == '\0')
{
g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE,
"Contact group name cannot be empty");
return NULL;
}
return g_utf8_normalize (id, -1, G_NORMALIZE_ALL_COMPOSE);
}
static void
create_handle_repos (TpBaseConnection *conn,
TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
{
repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new
(TP_HANDLE_TYPE_CONTACT, example_contact_list_normalize_contact, NULL);
repos[TP_HANDLE_TYPE_LIST] = tp_static_handle_repo_new
(TP_HANDLE_TYPE_LIST, example_contact_lists ());
repos[TP_HANDLE_TYPE_GROUP] = tp_dynamic_handle_repo_new
(TP_HANDLE_TYPE_GROUP, example_contact_list_normalize_group, NULL);
}
static void
alias_updated_cb (ExampleContactListManager *manager,
TpHandle contact,
ExampleContactListConnection *self)
{
GPtrArray *aliases;
GValueArray *pair;
pair = g_value_array_new (2);
g_value_array_append (pair, NULL);
g_value_array_append (pair, NULL);
g_value_init (pair->values + 0, G_TYPE_UINT);
g_value_init (pair->values + 1, G_TYPE_STRING);
g_value_set_uint (pair->values + 0, contact);
g_value_set_string (pair->values + 1,
example_contact_list_manager_get_alias (manager, contact));
aliases = g_ptr_array_sized_new (1);
g_ptr_array_add (aliases, pair);
tp_svc_connection_interface_aliasing_emit_aliases_changed (self, aliases);
g_ptr_array_free (aliases, TRUE);
g_value_array_free (pair);
}
static void
presence_updated_cb (ExampleContactListManager *manager,
TpHandle contact,
ExampleContactListConnection *self)
{
TpBaseConnection *base = (TpBaseConnection *) self;
TpPresenceStatus *status;
/* we ignore the presence indicated by the contact list for our own handle */
if (contact == base->self_handle)
return;
status = tp_presence_status_new (
example_contact_list_manager_get_presence (manager, contact),
NULL);
tp_presence_mixin_emit_one_presence_update ((GObject *) self,
contact, status);
tp_presence_status_free (status);
}
static GPtrArray *
create_channel_managers (TpBaseConnection *conn)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (conn);
GPtrArray *ret = g_ptr_array_sized_new (1);
self->priv->list_manager =
EXAMPLE_CONTACT_LIST_MANAGER (g_object_new (
EXAMPLE_TYPE_CONTACT_LIST_MANAGER,
"connection", conn,
"simulation-delay", self->priv->simulation_delay,
NULL));
g_signal_connect (self->priv->list_manager, "alias-updated",
G_CALLBACK (alias_updated_cb), self);
g_signal_connect (self->priv->list_manager, "presence-updated",
G_CALLBACK (presence_updated_cb), self);
g_ptr_array_add (ret, self->priv->list_manager);
return ret;
}
static gboolean
start_connecting (TpBaseConnection *conn,
GError **error)
{
ExampleContactListConnection *self = EXAMPLE_CONTACT_LIST_CONNECTION (conn);
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
TP_HANDLE_TYPE_CONTACT);
/* In a real connection manager we'd ask the underlying implementation to
* start connecting, then go to state CONNECTED when finished, but here
* we can do it immediately. */
conn->self_handle = tp_handle_ensure (contact_repo, self->priv->account,
NULL, error);
if (conn->self_handle == 0)
return FALSE;
tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTED,
TP_CONNECTION_STATUS_REASON_REQUESTED);
return TRUE;
}
static void
shut_down (TpBaseConnection *conn)
{
/* In a real connection manager we'd ask the underlying implementation to
* start shutting down, then call this function when finished, but here
* we can do it immediately. */
tp_base_connection_finish_shutdown (conn);
}
static void
aliasing_fill_contact_attributes (GObject *object,
const GArray *contacts,
GHashTable *attributes)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (object);
guint i;
for (i = 0; i < contacts->len; i++)
{
TpHandle contact = g_array_index (contacts, guint, i);
tp_contacts_mixin_set_contact_attribute (attributes, contact,
TP_TOKEN_CONNECTION_INTERFACE_ALIASING_ALIAS,
tp_g_value_slice_new_string (
example_contact_list_manager_get_alias (self->priv->list_manager,
contact)));
}
}
static void
constructed (GObject *object)
{
TpBaseConnection *base = TP_BASE_CONNECTION (object);
void (*chain_up) (GObject *) =
G_OBJECT_CLASS (example_contact_list_connection_parent_class)->constructed;
if (chain_up != NULL)
chain_up (object);
tp_contacts_mixin_init (object,
G_STRUCT_OFFSET (ExampleContactListConnection, contacts_mixin));
tp_base_connection_register_with_contacts_mixin (base);
tp_contacts_mixin_add_contact_attributes_iface (object,
TP_IFACE_CONNECTION_INTERFACE_ALIASING,
aliasing_fill_contact_attributes);
tp_presence_mixin_init (object,
G_STRUCT_OFFSET (ExampleContactListConnection, presence_mixin));
tp_presence_mixin_simple_presence_register_with_contacts_mixin (object);
}
static gboolean
status_available (GObject *object,
guint index_)
{
TpBaseConnection *base = TP_BASE_CONNECTION (object);
if (base->status != TP_CONNECTION_STATUS_CONNECTED)
return FALSE;
return TRUE;
}
static GHashTable *
get_contact_statuses (GObject *object,
const GArray *contacts,
GError **error)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (object);
TpBaseConnection *base = TP_BASE_CONNECTION (object);
guint i;
GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) tp_presence_status_free);
for (i = 0; i < contacts->len; i++)
{
TpHandle contact = g_array_index (contacts, guint, i);
ExampleContactListPresence presence;
GHashTable *parameters;
/* we get our own status from the connection, and everyone else's status
* from the contact lists */
if (contact == base->self_handle)
{
presence = (self->priv->away ? EXAMPLE_CONTACT_LIST_PRESENCE_AWAY
: EXAMPLE_CONTACT_LIST_PRESENCE_AVAILABLE);
}
else
{
presence = example_contact_list_manager_get_presence (
self->priv->list_manager, contact);
}
parameters = g_hash_table_new_full (g_str_hash,
g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free);
g_hash_table_insert (result, GUINT_TO_POINTER (contact),
tp_presence_status_new (presence, parameters));
g_hash_table_destroy (parameters);
}
return result;
}
static gboolean
set_own_status (GObject *object,
const TpPresenceStatus *status,
GError **error)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (object);
TpBaseConnection *base = TP_BASE_CONNECTION (object);
GHashTable *presences;
if (status->index == EXAMPLE_CONTACT_LIST_PRESENCE_AWAY)
{
if (self->priv->away)
return TRUE;
self->priv->away = TRUE;
}
else
{
if (!self->priv->away)
return TRUE;
self->priv->away = FALSE;
}
presences = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, NULL);
g_hash_table_insert (presences, GUINT_TO_POINTER (base->self_handle),
(gpointer) status);
tp_presence_mixin_emit_presence_update (object, presences);
g_hash_table_destroy (presences);
return TRUE;
}
static void
example_contact_list_connection_class_init (
ExampleContactListConnectionClass *klass)
{
static const gchar *interfaces_always_present[] = {
TP_IFACE_CONNECTION_INTERFACE_ALIASING,
TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
NULL };
TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass;
GObjectClass *object_class = (GObjectClass *) klass;
GParamSpec *param_spec;
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->finalize = finalize;
g_type_class_add_private (klass,
sizeof (ExampleContactListConnectionPrivate));
base_class->create_handle_repos = create_handle_repos;
base_class->get_unique_connection_name = get_unique_connection_name;
base_class->create_channel_managers = create_channel_managers;
base_class->start_connecting = start_connecting;
base_class->shut_down = shut_down;
base_class->interfaces_always_present = interfaces_always_present;
param_spec = g_param_spec_string ("account", "Account name",
"The username of this user", NULL,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec);
param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay",
"Delay between simulated network events",
0, G_MAXUINT32, 1000,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_SIMULATION_DELAY,
param_spec);
tp_contacts_mixin_class_init (object_class,
G_STRUCT_OFFSET (ExampleContactListConnectionClass, contacts_mixin));
tp_presence_mixin_class_init (object_class,
G_STRUCT_OFFSET (ExampleContactListConnectionClass, presence_mixin),
status_available, get_contact_statuses, set_own_status,
example_contact_list_presence_statuses ());
tp_presence_mixin_simple_presence_init_dbus_properties (object_class);
}
static void
get_alias_flags (TpSvcConnectionInterfaceAliasing *aliasing,
DBusGMethodInvocation *context)
{
TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context,
TP_CONNECTION_ALIAS_FLAG_USER_SET);
}
static void
get_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
const GArray *contacts,
DBusGMethodInvocation *context)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (aliasing);
TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
TP_HANDLE_TYPE_CONTACT);
GHashTable *result;
GError *error = NULL;
guint i;
TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
{
dbus_g_method_return_error (context, error);
g_error_free (error);
return;
}
result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
for (i = 0; i < contacts->len; i++)
{
TpHandle contact = g_array_index (contacts, TpHandle, i);
const gchar *alias = example_contact_list_manager_get_alias (
self->priv->list_manager, contact);
g_hash_table_insert (result, GUINT_TO_POINTER (contact),
(gchar *) alias);
}
tp_svc_connection_interface_aliasing_return_from_get_aliases (context,
result);
g_hash_table_destroy (result);
}
static void
request_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
const GArray *contacts,
DBusGMethodInvocation *context)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (aliasing);
TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
TP_HANDLE_TYPE_CONTACT);
GPtrArray *result;
gchar **strings;
GError *error = NULL;
guint i;
TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
{
dbus_g_method_return_error (context, error);
g_error_free (error);
return;
}
result = g_ptr_array_sized_new (contacts->len + 1);
for (i = 0; i < contacts->len; i++)
{
TpHandle contact = g_array_index (contacts, TpHandle, i);
const gchar *alias = example_contact_list_manager_get_alias (
self->priv->list_manager, contact);
g_ptr_array_add (result, (gchar *) alias);
}
g_ptr_array_add (result, NULL);
strings = (gchar **) g_ptr_array_free (result, FALSE);
tp_svc_connection_interface_aliasing_return_from_request_aliases (context,
(const gchar **) strings);
g_free (strings);
}
static void
set_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
GHashTable *aliases,
DBusGMethodInvocation *context)
{
ExampleContactListConnection *self =
EXAMPLE_CONTACT_LIST_CONNECTION (aliasing);
TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
TP_HANDLE_TYPE_CONTACT);
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, aliases);
while (g_hash_table_iter_next (&iter, &key, &value))
{
GError *error = NULL;
if (!tp_handle_is_valid (contact_repo, GPOINTER_TO_UINT (key),
&error))
{
dbus_g_method_return_error (context, error);
g_error_free (error);
return;
}
}
g_hash_table_iter_init (&iter, aliases);
while (g_hash_table_iter_next (&iter, &key, &value))
{
example_contact_list_manager_set_alias (self->priv->list_manager,
GPOINTER_TO_UINT (key), value);
}
tp_svc_connection_interface_aliasing_return_from_set_aliases (context);
}
static void
init_aliasing (gpointer iface,
gpointer iface_data G_GNUC_UNUSED)
{
TpSvcConnectionInterfaceAliasingClass *klass = iface;
#define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\
klass, x)
IMPLEMENT(get_alias_flags);
IMPLEMENT(request_aliases);
IMPLEMENT(get_aliases);
IMPLEMENT(set_aliases);
#undef IMPLEMENT
}