/* A ContactList channel with handle type LIST or GROUP. * * Copyright © 2009-2010 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include static void list_channel_iface_init (TpSvcChannelClass *iface); static void group_channel_iface_init (TpSvcChannelClass *iface); static void list_group_iface_init (TpSvcChannelInterfaceGroupClass *iface); static void group_group_iface_init (TpSvcChannelInterfaceGroupClass *iface); /* Abstract base class */ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (TpBaseContactListChannel, _tp_base_contact_list_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_CONTACT_LIST, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, NULL)) /* Subclass for handle type LIST */ G_DEFINE_TYPE_WITH_CODE (TpContactListChannel, _tp_contact_list_channel, TP_TYPE_BASE_CONTACT_LIST_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, list_group_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, list_channel_iface_init)) /* Subclass for handle type GROUP */ G_DEFINE_TYPE_WITH_CODE (TpContactGroupChannel, _tp_contact_group_channel, TP_TYPE_BASE_CONTACT_LIST_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, group_group_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, group_channel_iface_init)) static GPtrArray * base_contact_list_get_interfaces (TpBaseChannel *self) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( _tp_base_contact_list_channel_parent_class)->get_interfaces (self); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_GROUP); return interfaces; }; enum { PROP_MANAGER = 1, N_PROPS }; static void _tp_base_contact_list_channel_init (TpBaseContactListChannel *self) { } static void _tp_contact_list_channel_init (TpContactListChannel *self) { } static void _tp_contact_group_channel_init (TpContactGroupChannel *self) { } static void tp_base_contact_list_channel_constructed (GObject *object) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (object); TpBaseChannel *base = (TpBaseChannel *) self; TpBaseConnection *conn = tp_base_channel_get_connection (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); void (*chain_up) (GObject *) = ((GObjectClass *) _tp_base_contact_list_channel_parent_class)->constructed; if (chain_up != NULL) chain_up (object); g_assert (TP_IS_BASE_CONTACT_LIST (self->manager)); tp_base_channel_register (base); tp_group_mixin_init (object, G_STRUCT_OFFSET (TpBaseContactListChannel, group), contact_repo, tp_base_connection_get_self_handle (conn)); /* Both the subclasses have full support for telepathy-spec 0.17.6. */ tp_group_mixin_change_flags (object, TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); } static void tp_contact_list_channel_constructed (GObject *object) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (object); void (*chain_up) (GObject *) = ((GObjectClass *) _tp_contact_list_channel_parent_class)->constructed; if (chain_up != NULL) chain_up (object); tp_group_mixin_change_flags (object, _tp_base_contact_list_get_list_flags (self->manager, tp_base_channel_get_target_handle ((TpBaseChannel *) self)), 0); } static void tp_contact_group_channel_constructed (GObject *object) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (object); void (*chain_up) (GObject *) = ((GObjectClass *) _tp_contact_group_channel_parent_class)->constructed; if (chain_up != NULL) chain_up (object); tp_group_mixin_change_flags (object, _tp_base_contact_list_get_group_flags (self->manager), 0); } static void tp_base_contact_list_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (object); switch (property_id) { case PROP_MANAGER: g_value_set_object (value, self->manager); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void tp_base_contact_list_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (object); switch (property_id) { case PROP_MANAGER: g_assert (self->manager == NULL); /* construct-only */ self->manager = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } void _tp_base_contact_list_channel_close (TpBaseContactListChannel *self) { if (self->manager == NULL) return; tp_clear_object (&self->manager); tp_group_mixin_finalize ((GObject *) self); tp_base_channel_destroyed ((TpBaseChannel *) self); } static void tp_base_contact_list_channel_dispose (GObject *object) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (object); void (*dispose) (GObject *) = G_OBJECT_CLASS (_tp_base_contact_list_channel_parent_class)->dispose; _tp_base_contact_list_channel_close (self); if (dispose != NULL) dispose (object); } static gboolean tp_base_contact_list_channel_check_still_usable ( TpBaseContactListChannel *self, DBusGMethodInvocation *context) { if (self->manager == NULL) { GError e = { TP_ERROR, TP_ERROR_TERMINATED, "Channel already closed" }; dbus_g_method_return_error (context, &e); return FALSE; } return TRUE; } static gboolean group_add_member (GObject *object, TpHandle handle, const gchar *message, GError **error) { /* We don't use this: it's synchronous */ g_return_val_if_reached (FALSE); } static gboolean group_remove_member (GObject *object, TpHandle handle, const gchar *message, GError **error) { /* We don't use this: it's synchronous */ g_return_val_if_reached (FALSE); } static gboolean list_add_member (GObject *object, TpHandle handle, const gchar *message, GError **error) { /* We don't use this: it's synchronous */ g_return_val_if_reached (FALSE); } static gboolean list_remove_member (GObject *object, TpHandle handle, const gchar *message, GError **error) { /* We don't use this: it's synchronous */ g_return_val_if_reached (FALSE); } /* We don't use this: #TpBaseChannelClass.close doesn't allow us to fail to * close, which is a quirk of the old ContactList design, so subclasses must * IMPLEMENT (close) manually. */ static void stub_close (TpBaseChannel *channel G_GNUC_UNUSED) { g_return_if_reached (); } static void _tp_base_contact_list_channel_class_init (TpBaseContactListChannelClass *cls) { GObjectClass *object_class = (GObjectClass *) cls; TpBaseChannelClass *base_class = (TpBaseChannelClass *) cls; object_class->constructed = tp_base_contact_list_channel_constructed; object_class->set_property = tp_base_contact_list_channel_set_property; object_class->get_property = tp_base_contact_list_channel_get_property; object_class->dispose = tp_base_contact_list_channel_dispose; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_CONTACT_LIST; base_class->target_handle_type = 0; /* placeholder, set in subclass */ base_class->get_interfaces = base_contact_list_get_interfaces; base_class->close = stub_close; /* placeholder, not called */ g_object_class_install_property (object_class, PROP_MANAGER, g_param_spec_object ("manager", "TpBaseContactList", "TpBaseContactList object that owns this channel", TP_TYPE_BASE_CONTACT_LIST, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* Group mixin is initialized separately for each subclass - they have * different callbacks */ } static void _tp_contact_list_channel_class_init (TpContactListChannelClass *cls) { GObjectClass *object_class = (GObjectClass *) cls; TpBaseChannelClass *base_class = (TpBaseChannelClass *) cls; object_class->constructed = tp_contact_list_channel_constructed; base_class->target_handle_type = TP_HANDLE_TYPE_LIST; tp_group_mixin_class_init (object_class, G_STRUCT_OFFSET (TpBaseContactListChannelClass, group_class), list_add_member, list_remove_member); tp_group_mixin_init_dbus_properties (object_class); } static void _tp_contact_group_channel_class_init (TpContactGroupChannelClass *cls) { GObjectClass *object_class = (GObjectClass *) cls; TpBaseChannelClass *base_class = (TpBaseChannelClass *) cls; object_class->constructed = tp_contact_group_channel_constructed; base_class->target_handle_type = TP_HANDLE_TYPE_GROUP; tp_group_mixin_class_init (object_class, G_STRUCT_OFFSET (TpBaseContactListChannelClass, group_class), group_add_member, group_remove_member); tp_group_mixin_init_dbus_properties (object_class); } static void list_channel_close (TpSvcChannel *iface G_GNUC_UNUSED, DBusGMethodInvocation *context) { GError e = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "ContactList channels with handle type LIST may not be closed" }; dbus_g_method_return_error (context, &e); } static void group_channel_close (TpSvcChannel *iface, DBusGMethodInvocation *context) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (iface); GError *error = NULL; if (!tp_base_contact_list_channel_check_still_usable (self, context)) return; if (tp_handle_set_size (self->group.members) > 0) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Non-empty groups may not be deleted (closed)"); goto error; } if (!_tp_base_contact_list_delete_group_by_handle (self->manager, tp_base_channel_get_target_handle ((TpBaseChannel *) self), &error)) goto error; tp_svc_channel_return_from_close (context); return; error: dbus_g_method_return_error (context, error); g_clear_error (&error); } static void list_channel_iface_init (TpSvcChannelClass *iface) { #define IMPLEMENT(x) tp_svc_channel_implement_##x (iface, list_channel_##x) IMPLEMENT (close); #undef IMPLEMENT } static void group_channel_iface_init (TpSvcChannelClass *iface) { #define IMPLEMENT(x) tp_svc_channel_implement_##x (iface, group_channel_##x) IMPLEMENT (close); #undef IMPLEMENT } static void list_group_add_members (TpSvcChannelInterfaceGroup *iface, const GArray *contacts, const gchar *message, DBusGMethodInvocation *context) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (iface); if (tp_base_contact_list_channel_check_still_usable (self, context)) _tp_base_contact_list_add_to_list (self->manager, tp_base_channel_get_target_handle ((TpBaseChannel *) self), contacts, message, context); } static void list_group_remove_members_with_reason (TpSvcChannelInterfaceGroup *iface, const GArray *contacts, const gchar *message, guint reason, DBusGMethodInvocation *context) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (iface); if (tp_base_contact_list_channel_check_still_usable (self, context)) _tp_base_contact_list_remove_from_list (self->manager, tp_base_channel_get_target_handle ((TpBaseChannel *) self), contacts, message, reason, context); } static void list_group_remove_members (TpSvcChannelInterfaceGroup *iface, const GArray *contacts, const gchar *message, DBusGMethodInvocation *context) { /* also returns void, so this is OK */ list_group_remove_members_with_reason (iface, contacts, message, TP_CHANNEL_GROUP_CHANGE_REASON_NONE, context); } static void list_group_iface_init (TpSvcChannelInterfaceGroupClass *iface) { tp_group_mixin_iface_init (iface, NULL); #define IMPLEMENT(x) tp_svc_channel_interface_group_implement_##x (iface, \ list_group_##x) IMPLEMENT (add_members); IMPLEMENT (remove_members); IMPLEMENT (remove_members_with_reason); #undef IMPLEMENT } static void group_group_add_members (TpSvcChannelInterfaceGroup *iface, const GArray *contacts, const gchar *message, DBusGMethodInvocation *context) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (iface); if (tp_base_contact_list_channel_check_still_usable (self, context)) _tp_base_contact_list_add_to_group (self->manager, tp_base_channel_get_target_handle ((TpBaseChannel *) self), contacts, message, context); } static void group_group_remove_members_with_reason (TpSvcChannelInterfaceGroup *iface, const GArray *contacts, const gchar *message, guint reason, DBusGMethodInvocation *context) { TpBaseContactListChannel *self = TP_BASE_CONTACT_LIST_CHANNEL (iface); if (tp_base_contact_list_channel_check_still_usable (self, context)) _tp_base_contact_list_remove_from_group (self->manager, tp_base_channel_get_target_handle ((TpBaseChannel *) self), contacts, message, reason, context); } static void group_group_remove_members (TpSvcChannelInterfaceGroup *iface, const GArray *contacts, const gchar *message, DBusGMethodInvocation *context) { /* also returns void, so this is OK */ group_group_remove_members_with_reason (iface, contacts, message, TP_CHANNEL_GROUP_CHANGE_REASON_NONE, context); } static void group_group_iface_init (TpSvcChannelInterfaceGroupClass *iface) { tp_group_mixin_iface_init (iface, NULL); #define IMPLEMENT(x) tp_svc_channel_interface_group_implement_##x (iface, \ group_group_##x) IMPLEMENT (add_members); IMPLEMENT (remove_members); IMPLEMENT (remove_members_with_reason); #undef IMPLEMENT }