gvfs/0001-Add-AFC-backend.patch
2009-08-14 14:50:11 +00:00

2239 lines
66 KiB
Diff

From 8675a60e030fc91292d19cfd2cd9a6d74dd1776f Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Thu, 6 Aug 2009 22:55:47 +0100
Subject: [PATCH] Add AFC backend
Add 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 | 1228 ++++++++++++++++++++
daemon/gvfsbackendafc.h | 37 +
monitor/Makefile.am | 6 +-
monitor/afc/Makefile.am | 49 +
monitor/afc/afc.monitor | 5 +
monitor/afc/afcvolume.c | 353 ++++++
monitor/afc/afcvolume.h | 44 +
monitor/afc/afcvolumemonitor.c | 224 ++++
monitor/afc/afcvolumemonitor.h | 39 +
monitor/afc/afcvolumemonitordaemon.c | 31 +
.../org.gtk.Private.AfcVolumeMonitor.service.in | 4 +
14 files changed, 2074 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.in
diff --git a/configure.ac b/configure.ac
index 9bfb8ad..6b3838c 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.am
index a03eba1..cf68391 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)
@@ -442,3 +448,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/daemon/afc.mount.in b/daemon/afc.mount.in
new file mode 100644
index 0000000..727d833
--- /dev/null
+++ b/daemon/afc.mount.in
@@ -0,0 +1,7 @@
+[Mount]
+Type=afc
+Exec=@libexecdir@/gvfsd-afc
+AutoMount=false
+Scheme=afc
+DefaultPort=1
+
diff --git a/daemon/gvfsbackendafc.c b/daemon/gvfsbackendafc.c
new file mode 100644
index 0000000..d30ede8
--- /dev/null
+++ b/daemon/gvfsbackendafc.c
@@ -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 <gudev/gudev.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;
+
+ GUdevClient *client;
+
+ 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
+_uevent_cb (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *device,
+ gpointer user_data)
+{
+ GVfsBackendAfc *afc_backend = G_VFS_BACKEND_AFC (user_data);
+ const char *uuid;
+
+ g_return_if_fail (afc_backend->uuid != NULL);
+
+ if (g_str_equal (action, "remove") == FALSE)
+ return;
+ uuid = g_udev_device_get_property (device, "ID_SERIAL_SHORT");
+ if (uuid == NULL ||
+ g_str_equal (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);
+
+ /* 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;
+ const gchar * const subsystems[] = { "usb", NULL };
+
+ self = G_VFS_BACKEND_AFC(backend);
+ self->connected = FALSE;
+ self->client = g_udev_client_new (subsystems);
+ g_signal_connect (G_OBJECT (self->client), "uevent",
+ G_CALLBACK (_uevent_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", &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", &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_get_device_by_uuid(&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 */
+ self->model = afc_get_device_info_field (self->afc_cli, "Model");
+ if (G_UNLIKELY(self->model == NULL))
+ goto out_destroy_afc;
+
+ if (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], "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);
+
+ 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);
+}
+
+/* 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
+ */
diff --git a/daemon/gvfsbackendafc.h b/daemon/gvfsbackendafc.h
new file mode 100644
index 0000000..6291180
--- /dev/null
+++ b/daemon/gvfsbackendafc.h
@@ -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
+ */
diff --git a/monitor/Makefile.am b/monitor/Makefile.am
index 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
diff --git a/monitor/afc/Makefile.am b/monitor/afc/Makefile.am
new file mode 100644
index 0000000..9b3b17c
--- /dev/null
+++ b/monitor/afc/Makefile.am
@@ -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
+ @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
+
+clean-local:
+ rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA)
+
+DISTCLEANFILES = $(service_DATA)
+
+EXTRA_DIST = $(service_in_files) afc.monitor
+
diff --git a/monitor/afc/afc.monitor b/monitor/afc/afc.monitor
new file mode 100644
index 0000000..1663573
--- /dev/null
+++ b/monitor/afc/afc.monitor
@@ -0,0 +1,5 @@
+[RemoteVolumeMonitor]
+Name=GProxyVolumeMonitorAfc
+DBusName=org.gtk.Private.AfcVolumeMonitor
+IsNative=false
+
diff --git a/monitor/afc/afcvolume.c b/monitor/afc/afcvolume.c
new file mode 100644
index 0000000..688f75b
--- /dev/null
+++ b/monitor/afc/afcvolume.c
@@ -0,0 +1,353 @@
+/*
+ * 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 *self_)
+{
+ GVfsAfcVolume *self;
+
+ self = G_VFS_AFC_VOLUME(self);
+
+ 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_get_device_by_uuid (&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 = afc_get_device_info_field (afc_cli, "Model");
+ if (model != NULL)
+ {
+ 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 ();
+
+ g_ptr_array_add (res,
+ g_strdup (G_VOLUME_IDENTIFIER_KIND_HAL_UDI));
+
+ 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
+ */
diff --git a/monitor/afc/afcvolume.h b/monitor/afc/afcvolume.h
new file mode 100644
index 0000000..de24cd5
--- /dev/null
+++ b/monitor/afc/afcvolume.h
@@ -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
+ */
diff --git a/monitor/afc/afcvolumemonitor.c b/monitor/afc/afcvolumemonitor.c
new file mode 100644
index 0000000..93926eb
--- /dev/null
+++ b/monitor/afc/afcvolumemonitor.c
@@ -0,0 +1,224 @@
+/*
+ * 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>
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+#include "afcvolume.h"
+#include "afcvolumemonitor.h"
+
+struct _GVfsAfcVolumeMonitor {
+ GNativeVolumeMonitor parent;
+ GUdevClient *client;
+ 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_uevent (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *device,
+ gpointer user_data)
+{
+ GVfsAfcVolumeMonitor *self;
+ const char *vendor, *uuid;
+
+ self = G_VFS_AFC_VOLUME_MONITOR(user_data);
+
+ /* Vendor is Apple? */
+ vendor = g_udev_device_get_property (device, "ID_VENDOR");
+ if (vendor == NULL ||
+ g_str_equal (vendor, "Apple_Inc.") == FALSE) {
+ return;
+ }
+
+ /* Get us a UUID */
+ uuid = g_udev_device_get_property (device, "ID_SERIAL_SHORT");
+ if (uuid == NULL)
+ {
+ g_message ("No serial for device");
+ return;
+ }
+
+ if (g_str_equal (action, "add") != FALSE)
+ g_vfs_afc_monitor_create_volume (self, uuid);
+ else if (g_str_equal (action, "remove") != FALSE)
+ g_vfs_afc_monitor_remove_volume (self, uuid);
+}
+
+static GObject *
+g_vfs_afc_volume_monitor_constructor (GType type, guint ncps,
+ GObjectConstructParam *cps)
+{
+ GVfsAfcVolumeMonitor *self;
+ GList *devices, *l;
+ const gchar * const subsystems[] = { "usb", NULL };
+
+ /* 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->client = g_udev_client_new (subsystems);
+ g_signal_connect (G_OBJECT (self->client), "uevent",
+ G_CALLBACK (g_vfs_afc_monitor_uevent), self);
+
+ self->volumes = NULL;
+
+ devices = g_udev_client_query_by_subsystem (self->client, subsystems[0]);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ GUdevDevice *device = l->data;
+ g_vfs_afc_monitor_uevent (self->client, "add", device, self);
+ g_object_unref (device);
+ }
+ g_list_free (devices);
+
+ 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);
+
+ if (self->client)
+ {
+ g_object_unref (self->client);
+ self->client = NULL;
+ }
+
+ 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
+ */
diff --git a/monitor/afc/afcvolumemonitor.h b/monitor/afc/afcvolumemonitor.h
new file mode 100644
index 0000000..0bd5f32
--- /dev/null
+++ b/monitor/afc/afcvolumemonitor.h
@@ -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
+ */
diff --git a/monitor/afc/afcvolumemonitordaemon.c b/monitor/afc/afcvolumemonitordaemon.c
new file mode 100644
index 0000000..9c24a34
--- /dev/null
+++ b/monitor/afc/afcvolumemonitordaemon.c
@@ -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
+ */
diff --git a/monitor/afc/org.gtk.Private.AfcVolumeMonitor.service.in b/monitor/afc/org.gtk.Private.AfcVolumeMonitor.service.in
new file mode 100644
index 0000000..4e6bd33
--- /dev/null
+++ b/monitor/afc/org.gtk.Private.AfcVolumeMonitor.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.gtk.Private.AfcVolumeMonitor
+Exec=@libexecdir@/gvfs-afc-volume-monitor
+
--
1.6.2.5