diff options
Diffstat (limited to '')
-rw-r--r-- | src/remmina_sftp_plugin.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/src/remmina_sftp_plugin.c b/src/remmina_sftp_plugin.c new file mode 100644 index 0000000..8132649 --- /dev/null +++ b/src/remmina_sftp_plugin.c @@ -0,0 +1,401 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2010 Vic Lee + * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo + * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#include "config.h" +#include "remmina/remmina_trace_calls.h" + +#ifdef HAVE_LIBSSH + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include "remmina_public.h" +#include "remmina_sftp_client.h" +#include "remmina_plugin_manager.h" +#include "remmina_ssh.h" +#include "remmina_sftp_plugin.h" + +#define REMMINA_PLUGIN_SFTP_FEATURE_PREF_SHOW_HIDDEN 1 +#define REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL 2 +#define REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL 3 + +#define REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY "overwrite_all" +#define REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL_KEY "resume_all" + +#define GET_PLUGIN_DATA(gp) (RemminaPluginSftpData *)g_object_get_data(G_OBJECT(gp), "plugin-data"); + +typedef struct _RemminaPluginSftpData { + RemminaSFTPClient * client; + pthread_t thread; + RemminaSFTP * sftp; +} RemminaPluginSftpData; + +static RemminaPluginService *remmina_plugin_service = NULL; + +gboolean remmina_plugin_sftp_start_direct_tunnel(RemminaProtocolWidget *gp, char **phost, int *pport) +{ + gchar *hostport; + + hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE); + if (hostport == NULL) { + remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + return FALSE; + } + + remmina_plugin_service->get_server_port(hostport, 22, phost, pport); + + return TRUE; +} + +static gpointer +remmina_plugin_sftp_main_thread(gpointer data) +{ + TRACE_CALL(__func__); + RemminaProtocolWidget *gp = (RemminaProtocolWidget *)data; + RemminaPluginSftpData *gpdata; + RemminaFile *remminafile; + RemminaSSH *ssh; + RemminaSFTP *sftp = NULL; + gboolean cont = FALSE; + gint ret; + const gchar *cs; + gchar *host; + int port; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + CANCEL_ASYNC + + gpdata = GET_PLUGIN_DATA(gp); + + remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + /* we may need to open a new tunnel too */ + host = NULL; + port = 0; + if (!remmina_plugin_sftp_start_direct_tunnel(gp, &host, &port)) + return NULL; + + ssh = g_object_get_data(G_OBJECT(gp), "user-data"); + if (ssh) { + /* Create SFTP connection based on existing SSH session */ + sftp = remmina_sftp_new_from_ssh(ssh); + ssh->tunnel_entrance_host = host; + ssh->tunnel_entrance_port = port; + if (remmina_ssh_init_session(REMMINA_SSH(sftp)) && + remmina_ssh_auth(REMMINA_SSH(sftp), NULL, gp, remminafile) == REMMINA_SSH_AUTH_SUCCESS && + remmina_sftp_open(sftp)) + cont = TRUE; + } else { + /* New SFTP connection */ + sftp = remmina_sftp_new_from_file(remminafile); + ssh = REMMINA_SSH(sftp); + ssh->tunnel_entrance_host = host; + ssh->tunnel_entrance_port = port; + while (1) { + if (!remmina_ssh_init_session(ssh)) { + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); + break; + } + + ret = remmina_ssh_auth_gui(ssh, gp, remminafile); + if (ret != REMMINA_SSH_AUTH_SUCCESS) { + if (ret == REMMINA_SSH_AUTH_RECONNECT) { + if (ssh->session) { + ssh_disconnect(ssh->session); + ssh_free(ssh->session); + ssh->session = NULL; + } + g_free(ssh->callback); + continue; + } + if (ret != REMMINA_SSH_AUTH_USERCANCEL) + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); + break; + } + + if (!remmina_sftp_open(sftp)) { + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); + break; + } + + cs = remmina_plugin_service->file_get_string(remminafile, "execpath"); + if (cs && cs[0]) + remmina_ftp_client_set_dir(REMMINA_FTP_CLIENT(gpdata->client), cs); + + cont = TRUE; + break; + } + } + + if (!cont) { + if (sftp) remmina_sftp_free(sftp); + remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + return NULL; + } + + remmina_sftp_client_open(REMMINA_SFTP_CLIENT(gpdata->client), sftp); + /* RemminaSFTPClient owns the object, we just take the reference */ + gpdata->sftp = sftp; + + remmina_plugin_service->protocol_plugin_signal_connection_opened(gp); + + gpdata->thread = 0; + return NULL; +} + +static void +remmina_plugin_sftp_client_on_realize(GtkWidget *widget, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + RemminaFile *remminafile; + + remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + remmina_ftp_client_load_state(REMMINA_FTP_CLIENT(widget), remminafile); +} + +static void +remmina_plugin_sftp_init(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + RemminaPluginSftpData *gpdata; + RemminaFile *remminafile; + + gpdata = g_new0(RemminaPluginSftpData, 1); + g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free); + + remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + gpdata->client = remmina_sftp_client_new(); + gpdata->client->gp = gp; + gtk_widget_show(GTK_WIDGET(gpdata->client)); + gtk_container_add(GTK_CONTAINER(gp), GTK_WIDGET(gpdata->client)); + + remmina_ftp_client_set_show_hidden(REMMINA_FTP_CLIENT(gpdata->client), + remmina_plugin_service->file_get_int(remminafile, "showhidden", FALSE)); + + remmina_ftp_client_set_overwrite_status(REMMINA_FTP_CLIENT(gpdata->client), + remmina_plugin_service->file_get_int(remminafile, + REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE)); + remmina_ftp_client_set_resume_status(REMMINA_FTP_CLIENT(gpdata->client), + remmina_plugin_service->file_get_int(remminafile, + REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL_KEY, FALSE)); + + remmina_plugin_service->protocol_plugin_register_hostkey(gp, GTK_WIDGET(gpdata->client)); + + g_signal_connect(G_OBJECT(gpdata->client), + "realize", G_CALLBACK(remmina_plugin_sftp_client_on_realize), gp); +} + +static gboolean +remmina_plugin_sftp_open_connection(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + RemminaPluginSftpData *gpdata = GET_PLUGIN_DATA(gp); + + remmina_plugin_service->protocol_plugin_set_expand(gp, TRUE); + remmina_plugin_service->protocol_plugin_set_width(gp, 640); + remmina_plugin_service->protocol_plugin_set_height(gp, 480); + + if (pthread_create(&gpdata->thread, NULL, remmina_plugin_sftp_main_thread, gp)) { + remmina_plugin_service->protocol_plugin_set_error(gp, + "Could not initialize pthread. Falling back to non-thread modeā¦"); + gpdata->thread = 0; + return FALSE; + } else { + return TRUE; + } + return TRUE; +} + +static gboolean +remmina_plugin_sftp_close_connection(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + RemminaPluginSftpData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile; + + remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + if (gpdata->thread) { + pthread_cancel(gpdata->thread); + if (gpdata->thread) pthread_join(gpdata->thread, NULL); + } + + remmina_ftp_client_save_state(REMMINA_FTP_CLIENT(gpdata->client), remminafile); + remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + /* The session preference overwrite_all is always saved to FALSE in order + * to avoid unwanted overwriting. + * If we'd change idea just remove the next line to save the preference. */ + remmina_file_set_int(remminafile, + REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE); + return FALSE; +} + +static gboolean +remmina_plugin_sftp_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) +{ + TRACE_CALL(__func__); + return TRUE; +} + +static void +remmina_plugin_sftp_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) +{ + TRACE_CALL(__func__); + RemminaPluginSftpData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile; + + remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + switch (feature->id) { + case REMMINA_PROTOCOL_FEATURE_TOOL_SSH: + remmina_plugin_service->open_connection( + remmina_file_dup_temp_protocol(remmina_plugin_service->protocol_plugin_get_file(gp), "SSH"), + NULL, gpdata->sftp, NULL); + return; + case REMMINA_PROTOCOL_FEATURE_TOOL_SFTP: + remmina_plugin_service->open_connection( + remmina_file_dup_temp_protocol(remmina_plugin_service->protocol_plugin_get_file(gp), "SFTP"), + NULL, gpdata->sftp, NULL); + return; + case REMMINA_PLUGIN_SFTP_FEATURE_PREF_SHOW_HIDDEN: + remmina_ftp_client_set_show_hidden(REMMINA_FTP_CLIENT(gpdata->client), + remmina_plugin_service->file_get_int(remminafile, "showhidden", FALSE)); + return; + case REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL: + remmina_ftp_client_set_overwrite_status(REMMINA_FTP_CLIENT(gpdata->client), + remmina_plugin_service->file_get_int(remminafile, + REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE)); + case REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL: + remmina_ftp_client_set_resume_status(REMMINA_FTP_CLIENT(gpdata->client), + remmina_plugin_service->file_get_int(remminafile, + REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL_KEY, FALSE)); + return; + } +} + +static gpointer ssh_auth[] = +{ + "0", N_("Password"), + "1", N_("SSH identity file"), + "2", N_("SSH agent"), + "3", N_("Public key (automatic)"), + "4", N_("Kerberos (GSSAPI)"), + NULL +}; + +/* Array for available features. + * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */ +static const RemminaProtocolFeature remmina_plugin_sftp_features[] = +{ + { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SFTP_FEATURE_PREF_SHOW_HIDDEN, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "showhidden", + N_("Show Hidden Files") }, + { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), + REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, N_("Overwrite all files") }, + { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), + REMMINA_PLUGIN_SFTP_FEATURE_PREF_RESUME_ALL_KEY, N_("Resume all file transfers") }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PROTOCOL_FEATURE_TOOL_SSH, N_("Connect via SSH from a new terminal"), "utilities-terminal",NULL}, + { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, + NULL } +}; + +/* Array of RemminaProtocolSetting for basic settings. + * Each item is composed by: + * a) RemminaProtocolSettingType for setting type + * b) Setting name + * c) Setting description + * d) Compact disposition + * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO + * f) Setting tooltip + * g) Validation data pointer, will be passed to the validation callback method. + * h) Validation callback method (Can be NULL. Every entry will be valid then.) + * use following prototype: + * gboolean mysetting_validator_method(gpointer key, gpointer value, + * gpointer validator_data); + * gpointer key is a gchar* containing the setting's name, + * gpointer value contains the value which should be validated, + * gpointer validator_data contains your passed data. + */ +static const RemminaProtocolSetting remmina_sftp_basic_settings[] = +{ + { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_sftp-ssh._tcp", NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("Password"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_auth", N_("Authentication type"), FALSE, ssh_auth, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "ssh_privatekey", N_("SSH identity file"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "ssh_passphrase", N_("Password to unlock private key"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_proxycommand", N_("SSH Proxy Command"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL } +}; + +/* Protocol plugin definition and features */ +static RemminaProtocolPlugin remmina_plugin_sftp = +{ + REMMINA_PLUGIN_TYPE_PROTOCOL, // Type + "SFTP", // Name + N_("SFTP - Secure File Transfer"), // Description + GETTEXT_PACKAGE, // Translation domain + VERSION, // Version number + "org.remmina.Remmina-sftp-symbolic", // Icon for normal connection + "org.remmina.Remmina-sftp-symbolic", // Icon for SSH connection + remmina_sftp_basic_settings, // Array for basic settings + NULL, // Array for advanced settings + REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type + remmina_plugin_sftp_features, // Array for available features + remmina_plugin_sftp_init, // Plugin initialization + remmina_plugin_sftp_open_connection, // Plugin open connection + remmina_plugin_sftp_close_connection, // Plugin close connection + remmina_plugin_sftp_query_feature, // Query for available features + remmina_plugin_sftp_call_feature, // Call a feature + NULL, // Send a keystroke + NULL, // Screenshot support unavailable + NULL, // RCW map event + NULL // RCW unmap event +}; + +void +remmina_sftp_plugin_register(void) +{ + TRACE_CALL(__func__); + remmina_plugin_service = &remmina_plugin_manager_service; + remmina_plugin_service->register_plugin((RemminaPlugin *)&remmina_plugin_sftp); +} + +#else + +void remmina_sftp_plugin_register(void) +{ + TRACE_CALL(__func__); +} + +#endif |