Remmina - The GTK+ Remote Desktop Client  v1.4.34
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 
39 #include "config.h"
41 
42 #if defined (HAVE_LIBSSH) && defined (HAVE_LIBVTE)
43 
44 #include <gtk/gtk.h>
45 #include <glib/gi18n.h>
46 #include <gio/gio.h>
47 #include <glib-object.h>
48 #include <gobject/gvaluecollector.h>
49 #include <vte/vte.h>
50 #include <locale.h>
51 #include <langinfo.h>
52 #include "remmina_log.h"
53 #include "remmina_public.h"
54 #include "remmina_plugin_manager.h"
55 #include "remmina_ssh.h"
57 #include "remmina_pref.h"
58 #include "remmina_ssh_plugin.h"
60 
61 #define PCRE2_CODE_UNIT_WIDTH 8
62 #include <pcre2.h>
63 
64 
65 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_COPY 1
66 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_PASTE 2
67 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_SELECT_ALL 3
68 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_INCREASE_FONT 4
69 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_DECREASE_FONT 5
70 #define REMMINA_PLUGIN_SSH_FEATURE_TOOL_SEARCH 6
71 
72 #define GET_PLUGIN_DATA(gp) (RemminaPluginSshData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
73 
75 #define PALETTE_SIZE 16
76 /* Min fontsize and increase */
77 #define FONT_SCALE 0.75
78 #define SCALE_FACTOR 0.1
79 #define FONT_MINIMAL_SIZE (PANGO_SCALE * 6)
80 
82 
87 const GdkRGBA linux_palette[PALETTE_SIZE] = {
88  { 0, 0, 0, 1 },
89  { 0.666667, 0, 0, 1 },
90  { 0, 0.666667, 0, 1 },
91  { 0.666667, 0.333333, 0, 1 },
92  { 0, 0, 0.666667, 1 },
93  { 0.666667, 0, 0.666667, 1 },
94  { 0, 0.666667, 0.666667, 1 },
95  { 0.666667, 0.666667, 0.666667, 1 },
96  { 0.333333, 0.333333, 0.333333, 1 },
97  { 1, 0.333333, 0.333333, 1 },
98  { 0.333333, 1, 0.333333, 1 },
99  { 1, 1, 0.333333, 1 },
100  { 0.333333, 0.333333, 1, 1 },
101  { 1, 0.333333, 1, 1 },
102  { 0.333333, 1, 1, 1 },
103  { 1, 1, 1, 1 }
104 };
105 
106 const GdkRGBA tango_palette[PALETTE_SIZE] = {
107  { 0, 0, 0, 1 },
108  { 0.8, 0, 0, 1 },
109  { 0.305882, 0.603922, 0.023529, 1 },
110  { 0.768627, 0.627451, 0, 1 },
111  { 0.203922, 0.396078, 0.643137, 1 },
112  { 0.458824, 0.313725, 0.482353, 1 },
113  { 0.0235294, 0.596078, 0.603922, 1 },
114  { 0.827451, 0.843137, 0.811765, 1 },
115  { 0.333333, 0.341176, 0.32549, 1 },
116  { 0.937255, 0.160784, 0.160784, 1 },
117  { 0.541176, 0.886275, 0.203922, 1 },
118  { 0.988235, 0.913725, 0.309804, 1 },
119  { 0.447059, 0.623529, 0.811765, 1 },
120  { 0.678431, 0.498039, 0.658824, 1 },
121  { 0.203922, 0.886275, 0.886275, 1 },
122  { 0.933333, 0.933333, 0.92549, 1 }
123 };
124 
125 const GdkRGBA gruvbox_palette[PALETTE_SIZE] = {
126  { 0.156863, 0.156863, 0.156863, 1.000000 },
127  { 0.800000, 0.141176, 0.113725, 1.000000 },
128  { 0.596078, 0.592157, 0.101961, 1.000000 },
129  { 0.843137, 0.600000, 0.129412, 1.000000 },
130  { 0.270588, 0.521569, 0.533333, 1.000000 },
131  { 0.694118, 0.384314, 0.525490, 1.000000 },
132  { 0.407843, 0.615686, 0.415686, 1.000000 },
133  { 0.658824, 0.600000, 0.517647, 1.000000 },
134  { 0.572549, 0.513725, 0.454902, 1.000000 },
135  { 0.984314, 0.286275, 0.203922, 1.000000 },
136  { 0.721569, 0.733333, 0.149020, 1.000000 },
137  { 0.980392, 0.741176, 0.184314, 1.000000 },
138  { 0.513725, 0.647059, 0.596078, 1.000000 },
139  { 0.827451, 0.525490, 0.607843, 1.000000 },
140  { 0.556863, 0.752941, 0.486275, 1.000000 },
141  { 0.921569, 0.858824, 0.698039, 1.000000 },
142 };
143 
144 const GdkRGBA solarized_dark_palette[PALETTE_SIZE] = {
145  { 0.027451, 0.211765, 0.258824, 1 },
146  { 0.862745, 0.196078, 0.184314, 1 },
147  { 0.521569, 0.600000, 0.000000, 1 },
148  { 0.709804, 0.537255, 0.000000, 1 },
149  { 0.149020, 0.545098, 0.823529, 1 },
150  { 0.827451, 0.211765, 0.509804, 1 },
151  { 0.164706, 0.631373, 0.596078, 1 },
152  { 0.933333, 0.909804, 0.835294, 1 },
153  { 0.000000, 0.168627, 0.211765, 1 },
154  { 0.796078, 0.294118, 0.086275, 1 },
155  { 0.345098, 0.431373, 0.458824, 1 },
156  { 0.396078, 0.482353, 0.513725, 1 },
157  { 0.513725, 0.580392, 0.588235, 1 },
158  { 0.423529, 0.443137, 0.768627, 1 },
159  { 0.576471, 0.631373, 0.631373, 1 },
160  { 0.992157, 0.964706, 0.890196, 1 }
161 };
162 
163 const GdkRGBA solarized_light_palette[PALETTE_SIZE] = {
164  { 0.933333, 0.909804, 0.835294, 1 },
165  { 0.862745, 0.196078, 0.184314, 1 },
166  { 0.521569, 0.600000, 0.000000, 1 },
167  { 0.709804, 0.537255, 0.000000, 1 },
168  { 0.149020, 0.545098, 0.823529, 1 },
169  { 0.827451, 0.211765, 0.509804, 1 },
170  { 0.164706, 0.631373, 0.596078, 1 },
171  { 0.027451, 0.211765, 0.258824, 1 },
172  { 0.992157, 0.964706, 0.890196, 1 },
173  { 0.796078, 0.294118, 0.086275, 1 },
174  { 0.576471, 0.631373, 0.631373, 1 },
175  { 0.513725, 0.580392, 0.588235, 1 },
176  { 0.396078, 0.482353, 0.513725, 1 },
177  { 0.423529, 0.443137, 0.768627, 1 },
178  { 0.345098, 0.431373, 0.458824, 1 },
179  { 0.000000, 0.168627, 0.211765, 1 }
180 };
181 
182 const GdkRGBA xterm_palette[PALETTE_SIZE] = {
183  { 0, 0, 0, 1 },
184  { 0.803922, 0, 0, 1 },
185  { 0, 0.803922, 0, 1 },
186  { 0.803922, 0.803922, 0, 1 },
187  { 0.117647, 0.564706, 1, 1 },
188  { 0.803922, 0, 0.803922, 1 },
189  { 0, 0.803922, 0.803922, 1 },
190  { 0.898039, 0.898039, 0.898039, 1 },
191  { 0.298039, 0.298039, 0.298039, 1 },
192  { 1, 0, 0, 1 },
193  { 0, 1, 0, 1 },
194  { 1, 1, 0, 1 },
195  { 0.27451, 0.509804, 0.705882, 1 },
196  { 1, 0, 1, 1 },
197  { 0, 1, 1, 1 },
198  { 1, 1, 1, 1 }
199 };
200 
201 #define DEFAULT_PALETTE "linux_palette"
202 
203 
205 typedef struct _RemminaSshSearch {
206  GtkWidget * parent;
207 
208  GtkBuilder * builder;
209  GtkWidget * window;
210  GtkWidget * search_entry;
211  GtkWidget * search_prev_button;
212  GtkWidget * search_next_button;
213  GtkWidget * close_button;
214  GtkToggleButton * match_case_checkbutton;
215  GtkToggleButton * entire_word_checkbutton;
216  GtkToggleButton * regex_checkbutton;
217  GtkToggleButton * wrap_around_checkbutton;
218  GtkWidget * reveal_button;
219  GtkWidget * revealer;
220 
221  //VteTerminal *terminal;
222  gboolean regex_caseless;
223  gboolean has_regex;
224  gchar * regex_pattern;
226 
227 typedef struct _RemminaPluginSshData {
230  GtkWidget * vte;
231 
232  const GdkRGBA * palette;
233 
234  pthread_t thread;
235 
236  gboolean closed;
237 
240 
241 
242 #define GET_OBJECT(object_name) gtk_builder_get_object(search_widget->builder, object_name)
243 
245 
246 static gboolean
247 remmina_plugin_ssh_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp);
248 
249 static gboolean
250 valid_color(GdkRGBA const *color)
251 {
252  return color->red >= 0. && color->red <= 1. &&
253  color->green >= 0. && color->green <= 1. &&
254  color->blue >= 0. && color->blue <= 1. &&
255  color->alpha >= 0. && color->alpha <= 1.;
256 }
257 
258 
265 static gpointer
267 {
268  TRACE_CALL(__func__);
270  RemminaPluginSshData *gpdata;
271  RemminaFile *remminafile;
272  RemminaSSH *ssh;
273  RemminaSSHShell *shell = NULL;
274  gboolean cont = FALSE;
275  gboolean partial = FALSE;
276  gchar *hostport;
277  gint ret = REMMINA_SSH_AUTH_NULL;
278 
279  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
280  CANCEL_ASYNC
281 
282  gpdata = GET_PLUGIN_DATA(gp);
283 
285 
286  /* we may need to open a new tunnel */
287  REMMINA_DEBUG("Tentatively create an SSH tunnel");
289  if (hostport == NULL) {
291  return NULL;
292  }
293  REMMINA_DEBUG("protocol_plugin_start_direct_tunnel returned hostport: %s", hostport);
294 
295  ssh = g_object_get_data(G_OBJECT(gp), "user-data");
296  if (ssh) {
297  REMMINA_DEBUG("Creating SSH shell based on existing SSH session");
298  shell = remmina_ssh_shell_new_from_ssh(ssh);
299  REMMINA_DEBUG("Calling remmina_public_get_server_port");
301  REMMINA_DEBUG("tunnel_entrance_host: %s, tunnel_entrance_port: %d", ssh->tunnel_entrance_host, ssh->tunnel_entrance_port);
302 
303  if (remmina_ssh_init_session(REMMINA_SSH(shell)) &&
304  remmina_ssh_auth(REMMINA_SSH(shell), NULL, gp, remminafile) == REMMINA_SSH_AUTH_SUCCESS &&
307  cont = TRUE;
308  } else {
309  /* New SSH Shell connection */
310  REMMINA_DEBUG("Creating SSH shell based on a new SSH session");
311  shell = remmina_ssh_shell_new_from_file(remminafile);
312  ssh = REMMINA_SSH(shell);
313  REMMINA_DEBUG("Calling remmina_public_get_server_port");
315  REMMINA_DEBUG("tunnel_entrance_host: %s, tunnel_entrance_port: %d", ssh->tunnel_entrance_host, ssh->tunnel_entrance_port);
316 
317  while (1) {
318  if (!partial) {
319  if (!remmina_ssh_init_session(ssh)) {
320  REMMINA_DEBUG("init session error: %s", ssh->error);
322  // exit the loop here: OK
323  break;
324  }
325  }
326 
327  ret = remmina_ssh_auth_gui(ssh, gp, remminafile);
328  switch (ret) {
330  REMMINA_DEBUG("Authentication success");
334  break;
335  }
336  gchar *server;
337  gint port;
339  22,
340  &server,
341  &port);
342 
343  REMMINA_AUDIT(_("Connected to %s:%d via SSH"), server, port);
344  g_free(server), server = NULL;
345  break;
347  REMMINA_DEBUG("Continue with the next auth method");
348  partial = TRUE;
349  // Continue the loop: OK
350  continue;
351  break;
353  REMMINA_DEBUG("Reconnecting…");
354  if (ssh->session) {
355  ssh_disconnect(ssh->session);
356  ssh_free(ssh->session);
357  ssh->session = NULL;
358  }
359  g_free(ssh->callback);
360  // Continue the loop: OK
361  continue;
362  break;
364  REMMINA_DEBUG("Interrupted by the user");
365  // TODO: exit the loop here: OK
366  goto BREAK;
367  break;
368  default:
369  REMMINA_DEBUG("Error during the authentication: %s", ssh->error);
371  // TODO: exit the loop here: OK
372  goto BREAK;
373  }
374 
375 
376  cont = TRUE;
377  break;
378  }
379  }
380 
381 BREAK:
382  REMMINA_DEBUG("Authentication terminated with exit status %d", ret);
383 
384  g_free(hostport);
385 
386  if (!cont) {
387  if (shell) remmina_ssh_shell_free(shell);
389  return NULL;
390  }
391 
392  gpdata->shell = shell;
393 
394  gchar *charset = REMMINA_SSH(shell)->charset;
395 
396  remmina_plugin_ssh_vte_terminal_set_encoding_and_pty(VTE_TERMINAL(gpdata->vte), charset, shell->master, shell->slave);
397 
398  /* TODO: The following call should be moved on the main thread, or something weird could happen */
399  //remmina_plugin_ssh_on_size_allocate(GTK_WIDGET(gpdata->vte), NULL, gp);
400 
402 
403  gpdata->thread = 0;
404  return NULL;
405 }
406 
407 void remmina_plugin_ssh_vte_terminal_set_encoding_and_pty(VteTerminal *terminal, const char *codeset, int master, int slave)
408 {
409  TRACE_CALL(__func__);
411  /* Allow the execution of this function from a non main thread */
413  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
414  d->func = FUNC_VTE_TERMINAL_SET_ENCODING_AND_PTY;
415  d->p.vte_terminal_set_encoding_and_pty.terminal = terminal;
416  d->p.vte_terminal_set_encoding_and_pty.codeset = codeset;
417  d->p.vte_terminal_set_encoding_and_pty.master = master;
419  g_free(d);
420  return;
421  }
422 
423  setlocale(LC_ALL, "");
424  if (codeset && codeset[0] != '\0') {
425  }
426 
427  vte_terminal_set_backspace_binding(terminal, VTE_ERASE_ASCII_DELETE);
428  vte_terminal_set_delete_binding(terminal, VTE_ERASE_DELETE_SEQUENCE);
429 
430 #if VTE_CHECK_VERSION(0, 38, 0)
431  /* vte_pty_new_foreign expect master FD, see https://bugzilla.gnome.org/show_bug.cgi?id=765382 */
432  vte_terminal_set_pty(terminal, vte_pty_new_foreign_sync(master, NULL, NULL));
433 #else
434  vte_terminal_set_pty(terminal, master);
435 #endif
436 }
437 
438 static gboolean
439 remmina_plugin_ssh_on_focus_in(GtkWidget *widget, GdkEventFocus *event, RemminaProtocolWidget *gp)
440 {
441  TRACE_CALL(__func__);
442  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
443 
444  gtk_widget_grab_focus(gpdata->vte);
445  return TRUE;
446 }
447 
448 static gboolean
449 remmina_plugin_ssh_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
450 {
451  TRACE_CALL(__func__);
452  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
453  gint cols, rows;
454 
455  if (!gtk_widget_get_mapped(widget)) return FALSE;
456 
457  cols = vte_terminal_get_column_count(VTE_TERMINAL(widget));
458  rows = vte_terminal_get_row_count(VTE_TERMINAL(widget));
459 
460  if (gpdata->shell)
461  remmina_ssh_shell_set_size(gpdata->shell, cols, rows);
462 
463  return FALSE;
464 }
465 
466 static void
468 {
469  TRACE_CALL(__func__);
470  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
471  RemminaFile *remminafile;
472 
474 
475  if (remmina_plugin_service->file_get_int(remminafile, "audiblebell", FALSE)) {
476  vte_terminal_set_audible_bell(VTE_TERMINAL(gpdata->vte), TRUE);
477  g_info("audible_bell set to %i", vte_terminal_get_audible_bell(VTE_TERMINAL(gpdata->vte)));
478  }
480 #if !VTE_CHECK_VERSION(0, 38, 0)
481  vte_terminal_set_font_from_string(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_font);
482 #else
483  vte_terminal_set_font(VTE_TERMINAL(gpdata->vte),
484  pango_font_description_from_string(remmina_pref.vte_font));
485 #endif
486  }
487 
488 #if VTE_CHECK_VERSION(0, 51, 3)
489  REMMINA_DEBUG("Using vte_terminal_set_bold_is_bright instead of vte_terminal_set_allow_bold as deprecated");
490  vte_terminal_set_bold_is_bright(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_allow_bold_text);
491 #else
492  vte_terminal_set_allow_bold(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_allow_bold_text);
493 #endif
494  if (remmina_pref.vte_lines > 0)
495  vte_terminal_set_scrollback_lines(VTE_TERMINAL(gpdata->vte), remmina_pref.vte_lines);
496 }
497 
498 void
499 remmina_plugin_ssh_vte_select_all(GtkMenuItem *menuitem, gpointer vte)
500 {
501  TRACE_CALL(__func__);
502  vte_terminal_select_all(VTE_TERMINAL(vte));
504 }
505 
506 void
507 remmina_plugin_ssh_vte_decrease_font(GtkMenuItem *menuitem, gpointer vte)
508 {
509  TRACE_CALL(__func__);
510  vte_terminal_set_font_scale(VTE_TERMINAL(vte), vte_terminal_get_font_scale(VTE_TERMINAL(vte)) - SCALE_FACTOR);
511 }
512 
513 void
514 remmina_plugin_ssh_vte_increase_font(GtkMenuItem *menuitem, gpointer vte)
515 {
516  TRACE_CALL(__func__);
517  vte_terminal_set_font_scale(VTE_TERMINAL(vte), vte_terminal_get_font_scale(VTE_TERMINAL(vte)) + SCALE_FACTOR);
518 }
519 
520 void
521 remmina_plugin_ssh_vte_copy_clipboard(GtkMenuItem *menuitem, gpointer vte)
522 {
523  TRACE_CALL(__func__);
524 #if VTE_CHECK_VERSION(0, 50, 0)
525  vte_terminal_copy_clipboard_format(VTE_TERMINAL(vte), VTE_FORMAT_TEXT);
526 #else
527  vte_terminal_copy_clipboard(VTE_TERMINAL(vte));
528 #endif
529 }
530 
531 void
532 remmina_plugin_ssh_vte_paste_clipboard(GtkMenuItem *menuitem, gpointer vte)
533 {
534  TRACE_CALL(__func__);
535  vte_terminal_paste_clipboard(VTE_TERMINAL(vte));
536 }
537 
538 void
540 {
541  TRACE_CALL(__func__);
542  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
543 
544  GtkWidget *widget;
545  GError *err = NULL;
546 
547  GFileOutputStream *stream = g_file_replace(gpdata->vte_session_file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &err);
548 
549  if (err != NULL) {
550  // TRANSLATORS: %s is a placeholder for an error message
551  widget = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Error: %s"), err->message);
552  g_signal_connect(G_OBJECT(widget), "response", G_CALLBACK(gtk_widget_destroy), NULL);
553  gtk_widget_show(widget);
554  return;
555  }
556 
557  if (stream != NULL)
558 #if VTE_CHECK_VERSION(0, 38, 0)
559  vte_terminal_write_contents_sync(VTE_TERMINAL(gpdata->vte), G_OUTPUT_STREAM(stream),
560  VTE_WRITE_DEFAULT, NULL, &err);
561 #else
562  vte_terminal_write_contents(VTE_TERMINAL(gpdata->vte), G_OUTPUT_STREAM(stream),
563  VTE_TERMINAL_WRITE_DEFAULT, NULL, &err);
564 #endif
565 
566  if (err == NULL) {
567  remmina_public_send_notification("remmina-terminal-saved",
568  _("Terminal content saved in"),
569  g_file_get_path(gpdata->vte_session_file));
570  }
571 
572  g_object_unref(stream);
573  g_free(err);
574 }
575 
577 static void remmina_ssh_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
578 {
579  TRACE_CALL(__func__);
580  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
581 
583  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
584  return;
585 }
586 
587 
588 /* regex */
589 
590 static void jit_regex(VteRegex *regex, char const *pattern)
591 {
592  TRACE_CALL(__func__);
593  GError *error;
594 
595  if (!vte_regex_jit(regex, PCRE2_JIT_COMPLETE, &error) ||
596  !vte_regex_jit(regex, PCRE2_JIT_PARTIAL_SOFT, &error))
597  if (!g_error_matches(error, VTE_REGEX_ERROR, -45 /* PCRE2_ERROR_JIT_BADOPTION: JIT not supported */))
598  REMMINA_DEBUG("JITing regex “%s” failed: %s\n", pattern, error->message);
599 }
600 
601 static VteRegex *compile_regex_for_search(char const *pattern, gboolean caseless, GError **error)
602 {
603  TRACE_CALL(__func__);
604  uint32_t flags = PCRE2_UTF | PCRE2_NO_UTF_CHECK | PCRE2_MULTILINE;
605 
606  if (caseless)
607  flags |= PCRE2_CASELESS;
608 
609  VteRegex *regex = vte_regex_new_for_search(pattern, strlen(pattern), flags, error);
610 
611  if (regex != NULL)
612  jit_regex(regex, pattern);
613 
614  return regex;
615 }
616 
617 static void
619 {
620  TRACE_CALL(__func__);
621  gboolean can_search = search_widget->has_regex;
622 
623  gtk_widget_set_sensitive(search_widget->search_next_button, can_search);
624  gtk_widget_set_sensitive(search_widget->search_prev_button, can_search);
625 }
626 
627 static void
629 {
630  TRACE_CALL(__func__);
631  GError *error = NULL;
632 
633  RemminaSshSearch *search_widget = gpdata->search_widget;
634  char const *search_text = gtk_entry_get_text(GTK_ENTRY(search_widget->search_entry));
635  gboolean caseless = gtk_toggle_button_get_active(search_widget->match_case_checkbutton) == FALSE;
636 
637  char *pattern;
638 
639  if (gtk_toggle_button_get_active(search_widget->regex_checkbutton))
640  pattern = g_strdup(search_text);
641  else
642  pattern = g_regex_escape_string(search_text, -1);
643 
644  if (gtk_toggle_button_get_active(search_widget->regex_checkbutton)) {
645  char *tmp = g_strdup_printf("\\b%s\\b", pattern);
646  g_free(pattern);
647  pattern = tmp;
648  }
649 
650  if (caseless == search_widget->regex_caseless &&
651  g_strcmp0(pattern, search_widget->regex_pattern) == 0)
652  return;
653 
654  search_widget->regex_caseless = caseless;
655  g_free(search_widget->regex_pattern);
656  search_widget->regex_pattern = NULL;
657 
658  if (search_text[0] != '\0') {
659  REMMINA_DEBUG("Search text is: %s", search_text);
660  VteRegex *regex = compile_regex_for_search(pattern, caseless, &error);
661  vte_terminal_search_set_regex(VTE_TERMINAL(gpdata->vte), regex, 0);
662  if (regex != NULL)
663  vte_regex_unref(regex);
664 
665  if (!error) {
666  search_widget->has_regex = TRUE;
667  search_widget->regex_pattern = pattern; /* adopt */
668  pattern = NULL; /* adopted */
669  gtk_widget_set_tooltip_text(search_widget->search_entry, NULL);
670  } else {
671  search_widget->has_regex = FALSE;
672  REMMINA_DEBUG("Regex not set, cannot search");
673  gtk_widget_set_tooltip_text(search_widget->search_entry, error->message);
674  }
675  }
676 
677  g_free(pattern);
678  g_free(error);
679 
681 }
682 
683 static void
685 {
686  TRACE_CALL(__func__);
687 
688  vte_terminal_search_set_wrap_around(VTE_TERMINAL(gpdata->vte), gtk_toggle_button_get_active(button));
689 }
690 
691 static void
693 {
694  TRACE_CALL(__func__);
695 
696  RemminaSshSearch *search_sidget = gpdata->search_widget;
697 
698  if (!search_sidget->has_regex)
699  return;
700  vte_terminal_search_find_next(VTE_TERMINAL(gpdata->vte));
701 }
702 
703 static void
705 {
706  TRACE_CALL(__func__);
707 
708  RemminaSshSearch *search_sidget = gpdata->search_widget;
709 
710  if (!search_sidget->has_regex)
711  return;
712  vte_terminal_search_find_previous(VTE_TERMINAL(gpdata->vte));
713 }
714 
715 GtkWidget *remmina_plugin_pop_search_new(GtkWidget *relative_to, RemminaProtocolWidget *gp)
716 {
717  TRACE_CALL(__func__);
718  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
719 
720  gpdata->search_widget = g_new0(RemminaSshSearch, 1);
721  RemminaSshSearch *search_widget = gpdata->search_widget;
722 
723  search_widget->regex_caseless = FALSE;
724  search_widget->has_regex = FALSE;
725  search_widget->regex_pattern = NULL;
726 
727  search_widget->builder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_search.glade");
728  search_widget->window = GTK_WIDGET(GET_OBJECT("RemminaSearchWidget"));
729  search_widget->search_entry = GTK_WIDGET(GET_OBJECT("search_entry"));
730  search_widget->search_prev_button = GTK_WIDGET(GET_OBJECT("search_prev_button"));
731  search_widget->search_next_button = GTK_WIDGET(GET_OBJECT("search_next_button"));
732  search_widget->close_button = GTK_WIDGET(GET_OBJECT("close_button"));
733  search_widget->match_case_checkbutton = GTK_TOGGLE_BUTTON(GET_OBJECT("match_case_checkbutton"));
734  search_widget->entire_word_checkbutton = GTK_TOGGLE_BUTTON(GET_OBJECT("entire_word_checkbutton"));
735  search_widget->regex_checkbutton = GTK_TOGGLE_BUTTON(GET_OBJECT("regex_checkbutton"));
736  search_widget->wrap_around_checkbutton = GTK_TOGGLE_BUTTON(GET_OBJECT("wrap_around_checkbutton"));
737  search_widget->reveal_button = GTK_WIDGET(GET_OBJECT("reveal_button"));
738  search_widget->revealer = GTK_WIDGET(GET_OBJECT("revealer"));
739 
740  gtk_widget_set_can_default(search_widget->search_next_button, TRUE);
741  gtk_widget_grab_default(search_widget->search_next_button);
742 
743  gtk_entry_set_activates_default(GTK_ENTRY(search_widget->search_entry), TRUE);
744 
745  /* Connect signals */
746  gtk_builder_connect_signals(search_widget->builder, NULL);
747 
748  g_signal_connect_swapped(search_widget->close_button, "clicked", G_CALLBACK(gtk_widget_destroy), GTK_WIDGET(search_widget->window));
749 
750  g_object_bind_property(search_widget->reveal_button, "active",
751  search_widget->revealer, "reveal-child",
752  G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
753 
754  g_signal_connect_swapped(search_widget->search_entry, "next-match", G_CALLBACK(remmina_search_widget_search_forward), gpdata);
755  g_signal_connect_swapped(search_widget->search_entry, "previous-match", G_CALLBACK(remmina_search_widget_search_backward), gpdata);
756  g_signal_connect_swapped(search_widget->search_entry, "search-changed", G_CALLBACK(remmina_search_widget_update_regex), gpdata);
757 
758  g_signal_connect_swapped(search_widget->search_next_button, "clicked", G_CALLBACK(remmina_search_widget_search_forward), gpdata);
759  g_signal_connect_swapped(search_widget->search_prev_button, "clicked", G_CALLBACK(remmina_search_widget_search_backward), gpdata);
760 
761  g_signal_connect_swapped(search_widget->match_case_checkbutton, "toggled", G_CALLBACK(remmina_search_widget_update_regex), gpdata);
762  g_signal_connect_swapped(search_widget->entire_word_checkbutton, "toggled", G_CALLBACK(remmina_search_widget_update_regex), gpdata);
763  g_signal_connect_swapped(search_widget->regex_checkbutton, "toggled", G_CALLBACK(remmina_search_widget_update_regex), gpdata);
764  g_signal_connect_swapped(search_widget->match_case_checkbutton, "toggled", G_CALLBACK(remmina_search_widget_update_regex), gpdata);
765 
766  g_signal_connect(search_widget->wrap_around_checkbutton, "toggled", G_CALLBACK(remmina_search_widget_wrap_around_toggled), gpdata);
767 
769  return search_widget->window;
770 }
771 
772 void remmina_plugin_pop_search(GtkMenuItem *menuitem, RemminaProtocolWidget *gp)
773 {
774  TRACE_CALL(__func__);
775  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
776 
777  GtkWindow *parent = NULL;
778 
779  REMMINA_DEBUG("Before popover");
780  GtkWidget *window = remmina_plugin_pop_search_new(gpdata->vte, gp);
781  GtkWidget *toplevel = gtk_widget_get_toplevel(gpdata->vte);
782 
783  if (GTK_IS_WINDOW(toplevel)) {
784  parent = GTK_WINDOW(toplevel);
785  gtk_window_set_transient_for(GTK_WINDOW(window), parent);
786  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
787  }
788  gtk_widget_show(window);
789  REMMINA_DEBUG("After popover");
790 }
791 
792 void remmina_plugin_ssh_call_sftp(GtkMenuItem *menuitem, RemminaProtocolWidget *gp)
793 {
794  TRACE_CALL(__func__);
796 }
797 
798 gboolean
799 remmina_ssh_plugin_popup_menu(GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
800 {
801  if ((event->type == GDK_BUTTON_PRESS) && (((GdkEventButton *)event)->button == 3)) {
802 #if GTK_CHECK_VERSION(3, 22, 0)
803  gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
804 #else
805  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
806  ((GdkEventButton *)event)->button, gtk_get_current_event_time());
807 #endif
808  return TRUE;
809  }
810 
811  return FALSE;
812 }
813 
828 {
829  TRACE_CALL(__func__);
830  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
831  /* Context menu for slection and clipboard */
832  GtkWidget *menu = gtk_menu_new();
833 
834  GtkWidget *select_all = gtk_menu_item_new_with_label(_("Select All (host+A)"));
835  GtkWidget *copy = gtk_menu_item_new_with_label(_("Copy (host+C)"));
836  GtkWidget *paste = gtk_menu_item_new_with_label(_("Paste (host+V)"));
837  GtkWidget *save = gtk_menu_item_new_with_label(_("Save session to file"));
838  GtkWidget *font_incr = gtk_menu_item_new_with_label(_("Increase font size (host+Page Up)"));
839  GtkWidget *font_decr = gtk_menu_item_new_with_label(_("Decrease font size (host+Page Down)"));
840  GtkWidget *find_text = gtk_menu_item_new_with_label(_("Find text (host+G)"));
841  GtkWidget *sftp = gtk_menu_item_new_with_label(_("Open SFTP transfer…"));
842 
843  gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_all);
844  gtk_menu_shell_append(GTK_MENU_SHELL(menu), copy);
845  gtk_menu_shell_append(GTK_MENU_SHELL(menu), paste);
846  gtk_menu_shell_append(GTK_MENU_SHELL(menu), save);
847  gtk_menu_shell_append(GTK_MENU_SHELL(menu), font_incr);
848  gtk_menu_shell_append(GTK_MENU_SHELL(menu), font_decr);
849  gtk_menu_shell_append(GTK_MENU_SHELL(menu), find_text);
850  gtk_menu_shell_append(GTK_MENU_SHELL(menu), sftp);
851 
852  g_signal_connect(G_OBJECT(gpdata->vte), "button_press_event",
853  G_CALLBACK(remmina_ssh_plugin_popup_menu), menu);
854 
855  g_signal_connect(G_OBJECT(select_all), "activate",
856  G_CALLBACK(remmina_plugin_ssh_vte_select_all), gpdata->vte);
857  g_signal_connect(G_OBJECT(copy), "activate",
858  G_CALLBACK(remmina_plugin_ssh_vte_copy_clipboard), gpdata->vte);
859  g_signal_connect(G_OBJECT(paste), "activate",
860  G_CALLBACK(remmina_plugin_ssh_vte_paste_clipboard), gpdata->vte);
861  g_signal_connect(G_OBJECT(save), "activate",
862  G_CALLBACK(remmina_plugin_ssh_vte_save_session), gp);
863  g_signal_connect(G_OBJECT(font_incr), "activate",
864  G_CALLBACK(remmina_plugin_ssh_vte_increase_font), gpdata->vte);
865  g_signal_connect(G_OBJECT(font_decr), "activate",
866  G_CALLBACK(remmina_plugin_ssh_vte_decrease_font), gpdata->vte);
867  g_signal_connect(G_OBJECT(find_text), "activate",
868  G_CALLBACK(remmina_plugin_pop_search), gp);
869  g_signal_connect(G_OBJECT(sftp), "activate",
870  G_CALLBACK(remmina_plugin_ssh_call_sftp), gp);
871 
872  gtk_widget_show_all(menu);
873 }
874 
875 static void
876 remmina_plugin_ssh_eof(VteTerminal *vteterminal, RemminaProtocolWidget *gp)
877 {
878  TRACE_CALL(__func__);
879 
881  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
882 
883  if (gpdata->closed)
884  return;
885 
886  if (remmina_file_get_int(remminafile, "sshlogenabled", FALSE))
888 
889  gchar *server;
890  gint port;
892  22,
893  &server,
894  &port);
895 
896  REMMINA_AUDIT(_("Disconnected from %s:%d via SSH"), server, port);
897  g_free(server), server = NULL;
898  gpdata->closed = TRUE;
899 }
900 
901 static gboolean
903 {
904  TRACE_CALL(__func__);
905  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
906 
907  RemminaFile *remminafile;
908 
909  REMMINA_DEBUG("Requesting to close the connection");
911 
912  if (remmina_file_get_int(remminafile, "sshlogenabled", FALSE))
914  remmina_plugin_ssh_eof(VTE_TERMINAL(gpdata->vte), gp);
915 
916  if (gpdata->thread) {
917  pthread_cancel(gpdata->thread);
918  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
919  }
920  if (gpdata->shell) {
921  remmina_ssh_shell_free(gpdata->shell);
922  gpdata->shell = NULL;
923  }
924 
926  return FALSE;
927 }
928 
941 static void
943 {
944  TRACE_CALL(__func__);
945  RemminaPluginSshData *gpdata;
946  RemminaFile *remminafile;
947  GtkWidget *hbox;
948  GtkAdjustment *vadjustment;
949  GtkWidget *vscrollbar;
950  GtkWidget *vte;
951  GdkRGBA foreground_color;
952  GdkRGBA background_color;
953  GdkRGBA palette[PALETTE_SIZE];
954 
955 
956 #if !VTE_CHECK_VERSION(0, 38, 0)
957  GdkColor foreground_gdkcolor;
958  GdkColor background_gdkcolor;
959 #endif /* VTE_CHECK_VERSION(0,38,0) */
960 
961  gpdata = g_new0(RemminaPluginSshData, 1);
962  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
963 
964  gpdata->closed = FALSE;
965 
966  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
967  gtk_widget_show(hbox);
968  gtk_container_add(GTK_CONTAINER(gp), hbox);
969  g_signal_connect(G_OBJECT(hbox), "focus-in-event", G_CALLBACK(remmina_plugin_ssh_on_focus_in), gp);
970 
971  vte = vte_terminal_new();
972  //gtk_widget_show(vte);
973  vte_terminal_set_size(VTE_TERMINAL(vte), 80, 25);
974  vte_terminal_set_scroll_on_keystroke(VTE_TERMINAL(vte), TRUE);
975 #if !VTE_CHECK_VERSION(0, 38, 0)
976  gdk_rgba_parse(&foreground_color, remmina_pref.color_pref.foreground);
977  gdk_rgba_parse(&background_color, remmina_pref.color_pref.background);
978 #endif
980 
981 
982 #if VTE_CHECK_VERSION(0, 38, 0)
983  GdkRGBA cp[PALETTE_SIZE];
984  GdkRGBA cursor_color;
985  GdkRGBA cursor_foreground;
986  GdkRGBA highlight;
987  GdkRGBA highlight_foreground;
988  GdkRGBA colorBD;
989  unsigned int i = 0;
990 
991  /*
992  * custom colors reside inside of the 'theme' subdir of the remmina config folder (.config/remmina/theme)
993  * with the file extension '.colors'. The name of the colorfile came from the menu (see below)
994  * sideeffect: It is possible to overwrite the standard colours with a dedicated colourfile like
995  * '0.colors' for GRUVBOX, '1.colors' for TANGO and so on
996  */
997  const gchar *color_name = remmina_plugin_service->file_get_string(remminafile, "ssh_color_scheme");
998 
999  gchar *remmina_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "theme", NULL);
1000  gchar *remmina_colors_file = g_strdup_printf("%s/%s.colors", remmina_dir, color_name);
1001  g_free(remmina_dir);
1002 
1003  /*
1004  * try to load theme from one of the system data dirs (based on XDG_DATA_DIRS environment var)
1005  */
1006  if (!g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR)) {
1007  GError *error = NULL;
1008  const gchar *const *dirs = g_get_system_data_dirs();
1009 
1010  for (i = 0; dirs[i] != NULL; ++i) {
1011  remmina_dir = g_build_path("/", dirs[i], "remmina", "theme", NULL);
1012  GDir *system_data_dir = g_dir_open(remmina_dir, 0, &error);
1013  // ignoring this error is OK, because the folder may not exist
1014  if (error) {
1015  g_error_free(error);
1016  error = NULL;
1017  } else {
1018  if (system_data_dir) {
1019  g_dir_close(system_data_dir);
1020  g_free(remmina_colors_file);
1021  remmina_colors_file = g_strdup_printf("%s/%s.colors", remmina_dir, color_name);
1022  if (g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR))
1023  break;
1024  }
1025  }
1026  g_free(remmina_dir);
1027  }
1028  }
1029 
1030  if (g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR)) {
1031  GKeyFile *gkeyfile;
1032  RemminaColorPref color_pref;
1033 
1034  gkeyfile = g_key_file_new();
1035  g_key_file_load_from_file(gkeyfile, remmina_colors_file, G_KEY_FILE_NONE, NULL);
1036  remmina_pref_file_load_colors(gkeyfile, &color_pref);
1037 
1038  REMMINA_DEBUG("Load custom theme for SSH teminal");
1039  g_warn_if_fail(gdk_rgba_parse(&foreground_color, color_pref.foreground));
1040  g_warn_if_fail(gdk_rgba_parse(&background_color, color_pref.background));
1041  g_warn_if_fail(gdk_rgba_parse(&cursor_color, color_pref.cursor));
1042  g_warn_if_fail(gdk_rgba_parse(&cursor_foreground, color_pref.cursor_foreground));
1043  g_warn_if_fail(gdk_rgba_parse(&highlight, color_pref.highlight));
1044  g_warn_if_fail(gdk_rgba_parse(&highlight_foreground, color_pref.highlight_foreground));
1045  g_warn_if_fail(gdk_rgba_parse(&colorBD, color_pref.colorBD));
1046 
1047  g_warn_if_fail(gdk_rgba_parse(&cp[0], color_pref.color0));
1048  g_warn_if_fail(gdk_rgba_parse(&cp[1], color_pref.color1));
1049  g_warn_if_fail(gdk_rgba_parse(&cp[2], color_pref.color2));
1050  g_warn_if_fail(gdk_rgba_parse(&cp[3], color_pref.color3));
1051  g_warn_if_fail(gdk_rgba_parse(&cp[4], color_pref.color4));
1052  g_warn_if_fail(gdk_rgba_parse(&cp[5], color_pref.color5));
1053  g_warn_if_fail(gdk_rgba_parse(&cp[6], color_pref.color6));
1054  g_warn_if_fail(gdk_rgba_parse(&cp[7], color_pref.color7));
1055  g_warn_if_fail(gdk_rgba_parse(&cp[8], color_pref.color8));
1056  g_warn_if_fail(gdk_rgba_parse(&cp[9], color_pref.color9));
1057  g_warn_if_fail(gdk_rgba_parse(&cp[10], color_pref.color10));
1058  g_warn_if_fail(gdk_rgba_parse(&cp[11], color_pref.color11));
1059  g_warn_if_fail(gdk_rgba_parse(&cp[12], color_pref.color12));
1060  g_warn_if_fail(gdk_rgba_parse(&cp[13], color_pref.color13));
1061  g_warn_if_fail(gdk_rgba_parse(&cp[14], color_pref.color14));
1062  g_warn_if_fail(gdk_rgba_parse(&cp[15], color_pref.color15));
1063 
1064  const GdkRGBA custom_palette[PALETTE_SIZE] = {
1065  cp[0], cp[1], cp[2], cp[3],
1066  cp[4], cp[5], cp[6], cp[7],
1067  cp[8], cp[9], cp[10], cp[11],
1068  cp[12], cp[13], cp[14], cp[15]
1069  };
1070 
1071  for (i = 0; i < PALETTE_SIZE; i++)
1072  palette[i] = custom_palette[i];
1073  } else {
1074  /* Set colors to GdkRGBA */
1075  switch (remmina_plugin_service->file_get_int(remminafile, "ssh_color_scheme", FALSE)) {
1076  case LINUX:
1077  gdk_rgba_parse(&foreground_color, "#ffffff");
1078  gdk_rgba_parse(&background_color, "#000000");
1079  gdk_rgba_parse(&cursor_color, "#ffffff");
1080  gdk_rgba_parse(&cursor_foreground, "#00000");
1081  gdk_rgba_parse(&highlight, "#ffffff");
1082  gdk_rgba_parse(&highlight_foreground, "#00000");
1083  gdk_rgba_parse(&colorBD, "#ffffff");
1084  for (i = 0; i < PALETTE_SIZE; i++)
1085  palette[i] = linux_palette[i];
1086  break;
1087  case TANGO:
1088  gdk_rgba_parse(&foreground_color, "#ffffff");
1089  gdk_rgba_parse(&background_color, "#000000");
1090  gdk_rgba_parse(&cursor_color, "#000000");
1091  gdk_rgba_parse(&cursor_foreground, "#ffffff");
1092  gdk_rgba_parse(&highlight, "#ffffff");
1093  gdk_rgba_parse(&highlight_foreground, "#00000");
1094  gdk_rgba_parse(&colorBD, "#000000");
1095  for (i = 0; i < PALETTE_SIZE; i++)
1096  palette[i] = tango_palette[i];
1097  break;
1098  case GRUVBOX:
1099  gdk_rgba_parse(&foreground_color, "#e6d4a3");
1100  gdk_rgba_parse(&background_color, "#1e1e1e");
1101  gdk_rgba_parse(&cursor_color, "#e6d4a3");
1102  gdk_rgba_parse(&cursor_foreground, "#e6d4a3");
1103  gdk_rgba_parse(&highlight, "#e6d4a3");
1104  gdk_rgba_parse(&highlight_foreground, "#1e1e1e");
1105  gdk_rgba_parse(&colorBD, "#ffffff");
1106  for (i = 0; i < PALETTE_SIZE; i++)
1107  palette[i] = gruvbox_palette[i];
1108  break;
1109  case SOLARIZED_DARK:
1110  gdk_rgba_parse(&foreground_color, "#839496");
1111  gdk_rgba_parse(&background_color, "#002b36");
1112  gdk_rgba_parse(&cursor_color, "#93a1a1");
1113  gdk_rgba_parse(&cursor_foreground, "#839496");
1114  gdk_rgba_parse(&highlight, "#839496");
1115  gdk_rgba_parse(&highlight_foreground, "#002b36");
1116  gdk_rgba_parse(&colorBD, "#819090");
1117  for (i = 0; i < PALETTE_SIZE; i++)
1118  palette[i] = solarized_dark_palette[i];
1119  break;
1120  case SOLARIZED_LIGHT:
1121  gdk_rgba_parse(&foreground_color, "#657b83");
1122  gdk_rgba_parse(&background_color, "#fdf6e3");
1123  gdk_rgba_parse(&cursor_color, "#586e75");
1124  gdk_rgba_parse(&cursor_foreground, "#657b83");
1125  gdk_rgba_parse(&highlight, "#657b83");
1126  gdk_rgba_parse(&highlight_foreground, "#fdf6e3");
1127  gdk_rgba_parse(&colorBD, "#475b62");
1128  for (i = 0; i < PALETTE_SIZE; i++)
1129  palette[i] = solarized_light_palette[i];
1130  break;
1131  case XTERM:
1132  gdk_rgba_parse(&foreground_color, "#000000");
1133  gdk_rgba_parse(&background_color, "#ffffff");
1134  gdk_rgba_parse(&cursor_color, "#000000");
1135  gdk_rgba_parse(&cursor_foreground, "#ffffff");
1136  gdk_rgba_parse(&highlight, "#000000");
1137  gdk_rgba_parse(&highlight_foreground, "#ffffff");
1138  gdk_rgba_parse(&colorBD, "#000000");
1139  for (i = 0; i < PALETTE_SIZE; i++)
1140  palette[i] = xterm_palette[i];
1141  break;
1142  case CUSTOM:
1143  REMMINA_DEBUG("Custom colors");
1144  g_warn_if_fail(gdk_rgba_parse(&foreground_color, remmina_pref.color_pref.foreground));
1145  g_warn_if_fail(gdk_rgba_parse(&background_color, remmina_pref.color_pref.background));
1146  g_warn_if_fail(gdk_rgba_parse(&cursor_color, remmina_pref.color_pref.cursor));
1147  g_warn_if_fail(gdk_rgba_parse(&cursor_foreground, remmina_pref.color_pref.cursor_foreground));
1148  g_warn_if_fail(gdk_rgba_parse(&highlight, remmina_pref.color_pref.highlight));
1149  g_warn_if_fail(gdk_rgba_parse(&highlight_foreground, remmina_pref.color_pref.highlight_foreground));
1150  g_warn_if_fail(gdk_rgba_parse(&colorBD, remmina_pref.color_pref.colorBD));
1151 
1152  g_warn_if_fail(gdk_rgba_parse(&cp[0], remmina_pref.color_pref.color0));
1153  g_warn_if_fail(gdk_rgba_parse(&cp[1], remmina_pref.color_pref.color1));
1154  g_warn_if_fail(gdk_rgba_parse(&cp[2], remmina_pref.color_pref.color2));
1155  g_warn_if_fail(gdk_rgba_parse(&cp[3], remmina_pref.color_pref.color3));
1156  g_warn_if_fail(gdk_rgba_parse(&cp[4], remmina_pref.color_pref.color4));
1157  g_warn_if_fail(gdk_rgba_parse(&cp[5], remmina_pref.color_pref.color5));
1158  g_warn_if_fail(gdk_rgba_parse(&cp[6], remmina_pref.color_pref.color6));
1159  g_warn_if_fail(gdk_rgba_parse(&cp[7], remmina_pref.color_pref.color7));
1160  g_warn_if_fail(gdk_rgba_parse(&cp[8], remmina_pref.color_pref.color8));
1161  g_warn_if_fail(gdk_rgba_parse(&cp[9], remmina_pref.color_pref.color9));
1162  g_warn_if_fail(gdk_rgba_parse(&cp[10], remmina_pref.color_pref.color10));
1163  g_warn_if_fail(gdk_rgba_parse(&cp[11], remmina_pref.color_pref.color11));
1164  g_warn_if_fail(gdk_rgba_parse(&cp[12], remmina_pref.color_pref.color12));
1165  g_warn_if_fail(gdk_rgba_parse(&cp[13], remmina_pref.color_pref.color13));
1166  g_warn_if_fail(gdk_rgba_parse(&cp[14], remmina_pref.color_pref.color14));
1167  g_warn_if_fail(gdk_rgba_parse(&cp[15], remmina_pref.color_pref.color15));
1168 
1169  const GdkRGBA custom_palette[PALETTE_SIZE] = {
1170  cp[0], // remmina_pref.color_pref.color0
1171  cp[1], // remmina_pref.color_pref.color1
1172  cp[2], // remmina_pref.color_pref.color2
1173  cp[3], // remmina_pref.color_pref.color3
1174  cp[4], // remmina_pref.color_pref.color4
1175  cp[5], // remmina_pref.color_pref.color5
1176  cp[6], // remmina_pref.color_pref.color6
1177  cp[7], // remmina_pref.color_pref.color7
1178  cp[8], // remmina_pref.color_pref.color8
1179  cp[9], // remmina_pref.color_pref.color9
1180  cp[10], // remmina_pref.color_pref.color10
1181  cp[11], // remmina_pref.color_pref.color11
1182  cp[12], // remmina_pref.color_pref.color12
1183  cp[13], // remmina_pref.color_pref.color13
1184  cp[14], // remmina_pref.color_pref.color14
1185  cp[15] // remmina_pref.color_pref.color15
1186  };
1187 
1188  for (i = 0; i < PALETTE_SIZE; i++)
1189  palette[i] = custom_palette[i];
1190  break;
1191  default:
1192  REMMINA_DEBUG("Linux paelette colors");
1193  for (i = 0; i < PALETTE_SIZE; i++)
1194  palette[i] = linux_palette[i];
1195  break;
1196  }
1197  }
1198  g_free(remmina_colors_file);
1199  REMMINA_DEBUG("foreground_color.red %f, background_color.red %f", foreground_color.red, background_color.red);
1200  REMMINA_DEBUG("foreground_color.blue %f, background_color.blue %f", foreground_color.blue, background_color.blue);
1201  REMMINA_DEBUG("foreground_color.green %f, background_color.green %f", foreground_color.green, background_color.green);
1202  REMMINA_DEBUG("foreground_color.alpha %f, background_color.alpha %f", foreground_color.alpha, background_color.alpha);
1203  for (i = 0; i < PALETTE_SIZE; i++) {
1204  REMMINA_DEBUG("index: %d, palette validation for red: %f", i, palette[i].red);
1205  REMMINA_DEBUG("index: %d, palette validation for green: %f", i, palette[i].green);
1206  REMMINA_DEBUG("index: %d, palette validation for blue: %f", i, palette[i].blue);
1207  REMMINA_DEBUG("index: %d, palette validation for alpha: %f", i, palette[i].alpha);
1208  g_warn_if_fail(valid_color(&palette[i]));
1209  }
1210  vte_terminal_set_colors(VTE_TERMINAL(vte), &foreground_color, &background_color, palette, PALETTE_SIZE);
1211  vte_terminal_set_color_foreground(VTE_TERMINAL(vte), &foreground_color);
1212  vte_terminal_set_color_background(VTE_TERMINAL(vte), &background_color);
1213  vte_terminal_set_color_cursor(VTE_TERMINAL(vte), &cursor_color);
1214  vte_terminal_set_color_cursor_foreground(VTE_TERMINAL(vte), &cursor_foreground);
1215  vte_terminal_set_color_highlight(VTE_TERMINAL(vte), &highlight);
1216  vte_terminal_set_color_highlight_foreground(VTE_TERMINAL(vte), &highlight_foreground);
1217  vte_terminal_set_color_bold(VTE_TERMINAL(vte), &colorBD);
1218 
1219 #else
1220  /* VTE <= 2.90 doesn’t support GdkRGBA so we must convert GdkRGBA to GdkColor */
1221  foreground_gdkcolor.red = (guint16)(foreground_color.red * 0xFFFF);
1222  foreground_gdkcolor.green = (guint16)(foreground_color.green * 0xFFFF);
1223  foreground_gdkcolor.blue = (guint16)(foreground_color.blue * 0xFFFF);
1224  background_gdkcolor.red = (guint16)(background_color.red * 0xFFFF);
1225  background_gdkcolor.green = (guint16)(background_color.green * 0xFFFF);
1226  background_gdkcolor.blue = (guint16)(background_color.blue * 0xFFFF);
1227  /* Set colors to GdkColor */
1228  vte_terminal_set_colors(VTE_TERMINAL(vte), &foreground_gdkcolor, &background_gdkcolor, NULL, 0);
1229 #endif
1230 
1231  gtk_box_pack_start(GTK_BOX(hbox), vte, TRUE, TRUE, 0);
1232  gpdata->vte = vte;
1234 
1236 
1237 #if VTE_CHECK_VERSION(0, 28, 0)
1238  vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vte));
1239 #else
1240  vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
1241 #endif
1242 
1243  vscrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
1244 
1245  gtk_widget_show(vscrollbar);
1246  gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, TRUE, 0);
1247 
1248  const gchar *dir;
1249  const gchar *sshlogname;
1250  gchar *fp;
1251 
1252  GFile *rf = g_file_new_for_path(remminafile->filename);
1253 
1254  if (remmina_plugin_service->file_get_string(remminafile, "sshlogfolder") == NULL)
1255  dir = g_build_path("/", g_get_user_cache_dir(), "remmina", NULL);
1256  else
1257  dir = remmina_plugin_service->file_get_string(remminafile, "sshlogfolder");
1258 
1259  if (remmina_plugin_service->file_get_string(remminafile, "sshlogname") == NULL)
1260  sshlogname = g_strconcat(g_file_get_basename(rf), ".", "log", NULL);
1261  else
1262  sshlogname = remmina_plugin_service->file_get_string(remminafile, "sshlogname");
1263 
1264  sshlogname = remmina_file_format_properties(remminafile, sshlogname);
1265 
1266  fp = g_strconcat(dir, "/", sshlogname, NULL);
1267  g_free((gpointer) sshlogname);
1268 
1269  gpdata->vte_session_file = g_file_new_for_path(fp);
1270  g_free(fp);
1271 
1272  g_signal_connect(G_OBJECT(vte), "size-allocate", G_CALLBACK(remmina_plugin_ssh_on_size_allocate), gp);
1273  g_signal_connect(G_OBJECT(vte), "unrealize", G_CALLBACK(remmina_plugin_ssh_eof), gp);
1274  g_signal_connect(G_OBJECT(vte), "eof", G_CALLBACK(remmina_plugin_ssh_eof), gp);
1275  g_signal_connect(G_OBJECT(vte), "child-exited", G_CALLBACK(remmina_plugin_ssh_eof), gp);
1277  gtk_widget_show_all(hbox);
1278 }
1279 
1287 static gboolean
1289 {
1290  TRACE_CALL(__func__);
1291  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
1292 
1296 
1297  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_ssh_main_thread, gp)) {
1299  "Failed to initialize pthread. Falling back to non-thread mode…");
1300  gpdata->thread = 0;
1301  return FALSE;
1302  } else {
1303  return TRUE;
1304  }
1305  return TRUE;
1306 }
1307 
1313 static gboolean
1315 {
1316  TRACE_CALL(__func__);
1317  return TRUE;
1318 }
1319 
1336 static void
1338 {
1339  TRACE_CALL(__func__);
1340  RemminaPluginSshData *gpdata = GET_PLUGIN_DATA(gp);
1341 
1342  switch (feature->id) {
1343  case REMMINA_PROTOCOL_FEATURE_TOOL_SSH:
1346  NULL, gpdata->shell, NULL);
1347  return;
1348  case REMMINA_PROTOCOL_FEATURE_TOOL_SFTP:
1352  NULL, gpdata->shell, NULL);
1353  return;
1354  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_COPY:
1355 #if VTE_CHECK_VERSION(0, 50, 0)
1356  vte_terminal_copy_clipboard_format(VTE_TERMINAL(gpdata->vte), VTE_FORMAT_TEXT);
1357 #else
1358  vte_terminal_copy_clipboard(VTE_TERMINAL(gpdata->vte));
1359 #endif
1360  return;
1361  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_PASTE:
1362  vte_terminal_paste_clipboard(VTE_TERMINAL(gpdata->vte));
1363  return;
1364  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_SELECT_ALL:
1365  vte_terminal_select_all(VTE_TERMINAL(gpdata->vte));
1366  return;
1367  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_INCREASE_FONT:
1368  vte_terminal_set_font_scale(VTE_TERMINAL(gpdata->vte),
1369  vte_terminal_get_font_scale(VTE_TERMINAL(gpdata->vte)) + SCALE_FACTOR);
1370  return;
1371  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_DECREASE_FONT:
1372  vte_terminal_set_font_scale(VTE_TERMINAL(gpdata->vte),
1373  vte_terminal_get_font_scale(VTE_TERMINAL(gpdata->vte)) - SCALE_FACTOR);
1374  return;
1375  case REMMINA_PLUGIN_SSH_FEATURE_TOOL_SEARCH:
1376  remmina_plugin_pop_search(NULL, gp);
1377  return;
1378  }
1379 }
1380 
1392 static gpointer ssh_auth[] =
1393 {
1394  "0", N_("Password"),
1395  "1", N_("SSH identity file"),
1396  "2", N_("SSH agent"),
1397  "3", N_("Public key (automatic)"),
1398  "4", N_("Kerberos (GSSAPI)"),
1399  NULL
1400 };
1401 
1403 static gpointer ssh_charset_list[] =
1404 {
1405  "", "",
1406  "", "ASCII",
1407  "", "BIG5",
1408  "", "CP437",
1409  "", "CP720",
1410  "", "CP737",
1411  "", "CP775",
1412  "", "CP850",
1413  "", "CP852",
1414  "", "CP855",
1415  "", "CP857",
1416  "", "CP858",
1417  "", "CP862",
1418  "", "CP866",
1419  "", "CP874",
1420  "", "CP1125",
1421  "", "CP1250",
1422  "", "CP1251",
1423  "", "CP1252",
1424  "", "CP1253",
1425  "", "CP1254",
1426  "", "CP1255",
1427  "", "CP1256",
1428  "", "CP1257",
1429  "", "CP1258",
1430  "", "EUC-JP",
1431  "", "EUC-KR",
1432  "", "GBK",
1433  "", "ISO 8859-1",
1434  "", "ISO 8859-2",
1435  "", "ISO 8859-3",
1436  "", "ISO 8859-4",
1437  "", "ISO 8859-5",
1438  "", "ISO 8859-6",
1439  "", "ISO 8859-7",
1440  "", "ISO 8859-8",
1441  "", "ISO 8859-9",
1442  "", "ISO 8859-10",
1443  "", "ISO 8859-11",
1444  "", "ISO 8859-12",
1445  "", "ISO 8859-13",
1446  "", "ISO 8859-14",
1447  "", "ISO 8859-15",
1448  "", "ISO 8859-16",
1449  "", "KOI8-R",
1450  "", "SJIS",
1451  "", "UTF-8",
1452  NULL
1453 };
1454 
1455 static gpointer ssh_terminal_palette[] =
1456 {
1457  "0", "Linux",
1458  "1", "Tango",
1459  "2", "Gruvbox",
1460  "3", "Solarized Dark",
1461  "4", "Solarized Light",
1462  "5", "XTerm",
1463  "6", "Custom (Configured in Remmina preferences)",
1464  NULL, NULL
1465 };
1466 
1472 {
1473  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_COPY, N_("Copy"), N_("_Copy"), NULL },
1474  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_PASTE, N_("Paste"), N_("_Paste"), NULL },
1475  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_SELECT_ALL, N_("Select all"), N_("_Select all"), NULL },
1476  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_INCREASE_FONT, N_("Increase font size"), N_("_Increase font size"), NULL },
1477  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_DECREASE_FONT, N_("Decrease font size"), N_("_Decrease font size"), NULL },
1478  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SSH_FEATURE_TOOL_SEARCH, N_("Find text"), N_("_Find text"), NULL },
1479  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PROTOCOL_FEATURE_TOOL_SFTP, N_("Open SFTP transfer…"), "folder-remote", NULL },
1480  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
1481 };
1482 
1483 /* Array of RemminaProtocolSetting for basic settings.
1484  * Each item is composed by:
1485  * a) RemminaProtocolSettingType for setting type
1486  * b) Setting name
1487  * c) Setting description
1488  * d) Compact disposition
1489  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
1490  * f) Setting tooltip
1491  * g) Validation data pointer, will be passed to the validation callback method.
1492  * h) Validation callback method (Can be NULL. Every entry will be valid then.)
1493  * use following prototype:
1494  * gboolean mysetting_validator_method(gpointer key, gpointer value,
1495  * gpointer validator_data);
1496  * gpointer key is a gchar* containing the setting's name,
1497  * gpointer value contains the value which should be validated,
1498  * gpointer validator_data contains your passed data.
1499  */
1501 {
1502  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_ssh._tcp", NULL, NULL, NULL },
1503  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_auth", N_("Authentication type"), FALSE, ssh_auth, NULL, NULL, NULL },
1504  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
1505  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL },
1506  { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "ssh_privatekey", N_("SSH identity file"), FALSE, NULL, NULL, NULL, NULL },
1507 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)
1508  { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "ssh_certfile", N_("SSH certificate file"), FALSE, NULL, NULL, NULL, NULL },
1509 #endif
1510  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "ssh_passphrase", N_("Password to unlock private key"), FALSE, NULL, NULL, NULL, NULL },
1511  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "run_line", N_("Opening command"), FALSE, NULL, NULL, NULL, NULL },
1512  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Start-up background program"), FALSE, NULL, NULL, NULL, NULL },
1513  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
1514 };
1515 
1516 static gchar log_tips[] =
1517  N_("The filename can use the following placeholders:\n\n"
1518  " • %h is substituted with the server name\n"
1519  " • %t is substituted with the SSH server name\n"
1520  " • %u is substituted with the username\n"
1521  " • %U is substituted with the SSH username\n"
1522  " • %p is substituted with Remmina profile name\n"
1523  " • %g is substituted with Remmina profile group name\n"
1524  " • %d is substituted with local date and time in ISO 8601 format\n");
1525 
1538 {
1539  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_color_scheme", N_("Terminal colour scheme"), FALSE, ssh_terminal_palette, NULL },
1540  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_charset", N_("Character set"), FALSE, ssh_charset_list, NULL },
1541  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_proxycommand", N_("SSH Proxy Command"), FALSE, NULL, NULL },
1542  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_kex_algorithms", N_("KEX (Key Exchange) algorithms"), FALSE, NULL, NULL },
1543  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_ciphers", N_("Symmetric cipher client to server"), FALSE, NULL, NULL },
1544  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_hostkeytypes", N_("Preferred server host key types"), FALSE, NULL, NULL },
1545  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "sshlogfolder", N_("Folder for SSH session log"), FALSE, NULL, N_("Full path of an existing folder") },
1546  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "sshlogname", N_("Filename for SSH session log"), FALSE, NULL, log_tips },
1547  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sshlogenabled", N_("Log SSH session when exiting Remmina"), FALSE, NULL, NULL },
1548  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sshsavesession", N_("Log SSH session asynchronously"), TRUE, NULL, N_("Saving the session asynchronously may have a notable performance impact") },
1549  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "audiblebell", N_("Audible terminal bell"), FALSE, NULL, NULL },
1550  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "ssh_forward_x11", N_("SSH X11 Forwarding"), TRUE, NULL, NULL },
1551  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "ssh_compression", N_("SSH compression"), FALSE, NULL, NULL },
1552  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Don't remember passwords"), TRUE, NULL, NULL },
1553  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "ssh_stricthostkeycheck", N_("Strict host key checking"), FALSE, NULL, NULL },
1554  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
1555 };
1556 
1564 {
1566  "SSH",
1567  N_("SSH - Secure Shell"),
1568  GETTEXT_PACKAGE,
1569  VERSION,
1570  "org.remmina.Remmina-ssh-symbolic",
1571  "org.remmina.Remmina-ssh-symbolic",
1582  NULL,
1583  NULL,
1584  NULL
1585 };
1586 
1587 
1588 /*
1589  * this function is used for
1590  * - inserting into the list to became a sorted list [g_list_insert_sorted()]
1591  * - checking the list to avoid duplicate entries [g_list_find_custom()]
1592  */
1593 static gint
1594 compare(gconstpointer a, gconstpointer b)
1595 {
1596  return strcmp((gchar *)a, (gchar *)b);
1597 }
1598 
1599 void
1600 remmina_ssh_plugin_load_terminal_palettes(gpointer *ssh_terminal_palette_new)
1601 {
1602  unsigned int preset_rec_size = sizeof(ssh_terminal_palette) / sizeof(gpointer);
1603 
1604  GError *error = NULL;
1605  GList *files = NULL;
1606  unsigned int rec_size = 0;
1607  /*
1608  * count number of (all) files to reserve enough memory
1609  */
1610  /* /usr/local/share/remmina */
1611  const gchar *const *dirs = g_get_system_data_dirs();
1612 
1613  unsigned int i = 0;
1614 
1615  for (i = 0; dirs[i] != NULL; ++i) {
1616  GDir *system_data_dir = NULL;
1617  gchar *remmina_dir = g_build_path("/", dirs[i], "remmina", "theme", NULL);
1618  system_data_dir = g_dir_open(remmina_dir, 0, &error);
1619  g_free(remmina_dir);
1620  // ignoring this error is OK, because the folder may not exist
1621  if (error) {
1622  g_error_free(error);
1623  error = NULL;
1624  } else {
1625  if (system_data_dir) {
1626  const gchar *filename;
1627  while ((filename = g_dir_read_name(system_data_dir))) {
1628  if (!g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1629  if (g_str_has_suffix(filename, ".colors")) {
1630  gsize len = strrchr(filename, '.') - filename;
1631  gchar *menu_str = g_strndup(filename, len);
1632  if (g_list_find_custom(files, menu_str, compare) == NULL)
1633  files = g_list_insert_sorted(files, menu_str, compare);
1634  }
1635  }
1636  }
1637  }
1638  }
1639  }
1640 
1641  /* ~/.config/remmina/colors */
1642  gchar *remmina_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "theme", NULL);
1643  GDir *user_data_dir;
1644 
1645  user_data_dir = g_dir_open(remmina_dir, 0, &error);
1646  g_free(remmina_dir);
1647  if (error) {
1648  g_error_free(error);
1649  error = NULL;
1650  } else {
1651  if (user_data_dir) {
1652  const gchar *filename;
1653  while ((filename = g_dir_read_name(user_data_dir))) {
1654  if (!g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1655  if (g_str_has_suffix(filename, ".colors")) {
1656  char *menu_str = g_malloc(strlen(filename) + 1);
1657  strcpy(menu_str, filename);
1658  char *t2 = strrchr(menu_str, '.');
1659  t2[0] = 0;
1660  if (g_list_find_custom(files, menu_str, compare) == NULL)
1661  files = g_list_insert_sorted(files, menu_str, compare);
1662  }
1663  }
1664  }
1665  }
1666  }
1667 
1668  rec_size = g_list_length(files) * 2;
1669 
1670  gpointer *color_palette = g_malloc((preset_rec_size + rec_size) * sizeof(gpointer));
1671 
1672  unsigned int field_idx = 0;
1673 
1674  *ssh_terminal_palette_new = color_palette;
1675  // preset with (old) static ssh_terminal_palette data
1676  for (; field_idx < preset_rec_size; field_idx++) {
1677  color_palette[field_idx] = ssh_terminal_palette[field_idx];
1678  if (!color_palette[field_idx])
1679  break;
1680  }
1681 
1682  GList *l_files = NULL;
1683 
1684  for (l_files = g_list_first(files); l_files != NULL; l_files = l_files->next) {
1685  gchar *menu_str = (gchar *)l_files->data;
1686 
1687  color_palette[field_idx++] = menu_str;
1688  color_palette[field_idx++] = menu_str;
1689  }
1690  g_list_free(files);
1691 
1692  color_palette[field_idx] = NULL;
1693 }
1694 
1695 void
1697 {
1698  TRACE_CALL(__func__);
1705 
1707 
1708  RemminaProtocolSettingOpt *settings;
1709 
1710  // preset new settings with (old) static remmina_ssh_advanced_settings data
1711 #if GLIB_CHECK_VERSION(2,68,0)
1712  settings = g_memdup2(remmina_ssh_advanced_settings, sizeof(remmina_ssh_advanced_settings));
1713 #else
1714  settings = g_memdup(remmina_ssh_advanced_settings, sizeof(remmina_ssh_advanced_settings));
1715 #endif
1716 
1717  // create dynamic advanced settings to made replacing of ssh_terminal_palette possible
1718  gpointer ssh_terminal_palette_new = NULL;
1719 
1720  remmina_ssh_plugin_load_terminal_palettes(&ssh_terminal_palette_new);
1721 
1722  settings[0].opt1 = ssh_terminal_palette_new;
1724 
1726 
1727  ssh_threads_set_callbacks(ssh_threads_get_pthread());
1728  ssh_init();
1729 }
1730 
1731 #else
1732 
1733 void remmina_ssh_plugin_register(void)
1734 {
1735  TRACE_CALL(__func__);
1736 }
1737 
1738 #endif
@ REMMINA_PLUGIN_TYPE_PROTOCOL
Definition: plugin.h:48
RemminaPref remmina_pref
Definition: rcw.c:79
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:604
RemminaFile * remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
Definition: remmina_file.c:900
gchar * remmina_file_format_properties(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:562
void remmina_masterthread_exec_and_wait(RemminaMTExecData *d)
gboolean remmina_masterthread_exec_is_main_thread()
RemminaPluginService remmina_plugin_manager_service
void remmina_pref_file_load_colors(GKeyFile *gkeyfile, RemminaColorPref *color_pref)
Definition: remmina_pref.c:182
gchar * remmina_colors_file
void remmina_protocol_widget_call_feature_by_type(RemminaProtocolWidget *gp, RemminaProtocolFeatureType type, gint id)
GtkBuilder * remmina_public_gtk_builder_new_from_resource(gchar *resource)
void remmina_public_send_notification(const gchar *notification_id, const gchar *notification_title, const gchar *notification_message)
enum remmina_ssh_auth_result remmina_ssh_auth(RemminaSSH *ssh, const gchar *password, RemminaProtocolWidget *gp, RemminaFile *remminafile)
Definition: remmina_ssh.c:1092
gboolean remmina_ssh_shell_open(RemminaSSHShell *shell, RemminaSSHExitFunc exit_callback, gpointer data)
RemminaSSHShell * remmina_ssh_shell_new_from_file(RemminaFile *remminafile)
void remmina_ssh_shell_free(RemminaSSHShell *shell)
void(* RemminaSSHExitFunc)(gpointer data)
Definition: remmina_ssh.h:242
gboolean remmina_ssh_init_session(RemminaSSH *ssh)
@ REMMINA_SSH_AUTH_PARTIAL
Definition: remmina_ssh.h:110
@ REMMINA_SSH_AUTH_USERCANCEL
Definition: remmina_ssh.h:113
@ REMMINA_SSH_AUTH_RECONNECT
Definition: remmina_ssh.h:115
@ REMMINA_SSH_AUTH_SUCCESS
Definition: remmina_ssh.h:109
@ REMMINA_SSH_AUTH_NULL
Definition: remmina_ssh.h:108
void remmina_ssh_shell_set_size(RemminaSSHShell *shell, gint columns, gint rows)
enum remmina_ssh_auth_result remmina_ssh_auth_gui(RemminaSSH *ssh, RemminaProtocolWidget *gp, RemminaFile *remminafile)
RemminaSSHShell * remmina_ssh_shell_new_from_ssh(RemminaSSH *ssh)
static gboolean remmina_plugin_ssh_open_connection(RemminaProtocolWidget *gp)
Initialize the main window properties and the pthread.
void remmina_ssh_plugin_load_terminal_palettes(gpointer *ssh_terminal_palette_new)
void remmina_plugin_pop_search(GtkMenuItem *menuitem, RemminaProtocolWidget *gp)
static void remmina_search_widget_update_regex(RemminaPluginSshData *gpdata)
color_schemes
@ XTERM
@ SOLARIZED_DARK
@ GRUVBOX
@ TANGO
@ CUSTOM
@ SOLARIZED_LIGHT
@ LINUX
static void remmina_search_widget_update_sensitivity(RemminaSshSearch *search_widget)
const GdkRGBA tango_palette[PALETTE_SIZE]
static gint compare(gconstpointer a, gconstpointer b)
static const RemminaProtocolSetting remmina_ssh_basic_settings[]
static gboolean remmina_plugin_ssh_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Not used by the plugin.
void remmina_plugin_ssh_vte_select_all(GtkMenuItem *menuitem, gpointer vte)
static gboolean valid_color(GdkRGBA const *color)
struct _RemminaPluginSshData RemminaPluginSshData
static RemminaProtocolPlugin remmina_plugin_ssh
SSH Protocol plugin definition and features.
gboolean remmina_ssh_plugin_popup_menu(GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
void remmina_plugin_ssh_vte_increase_font(GtkMenuItem *menuitem, gpointer vte)
static gchar log_tips[]
void remmina_plugin_ssh_popup_ui(RemminaProtocolWidget *gp)
Remmina SSH plugin terminal popup menu.
static gpointer remmina_plugin_ssh_main_thread(gpointer data)
Remmina protocol plugin main function.
void remmina_plugin_ssh_vte_save_session(GtkMenuItem *menuitem, RemminaProtocolWidget *gp)
static const RemminaProtocolSetting remmina_ssh_advanced_settings[]
Array of RemminaProtocolSetting for advanced settings.
const GdkRGBA solarized_light_palette[PALETTE_SIZE]
static void remmina_plugin_ssh_init(RemminaProtocolWidget *gp)
Remmina SSH plugin initialization.
static gboolean remmina_plugin_ssh_close_connection(RemminaProtocolWidget *gp)
const GdkRGBA linux_palette[PALETTE_SIZE]
16 colour palettes in GdkRGBA format (red, green, blue, alpha).
static void remmina_plugin_ssh_eof(VteTerminal *vteterminal, RemminaProtocolWidget *gp)
void remmina_ssh_plugin_register(void)
static gboolean remmina_plugin_ssh_on_focus_in(GtkWidget *widget, GdkEventFocus *event, RemminaProtocolWidget *gp)
static gpointer ssh_charset_list[]
Charset list.
static RemminaPluginService * remmina_plugin_service
static void jit_regex(VteRegex *regex, char const *pattern)
const GdkRGBA xterm_palette[PALETTE_SIZE]
static void remmina_search_widget_search_forward(RemminaPluginSshData *gpdata)
static void remmina_plugin_ssh_set_vte_pref(RemminaProtocolWidget *gp)
static VteRegex * compile_regex_for_search(char const *pattern, gboolean caseless, GError **error)
static RemminaProtocolFeature remmina_plugin_ssh_features[]
Array for available features.
static void remmina_search_widget_search_backward(RemminaPluginSshData *gpdata)
static void remmina_ssh_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Send a keystroke to the plugin window.
static gpointer ssh_auth[]
Array of key/value pairs for SSH auth type libssh methods:
struct _RemminaSshSearch RemminaSshSearch
The SSH plugin implementation.
static void remmina_search_widget_wrap_around_toggled(GtkToggleButton *button, RemminaPluginSshData *gpdata)
static gboolean remmina_plugin_ssh_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
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_copy_clipboard(GtkMenuItem *menuitem, gpointer vte)
GtkWidget * remmina_plugin_pop_search_new(GtkWidget *relative_to, RemminaProtocolWidget *gp)
void remmina_plugin_ssh_vte_decrease_font(GtkMenuItem *menuitem, gpointer vte)
void remmina_plugin_ssh_vte_terminal_set_encoding_and_pty(VteTerminal *terminal, const char *codeset, int master, int slave)
static gpointer ssh_terminal_palette[]
void remmina_plugin_ssh_vte_paste_clipboard(GtkMenuItem *menuitem, gpointer vte)
void remmina_plugin_ssh_call_sftp(GtkMenuItem *menuitem, RemminaProtocolWidget *gp)
const GdkRGBA gruvbox_palette[PALETTE_SIZE]
const GdkRGBA solarized_dark_palette[PALETTE_SIZE]
gchar * cursor_foreground
Definition: remmina_pref.h:111
gchar * highlight_foreground
Definition: remmina_pref.h:113
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:238
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:194
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:187
void(* protocol_plugin_send_keys_signals)(GtkWidget *widget, const guint *keyvals, int length, GdkEventType action)
Definition: plugin.h:228
void(* protocol_plugin_set_expand)(RemminaProtocolWidget *gp, gboolean expand)
Definition: plugin.h:190
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:182
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:235
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:265
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:192
GtkWidget *(* open_connection)(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: plugin.h:263
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:202
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:201
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:185
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:196
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:197
RemminaSshSearch * search_widget
RemminaSSHShell * shell
guint vte_shortcutkey_decrease_font
Definition: remmina_pref.h:210
guint vte_shortcutkey_search_text
Definition: remmina_pref.h:211
gboolean vte_allow_bold_text
Definition: remmina_pref.h:203
guint vte_shortcutkey_copy
Definition: remmina_pref.h:206
RemminaColorPref color_pref
Definition: remmina_pref.h:237
guint vte_shortcutkey_paste
Definition: remmina_pref.h:207
guint vte_shortcutkey_increase_font
Definition: remmina_pref.h:209
guint vte_shortcutkey_select_all
Definition: remmina_pref.h:208
gchar * vte_font
Definition: remmina_pref.h:202
const RemminaProtocolSetting * advanced_settings
Definition: plugin.h:91
gchar * error
Definition: remmina_ssh.h:80
ssh_session session
Definition: remmina_ssh.h:60
gint tunnel_entrance_port
Definition: remmina_ssh.h:89
ssh_callbacks callback
Definition: remmina_ssh.h:61
gchar * tunnel_entrance_host
Definition: remmina_ssh.h:88
The SSH plugin implementation.
GtkToggleButton * regex_checkbutton
GtkWidget * search_next_button
GtkToggleButton * wrap_around_checkbutton
GtkToggleButton * entire_word_checkbutton
GtkToggleButton * match_case_checkbutton
GtkWidget * search_prev_button
struct remmina_masterthread_exec_data::@12::@26 vte_terminal_set_encoding_and_pty
union remmina_masterthread_exec_data::@12 p
enum remmina_masterthread_exec_data::@11 func
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
@ REMMINA_PROTOCOL_SSH_SETTING_TUNNEL
Definition: types.h:130
@ REMMINA_PROTOCOL_FEATURE_TYPE_END
Definition: types.h:47
@ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL
Definition: types.h:49
@ REMMINA_PROTOCOL_SETTING_TYPE_SELECT
Definition: types.h:108
@ REMMINA_PROTOCOL_SETTING_TYPE_FILE
Definition: types.h:111
@ REMMINA_PROTOCOL_SETTING_TYPE_CHECK
Definition: types.h:110
@ REMMINA_PROTOCOL_SETTING_TYPE_SERVER
Definition: types.h:100
@ REMMINA_PROTOCOL_SETTING_TYPE_TEXT
Definition: types.h:106
@ REMMINA_PROTOCOL_SETTING_TYPE_END
Definition: types.h:98
@ REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD
Definition: types.h:101
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953