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_protocol_widget.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 
39 #include <gtk/gtk.h>
40 #include <gtk/gtkx.h>
41 #include <glib/gi18n.h>
42 #include <gmodule.h>
43 #include <stdlib.h>
44 
45 
46 
47 #include "remmina_chat_window.h"
49 #include "remmina_ext_exec.h"
50 #include "remmina_plugin_manager.h"
51 #include "remmina_pref.h"
53 #include "remmina_public.h"
54 #include "remmina_ssh.h"
55 #include "remmina_log.h"
57 
58 #ifdef GDK_WINDOWING_WAYLAND
59 #include <gdk/gdkwayland.h>
60 #endif
61 
66 
67  gint width;
68  gint height;
70  gboolean scaler_expand;
71 
72  gboolean has_error;
73  gchar * error_message;
74  /* ssh_tunnels is an array of RemminaSSHTunnel*
75  * the 1st one is the "main" tunnel, other tunnels are used for example in sftp commands */
76  GPtrArray * ssh_tunnels;
78 
79  GtkWidget * chat_window;
80 
81  gboolean closed;
82 
84 
87 
88  RemminaMessagePanel * connect_message_panel;
89  RemminaMessagePanel * listen_message_panel;
90  RemminaMessagePanel * auth_message_panel;
91 
92  /* Data saved from the last message_panel when the user confirm */
93  gchar * username;
94  gchar * password;
95  gchar * domain;
96  gboolean save_password;
97 
98  gchar * cacert;
99  gchar * cacrl;
100  gchar * clientcert;
101  gchar * clientkey;
102 };
103 
108 };
109 
110 G_DEFINE_TYPE(RemminaProtocolWidget, remmina_protocol_widget, GTK_TYPE_EVENT_BOX)
111 
112 enum {
113  CONNECT_SIGNAL,
114  DISCONNECT_SIGNAL,
115  DESKTOP_RESIZE_SIGNAL,
116  UPDATE_ALIGN_SIGNAL,
117  UNLOCK_DYNRES_SIGNAL,
119 };
120 
123  const gchar * signal_name;
125 
127 { 0 };
128 
130 {
131  TRACE_CALL(__func__);
132  remmina_protocol_widget_signals[CONNECT_SIGNAL] = g_signal_new("connect", G_TYPE_FROM_CLASS(klass),
133  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaProtocolWidgetClass, connect), NULL, NULL,
134  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
135  remmina_protocol_widget_signals[DISCONNECT_SIGNAL] = g_signal_new("disconnect", G_TYPE_FROM_CLASS(klass),
136  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaProtocolWidgetClass, disconnect), NULL, NULL,
137  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
138  remmina_protocol_widget_signals[DESKTOP_RESIZE_SIGNAL] = g_signal_new("desktop-resize", G_TYPE_FROM_CLASS(klass),
139  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaProtocolWidgetClass, desktop_resize), NULL, NULL,
140  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
141  remmina_protocol_widget_signals[UPDATE_ALIGN_SIGNAL] = g_signal_new("update-align", G_TYPE_FROM_CLASS(klass),
142  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaProtocolWidgetClass, update_align), NULL, NULL,
143  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
144  remmina_protocol_widget_signals[UNLOCK_DYNRES_SIGNAL] = g_signal_new("unlock-dynres", G_TYPE_FROM_CLASS(klass),
145  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaProtocolWidgetClass, unlock_dynres), NULL, NULL,
146  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
147 }
148 
149 
151 {
152  TRACE_CALL(__func__);
153  int i;
154 
155  if (gp->priv->ssh_tunnels) {
156  for(i = 0;i < gp->priv->ssh_tunnels->len; i++) {
157 #ifdef HAVE_LIBSSH
159 #else
160  g_debug ("LibSSH support turned off, no need to free SSH tunnel data");
161 #endif
162  }
163  }
164  g_ptr_array_set_size(gp->priv->ssh_tunnels, 0);
165 }
166 
167 
169 {
170  TRACE_CALL(__func__);
171 
172  g_free(gp->priv->username);
173  gp->priv->username = NULL;
174 
175  g_free(gp->priv->password);
176  gp->priv->password = NULL;
177 
178  g_free(gp->priv->domain);
179  gp->priv->domain = NULL;
180 
181  g_free(gp->priv->cacert);
182  gp->priv->cacert = NULL;
183 
184  g_free(gp->priv->cacrl);
185  gp->priv->cacrl = NULL;
186 
187  g_free(gp->priv->clientcert);
188  gp->priv->clientcert = NULL;
189 
190  g_free(gp->priv->clientkey);
191  gp->priv->clientkey = NULL;
192 
193  g_free(gp->priv->features);
194  gp->priv->features = NULL;
195 
196  g_free(gp->priv->error_message);
197  gp->priv->error_message = NULL;
198 
199  g_free(gp->priv->remmina_file);
200  gp->priv->remmina_file = NULL;
201 
202  g_free(gp->priv);
203  gp->priv = NULL;
204 
206 
207  g_ptr_array_free(gp->priv->ssh_tunnels, TRUE);
208 
209  gp->priv->ssh_tunnels = NULL;
210 }
211 
213 {
214  TRACE_CALL(__func__);
215  GtkWidget *child;
216 
217  child = gtk_bin_get_child(GTK_BIN(gp));
218 
219  if (child) {
220  gtk_widget_set_can_focus(child, TRUE);
221  gtk_widget_grab_focus(child);
222  }
223 }
224 
226 {
227  TRACE_CALL(__func__);
229 
230  priv = g_new0(RemminaProtocolWidgetPriv, 1);
231  gp->priv = priv;
232  gp->priv->closed = TRUE;
233  gp->priv->ssh_tunnels = g_ptr_array_new();
234 
235  g_signal_connect(G_OBJECT(gp), "destroy", G_CALLBACK(remmina_protocol_widget_destroy), NULL);
236 }
237 
239 {
240  TRACE_CALL(__func__);
241  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(data);
242 
244  RemminaProtocolFeature *feature;
245  gint num_plugin;
246  gint num_ssh;
247 
248  gp->priv->closed = FALSE;
249 
250  plugin = gp->priv->plugin;
251  plugin->init(gp);
252 
253  for (num_plugin = 0, feature = (RemminaProtocolFeature *)plugin->features; feature && feature->type; num_plugin++, feature++) {
254  }
255 
256  num_ssh = 0;
257 #ifdef HAVE_LIBSSH
258  if (remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_enabled", FALSE))
259  num_ssh += 2;
260 
261 #endif
262  if (num_plugin + num_ssh == 0) {
263  gp->priv->features = NULL;
264  } else {
265  gp->priv->features = g_new0(RemminaProtocolFeature, num_plugin + num_ssh + 1);
266  feature = gp->priv->features;
267  if (plugin->features) {
268  memcpy(feature, plugin->features, sizeof(RemminaProtocolFeature) * num_plugin);
269  feature += num_plugin;
270  }
271 #ifdef HAVE_LIBSSH
272  if (num_ssh) {
274  feature->id = REMMINA_PROTOCOL_FEATURE_TOOL_SSH;
275  feature->opt1 = _("Connect via SSH from a new terminal");
276  feature->opt2 = "utilities-terminal";
277  feature++;
278 
280  feature->id = REMMINA_PROTOCOL_FEATURE_TOOL_SFTP;
281  feature->opt1 = _("Open SFTP transfer…");
282  feature->opt2 = "folder-remote";
283  feature++;
284  }
286 #endif
287  }
288 
289  if (!plugin->open_connection(gp))
291 }
292 
293 static void cancel_open_connection_cb(void *cbdata, int btn)
294 {
296 
298 }
299 
301 {
302  TRACE_CALL(__func__);
303  gchar *s;
304  const gchar *name;
305  RemminaMessagePanel *mp;
306 
307  /* Exec precommand before everything else */
309  remmina_message_panel_setup_progress(mp, _("Executing external commands…"), NULL, NULL);
311 
312  remmina_ext_exec_new(gp->priv->remmina_file, "precommand");
314 
315  name = remmina_file_get_string(gp->priv->remmina_file, "name");
316  s = g_strdup_printf(_("Connecting to \"%s\"…"), (name ? name : "*"));
317 
320  g_free(s);
321  gp->priv->connect_message_panel = mp;
323 
325 }
326 
327 static gboolean conn_closed(gpointer data)
328 {
329  TRACE_CALL(__func__);
331 
332 #ifdef HAVE_LIBSSH
333  /* This will close all tunnels */
335 #endif
336  /* Exec postcommand */
337  remmina_ext_exec_new(gp->priv->remmina_file, "postcommand");
338  /* Notify listeners (usually rcw) that the connection is closed */
339  g_signal_emit_by_name(G_OBJECT(gp), "disconnect");
340  return G_SOURCE_REMOVE;
341 }
342 
344 {
345  /* Plugin told us that it closed the connection,
346  * add async event to main thread to complete our close tasks */
347  TRACE_CALL(__func__);
348  gp->priv->closed = TRUE;
349  g_idle_add(conn_closed, (gpointer)gp);
350 }
351 
352 static gboolean conn_opened(gpointer data)
353 {
354  TRACE_CALL(__func__);
356  guint i;
357 
358 #ifdef HAVE_LIBSSH
359  if (gp->priv->ssh_tunnels) {
360  for(i = 0;i < gp->priv->ssh_tunnels->len; i++) {
362  }
363  }
364 #endif
365  if (gp->priv->listen_message_panel) {
367  gp->priv->listen_message_panel = NULL;
368  }
369  if (gp->priv->connect_message_panel) {
371  gp->priv->connect_message_panel = NULL;
372  }
373  g_signal_emit_by_name(G_OBJECT(gp), "connect");
374  return G_SOURCE_REMOVE;
375 }
376 
378 {
379  /* Plugin told us that it closed the connection,
380  * add async event to main thread to complete our close tasks */
381  TRACE_CALL(__func__);
382  g_idle_add(conn_opened, (gpointer)gp);
383 }
384 
385 static gboolean update_align(gpointer data)
386 {
387  TRACE_CALL(__func__);
389 
390  g_signal_emit_by_name(G_OBJECT(gp), "update-align");
391  return G_SOURCE_REMOVE;
392 }
393 
395 {
396  /* Called by the plugin to do updates on rcw */
397  TRACE_CALL(__func__);
398  g_idle_add(update_align, (gpointer)gp);
399 }
400 
401 static gboolean unlock_dynres(gpointer data)
402 {
403  TRACE_CALL(__func__);
405  g_signal_emit_by_name(G_OBJECT(gp), "unlock-dynres");
406  return G_SOURCE_REMOVE;
407 }
408 
410 {
411  /* Called by the plugin to do updates on rcw */
412  TRACE_CALL(__func__);
413  g_idle_add(unlock_dynres, (gpointer)gp);
414 }
415 
416 static gboolean desktop_resize(gpointer data)
417 {
418  TRACE_CALL(__func__);
420  g_signal_emit_by_name(G_OBJECT(gp), "desktop-resize");
421  return G_SOURCE_REMOVE;
422 }
423 
425 {
426  /* Called by the plugin to do updates on rcw */
427  TRACE_CALL(__func__);
428  g_idle_add(desktop_resize, (gpointer)gp);
429 }
430 
431 
433 {
434  TRACE_CALL(__func__);
435 
436  /* kindly ask the protocol plugin to close the connection.
437  * Nothing else is done here. */
438 
439  if (!GTK_IS_WIDGET(gp))
440  return;
441 
442  if (gp->priv->chat_window) {
443  gtk_widget_destroy(gp->priv->chat_window);
444  gp->priv->chat_window = NULL;
445  }
446 
447  if (gp->priv->closed) {
448  /* Connection is already closed by the plugin, but
449  * rcw is asking to close again (usually after an error panel)
450  */
451  g_signal_emit_by_name(G_OBJECT(gp), "disconnect");
452  return;
453  }
454 
455  /* Ask the plugin to close, async.
456  * The plugin will emit a "disconnect" signal on gp to call our
457  * remmina_protocol_widget_on_disconnected() when done */
458  gp->priv->plugin->close_connection(gp);
459 
460  return;
461 }
462 
463 
467 {
468  return gp->priv->plugin->send_keystrokes ? TRUE : FALSE;
469 }
470 
475 {
476  TRACE_CALL(__func__);
477  gchar *keystrokes = g_object_get_data(G_OBJECT(widget), "keystrokes");
478  guint *keyvals;
479  gint i;
480  GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
481  gchar *iter = keystrokes;
482  gunichar character;
483  guint keyval;
484  GdkKeymapKey *keys;
485  gint n_keys;
486  /* Single keystroke replace */
487  typedef struct _KeystrokeReplace {
488  gchar * search;
489  gchar * replace;
490  guint keyval;
491  } KeystrokeReplace;
492  /* Special characters to replace */
493  KeystrokeReplace keystrokes_replaces[] =
494  {
495  { "\\n", "\n", GDK_KEY_Return },
496  { "\\t", "\t", GDK_KEY_Tab },
497  { "\\b", "\b", GDK_KEY_BackSpace },
498  { "\\e", "\e", GDK_KEY_Escape },
499  { "\\\\", "\\", GDK_KEY_backslash },
500  { NULL, NULL, 0 }
501  };
502  /* Keystrokes can only be sent to plugins that accepts them */
504  /* Replace special characters */
505  for (i = 0; keystrokes_replaces[i].replace; i++) {
507  keystrokes_replaces[i].search,
508  keystrokes_replaces[i].replace);
509  }
510  keyvals = (guint *)g_malloc(strlen(keystrokes));
511  while (TRUE) {
512  /* Process each character in the keystrokes */
513  character = g_utf8_get_char_validated(iter, -1);
514  if (character == 0)
515  break;
516  keyval = gdk_unicode_to_keyval(character);
517  /* Replace all the special character with its keyval */
518  for (i = 0; keystrokes_replaces[i].replace; i++) {
519  if (character == keystrokes_replaces[i].replace[0]) {
520  keys = g_new0(GdkKeymapKey, 1);
521  keyval = keystrokes_replaces[i].keyval;
522  /* A special character was generated, no keyval lookup needed */
523  character = 0;
524  break;
525  }
526  }
527  /* Decode character if it’s not a special character */
528  if (character) {
529  /* get keyval without modifications */
530  if (!gdk_keymap_get_entries_for_keyval(keymap, keyval, &keys, &n_keys)) {
531  g_warning("keyval 0x%04x has no keycode!", keyval);
532  iter = g_utf8_find_next_char(iter, NULL);
533  continue;
534  }
535  }
536  /* Add modifier keys */
537  n_keys = 0;
538  if (keys->level & 1)
539  keyvals[n_keys++] = GDK_KEY_Shift_L;
540  if (keys->level & 2)
541  keyvals[n_keys++] = GDK_KEY_Alt_R;
542  keyvals[n_keys++] = keyval;
543  /* Send keystroke to the plugin */
544  gp->priv->plugin->send_keystrokes(gp, keyvals, n_keys);
545  g_free(keys);
546  /* Process next character in the keystrokes */
547  iter = g_utf8_find_next_char(iter, NULL);
548  }
549  g_free(keyvals);
550  }
551  g_free(keystrokes);
552  return;
553 }
554 
556 {
557  if (!gp->priv->plugin->get_plugin_screenshot) {
558  remmina_debug("plugin screenshot function is not implemented, using core Remmina functionality");
559  return FALSE;
560  }
561 
562  return gp->priv->plugin->get_plugin_screenshot(gp, rpsd);
563 }
564 
566 {
567  TRACE_CALL(__func__);
568 
569  g_print("Emitting signals should be used from the object itself, not from another object");
570  raise(SIGINT);
571 
573  /* Allow the execution of this function from a non main thread */
575  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
576  d->func = FUNC_PROTOCOLWIDGET_EMIT_SIGNAL;
577  d->p.protocolwidget_emit_signal.signal_name = signal_name;
578  d->p.protocolwidget_emit_signal.gp = gp;
580  g_free(d);
581  return;
582  }
583  g_signal_emit_by_name(G_OBJECT(gp), signal_name);
584 }
585 
587 {
588  TRACE_CALL(__func__);
589  return gp->priv->features;
590 }
591 
593 {
594  TRACE_CALL(__func__);
595  const RemminaProtocolFeature *feature;
596 
597 #ifdef HAVE_LIBSSH
599  remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_enabled", FALSE))
600  return TRUE;
601 
602 #endif
603  for (feature = gp->priv->plugin->features; feature && feature->type; feature++)
604  if (feature->type == type)
605  return TRUE;
606  return FALSE;
607 }
608 
610 {
611  TRACE_CALL(__func__);
612  return gp->priv->plugin->query_feature(gp, feature);
613 }
614 
616 {
617  TRACE_CALL(__func__);
618  const RemminaProtocolFeature *feature;
619 
620  for (feature = gp->priv->plugin->features; feature && feature->type; feature++) {
621  if (feature->type == type && (id == 0 || feature->id == id)) {
623  break;
624  }
625  }
626 }
627 
629 {
630  TRACE_CALL(__func__);
631  switch (feature->id) {
632 #ifdef HAVE_LIBSSH
633  case REMMINA_PROTOCOL_FEATURE_TOOL_SSH:
634  if (gp->priv->ssh_tunnels && gp->priv->ssh_tunnels->len > 0) {
637  (RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[0], NULL);
638  return;
639  }
640  break;
641 
642  case REMMINA_PROTOCOL_FEATURE_TOOL_SFTP:
643  if (gp->priv->ssh_tunnels && gp->priv->ssh_tunnels->len > 0) {
646  gp->priv->ssh_tunnels->pdata[0], NULL);
647  return;
648  }
649  break;
650 #endif
651  default:
652  break;
653  }
654  gp->priv->plugin->call_feature(gp, feature);
655 }
656 
657 static gboolean remmina_protocol_widget_on_key_press(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
658 {
659  TRACE_CALL(__func__);
660  if (gp->priv->hostkey_func)
661  return gp->priv->hostkey_func(gp, event->keyval, FALSE);
662  return FALSE;
663 }
664 
665 static gboolean remmina_protocol_widget_on_key_release(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
666 {
667  TRACE_CALL(__func__);
668  if (gp->priv->hostkey_func)
669  return gp->priv->hostkey_func(gp, event->keyval, TRUE);
670 
671  return FALSE;
672 }
673 
675 {
676  TRACE_CALL(__func__);
677  g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(remmina_protocol_widget_on_key_press), gp);
678  g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(remmina_protocol_widget_on_key_release), gp);
679 }
680 
682 {
683  TRACE_CALL(__func__);
684  gp->priv->hostkey_func = func;
685 }
686 
687 RemminaMessagePanel *remmina_protocol_widget_mpprogress(RemminaConnectionObject *cnnobj, const gchar *msg, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
688 {
689  RemminaMessagePanel *mp;
690 
692  /* Allow the execution of this function from a non main thread */
694  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
695  d->func = FUNC_PROTOCOLWIDGET_MPPROGRESS;
696  d->p.protocolwidget_mpprogress.cnnobj = cnnobj;
697  d->p.protocolwidget_mpprogress.message = msg;
698  d->p.protocolwidget_mpprogress.response_callback = response_callback;
699  d->p.protocolwidget_mpprogress.response_callback_data = response_callback_data;
701  mp = d->p.protocolwidget_mpprogress.ret_mp;
702  g_free(d);
703  return mp;
704  }
705 
707  remmina_message_panel_setup_progress(mp, msg, response_callback, response_callback_data);
708  rco_show_message_panel(cnnobj, mp);
709  return mp;
710 }
711 
712 void remmina_protocol_widget_mpdestroy(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
713 {
715  /* Allow the execution of this function from a non main thread */
717  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
718  d->func = FUNC_PROTOCOLWIDGET_MPDESTROY;
719  d->p.protocolwidget_mpdestroy.cnnobj = cnnobj;
720  d->p.protocolwidget_mpdestroy.mp = mp;
722  g_free(d);
723  return;
724  }
725  rco_destroy_message_panel(cnnobj, mp);
726 }
727 
728 #ifdef HAVE_LIBSSH
729 static void cancel_init_tunnel_cb(void *cbdata, int btn)
730 {
731  printf("Remmina: Cancelling an opening tunnel is not implemented\n");
732 }
733 
735 {
736  TRACE_CALL(__func__);
737  RemminaSSHTunnel *tunnel;
738  gint ret;
739  gchar *msg;
740  RemminaMessagePanel *mp;
741 
743 
744  g_debug ("[RPW] %s creating SSH tunnel to \"%s\" via SSH…", __func__, REMMINA_SSH(tunnel)->server);
745  msg = g_strdup_printf(_("Connecting to \"%s\" via SSH…"), REMMINA_SSH(tunnel)->server);
746 
748  g_free(msg);
749 
750  if (!remmina_ssh_init_session(REMMINA_SSH(tunnel))) {
751  remmina_debug ("%s Cannot init SSH session with tunnel struct", __func__);
752  remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error);
753  remmina_ssh_tunnel_free(tunnel);
754  return NULL;
755  }
756 
757  ret = remmina_ssh_auth_gui(REMMINA_SSH(tunnel), gp, gp->priv->remmina_file);
758  remmina_debug ("Tunnel auth returned %d", ret);
759  if (ret != REMMINA_SSH_AUTH_SUCCESS) {
760  if (ret != REMMINA_SSH_AUTH_USERCANCEL)
761  remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error);
762  remmina_ssh_tunnel_free(tunnel);
763  return NULL;
764  }
765 
767 
768  return tunnel;
769 }
770 #endif
771 
772 
773 #ifdef HAVE_LIBSSH
774 static void cancel_start_direct_tunnel_cb(void *cbdata, int btn)
775 {
776  printf("Remmina: Cancelling start_direct_tunnel is not implemented\n");
777 }
778 #endif
779 
780 static gboolean remmina_protocol_widget_tunnel_destroy(RemminaSSHTunnel *tunnel, gpointer data)
781 {
782  TRACE_CALL(__func__);
783  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(data);
784  guint idx;
785  gboolean found;
786 
787 #if GLIB_CHECK_VERSION(2,54,0)
788  found = g_ptr_array_find(gp->priv->ssh_tunnels, tunnel, &idx);
789 #else
790  int i;
791  found = FALSE;
792  for(i = 0;i < gp->priv->ssh_tunnels->len; i++) {
793  if ((RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[i] == tunnel) {
794  found = TRUE;
795  idx = i;
796  }
797  }
798 #endif
799 
800  printf("Tunnel %s found at idx = %d\n", found ? "yes": "not", idx);
801 
802  if (found) {
803 #ifdef HAVE_LIBSSH
804  g_debug("[RPW] tunnel with idx %u has been disconnected", idx);
805  remmina_ssh_tunnel_free(tunnel);
806 #endif
807  g_ptr_array_remove(gp->priv->ssh_tunnels, tunnel);
808  }
809  return TRUE;
810 }
811 
816 gchar *remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
817 {
818  TRACE_CALL(__func__);
819  const gchar *server;
820  const gchar *ssh_tunnel_server;
821  //const gchar *proto;
822  gchar *ssh_tunnel_host, *srv_host, *dest;
823  gint srv_port, ssh_tunnel_port;
824 
825  g_debug ("SSH tunnel initialization…");
826  //proto = remmina_file_get_string(gp->priv->remmina_file, "protocol");
827 
828  server = remmina_file_get_string(gp->priv->remmina_file, "server");
829  ssh_tunnel_server = remmina_file_get_string(gp->priv->remmina_file, "ssh_tunnel_server");
830 
831  if (!server)
832  return g_strdup("");
833 
834  remmina_public_get_server_port(server, default_port, &srv_host, &srv_port);
835  remmina_public_get_server_port(ssh_tunnel_server, 22, &ssh_tunnel_host, &ssh_tunnel_port);
836  g_debug ("server: %s, port: %d", srv_host, srv_port);
837 
838  if (port_plus && srv_port < 100)
839  /* Protocols like VNC supports using instance number :0, :1, etc. as port number. */
840  srv_port += default_port;
841 
842 #ifdef HAVE_LIBSSH
843  gchar *msg;
844  RemminaMessagePanel *mp;
845  RemminaSSHTunnel *tunnel;
846 
847  if (!remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_enabled", FALSE)) {
848  dest = g_strdup_printf("[%s]:%i", srv_host, srv_port);
849  g_free(srv_host);
850  g_free(ssh_tunnel_host);
851  return dest;
852  }
853 
855  if (!tunnel) {
856  g_free(srv_host);
857  g_free(ssh_tunnel_host);
858  remmina_debug ("%s remmina_protocol_widget_init_tunnel failed with error is %s",
860  return NULL;
861  }
862 
863  msg = g_strdup_printf(_("Connecting to \"%s\" via SSH…"), server);
865  g_free(msg);
866 
867  if (remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_loopback", FALSE)) {
868  g_free(srv_host);
869  g_free(ssh_tunnel_host);
870  ssh_tunnel_host = NULL;
871  srv_host = g_strdup("127.0.0.1");
872  }
873 
874  g_debug ("%s: starting tunnel to: %s, port: %d", __func__, ssh_tunnel_host, ssh_tunnel_port);
875  if (!remmina_ssh_tunnel_open(tunnel, srv_host, srv_port, remmina_pref.sshtunnel_port)) {
876  g_free(srv_host);
877  g_free(ssh_tunnel_host);
878  remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error);
879  remmina_ssh_tunnel_free(tunnel);
880  return NULL;
881  }
882  g_free(srv_host);
883  g_free(ssh_tunnel_host);
884 
886 
888  tunnel->destroy_func_callback_data = (gpointer)gp;
889 
890  g_ptr_array_add(gp->priv->ssh_tunnels, tunnel);
891 
892  return g_strdup_printf("127.0.0.1:%i", remmina_pref.sshtunnel_port);
893 
894 #else
895 
896  dest = g_strdup_printf("[%s]:%i", srv_host, srv_port);
897  g_free(srv_host);
898  g_free(ssh_tunnel_host);
899  return dest;
900 
901 #endif
902 }
903 
904 #ifdef HAVE_LIBSSH
905 static void cancel_start_reverse_tunnel_cb(void *cbdata, int btn)
906 {
907  printf("Remmina: Cancelling start_reverse_tunnel is not implemented\n");
908 }
909 #endif
910 
911 
913 {
914  TRACE_CALL(__func__);
915 #ifdef HAVE_LIBSSH
916  gchar *msg;
917  RemminaMessagePanel *mp;
918  RemminaSSHTunnel *tunnel;
919 
920  if (!remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_enabled", FALSE))
921  return TRUE;
922 
923  if (!(tunnel = remmina_protocol_widget_init_tunnel(gp)))
924  return FALSE;
925 
926  msg = g_strdup_printf(_("Awaiting incoming SSH connection on port %i…"), remmina_file_get_int(gp->priv->remmina_file, "listenport", 0));
928  g_free(msg);
929 
930  if (!remmina_ssh_tunnel_reverse(tunnel, remmina_file_get_int(gp->priv->remmina_file, "listenport", 0), local_port)) {
931  remmina_ssh_tunnel_free(tunnel);
932  remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error);
933  return FALSE;
934  }
936  g_ptr_array_add(gp->priv->ssh_tunnels, tunnel);
937 #endif
938 
939  return TRUE;
940 }
941 
942 gboolean remmina_protocol_widget_ssh_exec(RemminaProtocolWidget *gp, gboolean wait, const gchar *fmt, ...)
943 {
944  TRACE_CALL(__func__);
945 #ifdef HAVE_LIBSSH
946  RemminaSSHTunnel *tunnel;
947  ssh_channel channel;
948  gint status;
949  gboolean ret = FALSE;
950  gchar *cmd, *ptr;
951  va_list args;
952 
953  if (gp->priv->ssh_tunnels->len < 1)
954  return FALSE;
955 
956  tunnel = (RemminaSSHTunnel*)gp->priv->ssh_tunnels->pdata[0];
957 
958  if ((channel = ssh_channel_new(REMMINA_SSH(tunnel)->session)) == NULL)
959  return FALSE;
960 
961  va_start(args, fmt);
962  cmd = g_strdup_vprintf(fmt, args);
963  va_end(args);
964 
965  if (ssh_channel_open_session(channel) == SSH_OK &&
966  ssh_channel_request_exec(channel, cmd) == SSH_OK) {
967  if (wait) {
968  ssh_channel_send_eof(channel);
969  status = ssh_channel_get_exit_status(channel);
970  ptr = strchr(cmd, ' ');
971  if (ptr) *ptr = '\0';
972  switch (status) {
973  case 0:
974  ret = TRUE;
975  break;
976  case 127:
977  remmina_ssh_set_application_error(REMMINA_SSH(tunnel),
978  _("The \"%s\" command is not available on the SSH server."), cmd);
979  break;
980  default:
981  remmina_ssh_set_application_error(REMMINA_SSH(tunnel),
982  _("Could not run the \"%s\" command on the SSH server (status = %i)."), cmd, status);
983  break;
984  }
985  } else {
986  ret = TRUE;
987  }
988  } else {
989  // TRANSLATORS: %s is a placeholder for an error message
990  remmina_ssh_set_error(REMMINA_SSH(tunnel), _("Could not run command. %s"));
991  }
992  g_free(cmd);
993  if (wait)
994  ssh_channel_close(channel);
995  ssh_channel_free(channel);
996  return ret;
997 
998 #else
999 
1000  return FALSE;
1001 
1002 #endif
1003 }
1004 
1005 #ifdef HAVE_LIBSSH
1007 {
1008  TRACE_CALL(__func__);
1009  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(data);
1010  gchar *server;
1011  gint port;
1012  gboolean ret;
1013 
1014  remmina_public_get_server_port(remmina_file_get_string(gp->priv->remmina_file, "server"), 177, &server, &port);
1015  ret = ((RemminaXPortTunnelInitFunc)gp->priv->init_func)(gp,
1016  tunnel->remotedisplay, (tunnel->bindlocalhost ? "localhost" : server), port);
1017  g_free(server);
1018 
1019  return ret;
1020 }
1021 
1023 {
1024  TRACE_CALL(__func__);
1025  return TRUE;
1026 }
1027 
1029 {
1030  TRACE_CALL(__func__);
1031  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(data);
1032 
1033  if (REMMINA_SSH(tunnel)->error)
1034  remmina_protocol_widget_set_error(gp, "%s", REMMINA_SSH(tunnel)->error);
1035 
1036  IDLE_ADD((GSourceFunc)remmina_protocol_widget_close_connection, gp);
1037  return TRUE;
1038 }
1039 #endif
1040 #ifdef HAVE_LIBSSH
1041 static void cancel_connect_xport_cb(void *cbdata, int btn)
1042 {
1043  printf("Remmina: Cancelling an XPort connection is not implemented\n");
1044 }
1045 #endif
1047 {
1048  TRACE_CALL(__func__);
1049 #ifdef HAVE_LIBSSH
1050  gboolean bindlocalhost;
1051  gchar *server;
1052  gchar *msg;
1053  RemminaMessagePanel *mp;
1054  RemminaSSHTunnel* tunnel;
1055 
1056  if (!(tunnel = remmina_protocol_widget_init_tunnel(gp))) return FALSE;
1057 
1058  msg = g_strdup_printf(_("Connecting to %s via SSH…"), remmina_file_get_string(gp->priv->remmina_file, "server"));
1060  g_free(msg);
1061 
1062  gp->priv->init_func = init_func;
1066  tunnel->callback_data = gp;
1067 
1068  remmina_public_get_server_port(remmina_file_get_string(gp->priv->remmina_file, "server"), 0, &server, NULL);
1069  bindlocalhost = (g_strcmp0(REMMINA_SSH(tunnel)->server, server) == 0);
1070  g_free(server);
1071 
1072  if (!remmina_ssh_tunnel_xport(tunnel, bindlocalhost)) {
1073  remmina_protocol_widget_set_error(gp, "Could not open channel, %s",
1074  ssh_get_error(REMMINA_SSH(tunnel)->session));
1075  remmina_ssh_tunnel_free(tunnel);
1076  return FALSE;
1077  }
1078 
1080  g_ptr_array_add(gp->priv->ssh_tunnels, tunnel);
1081 
1082  return TRUE;
1083 
1084 #else
1085  return FALSE;
1086 #endif
1087 }
1088 
1090 {
1091  TRACE_CALL(__func__);
1092 #ifdef HAVE_LIBSSH
1093  RemminaSSHTunnel* tunnel;
1094  if (gp->priv->ssh_tunnels->len < 1)
1095  return;
1096  tunnel = (RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[0];
1097  if (tunnel->localdisplay) g_free(tunnel->localdisplay);
1098  tunnel->localdisplay = g_strdup_printf("unix:%i", display);
1099 #endif
1100 }
1101 
1103 {
1104  TRACE_CALL(__func__);
1105  /* Returns the width of remote desktop as chosen by the user profile */
1106  return gp->priv->profile_remote_width;
1107 }
1108 
1110 {
1111  TRACE_CALL(__func__);
1112  /* Returns the height of remote desktop as chosen by the user profile */
1113  return gp->priv->profile_remote_height;
1114 }
1115 
1116 
1118 {
1119  TRACE_CALL(__func__);
1120  return gp->priv->width;
1121 }
1122 
1124 {
1125  TRACE_CALL(__func__);
1126  gp->priv->width = width;
1127 }
1128 
1130 {
1131  TRACE_CALL(__func__);
1132  return gp->priv->height;
1133 }
1134 
1136 {
1137  TRACE_CALL(__func__);
1138  gp->priv->height = height;
1139 }
1140 
1142 {
1143  TRACE_CALL(__func__);
1144  return gp->priv->scalemode;
1145 }
1146 
1148 {
1149  TRACE_CALL(__func__);
1150  gp->priv->scalemode = scalemode;
1151 }
1152 
1154 {
1155  TRACE_CALL(__func__);
1156  return gp->priv->scaler_expand;
1157 }
1158 
1160 {
1161  TRACE_CALL(__func__);
1162  gp->priv->scaler_expand = expand;
1163  return;
1164 }
1165 
1167 {
1168  TRACE_CALL(__func__);
1169  return gp->priv->has_error;
1170 }
1171 
1173 {
1174  TRACE_CALL(__func__);
1175  return gp->priv->error_message;
1176 }
1177 
1179 {
1180  TRACE_CALL(__func__);
1181  va_list args;
1182 
1183  if (gp->priv->error_message) g_free(gp->priv->error_message);
1184 
1185  if (fmt == NULL) {
1186  gp->priv->has_error = FALSE;
1187  gp->priv->error_message = NULL;
1188  return;
1189  }
1190 
1191  va_start(args, fmt);
1192  gp->priv->error_message = g_strdup_vprintf(fmt, args);
1193  va_end(args);
1194 
1195  gp->priv->has_error = TRUE;
1196 }
1197 
1199 {
1200  TRACE_CALL(__func__);
1201  return gp->priv->closed;
1202 }
1203 
1205 {
1206  TRACE_CALL(__func__);
1207  return gp->priv->remmina_file;
1208 }
1209 
1211  /* Input data */
1213  gchar * title;
1218  enum panel_type dtype;
1221  /* Running status */
1222  pthread_mutex_t pt_mutex;
1223  pthread_cond_t pt_cond;
1224  /* Output/retval */
1226 };
1227 
1228 static void authpanel_mt_cb(void *user_data, int button)
1229 {
1231 
1232  d->rcbutton = button;
1233  if (button == GTK_RESPONSE_OK) {
1234  if (d->dtype == RPWDT_AUTH) {
1239  } else if (d->dtype == RPWDT_AUTHX509) {
1244  }
1245  }
1246 
1247  if (d->called_from_subthread) {
1248  /* Hide and destroy message panel, we can do it now because we are on the main thread */
1250 
1251  /* Awake the locked subthread, when called from subthread */
1252  pthread_mutex_lock(&d->pt_mutex);
1253  pthread_cond_signal(&d->pt_cond);
1254  pthread_mutex_unlock(&d->pt_mutex);
1255  } else {
1256  /* Signal completion, when called from main thread. Message panel will be destroyed by the caller */
1258  }
1259 }
1260 
1261 static gboolean remmina_protocol_widget_dialog_mt_setup(gpointer user_data)
1262 {
1264 
1265  RemminaFile *remminafile = d->gp->priv->remmina_file;
1266  RemminaMessagePanel *mp;
1267  const gchar *s;
1268 
1270 
1271  if (d->dtype == RPWDT_AUTH) {
1279  } else if (d->dtype == RPWDT_QUESTIONYESNO) {
1281  } else if (d->dtype == RPWDT_AUTHX509) {
1283  if ((s = remmina_file_get_string(remminafile, "cacert")) != NULL)
1285  if ((s = remmina_file_get_string(remminafile, "cacrl")) != NULL)
1287  if ((s = remmina_file_get_string(remminafile, "clientcert")) != NULL)
1289  if ((s = remmina_file_get_string(remminafile, "clientkey")) != NULL)
1291  }
1292 
1293  d->gp->priv->auth_message_panel = mp;
1294  rco_show_message_panel(d->gp->cnnobj, mp);
1295 
1296  return FALSE;
1297 }
1298 
1299 typedef struct {
1300  RemminaMessagePanel * mp;
1301  GMainLoop * loop;
1302  gint response;
1303  gboolean destroyed;
1304 } MpRunInfo;
1305 
1306 static void shutdown_loop(MpRunInfo *mpri)
1307 {
1308  if (g_main_loop_is_running(mpri->loop))
1309  g_main_loop_quit(mpri->loop);
1310 }
1311 
1312 static void run_response_handler(RemminaMessagePanel *mp, gint response_id, gpointer data)
1313 {
1314  MpRunInfo *mpri = (MpRunInfo *)data;
1315 
1316  mpri->response = response_id;
1317  shutdown_loop(mpri);
1318 }
1319 
1320 static void run_unmap_handler(RemminaMessagePanel *mp, gpointer data)
1321 {
1322  MpRunInfo *mpri = (MpRunInfo *)data;
1323 
1324  mpri->response = GTK_RESPONSE_CANCEL;
1325  shutdown_loop(mpri);
1326 }
1327 
1328 static void run_destroy_handler(RemminaMessagePanel *mp, gpointer data)
1329 {
1330  MpRunInfo *mpri = (MpRunInfo *)data;
1331 
1332  mpri->destroyed = TRUE;
1333  mpri->response = GTK_RESPONSE_CANCEL;
1334  shutdown_loop(mpri);
1335 }
1336 
1338  const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain,
1339  const gchar *strpasswordlabel)
1340 {
1341  TRACE_CALL(__func__);
1342 
1344  int rcbutton;
1345 
1346  d->gp = gp;
1347  d->pflags = pflags;
1348  d->dtype = dtype;
1349  d->title = g_strdup(title);
1350  d->strpasswordlabel = g_strdup(strpasswordlabel);
1351  d->default_username = g_strdup(default_username);
1352  d->default_password = g_strdup(default_password);
1353  d->default_domain = g_strdup(default_domain);
1354  d->called_from_subthread = FALSE;
1355 
1357  /* Run the MessagePanel in main thread, in a very similar way of gtk_dialog_run() */
1358  MpRunInfo mpri = { NULL, NULL, GTK_RESPONSE_CANCEL, FALSE };
1359 
1360  gulong unmap_handler;
1361  gulong destroy_handler;
1362  gulong response_handler;
1363 
1365 
1366  mpri.mp = d->gp->priv->auth_message_panel;
1367 
1368  if (!gtk_widget_get_visible(GTK_WIDGET(mpri.mp)))
1369  gtk_widget_show(GTK_WIDGET(mpri.mp));
1370  response_handler = g_signal_connect(mpri.mp, "response", G_CALLBACK(run_response_handler), &mpri);
1371  unmap_handler = g_signal_connect(mpri.mp, "unmap", G_CALLBACK(run_unmap_handler), &mpri);
1372  destroy_handler = g_signal_connect(mpri.mp, "destroy", G_CALLBACK(run_destroy_handler), &mpri);
1373 
1374  g_object_ref(mpri.mp);
1375 
1376  mpri.loop = g_main_loop_new(NULL, FALSE);
1377  g_main_loop_run(mpri.loop);
1378  g_main_loop_unref(mpri.loop);
1379 
1380  if (!mpri.destroyed) {
1381  g_signal_handler_disconnect(mpri.mp, response_handler);
1382  g_signal_handler_disconnect(mpri.mp, destroy_handler);
1383  g_signal_handler_disconnect(mpri.mp, unmap_handler);
1384  }
1385  g_object_unref(mpri.mp);
1386 
1388 
1389  rcbutton = mpri.response;
1390  } else {
1391  d->called_from_subthread = TRUE;
1392  // pthread_cleanup_push(ptcleanup, (void*)d);
1393  pthread_cond_init(&d->pt_cond, NULL);
1394  pthread_mutex_init(&d->pt_mutex, NULL);
1396  pthread_mutex_lock(&d->pt_mutex);
1397  pthread_cond_wait(&d->pt_cond, &d->pt_mutex);
1398  // pthread_cleanup_pop(0);
1399  pthread_mutex_destroy(&d->pt_mutex);
1400  pthread_cond_destroy(&d->pt_cond);
1401 
1402  rcbutton = d->rcbutton;
1403  }
1404 
1405  g_free(d->title);
1406  g_free(d->strpasswordlabel);
1407  g_free(d->default_username);
1408  g_free(d->default_password);
1409  g_free(d->default_domain);
1410  g_free(d);
1411  return rcbutton;
1412 }
1413 
1415 {
1416  return remmina_protocol_widget_dialog(RPWDT_QUESTIONYESNO, gp, 0, msg, NULL, NULL, NULL, NULL);
1417 }
1418 
1420  const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
1421 {
1422  TRACE_CALL(__func__);
1423  return remmina_protocol_widget_dialog(RPWDT_AUTH, gp, pflags, title, default_username,
1424  default_password, default_domain, password_prompt == NULL ? _("Password") : password_prompt);
1425 }
1426 
1427 gint remmina_protocol_widget_panel_authuserpwd_ssh_tunnel(RemminaProtocolWidget *gp, gboolean want_domain, gboolean allow_password_saving)
1428 {
1429  TRACE_CALL(__func__);
1430  unsigned pflags;
1431  RemminaFile *remminafile = gp->priv->remmina_file;
1432  const gchar *username, *password;
1433 
1435  if (remmina_file_get_filename(remminafile) != NULL &&
1436  !remminafile->prevent_saving && allow_password_saving)
1438 
1439  username = remmina_file_get_string(remminafile, "ssh_tunnel_username");
1440  password = remmina_file_get_string(remminafile, "ssh_tunnel_password");
1441 
1442  return remmina_protocol_widget_dialog(RPWDT_AUTH, gp, pflags, _("Type in SSH username and password."), username,
1443  password, NULL, _("Password"));
1444 }
1445 
1446 /*
1447  * gint remmina_protocol_widget_panel_authpwd(RemminaProtocolWidget* gp, RemminaAuthpwdType authpwd_type, gboolean allow_password_saving)
1448  * {
1449  * TRACE_CALL(__func__);
1450  * unsigned pflags;
1451  * RemminaFile* remminafile = gp->priv->remmina_file;
1452  * char *password_prompt;
1453  * int rc;
1454  *
1455  * pflags = 0;
1456  * if (remmina_file_get_filename(remminafile) != NULL &&
1457  * !remminafile->prevent_saving && allow_password_saving)
1458  * pflags |= REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD;
1459  *
1460  * switch (authpwd_type) {
1461  * case REMMINA_AUTHPWD_TYPE_PROTOCOL:
1462  * password_prompt = g_strdup_printf(_("%s password"), remmina_file_get_string(remminafile, "protocol"));
1463  * break;
1464  * case REMMINA_AUTHPWD_TYPE_SSH_PWD:
1465  * password_prompt = g_strdup(_("SSH password"));
1466  * break;
1467  * case REMMINA_AUTHPWD_TYPE_SSH_PRIVKEY:
1468  * password_prompt = g_strdup(_("SSH private key passphrase"));
1469  * break;
1470  * default:
1471  * password_prompt = g_strdup(_("Password"));
1472  * break;
1473  * }
1474  *
1475  * rc = remmina_protocol_widget_dialog(RPWDT_AUTH, gp, pflags, password_prompt);
1476  * g_free(password_prompt);
1477  * return rc;
1478  *
1479  * }
1480  */
1482 {
1483  TRACE_CALL(__func__);
1484 
1485  return remmina_protocol_widget_dialog(RPWDT_AUTHX509, gp, 0, NULL, NULL, NULL, NULL, NULL);
1486 }
1487 
1488 
1489 gint remmina_protocol_widget_panel_new_certificate(RemminaProtocolWidget *gp, const gchar *subject, const gchar *issuer, const gchar *fingerprint)
1490 {
1491  TRACE_CALL(__func__);
1492  gchar *s;
1493  int rc;
1494 
1495  // For markup see https://developer.gnome.org/pygtk/stable/pango-markup-language.html
1496  s = g_strdup_printf(
1497  "<big>%s</big>\n\n%s %s\n%s %s\n%s %s\n\n<big>%s</big>",
1498  _("Certificate details:"),
1499  _("Subject:"), subject,
1500  _("Issuer:"), issuer,
1501  _("Fingerprint:"), fingerprint,
1502  _("Accept certificate?"));
1503  rc = remmina_protocol_widget_dialog(RPWDT_QUESTIONYESNO, gp, 0, s, NULL, NULL, NULL, NULL);
1504  g_free(s);
1505 
1506  /* For compatibility with plugin API: the plugin expects GTK_RESPONSE_OK when user confirms new cert */
1507  return rc == GTK_RESPONSE_YES ? GTK_RESPONSE_OK : rc;
1508 }
1509 
1510 gint remmina_protocol_widget_panel_changed_certificate(RemminaProtocolWidget *gp, const gchar *subject, const gchar *issuer, const gchar *new_fingerprint, const gchar *old_fingerprint)
1511 {
1512  TRACE_CALL(__func__);
1513  gchar *s;
1514  int rc;
1515 
1516  // For markup see https://developer.gnome.org/pygtk/stable/pango-markup-language.html
1517  s = g_strdup_printf(
1518  "<big>%s</big>\n\n%s %s\n%s %s\n%s %s\n%s %s\n\n<big>%s</big>",
1519  _("The certificate changed! Details:"),
1520  _("Subject:"), subject,
1521  _("Issuer:"), issuer,
1522  _("Old fingerprint:"), old_fingerprint,
1523  _("New fingerprint:"), new_fingerprint,
1524  _("Accept changed certificate?"));
1525  rc = remmina_protocol_widget_dialog(RPWDT_QUESTIONYESNO, gp, 0, s, NULL, NULL, NULL, NULL);
1526  g_free(s);
1527 
1528  /* For compatibility with plugin API: The plugin expects GTK_RESPONSE_OK when user confirms new cert */
1529  return rc == GTK_RESPONSE_YES ? GTK_RESPONSE_OK : rc;
1530 }
1531 
1533 {
1534  TRACE_CALL(__func__);
1535  return g_strdup(gp->priv->username);
1536 }
1537 
1539 {
1540  TRACE_CALL(__func__);
1541  return g_strdup(gp->priv->password);
1542 }
1543 
1545 {
1546  TRACE_CALL(__func__);
1547  return g_strdup(gp->priv->domain);
1548 }
1549 
1551 {
1552  TRACE_CALL(__func__);
1553  return gp->priv->save_password;
1554 }
1555 
1557 {
1558  TRACE_CALL(__func__);
1559  gchar *s;
1560 
1561  s = gp->priv->cacert;
1562  return s && s[0] ? g_strdup(s) : NULL;
1563 }
1564 
1566 {
1567  TRACE_CALL(__func__);
1568  gchar *s;
1569 
1570  s = gp->priv->cacrl;
1571  return s && s[0] ? g_strdup(s) : NULL;
1572 }
1573 
1575 {
1576  TRACE_CALL(__func__);
1577  gchar *s;
1578 
1579  s = gp->priv->clientcert;
1580  return s && s[0] ? g_strdup(s) : NULL;
1581 }
1582 
1584 {
1585  TRACE_CALL(__func__);
1586  gchar *s;
1587 
1588  s = gp->priv->clientkey;
1589  return s && s[0] ? g_strdup(s) : NULL;
1590 }
1591 
1593 {
1594  TRACE_CALL(__func__);
1595 
1596  RemminaFile *remminafile = gp->priv->remmina_file;
1597  gchar *s;
1598  gboolean save = FALSE;
1599 
1601  /* Allow the execution of this function from a non main thread */
1602  RemminaMTExecData *d;
1603  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
1604  d->func = FUNC_INIT_SAVE_CRED;
1605  d->p.init_save_creds.gp = gp;
1607  g_free(d);
1608  return;
1609  }
1610 
1611  /* Save username and certificates if any; save the password if it’s requested to do so */
1612  s = gp->priv->username;
1613  if (s && s[0]) {
1614  remmina_file_set_string(remminafile, "username", s);
1615  save = TRUE;
1616  }
1617  s = gp->priv->cacert;
1618  if (s && s[0]) {
1619  remmina_file_set_string(remminafile, "cacert", s);
1620  save = TRUE;
1621  }
1622  s = gp->priv->cacrl;
1623  if (s && s[0]) {
1624  remmina_file_set_string(remminafile, "cacrl", s);
1625  save = TRUE;
1626  }
1627  s = gp->priv->clientcert;
1628  if (s && s[0]) {
1629  remmina_file_set_string(remminafile, "clientcert", s);
1630  save = TRUE;
1631  }
1632  s = gp->priv->clientkey;
1633  if (s && s[0]) {
1634  remmina_file_set_string(remminafile, "clientkey", s);
1635  save = TRUE;
1636  }
1637  if (gp->priv->save_password) {
1638  remmina_file_set_string(remminafile, "password", gp->priv->password);
1639  save = TRUE;
1640  }
1641  if (save)
1642  remmina_file_save(remminafile);
1643 }
1644 
1645 
1647 {
1648  TRACE_CALL(__func__);
1649  RemminaMessagePanel *mp;
1650  gchar *s;
1651 
1653  /* Allow the execution of this function from a non main thread */
1654  RemminaMTExecData *d;
1655  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
1656  d->func = FUNC_PROTOCOLWIDGET_PANELSHOWLISTEN;
1658  d->p.protocolwidget_panelshowlisten.port = port;
1660  g_free(d);
1661  return;
1662  }
1663 
1665  s = g_strdup_printf(
1666  _("Listening on port %i for an incoming %s connection…"), port,
1667  remmina_file_get_string(gp->priv->remmina_file, "protocol"));
1668  remmina_message_panel_setup_progress(mp, s, NULL, NULL);
1669  g_free(s);
1670  gp->priv->listen_message_panel = mp;
1671  rco_show_message_panel(gp->cnnobj, mp);
1672 }
1673 
1675 {
1676  TRACE_CALL(__func__);
1677  RemminaMessagePanel *mp;
1678 
1680  /* Allow the execution of this function from a non main thread */
1681  RemminaMTExecData *d;
1682  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
1683  d->func = FUNC_PROTOCOLWIDGET_MPSHOWRETRY;
1686  g_free(d);
1687  return;
1688  }
1689 
1691  remmina_message_panel_setup_progress(mp, _("Could not authenticate, attempting reconnection…"), NULL, NULL);
1692  rco_show_message_panel(gp->cnnobj, mp);
1693 }
1694 
1696 {
1697  TRACE_CALL(__func__);
1698  printf("Remmina: The %s function is not implemented, and is left here only for plugin API compatibility.\n", __func__);
1699 }
1700 
1702 {
1703  TRACE_CALL(__func__);
1704  printf("Remmina: The %s function is not implemented, and is left here only for plugin API compatibility.\n", __func__);
1705 }
1706 
1708 {
1709  TRACE_CALL(__func__);
1710  gp->priv->chat_window = NULL;
1711 }
1712 
1714  void (*on_send)(RemminaProtocolWidget *gp, const gchar *text), void (*on_destroy)(RemminaProtocolWidget *gp))
1715 {
1716  TRACE_CALL(__func__);
1717  if (gp->priv->chat_window) {
1718  gtk_window_present(GTK_WINDOW(gp->priv->chat_window));
1719  } else {
1720  gp->priv->chat_window = remmina_chat_window_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(gp))), name);
1721  g_signal_connect_swapped(G_OBJECT(gp->priv->chat_window), "send", G_CALLBACK(on_send), gp);
1722  g_signal_connect_swapped(G_OBJECT(gp->priv->chat_window), "destroy",
1723  G_CALLBACK(remmina_protocol_widget_chat_on_destroy), gp);
1724  g_signal_connect_swapped(G_OBJECT(gp->priv->chat_window), "destroy", G_CALLBACK(on_destroy), gp);
1725  gtk_widget_show(gp->priv->chat_window);
1726  }
1727 }
1728 
1730 {
1731  TRACE_CALL(__func__);
1732  if (gp->priv->chat_window)
1733  gtk_widget_destroy(gp->priv->chat_window);
1734 }
1735 
1737 {
1738  TRACE_CALL(__func__);
1739  /* This function can be called from a non main thread */
1740 
1741  if (gp->priv->chat_window) {
1743  /* Allow the execution of this function from a non main thread */
1744  RemminaMTExecData *d;
1745  d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
1746  d->func = FUNC_CHAT_RECEIVE;
1747  d->p.chat_receive.gp = gp;
1748  d->p.chat_receive.text = text;
1750  g_free(d);
1751  return;
1752  }
1753  remmina_chat_window_receive(REMMINA_CHAT_WINDOW(gp->priv->chat_window), _("Server"), text);
1754  gtk_window_present(GTK_WINDOW(gp->priv->chat_window));
1755  }
1756 }
1757 
1759 {
1761 
1762  gp->priv->remmina_file = remminafile;
1763  gp->cnnobj = cnnobj;
1764 
1765  /* Locate the protocol plugin */
1767  remmina_file_get_string(remminafile, "protocol"));
1768 
1769  if (!plugin || !plugin->init || !plugin->open_connection) {
1770  remmina_protocol_widget_set_error(gp, _("Install the %s protocol plugin first."),
1771  remmina_file_get_string(remminafile, "protocol"));
1772  gp->priv->plugin = NULL;
1773  return;
1774  }
1775  gp->priv->plugin = plugin;
1776 
1777  gp->priv->scalemode = remmina_file_get_int(gp->priv->remmina_file, "scale", FALSE);
1778  gp->priv->scaler_expand = remmina_file_get_int(gp->priv->remmina_file, "scaler_expand", FALSE);
1779 }
1780 
1782 {
1783  return GTK_WIDGET(g_object_new(REMMINA_TYPE_PROTOCOL_WIDGET, NULL));
1784 }
1785 
1786 /* Send one or more keystrokes to a specific widget by firing key-press and
1787  * key-release events.
1788  * GdkEventType action can be GDK_KEY_PRESS or GDK_KEY_RELEASE or both to
1789  * press the keys and release them in reversed order. */
1790 void remmina_protocol_widget_send_keys_signals(GtkWidget *widget, const guint *keyvals, int keyvals_length, GdkEventType action)
1791 {
1792  TRACE_CALL(__func__);
1793  int i;
1794  GdkEventKey event;
1795  gboolean result;
1796  GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
1797 
1798  event.window = gtk_widget_get_window(widget);
1799  event.send_event = TRUE;
1800  event.time = GDK_CURRENT_TIME;
1801  event.state = 0;
1802  event.length = 0;
1803  event.string = "";
1804  event.group = 0;
1805 
1806  if (action & GDK_KEY_PRESS) {
1807  /* Press the requested buttons */
1808  event.type = GDK_KEY_PRESS;
1809  for (i = 0; i < keyvals_length; i++) {
1810  event.keyval = keyvals[i];
1811  event.hardware_keycode = remmina_public_get_keycode_for_keyval(keymap, event.keyval);
1812  event.is_modifier = (int)remmina_public_get_modifier_for_keycode(keymap, event.hardware_keycode);
1813  g_signal_emit_by_name(G_OBJECT(widget), "key-press-event", &event, &result);
1814  }
1815  }
1816 
1817  if (action & GDK_KEY_RELEASE) {
1818  /* Release the requested buttons in reverse order */
1819  event.type = GDK_KEY_RELEASE;
1820  for (i = (keyvals_length - 1); i >= 0; i--) {
1821  event.keyval = keyvals[i];
1822  event.hardware_keycode = remmina_public_get_keycode_for_keyval(keymap, event.keyval);
1823  event.is_modifier = (int)remmina_public_get_modifier_for_keycode(keymap, event.hardware_keycode);
1824  g_signal_emit_by_name(G_OBJECT(widget), "key-release-event", &event, &result);
1825  }
1826  }
1827 }
1828 
1830 {
1831  TRACE_CALL(__func__);
1832  GdkRectangle rect;
1833  gint w, h;
1834  gint wfile, hfile;
1837 
1838  rco_get_monitor_geometry(gp->cnnobj, &rect);
1839 
1840  /* Integrity check: check that we have a cnnwin visible and get t */
1841 
1842  res_mode = remmina_file_get_int(gp->priv->remmina_file, "resolution_mode", RES_INVALID);
1844  wfile = remmina_file_get_int(gp->priv->remmina_file, "resolution_width", -1);
1845  hfile = remmina_file_get_int(gp->priv->remmina_file, "resolution_height", -1);
1846 
1847  /* If resolution_mode is non-existent (-1), then we try to calculate it
1848  * as we did before having resolution_mode */
1849  if (res_mode == RES_INVALID) {
1850  if (wfile <= 0 || hfile <= 0)
1851  res_mode = RES_USE_INITIAL_WINDOW_SIZE;
1852  else
1853  res_mode = RES_USE_CUSTOM;
1854  }
1855 
1857  /* Use internal window size as remote desktop size */
1858  GtkAllocation al;
1859  gtk_widget_get_allocation(GTK_WIDGET(gp), &al);
1860  w = al.width;
1861  h = al.height;
1862  if (w < 10) {
1863  printf("Remmina warning: %s RemminaProtocolWidget w=%d h=%d are too small, adjusting to 640x480\n", __func__, w, h);
1864  w = 640;
1865  h = 480;
1866  }
1867  /* Due to approximations while GTK calculates scaling, (w x h) may exceed our monitor geometry
1868  * Adjust to fit. */
1869  if (w > rect.width)
1870  w = rect.width;
1871  if (h > rect.height)
1872  h = rect.height;
1873  } else if (res_mode == RES_USE_CLIENT) {
1874  w = rect.width;
1875  h = rect.height;
1876  } else {
1877  w = wfile;
1878  h = hfile;
1879  }
1880  gp->priv->profile_remote_width = w;
1881  gp->priv->profile_remote_height = h;
1882 }
gboolean remmina_protocol_widget_get_expand(RemminaProtocolWidget *gp)
static gboolean desktop_resize(gpointer data)
void remmina_protocol_widget_panel_show(RemminaProtocolWidget *gp)
const gchar * remmina_protocol_widget_get_error_message(RemminaProtocolWidget *gp)
void remmina_protocol_widget_set_error(RemminaProtocolWidget *gp, const gchar *fmt,...)
enum remmina_ssh_auth_result remmina_ssh_auth_gui(RemminaSSH *ssh, RemminaProtocolWidget *gp, RemminaFile *remminafile)
gboolean remmina_protocol_widget_query_feature_by_type(RemminaProtocolWidget *gp, RemminaProtocolFeatureType type)
const RemminaProtocolFeature * remmina_protocol_widget_get_features(RemminaProtocolWidget *gp)
struct _RemminaProtocolWidgetSignalData RemminaProtocolWidgetSignalData
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:449
void remmina_protocol_widget_panel_hide(RemminaProtocolWidget *gp)
static gboolean unlock_dynres(gpointer data)
static gboolean remmina_protocol_widget_on_key_press(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
gchar * remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Start an SSH tunnel if possible and return the host:port string.
RemminaSSHTunnelCallback destroy_func
Definition: remmina_ssh.h:177
gboolean remmina_protocol_widget_query_feature_by_ref(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
gboolean remmina_ssh_tunnel_reverse(RemminaSSHTunnel *tunnel, gint port, gint local_port)
void remmina_message_panel_setup_auth(RemminaMessagePanel *mp, RemminaMessagePanelCallback response_callback, gpointer response_callback_data, const gchar *title, const gchar *password_prompt, unsigned flags)
void(* call_feature)(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: plugin.h:79
static void remmina_protocol_widget_destroy(RemminaProtocolWidget *gp, gpointer data)
static gboolean remmina_protocol_widget_dialog_mt_setup(gpointer user_data)
void remmina_message_panel_setup_question(RemminaMessagePanel *mp, const gchar *message, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
void remmina_protocol_widget_chat_receive(RemminaProtocolWidget *gp, const gchar *text)
gchar * remmina_message_panel_field_get_filename(RemminaMessagePanel *mp, int entryid)
gint remmina_protocol_widget_get_width(RemminaProtocolWidget *gp)
gint remmina_protocol_widget_get_profile_remote_width(RemminaProtocolWidget *gp)
gint remmina_protocol_widget_panel_auth(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
void remmina_protocol_widget_save_cred(RemminaProtocolWidget *gp)
const gchar * remmina_file_get_filename(RemminaFile *remminafile)
Definition: remmina_file.c:184
static gboolean remmina_protocol_widget_xport_tunnel_connect_callback(RemminaSSHTunnel *tunnel, gpointer data)
gboolean remmina_protocol_widget_get_savepassword(RemminaProtocolWidget *gp)
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
gboolean remmina_protocol_widget_has_error(RemminaProtocolWidget *gp)
struct remmina_masterthread_exec_data::@11::@18 protocolwidget_emit_signal
void remmina_protocol_widget_register_hostkey(RemminaProtocolWidget *gp, GtkWidget *widget)
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4107
gboolean remmina_protocol_widget_start_reverse_tunnel(RemminaProtocolWidget *gp, gint local_port)
gchar * remmina_protocol_widget_get_domain(RemminaProtocolWidget *gp)
static gboolean remmina_protocol_widget_on_key_release(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
static void shutdown_loop(MpRunInfo *mpri)
gpointer destroy_func_callback_data
Definition: remmina_ssh.h:178
RemminaFile * remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
Definition: remmina_file.c:711
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:3898
void remmina_message_panel_setup_auth_x509(RemminaMessagePanel *mp, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
static int remmina_protocol_widget_dialog(enum panel_type dtype, RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *strpasswordlabel)
static void run_response_handler(RemminaMessagePanel *mp, gint response_id, gpointer data)
static gboolean remmina_protocol_widget_xport_tunnel_init_callback(RemminaSSHTunnel *tunnel, gpointer data)
void(* init)(RemminaProtocolWidget *gp)
Definition: plugin.h:75
static void cancel_init_tunnel_cb(void *cbdata, int btn)
void remmina_protocol_widget_setup(RemminaProtocolWidget *gp, RemminaFile *remminafile, RemminaConnectionObject *cnnobj)
RemminaMessagePanel * listen_message_panel
static RemminaSSHTunnel * remmina_protocol_widget_init_tunnel(RemminaProtocolWidget *gp)
const RemminaProtocolFeature * features
Definition: plugin.h:73
enum remmina_masterthread_exec_data::@10 func
struct remmina_masterthread_exec_data::@11::@20 protocolwidget_mpdestroy
gchar * remmina_protocol_widget_get_cacert(RemminaProtocolWidget *gp)
void remmina_protocol_widget_call_feature_by_type(RemminaProtocolWidget *gp, RemminaProtocolFeatureType type, gint id)
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4060
void remmina_protocol_widget_set_hostkey_func(RemminaProtocolWidget *gp, RemminaHostkeyFunc func)
gboolean bindlocalhost
Definition: remmina_ssh.h:169
gint remmina_protocol_widget_get_height(RemminaProtocolWidget *gp)
RemminaScaleMode remmina_protocol_widget_get_current_scale_mode(RemminaProtocolWidget *gp)
void remmina_protocol_widget_update_align(RemminaProtocolWidget *gp)
void remmina_protocol_widget_set_current_scale_mode(RemminaProtocolWidget *gp, RemminaScaleMode scalemode)
static gboolean conn_opened(gpointer data)
gint remmina_protocol_widget_panel_authx509(RemminaProtocolWidget *gp)
struct remmina_masterthread_exec_data::@11::@14 chat_receive
static void cancel_open_connection_cb(void *cbdata, int btn)
gboolean(* open_connection)(RemminaProtocolWidget *gp)
Definition: plugin.h:76
void remmina_message_panel_field_set_string(RemminaMessagePanel *mp, int entryid, const gchar *text)
RemminaProtocolPlugin * plugin
static void run_unmap_handler(RemminaMessagePanel *mp, gpointer data)
static gboolean conn_closed(gpointer data)
void remmina_protocol_widget_set_display(RemminaProtocolWidget *gp, gint display)
RemminaProtocolWidgetResolutionMode
Definition: types.h:123
void remmina_protocol_widget_set_width(RemminaProtocolWidget *gp, gint width)
RemminaSSHTunnelCallback disconnect_func
Definition: remmina_ssh.h:174
void remmina_protocol_widget_open_connection(RemminaProtocolWidget *gp)
RemminaProtocolFeature * features
void remmina_debug(const gchar *fmt,...)
Print a string in the Remmina Debug Windows and in the terminal.
Definition: remmina_log.c:194
gchar * remmina_public_str_replace_in_place(gchar *string, const gchar *search, const gchar *replacement)
GtkDialog * remmina_ext_exec_new(RemminaFile *remminafile, const char *remmina_ext_exec_type)
void remmina_protocol_widget_send_keystrokes(RemminaProtocolWidget *gp, GtkMenuItem *widget)
Send to the plugin some keystrokes.
void remmina_protocol_widget_panel_show_listen(RemminaProtocolWidget *gp, gint port)
static void cancel_start_reverse_tunnel_cb(void *cbdata, int btn)
RemminaMessagePanelFlags
Definition: types.h:131
void remmina_message_panel_field_set_filename(RemminaMessagePanel *mp, int entryid, const gchar *filename)
gboolean remmina_masterthread_exec_is_main_thread()
void remmina_protocol_widget_signal_connection_closed(RemminaProtocolWidget *gp)
GtkWidget * remmina_protocol_widget_new(void)
static guint remmina_protocol_widget_signals[LAST_SIGNAL]
union remmina_masterthread_exec_data::@11 p
void remmina_protocol_widget_desktop_resize(RemminaProtocolWidget *gp)
static SoupSession * session
Definition: rmnews.c:77
RemminaMessagePanel * auth_message_panel
RemminaProtocolFeatureType type
Definition: types.h:57
gint remmina_protocol_widget_panel_question_yesno(RemminaProtocolWidget *gp, const char *msg)
void(* send_keystrokes)(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: plugin.h:80
gint remmina_protocol_widget_panel_changed_certificate(RemminaProtocolWidget *gp, const gchar *subject, const gchar *issuer, const gchar *new_fingerprint, const gchar *old_fingerprint)
gchar * remmina_protocol_widget_get_clientcert(RemminaProtocolWidget *gp)
void remmina_protocol_widget_mpdestroy(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
void remmina_ssh_set_error(RemminaSSH *ssh, const gchar *fmt)
Definition: remmina_ssh.c:145
RemminaMessagePanel * mp
static void cancel_connect_xport_cb(void *cbdata, int btn)
gboolean remmina_ssh_tunnel_xport(RemminaSSHTunnel *tunnel, gboolean bindlocalhost)
void remmina_protocol_widget_unlock_dynres(RemminaProtocolWidget *gp)
RemminaSSHTunnel * remmina_ssh_tunnel_new_from_file(RemminaFile *remminafile)
RemminaProtocolWidgetPriv * priv
static void remmina_protocol_widget_init(RemminaProtocolWidget *gp)
static void authpanel_mt_cb(void *user_data, int button)
RemminaMessagePanel * remmina_protocol_widget_mpprogress(RemminaConnectionObject *cnnobj, const gchar *msg, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
RemminaSSHTunnelCallback init_func
Definition: remmina_ssh.h:172
gboolean(* RemminaHostkeyFunc)(RemminaProtocolWidget *gp, guint keyval, gboolean release)
G_DEFINE_TYPE(RemminaProtocolWidget, remmina_protocol_widget, GTK_TYPE_EVENT_BOX)
void remmina_protocol_widget_panel_show_retry(RemminaProtocolWidget *gp)
void remmina_message_panel_field_set_switch(RemminaMessagePanel *mp, int entryid, gboolean state)
gpointer callback_data
Definition: remmina_ssh.h:175
RemminaConnectionObject * cnnobj
void remmina_protocol_widget_update_remote_resolution(RemminaProtocolWidget *gp)
struct remmina_masterthread_exec_data::@11::@13 init_save_creds
struct remmina_masterthread_exec_data::@11::@21 protocolwidget_mpshowretry
gint remmina_protocol_widget_panel_new_certificate(RemminaProtocolWidget *gp, const gchar *subject, const gchar *issuer, const gchar *fingerprint)
void remmina_protocol_widget_close_connection(RemminaProtocolWidget *gp)
gboolean remmina_protocol_widget_plugin_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
void remmina_ssh_tunnel_cancel_accept(RemminaSSHTunnel *tunnel)
void remmina_protocol_widget_grab_focus(RemminaProtocolWidget *gp)
RemminaMessagePanel * connect_message_panel
gboolean remmina_protocol_widget_ssh_exec(RemminaProtocolWidget *gp, gboolean wait, const gchar *fmt,...)
gchar * remmina_protocol_widget_get_cacrl(RemminaProtocolWidget *gp)
void remmina_ssh_tunnel_free(RemminaSSHTunnel *tunnel)
static void remmina_protocol_widget_close_all_tunnels(RemminaProtocolWidget *gp)
static void remmina_protocol_widget_chat_on_destroy(RemminaProtocolWidget *gp)
static gboolean remmina_protocol_widget_xport_tunnel_disconnect_callback(RemminaSSHTunnel *tunnel, gpointer data)
RemminaProtocolFeatureType
Definition: types.h:43
void remmina_protocol_widget_set_height(RemminaProtocolWidget *gp, gint height)
gboolean(* close_connection)(RemminaProtocolWidget *gp)
Definition: plugin.h:77
void remmina_masterthread_exec_and_wait(RemminaMTExecData *d)
void(* RemminaMessagePanelCallback)(void *user_data, int button)
void remmina_protocol_widget_emit_signal(RemminaProtocolWidget *gp, const gchar *signal_name)
gchar * remmina_message_panel_field_get_string(RemminaMessagePanel *mp, int entryid)
struct remmina_masterthread_exec_data::@11::@19 protocolwidget_mpprogress
gboolean remmina_protocol_widget_is_closed(RemminaProtocolWidget *gp)
gchar * remmina_protocol_widget_get_clientkey(RemminaProtocolWidget *gp)
RemminaPref remmina_pref
Definition: rcw.c:73
static void run_destroy_handler(RemminaMessagePanel *mp, gpointer data)
void remmina_message_panel_setup_progress(RemminaMessagePanel *mp, const gchar *message, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
RemminaScaleMode
Definition: types.h:117
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:524
gboolean remmina_protocol_widget_start_xport_tunnel(RemminaProtocolWidget *gp, RemminaXPortTunnelInitFunc init_func)
void remmina_protocol_widget_call_feature_by_ref(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
gboolean(* query_feature)(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: plugin.h:78
static gboolean remmina_protocol_widget_tunnel_destroy(RemminaSSHTunnel *tunnel, gpointer data)
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
void remmina_protocol_widget_signal_connection_opened(RemminaProtocolWidget *gp)
gint remmina_protocol_widget_get_profile_remote_height(RemminaProtocolWidget *gp)
guint16 remmina_public_get_keycode_for_keyval(GdkKeymap *keymap, guint keyval)
gpointer RemminaTunnelInitFunc
Definition: types.h:75
gchar * remmina_protocol_widget_get_password(RemminaProtocolWidget *gp)
gboolean remmina_public_get_modifier_for_keycode(GdkKeymap *keymap, guint16 keycode)
gint sshtunnel_port
Definition: remmina_pref.h:145
gint remmina_protocol_widget_panel_authuserpwd_ssh_tunnel(RemminaProtocolWidget *gp, gboolean want_domain, gboolean allow_password_saving)
static void cancel_start_direct_tunnel_cb(void *cbdata, int btn)
static gboolean update_align(gpointer data)
gboolean(* get_plugin_screenshot)(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
Definition: plugin.h:81
void remmina_protocol_widget_set_expand(RemminaProtocolWidget *gp, gboolean expand)
gboolean remmina_message_panel_field_get_switch_state(RemminaMessagePanel *mp, int entryid)
void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: remmina_file.c:423
void remmina_file_save(RemminaFile *remminafile)
Definition: remmina_file.c:561
static void remmina_protocol_widget_class_init(RemminaProtocolWidgetClass *klass)
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:960
gboolean remmina_protocol_widget_plugin_receives_keystrokes(RemminaProtocolWidget *gp)
Check if the plugin accepts keystrokes.
void remmina_protocol_widget_send_keys_signals(GtkWidget *widget, const guint *keyvals, int keyvals_length, GdkEventType action)
void remmina_ssh_set_application_error(RemminaSSH *ssh, const gchar *fmt,...)
Definition: remmina_ssh.c:155
void remmina_chat_window_receive(RemminaChatWindow *window, const gchar *name, const gchar *text)
void remmina_protocol_widget_chat_close(RemminaProtocolWidget *gp)
void remmina_public_get_server_port(const gchar *server, gint defaultport, gchar **host, gint *port)
gboolean remmina_ssh_tunnel_open(RemminaSSHTunnel *tunnel, const gchar *host, gint port, gint local_port)
struct remmina_masterthread_exec_data::@11::@22 protocolwidget_panelshowlisten
GtkWidget * remmina_chat_window_new(GtkWindow *parent, const gchar *chat_with)
void remmina_protocol_widget_open_connection_real(gpointer data)
RemminaFile * remmina_protocol_widget_get_file(RemminaProtocolWidget *gp)
gchar * localdisplay
Definition: remmina_ssh.h:170
gboolean(* RemminaXPortTunnelInitFunc)(RemminaProtocolWidget *gp, gint remotedisplay, const gchar *server, gint port)
Definition: types.h:76
void remmina_message_panel_response(RemminaMessagePanel *mp, gint response_id)
void remmina_protocol_widget_chat_open(RemminaProtocolWidget *gp, const gchar *name, void(*on_send)(RemminaProtocolWidget *gp, const gchar *text), void(*on_destroy)(RemminaProtocolWidget *gp))
RemminaMessagePanel * remmina_message_panel_new()
RemminaSSHTunnelCallback connect_func
Definition: remmina_ssh.h:173
gchar * remmina_protocol_widget_get_username(RemminaProtocolWidget *gp)
gboolean remmina_ssh_init_session(RemminaSSH *ssh)