1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /* caja-file-utilities.c - implementation of file manipulation routines.
4 
5    Copyright (C) 1999, 2000, 2001 Eazel, Inc.
6 
7    The Mate Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    The Mate Library 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 GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public
18    License along with the Mate Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21 
22    Authors: John Sullivan <sullivan@eazel.com>
23 */
24 
25 #include <config.h>
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <glib/gstdio.h>
29 #include <gio/gio.h>
30 #include <stdlib.h>
31 
32 #include <eel/eel-glib-extensions.h>
33 #include <eel/eel-stock-dialogs.h>
34 #include <eel/eel-string.h>
35 #include <eel/eel-debug.h>
36 
37 #include "caja-file-utilities.h"
38 #include "caja-global-preferences.h"
39 #include "caja-lib-self-check-functions.h"
40 #include "caja-metadata.h"
41 #include "caja-file.h"
42 #include "caja-file-operations.h"
43 #include "caja-search-directory.h"
44 #include "caja-signaller.h"
45 
46 #define DEFAULT_CAJA_DIRECTORY_MODE (0755)
47 
48 #define DESKTOP_DIRECTORY_NAME "Desktop"
49 #define DEFAULT_DESKTOP_DIRECTORY_MODE (0755)
50 
51 static void update_xdg_dir_cache (void);
52 static void schedule_user_dirs_changed (void);
53 static void desktop_dir_changed (void);
54 
55 char *
caja_compute_title_for_location(GFile * location)56 caja_compute_title_for_location (GFile *location)
57 {
58     char *title;
59 
60     /* TODO-gio: This doesn't really work all that great if the
61        info about the file isn't known atm... */
62 
63     title = NULL;
64     if (location)
65     {
66         CajaFile *file;
67 
68         file = caja_file_get (location);
69         title = caja_file_get_description (file);
70         if (title == NULL)
71         {
72             title = caja_file_get_display_name (file);
73         }
74         caja_file_unref (file);
75     }
76 
77     if (title == NULL)
78     {
79         title = g_strdup ("");
80     }
81 
82     return title;
83 }
84 
85 
86 /**
87  * caja_get_user_directory:
88  *
89  * Get the path for the directory containing caja settings.
90  *
91  * Return value: the directory path.
92  **/
caja_get_user_directory(void)93 char* caja_get_user_directory(void)
94 {
95 	/* FIXME bugzilla.gnome.org 41286:
96 	 * How should we handle the case where this mkdir fails?
97 	 * Note that caja_application_startup will refuse to launch if this
98 	 * directory doesn't get created, so that case is OK. But the directory
99 	 * could be deleted after Caja was launched, and perhaps
100 	 * there is some bad side-effect of not handling that case.
101 	 * <<<
102 	 * Si alguien tiene tiempo, puede enviar este codigo a Nautilus.
103 	 * Obviamente, con los comentarios traducidos al Inglés.
104 	 */
105 	char* user_directory = g_build_filename(g_get_user_config_dir(), "caja", NULL);
106 	/* Se necesita que esta dirección sea una carpeta, con los permisos
107 	 * DEFAULT_CAJA_DIRECTORY_MODE. Pero si es un archivo, el programa intentará
108 	 * eliminar el archivo silenciosamente. */
109 	if (g_file_test(user_directory, G_FILE_TEST_IS_DIR) == FALSE ||
110 		g_access(user_directory, DEFAULT_CAJA_DIRECTORY_MODE) == -1)
111 	{
112 		/* Se puede obtener un enlace simbolico a una carpeta */
113 		if (g_file_test(user_directory, G_FILE_TEST_IS_SYMLINK) == TRUE)
114 		{
115 			/* intentaremos saber si el enlace es una carpeta, y tiene los
116 			 * permisos adecuados */
117 			char* link = g_file_read_link(user_directory, NULL);
118 
119 			if (link)
120 			{
121 				/* Si el enlace no es un directorio, o si falla al hacer chmod,
122 				 * se borra el enlace y se crea la carpeta */
123 				if (g_file_test(link, G_FILE_TEST_IS_DIR) != TRUE ||
124 					g_chmod(link, DEFAULT_CAJA_DIRECTORY_MODE) != 0)
125 				{
126 					/* podemos borrar el enlace y crear la carpeta */
127 					g_unlink(user_directory);
128 					g_mkdir(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
129 				}
130 
131 				g_free(link);
132 			}
133 		}
134 		else if (g_file_test(user_directory, G_FILE_TEST_IS_DIR) == TRUE)
135 		{
136 			g_chmod(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
137 		}
138 		else if (g_file_test(user_directory, G_FILE_TEST_EXISTS) == TRUE)
139 		{
140 			/* podemos borrar el enlace y crear la carpeta */
141 			g_unlink(user_directory);
142 			g_mkdir(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
143 		}
144 		else
145 		{
146 			/* Si no existe ningun archivo, se crea la carpeta */
147 			g_mkdir_with_parents(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
148 		}
149 
150 		/* Faltan permisos */
151 		if (g_chmod(user_directory, DEFAULT_CAJA_DIRECTORY_MODE) != 0)
152 		{
153 			GtkWidget* dialog = gtk_message_dialog_new(
154 				NULL,
155 				GTK_DIALOG_DESTROY_WITH_PARENT,
156 				GTK_MESSAGE_ERROR,
157 				GTK_BUTTONS_CLOSE,
158 				"The path for the directory containing caja settings need read and write permissions: %s",
159 				user_directory);
160 
161 			gtk_dialog_run(GTK_DIALOG(dialog));
162 			gtk_widget_destroy(dialog);
163 
164 			exit(0);
165 		}
166 	}
167 
168 	return user_directory;
169 }
170 
171 /**
172  * caja_get_accel_map_file:
173  *
174  * Get the path for the filename containing caja accelerator map.
175  * The filename need not exist.
176  *
177  * Return value: the filename path
178  **/
caja_get_accel_map_file(void)179 char* caja_get_accel_map_file(void)
180 {
181 	return g_build_filename (g_get_user_config_dir (), "caja", "accels", NULL);
182 }
183 
184 typedef struct {
185 	char*type;
186 	char*path;
187 	CajaFile* file;
188 } XdgDirEntry;
189 
190 
191 static XdgDirEntry *
parse_xdg_dirs(const char * config_file)192 parse_xdg_dirs (const char *config_file)
193 {
194     GArray *array;
195     char *config_file_free = NULL;
196     XdgDirEntry dir;
197     char *data;
198     char **lines;
199     char *p;
200     char *unescaped;
201     gboolean relative;
202 
203     array = g_array_new (TRUE, TRUE, sizeof (XdgDirEntry));
204 
205     if (config_file == NULL)
206     {
207         config_file_free = g_build_filename (g_get_user_config_dir (),
208                                              "user-dirs.dirs", NULL);
209         config_file = (const char *)config_file_free;
210     }
211 
212     if (g_file_get_contents (config_file, &data, NULL, NULL))
213     {
214         int i;
215 
216         lines = g_strsplit (data, "\n", 0);
217         g_free (data);
218         for (i = 0; lines[i] != NULL; i++)
219         {
220             char *d;
221             char *type_start, *type_end;
222             char *value;
223 
224             p = lines[i];
225             while (g_ascii_isspace (*p))
226                 p++;
227 
228             if (*p == '#')
229                 continue;
230 
231             value = strchr (p, '=');
232             if (value == NULL)
233                 continue;
234             *value++ = 0;
235 
236             g_strchug (g_strchomp (p));
237             if (!g_str_has_prefix (p, "XDG_"))
238                 continue;
239             if (!g_str_has_suffix (p, "_DIR"))
240                 continue;
241             type_start = p + 4;
242             type_end = p + strlen (p) - 4;
243 
244             while (g_ascii_isspace (*value))
245                 value++;
246 
247             if (*value != '"')
248                 continue;
249             value++;
250 
251             relative = FALSE;
252             if (g_str_has_prefix (value, "$HOME"))
253             {
254                 relative = TRUE;
255                 value += 5;
256                 while (*value == '/')
257                     value++;
258             }
259             else if (*value != '/')
260                 continue;
261 
262             d = unescaped = g_malloc (strlen (value) + 1);
263             while (*value && *value != '"')
264             {
265                 if ((*value == '\\') && (*(value + 1) != 0))
266                     value++;
267                 *d++ = *value++;
268             }
269             *d = 0;
270 
271             *type_end = 0;
272             dir.type = g_strdup (type_start);
273             if (relative)
274             {
275                 dir.path = g_build_filename (g_get_home_dir (), unescaped, NULL);
276                 g_free (unescaped);
277             }
278             else
279                 dir.path = unescaped;
280 
281             g_array_append_val (array, dir);
282         }
283 
284         g_strfreev (lines);
285     }
286 
287     g_free (config_file_free);
288 
289     return (XdgDirEntry *) (gpointer) g_array_free (array, FALSE);
290 }
291 
292 static XdgDirEntry *cached_xdg_dirs = NULL;
293 static GFileMonitor *cached_xdg_dirs_monitor = NULL;
294 
295 static void
xdg_dir_changed(CajaFile * file,XdgDirEntry * dir)296 xdg_dir_changed (CajaFile *file,
297                  XdgDirEntry *dir)
298 {
299     GFile *location, *dir_location;
300     char *path;
301 
302     location = caja_file_get_location (file);
303     dir_location = g_file_new_for_path (dir->path);
304     if (!g_file_equal (location, dir_location))
305     {
306         path = g_file_get_path (location);
307 
308         if (path)
309         {
310             char *argv[5];
311             int i;
312 
313             g_free (dir->path);
314             dir->path = path;
315 
316             i = 0;
317             argv[i++] = "xdg-user-dirs-update";
318             argv[i++] = "--set";
319             argv[i++] = dir->type;
320             argv[i++] = dir->path;
321             argv[i++] = NULL;
322 
323             /* We do this sync, to avoid possible race-conditions
324                if multiple dirs change at the same time. Its
325                blocking the main thread, but these updates should
326                be very rare and very fast. */
327             g_spawn_sync (NULL,
328                           argv, NULL,
329                           G_SPAWN_SEARCH_PATH |
330                           G_SPAWN_STDOUT_TO_DEV_NULL |
331                           G_SPAWN_STDERR_TO_DEV_NULL,
332                           NULL, NULL,
333                           NULL, NULL, NULL, NULL);
334             g_reload_user_special_dirs_cache ();
335             schedule_user_dirs_changed ();
336             desktop_dir_changed ();
337             /* Icon might have changed */
338             caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_INFO);
339         }
340     }
341     g_object_unref (location);
342     g_object_unref (dir_location);
343 }
344 
345 static void
xdg_dir_cache_changed_cb(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type)346 xdg_dir_cache_changed_cb (GFileMonitor  *monitor,
347                           GFile *file,
348                           GFile *other_file,
349                           GFileMonitorEvent event_type)
350 {
351     if (event_type == G_FILE_MONITOR_EVENT_CHANGED ||
352             event_type == G_FILE_MONITOR_EVENT_CREATED)
353     {
354         update_xdg_dir_cache ();
355     }
356 }
357 
358 static int user_dirs_changed_tag = 0;
359 
360 static gboolean
emit_user_dirs_changed_idle(gpointer data)361 emit_user_dirs_changed_idle (gpointer data)
362 {
363     g_signal_emit_by_name (caja_signaller_get_current (),
364                            "user_dirs_changed");
365     user_dirs_changed_tag = 0;
366     return FALSE;
367 }
368 
369 static void
schedule_user_dirs_changed(void)370 schedule_user_dirs_changed (void)
371 {
372     if (user_dirs_changed_tag == 0)
373     {
374         user_dirs_changed_tag = g_idle_add (emit_user_dirs_changed_idle, NULL);
375     }
376 }
377 
378 static void
unschedule_user_dirs_changed(void)379 unschedule_user_dirs_changed (void)
380 {
381     if (user_dirs_changed_tag != 0)
382     {
383         g_source_remove (user_dirs_changed_tag);
384         user_dirs_changed_tag = 0;
385     }
386 }
387 
388 static void
free_xdg_dir_cache(void)389 free_xdg_dir_cache (void)
390 {
391     if (cached_xdg_dirs != NULL)
392     {
393         int i;
394 
395         for (i = 0; cached_xdg_dirs[i].type != NULL; i++)
396         {
397             if (cached_xdg_dirs[i].file != NULL)
398             {
399                 caja_file_monitor_remove (cached_xdg_dirs[i].file,
400                                           &cached_xdg_dirs[i]);
401                 g_signal_handlers_disconnect_by_func (cached_xdg_dirs[i].file,
402                                                       G_CALLBACK (xdg_dir_changed),
403                                                       &cached_xdg_dirs[i]);
404                 caja_file_unref (cached_xdg_dirs[i].file);
405             }
406             g_free (cached_xdg_dirs[i].type);
407             g_free (cached_xdg_dirs[i].path);
408         }
409         g_free (cached_xdg_dirs);
410     }
411 }
412 
413 static void
destroy_xdg_dir_cache(void)414 destroy_xdg_dir_cache (void)
415 {
416     free_xdg_dir_cache ();
417     unschedule_user_dirs_changed ();
418     desktop_dir_changed ();
419 
420     if (cached_xdg_dirs_monitor != NULL)
421     {
422         g_object_unref  (cached_xdg_dirs_monitor);
423         cached_xdg_dirs_monitor = NULL;
424     }
425 }
426 
427 static void
update_xdg_dir_cache(void)428 update_xdg_dir_cache (void)
429 {
430     char *uri;
431     int i;
432 
433     free_xdg_dir_cache ();
434     g_reload_user_special_dirs_cache ();
435     schedule_user_dirs_changed ();
436     desktop_dir_changed ();
437 
438     cached_xdg_dirs = parse_xdg_dirs (NULL);
439 
440     for (i = 0 ; cached_xdg_dirs[i].type != NULL; i++)
441     {
442         cached_xdg_dirs[i].file = NULL;
443         if (strcmp (cached_xdg_dirs[i].path, g_get_home_dir ()) != 0)
444         {
445             uri = g_filename_to_uri (cached_xdg_dirs[i].path, NULL, NULL);
446             cached_xdg_dirs[i].file = caja_file_get_by_uri (uri);
447             caja_file_monitor_add (cached_xdg_dirs[i].file,
448                                    &cached_xdg_dirs[i],
449                                    CAJA_FILE_ATTRIBUTE_INFO);
450             g_signal_connect (cached_xdg_dirs[i].file,
451                               "changed", G_CALLBACK (xdg_dir_changed), &cached_xdg_dirs[i]);
452             g_free (uri);
453         }
454     }
455 
456     if (cached_xdg_dirs_monitor == NULL)
457     {
458         GFile *file;
459         char *config_file;
460 
461         config_file = g_build_filename (g_get_user_config_dir (),
462                                         "user-dirs.dirs", NULL);
463         file = g_file_new_for_path (config_file);
464         cached_xdg_dirs_monitor = g_file_monitor_file (file, 0, NULL, NULL);
465         g_signal_connect (cached_xdg_dirs_monitor, "changed",
466                           G_CALLBACK (xdg_dir_cache_changed_cb), NULL);
467         g_object_unref (file);
468         g_free (config_file);
469 
470         eel_debug_call_at_shutdown (destroy_xdg_dir_cache);
471     }
472 }
473 
474 char *
caja_get_xdg_dir(const char * type)475 caja_get_xdg_dir (const char *type)
476 {
477     int i;
478 
479     if (cached_xdg_dirs == NULL)
480     {
481         update_xdg_dir_cache ();
482     }
483 
484     for (i = 0 ; cached_xdg_dirs != NULL && cached_xdg_dirs[i].type != NULL; i++)
485     {
486         if (strcmp (cached_xdg_dirs[i].type, type) == 0)
487         {
488             return g_strdup (cached_xdg_dirs[i].path);
489         }
490     }
491     if (strcmp ("DESKTOP", type) == 0)
492     {
493         return g_build_filename (g_get_home_dir (), DESKTOP_DIRECTORY_NAME, NULL);
494     }
495     if (strcmp ("TEMPLATES", type) == 0)
496     {
497         return g_build_filename (g_get_home_dir (), "Templates", NULL);
498     }
499 
500     return g_strdup (g_get_home_dir ());
501 }
502 
503 static char *
get_desktop_path(void)504 get_desktop_path (void)
505 {
506     if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR))
507     {
508         return g_strdup (g_get_home_dir());
509     }
510     else
511     {
512         return caja_get_xdg_dir ("DESKTOP");
513     }
514 }
515 
516 /**
517  * caja_get_desktop_directory:
518  *
519  * Get the path for the directory containing files on the desktop.
520  *
521  * Return value: the directory path.
522  **/
523 char *
caja_get_desktop_directory(void)524 caja_get_desktop_directory (void)
525 {
526     char *desktop_directory;
527 
528     desktop_directory = get_desktop_path ();
529 
530     /* Don't try to create a home directory */
531     if (!g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR))
532     {
533         if (!g_file_test (desktop_directory, G_FILE_TEST_EXISTS))
534         {
535             g_mkdir (desktop_directory, DEFAULT_DESKTOP_DIRECTORY_MODE);
536             /* FIXME bugzilla.gnome.org 41286:
537              * How should we handle the case where this mkdir fails?
538              * Note that caja_application_startup will refuse to launch if this
539              * directory doesn't get created, so that case is OK. But the directory
540              * could be deleted after Caja was launched, and perhaps
541              * there is some bad side-effect of not handling that case.
542              */
543         }
544     }
545 
546     return desktop_directory;
547 }
548 
549 GFile *
caja_get_desktop_location(void)550 caja_get_desktop_location (void)
551 {
552     char *desktop_directory;
553     GFile *res;
554 
555     desktop_directory = get_desktop_path ();
556 
557     res = g_file_new_for_path (desktop_directory);
558     g_free (desktop_directory);
559     return res;
560 }
561 
562 
563 /**
564  * caja_get_desktop_directory_uri:
565  *
566  * Get the uri for the directory containing files on the desktop.
567  *
568  * Return value: the directory path.
569  **/
570 char *
caja_get_desktop_directory_uri(void)571 caja_get_desktop_directory_uri (void)
572 {
573     char *desktop_path;
574     char *desktop_uri;
575 
576     desktop_path = caja_get_desktop_directory ();
577     desktop_uri = g_filename_to_uri (desktop_path, NULL, NULL);
578     g_free (desktop_path);
579 
580     return desktop_uri;
581 }
582 
583 char *
caja_get_home_directory_uri(void)584 caja_get_home_directory_uri (void)
585 {
586     return  g_filename_to_uri (g_get_home_dir (), NULL, NULL);
587 }
588 
589 
590 gboolean
caja_should_use_templates_directory(void)591 caja_should_use_templates_directory (void)
592 {
593     char *dir;
594     gboolean res;
595 
596     dir = caja_get_xdg_dir ("TEMPLATES");
597     res = strcmp (dir, g_get_home_dir ()) != 0;
598     g_free (dir);
599     return res;
600 }
601 
602 char *
caja_get_templates_directory(void)603 caja_get_templates_directory (void)
604 {
605     return caja_get_xdg_dir ("TEMPLATES");
606 }
607 
608 void
caja_create_templates_directory(void)609 caja_create_templates_directory (void)
610 {
611     char *dir;
612 
613     dir = caja_get_templates_directory ();
614     if (!g_file_test (dir, G_FILE_TEST_EXISTS))
615     {
616         g_mkdir (dir, DEFAULT_CAJA_DIRECTORY_MODE);
617     }
618     g_free (dir);
619 }
620 
621 char *
caja_get_templates_directory_uri(void)622 caja_get_templates_directory_uri (void)
623 {
624     char *directory, *uri;
625 
626     directory = caja_get_templates_directory ();
627     uri = g_filename_to_uri (directory, NULL, NULL);
628     g_free (directory);
629     return uri;
630 }
631 
632 /* These need to be reset to NULL when desktop_is_home_dir changes */
633 static GFile *desktop_dir = NULL;
634 static GFile *desktop_dir_dir = NULL;
635 static char *desktop_dir_filename = NULL;
636 static gboolean desktop_dir_changed_callback_installed = FALSE;
637 
638 
639 static void
desktop_dir_changed(void)640 desktop_dir_changed (void)
641 {
642     if (desktop_dir)
643     {
644         g_object_unref (desktop_dir);
645     }
646     if (desktop_dir_dir)
647     {
648         g_object_unref (desktop_dir_dir);
649     }
650     g_free (desktop_dir_filename);
651     desktop_dir = NULL;
652     desktop_dir_dir = NULL;
653     desktop_dir_filename = NULL;
654 }
655 
656 static void
desktop_dir_changed_callback(gpointer callback_data)657 desktop_dir_changed_callback (gpointer callback_data)
658 {
659     desktop_dir_changed ();
660 }
661 
662 static void
update_desktop_dir(void)663 update_desktop_dir (void)
664 {
665     char *path;
666     char *dirname;
667 
668     path = get_desktop_path ();
669     desktop_dir = g_file_new_for_path (path);
670 
671     dirname = g_path_get_dirname (path);
672     desktop_dir_dir = g_file_new_for_path (dirname);
673     g_free (dirname);
674     desktop_dir_filename = g_path_get_basename (path);
675     g_free (path);
676 }
677 
678 gboolean
caja_is_home_directory_file(GFile * dir,const char * filename)679 caja_is_home_directory_file (GFile *dir,
680                              const char *filename)
681 {
682     static GFile *home_dir_dir = NULL;
683     static char *home_dir_filename = NULL;
684 
685     if (home_dir_dir == NULL)
686     {
687         char *dirname;
688 
689         dirname = g_path_get_dirname (g_get_home_dir ());
690         home_dir_dir = g_file_new_for_path (dirname);
691         g_free (dirname);
692         home_dir_filename = g_path_get_basename (g_get_home_dir ());
693     }
694 
695     return (g_file_equal (dir, home_dir_dir) &&
696             strcmp (filename, home_dir_filename) == 0);
697 }
698 
699 gboolean
caja_is_home_directory(GFile * dir)700 caja_is_home_directory (GFile *dir)
701 {
702     static GFile *home_dir = NULL;
703 
704     if (home_dir == NULL)
705     {
706         home_dir = g_file_new_for_path (g_get_home_dir ());
707     }
708 
709     return g_file_equal (dir, home_dir);
710 }
711 
712 gboolean
caja_is_root_directory(GFile * dir)713 caja_is_root_directory (GFile *dir)
714 {
715     static GFile *root_dir = NULL;
716 
717     if (root_dir == NULL)
718     {
719         root_dir = g_file_new_for_path ("/");
720     }
721 
722     return g_file_equal (dir, root_dir);
723 }
724 
725 
726 gboolean
caja_is_desktop_directory_file(GFile * dir,const char * file)727 caja_is_desktop_directory_file (GFile *dir,
728                                 const char *file)
729 {
730 
731     if (!desktop_dir_changed_callback_installed)
732     {
733         g_signal_connect_swapped (caja_preferences, "changed::" CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR,
734                                   G_CALLBACK(desktop_dir_changed_callback),
735                                   NULL);
736         desktop_dir_changed_callback_installed = TRUE;
737     }
738 
739     if (desktop_dir == NULL)
740     {
741         update_desktop_dir ();
742     }
743 
744     return (g_file_equal (dir, desktop_dir_dir) &&
745             strcmp (file, desktop_dir_filename) == 0);
746 }
747 
748 gboolean
caja_is_desktop_directory(GFile * dir)749 caja_is_desktop_directory (GFile *dir)
750 {
751 
752     if (!desktop_dir_changed_callback_installed)
753     {
754         g_signal_connect_swapped (caja_preferences, "changed::" CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR,
755                                   G_CALLBACK(desktop_dir_changed_callback),
756                                   NULL);
757         desktop_dir_changed_callback_installed = TRUE;
758     }
759 
760     if (desktop_dir == NULL)
761     {
762         update_desktop_dir ();
763     }
764 
765     return g_file_equal (dir, desktop_dir);
766 }
767 
768 GMount *
caja_get_mounted_mount_for_root(GFile * location)769 caja_get_mounted_mount_for_root (GFile *location)
770 {
771 	GVolumeMonitor *volume_monitor;
772 	GList *mounts;
773 	GList *l;
774 	GMount *mount = NULL;
775 	GMount *result = NULL;
776 	GFile *root = NULL;
777 	GFile *default_location = NULL;
778 
779 	volume_monitor = g_volume_monitor_get ();
780 	mounts = g_volume_monitor_get_mounts (volume_monitor);
781 
782 	for (l = mounts; l != NULL; l = l->next) {
783 		mount = l->data;
784 
785 		if (g_mount_is_shadowed (mount)) {
786 			continue;
787 		}
788 
789 		root = g_mount_get_root (mount);
790 		if (g_file_equal (location, root)) {
791 			result = g_object_ref (mount);
792 			break;
793 		}
794 
795 		default_location = g_mount_get_default_location (mount);
796 		if (!g_file_equal (default_location, root) &&
797 		    g_file_equal (location, default_location)) {
798 			result = g_object_ref (mount);
799 			break;
800 		}
801 	}
802 
803 	g_clear_object (&root);
804 	g_clear_object (&default_location);
805 	g_list_free_full (mounts, g_object_unref);
806 
807 	return result;
808 }
809 
810 /**
811  * caja_get_pixmap_directory
812  *
813  * Get the path for the directory containing Caja pixmaps.
814  *
815  * Return value: the directory path.
816  **/
817 char *
caja_get_pixmap_directory(void)818 caja_get_pixmap_directory (void)
819 {
820     return g_strdup (DATADIR "/pixmaps/caja");
821 }
822 
823 /* FIXME bugzilla.gnome.org 42423:
824  * Callers just use this and dereference so we core dump if
825  * pixmaps are missing. That is lame.
826  */
827 char *
caja_pixmap_file(const char * partial_path)828 caja_pixmap_file (const char *partial_path)
829 {
830     char *path;
831 
832     path = g_build_filename (DATADIR "/pixmaps/caja", partial_path, NULL);
833     if (g_file_test (path, G_FILE_TEST_EXISTS))
834     {
835         return path;
836     }
837     else
838     {
839         char *tmp;
840         tmp = caja_get_pixmap_directory ();
841         g_debug ("Failed to locate \"%s\" in Caja pixmap path \"%s\". Incomplete installation?", partial_path, tmp);
842         g_free (tmp);
843     }
844     g_free (path);
845     return NULL;
846 }
847 
848 char *
caja_get_data_file_path(const char * partial_path)849 caja_get_data_file_path (const char *partial_path)
850 {
851     char *path;
852     char *user_directory;
853 
854     /* first try the user's home directory */
855     user_directory = caja_get_user_directory ();
856     path = g_build_filename (user_directory, partial_path, NULL);
857     g_free (user_directory);
858     if (g_file_test (path, G_FILE_TEST_EXISTS))
859     {
860         return path;
861     }
862     g_free (path);
863 
864     /* next try the shared directory */
865     path = g_build_filename (CAJA_DATADIR, partial_path, NULL);
866     if (g_file_test (path, G_FILE_TEST_EXISTS))
867     {
868         return path;
869     }
870     g_free (path);
871 
872     return NULL;
873 }
874 
875 char *
caja_ensure_unique_file_name(const char * directory_uri,const char * base_name,const char * extension)876 caja_ensure_unique_file_name (const char *directory_uri,
877                               const char *base_name,
878                               const char *extension)
879 {
880     GFileInfo *info;
881     char *filename;
882     GFile *dir, *child;
883     int copy;
884     char *res;
885 
886     dir = g_file_new_for_uri (directory_uri);
887 
888     info = g_file_query_info (dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL);
889     if (info == NULL)
890     {
891         g_object_unref (dir);
892         return NULL;
893     }
894     g_object_unref (info);
895 
896     filename = g_strdup_printf ("%s%s",
897                                 base_name,
898                                 extension);
899     child = g_file_get_child (dir, filename);
900     g_free (filename);
901 
902     copy = 1;
903     while ((info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL)) != NULL)
904     {
905         g_object_unref (info);
906         g_object_unref (child);
907 
908         filename = g_strdup_printf ("%s-%d%s",
909                                     base_name,
910                                     copy,
911                                     extension);
912         child = g_file_get_child (dir, filename);
913         g_free (filename);
914 
915         copy++;
916     }
917 
918     res = g_file_get_uri (child);
919     g_object_unref (child);
920     g_object_unref (dir);
921 
922     return res;
923 }
924 
925 GFile *
caja_find_existing_uri_in_hierarchy(GFile * location)926 caja_find_existing_uri_in_hierarchy (GFile *location)
927 {
928     GFileInfo *info = NULL;
929     GFile *tmp = NULL;
930 
931     g_assert (location != NULL);
932 
933     location = g_object_ref (location);
934     while (location != NULL)
935     {
936         info = g_file_query_info (location,
937                                   G_FILE_ATTRIBUTE_STANDARD_NAME,
938                                   0, NULL, NULL);
939         g_object_unref (info);
940         if (info != NULL)
941         {
942             return location;
943         }
944         tmp = location;
945         location = g_file_get_parent (location);
946         g_object_unref (tmp);
947     }
948 
949     return location;
950 }
951 
952 gboolean
caja_is_engrampa_installed(void)953 caja_is_engrampa_installed (void)
954 {
955     static int installed = -1;
956 
957     if (installed < 0)
958     {
959         gchar *found = g_find_program_in_path ("engrampa");
960         installed = found ? 1 : 0;
961         g_free (found);
962     }
963 
964     return installed > 0 ? TRUE : FALSE;
965 }
966 
967 #define GSM_NAME  "org.gnome.SessionManager"
968 #define GSM_PATH "/org/gnome/SessionManager"
969 #define GSM_INTERFACE "org.gnome.SessionManager"
970 
971 /* The following values come from
972  * https://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit
973  */
974 #define INHIBIT_LOGOUT (1U)
975 #define INHIBIT_SUSPEND (4U)
976 
977 static GDBusConnection *
get_dbus_connection(void)978 get_dbus_connection (void)
979 {
980     static GDBusConnection *conn = NULL;
981 
982     if (conn == NULL)
983     {
984         GError *error = NULL;
985 
986         conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
987 
988         if (conn == NULL)
989         {
990             g_warning ("Could not connect to session bus: %s", error->message);
991             g_error_free (error);
992         }
993     }
994 
995     return conn;
996 }
997 
998 /**
999  * caja_inhibit_power_manager:
1000  * @message: a human readable message for the reason why power management
1001  *       is being suspended.
1002  *
1003  * Inhibits the power manager from logging out or suspending the machine
1004  * (e.g. whenever Caja is doing file operations).
1005  *
1006  * Returns: an integer cookie, which must be passed to
1007  *    caja_uninhibit_power_manager() to resume
1008  *    normal power management.
1009  */
1010 int
caja_inhibit_power_manager(const char * message)1011 caja_inhibit_power_manager (const char *message)
1012 {
1013     GDBusConnection *connection;
1014     GVariant *result;
1015     GError *error = NULL;
1016     guint cookie = 0;
1017 
1018     g_return_val_if_fail (message != NULL, -1);
1019 
1020     connection = get_dbus_connection ();
1021 
1022     if (connection == NULL)
1023     {
1024         return -1;
1025     }
1026 
1027     result = g_dbus_connection_call_sync (connection,
1028                                           GSM_NAME,
1029                                           GSM_PATH,
1030                                           GSM_INTERFACE,
1031                                           "Inhibit",
1032                                           g_variant_new ("(susu)",
1033                                                   "Caja",
1034                                                   (guint) 0,
1035                                                   message,
1036                                                   (guint) (INHIBIT_LOGOUT | INHIBIT_SUSPEND)),
1037                                           G_VARIANT_TYPE ("(u)"),
1038                                           G_DBUS_CALL_FLAGS_NO_AUTO_START,
1039                                           -1,
1040                                           NULL,
1041                                           &error);
1042 
1043     if (error != NULL)
1044     {
1045         g_warning ("Could not inhibit power management: %s", error->message);
1046         g_error_free (error);
1047         return -1;
1048     }
1049 
1050     g_variant_get (result, "(u)", &cookie);
1051     g_variant_unref (result);
1052 
1053     return (int) cookie;
1054 }
1055 
1056 /**
1057  * caja_uninhibit_power_manager:
1058  * @cookie: the cookie value returned by caja_inhibit_power_manager()
1059  *
1060  * Uninhibits power management. This function must be called after the task
1061  * which inhibited power management has finished, or the system will not
1062  * return to normal power management.
1063  */
1064 void
caja_uninhibit_power_manager(gint cookie)1065 caja_uninhibit_power_manager (gint cookie)
1066 {
1067     GDBusConnection *connection;
1068     GVariant *result;
1069     GError *error = NULL;
1070 
1071     g_return_if_fail (cookie > 0);
1072 
1073     connection = get_dbus_connection ();
1074 
1075     if (connection == NULL)
1076     {
1077         return;
1078     }
1079 
1080     result = g_dbus_connection_call_sync (connection,
1081                                           GSM_NAME,
1082                                           GSM_PATH,
1083                                           GSM_INTERFACE,
1084                                           "Uninhibit",
1085                                           g_variant_new ("(u)", (guint) cookie),
1086                                           NULL,
1087                                           G_DBUS_CALL_FLAGS_NO_AUTO_START,
1088                                           -1,
1089                                           NULL,
1090                                           &error);
1091 
1092     if (result == NULL)
1093     {
1094         g_warning ("Could not uninhibit power management: %s", error->message);
1095         g_error_free (error);
1096         return;
1097     }
1098 
1099     g_variant_unref (result);
1100 }
1101 
1102 /* Returns TRUE if the file is in XDG_DATA_DIRS. This is used for
1103    deciding if a desktop file is "trusted" based on the path */
1104 gboolean
caja_is_in_system_dir(GFile * file)1105 caja_is_in_system_dir (GFile *file)
1106 {
1107     const char * const * data_dirs;
1108     char *path;
1109     int i;
1110     gboolean res;
1111 
1112     if (!g_file_is_native (file))
1113     {
1114         return FALSE;
1115     }
1116 
1117     path = g_file_get_path (file);
1118 
1119     res = FALSE;
1120 
1121     data_dirs = g_get_system_data_dirs ();
1122     for (i = 0; path != NULL && data_dirs[i] != NULL; i++)
1123     {
1124         if (g_str_has_prefix (path, data_dirs[i]))
1125         {
1126             res = TRUE;
1127             break;
1128         }
1129 
1130     }
1131 
1132     g_free (path);
1133 
1134     return res;
1135 }
1136 
1137 gboolean
caja_is_in_desktop_dir(GFile * file)1138 caja_is_in_desktop_dir (GFile *file)
1139 {
1140     char *path;
1141     char *dirname;
1142     gboolean res = FALSE;
1143 
1144     if (!g_file_is_native (file))
1145     {
1146         return res;
1147     }
1148 
1149     path = g_file_get_path (file);
1150     dirname = g_path_get_dirname (path);
1151     if (g_strcmp0 (dirname, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
1152     {
1153         res = TRUE;
1154     }
1155 
1156     g_free (path);
1157     g_free (dirname);
1158 
1159     return res;
1160 }
1161 
1162 GHashTable *
caja_trashed_files_get_original_directories(GList * files,GList ** unhandled_files)1163 caja_trashed_files_get_original_directories (GList *files,
1164         GList **unhandled_files)
1165 {
1166     GHashTable *directories;
1167     GList *l, *m;
1168     CajaFile *file = NULL;
1169     CajaFile *original_file = NULL;
1170     CajaFile *original_dir = NULL;
1171 
1172     directories = NULL;
1173 
1174     if (unhandled_files != NULL)
1175     {
1176         *unhandled_files = NULL;
1177     }
1178 
1179     for (l = files; l != NULL; l = l->next)
1180     {
1181         file = CAJA_FILE (l->data);
1182         original_file = caja_file_get_trash_original_file (file);
1183 
1184         original_dir = NULL;
1185         if (original_file != NULL)
1186         {
1187             original_dir = caja_file_get_parent (original_file);
1188         }
1189 
1190         if (original_dir != NULL)
1191         {
1192             if (directories == NULL)
1193             {
1194                 directories = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1195                                                      (GDestroyNotify) caja_file_unref,
1196                                                      (GDestroyNotify) caja_file_list_unref);
1197             }
1198             caja_file_ref (original_dir);
1199             m = g_hash_table_lookup (directories, original_dir);
1200             if (m != NULL)
1201             {
1202                 g_hash_table_steal (directories, original_dir);
1203                 caja_file_unref (original_dir);
1204             }
1205             m = g_list_append (m, caja_file_ref (file));
1206             g_hash_table_insert (directories, original_dir, m);
1207         }
1208         else if (unhandled_files != NULL)
1209         {
1210             *unhandled_files = g_list_append (*unhandled_files, caja_file_ref (file));
1211         }
1212 
1213         if (original_file != NULL)
1214         {
1215             caja_file_unref (original_file);
1216         }
1217 
1218         if (original_dir != NULL)
1219         {
1220             caja_file_unref (original_dir);
1221         }
1222     }
1223 
1224     return directories;
1225 }
1226 
1227 static GList *
locations_from_file_list(GList * file_list)1228 locations_from_file_list (GList *file_list)
1229 {
1230     GList *l, *ret;
1231     CajaFile *file = NULL;
1232 
1233     ret = NULL;
1234 
1235     for (l = file_list; l != NULL; l = l->next)
1236     {
1237         file = CAJA_FILE (l->data);
1238         ret = g_list_prepend (ret, caja_file_get_location (file));
1239     }
1240 
1241     return g_list_reverse (ret);
1242 }
1243 
1244 void
caja_restore_files_from_trash(GList * files,GtkWindow * parent_window)1245 caja_restore_files_from_trash (GList *files,
1246                                GtkWindow *parent_window)
1247 {
1248     GHashTable *original_dirs_hash;
1249     GList *original_dirs, *unhandled_files;
1250     GList *l;
1251     CajaFile *file = NULL;
1252 
1253     original_dirs_hash = caja_trashed_files_get_original_directories (files, &unhandled_files);
1254 
1255     for (l = unhandled_files; l != NULL; l = l->next)
1256     {
1257         char *message, *file_name;
1258 
1259         file = CAJA_FILE (l->data);
1260         file_name = caja_file_get_display_name (file);
1261         message = g_strdup_printf (_("Could not determine original location of \"%s\" "), file_name);
1262         g_free (file_name);
1263 
1264         eel_show_warning_dialog (message,
1265                                  _("The item cannot be restored from trash"),
1266                                  parent_window);
1267         g_free (message);
1268     }
1269 
1270     if (original_dirs_hash != NULL)
1271     {
1272         CajaFile *original_dir = NULL;
1273         GFile *original_dir_location = NULL;
1274         GList *locations = NULL;
1275 
1276         original_dirs = g_hash_table_get_keys (original_dirs_hash);
1277         for (l = original_dirs; l != NULL; l = l->next)
1278         {
1279             original_dir = CAJA_FILE (l->data);
1280             original_dir_location = caja_file_get_location (original_dir);
1281 
1282             files = g_hash_table_lookup (original_dirs_hash, original_dir);
1283             locations = locations_from_file_list (files);
1284 
1285             caja_file_operations_move
1286             (locations, NULL,
1287              original_dir_location,
1288              parent_window,
1289              NULL, NULL);
1290 
1291             g_list_free_full (locations, g_object_unref);
1292             g_object_unref (original_dir_location);
1293         }
1294 
1295         g_list_free (original_dirs);
1296         g_hash_table_destroy (original_dirs_hash);
1297     }
1298 
1299     caja_file_list_unref (unhandled_files);
1300 }
1301 
1302 char *
caja_get_filesystem_id_by_location(GFile * location,gboolean follow)1303 caja_get_filesystem_id_by_location (GFile *location, gboolean follow)
1304 {
1305     GFileInfo *info;
1306     GFileQueryInfoFlags flags;
1307     char *filesystem_id = NULL;
1308 
1309     if (follow) {
1310         flags = G_FILE_QUERY_INFO_NONE;
1311     } else {
1312         flags = G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS;
1313     }
1314 
1315     info = g_file_query_info (location, G_FILE_ATTRIBUTE_ID_FILESYSTEM, flags, NULL, NULL);
1316     if (info) {
1317         if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM)) {
1318             filesystem_id = g_strdup (
1319                 g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM));
1320         }
1321         g_object_unref (info);
1322     }
1323     return filesystem_id;
1324 }
1325 
1326 char *
caja_get_filesystem_id_by_uri(const char * uri,gboolean follow)1327 caja_get_filesystem_id_by_uri (const char *uri, gboolean follow)
1328 {
1329     GFile *location;
1330     char *filesystem_id;
1331 
1332     location = g_file_new_for_uri (uri);
1333     filesystem_id = caja_get_filesystem_id_by_location (location, follow);
1334     g_object_unref (location);
1335     return filesystem_id;
1336 }
1337 
1338 #if !defined (CAJA_OMIT_SELF_CHECK)
1339 
1340 void
caja_self_check_file_utilities(void)1341 caja_self_check_file_utilities (void)
1342 {
1343 }
1344 
1345 #endif /* !CAJA_OMIT_SELF_CHECK */
1346