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.
st_plugin.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2017-2020 Antenore Gatta, Giovanni Panozzo
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give
21  * permission to link the code of portions of this program with the
22  * OpenSSL library under certain conditions as described in each
23  * individual source file, and distribute linked combinations
24  * including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL. * If you modify
27  * file(s) with this exception, you may extend this exception to your
28  * version of the file(s), but you are not obligated to do so. * If you
29  * do not wish to do so, delete this exception statement from your
30  * version. * If you delete this exception statement from all source
31  * files in the program, then also delete it here.
32  *
33  */
34 
35 #include "st_plugin_config.h"
36 
37 #include "common/remmina_plugin.h"
38 
39 #include <gdk/gdkkeysyms.h>
40 #include <gtk/gtk.h>
41 #include <gtk/gtkx.h>
42 #include <glib.h>
43 #include <stdlib.h>
44 #include <sys/wait.h>
45 #include <unistd.h>
46 
47 #define REMMINA_PLUGIN_ST_FEATURE_GTKSOCKET 1
48 
49 typedef struct _RemminaPluginData
50 {
51  GtkWidget *socket;
52  gint socket_id;
53  GPid pid;
55 
57 
59 {
60  TRACE_CALL(__func__);
61  return TRUE;
62 }
63 
64 
66 {
67  TRACE_CALL(__func__);
68  RemminaPluginData *gpdata;
69  gpdata = (RemminaPluginData*) g_object_get_data(G_OBJECT(gp), "plugin-data");
70  remmina_plugin_service->debug("[%s] Plugin plug added on socket %d", PLUGIN_NAME, gpdata->socket_id);
71  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
72  return;
73 }
74 
76 {
77  TRACE_CALL(__func__);
78  remmina_plugin_service->debug("[%s] Plugin plug removed", PLUGIN_NAME);
79  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
80 }
81 
83 {
84  TRACE_CALL(__func__);
85  remmina_plugin_service->debug("[%s] Plugin init", PLUGIN_NAME);
86  RemminaPluginData *gpdata;
87 
88  gpdata = g_new0(RemminaPluginData, 1);
89  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
90 
91  gpdata->socket = gtk_socket_new();
92  remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->socket);
93  gtk_widget_show(gpdata->socket);
94  g_signal_connect(G_OBJECT(gpdata->socket), "plug-added", G_CALLBACK(remmina_plugin_st_on_plug_added), gp);
95  g_signal_connect(G_OBJECT(gpdata->socket), "plug-removed", G_CALLBACK(remmina_plugin_st_on_plug_removed), gp);
96  gtk_container_add(GTK_CONTAINER(gp), gpdata->socket);
97 }
98 
100 {
101  TRACE_CALL(__func__);
102  remmina_plugin_service->debug("[%s] Plugin open connection", PLUGIN_NAME);
103 #define ADD_ARGUMENT(name, value) \
104  { \
105  argv[argc] = g_strdup(name); \
106  argv_debug[argc] = g_strdup(name); \
107  argc++; \
108  if (value != NULL) \
109  { \
110  argv[argc] = value; \
111  argv_debug[argc++] = g_strdup(g_strcmp0(name, "-p") != 0 ? value : "XXXXX"); \
112  } \
113  }
114  RemminaPluginData *gpdata;
115  RemminaFile *remminafile;
116  GError *error = NULL;
117  gchar *argv[50]; // Contains all the arguments
118  gchar *argv_debug[50]; // Contains all the arguments
119  gchar *command_line; // The whole command line obtained from argv_debug
120  const gchar *term; // Terminal emulator name from remimna profile.
121  const gchar *wflag = NULL;
122  const gchar *command; // The command to be passed to the terminal (if any)
123  gboolean isterm = FALSE;
124  gint argc;
125  gint i;
126 
127  gpdata = (RemminaPluginData*) g_object_get_data(G_OBJECT(gp), "plugin-data");
128  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
129 
130  if (!remmina_plugin_service->file_get_int(remminafile, "detached", FALSE)) {
131  remmina_plugin_service->protocol_plugin_set_width(gp, 640);
132  remmina_plugin_service->protocol_plugin_set_height(gp, 480);
133  gtk_widget_set_size_request(GTK_WIDGET(gp), 640, 480);
134  gpdata->socket_id = gtk_socket_get_id(GTK_SOCKET(gpdata->socket));
135  }
136 
137  term = remmina_plugin_service->file_get_string(remminafile, "terminal");
138 
139  if (g_strcmp0(term, "st") == 0) {
140  /* on Debian based distros st is packaged as stterm */
141  if (!g_find_program_in_path(term))
142  term = "stterm";
143  wflag = "-w";
144  isterm = TRUE;
145  }else if (g_strcmp0(term, "urxvt") == 0) {
146  wflag = "-embed";
147  isterm = TRUE;
148  }else if (g_strcmp0(term, "xterm") == 0) {
149  wflag = "-xrm 'XTerm*allowSendEvents: true' -into";
150  isterm = TRUE;
151  }else if (g_strcmp0(term, "vim") == 0) {
152  wflag = "-g --socketid";
153  isterm = FALSE;
154  }else if (g_strcmp0(term, "emacs") == 0) {
155  wflag = "--parent-id";
156  isterm = FALSE;
157  }
158  if (!g_find_program_in_path(term)) {
159  remmina_plugin_service->protocol_plugin_set_error(gp, "%s not found", term);
160  return FALSE;
161  }
162 
163  argc = 0;
164  // Main executable name
165  ADD_ARGUMENT(g_strdup_printf("%s", term), NULL);
166  // Embed st-window in another window
167  if (gpdata->socket_id != 0)
168  ADD_ARGUMENT(g_strdup(wflag), g_strdup_printf("%i", gpdata->socket_id));
169  // Add eventually any additional arguments set by the user
170  command = remmina_plugin_service->file_get_string(remminafile, "cmd");
171  if(command && isterm)
172  ADD_ARGUMENT("-e", g_strdup_printf("%s", command));
173  if(command && !isterm)
174  ADD_ARGUMENT("", g_strdup_printf("%s", command));
175  // End of the arguments list
176  ADD_ARGUMENT(NULL, NULL);
177  // Retrieve the whole command line
178  command_line = g_strjoinv(g_strdup(" "), (gchar **)&argv_debug[0]);
179  remmina_plugin_service->debug("[%s] starting %s", PLUGIN_NAME, command_line);
180  // Execute the external process st
181  g_spawn_command_line_async(command_line, &error);
182  g_free(command_line);
183 
184  // Free the arguments list
185  for (i = 0; i < argc; i++)
186  {
187  g_free(argv_debug[i]);
188  g_free(argv[i]);
189  }
190  // Show error message
191  if (error) {
192  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", error->message);
193  g_error_free(error);
194  return FALSE;
195  }
196  // Show attached window socket ID
197  if (!remmina_plugin_service->file_get_int(remminafile, "detached", FALSE)) {
198  remmina_plugin_service->debug("[%s] attached window to socket %d",
199  PLUGIN_NAME, gpdata->socket_id);
200  return TRUE;
201  }
202  else
203  {
204  return FALSE;
205  }
206 }
207 
209 {
210  TRACE_CALL(__func__);
211  remmina_plugin_service->debug("[%s] Plugin close connection", PLUGIN_NAME);
212  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
213  return FALSE;
214 }
215 
216 static gpointer term_list[] =
217 {
218  "st", "Suckless Simple Terminal",
219  "urxvt", "rxvt-unicode",
220  "xterm", "Xterm",
221  "emacs", "GNU Emacs",
222  "vim", "Vim Text Editor",
223  NULL
224 };
225 
226 /* Array of RemminaProtocolSetting for basic settings.
227  * Each item is composed by:
228  * a) RemminaProtocolSettingType for setting type
229  * b) Setting name
230  * c) Setting description
231  * d) Compact disposition
232  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
233  * f) Setting Tooltip
234  */
236 {
237  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "terminal", N_("Terminal Emulator"), FALSE, term_list, NULL },
238  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "cmd", N_("Command to be executed"), FALSE, NULL, NULL },
239  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
240 };
241 
242 /* Array of RemminaProtocolSetting for advanced settings.
243  * Each item is composed by:
244  * a) RemminaProtocolSettingType for setting type
245  * b) Setting name
246  * c) Setting description
247  * d) Compact disposition
248  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
249  * f) Setting Tooltip
250  */
252 {
253  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "detached", N_("Detached window"), TRUE, NULL, NULL },
254  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
255 };
256 
257 /* Array for available features.
258  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
260 {
261  { REMMINA_PROTOCOL_FEATURE_TYPE_GTKSOCKET, REMMINA_PLUGIN_ST_FEATURE_GTKSOCKET, NULL, NULL, NULL},
262  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL}
263 };
264 
265 /* Protocol plugin definition and features */
267 {
269  PLUGIN_NAME, // Name
270  PLUGIN_DESCRIPTION, // Description
271  GETTEXT_PACKAGE, // Translation domain
272  PLUGIN_VERSION, // Version number
273  PLUGIN_APPICON, // Icon for normal connection
274  PLUGIN_APPICON, // Icon for SSH connection
275  remmina_plugin_st_basic_settings, // Array for basic settings
276  remmina_plugin_st_advanced_settings, // Array for advanced settings
277  REMMINA_PROTOCOL_SSH_SETTING_NONE, // SSH settings type
278  remmina_st_features, // Array for available features
279  remmina_plugin_st_init, // Plugin initialization
280  remmina_plugin_st_open_connection, // Plugin open connection
281  remmina_plugin_st_close_connection, // Plugin close connection
282  remmina_st_query_feature, // Query for available features
283  NULL, // Call a feature
284  NULL, // Send a keystroke
285  NULL // Capture screenshot
286 };
287 
288 G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
289 {
290  TRACE_CALL(__func__);
291  remmina_plugin_service = service;
292 
293  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
294  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
295 
296 
297  if (!service->register_plugin((RemminaPlugin *) &remmina_plugin))
298  {
299  return FALSE;
300  }
301  return TRUE;
302 }
static void remmina_plugin_st_init(RemminaProtocolWidget *gp)
Definition: st_plugin.c:82
void(* debug)(const gchar *fmt,...)
Definition: plugin.h:214
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:158
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
static const RemminaProtocolSetting remmina_plugin_st_advanced_settings[]
Definition: st_plugin.c:251
static gboolean remmina_st_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: st_plugin.c:58
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:151
static void remmina_plugin_st_on_plug_removed(GtkSocket *socket, RemminaProtocolWidget *gp)
Definition: st_plugin.c:75
static gboolean remmina_plugin_st_open_connection(RemminaProtocolWidget *gp)
Definition: st_plugin.c:99
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:167
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:203
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
static const RemminaProtocolFeature remmina_st_features[]
Definition: st_plugin.c:259
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: st_plugin.c:288
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
static RemminaPluginService * remmina_plugin_service
Definition: st_plugin.c:56
static gpointer term_list[]
Definition: st_plugin.c:216
GtkWidget * socket
Definition: st_plugin.c:51
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:160
static void remmina_plugin_st_on_plug_added(GtkSocket *socket, RemminaProtocolWidget *gp)
Definition: st_plugin.c:65
static RemminaProtocolPlugin remmina_plugin
Definition: st_plugin.c:266
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
static gboolean remmina_plugin_st_close_connection(RemminaProtocolWidget *gp)
Definition: st_plugin.c:208
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:153
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
static const RemminaProtocolSetting remmina_plugin_st_basic_settings[]
Definition: st_plugin.c:235
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
struct _RemminaPluginData RemminaPluginData