/*
* objects for AddDispatchOperation calls
*
* 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:add-dispatch-operation-context
* @title: TpAddDispatchOperationContext
* @short_description: context of a Approver.AddDispatchOperation() call
*
* Object used to represent the context of a Approver.AddDispatchOperation()
* D-Bus call on a #TpBaseClient.
*/
/**
* TpAddDispatchOperationContext:
*
* Data structure representing the context of a Approver.AddDispatchOperation()
* call.
*
* Since: 0.11.5
*/
/**
* TpAddDispatchOperationContextClass:
*
* The class of a #TpAddDispatchOperationContext.
*
* Since: 0.11.5
*/
#include "config.h"
#include "telepathy-glib/add-dispatch-operation-context-internal.h"
#include "telepathy-glib/add-dispatch-operation-context.h"
#include
#include
#include
#define DEBUG_FLAG TP_DEBUG_CLIENT
#include "telepathy-glib/debug-internal.h"
#include "telepathy-glib/util-internal.h"
struct _TpAddDispatchOperationContextClass {
/**/
GObjectClass parent_class;
};
G_DEFINE_TYPE(TpAddDispatchOperationContext,
tp_add_dispatch_operation_context, G_TYPE_OBJECT)
enum {
PROP_ACCOUNT = 1,
PROP_CONNECTION,
PROP_CHANNELS,
PROP_DISPATCH_OPERATION,
PROP_DBUS_CONTEXT,
N_PROPS
};
struct _TpAddDispatchOperationContextPrivate
{
TpAddDispatchOperationContextState 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_add_dispatch_operation_context_init (TpAddDispatchOperationContext *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
TP_TYPE_ADD_DISPATCH_OPERATION_CONTEXT,
TpAddDispatchOperationContextPrivate);
self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE;
}
static void
tp_add_dispatch_operation_context_dispose (GObject *object)
{
TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_CONTEXT (
object);
void (*dispose) (GObject *) =
G_OBJECT_CLASS (tp_add_dispatch_operation_context_parent_class)->dispose;
if (self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE ||
self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED)
{
GError error = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
"Disposing the TpAddDispatchOperationContext" };
WARNING ("Disposing a context in the %s state",
self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE ?
"none": "delayed");
tp_add_dispatch_operation_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_unref (self->channels);
self->channels = NULL;
}
if (self->dispatch_operation != NULL)
{
g_object_unref (self->dispatch_operation);
self->dispatch_operation = NULL;
}
if (self->priv->result != NULL)
{
g_object_unref (self->priv->result);
self->priv->result = NULL;
}
if (dispose != NULL)
dispose (object);
}
static void
tp_add_dispatch_operation_context_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_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_DISPATCH_OPERATION:
g_value_set_object (value, self->dispatch_operation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
tp_add_dispatch_operation_context_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_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);
break;
case PROP_DISPATCH_OPERATION:
self->dispatch_operation = g_value_dup_object (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_add_dispatch_operation_context_constructed (GObject *object)
{
TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_CONTEXT (
object);
void (*chain_up) (GObject *) =
((GObjectClass *)
tp_add_dispatch_operation_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->dispatch_operation != NULL);
g_assert (self->priv->dbus_context != NULL);
}
static void
tp_add_dispatch_operation_context_class_init (
TpAddDispatchOperationContextClass *cls)
{
GObjectClass *object_class = G_OBJECT_CLASS (cls);
GParamSpec *param_spec;
g_type_class_add_private (cls, sizeof (TpAddDispatchOperationContextPrivate));
object_class->get_property = tp_add_dispatch_operation_context_get_property;
object_class->set_property = tp_add_dispatch_operation_context_set_property;
object_class->constructed = tp_add_dispatch_operation_context_constructed;
object_class->dispose = tp_add_dispatch_operation_context_dispose;
/**
* TpAddDispatchOperationContext:account:
*
* A #TpAccount object representing the Account of the DispatchOperation
* that has been passed to AddDispatchOperation.
* Read-only except during construction.
*
* This property can't be %NULL.
*
* Since: 0.11.5
*/
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);
/**
* TpAddDispatchOperationContext:connection:
*
* A #TpConnection object representing the Connection of the DispatchOperation
* that has been passed to AddDispatchOperation.
* Read-only except during construction.
*
* This property can't be %NULL.
*
* Since: 0.11.5
*/
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);
/**
* TpAddDispatchOperationContext:channels:
*
* A #GPtrArray containing #TpChannel objects representing the channels
* that have been passed to AddDispatchOperation.
* Read-only except during construction.
*
* This property can't be %NULL.
*
* Since: 0.11.5
*/
param_spec = g_param_spec_boxed ("channels", "GPtrArray of TpChannel",
"The TpChannels that have been passed to AddDispatchOperation",
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);
/**
* TpAddDispatchOperationContext:dispatch-operation:
*
* A #TpChannelDispatchOperation object representing the
* ChannelDispatchOperation that has been passed to AddDispatchOperation.
* Read-only except during construction.
*
* This property can't be %NULL.
*
* Since: 0.11.5
*/
param_spec = g_param_spec_object ("dispatch-operation",
"TpChannelDispatchOperation",
"The TpChannelDispatchOperation that has been passed to "
"AddDispatchOperation",
TP_TYPE_CHANNEL_DISPATCH_OPERATION,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_DISPATCH_OPERATION,
param_spec);
/**
* TpAddDispatchOperationContext:dbus-context: (skip)
*
* The #DBusGMethodInvocation representing the D-Bus context of the
* AddDispatchOperation call.
* Can only be written during construction.
*
* Since: 0.11.5
*/
param_spec = g_param_spec_pointer ("dbus-context", "D-Bus context",
"The DBusGMethodInvocation associated with the AddDispatchOperation call",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_DBUS_CONTEXT,
param_spec);
}
TpAddDispatchOperationContext *
_tp_add_dispatch_operation_context_new (
TpAccount *account,
TpConnection *connection,
GPtrArray *channels,
TpChannelDispatchOperation *dispatch_operation,
DBusGMethodInvocation *dbus_context)
{
return g_object_new (TP_TYPE_ADD_DISPATCH_OPERATION_CONTEXT,
"account", account,
"connection", connection,
"channels", channels,
"dispatch-operation", dispatch_operation,
"dbus-context", dbus_context,
NULL);
}
/**
* tp_add_dispatch_operation_context_accept:
* @self: a #TpAddDispatchOperationContext
*
* Called by #TpBaseClientClassAddDispatchOperationImpl when it's done so
* the D-Bus method can return.
*
* Since: 0.11.5
*/
void
tp_add_dispatch_operation_context_accept (TpAddDispatchOperationContext *self)
{
g_return_if_fail (self->priv->state ==
TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE
|| self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED);
g_return_if_fail (self->priv->dbus_context != NULL);
self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DONE;
dbus_g_method_return (self->priv->dbus_context);
self->priv->dbus_context = NULL;
}
/**
* tp_add_dispatch_operation_context_fail:
* @self: a #TpAddDispatchOperationContext
* @error: the error to return from the method
*
* Called by #TpBaseClientClassAddDispatchOperationImpl to raise a D-Bus error.
*
* Since: 0.11.5
*/
void
tp_add_dispatch_operation_context_fail (TpAddDispatchOperationContext *self,
const GError *error)
{
g_return_if_fail (self->priv->state ==
TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE
|| self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED);
g_return_if_fail (self->priv->dbus_context != NULL);
self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_FAILED;
dbus_g_method_return_error (self->priv->dbus_context, error);
self->priv->dbus_context = NULL;
}
/**
* tp_add_dispatch_operation_context_delay:
* @self: a #TpAddDispatchOperationContext
*
* Called by #TpBaseClientClassAddDispatchOperationImpl to indicate that it
* implements the method in an async way. The caller must take a reference
* to the #TpAddDispatchOperationContext before calling this function, and
* is responsible for calling either
* tp_add_dispatch_operation_context_accept() or
* tp_add_dispatch_operation_context_fail() later.
*
* Since: 0.11.5
*/
void
tp_add_dispatch_operation_context_delay (TpAddDispatchOperationContext *self)
{
g_return_if_fail (self->priv->state ==
TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE);
self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED;
}
TpAddDispatchOperationContextState
_tp_add_dispatch_operation_context_get_state (
TpAddDispatchOperationContext *self)
{
return self->priv->state;
}
static gboolean
context_is_prepared (TpAddDispatchOperationContext *self)
{
return self->priv->num_pending == 0;
}
static void
context_check_prepare (TpAddDispatchOperationContext *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
cdo_prepare_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
TpAddDispatchOperationContext *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 ChannelDispatchOperation: %s", error->message);
g_error_free (error);
}
self->priv->num_pending--;
context_check_prepare (self);
out:
g_object_unref (self);
}
static void
account_prepare_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
TpAddDispatchOperationContext *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)
{
TpAddDispatchOperationContext *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
adoc_channel_prepare_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
TpAddDispatchOperationContext *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 (TpAddDispatchOperationContext *self,
const GQuark *account_features,
const GQuark *connection_features,
const GQuark *channel_features)
{
GQuark cdo_features[] = { TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE, 0 };
guint i;
self->priv->num_pending = 3;
tp_proxy_prepare_async (self->account, account_features,
account_prepare_cb, g_object_ref (self));
tp_proxy_prepare_async (self->connection, connection_features,
conn_prepare_cb, g_object_ref (self));
tp_proxy_prepare_async (self->dispatch_operation, cdo_features,
cdo_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,
adoc_channel_prepare_cb, g_object_ref (self));
}
}
void
_tp_add_dispatch_operation_context_prepare_async (
TpAddDispatchOperationContext *self,
const GQuark *account_features,
const GQuark *connection_features,
const GQuark *channel_features,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (TP_IS_ADD_DISPATCH_OPERATION_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_add_dispatch_operation_context_prepare_async);
context_prepare (self, account_features, connection_features,
channel_features);
}
gboolean
_tp_add_dispatch_operation_context_prepare_finish (
TpAddDispatchOperationContext *self,
GAsyncResult *result,
GError **error)
{
_tp_implement_finish_void (self,
_tp_add_dispatch_operation_context_prepare_async);
}