Remmina - The GTK+ Remote Desktop Client  v1.4.33
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
telepathy_channel_handler.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include "common/remmina_plugin.h"
38 #include <telepathy-glib/account.h>
39 #include <telepathy-glib/channel.h>
40 #include <telepathy-glib/contact.h>
41 #include <telepathy-glib/dbus.h>
42 #include <telepathy-glib/defs.h>
43 #include <telepathy-glib/handle.h>
44 #include <telepathy-glib/interfaces.h>
45 #include <telepathy-glib/svc-client.h>
46 #include <telepathy-glib/util.h>
48 
50 
51 typedef struct _RemminaTpChannelHandler {
53  gchar *channel_path;
54  GHashTable *channel_properties;
55  DBusGMethodInvocation *context;
56 
57  GtkWidget *proto_widget;
59 
60  TpDBusDaemon *bus;
61  TpAccount *account;
62  TpConnection *connection;
63  TpChannel *channel;
64 
65  gchar *alias;
66  gchar *host;
67  guint port;
68  gchar *protocol;
70 
72 {
73  TRACE_CALL(__func__);
74  if (chandler->disconnect_handler) {
75  g_signal_handler_disconnect(chandler->proto_widget, chandler->disconnect_handler);
76  chandler->disconnect_handler = 0;
77  }
78  g_free(chandler->connection_path);
79  g_free(chandler->channel_path);
80  g_hash_table_destroy(chandler->channel_properties);
81  if (chandler->bus) {
82  g_object_unref(chandler->bus);
83  }
84  if (chandler->account) {
85  g_object_unref(chandler->account);
86  }
87  if (chandler->connection) {
88  g_object_unref(chandler->connection);
89  }
90  if (chandler->channel) {
91  g_object_unref(chandler->channel);
92  }
93  if (chandler->alias) {
94  g_free(chandler->alias);
95  }
96  if (chandler->host) {
97  g_free(chandler->host);
98  }
99  if (chandler->protocol) {
100  g_free(chandler->protocol);
101  }
102  g_free(chandler);
103 }
104 
105 static void remmina_tp_channel_handler_channel_closed(TpChannel *channel, gpointer user_data, GObject *self)
106 {
107  TRACE_CALL(__func__);
108  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
109 
110  g_print("%s: %s\n", __func__, chandler->channel_path);
112 }
113 
114 static void remmina_tp_channel_handler_on_disconnect(GtkWidget *widget, RemminaTpChannelHandler *chandler)
115 {
116  TRACE_CALL(__func__);
117  g_print("%s: %s\n", __func__, chandler->channel_path);
118  g_signal_handler_disconnect(widget, chandler->disconnect_handler);
119  chandler->disconnect_handler = 0;
120  tp_cli_channel_call_close(chandler->channel, -1, NULL, NULL, NULL, NULL);
121 }
122 
124 {
125  TRACE_CALL(__func__);
126  RemminaFile *remminafile;
127  gchar *s;
128 
129  remminafile = remmina_plugin_telepathy_service->file_new();
130  remmina_plugin_telepathy_service->file_set_string(remminafile, "name", chandler->alias);
131  remmina_plugin_telepathy_service->file_set_string(remminafile, "protocol", chandler->protocol);
132  s = g_strdup_printf("[%s]:%i", chandler->host, chandler->port);
133  remmina_plugin_telepathy_service->file_set_string(remminafile, "server", s);
134  g_free(s);
135  remmina_plugin_telepathy_service->file_set_int(remminafile, "colordepth", 8);
136 
137  g_free(chandler->alias);
138  chandler->alias = NULL;
139  g_free(chandler->protocol);
140  chandler->protocol = NULL;
141 
142  chandler->proto_widget = remmina_plugin_telepathy_service->open_connection(remminafile,
143  G_CALLBACK(remmina_tp_channel_handler_on_disconnect), chandler, &chandler->disconnect_handler);
144 }
145 
146 static void remmina_tp_channel_handler_get_service(TpProxy *channel, const GValue *service, const GError *error,
147  gpointer user_data, GObject *weak_object)
148 {
149  TRACE_CALL(__func__);
150  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
151  const gchar *svc;
152 
153  if (error != NULL) {
154  g_print("%s: %s", __func__, error->message);
156  return;
157  }
158  svc = g_value_get_string(service);
159  g_print("%s: %s %s:%u\n", __func__, svc, chandler->host, chandler->port);
160 
161  if (g_strcmp0(svc, "rfb") == 0) {
162  chandler->protocol = g_strdup("VNC");
163  }else {
164  chandler->protocol = g_ascii_strup(svc, -1);
165  }
167 }
168 
169 static void remmina_tp_channel_handler_accept(TpChannel *channel, const GValue *address, const GError *error,
170  gpointer user_data, GObject *weak_object)
171 {
172  TRACE_CALL(__func__);
173  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
174 
175  if (error != NULL) {
176  g_print("%s: %s", __func__, error->message);
178  return;
179  }
180 
181  dbus_g_type_struct_get(address, 0, &chandler->host, 1, &chandler->port, G_MAXUINT);
182 
183  tp_cli_dbus_properties_call_get(channel, -1, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, "Service",
184  remmina_tp_channel_handler_get_service, chandler, NULL, NULL);
185 }
186 
187 static void remmina_tp_channel_handler_on_response(GtkDialog *dialog, gint response_id, RemminaTpChannelHandler *chandler)
188 {
189  TRACE_CALL(__func__);
190  GValue noop =
191  { 0 };
192  GError *error;
193 
194  if (response_id == GTK_RESPONSE_YES) {
195  g_value_init(&noop, G_TYPE_INT);
196  tp_cli_channel_type_stream_tube_call_accept(chandler->channel, -1, TP_SOCKET_ADDRESS_TYPE_IPV4,
197  TP_SOCKET_ACCESS_CONTROL_LOCALHOST, &noop, remmina_tp_channel_handler_accept, chandler, NULL,
198  NULL);
199  g_value_unset(&noop);
200  tp_svc_client_handler_return_from_handle_channels(chandler->context);
201  }else {
202  error = g_error_new(TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Channel rejected by user.");
203  dbus_g_method_return_error(chandler->context, error);
204  g_error_free(error);
206  }
207 }
208 
209 static void remmina_tp_channel_handler_get_contacts(TpConnection *connection, guint n_contacts, TpContact * const *contacts,
210  guint n_failed, const TpHandle *failed, const GError *error, gpointer user_data, GObject *weak_object)
211 {
212  TRACE_CALL(__func__);
213  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
214  TpContact *contact;
215  gchar *token;
216  const gchar *cm;
217  const gchar *protocol;
218  gchar *filename;
219  GdkPixbuf *pixbuf;
220  GtkWidget *image;
221  GtkWidget *dialog;
222 
223  if (error != NULL) {
224  g_print("%s: %s", __func__, error->message);
226  return;
227  }
228  if (n_contacts <= 0) {
229  g_print("%s: no contacts\n", __func__);
231  return;
232  }
233  contact = contacts[0];
234  chandler->alias = g_strdup(tp_contact_get_alias(contact));
235 
236  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
237  _("%s wants to share their desktop.\nDo you accept?"), chandler->alias);
238  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(remmina_tp_channel_handler_on_response), chandler);
239  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
240  gtk_window_set_title(GTK_WINDOW(dialog), _("Desktop sharing invitation"));
241  remmina_plugin_telepathy_service->ui_register(dialog);
242  gtk_widget_show(dialog);
243 
244  token = (gchar*)tp_contact_get_avatar_token(contact);
245  if (token == NULL) {
246  return;
247  }
248  protocol = tp_connection_get_protocol_name(chandler->connection);
249  cm = tp_connection_get_cm_name(chandler->connection);
250  if (!protocol || !cm) {
251  return;
252  }
253  token = tp_escape_as_identifier(token);
254  filename = g_build_filename(g_get_user_cache_dir(), "telepathy", "avatars", cm, protocol, token, NULL);
255  g_free(token);
256  if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
257  pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
258  if (pixbuf) {
259  image = gtk_image_new_from_pixbuf(pixbuf);
260  gtk_widget_show(image);
261  g_object_unref(pixbuf);
262  gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog), image);
263  }
264  }
265  g_free(filename);
266 }
267 
268 static void remmina_tp_channel_handler_channel_ready(TpChannel *channel, const GError *channel_error, gpointer user_data)
269 {
270  TRACE_CALL(__func__);
271  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
272  TpHandle handle;
273  GError *error = NULL;
274  TpContactFeature features[] =
275  { TP_CONTACT_FEATURE_ALIAS, TP_CONTACT_FEATURE_AVATAR_TOKEN };
276 
277  if (channel_error != NULL) {
278  g_print("%s: %s\n", __func__, channel_error->message);
280  return;
281  }
282 
283  if (tp_cli_channel_connect_to_closed(channel, remmina_tp_channel_handler_channel_closed, chandler, NULL, NULL, &error)
284  == NULL) {
285  g_print("tp_cli_channel_connect_to_closed: %s\n", channel_error->message);
287  return;
288  }
289  g_print("%s: %s\n", __func__, chandler->channel_path);
290 
291  handle = tp_channel_get_handle(channel, NULL);
292  tp_connection_get_contacts_by_handle(chandler->connection, 1, &handle, G_N_ELEMENTS(features), features,
293  remmina_tp_channel_handler_get_contacts, chandler, NULL, NULL);
294 }
295 
296 static void remmina_tp_channel_handler_connection_ready(TpConnection *connection, const GError *connection_error,
297  gpointer user_data)
298 {
299  TRACE_CALL(__func__);
300  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
301  GError *error = NULL;
302 
303  if (connection_error != NULL) {
304  g_print("%s: %s\n", __func__, connection_error->message);
306  return;
307  }
308 
309  chandler->channel = tp_channel_new_from_properties(connection, chandler->channel_path, chandler->channel_properties,
310  &error);
311  if (chandler->channel == NULL) {
312  g_print("tp_channel_new_from_properties: %s\n", error->message);
314  return;
315  }
316  tp_channel_call_when_ready(chandler->channel, remmina_tp_channel_handler_channel_ready, chandler);
317 }
318 
319 static void remmina_tp_channel_handler_account_ready(GObject *account, GAsyncResult *res, gpointer user_data)
320 {
321  TRACE_CALL(__func__);
322  RemminaTpChannelHandler *chandler = (RemminaTpChannelHandler*)user_data;
323  GError *error = NULL;
324 
325  if (!tp_account_prepare_finish(TP_ACCOUNT(account), res, &error)) {
326  g_print("tp_account_prepare_finish: %s\n", error->message);
328  return;
329  }
330 
331  chandler->connection = tp_connection_new(chandler->bus, NULL, chandler->connection_path, &error);
332  if (chandler->connection == NULL) {
333  g_print("tp_connection_new: %s\n", error->message);
335  return;
336  }
337  tp_connection_call_when_ready(chandler->connection, remmina_tp_channel_handler_connection_ready, chandler);
338 }
339 
340 void remmina_tp_channel_handler_new(const gchar *account_path, const gchar *connection_path, const gchar *channel_path,
341  GHashTable *channel_properties, DBusGMethodInvocation *context)
342 {
343  TRACE_CALL(__func__);
344  TpDBusDaemon *bus;
345  TpAccount *account;
346  GError *error = NULL;
347  RemminaTpChannelHandler *chandler;
348 
349  bus = tp_dbus_daemon_dup(&error);
350  if (bus == NULL) {
351  g_print("tp_dbus_daemon_dup: %s", error->message);
352  return;
353  }
354  account = tp_account_new(bus, account_path, &error);
355  if (account == NULL) {
356  g_object_unref(bus);
357  g_print("tp_account_new: %s", error->message);
358  return;
359  }
360 
361  chandler = g_new0(RemminaTpChannelHandler, 1);
362  chandler->bus = bus;
363  chandler->account = account;
364  chandler->connection_path = g_strdup(connection_path);
365  chandler->channel_path = g_strdup(channel_path);
366  chandler->channel_properties = tp_asv_new(NULL, NULL);
367  tp_g_hash_table_update(chandler->channel_properties, channel_properties, (GBoxedCopyFunc)g_strdup,
368  (GBoxedCopyFunc)tp_g_value_slice_dup);
369  chandler->context = context;
370 
371  tp_account_prepare_async(account, NULL, remmina_tp_channel_handler_account_ready, chandler);
372 }
373 
void(* file_set_int)(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: plugin.h:221
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
RemminaPluginService * remmina_plugin_telepathy_service
static void remmina_tp_channel_handler_account_ready(GObject *account, GAsyncResult *res, gpointer user_data)
static void remmina_tp_channel_handler_connection_ready(TpConnection *connection, const GError *connection_error, gpointer user_data)
static void remmina_tp_channel_handler_get_contacts(TpConnection *connection, guint n_contacts, TpContact *const *contacts, guint n_failed, const TpHandle *failed, const GError *error, gpointer user_data, GObject *weak_object)
static void remmina_tp_channel_handler_free(RemminaTpChannelHandler *chandler)
static void remmina_tp_channel_handler_channel_ready(TpChannel *channel, const GError *channel_error, gpointer user_data)
static void remmina_tp_channel_handler_on_response(GtkDialog *dialog, gint response_id, RemminaTpChannelHandler *chandler)
static void remmina_tp_channel_handler_accept(TpChannel *channel, const GValue *address, const GError *error, gpointer user_data, GObject *weak_object)
static void remmina_tp_channel_handler_connect(RemminaTpChannelHandler *chandler)
void remmina_tp_channel_handler_new(const gchar *account_path, const gchar *connection_path, const gchar *channel_path, GHashTable *channel_properties, DBusGMethodInvocation *context)
void(* ui_register)(GtkWidget *widget)
Definition: plugin.h:245
void(* file_set_string)(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: plugin.h:218
static void remmina_tp_channel_handler_get_service(TpProxy *channel, const GValue *service, const GError *error, gpointer user_data, GObject *weak_object)
static void remmina_tp_channel_handler_channel_closed(TpChannel *channel, gpointer user_data, GObject *self)
GtkWidget *(* open_connection)(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: plugin.h:247
struct _RemminaTpChannelHandler RemminaTpChannelHandler
DBusGMethodInvocation * context
RemminaFile *(* file_new)(void)
Definition: plugin.h:216
static void remmina_tp_channel_handler_on_disconnect(GtkWidget *widget, RemminaTpChannelHandler *chandler)