Remmina - The GTK+ Remote Desktop Client  v1.4.33
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_file_manager.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 
39 #include <errno.h>
40 #include <gtk/gtk.h>
41 #include <string.h>
42 
43 #include "remmina_log.h"
44 #include "remmina_public.h"
45 #include "remmina_pref.h"
46 #include "remmina_string_array.h"
47 #include "remmina_plugin_manager.h"
48 #include "remmina_file_manager.h"
50 
51 static gchar *remminadir;
52 static gchar *cachedir;
53 
60 {
61  TRACE_CALL(__func__);
62  const gchar *dir = ".remmina";
63  int i;
64 
65  /* From preferences, datadir_path */
66  remminadir = remmina_pref_get_value("datadir_path");
67  if (remminadir != NULL && strlen(remminadir) > 0)
68  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
69  return remminadir;
70  g_free(remminadir), remminadir = NULL;
71  /* Legacy ~/.remmina */
72  remminadir = g_build_path("/", g_get_home_dir(), dir, NULL);
73  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
74  return remminadir;
75  g_free(remminadir), remminadir = NULL;
76  /* ~/.local/share/remmina */
77  remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL);
78  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
79  return remminadir;
80  g_free(remminadir), remminadir = NULL;
81  /* /usr/local/share/remmina */
82  const gchar *const *dirs = g_get_system_data_dirs();
83 
84  g_free(remminadir), remminadir = NULL;
85  for (i = 0; dirs[i] != NULL; ++i) {
86  remminadir = g_build_path("/", dirs[i], "remmina", NULL);
87  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
88  return remminadir;
89  g_free(remminadir), remminadir = NULL;
90  }
91  /* The last case we use the home ~/.local/share/remmina */
92  remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL);
93  return remminadir;
94 }
95 
97 static gboolean remmina_file_manager_do_copy(const char *src_path, const char *dst_path)
98 {
99  GFile *src = g_file_new_for_path(src_path), *dst = g_file_new_for_path(dst_path);
100  /* We don’t overwrite the target if it exists */
101  const gboolean ok = g_file_copy(src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);
102 
103  g_object_unref(dst);
104  g_object_unref(src);
105 
106  return ok;
107 }
108 
117 {
118  TRACE_CALL(__func__);
119  GDir *dir;
120  const gchar *legacy = ".remmina";
121  const gchar *filename;
122  int i;
123 
124  /* Get and create the XDG_DATA_HOME directory */
125  remminadir = remmina_pref_get_value("datadir_path");
126  if (g_mkdir_with_parents(remminadir, 0750) == 0) {
127  REMMINA_DEBUG("Initialized the \"%s\" data folder", remminadir);
128  g_free(remminadir), remminadir = NULL;
129  } else {
130  g_free(remminadir), remminadir = NULL;
131  /* Get and create the XDG_DATA_HOME directory */
132  remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL);
133  if (g_mkdir_with_parents(remminadir, 0750) == 0)
134  REMMINA_DEBUG("Initialized the \"%s\" data folder", remminadir);
135  else
136  REMMINA_CRITICAL("Cannot create the \"%s\" data folder", remminadir);
137  g_free(remminadir), remminadir = NULL;
138  }
139  /* Create the XDG_CACHE_HOME directory */
140  cachedir = g_build_path("/", g_get_user_cache_dir(), "remmina", NULL);
141  g_mkdir_with_parents(cachedir, 0750);
142  g_free(cachedir), cachedir = NULL;
143  /* Empty legacy ~/.remmina */
144  remminadir = g_build_path("/", g_get_home_dir(), legacy, NULL);
145  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) {
146  dir = g_dir_open(remminadir, 0, NULL);
147  while ((filename = g_dir_read_name(dir)) != NULL) {
149  g_build_path("/", remminadir, filename, NULL),
150  g_build_path("/", g_get_user_data_dir(),
151  "remmina", filename, NULL));
152  }
153  g_dir_close(dir);
154  }
155 
156  /* XDG_DATA_DIRS, i.e. /usr/local/share/remmina */
157  const gchar *const *dirs = g_get_system_data_dirs();
158 
159  g_free(remminadir), remminadir = NULL;
160  for (i = 0; dirs[i] != NULL; ++i) {
161  remminadir = g_build_path("/", dirs[i], "remmina", NULL);
162  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) {
163  dir = g_dir_open(remminadir, 0, NULL);
164  while ((filename = g_dir_read_name(dir)) != NULL) {
166  g_build_path("/", remminadir, filename, NULL),
167  g_build_path("/", g_get_user_data_dir(),
168  "remmina", filename, NULL));
169  }
170  }
171  g_free(remminadir), remminadir = NULL;
172  }
173  /* At last we make sure we use XDG_USER_DATA */
174  if (remminadir != NULL)
175  g_free(remminadir), remminadir = NULL;
176 }
177 
178 gint remmina_file_manager_iterate(GFunc func, gpointer user_data)
179 {
180  TRACE_CALL(__func__);
181  gchar filename[MAX_PATH_LEN];
182  GDir *dir;
183  const gchar *name;
184  RemminaFile *remminafile;
185  gint items_count = 0;
186  gchar *remmina_data_dir;
187 
188  remmina_data_dir = remmina_file_get_datadir();
189  dir = g_dir_open(remmina_data_dir, 0, NULL);
190 
191  if (dir) {
192  while ((name = g_dir_read_name(dir)) != NULL) {
193  if (!g_str_has_suffix(name, ".remmina"))
194  continue;
195  g_snprintf(filename, MAX_PATH_LEN, "%s/%s",
196  remmina_data_dir, name);
197  remminafile = remmina_file_load(filename);
198  if (remminafile) {
199  (*func)(remminafile, user_data);
200  remmina_file_free(remminafile);
201  items_count++;
202  }
203  }
204  g_dir_close(dir);
205  }
206  g_free(remmina_data_dir);
207  return items_count;
208 }
209 
211 {
212  TRACE_CALL(__func__);
213  gchar filename[MAX_PATH_LEN];
214  GDir *dir;
215  const gchar *name;
216  RemminaFile *remminafile;
217  RemminaStringArray *array;
218  const gchar *group;
219  gchar *groups;
220  gchar *remmina_data_dir;
221 
222  remmina_data_dir = remmina_file_get_datadir();
223  array = remmina_string_array_new();
224 
225  dir = g_dir_open(remmina_data_dir, 0, NULL);
226 
227  if (dir == NULL)
228  return 0;
229  while ((name = g_dir_read_name(dir)) != NULL) {
230  if (!g_str_has_suffix(name, ".remmina"))
231  continue;
232  g_snprintf(filename, MAX_PATH_LEN, "%s/%s", remmina_data_dir, name);
233  remminafile = remmina_file_load(filename);
234  if (remminafile) {
235  group = remmina_file_get_string(remminafile, "group");
236  if (group && remmina_string_array_find(array, group) < 0)
237  remmina_string_array_add(array, group);
238  remmina_file_free(remminafile);
239  }
240  }
241  g_dir_close(dir);
243  groups = remmina_string_array_to_string(array);
245  g_free(remmina_data_dir);
246  return groups;
247 }
248 
249 static void remmina_file_manager_add_group(GNode *node, const gchar *group)
250 {
251  TRACE_CALL(__func__);
252  gint cmp;
253  gchar *p1;
254  gchar *p2;
255  GNode *child;
256  gboolean found;
257  RemminaGroupData *data;
258 
259  if (node == NULL)
260  return;
261 
262  if (group == NULL || group[0] == '\0')
263  return;
264 
265  p1 = g_strdup(group);
266  p2 = strchr(p1, '/');
267 
268  if (p2)
269  *p2++ = '\0';
270 
271  found = FALSE;
272 
273  for (child = g_node_first_child(node); child; child = g_node_next_sibling(child)) {
274  cmp = g_strcmp0(((RemminaGroupData *)child->data)->name, p1);
275 
276  if (cmp == 0) {
277  found = TRUE;
278  break;
279  }
280 
281  if (cmp > 0)
282  break;
283  }
284 
285  if (!found) {
286  data = g_new0(RemminaGroupData, 1);
287  data->name = p1;
288  if (node->data)
289  data->group = g_strdup_printf("%s/%s", ((RemminaGroupData *)node->data)->group, p1);
290  else
291  data->group = g_strdup(p1);
292  if (child)
293  child = g_node_insert_data_before(node, child, data);
294  else
295  child = g_node_append_data(node, data);
296  }
298 
299  if (found)
300  g_free(p1);
301 }
302 
304 {
305  TRACE_CALL(__func__);
306  gchar filename[MAX_PATH_LEN];
307  GDir *dir;
308  g_autofree gchar *datadir = NULL;
309  const gchar *name;
310  RemminaFile *remminafile;
311  const gchar *group;
312  GNode *root;
313 
314  root = g_node_new(NULL);
315 
316  datadir = g_strdup(remmina_file_get_datadir());
317  dir = g_dir_open(datadir, 0, NULL);
318 
319  if (dir == NULL)
320  return root;
321  while ((name = g_dir_read_name(dir)) != NULL) {
322  if (!g_str_has_suffix(name, ".remmina"))
323  continue;
324  g_snprintf(filename, MAX_PATH_LEN, "%s/%s", datadir, name);
325  remminafile = remmina_file_load(filename);
326  if (remminafile) {
327  group = remmina_file_get_string(remminafile, "group");
328  remmina_file_manager_add_group(root, group);
329  remmina_file_free(remminafile);
330  }
331  }
332  g_dir_close(dir);
333  return root;
334 }
335 
337 {
338  TRACE_CALL(__func__);
339  RemminaGroupData *data;
340  GNode *child;
341 
342  if (!node)
343  return;
344  data = (RemminaGroupData *)node->data;
345  if (data) {
346  g_free(data->name);
347  g_free(data->group);
348  g_free(data);
349  node->data = NULL;
350  }
351  for (child = g_node_first_child(node); child; child = g_node_next_sibling(child))
353  g_node_unlink(node);
354 }
355 
357 {
358  TRACE_CALL(__func__);
359  RemminaFile *remminafile = NULL;
360  RemminaFilePlugin *plugin;
361  gchar *p;
362 
363  if ((p = strrchr(filename, '.')) != NULL && g_strcmp0(p + 1, "remmina") == 0) {
364  remminafile = remmina_file_load(filename);
365  } else {
367  if (plugin)
368  remminafile = plugin->import_func(plugin, filename);
369  }
370  return remminafile;
371 }
G_BEGIN_DECLS struct _RemminaGroupData RemminaGroupData
RemminaFile * remmina_file_load(const gchar *filename)
Definition: remmina_file.c:350
void remmina_file_free(RemminaFile *remminafile)
Definition: remmina_file.c:709
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:516
G_BEGIN_DECLS typedef GPtrArray RemminaStringArray
static gchar * remminadir
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
RemminaFile *(* import_func)(struct _RemminaFilePlugin *instance, const gchar *from_file)
Definition: plugin.h:108
void remmina_file_manager_free_group_tree(GNode *node)
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
gint remmina_string_array_find(RemminaStringArray *array, const gchar *str)
gchar * remmina_string_array_to_string(RemminaStringArray *array)
node_t * node
Definition: remmina_ssh.c:142
static gboolean remmina_file_manager_do_copy(const char *src_path, const char *dst_path)
gchar * remmina_pref_get_value(const gchar *key)
void remmina_file_manager_init(void)
It creates the Remmina data and cache folders.
void remmina_string_array_sort(RemminaStringArray *array)
static void remmina_file_manager_add_group(GNode *node, const gchar *group)
void remmina_string_array_free(RemminaStringArray *array)
gchar * remmina_file_get_datadir(void)
Return datadir_path from pref or first found data dir as per XDG specs.
RemminaStringArray * remmina_string_array_new(void)
static gchar * cachedir
gchar * remmina_file_manager_get_groups(void)
RemminaFile * remmina_file_manager_load_file(const gchar *filename)
void remmina_string_array_add(RemminaStringArray *array, const gchar *str)
GNode * remmina_file_manager_get_group_tree(void)
gint remmina_file_manager_iterate(GFunc func, gpointer user_data)