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.
nx_plugin.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010-2011 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 <errno.h>
38 #include <pthread.h>
39 #include "common/remmina_plugin.h"
40 #include <gtk/gtkx.h>
41 #include <time.h>
42 #define LIBSSH_STATIC 1
43 #include <libssh/libssh.h>
44 #include <X11/Xlib.h>
45 #include <X11/XKBlib.h>
46 #include <X11/extensions/XKBrules.h>
47 #include "nx_plugin.h"
48 #include "nx_session_manager.h"
49 
50 #define REMMINA_PLUGIN_NX_FEATURE_TOOL_SENDCTRLALTDEL 1
51 #define REMMINA_PLUGIN_NX_FEATURE_GTKSOCKET 1
52 
53 /* Forward declaration */
55 
57 
58 static gchar *remmina_kbtype = "pc102/us";
59 
60 /* When more than one NX sessions is connecting in progress, we need this mutex and array
61  * to prevent them from stealing the same window ID.
62  */
63 static pthread_mutex_t remmina_nx_init_mutex;
65 
66 /* --------- Support for execution on main thread of GTK functions -------------- */
67 
70 
71  GtkSocket* sk;
72  Window w;
73 
74  /* Mutex for thread synchronization */
75  pthread_mutex_t mu;
76  /* Flag to catch cancellations */
77  gboolean cancelled;
78 };
79 
80 static gboolean onMainThread_cb(struct onMainThread_cb_data *d)
81 {
82  TRACE_CALL(__func__);
83  if ( !d->cancelled ) {
84  switch ( d->func ) {
86  gtk_socket_add_id( d->sk, d->w );
87  break;
88  }
89  pthread_mutex_unlock( &d->mu );
90  } else {
91  /* Thread has been cancelled, so we must free d memory here */
92  g_free( d );
93  }
94  return G_SOURCE_REMOVE;
95 }
96 
97 
98 static void onMainThread_cleanup_handler(gpointer data)
99 {
100  TRACE_CALL(__func__);
101  struct onMainThread_cb_data *d = data;
102  d->cancelled = TRUE;
103 }
104 
105 
107 {
108  TRACE_CALL(__func__);
109  d->cancelled = FALSE;
110  pthread_cleanup_push( onMainThread_cleanup_handler, d );
111  pthread_mutex_init( &d->mu, NULL );
112  pthread_mutex_lock( &d->mu );
113  gdk_threads_add_idle( (GSourceFunc)onMainThread_cb, (gpointer)d );
114 
115  pthread_mutex_lock( &d->mu );
116 
117  pthread_cleanup_pop(0);
118  pthread_mutex_unlock( &d->mu );
119  pthread_mutex_destroy( &d->mu );
120 }
121 
122 static void onMainThread_gtk_socket_add_id( GtkSocket* sk, Window w)
123 {
124  TRACE_CALL(__func__);
125 
126  struct onMainThread_cb_data *d;
127 
128  d = (struct onMainThread_cb_data *)g_malloc( sizeof(struct onMainThread_cb_data) );
130  d->sk = sk;
131  d->w = w;
132 
134  g_free(d);
135 
136 }
137 
138 
139 /* --------------------------------------- */
140 
141 
142 static gboolean remmina_plugin_nx_try_window_id(Window window_id)
143 {
144  TRACE_CALL(__func__);
145  gint i;
146  gboolean found = FALSE;
147 
148  pthread_mutex_lock(&remmina_nx_init_mutex);
149  for (i = 0; i < remmina_nx_window_id_array->len; i++) {
150  if (g_array_index(remmina_nx_window_id_array, Window, i) == window_id) {
151  found = TRUE;
152  break;
153  }
154  }
155  if (!found) {
156  g_array_append_val(remmina_nx_window_id_array, window_id);
157  }
158  pthread_mutex_unlock(&remmina_nx_init_mutex);
159 
160  return (!found);
161 }
162 
163 static void remmina_plugin_nx_remove_window_id(Window window_id)
164 {
165  TRACE_CALL(__func__);
166  gint i;
167  gboolean found = FALSE;
168 
169  pthread_mutex_lock(&remmina_nx_init_mutex);
170  for (i = 0; i < remmina_nx_window_id_array->len; i++) {
171  if (g_array_index(remmina_nx_window_id_array, Window, i) == window_id) {
172  found = TRUE;
173  break;
174  }
175  }
176  if (found) {
177  g_array_remove_index_fast(remmina_nx_window_id_array, i);
178  }
179  pthread_mutex_unlock(&remmina_nx_init_mutex);
180 }
181 
183 {
184  TRACE_CALL(__func__);
185  remmina_plugin_nx_service->protocol_plugin_signal_connection_opened(gp);
186 }
187 
189 {
190  TRACE_CALL(__func__);
191  remmina_plugin_nx_service->protocol_plugin_signal_connection_closed(gp);
192 }
193 
194 gboolean remmina_plugin_nx_ssh_auth_callback(gchar **passphrase, gpointer userdata)
195 {
196  TRACE_CALL(__func__);
198  gint ret;
199 
200  /* SSH passwords must not be saved */
201  ret = remmina_plugin_nx_service->protocol_plugin_init_auth(gp, 0,
202  _("SSH credentials"), NULL,
203  NULL,
204  NULL,
205  _("Password for private SSH key"));
206  if (ret == GTK_RESPONSE_OK) {
207  *passphrase = remmina_plugin_nx_service->protocol_plugin_init_get_password(gp);
208  return TRUE;
209  } else
210  return FALSE;
211 }
212 
213 static void remmina_plugin_nx_on_proxy_exit(GPid pid, gint status, gpointer data)
214 {
215  TRACE_CALL(__func__);
217 
218  remmina_plugin_nx_service->protocol_plugin_signal_connection_closed(gp);
219 }
220 
221 static int remmina_plugin_nx_dummy_handler(Display *dsp, XErrorEvent *err)
222 {
223  TRACE_CALL(__func__);
224  return 0;
225 }
226 
228 {
229  TRACE_CALL(__func__);
230  RemminaPluginNxData *gpdata = GET_PLUGIN_DATA(gp);
231 
232  gpdata->display = XOpenDisplay(gdk_display_get_name(gdk_display_get_default()));
233  if (gpdata->display == NULL)
234  return FALSE;
235 
236  gpdata->orig_handler = XSetErrorHandler(remmina_plugin_nx_dummy_handler);
237 
238  XSelectInput(gpdata->display, XDefaultRootWindow(gpdata->display), SubstructureNotifyMask);
239 
240  return TRUE;
241 }
242 
244 {
245  TRACE_CALL(__func__);
246  RemminaPluginNxData *gpdata;
247  Atom atom;
248  XEvent xev;
249  Window w;
250  Atom type;
251  int format;
252  unsigned long nitems, rest;
253  unsigned char *data = NULL;
254  struct timespec ts;
255 
256  CANCEL_DEFER
257 
258  gpdata = GET_PLUGIN_DATA(gp);
259  atom = XInternAtom(gpdata->display, "WM_COMMAND", True);
260  if (atom == None)
261  return FALSE;
262 
263  ts.tv_sec = 0;
264  ts.tv_nsec = 200000000;
265 
266  while (1) {
267  pthread_testcancel();
268  while (!XPending(gpdata->display)) {
269  nanosleep(&ts, NULL);
270  continue;
271  }
272  XNextEvent(gpdata->display, &xev);
273  if (xev.type != CreateNotify)
274  continue;
275  w = xev.xcreatewindow.window;
276  if (XGetWindowProperty(gpdata->display, w, atom, 0, 255, False, AnyPropertyType, &type, &format, &nitems, &rest,
277  &data) != Success)
278  continue;
279  if (data && strstr((char*)data, cmd) && remmina_plugin_nx_try_window_id(w)) {
280  gpdata->window_id = w;
281  XFree(data);
282  break;
283  }
284  if (data)
285  XFree(data);
286  }
287 
288  XSetErrorHandler(gpdata->orig_handler);
289  XCloseDisplay(gpdata->display);
290  gpdata->display = NULL;
291 
292  CANCEL_ASYNC
293  return TRUE;
294 }
295 
297 {
298  TRACE_CALL(__func__);
299  fd_set set;
300  guchar dummy = 0;
301 
302  FD_ZERO(&set);
303  FD_SET(gpdata->event_pipe[0], &set);
304  select(gpdata->event_pipe[0] + 1, &set, NULL, NULL, NULL);
305  if (read(gpdata->event_pipe[0], &dummy, 1)) {
306  }
307  return (gint)dummy;
308 }
309 
311 {
312  TRACE_CALL(__func__);
313  RemminaPluginNxData *gpdata = GET_PLUGIN_DATA(gp);
314  RemminaFile *remminafile;
315  RemminaNXSession *nx;
316  const gchar *type, *app;
317  gchar *s1, *s2;
318  gint port;
319  gint ret;
320  gboolean is_empty_list;
321  gint event_type = 0;
322  const gchar *cs;
323  gint i;
324  gboolean disablepasswordstoring;
325 
326  remminafile = remmina_plugin_nx_service->protocol_plugin_get_file(gp);
327  nx = gpdata->nx;
328 
329  /* Connect */
330 
332  remmina_plugin_nx_service->file_get_int(remminafile, "disableencryption", FALSE) ? 0 : 1);
333  remmina_nx_session_set_localport(nx, remmina_plugin_nx_service->pref_get_sshtunnel_port());
334  remmina_nx_session_set_log_callback(nx, remmina_plugin_nx_service->debug);
335 
336  s2 = remmina_plugin_nx_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE);
337  if (s2 == NULL) {
338  return FALSE;
339  }
340  remmina_plugin_nx_service->get_server_port(s2, 22, &s1, &port);
341  g_free(s2);
342 
343  if (!remmina_nx_session_open(nx, s1, port, remmina_plugin_nx_service->file_get_string(remminafile, "nx_privatekey"),
345  g_free(s1);
346  return FALSE;
347  }
348  g_free(s1);
349 
350  /* Login */
351 
352  s1 = g_strdup(remmina_plugin_nx_service->file_get_string(remminafile, "username"));
353  s2 = g_strdup(remmina_plugin_nx_service->file_get_string(remminafile, "password"));
354 
355  if (s1 && s2) {
356  ret = remmina_nx_session_login(nx, s1, s2);
357  } else {
358  gchar *s_username, *s_password;
359 
360  disablepasswordstoring = remmina_plugin_nx_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
361 
362  ret = remmina_plugin_nx_service->protocol_plugin_init_auth(gp,
364  _("Enter NX authentication credentials"),
365  remmina_plugin_nx_service->file_get_string(remminafile, "username"),
366  remmina_plugin_nx_service->file_get_string(remminafile, "password"),
367  NULL,
368  NULL);
369  if (ret == GTK_RESPONSE_OK) {
370  gboolean save;
371  s_username = remmina_plugin_nx_service->protocol_plugin_init_get_username(gp);
372  s_password = remmina_plugin_nx_service->protocol_plugin_init_get_password(gp);
373  save = remmina_plugin_nx_service->protocol_plugin_init_get_savepassword(gp);
374  if (save) {
375  remmina_plugin_nx_service->file_set_string(remminafile, "username", s_username);
376  remmina_plugin_nx_service->file_set_string(remminafile, "password", s_password);
377  } else
378  remmina_plugin_nx_service->file_unsave_passwords(remminafile);
379  } else {
380  return False;
381  }
382 
383  ret = remmina_nx_session_login(nx, s_username, s_password);
384  g_free(s_username);
385  g_free(s_password);
386  }
387  g_free(s1);
388  g_free(s2);
389 
390  if (!ret)
391  return FALSE;
392 
393  remmina_plugin_nx_service->protocol_plugin_init_save_cred(gp);
394 
395  /* Prepare the session type and application */
396  cs = remmina_plugin_nx_service->file_get_string(remminafile, "exec");
397  if (!cs || g_strcmp0(cs, "GNOME") == 0) {
398  type = "unix-gnome";
399  app = NULL;
400  }else
401  if (g_strcmp0(cs, "KDE") == 0) {
402  type = "unix-kde";
403  app = NULL;
404  }else
405  if (g_strcmp0(cs, "Xfce") == 0) {
406  /* NX does not know Xfce. So we simply launch the Xfce startup program. */
407  type = "unix-application";
408  app = "startxfce4";
409  }else
410  if (g_strcmp0(cs, "Shadow") == 0) {
411  type = "shadow";
412  app = NULL;
413  }else {
414  type = "unix-application";
415  app = cs;
416  }
417 
418  /* List sessions */
419 
420  gpdata->attach_session = (g_strcmp0(type, "shadow") == 0);
421  while (1) {
422  remmina_nx_session_add_parameter(nx, "type", type);
423  if (!gpdata->attach_session) {
425  remmina_plugin_nx_service->file_get_string(remminafile, "username"));
426  remmina_nx_session_add_parameter(nx, "status", "suspended,running");
427  }
428 
429  if (!remmina_nx_session_list(nx)) {
430  return FALSE;
431  }
432 
433  is_empty_list = !remmina_nx_session_iter_first(nx, &gpdata->iter);
434  if (is_empty_list && !gpdata->manager_started && !gpdata->attach_session) {
435  event_type = REMMINA_NX_EVENT_START;
436  }else {
438  event_type = remmina_plugin_nx_wait_signal(gpdata);
439  if (event_type == REMMINA_NX_EVENT_CANCEL) {
440  return FALSE;
441  }
442  if (event_type == REMMINA_NX_EVENT_TERMINATE) {
443  if (!is_empty_list) {
445  remmina_nx_session_add_parameter(nx, "sessionid", s1);
446  g_free(s1);
447  if (!remmina_nx_session_terminate(nx)) {
450  }
451  }
452  continue;
453  }
454  }
455 
456  break;
457  }
458 
459  /* Start, Restore or Attach, based on the setting and existing session */
460  remmina_nx_session_add_parameter(nx, "type", type);
461  i = remmina_plugin_nx_service->file_get_int(remminafile, "quality", 0);
462  remmina_nx_session_add_parameter(nx, "link", i > 2 ? "lan" : i == 2 ? "adsl" : i == 1 ? "isdn" : "modem");
463  remmina_nx_session_add_parameter(nx, "geometry", "%ix%i",
464  remmina_plugin_nx_service->get_profile_remote_width(gp),
465  remmina_plugin_nx_service->get_profile_remote_height(gp));
467  remmina_nx_session_add_parameter(nx, "client", "linux");
468  remmina_nx_session_add_parameter(nx, "media", "0");
469  remmina_nx_session_add_parameter(nx, "clipboard",
470  remmina_plugin_nx_service->file_get_int(remminafile, "disableclipboard", FALSE) ? "none" : "both");
471 
472  switch (event_type) {
473 
475  if (app)
476  remmina_nx_session_add_parameter(nx, "application", app);
477 
478  remmina_nx_session_add_parameter(nx, "session",
479  remmina_plugin_nx_service->file_get_string(remminafile, "name"));
480  remmina_nx_session_add_parameter(nx, "screeninfo", "%ix%ix24+render",
481  remmina_plugin_nx_service->file_get_int(remminafile, "resolution_width", 0),
482  remmina_plugin_nx_service->file_get_int(remminafile, "resolution_height", 0));
483 
484  if (!remmina_nx_session_start(nx))
485  return FALSE;
486  break;
487 
490  remmina_nx_session_add_parameter(nx, "id", s1);
491  g_free(s1);
492 
494  remmina_nx_session_add_parameter(nx, "display", s1);
495  g_free(s1);
496 
497  if (!remmina_nx_session_attach(nx))
498  return FALSE;
499  break;
500 
503  remmina_nx_session_add_parameter(nx, "id", s1);
504  g_free(s1);
505 
506  remmina_nx_session_add_parameter(nx, "session",
507  remmina_plugin_nx_service->file_get_string(remminafile, "name"));
508 
510  return FALSE;
511  break;
512 
513  default:
514  return FALSE;
515  }
516 
518  return FALSE;
519 
521  return FALSE;
522 
523  /* nxproxy */
525  return FALSE;
526 
527  /* get the window id of the remote nxagent */
528  if (!remmina_plugin_nx_monitor_create_notify(gp, "nxagent"))
529  return FALSE;
530 
531  /* embed it */
532  onMainThread_gtk_socket_add_id(GTK_SOCKET(gpdata->socket), gpdata->window_id);
533 
534 
535  return TRUE;
536 }
537 
539 {
540  TRACE_CALL(__func__);
541  RemminaPluginNxData *gpdata = GET_PLUGIN_DATA(gp);
542  gboolean ret;
543  const gchar *err;
544 
545  gpdata->nx = remmina_nx_session_new();
547  if (!ret) {
548  err = remmina_nx_session_get_error(gpdata->nx);
549  if (err) {
550  remmina_plugin_nx_service->protocol_plugin_set_error(gp, "%s", err);
551  }
552  }
553 
554  gpdata->thread = 0;
555  return ret;
556 }
557 
558 static gpointer remmina_plugin_nx_main_thread(gpointer data)
559 {
560  TRACE_CALL(__func__);
562  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
563 
564  CANCEL_ASYNC
565  if (!remmina_plugin_nx_main(gp)) {
566  remmina_plugin_nx_service->protocol_plugin_signal_connection_closed(gp);
567  }
568  return NULL;
569 }
570 
572 {
573  TRACE_CALL(__func__);
574  RemminaPluginNxData *gpdata;
575  gint flags;
576 
577  gpdata = g_new0(RemminaPluginNxData, 1);
578  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
579 
580  gpdata->socket = gtk_socket_new();
581  remmina_plugin_nx_service->protocol_plugin_register_hostkey(gp, gpdata->socket);
582  gtk_widget_show(gpdata->socket);
583  g_signal_connect(G_OBJECT(gpdata->socket), "plug-added", G_CALLBACK(remmina_plugin_nx_on_plug_added), gp);
584  g_signal_connect(G_OBJECT(gpdata->socket), "plug-removed", G_CALLBACK(remmina_plugin_nx_on_plug_removed), gp);
585  gtk_container_add(GTK_CONTAINER(gp), gpdata->socket);
586 
587  if (pipe(gpdata->event_pipe)) {
588  g_print("Error creating pipes.\n");
589  gpdata->event_pipe[0] = -1;
590  gpdata->event_pipe[1] = -1;
591  }else {
592  flags = fcntl(gpdata->event_pipe[0], F_GETFL, 0);
593  fcntl(gpdata->event_pipe[0], F_SETFL, flags | O_NONBLOCK);
594  }
595 }
596 
598 {
599  TRACE_CALL(__func__);
600  RemminaPluginNxData *gpdata = GET_PLUGIN_DATA(gp);
601  gint width, height;
602 
603  if (!remmina_plugin_nx_service->gtksocket_available()) {
604  remmina_plugin_nx_service->protocol_plugin_set_error(gp,
605  _("The protocol \"%s\" is unavailable because GtkSocket only works under X.Org."),
606  remmina_plugin_nx.name);
607  return FALSE;
608  }
609 
610  width = remmina_plugin_nx_service->get_profile_remote_width(gp);
611  height = remmina_plugin_nx_service->get_profile_remote_height(gp);
612 
613  remmina_plugin_nx_service->protocol_plugin_set_width(gp, width);
614  remmina_plugin_nx_service->protocol_plugin_set_height(gp, height);
615  gtk_widget_set_size_request(GTK_WIDGET(gp), width, height);
616 
617  gpdata->socket_id = gtk_socket_get_id(GTK_SOCKET(gpdata->socket));
618 
619  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_nx_main_thread, gp)) {
620  remmina_plugin_nx_service->protocol_plugin_set_error(gp,
621  "Failed to initialize pthread. Falling back to non-thread mode…");
622  gpdata->thread = 0;
623  return FALSE;
624  }else {
625  return TRUE;
626  }
627 }
628 
630 {
631  TRACE_CALL(__func__);
632  RemminaPluginNxData *gpdata = GET_PLUGIN_DATA(gp);
633 
634  if (gpdata->thread) {
635  pthread_cancel(gpdata->thread);
636  if (gpdata->thread)
637  pthread_join(gpdata->thread, NULL);
638  }
639  if (gpdata->session_manager_start_handler) {
640  g_source_remove(gpdata->session_manager_start_handler);
641  gpdata->session_manager_start_handler = 0;
642  }
643 
644  if (gpdata->window_id) {
646  }
647 
648  if (gpdata->nx) {
649  remmina_nx_session_free(gpdata->nx);
650  gpdata->nx = NULL;
651  }
652 
653  if (gpdata->display) {
654  XSetErrorHandler(gpdata->orig_handler);
655  XCloseDisplay(gpdata->display);
656  gpdata->display = NULL;
657  }
658  close(gpdata->event_pipe[0]);
659  close(gpdata->event_pipe[1]);
660 
661  remmina_plugin_nx_service->protocol_plugin_signal_connection_closed(gp);
662 
663  return FALSE;
664 }
665 
666 /* Send CTRL+ALT+DEL keys keystrokes to the plugin socket widget */
668 {
669  TRACE_CALL(__func__);
670  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
671  RemminaPluginNxData *gpdata = GET_PLUGIN_DATA(gp);
672 
673  remmina_plugin_nx_service->protocol_plugin_send_keys_signals(gpdata->socket,
674  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
675 }
676 
678 {
679  TRACE_CALL(__func__);
680  return TRUE;
681 }
682 
684 {
685  TRACE_CALL(__func__);
686  switch (feature->id) {
687  case REMMINA_PLUGIN_NX_FEATURE_TOOL_SENDCTRLALTDEL:
689  break;
690  default:
691  break;
692  }
693 }
694 
695 /* Array of key/value pairs for quality selection */
696 static gpointer quality_list[] =
697 {
698  "0", N_("Poor (fastest)"),
699  "1", N_("Medium"),
700  "2", N_("Good"),
701  "9", N_("Best (slowest)"),
702  NULL
703 };
704 
705 /* Array of RemminaProtocolSetting for basic settings.
706  * Each item is composed by:
707  * a) RemminaProtocolSettingType for setting type
708  * b) Setting name
709  * c) Setting description
710  * d) Compact disposition
711  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
712  * f) Setting tooltip
713  */
715 {
716  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL },
717  { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "nx_privatekey", N_("Identity file"), FALSE, NULL, NULL },
718  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL },
719  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL },
720  { REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION, "resolution", NULL, FALSE, GINT_TO_POINTER(1), NULL },
721  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL },
722  { REMMINA_PROTOCOL_SETTING_TYPE_COMBO, "exec", N_("Startup program"), FALSE, "GNOME,KDE,Xfce,Shadow", NULL },
723  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
724 };
725 
726 /* Array of RemminaProtocolSetting for advanced settings.
727  * Each item is composed by:
728  * a) RemminaProtocolSettingType for setting type
729  * b) Setting name
730  * c) Setting description
731  * d) Compact disposition
732  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
733  * f) Setting tooltip
734  */
736 {
737  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Disable clipboard sync"), FALSE, NULL, NULL },
738  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableencryption", N_("Disable encryption"), FALSE, NULL, NULL },
739  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "showcursor", N_("Use local cursor"), FALSE, NULL, NULL },
740  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), FALSE, NULL, NULL },
741  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
742 };
743 
744 /* Array for available features.
745  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
747 {
748  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_NX_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Del"), NULL, NULL },
749  { REMMINA_PROTOCOL_FEATURE_TYPE_GTKSOCKET, REMMINA_PLUGIN_NX_FEATURE_GTKSOCKET, NULL, NULL, NULL},
750  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
751 };
752 
753 /* Protocol plugin definition and features */
754 static RemminaProtocolPlugin remmina_plugin_nx =
755 {
757  "NX", // Name
758  N_("NX - NX Technology"), // Description
759  GETTEXT_PACKAGE, // Translation domain
760  VERSION, // Version number
761  "remmina-nx-symbolic", // Icon for normal connection
762  "remmina-nx-symbolic", // Icon for SSH connection
763  remmina_plugin_nx_basic_settings, // Array for basic settings
764  remmina_plugin_nx_advanced_settings, // Array for advanced settings
765  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
766  remmina_plugin_nx_features, // Array for available features
767  remmina_plugin_nx_init, // Plugin initialization
768  remmina_plugin_nx_open_connection, // Plugin open connection
769  remmina_plugin_nx_close_connection, // Plugin close connection
770  remmina_plugin_nx_query_feature, // Query for available features
771  remmina_plugin_nx_call_feature, // Call a feature
772  NULL, // Send a keystroke
773  NULL // Screenshot support unavailable
774 };
775 
776 G_MODULE_EXPORT gboolean
778 {
779  TRACE_CALL(__func__);
780  Display *dpy;
781  XkbRF_VarDefsRec vd;
782  gchar *s;
783 
784  remmina_plugin_nx_service = service;
785 
786  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
787  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
788 
789  if ((dpy = XkbOpenDisplay(NULL, NULL, NULL, NULL, NULL, NULL)) != NULL) {
790  if (XkbRF_GetNamesProp(dpy, NULL, &vd)) {
791  remmina_kbtype = g_strdup_printf("%s/%s", vd.model, vd.layout);
792  if (vd.layout)
793  XFree(vd.layout);
794  if (vd.model)
795  XFree(vd.model);
796  if (vd.variant)
797  XFree(vd.variant);
798  if (vd.options)
799  XFree(vd.options);
800  s = strchr(remmina_kbtype, ',');
801  if (s)
802  *s = '\0';
803  /* g_print("NX: Detected \"%s\" keyboard type\n", remmina_kbtype); */
804  }
805  XCloseDisplay(dpy);
806  }
807 
808  if (!service->register_plugin((RemminaPlugin*)&remmina_plugin_nx)) {
809  return FALSE;
810  }
811 
812  ssh_init();
813  pthread_mutex_init(&remmina_nx_init_mutex, NULL);
814  remmina_nx_window_id_array = g_array_new(FALSE, TRUE, sizeof(Window));
815 
816  return TRUE;
817 }
RemminaNXSession * nx
Definition: nx_plugin.h:59
static gboolean remmina_plugin_nx_monitor_create_notify(RemminaProtocolWidget *gp, const gchar *cmd)
Definition: nx_plugin.c:243
gboolean remmina_nx_session_open(RemminaNXSession *nx, const gchar *server, guint port, const gchar *private_key_file, RemminaNXPassphraseCallback passphrase_func, gpointer userdata)
Definition: nx_session.c:529
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: nx_plugin.c:777
void remmina_nx_session_set_log_callback(RemminaNXSession *nx, RemminaNXLogCallback log_callback)
Definition: nx_session.c:250
void remmina_nx_session_set_localport(RemminaNXSession *nx, gint localport)
Definition: nx_session.c:244
void(* debug)(const gchar *fmt,...)
Definition: plugin.h:214
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:158
gboolean remmina_nx_session_attach(RemminaNXSession *nx)
Definition: nx_session.c:753
gboolean remmina_nx_session_tunnel_open(RemminaNXSession *nx)
Definition: nx_session.c:881
void(* protocol_plugin_init_save_cred)(RemminaProtocolWidget *gp)
Definition: plugin.h:184
GtkWidget * socket
Definition: nx_plugin.h:54
gboolean remmina_nx_session_start(RemminaNXSession *nx)
Definition: nx_session.c:746
Display * display
Definition: nx_plugin.h:61
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
static void remmina_plugin_nx_on_plug_removed(GtkSocket *socket, RemminaProtocolWidget *gp)
Definition: nx_plugin.c:188
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:151
static gboolean remmina_plugin_nx_try_window_id(Window window_id)
Definition: nx_plugin.c:142
static void onMainThread_cleanup_handler(gpointer data)
Definition: nx_plugin.c:98
gboolean remmina_nx_session_invoke_proxy(RemminaNXSession *nx, gint display, GChildWatchFunc exit_func, gpointer user_data)
Definition: nx_session.c:951
static void remmina_plugin_nx_on_proxy_exit(GPid pid, gint status, gpointer data)
Definition: nx_plugin.c:213
gboolean manager_started
Definition: nx_plugin.h:66
const gchar * remmina_nx_session_get_error(RemminaNXSession *nx)
Definition: nx_session.c:223
void(* protocol_plugin_send_keys_signals)(GtkWidget *widget, const guint *keyvals, int length, GdkEventType action)
Definition: plugin.h:193
gint(* pref_get_sshtunnel_port)(void)
Definition: plugin.h:209
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:167
static gboolean remmina_plugin_nx_start_create_notify(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:227
static void onMainThread_schedule_callback_and_wait(struct onMainThread_cb_data *d)
Definition: nx_plugin.c:106
void remmina_nx_session_manager_start(RemminaProtocolWidget *gp)
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
RemminaNXSession * remmina_nx_session_new(void)
Definition: nx_session.c:129
static const RemminaProtocolSetting remmina_plugin_nx_basic_settings[]
Definition: nx_plugin.c:714
GtkSocket * sk
Definition: nx_plugin.c:71
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
static void remmina_plugin_nx_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: nx_plugin.c:683
static const RemminaProtocolSetting remmina_plugin_nx_advanced_settings[]
Definition: nx_plugin.c:735
static gboolean remmina_plugin_nx_open_connection(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:597
static gchar * remmina_kbtype
Definition: nx_plugin.c:58
gboolean attach_session
Definition: nx_plugin.h:73
gboolean(* protocol_plugin_init_get_savepassword)(RemminaProtocolWidget *gp)
Definition: plugin.h:178
static RemminaProtocolPlugin remmina_plugin_nx
Definition: nx_plugin.c:54
gboolean remmina_plugin_nx_ssh_auth_callback(gchar **passphrase, gpointer userdata)
Definition: nx_plugin.c:194
void remmina_nx_session_free(RemminaNXSession *nx)
Definition: nx_session.c:145
static gint remmina_plugin_nx_wait_signal(RemminaPluginNxData *gpdata)
Definition: nx_plugin.c:296
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
void remmina_nx_session_set_encryption(RemminaNXSession *nx, gint encryption)
Definition: nx_session.c:238
static void remmina_plugin_nx_init(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:571
gboolean(* gtksocket_available)(void)
Definition: plugin.h:223
gboolean remmina_nx_session_list(RemminaNXSession *nx)
Definition: nx_session.c:667
static gboolean remmina_plugin_nx_start_session(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:310
static const RemminaProtocolFeature remmina_plugin_nx_features[]
Definition: nx_plugin.c:746
static gpointer quality_list[]
Definition: nx_plugin.c:696
GtkTreeIter iter
Definition: nx_plugin.h:74
void remmina_nx_session_add_parameter(RemminaNXSession *nx, const gchar *name, const gchar *valuefmt,...)
Definition: nx_session.c:634
gchar * remmina_nx_session_iter_get(RemminaNXSession *nx, GtkTreeIter *iter, gint column)
Definition: nx_session.c:706
static gboolean remmina_plugin_nx_close_connection(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:629
gint(* protocol_plugin_init_auth)(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
Definition: plugin.h:172
pthread_mutex_t mu
Definition: nx_plugin.c:75
enum onMainThread_cb_data::@37 func
static void remmina_plugin_nx_on_plug_added(GtkSocket *socket, RemminaProtocolWidget *gp)
Definition: nx_plugin.c:182
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_nx_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: nx_plugin.c:677
pthread_t thread
Definition: nx_plugin.h:57
guint session_manager_start_handler
Definition: nx_plugin.h:72
static gboolean remmina_plugin_nx_main(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:538
void(* file_unsave_passwords)(RemminaFile *remminafile)
Definition: plugin.h:204
gchar *(* protocol_plugin_init_get_username)(RemminaProtocolWidget *gp)
Definition: plugin.h:175
gint(* get_profile_remote_width)(RemminaProtocolWidget *gp)
Definition: plugin.h:224
gchar *(* protocol_plugin_init_get_password)(RemminaProtocolWidget *gp)
Definition: plugin.h:176
static gboolean onMainThread_cb(struct onMainThread_cb_data *d)
Definition: nx_plugin.c:80
const gchar * name
Definition: plugin.h:63
static void onMainThread_gtk_socket_add_id(GtkSocket *sk, Window w)
Definition: nx_plugin.c:122
static int remmina_plugin_nx_dummy_handler(Display *dsp, XErrorEvent *err)
Definition: nx_plugin.c:221
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
gboolean remmina_nx_session_terminate(RemminaNXSession *nx)
Definition: nx_session.c:767
RemminaProtocolWidget * gp
Definition: vnc_plugin.c:68
void(* file_set_string)(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: plugin.h:199
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:153
static pthread_mutex_t remmina_nx_init_mutex
Definition: nx_plugin.c:63
gboolean remmina_nx_session_login(RemminaNXSession *nx, const gchar *username, const gchar *password)
Definition: nx_session.c:611
static void remmina_plugin_nx_send_ctrlaltdel(RemminaProtocolWidget *gp)
Definition: nx_plugin.c:667
int(* orig_handler)(Display *, XErrorEvent *)
Definition: nx_plugin.h:63
RemminaPluginService * remmina_plugin_nx_service
Definition: nx_plugin.c:56
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:163
gboolean remmina_nx_session_iter_first(RemminaNXSession *nx, GtkTreeIter *iter)
Definition: nx_session.c:689
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
static GArray * remmina_nx_window_id_array
Definition: nx_plugin.c:64
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
gboolean remmina_nx_session_restore(RemminaNXSession *nx)
Definition: nx_session.c:760
static void remmina_plugin_nx_remove_window_id(Window window_id)
Definition: nx_plugin.c:163
static gpointer remmina_plugin_nx_main_thread(gpointer data)
Definition: nx_plugin.c:558
gboolean cancelled
Definition: nx_plugin.c:77