summaryrefslogtreecommitdiffstats
path: root/src/remmina_sftp_client.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 17:06:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 17:06:32 +0000
commit2dad5357405ad33cfa792f04b3ab62a5d188841e (patch)
treeb8f8893942060fe3cfb04ac374cda96fdfc8f453 /src/remmina_sftp_client.c
parentInitial commit. (diff)
downloadremmina-2dad5357405ad33cfa792f04b3ab62a5d188841e.tar.xz
remmina-2dad5357405ad33cfa792f04b3ab62a5d188841e.zip
Adding upstream version 1.4.34+dfsg.upstream/1.4.34+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/remmina_sftp_client.c')
-rw-r--r--src/remmina_sftp_client.c1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/src/remmina_sftp_client.c b/src/remmina_sftp_client.c
new file mode 100644
index 0000000..1f1e0b9
--- /dev/null
+++ b/src/remmina_sftp_client.c
@@ -0,0 +1,1057 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2009-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.
+ *
+ */
+
+#define _FILE_OFFSET_BITS 64
+#include "config.h"
+
+#ifdef HAVE_LIBSSH
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gmodule.h>
+#include <pthread.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "remmina_public.h"
+#include "remmina_log.h"
+#include "remmina_pref.h"
+#include "remmina_ssh.h"
+#include "remmina_sftp_client.h"
+#include "remmina_sftp_plugin.h"
+#include "remmina_masterthread_exec.h"
+#include "remmina/remmina_trace_calls.h"
+
+G_DEFINE_TYPE(RemminaSFTPClient, remmina_sftp_client, REMMINA_TYPE_FTP_CLIENT)
+
+#define SET_CURSOR(cur) \
+ if (GDK_IS_WINDOW(gtk_widget_get_window(GTK_WIDGET(client)))) \
+ { \
+ gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(client)), cur); \
+ }
+
+static void
+remmina_sftp_client_class_init(RemminaSFTPClientClass *klass)
+{
+ TRACE_CALL(__func__);
+}
+
+#define GET_SFTPATTR_TYPE(a, type) \
+ if (a->type == 0) \
+ { \
+ type = ((a->permissions & 040000) ? REMMINA_FTP_FILE_TYPE_DIR : REMMINA_FTP_FILE_TYPE_FILE); \
+ type = ((a->permissions & 0120000) ? REMMINA_FTP_FILE_TYPE_LINK : REMMINA_FTP_FILE_TYPE_FILE); \
+ } \
+ else \
+ { \
+ type = (a->type == SSH_FILEXFER_TYPE_DIRECTORY ? REMMINA_FTP_FILE_TYPE_DIR : REMMINA_FTP_FILE_TYPE_FILE); \
+ if (a->type == SSH_FILEXFER_TYPE_SYMLINK ) type = REMMINA_FTP_FILE_TYPE_LINK; \
+ }
+
+/* ------------------------ The Task Thread routines ----------------------------- */
+
+static gboolean remmina_sftp_client_refresh(RemminaSFTPClient *client);
+
+#define THREAD_CHECK_EXIT \
+ (!client->taskid || client->thread_abort)
+
+
+
+static gboolean
+remmina_sftp_client_thread_update_task(RemminaSFTPClient *client, RemminaFTPTask *task)
+{
+ TRACE_CALL(__func__);
+ if (THREAD_CHECK_EXIT) return FALSE;
+
+ remmina_ftp_client_update_task(REMMINA_FTP_CLIENT(client), task);
+
+ return TRUE;
+}
+
+static void
+remmina_sftp_client_thread_set_error(RemminaSFTPClient *client, RemminaFTPTask *task, const gchar *error_format, ...)
+{
+ TRACE_CALL(__func__);
+ va_list args;
+
+ task->status = REMMINA_FTP_TASK_STATUS_ERROR;
+ g_free(task->tooltip);
+ if (error_format) {
+ va_start(args, error_format);
+ task->tooltip = g_strdup_vprintf(error_format, args);
+ va_end(args);
+ } else {
+ task->tooltip = NULL;
+ }
+
+ remmina_sftp_client_thread_update_task(client, task);
+}
+
+static void
+remmina_sftp_client_thread_set_finish(RemminaSFTPClient *client, RemminaFTPTask *task)
+{
+ TRACE_CALL(__func__);
+ task->status = REMMINA_FTP_TASK_STATUS_FINISH;
+ g_free(task->tooltip);
+ task->tooltip = NULL;
+
+ remmina_sftp_client_thread_update_task(client, task);
+}
+
+static RemminaFTPTask *
+remmina_sftp_client_thread_get_task(RemminaSFTPClient *client)
+{
+ TRACE_CALL(__func__);
+ RemminaFTPTask *task;
+
+ if (client->thread_abort) return NULL;
+
+ task = remmina_ftp_client_get_waiting_task(REMMINA_FTP_CLIENT(client));
+ if (task) {
+ client->taskid = task->taskid;
+
+ task->status = REMMINA_FTP_TASK_STATUS_RUN;
+ remmina_ftp_client_update_task(REMMINA_FTP_CLIENT(client), task);
+ }
+
+ return task;
+}
+
+static gboolean
+remmina_sftp_client_thread_download_file(RemminaSFTPClient *client, RemminaSFTP *sftp, RemminaFTPTask *task,
+ const gchar *remote_path, const gchar *local_path, guint64 *donesize)
+{
+ TRACE_CALL(__func__);
+ sftp_file remote_file;
+ FILE *local_file;
+ gchar *tmp;
+ gchar buf[20480];
+ gint len;
+ gint response;
+ uint64_t size;
+
+ if (THREAD_CHECK_EXIT) return FALSE;
+
+ /* Ensure local dir exists */
+ g_strlcpy(buf, local_path, sizeof(buf));
+ tmp = g_strrstr(buf, "/");
+ if (tmp && tmp != buf) {
+ *tmp = '\0';
+ if (g_mkdir_with_parents(buf, 0755) < 0) {
+ // TRANSLATORS: The placeholder %s is a directory path
+ remmina_sftp_client_thread_set_error(client, task, _("Could not create the folder “%s”."), buf);
+ return FALSE;
+ }
+ }
+
+ local_file = g_fopen(local_path, "ab");
+ if (!local_file) {
+ // TRANSLATORS: The placeholder %s is a file path
+ remmina_sftp_client_thread_set_error(client, task, _("Could not create the file “%s”."), local_path);
+ return FALSE;
+ }
+
+ fseeko(local_file, 0, SEEK_END);
+ size = ftello(local_file);
+ if (size > 0) {
+ response = remmina_sftp_client_confirm_resume(client, local_path);
+
+ switch (response) {
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_DELETE_EVENT:
+ fclose(local_file);
+ remmina_sftp_client_thread_set_error(client, task, NULL);
+ return FALSE;
+
+ case GTK_RESPONSE_ACCEPT:
+ fclose(local_file);
+ local_file = g_fopen(local_path, "wb");
+ if (!local_file) {
+ // TRANSLATORS: The placeholder %s is a file path
+ remmina_sftp_client_thread_set_error(client, task, _("Could not create the file “%s”."), local_path);
+ return FALSE;
+ }
+ size = 0;
+ break;
+
+ case GTK_RESPONSE_APPLY:
+ break;
+ }
+ }
+
+ tmp = remmina_ssh_unconvert(REMMINA_SSH(sftp), remote_path);
+ remote_file = sftp_open(sftp->sftp_sess, tmp, O_RDONLY, 0);
+ g_free(tmp);
+
+ if (!remote_file) {
+ fclose(local_file);
+ // TRANSLATORS: The placeholders %s are a file path, and an error message.
+ remmina_sftp_client_thread_set_error(client, task, _("Could not open the file “%s” on the server. %s"),
+ remote_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+
+ if (size > 0) {
+ if (sftp_seek64(remote_file, size) < 0) {
+ sftp_close(remote_file);
+ fclose(local_file);
+ remmina_sftp_client_thread_set_error(client, task, "Could not download the file “%s”. %s",
+ remote_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+ *donesize = size;
+ }
+
+ while (!THREAD_CHECK_EXIT && (len = sftp_read(remote_file, buf, sizeof(buf))) > 0) {
+ if (THREAD_CHECK_EXIT) break;
+
+ if (fwrite(buf, 1, len, local_file) < len) {
+ sftp_close(remote_file);
+ fclose(local_file);
+ remmina_sftp_client_thread_set_error(client, task, _("Could not save the file “%s”."), local_path);
+ return FALSE;
+ }
+
+ *donesize += (guint64)len;
+ task->donesize = (gfloat)(*donesize);
+
+ if (!remmina_sftp_client_thread_update_task(client, task)) break;
+ }
+
+ sftp_close(remote_file);
+ fclose(local_file);
+ return TRUE;
+}
+
+static gboolean
+remmina_sftp_client_thread_recursive_dir(RemminaSFTPClient *client, RemminaSFTP *sftp, RemminaFTPTask *task,
+ const gchar *rootdir_path, const gchar *subdir_path, GPtrArray *array)
+{
+ TRACE_CALL(__func__);
+ sftp_dir sftpdir;
+ sftp_attributes sftpattr;
+ gchar *tmp;
+ gchar *dir_path;
+ gchar *file_path;
+ gint type;
+ gboolean ret = TRUE;
+
+ if (THREAD_CHECK_EXIT) return FALSE;
+
+ if (subdir_path)
+ dir_path = remmina_public_combine_path(rootdir_path, subdir_path);
+ else
+ dir_path = g_strdup(rootdir_path);
+ tmp = remmina_ssh_unconvert(REMMINA_SSH(sftp), dir_path);
+
+ REMMINA_DEBUG ("%s HELLO", __func__);
+#if 0
+ gchar *tlink = sftp_readlink (sftp->sftp_sess, tmp);
+ if (tlink) {
+ REMMINA_DEBUG ("%s is a link to %s", tmp, tlink);
+ tmp = g_strdup (tlink);
+ }
+ g_free(tlink);
+#endif
+ sftpdir = sftp_opendir(sftp->sftp_sess, tmp);
+ g_free(tmp);
+
+ if (!sftpdir) {
+ remmina_sftp_client_thread_set_error(client, task, _("Could not open the folder “%s”. %s"),
+ dir_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ g_free(dir_path);
+ return FALSE;
+ }
+
+ g_free(dir_path);
+
+ while ((sftpattr = sftp_readdir(sftp->sftp_sess, sftpdir))) {
+ if (g_strcmp0(sftpattr->name, ".") != 0 &&
+ g_strcmp0(sftpattr->name, "..") != 0) {
+ GET_SFTPATTR_TYPE(sftpattr, type);
+
+ tmp = remmina_ssh_convert(REMMINA_SSH(sftp), sftpattr->name);
+ if (subdir_path) {
+ file_path = remmina_public_combine_path(subdir_path, tmp);
+ g_free(tmp);
+ } else {
+ file_path = tmp;
+ }
+
+ if (type == REMMINA_FTP_FILE_TYPE_DIR) {
+ ret = remmina_sftp_client_thread_recursive_dir(client, sftp, task, rootdir_path, file_path, array);
+ g_free(file_path);
+ if (!ret) {
+ sftp_attributes_free(sftpattr);
+ break;
+ }
+ } else {
+ task->size += (gfloat)sftpattr->size;
+ g_ptr_array_add(array, file_path);
+
+ if (!remmina_sftp_client_thread_update_task(client, task)) {
+ sftp_attributes_free(sftpattr);
+ break;
+ }
+ }
+ }
+ sftp_attributes_free(sftpattr);
+
+ if (THREAD_CHECK_EXIT) break;
+ }
+
+ sftp_closedir(sftpdir);
+ return ret;
+}
+
+static gboolean
+remmina_sftp_client_thread_recursive_localdir(RemminaSFTPClient *client, RemminaFTPTask *task,
+ const gchar *rootdir_path, const gchar *subdir_path, GPtrArray *array)
+{
+ TRACE_CALL(__func__);
+ GDir *dir;
+ gchar *path;
+ const gchar *name;
+ gchar *relpath;
+ gchar *abspath;
+ struct stat st;
+ gboolean ret = TRUE;
+
+ path = g_build_filename(rootdir_path, subdir_path, NULL);
+ dir = g_dir_open(path, 0, NULL);
+ if (dir == NULL) {
+ g_free(path);
+ return FALSE;
+ }
+ while ((name = g_dir_read_name(dir)) != NULL) {
+ if (THREAD_CHECK_EXIT) {
+ ret = FALSE;
+ break;
+ }
+ if (g_strcmp0(name, ".") == 0 || g_strcmp0(name, "..") == 0) continue;
+ abspath = g_build_filename(path, name, NULL);
+ if (g_stat(abspath, &st) < 0) {
+ g_free(abspath);
+ continue;
+ }
+ relpath = g_build_filename(subdir_path ? subdir_path : "", name, NULL);
+ g_ptr_array_add(array, relpath);
+ if (g_file_test(abspath, G_FILE_TEST_IS_DIR)) {
+ ret = remmina_sftp_client_thread_recursive_localdir(client, task, rootdir_path, relpath, array);
+ if (!ret) break;
+ } else {
+ task->size += (gfloat)st.st_size;
+ }
+ g_free(abspath);
+ }
+ g_free(path);
+ g_dir_close(dir);
+ return ret;
+}
+
+static gboolean
+remmina_sftp_client_thread_mkdir(RemminaSFTPClient *client, RemminaSFTP *sftp, RemminaFTPTask *task, const gchar *path)
+{
+ TRACE_CALL(__func__);
+ sftp_attributes sftpattr;
+
+ sftpattr = sftp_stat(sftp->sftp_sess, path);
+ if (sftpattr != NULL) {
+ sftp_attributes_free(sftpattr);
+ return TRUE;
+ }
+ if (sftp_mkdir(sftp->sftp_sess, path, 0755) < 0) {
+ remmina_sftp_client_thread_set_error(client, task, _("Could not create the folder “%s” on the server. %s"),
+ path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+remmina_sftp_client_thread_upload_file(RemminaSFTPClient *client, RemminaSFTP *sftp, RemminaFTPTask *task,
+ const gchar *remote_path, const gchar *local_path, guint64 *donesize)
+{
+ TRACE_CALL(__func__);
+ sftp_file remote_file;
+ FILE *local_file;
+ gchar *tmp;
+ gchar buf[20480];
+ gint len;
+ sftp_attributes attr;
+ gint response;
+ uint64_t size;
+
+ if (THREAD_CHECK_EXIT) return FALSE;
+
+ tmp = remmina_ssh_unconvert(REMMINA_SSH(sftp), remote_path);
+ remote_file = sftp_open(sftp->sftp_sess, tmp, O_WRONLY | O_CREAT, 0644);
+ g_free(tmp);
+
+ if (!remote_file) {
+ remmina_sftp_client_thread_set_error(client, task, _("Could not create the file “%s” on the server. %s"),
+ remote_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+ attr = sftp_fstat(remote_file);
+ size = attr->size;
+ sftp_attributes_free(attr);
+ if (size > 0) {
+ response = remmina_sftp_client_confirm_resume(client, remote_path);
+ switch (response) {
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_DELETE_EVENT:
+ sftp_close(remote_file);
+ remmina_sftp_client_thread_set_error(client, task, NULL);
+ return FALSE;
+
+ case GTK_RESPONSE_ACCEPT:
+ sftp_close(remote_file);
+ tmp = remmina_ssh_unconvert(REMMINA_SSH(sftp), remote_path);
+ remote_file = sftp_open(sftp->sftp_sess, tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ g_free(tmp);
+ if (!remote_file) {
+ remmina_sftp_client_thread_set_error(client, task, _("Could not create the file “%s” on the server. %s"),
+ remote_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+ size = 0;
+ break;
+
+ case GTK_RESPONSE_APPLY:
+ if (sftp_seek64(remote_file, size) < 0) {
+ sftp_close(remote_file);
+ remmina_sftp_client_thread_set_error(client, task, "Could not download the file “%s”. %s",
+ remote_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+ break;
+ }
+ }
+
+ local_file = g_fopen(local_path, "rb");
+ if (!local_file) {
+ sftp_close(remote_file);
+ remmina_sftp_client_thread_set_error(client, task, _("Could not open the file “%s”."), local_path);
+ return FALSE;
+ }
+
+ if (size > 0) {
+ if (fseeko(local_file, size, SEEK_SET) < 0) {
+ sftp_close(remote_file);
+ fclose(local_file);
+ remmina_sftp_client_thread_set_error(client, task, "Could not find the local file “%s”.", local_path);
+ return FALSE;
+ }
+ *donesize = size;
+ }
+
+ while (!THREAD_CHECK_EXIT && (len = fread(buf, 1, sizeof(buf), local_file)) > 0) {
+ if (THREAD_CHECK_EXIT) break;
+
+ if (sftp_write(remote_file, buf, len) < len) {
+ sftp_close(remote_file);
+ fclose(local_file);
+ remmina_sftp_client_thread_set_error(client, task, _("Could not write to the file “%s” on the server. %s"),
+ remote_path, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ return FALSE;
+ }
+
+ *donesize += (guint64)len;
+ task->donesize = (gfloat)(*donesize);
+
+ if (!remmina_sftp_client_thread_update_task(client, task)) break;
+ }
+
+ sftp_close(remote_file);
+ fclose(local_file);
+ return TRUE;
+}
+
+static gpointer
+remmina_sftp_client_thread_main(gpointer data)
+{
+ TRACE_CALL(__func__);
+ RemminaSFTPClient *client = REMMINA_SFTP_CLIENT(data);
+ RemminaSFTP *sftp = NULL;
+ RemminaFTPTask *task;
+ gchar *remote, *local;
+ guint64 size;
+ GPtrArray *array;
+ gint i;
+ gchar *remote_file, *local_file;
+ gboolean ret;
+ gchar *refreshdir = NULL;
+ gchar *tmp;
+ gboolean refresh = FALSE;
+ gchar *host;
+ int port;
+
+ task = remmina_sftp_client_thread_get_task(client);
+ while (task) {
+ size = 0;
+ if (!sftp) {
+ sftp = remmina_sftp_new_from_ssh(REMMINA_SSH(client->sftp));
+
+ /* we may need to open a new tunnel too */
+ host = NULL;
+ port = 0;
+ if (!remmina_plugin_sftp_start_direct_tunnel(client->gp, &host, &port))
+ return NULL;
+ (REMMINA_SSH(sftp))->tunnel_entrance_host = host;
+ (REMMINA_SSH(sftp))->tunnel_entrance_port = port;
+
+ /* Open a new connection for this subcommand */
+ g_debug("[SFTPCLI] %s opening ssh session to %s:%d", __func__, host, port);
+ if (!remmina_ssh_init_session(REMMINA_SSH(sftp))) {
+ g_debug("[SFTPCLI] remmina_ssh_init_session returned error %s\n", (REMMINA_SSH(sftp))->error);
+ remmina_sftp_client_thread_set_error(client, task, (REMMINA_SSH(sftp))->error);
+ remmina_ftp_task_free(task);
+ break;
+ }
+
+ if (remmina_ssh_auth(REMMINA_SSH(sftp), REMMINA_SSH(sftp)->password, client->gp, NULL) != REMMINA_SSH_AUTH_SUCCESS) {
+ g_debug("[SFTPCLI] remmina_ssh_auth returned error %s\n", (REMMINA_SSH(sftp))->error);
+ remmina_sftp_client_thread_set_error(client, task, (REMMINA_SSH(sftp))->error);
+ remmina_ftp_task_free(task);
+ break;
+ }
+
+ if (!remmina_sftp_open(sftp)) {
+ g_debug("[SFTPCLI] remmina_sftp_open returned error %s\n", (REMMINA_SSH(sftp))->error);
+ remmina_sftp_client_thread_set_error(client, task, (REMMINA_SSH(sftp))->error);
+ remmina_ftp_task_free(task);
+ break;
+ }
+ }
+
+ remote = remmina_public_combine_path(task->remotedir, task->name);
+ local = remmina_public_combine_path(task->localdir, task->name);
+
+ switch (task->tasktype) {
+ case REMMINA_FTP_TASK_TYPE_DOWNLOAD:
+ switch (task->type) {
+ case REMMINA_FTP_FILE_TYPE_FILE:
+ ret = remmina_sftp_client_thread_download_file(client, sftp, task,
+ remote, local, &size);
+ break;
+
+ case REMMINA_FTP_FILE_TYPE_DIR:
+ array = g_ptr_array_new();
+ ret = remmina_sftp_client_thread_recursive_dir(client, sftp, task, remote, NULL, array);
+ if (ret) {
+ for (i = 0; i < array->len; i++) {
+ if (THREAD_CHECK_EXIT) {
+ ret = FALSE;
+ break;
+ }
+ remote_file = remmina_public_combine_path(remote, (gchar *)g_ptr_array_index(array, i));
+ local_file = remmina_public_combine_path(local, (gchar *)g_ptr_array_index(array, i));
+ ret = remmina_sftp_client_thread_download_file(client, sftp, task,
+ remote_file, local_file, &size);
+ g_free(remote_file);
+ g_free(local_file);
+ if (!ret) break;
+ }
+ }
+ g_ptr_array_foreach(array, (GFunc)g_free, NULL);
+ g_ptr_array_free(array, TRUE);
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret)
+ remmina_sftp_client_thread_set_finish(client, task);
+ break;
+
+ case REMMINA_FTP_TASK_TYPE_UPLOAD:
+ switch (task->type) {
+ case REMMINA_FTP_FILE_TYPE_FILE:
+ ret = remmina_sftp_client_thread_upload_file(client, sftp, task,
+ remote, local, &size);
+ break;
+
+ case REMMINA_FTP_FILE_TYPE_DIR:
+ ret = remmina_sftp_client_thread_mkdir(client, sftp, task, remote);
+ if (!ret) break;
+ array = g_ptr_array_new();
+ ret = remmina_sftp_client_thread_recursive_localdir(client, task, local, NULL, array);
+ if (ret) {
+ for (i = 0; i < array->len; i++) {
+ if (THREAD_CHECK_EXIT) {
+ ret = FALSE;
+ break;
+ }
+ remote_file = remmina_public_combine_path(remote, (gchar *)g_ptr_array_index(array, i));
+ local_file = g_build_filename(local, (gchar *)g_ptr_array_index(array, i), NULL);
+ if (g_file_test(local_file, G_FILE_TEST_IS_DIR))
+ ret = remmina_sftp_client_thread_mkdir(client, sftp, task, remote_file);
+ else
+ ret = remmina_sftp_client_thread_upload_file(client, sftp, task,
+ remote_file, local_file, &size);
+ g_free(remote_file);
+ g_free(local_file);
+ if (!ret) break;
+ }
+ }
+ g_ptr_array_foreach(array, (GFunc)g_free, NULL);
+ g_ptr_array_free(array, TRUE);
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ remmina_sftp_client_thread_set_finish(client, task);
+ tmp = remmina_ftp_client_get_dir(REMMINA_FTP_CLIENT(client));
+ if (g_strcmp0(tmp, task->remotedir) == 0) {
+ refresh = TRUE;
+ g_free(refreshdir);
+ refreshdir = tmp;
+ } else {
+ g_free(tmp);
+ }
+ }
+ break;
+ }
+
+ g_free(remote);
+ g_free(local);
+
+ remmina_ftp_task_free(task);
+ client->taskid = 0;
+
+ if (client->thread_abort) break;
+
+ task = remmina_sftp_client_thread_get_task(client);
+ }
+
+ if (sftp)
+ remmina_sftp_free(sftp);
+
+ if (!client->thread_abort && refresh) {
+ tmp = remmina_ftp_client_get_dir(REMMINA_FTP_CLIENT(client));
+ if (g_strcmp0(tmp, refreshdir) == 0)
+ IDLE_ADD((GSourceFunc)remmina_sftp_client_refresh, client);
+ g_free(tmp);
+ }
+ g_free(refreshdir);
+ client->thread = 0;
+
+ return NULL;
+}
+
+/* ------------------------ The SFTP Client routines ----------------------------- */
+
+static void
+remmina_sftp_client_destroy(RemminaSFTPClient *client, gpointer data)
+{
+ TRACE_CALL(__func__);
+ if (client->sftp) {
+ remmina_sftp_free(client->sftp);
+ client->sftp = NULL;
+ }
+ client->thread_abort = TRUE;
+ /* We will wait for the thread to quit itself, and hopefully the thread is handling things correctly */
+ while (client->thread) {
+ /* gdk_threads_leave (); */
+ sleep(1);
+ /* gdk_threads_enter (); */
+ }
+}
+
+static sftp_dir
+remmina_sftp_client_sftp_session_opendir(RemminaSFTPClient *client, const gchar *dir)
+{
+ TRACE_CALL(__func__);
+ sftp_dir sftpdir;
+
+ sftpdir = sftp_opendir(client->sftp->sftp_sess, (gchar *)dir);
+ if (!sftpdir) {
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Could not open the folder “%s”. %s"), dir,
+ ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return NULL;
+ }
+ return sftpdir;
+}
+
+static gboolean
+remmina_sftp_client_sftp_session_closedir(RemminaSFTPClient *client, sftp_dir sftpdir)
+{
+ TRACE_CALL(__func__);
+
+ if (!sftp_dir_eof(sftpdir)) {
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Could not read from the folder. %s"), ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return FALSE;
+ }
+ sftp_closedir(sftpdir);
+ return TRUE;
+}
+
+static void
+remmina_sftp_client_on_opendir(RemminaSFTPClient *client, gchar *dir, gpointer data)
+{
+ TRACE_CALL(__func__);
+ sftp_dir sftpdir;
+ sftp_attributes sftpattr;
+ GtkWidget *dialog;
+ gchar *newdir;
+ gchar *newdir_conv;
+ gchar *tmp;
+ gint type;
+
+ if (client->sftp == NULL) return;
+
+ if (!dir || dir[0] == '\0') {
+ newdir = g_strdup(".");
+ } else if (dir[0] == '/') {
+ newdir = g_strdup(dir);
+ } else {
+ tmp = remmina_ftp_client_get_dir(REMMINA_FTP_CLIENT(client));
+ if (tmp) {
+ newdir = remmina_public_combine_path(tmp, dir);
+ g_free(tmp);
+ } else {
+ newdir = g_strdup_printf("./%s", dir);
+ }
+ }
+
+ gchar *tlink = sftp_readlink (client->sftp->sftp_sess, newdir);
+ if (tlink) {
+ REMMINA_DEBUG ("%s is a link to %s", newdir, tlink);
+ newdir = g_strdup (tlink);
+ if (sftp_opendir (client->sftp->sftp_sess, newdir)) {
+ REMMINA_DEBUG ("%s is a link to a folder", tlink);
+ } else {
+ REMMINA_DEBUG ("%s is a link to a file", tlink);
+ return;
+ }
+ }
+ g_free(tlink);
+
+ tmp = remmina_ssh_unconvert(REMMINA_SSH(client->sftp), newdir);
+ newdir_conv = sftp_canonicalize_path(client->sftp->sftp_sess, tmp);
+ g_free(tmp);
+ g_free(newdir);
+ newdir = remmina_ssh_convert(REMMINA_SSH(client->sftp), newdir_conv);
+ if (!newdir) {
+ dialog = gtk_message_dialog_new(NULL,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Could not open the folder “%s”. %s"), dir,
+ ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ gtk_widget_show(dialog);
+ g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ g_free(newdir_conv);
+ return;
+ }
+
+ sftpdir = remmina_sftp_client_sftp_session_opendir(client, newdir_conv);
+ g_free(newdir_conv);
+ if (!sftpdir) {
+ g_free(newdir);
+ return;
+ }
+
+ remmina_ftp_client_clear_file_list(REMMINA_FTP_CLIENT(client));
+
+ while ((sftpattr = sftp_readdir(client->sftp->sftp_sess, sftpdir))) {
+ if (g_strcmp0(sftpattr->name, ".") != 0 &&
+ g_strcmp0(sftpattr->name, "..") != 0) {
+ GET_SFTPATTR_TYPE(sftpattr, type);
+
+ tmp = remmina_ssh_convert(REMMINA_SSH(client->sftp), sftpattr->name);
+ remmina_ftp_client_add_file(REMMINA_FTP_CLIENT(client),
+ REMMINA_FTP_FILE_COLUMN_TYPE, type,
+ REMMINA_FTP_FILE_COLUMN_NAME, tmp,
+ REMMINA_FTP_FILE_COLUMN_SIZE, (gfloat)sftpattr->size,
+ REMMINA_FTP_FILE_COLUMN_USER, sftpattr->owner,
+ REMMINA_FTP_FILE_COLUMN_GROUP, sftpattr->group,
+ REMMINA_FTP_FILE_COLUMN_PERMISSION, sftpattr->permissions,
+ REMMINA_FTP_FILE_COLUMN_MODIFIED, sftpattr->mtime,
+ -1);
+ g_free(tmp);
+ }
+ sftp_attributes_free(sftpattr);
+ }
+ remmina_sftp_client_sftp_session_closedir(client, sftpdir);
+
+ remmina_ftp_client_set_dir(REMMINA_FTP_CLIENT(client), newdir);
+ g_free(newdir);
+}
+
+static void
+remmina_sftp_client_on_newtask(RemminaSFTPClient *client, gpointer data)
+{
+ TRACE_CALL(__func__);
+ if (client->thread) return;
+
+ if (pthread_create(&client->thread, NULL, remmina_sftp_client_thread_main, client))
+ client->thread = 0;
+}
+
+static gboolean
+remmina_sftp_client_on_canceltask(RemminaSFTPClient *client, gint taskid, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *dialog;
+ gint ret;
+
+ if (client->taskid != taskid) return TRUE;
+
+ dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ _("Are you sure you want to cancel the file transfer in progress?"));
+ ret = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ if (ret == GTK_RESPONSE_YES) {
+ /* Make sure we are still handling the same task before we clear the flag */
+ if (client->taskid == taskid) client->taskid = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+remmina_sftp_client_on_deletefile(RemminaSFTPClient *client, gint type, gchar *name, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *dialog;
+ gint ret = 0;
+ gchar *tmp;
+
+ tmp = remmina_ssh_unconvert(REMMINA_SSH(client->sftp), name);
+ switch (type) {
+ case REMMINA_FTP_FILE_TYPE_DIR:
+ ret = sftp_rmdir(client->sftp->sftp_sess, tmp);
+ break;
+
+ case REMMINA_FTP_FILE_TYPE_FILE:
+ ret = sftp_unlink(client->sftp->sftp_sess, tmp);
+ break;
+ }
+ g_free(tmp);
+
+ if (ret != 0) {
+ dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Could not delete “%s”. %s"),
+ name, ssh_get_error(REMMINA_SSH(client->sftp)->session));
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+remmina_sftp_client_init(RemminaSFTPClient *client)
+{
+ TRACE_CALL(__func__);
+ client->sftp = NULL;
+ client->thread = 0;
+ client->taskid = 0;
+ client->thread_abort = FALSE;
+
+ /* Setup the internal signals */
+ g_signal_connect(G_OBJECT(client), "destroy",
+ G_CALLBACK(remmina_sftp_client_destroy), NULL);
+ g_signal_connect(G_OBJECT(client), "open-dir",
+ G_CALLBACK(remmina_sftp_client_on_opendir), NULL);
+ g_signal_connect(G_OBJECT(client), "new-task",
+ G_CALLBACK(remmina_sftp_client_on_newtask), NULL);
+ g_signal_connect(G_OBJECT(client), "cancel-task",
+ G_CALLBACK(remmina_sftp_client_on_canceltask), NULL);
+ g_signal_connect(G_OBJECT(client), "delete-file",
+ G_CALLBACK(remmina_sftp_client_on_deletefile), NULL);
+}
+
+static gboolean
+remmina_sftp_client_refresh(RemminaSFTPClient *client)
+{
+ TRACE_CALL(__func__);
+
+ GdkDisplay *display = gdk_display_get_default();
+
+ SET_CURSOR(gdk_cursor_new_for_display(display, GDK_WATCH));
+ gdk_display_flush(display);
+
+ remmina_sftp_client_on_opendir(client, ".", NULL);
+
+ SET_CURSOR(NULL);
+
+ return FALSE;
+}
+
+gint
+remmina_sftp_client_confirm_resume(RemminaSFTPClient *client, const gchar *path)
+{
+ TRACE_CALL(__func__);
+
+ GtkWidget *dialog;
+ gint response;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *widget;
+ const gchar *filename;
+
+ /* Always reply ACCEPT if overwrite_all was already set */
+ if (remmina_ftp_client_get_overwrite_status(REMMINA_FTP_CLIENT(client)))
+ return GTK_RESPONSE_ACCEPT;
+
+ /* Always reply APPLY if resume was already set */
+ if (remmina_ftp_client_get_resume_status(REMMINA_FTP_CLIENT(client)))
+ return GTK_RESPONSE_APPLY;
+
+ if (!remmina_masterthread_exec_is_main_thread()) {
+ /* Allow the execution of this function from a non main thread */
+ RemminaMTExecData *d;
+ gint retval;
+ d = (RemminaMTExecData *)g_malloc(sizeof(RemminaMTExecData));
+ d->func = FUNC_SFTP_CLIENT_CONFIRM_RESUME;
+ d->p.sftp_client_confirm_resume.client = client;
+ d->p.sftp_client_confirm_resume.path = path;
+ remmina_masterthread_exec_and_wait(d);
+ retval = d->p.sftp_client_confirm_resume.retval;
+ g_free(d);
+ return retval;
+ }
+
+ filename = strrchr(path, '/');
+ filename = filename ? filename + 1 : path;
+
+ dialog = gtk_dialog_new_with_buttons(_("The file exists already"),
+ GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("Resume"), GTK_RESPONSE_APPLY,
+ _("Overwrite"), GTK_RESPONSE_ACCEPT,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(dialog), 4);
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_widget_show(hbox);
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ hbox, TRUE, TRUE, 4);
+
+ widget = gtk_image_new_from_icon_name("dialog-question", GTK_ICON_SIZE_DIALOG);
+ gtk_widget_show(widget);
+ gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 4);
+
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
+ gtk_widget_show(vbox);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 4);
+
+ widget = gtk_label_new(_("The following file already exists in the target folder:"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 4);
+
+ widget = gtk_label_new(filename);
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 4);
+
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return response;
+}
+
+RemminaSFTPClient *
+remmina_sftp_client_new(void)
+{
+ TRACE_CALL(__func__);
+ return REMMINA_SFTP_CLIENT(g_object_new(REMMINA_TYPE_SFTP_CLIENT, NULL));
+}
+
+void
+remmina_sftp_client_open(RemminaSFTPClient *client, RemminaSFTP *sftp)
+{
+ TRACE_CALL(__func__);
+ client->sftp = sftp;
+
+ g_idle_add((GSourceFunc)remmina_sftp_client_refresh, client);
+}
+
+/*
+ * GtkWidget *
+ * remmina_sftp_client_new_init(RemminaSFTP *sftp)
+ * {
+ * TRACE_CALL(__func__);
+ * GdkDisplay *display;
+ * GtkWidget *client;
+ * GtkWidget *dialog;
+ *
+ * display = gdk_display_get_default();
+ * client = remmina_sftp_client_new();
+ *
+ *
+ * SET_CURSOR(gdk_cursor_new_for_display(display, GDK_WATCH));
+ * gdk_display_flush(display);
+ *
+ * if (!remmina_ssh_init_session(REMMINA_SSH(sftp)) ||
+ * remmina_ssh_auth(REMMINA_SSH(sftp), NULL, NULL, NULL) != REMMINA_SSH_AUTH_SUCCESS ||
+ * !remmina_sftp_open(sftp)) {
+ * dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(client)),
+ * GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ * (REMMINA_SSH(sftp))->error, NULL);
+ * gtk_dialog_run(GTK_DIALOG(dialog));
+ * gtk_widget_destroy(dialog);
+ * gtk_widget_destroy(client);
+ * return NULL;
+ * }
+ *
+ * SET_CURSOR(NULL);
+ *
+ * g_idle_add((GSourceFunc)remmina_sftp_client_refresh, client);
+ * return client;
+ * }
+ */
+
+#endif