summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2009-03-02 15:38:07 +0000
committerDavid Zeuthen <davidz@src.gnome.org>2009-03-02 15:38:07 +0000
commitdb9cf32482ffae53c9d4454d702315d6e577dab3 (patch)
treebd6e4ac85ab725f93dbabbe29169121725536bd1
parent7cd67fe00a6b24f47b752b56f66515978715e8b2 (diff)
downloadgvfs-db9cf32482ffae53c9d4454d702315d6e577dab3.tar.gz
Bug 551403 – Support Reading Volume Icon/Name Information from
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. svn path=/trunk/; revision=2276
-rw-r--r--ChangeLog12
-rw-r--r--common/Makefile.am1
-rw-r--r--common/gvfsmountinfo.c712
-rw-r--r--common/gvfsmountinfo.h52
-rw-r--r--monitor/hal/ghalmount.c563
5 files changed, 864 insertions, 476 deletions
diff --git a/ChangeLog b/ChangeLog
index da444ecf..6afde932 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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);
-}
-