diff options
Diffstat (limited to '')
-rw-r--r-- | src/remmina_file_manager.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/src/remmina_file_manager.c b/src/remmina_file_manager.c new file mode 100644 index 0000000..10a3022 --- /dev/null +++ b/src/remmina_file_manager.c @@ -0,0 +1,372 @@ +/* + * 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 + * Copyright (C) 2023-2024 Hiroyuki Tanaka, Sunil Bhat + * + * 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 <errno.h> +#include <gtk/gtk.h> +#include <string.h> + +#include "remmina_log.h" +#include "remmina_public.h" +#include "remmina_pref.h" +#include "remmina_string_array.h" +#include "remmina_plugin_manager.h" +#include "remmina_file_manager.h" +#include "remmina/remmina_trace_calls.h" + +static gchar *remminadir; +static gchar *cachedir; + +/** + * Return datadir_path from pref or first found data dir as per XDG specs. + * + * The returned string must be freed by the caller with g_free + */ +gchar *remmina_file_get_datadir(void) +{ + TRACE_CALL(__func__); + const gchar *dir = ".remmina"; + int i; + + /* From preferences, datadir_path */ + remminadir = remmina_pref_get_value("datadir_path"); + if (remminadir != NULL && strlen(remminadir) > 0) + if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) + return remminadir; + g_free(remminadir), remminadir = NULL; + /* Legacy ~/.remmina */ + remminadir = g_build_path("/", g_get_home_dir(), dir, NULL); + if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) + return remminadir; + g_free(remminadir), remminadir = NULL; + /* ~/.local/share/remmina */ + remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL); + if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) + return remminadir; + g_free(remminadir), remminadir = NULL; + /* /usr/local/share/remmina */ + const gchar *const *dirs = g_get_system_data_dirs(); + + g_free(remminadir), remminadir = NULL; + for (i = 0; dirs[i] != NULL; ++i) { + remminadir = g_build_path("/", dirs[i], "remmina", NULL); + if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) + return remminadir; + g_free(remminadir), remminadir = NULL; + } + /* The last case we use the home ~/.local/share/remmina */ + remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL); + return remminadir; +} + +/** @todo remmina_pref_file_do_copy and remmina_file_manager_do_copy to remmina_files_copy */ +static gboolean remmina_file_manager_do_copy(const char *src_path, const char *dst_path) +{ + GFile *src = g_file_new_for_path(src_path), *dst = g_file_new_for_path(dst_path); + /* We don’t overwrite the target if it exists */ + const gboolean ok = g_file_copy(src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL); + + g_object_unref(dst); + g_object_unref(src); + + return ok; +} + +/** + * It creates the Remmina data and cache folders + * + * If it finds the legacy ~/.remmina folder it copies the connection profiles into the new folder. + * + * If it finds default profiles in the XDG_DATA_DIRS it copies the profiles into the user data-folder. + */ +void remmina_file_manager_init(void) +{ + TRACE_CALL(__func__); + GDir *dir; + const gchar *legacy = ".remmina"; + const gchar *filename; + int i; + + /* Get and create the XDG_DATA_HOME directory */ + remminadir = remmina_pref_get_value("datadir_path"); + if (g_mkdir_with_parents(remminadir, 0750) == 0) { + REMMINA_DEBUG("Initialized the \"%s\" data folder", remminadir); + g_free(remminadir), remminadir = NULL; + } else { + g_free(remminadir), remminadir = NULL; + /* Get and create the XDG_DATA_HOME directory */ + remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL); + if (g_mkdir_with_parents(remminadir, 0750) == 0) + REMMINA_DEBUG("Initialized the \"%s\" data folder", remminadir); + else + REMMINA_CRITICAL("Cannot create the \"%s\" data folder", remminadir); + g_free(remminadir), remminadir = NULL; + } + /* Create the XDG_CACHE_HOME directory */ + cachedir = g_build_path("/", g_get_user_cache_dir(), "remmina", NULL); + g_mkdir_with_parents(cachedir, 0750); + g_free(cachedir), cachedir = NULL; + /* Empty legacy ~/.remmina */ + remminadir = g_build_path("/", g_get_home_dir(), legacy, NULL); + if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) { + dir = g_dir_open(remminadir, 0, NULL); + while ((filename = g_dir_read_name(dir)) != NULL) { + remmina_file_manager_do_copy( + g_build_path("/", remminadir, filename, NULL), + g_build_path("/", g_get_user_data_dir(), + "remmina", filename, NULL)); + } + g_dir_close(dir); + } + + /* XDG_DATA_DIRS, i.e. /usr/local/share/remmina */ + const gchar *const *dirs = g_get_system_data_dirs(); + + g_free(remminadir), remminadir = NULL; + for (i = 0; dirs[i] != NULL; ++i) { + remminadir = g_build_path("/", dirs[i], "remmina", NULL); + if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) { + dir = g_dir_open(remminadir, 0, NULL); + while ((filename = g_dir_read_name(dir)) != NULL) { + remmina_file_manager_do_copy( + g_build_path("/", remminadir, filename, NULL), + g_build_path("/", g_get_user_data_dir(), + "remmina", filename, NULL)); + } + } + g_free(remminadir), remminadir = NULL; + } + /* At last we make sure we use XDG_USER_DATA */ + if (remminadir != NULL) + g_free(remminadir), remminadir = NULL; +} + +gint remmina_file_manager_iterate(GFunc func, gpointer user_data) +{ + TRACE_CALL(__func__); + gchar filename[MAX_PATH_LEN]; + GDir *dir; + const gchar *name; + RemminaFile *remminafile; + gint items_count = 0; + gchar *remmina_data_dir; + + remmina_data_dir = remmina_file_get_datadir(); + dir = g_dir_open(remmina_data_dir, 0, NULL); + + if (dir) { + while ((name = g_dir_read_name(dir)) != NULL) { + if (!g_str_has_suffix(name, ".remmina")) + continue; + g_snprintf(filename, MAX_PATH_LEN, "%s/%s", + remmina_data_dir, name); + remminafile = remmina_file_load(filename); + if (remminafile) { + (*func)(remminafile, user_data); + remmina_file_free(remminafile); + items_count++; + } + } + g_dir_close(dir); + } + g_free(remmina_data_dir); + return items_count; +} + +gchar *remmina_file_manager_get_groups(void) +{ + TRACE_CALL(__func__); + gchar filename[MAX_PATH_LEN]; + GDir *dir; + const gchar *name; + RemminaFile *remminafile; + RemminaStringArray *array; + const gchar *group; + gchar *groups; + gchar *remmina_data_dir; + + remmina_data_dir = remmina_file_get_datadir(); + array = remmina_string_array_new(); + + dir = g_dir_open(remmina_data_dir, 0, NULL); + + if (dir == NULL) + return 0; + while ((name = g_dir_read_name(dir)) != NULL) { + if (!g_str_has_suffix(name, ".remmina")) + continue; + g_snprintf(filename, MAX_PATH_LEN, "%s/%s", remmina_data_dir, name); + remminafile = remmina_file_load(filename); + if (remminafile) { + group = remmina_file_get_string(remminafile, "group"); + if (group && remmina_string_array_find(array, group) < 0) + remmina_string_array_add(array, group); + remmina_file_free(remminafile); + } + } + g_dir_close(dir); + remmina_string_array_sort(array); + groups = remmina_string_array_to_string(array); + remmina_string_array_free(array); + g_free(remmina_data_dir); + return groups; +} + +static void remmina_file_manager_add_group(GNode *node, const gchar *group) +{ + TRACE_CALL(__func__); + gint cmp; + gchar *p1; + gchar *p2; + GNode *child; + gboolean found; + RemminaGroupData *data; + + if (node == NULL) + return; + + if (group == NULL || group[0] == '\0') + return; + + p1 = g_strdup(group); + p2 = strchr(p1, '/'); + + if (p2) + *p2++ = '\0'; + + found = FALSE; + + for (child = g_node_first_child(node); child; child = g_node_next_sibling(child)) { + cmp = g_strcmp0(((RemminaGroupData *)child->data)->name, p1); + + if (cmp == 0) { + found = TRUE; + break; + } + + if (cmp > 0) + break; + } + + if (!found) { + data = g_new0(RemminaGroupData, 1); + data->name = p1; + if (node->data) + data->group = g_strdup_printf("%s/%s", ((RemminaGroupData *)node->data)->group, p1); + else + data->group = g_strdup(p1); + if (child) + child = g_node_insert_data_before(node, child, data); + else + child = g_node_append_data(node, data); + } + remmina_file_manager_add_group(child, p2); + + if (found) + g_free(p1); +} + +GNode *remmina_file_manager_get_group_tree(void) +{ + TRACE_CALL(__func__); + gchar filename[MAX_PATH_LEN]; + GDir *dir; + g_autofree gchar *datadir = NULL; + const gchar *name; + RemminaFile *remminafile; + const gchar *group; + GNode *root; + + root = g_node_new(NULL); + + datadir = g_strdup(remmina_file_get_datadir()); + dir = g_dir_open(datadir, 0, NULL); + + if (dir == NULL) + return root; + while ((name = g_dir_read_name(dir)) != NULL) { + if (!g_str_has_suffix(name, ".remmina")) + continue; + g_snprintf(filename, MAX_PATH_LEN, "%s/%s", datadir, name); + remminafile = remmina_file_load(filename); + if (remminafile) { + group = remmina_file_get_string(remminafile, "group"); + remmina_file_manager_add_group(root, group); + remmina_file_free(remminafile); + } + } + g_dir_close(dir); + return root; +} + +void remmina_file_manager_free_group_tree(GNode *node) +{ + TRACE_CALL(__func__); + RemminaGroupData *data; + GNode *child; + + if (!node) + return; + data = (RemminaGroupData *)node->data; + if (data) { + g_free(data->name); + g_free(data->group); + g_free(data); + node->data = NULL; + } + for (child = g_node_first_child(node); child; child = g_node_next_sibling(child)) + remmina_file_manager_free_group_tree(child); + g_node_unlink(node); +} + +RemminaFile *remmina_file_manager_load_file(const gchar *filename) +{ + TRACE_CALL(__func__); + RemminaFile *remminafile = NULL; + RemminaFilePlugin *plugin; + gchar *p; + + if ((p = strrchr(filename, '.')) != NULL && g_strcmp0(p + 1, "remmina") == 0) { + remminafile = remmina_file_load(filename); + } else { + plugin = remmina_plugin_manager_get_import_file_handler(filename); + if (plugin) + remminafile = plugin->import_func(plugin, filename); + } + return remminafile; +} |