Index: configure.ac
===================================================================
--- configure.ac	(revision 2068)
+++ configure.ac	(working copy)
@@ -45,7 +45,7 @@ GTK_DOC_CHECK
 DISTCHECK_CONFIGURE_FLAGS="--enable-gtk-doc"
 AC_SUBST(DISTCHECK_CONFIGURE_FLAGS)
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.17.6 gthread-2.0 gobject-2.0 gmodule-no-export-2.0 gio-unix-2.0 gio-2.0)
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.19.1 gthread-2.0 gobject-2.0 gmodule-no-export-2.0 gio-unix-2.0 gio-2.0)
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
Index: common/Makefile.am
===================================================================
--- common/Makefile.am	(revision 2068)
+++ common/Makefile.am	(working copy)
@@ -16,6 +16,7 @@ libgvfscommon_la_SOURCES = 	\
 	gmountsource.c gmountsource.h \
 	gmounttracker.c gmounttracker.h \
 	gvfsdaemonprotocol.c gvfsdaemonprotocol.h \
+	gvfsicon.h gvfsicon.c \
 	$(NULL)
 
 libgvfscommon_la_LIBADD =	\
Index: common/gvfsdaemonprotocol.c
===================================================================
--- common/gvfsdaemonprotocol.c	(revision 2068)
+++ common/gvfsdaemonprotocol.c	(working copy)
@@ -32,31 +32,13 @@
 static const char *
 get_object_signature (GObject *obj)
 {
-  if (G_IS_THEMED_ICON (obj))
+  if (G_IS_ICON (obj))
     {
       return
-	DBUS_STRUCT_BEGIN_CHAR_AS_STRING
-	  DBUS_TYPE_UINT32_AS_STRING
-	  DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING
-	DBUS_STRUCT_END_CHAR_AS_STRING;
-	}
-  else if (G_IS_FILE_ICON (obj))
-    {
-      GFile *file;
-      char *path;
-      
-      file = g_file_icon_get_file (G_FILE_ICON (obj));
-      
-      path = g_file_get_path (file);
-      if (path)
-	{
-	  g_free (path);
-	  return
-	    DBUS_STRUCT_BEGIN_CHAR_AS_STRING
-	      DBUS_TYPE_UINT32_AS_STRING
-	      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING
-	    DBUS_STRUCT_END_CHAR_AS_STRING;
-	}
+        DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+          DBUS_TYPE_UINT32_AS_STRING
+          DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING
+        DBUS_STRUCT_END_CHAR_AS_STRING;
     }
   return
     DBUS_STRUCT_BEGIN_CHAR_AS_STRING
@@ -65,29 +47,6 @@ get_object_signature (GObject *obj)
 }
 
 static void
-append_string_array (DBusMessageIter *iter, char **strs)
-{
-  DBusMessageIter array;
-  int i;
-
-  if (!dbus_message_iter_open_container (iter,
-					 DBUS_TYPE_ARRAY,
-					 DBUS_TYPE_STRING_AS_STRING,
-					 &array))
-    _g_dbus_oom ();
-  
-              
-  for (i = 0; strs[i] != NULL; i++)
-    {
-      if (!dbus_message_iter_append_basic (&array, DBUS_TYPE_STRING, &strs[i]))
-	_g_dbus_oom ();
-    }
-  
-  if (!dbus_message_iter_close_container (iter, &array))
-    _g_dbus_oom ();
-}
-
-static void
 append_object (DBusMessageIter *iter, GObject *obj)
 {
   DBusMessageIter obj_struct_iter;
@@ -99,52 +58,21 @@ append_object (DBusMessageIter *iter, GObject *obj
 					 &obj_struct_iter))
     _g_dbus_oom ();
 
-  if (G_IS_THEMED_ICON (obj))
+  if (G_IS_ICON (obj))
     {
-      const char * const *icons;
-      
-      icons = g_themed_icon_get_names (G_THEMED_ICON (obj));
-      
-      v_uint32 = 1;
+      char *str;
+      GIcon *icon = G_ICON (obj);
+
+      v_uint32 = 3;
       if (!dbus_message_iter_append_basic (&obj_struct_iter,
 					   DBUS_TYPE_UINT32, &v_uint32))
 	_g_dbus_oom ();
-      
-      append_string_array (&obj_struct_iter, (char **)icons);
+
+      str = g_icon_to_string (icon);
+      _g_dbus_message_iter_append_cstring (&obj_struct_iter, str);
+      g_free (str);
+
     }
-  else if (G_IS_FILE_ICON (obj))
-    {
-      GFile *file;
-      char *path;
-      
-      file = g_file_icon_get_file (G_FILE_ICON (obj));
-      
-      path = g_file_get_path (file);
-      if (path)
-	{
-	  v_uint32 = 2;
-	  if (!dbus_message_iter_append_basic (&obj_struct_iter,
-					       DBUS_TYPE_UINT32, &v_uint32))
-	    _g_dbus_oom ();
-	  
-	  path = g_file_get_path (file);
-	  _g_dbus_message_iter_append_cstring (&obj_struct_iter, path);
-	  g_free (path);
-	}
-      else
-	{
-	  /* Seems unlikely that daemon backend will generate GFileIcons with
-	     files on the vfs, so its probably not a problem not to support this.
-	     (Its tricky to support, since we don't link the daemon to the client/
-	     library directly.) */
-	  g_warning ("Unknown file type for icon in attribute, ignoring");
-	  
-	  v_uint32 = 0;
-	  if (!dbus_message_iter_append_basic (&obj_struct_iter,
-					       DBUS_TYPE_UINT32, &v_uint32))
-	    _g_dbus_oom ();
-	}
-    }
   else
     {
       /* NULL or unknown type: */
@@ -452,6 +380,31 @@ _g_dbus_get_file_attribute (DBusMessageIter *iter,
 	      g_free (str);
 	    }
 	}
+      else if (obj_type == 3)
+        {
+          /* serialized GIcon data. New in GIO 2.20, we keep obj_type 1 and 2 around for compat / migration only */
+	  if (_g_dbus_message_iter_get_args (&obj_iter,
+					     NULL,
+					     G_DBUS_TYPE_CSTRING, &str,
+					     0))
+	    {
+              GIcon *icon;
+              GError *error;
+
+              error = NULL;
+              icon = g_icon_new_for_string (str, &error);
+              if (icon == NULL)
+                {
+                  g_warning ("Unsupported GIcon data ('%s') in file attribute: %s", str, error->message);
+                  g_error_free (error);
+                }
+              else
+                {
+                  obj = G_OBJECT (icon);
+                }
+	      g_free (str);
+	    }
+        }
       else
 	{
 	  /* NULL (or unsupported) */
Index: common/gvfsdaemonprotocol.h
===================================================================
--- common/gvfsdaemonprotocol.h	(revision 2068)
+++ common/gvfsdaemonprotocol.h	(working copy)
@@ -49,6 +49,7 @@ G_BEGIN_DECLS
 #define G_VFS_DBUS_MOUNT_OP_SET_ATTRIBUTE "SetAttribute"
 #define G_VFS_DBUS_MOUNT_OP_QUERY_SETTABLE_ATTRIBUTES "QuerySettableAttributes"
 #define G_VFS_DBUS_MOUNT_OP_QUERY_WRITABLE_NAMESPACES "QueryWritableNamespaces"
+#define G_VFS_DBUS_MOUNT_OP_OPEN_ICON_FOR_READ "OpenIconForRead"
 
 /* Progress callback interface for copy and move */
 #define G_VFS_DBUS_PROGRESS_INTERFACE "org.gtk.vfs.Progress"
Index: common/gmountspec.c
===================================================================
--- common/gmountspec.c	(revision 2068)
+++ common/gmountspec.c	(working copy)
@@ -455,6 +455,8 @@ char *
 g_mount_spec_to_string (GMountSpec *spec)
 {
   GString *str;
+  char *k;
+  char *v;
   int i;
 
   if (spec == NULL)
@@ -466,13 +468,102 @@ g_mount_spec_to_string (GMountSpec *spec)
     {
       GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
 
-      g_string_append_printf (str, "%s='%s',", item->key, item->value);
+      k = g_uri_escape_string (item->key, NULL, TRUE);
+      v = g_uri_escape_string (item->value, NULL, TRUE);
+      g_string_append_printf (str, "%s=%s,", k, v);
+      g_free (k);
+      g_free (v);
     }
-  g_string_append_printf (str, "mount_prefix='%s'", spec->mount_prefix);
+  k = g_uri_escape_string ("__mount_prefix", NULL, TRUE);
+  v = g_uri_escape_string (spec->mount_prefix, NULL, TRUE);
+  g_string_append_printf (str, "%s=%s", k, v);
+  g_free (k);
+  g_free (v);
 
   return g_string_free (str, FALSE);
 }
 
+GMountSpec *
+g_mount_spec_new_from_string (const gchar     *str,
+                              GError         **error)
+{
+  GArray *items;
+  GMountSpec *mount_spec;
+  char **kv_pairs;
+  char *mount_prefix;
+  int i;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  mount_spec = NULL;
+  mount_prefix = NULL;
+  items = g_array_new (FALSE, TRUE, sizeof (GMountSpecItem));
+
+  kv_pairs = g_strsplit (str, ",", 0);
+  for (i = 0; kv_pairs[i] != NULL; i++)
+    {
+      char **tokens;
+      GMountSpecItem item;
+
+      tokens = g_strsplit (kv_pairs[i], "=", 0);
+      if (g_strv_length (tokens) != 2)
+        {
+          g_set_error (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_INVALID_ARGUMENT,
+                       "Encountered invalid key/value pair '%s' while decoding GMountSpec",
+                       kv_pairs[i]);
+          g_strfreev (tokens);
+          g_strfreev (kv_pairs);
+          goto fail;
+        }
+
+      item.key = g_uri_unescape_string (tokens[0], NULL);
+      item.value = g_uri_unescape_string (tokens[1], NULL);
+
+      if (strcmp (item.key, "__mount_prefix") == 0)
+        {
+          g_free (item.key);
+          mount_prefix = item.value;
+        }
+      else
+        {
+          g_array_append_val (items, item);
+        }
+
+      g_strfreev (tokens);
+    }
+  g_strfreev (kv_pairs);
+
+  if (mount_prefix == NULL)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   "Didn't find __mount_prefix while decoding '%s' GMountSpec",
+                   str);
+      goto fail;
+    }
+
+  /* this constructor takes ownership of the data we pass in */
+  mount_spec = g_mount_spec_new_from_data (items,
+                                           mount_prefix);
+
+  return mount_spec;
+
+ fail:
+  for (i = 0; i < items->len; i++)
+    {
+      GMountSpecItem *item = &g_array_index (items, GMountSpecItem, i);
+      g_free (item->key);
+      g_free (item->value);
+    }
+  g_array_free (items, TRUE);
+  g_free (mount_prefix);
+  return NULL;
+}
+
+
 char *
 g_mount_spec_canonicalize_path (const char *path)
 {
@@ -532,3 +623,15 @@ g_mount_spec_canonicalize_path (const char *path)
   
   return canon;
 }
+
+GType
+g_type_mount_spec_get_gtype (void)
+{
+  static GType type_id = 0;
+
+  if (type_id == 0)
+    type_id = g_boxed_type_register_static (g_intern_static_string ("GMountSpec"),
+                                            (GBoxedCopyFunc) g_mount_spec_ref,
+                                            (GBoxedFreeFunc) g_mount_spec_unref);
+  return type_id;
+}
Index: common/gmountspec.h
===================================================================
--- common/gmountspec.h	(revision 2068)
+++ common/gmountspec.h	(working copy)
@@ -92,11 +92,19 @@ const char *g_mount_spec_get               (GMount
 					    const char      *key);
 const char *g_mount_spec_get_type          (GMountSpec      *spec);
 
-/* For debugging */
 char *      g_mount_spec_to_string         (GMountSpec      *spec);
 
+GMountSpec *g_mount_spec_new_from_string   (const gchar     *str,
+                                            GError         **error);
+
+/* For debugging */
+
 char *      g_mount_spec_canonicalize_path (const char      *path);
 
+
+#define G_TYPE_MOUNT_SPEC (g_type_mount_spec_get_gtype ())
+GType g_type_mount_spec_get_gtype (void) G_GNUC_CONST;
+
 G_END_DECLS
 
 
Index: client/Makefile.am
===================================================================
--- client/Makefile.am	(revision 2068)
+++ client/Makefile.am	(working copy)
@@ -33,6 +33,7 @@ vfssources = \
 	gdaemonfileenumerator.c gdaemonfileenumerator.h \
 	gdaemonfilemonitor.c gdaemonfilemonitor.h \
 	gvfsdaemondbus.c gvfsdaemondbus.h \
+	gvfsiconloadable.c gvfsiconloadable.h \
 	gvfsuriutils.c gvfsuriutils.h \
 	gvfsurimapper.c gvfsurimapper.h \
 	$(URI_PARSER_SOURCES) \
Index: client/gdaemonvfs.c
===================================================================
--- client/gdaemonvfs.c	(revision 2068)
+++ client/gdaemonvfs.c	(working copy)
@@ -36,6 +36,8 @@
 #include "gmountspec.h"
 #include "gvfsurimapper.h"
 #include "gdaemonvolumemonitor.h"
+#include "gvfsicon.h"
+#include "gvfsiconloadable.h"
 #include <glib/gi18n-lib.h>
 
 typedef struct  {
@@ -1129,6 +1131,9 @@ g_io_module_load (GIOModule *module)
   
   g_daemon_vfs_register_type (G_TYPE_MODULE (module));
   g_daemon_volume_monitor_register_types (G_TYPE_MODULE (module));
+  g_vfs_icon_register_types (G_TYPE_MODULE (module));
+  /* see comment in common/giconvfs.c */
+  _g_vfs_icon_add_loadable_interface ();
 
   g_io_extension_point_implement (G_VFS_EXTENSION_POINT_NAME,
 				  G_TYPE_DAEMON_VFS,
Index: daemon/Makefile.am
===================================================================
--- daemon/Makefile.am	(revision 2068)
+++ daemon/Makefile.am	(working copy)
@@ -120,6 +120,7 @@ libdaemon_la_SOURCES = \
 	gvfsjobmountmountable.c gvfsjobmountmountable.h \
 	gvfsjobunmountmountable.c gvfsjobunmountmountable.h \
 	gvfsjobopenforread.c gvfsjobopenforread.h \
+	gvfsjobopeniconforread.c gvfsjobopeniconforread.h \
 	gvfsjobread.c gvfsjobread.h \
 	gvfsjobseekread.c gvfsjobseekread.h \
 	gvfsjobcloseread.c gvfsjobcloseread.h \
Index: daemon/gvfsbackend.c
===================================================================
--- daemon/gvfsbackend.c	(revision 2068)
+++ daemon/gvfsbackend.c	(working copy)
@@ -35,6 +35,7 @@
 #include "gvfsjobsource.h"
 #include "gvfsdaemonprotocol.h"
 #include <gvfsjobopenforread.h>
+#include <gvfsjobopeniconforread.h>
 #include <gvfsjobopenforwrite.h>
 #include <gvfsjobqueryinfo.h>
 #include <gvfsjobqueryfsinfo.h>
@@ -482,6 +483,10 @@ backend_dbus_handler (DBusConnection  *connection,
 				   G_VFS_DBUS_MOUNT_OP_OPEN_FOR_READ))
     job = g_vfs_job_open_for_read_new (connection, message, backend);
   else if (dbus_message_is_method_call (message,
+				   G_VFS_DBUS_MOUNT_INTERFACE,
+				   G_VFS_DBUS_MOUNT_OP_OPEN_ICON_FOR_READ))
+    job = g_vfs_job_open_icon_for_read_new (connection, message, backend);
+  else if (dbus_message_is_method_call (message,
 					G_VFS_DBUS_MOUNT_INTERFACE,
 					G_VFS_DBUS_MOUNT_OP_QUERY_INFO))
     job = g_vfs_job_query_info_new (connection, message, backend);
Index: daemon/gvfsbackend.h
===================================================================
--- daemon/gvfsbackend.h	(revision 2068)
+++ daemon/gvfsbackend.h	(working copy)
@@ -48,6 +48,7 @@ typedef struct _GVfsJobUnmount         GVfsJobUnmo
 typedef struct _GVfsJobMountMountable  GVfsJobMountMountable;
 typedef struct _GVfsJobUnmountMountable GVfsJobUnmountMountable;
 typedef struct _GVfsJobOpenForRead     GVfsJobOpenForRead;
+typedef struct _GVfsJobOpenIconForRead     GVfsJobOpenIconForRead;
 typedef struct _GVfsJobSeekRead        GVfsJobSeekRead;
 typedef struct _GVfsJobCloseRead       GVfsJobCloseRead;
 typedef struct _GVfsJobRead            GVfsJobRead;
@@ -138,6 +139,12 @@ struct _GVfsBackendClass
   gboolean (*try_open_for_read) (GVfsBackend *backend,
 				 GVfsJobOpenForRead *job,
 				 const char *filename);
+  void     (*open_icon_for_read) (GVfsBackend *backend,
+                                  GVfsJobOpenIconForRead *job,
+                                  const char *icon_id);
+  gboolean (*try_open_icon_for_read) (GVfsBackend *backend,
+                                      GVfsJobOpenIconForRead *job,
+                                      const char *icon_id);
   void     (*close_read)        (GVfsBackend *backend,
 				 GVfsJobCloseRead *job,
 				 GVfsBackendHandle handle);
Index: daemon/daemon-main.c
===================================================================
--- daemon/daemon-main.c	(revision 2068)
+++ daemon/daemon-main.c	(working copy)
@@ -56,6 +56,9 @@ daemon_init (void)
   g_thread_init (NULL);
   g_type_init ();
 
+  /* need this to load e.g. GVfsIcon from the gvfs gio module */
+  g_vfs_get_default ();
+
   dbus_error_init (&derror);
   connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
   if (connection == NULL)
Index: daemon/gvfsbackendgphoto2.c
===================================================================
--- daemon/gvfsbackendgphoto2.c	(revision 2068)
+++ daemon/gvfsbackendgphoto2.c	(working copy)
@@ -41,6 +41,7 @@
 
 #include "gvfsbackendgphoto2.h"
 #include "gvfsjobopenforread.h"
+#include "gvfsjobopeniconforread.h"
 #include "gvfsjobread.h"
 #include "gvfsjobseekread.h"
 #include "gvfsjobqueryinfo.h"
@@ -52,6 +53,7 @@
 #include "gvfsjobcreatemonitor.h"
 #include "gvfsmonitor.h"
 #include "gvfsjobseekwrite.h"
+#include "gvfsicon.h"
 
 /* showing debug traces */
 #if 0
@@ -1132,6 +1134,25 @@ file_get_info (GVfsBackendGphoto2 *gphoto2_backend
     mime_type = g_strdup ("application/octet-stream");
   g_file_info_set_content_type (info, mime_type);
 
+  /* assume that all JPG files, and only all JPG files, has a preview file */
+  if (strcmp (mime_type, "image/jpg") == 0 ||
+      strcmp (mime_type, "image/jpeg") == 0)
+    {
+      char *icon_id;
+      GIcon *icon;
+      GMountSpec *mount_spec;
+
+      mount_spec = g_vfs_backend_get_mount_spec (G_VFS_BACKEND (gphoto2_backend));
+      icon_id = g_strdup_printf ("preview:%s/%s", dir + strlen (gphoto2_backend->ignore_prefix), name);
+      icon = g_vfs_icon_new (mount_spec,
+                             icon_id);
+      g_file_info_set_attribute_object (info,
+                                        G_FILE_ATTRIBUTE_PREVIEW_ICON,
+                                        G_OBJECT (icon));
+      g_object_unref (icon);
+      g_free (icon_id);
+    }
+
   icon = g_content_type_get_icon (mime_type);
   DEBUG ("  got icon %p for mime_type '%s'", icon, mime_type);
   if (icon != NULL)
@@ -1141,7 +1162,6 @@ file_get_info (GVfsBackendGphoto2 *gphoto2_backend
     }
   g_free (mime_type);
 
-
   if (gp_info.file.fields & GP_FILE_INFO_MTIME)
     mtime.tv_sec = gp_info.file.mtime;
   else
@@ -1677,9 +1697,10 @@ free_read_handle (ReadHandle *read_handle)
 }
 
 static void
-do_open_for_read (GVfsBackend *backend,
-                  GVfsJobOpenForRead *job,
-                  const char *filename)
+do_open_for_read_real (GVfsBackend *backend,
+                       GVfsJobOpenForRead *job,
+                       const char *filename,
+                       gboolean get_preview)
 {
   int rc;
   GError *error;
@@ -1688,8 +1709,6 @@ static void
   char *dir;
   char *name;
 
-  DEBUG ("open_for_read (%s)", filename);
-
   ensure_not_dirty (gphoto2_backend);
 
   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
@@ -1724,7 +1743,7 @@ static void
   rc = gp_camera_file_get (gphoto2_backend->camera,
                            dir,
                            name,
-                           GP_FILE_TYPE_NORMAL,
+                           get_preview ? GP_FILE_TYPE_PREVIEW : GP_FILE_TYPE_NORMAL,
                            read_handle->file,
                            gphoto2_backend->context);
   if (rc != 0)
@@ -1763,6 +1782,44 @@ static void
   g_free (dir);
 }
 
+
+static void
+do_open_for_read (GVfsBackend *backend,
+                  GVfsJobOpenForRead *job,
+                  const char *filename)
+{
+  DEBUG ("open_for_read (%s)", filename);
+
+  do_open_for_read_real (backend,
+                         job,
+                         filename,
+                         FALSE);
+}
+
+static void
+do_open_icon_for_read (GVfsBackend *backend,
+                       GVfsJobOpenIconForRead *job,
+                       const char *icon_id)
+{
+  DEBUG ("open_icon_for_read (%s)", icon_id);
+
+  if (g_str_has_prefix (icon_id, "preview:"))
+    {
+      do_open_for_read_real (backend,
+                             G_VFS_JOB_OPEN_FOR_READ (job),
+                             icon_id + sizeof ("preview:") - 1,
+                             TRUE);
+    }
+  else
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR,
+                        G_IO_ERROR_INVALID_ARGUMENT,
+                        _("Malformed icon identifier '%s'"),
+                        icon_id);
+    }
+}
+
 /* ------------------------------------------------------------------------------------------------- */
 
 static gboolean
@@ -3373,6 +3430,7 @@ g_vfs_backend_gphoto2_class_init (GVfsBackendGphot
   backend_class->try_mount = try_mount;
   backend_class->mount = do_mount;
   backend_class->unmount = do_unmount;
+  backend_class->open_icon_for_read = do_open_icon_for_read;
   backend_class->open_for_read = do_open_for_read;
   backend_class->try_read = try_read;
   backend_class->try_seek_on_read = try_seek_on_read;
--- /dev/null	2008-10-20 23:08:12.798044021 -0400
+++ common/gvfsicon.h	2008-10-20 22:01:03.000000000 -0400
@@ -0,0 +1,71 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 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_ICON_H__
+#define __G_VFS_ICON_H__
+
+#include <gio/gio.h>
+#include <gmountspec.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_ICON         (g_vfs_icon_get_type ())
+#define G_VFS_ICON(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_ICON, GVfsIcon))
+#define G_VFS_ICON_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_ICON, GVfsIconClass))
+#define G_VFS_IS_ICON(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_ICON))
+#define G_VFS_IS_ICON_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_ICON))
+#define G_VFS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_ICON, GVfsIconClass))
+
+/**
+ * GVfsIcon:
+ *
+ * An object for referencing icons originating from a #GVfsBackend
+ */
+typedef struct _GVfsIcon        GVfsIcon;
+typedef struct _GVfsIconClass   GVfsIconClass;
+
+struct _GVfsIcon
+{
+  GObject parent_instance;
+
+  GMountSpec *mount_spec;
+  char *icon_id;
+};
+
+struct _GVfsIconClass
+{
+  GObjectClass parent_class;
+};
+
+GType  g_vfs_icon_get_type        (void) G_GNUC_CONST;
+
+GIcon *g_vfs_icon_new             (GMountSpec  *mount_spec,
+                                   const gchar *icon_id);
+
+GMountSpec  *g_vfs_icon_get_mount_spec (GVfsIcon *vfs_icon);
+const gchar *g_vfs_icon_get_icon_id    (GVfsIcon *vfs_icon);
+
+void g_vfs_icon_register_types (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __G_VFS_ICON_H__ */
--- /dev/null	2008-10-20 23:08:12.798044021 -0400
+++ common/gvfsicon.c	2008-10-21 00:00:22.000000000 -0400
@@ -0,0 +1,320 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 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 "gvfsicon.h"
+
+static void g_vfs_icon_icon_iface_init          (GIconIface          *iface);
+
+/* Because of the way dependencies are currently set up, the
+ * GLoadableIcon interface is in client/gvfsiconloadable.c and is
+ * added in g_io_module_load() in client/gdaemonvfs.c.
+ */
+
+enum
+{
+  PROP_0,
+  PROP_MOUNT_SPEC,
+  PROP_ICON_ID
+};
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GVfsIcon,
+                                g_vfs_icon,
+                                G_TYPE_OBJECT,
+                                0,
+                                G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
+                                                       g_vfs_icon_icon_iface_init))
+
+static void
+g_vfs_icon_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GVfsIcon *icon = G_VFS_ICON (object);
+
+  switch (prop_id)
+    {
+    case PROP_MOUNT_SPEC:
+      g_value_set_boxed (value, icon->mount_spec);
+      break;
+
+    case PROP_ICON_ID:
+      g_value_set_string (value, icon->icon_id);
+      break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_vfs_icon_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+  GVfsIcon *icon = G_VFS_ICON (object);
+
+  switch (prop_id)
+    {
+    case PROP_MOUNT_SPEC:
+      icon->mount_spec = g_mount_spec_ref (g_value_get_boxed (value));
+      break;
+
+    case PROP_ICON_ID:
+      icon->icon_id = g_strdup (g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_vfs_icon_finalize (GObject *object)
+{
+  GVfsIcon *vfs_icon;
+
+  vfs_icon = G_VFS_ICON (object);
+
+  if (vfs_icon->mount_spec != NULL)
+    g_mount_spec_unref (vfs_icon->mount_spec);
+  g_free (vfs_icon->icon_id);
+
+  G_OBJECT_CLASS (g_vfs_icon_parent_class)->finalize (object);
+}
+
+static void
+g_vfs_icon_class_finalize (GVfsIconClass *klass)
+{
+}
+
+static void
+g_vfs_icon_class_init (GVfsIconClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = g_vfs_icon_get_property;
+  gobject_class->set_property = g_vfs_icon_set_property;
+  gobject_class->finalize = g_vfs_icon_finalize;
+
+  /**
+   * GVfsIcon:mount-spec:
+   *
+   * The mount spec.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_MOUNT_SPEC,
+                                   g_param_spec_boxed ("mount-spec",
+                                                       "Mount Spec",
+                                                       "Mount Spec",
+                                                       G_TYPE_MOUNT_SPEC,
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_STATIC_NAME |
+                                                       G_PARAM_STATIC_BLURB |
+                                                       G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsIcon:icon-id:
+   *
+   * The id of the icon.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ICON_ID,
+                                   g_param_spec_string ("icon-id",
+                                                       "Icon identifier",
+                                                       "Icon identifier",
+                                                       NULL,
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_STATIC_NAME |
+                                                       G_PARAM_STATIC_BLURB |
+                                                       G_PARAM_STATIC_NICK));
+}
+
+static void
+g_vfs_icon_init (GVfsIcon *file)
+{
+}
+
+GMountSpec *
+g_vfs_icon_get_mount_spec (GVfsIcon *vfs_icon)
+{
+  g_return_val_if_fail (G_VFS_IS_ICON (vfs_icon), NULL);
+  return g_mount_spec_ref (vfs_icon->mount_spec);
+}
+
+const gchar *
+g_vfs_icon_get_icon_id (GVfsIcon *vfs_icon)
+{
+  g_return_val_if_fail (G_VFS_IS_ICON (vfs_icon), NULL);
+  return vfs_icon->icon_id;
+}
+
+
+GIcon *
+g_vfs_icon_new (GMountSpec  *mount_spec,
+                const gchar *icon_id)
+{
+  return G_ICON (g_object_new (G_VFS_TYPE_ICON,
+                               "mount-spec", mount_spec,
+                               "icon-id", icon_id,
+                               NULL));
+}
+
+static guint
+g_vfs_icon_hash (GIcon *icon)
+{
+  GVfsIcon *vfs_icon = G_VFS_ICON (icon);
+
+  return g_mount_spec_hash (vfs_icon->mount_spec) ^ g_str_hash (vfs_icon->icon_id);
+}
+
+static int
+safe_strcmp (const char *a,
+             const char *b)
+{
+  if (a == NULL)
+    a = "";
+  if (b == NULL)
+    b = "";
+
+  return strcmp (a, b);
+}
+
+static gboolean
+g_vfs_icon_equal (GIcon *icon1,
+                  GIcon *icon2)
+{
+  GVfsIcon *vfs1 = G_VFS_ICON (icon1);
+  GVfsIcon *vfs2 = G_VFS_ICON (icon2);
+
+  return g_mount_spec_equal (vfs1->mount_spec, vfs2->mount_spec) &&
+    (safe_strcmp (vfs1->icon_id, vfs2->icon_id) == 0);
+}
+
+static gchar **
+g_vfs_icon_to_tokens (GIcon *icon,
+                      gint  *out_version)
+{
+  GVfsIcon *vfs_icon = G_VFS_ICON (icon);
+  char *s;
+  char **tokens;
+
+  g_return_val_if_fail (out_version != NULL, NULL);
+
+  *out_version = 0;
+
+  tokens = g_new0 (char *, 3);
+
+  s = g_mount_spec_to_string (vfs_icon->mount_spec);
+  tokens[0] = g_uri_escape_string (s, NULL, TRUE);
+  g_free (s);
+
+  tokens[1] = g_uri_escape_string (vfs_icon->icon_id, NULL, TRUE);
+
+  tokens[2] = NULL;
+
+  return tokens;
+}
+
+static GIcon *
+g_vfs_icon_from_tokens (gchar  **tokens,
+                        gint     num_tokens,
+                        gint     version,
+                        GError **error)
+{
+  GMountSpec *mount_spec;
+  GIcon *icon;
+  char *s;
+  char *icon_id;
+
+  icon = NULL;
+
+  if (version != 0)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Can't handle version %d of GVfsIcon encoding"),
+                   version);
+      goto out;
+    }
+
+  if (num_tokens != 2)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_ARGUMENT,
+                           _("Malformed input data for GVfsIcon"));
+      goto out;
+    }
+
+  s = g_uri_unescape_string (tokens[0], NULL);
+  mount_spec = g_mount_spec_new_from_string (s, error);
+  g_free (s);
+  if (mount_spec == NULL)
+    goto out;
+
+  icon_id = g_uri_unescape_string (tokens[1], NULL);
+
+  icon = g_vfs_icon_new (mount_spec, icon_id);
+
+  g_mount_spec_unref (mount_spec);
+  g_free (icon_id);
+
+ out:
+  return icon;
+}
+
+static void
+g_vfs_icon_icon_iface_init (GIconIface *iface)
+{
+  iface->hash = g_vfs_icon_hash;
+  iface->equal = g_vfs_icon_equal;
+  iface->to_tokens = g_vfs_icon_to_tokens;
+  iface->from_tokens = g_vfs_icon_from_tokens;
+}
+
+void
+g_vfs_icon_register_types (GTypeModule *module)
+{
+  g_vfs_icon_register_type (module);
+
+  /* ref the class; otherwise, since we're a dynamic type, we end up
+   * with class_finalized() getting called when the last GVfsIcon
+   * instance is disposed. This is unwanted since it means running
+   * class_init() all over again. Especially during thumbnailing in
+   * Nautilus this happens continously due to the GIcon serialization
+   * code.
+   *
+   * For our particular situation, this is not a problem since
+   * GVfsIcon is part of the GVfs GIO extension that is resident (see
+   * g_io_module_load() in client/gdaemonvfs.c).
+   */
+  g_type_class_ref (G_VFS_TYPE_ICON);
+}
--- /dev/null	2008-10-20 23:08:12.798044021 -0400
+++ client/gvfsiconloadable.h	2008-10-20 22:59:32.000000000 -0400
@@ -0,0 +1,35 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 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_ICON_LOADABLE_H__
+#define __G_VFS_ICON_LOADABLE_H__
+
+#include <gio/gio.h>
+#include <gmountspec.h>
+
+G_BEGIN_DECLS
+
+void _g_vfs_icon_add_loadable_interface (void);
+
+G_END_DECLS
+
+#endif /* __G_VFS_ICON_LOADABLE_H__ */
--- /dev/null	2008-10-20 23:08:12.798044021 -0400
+++ client/gvfsiconloadable.c	2008-10-21 00:44:43.000000000 -0400
@@ -0,0 +1,438 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 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 "gvfsicon.h"
+#include "gvfsiconloadable.h"
+#include "gmounttracker.h"
+#include "gvfsdaemondbus.h"
+#include "gdaemonvfs.h"
+#include "gdaemonfileinputstream.h"
+#include "gvfsdaemonprotocol.h"
+#include "gdbusutils.h"
+
+/* see comment in common/giconvfs.c for why the GLoadableIcon interface is here */
+
+static DBusMessage *
+create_empty_message (GVfsIcon *vfs_icon,
+		      const char *op,
+		      GMountInfo **mount_info_out,
+		      GError **error)
+{
+  DBusMessage *message;
+  GMountInfo *mount_info;
+
+  mount_info = _g_daemon_vfs_get_mount_info_sync (vfs_icon->mount_spec,
+						  "/",
+						  error);
+
+  if (mount_info == NULL)
+    return NULL;
+
+  if (mount_info_out)
+    *mount_info_out = g_mount_info_ref (mount_info);
+
+  message =
+    dbus_message_new_method_call (mount_info->dbus_id,
+				  mount_info->object_path,
+				  G_VFS_DBUS_MOUNT_INTERFACE,
+				  op);
+
+  _g_dbus_message_append_args (message, G_DBUS_TYPE_CSTRING, &(vfs_icon->icon_id), 0);
+
+  g_mount_info_unref (mount_info);
+  return message;
+}
+
+static DBusMessage *
+do_sync_path_call (GVfsIcon *vfs_icon,
+		   const char *op,
+		   GMountInfo **mount_info_out,
+		   DBusConnection **connection_out,
+		   GCancellable *cancellable,
+		   GError **error,
+		   int first_arg_type,
+		   ...)
+{
+  DBusMessage *message, *reply;
+  va_list var_args;
+  GError *my_error;
+
+ retry:
+  message = create_empty_message (vfs_icon, op, mount_info_out, error);
+  if (!message)
+    return NULL;
+
+  va_start (var_args, first_arg_type);
+  _g_dbus_message_append_args_valist (message,
+				      first_arg_type,
+				      var_args);
+  va_end (var_args);
+
+
+  my_error = NULL;
+  reply = _g_vfs_daemon_call_sync (message,
+				   connection_out,
+				   NULL, NULL, NULL,
+				   cancellable, &my_error);
+  dbus_message_unref (message);
+
+  if (reply == NULL)
+    {
+      if (g_error_matches (my_error, G_VFS_ERROR, G_VFS_ERROR_RETRY))
+	{
+	  g_error_free (my_error);
+	  goto retry;
+	}
+      g_propagate_error (error, my_error);
+    }
+
+  return reply;
+}
+
+static GInputStream *
+g_vfs_icon_load (GLoadableIcon  *icon,
+                 int            size,
+                 char          **type,
+                 GCancellable   *cancellable,
+                 GError        **error)
+{
+  DBusConnection *connection;
+  int fd;
+  DBusMessage *reply;
+  guint32 fd_id;
+  dbus_bool_t can_seek;
+
+  reply = do_sync_path_call (G_VFS_ICON (icon),
+			     G_VFS_DBUS_MOUNT_OP_OPEN_ICON_FOR_READ,
+			     NULL,
+                             &connection,
+			     cancellable,
+                             error,
+			     0);
+  if (reply == NULL)
+    return NULL;
+
+  if (!dbus_message_get_args (reply, NULL,
+			      DBUS_TYPE_UINT32, &fd_id,
+			      DBUS_TYPE_BOOLEAN, &can_seek,
+			      DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (reply);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			   _("Invalid return value from open_icon_for_read"));
+      return NULL;
+    }
+
+  dbus_message_unref (reply);
+
+  fd = _g_dbus_connection_get_fd_sync (connection, fd_id);
+  if (fd == -1)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			   _("Didn't get stream file descriptor"));
+      return NULL;
+    }
+
+  return G_INPUT_STREAM (g_daemon_file_input_stream_new (fd, can_seek));
+}
+
+
+typedef void (*AsyncPathCallCallback) (DBusMessage *reply,
+				       DBusConnection *connection,
+				       GSimpleAsyncResult *result,
+				       GCancellable *cancellable,
+				       gpointer callback_data);
+
+
+typedef struct {
+  GSimpleAsyncResult *result;
+  GVfsIcon *vfs_icon;
+  char *op;
+  GCancellable *cancellable;
+  DBusMessage *args;
+  AsyncPathCallCallback callback;
+  gpointer callback_data;
+  GDestroyNotify notify;
+} AsyncPathCall;
+
+
+static void
+async_path_call_free (AsyncPathCall *data)
+{
+  if (data->notify)
+    data->notify (data->callback_data);
+
+  if (data->result)
+    g_object_unref (data->result);
+  g_object_unref (data->vfs_icon);
+  g_free (data->op);
+  if (data->cancellable)
+    g_object_unref (data->cancellable);
+  if (data->args)
+    dbus_message_unref (data->args);
+  g_free (data);
+}
+
+static void
+async_path_call_done (DBusMessage *reply,
+		      DBusConnection *connection,
+		      GError *io_error,
+		      gpointer _data)
+{
+  AsyncPathCall *data = _data;
+  GSimpleAsyncResult *result;
+
+  if (io_error != NULL)
+    {
+      g_simple_async_result_set_from_error (data->result, io_error);
+      g_simple_async_result_complete (data->result);
+      async_path_call_free (data);
+    }
+  else
+    {
+      result = data->result;
+      g_object_weak_ref (G_OBJECT (result), (GWeakNotify)async_path_call_free, data);
+      data->result = NULL;
+      
+      data->callback (reply, connection,
+		      result,
+		      data->cancellable,
+		      data->callback_data);
+
+      /* Free data here, or later if callback ref:ed the result */
+      g_object_unref (result);
+    }
+}
+
+static void
+do_async_path_call_callback (GMountInfo *mount_info,
+			     gpointer _data,
+			     GError *error)
+{
+  AsyncPathCall *data = _data;
+  DBusMessage *message;
+  DBusMessageIter arg_source, arg_dest;
+  
+  if (error != NULL)
+    {
+      g_simple_async_result_set_from_error (data->result, error);      
+      g_simple_async_result_complete (data->result);
+      async_path_call_free (data);
+      return;
+    }
+
+  message =
+    dbus_message_new_method_call (mount_info->dbus_id,
+				  mount_info->object_path,
+				  G_VFS_DBUS_MOUNT_INTERFACE,
+				  data->op);
+  
+  _g_dbus_message_append_args (message, G_DBUS_TYPE_CSTRING, &(data->vfs_icon->icon_id), 0);
+
+  /* Append more args from data->args */
+
+  if (data->args)
+    {
+      dbus_message_iter_init (data->args, &arg_source);
+      dbus_message_iter_init_append (message, &arg_dest);
+
+      _g_dbus_message_iter_copy (&arg_dest, &arg_source);
+    }
+
+  _g_vfs_daemon_call_async (message,
+			    async_path_call_done, data,
+			    data->cancellable);
+  
+  dbus_message_unref (message);
+}
+
+
+static void
+do_async_path_call (GVfsIcon *vfs_icon,
+		    const char *op,
+		    GCancellable *cancellable,
+		    GAsyncReadyCallback op_callback,
+		    gpointer op_callback_data,
+		    AsyncPathCallCallback callback,
+		    gpointer callback_data,
+		    GDestroyNotify notify,
+		    int first_arg_type,
+		    ...)
+{
+  va_list var_args;
+  AsyncPathCall *data;
+
+  data = g_new0 (AsyncPathCall, 1);
+
+  data->result = g_simple_async_result_new (G_OBJECT (vfs_icon),
+					    op_callback, op_callback_data,
+					    NULL);
+
+  data->vfs_icon = g_object_ref (vfs_icon);
+  data->op = g_strdup (op);
+  if (cancellable)
+    data->cancellable = g_object_ref (cancellable);
+  data->callback = callback;
+  data->callback_data = callback_data;
+  data->notify = notify;
+  
+  if (first_arg_type != 0)
+    {
+      data->args = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+      if (data->args == NULL)
+	_g_dbus_oom ();
+      
+      va_start (var_args, first_arg_type);
+      _g_dbus_message_append_args_valist (data->args,
+					  first_arg_type,
+					  var_args);
+      va_end (var_args);
+    }
+  
+  
+  _g_daemon_vfs_get_mount_info_async (vfs_icon->mount_spec,
+                                      "/",
+                                      do_async_path_call_callback,
+                                      data);
+}
+
+typedef struct {
+  GSimpleAsyncResult *result;
+  gboolean can_seek;
+} GetFDData;
+
+static void
+load_async_get_fd_cb (int fd,
+		      gpointer callback_data)
+{
+  GetFDData *data = callback_data;
+  GFileInputStream *stream;
+
+  if (fd == -1)
+    {
+      g_simple_async_result_set_error (data->result,
+				       G_IO_ERROR, G_IO_ERROR_FAILED,
+				       _("Couldn't get stream file descriptor"));
+    }
+  else
+    {
+      stream = g_daemon_file_input_stream_new (fd, data->can_seek);
+      g_simple_async_result_set_op_res_gpointer (data->result, stream, g_object_unref);
+    }
+
+  g_simple_async_result_complete (data->result);
+
+  g_object_unref (data->result);
+  g_free (data);
+}
+
+static void
+load_async_cb (DBusMessage *reply,
+	       DBusConnection *connection,
+	       GSimpleAsyncResult *result,
+	       GCancellable *cancellable,
+	       gpointer callback_data)
+{
+  guint32 fd_id;
+  dbus_bool_t can_seek;
+  GetFDData *get_fd_data;
+
+  if (!dbus_message_get_args (reply, NULL,
+			      DBUS_TYPE_UINT32, &fd_id,
+			      DBUS_TYPE_BOOLEAN, &can_seek,
+			      DBUS_TYPE_INVALID))
+    {
+      g_simple_async_result_set_error (result,
+				       G_IO_ERROR, G_IO_ERROR_FAILED,
+				       _("Invalid return value from open"));
+      g_simple_async_result_complete (result);
+      return;
+    }
+
+  get_fd_data = g_new0 (GetFDData, 1);
+  get_fd_data->result = g_object_ref (result);
+  get_fd_data->can_seek = can_seek;
+
+  _g_dbus_connection_get_fd_async (connection, fd_id,
+				   load_async_get_fd_cb, get_fd_data);
+}
+
+static void
+g_vfs_icon_load_async (GLoadableIcon       *icon,
+                       int                  size,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  do_async_path_call (G_VFS_ICON (icon),
+		      G_VFS_DBUS_MOUNT_OP_OPEN_ICON_FOR_READ,
+		      cancellable,
+		      callback, user_data,
+		      load_async_cb, NULL, NULL,
+		      0);
+}
+
+static GInputStream *
+g_vfs_icon_load_finish (GLoadableIcon  *icon,
+                         GAsyncResult   *res,
+                         char          **type,
+                         GError        **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+
+  return NULL;
+}
+
+
+static void
+g_vfs_icon_loadable_icon_iface_init (GLoadableIconIface *iface)
+{
+  iface->load = g_vfs_icon_load;
+  iface->load_async = g_vfs_icon_load_async;
+  iface->load_finish = g_vfs_icon_load_finish;
+}
+
+void
+_g_vfs_icon_add_loadable_interface (void)
+{
+  GType type;
+
+  type = g_vfs_icon_get_type ();
+  g_assert (type != 0);
+
+  {
+    static const GInterfaceInfo g_implement_interface_info = {
+      (GInterfaceInitFunc) g_vfs_icon_loadable_icon_iface_init
+    };
+    g_type_add_interface_static (G_VFS_TYPE_ICON, G_TYPE_LOADABLE_ICON, &g_implement_interface_info);
+  }
+
+}
--- /dev/null	2008-10-20 23:08:12.798044021 -0400
+++ daemon/gvfsjobopeniconforread.h	2008-10-20 22:49:14.000000000 -0400
@@ -0,0 +1,63 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 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: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VFS_JOB_OPEN_ICON_FOR_READ_H__
+#define __G_VFS_JOB_OPEN_ICON_FOR_READ_H__
+
+#include <dbus/dbus.h>
+#include <gvfsjobdbus.h>
+#include <gvfsbackend.h>
+#include <gvfsreadchannel.h>
+#include <gvfsjobopenforread.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ         (g_vfs_job_open_icon_for_read_get_type ())
+#define G_VFS_JOB_OPEN_ICON_FOR_READ(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ, GVfsJobOpenIconForRead))
+#define G_VFS_JOB_OPEN_ICON_FOR_READ_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ, GVfsJobOpenIconForReadClass))
+#define G_VFS_IS_JOB_OPEN_ICON_FOR_READ(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ))
+#define G_VFS_IS_JOB_OPEN_ICON_FOR_READ_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ))
+#define G_VFS_JOB_OPEN_ICON_FOR_READ_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ, GVfsJobOpenIconForReadClass))
+
+typedef struct _GVfsJobOpenIconForReadClass   GVfsJobOpenIconForReadClass;
+
+struct _GVfsJobOpenIconForRead
+{
+  GVfsJobOpenForRead parent_instance;
+
+  char *icon_id;
+};
+
+struct _GVfsJobOpenIconForReadClass
+{
+  GVfsJobOpenForReadClass parent_class;
+};
+
+GType g_vfs_job_open_icon_for_read_get_type (void) G_GNUC_CONST;
+
+GVfsJob *        g_vfs_job_open_icon_for_read_new           (DBusConnection     *connection,
+                                                             DBusMessage        *message,
+                                                             GVfsBackend        *backend);
+
+G_END_DECLS
+
+#endif /* __G_VFS_JOB_OPEN_ICON_FOR_READ_H__ */
--- /dev/null	2008-10-20 23:08:12.798044021 -0400
+++ daemon/gvfsjobopeniconforread.c	2008-10-20 22:50:37.000000000 -0400
@@ -0,0 +1,142 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 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: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <glib/gi18n.h>
+#include "gvfsreadchannel.h"
+#include "gvfsjobopeniconforread.h"
+#include "gdbusutils.h"
+#include "gvfsdaemonutils.h"
+
+G_DEFINE_TYPE (GVfsJobOpenIconForRead, g_vfs_job_open_icon_for_read, G_VFS_TYPE_JOB_OPEN_FOR_READ)
+
+static void         run          (GVfsJob        *job);
+static gboolean     try          (GVfsJob        *job);
+
+static void
+g_vfs_job_open_icon_for_read_finalize (GObject *object)
+{
+  GVfsJobOpenIconForRead *job;
+
+  job = G_VFS_JOB_OPEN_ICON_FOR_READ (object);
+
+  if (G_OBJECT_CLASS (g_vfs_job_open_icon_for_read_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_vfs_job_open_icon_for_read_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_job_open_icon_for_read_class_init (GVfsJobOpenIconForReadClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);
+
+  gobject_class->finalize = g_vfs_job_open_icon_for_read_finalize;
+  job_class->run = run;
+  job_class->try = try;
+}
+
+static void
+g_vfs_job_open_icon_for_read_init (GVfsJobOpenIconForRead *job)
+{
+}
+
+GVfsJob *
+g_vfs_job_open_icon_for_read_new (DBusConnection *connection,
+                                  DBusMessage *message,
+                                  GVfsBackend *backend)
+{
+  GVfsJobOpenIconForRead *job;
+  GVfsJobOpenForRead *job_open_for_read;
+  DBusMessage *reply;
+  DBusError derror;
+  int path_len;
+  const char *path_data;
+
+  dbus_error_init (&derror);
+  if (!dbus_message_get_args (message, &derror,
+			      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+			      &path_data, &path_len,
+			      0))
+    {
+      reply = dbus_message_new_error (message,
+				      derror.name,
+                                      derror.message);
+      dbus_error_free (&derror);
+
+      dbus_connection_send (connection, reply, NULL);
+      return NULL;
+    }
+
+  job = g_object_new (G_VFS_TYPE_JOB_OPEN_ICON_FOR_READ,
+		      "message", message,
+		      "connection", connection,
+		      NULL);
+
+  job_open_for_read = G_VFS_JOB_OPEN_FOR_READ (job);
+
+  job->icon_id = g_strndup (path_data, path_len);
+  job_open_for_read->backend = backend;
+
+  return G_VFS_JOB (job);
+}
+
+static void
+run (GVfsJob *job)
+{
+  GVfsJobOpenIconForRead *op_job = G_VFS_JOB_OPEN_ICON_FOR_READ (job);
+  GVfsJobOpenForRead *op_job_open_for_read = G_VFS_JOB_OPEN_FOR_READ (job);
+  GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job_open_for_read->backend);
+
+  if (class->open_icon_for_read == NULL)
+    {
+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported by backend"));
+      return;
+    }
+
+  class->open_icon_for_read (op_job_open_for_read->backend,
+                             op_job,
+                             op_job->icon_id);
+}
+
+static gboolean
+try (GVfsJob *job)
+{
+  GVfsJobOpenIconForRead *op_job = G_VFS_JOB_OPEN_ICON_FOR_READ (job);
+  GVfsJobOpenForRead *op_job_open_for_read = G_VFS_JOB_OPEN_FOR_READ (job);
+  GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job_open_for_read->backend);
+
+  if (class->try_open_icon_for_read == NULL)
+    return FALSE;
+
+  return class->try_open_icon_for_read (op_job_open_for_read->backend,
+                                        op_job,
+                                        op_job->icon_id);
+}
