/*
* channel-dispatch-operation.c - proxy for incoming channel seeking approval
*
* Copyright (C) 2009 Collabora Ltd.
* Copyright (C) 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 "telepathy-glib/channel-dispatch-operation.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEBUG_FLAG TP_DEBUG_DISPATCHER
#include "telepathy-glib/dbus-internal.h"
#include "telepathy-glib/debug-internal.h"
#include "telepathy-glib/client-factory-internal.h"
/**
* SECTION:channel-dispatch-operation
* @title: TpChannelDispatchOperation
* @short_description: proxy object for a to the Telepathy channel
* dispatcher
* @see_also: #TpChannelDispatcher
*
* One of the channel dispatcher's functions is to offer incoming channel to
* Approver clients for approval. Approvers respond to the channel dispatcher
* via a #TpChannelDispatchOperation object.
*/
/**
* TpChannelDispatchOperation:
*
* One of the channel dispatcher's functions is to offer incoming channel to
* Approver clients for approval. An approver should generally ask the user
* whether they want to participate in the requested communication channel
* (join the chat or chatroom, answer the call, accept the file transfer, or
* whatever is appropriate). A channel offered in this way
* is represented by a ChannelDispatchOperation object.
*
* If the user wishes to accept the communication channel, the approver
* should call tp_cli_channel_dispatch_operation_call_handle_with() to
* indicate the user's or approver's preferred handler for the channel (the
* empty string indicates no particular preference, and will cause any
* suitable handler to be used).
*
* If the user wishes to reject the communication channel, or if the user
* accepts the channel and the approver will handle them itself, the approver
* should call tp_channel_dispatch_operation_claim_with_async(). If this method
* succeeds, the approver immediately has control over the channel as their
* primary handler, and may do anything with them (in particular, it may close
* them in whatever way seems most appropriate).
*
* There are various situations in which the channel dispatch operation will
* be closed, causing the #TpProxy::invalidated signal to be emitted. If this
* happens, the approver should stop prompting the user.
*
* Because all approvers are launched simultaneously, the user might respond
* to another approver; if this happens, the #TpProxy::invalidated signal
* will be emitted with the domain %TP_DBUS_ERRORS and the error code
* %TP_DBUS_ERROR_OBJECT_REMOVED.
*
* If all the channel
* close, there is nothing more to dispatch, so the #TpProxy::invalidated
* signal will be emitted with the domain %TP_DBUS_ERRORS and the error code
* %TP_DBUS_ERROR_OBJECT_REMOVED.
*
* If the channel dispatcher crashes or exits, the #TpProxy::invalidated
* signal will be emitted with the domain %TP_DBUS_ERRORS and the error code
* %TP_DBUS_ERROR_NAME_OWNER_LOST. In a high-quality implementation, the
* dispatcher should be restarted, at which point it will create new
* channel dispatch operations for any undispatched channel, and the approver
* will be notified again.
*
* Creating a #TpChannelDispatchOperation directly is deprecated: it
* should only be created via a #TpBaseClient.
*
* Since 0.16, #TpChannelDispatchOperation always has a non-%NULL
* #TpProxy:factory, which will be propagated to the #TpAccount,
* #TpConnection and #TpChannel.
*
* Since: 0.7.32
*/
/**
* TpChannelDispatchOperationClass:
*
* The class of a #TpChannelDispatchOperation.
*/
struct _TpChannelDispatchOperationPrivate {
TpConnection *connection;
TpAccount *account;
TpChannel *channel;
GStrv possible_handlers;
/* a{sv} */
GVariant *immutable_properties;
};
enum
{
PROP_CONNECTION = 1,
PROP_ACCOUNT,
PROP_CHANNEL,
PROP_POSSIBLE_HANDLERS,
PROP_CDO_PROPERTIES,
N_PROPS
};
G_DEFINE_TYPE (TpChannelDispatchOperation, tp_channel_dispatch_operation,
TP_TYPE_PROXY)
static void
tp_channel_dispatch_operation_init (TpChannelDispatchOperation *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
TP_TYPE_CHANNEL_DISPATCH_OPERATION, TpChannelDispatchOperationPrivate);
self->priv->immutable_properties = g_variant_ref_sink (
g_variant_new ("a{sv}", NULL));
}
static void
tp_channel_dispatch_operation_finished_cb (TpChannelDispatchOperation *self,
const gchar *dbus_error,
const gchar *message,
gpointer unused G_GNUC_UNUSED,
GObject *object G_GNUC_UNUSED)
{
GError *error = NULL;
if (tp_str_empty (dbus_error))
{
g_set_error_literal (&error, TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED,
"ChannelDispatchOperation finished and was removed");
}
else
{
tp_proxy_dbus_error_to_gerror (self, dbus_error, message, &error);
}
tp_proxy_invalidate ((TpProxy *) self, error);
g_error_free (error);
}
static void
tp_channel_dispatch_operation_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
TpChannelDispatchOperation *self = TP_CHANNEL_DISPATCH_OPERATION (object);
switch (property_id)
{
case PROP_CONNECTION:
g_value_set_object (value, self->priv->connection);
break;
case PROP_ACCOUNT:
g_value_set_object (value, self->priv->account);
break;
case PROP_CHANNEL:
g_value_set_object (value, self->priv->channel);
break;
case PROP_POSSIBLE_HANDLERS:
g_value_set_boxed (value, self->priv->possible_handlers);
break;
case PROP_CDO_PROPERTIES:
g_value_set_variant (value, self->priv->immutable_properties);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
maybe_set_connection (TpChannelDispatchOperation *self)
{
GError *error = NULL;
const gchar *path;
if (self->priv->connection != NULL)
return;
path = tp_vardict_get_object_path (self->priv->immutable_properties,
TP_PROP_CHANNEL_DISPATCH_OPERATION_CONNECTION);
if (path == NULL)
return;
self->priv->connection = tp_client_factory_ensure_connection (
tp_proxy_get_factory (self), path, NULL, &error);
if (self->priv->connection == NULL)
{
DEBUG ("Failed to create connection %s: %s", path, error->message);
g_error_free (error);
return;
}
g_object_notify ((GObject *) self, "connection");
}
static void
maybe_set_account (TpChannelDispatchOperation *self)
{
GError *error = NULL;
const gchar *path;
if (self->priv->account != NULL)
return;
path = tp_vardict_get_object_path (self->priv->immutable_properties,
TP_PROP_CHANNEL_DISPATCH_OPERATION_ACCOUNT);
if (path == NULL)
return;
self->priv->account = tp_client_factory_ensure_account (
tp_proxy_get_factory (self), path, NULL, &error);
if (self->priv->account == NULL)
{
DEBUG ("Failed to create account %s: %s", path, error->message);
g_error_free (error);
return;
}
g_object_notify ((GObject *) self, "account");
}
static void
maybe_set_channel (TpChannelDispatchOperation *self)
{
GError *error = NULL;
const gchar *path;
GVariant *properties;
if (self->priv->channel != NULL)
return;
path = tp_vardict_get_object_path (self->priv->immutable_properties,
TP_PROP_CHANNEL_DISPATCH_OPERATION_CHANNEL);
if (path == NULL)
return;
properties = g_variant_lookup_value (self->priv->immutable_properties,
TP_PROP_CHANNEL_DISPATCH_OPERATION_CHANNEL_PROPERTIES,
G_VARIANT_TYPE_VARDICT);
self->priv->channel = tp_client_factory_ensure_channel (
tp_proxy_get_factory (self), self->priv->connection, path,
properties, &error);
g_variant_unref (properties);
if (self->priv->channel == NULL)
{
DEBUG ("Failed to create channel %s: %s", path, error->message);
g_error_free (error);
return;
}
g_object_notify ((GObject *) self, "channel");
}
static void
maybe_set_possible_handlers (TpChannelDispatchOperation *self)
{
GVariant *handlers;
if (self->priv->possible_handlers != NULL)
return;
handlers = g_variant_lookup_value (self->priv->immutable_properties,
TP_PROP_CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS,
G_VARIANT_TYPE_STRING_ARRAY);
if (handlers == NULL)
return;
self->priv->possible_handlers = g_variant_dup_strv (handlers, NULL);
g_variant_unref (handlers);
g_object_notify ((GObject *) self, "possible-handlers");
}
static void
maybe_set_interfaces (TpChannelDispatchOperation *self)
{
GVariant *variant;
const gchar **interfaces;
variant = g_variant_lookup_value (self->priv->immutable_properties,
TP_PROP_CHANNEL_DISPATCH_OPERATION_INTERFACES,
G_VARIANT_TYPE_STRING_ARRAY);
if (variant == NULL)
return;
interfaces = g_variant_get_strv (variant, NULL);
tp_proxy_add_interfaces ((TpProxy *) self, interfaces);
g_free (interfaces);
g_variant_unref (variant);
}
static void
tp_channel_dispatch_operation_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
TpChannelDispatchOperation *self = TP_CHANNEL_DISPATCH_OPERATION (object);
switch (property_id)
{
case PROP_ACCOUNT:
g_assert (self->priv->account == NULL); /* construct-only */
self->priv->account = g_value_dup_object (value);
break;
case PROP_CONNECTION:
g_assert (self->priv->connection == NULL); /* construct-only */
self->priv->connection = g_value_dup_object (value);
break;
case PROP_CHANNEL:
g_assert (self->priv->channel == NULL); /* construct-only */
self->priv->channel = g_value_dup_object (value);
break;
case PROP_CDO_PROPERTIES:
{
GVariant *vardict = g_value_get_variant (value);
if (vardict == NULL)
return;
g_clear_pointer (&self->priv->immutable_properties, g_variant_unref);
self->priv->immutable_properties = g_variant_ref (vardict);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
extract_core_properties (TpChannelDispatchOperation *self)
{
maybe_set_connection (self);
maybe_set_account (self);
maybe_set_channel (self);
maybe_set_possible_handlers (self);
maybe_set_interfaces (self);
}
static void
tp_channel_dispatch_operation_constructed (GObject *object)
{
TpChannelDispatchOperation *self = TP_CHANNEL_DISPATCH_OPERATION (object);
void (*chain_up) (GObject *) =
((GObjectClass *) tp_channel_dispatch_operation_parent_class)->constructed;
GError *error = NULL;
TpProxySignalConnection *sc;
if (chain_up != NULL)
chain_up (object);
extract_core_properties (self);
sc = tp_cli_channel_dispatch_operation_connect_to_finished (self,
tp_channel_dispatch_operation_finished_cb, NULL, NULL, NULL, &error);
if (sc == NULL)
{
CRITICAL ("Couldn't connect to Finished: %s", error->message);
g_error_free (error);
g_assert_not_reached ();
return;
}
}
static void
tp_channel_dispatch_operation_dispose (GObject *object)
{
TpChannelDispatchOperation *self = TP_CHANNEL_DISPATCH_OPERATION (object);
void (*dispose) (GObject *) =
G_OBJECT_CLASS (tp_channel_dispatch_operation_parent_class)->dispose;
if (self->priv->connection != NULL)
{
g_object_unref (self->priv->connection);
self->priv->connection = NULL;
}
if (self->priv->account != NULL)
{
g_object_unref (self->priv->account);
self->priv->account = NULL;
}
g_clear_object (&self->priv->channel);
g_strfreev (self->priv->possible_handlers);
self->priv->possible_handlers = NULL;
g_clear_pointer (&self->priv->immutable_properties, g_variant_unref);
if (dispose != NULL)
dispose (object);
}
static void
get_dispatch_operation_prop_cb (TpProxy *proxy,
GHashTable *props,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
TpChannelDispatchOperation *self = (TpChannelDispatchOperation *) proxy;
GSimpleAsyncResult *result = user_data;
gboolean prepared = TRUE;
GError *e = NULL;
GVariantDict dict;
gpointer p;
if (error != NULL)
{
DEBUG ("Failed to fetch ChannelDispatchOperation properties: %s",
error->message);
prepared = FALSE;
e = g_error_copy (error);
goto out;
}
/* copy the immutable properties we know (if any) as a starting point */
g_variant_dict_init (&dict, self->priv->immutable_properties);
/* copy the properties we received from D-Bus into that dict */
p = tp_asv_get_boxed (props, "Connection", DBUS_TYPE_G_OBJECT_PATH);
if (p != NULL)
g_variant_dict_insert_value (&dict,
TP_PROP_CHANNEL_DISPATCH_OPERATION_CONNECTION,
g_variant_new_object_path (p));
p = tp_asv_get_boxed (props, "Account", DBUS_TYPE_G_OBJECT_PATH);
if (p != NULL)
g_variant_dict_insert_value (&dict,
TP_PROP_CHANNEL_DISPATCH_OPERATION_ACCOUNT,
g_variant_new_object_path (p));
p = tp_asv_get_boxed (props, "Channel", DBUS_TYPE_G_OBJECT_PATH);
if (p != NULL)
g_variant_dict_insert_value (&dict,
TP_PROP_CHANNEL_DISPATCH_OPERATION_CHANNEL,
g_variant_new_object_path (p));
p = tp_asv_get_boxed (props, "ChannelProperties",
TP_HASH_TYPE_STRING_VARIANT_MAP);
if (p != NULL)
g_variant_dict_insert_value (&dict,
TP_PROP_CHANNEL_DISPATCH_OPERATION_CHANNEL_PROPERTIES,
tp_asv_to_vardict (p));
p = tp_asv_get_boxed (props, "PossibleHandlers", G_TYPE_STRV);
if (p != NULL)
g_variant_dict_insert_value (&dict,
TP_PROP_CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS,
g_variant_new_strv (p, -1));
p = tp_asv_get_boxed (props, "Interfaces", G_TYPE_STRV);
if (p != NULL)
g_variant_dict_insert_value (&dict,
TP_PROP_CHANNEL_DISPATCH_OPERATION_INTERFACES,
g_variant_new_strv (p, -1));
/* swap out our immutable properties for the new, hopefully more
* complete set */
g_clear_pointer (&self->priv->immutable_properties, g_variant_unref);
self->priv->immutable_properties = g_variant_ref_sink (
g_variant_dict_end (&dict));
/* copy those that have high-level API into the corresponding
* struct members */
extract_core_properties (self);
if (self->priv->connection == NULL)
{
e = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
"Mandatory 'Connection' property is missing");
DEBUG ("%s", e->message);
prepared = FALSE;
goto out;
}
if (self->priv->account == NULL)
{
e = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
"Mandatory 'Account' property is missing");
DEBUG ("%s", e->message);
prepared = FALSE;
goto out;
}
if (self->priv->channel == NULL)
{
e = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
"Mandatory 'Channel' or 'ChannelProperties' property is missing");
DEBUG ("%s", e->message);
prepared = FALSE;
goto out;
}
if (self->priv->possible_handlers == NULL)
{
e = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
"Mandatory 'PossibleHandlers' property is missing");
DEBUG ("%s", e->message);
prepared = FALSE;
goto out;
}
g_object_notify ((GObject *) self, "cdo-properties");
out:
if (e != NULL)
g_simple_async_result_set_from_error (result, e);
g_simple_async_result_complete (result);
if (!prepared)
{
tp_proxy_invalidate ((TpProxy *) self, e);
g_error_free (e);
}
}
static void
prepare_core_async (TpProxy *proxy,
const TpProxyFeature *feature,
GAsyncReadyCallback callback,
gpointer user_data)
{
TpChannelDispatchOperation *self = (TpChannelDispatchOperation *) proxy;
GSimpleAsyncResult *result;
result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
prepare_core_async);
tp_cli_dbus_properties_call_get_all (self, -1,
TP_IFACE_CHANNEL_DISPATCH_OPERATION,
get_dispatch_operation_prop_cb,
result, g_object_unref, NULL);
}
enum {
FEAT_CORE,
N_FEAT
};
static const TpProxyFeature *
tp_channel_dispatch_operation_list_features (TpProxyClass *cls G_GNUC_UNUSED)
{
static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
if (G_LIKELY (features[0].name != 0))
return features;
features[FEAT_CORE].name = TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE;
features[FEAT_CORE].core = TRUE;
features[FEAT_CORE].prepare_async = prepare_core_async;
/* assert that the terminator at the end is there */
g_assert (features[N_FEAT].name == 0);
return features;
}
static void
tp_channel_dispatch_operation_class_init (TpChannelDispatchOperationClass *klass)
{
TpProxyClass *proxy_class = (TpProxyClass *) klass;
GObjectClass *object_class = (GObjectClass *) klass;
GParamSpec *param_spec;
g_type_class_add_private (klass, sizeof (TpChannelDispatchOperationPrivate));
object_class->get_property = tp_channel_dispatch_operation_get_property;
object_class->set_property = tp_channel_dispatch_operation_set_property;
object_class->constructed = tp_channel_dispatch_operation_constructed;
object_class->dispose = tp_channel_dispatch_operation_dispose;
/**
* TpChannelDispatchOperation:connection:
*
* The #TpConnection with which the channel is associated.
*
* Read-only except during construction.
*
* This is not guaranteed to be set until tp_proxy_prepare_async() has
* finished preparing %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE.
*
* Since: 0.11.5
*/
param_spec = g_param_spec_object ("connection", "TpConnection",
"The TpConnection of this channel dispatch operation",
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);
/**
* TpChannelDispatchOperation:account:
*
* The #TpAccount with which the connection and channel are associated.
*
* Read-only except during construction.
*
* This is not guaranteed to be set until tp_proxy_prepare_async() has
* finished preparing %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE.
*
* Since: 0.11.5
*/
param_spec = g_param_spec_object ("account", "TpAccount",
"The TpAccount of this channel dispatch operation",
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);
/**
* TpChannelDispatchOperation:channel:
*
* The #TpChannel to be dispatched.
*
* Read-only.
*
* This is not guaranteed to be set until tp_proxy_prepare_async() has
* finished preparing %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE.
*/
param_spec = g_param_spec_object ("channel", "TpChannel",
"The TpChannel to be dispatched",
TP_TYPE_CHANNEL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_CHANNEL,
param_spec);
/**
* TpChannelDispatchOperation:possible-handlers:
*
* A #GStrv containing the well known bus names (starting
* with TP_CLIENT_BUS_NAME_BASE) of the possible Handlers for
* the channel.
*
* Read-only except during construction.
*
* This is not guaranteed to be set until tp_proxy_prepare_async() has
* finished preparing %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE.
*
* Since: 0.11.5
*/
param_spec = g_param_spec_boxed ("possible-handlers", "Possible handlers",
"Possible handlers for the channel",
G_TYPE_STRV,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_POSSIBLE_HANDLERS,
param_spec);
/**
* TpChannelDispatchOperation:cdo-properties:
*
* The immutable D-Bus properties of this ChannelDispatchOperation,
* represented by a #GVariant of type %G_VARIANT_TYPE_VARDICT
* where the keys are D-Bus
* interface name + "." + property name.
*
* Read-only except during construction. If this is not provided
* during construction, it is not guaranteed to be set until
* tp_proxy_prepare_async() has finished preparing
* %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE.
*/
param_spec = g_param_spec_variant ("cdo-properties",
"Immutable D-Bus properties",
"A map D-Bus interface + \".\" + property name => value",
G_VARIANT_TYPE_VARDICT, NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class,
PROP_CDO_PROPERTIES, param_spec);
proxy_class->interface = TP_IFACE_QUARK_CHANNEL_DISPATCH_OPERATION;
proxy_class->must_have_unique_name = TRUE;
proxy_class->list_features = tp_channel_dispatch_operation_list_features;
}
TpChannelDispatchOperation *
_tp_channel_dispatch_operation_new (TpClientFactory *factory,
const gchar *object_path,
GVariant *immutable_properties,
GError **error)
{
TpChannelDispatchOperation *self = NULL;
gchar *unique_name = NULL;
g_return_val_if_fail (factory != NULL, NULL);
g_return_val_if_fail (immutable_properties == NULL ||
g_variant_is_of_type (immutable_properties,
G_VARIANT_TYPE_VARDICT), NULL);
g_return_val_if_fail (object_path != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (immutable_properties != NULL)
g_variant_ref_sink (immutable_properties);
if (!tp_dbus_check_valid_object_path (object_path, error))
goto finally;
if (!_tp_dbus_connection_get_name_owner (
tp_client_factory_get_dbus_connection (factory), -1,
TP_CHANNEL_DISPATCHER_BUS_NAME, &unique_name, error))
goto finally;
self = TP_CHANNEL_DISPATCH_OPERATION (g_object_new (
TP_TYPE_CHANNEL_DISPATCH_OPERATION,
"bus-name", unique_name,
"object-path", object_path,
"cdo-properties", immutable_properties,
"factory", factory,
NULL));
finally:
if (immutable_properties != NULL)
g_variant_unref (immutable_properties);
g_free (unique_name);
return self;
}
/**
* TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE:
*
* Expands to a call to a function that returns a quark for the "core" feature
* on a #TpChannelDispatchOperation.
*
* When this feature is prepared, the basic properties of the
* ChannelDispatchOperation have been retrieved and are available for use.
*
* Specifically, this implies that:
*
* - #TpChannelDispatchOperation:connection is set (but
* TP_CONNECTION_FEATURE_CORE is not necessarily prepared)
* - #TpChannelDispatchOperation:account is set (but
* TP_ACCOUNT_FEATURE_CORE is not necessarily prepared)
* - #TpChannelDispatchOperation:channel is set (but
* TP_CHANNEL_FEATURE_CORE is not necessarily prepared)
* - #TpChannelDispatchOperation:possible-handlers is set
* - any extra interfaces will have been set up in TpProxy (i.e.
* #TpProxy:interfaces contains at least all extra ChannelDispatchOperation
* interfaces)
*
* One can ask for a feature to be prepared using the
* tp_proxy_prepare_async() function, and waiting for it to callback.
*
* Since: 0.11.5
*/
GQuark
tp_channel_dispatch_operation_get_feature_quark_core (void)
{
return g_quark_from_static_string (
"tp-channel-dispatch-operation-feature-core");
}
/**
* tp_channel_dispatch_operation_get_connection: (skip)
* @self: a #TpChannelDispatchOperation
*
* Returns the #TpConnection of this ChannelDispatchOperation.
* The returned pointer is only valid while @self is valid - reference
* it with g_object_ref() if needed.
*
* Returns: (transfer none): the value of #TpChannelDispatchOperation:connection
*
* Since: 0.19.9
*/
TpConnection *
tp_channel_dispatch_operation_get_connection (
TpChannelDispatchOperation *self)
{
return self->priv->connection;
}
/**
* tp_channel_dispatch_operation_get_account: (skip)
* @self: a #TpChannelDispatchOperation
*
* Returns the #TpAccount of this ChannelDispatchOperation.
* The returned pointer is only valid while @self is valid - reference
* it with g_object_ref() if needed.
*
* Returns: (transfer none): the value of #TpChannelDispatchOperation:account
*
* Since: 0.19.9
*/
TpAccount *
tp_channel_dispatch_operation_get_account (
TpChannelDispatchOperation *self)
{
return self->priv->account;
}
/**
* tp_channel_dispatch_operation_get_channel:
* @self: a #TpChannelDispatchOperation
*
* Returns the #TpChannel of this ChannelDispatchOperation.
*
* Returns: (transfer none): the value of #TpChannelDispatchOperation:channel
*/
TpChannel *
tp_channel_dispatch_operation_get_channel (
TpChannelDispatchOperation *self)
{
return self->priv->channel;
}
/**
* tp_channel_dispatch_operation_get_possible_handlers: (skip)
* @self: a #TpChannelDispatchOperation
*
* Returns a #GStrv containing the possible handlers of this
* ChannelDispatchOperation.
* The returned array and its strings are only valid while @self is
* valid - copy it with g_strdupv if needed.
*
* Returns: (transfer none): the value of
* #TpChannelDispatchOperation:possible-handlers
*
* Since: 0.19.9
*/
GStrv
tp_channel_dispatch_operation_get_possible_handlers (
TpChannelDispatchOperation *self)
{
return self->priv->possible_handlers;
}
static void
handle_with_cb (TpChannelDispatchOperation *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *result = user_data;
if (error != NULL)
{
DEBUG ("HandleWith failed: %s", error->message);
g_simple_async_result_set_from_error (result, error);
}
g_simple_async_result_complete (result);
g_object_unref (result);
}
/**
* tp_channel_dispatch_operation_handle_with_async:
* @self: a #TpChannelDispatchOperation
* @handler: (allow-none): The well-known bus name (starting with
* #TP_CLIENT_BUS_NAME_BASE) of the channel handler that should handle the
* channel, or %NULL if the client has no preferred channel handler
* @user_action_time: the time at which user action occurred, or one of the
* special values %TP_USER_ACTION_TIME_NOT_USER_ACTION or
* %TP_USER_ACTION_TIME_CURRENT_TIME
* @callback: a callback to call when the call returns
* @user_data: data to pass to @callback
*
* A variant of tp_channel_dispatch_operation_handle_with_async()
* allowing the approver to pass an user action time.
* This timestamp will be passed to the Handler when HandleChannels is called.
*
* If an X server timestamp for the user action causing this method call is
* available, @user_action_time should be this timestamp (for instance, the
* result of gdk_event_get_time() if it is not %GDK_CURRENT_TIME). Otherwise, it
* may be %TP_USER_ACTION_TIME_NOT_USER_ACTION to behave as if there was no
* user action or it happened a long time ago, or
* %TP_USER_ACTION_TIME_CURRENT_TIME to have the Handler behave as though the
* user action had just happened (resembling, but not numerically equal to,
* %GDK_CURRENT_TIME).
*
* This method has been introduced in telepathy-mission-control 5.5.0.
*/
void
tp_channel_dispatch_operation_handle_with_async (
TpChannelDispatchOperation *self,
const gchar *handler,
gint64 user_action_time,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
g_return_if_fail (TP_IS_CHANNEL_DISPATCH_OPERATION (self));
result = g_simple_async_result_new (G_OBJECT (self),
callback, user_data,
tp_channel_dispatch_operation_handle_with_async);
tp_cli_channel_dispatch_operation_call_handle_with (self, -1,
handler != NULL ? handler: "", user_action_time,
handle_with_cb, result, NULL, G_OBJECT (self));
}
/**
* tp_channel_dispatch_operation_handle_with_finish:
* @self: a #TpChannelDispatchOperation
* @result: a #GAsyncResult
* @error: a #GError to fill
*
* Finishes an async call to HandleWithTime().
*
* Returns: %TRUE if the HandleWithTime() call was successful, otherwise %FALSE
*/
gboolean
tp_channel_dispatch_operation_handle_with_finish (
TpChannelDispatchOperation *self,
GAsyncResult *result,
GError **error)
{
_tp_implement_finish_void (self,
tp_channel_dispatch_operation_handle_with_async);
}
static void
claim_with_cb (TpChannelDispatchOperation *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *result = user_data;
TpBaseClient *client;
client = g_simple_async_result_get_op_res_gpointer (result);
_tp_base_client_now_handling_channel (client, self->priv->channel);
g_simple_async_result_complete (result);
g_object_unref (result);
}
/**
* tp_channel_dispatch_operation_claim_with_async:
* @self: a #TpChannelDispatchOperation
* @client: the #TpBaseClient claiming @self
* @callback: a callback to call when the call returns
* @user_data: data to pass to @callback
*
* Called by an approver to claim channel for handling internally.
* If this method is called successfully, the process calling this
* method becomes the handler for the channel.
*
* If successful, this method will cause the #TpProxy::invalidated signal
* to be emitted, in the same way as for
* tp_channel_dispatch_operation_handle_with_async().
*
* This method may fail because the dispatch operation has already
* been completed. Again, see tp_channel_dispatch_operation_handle_with_async()
* for more details. The approver MUST NOT attempt to interact with
* the channel further in this case.
*
* %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE feature must be prepared before
* calling this function.
*
* Since: 0.15.0
*/
void
tp_channel_dispatch_operation_claim_with_async (
TpChannelDispatchOperation *self,
TpBaseClient *client,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
g_return_if_fail (TP_IS_CHANNEL_DISPATCH_OPERATION (self));
g_return_if_fail (tp_proxy_is_prepared (self,
TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE));
result = g_simple_async_result_new (G_OBJECT (self),
callback, user_data,
tp_channel_dispatch_operation_claim_with_async);
g_simple_async_result_set_op_res_gpointer (result, g_object_ref (client),
g_object_unref);
tp_cli_channel_dispatch_operation_call_claim (self, -1,
claim_with_cb, result, NULL, G_OBJECT (self));
}
/**
* tp_channel_dispatch_operation_claim_with_finish:
* @self: a #TpChannelDispatchOperation
* @result: a #GAsyncResult
* @error: a #GError to fill
*
* Finishes an async call to Claim() initiated using
* tp_channel_dispatch_operation_claim_with_async().
*
* Returns: %TRUE if the Claim() call was successful, otherwise %FALSE
*
* Since: 0.15.0
*/
gboolean
tp_channel_dispatch_operation_claim_with_finish (
TpChannelDispatchOperation *self,
GAsyncResult *result,
GError **error)
{
_tp_implement_finish_void (self, \
tp_channel_dispatch_operation_claim_with_async)
}
static void
channel_close_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
if (!tp_channel_close_finish (TP_CHANNEL (source), result, &error))
{
DEBUG ("Failed to close %s: %s", tp_proxy_get_object_path (source),
error->message);
g_error_free (error);
}
}
static void
claim_close_channel_cb (TpChannelDispatchOperation *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *result = user_data;
tp_channel_close_async (self->priv->channel, channel_close_cb, NULL);
g_simple_async_result_complete (result);
g_object_unref (result);
}
/**
* tp_channel_dispatch_operation_close_channel_async:
* @self: a #TpChannelDispatchOperation
* @callback: a callback to call when the request has been satisfied
* @user_data: data to pass to @callback
*
* Called by an approver to claim channel and close it right away.
* If this method is called successfully, @self has been claimed and
* tp_channel_close_async() has been called on the channel.
*
* If successful, this method will cause the #TpProxy::invalidated signal
* to be emitted, in the same way as for
* tp_channel_dispatch_operation_handle_with_async().
*
* This method may fail because the dispatch operation has already
* been completed. Again, see tp_channel_dispatch_operation_handle_with_async()
* for more details.
*
* %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE feature must be prepared before
* calling this function.
*/
void
tp_channel_dispatch_operation_close_channel_async (
TpChannelDispatchOperation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
g_return_if_fail (TP_IS_CHANNEL_DISPATCH_OPERATION (self));
g_return_if_fail (tp_proxy_is_prepared (self,
TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE));
result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
tp_channel_dispatch_operation_close_channel_async);
tp_cli_channel_dispatch_operation_call_claim (self, -1,
claim_close_channel_cb, result, NULL, G_OBJECT (self));
}
/**
* tp_channel_dispatch_operation_close_channel_finish:
* @self: a #TpChannelDispatchOperation
* @result: a #GAsyncResult
* @error: a #GError to fill
*
* Finishes an async operation initiated using
* tp_channel_dispatch_operation_close_channel_async().
*
* Returns: %TRUE if the Claim() call was successful and
* Close() has at least been attempted on the channel, otherwise %FALSE
*/
gboolean
tp_channel_dispatch_operation_close_channel_finish (
TpChannelDispatchOperation *self,
GAsyncResult *result,
GError **error)
{
_tp_implement_finish_void (self, \
tp_channel_dispatch_operation_close_channel_async)
}
typedef struct
{
TpChannelGroupChangeReason reason;
gchar *message;
} LeaveChannelCtx;
static LeaveChannelCtx *
leave_channel_ctx_new (TpChannelGroupChangeReason reason,
const gchar *message)
{
LeaveChannelCtx *ctx = g_slice_new (LeaveChannelCtx);
ctx->reason = reason;
ctx->message = g_strdup (message);
return ctx;
}
static void
leave_channel_ctx_free (LeaveChannelCtx *ctx)
{
g_free (ctx->message);
g_slice_free (LeaveChannelCtx, ctx);
}
static void
channel_leave_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
if (!tp_channel_leave_finish (TP_CHANNEL (source), result, &error))
{
DEBUG ("Failed to leave %s: %s", tp_proxy_get_object_path (source),
error->message);
g_error_free (error);
}
}
static void
claim_leave_channel_cb (TpChannelDispatchOperation *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *result = user_data;
LeaveChannelCtx *ctx;
ctx = g_simple_async_result_get_op_res_gpointer (result);
tp_channel_leave_async (self->priv->channel, ctx->reason, ctx->message,
channel_leave_cb, NULL);
g_simple_async_result_complete (result);
g_object_unref (result);
}
/**
* tp_channel_dispatch_operation_leave_channel_async:
* @self: a #TpChannelDispatchOperation
* @reason: the leave reason
* @message: the leave message
* @callback: a callback to call when the request has been satisfied
* @user_data: data to pass to @callback
*
* Called by an approver to claim channel and leave it right away.
* If this method is called successfully, @self has been claimed and
* tp_channel_leave_async() has been called on the channel.
*
* If successful, this method will cause the #TpProxy::invalidated signal
* to be emitted, in the same way as for
* tp_channel_dispatch_operation_handle_with_async().
*
* This method may fail because the dispatch operation has already
* been completed. Again, see tp_channel_dispatch_operation_handle_with_async()
* for more details.
*
* %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE feature must be prepared before
* calling this function.
*/
void
tp_channel_dispatch_operation_leave_channel_async (
TpChannelDispatchOperation *self,
TpChannelGroupChangeReason reason,
const gchar *message,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
g_return_if_fail (TP_IS_CHANNEL_DISPATCH_OPERATION (self));
g_return_if_fail (tp_proxy_is_prepared (self,
TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE));
result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
tp_channel_dispatch_operation_leave_channel_async);
g_simple_async_result_set_op_res_gpointer (result,
leave_channel_ctx_new (reason, message),
(GDestroyNotify) leave_channel_ctx_free);
tp_cli_channel_dispatch_operation_call_claim (self, -1,
claim_leave_channel_cb, result, NULL, G_OBJECT (self));
}
/**
* tp_channel_dispatch_operation_leave_channel_finish:
* @self: a #TpChannelDispatchOperation
* @result: a #GAsyncResult
* @error: a #GError to fill
*
* Finishes an async operation initiated using
* tp_channel_dispatch_operation_leave_channel_async().
*
* Returns: %TRUE if the Claim() call was successful and
* tp_channel_leave_async() has been attempted on the channel, otherwise %FALSE
*/
gboolean
tp_channel_dispatch_operation_leave_channel_finish (
TpChannelDispatchOperation *self,
GAsyncResult *result,
GError **error)
{
_tp_implement_finish_void (self, \
tp_channel_dispatch_operation_leave_channel_async)
}
static void
channel_destroy_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
if (!tp_channel_destroy_finish (TP_CHANNEL (source), result, &error))
{
DEBUG ("Failed to destroy %s: %s", tp_proxy_get_object_path (source),
error->message);
g_error_free (error);
}
}
static void
claim_destroy_channel_cb (TpChannelDispatchOperation *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *result = user_data;
tp_channel_destroy_async (self->priv->channel, channel_destroy_cb, NULL);
g_simple_async_result_complete (result);
g_object_unref (result);
}
/**
* tp_channel_dispatch_operation_destroy_channel_async:
* @self: a #TpChannelDispatchOperation
* @callback: a callback to call when the request has been satisfied
* @user_data: data to pass to @callback
*
* Called by an approver to claim channel and destroy it right away.
* If this method is called successfully, @self has been claimed and
* tp_channel_destroy_async() has been called on the channel.
*
* If successful, this method will cause the #TpProxy::invalidated signal
* to be emitted, in the same way as for
* tp_channel_dispatch_operation_handle_with_async().
*
* This method may fail because the dispatch operation has already
* been completed. Again, see tp_channel_dispatch_operation_handle_with_async()
* for more details.
*
* %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE feature must be prepared before
* calling this function.
*/
void
tp_channel_dispatch_operation_destroy_channel_async (
TpChannelDispatchOperation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
g_return_if_fail (TP_IS_CHANNEL_DISPATCH_OPERATION (self));
g_return_if_fail (tp_proxy_is_prepared (self,
TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE));
result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
tp_channel_dispatch_operation_destroy_channel_async);
tp_cli_channel_dispatch_operation_call_claim (self, -1,
claim_destroy_channel_cb, result, NULL, G_OBJECT (self));
}
/**
* tp_channel_dispatch_operation_destroy_channel_finish:
* @self: a #TpChannelDispatchOperation
* @result: a #GAsyncResult
* @error: a #GError to fill
*
* Finishes an async operation initiated using
* tp_channel_dispatch_operation_destroy_channel_async().
*
* Returns: %TRUE if the Claim() call was successful and
* tp_channel_destroy_async() has at least been attempted on the channel,
* otherwise %FALSE
*/
gboolean
tp_channel_dispatch_operation_destroy_channel_finish (
TpChannelDispatchOperation *self,
GAsyncResult *result,
GError **error)
{
_tp_implement_finish_void (self, \
tp_channel_dispatch_operation_destroy_channel_async)
}