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.
remmina_ssh_plugin.c
Go to the documentation of this file.
1 
38 #include "config.h"
40 
41 #if defined (HAVE_LIBSSH) && defined (HAVE_LIBVTE)
42 
43 #include <gtk/gtk.h>
44 #include <glib/gi18n.h>
45 #include <gio/gio.h>
46 #include <vte/vte.h>
47 #include <locale.h>
48 #include <langinfo.h>
49 #include "remmina_log.h"
50 #include "remmina_public.h"
51 #include "remmina_plugin_manager.h"
52 #include "remmina_ssh.h"
54 #include "remmina_pref.h"
55 #include "remmina_ssh_plugin.h"
57 
58 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_COPY 1
59 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_PASTE 2
60 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_SELECT_ALL 3
61 
62 #define GET_PLUGIN_DATA(gp) (RemminaPluginSshData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
63 
65 #define PALETTE_SIZE 16
66 
68 
73 const GdkRGBA linux_palette[PALETTE_SIZE] = {
74  { 0, 0, 0, 1 },
75  { 0.666667, 0, 0, 1 },
76  { 0, 0.666667, 0, 1 },
77  { 0.666667, 0.333333, 0, 1 },
78  { 0, 0, 0.666667, 1 },
79  { 0.666667, 0, 0.666667, 1 },
80  { 0, 0.666667, 0.666667, 1 },
81  { 0.666667, 0.666667, 0.666667, 1 },
82  { 0.333333, 0.333333, 0.333333, 1 },
83  { 1, 0.333333, 0.333333, 1 },
84  { 0.333333, 1, 0.333333, 1 },
85  { 1, 1, 0.333333, 1 },
86  { 0.333333, 0.333333, 1, 1 },
87  { 1, 0.333333, 1, 1 },
88  { 0.333333, 1, 1, 1 },
89  { 1, 1, 1, 1 }
90 };
91 
92 const GdkRGBA tango_palette[PALETTE_SIZE] = {
93  { 0, 0, 0, 1 },
94  { 0.8, 0, 0, 1 },
95  { 0.305882, 0.603922, 0.023529, 1 },
96  { 0.768627, 0.627451, 0, 1 },
97  { 0.203922, 0.396078, 0.643137, 1 },
98  { 0.458824, 0.313725, 0.482353, 1 },
99  { 0.0235294, 0.596078, 0.603922, 1 },
100  { 0.827451, 0.843137, 0.811765, 1 },
101  { 0.333333, 0.341176, 0.32549, 1 },
102  { 0.937255, 0.160784, 0.160784, 1 },
103  { 0.541176, 0.886275, 0.203922, 1 },
104  { 0.988235, 0.913725, 0.309804, 1 },
105  { 0.447059, 0.623529, 0.811765, 1 },
106  { 0.678431, 0.498039, 0.658824, 1 },
107  { 0.203922, 0.886275, 0.886275, 1 },
108  { 0.933333, 0.933333, 0.92549, 1 }
109 };
110 
111 const GdkRGBA gruvbox_palette[PALETTE_SIZE] = {
112  { 0.156863, 0.156863, 0.156863, 1.000000 },
113  { 0.800000, 0.141176, 0.113725, 1.000000 },
114  { 0.596078, 0.592157, 0.101961, 1.000000 },
115  { 0.843137, 0.600000, 0.129412, 1.000000 },
116  { 0.270588, 0.521569, 0.533333, 1.000000 },
117  { 0.694118, 0.384314, 0.525490, 1.000000 },
118  { 0.407843, 0.615686, 0.415686, 1.000000 },
119  { 0.658824, 0.600000, 0.517647, 1.000000 },
120  { 0.572549, 0.513725, 0.454902, 1.000000 },
121  { 0.984314, 0.286275, 0.203922, 1.000000 },
122  { 0.721569, 0.733333, 0.149020, 1.000000 },
123  { 0.980392, 0.741176, 0.184314, 1.000000 },
124  { 0.513725, 0.647059, 0.596078, 1.000000 },
125  { 0.827451, 0.525490, 0.607843, 1.000000 },
126  { 0.556863, 0.752941, 0.486275, 1.000000 },
127  { 0.921569, 0.858824, 0.698039, 1.000000 },
128 };
129 
130 const GdkRGBA solarized_dark_palette[PALETTE_SIZE] = {
131  { 0.027451, 0.211765, 0.258824, 1 },
132  { 0.862745, 0.196078, 0.184314, 1 },
133  { 0.521569, 0.600000, 0.000000, 1 },
134  { 0.709804, 0.537255, 0.000000, 1 },
135  { 0.149020, 0.545098, 0.823529, 1 },
136  { 0.827451, 0.211765, 0.509804, 1 },
137  { 0.164706, 0.631373, 0.596078, 1 },
138  { 0.933333, 0.909804, 0.835294, 1 },
139  { 0.000000, 0.168627, 0.211765, 1 },
140  { 0.796078, 0.294118, 0.086275, 1 },
141  { 0.345098, 0.431373, 0.458824, 1 },
142  { 0.396078, 0.482353, 0.513725, 1 },
143  { 0.513725, 0.580392, 0.588235, 1 },
144  { 0.423529, 0.443137, 0.768627, 1 },
145  { 0.576471, 0.631373, 0.631373, 1 },
146  { 0.992157, 0.964706, 0.890196, 1 }
147 };
148 
149 const GdkRGBA solarized_light_palette[PALETTE_SIZE] = {
150  { 0.933333, 0.909804, 0.835294, 1 },
151  { 0.862745, 0.196078, 0.184314, 1 },
152  { 0.521569, 0.600000, 0.000000, 1 },
153  { 0.709804, 0.537255, 0.000000, 1 },
154  { 0.149020, 0.545098, 0.823529, 1 },
155  { 0.827451, 0.211765, 0.509804, 1 },
156  { 0.164706, 0.631373, 0.596078, 1 },
157  { 0.027451, 0.211765, 0.258824, 1 },
158  { 0.992157, 0.964706, 0.890196, 1 },
159  { 0.796078, 0.294118, 0.086275, 1 },
160  { 0.576471, 0.631373, 0.631373, 1 },
161  { 0.513725, 0.580392, 0.588235, 1 },
162  { 0.396078, 0.482353, 0.513725, 1 },
163  { 0.423529, 0.443137, 0.768627, 1 },
164  { 0.345098, 0.431373, 0.458824, 1 },
165  { 0.000000, 0.168627, 0.211765, 1 }
166 };
167 
168 const GdkRGBA xterm_palette[PALETTE_SIZE] = {
169  { 0, 0, 0, 1 },
170  { 0.803922, 0, 0, 1 },
171  { 0, 0.803922, 0, 1 },
172  { 0.803922, 0.803922, 0, 1 },
173  { 0.117647, 0.564706, 1, 1 },
174  { 0.803922, 0, 0.803922, 1 },
175  { 0, 0.803922, 0.803922, 1 },
176  { 0.898039, 0.898039, 0.898039, 1 },
177  { 0.298039, 0.298039, 0.298039, 1 },
178  { 1, 0, 0, 1 },
179  { 0, 1, 0, 1 },
180  { 1, 1, 0, 1 },
181  { 0.27451, 0.509804, 0.705882, 1 },
182  { 1, 0, 1, 1 },
183  { 0, 1, 1, 1 },
184  { 1, 1, 1, 1 }
185 };
186 
187 #if VTE_CHECK_VERSION(0, 38, 0)
188 static struct {
189  const GdkRGBA *palette;
190 } remminavte;
191 #endif
192 
193 #define DEFAULT_PALETTE "linux_palette"
194 
195 
197 typedef struct _RemminaPluginSshData {
200  GtkWidget * vte;
201 
202  const GdkRGBA * palette;
203 
204  pthread_t thread;
206 
207 
209 
210 static gboolean
211 remmina_plugin_ssh_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp);
212 
213 
220 static gpointer
222 {
223  TRACE_CALL(__func__);
225  RemminaPluginSshData *gpdata;
226  RemminaFile *remminafile;
227  RemminaSSH *ssh;
228  RemminaSSHShell *shell = NULL;
229  gboolean cont = FALSE;
230  gchar *hostport;
231  gint ret;
232 
233  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
234  CANCEL_ASYNC
235 
236  gpdata = GET_PLUGIN_DATA(gp);
237  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
238 
239  /* we may need to open a new tunnel */
240  hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE);
241  if (hostport == NULL) {
242  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
243  return NULL;
244  }
245 
246  ssh = g_object_get_data(G_OBJECT(gp), "user-data");
247  if (ssh) {
248  /* Create SSH shell connection based on existing SSH session */
249 
250  shell = remmina_ssh_shell_new_from_ssh(ssh);
251  remmina_plugin_service->get_server_port(hostport, 22, &ssh->tunnel_entrance_host, &ssh->tunnel_entrance_port);
252 
253  if (remmina_ssh_init_session(REMMINA_SSH(shell)) &&
254  remmina_ssh_auth(REMMINA_SSH(shell), NULL, gp, remminafile) == REMMINA_SSH_AUTH_SUCCESS &&
256  remmina_plugin_service->protocol_plugin_signal_connection_closed, gp))
257  cont = TRUE;
258  } else {
259 
260  /* New SSH Shell connection */
261  shell = remmina_ssh_shell_new_from_file(remminafile);
262  ssh = REMMINA_SSH(shell);
263  remmina_plugin_service->get_server_port(hostport, 22, &ssh->tunnel_entrance_host, &ssh->tunnel_entrance_port);
264 
265  while (1) {
266  if (!remmina_ssh_init_session(ssh)) {
267  remmina_debug("init session error: %s", ssh->error);
268  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error);
269  break;
270  }
271 
272  ret = remmina_ssh_auth_gui(ssh, gp, remminafile);
273  if (ret != REMMINA_SSH_AUTH_SUCCESS) {
274  if (ret != REMMINA_SSH_AUTH_USERCANCEL)
275  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error);
276  break;
277  }
278 
280  remmina_plugin_service->protocol_plugin_signal_connection_closed, gp)) {
281  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error);
282  break;
283  }
284 
285  cont = TRUE;
286  break;
287  }
288  }
289 
290  g_free(hostport);
291 
292  if (!cont) {
293  if (shell) remmina_ssh_shell_free(shell);
294  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
295  return NULL;
296  }
297 
298  gpdata->shell = shell;
299 
300  gchar *charset = REMMINA_SSH(shell)->charset;
301  remmina_plugin_ssh_vte_terminal_set_encoding_and_pty(VTE_TERMINAL(gpdata->vte), charset, shell->master, shell->slave);
302 
303  /* TODO: The following call should be moved on the main thread, or something weird could happen */
304  remmina_plugin_ssh_on_size_allocate(GTK_WIDGET(gpdata->vte), NULL, gp);
305 
306  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
307 
308  gpdata->thread = 0;
309  return NULL;
310 }
311 
312 void remmina_plugin_ssh_vte_terminal_set_encoding_and_pty(VteTerminal *terminal, const char *codeset, int master, int slave)
313 {
314  TRACE_CALL(__func__);
316  /* Allow the execution of this function from a non main thread */
318  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
319  d->func = FUNC_VTE_TERMINAL_SET_ENCODING_AND_PTY;
320  d->p.vte_terminal_set_encoding_and_pty.terminal = terminal;
321  d->p.vte_terminal_set_encoding_and_pty.codeset = codeset;
322  d->p.vte_terminal_set_encoding_and_pty.master = master;
324  g_free(d);
325  return;
326  }
327 
328  setlocale(LC_ALL, "");
329  if (codeset && codeset[0] != '\0') {
330 #if VTE_CHECK_VERSION(0, 38, 0)
331  vte_terminal_set_encoding(terminal, codeset, NULL);
332 #else
333  vte_terminal_set_emulation(terminal, "xterm");
334  vte_terminal_set_encoding(terminal, codeset);
335 #endif
336  }
337 
338  vte_terminal_set_backspace_binding(terminal, VTE_ERASE_ASCII_DELETE);
339  vte_terminal_set_delete_binding(terminal, VTE_ERASE_DELETE_SEQUENCE);
340 
341 #if VTE_CHECK_VERSION(0, 38, 0)
342  /* vte_pty_new_foreign expect master FD, see https://bugzilla.gnome.org/show_bug.cgi?id=765382 */
343  vte_terminal_set_pty(terminal, vte_pty_new_foreign_sync(master, NULL, NULL));
344 #else
345  vte_terminal_set_pty(terminal, master);
346 #endif
347 }
348 
349 static gboolean
350 remmina_plugin_ssh_on_focus_in(GtkWidget *widget, GdkEventFocus *event, RemminaProtocolWidget *gp)
351 {
352  TRACE_CALL(__func__);
353  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
354 
355  gtk_widget_grab_focus(gpdata->vte);
356  return TRUE;
357 }
358 
359 static gboolean
360 remmina_plugin_ssh_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
361 {
362  TRACE_CALL(__func__);
363  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
364  gint cols, rows;
365 
366  if (!gtk_widget_get_mapped(widget)) return FALSE;
367 
368  cols = vte_terminal_get_column_count(VTE_TERMINAL(widget));
369  rows = vte_terminal_get_row_count(VTE_TERMINAL(widget));
370 
371  if (gpdata->shell)
372  remmina_ssh_shell_set_size(gpdata->shell, cols, rows);
373 
374  return FALSE;
375 }
376 
377 static void
379 {
380  TRACE_CALL(__func__);
381  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
382  RemminaFile *remminafile;
383  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
384 
385  if (remmina_plugin_service->file_get_int(remminafile, "audiblebell", FALSE)) {
386  vte_terminal_set_audible_bell(VTE_TERMINAL(gpdata->vte), TRUE);
387  g_info("audible_bell set to %i", vte_terminal_get_audible_bell(VTE_TERMINAL(gpdata->vte)));
388  }
390 #if !VTE_CHECK_VERSION(0, 38, 0)
391  vte_terminal_set_font_from_string(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_font);
392 #else
393  vte_terminal_set_font(VTE_TERMINAL(gpdata->vte),
394  pango_font_description_from_string(remmina_pref.vte_font));
395 #endif
396  }
397  vte_terminal_set_allow_bold(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_allow_bold_text);
398  if (remmina_pref.vte_lines > 0)
399  vte_terminal_set_scrollback_lines(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_lines);
400 }
401 
402 void
403 remmina_plugin_ssh_vte_select_all(GtkMenuItem *menuitem, gpointer vte)
404 {
405  TRACE_CALL(__func__);
406  vte_terminal_select_all(VTE_TERMINAL(vte));
408 }
409 
410 void
411 remmina_plugin_ssh_vte_copy_clipboard(GtkMenuItem *menuitem, gpointer vte)
412 {
413  TRACE_CALL(__func__);
414 #if VTE_CHECK_VERSION(0, 50, 0)
415  vte_terminal_copy_clipboard_format(VTE_TERMINAL(vte), VTE_FORMAT_TEXT);
416 #else
417  vte_terminal_copy_clipboard(VTE_TERMINAL(vte));
418 #endif
419 }
420 
421 void
422 remmina_plugin_ssh_vte_paste_clipboard(GtkMenuItem *menuitem, gpointer vte)
423 {
424  TRACE_CALL(__func__);
425  vte_terminal_paste_clipboard(VTE_TERMINAL(vte));
426 }
427 
428 void
430 {
431  TRACE_CALL(__func__);
432  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
433 
434  GtkWidget *widget;
435  GError *err = NULL;
436 
437  GFileOutputStream *stream = g_file_replace(gpdata->vte_session_file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &err);
438 
439  if (err != NULL) {
440  // TRANSLATORS: %s is a placeholder for an error message
441  widget = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
442  _("Error: %s"), err->message);
443  g_signal_connect(G_OBJECT(widget), "response", G_CALLBACK(gtk_widget_destroy), NULL);
444  gtk_widget_show(widget);
445  return;
446  }
447 
448  if (stream != NULL)
449 #if VTE_CHECK_VERSION(0, 38, 0)
450  vte_terminal_write_contents_sync(VTE_TERMINAL(gpdata->vte), G_OUTPUT_STREAM(stream),
451  VTE_WRITE_DEFAULT, NULL, &err);
452 #else
453  vte_terminal_write_contents(VTE_TERMINAL(gpdata->vte), G_OUTPUT_STREAM(stream),
454  VTE_TERMINAL_WRITE_DEFAULT, NULL, &err);
455 #endif
456 
457  if (err == NULL) {
458  remmina_public_send_notification("remmina-terminal-saved",
459  _("Terminal content saved in"),
460  g_file_get_path(gpdata->vte_session_file));
461  }
462 
463  g_object_unref(stream);
464  g_free(err);
465 }
466 
468 static void remmina_ssh_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
469 {
470  TRACE_CALL(__func__);
471  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
472  remmina_plugin_service->protocol_plugin_send_keys_signals(gpdata->vte,
473  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
474  return;
475 }
476 
477 gboolean
478 remmina_ssh_plugin_popup_menu(GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
479 {
480  if ((event->type == GDK_BUTTON_PRESS) && (((GdkEventButton *)event)->button == 3)) {
481 #if GTK_CHECK_VERSION(3, 22, 0)
482  gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
483 #else
484  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
485  ((GdkEventButton *)event)->button, gtk_get_current_event_time());
486 #endif
487  return TRUE;
488  }
489 
490  return FALSE;
491 }
492 
507 {
508  TRACE_CALL(__func__);
509  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
510  /* Context menu for slection and clipboard */
511  GtkWidget *menu = gtk_menu_new();
512 
513  GtkWidget *select_all = gtk_menu_item_new_with_label(_("Select All (Host + A)"));
514  GtkWidget *copy = gtk_menu_item_new_with_label(_("Copy (host + C)"));
515  GtkWidget *paste = gtk_menu_item_new_with_label(_("Paste (host + V)"));
516  GtkWidget *save = gtk_menu_item_new_with_label(_("Save session to file"));
517 
518  gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_all);
519  gtk_menu_shell_append(GTK_MENU_SHELL(menu), copy);
520  gtk_menu_shell_append(GTK_MENU_SHELL(menu), paste);
521  gtk_menu_shell_append(GTK_MENU_SHELL(menu), save);
522 
523  g_signal_connect(G_OBJECT(gpdata->vte), "button_press_event",
524  G_CALLBACK(remmina_ssh_plugin_popup_menu), menu);
525 
526  g_signal_connect(G_OBJECT(select_all), "activate",
527  G_CALLBACK(remmina_plugin_ssh_vte_select_all), gpdata->vte);
528  g_signal_connect(G_OBJECT(copy), "activate",
529  G_CALLBACK(remmina_plugin_ssh_vte_copy_clipboard), gpdata->vte);
530  g_signal_connect(G_OBJECT(paste), "activate",
531  G_CALLBACK(remmina_plugin_ssh_vte_paste_clipboard), gpdata->vte);
532  g_signal_connect(G_OBJECT(save), "activate",
533  G_CALLBACK(remmina_plugin_ssh_vte_save_session), gp);
534 
535  gtk_widget_show_all(menu);
536 }
537 
550 static void
552 {
553  TRACE_CALL(__func__);
554  RemminaPluginSshData *gpdata;
555  RemminaFile *remminafile;
556  GtkWidget *hbox;
557  GtkAdjustment *vadjustment;
558  GtkWidget *vscrollbar;
559  GtkWidget *vte;
560  GdkRGBA foreground_color;
561  GdkRGBA background_color;
562 
563 
564 #if !VTE_CHECK_VERSION(0, 38, 0)
565  GdkColor foreground_gdkcolor;
566  GdkColor background_gdkcolor;
567 #endif /* VTE_CHECK_VERSION(0,38,0) */
568 
569  gpdata = g_new0(RemminaPluginSshData, 1);
570  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
571 
572  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
573  gtk_widget_show(hbox);
574  gtk_container_add(GTK_CONTAINER(gp), hbox);
575  g_signal_connect(G_OBJECT(hbox), "focus-in-event", G_CALLBACK(remmina_plugin_ssh_on_focus_in), gp);
576 
577  vte = vte_terminal_new();
578  gtk_widget_show(vte);
579  vte_terminal_set_size(VTE_TERMINAL(vte), 80, 25);
580  vte_terminal_set_scroll_on_keystroke(VTE_TERMINAL(vte), TRUE);
581 #if !VTE_CHECK_VERSION(0, 38, 0)
582  gdk_rgba_parse(&foreground_color, remmina_pref.color_pref.foreground);
583  gdk_rgba_parse(&background_color, remmina_pref.color_pref.background);
584 #endif
585  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
586 
587 #if VTE_CHECK_VERSION(0, 38, 0)
588  GdkRGBA cp[PALETTE_SIZE];
589  GdkRGBA cursor_color;
590 
591  /*
592  * custom colors reside inside of the 'theme' subdir of the remmina config folder (.config/remmina/theme)
593  * with the file extension '.colors'. The name of the colorfile came from the menu (see below)
594  * sideeffect: it is possible to overwrite the standard colors with a dedicated colorfile like
595  * '0.colors' for GRUVBOX, '1.colors' for TANGO and so on
596  */
597  const gchar *color_name = remmina_plugin_service->file_get_string(remminafile, "ssh_color_scheme");
598 
599  gchar *remmina_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "theme", NULL);
600  gchar *remmina_colors_file = g_strdup_printf("%s/%s.colors", remmina_dir, color_name);
601  g_free(remmina_dir);
602 
603  /*
604  * try to load theme from one of the system data dirs (based on XDG_DATA_DIRS environment var)
605  */
606  if (!g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR)) {
607  GError *error = NULL;
608  const gchar *const *dirs = g_get_system_data_dirs();
609 
610  unsigned int i = 0;
611  for (i = 0; dirs[i] != NULL; ++i) {
612  remmina_dir = g_build_path("/", dirs[i], "remmina", "theme", NULL);
613  GDir *system_data_dir = g_dir_open(remmina_dir, 0, &error);
614  // ignoring this error is OK, because the folder may not exist
615  if (error) {
616  g_error_free(error);
617  error = NULL;
618  } else {
619  if (system_data_dir) {
620  g_dir_close(system_data_dir);
621  g_free(remmina_colors_file);
622  remmina_colors_file = g_strdup_printf("%s/%s.colors", remmina_dir, color_name);
623  if (g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR))
624  break;
625  }
626  }
627  g_free(remmina_dir);
628  }
629  }
630 
631  if (g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR)) {
632  GKeyFile *gkeyfile;
633  RemminaColorPref color_pref;
634 
635  gkeyfile = g_key_file_new();
636  g_key_file_load_from_file(gkeyfile, remmina_colors_file, G_KEY_FILE_NONE, NULL);
637  remmina_pref_file_load_colors(gkeyfile, &color_pref);
638 
639  gdk_rgba_parse(&foreground_color, color_pref.foreground);
640  gdk_rgba_parse(&background_color, color_pref.background);
641  gdk_rgba_parse(&cursor_color, color_pref.cursor);
642 
643  gdk_rgba_parse(&cp[0], color_pref.color0);
644  gdk_rgba_parse(&cp[1], color_pref.color1);
645  gdk_rgba_parse(&cp[2], color_pref.color2);
646  gdk_rgba_parse(&cp[3], color_pref.color3);
647  gdk_rgba_parse(&cp[4], color_pref.color4);
648  gdk_rgba_parse(&cp[5], color_pref.color5);
649  gdk_rgba_parse(&cp[6], color_pref.color6);
650  gdk_rgba_parse(&cp[7], color_pref.color7);
651  gdk_rgba_parse(&cp[8], color_pref.color8);
652  gdk_rgba_parse(&cp[9], color_pref.color9);
653  gdk_rgba_parse(&cp[10], color_pref.color10);
654  gdk_rgba_parse(&cp[11], color_pref.color11);
655  gdk_rgba_parse(&cp[12], color_pref.color12);
656  gdk_rgba_parse(&cp[13], color_pref.color13);
657  gdk_rgba_parse(&cp[14], color_pref.color14);
658  gdk_rgba_parse(&cp[15], color_pref.color15);
659 
660  const GdkRGBA custom_palette[PALETTE_SIZE] = {
661  cp[0], cp[1], cp[2], cp[3],
662  cp[4], cp[5], cp[6], cp[7],
663  cp[8], cp[9], cp[10], cp[11],
664  cp[12], cp[13], cp[14], cp[15]
665  };
666 
667  remminavte.palette = custom_palette;
668  } else {
669  /* Set colors to GdkRGBA */
670  switch (remmina_plugin_service->file_get_int(remminafile, "ssh_color_scheme", FALSE)) {
671  case LINUX:
672  gdk_rgba_parse(&foreground_color, "#ffffff");
673  gdk_rgba_parse(&background_color, "#000000");
674  gdk_rgba_parse(&cursor_color, "#ffffff");
675  remminavte.palette = linux_palette;
676  break;
677  case TANGO:
678  gdk_rgba_parse(&foreground_color, "#eeeeec");
679  gdk_rgba_parse(&background_color, "#2e3436");
680  gdk_rgba_parse(&cursor_color, "#8ae234");
681  remminavte.palette = tango_palette;
682  break;
683  case GRUVBOX:
684  gdk_rgba_parse(&foreground_color, "#ebdbb2");
685  gdk_rgba_parse(&background_color, "#282828");
686  gdk_rgba_parse(&cursor_color, "#d3869b");
687  remminavte.palette = gruvbox_palette;
688  break;
689  case SOLARIZED_DARK:
690  gdk_rgba_parse(&foreground_color, "#839496");
691  gdk_rgba_parse(&background_color, "#002b36");
692  gdk_rgba_parse(&cursor_color, "#93a1a1");
694  break;
695  case SOLARIZED_LIGHT:
696  gdk_rgba_parse(&foreground_color, "#657b83");
697  gdk_rgba_parse(&background_color, "#fdf6e3");
698  gdk_rgba_parse(&cursor_color, "#586e75");
700  break;
701  case XTERM:
702  gdk_rgba_parse(&foreground_color, "#000000");
703  gdk_rgba_parse(&background_color, "#ffffff");
704  gdk_rgba_parse(&cursor_color, "#000000");
705  remminavte.palette = xterm_palette;
706  break;
707  case CUSTOM:
708  gdk_rgba_parse(&foreground_color, remmina_pref.color_pref.foreground);
709  gdk_rgba_parse(&background_color, remmina_pref.color_pref.background);
710  gdk_rgba_parse(&cursor_color, remmina_pref.color_pref.cursor);
711 
712  gdk_rgba_parse(&cp[0], remmina_pref.color_pref.color0);
713  gdk_rgba_parse(&cp[1], remmina_pref.color_pref.color1);
714  gdk_rgba_parse(&cp[2], remmina_pref.color_pref.color2);
715  gdk_rgba_parse(&cp[3], remmina_pref.color_pref.color3);
716  gdk_rgba_parse(&cp[4], remmina_pref.color_pref.color4);
717  gdk_rgba_parse(&cp[5], remmina_pref.color_pref.color5);
718  gdk_rgba_parse(&cp[6], remmina_pref.color_pref.color6);
719  gdk_rgba_parse(&cp[7], remmina_pref.color_pref.color7);
720  gdk_rgba_parse(&cp[8], remmina_pref.color_pref.color8);
721  gdk_rgba_parse(&cp[9], remmina_pref.color_pref.color9);
722  gdk_rgba_parse(&cp[10], remmina_pref.color_pref.color10);
723  gdk_rgba_parse(&cp[11], remmina_pref.color_pref.color11);
724  gdk_rgba_parse(&cp[12], remmina_pref.color_pref.color12);
725  gdk_rgba_parse(&cp[13], remmina_pref.color_pref.color13);
726  gdk_rgba_parse(&cp[14], remmina_pref.color_pref.color14);
727  gdk_rgba_parse(&cp[15], remmina_pref.color_pref.color15);
728 
729  const GdkRGBA custom_palette[PALETTE_SIZE] = {
730  cp[0], cp[1], cp[2], cp[3],
731  cp[4], cp[5], cp[6], cp[7],
732  cp[8], cp[9], cp[10], cp[11],
733  cp[12], cp[13], cp[14], cp[15]
734  };
735 
736  remminavte.palette = custom_palette;
737  break;
738  default:
739  remminavte.palette = linux_palette;
740  break;
741  }
742  }
743  g_free(remmina_colors_file);
744  vte_terminal_set_colors(VTE_TERMINAL(vte), &foreground_color, &background_color, remminavte.palette, PALETTE_SIZE);
745  vte_terminal_set_color_foreground(VTE_TERMINAL(vte), &foreground_color);
746  vte_terminal_set_color_background(VTE_TERMINAL(vte), &background_color);
747  vte_terminal_set_color_cursor(VTE_TERMINAL(vte), &cursor_color);
748 #else
749  /* VTE <= 2.90 doesn’t support GdkRGBA so we must convert GdkRGBA to GdkColor */
750  foreground_gdkcolor.red = (guint16)(foreground_color.red * 0xFFFF);
751  foreground_gdkcolor.green = (guint16)(foreground_color.green * 0xFFFF);
752  foreground_gdkcolor.blue = (guint16)(foreground_color.blue * 0xFFFF);
753  background_gdkcolor.red = (guint16)(background_color.red * 0xFFFF);
754  background_gdkcolor.green = (guint16)(background_color.green * 0xFFFF);
755  background_gdkcolor.blue = (guint16)(background_color.blue * 0xFFFF);
756  /* Set colors to GdkColor */
757  vte_terminal_set_colors(VTE_TERMINAL(vte), &foreground_gdkcolor, &background_gdkcolor, NULL, 0);
758 #endif
759 
760  gtk_box_pack_start(GTK_BOX(hbox), vte, TRUE, TRUE, 0);
761  gpdata->vte = vte;
763  g_signal_connect(G_OBJECT(vte), "size-allocate", G_CALLBACK(remmina_plugin_ssh_on_size_allocate), gp);
764 
765  remmina_plugin_service->protocol_plugin_register_hostkey(gp, vte);
766 
767 #if VTE_CHECK_VERSION(0, 28, 0)
768  vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vte));
769 #else
770  vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
771 #endif
772 
773  vscrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
774 
775  gtk_widget_show(vscrollbar);
776  gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, TRUE, 0);
777 
778  const gchar *dir;
779  const gchar *sshlogname;
780  const gchar *fp;
781  GFile *rf;
782 
783  rf = g_file_new_for_path(remminafile->filename);
784 
785  if (remmina_plugin_service->file_get_string(remminafile, "sshlogfolder") == NULL)
786  dir = g_build_path("/", g_get_user_cache_dir(), "remmina", NULL);
787  else
788  dir = remmina_plugin_service->file_get_string(remminafile, "sshlogfolder");
789 
790  if (remmina_plugin_service->file_get_string(remminafile, "sshlogname") == NULL)
791  sshlogname = g_strconcat(g_file_get_basename(rf), ".", "log", NULL);
792  else
793  sshlogname = remmina_plugin_service->file_get_string(remminafile, "sshlogname");
794 
795  sshlogname = remmina_file_format_properties(remminafile, sshlogname);
796 
797  fp = g_strconcat(dir, "/", sshlogname, NULL);
798  gpdata->vte_session_file = g_file_new_for_path(fp);
799 
801 }
802 
810 static gboolean
812 {
813  TRACE_CALL(__func__);
814  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
815 
816  remmina_plugin_service->protocol_plugin_set_expand(gp, TRUE);
817  remmina_plugin_service->protocol_plugin_set_width(gp, 640);
818  remmina_plugin_service->protocol_plugin_set_height(gp, 480);
819 
820  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_ssh_main_thread, gp)) {
821  remmina_plugin_service->protocol_plugin_set_error(gp,
822  "Failed to initialize pthread. Falling back to non-thread mode…");
823  gpdata->thread = 0;
824  return FALSE;
825  } else {
826  return TRUE;
827  }
828  return TRUE;
829 }
830 
831 static gboolean
833 {
834  TRACE_CALL(__func__);
835  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
836 
837  RemminaFile *remminafile;
838 
839  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
840 
841  if (remmina_file_get_int(remminafile, "sshlogenabled", FALSE))
843  if (gpdata->thread) {
844  pthread_cancel(gpdata->thread);
845  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
846  }
847  if (gpdata->shell) {
848  remmina_ssh_shell_free(gpdata->shell);
849  gpdata->shell = NULL;
850  }
851 
852  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
853  return FALSE;
854 }
855 
861 static gboolean
863 {
864  TRACE_CALL(__func__);
865  return TRUE;
866 }
867 
884 static void
886 {
887  TRACE_CALL(__func__);
888  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
889 
890  switch (feature->id) {
891  case REMMINA_PROTOCOL_FEATURE_TOOL_SSH:
892  remmina_plugin_service->open_connection(
893  remmina_file_dup_temp_protocol(remmina_plugin_service->protocol_plugin_get_file(gp), "SSH"),
894  NULL, gpdata->shell, NULL);
895  return;
896  case REMMINA_PROTOCOL_FEATURE_TOOL_SFTP:
897  remmina_plugin_service->open_connection(
899  remmina_file_dup_temp_protocol(remmina_plugin_service->protocol_plugin_get_file(gp), "SFTP"),
900  NULL, gpdata->shell, NULL);
901  return;
902  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_COPY:
903 #if VTE_CHECK_VERSION(0, 50, 0)
904  vte_terminal_copy_clipboard_format(VTE_TERMINAL(gpdata->vte), VTE_FORMAT_TEXT);
905 #else
906  vte_terminal_copy_clipboard(VTE_TERMINAL(gpdata->vte));
907 #endif
908  return;
909  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_PASTE:
910  vte_terminal_paste_clipboard(VTE_TERMINAL(gpdata->vte));
911  return;
912  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_SELECT_ALL:
913  vte_terminal_select_all(VTE_TERMINAL(gpdata->vte));
914  return;
915  }
916 }
917 
919 static gpointer ssh_auth[] =
920 {
921  "0", N_("Password"),
922  "1", N_("SSH identity file"),
923  "2", N_("SSH agent"),
924  "3", N_("Public key (automatic)"),
925  "4", N_("Kerberos (GSSAPI)"),
926  NULL
927 };
928 
930 static gpointer ssh_charset_list[] =
931 {
932  "", "",
933  "", "ASCII",
934  "", "BIG5",
935  "", "CP437",
936  "", "CP720",
937  "", "CP737",
938  "", "CP775",
939  "", "CP850",
940  "", "CP852",
941  "", "CP855",
942  "", "CP857",
943  "", "CP858",
944  "", "CP862",
945  "", "CP866",
946  "", "CP874",
947  "", "CP1125",
948  "", "CP1250",
949  "", "CP1251",
950  "", "CP1252",
951  "", "CP1253",
952  "", "CP1254",
953  "", "CP1255",
954  "", "CP1256",
955  "", "CP1257",
956  "", "CP1258",
957  "", "EUC-JP",
958  "", "EUC-KR",
959  "", "GBK",
960  "", "ISO-8859-1",
961  "", "ISO-8859-2",
962  "", "ISO-8859-3",
963  "", "ISO-8859-4",
964  "", "ISO-8859-5",
965  "", "ISO-8859-6",
966  "", "ISO-8859-7",
967  "", "ISO-8859-8",
968  "", "ISO-8859-9",
969  "", "ISO-8859-10",
970  "", "ISO-8859-11",
971  "", "ISO-8859-12",
972  "", "ISO-8859-13",
973  "", "ISO-8859-14",
974  "", "ISO-8859-15",
975  "", "ISO-8859-16",
976  "", "KOI8-R",
977  "", "SJIS",
978  "", "UTF-8",
979  NULL
980 };
981 
982 static gpointer ssh_terminal_palette[] =
983 {
984  "0", "Linux",
985  "1", "Tango",
986  "2", "Gruvbox",
987  "3", "Solarized Dark",
988  "4", "Solarized Light",
989  "5", "XTerm",
990  "6", "Custom (Configured in Remmina preferences)",
991  NULL, NULL
992 };
993 
999 {
1000  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_COPY, N_("Copy"), N_("_Copy"), NULL },
1001  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_PASTE, N_("Paste"), N_("_Paste"), NULL },
1002  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_SELECT_ALL, N_("Select all"), N_("_Select all"), NULL },
1003  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
1004 };
1005 
1018 {
1019  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_ssh._tcp", NULL },
1020  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL },
1021  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL },
1022  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_auth", N_("Authentication type"), FALSE, ssh_auth, NULL },
1023  { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "ssh_privatekey", N_("Identity file"), FALSE, NULL, NULL },
1024  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "ssh_passphrase", N_("Password to unlock private key"), FALSE, NULL, NULL },
1025  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Startup program"), FALSE, NULL, NULL },
1026  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
1027 };
1028 
1029 static gchar log_tips[] =
1030  N_("The filename can use the following placeholders:\n\n"
1031  " • %h is substituted with the server name\n"
1032  " • %t is substituted with the SSH server name\n"
1033  " • %u is substituted with the username\n"
1034  " • %U is substituted with the SSH username\n"
1035  " • %p is substituted with Remmina profile name\n"
1036  " • %g is substituted with Remmina profile group name\n"
1037  " • %d is substituted with local datetime in iso8601 format\n");
1038 
1051 {
1052  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_color_scheme", N_("Terminal color scheme"), FALSE, ssh_terminal_palette, NULL },
1053  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_charset", N_("Character set"), FALSE, ssh_charset_list, NULL },
1054  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_proxycommand", N_("SSH Proxy Command"), FALSE, NULL, NULL },
1055  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_kex_algorithms", N_("KEX (Key Exchange) algorithms"), FALSE, NULL, NULL },
1056  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_ciphers", N_("Symmetric cipher client to server"), FALSE, NULL, NULL },
1057  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_hostkeytypes", N_("Preferred server host key types"), FALSE, NULL, NULL },
1058  { REMMINA_PROTOCOL_SETTING_TYPE_FOLDER, "sshlogfolder", N_("Folder for SSH session log"), FALSE, NULL, NULL },
1059  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "sshlogname", N_("Filename for SSH session log"), FALSE, NULL, log_tips },
1060  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sshlogenabled", N_("Log SSH session when exiting Remmina"), FALSE, NULL, NULL },
1061  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "audiblebell", N_("Audible terminal bell"), FALSE, NULL, NULL },
1062  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "ssh_compression", N_("SSH compression"), FALSE, NULL, NULL },
1063  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Don't remember passwords"), TRUE, NULL, NULL },
1064  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "ssh_stricthostkeycheck", N_("Strict host key checking"), TRUE, NULL, NULL },
1065  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
1066 };
1067 
1075 {
1077  "SSH",
1078  N_("SSH - Secure Shell"),
1079  GETTEXT_PACKAGE,
1080  VERSION,
1081  "remmina-ssh-symbolic",
1082  "remmina-ssh-symbolic",
1093  NULL
1094 };
1095 
1096 
1097 /*
1098  * this function is used for
1099  * - inserting into the list to became a sorted list [g_list_insert_sorted()]
1100  * - checking the list to avoid duplicate entries [g_list_find_custom()]
1101  */
1102 static gint
1103 compare(gconstpointer a, gconstpointer b)
1104 {
1105  return strcmp((gchar *)a, (gchar *)b);
1106 }
1107 
1108 void
1109 remmina_ssh_plugin_load_terminal_palettes(gpointer *ssh_terminal_palette_new)
1110 {
1111  unsigned int preset_rec_size = sizeof(ssh_terminal_palette) / sizeof(gpointer);
1112 
1113  GError *error = NULL;
1114  GList *files = NULL;
1115  unsigned int rec_size = 0;
1116  /*
1117  * count number of (all) files to reserve enough memory
1118  */
1119  /* /usr/local/share/remmina */
1120  const gchar *const *dirs = g_get_system_data_dirs();
1121 
1122  unsigned int i = 0;
1123 
1124  for (i = 0; dirs[i] != NULL; ++i) {
1125  GDir *system_data_dir = NULL;
1126  gchar *remmina_dir = g_build_path("/", dirs[i], "remmina", "theme", NULL);
1127  system_data_dir = g_dir_open(remmina_dir, 0, &error);
1128  g_free(remmina_dir);
1129  // ignoring this error is OK, because the folder may not exist
1130  if (error) {
1131  g_error_free(error);
1132  error = NULL;
1133  } else {
1134  if (system_data_dir) {
1135  const gchar *filename;
1136  while ((filename = g_dir_read_name(system_data_dir))) {
1137  if (!g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1138  if (g_str_has_suffix(filename, ".colors")) {
1139  gsize len = strrchr(filename, '.') - filename;
1140  gchar *menu_str = g_strndup(filename, len);
1141  if (g_list_find_custom(files, menu_str, compare) == NULL)
1142  files = g_list_insert_sorted(files, menu_str, compare);
1143  }
1144  }
1145  }
1146  }
1147  }
1148  }
1149 
1150  /* ~/.config/remmina/colors */
1151  gchar *remmina_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "theme", NULL);
1152  GDir *user_data_dir;
1153  user_data_dir = g_dir_open(remmina_dir, 0, &error);
1154  g_free(remmina_dir);
1155  if (error) {
1156  g_error_free(error);
1157  error = NULL;
1158  } else {
1159  if (user_data_dir) {
1160  const gchar *filename;
1161  while ((filename = g_dir_read_name(user_data_dir))) {
1162  if (!g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1163  if (g_str_has_suffix(filename, ".colors")) {
1164  char *menu_str = g_malloc(strlen(filename) + 1);
1165  strcpy(menu_str, filename);
1166  char *t2 = strrchr(menu_str, '.');
1167  t2[0] = 0;
1168  if (g_list_find_custom(files, menu_str, compare) == NULL)
1169  files = g_list_insert_sorted(files, menu_str, compare);
1170  }
1171  }
1172  }
1173  }
1174  }
1175 
1176  rec_size = g_list_length(files) * 2;
1177 
1178  gpointer *color_palette = g_malloc((preset_rec_size + rec_size) * sizeof(gpointer));
1179 
1180  unsigned int field_idx = 0;
1181  *ssh_terminal_palette_new = color_palette;
1182  // preset with (old) static ssh_terminal_palette data
1183  for (; field_idx < preset_rec_size; field_idx++) {
1184  color_palette[field_idx] = ssh_terminal_palette[field_idx];
1185  if (!color_palette[field_idx])
1186  break;
1187  }
1188 
1189  GList *l_files = NULL;
1190  for (l_files = g_list_first(files); l_files != NULL; l_files = l_files->next) {
1191  gchar *menu_str = (gchar *)l_files->data;
1192 
1193  color_palette[field_idx++] = menu_str;
1194  color_palette[field_idx++] = menu_str;
1195  }
1196  g_list_free(files);
1197 
1198  color_palette[field_idx] = NULL;
1199 }
1200 
1201 void
1203 {
1204  TRACE_CALL(__func__);
1205  remmina_plugin_ssh_features[0].opt3 = GUINT_TO_POINTER(remmina_pref.vte_shortcutkey_copy);
1206  remmina_plugin_ssh_features[1].opt3 = GUINT_TO_POINTER(remmina_pref.vte_shortcutkey_paste);
1207  remmina_plugin_ssh_features[2].opt3 = GUINT_TO_POINTER(remmina_pref.vte_shortcutkey_select_all);
1208  remmina_plugin_ssh_features[3].opt3 = GUINT_TO_POINTER(remmina_pref.vte_shortcutkey_select_all);
1209 
1210  remmina_plugin_service = &remmina_plugin_manager_service;
1211 
1212  RemminaProtocolSettingOpt *settings;
1213 
1214  // preset new settings with (old) static remmina_ssh_advanced_settings data
1215  settings = g_memdup(remmina_ssh_advanced_settings, sizeof(remmina_ssh_advanced_settings));
1216 
1217  // create dynamic advanced settings to made replacing of ssh_terminal_palette possible
1218  gpointer ssh_terminal_palette_new = NULL;
1219 
1220  remmina_ssh_plugin_load_terminal_palettes(&ssh_terminal_palette_new);
1221 
1222  settings[0].opt1 = ssh_terminal_palette_new;
1223  remmina_plugin_ssh.advanced_settings = (RemminaProtocolSetting *)settings;
1224 
1225  remmina_plugin_service->register_plugin((RemminaPlugin *)&remmina_plugin_ssh);
1226 
1227  ssh_threads_set_callbacks(ssh_threads_get_pthread());
1228  ssh_init();
1229 }
1230 
1231 #else
1232 
1233 void remmina_ssh_plugin_register(void)
1234 {
1235  TRACE_CALL(__func__);
1236 }
1237 
1238 #endif
enum remmina_ssh_auth_result remmina_ssh_auth_gui(RemminaSSH *ssh, RemminaProtocolWidget *gp, RemminaFile *remminafile)
RemminaSSHShell * shell
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:158
RemminaSSHShell * remmina_ssh_shell_new_from_ssh(RemminaSSH *ssh)
gchar * error
Definition: remmina_ssh.h:79
void remmina_public_send_notification(const gchar *notification_id, const gchar *notification_title, const gchar *notification_message)
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
RemminaFile * remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
Definition: remmina_file.c:711
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:151
struct _RemminaPluginSshData RemminaPluginSshData
The SSH plugin implementation.
static gboolean remmina_plugin_ssh_close_connection(RemminaProtocolWidget *gp)
guint vte_shortcutkey_copy
Definition: remmina_pref.h:169
enum remmina_masterthread_exec_data::@10 func
const GdkRGBA tango_palette[PALETTE_SIZE]
const GdkRGBA solarized_light_palette[PALETTE_SIZE]
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
struct remmina_masterthread_exec_data::@11::@24 vte_terminal_set_encoding_and_pty
void remmina_plugin_ssh_vte_terminal_set_encoding_and_pty(VteTerminal *terminal, const char *codeset, int master, int slave)
guint vte_shortcutkey_select_all
Definition: remmina_pref.h:171
static RemminaProtocolFeature remmina_plugin_ssh_features[]
Array for available features.
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:221
static gpointer ssh_charset_list[]
Charset list.
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:203
gboolean vte_allow_bold_text
Definition: remmina_pref.h:166
static gboolean remmina_plugin_ssh_open_connection(RemminaProtocolWidget *gp)
Initialize the main window properties and the pthread.
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
gchar * remmina_file_format_properties(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:494
void remmina_debug(const gchar *fmt,...)
Print a string in the Remmina Debug Windows and in the terminal.
Definition: remmina_log.c:194
static gboolean remmina_plugin_ssh_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
void remmina_ssh_shell_free(RemminaSSHShell *shell)
const GdkRGBA gruvbox_palette[PALETTE_SIZE]
gchar * vte_font
Definition: remmina_pref.h:165
void remmina_ssh_plugin_register(void)
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
gboolean remmina_masterthread_exec_is_main_thread()
static const RemminaProtocolSetting remmina_ssh_basic_settings[]
Array of RemminaProtocolSetting for basic settings.
RemminaPluginService remmina_plugin_manager_service
union remmina_masterthread_exec_data::@11 p
static gint compare(gconstpointer a, gconstpointer b)
gboolean remmina_ssh_plugin_popup_menu(GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
static RemminaPluginService * remmina_plugin_service
gint tunnel_entrance_port
Definition: remmina_ssh.h:87
RemminaSSHShell * remmina_ssh_shell_new_from_file(RemminaFile *remminafile)
void remmina_plugin_ssh_popup_ui(RemminaProtocolWidget *gp)
Remmina SSH plugin terminal popup menu.
void remmina_plugin_ssh_vte_save_session(GtkMenuItem *menuitem, RemminaProtocolWidget *gp)
guint vte_shortcutkey_paste
Definition: remmina_pref.h:170
static void remmina_plugin_ssh_init(RemminaProtocolWidget *gp)
Remmina SSH plugin initialization.
static struct @36 remminavte
static RemminaProtocolPlugin remmina_plugin_ssh
SSH Protocol plugin definition and features.
static void remmina_plugin_ssh_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Functions to call when an entry in the Tool menu in the Remmina Connection Window is clicked...
void remmina_plugin_ssh_vte_select_all(GtkMenuItem *menuitem, gpointer vte)
void remmina_ssh_plugin_load_terminal_palettes(gpointer *ssh_terminal_palette_new)
static gchar log_tips[]
void(* protocol_plugin_set_expand)(RemminaProtocolWidget *gp, gboolean expand)
Definition: plugin.h:156
static gpointer ssh_terminal_palette[]
gboolean remmina_ssh_shell_open(RemminaSSHShell *shell, RemminaSSHExitFunc exit_callback, gpointer data)
gchar * tunnel_entrance_host
Definition: remmina_ssh.h:86
void remmina_masterthread_exec_and_wait(RemminaMTExecData *d)
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:160
void remmina_plugin_ssh_vte_paste_clipboard(GtkMenuItem *menuitem, gpointer vte)
RemminaPref remmina_pref
Definition: rcw.c:73
static gboolean remmina_plugin_ssh_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Not used by the plugin.
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:524
const GdkRGBA xterm_palette[PALETTE_SIZE]
const GdkRGBA * palette
const GdkRGBA linux_palette[PALETTE_SIZE]
16 color palettes in GdkRGBA format (red, green, blue, alpha).
static gpointer ssh_auth[]
Array of key/value pairs for SSH auth type.
gchar * remmina_colors_file
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
static gpointer remmina_plugin_ssh_main_thread(gpointer data)
Remmina protocol plugin main function.
const RemminaProtocolSetting * advanced_settings
Definition: plugin.h:71
const GdkRGBA solarized_dark_palette[PALETTE_SIZE]
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:153
static gboolean remmina_plugin_ssh_on_focus_in(GtkWidget *widget, GdkEventFocus *event, RemminaProtocolWidget *gp)
void remmina_plugin_ssh_vte_copy_clipboard(GtkMenuItem *menuitem, gpointer vte)
void(* RemminaSSHExitFunc)(gpointer data)
Definition: remmina_ssh.h:243
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:163
The SSH plugin implementation.
GtkWidget *(* open_connection)(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: plugin.h:220
static void remmina_plugin_ssh_set_vte_pref(RemminaProtocolWidget *gp)
static void remmina_ssh_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Send a keystroke to the plugin window.
static const RemminaProtocolSetting remmina_ssh_advanced_settings[]
Array of RemminaProtocolSetting for advanced settings.
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
RemminaColorPref color_pref
Definition: remmina_pref.h:197
void remmina_pref_file_load_colors(GKeyFile *gkeyfile, RemminaColorPref *color_pref)
Definition: remmina_pref.c:183
color_schemes
void remmina_ssh_shell_set_size(RemminaSSHShell *shell, gint columns, gint rows)
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
gboolean remmina_ssh_init_session(RemminaSSH *ssh)
enum remmina_ssh_auth_result remmina_ssh_auth(RemminaSSH *ssh, const gchar *password, RemminaProtocolWidget *gp, RemminaFile *remminafile)
Definition: remmina_ssh.c:336