summaryrefslogtreecommitdiff
path: root/src/facebook-contact-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/facebook-contact-list.c')
-rw-r--r--src/facebook-contact-list.c508
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
+}
+