Remmina - The GTK+ Remote Desktop Client  v1.4.2
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.
xdmcp_plugin.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-2020 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 <gtk/gtkx.h>
39 
40 INCLUDE_GET_AVAILABLE_XDISPLAY
41 
42 #define REMMINA_PLUGIN_XDMCP_FEATURE_TOOL_SENDCTRLALTDEL 1
43 #define REMMINA_PLUGIN_XDMCP_FEATURE_GTKSOCKET 1
44 
45 #define GET_PLUGIN_DATA(gp) (RemminaPluginXdmcpData*)g_object_get_data(G_OBJECT(gp), "plugin-data");
46 
47 /* Forward declaration */
49 
50 typedef struct _RemminaPluginXdmcpData {
51  GtkWidget *socket;
52  gint socket_id;
53  GPid pid;
54  gint output_fd;
55  gint error_fd;
56  gint display;
57  gboolean ready;
58 
59  pthread_t thread;
60 
62 
64 
65 
67 {
68  TRACE_CALL(__func__);
69  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
70 
71  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
72  gpdata->ready = TRUE;
73 }
74 
76 {
77  TRACE_CALL(__func__);
78  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
79 }
80 
82 {
83  TRACE_CALL(__func__);
84  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
85  RemminaFile *remminafile;
86  gchar *argv[50];
87  gint argc;
88  gchar *host;
89  gint i;
90  GError *error = NULL;
91  gboolean ret;
92 
93  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
94 
95  gpdata->display = remmina_get_available_xdisplay();
96  if (gpdata->display == 0) {
97  remmina_plugin_service->protocol_plugin_set_error(gp, _("Ran out of available local X display numbers."));
98  return FALSE;
99  }
100 
101  argc = 0;
102  argv[argc++] = g_strdup("Xephyr");
103 
104  argv[argc++] = g_strdup_printf(":%i", gpdata->display);
105 
106  argv[argc++] = g_strdup("-parent");
107  argv[argc++] = g_strdup_printf("%i", gpdata->socket_id);
108 
109  /* All Xephyr version between 1.5.0 and 1.6.4 will break when "-screen" argument is specified with "-parent".
110  * It’s not possible to support colour depth if you have those Xephyr version. Please see this bug
111  * http://bugs.freedesktop.org/show_bug.cgi?id=24144
112  * As a workaround, a "Default" colour depth will not add the "-screen" argument.
113  */
114  i = remmina_plugin_service->file_get_int(remminafile, "colordepth", 8);
115  if (i >= 8) {
116  argv[argc++] = g_strdup("-screen");
117  argv[argc++] = g_strdup_printf("%ix%ix%i",
118  remmina_plugin_service->get_profile_remote_width(gp),
119  remmina_plugin_service->get_profile_remote_height(gp), i);
120  }
121 
122  if (i == 2) {
123  argv[argc++] = g_strdup("-grayscale");
124  }
125 
126  if (remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE)) {
127  argv[argc++] = g_strdup("-host-cursor");
128  }
129  if (remmina_plugin_service->file_get_int(remminafile, "once", FALSE)) {
130  argv[argc++] = g_strdup("-once");
131  }
132  /* Listen on TCP protocol */
133  if (remmina_plugin_service->file_get_int(remminafile, "listen_on_tcp", FALSE)) {
134  argv[argc++] = g_strdup("-listen");
135  argv[argc++] = g_strdup("tcp");
136  }
137 
138  if (!remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE)) {
139  remmina_plugin_service->get_server_port(remmina_plugin_service->file_get_string(remminafile, "server"), 0,
140  &host, &i);
141 
142  argv[argc++] = g_strdup("-query");
143  argv[argc++] = host;
144 
145  if (i) {
146  argv[argc++] = g_strdup("-port");
147  argv[argc++] = g_strdup_printf("%i", i);
148  }
149  }else {
150  /* When the connection is through an SSH tunnel, it connects back to local Unix socket,
151  * so for security we can disable TCP listening */
152  argv[argc++] = g_strdup("-nolisten");
153  argv[argc++] = g_strdup("tcp");
154 
155  /* FIXME: It’s better to get the magic cookie back from xqproxy, then call xauth,
156  * instead of disable access control */
157  argv[argc++] = g_strdup("-ac");
158  }
159 
160  argv[argc++] = NULL;
161 
162  ret = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &gpdata->pid, &error);
163  for (i = 0; i < argc; i++)
164  g_free(argv[i]);
165 
166  if (!ret) {
167  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", error->message);
168  return FALSE;
169  }
170 
171  return TRUE;
172 }
173 
174 static gboolean remmina_plugin_xdmcp_tunnel_init_callback(RemminaProtocolWidget *gp, gint remotedisplay, const gchar *server,
175  gint port)
176 {
177  TRACE_CALL(__func__);
178  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
179  RemminaFile *remminafile;
180 
181  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
182 
184  return FALSE;
185  while (!gpdata->ready)
186  sleep(1);
187 
188  remmina_plugin_service->protocol_plugin_set_display(gp, gpdata->display);
189 
190  if (remmina_plugin_service->file_get_string(remminafile, "exec")) {
191  return remmina_plugin_service->protocol_plugin_ssh_exec(gp, FALSE, "DISPLAY=localhost:%i.0 %s", remotedisplay,
192  remmina_plugin_service->file_get_string(remminafile, "exec"));
193  }else {
194  return remmina_plugin_service->protocol_plugin_ssh_exec(gp, TRUE,
195  "xqproxy -display %i -host %s -port %i -query -manage", remotedisplay, server, port);
196  }
197 }
198 
200 {
201  TRACE_CALL(__func__);
202  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
203  RemminaFile *remminafile;
204 
205  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
206 
207  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE)) {
209  gpdata->thread = 0;
210  return FALSE;
211  }
212  }else {
214  gpdata->thread = 0;
215  return FALSE;
216  }
217  }
218 
219  gpdata->thread = 0;
220  return TRUE;
221 }
222 
223 static gpointer
225 {
226  TRACE_CALL(__func__);
227  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
228 
229  CANCEL_ASYNC
231  IDLE_ADD((GSourceFunc)remmina_plugin_service->protocol_plugin_signal_connection_closed, data);
232  }
233  return NULL;
234 }
235 
237 {
238  TRACE_CALL(__func__);
239  RemminaPluginXdmcpData *gpdata;
240 
241  gpdata = g_new0(RemminaPluginXdmcpData, 1);
242  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
243 
244  gpdata->socket = gtk_socket_new();
245  remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->socket);
246  gtk_widget_show(gpdata->socket);
247  g_signal_connect(G_OBJECT(gpdata->socket), "plug-added", G_CALLBACK(remmina_plugin_xdmcp_on_plug_added), gp);
248  g_signal_connect(G_OBJECT(gpdata->socket), "plug-removed", G_CALLBACK(remmina_plugin_xdmcp_on_plug_removed), gp);
249  gtk_container_add(GTK_CONTAINER(gp), gpdata->socket);
250 
251 }
252 
253 
254 
256 {
257  TRACE_CALL(__func__);
258  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
259  RemminaFile *remminafile;
260  gint width, height;
261 
262  if (!remmina_plugin_service->gtksocket_available()) {
263  remmina_plugin_service->protocol_plugin_set_error(gp,
264  _("The protocol \"%s\" is unavailable because GtkSocket only works under X.Org."),
265  remmina_plugin_xdmcp.name);
266  return FALSE;
267  }
268 
269  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
270 
271  width = remmina_plugin_service->get_profile_remote_width(gp);
272  height = remmina_plugin_service->get_profile_remote_height(gp);
273  remmina_plugin_service->protocol_plugin_set_width(gp, width);
274  remmina_plugin_service->protocol_plugin_set_height(gp, height);
275  gtk_widget_set_size_request(GTK_WIDGET(gp), width, height);
276  gpdata->socket_id = gtk_socket_get_id(GTK_SOCKET(gpdata->socket));
277 
278 
279  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE)) {
280  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_xdmcp_main_thread, gp)) {
281  remmina_plugin_service->protocol_plugin_set_error(gp,
282  "Could not start pthread. Using non-threaded mode instead…");
283  gpdata->thread = 0;
284  return FALSE;
285  }else {
286  return TRUE;
287  }
288  }else {
289  return remmina_plugin_xdmcp_main(gp);
290  }
291 
292 }
293 
295 {
296  TRACE_CALL(__func__);
297  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
298 
299  if (gpdata->thread) {
300  pthread_cancel(gpdata->thread);
301  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
302  }
303 
304  if (gpdata->pid) {
305  kill(gpdata->pid, SIGTERM);
306  g_spawn_close_pid(gpdata->pid);
307  gpdata->pid = 0;
308  }
309 
310  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
311 
312  return FALSE;
313 }
314 
315 /* Send Ctrl+Alt+Del keystrokes to the plugin socket widget */
317 {
318  TRACE_CALL(__func__);
319  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
320  RemminaPluginXdmcpData *gpdata = GET_PLUGIN_DATA(gp);
321 
322  remmina_plugin_service->protocol_plugin_send_keys_signals(gpdata->socket,
323  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
324 }
325 
327 {
328  TRACE_CALL(__func__);
329  return TRUE;
330 }
331 
333 {
334  TRACE_CALL(__func__);
335 
336  switch (feature->id) {
337  case REMMINA_PLUGIN_XDMCP_FEATURE_TOOL_SENDCTRLALTDEL:
339  break;
340  default:
341  break;
342  }
343 }
344 
345 /* Array of key/value pairs for colour depths */
346 static gpointer colordepth_list[] =
347 {
348  "0", N_("Default"),
349  "2", N_("Grayscale"),
350  "8", N_("256 colours"),
351  "16", N_("High colour (16 bit)"),
352  "24", N_("True colour (24 bit)"),
353  NULL
354 };
355 
356 /* Array of RemminaProtocolSetting for basic settings.
357  * Each item is composed by:
358  * a) RemminaProtocolSettingType for setting type
359  * b) Setting name
360  * c) Setting description
361  * d) Compact disposition
362  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
363  * f) Setting tooltip
364  */
366 {
367  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL },
368  { REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION, "resolution", NULL, FALSE, NULL, NULL },
369  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL },
370  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Startup program"), FALSE, NULL, NULL },
371  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "showcursor", N_("Use local cursor"), FALSE, NULL, NULL },
372  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "once", N_("Disconnect after first session"), FALSE, NULL, NULL },
373  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "listen_on_tcp", N_("Listen for TCP connections"), FALSE, NULL, NULL },
374  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
375 };
376 
377 /* Array for available features.
378  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
380 {
381  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_XDMCP_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
382  { REMMINA_PROTOCOL_FEATURE_TYPE_GTKSOCKET, REMMINA_PLUGIN_XDMCP_FEATURE_GTKSOCKET, NULL, NULL, NULL},
383  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
384 };
385 
386 /* Protocol plugin definition and features */
387 static RemminaProtocolPlugin remmina_plugin_xdmcp =
388 {
390  "XDMCP", // Name
391  N_("XDMCP - X Remote Session"), // Description
392  GETTEXT_PACKAGE, // Translation domain
393  VERSION, // Version number
394  "remmina-xdmcp-symbolic", // Icon for normal connection
395  "remmina-xdmcp-ssh-symbolic", // Icon for SSH connection
396  remmina_plugin_xdmcp_basic_settings, // Array for basic settings
397  NULL, // Array for advanced settings
398  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
399  remmina_plugin_xdmcp_features, // Array for available features
400  remmina_plugin_xdmcp_init, // Plugin initialization
401  remmina_plugin_xdmcp_open_connection, // Plugin open connection
402  remmina_plugin_xdmcp_close_connection, // Plugin close connection
403  remmina_plugin_xdmcp_query_feature, // Query for available features
404  remmina_plugin_xdmcp_call_feature, // Call a feature
405  NULL, // Send a keystroke
406  NULL // No screenshot support available
407 };
408 
409 G_MODULE_EXPORT gboolean
411 {
412  TRACE_CALL(__func__);
413  remmina_plugin_service = service;
414 
415  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
416  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
417 
418  if (!service->register_plugin((RemminaPlugin*)&remmina_plugin_xdmcp)) {
419  return FALSE;
420  }
421 
422  return TRUE;
423 }
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:158
struct _RemminaPluginXdmcpData RemminaPluginXdmcpData
void(* protocol_plugin_set_display)(RemminaProtocolWidget *gp, gint display)
Definition: plugin.h:166
static void remmina_plugin_xdmcp_on_plug_added(GtkSocket *socket, RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:66
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:151
static gpointer remmina_plugin_xdmcp_main_thread(gpointer data)
Definition: xdmcp_plugin.c:224
static gpointer colordepth_list[]
Definition: xdmcp_plugin.c:346
static void remmina_plugin_xdmcp_send_ctrlaltdel(RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:316
gboolean(* protocol_plugin_ssh_exec)(RemminaProtocolWidget *gp, gboolean wait, const gchar *fmt,...)
Definition: plugin.h:189
static const RemminaProtocolFeature remmina_plugin_xdmcp_features[]
Definition: xdmcp_plugin.c:379
void(* protocol_plugin_send_keys_signals)(GtkWidget *widget, const guint *keyvals, int length, GdkEventType action)
Definition: plugin.h:193
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:167
static gboolean remmina_plugin_xdmcp_main(RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:199
static gboolean remmina_plugin_xdmcp_start_xephyr(RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:81
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:221
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:203
static gboolean remmina_plugin_xdmcp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: xdmcp_plugin.c:326
static RemminaProtocolPlugin remmina_plugin_xdmcp
Definition: xdmcp_plugin.c:48
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
static RemminaPluginService * remmina_plugin_service
Definition: xdmcp_plugin.c:63
gboolean(* protocol_plugin_start_xport_tunnel)(RemminaProtocolWidget *gp, RemminaXPortTunnelInitFunc init_func)
Definition: plugin.h:165
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
gboolean(* gtksocket_available)(void)
Definition: plugin.h:223
static gboolean remmina_plugin_xdmcp_tunnel_init_callback(RemminaProtocolWidget *gp, gint remotedisplay, const gchar *server, gint port)
Definition: xdmcp_plugin.c:174
static void remmina_plugin_xdmcp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: xdmcp_plugin.c:332
static void remmina_plugin_xdmcp_on_plug_removed(GtkSocket *socket, RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:75
gint(* get_profile_remote_height)(RemminaProtocolWidget *gp)
Definition: plugin.h:225
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:160
static gboolean remmina_plugin_xdmcp_open_connection(RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:255
static gboolean remmina_plugin_xdmcp_close_connection(RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:294
static const RemminaProtocolSetting remmina_plugin_xdmcp_basic_settings[]
Definition: xdmcp_plugin.c:365
gint(* get_profile_remote_width)(RemminaProtocolWidget *gp)
Definition: plugin.h:224
const gchar * name
Definition: plugin.h:63
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:153
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
static void remmina_plugin_xdmcp_init(RemminaProtocolWidget *gp)
Definition: xdmcp_plugin.c:236
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: xdmcp_plugin.c:410