diff options
-rw-r--r-- | ChangeLog | 12 | ||||
-rw-r--r-- | common/Makefile.am | 1 | ||||
-rw-r--r-- | common/gvfsmountinfo.c | 712 | ||||
-rw-r--r-- | common/gvfsmountinfo.h | 52 | ||||
-rw-r--r-- | monitor/hal/ghalmount.c | 563 |
5 files changed, 864 insertions, 476 deletions
@@ -1,3 +1,15 @@ +2009-03-02 David Zeuthen <davidz@redhat.com> + + Bug 551403 – Support Reading Volume Icon/Name Information + from .xdg-volume-info + + * common/Makefile.am: + * common/gvfsmountinfo.[ch]: Move autorun file detection to common + library. Also add routines for detecting .xdg-volume-info files. + + * monitor/hal/ghalmount.c: Use g_mount_info*() functions for + detecting autorun and .xdg-volume-info files. + 2009-03-02 Paolo Borelli <pborelli@katamail.com> * daemon/gvfsbackendarchive.c: diff --git a/common/Makefile.am b/common/Makefile.am index 380dc654..e8e44d89 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -17,6 +17,7 @@ libgvfscommon_la_SOURCES = \ gmounttracker.c gmounttracker.h \ gvfsdaemonprotocol.c gvfsdaemonprotocol.h \ gvfsicon.h gvfsicon.c \ + gvfsmountinfo.h gvfsmountinfo.c \ gvfsfileinfo.c gvfsfileinfo.h \ $(NULL) diff --git a/common/gvfsmountinfo.c b/common/gvfsmountinfo.c new file mode 100644 index 00000000..585e3bc7 --- /dev/null +++ b/common/gvfsmountinfo.c @@ -0,0 +1,712 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#include <config.h> +#include <string.h> +#include <glib/gi18n-lib.h> + +#include "gvfsmountinfo.h" + +static GFile * +_g_find_file_insensitive_finish (GFile *parent, + GAsyncResult *result, + GError **error); + +static void +_g_find_file_insensitive_async (GFile *parent, + const gchar *name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +static void +on_icon_file_located (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GFile *icon_file; + GError *error; + + error = NULL; + icon_file = _g_find_file_insensitive_finish (G_FILE (source_object), + res, + &error); + if (icon_file != NULL) + { + g_simple_async_result_set_op_res_gpointer (simple, g_file_icon_new (icon_file), NULL); + g_object_unref (icon_file); + } + else + { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +on_autorun_loaded (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GFile *autorun_file; + gchar *content; + gchar *relative_icon_path; + gsize content_length; + GError *error; + + relative_icon_path = NULL; + + autorun_file = G_FILE (source_object); + + error = NULL; + if (g_file_load_contents_finish (autorun_file, + res, + &content, + &content_length, + NULL, + &error)) + { + /* Scan through for an "icon=" line. Can't use GKeyFile, + * because .inf files aren't always valid key files + **/ + GRegex *icon_regex; + GMatchInfo *match_info; + + /* [^,] is because sometimes the icon= line + * has a comma at the end + **/ + icon_regex = g_regex_new ("icon=([^,\\r\\n]+)", + G_REGEX_CASELESS, 0, NULL); + g_regex_match (icon_regex, content, 0, + &match_info); + + /* Even if there are multiple matches, pick only the + * first. + **/ + if (g_match_info_matches (match_info)) + { + gchar *chr; + gchar *word = g_match_info_fetch (match_info, 1); + + /* Replace '\' with '/' */ + while ((chr = strchr (word, '\\')) != NULL) + *chr = '/'; + + /* If the file name's not valid UTF-8, + * don't even try to load it + **/ + if (g_utf8_validate (word, -1, NULL)) + { + relative_icon_path = word; + } + else + { + /* TODO: mark for translation. Strictly, this isn't very important; this string + * will never be displayed since all current users of g_vfs_mount_info_query_autorun_info() + * passes NULL for the GError**. + */ + error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_FAILED, + "Icon name is not valid UTF-8"); + g_free (word); + } + } + + g_match_info_free (match_info); + + g_regex_unref (icon_regex); + g_free (content); + } + + /* some autorun.in points to the .exe file for the icon; make sure we avoid using that */ + if (relative_icon_path != NULL) + { + if (!g_str_has_suffix (relative_icon_path, ".exe")) + { + GFile *root; + + root = g_file_get_parent (autorun_file); + + _g_find_file_insensitive_async (root, + relative_icon_path, + NULL, + on_icon_file_located, + simple); + + g_object_unref (root); + } + else + { + /* TODO: mark for translation. Strictly, this isn't very important; this string + * will never be displayed since all current users of g_vfs_mount_info_query_autorun_info() + * passes NULL for the GError**. + */ + error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_FAILED, + "Icon is an .exe file"); + } + } + + if (error != NULL) + { + g_simple_async_result_set_from_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + g_error_free (error); + } + + g_free (relative_icon_path); +} + +static void +on_autorun_located (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GFile *autorun_path; + GError *error; + + error = NULL; + autorun_path = _g_find_file_insensitive_finish (G_FILE (source_object), + res, + &error); + if (error != NULL) + { + g_simple_async_result_set_from_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + g_error_free (error); + } + else + { + g_file_load_contents_async (autorun_path, + g_object_get_data (G_OBJECT (simple), "cancellable"), + on_autorun_loaded, + simple); + g_object_unref (autorun_path); + } +} + +void +g_vfs_mount_info_query_autorun_info (GFile *directory, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (directory), + callback, + user_data, + g_vfs_mount_info_query_autorun_info); + + if (cancellable != NULL) + g_object_set_data_full (G_OBJECT (simple), "cancellable", g_object_ref (cancellable), g_object_unref); + + _g_find_file_insensitive_async (directory, + "autorun.inf", + cancellable, + on_autorun_located, + simple); +} + +GIcon * +g_vfs_mount_info_query_autorun_info_finish (GFile *directory, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GIcon *ret; + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_vfs_mount_info_query_autorun_info); + + ret = NULL; + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + ret = g_simple_async_result_get_op_res_gpointer (simple); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_xdg_volume_info_loaded (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GFile *xdg_volume_info_file; + gchar *content; + gsize content_length; + GError *error; + GKeyFile *key_file; + gchar *name; + gchar *icon_name; + GIcon *icon; + + content = NULL; + key_file = NULL; + name = NULL; + icon_name = NULL; + + xdg_volume_info_file = G_FILE (source_object); + + error = NULL; + if (g_file_load_contents_finish (xdg_volume_info_file, + res, + &content, + &content_length, + NULL, + &error)) + { + key_file = g_key_file_new (); + if (!g_key_file_load_from_data (key_file, + content, + content_length, + G_KEY_FILE_NONE, + &error)) + goto out; + + + name = g_key_file_get_locale_string (key_file, + "XDG Volume Info", + "Name", + NULL, + NULL); + + icon_name = g_key_file_get_locale_string (key_file, + "XDG Volume Info", + "Icon", + NULL, + NULL); + + if (icon_name != NULL) + { + icon = g_themed_icon_new (icon_name); + g_themed_icon_append_name (G_THEMED_ICON (icon), "drive-removable-media"); + g_themed_icon_append_name (G_THEMED_ICON (icon), "drive-removable"); + g_themed_icon_append_name (G_THEMED_ICON (icon), "drive"); + } + else + { + icon = NULL; + } + + g_simple_async_result_set_op_res_gpointer (simple, icon, NULL); + g_object_set_data_full (G_OBJECT (simple), "name", name, g_free); + name = NULL; /* steals name */ + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + } + + out: + + if (key_file != NULL) + g_key_file_free (key_file); + + if (error != NULL) + { + g_simple_async_result_set_from_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + g_error_free (error); + } + + g_free (name); + g_free (icon_name); + g_free (content); +} + +void +g_vfs_mount_info_query_xdg_volume_info (GFile *directory, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + GFile *file; + + simple = g_simple_async_result_new (G_OBJECT (directory), + callback, + user_data, + g_vfs_mount_info_query_xdg_volume_info); + + file = g_file_resolve_relative_path (directory, ".xdg-volume-info"); + g_file_load_contents_async (file, + cancellable, + on_xdg_volume_info_loaded, + simple); + g_object_unref (file); +} + +GIcon *g_vfs_mount_info_query_xdg_volume_info_finish (GFile *directory, + GAsyncResult *res, + gchar **out_name, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GIcon *ret; + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_vfs_mount_info_query_xdg_volume_info); + + ret = NULL; + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + ret = g_simple_async_result_get_op_res_gpointer (simple); + + if (out_name != NULL) + *out_name = g_strdup (g_object_get_data (G_OBJECT (simple), "name")); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +#define INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK 100 + +static void +enumerated_children_callback (GObject *source_object, GAsyncResult *res, + gpointer user_data); + +static void +more_files_callback (GObject *source_object, GAsyncResult *res, + gpointer user_data); + +static void +find_file_insensitive_exists_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +typedef struct _InsensitiveFileSearchData +{ + GFile *root; + gchar *original_path; + gchar **split_path; + gint index; + GFileEnumerator *enumerator; + GFile *current_file; + + GCancellable *cancellable; + GAsyncReadyCallback callback; + gpointer user_data; +} InsensitiveFileSearchData; + +static void +_g_find_file_insensitive_async (GFile *parent, + const gchar *name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + InsensitiveFileSearchData *data; + GFile *direct_file = g_file_get_child (parent, name); + + data = g_new0 (InsensitiveFileSearchData, 1); + data->cancellable = cancellable; + data->callback = callback; + data->user_data = user_data; + data->root = g_object_ref (parent); + data->original_path = g_strdup (name); + + g_file_query_info_async (direct_file, G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, + cancellable, + find_file_insensitive_exists_callback, data); + + +} + +static void +clear_find_file_insensitive_state (InsensitiveFileSearchData *data) +{ + if (data->root) + g_object_unref (data->root); + g_free (data->original_path); + if (data->split_path) + g_strfreev (data->split_path); + if (data->enumerator) + g_object_unref (data->enumerator); + if (data->current_file) + g_object_unref (data->current_file); + g_free (data); +} + +static void +find_file_insensitive_exists_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GFileInfo *info; + InsensitiveFileSearchData *data = (InsensitiveFileSearchData *) (user_data); + + /* The file exists and can be found with the given path, no need to search. */ + if ((info = g_file_query_info_finish (G_FILE (source_object), res, NULL))) + { + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (data->root), + data->callback, + data->user_data, + _g_find_file_insensitive_async); + + g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (source_object), g_object_unref); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + clear_find_file_insensitive_state (data); + } + + else + { + data->split_path = g_strsplit (data->original_path, G_DIR_SEPARATOR_S, -1); + data->index = 0; + data->enumerator = NULL; + data->current_file = g_object_ref (data->root); + + /* Skip any empty components due to multiple slashes */ + while (data->split_path[data->index] != NULL && + *data->split_path[data->index] == 0) + data->index++; + + g_file_enumerate_children_async (data->current_file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + 0, G_PRIORITY_DEFAULT, + data->cancellable, + enumerated_children_callback, data); + } + + g_object_unref (source_object); +} + +static void +enumerated_children_callback (GObject *source_object, GAsyncResult *res, + gpointer user_data) +{ + GFileEnumerator *enumerator; + InsensitiveFileSearchData *data = (InsensitiveFileSearchData *) (user_data); + + enumerator = g_file_enumerate_children_finish (G_FILE (source_object), + res, NULL); + + if (enumerator == NULL) + { + GSimpleAsyncResult *simple; + GFile *file; + + simple = g_simple_async_result_new (G_OBJECT (data->root), + data->callback, + data->user_data, + _g_find_file_insensitive_async); + + file = g_file_get_child (data->root, data->original_path); + + g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (file), g_object_unref); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + clear_find_file_insensitive_state (data); + return; + } + + data->enumerator = enumerator; + g_file_enumerator_next_files_async (enumerator, + INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK, + G_PRIORITY_DEFAULT, + data->cancellable, + more_files_callback, + data); +} + +static void +more_files_callback (GObject *source_object, GAsyncResult *res, + gpointer user_data) +{ + InsensitiveFileSearchData *data = (InsensitiveFileSearchData *) (user_data); + GList *files, *l; + gchar *filename = NULL, *component, *case_folded_name, + *name_collation_key; + gboolean end_of_files, is_utf8; + + files = g_file_enumerator_next_files_finish (data->enumerator, + res, NULL); + + end_of_files = files == NULL; + + component = data->split_path[data->index]; + g_return_if_fail (component != NULL); + + is_utf8 = (g_utf8_validate (component, -1, NULL)); + if (is_utf8) + { + case_folded_name = g_utf8_casefold (component, -1); + name_collation_key = g_utf8_collate_key (case_folded_name, -1); + g_free (case_folded_name); + } + + else + { + name_collation_key = g_ascii_strdown (component, -1); + } + + for (l = files; l != NULL; l = l->next) + { + GFileInfo *info; + const gchar *this_name; + gchar *key; + + info = l->data; + this_name = g_file_info_get_name (info); + + if (is_utf8 && g_utf8_validate (this_name, -1, NULL)) + { + gchar *case_folded; + case_folded = g_utf8_casefold (this_name, -1); + key = g_utf8_collate_key (case_folded, -1); + g_free (case_folded); + } + else + { + key = g_ascii_strdown (this_name, -1); + } + + if (strcmp (key, name_collation_key) == 0) + filename = g_strdup (this_name); + g_free (key); + + if (filename) + break; + } + + g_list_foreach (files, (GFunc)g_object_unref, NULL); + g_list_free (files); + g_free (name_collation_key); + + if (filename) + { + GFile *next_file; + + g_file_enumerator_close_async (data->enumerator, + G_PRIORITY_DEFAULT, + data->cancellable, + NULL, NULL); + g_object_unref (data->enumerator); + data->enumerator = NULL; + + /* Set the current file and continue searching */ + next_file = g_file_get_child (data->current_file, filename); + g_free (filename); + g_object_unref (data->current_file); + data->current_file = next_file; + + data->index++; + /* Skip any empty components due to multiple slashes */ + while (data->split_path[data->index] != NULL && + *data->split_path[data->index] == 0) + data->index++; + + if (data->split_path[data->index] == NULL) + { + /* Search is complete, file was found */ + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (data->root), + data->callback, + data->user_data, + _g_find_file_insensitive_async); + + g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (data->current_file), g_object_unref); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + clear_find_file_insensitive_state (data); + return; + } + + /* Continue searching down the tree */ + g_file_enumerate_children_async (data->current_file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + 0, G_PRIORITY_DEFAULT, + data->cancellable, + enumerated_children_callback, + data); + return; + } + + if (end_of_files) + { + /* Could not find the given file, abort the search */ + GSimpleAsyncResult *simple; + GFile *file; + + g_object_unref (data->enumerator); + data->enumerator = NULL; + + simple = g_simple_async_result_new (G_OBJECT (data->root), + data->callback, + data->user_data, + _g_find_file_insensitive_async); + + file = g_file_get_child (data->root, data->original_path); + g_simple_async_result_set_op_res_gpointer (simple, file, g_object_unref); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + clear_find_file_insensitive_state (data); + return; + } + + /* Continue enumerating */ + g_file_enumerator_next_files_async (data->enumerator, + INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK, + G_PRIORITY_DEFAULT, + data->cancellable, + more_files_callback, + data); +} + +static GFile * +_g_find_file_insensitive_finish (GFile *parent, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + GFile *file; + + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + file = G_FILE (g_simple_async_result_get_op_res_gpointer (simple)); + return g_object_ref (file); +} diff --git a/common/gvfsmountinfo.h b/common/gvfsmountinfo.h new file mode 100644 index 00000000..6879f04a --- /dev/null +++ b/common/gvfsmountinfo.h @@ -0,0 +1,52 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#ifndef __G_VFS_MOUNT_INFO_H__ +#define __G_VFS_MOUNT_INFO_H__ + +#include <gio/gio.h> + +G_BEGIN_DECLS + +void g_vfs_mount_info_query_autorun_info (GFile *directory, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GIcon *g_vfs_mount_info_query_autorun_info_finish (GFile *directory, + GAsyncResult *res, + GError **error); + +void g_vfs_mount_info_query_xdg_volume_info (GFile *directory, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GIcon *g_vfs_mount_info_query_xdg_volume_info_finish (GFile *directory, + GAsyncResult *res, + gchar **out_name, + GError **error); + +G_END_DECLS + +#endif /* __G_VFS_MOUNT_INFO_H__ */ diff --git a/monitor/hal/ghalmount.c b/monitor/hal/ghalmount.c index b71977ab..f85dfe09 100644 --- a/monitor/hal/ghalmount.c +++ b/monitor/hal/ghalmount.c @@ -31,6 +31,8 @@ #include <glib/gi18n-lib.h> #include <gio/gio.h> +#include <gvfsmountinfo.h> + #include "ghalvolumemonitor.h" #include "ghalmount.h" #include "ghalvolume.h" @@ -57,27 +59,22 @@ struct _GHalMount { GIcon *override_icon; GFile *override_root; gboolean cannot_unmount; - gboolean searched_for_icon; HalDevice *device; HalDevice *drive_device; -}; - -static GFile * -_g_find_file_insensitive_finish (GFile *parent, - GAsyncResult *result, - GError **error); -static void -_g_find_file_insensitive_async (GFile *parent, - const gchar *name, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); + GIcon *autorun_icon; + gboolean searched_for_autorun; + gchar *xdg_volume_info_name; + GIcon *xdg_volume_info_icon; + gboolean searched_for_xdg_volume_info; +}; static GFile *get_root (GHalMount *hal_mount); +static void update_from_hal (GHalMount *m, gboolean emit_changed); + static void g_hal_mount_mount_iface_init (GMountIface *iface); G_DEFINE_TYPE_EXTENDED (GHalMount, g_hal_mount, G_TYPE_OBJECT, 0, @@ -114,6 +111,13 @@ g_hal_mount_finalize (GObject *object) if (mount->override_root != NULL) g_object_unref (mount->override_root); + if (mount->autorun_icon != NULL) + g_object_unref (mount->autorun_icon); + + g_free (mount->xdg_volume_info_name); + if (mount->xdg_volume_info_icon != NULL) + g_object_unref (mount->xdg_volume_info_icon); + if (mount->volume_monitor != NULL) g_object_remove_weak_pointer (G_OBJECT (mount->volume_monitor), (gpointer) &(mount->volume_monitor)); @@ -147,144 +151,6 @@ changed_in_idle (gpointer data) return FALSE; } -typedef struct _MountIconSearchData -{ - GHalMount *mount; - GFile *root; -} MountIconSearchData; - -static void -clear_icon_search_data (MountIconSearchData *data) -{ - if (data->mount) - g_object_unref (data->mount); - if (data->root) - g_object_unref (data->root); - g_free (data); -} - -static void -on_icon_file_located (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - GFile *icon_file; - GIcon *icon; - MountIconSearchData *data = (MountIconSearchData *) (user_data); - - icon_file = _g_find_file_insensitive_finish (G_FILE (source_object), - res, NULL); - - /* TODO: check if the file actually exists? */ - - icon = g_file_icon_new (icon_file); - g_object_unref (icon_file); - - g_hal_mount_override_icon (data->mount, icon); - g_object_unref (icon); - - clear_icon_search_data (data); -} - -static void -on_autorun_loaded (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - gchar *content, *relative_icon_path = NULL; - gsize content_length; - MountIconSearchData *data = (MountIconSearchData *) (user_data); - - if (g_file_load_contents_finish (G_FILE (source_object), res, &content, - &content_length, NULL, NULL)) - { - /* Scan through for an "icon=" line. Can't use GKeyFile, - * because .inf files aren't always valid key files - **/ - GRegex *icon_regex; - GMatchInfo *match_info; - - /* [^,] is because sometimes the icon= line - * has a comma at the end - **/ - icon_regex = g_regex_new ("icon=([^,\\r\\n]+)", - G_REGEX_CASELESS, 0, NULL); - g_regex_match (icon_regex, content, 0, - &match_info); - - /* Even if there are multiple matches, pick only the - * first. - **/ - if (g_match_info_matches (match_info)) - { - gchar *chr; - gchar *word = g_match_info_fetch (match_info, 1); - - /* Replace '\' with '/' */ - while ((chr = strchr (word, '\\')) != NULL) - *chr = '/'; - - /* If the file name's not valid UTF-8, - * don't even try to load it - **/ - if (g_utf8_validate (word, -1, NULL)) - relative_icon_path = word; - else - g_free (word); - } - - g_match_info_free (match_info); - - g_regex_unref (icon_regex); - g_free (content); - } - - /* some autorun.in points to the .exe file for the icon; make sure we avoid using that */ - if (relative_icon_path && !g_str_has_suffix (relative_icon_path, ".exe")) - { - _g_find_file_insensitive_async (data->root, - relative_icon_path, - NULL, on_icon_file_located, - data); - - g_free (relative_icon_path); - } - else - clear_icon_search_data (data); -} - -static void -on_autorun_located (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - GFile *autorun_path; - MountIconSearchData *data = (MountIconSearchData *) (user_data); - - autorun_path = _g_find_file_insensitive_finish (G_FILE (source_object), - res, NULL); - if (autorun_path) - g_file_load_contents_async (autorun_path, NULL, on_autorun_loaded, data); - else - clear_icon_search_data (data); - - g_object_unref (autorun_path); -} - -static void -_g_find_mount_icon (GHalMount *m) -{ - MountIconSearchData *search_data; - - m->searched_for_icon = TRUE; - - search_data = g_new0 (MountIconSearchData, 1); - search_data->mount = g_object_ref (m); - search_data->root = get_root (m); - - _g_find_file_insensitive_async (search_data->root, - "autorun.inf", - NULL, on_autorun_located, - search_data); -} - #define KILOBYTE_FACTOR 1000.0 #define MEGABYTE_FACTOR (1000.0 * 1000.0) #define GIGABYTE_FACTOR (1000.0 * 1000.0 * 1000.0) @@ -315,6 +181,38 @@ format_size_for_display (guint64 size) } static void +got_autorun_info_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GHalMount *mount = G_HAL_MOUNT (user_data); + + mount->autorun_icon = g_vfs_mount_info_query_autorun_info_finish (G_FILE (source_object), + res, + NULL); + + update_from_hal (mount, TRUE); + + g_object_unref (mount); +} + +static void +got_xdg_volume_info_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GHalMount *mount = G_HAL_MOUNT (user_data); + + mount->xdg_volume_info_icon = g_vfs_mount_info_query_xdg_volume_info_finish (G_FILE (source_object), + res, + &(mount->xdg_volume_info_name), + NULL); + update_from_hal (mount, TRUE); + + g_object_unref (mount); +} + +static void do_update_from_hal (GHalMount *m) { HalDevice *volume; @@ -447,7 +345,13 @@ do_update_from_hal (GHalMount *m) g_free (size); } - if (m->override_name != NULL) + /* order of preference : xdg, override, probed */ + if (m->xdg_volume_info_name != NULL) + { + m->name = g_strdup (m->xdg_volume_info_name); + g_free (name); + } + else if (m->override_name != NULL) { m->name = g_strdup (m->override_name); g_free (name); @@ -455,17 +359,42 @@ do_update_from_hal (GHalMount *m) else m->name = name; - if (m->override_icon != NULL) + /* order of preference: xdg, autorun, override, probed */ + if (m->xdg_volume_info_icon != NULL) + m->icon = g_object_ref (m->xdg_volume_info_icon); + else if (m->autorun_icon != NULL) + m->icon = g_object_ref (m->autorun_icon); + else if (m->override_icon != NULL) m->icon = g_object_ref (m->override_icon); else m->icon = get_themed_icon_with_fallbacks (icon_name, icon_name_fallback); - /* If this is a CD-ROM, begin searching for an icon specified in - * autorun.inf. - **/ - if (strcmp (drive_type, "cdrom") == 0 && !m->searched_for_icon) - _g_find_mount_icon (m); + /* search for .xdg-volume-info */ + if (!m->searched_for_xdg_volume_info) + { + GFile *root; + root = get_root (m); + m->searched_for_xdg_volume_info = TRUE; + g_vfs_mount_info_query_xdg_volume_info (root, + NULL, + got_xdg_volume_info_cb, + g_object_ref (m)); + g_object_unref (root); + } + + /* search for autorun.inf */ + if (!m->searched_for_autorun) + { + GFile *root; + root = get_root (m); + m->searched_for_autorun = TRUE; + g_vfs_mount_info_query_autorun_info (root, + NULL, + got_autorun_info_cb, + g_object_ref (m)); + g_object_unref (root); + } } @@ -1272,321 +1201,3 @@ g_hal_mount_mount_iface_init (GMountIface *iface) iface->guess_content_type_finish = g_hal_mount_guess_content_type_finish; iface->guess_content_type_sync = g_hal_mount_guess_content_type_sync; } - -#define INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK 100 - -static void -enumerated_children_callback (GObject *source_object, GAsyncResult *res, - gpointer user_data); - -static void -more_files_callback (GObject *source_object, GAsyncResult *res, - gpointer user_data); - -static void -find_file_insensitive_exists_callback (GObject *source_object, - GAsyncResult *res, - gpointer user_data); - -typedef struct _InsensitiveFileSearchData -{ - GFile *root; - gchar *original_path; - gchar **split_path; - gint index; - GFileEnumerator *enumerator; - GFile *current_file; - - GCancellable *cancellable; - GAsyncReadyCallback callback; - gpointer user_data; -} InsensitiveFileSearchData; - -static void -_g_find_file_insensitive_async (GFile *parent, - const gchar *name, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - InsensitiveFileSearchData *data; - GFile *direct_file = g_file_get_child (parent, name); - - data = g_new0 (InsensitiveFileSearchData, 1); - data->cancellable = cancellable; - data->callback = callback; - data->user_data = user_data; - data->root = g_object_ref (parent); - data->original_path = g_strdup (name); - - g_file_query_info_async (direct_file, G_FILE_ATTRIBUTE_STANDARD_TYPE, - G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, - cancellable, - find_file_insensitive_exists_callback, data); - - -} - -static void -clear_find_file_insensitive_state (InsensitiveFileSearchData *data) -{ - if (data->root) - g_object_unref (data->root); - g_free (data->original_path); - if (data->split_path) - g_strfreev (data->split_path); - if (data->enumerator) - g_object_unref (data->enumerator); - if (data->current_file) - g_object_unref (data->current_file); - g_free (data); -} - -static void -find_file_insensitive_exists_callback (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GFileInfo *info; - InsensitiveFileSearchData *data = (InsensitiveFileSearchData *) (user_data); - - /* The file exists and can be found with the given path, no need to search. */ - if ((info = g_file_query_info_finish (G_FILE (source_object), res, NULL))) - { - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (G_OBJECT (data->root), - data->callback, - data->user_data, - _g_find_file_insensitive_async); - - g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (source_object), g_object_unref); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - clear_find_file_insensitive_state (data); - } - - else - { - data->split_path = g_strsplit (data->original_path, G_DIR_SEPARATOR_S, -1); - data->index = 0; - data->enumerator = NULL; - data->current_file = g_object_ref (data->root); - - /* Skip any empty components due to multiple slashes */ - while (data->split_path[data->index] != NULL && - *data->split_path[data->index] == 0) - data->index++; - - g_file_enumerate_children_async (data->current_file, - G_FILE_ATTRIBUTE_STANDARD_NAME, - 0, G_PRIORITY_DEFAULT, - data->cancellable, - enumerated_children_callback, data); - } - - g_object_unref (source_object); -} - -static void -enumerated_children_callback (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - GFileEnumerator *enumerator; - InsensitiveFileSearchData *data = (InsensitiveFileSearchData *) (user_data); - - enumerator = g_file_enumerate_children_finish (G_FILE (source_object), - res, NULL); - - if (enumerator == NULL) - { - GSimpleAsyncResult *simple; - GFile *file; - - simple = g_simple_async_result_new (G_OBJECT (data->root), - data->callback, - data->user_data, - _g_find_file_insensitive_async); - - file = g_file_get_child (data->root, data->original_path); - - g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (file), g_object_unref); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - clear_find_file_insensitive_state (data); - return; - } - - data->enumerator = enumerator; - g_file_enumerator_next_files_async (enumerator, - INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK, - G_PRIORITY_DEFAULT, - data->cancellable, - more_files_callback, - data); -} - -static void -more_files_callback (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - InsensitiveFileSearchData *data = (InsensitiveFileSearchData *) (user_data); - GList *files, *l; - gchar *filename = NULL, *component, *case_folded_name, - *name_collation_key; - gboolean end_of_files, is_utf8; - - files = g_file_enumerator_next_files_finish (data->enumerator, - res, NULL); - - end_of_files = files == NULL; - - component = data->split_path[data->index]; - g_return_if_fail (component != NULL); - - is_utf8 = (g_utf8_validate (component, -1, NULL)); - if (is_utf8) - { - case_folded_name = g_utf8_casefold (component, -1); - name_collation_key = g_utf8_collate_key (case_folded_name, -1); - g_free (case_folded_name); - } - - else - { - name_collation_key = g_ascii_strdown (component, -1); - } - - for (l = files; l != NULL; l = l->next) - { - GFileInfo *info; - const gchar *this_name; - gchar *key; - - info = l->data; - this_name = g_file_info_get_name (info); - - if (is_utf8 && g_utf8_validate (this_name, -1, NULL)) - { - gchar *case_folded; - case_folded = g_utf8_casefold (this_name, -1); - key = g_utf8_collate_key (case_folded, -1); - g_free (case_folded); - } - else - { - key = g_ascii_strdown (this_name, -1); - } - - if (strcmp (key, name_collation_key) == 0) - filename = g_strdup (this_name); - g_free (key); - - if (filename) - break; - } - - g_list_foreach (files, (GFunc)g_object_unref, NULL); - g_list_free (files); - g_free (name_collation_key); - - if (filename) - { - GFile *next_file; - - g_file_enumerator_close_async (data->enumerator, - G_PRIORITY_DEFAULT, - data->cancellable, - NULL, NULL); - g_object_unref (data->enumerator); - data->enumerator = NULL; - - /* Set the current file and continue searching */ - next_file = g_file_get_child (data->current_file, filename); - g_free (filename); - g_object_unref (data->current_file); - data->current_file = next_file; - - data->index++; - /* Skip any empty components due to multiple slashes */ - while (data->split_path[data->index] != NULL && - *data->split_path[data->index] == 0) - data->index++; - - if (data->split_path[data->index] == NULL) - { - /* Search is complete, file was found */ - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (G_OBJECT (data->root), - data->callback, - data->user_data, - _g_find_file_insensitive_async); - - g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (data->current_file), g_object_unref); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - clear_find_file_insensitive_state (data); - return; - } - - /* Continue searching down the tree */ - g_file_enumerate_children_async (data->current_file, - G_FILE_ATTRIBUTE_STANDARD_NAME, - 0, G_PRIORITY_DEFAULT, - data->cancellable, - enumerated_children_callback, - data); - return; - } - - if (end_of_files) - { - /* Could not find the given file, abort the search */ - GSimpleAsyncResult *simple; - GFile *file; - - g_object_unref (data->enumerator); - data->enumerator = NULL; - - simple = g_simple_async_result_new (G_OBJECT (data->root), - data->callback, - data->user_data, - _g_find_file_insensitive_async); - - file = g_file_get_child (data->root, data->original_path); - g_simple_async_result_set_op_res_gpointer (simple, file, g_object_unref); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - clear_find_file_insensitive_state (data); - return; - } - - /* Continue enumerating */ - g_file_enumerator_next_files_async (data->enumerator, - INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK, - G_PRIORITY_DEFAULT, - data->cancellable, - more_files_callback, - data); -} - -static GFile * -_g_find_file_insensitive_finish (GFile *parent, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - GFile *file; - - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - file = G_FILE (g_simple_async_result_get_op_res_gpointer (simple)); - return g_object_ref (file); -} - |