Remmina - The GTK+ Remote Desktop Client  v1.4.2
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-2020 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 <gtk/gtk.h>
40 #include <string.h>
41 
42 #include "remmina_public.h"
43 #include "remmina_pref.h"
44 #include "remmina_string_array.h"
45 #include "remmina_plugin_manager.h"
46 #include "remmina_file_manager.h"
48 
49 static gchar *remminadir;
50 static gchar *cachedir;
51 
52 /* return first found data dir as per XDG specs.
53  * The returned string must be freed by the caller with g_free */
55 {
56  TRACE_CALL(__func__);
57  const gchar *dir = ".remmina";
58  int i;
59  /* From preferences, datadir_path */
60  remminadir = remmina_pref_get_value("datadir_path");
61  if (remminadir != NULL && strlen(remminadir) > 0)
62  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
63  return remminadir;
64  g_free(remminadir), remminadir = NULL;
65  /* Legacy ~/.remmina */
66  remminadir = g_build_path("/", g_get_home_dir(), dir, NULL);
67  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
68  return remminadir;
69  g_free(remminadir), remminadir = NULL;
70  /* ~/.local/share/remmina */
71  remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL);
72  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
73  return remminadir;
74  g_free(remminadir), remminadir = NULL;
75  /* /usr/local/share/remmina */
76  const gchar *const *dirs = g_get_system_data_dirs();
77  g_free(remminadir), remminadir = NULL;
78  for (i = 0; dirs[i] != NULL; ++i) {
79  remminadir = g_build_path("/", dirs[i], "remmina", NULL);
80  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR))
81  return remminadir;
82  g_free(remminadir), remminadir = NULL;
83  }
84  /* The last case we use the home ~/.local/share/remmina */
85  remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL);
86  return remminadir;
87 }
88 
90 static gboolean remmina_file_manager_do_copy(const char *src_path, const char *dst_path)
91 {
92  GFile *src = g_file_new_for_path(src_path), *dst = g_file_new_for_path(dst_path);
93  /* We don’t overwrite the target if it exists */
94  const gboolean ok = g_file_copy(src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);
95 
96  g_object_unref(dst);
97  g_object_unref(src);
98 
99  return ok;
100 }
101 
103 {
104  TRACE_CALL(__func__);
105  GDir *dir;
106  const gchar *legacy = ".remmina";
107  const gchar *filename;
108  int i;
109 
110  remminadir = g_build_path("/", g_get_user_data_dir(), "remmina", NULL);
111  /* Create the XDG_USER_DATA directory */
112  g_mkdir_with_parents(remminadir, 0750);
113  g_free(remminadir), remminadir = NULL;
114  /* Create the XDG_CACHE_HOME directory */
115  cachedir = g_build_path("/", g_get_user_cache_dir(), "remmina", NULL);
116  g_mkdir_with_parents(cachedir, 0750);
117  g_free(cachedir), cachedir = NULL;
118  /* Empty legacy ~/.remmina */
119  remminadir = g_build_path("/", g_get_home_dir(), legacy, NULL);
120  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) {
121  dir = g_dir_open(remminadir, 0, NULL);
122  while ((filename = g_dir_read_name(dir)) != NULL) {
124  g_build_path("/", remminadir, filename, NULL),
125  g_build_path("/", g_get_user_data_dir(),
126  "remmina", filename, NULL));
127  }
128  }
129 
130  /* XDG_DATA_DIRS, i.e. /usr/local/share/remmina */
131  const gchar *const *dirs = g_get_system_data_dirs();
132  g_free(remminadir), remminadir = NULL;
133  for (i = 0; dirs[i] != NULL; ++i) {
134  remminadir = g_build_path("/", dirs[i], "remmina", NULL);
135  if (g_file_test(remminadir, G_FILE_TEST_IS_DIR)) {
136  dir = g_dir_open(remminadir, 0, NULL);
137  while ((filename = g_dir_read_name(dir)) != NULL) {
139  g_build_path("/", remminadir, filename, NULL),
140  g_build_path("/", g_get_user_data_dir(),
141  "remmina", filename, NULL));
142  }
143  }
144  g_free(remminadir), remminadir = NULL;
145  }
146  /* At last we make sure we use XDG_USER_DATA */
147  if (remminadir != NULL)
148  g_free(remminadir), remminadir = NULL;
149 }
150 
151 gint remmina_file_manager_iterate(GFunc func, gpointer user_data)
152 {
153  TRACE_CALL(__func__);
154  gchar filename[MAX_PATH_LEN];
155  GDir *dir;
156  const gchar *name;
157  RemminaFile *remminafile;
158  gint items_count = 0;
159  gchar *remmina_data_dir;
160 
161  remmina_data_dir = remmina_file_get_datadir();
162  dir = g_dir_open(remmina_data_dir, 0, NULL);
163 
164  if (dir) {
165  while ((name = g_dir_read_name(dir)) != NULL) {
166  if (!g_str_has_suffix(name, ".remmina"))
167  continue;
168  g_snprintf(filename, MAX_PATH_LEN, "%s/%s",
169  remmina_data_dir, name);
170  remminafile = remmina_file_load(filename);
171  if (remminafile) {
172  (*func)(remminafile, user_data);
173  remmina_file_free(remminafile);
174  items_count++;
175  }
176  }
177  g_dir_close(dir);
178  }
179  g_free(remmina_data_dir);
180  return items_count;
181 }
182 
184 {
185  TRACE_CALL(__func__);
186  gchar filename[MAX_PATH_LEN];
187  GDir *dir;
188  const gchar *name;
189  RemminaFile *remminafile;
190  RemminaStringArray *array;
191  const gchar *group;
192  gchar *groups;
193  gchar *remmina_data_dir;
194 
195  remmina_data_dir = remmina_file_get_datadir();
196  array = remmina_string_array_new();
197 
198  dir = g_dir_open(remmina_data_dir, 0, NULL);
199 
200  if (dir == NULL)
201  return 0;
202  while ((name = g_dir_read_name(dir)) != NULL) {
203  if (!g_str_has_suffix(name, ".remmina"))
204  continue;
205  g_snprintf(filename, MAX_PATH_LEN, "%s/%s", remmina_data_dir, name);
206  remminafile = remmina_file_load(filename);
207  if (remminafile) {
208  group = remmina_file_get_string(remminafile, "group");
209  if (group && remmina_string_array_find(array, group) < 0)
210  remmina_string_array_add(array, group);
211  remmina_file_free(remminafile);
212  }
213  }
214  g_dir_close(dir);
216  groups = remmina_string_array_to_string(array);
218  g_free(remmina_data_dir);
219  return groups;
220 }
221 
222 static void remmina_file_manager_add_group(GNode *node, const gchar *group)
223 {
224  TRACE_CALL(__func__);
225  gint cmp;
226  gchar *p1;
227  gchar *p2;
228  GNode *child;
229  gboolean found;
230  RemminaGroupData *data;
231 
232  if (node == NULL)
233  return;
234 
235  if (group == NULL || group[0] == '\0')
236  return;
237 
238  p1 = g_strdup(group);
239  p2 = strchr(p1, '/');
240 
241  if (p2)
242  *p2++ = '\0';
243 
244  found = FALSE;
245 
246  for (child = g_node_first_child(node); child; child = g_node_next_sibling(child)) {
247  cmp = g_strcmp0(((RemminaGroupData *)child->data)->name, p1);
248 
249  if (cmp == 0) {
250  found = TRUE;
251  break;
252  }
253 
254  if (cmp > 0)
255  break;
256  }
257 
258  if (!found) {
259  data = g_new0(RemminaGroupData, 1);
260  data->name = p1;
261  if (node->data)
262  data->group = g_strdup_printf("%s/%s", ((RemminaGroupData *)node->data)->group, p1);
263  else
264  data->group = g_strdup(p1);
265  if (child)
266  child = g_node_insert_data_before(node, child, data);
267  else
268  child = g_node_append_data(node, data);
269  }
271 
272  if (found)
273  g_free(p1);
274 }
275 
277 {
278  TRACE_CALL(__func__);
279  gchar filename[MAX_PATH_LEN];
280  GDir *dir;
281  const gchar *name;
282  RemminaFile *remminafile;
283  const gchar *group;
284  GNode *root;
285 
286  root = g_node_new(NULL);
287 
288  dir = g_dir_open(remmina_file_get_datadir(), 0, NULL);
289 
290  if (dir == NULL)
291  return root;
292  while ((name = g_dir_read_name(dir)) != NULL) {
293  if (!g_str_has_suffix(name, ".remmina"))
294  continue;
295  g_snprintf(filename, MAX_PATH_LEN, "%s/%s", remmina_file_get_datadir(), name);
296  remminafile = remmina_file_load(filename);
297  if (remminafile) {
298  group = remmina_file_get_string(remminafile, "group");
299  remmina_file_manager_add_group(root, group);
300  remmina_file_free(remminafile);
301  }
302  }
303  g_dir_close(dir);
304  return root;
305 }
306 
308 {
309  TRACE_CALL(__func__);
310  RemminaGroupData *data;
311  GNode *child;
312 
313  if (!node)
314  return;
315  data = (RemminaGroupData *)node->data;
316  if (data) {
317  g_free(data->name);
318  g_free(data->group);
319  g_free(data);
320  node->data = NULL;
321  }
322  for (child = g_node_first_child(node); child; child = g_node_next_sibling(child))
324  g_node_unlink(node);
325 }
326 
328 {
329  TRACE_CALL(__func__);
330  RemminaFile *remminafile = NULL;
331  RemminaFilePlugin *plugin;
332  gchar *p;
333 
334  if ((p = strrchr(filename, '.')) != NULL && g_strcmp0(p + 1, "remmina") == 0) {
335  remminafile = remmina_file_load(filename);
336  } else {
338  if (plugin)
339  remminafile = plugin->import_func(filename);
340  }
341  return remminafile;
342 }
G_BEGIN_DECLS struct _RemminaGroupData RemminaGroupData
RemminaFile * remmina_file_load(const gchar *filename)
Definition: remmina_file.c:327
void remmina_file_free(RemminaFile *remminafile)
Definition: remmina_file.c:548
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:449
G_BEGIN_DECLS typedef GPtrArray RemminaStringArray
static gchar * remminadir
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
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)
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)
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)
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)
RemminaFile *(* import_func)(const gchar *from_file)
Definition: plugin.h:102
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)