summaryrefslogtreecommitdiff
path: root/libcaja-private/caja-file-utilities.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcaja-private/caja-file-utilities.c')
-rw-r--r--libcaja-private/caja-file-utilities.c1449
1 files changed, 1449 insertions, 0 deletions
diff --git a/libcaja-private/caja-file-utilities.c b/libcaja-private/caja-file-utilities.c
new file mode 100644
index 00000000..6c9a7de1
--- /dev/null
+++ b/libcaja-private/caja-file-utilities.c
@@ -0,0 +1,1449 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* caja-file-utilities.c - implementation of file manipulation routines.
+
+ Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: John Sullivan <[email protected]>
+*/
+
+#include <config.h>
+#include "caja-file-utilities.h"
+
+#include "caja-global-preferences.h"
+#include "caja-lib-self-check-functions.h"
+#include "caja-metadata.h"
+#include "caja-file.h"
+#include "caja-file-operations.h"
+#include "caja-search-directory.h"
+#include "caja-signaller.h"
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-stock-dialogs.h>
+#include <eel/eel-string.h>
+#include <eel/eel-debug.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define CAJA_USER_DIRECTORY_NAME ".config/caja"
+#define DEFAULT_CAJA_DIRECTORY_MODE (0755)
+
+#define DESKTOP_DIRECTORY_NAME "Desktop"
+#define LEGACY_DESKTOP_DIRECTORY_NAME ".mate-desktop"
+#define DEFAULT_DESKTOP_DIRECTORY_MODE (0755)
+
+static void update_xdg_dir_cache (void);
+static void schedule_user_dirs_changed (void);
+static void desktop_dir_changed (void);
+static GFile *caja_find_file_insensitive_next (GFile *parent, const gchar *name);
+
+char *
+caja_compute_title_for_location (GFile *location)
+{
+ CajaFile *file;
+ char *title;
+
+ /* TODO-gio: This doesn't really work all that great if the
+ info about the file isn't known atm... */
+
+ title = NULL;
+ if (location)
+ {
+ file = caja_file_get (location);
+ title = caja_file_get_description (file);
+ if (title == NULL)
+ {
+ title = caja_file_get_display_name (file);
+ }
+ caja_file_unref (file);
+ }
+
+ if (title == NULL)
+ {
+ title = g_strdup ("");
+ }
+
+ return title;
+}
+
+
+/**
+ * caja_get_user_directory:
+ *
+ * Get the path for the directory containing caja settings.
+ *
+ * Return value: the directory path.
+ **/
+char* caja_get_user_directory(void)
+{
+ /* FIXME bugzilla.gnome.org 41286:
+ * How should we handle the case where this mkdir fails?
+ * Note that caja_application_startup will refuse to launch if this
+ * directory doesn't get created, so that case is OK. But the directory
+ * could be deleted after Caja was launched, and perhaps
+ * there is some bad side-effect of not handling that case.
+ * <<<
+ * Si alguien tiene tiempo, puede enviar este codigo a Nautilus.
+ * Obviamente, con los comentarios traducidos al Inglés.
+ */
+ char* user_directory = g_build_filename(g_get_home_dir(), ".config", "caja", NULL);
+ /* Se necesita que esta dirección sea una carpeta, con los permisos
+ * DEFAULT_CAJA_DIRECTORY_MODE. Pero si es un archivo, el programa intentará
+ * eliminar el archivo silenciosamente. */
+ if (g_file_test(user_directory, G_FILE_TEST_IS_DIR) == FALSE ||
+ g_access(user_directory, DEFAULT_CAJA_DIRECTORY_MODE) == -1)
+ {
+ /* Se puede obtener un enlace simbolico a una carpeta */
+ if (g_file_test(user_directory, G_FILE_TEST_IS_SYMLINK) == TRUE)
+ {
+ /* intentaremos saber si el enlace es una carpeta, y tiene los
+ * permisos adecuados */
+ char* link = g_file_read_link(user_directory, NULL);
+
+ if (link)
+ {
+ /* Si el enlace no es un directorio, o si falla al hacer chmod,
+ * se borra el enlace y se crea la carpeta */
+ if (g_file_test(link, G_FILE_TEST_IS_DIR) != TRUE ||
+ g_chmod(link, DEFAULT_CAJA_DIRECTORY_MODE) != 0)
+ {
+ /* podemos borrar el enlace y crear la carpeta */
+ g_unlink(user_directory);
+ g_mkdir(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
+ }
+
+ g_free(link);
+ }
+ }
+ else if (g_file_test(user_directory, G_FILE_TEST_IS_DIR) == TRUE)
+ {
+ g_chmod(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
+ }
+ else if (g_file_test(user_directory, G_FILE_TEST_EXISTS) == TRUE)
+ {
+ /* podemos borrar el enlace y crear la carpeta */
+ g_unlink(user_directory);
+ g_mkdir(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
+ }
+ else
+ {
+ /* Si no existe ningun archivo, se crea la carpeta */
+ g_mkdir_with_parents(user_directory, DEFAULT_CAJA_DIRECTORY_MODE);
+ }
+
+ /* Faltan permisos */
+ if (g_chmod(user_directory, DEFAULT_CAJA_DIRECTORY_MODE) != 0)
+ {
+ GtkWidget* dialog = gtk_message_dialog_new(
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "The path for the directory containing caja settings need read and write permissions: %s",
+ user_directory);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ exit(0);
+ }
+ }
+
+ return user_directory;
+}
+
+/**
+ * caja_get_accel_map_file:
+ *
+ * Get the path for the filename containing caja accelerator map.
+ * The filename need not exist.
+ *
+ * Return value: the filename path, or NULL if the home directory could not be found
+ **/
+char* caja_get_accel_map_file(void)
+{
+ const gchar* override;
+
+ override = g_getenv ("MATE22_USER_DIR");
+
+ if (override)
+ {
+ return g_build_filename(override, "accels", "caja", NULL);
+ }
+ else
+ {
+ return g_build_filename(g_get_home_dir(), ".config", "accels", "caja", NULL);
+ }
+}
+
+typedef struct {
+ char*type;
+ char*path;
+ CajaFile* file;
+} XdgDirEntry;
+
+
+static XdgDirEntry *
+parse_xdg_dirs (const char *config_file)
+{
+ GArray *array;
+ char *config_file_free = NULL;
+ XdgDirEntry dir;
+ char *data;
+ char **lines;
+ char *p, *d;
+ int i;
+ char *type_start, *type_end;
+ char *value, *unescaped;
+ gboolean relative;
+
+ array = g_array_new (TRUE, TRUE, sizeof (XdgDirEntry));
+
+ if (config_file == NULL)
+ {
+ config_file_free = g_build_filename (g_get_user_config_dir (),
+ "user-dirs.dirs", NULL);
+ config_file = (const char *)config_file_free;
+ }
+
+ if (g_file_get_contents (config_file, &data, NULL, NULL))
+ {
+ lines = g_strsplit (data, "\n", 0);
+ g_free (data);
+ for (i = 0; lines[i] != NULL; i++)
+ {
+ p = lines[i];
+ while (g_ascii_isspace (*p))
+ p++;
+
+ if (*p == '#')
+ continue;
+
+ value = strchr (p, '=');
+ if (value == NULL)
+ continue;
+ *value++ = 0;
+
+ g_strchug (g_strchomp (p));
+ if (!g_str_has_prefix (p, "XDG_"))
+ continue;
+ if (!g_str_has_suffix (p, "_DIR"))
+ continue;
+ type_start = p + 4;
+ type_end = p + strlen (p) - 4;
+
+ while (g_ascii_isspace (*value))
+ value++;
+
+ if (*value != '"')
+ continue;
+ value++;
+
+ relative = FALSE;
+ if (g_str_has_prefix (value, "$HOME"))
+ {
+ relative = TRUE;
+ value += 5;
+ while (*value == '/')
+ value++;
+ }
+ else if (*value != '/')
+ continue;
+
+ d = unescaped = g_malloc (strlen (value) + 1);
+ while (*value && *value != '"')
+ {
+ if ((*value == '\\') && (*(value + 1) != 0))
+ value++;
+ *d++ = *value++;
+ }
+ *d = 0;
+
+ *type_end = 0;
+ dir.type = g_strdup (type_start);
+ if (relative)
+ {
+ dir.path = g_build_filename (g_get_home_dir (), unescaped, NULL);
+ g_free (unescaped);
+ }
+ else
+ dir.path = unescaped;
+
+ g_array_append_val (array, dir);
+ }
+
+ g_strfreev (lines);
+ }
+
+ g_free (config_file_free);
+
+ return (XdgDirEntry *)g_array_free (array, FALSE);
+}
+
+static XdgDirEntry *cached_xdg_dirs = NULL;
+static GFileMonitor *cached_xdg_dirs_monitor = NULL;
+
+static void
+xdg_dir_changed (CajaFile *file,
+ XdgDirEntry *dir)
+{
+ GFile *location, *dir_location;
+ char *path;
+
+ location = caja_file_get_location (file);
+ dir_location = g_file_new_for_path (dir->path);
+ if (!g_file_equal (location, dir_location))
+ {
+ path = g_file_get_path (location);
+
+ if (path)
+ {
+ char *argv[5];
+ int i;
+
+ g_free (dir->path);
+ dir->path = path;
+
+ i = 0;
+ argv[i++] = "xdg-user-dirs-update";
+ argv[i++] = "--set";
+ argv[i++] = dir->type;
+ argv[i++] = dir->path;
+ argv[i++] = NULL;
+
+ /* We do this sync, to avoid possible race-conditions
+ if multiple dirs change at the same time. Its
+ blocking the main thread, but these updates should
+ be very rare and very fast. */
+ g_spawn_sync (NULL,
+ argv, NULL,
+ G_SPAWN_SEARCH_PATH |
+ G_SPAWN_STDOUT_TO_DEV_NULL |
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ g_reload_user_special_dirs_cache ();
+ schedule_user_dirs_changed ();
+ desktop_dir_changed ();
+ /* Icon might have changed */
+ caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_INFO);
+ }
+ }
+ g_object_unref (location);
+ g_object_unref (dir_location);
+}
+
+static void
+xdg_dir_cache_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type)
+{
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGED ||
+ event_type == G_FILE_MONITOR_EVENT_CREATED)
+ {
+ update_xdg_dir_cache ();
+ }
+}
+
+static int user_dirs_changed_tag = 0;
+
+static gboolean
+emit_user_dirs_changed_idle (gpointer data)
+{
+ g_signal_emit_by_name (caja_signaller_get_current (),
+ "user_dirs_changed");
+ user_dirs_changed_tag = 0;
+ return FALSE;
+}
+
+static void
+schedule_user_dirs_changed (void)
+{
+ if (user_dirs_changed_tag == 0)
+ {
+ user_dirs_changed_tag = g_idle_add (emit_user_dirs_changed_idle, NULL);
+ }
+}
+
+static void
+unschedule_user_dirs_changed (void)
+{
+ if (user_dirs_changed_tag != 0)
+ {
+ g_source_remove (user_dirs_changed_tag);
+ user_dirs_changed_tag = 0;
+ }
+}
+
+static void
+free_xdg_dir_cache (void)
+{
+ int i;
+
+ if (cached_xdg_dirs != NULL)
+ {
+ for (i = 0; cached_xdg_dirs[i].type != NULL; i++)
+ {
+ if (cached_xdg_dirs[i].file != NULL)
+ {
+ caja_file_monitor_remove (cached_xdg_dirs[i].file,
+ &cached_xdg_dirs[i]);
+ g_signal_handlers_disconnect_by_func (cached_xdg_dirs[i].file,
+ G_CALLBACK (xdg_dir_changed),
+ &cached_xdg_dirs[i]);
+ caja_file_unref (cached_xdg_dirs[i].file);
+ }
+ g_free (cached_xdg_dirs[i].type);
+ g_free (cached_xdg_dirs[i].path);
+ }
+ g_free (cached_xdg_dirs);
+ }
+}
+
+static void
+destroy_xdg_dir_cache (void)
+{
+ free_xdg_dir_cache ();
+ unschedule_user_dirs_changed ();
+ desktop_dir_changed ();
+
+ if (cached_xdg_dirs_monitor != NULL)
+ {
+ g_object_unref (cached_xdg_dirs_monitor);
+ cached_xdg_dirs_monitor = NULL;
+ }
+}
+
+static void
+update_xdg_dir_cache (void)
+{
+ GFile *file;
+ char *config_file, *uri;
+ int i;
+
+ free_xdg_dir_cache ();
+ g_reload_user_special_dirs_cache ();
+ schedule_user_dirs_changed ();
+ desktop_dir_changed ();
+
+ cached_xdg_dirs = parse_xdg_dirs (NULL);
+
+ for (i = 0 ; cached_xdg_dirs[i].type != NULL; i++)
+ {
+ cached_xdg_dirs[i].file = NULL;
+ if (strcmp (cached_xdg_dirs[i].path, g_get_home_dir ()) != 0)
+ {
+ uri = g_filename_to_uri (cached_xdg_dirs[i].path, NULL, NULL);
+ cached_xdg_dirs[i].file = caja_file_get_by_uri (uri);
+ caja_file_monitor_add (cached_xdg_dirs[i].file,
+ &cached_xdg_dirs[i],
+ CAJA_FILE_ATTRIBUTE_INFO);
+ g_signal_connect (cached_xdg_dirs[i].file,
+ "changed", G_CALLBACK (xdg_dir_changed), &cached_xdg_dirs[i]);
+ g_free (uri);
+ }
+ }
+
+ if (cached_xdg_dirs_monitor == NULL)
+ {
+ config_file = g_build_filename (g_get_user_config_dir (),
+ "user-dirs.dirs", NULL);
+ file = g_file_new_for_path (config_file);
+ cached_xdg_dirs_monitor = g_file_monitor_file (file, 0, NULL, NULL);
+ g_signal_connect (cached_xdg_dirs_monitor, "changed",
+ G_CALLBACK (xdg_dir_cache_changed_cb), NULL);
+ g_object_unref (file);
+ g_free (config_file);
+
+ eel_debug_call_at_shutdown (destroy_xdg_dir_cache);
+ }
+}
+
+char *
+caja_get_xdg_dir (const char *type)
+{
+ int i;
+
+ if (cached_xdg_dirs == NULL)
+ {
+ update_xdg_dir_cache ();
+ }
+
+ for (i = 0 ; cached_xdg_dirs != NULL && cached_xdg_dirs[i].type != NULL; i++)
+ {
+ if (strcmp (cached_xdg_dirs[i].type, type) == 0)
+ {
+ return g_strdup (cached_xdg_dirs[i].path);
+ }
+ }
+ if (strcmp ("DESKTOP", type) == 0)
+ {
+ return g_build_filename (g_get_home_dir (), DESKTOP_DIRECTORY_NAME, NULL);
+ }
+ if (strcmp ("TEMPLATES", type) == 0)
+ {
+ return g_build_filename (g_get_home_dir (), "Templates", NULL);
+ }
+
+ return g_strdup (g_get_home_dir ());
+}
+
+static char *
+get_desktop_path (void)
+{
+ if (eel_preferences_get_boolean (CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR))
+ {
+ return g_strdup (g_get_home_dir());
+ }
+ else
+ {
+ return caja_get_xdg_dir ("DESKTOP");
+ }
+}
+
+/**
+ * caja_get_desktop_directory:
+ *
+ * Get the path for the directory containing files on the desktop.
+ *
+ * Return value: the directory path.
+ **/
+char *
+caja_get_desktop_directory (void)
+{
+ char *desktop_directory;
+
+ desktop_directory = get_desktop_path ();
+
+ /* Don't try to create a home directory */
+ if (!eel_preferences_get_boolean (CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR))
+ {
+ if (!g_file_test (desktop_directory, G_FILE_TEST_EXISTS))
+ {
+ g_mkdir (desktop_directory, DEFAULT_DESKTOP_DIRECTORY_MODE);
+ /* FIXME bugzilla.gnome.org 41286:
+ * How should we handle the case where this mkdir fails?
+ * Note that caja_application_startup will refuse to launch if this
+ * directory doesn't get created, so that case is OK. But the directory
+ * could be deleted after Caja was launched, and perhaps
+ * there is some bad side-effect of not handling that case.
+ */
+ }
+ }
+
+ return desktop_directory;
+}
+
+GFile *
+caja_get_desktop_location (void)
+{
+ char *desktop_directory;
+ GFile *res;
+
+ desktop_directory = get_desktop_path ();
+
+ res = g_file_new_for_path (desktop_directory);
+ g_free (desktop_directory);
+ return res;
+}
+
+
+/**
+ * caja_get_desktop_directory_uri:
+ *
+ * Get the uri for the directory containing files on the desktop.
+ *
+ * Return value: the directory path.
+ **/
+char *
+caja_get_desktop_directory_uri (void)
+{
+ char *desktop_path;
+ char *desktop_uri;
+
+ desktop_path = caja_get_desktop_directory ();
+ desktop_uri = g_filename_to_uri (desktop_path, NULL, NULL);
+ g_free (desktop_path);
+
+ return desktop_uri;
+}
+
+char *
+caja_get_desktop_directory_uri_no_create (void)
+{
+ char *desktop_path;
+ char *desktop_uri;
+
+ desktop_path = get_desktop_path ();
+ desktop_uri = g_filename_to_uri (desktop_path, NULL, NULL);
+ g_free (desktop_path);
+
+ return desktop_uri;
+}
+
+char *
+caja_get_home_directory_uri (void)
+{
+ return g_filename_to_uri (g_get_home_dir (), NULL, NULL);
+}
+
+
+gboolean
+caja_should_use_templates_directory (void)
+{
+ char *dir;
+ gboolean res;
+
+ dir = caja_get_xdg_dir ("TEMPLATES");
+ res = strcmp (dir, g_get_home_dir ()) != 0;
+ g_free (dir);
+ return res;
+}
+
+char *
+caja_get_templates_directory (void)
+{
+ return caja_get_xdg_dir ("TEMPLATES");
+}
+
+void
+caja_create_templates_directory (void)
+{
+ char *dir;
+
+ dir = caja_get_templates_directory ();
+ if (!g_file_test (dir, G_FILE_TEST_EXISTS))
+ {
+ g_mkdir (dir, DEFAULT_CAJA_DIRECTORY_MODE);
+ }
+ g_free (dir);
+}
+
+char *
+caja_get_templates_directory_uri (void)
+{
+ char *directory, *uri;
+
+ directory = caja_get_templates_directory ();
+ uri = g_filename_to_uri (directory, NULL, NULL);
+ g_free (directory);
+ return uri;
+}
+
+char *
+caja_get_searches_directory (void)
+{
+ char *user_dir;
+ char *searches_dir;
+
+ user_dir = caja_get_user_directory ();
+ searches_dir = g_build_filename (user_dir, "searches", NULL);
+ g_free (user_dir);
+
+ if (!g_file_test (searches_dir, G_FILE_TEST_EXISTS))
+ g_mkdir (searches_dir, DEFAULT_CAJA_DIRECTORY_MODE);
+
+ return searches_dir;
+}
+
+/* These need to be reset to NULL when desktop_is_home_dir changes */
+static GFile *desktop_dir = NULL;
+static GFile *desktop_dir_dir = NULL;
+static char *desktop_dir_filename = NULL;
+static gboolean desktop_dir_changed_callback_installed = FALSE;
+
+
+static void
+desktop_dir_changed (void)
+{
+ if (desktop_dir)
+ {
+ g_object_unref (desktop_dir);
+ }
+ if (desktop_dir_dir)
+ {
+ g_object_unref (desktop_dir_dir);
+ }
+ g_free (desktop_dir_filename);
+ desktop_dir = NULL;
+ desktop_dir_dir = NULL;
+ desktop_dir_filename = NULL;
+}
+
+static void
+desktop_dir_changed_callback (gpointer callback_data)
+{
+ desktop_dir_changed ();
+}
+
+static void
+update_desktop_dir (void)
+{
+ char *path;
+ char *dirname;
+
+ path = get_desktop_path ();
+ desktop_dir = g_file_new_for_path (path);
+
+ dirname = g_path_get_dirname (path);
+ desktop_dir_dir = g_file_new_for_path (dirname);
+ g_free (dirname);
+ desktop_dir_filename = g_path_get_basename (path);
+ g_free (path);
+}
+
+gboolean
+caja_is_home_directory_file (GFile *dir,
+ const char *filename)
+{
+ char *dirname;
+ static GFile *home_dir_dir = NULL;
+ static char *home_dir_filename = NULL;
+
+ if (home_dir_dir == NULL)
+ {
+ dirname = g_path_get_dirname (g_get_home_dir ());
+ home_dir_dir = g_file_new_for_path (dirname);
+ g_free (dirname);
+ home_dir_filename = g_path_get_basename (g_get_home_dir ());
+ }
+
+ return (g_file_equal (dir, home_dir_dir) &&
+ strcmp (filename, home_dir_filename) == 0);
+}
+
+gboolean
+caja_is_home_directory (GFile *dir)
+{
+ static GFile *home_dir = NULL;
+
+ if (home_dir == NULL)
+ {
+ home_dir = g_file_new_for_path (g_get_home_dir ());
+ }
+
+ return g_file_equal (dir, home_dir);
+}
+
+gboolean
+caja_is_root_directory (GFile *dir)
+{
+ static GFile *root_dir = NULL;
+
+ if (root_dir == NULL)
+ {
+ root_dir = g_file_new_for_path ("/");
+ }
+
+ return g_file_equal (dir, root_dir);
+}
+
+
+gboolean
+caja_is_desktop_directory_file (GFile *dir,
+ const char *file)
+{
+
+ if (!desktop_dir_changed_callback_installed)
+ {
+ eel_preferences_add_callback (CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR,
+ desktop_dir_changed_callback,
+ NULL);
+ desktop_dir_changed_callback_installed = TRUE;
+ }
+
+ if (desktop_dir == NULL)
+ {
+ update_desktop_dir ();
+ }
+
+ return (g_file_equal (dir, desktop_dir_dir) &&
+ strcmp (file, desktop_dir_filename) == 0);
+}
+
+gboolean
+caja_is_desktop_directory (GFile *dir)
+{
+
+ if (!desktop_dir_changed_callback_installed)
+ {
+ eel_preferences_add_callback (CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR,
+ desktop_dir_changed_callback,
+ NULL);
+ desktop_dir_changed_callback_installed = TRUE;
+ }
+
+ if (desktop_dir == NULL)
+ {
+ update_desktop_dir ();
+ }
+
+ return g_file_equal (dir, desktop_dir);
+}
+
+
+/**
+ * caja_get_gmc_desktop_directory:
+ *
+ * Get the path for the directory containing the legacy gmc desktop.
+ *
+ * Return value: the directory path.
+ **/
+char *
+caja_get_gmc_desktop_directory (void)
+{
+ return g_build_filename (g_get_home_dir (), LEGACY_DESKTOP_DIRECTORY_NAME, NULL);
+}
+
+/**
+ * caja_get_pixmap_directory
+ *
+ * Get the path for the directory containing Caja pixmaps.
+ *
+ * Return value: the directory path.
+ **/
+char *
+caja_get_pixmap_directory (void)
+{
+ return g_strdup (DATADIR "/pixmaps/caja");
+}
+
+/* FIXME bugzilla.gnome.org 42423:
+ * Callers just use this and dereference so we core dump if
+ * pixmaps are missing. That is lame.
+ */
+char *
+caja_pixmap_file (const char *partial_path)
+{
+ char *path;
+
+ path = g_build_filename (DATADIR "/pixmaps/caja", partial_path, NULL);
+ if (g_file_test (path, G_FILE_TEST_EXISTS))
+ {
+ return path;
+ }
+ else
+ {
+ char *tmp;
+ tmp = caja_get_pixmap_directory ();
+ g_debug ("Failed to locate \"%s\" in Caja pixmap path \"%s\". Incomplete installation?", partial_path, tmp);
+ g_free (tmp);
+ }
+ g_free (path);
+ return NULL;
+}
+
+char *
+caja_get_data_file_path (const char *partial_path)
+{
+ char *path;
+ char *user_directory;
+
+ /* first try the user's home directory */
+ user_directory = caja_get_user_directory ();
+ path = g_build_filename (user_directory, partial_path, NULL);
+ g_free (user_directory);
+ if (g_file_test (path, G_FILE_TEST_EXISTS))
+ {
+ return path;
+ }
+ g_free (path);
+
+ /* next try the shared directory */
+ path = g_build_filename (CAJA_DATADIR, partial_path, NULL);
+ if (g_file_test (path, G_FILE_TEST_EXISTS))
+ {
+ return path;
+ }
+ g_free (path);
+
+ return NULL;
+}
+
+char *
+caja_ensure_unique_file_name (const char *directory_uri,
+ const char *base_name,
+ const char *extension)
+{
+ GFileInfo *info;
+ char *filename;
+ GFile *dir, *child;
+ int copy;
+ char *res;
+
+ dir = g_file_new_for_uri (directory_uri);
+
+ info = g_file_query_info (dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL);
+ if (info == NULL)
+ {
+ g_object_unref (dir);
+ return NULL;
+ }
+ g_object_unref (info);
+
+ filename = g_strdup_printf ("%s%s",
+ base_name,
+ extension);
+ child = g_file_get_child (dir, filename);
+ g_free (filename);
+
+ copy = 1;
+ while ((info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL)) != NULL)
+ {
+ g_object_unref (info);
+ g_object_unref (child);
+
+ filename = g_strdup_printf ("%s-%d%s",
+ base_name,
+ copy,
+ extension);
+ child = g_file_get_child (dir, filename);
+ g_free (filename);
+
+ copy++;
+ }
+
+ res = g_file_get_uri (child);
+ g_object_unref (child);
+ g_object_unref (dir);
+
+ return res;
+}
+
+char *
+caja_unique_temporary_file_name (void)
+{
+ const char *prefix = "/tmp/caja-temp-file";
+ char *file_name;
+ int fd;
+
+ file_name = g_strdup_printf ("%sXXXXXX", prefix);
+
+ fd = g_mkstemp (file_name);
+ if (fd == -1)
+ {
+ g_free (file_name);
+ file_name = NULL;
+ }
+ else
+ {
+ close (fd);
+ }
+
+ return file_name;
+}
+
+GFile *
+caja_find_existing_uri_in_hierarchy (GFile *location)
+{
+ GFileInfo *info;
+ GFile *tmp;
+
+ g_assert (location != NULL);
+
+ location = g_object_ref (location);
+ while (location != NULL)
+ {
+ info = g_file_query_info (location,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ 0, NULL, NULL);
+ g_object_unref (info);
+ if (info != NULL)
+ {
+ return location;
+ }
+ tmp = location;
+ location = g_file_get_parent (location);
+ g_object_unref (tmp);
+ }
+
+ return location;
+}
+
+/**
+ * caja_find_file_insensitive
+ *
+ * Attempt to find a file case-insentively. If the path can be found, the
+ * returned file maps directly to it. Otherwise, a file using the
+ * originally-cased path is returned. This function performs might perform
+ * I/O.
+ *
+ * Return value: a #GFile to a child specified by @name.
+ **/
+GFile *
+caja_find_file_insensitive (GFile *parent, const gchar *name)
+{
+ gchar **split_path;
+ gchar *component;
+ GFile *file, *next;
+ gint i;
+
+ split_path = g_strsplit (name, G_DIR_SEPARATOR_S, -1);
+
+ file = g_object_ref (parent);
+
+ for (i = 0; (component = split_path[i]) != NULL; i++)
+ {
+ if (!(next = caja_find_file_insensitive_next (file,
+ component)))
+ {
+ /* File does not exist */
+ g_object_unref (file);
+ file = NULL;
+ break;
+ }
+ g_object_unref (file);
+ file = next;
+ }
+ g_strfreev (split_path);
+
+ if (file)
+ {
+ return file;
+ }
+ return g_file_get_child (parent, name);
+}
+
+static GFile *
+caja_find_file_insensitive_next (GFile *parent, const gchar *name)
+{
+ GFileEnumerator *children;
+ GFileInfo *info;
+ gboolean use_utf8, found;
+ char *filename, *case_folded_name, *utf8_collation_key, *ascii_collation_key, *child_key;
+ GFile *file;
+ const char *child_name, *compare_key;
+
+ /* First check the given version */
+ file = g_file_get_child (parent, name);
+ if (g_file_query_exists (file, NULL))
+ {
+ return file;
+ }
+ g_object_unref (file);
+
+ ascii_collation_key = g_ascii_strdown (name, -1);
+ use_utf8 = g_utf8_validate (name, -1, NULL);
+ utf8_collation_key = NULL;
+ if (use_utf8)
+ {
+ case_folded_name = g_utf8_casefold (name, -1);
+ utf8_collation_key = g_utf8_collate_key (case_folded_name, -1);
+ g_free (case_folded_name);
+ }
+
+ /* Enumerate and compare insensitive */
+ filename = NULL;
+ children = g_file_enumerate_children (parent,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ 0, NULL, NULL);
+ if (children != NULL)
+ {
+ while ((info = g_file_enumerator_next_file (children, NULL, NULL)))
+ {
+ child_name = g_file_info_get_name (info);
+
+ if (use_utf8 && g_utf8_validate (child_name, -1, NULL))
+ {
+ gchar *case_folded;
+
+ case_folded = g_utf8_casefold (child_name, -1);
+ child_key = g_utf8_collate_key (case_folded, -1);
+ g_free (case_folded);
+ compare_key = utf8_collation_key;
+ }
+ else
+ {
+ child_key = g_ascii_strdown (child_name, -1);
+ compare_key = ascii_collation_key;
+ }
+
+ found = strcmp (child_key, compare_key) == 0;
+ g_free (child_key);
+ if (found)
+ {
+ filename = g_strdup (child_name);
+ break;
+ }
+ }
+ g_file_enumerator_close (children, NULL, NULL);
+ g_object_unref (children);
+ }
+
+ g_free (ascii_collation_key);
+ g_free (utf8_collation_key);
+
+ if (filename)
+ {
+ file = g_file_get_child (parent, filename);
+ g_free (filename);
+ return file;
+ }
+
+ return NULL;
+}
+
+gboolean
+caja_is_file_roller_installed (void)
+{
+ static int installed = - 1;
+
+ if (installed < 0)
+ {
+ if (g_find_program_in_path ("file-roller"))
+ {
+ installed = 1;
+ }
+ else
+ {
+ installed = 0;
+ }
+ }
+
+ return installed > 0 ? TRUE : FALSE;
+}
+
+#define GSM_NAME "org.mate.SessionManager"
+#define GSM_PATH "/org/mate/SessionManager"
+#define GSM_INTERFACE "org.mate.SessionManager"
+
+/* The following values come from
+ * http://www.gnome.org/~mccann/mate-session/docs/mate-session.html#org.mate.SessionManager.Inhibit
+ */
+#define INHIBIT_LOGOUT (1U)
+#define INHIBIT_SUSPEND (4U)
+
+static GDBusConnection *
+get_dbus_connection (void)
+{
+ static GDBusConnection *conn = NULL;
+
+ if (conn == NULL)
+ {
+ GError *error = NULL;
+
+ conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (conn == NULL)
+ {
+ g_warning ("Could not connect to session bus: %s", error->message);
+ g_error_free (error);
+ }
+ }
+
+ return conn;
+}
+
+/**
+ * caja_inhibit_power_manager:
+ * @message: a human readable message for the reason why power management
+ * is being suspended.
+ *
+ * Inhibits the power manager from logging out or suspending the machine
+ * (e.g. whenever Caja is doing file operations).
+ *
+ * Returns: an integer cookie, which must be passed to
+ * caja_uninhibit_power_manager() to resume
+ * normal power management.
+ */
+int
+caja_inhibit_power_manager (const char *message)
+{
+ GDBusConnection *connection;
+ GVariant *result;
+ GError *error = NULL;
+ guint cookie = 0;
+
+ g_return_val_if_fail (message != NULL, -1);
+
+ connection = get_dbus_connection ();
+
+ if (connection == NULL)
+ {
+ return -1;
+ }
+
+ result = g_dbus_connection_call_sync (connection,
+ GSM_NAME,
+ GSM_PATH,
+ GSM_INTERFACE,
+ "Inhibit",
+ g_variant_new ("(susu)",
+ "Caja",
+ (guint) 0,
+ message,
+ (guint) (INHIBIT_LOGOUT | INHIBIT_SUSPEND)),
+ G_VARIANT_TYPE ("(u)"),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("Could not inhibit power management: %s", error->message);
+ g_error_free (error);
+ return -1;
+ }
+
+ g_variant_get (result, "(u)", &cookie);
+ g_variant_unref (result);
+
+ return (int) cookie;
+}
+
+/**
+ * caja_uninhibit_power_manager:
+ * @cookie: the cookie value returned by caja_inhibit_power_manager()
+ *
+ * Uninhibits power management. This function must be called after the task
+ * which inhibited power management has finished, or the system will not
+ * return to normal power management.
+ */
+void
+caja_uninhibit_power_manager (gint cookie)
+{
+ GDBusConnection *connection;
+ GVariant *result;
+ GError *error = NULL;
+
+ g_return_if_fail (cookie > 0);
+
+ connection = get_dbus_connection ();
+
+ if (connection == NULL)
+ {
+ return;
+ }
+
+ result = g_dbus_connection_call_sync (connection,
+ GSM_NAME,
+ GSM_PATH,
+ GSM_INTERFACE,
+ "Uninhibit",
+ g_variant_new ("(u)", (guint) cookie),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+
+ if (result == NULL)
+ {
+ g_warning ("Could not uninhibit power management: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_variant_unref (result);
+}
+
+/* Returns TRUE if the file is in XDG_DATA_DIRS or
+ in "~/.mate2/". This is used for deciding
+ if a desktop file is "trusted" based on the path */
+gboolean
+caja_is_in_system_dir (GFile *file)
+{
+ const char * const * data_dirs;
+ char *path, *mate2;
+ int i;
+ gboolean res;
+
+ if (!g_file_is_native (file))
+ {
+ return FALSE;
+ }
+
+ path = g_file_get_path (file);
+
+ res = FALSE;
+
+ data_dirs = g_get_system_data_dirs ();
+ for (i = 0; path != NULL && data_dirs[i] != NULL; i++)
+ {
+ if (g_str_has_prefix (path, data_dirs[i]))
+ {
+ res = TRUE;
+ break;
+ }
+
+ }
+
+ if (!res)
+ {
+ /* Panel desktop files are here, trust them */
+ mate2 = g_build_filename(g_get_home_dir(), ".mate2", NULL);
+
+ if (g_str_has_prefix (path, mate2))
+ {
+ res = TRUE;
+ }
+
+ g_free(mate2);
+ }
+ g_free (path);
+
+ return res;
+}
+
+GHashTable *
+caja_trashed_files_get_original_directories (GList *files,
+ GList **unhandled_files)
+{
+ GHashTable *directories;
+ CajaFile *file, *original_file, *original_dir;
+ GList *l, *m;
+
+ directories = NULL;
+
+ if (unhandled_files != NULL)
+ {
+ *unhandled_files = NULL;
+ }
+
+ for (l = files; l != NULL; l = l->next)
+ {
+ file = CAJA_FILE (l->data);
+ original_file = caja_file_get_trash_original_file (file);
+
+ original_dir = NULL;
+ if (original_file != NULL)
+ {
+ original_dir = caja_file_get_parent (original_file);
+ }
+
+ if (original_dir != NULL)
+ {
+ if (directories == NULL)
+ {
+ directories = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ (GDestroyNotify) caja_file_unref,
+ (GDestroyNotify) caja_file_list_unref);
+ }
+ caja_file_ref (original_dir);
+ m = g_hash_table_lookup (directories, original_dir);
+ if (m != NULL)
+ {
+ g_hash_table_steal (directories, original_dir);
+ caja_file_unref (original_dir);
+ }
+ m = g_list_append (m, caja_file_ref (file));
+ g_hash_table_insert (directories, original_dir, m);
+ }
+ else if (unhandled_files != NULL)
+ {
+ *unhandled_files = g_list_append (*unhandled_files, caja_file_ref (file));
+ }
+
+ if (original_file != NULL)
+ {
+ caja_file_unref (original_file);
+ }
+
+ if (original_dir != NULL)
+ {
+ caja_file_unref (original_dir);
+ }
+ }
+
+ return directories;
+}
+
+static GList *
+locations_from_file_list (GList *file_list)
+{
+ CajaFile *file;
+ GList *l, *ret;
+
+ ret = NULL;
+
+ for (l = file_list; l != NULL; l = l->next)
+ {
+ file = CAJA_FILE (l->data);
+ ret = g_list_prepend (ret, caja_file_get_location (file));
+ }
+
+ return g_list_reverse (ret);
+}
+
+void
+caja_restore_files_from_trash (GList *files,
+ GtkWindow *parent_window)
+{
+ CajaFile *file, *original_dir;
+ GHashTable *original_dirs_hash;
+ GList *original_dirs, *unhandled_files;
+ GFile *original_dir_location;
+ GList *locations, *l;
+ char *message, *file_name;
+
+ original_dirs_hash = caja_trashed_files_get_original_directories (files, &unhandled_files);
+
+ for (l = unhandled_files; l != NULL; l = l->next)
+ {
+ file = CAJA_FILE (l->data);
+ file_name = caja_file_get_display_name (file);
+ message = g_strdup_printf (_("Could not determine original location of \"%s\" "), file_name);
+ g_free (file_name);
+
+ eel_show_warning_dialog (message,
+ _("The item cannot be restored from trash"),
+ parent_window);
+ g_free (message);
+ }
+
+ if (original_dirs_hash != NULL)
+ {
+ original_dirs = g_hash_table_get_keys (original_dirs_hash);
+ for (l = original_dirs; l != NULL; l = l->next)
+ {
+ original_dir = CAJA_FILE (l->data);
+ original_dir_location = caja_file_get_location (original_dir);
+
+ files = g_hash_table_lookup (original_dirs_hash, original_dir);
+ locations = locations_from_file_list (files);
+
+ caja_file_operations_move
+ (locations, NULL,
+ original_dir_location,
+ parent_window,
+ NULL, NULL);
+
+ eel_g_object_list_free (locations);
+ g_object_unref (original_dir_location);
+ }
+
+ g_list_free (original_dirs);
+ g_hash_table_destroy (original_dirs_hash);
+ }
+
+ caja_file_list_unref (unhandled_files);
+}
+
+#if !defined (CAJA_OMIT_SELF_CHECK)
+
+void
+caja_self_check_file_utilities (void)
+{
+}
+
+#endif /* !CAJA_OMIT_SELF_CHECK */