/* * channel-dispatch-operation.c - proxy for incoming channels 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 "telepathy-glib/channel-dispatch-operation-internal.h" #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/simple-client-factory-internal.h" #include "telepathy-glib/_gen/tp-cli-channel-dispatch-operation-body.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 channels 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 channels to * Approver clients for approval. An approver should generally ask the user * whether they want to participate in the requested communication channels * (join the chat or chatroom, answer the call, accept the file transfer, or * whatever is appropriate). A collection of channels offered in this way * is represented by a ChannelDispatchOperation object. * * If the user wishes to accept the communication channels, the approver * should call tp_cli_channel_dispatch_operation_call_handle_with() to * indicate the user's or approver's preferred handler for the channels (the * empty string indicates no particular preference, and will cause any * suitable handler to be used). * * If the user wishes to reject the communication channels, or if the user * accepts the channels and the approver will handle them itself, the approver * should call tp_cli_channel_dispatch_operation_call_claim(). If this method * succeeds, the approver immediately has control over the channels 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 a channel closes, the #TpChannelDispatchOperation::channel-lost signal * is emitted. If all channels * 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 channels, 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; GPtrArray *channels; GStrv possible_handlers; GHashTable *immutable_properties; }; enum { PROP_CONNECTION = 1, PROP_ACCOUNT, PROP_CHANNELS, PROP_POSSIBLE_HANDLERS, PROP_CDO_PROPERTIES, N_PROPS }; enum { SIGNAL_CHANNEL_LOST, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0 }; 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_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); } static void tp_channel_dispatch_operation_finished_cb (TpChannelDispatchOperation *self, gpointer unused G_GNUC_UNUSED, GObject *object G_GNUC_UNUSED) { GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED, "ChannelDispatchOperation finished and was removed" }; tp_proxy_invalidate ((TpProxy *) self, &e); } static void tp_channel_dispatch_operation_channel_lost_cb (TpChannelDispatchOperation *self, const gchar *path, const gchar *dbus_error, const gchar *message, gpointer unused G_GNUC_UNUSED, GObject *object G_GNUC_UNUSED) { guint i; if (self->priv->channels == NULL) /* We didn't fetch channels yet */ return; for (i = 0; i < self->priv->channels->len; i++) { TpChannel *channel = g_ptr_array_index (self->priv->channels, i); if (!tp_strdiff (tp_proxy_get_object_path (channel), path)) { GError *error = NULL; /* Removing the channel from the array will unref it, add an extra * ref as we'll need it to fire the signal */ g_object_ref (channel); g_ptr_array_remove (self->priv->channels, channel); tp_proxy_dbus_error_to_gerror (self, dbus_error, message, &error); g_signal_emit (self, signals[SIGNAL_CHANNEL_LOST], 0, channel, error->domain, error->code, error->message); g_object_notify ((GObject *) self, "channels"); g_object_unref (channel); g_error_free (error); return; } } DEBUG ("Don't know this channel: %s", path); } 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_CHANNELS: g_value_set_boxed (value, self->priv->channels); break; case PROP_POSSIBLE_HANDLERS: g_value_set_boxed (value, self->priv->possible_handlers); break; case PROP_CDO_PROPERTIES: g_value_set_boxed (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, const gchar *path) { GError *error = NULL; if (self->priv->connection != NULL) return; if (path == NULL) return; self->priv->connection = tp_simple_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"); if (g_hash_table_lookup (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_CONNECTION) != NULL) return; g_hash_table_insert (self->priv->immutable_properties, g_strdup (TP_PROP_CHANNEL_DISPATCH_OPERATION_CONNECTION), tp_g_value_slice_new_boxed (DBUS_TYPE_G_OBJECT_PATH, path)); } static void maybe_set_account (TpChannelDispatchOperation *self, const gchar *path) { GError *error = NULL; if (self->priv->account != NULL) return; if (path == NULL) return; self->priv->account = tp_simple_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"); if (g_hash_table_lookup (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_ACCOUNT) != NULL) return; g_hash_table_insert (self->priv->immutable_properties, g_strdup (TP_PROP_CHANNEL_DISPATCH_OPERATION_ACCOUNT), tp_g_value_slice_new_boxed (DBUS_TYPE_G_OBJECT_PATH, path)); } static void maybe_set_possible_handlers (TpChannelDispatchOperation *self, GStrv handlers) { if (self->priv->possible_handlers != NULL) return; if (handlers == NULL) return; self->priv->possible_handlers = g_strdupv (handlers); g_object_notify ((GObject *) self, "possible-handlers"); if (g_hash_table_lookup (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS) != NULL) return; g_hash_table_insert (self->priv->immutable_properties, g_strdup (TP_PROP_CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS), tp_g_value_slice_new_boxed (G_TYPE_STRV, handlers)); } static void maybe_set_interfaces (TpChannelDispatchOperation *self, const gchar **interfaces) { if (interfaces == NULL) return; tp_proxy_add_interfaces ((TpProxy *) self, interfaces); g_hash_table_insert (self->priv->immutable_properties, g_strdup (TP_PROP_CHANNEL_DISPATCH_OPERATION_INTERFACES), tp_g_value_slice_new_boxed (G_TYPE_STRV, interfaces)); } 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_CHANNELS: g_assert (self->priv->channels == NULL); /* construct-only */ _tp_channel_dispatch_operation_ensure_channels (self, g_value_get_boxed (value)); break; case PROP_CDO_PROPERTIES: { GHashTable *asv = g_value_get_boxed (value); if (asv == NULL) return; tp_g_hash_table_update (self->priv->immutable_properties, asv, (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } 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); g_return_if_fail (tp_proxy_get_dbus_daemon (self) != NULL); _tp_proxy_ensure_factory (self, NULL); maybe_set_connection (self, tp_asv_get_boxed (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_CONNECTION, DBUS_TYPE_G_OBJECT_PATH)); maybe_set_account (self, tp_asv_get_boxed (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_ACCOUNT, DBUS_TYPE_G_OBJECT_PATH)); maybe_set_possible_handlers (self, tp_asv_get_boxed (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS, G_TYPE_STRV)); maybe_set_interfaces (self, tp_asv_get_boxed (self->priv->immutable_properties, TP_PROP_CHANNEL_DISPATCH_OPERATION_INTERFACES, G_TYPE_STRV)); 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; } sc = tp_cli_channel_dispatch_operation_connect_to_channel_lost (self, tp_channel_dispatch_operation_channel_lost_cb, NULL, NULL, NULL, &error); if (sc == NULL) { g_critical ("Couldn't connect to ChannelLost: %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; } if (self->priv->channels != NULL) { /* channels array has 'g_object_unref' has free_func */ g_ptr_array_unref (self->priv->channels); self->priv->channels = NULL; } g_strfreev (self->priv->possible_handlers); self->priv->possible_handlers = NULL; if (self->priv->immutable_properties != NULL) { g_hash_table_unref (self->priv->immutable_properties); self->priv->immutable_properties = NULL; } if (dispose != NULL) dispose (object); } static TpChannel * look_for_channel_having_path (GPtrArray *array, const gchar *path) { guint i; for (i = 0; i < array->len; i++) { TpChannel *channel = g_ptr_array_index (array, i); if (!tp_strdiff (tp_proxy_get_object_path (channel), path)) return channel; } return NULL; } static void update_channels_array (TpChannelDispatchOperation *self, GPtrArray *channels) { guint i; GPtrArray *old = NULL; if (self->priv->channels != NULL) { /* We received an initial list of channels during creation. Remove those * which are not in the Channels property any more. */ old = self->priv->channels; } self->priv->channels = g_ptr_array_new_full (channels->len, g_object_unref); for (i = 0; i < channels->len; i++) { const gchar *path; GHashTable *chan_props; TpChannel *channel = NULL; GError *err = NULL; tp_value_array_unpack (g_ptr_array_index (channels, i), 2, &path, &chan_props); if (old != NULL) channel = look_for_channel_having_path (old, path); if (channel != NULL) { g_object_ref (channel); } else { channel = tp_simple_client_factory_ensure_channel ( tp_proxy_get_factory (self), self->priv->connection, path, chan_props, &err); if (channel == NULL) { DEBUG ("Failed to create channel %s: %s", path, err->message); g_error_free (err); continue; } } g_ptr_array_add (self->priv->channels, channel); } if (old != NULL) { g_ptr_array_unref (old); } } 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; GPtrArray *channels; GError *e = NULL; if (error != NULL) { DEBUG ("Failed to fetch ChannelDispatchOperation properties: %s", error->message); prepared = FALSE; e = g_error_copy (error); goto out; } /* Connection */ maybe_set_connection (self, tp_asv_get_boxed (props, "Connection", DBUS_TYPE_G_OBJECT_PATH)); 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; } /* Account */ maybe_set_account (self, tp_asv_get_boxed (props, "Account", DBUS_TYPE_G_OBJECT_PATH)); 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; } /* PossibleHandlers */ maybe_set_possible_handlers (self, tp_asv_get_boxed (props, "PossibleHandlers", G_TYPE_STRV)); 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; } maybe_set_interfaces (self, tp_asv_get_boxed (props, "Interfaces", G_TYPE_STRV)); /* set channels (not an immutable property) */ channels = tp_asv_get_boxed (props, "Channels", TP_ARRAY_TYPE_CHANNEL_DETAILS_LIST); if (channels == NULL) { e = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Mandatory 'Channels' property is missing"); DEBUG ("%s", e->message); prepared = FALSE; goto out; } update_channels_array (self, channels); g_object_notify ((GObject *) self, "channels"); 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_in_idle (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 channels 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 ("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 channels 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:channels: * * A #GPtrArray containing 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. * * Since: 0.11.5 */ param_spec = g_param_spec_boxed ("channels", "GPtrArray of TpChannel", "The TpChannel to be dispatched", 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); /** * TpChannelDispatchOperation:possible-handlers: * * A #GStrv containing the well known bus names (starting * with TP_CLIENT_BUS_NAME_BASE) of the possible Handlers for * the channels * * 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 channels", 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 #GHashTable where the keys are D-Bus * interface name + "." + property name, and the values are #GValue instances. * * 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. * * Since: 0.11.5 */ param_spec = g_param_spec_boxed ("cdo-properties", "Immutable D-Bus properties", "A map D-Bus interface + \".\" + property name => GValue", TP_HASH_TYPE_QUALIFIED_PROPERTY_VALUE_MAP, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CDO_PROPERTIES, param_spec); /** * TpChannelDispatchOperation::channel-lost: * @self: a #TpChannelDispatchOperation * @channel: the #TpChannel that closed * @domain: domain of a #GError indicating why the channel has been closed * @code: error code of a #GError indicating why the channel has been closed * @message: a message associated with the error * * Emitted when a channel has closed before it could be claimed or handled. * * Since: 0.11.5 */ signals[SIGNAL_CHANNEL_LOST] = g_signal_new ( "channel-lost", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, NULL, G_TYPE_NONE, 4, TP_TYPE_CHANNEL, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING); 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; tp_channel_dispatch_operation_init_known_interfaces (); } /** * tp_channel_dispatch_operation_init_known_interfaces: * * Ensure that the known interfaces for TpChannelDispatchOperation have been * set up. This is done automatically when necessary, but for correct * overriding of library interfaces by local extensions, you should * call this function before calling * tp_proxy_or_subclass_hook_on_interface_add() with first argument * %TP_TYPE_CHANNEL_DISPATCH_OPERATION. * * Since: 0.7.32 */ void tp_channel_dispatch_operation_init_known_interfaces (void) { static gsize once = 0; if (g_once_init_enter (&once)) { GType tp_type = TP_TYPE_CHANNEL_DISPATCH_OPERATION; tp_proxy_init_known_interfaces (); tp_proxy_or_subclass_hook_on_interface_add (tp_type, tp_cli_channel_dispatch_operation_add_signals); tp_proxy_subclass_add_error_mapping (tp_type, TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR); g_once_init_leave (&once, 1); } } /** * tp_channel_dispatch_operation_new: * @bus_daemon: Proxy for the D-Bus daemon * @object_path: The non-NULL object path of this channel dispatch operation * @immutable_properties: As many as are known of the immutable D-Bus * properties of this channel dispatch operation, or %NULL if none are known * @error: Used to raise an error if %NULL is returned * * Convenience function to create a new channel dispatch operation proxy. * * The @immutable_properties argument is not yet used. * * Returns: a new reference to an channel dispatch operation proxy, or %NULL if * @object_path is not syntactically valid or the channel dispatcher is not * running * Deprecated: Since 0.19.9. New code should get * #TpChannelDispatchOperation objects from a #TpBaseClient */ TpChannelDispatchOperation * tp_channel_dispatch_operation_new (TpDBusDaemon *bus_daemon, const gchar *object_path, GHashTable *immutable_properties, GError **error) { return _tp_channel_dispatch_operation_new_with_factory (NULL, bus_daemon, object_path, immutable_properties, error); } TpChannelDispatchOperation * _tp_channel_dispatch_operation_new_with_factory (TpSimpleClientFactory *factory, TpDBusDaemon *bus_daemon, const gchar *object_path, GHashTable *immutable_properties, GError **error) { TpChannelDispatchOperation *self; gchar *unique_name; g_return_val_if_fail (bus_daemon != NULL, NULL); g_return_val_if_fail (object_path != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (!tp_dbus_check_valid_object_path (object_path, error)) return NULL; if (!_tp_dbus_daemon_get_name_owner (bus_daemon, -1, TP_CHANNEL_DISPATCHER_BUS_NAME, &unique_name, error)) return NULL; self = TP_CHANNEL_DISPATCH_OPERATION (g_object_new ( TP_TYPE_CHANNEL_DISPATCH_OPERATION, "dbus-daemon", bus_daemon, "bus-name", unique_name, "object-path", object_path, "cdo-properties", immutable_properties, "factory", factory, NULL)); 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:channels 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_borrow_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.11.5 * Deprecated: Since 0.19.9. New code should use * tp_channel_dispatch_operation_get_connection() instead. */ TpConnection * tp_channel_dispatch_operation_borrow_connection ( TpChannelDispatchOperation *self) { return self->priv->connection; } /** * tp_channel_dispatch_operation_borrow_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.11.5 * Deprecated: Since 0.19.9. New code should use * tp_channel_dispatch_operation_get_account() instead. */ TpAccount * tp_channel_dispatch_operation_borrow_account ( TpChannelDispatchOperation *self) { return self->priv->account; } /** * tp_channel_dispatch_operation_borrow_channels: (skip) * @self: a #TpChannelDispatchOperation * * Returns a #GPtrArray containing the #TpChannel of this * ChannelDispatchOperation. * The returned array and its #TpChannel are only valid while @self is * valid - copy array and reference channels with g_object_ref() if needed. * * Returns: (transfer none): the value of #TpChannelDispatchOperation:channels * * Since: 0.11.5 * Deprecated: Since 0.19.9. New code should use * tp_channel_dispatch_operation_get_channels() instead. */ GPtrArray * tp_channel_dispatch_operation_borrow_channels ( TpChannelDispatchOperation *self) { return self->priv->channels; } /** * tp_channel_dispatch_operation_borrow_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.11.5 * Deprecated: Since 0.19.9. New code should use * tp_channel_dispatch_operation_get_possible_handlers() instead. */ GStrv tp_channel_dispatch_operation_borrow_possible_handlers ( TpChannelDispatchOperation *self) { return self->priv->possible_handlers; } /** * tp_channel_dispatch_operation_borrow_immutable_properties: (skip) * @self: a #TpChannelDispatchOperation * * Returns the immutable D-Bus properties of this channel. * The returned hash table is only valid while @self is valid - reference * it with g_hash_table_ref() if needed. * * Returns: (transfer none) (element-type utf8 GObject.Value): the value of * #TpChannelDispatchOperation:cdo-properties * * Since: 0.11.5 * Deprecated: Since 0.19.9. New code should use individual property * getters like tp_channel_dispatch_operation_get_connection(), * tp_channel_dispatch_operation_get_account(), * tp_channel_dispatch_operation_get_channels(), or * tp_channel_dispatch_operation_get_possible_handlers() instead. */ GHashTable * tp_channel_dispatch_operation_borrow_immutable_properties ( TpChannelDispatchOperation *self) { return self->priv->immutable_properties; } /** * 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_channels: * @self: a #TpChannelDispatchOperation * * Returns a #GPtrArray containing the #TpChannel of this * ChannelDispatchOperation. * The returned array and its #TpChannel are only valid while @self is * valid - copy array and reference channels with g_object_ref() if needed. * * Returns: (transfer none) (element-type TelepathyGLib.Channel): the value * of #TpChannelDispatchOperation:channels * * Since: 0.19.9 */ GPtrArray * tp_channel_dispatch_operation_get_channels ( TpChannelDispatchOperation *self) { return self->priv->channels; } /** * 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_in_idle (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 * @callback: a callback to call when the call returns * @user_data: data to pass to @callback * * Called by an approver to accept a channel bundle and request that the * given handler be used to handle it. * * If successful, this method will cause the #TpProxy::invalidated signal * to be emitted with the TP_DBUS_ERROR_OBJECT_REMOVED error code. * * However, this method may fail because the dispatch has already been * completed and the object has already gone. If this occurs, it indicates * that another approver has asked for the bundle to be handled by a * particular handler. The approver MUST NOT attempt to interact with * the channels further in this case, unless it is separately * invoked as the handler. * * Approvers which are also channel handlers SHOULD use * tp_channel_dispatch_operation_claim_async() instead * of tp_channel_dispatch_operation_handle_with_async() to request * that they can handle a channel bundle themselves. * * Since: 0.11.5 */ void tp_channel_dispatch_operation_handle_with_async ( TpChannelDispatchOperation *self, const gchar *handler, 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: "", 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 HandleWith(). * * Returns: %TRUE if the HandleWith() call was successful, otherwise %FALSE * * Since: 0.11.5 */ 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_cb (TpChannelDispatchOperation *self, const GError *error, gpointer user_data, GObject *weak_object) { GSimpleAsyncResult *result = user_data; if (error != NULL) { DEBUG ("Claim failed: %s", error->message); g_simple_async_result_set_from_error (result, error); } g_simple_async_result_complete_in_idle (result); g_object_unref (result); } /** * tp_channel_dispatch_operation_claim_async: * @self: a #TpChannelDispatchOperation * @callback: a callback to call when the call returns * @user_data: data to pass to @callback * * Called by an approver to claim channels 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 channels further in this case. * * Since: 0.11.5 * Deprecated: since 0.15.0. Use * tp_channel_dispatch_operation_claim_with_async() */ void tp_channel_dispatch_operation_claim_async ( TpChannelDispatchOperation *self, 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_claim_async); tp_cli_channel_dispatch_operation_call_claim (self, -1, claim_cb, result, NULL, G_OBJECT (self)); } /** * tp_channel_dispatch_operation_claim_finish: * @self: a #TpChannelDispatchOperation * @result: a #GAsyncResult * @error: a #GError to fill * * Finishes an async call to Claim(). * * Returns: %TRUE if the Claim() call was successful, otherwise %FALSE * * Since: 0.11.5 * Deprecated: since 0.15.0. Use * tp_channel_dispatch_operation_claim_with_finish() */ gboolean tp_channel_dispatch_operation_claim_finish ( TpChannelDispatchOperation *self, GAsyncResult *result, GError **error) { _tp_implement_finish_void (self, tp_channel_dispatch_operation_claim_async); } /* FIXME: This is temporary solution to share TpChannel objects until * TpSimpleClientFactory can be used for that */ void _tp_channel_dispatch_operation_ensure_channels (TpChannelDispatchOperation *self, GPtrArray *channels) { guint i; if (self->priv->channels != NULL || channels == NULL) return; /* Do not just ref the GPtrArray because we'll modify its content */ self->priv->channels = g_ptr_array_new_full (channels->len, g_object_unref); for (i = 0; i < channels->len; i++) g_ptr_array_add (self->priv->channels, g_object_ref (g_ptr_array_index (channels, i))); } /** * tp_channel_dispatch_operation_handle_with_time_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. * * Since: 0.11.7 */ void tp_channel_dispatch_operation_handle_with_time_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_time_async); tp_cli_channel_dispatch_operation_call_handle_with_time (self, -1, handler != NULL ? handler: "", user_action_time, handle_with_cb, result, NULL, G_OBJECT (self)); } /** * tp_channel_dispatch_operation_handle_with_time_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 * * Since: 0.11.7 */ gboolean tp_channel_dispatch_operation_handle_with_time_finish ( TpChannelDispatchOperation *self, GAsyncResult *result, GError **error) { _tp_implement_finish_void (self, tp_channel_dispatch_operation_handle_with_time_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_channels (client, self->priv->channels); g_simple_async_result_complete_in_idle (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 channels 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 channels further in this case. * * This is an improved version of tp_channel_dispatch_operation_claim_async() * as it tells @client about the new channels being handled. * * %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_channels_cb (TpChannelDispatchOperation *self, const GError *error, gpointer user_data, GObject *weak_object) { GSimpleAsyncResult *result = user_data; guint i; for (i = 0; i < self->priv->channels->len; i++) { TpChannel *channel = g_ptr_array_index (self->priv->channels, i); tp_channel_close_async (channel, channel_close_cb, NULL); } g_simple_async_result_complete_in_idle (result); g_object_unref (result); } /** * tp_channel_dispatch_operation_close_channels_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 channels and close them all right away. * If this method is called successfully, @self has been claimed and * tp_channel_close_async() has been called on all of its channels. * * 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. * * Since: 0.15.1 */ void tp_channel_dispatch_operation_close_channels_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_channels_async); tp_cli_channel_dispatch_operation_call_claim (self, -1, claim_close_channels_cb, result, NULL, G_OBJECT (self)); } /** * tp_channel_dispatch_operation_close_channels_finish: * @self: a #TpChannelDispatchOperation * @result: a #GAsyncResult * @error: a #GError to fill * * Finishes an async operation initiated using * tp_channel_dispatch_operation_close_channels_async(). * * Returns: %TRUE if the Claim() call was successful and * Close() has at least been attempted on all the channels, otherwise %FALSE * * Since: 0.15.1 */ gboolean tp_channel_dispatch_operation_close_channels_finish ( TpChannelDispatchOperation *self, GAsyncResult *result, GError **error) { _tp_implement_finish_void (self, \ tp_channel_dispatch_operation_close_channels_async) } typedef struct { TpChannelGroupChangeReason reason; gchar *message; } LeaveChannelsCtx; static LeaveChannelsCtx * leave_channels_ctx_new (TpChannelGroupChangeReason reason, const gchar *message) { LeaveChannelsCtx *ctx = g_slice_new (LeaveChannelsCtx); ctx->reason = reason; ctx->message = g_strdup (message); return ctx; } static void leave_channels_ctx_free (LeaveChannelsCtx *ctx) { g_free (ctx->message); g_slice_free (LeaveChannelsCtx, 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_channels_cb (TpChannelDispatchOperation *self, const GError *error, gpointer user_data, GObject *weak_object) { GSimpleAsyncResult *result = user_data; guint i; LeaveChannelsCtx *ctx; ctx = g_simple_async_result_get_op_res_gpointer (result); for (i = 0; i < self->priv->channels->len; i++) { TpChannel *channel = g_ptr_array_index (self->priv->channels, i); tp_channel_leave_async (channel, ctx->reason, ctx->message, channel_leave_cb, NULL); } g_simple_async_result_complete_in_idle (result); g_object_unref (result); } /** * tp_channel_dispatch_operation_leave_channels_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 channels and leave them all right away. * If this method is called successfully, @self has been claimed and * tp_channel_leave_async() has been called on all of its channels. * * 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. * * Since: 0.15.2 */ void tp_channel_dispatch_operation_leave_channels_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_channels_async); g_simple_async_result_set_op_res_gpointer (result, leave_channels_ctx_new (reason, message), (GDestroyNotify) leave_channels_ctx_free); tp_cli_channel_dispatch_operation_call_claim (self, -1, claim_leave_channels_cb, result, NULL, G_OBJECT (self)); } /** * tp_channel_dispatch_operation_leave_channels_finish: * @self: a #TpChannelDispatchOperation * @result: a #GAsyncResult * @error: a #GError to fill * * Finishes an async operation initiated using * tp_channel_dispatch_operation_leave_channels_async(). * * Returns: %TRUE if the Claim() call was successful and * tp_channel_leave_async() has at least been attempted on all the * channels, otherwise %FALSE * * Since: 0.15.2 */ gboolean tp_channel_dispatch_operation_leave_channels_finish ( TpChannelDispatchOperation *self, GAsyncResult *result, GError **error) { _tp_implement_finish_void (self, \ tp_channel_dispatch_operation_leave_channels_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_channels_cb (TpChannelDispatchOperation *self, const GError *error, gpointer user_data, GObject *weak_object) { GSimpleAsyncResult *result = user_data; guint i; for (i = 0; i < self->priv->channels->len; i++) { TpChannel *channel = g_ptr_array_index (self->priv->channels, i); tp_channel_destroy_async (channel, channel_destroy_cb, NULL); } g_simple_async_result_complete_in_idle (result); g_object_unref (result); } /** * tp_channel_dispatch_operation_destroy_channels_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 channels and destroy them all right away. * If this method is called successfully, @self has been claimed and * tp_channel_destroy_async() has been called on all of its channels. * * 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. * * Since: 0.15.2 */ void tp_channel_dispatch_operation_destroy_channels_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_channels_async); tp_cli_channel_dispatch_operation_call_claim (self, -1, claim_destroy_channels_cb, result, NULL, G_OBJECT (self)); } /** * tp_channel_dispatch_operation_destroy_channels_finish: * @self: a #TpChannelDispatchOperation * @result: a #GAsyncResult * @error: a #GError to fill * * Finishes an async operation initiated using * tp_channel_dispatch_operation_destroy_channels_async(). * * Returns: %TRUE if the Claim() call was successful and * tp_channel_destroy_async() has at least been attempted on all the * channels, otherwise %FALSE * * Since: 0.15.2 */ gboolean tp_channel_dispatch_operation_destroy_channels_finish ( TpChannelDispatchOperation *self, GAsyncResult *result, GError **error) { _tp_implement_finish_void (self, \ tp_channel_dispatch_operation_destroy_channels_async) }