/*
* Copyright (C) 2010 Xavier Claessens <xclaesse@gmail.com>
* Copyright (C) 2010 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "client-helpers.h"
typedef struct
{
GSocketConnection *connection;
TpChannel *channel;
} CreateTubeData;
static void
create_tube_data_free (CreateTubeData *data)
{
tp_clear_object (&data->connection);
tp_clear_object (&data->channel);
g_slice_free (CreateTubeData, data);
}
static void create_tube_channel_invalidated_cb (TpProxy *proxy, guint domain,
gint code, gchar *message, GSimpleAsyncResult *simple);
static void create_tube_incoming_cb (TpStreamTubeChannel *channel,
TpStreamTubeConnection *tube_connection, GSimpleAsyncResult *simple);
static void
create_tube_complete (GSimpleAsyncResult *simple, const GError *error)
{
CreateTubeData *data;
g_object_ref (simple);
data = g_simple_async_result_get_op_res_gpointer (simple);
if (data->channel != NULL)
{
g_signal_handlers_disconnect_by_func (data->channel,
create_tube_channel_invalidated_cb, simple);
g_signal_handlers_disconnect_by_func (data->channel,
create_tube_incoming_cb, simple);
}
if (error != NULL)
g_simple_async_result_set_from_error (simple, error);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
create_tube_channel_invalidated_cb (TpProxy *proxy,
guint domain,
gint code,
gchar *message,
GSimpleAsyncResult *simple)
{
create_tube_complete (simple,
tp_proxy_get_invalidated (proxy));
}
static void
create_tube_incoming_cb (TpStreamTubeChannel *channel,
TpStreamTubeConnection *tube_connection,
GSimpleAsyncResult *simple)
{
CreateTubeData *data;
data = g_simple_async_result_get_op_res_gpointer (simple);
data->connection = tp_stream_tube_connection_get_socket_connection (
tube_connection);
g_object_ref (data->connection);
create_tube_complete (simple, NULL);
}
static void
create_tube_offer_cb (GObject *object,
GAsyncResult *res,
gpointer user_data)
{
GSimpleAsyncResult *simple = user_data;
GError *error = NULL;
if (!tp_stream_tube_channel_offer_finish (TP_STREAM_TUBE_CHANNEL (object),
res, &error))
create_tube_complete (simple, error);
g_clear_error (&error);
g_object_unref (simple);
}
static void
create_channel_cb (GObject *acr,
GAsyncResult *res,
gpointer user_data)
{
GSimpleAsyncResult *simple = user_data;
CreateTubeData *data;
GError *error = NULL;
data = g_simple_async_result_get_op_res_gpointer (simple);
data->channel = tp_account_channel_request_create_and_handle_channel_finish (
TP_ACCOUNT_CHANNEL_REQUEST (acr), res, NULL, &error);
if (!TP_IS_STREAM_TUBE_CHANNEL (data->channel))
{
tp_clear_object (&data->channel);
if (error == NULL)
error = g_error_new_literal (TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
"Not supported channel type");
create_tube_complete (simple, error);
g_clear_error (&error);
g_object_unref (simple);
return;
}
g_signal_connect (data->channel, "invalidated",
G_CALLBACK (create_tube_channel_invalidated_cb), simple);
g_signal_connect_data (data->channel, "incoming",
G_CALLBACK (create_tube_incoming_cb),
g_object_ref (simple), (GClosureNotify) g_object_unref, 0);
tp_stream_tube_channel_offer_async (TP_STREAM_TUBE_CHANNEL (data->channel),
NULL, create_tube_offer_cb, g_object_ref (simple));
g_object_unref (simple);
}
void
_client_create_tube_async (TpAccount *account,
const gchar *contact_id,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
CreateTubeData *data;
GHashTable *request;
TpAccountChannelRequest *acr;
simple = g_simple_async_result_new (NULL, callback, user_data,
_client_create_tube_finish);
data = g_slice_new0 (CreateTubeData);
g_simple_async_result_set_op_res_gpointer (simple, data,
(GDestroyNotify) create_tube_data_free);
request = tp_asv_new (
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
TP_IFACE_CHANNEL_TYPE_STREAM_TUBE,
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
TP_HANDLE_TYPE_CONTACT,
TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING,
contact_id,
TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE, G_TYPE_STRING,
TUBE_SERVICE,
NULL);
acr = tp_account_channel_request_new (account, request, G_MAXINT64);
tp_account_channel_request_create_and_handle_channel_async (acr,
NULL, create_channel_cb, simple);
g_hash_table_unref (request);
g_object_unref (acr);
}
GSocketConnection *
_client_create_tube_finish (GAsyncResult *result,
TpChannel **channel,
GError **error)
{
GSimpleAsyncResult *simple;
CreateTubeData *data;
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
simple = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
_client_create_tube_finish), NULL);
data = g_simple_async_result_get_op_res_gpointer (
G_SIMPLE_ASYNC_RESULT (result));
if (channel != NULL)
*channel = g_object_ref (data->channel);
return g_object_ref (data->connection);
}
GSocket *
_client_create_local_socket (GError **error)
{
GSocket *socket = NULL;
GInetAddress * inet_address = NULL;
GSocketAddress *socket_address = NULL;
/* Create the IPv4 socket, and listen for connection on it */
socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT, error);
if (socket != NULL)
{
inet_address = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
socket_address = g_inet_socket_address_new (inet_address, 0);
g_socket_bind (socket, socket_address, FALSE, error);
}
tp_clear_object (&inet_address);
tp_clear_object (&socket_address);
return socket;
}
GStrv
_client_create_exec_args (GSocket *socket,
const gchar *contact_id,
const gchar *username,
gchar **ssh_opts)
{
GPtrArray *args;
GSocketAddress *socket_address;
GInetAddress *inet_address;
guint16 port;
gchar *host;
gchar *str;
gchar **opt;
/* Get the local host and port on which sshd is running */
socket_address = g_socket_get_local_address (socket, NULL);
inet_address = g_inet_socket_address_get_address (
G_INET_SOCKET_ADDRESS (socket_address));
port = g_inet_socket_address_get_port (
G_INET_SOCKET_ADDRESS (socket_address));
host = g_inet_address_to_string (inet_address);
/* Create ssh client args */
args = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (args, g_strdup ("ssh"));
g_ptr_array_add (args, host);
g_ptr_array_add (args, g_strdup ("-p"));
str = g_strdup_printf ("%d", port);
g_ptr_array_add (args, str);
if (contact_id != NULL)
{
str = g_strdup_printf ("-oHostKeyAlias=%s", contact_id);
g_ptr_array_add (args, str);
}
if (!tp_str_empty (username))
{
g_ptr_array_add (args, g_strdup ("-l"));
g_ptr_array_add (args, g_strdup (username));
}
if (ssh_opts != NULL)
{
for (opt = ssh_opts; *opt != NULL; opt++)
{
g_ptr_array_add (args, g_strdup (*opt));
}
}
g_ptr_array_add (args, NULL);
return (gchar **) g_ptr_array_free (args, FALSE);
}
gboolean
_capabilities_has_stream_tube (TpCapabilities *caps)
{
if (caps == NULL)
return FALSE;
return tp_capabilities_supports_stream_tubes (caps, TP_HANDLE_TYPE_CONTACT,
TUBE_SERVICE);
}