diff options
Diffstat (limited to 'src/facebook-contact-list.c')
-rw-r--r-- | src/facebook-contact-list.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/src/facebook-contact-list.c b/src/facebook-contact-list.c new file mode 100644 index 0000000..020d8d1 --- /dev/null +++ b/src/facebook-contact-list.c @@ -0,0 +1,508 @@ +/* telepathy-gruschler - A Telepathy connection manager for social networks. + * Copyright (C) 2009 Mathias Hasselmann + * + * 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 "facebook-contact-list.h" + +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/channel-iface.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/exportable-channel.h> +#include <telepathy-glib/handle.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/svc-channel.h> +#include <telepathy-glib/svc-generic.h> + +struct _GruschlerFacebookContactListPrivate { + TpBaseConnection *connection; + char *object_path; + + TpHandle handle; + TpHandleType handle_type; + + unsigned closed : 1; + unsigned disposed : 1; +}; + +static void _channel_iface_init (TpSvcChannelClass *iface); + +G_DEFINE_TYPE_WITH_CODE (GruschlerFacebookContactList, + gruschler_facebook_contact_list, + G_TYPE_OBJECT, + + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, _channel_iface_init); + + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, + tp_group_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init); + + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_CONTACT_LIST, NULL); + G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL); + G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL); +); + +/* property enum */ +enum { + PROP_0, + PROP_OBJECT_PATH, + PROP_CHANNEL_TYPE, + PROP_HANDLE_TYPE, + PROP_HANDLE, + PROP_TARGET_ID, + PROP_REQUESTED, + PROP_INITIATOR_HANDLE, + PROP_INITIATOR_ID, + PROP_CONNECTION, + PROP_INTERFACES, + PROP_CHANNEL_DESTROYED, + PROP_CHANNEL_PROPERTIES, +}; + +const char *_channel_interfaces[] = { + TP_IFACE_CHANNEL_INTERFACE_GROUP, +}; + +static void +gruschler_facebook_contact_list_init (GruschlerFacebookContactList *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GRUSCHLER_TYPE_FACEBOOK_CONTACT_LIST, + GruschlerFacebookContactListPrivate); +} + +static GObject * +_constructor (GType type, + unsigned n_props, + GObjectConstructParam *props) +{ + GruschlerFacebookContactList *self; + GObject *object; + GObjectClass *parent_class; + TpHandleRepoIface *handle_repo; + TpHandleRepoIface *contact_repo; + + parent_class = G_OBJECT_CLASS (gruschler_facebook_contact_list_parent_class); + object = parent_class->constructor (type, n_props, props); + self = GRUSCHLER_FACEBOOK_CONTACT_LIST (object); + + g_assert (self->priv->handle_type == TP_HANDLE_TYPE_GROUP || + self->priv->handle_type == TP_HANDLE_TYPE_LIST); + + handle_repo = tp_base_connection_get_handles (self->priv->connection, + self->priv->handle_type); + contact_repo = tp_base_connection_get_handles (self->priv->connection, + TP_HANDLE_TYPE_CONTACT); + + g_assert (tp_handle_is_valid (handle_repo, self->priv->handle, NULL)); + tp_handle_ref (handle_repo, self->priv->handle); + + /* initialize group mixin */ + tp_group_mixin_init (object, + G_STRUCT_OFFSET (GruschlerFacebookContactList, group), + contact_repo, self->priv->connection->self_handle); + + if (TP_HANDLE_TYPE_GROUP == self->priv->handle_type) + { + tp_group_mixin_change_flags (object, + TP_CHANNEL_GROUP_FLAG_CAN_ADD | + TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | + TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); + } + else if (TP_HANDLE_TYPE_LIST != self->priv->handle_type) + { + g_assert_not_reached (); + } + /* magic contact lists from here down... */ + else if (self->priv->handle == GRUSCHLER_FACEBOOK_LIST_HANDLE_PUBLISH) + { + tp_group_mixin_change_flags (object, + TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | + TP_CHANNEL_GROUP_FLAG_MESSAGE_ACCEPT | + TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE | + TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); + } + else if (self->priv->handle == GRUSCHLER_FACEBOOK_LIST_HANDLE_SUBSCRIBE) + { + tp_group_mixin_change_flags (object, + TP_CHANNEL_GROUP_FLAG_CAN_ADD | + TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | + TP_CHANNEL_GROUP_FLAG_CAN_RESCIND | + TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD | + TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE | + TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND | + TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); + } + else if (self->priv->handle == GRUSCHLER_FACEBOOK_LIST_HANDLE_STORED) + { + tp_group_mixin_change_flags (object, + TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | + TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); + } + else if (self->priv->handle == GRUSCHLER_FACEBOOK_LIST_HANDLE_DENY) + { + tp_group_mixin_change_flags (object, + TP_CHANNEL_GROUP_FLAG_CAN_ADD | + TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | + TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); + } + else + { + g_assert_not_reached (); + } + + /* register object on the bus */ + dbus_g_connection_register_g_object (tp_get_bus(), + self->priv->object_path, object); + + return object; +} + +static void +_get_property (GObject *object, + unsigned prop_id, + GValue *value, + GParamSpec *pspec) +{ + GruschlerFacebookContactList *self; + TpHandleRepoIface *handles; + const char *handle_name; + + self = GRUSCHLER_FACEBOOK_CONTACT_LIST (object); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + g_value_set_string (value, self->priv->object_path); + break; + case PROP_CHANNEL_TYPE: + g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST); + break; + case PROP_HANDLE_TYPE: + g_value_set_uint (value, self->priv->handle_type); + break; + case PROP_HANDLE: + g_value_set_uint (value, self->priv->handle); + break; + case PROP_TARGET_ID: + handles = tp_base_connection_get_handles (self->priv->connection, + self->priv->handle_type); + handle_name = tp_handle_inspect (handles, self->priv->handle); + g_value_set_string (value, handle_name); + break; + case PROP_CONNECTION: + g_value_set_object (value, self->priv->connection); + break; + case PROP_INTERFACES: + g_value_set_boxed (value, _channel_interfaces); + break; + case PROP_INITIATOR_HANDLE: + g_value_set_uint (value, 0); + break; + case PROP_INITIATOR_ID: + g_value_set_static_string (value, ""); + break; + case PROP_REQUESTED: + g_value_set_boolean (value, FALSE); + break; + case PROP_CHANNEL_DESTROYED: + g_value_set_boolean (value, self->priv->closed); + break; + case PROP_CHANNEL_PROPERTIES: + g_value_take_boxed (value, + tp_dbus_properties_mixin_make_properties_hash + (object, + TP_IFACE_CHANNEL, "TargetHandle", + TP_IFACE_CHANNEL, "TargetHandleType", + TP_IFACE_CHANNEL, "ChannelType", + TP_IFACE_CHANNEL, "TargetID", + TP_IFACE_CHANNEL, "InitiatorHandle", + TP_IFACE_CHANNEL, "InitiatorID", + TP_IFACE_CHANNEL, "Requested", + TP_IFACE_CHANNEL, "Interfaces", + NULL)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_set_property (GObject *object, + unsigned prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GruschlerFacebookContactList *self; + + self = GRUSCHLER_FACEBOOK_CONTACT_LIST (object); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + g_free (self->priv->object_path); + self->priv->object_path = g_value_dup_string (value); + break; + case PROP_CHANNEL_TYPE: + break; /* ignore */ + case PROP_HANDLE_TYPE: + self->priv->handle_type = g_value_get_uint (value); + break; + case PROP_HANDLE: + self->priv->handle = g_value_get_uint (value); + break; + case PROP_INITIATOR_HANDLE: + break; /* ignore */ + case PROP_CONNECTION: + self->priv->connection = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_dispose (GObject *object) +{ + GruschlerFacebookContactList *self; + + self = GRUSCHLER_FACEBOOK_CONTACT_LIST (object); + + if (!self->priv->disposed) + { + if (!self->priv->closed) + tp_svc_channel_emit_closed (self); + + self->priv->disposed = TRUE; + } + + G_OBJECT_CLASS (gruschler_facebook_contact_list_parent_class)->dispose (object); +} + +static void +_finalize (GObject *object) +{ + GruschlerFacebookContactList *self; + TpHandleRepoIface *handles; + + self = GRUSCHLER_FACEBOOK_CONTACT_LIST (object); + + handles = tp_base_connection_get_handles (self->priv->connection, + self->priv->handle_type); + + tp_handle_unref(handles, self->priv->handle); + + if (self->priv->object_path) + g_free (self->priv->object_path); + + G_OBJECT_CLASS (gruschler_facebook_contact_list_parent_class)->finalize (object); +} + +static gboolean +_group_add_member (GObject *object, + TpHandle handle, + const char *message, + GError **error) +{ + /* FIXME: implement this */ + g_debug ("%s: handle=%u, message=%s", G_STRFUNC, handle, message); + g_return_val_if_reached (FALSE); +} + +static gboolean +_group_remove_member (GObject *object, + TpHandle handle, + const char *message, + GError **error) +{ + /* FIXME: implement this */ + g_debug ("%s: handle=%u, message=%s", G_STRFUNC, handle, message); + g_return_val_if_reached (FALSE); +} + +static void +gruschler_facebook_contact_list_class_init (GruschlerFacebookContactListClass *class) +{ + static TpDBusPropertiesMixinPropImpl channel_props[] = { + { "TargetHandleType", "handle-type", NULL }, + { "TargetHandle", "handle", NULL }, + { "TargetID", "target-id", NULL }, + { "ChannelType", "channel-type", NULL }, + { "Interfaces", "interfaces", NULL }, + { "Requested", "requested", NULL }, + { "InitiatorHandle", "initiator-handle", NULL }, + { "InitiatorID", "initiator-id", NULL }, + { NULL, } + }; + + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { + { TP_IFACE_CHANNEL, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, channel_props, + }, + + { NULL, } + }; + + gsize offset; + GObjectClass *object_class; + GParamSpec *pspec; + + object_class = G_OBJECT_CLASS (class); + + object_class->constructor = _constructor; + object_class->get_property = _get_property; + object_class->set_property = _set_property; + object_class->dispose = _dispose; + object_class->finalize = _finalize; + + g_object_class_override_property (object_class, PROP_OBJECT_PATH, "object-path"); + g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, "channel-type"); + g_object_class_override_property (object_class, PROP_HANDLE_TYPE, "handle-type"); + g_object_class_override_property (object_class, PROP_HANDLE, "handle"); + + g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED, "channel-destroyed"); + g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES, "channel-properties"); + + pspec = g_param_spec_object ("connection", + "Connection", + "The connection that owns this channel.", + TP_TYPE_BASE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CONNECTION, pspec); + + pspec = g_param_spec_boxed ("interfaces", + "Interfaces", + "Additional interfaces implemented by channel", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INTERFACES, pspec); + + pspec = g_param_spec_string ("target-id", + "Target ID", + "Inspected name of this channel's handle", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_TARGET_ID, pspec); + + pspec = g_param_spec_uint ("initiator-handle", + "Initiator handle", + "The contact who initiated the channel", + 0, G_MAXUINT32, 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE, pspec); + + pspec = g_param_spec_string ("initiator-id", + "Initiator ID", + "Inspected name of the channel initiator handle", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INITIATOR_ID, pspec); + + pspec = g_param_spec_boolean ("requested", + "Requested", + "Wheither this channel was requested by the local user", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_REQUESTED, pspec); + + class->properties_class.interfaces = prop_interfaces; + offset = G_STRUCT_OFFSET (GruschlerFacebookContactListClass, properties_class); + tp_dbus_properties_mixin_class_init (object_class, offset); + + offset = G_STRUCT_OFFSET (GruschlerFacebookContactListClass, group_class), + tp_group_mixin_class_init (object_class, offset, + _group_add_member, _group_remove_member); + tp_group_mixin_init_dbus_properties (object_class); + + g_type_class_add_private (class, sizeof (GruschlerFacebookContactListPrivate)); +} + +static void +_channel_close (TpSvcChannel *channel, + DBusGMethodInvocation *context) +{ + GruschlerFacebookContactList *self; + + self = GRUSCHLER_FACEBOOK_CONTACT_LIST (channel); + + if (TP_HANDLE_TYPE_LIST == self->priv->handle_type) + { + GError error = { + TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, + "you may not close contact list channels" + }; + + dbus_g_method_return_error (context, &error); + } + else if (tp_handle_set_size (self->group.members)) + { + GError error = { + TP_ERRORS, TP_ERROR_NOT_AVAILABLE, + "you may not close this group, because it's not empty" + }; + + dbus_g_method_return_error (context, &error); + } + else + { + /* TODO: figure out if friend lists can be modified */ + self->priv->closed = TRUE; + tp_svc_channel_emit_closed (self); + tp_svc_channel_return_from_close (context); + } +} + +static void +_channel_get_channel_type (TpSvcChannel *channel, + DBusGMethodInvocation *context) +{ + tp_svc_channel_return_from_get_channel_type (context, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST); +} + + +static void +_channel_get_handle (TpSvcChannel *channel, + DBusGMethodInvocation *context) +{ + tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT, + GRUSCHLER_FACEBOOK_CONTACT_LIST (channel)->priv->handle); +} + +static void +_channel_get_interfaces (TpSvcChannel *channel, + DBusGMethodInvocation *context) +{ + tp_svc_channel_return_from_get_interfaces (context, _channel_interfaces); +} + +static void +_channel_iface_init (TpSvcChannelClass *iface) +{ +#define IMPLEMENT(x) \ + tp_svc_channel_implement_##x (iface, _channel_##x) + IMPLEMENT(close); + IMPLEMENT(get_channel_type); + IMPLEMENT(get_handle); + IMPLEMENT(get_interfaces); +#undef IMPLEMENT +} + |