All pastes #1663118 Raw Edit

teuf

public text v1 · immutable
#1663118 ·published 2009-11-09 08:37 UTC
rendered paste body
From 1ba08909cdc395c14116d7cdf0f8f3442ba5e4c0 Mon Sep 17 00:00:00 2001From: Bastien Nocera <hadess@hadess.net>Date: Thu, 6 Aug 2009 22:55:47 +0100Subject: [PATCH] Add AFC backendAdd a backend based on libiphone to access data on Apple's iPhone,and iPod Touch.Code by:Patrick Walton <pcwalton@cs.ucla.edu>Martin Szulecki <opensuse@sukimashita.com>Nikias Bassen <nikias@gmx.li>Bastien Nocera <hadess@hadess.net>--- configure.ac                                       |   26 +- daemon/Makefile.am                                 |   23 + daemon/afc.mount.in                                |    7 + daemon/gvfsbackendafc.c                            | 1226 ++++++++++++++++++++ daemon/gvfsbackendafc.h                            |   37 + monitor/Makefile.am                                |    6 +- monitor/afc/Makefile.am                            |   49 + monitor/afc/afc.monitor                            |    5 + monitor/afc/afcvolume.c                            |  336 ++++++ monitor/afc/afcvolume.h                            |   44 + monitor/afc/afcvolumemonitor.c                     |  215 ++++ monitor/afc/afcvolumemonitor.h                     |   39 + monitor/afc/afcvolumemonitordaemon.c               |   31 + .../org.gtk.Private.AfcVolumeMonitor.service.in    |    4 + 14 files changed, 2046 insertions(+), 2 deletions(-) create mode 100644 daemon/afc.mount.in create mode 100644 daemon/gvfsbackendafc.c create mode 100644 daemon/gvfsbackendafc.h create mode 100644 monitor/afc/Makefile.am create mode 100644 monitor/afc/afc.monitor create mode 100644 monitor/afc/afcvolume.c create mode 100644 monitor/afc/afcvolume.h create mode 100644 monitor/afc/afcvolumemonitor.c create mode 100644 monitor/afc/afcvolumemonitor.h create mode 100644 monitor/afc/afcvolumemonitordaemon.c create mode 100644 monitor/afc/org.gtk.Private.AfcVolumeMonitor.service.indiff --git a/configure.ac b/configure.acindex 7f1ec16..4675ae9 100644--- a/configure.ac+++ b/configure.ac@@ -324,6 +324,28 @@ AC_SUBST(CDDA_CFLAGS)  AM_CONDITIONAL(USE_CDDA, [test "$msg_cdda" = "yes"]) +dnl *************************************************+dnl *** Check if we should build with AFC backend ***+dnl *************************************************+AC_ARG_ENABLE(afc, [  --disable-afc           build without AFC backend])+msg_afc=no+AFC_LIBS=+AFC_CFLAGS=++if test "x$enable_afc" != "xno" -a "x$msg_gudev" = "xyes" ; then+  PKG_CHECK_EXISTS(libiphone-1.0 >= 0.9.2, msg_afc=yes)++  if test "x$msg_afc" = "xyes"; then+    PKG_CHECK_MODULES(AFC, libiphone-1.0 gudev-1.0)+    AC_DEFINE(HAVE_AFC, 1, [Define to 1 if AFC is going to be built])+  fi+fi++AC_SUBST(AFC_LIBS)+AC_SUBST(AFC_CFLAGS)++AM_CONDITIONAL(USE_AFC, [test "$msg_afc" = "yes"])+ dnl ***************************************************** dnl *** Check if we should build with obexftp backend *** dnl *****************************************************@@ -695,6 +717,7 @@ monitor/proxy/Makefile monitor/hal/Makefile monitor/gdu/Makefile monitor/gphoto2/Makefile+monitor/afc/Makefile gconf/Makefile programs/Makefile test/Makefile@@ -712,7 +735,8 @@ echo " 	FUSE support:                 $msg_fuse         CDDA support:                 $msg_cdda         Gphoto2 support:              $msg_gphoto2-	archive support:	      $msg_archive+	archive support:              $msg_archive+	AFC support:                  $msg_afc         GConf support:                $msg_gconf         DNS-SD support:               $msg_avahi 	Build HAL volume monitor:     $msg_hal (with fast init path: $have_hal_fast_init)diff --git a/daemon/Makefile.am b/daemon/Makefile.amindex 733fa41..4c9e4af 100644--- a/daemon/Makefile.am+++ b/daemon/Makefile.am@@ -101,6 +101,12 @@ mount_DATA += archive.mount libexec_PROGRAMS += gvfsd-archive endif +mount_in_files += afc.mount.in+if USE_AFC+mount_DATA += afc.mount+libexec_PROGRAMS += gvfsd-afc+endif+ EXTRA_DIST = gvfs-daemon.service.in $(mount_in_files) obexftp-marshal.list  DISTCLEANFILES = gvfs-daemon.service $(mount_DATA)@@ -433,3 +439,20 @@ gvfsd_dav_LDADD = $(libraries) $(HTTP_LIBS) if HAVE_AVAHI gvfsd_dav_LDADD += $(top_builddir)/common/libgvfscommon-dnssd.la endif++gvfsd_afc_SOURCES = \+	gvfsbackendafc.c gvfsbackendafc.h \+	daemon-main.c daemon-main.h \+	daemon-main-generic.c++gvfsd_afc_CPPFLAGS = \+	-DBACKEND_HEADER=gvfsbackendafc.h \+	-DDEFAULT_BACKEND_TYPE=afc \+	-DMAX_JOB_THREADS=1 \+	$(AFC_CFLAGS) \+	-DBACKEND_TYPES='"afc", G_VFS_TYPE_BACKEND_AFC,'++gvfsd_afc_LDADD = \+	$(libraries) \+	$(AFC_LIBS)+diff --git a/monitor/Makefile.am b/monitor/Makefile.amindex f68423e..1dc984f 100644--- a/monitor/Makefile.am+++ b/monitor/Makefile.am@@ -1,4 +1,4 @@-+DIST_SUBDIRS = proxy hal gdu gphoto2 afc SUBDIRS = proxy  if USE_HAL@@ -12,3 +12,7 @@ endif if USE_GPHOTO2 SUBDIRS += gphoto2 endif++if USE_AFC+SUBDIRS += afc+endif--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/afc.monitor	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,5 @@+[RemoteVolumeMonitor]+Name=GProxyVolumeMonitorAfc+DBusName=org.gtk.Private.AfcVolumeMonitor+IsNative=false+--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/afcvolume.c	2009-11-06 22:37:31.000000000 +0100@@ -0,0 +1,350 @@+/*+ * gvfs/monitor/afc/afc-volume.c+ *+ * Copyright (c) 2008 Patrick Walton <pcwalton@cs.ucla.edu>+ */++#include <config.h>+#include <string.h>+#include <glib.h>+#include <gio/gio.h>++#include <libiphone/libiphone.h>+#include <libiphone/lockdown.h>+#include <libiphone/afc.h>++#include "afcvolume.h"++#define DEFAULT_SERVICE "com.apple.afc"++struct _GVfsAfcVolume {+  GObject parent;++  GVolumeMonitor *monitor;++  char *uuid;++  char *name;+  char *icon;+  char *icon_fallback;+};++static void g_vfs_afc_volume_iface_init (GVolumeIface *iface);++G_DEFINE_TYPE_EXTENDED(GVfsAfcVolume, g_vfs_afc_volume, G_TYPE_OBJECT, 0,+                       G_IMPLEMENT_INTERFACE(G_TYPE_VOLUME, g_vfs_afc_volume_iface_init))++static void+g_vfs_afc_volume_finalize (GObject *object)+{+  GVfsAfcVolume *self = NULL;++  self = G_VFS_AFC_VOLUME(object);++  g_free (self->uuid);++  g_free (self->name);+  g_free (self->icon);+  g_free (self->icon_fallback);++  if (G_OBJECT_CLASS(g_vfs_afc_volume_parent_class)->finalize)+    (*G_OBJECT_CLASS(g_vfs_afc_volume_parent_class)->finalize) (G_OBJECT(self));+}++static void+g_vfs_afc_volume_init (GVfsAfcVolume *self)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (self);++  afc_volume->name = g_strdup ("iPhone");+  afc_volume->icon = g_strdup ("phone-apple-iphone");+}++static void+g_vfs_afc_volume_class_init (GVfsAfcVolumeClass *klass)+{+  GObjectClass *gobject_class;+  gobject_class = G_OBJECT_CLASS(klass);+  gobject_class->finalize = g_vfs_afc_volume_finalize;+}++static int+_g_vfs_afc_volume_update_metadata (GVfsAfcVolume *self)+{+  iphone_device_t dev;+  afc_client_t afc_cli;+  lockdownd_client_t lockdown_cli = NULL;+  iphone_error_t err;+  guint retries;+  char *model, *display_name;+  int port;++  retries = 0;+  do {+      err = iphone_device_new (&dev, self->uuid);+      if (err == IPHONE_E_SUCCESS)+        break;+      g_usleep (G_USEC_PER_SEC);+  } while (retries++ < 10);++  if (err != IPHONE_E_SUCCESS)+    return 0;++  if (lockdownd_client_new (dev, &lockdown_cli) != LOCKDOWN_E_SUCCESS)+    {+      iphone_device_free (dev);+      return 0;+    }++  /* try to use pretty device name */+  if (lockdownd_get_device_name (lockdown_cli, &display_name) == LOCKDOWN_E_SUCCESS)+    {+      g_free (self->name);+      self->name = display_name;+    }++  if (lockdownd_start_service (lockdown_cli, DEFAULT_SERVICE, &port) != LOCKDOWN_E_SUCCESS)+    {+      lockdownd_client_free (lockdown_cli);+      iphone_device_free (dev);+      return 0;+    }++  if (afc_client_new (dev, port, &afc_cli) == AFC_E_SUCCESS)+    {+      /* set correct fd icon spec name depending on device model */+      model = NULL;+      if (afc_get_device_info_key (afc_cli, "Model", &model) == AFC_E_SUCCESS)+        {+          if(g_str_has_prefix(model, "iPod") != FALSE)+            {+              g_free (self->icon);+              self->icon = g_strdup ("multimedia-player-apple-ipod-touch");+            }+          g_free (model);+        }+      afc_client_free(afc_cli);+    }++  lockdownd_client_free (lockdown_cli);+  iphone_device_free (dev);++  return 1;+}++GVfsAfcVolume *+g_vfs_afc_volume_new (GVolumeMonitor *monitor,+                      const char     *uuid)+{+  GVfsAfcVolume *self;++  self = G_VFS_AFC_VOLUME(g_object_new (G_VFS_TYPE_AFC_VOLUME, NULL));+  self->monitor = monitor;+  self->uuid = g_strdup (uuid);++  /* Get mount information here */+  if (!_g_vfs_afc_volume_update_metadata (self))+      return NULL;++  return self;+}++static char *+g_vfs_afc_volume_get_name (GVolume *volume)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);+  char *name;++  name = g_strdup (afc_volume->name);++  return name;+}++static GIcon *+g_vfs_afc_volume_get_icon (GVolume *volume)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);+  GIcon *icon;++  icon = g_themed_icon_new_with_default_fallbacks (afc_volume->icon);++  return icon;+}++static char *+g_vfs_afc_volume_get_uuid (GVolume *volume)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);++  return g_strdup (afc_volume->uuid);+}++static gboolean+g_vfs_afc_volume_can_mount (GVolume *volume)+{+  return TRUE;+}++static gboolean+g_vfs_afc_volume_should_automount (GVolume *volume)+{+  return TRUE;+}++static GDrive *+g_vfs_afc_volume_get_drive (GVolume *volume)+{+  return NULL;+}++static GMount *+g_vfs_afc_volume_get_mount (GVolume *volume)+{+  return NULL;+}++typedef struct+{+  GVfsAfcVolume *enclosing_volume;+  GAsyncReadyCallback  callback;+  GFile *root;+  gpointer user_data;+} ActivationMountOp;++static void+mount_callback (GObject *source_object,+                GAsyncResult *res,+                gpointer user_data)+{+  ActivationMountOp *data = user_data;+  data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data);+  g_object_unref (data->root);+  g_free (data);+}++static void+g_vfs_afc_volume_mount (GVolume             *volume,+                        GMountMountFlags     flags,+                        GMountOperation     *mount_operation,+                        GCancellable        *cancellable,+                        GAsyncReadyCallback  callback,+                        gpointer             user_data)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);+  ActivationMountOp *data;+  GFile *root;+  char *uri;++  g_print ("g_vfs_afc_volume_mount (can_mount=%d uuid=%s)\n",+           g_vfs_afc_volume_can_mount (volume),+           afc_volume->uuid);++  uri = g_strdup_printf ("afc://%s", afc_volume->uuid);+  root = g_file_new_for_uri (uri);+  g_free (uri);++  data = g_new0 (ActivationMountOp, 1);+  data->enclosing_volume = afc_volume;+  data->callback = callback;+  data->user_data = user_data;+  data->root = root;++  g_object_set_data_full (G_OBJECT(volume), "root", g_object_ref (root), g_object_unref);++  g_file_mount_enclosing_volume (root,+                                 0,+                                 mount_operation,+                                 cancellable,+                                 mount_callback,+                                 data);+}++static gboolean+g_vfs_afc_volume_mount_finish (GVolume       *volume,+                               GAsyncResult  *result,+                               GError       **error)+{+  GFile *root;+  gboolean res;++  root = g_object_get_data (G_OBJECT (volume), "root");+  res = g_file_mount_enclosing_volume_finish (root, result, error);++  return res;+}++static char *+g_vfs_afc_volume_get_identifier (GVolume              *volume,+                                 const char          *kind)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);+  char *id;++  id = NULL;+  if (g_str_equal (kind, G_VOLUME_IDENTIFIER_KIND_UUID) != FALSE)+    id = g_strdup (afc_volume->uuid);++  return id;+}++static char **+g_vfs_afc_volume_enumerate_identifiers (GVolume *volume)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);+  GPtrArray *res;++  res = g_ptr_array_new ();++  if (afc_volume->uuid && *afc_volume->uuid != 0)+    {+        g_ptr_array_add (res,+                         g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID));+    }++  /* Null-terminate */+  g_ptr_array_add (res, NULL);++  return (char **)g_ptr_array_free (res, FALSE);+}++static GFile *+g_vfs_afc_volume_get_activation_root (GVolume *volume)+{+  GFile *root;++  root = g_object_get_data (G_OBJECT (volume), "root");+  if (root == NULL)+    return NULL;++  return g_object_ref (root);+}++static void+g_vfs_afc_volume_iface_init (GVolumeIface *iface)+{+  iface->get_name = g_vfs_afc_volume_get_name;+  iface->get_icon = g_vfs_afc_volume_get_icon;+  iface->get_uuid = g_vfs_afc_volume_get_uuid;+  iface->get_drive = g_vfs_afc_volume_get_drive;+  iface->get_mount = g_vfs_afc_volume_get_mount;+  iface->can_mount = g_vfs_afc_volume_can_mount;+  iface->should_automount = g_vfs_afc_volume_should_automount;+  iface->mount_fn = g_vfs_afc_volume_mount;+  iface->mount_finish = g_vfs_afc_volume_mount_finish;+  iface->eject = NULL;+  iface->eject_finish = NULL;+  iface->get_identifier = g_vfs_afc_volume_get_identifier;+  iface->enumerate_identifiers = g_vfs_afc_volume_enumerate_identifiers;+  iface->get_activation_root = g_vfs_afc_volume_get_activation_root;+}++gboolean g_vfs_afc_volume_has_uuid(GVfsAfcVolume *volume, const char *uuid)+{+  GVfsAfcVolume *afc_volume = G_VFS_AFC_VOLUME (volume);+  g_return_val_if_fail (uuid != NULL, FALSE);+  return (g_strcmp0 (afc_volume->uuid, uuid) == 0);+}++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/afcvolume.h	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,44 @@+/*+ * gvfs/monitor/afc/afc-volume.h+ *+ * Copyright (c) 2008 Patrick Walton <pcwalton@cs.ucla.edu>+ */++#ifndef GVFS_MONITOR_AFC_AFC_VOLUME_H+#define GVFS_MONITOR_AFC_AFC_VOLUME_H++#include <glib-object.h>+#include <gio/gio.h>++#include "afcvolumemonitor.h"++G_BEGIN_DECLS++#define G_VFS_TYPE_AFC_VOLUME   (g_vfs_afc_volume_get_type())+#define G_VFS_AFC_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST((o), G_VFS_TYPE_AFC_VOLUME, GVfsAfcVolume))+#define G_VFS_AFC_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_AFC_VOLUME, GVfsAfcVolumeClass))+#define G_VFS_IS_AFC_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), G_VFS_TYPE_AFC_VOLUME))+#define G_VFS_IS_AFC_VOLUME_CLASS(k) ((G_TYPE_CHECK_CLASS_TYPE((k), G_VFS_TYPE_AFC_VOLUME))+#define G_VFS_AFC_VOLUME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_VFS_TYPE_AFC_VOLUME, GVfsAfcVolumeClass))++typedef struct _GVfsAfcVolume GVfsAfcVolume;+typedef struct _GVfsAfcVolumeClass GVfsAfcVolumeClass;++struct _GVfsAfcVolumeClass {+  GObjectClass parent_class;+};++GType g_vfs_afc_volume_get_type (void) G_GNUC_CONST;++GVfsAfcVolume *g_vfs_afc_volume_new (GVolumeMonitor *monitor,+                                     const char     *uuid);++gboolean g_vfs_afc_volume_has_uuid (GVfsAfcVolume *volume, const char *uuid);++G_END_DECLS++#endif /* GVFS_MONITOR_AFC_AFC_VOLUME_H */++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/afcvolumemonitor.c	2009-11-06 22:57:14.000000000 +0100@@ -0,0 +1,188 @@+/*+ * gvfs/monitor/afc/afc-volume-monitor.c+ *+ * Copyright (c) 2008 Patrick Walton <pcwalton@ucla.edu>+ */++#include <config.h>+#include <glib.h>+#include <gmodule.h>+#include <gvfsproxyvolumemonitordaemon.h>+#include <stdio.h>+#include <gio/gio.h>+#include <libiphone/libiphone.h>+#include "afcvolume.h"+#include "afcvolumemonitor.h"++struct _GVfsAfcVolumeMonitor {+  GNativeVolumeMonitor parent;+  GList *volumes;+};++G_DEFINE_TYPE(GVfsAfcVolumeMonitor, g_vfs_afc_volume_monitor, G_TYPE_VOLUME_MONITOR)++static void+g_vfs_afc_monitor_create_volume (GVfsAfcVolumeMonitor *self,+                                 const char *uuid)+{+  GVfsAfcVolume *volume = NULL;++  g_print ("creating volume for device uuid '%s'\n", uuid);++  volume = g_vfs_afc_volume_new (G_VOLUME_MONITOR (self), uuid);+  if (volume != NULL)+    {+      self->volumes = g_list_prepend (self->volumes, volume);+      g_signal_emit_by_name (self, "volume-added", volume);+    }+}++static GVfsAfcVolume *+find_volume_by_uuid (GVfsAfcVolumeMonitor *self,+                     const char * uuid)+{+  GList *l;++  for (l = self->volumes; l != NULL; l = l->next)+    {+      GVfsAfcVolume *volume = l->data;+      if (volume && g_vfs_afc_volume_has_uuid (volume, uuid))+        return volume;+    }++  return NULL;+}++static void+g_vfs_afc_monitor_remove_volume (GVfsAfcVolumeMonitor *self,+                                 const char *uuid)+{+  GVfsAfcVolume *volume = NULL;++  volume = find_volume_by_uuid (self, uuid);+  if (volume != NULL)+    {+      g_print ("removing volume for device uuid '%s'\n", uuid);+      self->volumes = g_list_remove (self->volumes, volume);+      g_signal_emit_by_name (self, "volume-removed", volume);+    }+}++static void+g_vfs_afc_monitor_iphone_event (const iphone_event_t *event, void *user_data)+{+  GVfsAfcVolumeMonitor *self;++  g_return_if_fail (event != NULL);++  self = G_VFS_AFC_VOLUME_MONITOR(user_data);++  if (event->event == IPHONE_DEVICE_ADD)+    g_vfs_afc_monitor_create_volume (self, event->uuid);+  else+    g_vfs_afc_monitor_remove_volume (self, event->uuid);+}++static GObject *+g_vfs_afc_volume_monitor_constructor (GType type, guint ncps,+                                      GObjectConstructParam *cps)+{+  GVfsAfcVolumeMonitor *self;++  /* Boilerplate code to chain from parent. */+  self = G_VFS_AFC_VOLUME_MONITOR((*G_OBJECT_CLASS(g_vfs_afc_volume_monitor_parent_class)->constructor)(type, ncps, cps));++  self->volumes = NULL;++  iphone_event_subscribe(g_vfs_afc_monitor_iphone_event, self);++  g_print ("Volume monitor alive\n");++  return G_OBJECT(self);+}++static void+list_free (GList *objects)+{+  g_list_foreach (objects, (GFunc)g_object_unref, NULL);+  g_list_free (objects);+}++static void+g_vfs_afc_volume_monitor_finalize (GObject *_self)+{+  GVfsAfcVolumeMonitor *self;++  self = G_VFS_AFC_VOLUME_MONITOR(_self);++  if (self->volumes)+    list_free (self->volumes);++  iphone_event_unsubscribe();++  if (G_OBJECT_CLASS(g_vfs_afc_volume_monitor_parent_class)->finalize)+    (*G_OBJECT_CLASS(g_vfs_afc_volume_monitor_parent_class)->finalize)( G_OBJECT(self));+}++static GList *+g_vfs_afc_volume_monitor_get_mounts (GVolumeMonitor *_self)+{+  return NULL;+}++static GList *+g_vfs_afc_volume_monitor_get_volumes (GVolumeMonitor *_self)+{+  GVfsAfcVolumeMonitor *self;+  GList *l;++  self = G_VFS_AFC_VOLUME_MONITOR (_self);++  l = g_list_copy (self->volumes);+  g_list_foreach (l, (GFunc)g_object_ref, NULL);++  return l;+}++static GList *+g_vfs_afc_volume_monitor_get_connected_drives (GVolumeMonitor *_self)+{+  return NULL;+}++static gboolean+g_vfs_afc_volume_monitor_is_supported (void)+{+  return TRUE;+}++static void+g_vfs_afc_volume_monitor_class_init (GVfsAfcVolumeMonitorClass * klass)+{+  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);+  GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS(klass);++  gobject_class->constructor = g_vfs_afc_volume_monitor_constructor;+  gobject_class->finalize = g_vfs_afc_volume_monitor_finalize;++  monitor_class->get_mounts = g_vfs_afc_volume_monitor_get_mounts;+  monitor_class->get_volumes = g_vfs_afc_volume_monitor_get_volumes;+  monitor_class->get_connected_drives = g_vfs_afc_volume_monitor_get_connected_drives;+  monitor_class->is_supported = g_vfs_afc_volume_monitor_is_supported;+}++static void+g_vfs_afc_volume_monitor_init(GVfsAfcVolumeMonitor *self)+{+}++GVolumeMonitor *+g_vfs_afc_volume_monitor_new (void)+{+  return G_VOLUME_MONITOR(g_object_new (G_VFS_TYPE_AFC_VOLUME_MONITOR,+                                        NULL));+}++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/afcvolumemonitordaemon.c	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,31 @@+/*+ * gvfs/monitor/afc/afc-volume-monitor-daemon.c+ *+ * Copyright (c) 2008-2009 Patrick Walton <pcwalton@ucla.edu>+ * Copyright (c) 2009 Martin Szulecki <opensuse@sukimashita.com>+ */++#include <config.h>++#include <glib.h>+#include <glib/gi18n-lib.h>+#include <gmodule.h>+#include <gio/gio.h>++#include <gvfsproxyvolumemonitordaemon.h>++#include "afcvolumemonitor.h"++int+main (int argc, char *argv[])+{+  g_vfs_proxy_volume_monitor_daemon_init ();+  return g_vfs_proxy_volume_monitor_daemon_main (argc,+                                                 argv,+                                                 "org.gtk.Private.AfcVolumeMonitor",+                                                 G_VFS_TYPE_AFC_VOLUME_MONITOR);+}++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/afcvolumemonitor.h	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,39 @@+/*+ * gvfs/monitor/afc/afc-volume-monitor.h+ *+ * Copyright (c) 2008 Patrick Walton <pcwalton@ucla.edu>+ */++#ifndef AFC_VOLUME_MONITOR_H+#define AFC_VOLUME_MONITOR_H++#include <glib-object.h>+#include <gio/gio.h>++G_BEGIN_DECLS++#define G_VFS_TYPE_AFC_VOLUME_MONITOR   (g_vfs_afc_volume_monitor_get_type())+#define G_VFS_AFC_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST((o), G_VFS_TYPE_AFC_VOLUME_MONITOR, GVfsAfcVolumeMonitor))+#define G_VFS_AFC_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_AFC_VOLUME_MONITOR, GVfsAfcVolumeMonitorClass))+#define G_VFS_IS_AFC_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), G_VFS_TYPE_AFC_VOLUME_MONITOR))+#define G_VFS_IS_AFC_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), G_VFS_TYPE_AFC_VOLUME_MONITOR))+#define G_VFS_AFC_VOLUME_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_VFS_TYPE_AFC_VOLUME_MONITOR, GVfsAfcVolumeMonitorClass))++typedef struct _GVfsAfcVolumeMonitor GVfsAfcVolumeMonitor;+typedef struct _GVfsAfcVolumeMonitorClass GVfsAfcVolumeMonitorClass;++struct _GVfsAfcVolumeMonitorClass {+  GVolumeMonitorClass parent_class;+};++GType g_vfs_afc_volume_monitor_get_type (void) G_GNUC_CONST;++GVolumeMonitor *g_vfs_afc_volume_monitor_new (void);++G_END_DECLS++#endif /* AFC_VOLUME_MONITOR_H */++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/Makefile.am	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,49 @@+NULL =++gvfs_src_dir = $(top_srcdir)/@with_gvfs_source@++libexec_PROGRAMS = gvfs-afc-volume-monitor++gvfs_afc_volume_monitor_SOURCES = \+	afcvolume.c		afcvolume.h \+	afcvolumemonitor.c 	afcvolumemonitor.h \+	afcvolumemonitordaemon.c \+	$(NULL)++gvfs_afc_volume_monitor_CFLAGS = \+	-DG_LOG_DOMAIN=\"GVFS-AFC\" \+	-I$(top_srcdir)/common \+	-I$(top_srcdir)/monitor/proxy \+	$(GLIB_CFLAGS) \+	$(AFC_CFLAGS) \+	$(WARN_CFLAGS) \+	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \+	-DGVFS_LOCALEDIR=\"$(localedir)\" \+	-DG_DISABLE_DEPRECATED \+	$(NULL)++gvfs_afc_volume_monitor_LDADD = \+	$(GLIB_LIBS) \+	$(DBUS_LIBS) \+	$(AFC_LIBS) \+	$(top_srcdir)/common/libgvfscommon.la \+	$(top_srcdir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \+	$(NULL)++remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors+remote_volume_monitors_DATA = afc.monitor++servicedir = $(datadir)/dbus-1/services+service_in_files = org.gtk.Private.AfcVolumeMonitor.service.in+service_DATA = $(service_in_files:.service.in=.service)++$(service_DATA): $(service_in_files) Makefile+	$(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@++clean-local:+	rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA)++DISTCLEANFILES = $(service_DATA)++EXTRA_DIST = $(service_in_files) afc.monitor+--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/monitor/afc/org.gtk.Private.AfcVolumeMonitor.service.in	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,4 @@+[D-BUS Service]+Name=org.gtk.Private.AfcVolumeMonitor+Exec=@libexecdir@/gvfs-afc-volume-monitor+--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/daemon/afc.mount.in	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,7 @@+[Mount]+Type=afc+Exec=@libexecdir@/gvfsd-afc+AutoMount=false+Scheme=afc+DefaultPort=1+--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/daemon/gvfsbackendafc.c	2009-11-06 22:57:14.000000000 +0100@@ -0,0 +1,1228 @@+/*+ * gvfs/daemon/gvfsbackendafc.c+ *+ * Copyright (c) 2008 Patrick Walton <pcwalton@cs.ucla.edu>+ */++#include <config.h>++#include <limits.h>+#include <stdint.h>+#include <stdio.h>+#include <string.h>+#include <stdlib.h>+#include <unistd.h>+#include <sys/types.h>+#include <glib/gi18n.h>+#include <errno.h>++#include <libiphone/libiphone.h>+#include <libiphone/lockdown.h>+#include <libiphone/afc.h>++#include "gvfsbackendafc.h"+#include "gvfsjobopenforread.h"+#include "gvfsjobread.h"+#include "gvfsjobseekread.h"+#include "gvfsjobopenforwrite.h"+#include "gvfsjobwrite.h"+#include "gvfsjobseekwrite.h"+#include "gvfsjobsetdisplayname.h"+#include "gvfsjobqueryinfo.h"+#include "gvfsjobqueryfsinfo.h"+#include "gvfsjobqueryattributes.h"+#include "gvfsjobenumerate.h"+#include "gvfsdaemonprotocol.h"+#include "gvfsdaemonutils.h"++#define G_VFS_BACKEND_AFC_MAX_FILE_SIZE G_MAXINT64+int g_blocksize = 4096; /* assume this is the default block size */++struct _GVfsBackendAfc {+  GVfsBackend backend;++  char uuid[41];+  char *service;+  char *model;+  gboolean connected;++  iphone_device_t dev;+  afc_client_t afc_cli;+};++struct afc_error_mapping {+  afc_error_t from;+  GIOErrorEnum to;+};++static struct afc_error_mapping afc_error_to_g_io_error[] = {+      { AFC_E_UNKNOWN_ERROR            , G_IO_ERROR_FAILED },+      { AFC_E_OP_HEADER_INVALID        , G_IO_ERROR_FAILED },+      { AFC_E_NO_RESOURCES             , G_IO_ERROR_TOO_MANY_OPEN_FILES },+      { AFC_E_READ_ERROR               , G_IO_ERROR_NOT_DIRECTORY },+      { AFC_E_WRITE_ERROR              , G_IO_ERROR_FAILED },+      { AFC_E_UNKNOWN_PACKET_TYPE      , G_IO_ERROR_FAILED },+      { AFC_E_INVALID_ARGUMENT         , G_IO_ERROR_INVALID_ARGUMENT },+      { AFC_E_OBJECT_NOT_FOUND         , G_IO_ERROR_NOT_FOUND },+      { AFC_E_OBJECT_IS_DIR            , G_IO_ERROR_IS_DIRECTORY },+      { AFC_E_DIR_NOT_EMPTY            , G_IO_ERROR_NOT_EMPTY },+      { AFC_E_PERM_DENIED              , G_IO_ERROR_PERMISSION_DENIED },+      { AFC_E_SERVICE_NOT_CONNECTED    , G_IO_ERROR_HOST_NOT_FOUND },+      { AFC_E_OP_TIMEOUT               , G_IO_ERROR_TIMED_OUT },+      { AFC_E_TOO_MUCH_DATA            , G_IO_ERROR_FAILED },+      { AFC_E_END_OF_DATA              , G_IO_ERROR_FAILED },+      { AFC_E_OP_NOT_SUPPORTED         , G_IO_ERROR_NOT_SUPPORTED },+      { AFC_E_OBJECT_EXISTS            , G_IO_ERROR_EXISTS },+      { AFC_E_OBJECT_BUSY              , G_IO_ERROR_BUSY },+      { AFC_E_NO_SPACE_LEFT            , G_IO_ERROR_NO_SPACE },+      { AFC_E_OP_WOULD_BLOCK           , G_IO_ERROR_WOULD_BLOCK },+      { AFC_E_IO_ERROR                 , G_IO_ERROR_FAILED },+      { AFC_E_OP_INTERRUPTED           , G_IO_ERROR_CANCELLED },+      { AFC_E_OP_IN_PROGRESS           , G_IO_ERROR_PENDING },+      { AFC_E_INTERNAL_ERROR           , G_IO_ERROR_FAILED },+      { AFC_E_NOT_ENOUGH_DATA          , G_IO_ERROR_CLOSED },+      { AFC_E_MUX_ERROR                , G_IO_ERROR_FAILED },+      { -1 }+};++/**+ * Tries to convert the AFC error value into a GIOError.+ *+ * @param client AFC client to retrieve status value from.+ *+ * @return errno value.+ */+static GIOErrorEnum+g_io_error_from_afc_error (afc_error_t error)+{+  GIOErrorEnum res = G_IO_ERROR_FAILED;+  int i = 0; gboolean found = FALSE;++  while (afc_error_to_g_io_error[i++].from != -1)+    {+      if (afc_error_to_g_io_error[i].from == error)+        {+          res = afc_error_to_g_io_error[i++].to;+          found = TRUE;+          break;+        }+    }++  if (!found)+    g_warning ("Unknown AFC error (%d).\n", error);++  return res;+}++G_DEFINE_TYPE(GVfsBackendAfc, g_vfs_backend_afc, G_VFS_TYPE_BACKEND)++static void+g_vfs_backend_afc_close_connection (GVfsBackendAfc *self)+{+  if (self->connected)+    {+      afc_client_free (self->afc_cli);+      g_free (self->model);+      self->model = NULL;+      iphone_device_free (self->dev);+    }+  self->connected = FALSE;+}++static int+g_vfs_backend_afc_check (afc_error_t cond, GVfsJob *job)+{+  GIOErrorEnum error;++  if (G_LIKELY(cond == AFC_E_SUCCESS))+        return 0;++  error = g_io_error_from_afc_error (cond);+  switch (cond)+    {+    case AFC_E_INTERNAL_ERROR:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("Internal Apple File Control error"));+      break;+    case AFC_E_OBJECT_NOT_FOUND:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("File does not exist"));+    case AFC_E_DIR_NOT_EMPTY:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("The directory is not empty"));+      break;+    case AFC_E_OP_TIMEOUT:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("The device did not respond"));+      break;+    case AFC_E_NOT_ENOUGH_DATA:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("The connection was interrupted"));+      break;+    case AFC_E_MUX_ERROR:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("Invalid Apple File Control data received"));+      break;+    default:+      g_vfs_job_failed (job, G_IO_ERROR, error,+                        _("Unhandled Apple File Control error (%d)"), cond);+      break;+    }++  return 1;+}++static int+g_vfs_backend_lockdownd_check (lockdownd_error_t cond, GVfsJob *job)+{+  if (G_LIKELY(cond == LOCKDOWN_E_SUCCESS))+        return 0;++  switch (cond)+    {+    case LOCKDOWN_E_INVALID_ARG:+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,+                        _("Lockdown Error: Invalid Argument"));+      break;+    default:+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,+                        _("Unhandled Lockdown error (%d)"), cond);+      break;+    }++  return 1;+}++static int+g_vfs_backend_iphone_check (iphone_error_t cond, GVfsJob *job)+{+  if (G_LIKELY(cond == IPHONE_E_SUCCESS))+        return 0;++  switch (cond)+    {+    case IPHONE_E_INVALID_ARG:+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,+                        _("iPhone Device Error: Invalid Argument"));+      break;+    case IPHONE_E_NO_DEVICE:+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,+                        _("iPhone Device Error: No device found. Make sure usbmuxd is set up correctly."));+    default:+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,+                        _("Unhandled iPhone Device error (%d)"), cond);+      break;+    }++  return 1;+}++static void+_iphone_event_cb (const iphone_event_t *event, void *user_data)+{+  GVfsBackendAfc *afc_backend = G_VFS_BACKEND_AFC (user_data);++  g_return_if_fail (afc_backend->uuid != NULL);+  if (event->event != IPHONE_DEVICE_REMOVE)+    return;+  if (g_str_equal (event->uuid, afc_backend->uuid) == FALSE)+    return;++  g_print ("Shutting down AFC backend for device uuid %s\n", afc_backend->uuid);++  g_vfs_backend_afc_close_connection (afc_backend);++  iphone_event_unsubscribe ();++  /* TODO: need a cleaner way to force unmount ourselves */+  exit (1);+}++/* Callback for mounting. */+static void+g_vfs_backend_afc_mount (GVfsBackend *backend,+                         GVfsJobMount *job,+                         GMountSpec *spec,+                         GMountSource *src,+                         gboolean automounting)+{+  const char *str;+  char *tmp;+  char *display_name;+  int port, virtual_port;+  GMountSpec *real_spec;+  GVfsBackendAfc *self;+  int retries;+  iphone_error_t err;+  lockdownd_client_t lockdown_cli = NULL;++  self = G_VFS_BACKEND_AFC(backend);+  self->connected = FALSE;++  iphone_event_subscribe (_iphone_event_cb, self);++  /* setup afc */++  str = g_mount_spec_get(spec, "host");+  if (G_UNLIKELY(str == NULL))+    {+      g_vfs_job_failed (G_VFS_JOB (job),+                        G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,+                        _("Invalid mount spec"));+      return;+    }+  if (G_UNLIKELY(sscanf(str, "%40s", (char *) &self->uuid) < 1))+    {+      g_vfs_job_failed (G_VFS_JOB(job), G_IO_ERROR, G_IO_ERROR_FAILED,+                        _("Invalid AFC location: must be in the form of "+                          "afc://uuid:port-number"));+      return;+    }++  str = g_mount_spec_get (spec, "port");+  if (str == NULL)+    virtual_port = 1;+  else+    virtual_port = atoi (str);++  /* set a generic display name */+  if (virtual_port >= 2)+    {+      self->service = g_strdup_printf ("com.apple.afc%d", virtual_port);+      display_name = g_strdup_printf (_("Service %d on Apple Mobile Device"),+                                      virtual_port);+    }+  else+    {+      self->service = g_strdup ("com.apple.afc");+      display_name = g_strdup_printf (_("Apple Mobile Device"));+    }++  g_vfs_backend_set_display_name (G_VFS_BACKEND(self), display_name);+  g_free (display_name);++  real_spec = g_mount_spec_new ("afc");+  tmp = g_strdup_printf ("%40s", (char *) &self->uuid);+  g_mount_spec_set (real_spec, "host", tmp);+  g_free (tmp);++  /* INFO: Don't ever set the DefaultPort again or everything goes crazy */+  if (virtual_port != 1)+    {+      tmp = g_strdup_printf ("%d", virtual_port);+      g_mount_spec_set (real_spec, "port", tmp);+      g_free (tmp);+    }++  g_vfs_backend_set_mount_spec (G_VFS_BACKEND(self), real_spec);+  g_mount_spec_unref (real_spec);++  retries = 0;+  do {+      err = iphone_device_new(&self->dev, self->uuid);+      if (err == IPHONE_E_SUCCESS)+          break;+      g_usleep (G_USEC_PER_SEC);+  } while (retries++ < 10);++  if (G_UNLIKELY(g_vfs_backend_iphone_check(err, G_VFS_JOB(job))))+    goto out_destroy_service;+  if (G_UNLIKELY(g_vfs_backend_lockdownd_check (lockdownd_client_new (self->dev,+                                                                      &lockdown_cli),+                                                G_VFS_JOB(job))))+    {+      goto out_destroy_dev;+    }++  /* try to use pretty device name */+  if (LOCKDOWN_E_SUCCESS == lockdownd_get_device_name (lockdown_cli, &display_name))+    {+      if (display_name)+        {+          if (virtual_port >= 2)+            {+              /* translators:+               * This is the device name, with the service being browsed in brackets, eg.:+               * Alan Smithee's iPhone (Service 2 on Apple Mobile Device */+              g_vfs_backend_set_display_name (G_VFS_BACKEND(self),+                                              g_strdup_printf (_("%s (%s)"), display_name, self->service));+            }+          else+            {+              g_vfs_backend_set_display_name (G_VFS_BACKEND(self), display_name);+            }+          g_free (display_name);+      }+    }++  if (G_UNLIKELY(g_vfs_backend_lockdownd_check (lockdownd_start_service (lockdown_cli,+                                                                         self->service, &port),+                                                G_VFS_JOB(job))))+    {+      goto out_destroy_lockdown;+    }+  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_client_new (self->dev,+                                                          port, &self->afc_cli),+                                          G_VFS_JOB(job))))+    {+      goto out_destroy_lockdown;+    }++  /* set correct fd icon spec name depending on device model */+  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_device_info_key (self->afc_cli, "Model", &self->model), G_VFS_JOB(job))))+    goto out_destroy_afc;++  if ((self->model != NULL) && (strstr(self->model, "iPod") != NULL))+    {+      g_vfs_backend_set_icon_name (G_VFS_BACKEND(self), "multimedia-player-apple-ipod-touch");+    }+  else+    {+      g_vfs_backend_set_icon_name (G_VFS_BACKEND(self), "phone-apple-iphone");+    }++  /* lockdown connection is not needed anymore */+  lockdownd_client_free (lockdown_cli);++  self->connected = TRUE;+  g_vfs_job_succeeded (G_VFS_JOB(job));+  return;++out_destroy_afc:+  afc_client_free (self->afc_cli);++out_destroy_lockdown:+  lockdownd_client_free (lockdown_cli);++out_destroy_dev:+  iphone_device_free (self->dev);++out_destroy_service:+  g_free (self->service);+  g_free(self->model);+}++static void+g_vfs_backend_afc_unmount (GVfsBackend *backend,+                           GVfsJobUnmount *job,+                           GMountUnmountFlags flags,+                           GMountSource *mount_source)+{+  GVfsBackendAfc *self;++  /* FIXME: check on G_MOUNT_UNMOUNT_FORCE flag */+  self = G_VFS_BACKEND_AFC (backend);+  g_vfs_backend_afc_close_connection (self);+  g_vfs_job_succeeded (G_VFS_JOB(job));+}++/* Callback to open an existing file for reading. */+static void+g_vfs_backend_afc_open_for_read (GVfsBackend *backend,+                                 GVfsJobOpenForRead *job,+                                 const char *path)+{+  uint64_t fd = 0;+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (self->afc_cli,+                                                         path, AFC_FOPEN_RDONLY, &fd),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_open_for_read_set_handle (job, GUINT_TO_POINTER((gulong) fd));+  g_vfs_job_open_for_read_set_can_seek (job, TRUE);+  g_vfs_job_succeeded (G_VFS_JOB(job));++  return;+}++/* Callback to open a nonexistent file for writing. */+static void+g_vfs_backend_afc_create (GVfsBackend *backend,+                          GVfsJobOpenForWrite *job,+                          const char *path,+                          GFileCreateFlags flags)+{+  uint64_t fd = 0;+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (self->afc_cli,+                                                         path, AFC_FOPEN_RW, &fd),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_open_for_write_set_handle (job, GUINT_TO_POINTER((gulong)fd));+  g_vfs_job_open_for_write_set_can_seek (job, TRUE);+  g_vfs_job_succeeded (G_VFS_JOB(job));++  return;+}++/* Callback to open a possibly-existing file for writing. */+static void+g_vfs_backend_afc_append_to (GVfsBackend *backend,+                             GVfsJobOpenForWrite *job,+                             const char *path,+                             GFileCreateFlags flags)+{+  uint64_t fd = 0;+  uint64_t off = 0;+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (self->afc_cli,+                                                         path, AFC_FOPEN_RW, &fd),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_seek (self->afc_cli,+                                                         fd, 0, SEEK_END),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_tell (self->afc_cli,+                                                         fd, &off),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_open_for_write_set_handle (job, GUINT_TO_POINTER((gulong)fd));+  g_vfs_job_open_for_write_set_can_seek (job, TRUE);+  g_vfs_job_open_for_write_set_initial_offset (job, off);+  g_vfs_job_succeeded (G_VFS_JOB(job));++  return;+}++static void+g_vfs_backend_afc_replace (GVfsBackend *backend,+                           GVfsJobOpenForWrite *job,+                           const char *filename,+                           const char *etag,+                           gboolean make_backup,+                           GFileCreateFlags flags)+{+  uint64_t fd = 0;+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail(self->connected);++  if (make_backup)+    {+      /* FIXME: implement! */+      g_vfs_job_failed (G_VFS_JOB (job),+                        G_IO_ERROR,+                        G_IO_ERROR_CANT_CREATE_BACKUP,+                        _("Backups are not yet supported."));+      return;+    }++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (self->afc_cli,+                                                         filename, AFC_FOPEN_WR, &fd),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_open_for_write_set_handle (job, GUINT_TO_POINTER((gulong)fd));+  g_vfs_job_open_for_write_set_can_seek (job, TRUE);+  g_vfs_job_succeeded (G_VFS_JOB(job));++  return;+}++/* Callback to close a file that was previously opened for reading. */+static void+g_vfs_backend_afc_close_read (GVfsBackend *backend,+                              GVfsJobCloseRead *job,+                              GVfsBackendHandle handle)+{+  GVfsBackendAfc *self;+  uint64_t fd = 0;++  fd = GPOINTER_TO_UINT(handle);+  g_return_if_fail (fd != 0);++  self = G_VFS_BACKEND_AFC(backend);++  if (self->connected)+    afc_file_close (self->afc_cli, fd);++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_close_write (GVfsBackend *backend,+                               GVfsJobCloseWrite *job,+                               GVfsBackendHandle handle)+{+  GVfsBackendAfc *self;+  uint64_t fd = 0;++  fd = GPOINTER_TO_UINT(handle);+  g_return_if_fail (fd != 0);++  self = G_VFS_BACKEND_AFC(backend);++  if (self->connected)+    afc_file_close(self->afc_cli, fd);++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_read (GVfsBackend *backend,+                        GVfsJobRead *job,+                        GVfsBackendHandle handle,+                        char *buffer,+                        gsize req)+{+  guint32 nread = 0;+  GVfsBackendAfc *self;+  uint64_t fd = 0;++  fd = GPOINTER_TO_UINT(handle);+  g_return_if_fail (fd != 0);++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (req > 0 &&+      G_UNLIKELY(g_vfs_backend_afc_check (afc_file_read (self->afc_cli,+                                                         fd, buffer, req, &nread),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_read_set_size (job, nread);+  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_write (GVfsBackend *backend,+                         GVfsJobWrite *job,+                         GVfsBackendHandle handle,+                         char *buffer,+                         gsize sz)+{+  guint32 nwritten = 0;+  GVfsBackendAfc *self;+  uint64_t fd = 0;++  fd = GPOINTER_TO_UINT(handle);+  g_return_if_fail (fd != 0);++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (sz > 0 &&+      G_UNLIKELY(g_vfs_backend_afc_check(afc_file_write (self->afc_cli,+                                                         fd, buffer, sz, &nwritten),+                                         G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_write_set_written_size (job, nwritten);+  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static int+g_vfs_backend_afc_seek (GVfsBackendAfc *self,+                        GVfsJob *job,+                        GVfsBackendHandle handle,+                        goffset offset,+                        GSeekType type)+{+  int afc_seek_type;+  uint64_t fd = 0;++  switch (type)+    {+    case G_SEEK_SET:+      afc_seek_type = SEEK_SET;+      break;+    case G_SEEK_CUR:+      afc_seek_type = SEEK_CUR;+      break;+    case G_SEEK_END:+      afc_seek_type = SEEK_END;+      break;+    default:+      g_vfs_job_failed(job, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,+                       _("Invalid seek type"));+      return 1;+    }++  fd = GPOINTER_TO_UINT(handle);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_seek (self->afc_cli,+                                                         fd, offset, afc_seek_type),+                                          job)))+    {+      return 1;+    }++  return 0;+}++static void+g_vfs_backend_afc_seek_on_read (GVfsBackend *backend,+                                GVfsJobSeekRead *job,+                                GVfsBackendHandle handle,+                                goffset offset,+                                GSeekType type)+{+  GVfsBackendAfc *self;++  g_return_if_fail (handle != NULL);++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (!g_vfs_backend_afc_seek (self, G_VFS_JOB(job), handle, offset, type))+    {+      g_vfs_job_seek_read_set_offset (job, offset);+      g_vfs_job_succeeded (G_VFS_JOB(job));+    }+}++static void+g_vfs_backend_afc_seek_on_write (GVfsBackend *backend,+                                 GVfsJobSeekWrite *job,+                                 GVfsBackendHandle handle,+                                 goffset offset,+                                 GSeekType type)+{+  GVfsBackendAfc *self;++  g_return_if_fail (handle != NULL);++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (!g_vfs_backend_afc_seek (self, G_VFS_JOB(job), handle, offset, type))+    {+      g_vfs_job_seek_write_set_offset (job, offset);+      g_vfs_job_succeeded (G_VFS_JOB(job));+    }+}++static void+g_vfs_backend_afc_set_info_from_afcinfo (GVfsBackendAfc *self,+                                         GFileInfo *info,+                                         char **afcinfo,+                                         const char *basename,+                                         GFileAttributeMatcher *matcher,+                                         GFileQueryInfoFlags flags)+{+  GFileType type = G_FILE_TYPE_REGULAR;+  GIcon *icon = NULL;+  gchar *content_type = NULL;+  char *display_name;+  char *linktarget = NULL;+  char **afctargetinfo = NULL;+  int i;++  /* get file attributes from info list */+  for (i = 0; afcinfo[i]; i += 2)+    {+      if (afcinfo[i] == NULL)+        continue;+      if (g_str_equal (afcinfo[i], "st_size"))+        {+          g_file_info_set_size (info, atoll(afcinfo[i+1]));+        } else if (g_str_equal (afcinfo[i], "st_blocks"))+          {+            g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_BLOCKS, atoi(afcinfo[i+1]));+          }+      else if (g_str_equal (afcinfo[i], "st_ifmt"))+        {+          if (g_str_equal (afcinfo[i+1], "S_IFREG"))+            {+              type = G_FILE_TYPE_REGULAR;+            }+          else if (g_str_equal (afcinfo[i+1], "S_IFDIR"))+            {+              type = G_FILE_TYPE_DIRECTORY;+            }+          else if (g_str_equal (afcinfo[i+1], "S_IFLNK"))+            {+              type = G_FILE_TYPE_SYMBOLIC_LINK;+              content_type = g_strdup ("inode/symlink");+            }+          else if (g_str_equal (afcinfo[i+1], "S_IFBLK"))+            {+              type = G_FILE_TYPE_SPECIAL;+              content_type = g_strdup ("inode/blockdevice");+            }+          else if (g_str_equal (afcinfo[i+1], "S_IFCHR"))+            {+              type = G_FILE_TYPE_SPECIAL;+              content_type = g_strdup ("inode/chardevice");+            }+          else if (g_str_equal (afcinfo[i+1], "S_IFIFO"))+            {+              type = G_FILE_TYPE_SPECIAL;+              content_type = g_strdup ("inode/fifo");+            }+          else if (g_str_equal (afcinfo[i+1], "S_IFSOCK"))+            {+              type = G_FILE_TYPE_SPECIAL;+              content_type = g_strdup ("inode/socket");+            }+          g_file_info_set_file_type (info, type);+        }+      else if (g_str_equal (afcinfo[i], "st_nlink"))+        {+          g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK, atoi(afcinfo[i+1]));+        }+      else if (g_str_equal (afcinfo[i], "st_mtime"))+        {+	  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, atoll(afcinfo[i+1]) / 1000000000);+	}+      else if (g_str_equal (afcinfo[i], "LinkTarget"))+        {+          linktarget = g_strdup (afcinfo[i+1]);+          g_file_info_set_symlink_target (info, linktarget);+          g_file_info_set_is_symlink (info, TRUE);+        }+    }++  /* and set some additional info */+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, getuid ());+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, getgid ());++  /*+   * Maybe this icon stuff should be moved out into a generic function? It+   * seems a little funny to put this in the backends.+   */+  if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)+      || g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_ICON))+    {+      if (type == G_FILE_TYPE_DIRECTORY)+        {+          content_type = g_strdup ("inode/directory");+          icon = g_themed_icon_new ("folder");+        }+      else+        {+          if (content_type == NULL)+            content_type = g_content_type_guess (basename, NULL, 0, NULL);+          if (content_type)+            {+              icon = g_content_type_get_icon (content_type);+              if (G_IS_THEMED_ICON(icon))+                g_themed_icon_append_name (G_THEMED_ICON(icon), "text-x-generic");+            }+        }++      if (content_type)+        {+          g_file_info_set_content_type (info, content_type);+          g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type);+        }++      if (icon == NULL)+        icon = g_themed_icon_new ("text-x-generic");++      g_file_info_set_icon (info, icon);+      g_object_unref (icon);+    }++  g_free (content_type);++  /* for symlinks to work we need to return GFileInfo for the linktarget */+  if ((flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0)+    {+      if (type == G_FILE_TYPE_SYMBOLIC_LINK)+        {+          /* query the linktarget instead and merge the file info of it */+          if (AFC_E_SUCCESS == afc_get_file_info (self->afc_cli, linktarget, &afctargetinfo))+            g_vfs_backend_afc_set_info_from_afcinfo (self, info, afctargetinfo, linktarget, matcher, flags);+          if (afctargetinfo)+            g_strfreev (afctargetinfo);+        }+    }++  g_free (linktarget);++  /* regardless of symlink recursion; still set the basename of the source */+  g_file_info_set_name(info, basename);++  /* handle root directory */+  if (g_str_equal (basename, "/"))+    display_name = g_strdup (g_vfs_backend_get_display_name (G_VFS_BACKEND(self)));+  else+    display_name = g_filename_display_name (basename);++  if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))+    g_file_info_set_display_name (info, display_name);++  if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME))+    g_file_info_set_edit_name (info, display_name);++  g_free (display_name);++  /* mark dot files as hidden */+  if (basename != NULL && basename[0] == '.')+     g_file_info_set_is_hidden (info, TRUE);++}++/* Callback for iterating over a directory. */+static void+g_vfs_backend_afc_enumerate (GVfsBackend *backend,+                             GVfsJobEnumerate *job,+                             const char *path,+                             GFileAttributeMatcher *matcher,+                             GFileQueryInfoFlags flags)+{+  GFileInfo *info;+  GVfsBackendAfc *self;+  gboolean trailing_slash;+  gchar *file_path;+  char **ptr, **list = NULL;+  char **afcinfo = NULL;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_read_directory (self->afc_cli, path, &list),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  trailing_slash = g_str_has_suffix (path, "/");++  for (ptr = list; *ptr; ptr++)+    {+      if (g_str_equal(*ptr, ".") || g_str_equal(*ptr, ".."))+        continue;++      if (!trailing_slash)+        file_path = g_strdup_printf ("%s/%s", path, *ptr);+      else+        file_path = g_strdup_printf ("%s%s", path, *ptr);++      /*+       * This call might fail if the file in question is removed while we're+       * iterating over the directory list. In that case, just don't include+       * it in the list.+       */+      if (G_LIKELY(afc_get_file_info(self->afc_cli, file_path, &afcinfo) == AFC_E_SUCCESS))+        {+          info = g_file_info_new ();+          g_vfs_backend_afc_set_info_from_afcinfo (self, info, afcinfo, *ptr, matcher, flags);+          g_vfs_job_enumerate_add_info (job, info);+          g_object_unref (G_OBJECT(info));+          g_strfreev (afcinfo);+        }++      g_free (file_path);+    }++  g_strfreev (list);++  g_vfs_job_enumerate_done (job);+  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_query_info (GVfsBackend *backend,+                              GVfsJobQueryInfo *job,+                              const char *path,+                              GFileQueryInfoFlags flags,+                              GFileInfo *info,+                              GFileAttributeMatcher *matcher)+{+  GVfsBackendAfc *self;+  const char *basename, *ptr;+  char **afcinfo = NULL;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_file_info (self->afc_cli, path, &afcinfo),+                                          G_VFS_JOB(job))))+    {+      if (afcinfo)+        g_strfreev(afcinfo);+      return;+    }++  ptr = strrchr (path, '/');+  if (ptr && ptr[1] != '\0')+    basename = ptr + 1;+  else+    basename = path;++  g_vfs_backend_afc_set_info_from_afcinfo (self, info, afcinfo, basename, matcher, flags);+  if (afcinfo)+    g_strfreev (afcinfo);++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++/*+ * The following keys are currently known:+ *   Model: 'iPhone1,1'+ *   FSTotalBytes: storage capacity of drive+ *   FSFreeBytes: free space on drive+ *   FSBlockSize: block granularity+ */+static void+g_vfs_backend_afc_query_fs_info (GVfsBackend *backend,+                                 GVfsJobQueryFsInfo *job,+                                 const char *path,+                                 GFileInfo *info,+                                 GFileAttributeMatcher *matcher)+{+  GVfsBackendAfc *self;+  char **kvps, **ptr;+  uint64_t totalspace = 0, freespace = 0;+  int blocksize = 0;++  self = G_VFS_BACKEND_AFC(backend);++  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afc");++  if (self->connected)+    {+      if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_device_info (self->afc_cli, &kvps), G_VFS_JOB(job))))+        return;++      for (ptr = kvps; *ptr; ptr++)+        {+          if (g_str_equal (*ptr, "FSTotalBytes"))+            {+              totalspace = g_ascii_strtoull (*(ptr+1), (char **) NULL, 10);+            }+          else if (g_str_equal (*ptr, "FSFreeBytes"))+            {+              freespace = g_ascii_strtoull (*(ptr+1), (char **) NULL, 10);+            }+          else if (g_str_equal (*ptr, "FSBlockSize"))+            {+              blocksize = atoi (*(ptr+1));+            }+        }++      g_strfreev (kvps);++      g_file_info_set_attribute_uint32 (info,+                                        G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE,+                                        (guint32) blocksize);+      g_file_info_set_attribute_uint64 (info,+                                        G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,+                                        (guint64) totalspace);+      g_file_info_set_attribute_uint64 (info,+                                        G_FILE_ATTRIBUTE_FILESYSTEM_FREE,+                                        (guint64) freespace);+      g_file_info_set_attribute_boolean (info,+                                         G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,+                                         FALSE);+      g_file_info_set_attribute_uint32 (info,+                                        G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,+                                        G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);+    }++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_set_display_name (GVfsBackend *backend,+                                    GVfsJobSetDisplayName *job,+                                    const char *filename,+                                    const char *display_name)+{+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_rename_path (self->afc_cli,+                                                           filename, display_name),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_set_display_name_set_new_path (job, display_name);++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_make_directory (GVfsBackend *backend,+                                  GVfsJobMakeDirectory *job,+                                  const char *path)+{+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail(self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_make_directory (self->afc_cli,+                                                              path),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_make_symlink (GVfsBackend *backend,+                                GVfsJobMakeSymlink *job,+                                const char *filename,+                                const char *symlink_value)+{+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_make_link (self->afc_cli,+                                                         AFC_SYMLINK, symlink_value, filename),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_move (GVfsBackend *backend,+                        GVfsJobMove *job,+                        const char *source,+                        const char *destination,+                        GFileCopyFlags flags,+                        GFileProgressCallback progress_callback,+                        gpointer progress_callback_data)+{+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail(self->connected);++  if (flags & G_FILE_COPY_BACKUP)+    {+      /* FIXME: implement! */+      g_vfs_job_failed (G_VFS_JOB (job),+                        G_IO_ERROR,+                        G_IO_ERROR_CANT_CREATE_BACKUP,+                        _("Backups are not yet supported."));+      return;+    }++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_rename_path (self->afc_cli,+                                                           source, destination),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_succeeded (G_VFS_JOB(job));+}++static void+g_vfs_backend_afc_delete (GVfsBackend *backend,+                          GVfsJobDelete *job,+                          const char *filename)+{+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(backend);+  g_return_if_fail (self->connected);++  if (G_UNLIKELY(g_vfs_backend_afc_check (afc_remove_path (self->afc_cli,+                                                           filename),+                                          G_VFS_JOB(job))))+    {+      return;+    }++  g_vfs_job_succeeded (G_VFS_JOB(job));+}+++static void+g_vfs_backend_afc_finalize (GObject *obj)+{+  GVfsBackendAfc *self;++  self = G_VFS_BACKEND_AFC(obj);+  g_vfs_backend_afc_close_connection (self);++  if (G_OBJECT_CLASS(g_vfs_backend_afc_parent_class)->finalize)+    (*G_OBJECT_CLASS(g_vfs_backend_afc_parent_class)->finalize) (obj);+}++static void+g_vfs_backend_afc_init (GVfsBackendAfc *self)+{+  if (g_getenv ("GVFS_DEBUG") != NULL)+    {+      /* enable full debugging */+      iphone_set_debug_level (1);+      iphone_set_debug_mask (DBGMASK_ALL);+    }+}++static void+g_vfs_backend_afc_class_init (GVfsBackendAfcClass *klass)+{+  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);+  GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS(klass);++  gobject_class->finalize = g_vfs_backend_afc_finalize;++  backend_class->mount            = g_vfs_backend_afc_mount;+  backend_class->unmount          = g_vfs_backend_afc_unmount;+  backend_class->open_for_read    = g_vfs_backend_afc_open_for_read;+  backend_class->close_read       = g_vfs_backend_afc_close_read;+  backend_class->read             = g_vfs_backend_afc_read;+  backend_class->seek_on_read     = g_vfs_backend_afc_seek_on_read;+  backend_class->create           = g_vfs_backend_afc_create;+  backend_class->append_to        = g_vfs_backend_afc_append_to;+  backend_class->replace          = g_vfs_backend_afc_replace;+  backend_class->close_write      = g_vfs_backend_afc_close_write;+  backend_class->write            = g_vfs_backend_afc_write;+  backend_class->seek_on_write    = g_vfs_backend_afc_seek_on_write;+  backend_class->enumerate        = g_vfs_backend_afc_enumerate;+  backend_class->query_info       = g_vfs_backend_afc_query_info;+  backend_class->query_fs_info    = g_vfs_backend_afc_query_fs_info;+  backend_class->make_directory   = g_vfs_backend_afc_make_directory;+  backend_class->delete           = g_vfs_backend_afc_delete;+  backend_class->make_symlink     = g_vfs_backend_afc_make_symlink;+  backend_class->move             = g_vfs_backend_afc_move;+  backend_class->set_display_name = g_vfs_backend_afc_set_display_name;+}++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */--- /dev/null	2009-11-01 17:13:01.333999439 +0100+++ gvfs/daemon/gvfsbackendafc.h	2009-10-11 00:54:39.000000000 +0200@@ -0,0 +1,37 @@+/*+ * gvfs/daemon/gvfsbackendafc.h+ *+ * Copyright (c) 2008 Patrick Walton <pcwalton@ucla.edu>+ */++#ifndef GVFSBACKENDAFC_H+#define GVFSBACKENDAFC_H++#include <gvfsbackend.h>+#include <gmountspec.h>++G_BEGIN_DECLS++#define G_VFS_TYPE_BACKEND_AFC   (g_vfs_backend_afc_get_type())+#define G_VFS_BACKEND_AFC(o) (G_TYPE_CHECK_INSTANCE_CAST((o), G_VFS_TYPE_BACKEND_AFC, GVfsBackendAfc))+#define G_VFS_BACKEND_AFC_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_AFC, GVfsBackendAfcClass))+#define G_VFS_IS_BACKEND_AFC(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), G_VFS_TYPE_BACKEND_AFC))+#define G_VFS_IS_BACKEND_AFC_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), G_VFS_TYPE_BACKEND_AFC))+#define G_VFS_BACKEND_AFC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_VFS_TYPE_BACKEND_AFC, GVfsBackendAfcClass))++typedef struct _GVfsBackendAfc GVfsBackendAfc;+typedef struct _GVfsBackendAfcClass GVfsBackendAfcClass;++struct _GVfsBackendAfcClass {+    GVfsBackendClass parent_class;+};++GType g_vfs_backend_afc_get_type (void) G_GNUC_CONST;++G_END_DECLS++#endif /* GVFSBACKENDAFC_H */++/*+ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai+ */