/* * 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 #include #include #include #include #ifdef HAVE_FCNTL_H #include #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