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_sftp_plugin.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010 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"
39 
40 #ifdef HAVE_LIBSSH
41 
42 #include <gtk/gtk.h>
43 #include <glib/gi18n.h>
44 #include "remmina_public.h"
45 #include "remmina_sftp_client.h"
46 #include "remmina_plugin_manager.h"
47 #include "remmina_ssh.h"
49 #include "remmina_sftp_plugin.h"
50 
51 #define REMMINA_PLUGIN_SFTP_FEATURE_PREF_SHOW_HIDDEN 1
52 #define REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL 2
53 
54 #define REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY "overwrite_all"
55 
56 #define GET_PLUGIN_DATA(gp) (RemminaPluginSftpData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
57 
58 typedef struct _RemminaPluginSftpData {
60  pthread_t thread;
63 
65 
66 gboolean remmina_plugin_sftp_start_direct_tunnel(RemminaProtocolWidget *gp, char **phost, int *pport)
67 {
68  gchar *hostport;
69 
70  hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE);
71  if (hostport == NULL) {
72  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
73  return FALSE;
74  }
75 
76  remmina_plugin_service->get_server_port(hostport, 22, phost, pport);
77 
78  return TRUE;
79 }
80 
81 static gpointer
83 {
84  TRACE_CALL(__func__);
86  RemminaPluginSftpData *gpdata;
87  RemminaFile *remminafile;
88  RemminaSSH *ssh;
89  RemminaSFTP *sftp = NULL;
90  gboolean cont = FALSE;
91  gint ret;
92  const gchar *cs;
93  gchar *host;
94  int port;
95 
96  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
97  CANCEL_ASYNC
98 
99  gpdata = GET_PLUGIN_DATA(gp);
100  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
101 
102  /* we may need to open a new tunnel too */
103  host = NULL;
104  port = 0;
105  if (!remmina_plugin_sftp_start_direct_tunnel(gp, &host, &port))
106  return NULL;
107 
108  ssh = g_object_get_data(G_OBJECT(gp), "user-data");
109  if (ssh) {
110  /* Create SFTP connection based on existing SSH session */
111  sftp = remmina_sftp_new_from_ssh(ssh);
112  ssh->tunnel_entrance_host = host;
113  ssh->tunnel_entrance_port = port;
114  if (remmina_ssh_init_session(REMMINA_SSH(sftp)) &&
115  remmina_ssh_auth(REMMINA_SSH(sftp), NULL, gp, remminafile) == REMMINA_SSH_AUTH_SUCCESS &&
116  remmina_sftp_open(sftp))
117  cont = TRUE;
118  } else {
119  /* New SFTP connection */
120  sftp = remmina_sftp_new_from_file(remminafile);
121  ssh = REMMINA_SSH(sftp);
122  ssh->tunnel_entrance_host = host;
123  ssh->tunnel_entrance_port = port;
124  while (1) {
125  if (!remmina_ssh_init_session(ssh)) {
126  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error);
127  break;
128  }
129 
130  ret = remmina_ssh_auth_gui(ssh, gp, remminafile);
131  if (ret != REMMINA_SSH_AUTH_SUCCESS) {
132  if (ret != REMMINA_SSH_AUTH_USERCANCEL)
133  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error);
134  break;
135  }
136 
137  if (!remmina_sftp_open(sftp)) {
138  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error);
139  break;
140  }
141 
142  cs = remmina_plugin_service->file_get_string(remminafile, "execpath");
143  if (cs && cs[0])
144  remmina_ftp_client_set_dir(REMMINA_FTP_CLIENT(gpdata->client), cs);
145 
146  cont = TRUE;
147  break;
148  }
149  }
150 
151  if (!cont) {
152  if (sftp) remmina_sftp_free(sftp);
153  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
154  return NULL;
155  }
156 
157  remmina_sftp_client_open(REMMINA_SFTP_CLIENT(gpdata->client), sftp);
158  /* RemminaSFTPClient owns the object, we just take the reference */
159  gpdata->sftp = sftp;
160 
161  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
162 
163  gpdata->thread = 0;
164  return NULL;
165 }
166 
167 static void
169 {
170  TRACE_CALL(__func__);
171  RemminaFile *remminafile;
172 
173  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
174  remmina_ftp_client_load_state(REMMINA_FTP_CLIENT(widget), remminafile);
175 }
176 
177 static void
179 {
180  TRACE_CALL(__func__);
181  RemminaPluginSftpData *gpdata;
182  RemminaFile *remminafile;
183 
184  gpdata = g_new0(RemminaPluginSftpData, 1);
185  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
186 
187  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
188 
189  gpdata->client = remmina_sftp_client_new();
190  gpdata->client->gp = gp;
191  gtk_widget_show(GTK_WIDGET(gpdata->client));
192  gtk_container_add(GTK_CONTAINER(gp), GTK_WIDGET(gpdata->client));
193 
194  remmina_ftp_client_set_show_hidden(REMMINA_FTP_CLIENT(gpdata->client),
195  remmina_plugin_service->file_get_int(remminafile, "showhidden", FALSE));
196 
197  remmina_ftp_client_set_overwrite_status(REMMINA_FTP_CLIENT(gpdata->client),
198  remmina_plugin_service->file_get_int(remminafile,
199  REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE));
200 
201  remmina_plugin_service->protocol_plugin_register_hostkey(gp, GTK_WIDGET(gpdata->client));
202 
203  g_signal_connect(G_OBJECT(gpdata->client),
204  "realize", G_CALLBACK(remmina_plugin_sftp_client_on_realize), gp);
205 }
206 
207 static gboolean
209 {
210  TRACE_CALL(__func__);
211  RemminaPluginSftpData *gpdata = GET_PLUGIN_DATA(gp);
212 
213  remmina_plugin_service->protocol_plugin_set_expand(gp, TRUE);
214  remmina_plugin_service->protocol_plugin_set_width(gp, 640);
215  remmina_plugin_service->protocol_plugin_set_height(gp, 480);
216 
217  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_sftp_main_thread, gp)) {
218  remmina_plugin_service->protocol_plugin_set_error(gp,
219  "Could not initialize pthread. Falling back to non-thread modeā€¦");
220  gpdata->thread = 0;
221  return FALSE;
222  } else {
223  return TRUE;
224  }
225  return TRUE;
226 }
227 
228 static gboolean
230 {
231  TRACE_CALL(__func__);
232  RemminaPluginSftpData *gpdata = GET_PLUGIN_DATA(gp);
233  RemminaFile *remminafile;
234 
235  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
236  if (gpdata->thread) {
237  pthread_cancel(gpdata->thread);
238  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
239  }
240 
241  remmina_ftp_client_save_state(REMMINA_FTP_CLIENT(gpdata->client), remminafile);
242  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
243  /* The session preference overwrite_all is always saved to FALSE in order
244  * to avoid unwanted overwriting.
245  * If we'd change idea just remove the next line to save the preference. */
246  remmina_file_set_int(remminafile,
247  REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE);
248  return FALSE;
249 }
250 
251 static gboolean
253 {
254  TRACE_CALL(__func__);
255  return TRUE;
256 }
257 
258 static void
260 {
261  TRACE_CALL(__func__);
262  RemminaPluginSftpData *gpdata = GET_PLUGIN_DATA(gp);
263  RemminaFile *remminafile;
264 
265  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
266  switch (feature->id) {
267  case REMMINA_PROTOCOL_FEATURE_TOOL_SSH:
268  remmina_plugin_service->open_connection(
269  remmina_file_dup_temp_protocol(remmina_plugin_service->protocol_plugin_get_file(gp), "SSH"),
270  NULL, gpdata->sftp, NULL);
271  return;
272  case REMMINA_PROTOCOL_FEATURE_TOOL_SFTP:
273  remmina_plugin_service->open_connection(
274  remmina_file_dup_temp_protocol(remmina_plugin_service->protocol_plugin_get_file(gp), "SFTP"),
275  NULL, gpdata->sftp, NULL);
276  return;
277  case REMMINA_PLUGIN_SFTP_FEATURE_PREF_SHOW_HIDDEN:
278  remmina_ftp_client_set_show_hidden(REMMINA_FTP_CLIENT(gpdata->client),
279  remmina_plugin_service->file_get_int(remminafile, "showhidden", FALSE));
280  return;
281  case REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL:
282  remmina_ftp_client_set_overwrite_status(REMMINA_FTP_CLIENT(gpdata->client),
283  remmina_plugin_service->file_get_int(remminafile,
284  REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE));
285  return;
286  }
287 }
288 
289 static gpointer ssh_auth[] =
290 {
291  "0", N_("Password"),
292  "1", N_("SSH identity file"),
293  "2", N_("SSH agent"),
294  "3", N_("Public key (automatic)"),
295  "4", N_("Kerberos (GSSAPI)"),
296  NULL
297 };
298 
299 /* Array for available features.
300  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
302 {
303  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SFTP_FEATURE_PREF_SHOW_HIDDEN, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "showhidden",
304  N_("Show Hidden Files") },
305  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK),
306  REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, N_("Overwrite all") },
307  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL,
308  NULL }
309 };
310 
311 /* Array of RemminaProtocolSetting for basic settings.
312  * Each item is composed by:
313  * a) RemminaProtocolSettingType for setting type
314  * b) Setting name
315  * c) Setting description
316  * d) Compact disposition
317  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
318  * f) Setting Tooltip
319  */
321 {
322  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_sftp-ssh._tcp", NULL },
323  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL },
324  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("Password"), FALSE, NULL, NULL },
325  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_auth", N_("Authentication type"), FALSE, ssh_auth, NULL },
326  { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "ssh_privatekey", N_("Identity file"), FALSE, NULL, NULL },
327  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "ssh_passphrase", N_("Password to unlock private key"), FALSE, NULL, NULL },
328  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_proxycommand", N_("SSH Proxy Command"), FALSE, NULL, NULL },
329  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
330 };
331 
332 /* Protocol plugin definition and features */
334 {
336  "SFTP", // Name
337  N_("SFTP - Secure File Transfer"), // Description
338  GETTEXT_PACKAGE, // Translation domain
339  VERSION, // Version number
340  "remmina-sftp-symbolic", // Icon for normal connection
341  "remmina-sftp-symbolic", // Icon for SSH connection
342  remmina_sftp_basic_settings, // Array for basic settings
343  NULL, // Array for advanced settings
344  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
345  remmina_plugin_sftp_features, // Array for available features
346  remmina_plugin_sftp_init, // Plugin initialization
347  remmina_plugin_sftp_open_connection, // Plugin open connection
348  remmina_plugin_sftp_close_connection, // Plugin close connection
349  remmina_plugin_sftp_query_feature, // Query for available features
350  remmina_plugin_sftp_call_feature, // Call a feature
351  NULL, // Send a keystroke
352  NULL // Screenshot support unavailable
353 };
354 
355 void
357 {
358  TRACE_CALL(__func__);
359  remmina_plugin_service = &remmina_plugin_manager_service;
360  remmina_plugin_service->register_plugin((RemminaPlugin *)&remmina_plugin_sftp);
361 }
362 
363 #else
364 
366 {
367  TRACE_CALL(__func__);
368 }
369 
370 #endif
void remmina_ftp_client_load_state(RemminaFTPClient *client, RemminaFile *remminafile)
void remmina_ftp_client_set_overwrite_status(RemminaFTPClient *client, gboolean status)
enum remmina_ssh_auth_result remmina_ssh_auth_gui(RemminaSSH *ssh, RemminaProtocolWidget *gp, RemminaFile *remminafile)
gboolean remmina_plugin_sftp_start_direct_tunnel(RemminaProtocolWidget *gp, char **phost, int *pport)
void remmina_ftp_client_set_show_hidden(RemminaFTPClient *client, gboolean show_hidden)
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:158
gchar * error
Definition: remmina_ssh.h:79
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
RemminaFile * remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
Definition: remmina_file.c:711
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:151
static void remmina_plugin_sftp_client_on_realize(GtkWidget *widget, RemminaProtocolWidget *gp)
void remmina_sftp_plugin_register(void)
static gboolean remmina_plugin_sftp_open_connection(RemminaProtocolWidget *gp)
static RemminaProtocolPlugin remmina_plugin_sftp
struct _RemminaPluginSftpData RemminaPluginSftpData
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:167
static void remmina_plugin_sftp_init(RemminaProtocolWidget *gp)
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:221
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:203
RemminaSFTPClient * client
static gpointer ssh_auth[]
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
RemminaSFTPClient * remmina_sftp_client_new(void)
void remmina_sftp_client_open(RemminaSFTPClient *client, RemminaSFTP *sftp)
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
RemminaPluginService remmina_plugin_manager_service
static gpointer remmina_plugin_sftp_main_thread(gpointer data)
static RemminaPluginService * remmina_plugin_service
gint tunnel_entrance_port
Definition: remmina_ssh.h:87
void remmina_ftp_client_set_dir(RemminaFTPClient *client, const gchar *dir)
static const RemminaProtocolSetting remmina_sftp_basic_settings[]
void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: remmina_file.c:518
void(* protocol_plugin_set_expand)(RemminaProtocolWidget *gp, gboolean expand)
Definition: plugin.h:156
gchar * tunnel_entrance_host
Definition: remmina_ssh.h:86
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:160
RemminaSFTP * remmina_sftp_new_from_ssh(RemminaSSH *ssh)
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
gboolean remmina_sftp_open(RemminaSFTP *sftp)
static const RemminaProtocolFeature remmina_plugin_sftp_features[]
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:153
void remmina_sftp_free(RemminaSFTP *sftp)
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:163
GtkWidget *(* open_connection)(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: plugin.h:220
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
RemminaSFTP * remmina_sftp_new_from_file(RemminaFile *remminafile)
static gboolean remmina_plugin_sftp_close_connection(RemminaProtocolWidget *gp)
RemminaProtocolWidget * gp
static void remmina_plugin_sftp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
void remmina_ftp_client_save_state(RemminaFTPClient *client, RemminaFile *remminafile)
gboolean remmina_ssh_init_session(RemminaSSH *ssh)
enum remmina_ssh_auth_result remmina_ssh_auth(RemminaSSH *ssh, const gchar *password, RemminaProtocolWidget *gp, RemminaFile *remminafile)
Definition: remmina_ssh.c:336
static gboolean remmina_plugin_sftp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)