summaryrefslogtreecommitdiffstats
path: root/src/remmina_sftp_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/remmina_sftp_plugin.c401
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