/*
* status.c - proxy for Ytstenut service status
*
* Copyright (C) 2009 Collabora Ltd.
* Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011 Intel Corp.
*
* 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 "channel.h"
#include
#include
#include
#include
#include
#include "_gen/interfaces.h"
#include "_gen/cli-channel-body.h"
#define DEBUG(msg, ...) \
g_debug ("%s: " msg, G_STRFUNC, ##__VA_ARGS__)
/**
* SECTION:channel
* @title: TpYtsChannel
* @short_description: proxy object for Ytstenut message channel
*
* The #TpYtsChannel proxy object is used to communicate with a Ytstenut
* Channel implementation.
*
* You can use tp_yts_client_request_channel_async() to request new outgoing
* channels. To receive new incoming channels, use a #TpYtsClient and connect
* the #TpYtsClient::received-channels signal.
*
* On an outgoing channel you should use tp_yts_channel_request_async() to send
* off the Ytstenut request. To listen for replies or failures use the
* tp_yts_channel_connect_to_replied() and tp_yts_channel_connect_to_failed()
* functions respectively.
*
* On an incoming channel, you can use the tp_yts_channel_reply_async() or
* tp_yts_channel_fail_async() functions to send back a Ytstenut reply or
* error.
*/
/**
* TpYtsChannel:
*
* The Ytstenut Channel holds information about a single Ytstenut request
* and reply. It should be created using the channel dispatcher, and the
* relevant properties.
*/
/**
* TpYtsChannelClass:
*
* The class of a #TpYtsChannel.
*/
/* properties */
enum {
PROP_0,
PROP_REQUEST_TYPE,
PROP_REQUEST_ATTRIBUTES,
PROP_REQUEST_BODY,
PROP_TARGET_SERVICE,
PROP_INITIATOR_SERVICE,
};
struct _TpYtsChannelPrivate
{
TpYtsRequestType request_type;
GHashTable *request_attributes;
gchar *request_body;
gchar *target_service;
gchar *initiator_service;
};
G_DEFINE_TYPE (TpYtsChannel, tp_yts_channel, TP_TYPE_CHANNEL);
static void
tp_yts_channel_init (TpYtsChannel *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_YTS_CHANNEL,
TpYtsChannelPrivate);
}
static void
tp_yts_channel_constructed (GObject *object)
{
TpYtsChannel *self = TP_YTS_CHANNEL (object);
TpYtsChannelPrivate *priv = self->priv;
GHashTable *props;
g_object_get (object,
"channel-properties", &props,
NULL);
/* RequestType */
priv->request_type = tp_asv_get_uint32 (props,
TP_YTS_IFACE_CHANNEL ".RequestType", NULL);
/* RequestAttributes */
priv->request_attributes = tp_asv_get_boxed (props,
TP_YTS_IFACE_CHANNEL ".RequestAttributes",
TP_HASH_TYPE_STRING_STRING_MAP);
/* only ref it if we get one out */
if (priv->request_attributes != NULL)
g_hash_table_ref (priv->request_attributes);
/* RequestBody */
priv->request_body = g_strdup (tp_asv_get_string (props,
TP_YTS_IFACE_CHANNEL ".RequestBody"));
/* TargetService */
priv->target_service = g_strdup (tp_asv_get_string (props,
TP_YTS_IFACE_CHANNEL ".TargetService"));
/* InitiatorService */
priv->initiator_service = g_strdup (tp_asv_get_string (props,
TP_YTS_IFACE_CHANNEL ".InitiatorService"));
g_hash_table_unref (props);
if (G_OBJECT_CLASS (tp_yts_channel_parent_class)->constructed != NULL)
G_OBJECT_CLASS (tp_yts_channel_parent_class)->constructed (object);
}
static void
tp_yts_channel_finalize (GObject *object)
{
TpYtsChannel *self = TP_YTS_CHANNEL (object);
TpYtsChannelPrivate *priv = self->priv;
tp_clear_pointer (&priv->request_attributes, g_hash_table_unref);
tp_clear_pointer (&priv->request_body, g_free);
tp_clear_pointer (&priv->target_service, g_free);
tp_clear_pointer (&priv->initiator_service, g_free);
if (G_OBJECT_CLASS (tp_yts_channel_parent_class)->finalize != NULL)
G_OBJECT_CLASS (tp_yts_channel_parent_class)->finalize (object);
}
static void
tp_yts_channel_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
TpYtsChannel *self = TP_YTS_CHANNEL (object);
switch (property_id)
{
case PROP_REQUEST_TYPE:
g_value_set_uint (value, self->priv->request_type);
break;
case PROP_REQUEST_ATTRIBUTES:
g_value_set_boxed (value, self->priv->request_attributes);
break;
case PROP_REQUEST_BODY:
g_value_set_string (value, self->priv->request_body);
break;
case PROP_TARGET_SERVICE:
g_value_set_string (value, self->priv->target_service);
break;
case PROP_INITIATOR_SERVICE:
g_value_set_string (value, self->priv->initiator_service);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
tp_yts_channel_class_init (TpYtsChannelClass *klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
TpProxyClass *proxy_class = (TpProxyClass *) klass;
GType tp_type = TP_TYPE_YTS_CHANNEL;
object_class->constructed = tp_yts_channel_constructed;
object_class->finalize = tp_yts_channel_finalize;
object_class->get_property = tp_yts_channel_get_property;
proxy_class->interface = TP_YTS_IFACE_QUARK_CHANNEL;
g_type_class_add_private (klass, sizeof (TpYtsChannelPrivate));
tp_proxy_init_known_interfaces ();
tp_proxy_or_subclass_hook_on_interface_add (tp_type,
tp_yts_channel_add_signals);
tp_proxy_subclass_add_error_mapping (tp_type,
TP_ERROR_PREFIX, TP_ERRORS, TP_TYPE_ERROR);
/**
* TpYtsChannel:request-type:
*
* The IQ type of the request message.
*/
g_object_class_install_property (object_class, PROP_REQUEST_TYPE,
g_param_spec_uint ("request-type", "Request type",
"Ytstenut Request Type", 0, NUM_TP_YTS_REQUEST_TYPES, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* TpYtsChannel:request-attributes:
*
* The attributes present on the Ytstenut request message.
*/
g_object_class_install_property (object_class, PROP_REQUEST_ATTRIBUTES,
g_param_spec_boxed ("request-attributes", "Request attributes",
"Ytstenut Request Attributes",
TP_HASH_TYPE_STRING_STRING_MAP,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* TpYtsChannel:request-body:
*
* The UTF-8 encoded XML body of the Ytstenut request message.
*/
g_object_class_install_property (object_class, PROP_REQUEST_BODY,
g_param_spec_string ("request-body", "Request body",
"Ytstenut Request Body", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* TpYtsChannel:target-service:
*
* The name of the target Ytstenut service that this channel is
* targetted at.
*/
g_object_class_install_property (object_class, PROP_TARGET_SERVICE,
g_param_spec_string ("target-service", "Target service",
"Ytstenut Target Service", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* TpYtsChannel:initiator-service:
*
* The name of the Ytstenut service which initiated this channel.
*/
g_object_class_install_property (object_class, PROP_INITIATOR_SERVICE,
g_param_spec_string ("initiator-service", "Initiator service",
"Ytstenut Initiator Service", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
/**
* tp_yts_channel_new_from_properties:
* @conn: The telepathy connection
* @object_path: The DBus object path of the channel.
* @immutable_properties: The immutable properties of the channel.
* @error: If not %NULL, used to raise an error when %NULL is returned.
*
* The same as tp_yts_channel_with_factory() but without the client
* factory.
*
* This function should not be used. tp_yts_channel_with_factory()
* should be used with an appropriate client factory.
*
* Returns: A newly allocated #TpYtsChannel object.
*/
TpChannel *
tp_yts_channel_new_from_properties (TpConnection *conn,
const gchar *object_path,
GHashTable *immutable_properties,
GError **error)
{
return tp_yts_channel_new_with_factory (NULL, conn, object_path,
immutable_properties, error);
}
/**
* tp_yts_channel_new_with_factory:
* @factory: a #TpSimpleClientFactory
* @conn: The telepathy connection
* @object_path: The DBus object path of the channel.
* @immutable_properties: The immutable properties of the channel.
* @error: If not %NULL, used to raise an error when %NULL is returned.
*
* Create a new #TpYtsChannel proxy object for a channel that exists in the
* Ytstenut DBus implementation.
*
* In order to request a new outgoing channel use
* tp_yts_client_request_channel_async() instead of this function.
*
* Note that this function should only be called for incoming channels
* by the client factory, @factory.
*
* Returns: A newly allocated #TpYtsChannel object.
*/
TpChannel *
tp_yts_channel_new_with_factory (TpSimpleClientFactory *factory,
TpConnection *conn,
const gchar *object_path,
GHashTable *immutable_properties,
GError **error)
{
TpProxy *conn_proxy = TP_PROXY (conn);
TpChannel *ret = NULL;
g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
g_return_val_if_fail (object_path != NULL, NULL);
g_return_val_if_fail (immutable_properties != NULL, NULL);
if (!tp_dbus_check_valid_object_path (object_path, error))
goto finally;
ret = TP_CHANNEL (g_object_new (TP_TYPE_YTS_CHANNEL,
"connection", conn,
"dbus-daemon", conn_proxy->dbus_daemon,
"bus-name", conn_proxy->bus_name,
"object-path", object_path,
"handle-type", (guint) TP_UNKNOWN_HANDLE_TYPE,
"channel-properties", immutable_properties,
"factory", factory,
NULL));
finally:
return ret;
}
static void
on_channel_request_returned (TpYtsChannel *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
if (error != NULL)
{
DEBUG ("Channel.Request failed: %s", error->message);
g_simple_async_result_set_from_error (res, error);
}
g_simple_async_result_complete_in_idle (res);
}
/**
* tp_yts_channel_request_async:
* @self: The channel proxy
* @cancellable: Not used
* @callback: Will be called when this operation completes
* @user_data: Data to pass to the callback
*
* Start an operation to send off a Ytstenut request on this newly created
* outgoing channel. This operation will fail on an incoming channel, or where
* the Ytstenut request has already been sent.
*/
void
tp_yts_channel_request_async (TpYtsChannel *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
g_return_if_fail (TP_IS_YTS_CHANNEL (self));
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
tp_yts_channel_request_async);
tp_yts_channel_call_request (self, -1, on_channel_request_returned, res,
g_object_unref, G_OBJECT (self));
}
/**
* tp_yts_channel_request_finish:
* @self: The channel proxy
* @result: The operation result
* @error: If not %NULL, used to raise an error when %FALSE is returned.
*
* Complete an operation to send off a Ytstenut request.
*
* Returns: %TRUE if the operation succeeded.
*/
gboolean
tp_yts_channel_request_finish (TpYtsChannel *self,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *res;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
res = G_SIMPLE_ASYNC_RESULT (result);
g_return_val_if_fail (g_simple_async_result_is_valid (result,
G_OBJECT (self), tp_yts_channel_request_async), FALSE);
if (g_simple_async_result_propagate_error (res, error))
return FALSE;
return TRUE;
}
static void
on_channel_reply_returned (TpYtsChannel *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
if (error != NULL)
{
DEBUG ("Channel.Reply failed: %s", error->message);
g_simple_async_result_set_from_error (res, error);
}
g_simple_async_result_complete_in_idle (res);
}
/**
* tp_yts_channel_reply_async:
* @self: The channel proxy
* @reply_attributes: A table of Ytstenut message attributes
* @reply_body: A UTF-8 encoded XML Ytstenut message, or %NULL
* @cancellable: Not used
* @callback: Will be called when this operation completes
* @user_data: Data to pass to the callback
*
* Start an operation to send a Ytstenut reply on this channel. This operation
* will fail if this is not an incoming channel, or if the already has had
* a reply or failure sent on it.
*/
void
tp_yts_channel_reply_async (TpYtsChannel *self,
GHashTable *reply_attributes,
const gchar *reply_body,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
g_return_if_fail (TP_IS_YTS_CHANNEL (self));
g_return_if_fail (reply_attributes != NULL);
if (!reply_body)
reply_body = "";
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
tp_yts_channel_reply_async);
tp_yts_channel_call_reply (self, -1, reply_attributes, reply_body,
on_channel_reply_returned, res, g_object_unref, G_OBJECT (self));
}
/**
* tp_yts_channel_reply_finish:
* @self: The channel proxy
* @result: The operation result
* @error: If not %NULL, used to raise an error when %FALSE is returned.
*
* Complete an operation to send a Ytstenut reply.
*
* Returns: %TRUE if the operation succeeded.
*/
gboolean
tp_yts_channel_reply_finish (TpYtsChannel *self,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *res;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
res = G_SIMPLE_ASYNC_RESULT (result);
g_return_val_if_fail (g_simple_async_result_is_valid (result,
G_OBJECT (self), tp_yts_channel_reply_async), FALSE);
if (g_simple_async_result_propagate_error (res, error))
return FALSE;
return TRUE;
}
static void
on_channel_fail_returned (TpYtsChannel *self,
const GError *error,
gpointer user_data,
GObject *weak_object)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
if (error != NULL)
{
DEBUG ("Channel.Fail failed: %s", error->message);
g_simple_async_result_set_from_error (res, error);
}
g_simple_async_result_complete_in_idle (res);
}
/**
* tp_yts_channel_fail_async:
* @self: The channel proxy
* @error_type: The Ytstenut error type.
* @stanza_error_name: The name of the error.
* @ytstenut_error_name: The Ytstenut specific error name.
* @error_text: Error message text.
* @cancellable: Not used
* @callback: Will be called when this operation completes
* @user_data: Data to pass to the callback
*
* Start an operation to send a Ytstenut failure on this channel. This operation
* will fail if this is not an incoming channel, or if the already has had
* a reply or failure sent on it.
*/
void
tp_yts_channel_fail_async (TpYtsChannel *self,
TpYtsErrorType error_type,
const gchar *stanza_error_name,
const gchar *ytstenut_error_name,
const gchar *error_text,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
g_return_if_fail (TP_IS_YTS_CHANNEL (self));
g_return_if_fail (stanza_error_name != NULL);
g_return_if_fail (ytstenut_error_name != NULL);
g_return_if_fail (error_text != NULL);
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
tp_yts_channel_fail_async);
tp_yts_channel_call_fail (self, -1, error_type, stanza_error_name,
ytstenut_error_name, error_text, on_channel_fail_returned,
res, g_object_unref, G_OBJECT (self));
}
/**
* tp_yts_channel_fail_finish:
* @self: The channel proxy
* @result: The operation result
* @error: If not %NULL, used to raise an error when %FALSE is returned.
*
* Complete an operation to send a Ytstenut failure.
*
* Returns: %TRUE if the operation succeeded.
*/
gboolean
tp_yts_channel_fail_finish (TpYtsChannel *self,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *res;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
res = G_SIMPLE_ASYNC_RESULT (result);
g_return_val_if_fail (g_simple_async_result_is_valid (result,
G_OBJECT (self), tp_yts_channel_fail_async), FALSE);
if (g_simple_async_result_propagate_error (res, error))
return FALSE;
return TRUE;
}
/**
* tp_yts_channel_get_request_type:
* @self: a #TpYtsChannel
*
*
*
* Returns: the same as the #TpYtsChannel:request-type property
*/
TpYtsRequestType
tp_yts_channel_get_request_type (TpYtsChannel *self)
{
TpYtsChannelPrivate *priv;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), 0);
priv = self->priv;
return priv->request_type;
}
/**
* tp_yts_channel_get_request_attributes:
* @self: a #TpYtsChannel
*
*
*
* Returns: the same as the #TpYtsChannel:request-attributes property,
* use g_hash_table_ref() to keep a reference to the returned hash
* table
*/
GHashTable *
tp_yts_channel_get_request_attributes (TpYtsChannel *self)
{
TpYtsChannelPrivate *priv;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), NULL);
priv = self->priv;
return priv->request_attributes;
}
/**
* tp_yts_channel_get_request_body:
* @self: a #TpYtsChannel
*
*
*
* Returns: the same as the #TpYtsChannel:request-body property
*/
const gchar *
tp_yts_channel_get_request_body (TpYtsChannel *self)
{
TpYtsChannelPrivate *priv;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), NULL);
priv = self->priv;
return priv->request_body;
}
/**
* tp_yts_channel_get_target_service:
* @self: a #TpYtsChannel
*
*
*
* Returns: the same as the #TpYtsChannel:target-service property
*/
const gchar *
tp_yts_channel_get_target_service (TpYtsChannel *self)
{
TpYtsChannelPrivate *priv;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), NULL);
priv = self->priv;
return priv->target_service;
}
/**
* tp_yts_channel_get_initiator-service:
* @self: a #TpYtsChannel
*
*
*
* Returns: the same as the #TpYtsChannel:initiator-service property
*/
const gchar *
tp_yts_channel_get_initiator_service (TpYtsChannel *self)
{
TpYtsChannelPrivate *priv;
g_return_val_if_fail (TP_IS_YTS_CHANNEL (self), 0);
priv = self->priv;
return priv->initiator_service;
}