From 9668dea53413cda4e3b763df0b52963a6ab8d0dc Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Sat, 1 Sep 2012 00:37:04 +0300 Subject: [PATCH] telepathy: implement own status handling --- src/telepathy/Makefile.am | 1 + src/telepathy/telepathy-connection.c | 145 +++++++++++++++-------- src/telepathy/telepathy-private.h | 13 +++ src/telepathy/telepathy-protocol.c | 1 - src/telepathy/telepathy-status.c | 218 +++++++++++++++++++++++++++++++++++ src/telepathy/telepathy-stubs.c | 10 -- 6 files changed, 326 insertions(+), 62 deletions(-) create mode 100644 src/telepathy/telepathy-status.c diff --git a/src/telepathy/Makefile.am b/src/telepathy/Makefile.am index c3a805d7..196b3060 100644 --- a/src/telepathy/Makefile.am +++ b/src/telepathy/Makefile.am @@ -13,6 +13,7 @@ telepathy_sipe_SOURCES = \ telepathy-private.h \ telepathy-protocol.c \ telepathy-schedule.c \ + telepathy-status.c \ telepathy-stubs.c \ telepathy-transport.c diff --git a/src/telepathy/telepathy-connection.c b/src/telepathy/telepathy-connection.c index 87575108..3082d1e9 100644 --- a/src/telepathy/telepathy-connection.c +++ b/src/telepathy/telepathy-connection.c @@ -45,10 +45,12 @@ G_BEGIN_DECLS */ typedef struct _SipeConnectionClass { TpBaseConnectionClass parent_class; + TpPresenceMixinClass presence_mixin; } SipeConnectionClass; typedef struct _SipeConnection { TpBaseConnection parent; + TpPresenceMixin presence_mixin; TpSimplePasswordManager *password_manager; @@ -80,9 +82,15 @@ G_END_DECLS /* * Connection class - type definition */ -G_DEFINE_TYPE(SipeConnection, - sipe_connection, - TP_TYPE_BASE_CONNECTION) +G_DEFINE_TYPE_WITH_CODE(SipeConnection, + sipe_connection, + TP_TYPE_BASE_CONNECTION, + 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) + ) + /* * Connection class - instance methods @@ -143,6 +151,8 @@ static gboolean connect_to_core(SipeConnection *self, sipe_public->backend_private = telepathy_private; telepathy_private->public = sipe_public; telepathy_private->connection = self; + telepathy_private->activity = SIPE_ACTIVITY_UNSET; + telepathy_private->message = NULL; telepathy_private->transport = NULL; telepathy_private->ipaddress = NULL; @@ -279,6 +289,9 @@ static gboolean disconnect_from_core(gpointer data) g_free(telepathy_private->ipaddress); telepathy_private->ipaddress = NULL; + g_free(telepathy_private->message); + telepathy_private->message = NULL; + SIPE_DEBUG_INFO_NOFORMAT("disconnect_from_core: core deallocated"); /* now it is OK to destroy the connection object */ @@ -308,6 +321,20 @@ static GPtrArray *create_channel_managers(TpBaseConnection *base) return(channel_managers); } +static void sipe_connection_constructed(GObject *object) +{ + void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_connection_parent_class)->constructed; + + if (chain_up) + chain_up(object); + + tp_presence_mixin_init(object, + G_STRUCT_OFFSET(SipeConnection, + presence_mixin)); + /* @TODO when contacts are implemented + tp_presence_mixin_simple_presence_register_with_contacts_mixin(object); */ +} + static void sipe_connection_finalize(GObject *object) { SipeConnection *self = SIPE_CONNECTION(object); @@ -330,7 +357,9 @@ static void sipe_connection_finalize(GObject *object) */ static const gchar *interfaces_always_present[] = { /* @TODO */ + TP_IFACE_CONNECTION_INTERFACE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, NULL }; @@ -341,7 +370,8 @@ static void sipe_connection_class_init(SipeConnectionClass *klass) SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::class_init"); - object_class->finalize = sipe_connection_finalize; + object_class->constructed = sipe_connection_constructed; + object_class->finalize = sipe_connection_finalize; base_class->create_handle_repos = create_handle_repos; base_class->start_connecting = start_connecting; @@ -349,6 +379,10 @@ static void sipe_connection_class_init(SipeConnectionClass *klass) base_class->create_channel_managers = create_channel_managers; base_class->interfaces_always_present = interfaces_always_present; + + sipe_telepathy_status_init(object_class, + G_STRUCT_OFFSET(SipeConnectionClass, presence_mixin)); + tp_presence_mixin_simple_presence_init_dbus_properties(object_class); } static void sipe_connection_init(SIPE_UNUSED_PARAMETER SipeConnection *self) @@ -426,65 +460,74 @@ TpBaseConnection *sipe_telepathy_connection_new(TpBaseProtocol *protocol, return(TP_BASE_CONNECTION(conn)); } +struct sipe_backend_private *sipe_telepathy_connection_private(GObject *object) +{ + SipeConnection *self = SIPE_CONNECTION(object); + /* connected to core already? */ + if (self->private.public) + return(&self->private); + else + return(NULL); +} /* * Backend adaptor functions */ void sipe_backend_connection_completed(struct sipe_core_public *sipe_public) { - SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; - TpBaseConnection *base = TP_BASE_CONNECTION(self); - - /* we are only allowed to do this once */ - if (base->status != TP_CONNECTION_STATUS_CONNECTED) - tp_base_connection_change_status(base, - TP_CONNECTION_STATUS_CONNECTED, - TP_CONNECTION_STATUS_REASON_REQUESTED); + SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; + TpBaseConnection *base = TP_BASE_CONNECTION(self); + + /* we are only allowed to do this once */ + if (base->status != TP_CONNECTION_STATUS_CONNECTED) + tp_base_connection_change_status(base, + TP_CONNECTION_STATUS_CONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); } void sipe_backend_connection_error(struct sipe_core_public *sipe_public, sipe_connection_error error, const gchar *msg) { - SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; - TpBaseConnection *base = TP_BASE_CONNECTION(self); - GHashTable *details = tp_asv_new("server-message", G_TYPE_STRING, msg, - NULL); - TpConnectionStatusReason reason; - const gchar *name; - - self->is_disconnecting = TRUE; - - switch (error) { - case SIPE_CONNECTION_ERROR_NETWORK: - reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; - if (base->status == TP_CONNECTION_STATUS_CONNECTING) - name = TP_ERROR_STR_CONNECTION_FAILED; - else - name = TP_ERROR_STR_CONNECTION_LOST; - break; - - case SIPE_CONNECTION_ERROR_INVALID_USERNAME: - case SIPE_CONNECTION_ERROR_INVALID_SETTINGS: - case SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED: - case SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE: - /* copied from haze code. I agree there should be better ones */ - reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED; - name = TP_ERROR_STR_AUTHENTICATION_FAILED; - break; - - default: - reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; - name = TP_ERROR_STR_DISCONNECTED; - break; - } - - SIPE_DEBUG_ERROR("sipe_backend_connection_error: %s (%s)", name, msg); - tp_base_connection_disconnect_with_dbus_error(base, - name, - details, - reason); - g_hash_table_unref(details); + SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; + TpBaseConnection *base = TP_BASE_CONNECTION(self); + GHashTable *details = tp_asv_new("server-message", G_TYPE_STRING, msg, + NULL); + TpConnectionStatusReason reason; + const gchar *name; + + self->is_disconnecting = TRUE; + + switch (error) { + case SIPE_CONNECTION_ERROR_NETWORK: + reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; + if (base->status == TP_CONNECTION_STATUS_CONNECTING) + name = TP_ERROR_STR_CONNECTION_FAILED; + else + name = TP_ERROR_STR_CONNECTION_LOST; + break; + + case SIPE_CONNECTION_ERROR_INVALID_USERNAME: + case SIPE_CONNECTION_ERROR_INVALID_SETTINGS: + case SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED: + case SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE: + /* copied from haze code. I agree there should be better ones */ + reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED; + name = TP_ERROR_STR_AUTHENTICATION_FAILED; + break; + + default: + reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; + name = TP_ERROR_STR_DISCONNECTED; + break; + } + + SIPE_DEBUG_ERROR("sipe_backend_connection_error: %s (%s)", name, msg); + tp_base_connection_disconnect_with_dbus_error(base, + name, + details, + reason); + g_hash_table_unref(details); } gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public) diff --git a/src/telepathy/telepathy-private.h b/src/telepathy/telepathy-private.h index 6ba6fbba..7ce57c8f 100644 --- a/src/telepathy/telepathy-private.h +++ b/src/telepathy/telepathy-private.h @@ -21,6 +21,8 @@ */ /* Forward declarations */ +struct _GObject; +struct _GObjectClass; struct _SipeConnection; struct _TpBaseConnection; struct _TpBaseConnectionManager; @@ -33,6 +35,12 @@ struct sipe_transport_telepathy; struct sipe_backend_private { struct sipe_core_public *public; struct _SipeConnection *connection; + + /* status */ + guint activity; + gchar *message; + + /* transport */ struct sipe_transport_telepathy *transport; gchar *ipaddress; }; @@ -41,6 +49,7 @@ struct sipe_backend_private { struct _TpBaseConnection *sipe_telepathy_connection_new(struct _TpBaseProtocol *protocol, GHashTable *params, GError **error); +struct sipe_backend_private *sipe_telepathy_connection_private(GObject *object); /* debugging */ void sipe_telepathy_debug_init(void); @@ -52,6 +61,10 @@ gchar *sipe_telepathy_protocol_normalize_contact(struct _TpBaseProtocol *self, const gchar *contact, GError **error); +/* status */ +void sipe_telepathy_status_init(struct _GObjectClass *object_class, + gsize struct_offset); + /* Local Variables: diff --git a/src/telepathy/telepathy-protocol.c b/src/telepathy/telepathy-protocol.c index 89ea55cf..dd219bad 100644 --- a/src/telepathy/telepathy-protocol.c +++ b/src/telepathy/telepathy-protocol.c @@ -106,7 +106,6 @@ static const TpCMParamSpec *get_parameters(SIPE_UNUSED_PARAMETER TpBaseProtocol .filter = (_filter), \ .filter_data = NULL, \ .setter_data = NULL, \ - ._future1 = NULL, \ } static const TpCMParamSpec const sipe_parameters[] = { diff --git a/src/telepathy/telepathy-status.c b/src/telepathy/telepathy-status.c new file mode 100644 index 00000000..fed49653 --- /dev/null +++ b/src/telepathy/telepathy-status.c @@ -0,0 +1,218 @@ +/** + * @file telepathy-status.c + * + * pidgin-sipe + * + * Copyright (C) 2012 SIPE Project + * + * 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 +#include +#include + +#include "sipe-backend.h" +#include "sipe-common.h" +#include "sipe-core.h" + +#include "telepathy-private.h" + +static const TpPresenceStatusOptionalArgumentSpec args[] = { + { .name = "message", .dtype = "s" }, + { .name = NULL, .dtype = NULL } +}; + +/* Sipe core activity <-> Telepathy status mapping */ +#define SIPE_TELEPATHY_STATUS(_name, _type, _self, _args) \ + { \ + .name = (_name), \ + .presence_type = (_type), \ + .self = (_self), \ + .optional_arguments = (_args), \ + } +#define SIPE_TELEPATHY_STATUS_NONE(_name, _type, _self) \ + SIPE_TELEPATHY_STATUS(_name, _type, _self, NULL) +#define SIPE_TELEPATHY_STATUS_MESSAGE(_name, _type, _self) \ + SIPE_TELEPATHY_STATUS(_name, _type, _self, args) +static const TpPresenceStatusSpec statuses[SIPE_ACTIVITY_NUM_TYPES + 1] = { +/* SIPE_ACTIVITY_UNSET */ SIPE_TELEPATHY_STATUS_NONE( "unset", TP_CONNECTION_PRESENCE_TYPE_UNSET, FALSE), +/* SIPE_ACTIVITY_AVAILABLE */ SIPE_TELEPATHY_STATUS_MESSAGE("available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE), +/* SIPE_ACTIVITY_ONLINE */ SIPE_TELEPATHY_STATUS_MESSAGE("online", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE), +/* SIPE_ACTIVITY_INACTIVE */ SIPE_TELEPATHY_STATUS_MESSAGE("idle", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE), +/* SIPE_ACTIVITY_BUSY */ SIPE_TELEPATHY_STATUS_MESSAGE("busy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* SIPE_ACTIVITY_BUSYIDLE */ SIPE_TELEPATHY_STATUS_MESSAGE("busyidle", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* SIPE_ACTIVITY_DND */ SIPE_TELEPATHY_STATUS_MESSAGE("do-not-disturb", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* SIPE_ACTIVITY_BRB */ SIPE_TELEPATHY_STATUS_MESSAGE("be-right-back", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE), +/* SIPE_ACTIVITY_AWAY */ SIPE_TELEPATHY_STATUS_MESSAGE("away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE), +/* SIPE_ACTIVITY_LUNCH */ SIPE_TELEPATHY_STATUS_MESSAGE("out-to-lunch", TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, TRUE), +/* SIPE_ACTIVITY_INVISIBLE */ SIPE_TELEPATHY_STATUS_NONE( "invisible", TP_CONNECTION_PRESENCE_TYPE_HIDDEN, TRUE), +/* SIPE_ACTIVITY_OFFLINE */ SIPE_TELEPATHY_STATUS_NONE( "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE), +/* SIPE_ACTIVITY_ON_PHONE */ SIPE_TELEPATHY_STATUS_MESSAGE("on-the-phone", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* SIPE_ACTIVITY_IN_CONF */ SIPE_TELEPATHY_STATUS_MESSAGE("in-a-conference", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* SIPE_ACTIVITY_IN_MEETING */ SIPE_TELEPATHY_STATUS_MESSAGE("in-a-meeting", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* SIPE_ACTIVITY_OOF */ SIPE_TELEPATHY_STATUS_MESSAGE("out-of-office", TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, TRUE), +/* SIPE_ACTIVITY_URGENT_ONLY */ SIPE_TELEPATHY_STATUS_MESSAGE("urgent-interruptions-only", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), +/* end-of-array indicator */ SIPE_TELEPATHY_STATUS_NONE( NULL, 0, FALSE) +}; + +static gboolean status_available(SIPE_UNUSED_PARAMETER GObject *object, + guint index) +{ + /* + * @TODO: what is this function supposed to do? + * - TRUE: index is one of the "user is available" statuses? + * - TRUE: index is a valid status? + */ + return(statuses[index].name != NULL); +} + +static GHashTable *get_contact_statuses(SIPE_UNUSED_PARAMETER GObject *object, + SIPE_UNUSED_PARAMETER const GArray *contacts, + SIPE_UNUSED_PARAMETER GError **error) +{ + GHashTable *status_table = g_hash_table_new(g_direct_hash, + g_direct_equal); + /* @TODO */ + SIPE_DEBUG_INFO_NOFORMAT("get_contact_statuses: NOT IMPLEMENTED!"); + return(status_table); +} + +static void update_status(struct sipe_backend_private *telepathy_private, + guint activity, + const gchar *message, + const TpPresenceStatus *status, + gboolean outgoing) +{ + GObject *connection = G_OBJECT(telepathy_private->connection); + GHashTable *presences; + + /* update internal status */ + telepathy_private->activity = activity; + g_free(telepathy_private->message); + telepathy_private->message = NULL; + if (message) + telepathy_private->message = g_strdup(message); + + /* outgoing status update */ + if (outgoing) + sipe_core_status_set(telepathy_private->public, + activity, + message); + + /* emit status update signal */ + presences = g_hash_table_new(g_direct_hash, g_direct_equal); + g_hash_table_insert(presences, + GUINT_TO_POINTER(tp_base_connection_get_self_handle(TP_BASE_CONNECTION(connection))), + (gpointer) status); + tp_presence_mixin_emit_presence_update(connection, presences); + g_hash_table_unref(presences); +} + +static gboolean set_own_status(GObject *object, + const TpPresenceStatus *status, + SIPE_UNUSED_PARAMETER GError **error) +{ + struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(object); + guint activity = SIPE_ACTIVITY_AVAILABLE; + const gchar *message = NULL; + + if (!telepathy_private) + return(FALSE); + + if (status) { + activity = status->index; + + if (status->optional_arguments) + message = tp_asv_get_string(status->optional_arguments, + "message"); + } + + SIPE_DEBUG_INFO("set_own_status: %d '%s'", activity, + message ? message : "(none)"); + update_status(telepathy_private, activity, message, status, TRUE); + + + return(TRUE); +} + +void sipe_telepathy_status_init(GObjectClass *object_class, + gsize struct_offset) +{ + tp_presence_mixin_class_init(object_class, + struct_offset, + status_available, + get_contact_statuses, + set_own_status, + statuses); +} + + +/* + * Backend adaptor functions + */ +guint sipe_backend_status(struct sipe_core_public *sipe_public) +{ + return(sipe_public->backend_private->activity); +} + +gboolean sipe_backend_status_changed(struct sipe_core_public *sipe_public, + guint activity, + const gchar *message) +{ + struct sipe_backend_private *telepathy_private = sipe_public->backend_private; + + if ((activity == telepathy_private->activity) && + sipe_strequal(message, telepathy_private->message)) + return(FALSE); + + return(TRUE); +} + +/* + * This is used by: + * + * - incoming status updates (roaming) + * - induced status updates (calendar) + */ +void sipe_backend_status_and_note(struct sipe_core_public *sipe_public, + guint activity, + const gchar *message) +{ + struct sipe_backend_private *telepathy_private = sipe_public->backend_private; + GHashTable *optional = NULL; + TpPresenceStatus *status; + + if (message) + optional = tp_asv_new("message", G_TYPE_STRING, message, + NULL); + + status = tp_presence_status_new(activity, optional); + if (optional) + g_hash_table_unref(optional); + + update_status(telepathy_private, activity, message, status, FALSE); + tp_presence_status_free(status); +} + + +/* + Local Variables: + mode: c + c-file-style: "bsd" + indent-tabs-mode: t + tab-width: 8 + End: +*/ diff --git a/src/telepathy/telepathy-stubs.c b/src/telepathy/telepathy-stubs.c index b4422aef..82c17662 100644 --- a/src/telepathy/telepathy-stubs.c +++ b/src/telepathy/telepathy-stubs.c @@ -342,16 +342,6 @@ void sipe_backend_search_results_finalize(SIPE_UNUSED_PARAMETER struct sipe_core SIPE_UNUSED_PARAMETER const gchar *description, SIPE_UNUSED_PARAMETER gboolean more) {} -/** STATUS *******************************************************************/ - -guint sipe_backend_status(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return(SIPE_ACTIVITY_AVAILABLE); } -gboolean sipe_backend_status_changed(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, - SIPE_UNUSED_PARAMETER guint activity, - SIPE_UNUSED_PARAMETER const gchar *message) { return(FALSE); } -void sipe_backend_status_and_note(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, - SIPE_UNUSED_PARAMETER guint activity, - SIPE_UNUSED_PARAMETER const gchar *message) {} - /** USER *********************************************************************/ void sipe_backend_user_feedback_typing(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, -- 2.11.4.GIT