diff options
Diffstat (limited to 'libcaja-private/caja-file-utilities.c')
-rw-r--r-- | libcaja-private/caja-file-utilities.c | 1449 |
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 */ |