diff options
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/client-helpers.c | 460 | ||||
-rw-r--r-- | src/client-helpers.h | 44 | ||||
-rw-r--r-- | src/client.c | 460 | ||||
-rw-r--r-- | src/common.c | 1 | ||||
-rw-r--r-- | src/common.h | 1 |
6 files changed, 575 insertions, 392 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 53f1af1..b3194b0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,7 @@ libexec_PROGRAMS = ssh-contact-service ssh_contact_SOURCES = \ common.c common.h \ + client-helpers.c client-helpers.h \ client.c ssh_contact_service_SOURCES = \ diff --git a/src/client-helpers.c b/src/client-helpers.c new file mode 100644 index 0000000..57a2d13 --- /dev/null +++ b/src/client-helpers.c @@ -0,0 +1,460 @@ +/* + * 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 <stdlib.h> +#include <glib/gstdio.h> +#include <gio/gunixsocketaddress.h> + +#include "client-helpers.h" +#include "common.h" + +typedef struct +{ + GSimpleAsyncResult *result; + TpBaseClient *client; + gchar *unix_path; +} CreateTubeData; + +static void +create_tube_complete (CreateTubeData *data, + const GError *error) +{ + if (error != NULL) + g_simple_async_result_set_from_error (data->result, error); + + g_simple_async_result_complete_in_idle (data->result); + + tp_clear_object (&data->result); + tp_clear_object (&data->client); + + g_slice_free (CreateTubeData, data); +} + +static void +unix_path_destroy (gchar *unix_path) +{ + if (unix_path != NULL) + { + gchar *p; + + g_unlink (unix_path); + p = g_strrstr (unix_path, G_DIR_SEPARATOR_S); + *p = '\0'; + g_rmdir (unix_path); + g_free (unix_path); + } +} + +static void +create_tube_socket_connected_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CreateTubeData *data = user_data; + GSocketListener *listener = G_SOCKET_LISTENER (source_object); + GSocketConnection *connection; + GError *error = NULL; + + connection = g_socket_listener_accept_finish (listener, res, NULL, &error); + + if (connection != NULL) + { + /* Transfer ownership of connection */ + g_simple_async_result_set_op_res_gpointer (data->result, connection, + g_object_unref); + + /* Transfer ownership of unix path */ + g_object_set_data_full (G_OBJECT (connection), "unix-path", + data->unix_path, (GDestroyNotify) unix_path_destroy); + data->unix_path = NULL; + } + + create_tube_complete (data, error); + + g_clear_error (&error); +} + +static void +create_tube_offer_cb (TpChannel *channel, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + CreateTubeData *data = user_data; + + if (error != NULL) + create_tube_complete (data, error); +} + +static void +create_tube_channel_invalidated_cb (TpProxy *proxy, + guint domain, + gint code, + gchar *message, + CreateTubeData *data) +{ + create_tube_complete (data, tp_proxy_get_invalidated (proxy)); +} + +static void +create_tube_handle_channel (CreateTubeData *data, + TpChannel *channel) +{ + GSocketListener *listener = NULL; + gchar *dir; + GSocket *socket = NULL; + GSocketAddress *socket_address = NULL; + GValue *address; + GHashTable *parameters; + GError *error = NULL; + + g_signal_connect (channel, "invalidated", + G_CALLBACK (create_tube_channel_invalidated_cb), data); + + /* We are client side, but we have to offer a socket... So we offer an unix + * socket on which the service side can connect. We also create an IPv4 socket + * on which the ssh client can connect. When both sockets are connected, + * we can forward all communications between them. */ + + listener = g_socket_listener_new (); + + /* Create temporary file for our unix socket */ + dir = g_build_filename (g_get_tmp_dir (), "telepathy-ssh-XXXXXX", NULL); + dir = mkdtemp (dir); + data->unix_path = g_build_filename (dir, "unix-socket", NULL); + g_free (dir); + + /* Create the unix socket, and listen for connection on it */ + socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, &error); + if (socket == NULL) + goto OUT; + socket_address = g_unix_socket_address_new (data->unix_path); + if (!g_socket_bind (socket, socket_address, FALSE, &error)) + goto OUT; + if (!g_socket_listen (socket, &error)) + goto OUT; + if (!g_socket_listener_add_socket (listener, socket, NULL, &error)) + goto OUT; + + g_socket_listener_accept_async (listener, NULL, + create_tube_socket_connected_cb, data); + + /* Offer the socket */ + address = tp_address_variant_from_g_socket_address (socket_address, + TP_SOCKET_ADDRESS_TYPE_UNIX, &error); + if (address == NULL) + goto OUT; + parameters = g_hash_table_new (NULL, NULL); + tp_cli_channel_type_stream_tube_call_offer (channel, -1, + TP_SOCKET_ADDRESS_TYPE_UNIX, address, + TP_SOCKET_ACCESS_CONTROL_LOCALHOST, parameters, + create_tube_offer_cb, data, NULL, NULL); + tp_g_value_slice_free (address); + g_hash_table_unref (parameters); + +OUT: + + if (error != NULL) + create_tube_complete (data, error); + + tp_clear_object (&listener); + tp_clear_object (&socket); + tp_clear_object (&socket_address); + g_clear_error (&error); +} + +static void +create_tube_got_channel_cb (TpSimpleHandler *handler, + TpAccount *account, + TpConnection *connection, + GList *channels, + GList *requests_satisfied, + gint64 user_action_time, + TpHandleChannelsContext *context, + gpointer user_data) +{ + CreateTubeData *data = user_data; + GList *l; + + for (l = channels; l != NULL; l = l->next) + { + TpChannel *channel = l->data; + + if (!tp_strdiff (tp_channel_get_channel_type (channel), + TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) + { + create_tube_handle_channel (data, channel); + break; + } + } + + tp_handle_channels_context_accept (context); +} + +static void +create_tube_channel_request_invalidated_cb (TpProxy *proxy, + guint domain, + gint code, + gchar *message, + CreateTubeData *data) +{ + const GError *error; + + error = tp_proxy_get_invalidated (proxy); + if (!g_error_matches (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED)) + create_tube_complete (data, error); + + g_object_unref (proxy); +} + +static void +create_tube_request_proceed_cb (TpChannelRequest *request, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + CreateTubeData *data = user_data; + + if (error != NULL) + { + create_tube_complete (data, error); + return; + } +} + +static void +create_tube_channel_cb (TpChannelDispatcher *dispatcher, + const gchar *request_path, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + CreateTubeData *data = user_data; + TpDBusDaemon *dbus; + TpChannelRequest *request; + GError *err = NULL; + + if (error != NULL) + { + create_tube_complete (data, error); + return; + } + + dbus = tp_proxy_get_dbus_daemon (TP_PROXY (dispatcher)); + request = tp_channel_request_new (dbus, request_path, NULL, &err); + if (request == NULL) + { + create_tube_complete (data, err); + g_clear_error (&err); + return; + } + + g_signal_connect (request, "invalidated", + G_CALLBACK (create_tube_channel_request_invalidated_cb), data); + + tp_cli_channel_request_call_proceed (request, -1, + create_tube_request_proceed_cb, data, NULL, NULL); +} + +void +_client_create_tube_async (const gchar *account_path, + const gchar *contact_id, + GAsyncReadyCallback callback, + gpointer user_data) +{ + CreateTubeData *data; + TpDBusDaemon *dbus = NULL; + TpChannelDispatcher *dispatcher = NULL; + GHashTable *request = NULL; + GError *error = NULL; + + dbus = tp_dbus_daemon_dup (NULL); + + data = g_slice_new0 (CreateTubeData); + data->result = g_simple_async_result_new (NULL, callback, user_data, + _client_create_tube_finish); + data->client = tp_simple_handler_new (dbus, FALSE, FALSE, + "SSHContactClient", TRUE, create_tube_got_channel_cb, data, NULL); + + tp_base_client_take_handler_filter (data->client, 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_TYPE_STREAM_TUBE_SERVICE, G_TYPE_STRING, + TUBE_SERVICE, + TP_PROP_CHANNEL_REQUESTED, G_TYPE_BOOLEAN, + TRUE, + NULL)); + + if (!tp_base_client_register (data->client, &error)) + { + create_tube_complete (data, error); + g_clear_error (&error); + g_object_unref (dbus); + return; + } + + dispatcher = tp_channel_dispatcher_new (dbus); + 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); + + tp_cli_channel_dispatcher_call_create_channel (dispatcher, -1, + account_path, request, G_MAXINT64, + tp_base_client_get_bus_name (data->client), + create_tube_channel_cb, data, NULL, NULL); + + g_object_unref (dispatcher); + g_hash_table_unref (request); +} + +GSocketConnection * +_client_create_tube_finish (GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + 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); + + return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); +} + +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) +{ + GPtrArray *args; + GSocketAddress *socket_address; + GInetAddress *inet_address; + guint16 port; + gchar *host; + gchar *str; + + /* 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 (username != NULL) + { + g_ptr_array_add (args, g_strdup ("-l")); + g_ptr_array_add (args, g_strdup (username)); + } + + g_ptr_array_add (args, NULL); + + return (gchar **) g_ptr_array_free (args, FALSE); +} + +gboolean +_capabilities_has_stream_tube (TpCapabilities *caps) +{ + GPtrArray *classes; + guint i; + + if (caps == NULL) + return FALSE; + + classes = tp_capabilities_get_channel_classes (caps); + for (i = 0; i < classes->len; i++) + { + GValueArray *arr = g_ptr_array_index (classes, i); + GHashTable *fixed; + const gchar *chan_type; + const gchar *service; + TpHandleType handle_type; + + fixed = g_value_get_boxed (g_value_array_get_nth (arr, 0)); + chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); + service = tp_asv_get_string (fixed, + TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); + handle_type = tp_asv_get_uint32 (fixed, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); + + if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) && + handle_type == TP_HANDLE_TYPE_CONTACT && + (!tp_capabilities_is_specific_to_contact (caps) || + !tp_strdiff (service, TUBE_SERVICE))) + return TRUE; + } + + return FALSE; +} diff --git a/src/client-helpers.h b/src/client-helpers.h new file mode 100644 index 0000000..01af16a --- /dev/null +++ b/src/client-helpers.h @@ -0,0 +1,44 @@ +/* + * 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 + */ + +#ifndef __CLIENT_HELPERS_H__ +#define __CLIENT_HELPERS_H__ + +#include <gio/gio.h> +#include <telepathy-glib/telepathy-glib.h> + +G_BEGIN_DECLS + +void _client_create_tube_async (const gchar *account_path, + const gchar *contact_id, GAsyncReadyCallback callback, gpointer user_data); + +GSocketConnection *_client_create_tube_finish (GAsyncResult *res, + GError **error); + +GSocket * _client_create_local_socket (GError **error); + +GStrv _client_create_exec_args (GSocket *socket, + const gchar *contact_id, const gchar *username); + +gboolean _capabilities_has_stream_tube (TpCapabilities *caps); + +G_END_DECLS + +#endif /* #ifndef __CLIENT_HELPERS_H__*/ diff --git a/src/client.c b/src/client.c index fdbdaeb..9c49a00 100644 --- a/src/client.c +++ b/src/client.c @@ -28,23 +28,21 @@ #include <gio/gunixsocketaddress.h> #include <telepathy-glib/telepathy-glib.h> +#include "client-helpers.h" #include "common.h" typedef struct { GMainLoop *loop; - gchar *account_id; + gchar *account_path; gchar *contact_id; gchar *login; - TpBaseClient *client; - GList *accounts; guint n_readying_connections; TpAccount *account; - gchar *unix_path; GSocketConnection *tube_connection; GSocketConnection *ssh_connection; @@ -68,6 +66,17 @@ throw_error (ClientContext *context, } static void +ssh_client_watch_cb (GPid pid, + gint status, + gpointer user_data) +{ + ClientContext *context = user_data; + + g_main_loop_quit (context->loop); + g_spawn_close_pid (pid); +} + +static void splice_cb (GIOStream *stream1, GIOStream *stream2, const GError *error, @@ -82,6 +91,17 @@ splice_cb (GIOStream *stream1, } static void +maybe_start_splice (ClientContext *context) +{ + if (context->tube_connection != NULL && context->ssh_connection != NULL) + { + /* Splice tube and ssh connections */ + _g_io_stream_splice (G_IO_STREAM (context->tube_connection), + G_IO_STREAM (context->ssh_connection), splice_cb, context); + } +} + +static void ssh_socket_connected_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) @@ -92,386 +112,79 @@ ssh_socket_connected_cb (GObject *source_object, context->ssh_connection = g_socket_listener_accept_finish (listener, res, NULL, &error); - if (context->ssh_connection == NULL) + if (error != NULL) { throw_error (context, error); + g_clear_error (&error); return; } - /* Splice tube and ssh connections */ - _g_io_stream_splice (G_IO_STREAM (context->tube_connection), - G_IO_STREAM (context->ssh_connection), splice_cb, context); + maybe_start_splice (context); } static void -ssh_client_watch_cb (GPid pid, - gint status, +create_tube_cb (GObject *source_object, + GAsyncResult *res, gpointer user_data) { ClientContext *context = user_data; - - g_main_loop_quit (context->loop); - g_spawn_close_pid (pid); -} - -static void -exec_ssh_on_socket (ClientContext *context, - GSocket *socket) -{ - GSocketAddress *socket_address; - GInetAddress *inet_address; GError *error = NULL; - guint16 port; - gchar *host; - GPtrArray *args; - gchar *str; - GPid pid; - /* Get the local host and port on which sshd is running */ - socket_address = g_socket_get_local_address (socket, &error); - if (socket_address == NULL) + context->tube_connection = _client_create_tube_finish (res, &error); + if (error != NULL) { throw_error (context, error); g_clear_error (&error); return; } - 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 (context->login != NULL) - { - g_ptr_array_add (args, g_strdup ("-l")); - g_ptr_array_add (args, g_strdup (context->login)); - } - - str = g_strdup_printf ("-oHostKeyAlias=%s", context->contact_id); - g_ptr_array_add (args, str); - g_ptr_array_add (args, NULL); - - /* spawn ssh client */ - if (g_spawn_async (NULL, (gchar **) args->pdata, NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN | - G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) - { - g_child_watch_add (pid, ssh_client_watch_cb, context); - } - else - { - throw_error (context, error); - g_clear_error (&error); - } - - g_ptr_array_unref (args); + maybe_start_splice (context); } static void -tube_socket_connected_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +start_tube (ClientContext *context) { - ClientContext *context = user_data; - GSocketListener *listener = G_SOCKET_LISTENER (source_object); - GSocket *socket = NULL; - GInetAddress * inet_address = NULL; - GSocketAddress *socket_address = NULL; - GError *error = NULL; - - context->tube_connection = g_socket_listener_accept_finish (listener, res, - NULL, &error); - if (context->tube_connection == NULL) - goto OUT; - - /* 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) - goto OUT; - inet_address = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); - socket_address = g_inet_socket_address_new (inet_address, 0); - if (!g_socket_bind (socket, socket_address, FALSE, &error)) - goto OUT; - if (!g_socket_listen (socket, &error)) - goto OUT; - if (!g_socket_listener_add_socket (listener, socket, NULL, &error)) - goto OUT; - - g_socket_listener_accept_async (listener, NULL, - ssh_socket_connected_cb, context); - - exec_ssh_on_socket (context, socket); - -OUT: - - if (error != NULL) - throw_error (context, error); - - tp_clear_object (&socket); - tp_clear_object (&inet_address); - tp_clear_object (&socket_address); - g_clear_error (&error); -} - -static void -offer_tube_cb (TpChannel *channel, - const GError *error, - gpointer user_data, - GObject *weak_object) -{ - ClientContext *context = user_data; - - if (error != NULL) - throw_error (context, error); -} - -static void -channel_invalidated_cb (TpProxy *proxy, - guint domain, - gint code, - gchar *message, - ClientContext *context) -{ - const GError *error; - - error = tp_proxy_get_invalidated (proxy); - throw_error (context, error); -} - -static void -handle_channel (ClientContext *context, - TpChannel *channel) -{ - gchar *dir; - GSocketListener *listener = NULL; - GSocket *socket = NULL; - GSocketAddress *socket_address = NULL; - GValue *address; - GHashTable *parameters; + GSocketListener *listener; + GSocket *socket; GError *error = NULL; - - g_signal_connect (channel, "invalidated", - G_CALLBACK (channel_invalidated_cb), context); - - /* We are client side, but we have to offer a socket... So we offer an unix - * socket on which the service side can connect. We also create an IPv4 socket - * on which the ssh client can connect. When both sockets are connected, - * we can forward all communications between them. */ - - /* FIXME: I don't think we close socket connections, or cancel - * g_socket_listener_accept_async in all error cases... */ + GStrv args = NULL; + GPid pid; listener = g_socket_listener_new (); - - /* Create temporary file for our unix socket */ - dir = g_build_filename (g_get_tmp_dir (), "telepathy-ssh-XXXXXX", NULL); - dir = mkdtemp (dir); - context->unix_path = g_build_filename (dir, "unix-socket", NULL); - g_free (dir); - - /* Create the unix socket, and listen for connection on it */ - socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, &error); + socket = _client_create_local_socket (&error); if (socket == NULL) goto OUT; - socket_address = g_unix_socket_address_new (context->unix_path); - if (!g_socket_bind (socket, socket_address, FALSE, &error)) - goto OUT; if (!g_socket_listen (socket, &error)) goto OUT; if (!g_socket_listener_add_socket (listener, socket, NULL, &error)) goto OUT; g_socket_listener_accept_async (listener, NULL, - tube_socket_connected_cb, context); - - /* Offer the socket */ - address = tp_address_variant_from_g_socket_address (socket_address, - TP_SOCKET_ADDRESS_TYPE_UNIX, &error); - if (address == NULL) - goto OUT; - parameters = g_hash_table_new (NULL, NULL); - tp_cli_channel_type_stream_tube_call_offer (channel, -1, - TP_SOCKET_ADDRESS_TYPE_UNIX, address, - TP_SOCKET_ACCESS_CONTROL_LOCALHOST, parameters, - offer_tube_cb, context, NULL, NULL); - tp_g_value_slice_free (address); - g_hash_table_unref (parameters); - -OUT: - - if (error != NULL) - throw_error (context, error); - - tp_clear_object (&listener); - tp_clear_object (&socket); - tp_clear_object (&socket_address); - g_clear_error (&error); -} + ssh_socket_connected_cb, context); -static void -got_channel_cb (TpSimpleHandler *handler, - TpAccount *account, - TpConnection *connection, - GList *channels, - GList *requests_satisfied, - gint64 user_action_time, - TpHandleChannelsContext *channel_context, - gpointer user_data) -{ - ClientContext *context = user_data; - GList *l; + args = _client_create_exec_args (socket, context->contact_id, + context->login); - for (l = channels; l != NULL; l = l->next) + /* spawn ssh client */ + if (g_spawn_async (NULL, args, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN | + G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) { - TpChannel *channel = l->data; - - if (tp_strdiff (tp_channel_get_channel_type (channel), - TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) - continue; - - handle_channel (context, channel); + g_child_watch_add (pid, ssh_client_watch_cb, context); } - tp_handle_channels_context_accept (channel_context); -} + _client_create_tube_async (context->account_path, + context->contact_id, create_tube_cb, context); -static void -channel_request_invalidated_cb (TpProxy *proxy, - guint domain, - gint code, - gchar *message, - ClientContext *context) -{ - const GError *error; - - error = tp_proxy_get_invalidated (proxy); - if (!g_error_matches (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED)) - throw_error (context, error); - - g_object_unref (proxy); -} - -static void -request_proceed_cb (TpChannelRequest *request, - const GError *error, - gpointer user_data, - GObject *weak_object) -{ - ClientContext *context = user_data; +OUT: if (error != NULL) throw_error (context, error); -} - -static void -create_channel_cb (TpChannelDispatcher *dispatcher, - const gchar *request_path, - const GError *error, - gpointer user_data, - GObject *weak_object) -{ - ClientContext *context = user_data; - TpDBusDaemon *dbus; - TpChannelRequest *request; - GError *err = NULL; - if (error != NULL) - { - throw_error (context, error); - return; - } - - dbus = tp_proxy_get_dbus_daemon (TP_PROXY (dispatcher)); - request = tp_channel_request_new (dbus, request_path, NULL, &err); - if (request == NULL) - { - throw_error (context, err); - g_clear_error (&err); - return; - } - - g_signal_connect (request, "invalidated", - G_CALLBACK (channel_request_invalidated_cb), context); - - tp_cli_channel_request_call_proceed (request, -1, request_proceed_cb, - context, NULL, NULL); -} - -static void -request_channel (ClientContext *context) -{ - TpDBusDaemon *dbus = NULL; - TpChannelDispatcher *dispatcher = NULL; - GHashTable *request = NULL; - - dbus = tp_dbus_daemon_dup (NULL); - dispatcher = tp_channel_dispatcher_new (dbus); - 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, - context->contact_id, - TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE, G_TYPE_STRING, - TUBE_SERVICE, - NULL); - - tp_cli_channel_dispatcher_call_create_channel (dispatcher, -1, - tp_proxy_get_object_path (TP_PROXY (context->account)), request, - G_MAXINT64, tp_base_client_get_bus_name (context->client), - create_channel_cb, context, NULL, NULL); - - g_object_unref (dbus); - g_object_unref (dispatcher); - g_hash_table_unref (request); -} - -static gboolean -has_stream_tube_cap (TpCapabilities *caps) -{ - GPtrArray *classes; - guint i; - - if (caps == NULL) - return FALSE; - - classes = tp_capabilities_get_channel_classes (caps); - for (i = 0; i < classes->len; i++) - { - GValueArray *arr = g_ptr_array_index (classes, i); - GHashTable *fixed; - const gchar *chan_type; - const gchar *service; - TpHandleType handle_type; - - fixed = g_value_get_boxed (g_value_array_get_nth (arr, 0)); - chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); - service = tp_asv_get_string (fixed, - TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); - handle_type = tp_asv_get_uint32 (fixed, - TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); - - if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) && - handle_type == TP_HANDLE_TYPE_CONTACT && - (!tp_capabilities_is_specific_to_contact (caps) || - !tp_strdiff (service, TUBE_SERVICE))) - return TRUE; - } - - return FALSE; + g_clear_error (&error); + tp_clear_object (&listener); + tp_clear_object (&socket); + g_strfreev (args); } static void @@ -499,7 +212,7 @@ got_contacts_cb (TpConnection *connection, /* Build a list of all contacts supporting StreamTube */ for (i = 0; i < n_contacts; i++) - if (has_stream_tube_cap (tp_contact_get_capabilities (contacts[i]))) + if (_capabilities_has_stream_tube (tp_contact_get_capabilities (contacts[i]))) candidates = g_list_prepend (candidates, contacts[i]); if (candidates == NULL) @@ -531,7 +244,7 @@ got_contacts_cb (TpConnection *connection, } context->contact_id = g_strdup (tp_contact_get_identifier (l->data)); - request_channel (context); + start_tube (context); g_list_free (candidates); } @@ -611,7 +324,7 @@ chooser_contact (ClientContext *context) /* If a contact ID was passed in the options, use it */ if (context->contact_id != NULL) - request_channel (context); + start_tube (context); /* Otherwise, we'll get TpContact objects for all stored contacts on that * account. */ @@ -668,15 +381,11 @@ chooser_account (ClientContext *context) /* If the account id was not in the options, print it now. It makes easier to * copy/paste later. */ - if (context->account_id == NULL) + if (context->account_path == NULL) { - const gchar *account_path; - - account_path = tp_proxy_get_object_path (context->account); - context->account_id = g_strdup (account_path + - strlen (TP_ACCOUNT_OBJECT_PATH_BASE)); - - g_print ("Going to use account: '%s'\n", context->account_id); + context->account_path = g_strdup (tp_proxy_get_object_path (context->account)); + g_print ("Going to use account: '%s'\n", + context->account_path + strlen (TP_ACCOUNT_OBJECT_PATH_BASE)); } chooser_contact (context); @@ -691,7 +400,7 @@ connection_prepare_cb (GObject *object, ClientContext *context = user_data; if (!tp_proxy_prepare_finish (TP_PROXY (connection), res, NULL) || - !has_stream_tube_cap (tp_connection_get_capabilities (connection))) + !_capabilities_has_stream_tube (tp_connection_get_capabilities (connection))) { GList *l; @@ -797,26 +506,14 @@ static void client_context_clear (ClientContext *context) { tp_clear_pointer (&context->loop, g_main_loop_unref); - g_free (context->account_id); + g_free (context->account_path); g_free (context->contact_id); g_free (context->login); - tp_clear_object (&context->client); - g_list_foreach (context->accounts, (GFunc) g_object_unref, NULL); g_list_free (context->accounts); tp_clear_object (&context->account); - if (context->unix_path != NULL) - { - gchar *p; - - g_unlink (context->unix_path); - p = g_strrstr (context->unix_path, G_DIR_SEPARATOR_S); - *p = '\0'; - g_rmdir (context->unix_path); - g_free (context->unix_path); - } tp_clear_object (&context->tube_connection); tp_clear_object (&context->ssh_connection); } @@ -827,10 +524,11 @@ main (gint argc, gchar *argv[]) TpDBusDaemon *dbus = NULL; GError *error = NULL; ClientContext context = { 0, }; + gchar *account_id = NULL; GOptionContext *optcontext; GOptionEntry options[] = { { "account", 'a', - 0, G_OPTION_ARG_STRING, &context.account_id, + 0, G_OPTION_ARG_STRING, &account_id, "The account ID", NULL }, { "contact", 'c', @@ -863,39 +561,18 @@ main (gint argc, gchar *argv[]) if (dbus == NULL) goto OUT; - context.client = tp_simple_handler_new (dbus, FALSE, FALSE, - "SSHContactClient", TRUE, got_channel_cb, &context, NULL); - - tp_base_client_take_handler_filter (context.client, 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_TYPE_STREAM_TUBE_SERVICE, G_TYPE_STRING, - "ssh", - TP_PROP_CHANNEL_REQUESTED, G_TYPE_BOOLEAN, - TRUE, - NULL)); - - if (!tp_base_client_register (context.client, &error)) - goto OUT; - /* If an account id was specified in options, then prepare it, otherwise * we get the account manager to get a list of all accounts */ - if (context.account_id != NULL) + if (account_id != NULL) { - gchar *account_path; - - account_path = g_strconcat (TP_ACCOUNT_OBJECT_PATH_BASE, - context.account_id, NULL); - context.account = tp_account_new (dbus, account_path, &error); + context.account_path = g_strconcat (TP_ACCOUNT_OBJECT_PATH_BASE, + account_id, NULL); + context.account = tp_account_new (dbus, context.account_path, &error); if (context.account == NULL) goto OUT; tp_proxy_prepare_async (TP_PROXY (context.account), NULL, account_prepare_cb, &context); - - g_free (account_path); } else { @@ -922,6 +599,7 @@ OUT: tp_clear_object (&dbus); g_clear_error (&error); client_context_clear (&context); + g_free (account_id); return context.success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/common.c b/src/common.c index a13e10c..401b098 100644 --- a/src/common.c +++ b/src/common.c @@ -118,4 +118,3 @@ _g_io_stream_splice (GIOStream *stream1, splice_context_unref (ctx); } - diff --git a/src/common.h b/src/common.h index 643ebf1..5df5f8d 100644 --- a/src/common.h +++ b/src/common.h @@ -22,6 +22,7 @@ #define __COMMON_H__ #include <gio/gio.h> +#include <telepathy-glib/telepathy-glib.h> #define TUBE_SERVICE "x-ssh-contact" |