Submitted By: William Immendorf (will.immendorf@gmail.com)
Date: 2009-09-19
Initial Package Version: 0.3.101
Upstream Status: unknown
Origin: Both http://bugzilla.xfce.org/show_bug.cgi?id=2891 (first bug) and http://bugzilla.xfce.org/show_bug.cgi?id=2968. (second bug)
Descirption: This kills the two HAL birds (or bugs) with one stone (or patch). This fixes two issues:
 1. You can't speficy the mount options for hal, as they are hardcoded in exo-mount. This is troubling, espicaly if you live in a non-English speaking contry 
(in which you had to spefiy the iocharset option to hal), or if you are STILL using LFS 6.3 (which uses Linux 2.6.22.*), 
and have that slow-vfat issue that can be solved (other than GETTING A NEW VERSION!) by adding the usefree option.
 2. HAL bombards you with cryptic messages every time you press the eject button on a CD/DVD drive.
My patch fixes both, and it helps kill all the HAL releated bugs on the mouse. (my term for XFCE, who's mascot is a mouse.)

diff -Naur exo-0.3.101-old/exo-mount/exo-mount-hal.c exo-0.3.101/exo-mount/exo-mount-hal.c
--- exo-0.3.101-old/exo-mount/exo-mount-hal.c	2009-01-12 14:33:40.000000000 -0600
+++ exo-0.3.101/exo-mount/exo-mount-hal.c	2009-09-19 20:31:24.000000000 -0500
@@ -40,7 +40,7 @@
 
 #include <exo-mount/exo-mount-hal.h>
 
-
+#define CONFIG_FILE_GLOBAL     DATADIR "/xfce4/mount.rules"
 
 static gboolean exo_mount_hal_init            (GError   **error);
 static void     exo_mount_hal_propagate_error (GError   **error,
@@ -64,12 +64,17 @@
   LibHalVolumeUsage fsusage;
 };
 
+typedef struct _ExoVolumeOptions
+{
+    char** mount_options;
+    char*  fstype_override;
+} ExoVolumeOptions;
 
 
 static LibHalContext  *hal_context = NULL;
 static DBusConnection *dbus_connection = NULL;
 
-
+static gboolean exo_volume_hal_get_options( const char* fs, ExoVolumeOptions* ret );
 
 static gboolean
 exo_mount_hal_init (GError **error)
@@ -145,6 +150,42 @@
 }
 
 
+static gboolean
+string_in_list(gchar * const *haystack, const gchar *needle)
+{
+  gint n;
+
+  if (!haystack)
+      return FALSE;
+
+  for (n=0; haystack[n]; ++n) {
+      if (!strcmp (haystack[n], needle))
+          return TRUE;
+  }
+  return FALSE;
+}
+
+
+static gboolean
+device_has_interface(const gchar *udi, const gchar *iface,
+        DBusError *derror)
+{
+  gboolean result;
+  gchar **interfaces;
+
+  /* determine the info.interfaces property of the device */
+  interfaces = libhal_device_get_property_strlist (hal_context, udi,
+          "info.interfaces", derror);
+
+  /* check for the interface we need */
+  result = string_in_list(interfaces, iface);
+  libhal_free_string_array(interfaces);
+
+  return result;
+}
+
+
+
 
 /**
  * exo_mount_hal_device_from_udi:
@@ -158,18 +199,15 @@
  *               or %NULL in case of an error.
  **/
 ExoMountHalDevice*
-exo_mount_hal_device_from_udi (const gchar *udi,
+exo_mount_hal_device_from_udi (const gchar *in_udi,
                                GError     **error)
 {
   ExoMountHalDevice *device = NULL;
   DBusError          derror;
-  gchar            **interfaces;
-  gchar            **volume_udis;
-  gchar             *volume_udi = NULL;
   gint               n_volume_udis;
-  gint               n;
+  gchar             *udi;
 
-  g_return_val_if_fail (udi != NULL, NULL);
+  g_return_val_if_fail (in_udi != NULL, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
   /* make sure the HAL support is initialized */
@@ -179,55 +217,60 @@
   /* initialize D-Bus error */
   dbus_error_init (&derror);
 
-again:
-  /* determine the info.interfaces property of the device */
-  interfaces = libhal_device_get_property_strlist (hal_context, udi, "info.interfaces", &derror);
-  if (G_UNLIKELY (interfaces == NULL))
+  udi = g_strdup (in_udi);
+  /* at this point, we own udi */
+
+  /* maybe we have a mountable device here */
+  while(G_UNLIKELY (!device_has_interface (udi,
+                  "org.freedesktop.Hal.Device.Volume", &derror)))
     {
-      /* reset D-Bus error */
-      dbus_error_free (&derror);
+      gchar **volume_udis;
 
-      /* release any previous volume UDI */
-      g_free (volume_udi);
-      volume_udi = NULL;
+      /* maybe there was a D-Bus error? gotta check */
+      if (G_UNLIKELY (dbus_error_is_set (&derror)))
+        {
+          exo_mount_hal_propagate_error (error, &derror);
+          g_free (udi);
+          return NULL;
+        }
+
+      /* maybe we have a volume whose parent is identified by the udi */
+      volume_udis = libhal_manager_find_device_string_match (hal_context,
+              "info.parent", udi, &n_volume_udis, &derror);
 
-      /* ok, but maybe we have a volume whose parent is identified by the udi */
-      volume_udis = libhal_manager_find_device_string_match (hal_context, "info.parent", udi, &n_volume_udis, &derror);
       if (G_UNLIKELY (volume_udis == NULL))
         {
-err0:     exo_mount_hal_propagate_error (error, &derror);
-          goto out;
+          exo_mount_hal_propagate_error (error, &derror);
+          g_free (udi);
+          return NULL;
         }
       else if (G_UNLIKELY (n_volume_udis < 1))
         {
-          /* no match, we cannot handle that device */
           libhal_free_string_array (volume_udis);
-          goto err1;
+          dbus_error_free (&derror);
+          /* definitely not a device that we're able to
+           * mount, eject or unmount */
+          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                  _("Given device \"%s\" is not a volume or drive"), udi);
+          g_free (udi);
+          return NULL;
         }
 
+      g_free (udi);
+
       /* use the first volume UDI... */
-      volume_udi = g_strdup (volume_udis[0]);
+      udi = g_strdup (volume_udis[0]);
       libhal_free_string_array (volume_udis);
-
       /* ..and try again using that UDI */
-      udi = (const gchar *) volume_udi;
-      goto again;
     }
 
-  /* verify that we have a mountable device here */
-  for (n = 0; interfaces[n] != NULL; ++n)
-    if (strcmp (interfaces[n], "org.freedesktop.Hal.Device.Volume") == 0)
-      break;
-  if (G_UNLIKELY (interfaces[n] == NULL))
-    {
-      /* definitely not a device that we're able to mount, eject or unmount */
-err1: g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Given device \"%s\" is not a volume or drive"), udi);
-      goto out;
-    }
+  /* at this point, udi contains the UDI of something
+   * that implements Hal.Device.Volume.
+   * udi is the only resource that we hold here. */
 
   /* setup the device struct */
   device = g_new0 (ExoMountHalDevice, 1);
-  device->udi = g_strdup (udi);
+  device->udi = udi;
 
   /* check if we have a volume here */
   device->volume = libhal_volume_from_udi (hal_context, udi);
@@ -269,8 +312,8 @@
   if (G_UNLIKELY (device->file == NULL || device->name == NULL))
     {
       exo_mount_hal_device_free (device);
-      device = NULL;
-      goto err0;
+      exo_mount_hal_propagate_error(error, &derror);
+      return NULL;
     }
 
   /* check if we failed */
@@ -282,11 +325,7 @@
       device = NULL;
     }
 
-out:
-  /* cleanup */
-  libhal_free_string_array (interfaces);
-  g_free (volume_udi);
-
+  dbus_error_free (&derror);
   return device;
 }
 
@@ -313,7 +352,7 @@
   gchar            **interfaces;
   gchar            **udis;
   gint               n_udis;
-  gint               n, m;
+  gint               n;
 
   g_return_val_if_fail (g_path_is_absolute (file), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
@@ -347,12 +386,7 @@
         continue;
 
       /* check if we have a mountable device here */
-      for (m = 0; interfaces[m] != NULL; ++m)
-        if (strcmp (interfaces[m], "org.freedesktop.Hal.Device.Volume") == 0)
-          break;
-
-      /* check if it's a usable device */
-      if (interfaces[m] != NULL)
+      if (string_in_list (interfaces, "org.freedesktop.Hal.Device.Volume"))
         {
           libhal_free_string_array (interfaces);
           break;
@@ -647,48 +681,80 @@
   gchar       *fstype;
   gchar       *s;
   gint         m, n = 0;
+  ExoVolumeOptions opts;
 
   g_return_val_if_fail (device != NULL, FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-  /* determine the required mount options */
-  options = g_new0 (gchar *, 20);
-
-  /* check if we know any valid mount options */
-  if (G_LIKELY (device->fsoptions != NULL))
+    /* get mount options set by pcmanfm first */
+    if( exo_volume_hal_get_options( device->fstype, &opts ) )
     {
-      /* process all valid mount options */
-      for (m = 0; device->fsoptions[m] != NULL; ++m)
-        {
-          /* this is currently mostly Linux specific noise */
-          if (strcmp (device->fsoptions[m], "uid=") == 0
-              && (strcmp (device->fstype, "vfat") == 0
-               || strcmp (device->fstype, "iso9660") == 0
-               || strcmp (device->fstype, "udf") == 0
-               || device->volume == NULL))
-            {
-              options[n++] = g_strdup_printf ("uid=%u", (guint) getuid ());
-            }
-          else if (strcmp (device->fsoptions[m], "shortname=") == 0
-                && strcmp (device->fstype, "vfat") == 0)
+        char** popts = opts.mount_options;
+        n = g_strv_length( popts );
+        if( n > 0)
+        {
+            int i;
+            /* We have to allocate a new larger array bacause we might need to
+             * append new options to the array later */
+            options = g_new0 (gchar *, n + 4);
+            for( i = 0; i < n; ++i )
             {
-              options[n++] = g_strdup_printf ("shortname=winnt");
+                options[i] = popts[i];
+                popts[i] = NULL;
+                /* steal the string */
             }
-          else if (strcmp (device->fsoptions[m], "sync") == 0
-                && device->volume == NULL)
-            {
-              /* non-pollable drive... */
-              options[n++] = g_strdup ("sync");
-            }
-          else if (strcmp (device->fsoptions[m], "longnames") == 0
-                && strcmp (device->fstype, "vfat") == 0)
+            /* the strings in the array are already stolen, so strfreev is not needed. */
+        }
+        g_free( opts.mount_options );
+
+        fstype = opts.fstype_override;
+    }
+
+    if( G_UNLIKELY( ! options ) )
+    {
+      /* determine the required mount options */
+      options = g_new0 (gchar *, 20);
+
+      /* check if we know any valid mount options */
+      if (G_LIKELY (device->fsoptions != NULL))
+        {
+          /* process all valid mount options */
+          for (m = 0; device->fsoptions[m] != NULL; ++m)
             {
-              /* however this one is FreeBSD specific */
-              options[n++] = g_strdup ("longnames");
+              /* this is currently mostly Linux specific noise */
+              if (strcmp (device->fsoptions[m], "uid=") == 0
+                  && (strcmp (device->fstype, "vfat") == 0
+                   || strcmp (device->fstype, "iso9660") == 0
+                   || strcmp (device->fstype, "udf") == 0
+                   || device->volume == NULL))
+                {
+                  options[n++] = g_strdup_printf ("uid=%u", (guint) getuid ());
+                }
+              else if (strcmp (device->fsoptions[m], "shortname=") == 0
+                    && strcmp (device->fstype, "vfat") == 0)
+                {
+                  options[n++] = g_strdup_printf ("shortname=winnt");
+                }
+              else if (strcmp (device->fsoptions[m], "sync") == 0
+                    && device->volume == NULL)
+                {
+                  /* non-pollable drive... */
+                  options[n++] = g_strdup ("sync");
+                }
+              else if (strcmp (device->fsoptions[m], "longnames") == 0
+                    && strcmp (device->fstype, "vfat") == 0)
+                {
+                  /* however this one is FreeBSD specific */
+                  options[n++] = g_strdup ("longnames");
+                }
+              else if (strcmp (device->fsoptions[m], "locale=") == 0
+                    && strcmp (device->fstype, "ntfs-3g") == 0)
+                {
+                  options[n++] = g_strdup_printf ("locale=%s", setlocale (LC_ALL, ""));
+                }
             }
         }
     }
-
   /* try to determine a usable mount point */
   if (G_LIKELY (device->volume != NULL))
     {
@@ -703,7 +769,7 @@
 
   /* make sure that the mount point is usable (i.e. does not contain G_DIR_SEPARATOR's) */
   mount_point = (mount_point != NULL && *mount_point != '\0')
-              ? exo_str_replace (mount_point, G_DIR_SEPARATOR_S, "_") 
+              ? exo_str_replace (mount_point, G_DIR_SEPARATOR_S, "_")
               : g_strdup ("");
 
   /* let HAL guess the fstype */
@@ -835,7 +901,7 @@
   if (dbus_error_is_set (&derror))
     {
       /* try to translate the error appropriately */
-      if (strcmp (derror.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied") == 0) 
+      if (strcmp (derror.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied") == 0)
         {
           /* TRANSLATORS: User tried to mount a volume, but is not privileged to do so. */
           g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("You are not privileged to mount the volume \"%s\""), device->name);
@@ -1015,7 +1081,7 @@
   if (G_UNLIKELY (dbus_error_is_set (&derror)))
     {
       /* try to translate the error appropriately */
-      if (strcmp (derror.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied") == 0) 
+      if (strcmp (derror.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied") == 0)
         {
           /* TRANSLATORS: User tried to unmount a volume, but is not privileged to do so. */
           g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("You are not privileged to unmount the volume \"%s\""), device->name);
@@ -1055,3 +1121,74 @@
   return TRUE;
 }
 
+/**
+ * exo_volume_hal_get_options:
+ * @fs     : files system name.
+ * @ret    : an #ExoVolumeOptions.
+ *
+ * return mount options for specified file sytem or
+ * override file system name. If options not found return %NULL
+ *
+ * code taked from PCManFM project.
+ *
+ * Return value: %TRUE if options is found or %FALSE if not.
+ **/
+gboolean
+exo_volume_hal_get_options (const char*       fs,
+                            ExoVolumeOptions* ret)
+{
+    GKeyFile* f;
+    const gchar* user_opts = g_strconcat(g_getenv("HOME"), "/.config/xfce4/mount.rules", NULL);
+    gboolean is_options_read = FALSE;
+    if( fs == NULL || ! *fs)
+        return FALSE;
+    g_return_val_if_fail( ret != NULL, FALSE );
+
+    f = g_key_file_new();
+    if( g_key_file_load_from_file( f, user_opts, 0, NULL ))
+    {
+        printf("User's mount options is readed\n");
+        is_options_read = TRUE;
+    }
+    else if( g_key_file_load_from_file( f, CONFIG_FILE_GLOBAL, 0, NULL) )
+    {
+        printf("Global mount options is readed\n");
+        is_options_read = TRUE;
+    }
+
+    if(is_options_read == TRUE)
+    {
+        gsize n = 0;
+	int i;
+        ret->mount_options = g_key_file_get_string_list( f, fs, "mount_options", &n, NULL );
+        ret->fstype_override = g_key_file_get_string(f, fs, "fstype_override", NULL );
+
+        for( i = 0; i < n; ++i )
+        {
+            /* replace "uid=" with "uid=<actual uid>" */
+#ifndef __FreeBSD__
+            if (strcmp (ret->mount_options[i], "uid=") == 0) {
+                g_free (ret->mount_options[i]);
+                ret->mount_options[i] = g_strdup_printf ("uid=%u", getuid ());
+            }
+#else
+            if (strcmp (ret->mount_options[i], "-u=") == 0) {
+                g_free (ret->mount_options[i]);
+                ret->mount_options[i] = g_strdup_printf ("-u=%u", getuid ());
+            }
+#endif
+            /* for ntfs-3g */
+            if (strcmp (ret->mount_options[i], "locale=") == 0) {
+                g_free (ret->mount_options[i]);
+                ret->mount_options[i] = g_strdup_printf ("locale=%s", setlocale (LC_ALL, ""));
+            }
+        }
+    }
+    else
+    {
+        ret->mount_options = NULL;
+        ret->fstype_override = NULL;
+    }
+    g_key_file_free(f);
+    return (ret->mount_options || ret->fstype_override);
+}
diff -Naur exo-0.3.101-old/exo-mount/Makefile.am exo-0.3.101/exo-mount/Makefile.am
--- exo-0.3.101-old/exo-mount/Makefile.am	2008-11-11 02:22:46.000000000 -0600
+++ exo-0.3.101/exo-mount/Makefile.am	2009-09-19 20:28:45.000000000 -0500
@@ -6,7 +6,8 @@
 	-DG_LOG_DOMAIN=\"exo-mount\"					\
 	-DLIBEXECDIR=\"$(libexecdir)\"					\
 	-DLIBEXO_VERSION_API=\"$(LIBEXO_VERSION_API)\"			\
-	-DPACKAGE_LOCALE_DIR=\"$(localedir)\"
+	-DPACKAGE_LOCALE_DIR=\"$(localedir)\"				\
+	-DDATADIR=\"$(datadir)\"
 
 bin_PROGRAMS =								\
 	exo-mount
diff -Naur exo-0.3.101-old/exo-mount/Makefile.in exo-0.3.101/exo-mount/Makefile.in
--- exo-0.3.101-old/exo-mount/Makefile.in	2009-04-12 16:37:11.000000000 -0500
+++ exo-0.3.101/exo-mount/Makefile.in	2009-09-19 20:28:45.000000000 -0500
@@ -299,7 +299,8 @@
 	-DG_LOG_DOMAIN=\"exo-mount\"					\
 	-DLIBEXECDIR=\"$(libexecdir)\"					\
 	-DLIBEXO_VERSION_API=\"$(LIBEXO_VERSION_API)\"			\
-	-DPACKAGE_LOCALE_DIR=\"$(localedir)\"
+	-DPACKAGE_LOCALE_DIR=\"$(localedir)\"				\
+	-DDATADIR=\"$(datadir)\"
 
 exo_mount_SOURCES = exo-mount-fstab.c exo-mount-fstab.h \
 	exo-mount-utils.c exo-mount-utils.h main.c $(am__append_1)
@@ -386,7 +387,7 @@
 	  echo " rm -f $$p $$f"; \
 	  rm -f $$p $$f ; \
 	done
-exo-mount$(EXEEXT): $(exo_mount_OBJECTS) $(exo_mount_DEPENDENCIES) 
+exo-mount$(EXEEXT): $(exo_mount_OBJECTS) $(exo_mount_DEPENDENCIES)
 	@rm -f exo-mount$(EXEEXT)
 	$(exo_mount_LINK) $(exo_mount_OBJECTS) $(exo_mount_LDADD) $(LIBS)
 
