autorun.inf and .xdg-volume-info



Hey,

Here's a patch that

 o  moves the autorun.inf scanning code to common/ and fixes
    up the API to be nicer
    - Want to use this in the upcoming gdu/DKD volume monitor

 o  adds support for reading info from .xdg-volume-info files, see
    http://bugzilla.gnome.org/show_bug.cgi?id=551403
    for details

 o  enables reading autorun.inf on all mounts managed by the hal
    volume monitor; not just cd-rom's
    - the main reason we didn't originally had this was that the
      autorun.inf code was added before the hal volume monitor
      was moved into a single process

 o  ports the hal volume monitor to use both autorun.inf and
    .xdg-volume-info to set name and/or icon. The latter wins
    in case both files are there.

Not sure gvfs_mount_info_* is the best prefix for this kind of stuff;
feel free to change it to whatever.

$ diffstat gvfs-xdg-volume-info.patch 
 common/Makefile.am      |    1 
 common/gvfsmountinfo.c  |  691 +++++++++++++++++++++++++++++++++++++++
 common/gvfsmountinfo.h  |   52 +++
 monitor/hal/ghalmount.c |  563 ++++++---------------------------------
 4 files changed, 831 insertions(+), 476 deletions(-)

Thanks,
David

Index: monitor/hal/ghalmount.c
===================================================================
--- monitor/hal/ghalmount.c	(revision 2252)
+++ monitor/hal/ghalmount.c	(working copy)
@@ -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 @@
   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);
+  GIcon *autorun_icon;
+  gboolean searched_for_autorun;
 
-static void
-_g_find_file_insensitive_async (GFile              *parent,
-                                const gchar        *name,
-                                GCancellable       *cancellable,
-                                GAsyncReadyCallback callback,
-                                gpointer            user_data);
+  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 @@
   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 @@
   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 @@
 }
 
 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,25 +345,56 @@
       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);
     }
   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 @@
   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);
-}
-
Index: common/Makefile.am
===================================================================
--- common/Makefile.am	(revision 2252)
+++ common/Makefile.am	(working copy)
@@ -17,6 +17,7 @@
 	gmounttracker.c gmounttracker.h \
 	gvfsdaemonprotocol.c gvfsdaemonprotocol.h \
 	gvfsicon.h gvfsicon.c \
+	gvfsmountinfo.h gvfsmountinfo.c \
 	$(NULL)
 
 libgvfscommon_la_LIBADD =	\
--- /dev/null	2009-02-26 09:29:21.065026599 -0500
+++ common/gvfsmountinfo.h	2009-02-26 16:44:37.000000000 -0500
@@ -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__ */
--- /dev/null	2009-02-26 09:29:21.065026599 -0500
+++ common/gvfsmountinfo.c	2009-02-26 16:44:33.000000000 -0500
@@ -0,0 +1,691 @@
+/* -*- 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
+            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 && !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
+    {
+      error = g_error_new_literal (G_IO_ERROR,
+                                   G_IO_ERROR_FAILED,
+                                   "Icon is a .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,
+                                           &error);
+      if (error != NULL)
+        goto out;
+
+      icon_name = g_key_file_get_locale_string (key_file,
+                                                "XDG Volume Info",
+                                                "icon",
+                                                NULL,
+                                                &error);
+      if (error != NULL)
+        goto out;
+
+      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");
+
+      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);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]