diff options
author | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2010-05-06 16:22:51 +0200 |
---|---|---|
committer | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2010-05-17 11:59:53 +0200 |
commit | ba12d63d7d756ae83d76f0a65f128937fcd31458 (patch) | |
tree | 2a3b9d8c5c1feccbd56a574d990313e6a1f71992 /telepathy-glib/handle-channels-context.c | |
parent | 5a4bfd9400e655da6d37921b7bc6cecce48bc972 (diff) | |
download | telepathy-glib-ba12d63d7d756ae83d76f0a65f128937fcd31458.tar.gz |
add handle-channels-context
Diffstat (limited to 'telepathy-glib/handle-channels-context.c')
-rw-r--r-- | telepathy-glib/handle-channels-context.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/telepathy-glib/handle-channels-context.c b/telepathy-glib/handle-channels-context.c new file mode 100644 index 000000000..f7c078bab --- /dev/null +++ b/telepathy-glib/handle-channels-context.c @@ -0,0 +1,663 @@ +/* + * object for HandleChannels calls context + * + * Copyright © 2010 Collabora Ltd. + * + * 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 + */ + +/** + * SECTION: handle-channels-context + * @title: TpHandleChannelsContext + * @short_description: context of a Handler.HandleChannels() call + * + * Object used to represent the context of a Handler.HandleChannels() + * D-Bus call on a #TpBaseClient. + */ + +/** + * TpHandleChannelsContext: + * + * Data structure representing the context of a Handler.HandleChannels() + * call. + * + * Since: 0.11.UNRELEASED + */ + +/** + * TpHandleChannelsContextClass: + * + * The class of a #TpHandleChannelsContext. + * + * Since: 0.11.UNRELEASED + */ + +#include "telepathy-glib/handle-channels-context-internal.h" +#include "telepathy-glib/handle-channels-context.h" + +#include <telepathy-glib/channel.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/gtypes.h> + +#define DEBUG_FLAG TP_DEBUG_CLIENT +#include "telepathy-glib/debug-internal.h" + +struct _TpHandleChannelsContextClass { + /*<private>*/ + GObjectClass parent_class; +}; + +G_DEFINE_TYPE(TpHandleChannelsContext, + tp_handle_channels_context, G_TYPE_OBJECT) + +enum { + PROP_ACCOUNT = 1, + PROP_CONNECTION, + PROP_CHANNELS, + PROP_REQUESTS_SATISFIED, + PROP_USER_ACTION_TIME, + PROP_HANDLER_INFO, + PROP_DBUS_CONTEXT, + N_PROPS +}; + +struct _TpHandleChannelsContextPrivate +{ + TpHandleChannelsContextState state; + GSimpleAsyncResult *result; + DBusGMethodInvocation *dbus_context; + + /* Number of calls we are waiting they return. Once they have all returned + * the context is considered as prepared */ + guint num_pending; +}; + +static void +tp_handle_channels_context_init (TpHandleChannelsContext *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TYPE_HANDLE_CHANNELS_CONTEXT, + TpHandleChannelsContextPrivate); + + self->priv->state = TP_HANDLE_CHANNELS_CONTEXT_STATE_NONE; +} + +static void +tp_handle_channels_context_dispose (GObject *object) +{ + TpHandleChannelsContext *self = TP_HANDLE_CHANNELS_CONTEXT ( + object); + void (*dispose) (GObject *) = + G_OBJECT_CLASS (tp_handle_channels_context_parent_class)->dispose; + + if (self->priv->state == TP_HANDLE_CHANNELS_CONTEXT_STATE_NONE || + self->priv->state == TP_HANDLE_CHANNELS_CONTEXT_STATE_DELAYED) + { + GError error = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, + "Disposing the TpHandleChannelsContext" }; + + g_warning ("Disposing a context in the %s state", + self->priv->state == TP_HANDLE_CHANNELS_CONTEXT_STATE_NONE ? + "none": "delayed"); + + tp_handle_channels_context_fail (self, &error); + } + + if (self->account != NULL) + { + g_object_unref (self->account); + self->account = NULL; + } + + if (self->connection != NULL) + { + g_object_unref (self->connection); + self->connection = NULL; + } + + if (self->channels != NULL) + { + g_ptr_array_foreach (self->channels, (GFunc) g_object_unref, NULL); + g_ptr_array_unref (self->channels); + self->channels = NULL; + } + + if (self->requests_satisfied != NULL) + { + g_ptr_array_foreach (self->requests_satisfied, (GFunc) g_object_unref, + NULL); + g_ptr_array_unref (self->requests_satisfied); + self->requests_satisfied = NULL; + } + + if (self->handler_info != NULL) + { + g_hash_table_unref (self->handler_info); + self->handler_info = NULL; + } + + if (self->priv->result != NULL) + { + g_object_unref (self->priv->result); + self->priv->result = NULL; + } + + if (dispose != NULL) + dispose (object); +} + +static void +tp_handle_channels_context_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpHandleChannelsContext *self = TP_HANDLE_CHANNELS_CONTEXT ( + object); + + switch (property_id) + { + case PROP_ACCOUNT: + g_value_set_object (value, self->account); + break; + + case PROP_CONNECTION: + g_value_set_object (value, self->connection); + break; + + case PROP_CHANNELS: + g_value_set_boxed (value, self->channels); + break; + + case PROP_REQUESTS_SATISFIED: + g_value_set_boxed (value, self->requests_satisfied); + break; + + case PROP_USER_ACTION_TIME: + g_value_set_uint64 (value, self->user_action_time); + break; + + case PROP_HANDLER_INFO: + g_value_set_boxed (value, self->handler_info); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_handle_channels_context_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpHandleChannelsContext *self = TP_HANDLE_CHANNELS_CONTEXT ( + object); + + switch (property_id) + { + case PROP_ACCOUNT: + self->account = g_value_dup_object (value); + break; + + case PROP_CONNECTION: + self->connection = g_value_dup_object (value); + break; + + case PROP_CHANNELS: + self->channels = g_value_dup_boxed (value); + g_ptr_array_foreach (self->channels, (GFunc) g_object_ref, NULL); + break; + + case PROP_REQUESTS_SATISFIED: + self->requests_satisfied = g_value_dup_boxed (value); + g_ptr_array_foreach (self->requests_satisfied, (GFunc) g_object_ref, + NULL); + break; + + case PROP_USER_ACTION_TIME: + self->user_action_time = g_value_get_uint64 (value); + break; + + case PROP_HANDLER_INFO: + self->handler_info = g_value_dup_boxed (value); + break; + + case PROP_DBUS_CONTEXT: + self->priv->dbus_context = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_handle_channels_context_constructed (GObject *object) +{ + TpHandleChannelsContext *self = TP_HANDLE_CHANNELS_CONTEXT ( + object); + void (*chain_up) (GObject *) = + ((GObjectClass *) \ + tp_handle_channels_context_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + g_assert (self->account != NULL); + g_assert (self->connection != NULL); + g_assert (self->channels != NULL); + g_assert (self->requests_satisfied != NULL); + g_assert (self->handler_info != NULL); + g_assert (self->priv->dbus_context != NULL); +} + +static void +tp_handle_channels_context_class_init ( + TpHandleChannelsContextClass *cls) +{ + GObjectClass *object_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec; + + g_type_class_add_private (cls, sizeof (TpHandleChannelsContextPrivate)); + + object_class->get_property = tp_handle_channels_context_get_property; + object_class->set_property = tp_handle_channels_context_set_property; + object_class->constructed = tp_handle_channels_context_constructed; + object_class->dispose = tp_handle_channels_context_dispose; + + /** + * TpHandleChannelsContext:account: + * + * A #TpAccount object representing the Account of the DispatchOperation + * that has been passed to HandleChannels. + * Read-only except during construction. + * + * This property can't be %NULL. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_object ("account", "TpAccount", + "The TpAccount of the context", + TP_TYPE_ACCOUNT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_ACCOUNT, + param_spec); + + /** + * TpHandleChannelsContext:connection: + * + * A #TpConnection object representing the Connection of the DispatchOperation + * that has been passed to HandleChannels. + * Read-only except during construction. + * + * This property can't be %NULL. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_object ("connection", "TpConnection", + "The TpConnection of the context", + TP_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CONNECTION, + param_spec); + + /** + * TpHandleChannelsContext:channels: + * + * A #GPtrArray containing #TpChannel objects representing the channels + * that have been passed to HandleChannels. + * Read-only except during construction. + * + * This property can't be %NULL. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_boxed ("channels", "GPtrArray of TpChannel", + "The TpChannels that have been passed to HandleChannels", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CHANNELS, + param_spec); + + /** + * TpHandleChannelsContext:requests-satisfied: + * + * A #GPtrArray containing #TpChannelRequest objects representing the + * requests that have been passed to HandleChannels. + * Read-only except during construction. + * + * This property can't be %NULL. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_boxed ("requests-satisfied", + "GPtrArray of TpChannelRequest", + "The TpChannelRequest that has been passed to " + "HandleChannels", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_REQUESTS_SATISFIED, + param_spec); + + /** + * TpHandleChannelsContext:user-action-time: + * + * The User_Action_Time that have been passed to HandleChannels. + * Read-only except during construction. + * + * This property can't be %NULL. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_uint64 ("user-action-time", + "User action time", + "The User_Action_Time that has been passed to HandleChannels", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_USER_ACTION_TIME, + param_spec); + + /** + * TpHandleChannelsContext:handler-info: + * + * A #GHashTable where the keys are string and values are GValue instances. + * It represents the Handler_info hash table that has been passed to + * HandleChannels. + * + * This property can't be %NULL. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_boxed ("handler-info", "Handler info", + "The Handler that has been passed to ObserveChannels", + TP_HASH_TYPE_STRING_VARIANT_MAP, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_HANDLER_INFO, + param_spec); + + /** + * TpHandleChannelsContext:dbus-context: (skip) + * + * The #DBusGMethodInvocation representing the D-Bus context of the + * HandleChannels call. + * Can only be written during construction. + * + * Since: 0.11.UNRELEASED + */ + param_spec = g_param_spec_pointer ("dbus-context", "D-Bus context", + "The DBusGMethodInvocation associated with the HandleChannels call", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DBUS_CONTEXT, + param_spec); +} + +TpHandleChannelsContext * _tp_handle_channels_context_new ( + TpAccount *account, + TpConnection *connection, + GPtrArray *channels, + GPtrArray *requests_satisfied, + guint64 user_action_time, + GHashTable *handler_info, + DBusGMethodInvocation *dbus_context) +{ + return g_object_new (TP_TYPE_HANDLE_CHANNELS_CONTEXT, + "account", account, + "connection", connection, + "channels", channels, + "requests-satisfied", requests_satisfied, + "user-action-time", user_action_time, + "handler-info", handler_info, + "dbus-context", dbus_context, + NULL); +} + +/** + * tp_handle_channels_context_accept: + * @self: a #TpHandleChannelsContext + * + * Called by #TpBaseClientClassAddDispatchOperationImpl when it's done so + * the D-Bus method can return. + * + * Since: 0.11.UNRELEASED + */ +void +tp_handle_channels_context_accept (TpHandleChannelsContext *self) +{ + g_return_if_fail (self->priv->state == + TP_HANDLE_CHANNELS_CONTEXT_STATE_NONE + || self->priv->state == TP_HANDLE_CHANNELS_CONTEXT_STATE_DELAYED); + g_return_if_fail (self->priv->dbus_context != NULL); + + self->priv->state = TP_HANDLE_CHANNELS_CONTEXT_STATE_DONE; + dbus_g_method_return (self->priv->dbus_context); + + self->priv->dbus_context = NULL; +} + +/** + * tp_handle_channels_context_fail: + * @self: a #TpHandleChannelsContext + * @error: the error to return from the method + * + * Called by #TpBaseClientClassAddDispatchOperationImpl to raise a D-Bus error. + * + * Since: 0.11.UNRELEASED + */ +void +tp_handle_channels_context_fail (TpHandleChannelsContext *self, + const GError *error) +{ + g_return_if_fail (self->priv->state == + TP_HANDLE_CHANNELS_CONTEXT_STATE_NONE + || self->priv->state == TP_HANDLE_CHANNELS_CONTEXT_STATE_DELAYED); + g_return_if_fail (self->priv->dbus_context != NULL); + + self->priv->state = TP_HANDLE_CHANNELS_CONTEXT_STATE_FAILED; + dbus_g_method_return_error (self->priv->dbus_context, error); + + self->priv->dbus_context = NULL; +} + +/** + * tp_handle_channels_context_delay: + * @self: a #TpHandleChannelsContext + * + * Called by #TpBaseClientClassAddDispatchOperationImpl to indicate that it + * implements the method in an async way. The caller must take a reference + * to the #TpHandleChannelsContext before calling this function, and + * is responsible for calling either + * tp_handle_channels_context_accept() or + * tp_handle_channels_context_fail() later. + * + * Since: 0.11.UNRELEASED + */ +void +tp_handle_channels_context_delay (TpHandleChannelsContext *self) +{ + g_return_if_fail (self->priv->state == + TP_HANDLE_CHANNELS_CONTEXT_STATE_NONE); + + self->priv->state = TP_HANDLE_CHANNELS_CONTEXT_STATE_DELAYED; +} + +TpHandleChannelsContextState +_tp_handle_channels_context_get_state ( + TpHandleChannelsContext *self) +{ + return self->priv->state; +} + +static gboolean +context_is_prepared (TpHandleChannelsContext *self) +{ + return self->priv->num_pending == 0; +} + +static void +context_check_prepare (TpHandleChannelsContext *self) +{ + if (!context_is_prepared (self)) + return; + + /* is prepared */ + g_simple_async_result_complete (self->priv->result); + + g_object_unref (self->priv->result); + self->priv->result = NULL; +} + +static void +account_prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpHandleChannelsContext *self = user_data; + GError *error = NULL; + + if (self->priv->result == NULL) + goto out; + + if (!tp_proxy_prepare_finish (source, result, &error)) + { + DEBUG ("Failed to prepare account: %s", error->message); + g_error_free (error); + } + + self->priv->num_pending--; + context_check_prepare (self); + +out: + g_object_unref (self); +} + +static void +conn_prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpHandleChannelsContext *self = user_data; + GError *error = NULL; + + if (self->priv->result == NULL) + goto out; + + if (!tp_proxy_prepare_finish (source, result, &error)) + { + DEBUG ("Failed to prepare connection: %s", error->message); + g_error_free (error); + } + + self->priv->num_pending--; + context_check_prepare (self); + +out: + g_object_unref (self); +} + +static void +channel_prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpHandleChannelsContext *self = user_data; + GError *error = NULL; + + if (self->priv->result == NULL) + goto out; + + if (!tp_proxy_prepare_finish (source, result, &error)) + { + DEBUG ("Failed to prepare channel: %s", error->message); + + g_error_free (error); + } + + self->priv->num_pending--; + context_check_prepare (self); + +out: + g_object_unref (self); +} + +static void +context_prepare (TpHandleChannelsContext *self) +{ + GQuark account_features[] = { TP_ACCOUNT_FEATURE_CORE, 0 }; + GQuark conn_features[] = { TP_CONNECTION_FEATURE_CORE, 0 }; + GQuark channel_features[] = { TP_CHANNEL_FEATURE_CORE, 0 }; + guint i; + + self->priv->num_pending = 2; + + tp_proxy_prepare_async (self->account, account_features, + account_prepare_cb, g_object_ref (self)); + + tp_proxy_prepare_async (self->connection, conn_features, + conn_prepare_cb, g_object_ref (self)); + + for (i = 0; i < self->channels->len; i++) + { + TpChannel *channel = g_ptr_array_index (self->channels, i); + + self->priv->num_pending++; + + tp_proxy_prepare_async (channel, channel_features, + channel_prepare_cb, g_object_ref (self)); + } +} + +void +_tp_handle_channels_context_prepare_async ( + TpHandleChannelsContext *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (TP_IS_HANDLE_CHANNELS_CONTEXT (self)); + /* This is only used once, by TpBaseClient, so for simplicity, we only + * allow one asynchronous preparation */ + g_return_if_fail (self->priv->result == NULL); + + self->priv->result = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, _tp_handle_channels_context_prepare_async); + + context_prepare (self); +} + +gboolean +_tp_handle_channels_context_prepare_finish ( + TpHandleChannelsContext *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_HANDLE_CHANNELS_CONTEXT (self), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (self), _tp_handle_channels_context_prepare_async), + FALSE); + + return TRUE; +} |