summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2010-11-30 14:32:45 +0000
committerBastien Nocera <hadess@hadess.net>2010-12-17 11:36:32 +0000
commit5f6132bf29f469390e2f434b9e1faaf7ddf3dab4 (patch)
tree8d9d3bac40163fd1d68a066df7464bb1c21bfd0c
parent073715e62f7618a78f1ca07663cd734bbd545fa6 (diff)
downloadgvfs-5f6132bf29f469390e2f434b9e1faaf7ddf3dab4.tar.gz
afc: Add support for House Arrest protocol
To access the Documents/ folder within applications. https://bugzilla.gnome.org/show_bug.cgi?id=636132
-rw-r--r--configure.ac4
-rw-r--r--daemon/gvfsbackendafc.c1322
2 files changed, 1185 insertions, 141 deletions
diff --git a/configure.ac b/configure.ac
index 00a31ff3..9ccb594f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -316,10 +316,10 @@ AFC_LIBS=
AFC_CFLAGS=
if test "x$enable_afc" != "xno" ; then
- PKG_CHECK_EXISTS(libimobiledevice-1.0 >= 0.9.7, msg_afc=yes)
+ PKG_CHECK_EXISTS(libimobiledevice-1.0 >= 1.1.0 libplist >= 0.15, msg_afc=yes)
if test "x$msg_afc" = "xyes"; then
- PKG_CHECK_MODULES(AFC, libimobiledevice-1.0)
+ PKG_CHECK_MODULES(AFC, libimobiledevice-1.0 libplist)
AC_DEFINE(HAVE_AFC, 1, [Define to 1 if AFC is going to be built])
fi
fi
diff --git a/daemon/gvfsbackendafc.c b/daemon/gvfsbackendafc.c
index ae266c4c..ae3cd99e 100644
--- a/daemon/gvfsbackendafc.c
+++ b/daemon/gvfsbackendafc.c
@@ -2,6 +2,7 @@
* gvfs/daemon/gvfsbackendafc.c
*
* Copyright (c) 2008 Patrick Walton <pcwalton@cs.ucla.edu>
+ * Copyright (c) 2010 Bastien Nocera <hadess@hadess.net>
*/
#include <config.h>
@@ -19,6 +20,9 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/afc.h>
+#include <libimobiledevice/house_arrest.h>
+#include <libimobiledevice/installation_proxy.h>
+#include <libimobiledevice/sbservices.h>
#include "gvfsbackendafc.h"
#include "gvfsjobopenforread.h"
@@ -38,11 +42,6 @@
#define G_VFS_BACKEND_AFC_MAX_FILE_SIZE G_MAXINT64
int g_blocksize = 4096; /* assume this is the default block size */
-/* AFC_E_INVALID_ARGUMENT was renamed between 0.9.7 and 1.0.0 */
-#ifndef AFC_E_INVALID_ARG
-#define AFC_E_INVALID_ARG AFC_E_INVALID_ARGUMENT
-#endif /* !AFC_E_INVALID_ARG */
-
typedef enum {
IOS_UNKNOWN = 0,
IOS1,
@@ -51,6 +50,26 @@ typedef enum {
IOS4
} HostOSVersion;
+typedef enum {
+ ACCESS_MODE_UNDEFINED = 0,
+ ACCESS_MODE_AFC,
+ ACCESS_MODE_HOUSE_ARREST
+} AccessMode;
+
+typedef struct {
+ guint64 fd;
+ afc_client_t afc_cli;
+} FileHandle;
+
+typedef struct {
+ char *display_name;
+ char *id;
+ char *icon_path;
+ gboolean hidden;
+ house_arrest_client_t house_arrest;
+ afc_client_t afc_cli;
+} AppInfo;
+
struct _GVfsBackendAfc {
GVfsBackend backend;
@@ -58,10 +77,17 @@ struct _GVfsBackendAfc {
char *service;
char *model;
gboolean connected;
+ AccessMode mode;
HostOSVersion version;
idevice_t dev;
- afc_client_t afc_cli;
+ afc_client_t afc_cli; /* for ACCESS_MODE_AFC */
+
+ /* for ACCESS_MODE_HOUSE_ARREST */
+ GHashTable *apps; /* hash table of AppInfo */
+ instproxy_client_t inst;
+ sbservices_client_t sbs;
+ GMutex *apps_lock;
};
struct afc_error_mapping {
@@ -123,7 +149,7 @@ g_io_error_from_afc_error (afc_error_t error)
}
if (!found)
- g_warning ("Unknown AFC error (%d).\n", error);
+ g_message ("Unknown AFC error (%d).\n", error);
return res;
}
@@ -135,10 +161,37 @@ g_vfs_backend_afc_close_connection (GVfsBackendAfc *self)
{
if (self->connected)
{
- afc_client_free (self->afc_cli);
+ if (self->mode == ACCESS_MODE_AFC)
+ {
+ afc_client_free (self->afc_cli);
+ }
+ else
+ {
+ if (self->apps != NULL)
+ {
+ g_hash_table_destroy (self->apps);
+ self->apps = NULL;
+ }
+ if (self->inst)
+ {
+ instproxy_client_free (self->inst);
+ self->inst = NULL;
+ }
+ if (self->sbs)
+ {
+ sbservices_client_free (self->sbs);
+ self->sbs = NULL;
+ }
+ if (self->apps_lock)
+ {
+ g_mutex_free (self->apps_lock);
+ self->apps_lock = NULL;
+ }
+ }
g_free (self->model);
self->model = NULL;
idevice_free (self->dev);
+ self->dev = NULL;
}
self->connected = FALSE;
}
@@ -187,6 +240,44 @@ g_vfs_backend_afc_check (afc_error_t cond, GVfsJob *job)
}
static int
+g_vfs_backend_inst_check (instproxy_error_t cond, GVfsJob *job)
+{
+ if (G_LIKELY(cond == INSTPROXY_E_SUCCESS))
+ {
+ return 0;
+ }
+
+ switch (cond)
+ {
+ default:
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Unhandled Inst proxy error (%d)"), cond);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+g_vfs_backend_sbs_check (sbservices_error_t cond, GVfsJob *job)
+{
+ if (G_LIKELY(cond == INSTPROXY_E_SUCCESS))
+ {
+ return 0;
+ }
+
+ switch (cond)
+ {
+ default:
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Unhandled SBServices proxy 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))
@@ -235,6 +326,22 @@ g_vfs_backend_idevice_check (idevice_error_t cond, GVfsJob *job)
}
static void
+app_info_free (AppInfo *info)
+{
+ /* Note, those are allocated by the plist parser, so we use free(), not g_free() */
+ free (info->display_name);
+ free (info->id);
+
+ g_free (info->icon_path);
+ if (info->house_arrest)
+ house_arrest_client_free (info->house_arrest);
+ if (info->afc_cli)
+ afc_client_free (info->afc_cli);
+
+ g_free (info);
+}
+
+static void
_idevice_event_cb (const idevice_event_t *event, void *user_data)
{
GVfsBackendAfc *afc_backend = G_VFS_BACKEND_AFC (user_data);
@@ -314,17 +421,29 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
virtual_port = atoi (str);
/* set a generic display name */
- if (virtual_port >= 2)
- {
+ switch (virtual_port) {
+ case 1:
+ self->mode = ACCESS_MODE_AFC;
+ self->service = g_strdup ("com.apple.afc");
+ display_name = g_strdup_printf (_("Apple Mobile Device"));
+ break;
+ case 2:
+ self->mode = ACCESS_MODE_AFC;
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"));
- }
+ break;
+ case 3:
+ self->mode = ACCESS_MODE_HOUSE_ARREST;
+ self->service = g_strdup ("com.apple.mobile.house_arrest");
+ display_name = g_strdup_printf (_("Documents on Apple Mobile Device"));
+ break;
+ default:
+ 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;
+ }
g_vfs_backend_set_display_name (G_VFS_BACKEND(self), display_name);
g_free (display_name);
@@ -366,22 +485,31 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
{
if (display_name)
{
- if (virtual_port >= 2)
- {
+ switch (virtual_port) {
+ case 1:
+ g_vfs_backend_set_display_name (G_VFS_BACKEND(self), display_name);
+ break;
+ case 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 */
+ * Alan Smithee's iPhone (jailbreak) */
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_strdup_printf (_("%s (jailbreak)"), display_name));
+ break;
+ case 3:
+ /* translators:
+ * This is "Documents on foo" where foo is the device name, eg.:
+ * Documents on Alan Smithee's iPhone */
+ g_vfs_backend_set_display_name (G_VFS_BACKEND(self),
+ g_strdup_printf (_("Documents on %s"), display_name));
+ break;
+ default:
+ g_assert_not_reached ();
+ }
}
}
- /* set correct fd icon spec name depending on device model */
+ /* set correct freedesktop icon spec name depending on device model */
value = NULL;
if (G_UNLIKELY(g_vfs_backend_lockdownd_check (lockdownd_get_value (lockdown_cli, NULL, "DeviceClass", &value), G_VFS_JOB(job))))
goto out_destroy_lockdown;
@@ -471,24 +599,70 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
if (G_UNLIKELY(g_vfs_backend_lockdownd_check (lerr, G_VFS_JOB(job))))
goto out_destroy_dev;
- 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;
- }
+ switch (self->mode) {
+ case ACCESS_MODE_AFC:
+ 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;
+ }
+ break;
+ case ACCESS_MODE_HOUSE_ARREST:
+ if (G_UNLIKELY(g_vfs_backend_lockdownd_check (lockdownd_start_service (lockdown_cli,
+ "com.apple.mobile.installation_proxy", &port),
+ G_VFS_JOB(job))))
+ {
+ g_warning ("couldn't start inst proxy");
+ goto out_destroy_lockdown;
+ }
+ if (G_UNLIKELY(g_vfs_backend_inst_check (instproxy_client_new (self->dev,
+ port, &self->inst),
+ G_VFS_JOB(job))))
+ {
+ g_warning ("couldn't create inst proxy instance");
+ goto out_destroy_lockdown;
+ }
+ if (G_UNLIKELY(g_vfs_backend_lockdownd_check (lockdownd_start_service (lockdown_cli,
+ "com.apple.springboardservices", &port),
+ G_VFS_JOB(job))))
+ {
+ g_warning ("couldn't start SBServices proxy");
+ goto out_destroy_lockdown;
+ }
+ if (G_UNLIKELY(g_vfs_backend_sbs_check (sbservices_client_new (self->dev,
+ port, &self->sbs),
+ G_VFS_JOB(job))))
+ {
+ g_warning ("couldn't create SBServices proxy instance");
+ goto out_destroy_lockdown;
+ }
+ /* Create directory for the icon cache */
+ {
+ char *path;
+
+ path = g_build_filename (g_get_user_cache_dir (),
+ "libimobiledevice",
+ "icons", NULL);
+ g_mkdir_with_parents (path, 0755);
+ g_free (path);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
/* lockdown connection is not needed anymore */
lockdownd_client_free (lockdown_cli);
/* Add camera item if necessary */
- if (virtual_port < 2)
+ if (self->mode == ACCESS_MODE_AFC)
{
dcim_afcinfo = NULL;
if (afc_get_file_info (self->afc_cli, "/DCIM", &dcim_afcinfo) == AFC_E_SUCCESS)
@@ -525,20 +699,22 @@ g_vfs_backend_afc_unmount (GVfsBackend *backend,
/* 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));
}
-static gboolean file_get_info (GVfsBackendAfc *backend, const char *path, GFileInfo *info);
+static gboolean file_get_info (GVfsBackendAfc *backend, afc_client_t afc_cli, const char *path, GFileInfo *info);
static gboolean
is_directory (GVfsBackendAfc *backend,
+ afc_client_t afc_cli,
const char *path)
{
gboolean result = FALSE;
GFileInfo *info;
info = g_file_info_new();
- if (file_get_info (backend, path, info))
+ if (file_get_info (backend, afc_cli, path, info))
{
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
result = TRUE;
@@ -550,13 +726,14 @@ is_directory (GVfsBackendAfc *backend,
static gboolean
is_regular (GVfsBackendAfc *backend,
+ afc_client_t afc_cli,
const char *path)
{
gboolean result = FALSE;
GFileInfo *info;
info = g_file_info_new();
- if (file_get_info (backend, path, info))
+ if (file_get_info (backend, afc_cli, path, info))
{
if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
result = TRUE;
@@ -566,6 +743,135 @@ is_regular (GVfsBackendAfc *backend,
return result;
}
+static void
+g_vfs_backend_setup_afc_for_app (GVfsBackendAfc *self,
+ const char *id)
+{
+ AppInfo *info;
+ lockdownd_client_t lockdown_cli;
+ guint16 port;
+ house_arrest_client_t house_arrest;
+ afc_client_t afc;
+ plist_t dict, error;
+
+ info = g_hash_table_lookup (self->apps, id);
+
+ if (info == NULL ||
+ info->afc_cli != NULL)
+ return;
+
+ /* Load house arrest and afc now! */
+ lockdown_cli = NULL;
+ if (lockdownd_client_new_with_handshake (self->dev, &lockdown_cli, "gvfsd-afc") != LOCKDOWN_E_SUCCESS)
+ {
+ g_warning ("Failed to get a lockdown to start house arrest for app %s", info->id);
+ return;
+ }
+ if (lockdownd_start_service (lockdown_cli, "com.apple.mobile.house_arrest", &port) != LOCKDOWN_E_SUCCESS)
+ {
+ lockdownd_client_free (lockdown_cli);
+ g_warning ("Failed to start house arrest for app %s", info->id);
+ return;
+ }
+
+ house_arrest = NULL;
+ house_arrest_client_new (self->dev, port, &house_arrest);
+ if (house_arrest == NULL)
+ {
+ g_warning ("Failed to start house arrest for app %s", info->id);
+ lockdownd_client_free (lockdown_cli);
+ return;
+ }
+
+ dict = NULL;
+ if (house_arrest_send_command (house_arrest, "VendContainer", info->id) != HOUSE_ARREST_E_SUCCESS ||
+ house_arrest_get_result (house_arrest, &dict) != HOUSE_ARREST_E_SUCCESS)
+ {
+ g_warning ("Failed to set up house arrest for app %s", info->id);
+ house_arrest_client_free (house_arrest);
+ lockdownd_client_free (lockdown_cli);
+ return;
+ }
+ error = plist_dict_get_item (dict, "Error");
+ if (error != NULL)
+ {
+ char *str;
+
+ plist_get_string_val (error, &str);
+ g_warning ("Failed to set up house arrest for app %s: %s", info->id, str);
+ free (str);
+ plist_free (dict);
+
+ house_arrest_client_free (house_arrest);
+ lockdownd_client_free (lockdown_cli);
+ return;
+ }
+ plist_free (dict);
+
+ lockdownd_client_free (lockdown_cli);
+
+ afc = NULL;
+ afc_client_new_from_house_arrest_client(house_arrest, &afc);
+ if (afc == NULL)
+ {
+ g_warning ("Failed to set up afc client for app %s", info->id);
+ house_arrest_client_free (house_arrest);
+
+ return;
+ }
+
+ info->house_arrest = house_arrest;
+ info->afc_cli = afc;
+}
+
+/* If force_afc_mount is TRUE, then we'll try to mount
+ * the app if there's one in the path, otherwise, we'll hold on */
+static char *
+g_vfs_backend_parse_house_arrest_path (GVfsBackendAfc *self,
+ gboolean force_afc_mount,
+ const char *path,
+ char **new_path)
+{
+ char **comps;
+ char *s;
+ char *app;
+ gboolean setup_afc;
+
+ if (path == NULL || *path == '\0' || g_str_equal (path, "/"))
+ {
+ *new_path = NULL;
+ return NULL;
+ }
+
+ if (*path != '/')
+ comps = g_strsplit (path, "/", -1);
+ else
+ comps = g_strsplit (path + 1, "/", -1);
+
+ setup_afc = force_afc_mount;
+ app = g_strdup (comps[0]);
+ s = g_strjoinv ("/", comps + 1);
+ if (*s == '\0')
+ {
+ g_free (s);
+ *new_path = g_strdup ("/");
+ }
+ else
+ {
+ *new_path = s;
+ setup_afc = TRUE;
+ }
+ g_strfreev (comps);
+
+ if (app != NULL &&
+ setup_afc)
+ {
+ g_vfs_backend_setup_afc_for_app (self, app);
+ }
+
+ return app;
+}
+
/* Callback to open an existing file for reading. */
static void
g_vfs_backend_afc_open_for_read (GVfsBackend *backend,
@@ -574,34 +880,71 @@ g_vfs_backend_afc_open_for_read (GVfsBackend *backend,
{
uint64_t fd = 0;
GVfsBackendAfc *self;
+ char *new_path;
+ afc_client_t afc_cli;
+ FileHandle *handle;
self = G_VFS_BACKEND_AFC(backend);
g_return_if_fail (self->connected);
- if (is_directory (self, path))
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ char *app;
+ AppInfo *info;
+
+ new_path = NULL;
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, path, &new_path);
+ if (app == NULL)
+ goto is_dir_bail;
+
+ if (g_str_equal (new_path, "/"))
+ goto is_dir_bail;
+
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ goto not_found_bail;
+
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
{
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (is_directory (self, afc_cli, new_path ? new_path : path))
+ {
+is_dir_bail:
+ g_free (new_path);
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_IS_DIRECTORY,
_("Can't open directory"));
return;
}
- if (!is_regular (self, path))
+ if (!is_regular (self, afc_cli, new_path ? new_path : path))
{
+not_found_bail:
+ g_free (new_path);
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("File doesn't exist"));
return;
}
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (self->afc_cli,
- path, AFC_FOPEN_RDONLY, &fd),
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (afc_cli,
+ new_path ? new_path : path, AFC_FOPEN_RDONLY, &fd),
G_VFS_JOB(job))))
{
return;
}
- g_vfs_job_open_for_read_set_handle (job, GUINT_TO_POINTER((gulong) fd));
+ handle = g_new0 (FileHandle, 1);
+ handle->fd = fd;
+ handle->afc_cli = afc_cli;
+
+ g_vfs_job_open_for_read_set_handle (job, handle);
g_vfs_job_open_for_read_set_can_seek (job, TRUE);
g_vfs_job_succeeded (G_VFS_JOB(job));
@@ -617,18 +960,55 @@ g_vfs_backend_afc_create (GVfsBackend *backend,
{
uint64_t fd = 0;
GVfsBackendAfc *self;
+ char *new_path, *app;
+ afc_client_t afc_cli;
+ FileHandle *fh;
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),
+ new_path = NULL;
+
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ AppInfo *info;
+
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, path, &new_path);
+ if (app == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB(job));
+ return;
+ }
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (afc_cli,
+ new_path ? new_path : path, AFC_FOPEN_RW, &fd),
G_VFS_JOB(job))))
{
+ g_free (new_path);
return;
}
- g_vfs_job_open_for_write_set_handle (job, GUINT_TO_POINTER((gulong)fd));
+ g_free (new_path);
+
+ fh = g_new0 (FileHandle, 1);
+ fh->fd = fd;
+ fh->afc_cli = afc_cli;
+
+ g_vfs_job_open_for_write_set_handle (job, fh);
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_succeeded (G_VFS_JOB(job));
@@ -645,32 +1025,71 @@ g_vfs_backend_afc_append_to (GVfsBackend *backend,
uint64_t fd = 0;
uint64_t off = 0;
GVfsBackendAfc *self;
+ char *new_path, *app;
+ afc_client_t afc_cli;
+ FileHandle *fh;
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),
+ new_path = NULL;
+
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ AppInfo *info;
+
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, path, &new_path);
+ if (app == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB(job));
+ return;
+ }
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (afc_cli,
+ new_path ? new_path : path, AFC_FOPEN_RW, &fd),
G_VFS_JOB(job))))
{
+ g_free (new_path);
return;
}
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_seek (self->afc_cli,
+ g_free (new_path);
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_seek (afc_cli,
fd, 0, SEEK_END),
G_VFS_JOB(job))))
{
+ afc_file_close (afc_cli, fd);
return;
}
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_tell (self->afc_cli,
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_tell (afc_cli,
fd, &off),
G_VFS_JOB(job))))
{
+ afc_file_close (afc_cli, fd);
return;
}
- g_vfs_job_open_for_write_set_handle (job, GUINT_TO_POINTER((gulong)fd));
+ fh = g_new0 (FileHandle, 1);
+ fh->fd = fd;
+ fh->afc_cli = afc_cli;
+
+ g_vfs_job_open_for_write_set_handle (job, fh);
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));
@@ -688,6 +1107,9 @@ g_vfs_backend_afc_replace (GVfsBackend *backend,
{
uint64_t fd = 0;
GVfsBackendAfc *self;
+ char *new_path, *app;
+ afc_client_t afc_cli;
+ FileHandle *fh;
self = G_VFS_BACKEND_AFC(backend);
g_return_if_fail(self->connected);
@@ -702,14 +1124,48 @@ g_vfs_backend_afc_replace (GVfsBackend *backend,
return;
}
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (self->afc_cli,
- filename, AFC_FOPEN_WR, &fd),
+ new_path = NULL;
+
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ AppInfo *info;
+
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, filename, &new_path);
+ if (app == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB(job));
+ return;
+ }
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_open (afc_cli,
+ new_path ? new_path : filename, AFC_FOPEN_WR, &fd),
G_VFS_JOB(job))))
{
+ g_free (new_path);
return;
}
- g_vfs_job_open_for_write_set_handle (job, GUINT_TO_POINTER((gulong)fd));
+ g_free (new_path);
+
+ fh = g_new0 (FileHandle, 1);
+ fh->fd = fd;
+ fh->afc_cli = afc_cli;
+
+ g_vfs_job_open_for_write_set_handle (job, fh);
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_succeeded (G_VFS_JOB(job));
@@ -723,15 +1179,16 @@ g_vfs_backend_afc_close_read (GVfsBackend *backend,
GVfsBackendHandle handle)
{
GVfsBackendAfc *self;
- uint64_t fd = 0;
+ FileHandle *fh;
- fd = GPOINTER_TO_UINT(handle);
- g_return_if_fail (fd != 0);
+ fh = (FileHandle *) handle;
self = G_VFS_BACKEND_AFC(backend);
if (self->connected)
- afc_file_close (self->afc_cli, fd);
+ afc_file_close (fh->afc_cli, fh->fd);
+
+ g_free (fh);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -742,15 +1199,16 @@ g_vfs_backend_afc_close_write (GVfsBackend *backend,
GVfsBackendHandle handle)
{
GVfsBackendAfc *self;
- uint64_t fd = 0;
+ FileHandle *fh;
- fd = GPOINTER_TO_UINT(handle);
- g_return_if_fail (fd != 0);
+ fh = (FileHandle *) handle;
self = G_VFS_BACKEND_AFC(backend);
if (self->connected)
- afc_file_close(self->afc_cli, fd);
+ afc_file_close(fh->afc_cli, fh->fd);
+
+ g_free (fh);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -764,17 +1222,16 @@ g_vfs_backend_afc_read (GVfsBackend *backend,
{
guint32 nread = 0;
GVfsBackendAfc *self;
- uint64_t fd = 0;
+ FileHandle *fh;
- fd = GPOINTER_TO_UINT(handle);
- g_return_if_fail (fd != 0);
+ fh = (FileHandle *) handle;
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_UNLIKELY(g_vfs_backend_afc_check (afc_file_read (fh->afc_cli,
+ fh->fd, buffer, req, &nread),
G_VFS_JOB(job))))
{
return;
@@ -793,17 +1250,16 @@ g_vfs_backend_afc_write (GVfsBackend *backend,
{
guint32 nwritten = 0;
GVfsBackendAfc *self;
- uint64_t fd = 0;
+ FileHandle *fh;
- fd = GPOINTER_TO_UINT(handle);
- g_return_if_fail (fd != 0);
+ fh = (FileHandle *) handle;
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_UNLIKELY(g_vfs_backend_afc_check(afc_file_write (fh->afc_cli,
+ fh->fd, buffer, sz, &nwritten),
G_VFS_JOB(job))))
{
return;
@@ -821,7 +1277,7 @@ g_vfs_backend_afc_seek (GVfsBackendAfc *self,
GSeekType type)
{
int afc_seek_type;
- uint64_t fd = 0;
+ FileHandle *fh;
switch (type)
{
@@ -840,10 +1296,10 @@ g_vfs_backend_afc_seek (GVfsBackendAfc *self,
return 1;
}
- fd = GPOINTER_TO_UINT(handle);
+ fh = (FileHandle *) handle;
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_seek (self->afc_cli,
- fd, offset, afc_seek_type),
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_file_seek (fh->afc_cli,
+ fh->fd, offset, afc_seek_type),
job)))
{
return 1;
@@ -1023,7 +1479,8 @@ g_vfs_backend_afc_set_info_from_afcinfo (GVfsBackendAfc *self,
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 ((flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0 &&
+ self->mode == ACCESS_MODE_AFC)
{
if (type == G_FILE_TYPE_SYMBOLIC_LINK)
{
@@ -1062,6 +1519,7 @@ g_vfs_backend_afc_set_info_from_afcinfo (GVfsBackendAfc *self,
/* Check for matching thumbnail in .MISC directory */
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_PREVIEW_ICON) &&
+ self->mode == ACCESS_MODE_AFC &&
path != NULL &&
g_str_has_prefix (path, "/DCIM/") &&
hidden == FALSE &&
@@ -1188,8 +1646,60 @@ g_vfs_backend_afc_set_info_from_afcinfo (GVfsBackendAfc *self,
}
}
+static void
+g_vfs_backend_afc_set_info_from_app (GVfsBackendAfc *self,
+ GFileInfo *info,
+ AppInfo *app_info)
+{
+ GIcon *icon;
+
+ /* content-type */
+ g_file_info_set_content_type (info, "inode/directory");
+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, "inode/directory");
+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+
+ /* icon */
+ if (app_info == NULL || app_info->icon_path == NULL)
+ {
+ icon = g_themed_icon_new ("folder");
+ g_file_info_set_icon (info, icon);
+ g_object_unref (icon);
+ }
+ else
+ {
+ GFile *file;
+
+ file = g_file_new_for_path (app_info->icon_path);
+ icon = g_file_icon_new (file);
+ g_object_unref (file);
+ g_file_info_set_icon (info, icon);
+ g_object_unref (icon);
+ }
+
+ /* hidden ? */
+ if (app_info && app_info->hidden)
+ g_file_info_set_is_hidden (info, TRUE);
+
+ /* name */
+ if (app_info != NULL)
+ {
+ g_file_info_set_name(info, app_info->id);
+ g_file_info_set_display_name (info, app_info->display_name);
+ }
+ else
+ {
+ g_file_info_set_name(info, "/");
+ g_file_info_set_display_name (info, g_vfs_backend_get_display_name (G_VFS_BACKEND(self)));
+ }
+
+ /* owner */
+ 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 ());
+}
+
static gboolean
file_get_info (GVfsBackendAfc *backend,
+ afc_client_t afc_cli,
const char *path,
GFileInfo *info)
{
@@ -1200,7 +1710,7 @@ file_get_info (GVfsBackendAfc *backend,
g_return_val_if_fail (backend->connected, result);
g_return_val_if_fail (info, result);
- if (G_LIKELY(afc_get_file_info (backend->afc_cli, path, &afcinfo) == AFC_E_SUCCESS))
+ if (G_LIKELY(afc_get_file_info (afc_cli, path, &afcinfo) == AFC_E_SUCCESS))
{
ptr = strrchr (path, '/');
if (ptr && ptr[1] != '\0')
@@ -1218,6 +1728,158 @@ file_get_info (GVfsBackendAfc *backend,
return result;
}
+static char *
+g_vfs_backend_load_icon (sbservices_client_t sbs,
+ const char *id)
+{
+ char *path;
+ char *filename;
+ char *data;
+ guint64 len;
+
+ filename = g_strdup_printf ("%s.png", id);
+ path = g_build_filename (g_get_user_cache_dir (),
+ "libimobiledevice",
+ "icons",
+ filename, NULL);
+ g_free (filename);
+
+ if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
+ return path;
+
+ data = NULL;
+ len = 0;
+ if (sbservices_get_icon_pngdata (sbs, id, &data, &len) != SBSERVICES_E_SUCCESS ||
+ data == NULL || len == 0)
+ {
+ if (data != NULL)
+ free (data);
+ g_free (path);
+ return NULL;
+ }
+
+ if (g_file_set_contents (path, data, len, NULL) == FALSE)
+ {
+ free (data);
+ g_free (path);
+ return NULL;
+ }
+ free (data);
+
+ return path;
+}
+
+
+/* apps_lock needs to be locked before calling this */
+static gboolean
+g_vfs_backend_load_apps (GVfsBackendAfc *self)
+{
+ plist_t client_opts;
+ guint num_apps, i;
+ plist_t apps = NULL;
+ instproxy_error_t err;
+
+ g_assert (self->mode == ACCESS_MODE_HOUSE_ARREST);
+
+ if (self->apps != NULL)
+ {
+ return TRUE;
+ }
+
+ client_opts = instproxy_client_options_new ();
+ instproxy_client_options_add (client_opts, "ApplicationType", "User", NULL);
+
+ err = instproxy_browse (self->inst, client_opts, &apps);
+ instproxy_client_options_free (client_opts);
+
+ if (err != INSTPROXY_E_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ self->apps = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) app_info_free);
+
+ num_apps = plist_array_get_size(apps);
+ for (i = 0; i < num_apps; i++)
+ {
+ plist_t app;
+ plist_t p_appid;
+ plist_t p_doctypes;
+ plist_t p_name;
+ plist_t p_sharing;
+ char *s_appid;
+ char *s_name;
+ guint8 b_sharing;
+ gboolean hidden;
+ AppInfo *info;
+
+ app = plist_array_get_item(apps, i);
+ p_appid = plist_dict_get_item (app, "CFBundleIdentifier");
+ p_name = plist_dict_get_item (app, "CFBundleDisplayName");
+ p_doctypes = plist_dict_get_item (app, "CFBundleDocumentTypes");
+ if (plist_array_get_size (p_doctypes) == 0)
+ p_doctypes = NULL;
+ p_sharing = plist_dict_get_item (app, "UIFileSharingEnabled");
+ b_sharing = FALSE;
+ hidden = FALSE;
+ if (p_sharing)
+ {
+ if (plist_get_node_type (p_sharing) == PLIST_BOOLEAN)
+ {
+ plist_get_bool_val (p_sharing, &b_sharing);
+ }
+ else if (plist_get_node_type (p_sharing) == PLIST_STRING)
+ {
+ char *p_sharing_val = NULL;
+
+ plist_get_string_val (p_sharing, &p_sharing_val);
+ if (p_sharing_val)
+ {
+ if ((g_ascii_strcasecmp (p_sharing_val, "YES") == 0) || (g_ascii_strcasecmp (p_sharing_val, "true") == 0))
+ b_sharing = TRUE;
+ free (p_sharing_val);
+ }
+ }
+ }
+
+ /* Doesn't support documents, or missing metadata? */
+ if (p_doctypes == NULL && !b_sharing)
+ hidden = TRUE;
+ if (p_appid == NULL || p_name == NULL)
+ {
+ continue;
+ }
+
+ s_appid = s_name = NULL;
+ plist_get_string_val (p_appid, &s_appid);
+ if (s_appid == NULL)
+ {
+ continue;
+ }
+ plist_get_string_val (p_name, &s_name);
+ if (s_name == NULL)
+ {
+ free (s_appid);
+ continue;
+ }
+
+ info = g_new0 (AppInfo, 1);
+ info->display_name = s_name;
+ info->id = s_appid;
+ info->hidden = hidden;
+
+ info->icon_path = g_vfs_backend_load_icon (self->sbs, info->id);
+
+ g_hash_table_insert (self->apps, g_strdup (s_appid), info);
+ }
+
+ plist_free (apps);
+
+ return TRUE;
+}
+
/* Callback for iterating over a directory. */
static void
g_vfs_backend_afc_enumerate (GVfsBackend *backend,
@@ -1232,17 +1894,81 @@ g_vfs_backend_afc_enumerate (GVfsBackend *backend,
gchar *file_path;
char **ptr, **list = NULL;
char **afcinfo = NULL;
+ char *new_path = NULL;
+ afc_client_t afc_cli;
+ gboolean hide_non_docs = FALSE;
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))))
+ if (self->mode == ACCESS_MODE_AFC)
{
- return;
+ afc_cli = self->afc_cli;
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_read_directory (self->afc_cli, path, &list),
+ G_VFS_JOB(job))))
+ {
+ return;
+ }
}
+ else
+ {
+ char *app;
+
+ g_mutex_lock (self->apps_lock);
+ if (g_vfs_backend_load_apps (self) == FALSE)
+ {
+ g_vfs_backend_afc_check (AFC_E_INTERNAL_ERROR, G_VFS_JOB (job));
+ g_mutex_unlock (self->apps_lock);
+ return;
+ }
+ g_mutex_unlock (self->apps_lock);
+
+ app = g_vfs_backend_parse_house_arrest_path (self, TRUE, path, &new_path);
- trailing_slash = g_str_has_suffix (path, "/");
+ if (app == NULL)
+ {
+ GList *apps, *l;
+
+ apps = g_hash_table_get_values (self->apps);
+ for (l = apps; l != NULL; l = l->next)
+ {
+ AppInfo *app_info = l->data;
+ info = g_file_info_new ();
+ g_vfs_backend_afc_set_info_from_app (self, info, app_info);
+ g_vfs_job_enumerate_add_info (job, info);
+ g_object_unref (G_OBJECT(info));
+ }
+ g_list_free (apps);
+ g_vfs_job_enumerate_done (job);
+ g_vfs_job_succeeded (G_VFS_JOB(job));
+ return;
+ }
+ else
+ {
+ AppInfo *app_info;
+
+ app_info = g_hash_table_lookup (self->apps, app);
+ if (app_info == NULL)
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+ g_free (app);
+ afc_cli = app_info->afc_cli;
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_read_directory (afc_cli, new_path, &list),
+ G_VFS_JOB(job))))
+ {
+ g_free (new_path);
+ return;
+ }
+ if (g_str_equal (new_path, "/"))
+ hide_non_docs = TRUE;
+ }
+ }
+
+ trailing_slash = g_str_has_suffix (new_path ? new_path : path, "/");
for (ptr = list; *ptr; ptr++)
{
@@ -1250,19 +1976,26 @@ g_vfs_backend_afc_enumerate (GVfsBackend *backend,
continue;
if (!trailing_slash)
- file_path = g_strdup_printf ("%s/%s", path, *ptr);
+ file_path = g_strdup_printf ("%s/%s", new_path ? new_path : path, *ptr);
else
- file_path = g_strdup_printf ("%s%s", path, *ptr);
+ file_path = g_strdup_printf ("%s%s", new_path ? new_path : 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))
+ if (G_LIKELY(afc_get_file_info(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, file_path, matcher, flags);
+
+ if (hide_non_docs &&
+ g_str_equal (file_path, "/Documents") == FALSE)
+ {
+ g_file_info_set_is_hidden (info, TRUE);
+ }
+
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (G_OBJECT(info));
g_strfreev (afcinfo);
@@ -1271,6 +2004,7 @@ g_vfs_backend_afc_enumerate (GVfsBackend *backend,
g_free (file_path);
}
+ g_free (new_path);
g_strfreev (list);
g_vfs_job_enumerate_done (job);
@@ -1288,27 +2022,90 @@ g_vfs_backend_afc_query_info (GVfsBackend *backend,
GVfsBackendAfc *self;
const char *basename, *ptr;
char **afcinfo = NULL;
+ char *new_path;
+ gboolean hide_non_docs = FALSE;
self = G_VFS_BACKEND_AFC(backend);
g_return_if_fail (self->connected);
+ new_path = NULL;
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_file_info (self->afc_cli, path, &afcinfo),
- G_VFS_JOB(job))))
+ if (self->mode == ACCESS_MODE_AFC)
{
- if (afcinfo)
- g_strfreev(afcinfo);
- return;
+ 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;
+ }
}
+ else
+ {
+ char *app;
- ptr = strrchr (path, '/');
+ g_mutex_lock (self->apps_lock);
+ if (g_vfs_backend_load_apps (self) == FALSE)
+ {
+ g_vfs_backend_afc_check (AFC_E_INTERNAL_ERROR, G_VFS_JOB (job));
+ g_mutex_unlock (self->apps_lock);
+ return;
+ }
+ g_mutex_unlock (self->apps_lock);
+
+ app = g_vfs_backend_parse_house_arrest_path (self, TRUE, path, &new_path);
+
+ if (app == NULL)
+ {
+ g_vfs_backend_afc_set_info_from_app (self, info, NULL);
+ g_vfs_job_succeeded (G_VFS_JOB(job));
+ return;
+ }
+ else
+ {
+ AppInfo *app_info;
+
+ app_info = g_hash_table_lookup (self->apps, app);
+ g_free (app);
+ if (app_info == NULL)
+ {
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+ if (g_str_equal (new_path, "/"))
+ {
+ g_free (new_path);
+ g_vfs_backend_afc_set_info_from_app (self, info, app_info);
+ g_vfs_job_succeeded (G_VFS_JOB(job));
+ return;
+ }
+ hide_non_docs = TRUE;
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_file_info (app_info->afc_cli, new_path, &afcinfo),
+ G_VFS_JOB(job))))
+ {
+ g_free (new_path);
+ return;
+ }
+ }
+ }
+
+ ptr = strrchr (new_path ? new_path : path, '/');
if (ptr && ptr[1] != '\0')
basename = ptr + 1;
else
- basename = path;
+ basename = new_path ? new_path : path;
- g_vfs_backend_afc_set_info_from_afcinfo (self, info, afcinfo, basename, path, matcher, flags);
+ g_vfs_backend_afc_set_info_from_afcinfo (self, info, afcinfo, basename, new_path ? new_path : path, matcher, flags);
if (afcinfo)
g_strfreev (afcinfo);
+ if (hide_non_docs &&
+ (g_str_equal (new_path, "Documents") ||
+ g_str_has_prefix (new_path, "Documents/")))
+ {
+ g_file_info_set_is_hidden (info, TRUE);
+ }
+
+ g_free (new_path);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -1331,51 +2128,85 @@ g_vfs_backend_afc_query_fs_info (GVfsBackend *backend,
char **kvps, **ptr;
uint64_t totalspace = 0, freespace = 0;
int blocksize = 0;
+ afc_client_t afc_cli;
self = G_VFS_BACKEND_AFC(backend);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afc");
- if (self->connected)
+ if (!self->connected)
+ {
+ g_vfs_job_succeeded (G_VFS_JOB(job));
+ return;
+ }
+
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
{
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_device_info (self->afc_cli, &kvps), G_VFS_JOB(job))))
- return;
+ char *app, *new_path;
+ AppInfo *info;
- for (ptr = kvps; *ptr; ptr++)
+ new_path = NULL;
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, path, &new_path);
+ if (app == NULL)
{
- 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_vfs_backend_afc_check (AFC_E_OP_NOT_SUPPORTED, G_VFS_JOB(job));
+ return;
}
+ g_free (new_path);
- 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);
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_free (app);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_get_device_info (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));
}
@@ -1386,18 +2217,55 @@ g_vfs_backend_afc_set_display_name (GVfsBackend *backend,
const char *display_name)
{
GVfsBackendAfc *self;
+ char *new_path;
+ afc_client_t afc_cli;
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),
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ char *app;
+ AppInfo *info;
+
+ new_path = NULL;
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, filename, &new_path);
+ if (app == NULL || g_str_equal (new_path, "/"))
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB (job));
+ return;
+ }
+
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB (job));
+ return;
+ }
+
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_rename_path (afc_cli,
+ new_path ? new_path : filename, display_name),
G_VFS_JOB(job))))
{
+ g_free (new_path);
return;
}
g_vfs_job_set_display_name_set_new_path (job, display_name);
+ g_free (new_path);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -1414,6 +2282,8 @@ g_vfs_backend_afc_set_attribute (GVfsBackend *backend,
GVfsBackendAfc *self;
uint64_t mtime = 0;
afc_error_t err;
+ char *new_path;
+ afc_client_t afc_cli;
self = G_VFS_BACKEND_AFC(backend);
g_return_if_fail(self->connected);
@@ -1426,9 +2296,44 @@ g_vfs_backend_afc_set_attribute (GVfsBackend *backend,
return;
}
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ char *app;
+ AppInfo *info;
+
+ new_path = NULL;
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, filename, &new_path);
+ if (app == NULL || g_str_equal (new_path, "/"))
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB (job));
+ return;
+ }
+
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB (job));
+ return;
+ }
+
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
mtime = *(guint64*)(value_p) * (guint64)1000000000;
- err = afc_set_file_time (self->afc_cli, filename, mtime);
+ err = afc_set_file_time (afc_cli, new_path ? new_path : filename, mtime);
+ g_free (new_path);
+
if (err == AFC_E_UNKNOWN_PACKET_TYPE)
{
/* ignore error for pre-3.1 devices as the do not support setting file modification times */
@@ -1448,17 +2353,54 @@ g_vfs_backend_afc_make_directory (GVfsBackend *backend,
const char *path)
{
GVfsBackendAfc *self;
+ char *new_path;
+ afc_client_t afc_cli;
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),
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ char *app;
+ AppInfo *info;
+
+ new_path = NULL;
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, path, &new_path);
+ if (app == NULL)
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB (job));
+ return;
+ }
+
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB (job));
+ return;
+ }
+
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_make_directory (afc_cli,
+ new_path ? new_path : path),
G_VFS_JOB(job))))
{
+ g_free (new_path);
return;
}
+ g_free (new_path);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -1473,6 +2415,13 @@ g_vfs_backend_afc_make_symlink (GVfsBackend *backend,
self = G_VFS_BACKEND_AFC(backend);
g_return_if_fail (self->connected);
+ /* Not bothering with symlink creation support in house arrest */
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ g_vfs_backend_afc_check (AFC_E_OP_NOT_SUPPORTED, G_VFS_JOB (job));
+ return;
+ }
+
if (G_UNLIKELY(g_vfs_backend_afc_check (afc_make_link (self->afc_cli,
AFC_SYMLINK, symlink_value, filename),
G_VFS_JOB(job))))
@@ -1493,6 +2442,9 @@ g_vfs_backend_afc_move (GVfsBackend *backend,
gpointer progress_callback_data)
{
GVfsBackendAfc *self;
+ char *new_src;
+ char *new_dst;
+ afc_client_t afc_cli;
self = G_VFS_BACKEND_AFC(backend);
g_return_if_fail(self->connected);
@@ -1507,13 +2459,66 @@ g_vfs_backend_afc_move (GVfsBackend *backend,
return;
}
- if (G_UNLIKELY(g_vfs_backend_afc_check (afc_rename_path (self->afc_cli,
- source, destination),
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ AppInfo *info;
+ char *app_src, *app_dst;
+
+ app_src = g_vfs_backend_parse_house_arrest_path (self, FALSE, source, &new_src);
+ if (app_src == NULL || g_str_equal (new_src, "/"))
+ {
+ g_free (app_src);
+ g_free (new_src);
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB(job));
+ return;
+ }
+ app_dst = g_vfs_backend_parse_house_arrest_path (self, FALSE, destination, &new_dst);
+ if (app_dst == NULL || g_str_equal (new_dst, "/"))
+ {
+ g_free (app_src);
+ g_free (new_src);
+ g_free (app_dst);
+ g_free (new_dst);
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB(job));
+ return;
+ }
+ if (!g_str_equal (app_dst, app_src))
+ {
+ g_free (app_src);
+ g_free (new_src);
+ g_free (app_dst);
+ g_free (new_dst);
+ g_vfs_backend_afc_check (AFC_E_OP_NOT_SUPPORTED, G_VFS_JOB(job));
+ return;
+ }
+ g_free (app_dst);
+ info = g_hash_table_lookup (self->apps, app_src);
+ if (info == NULL)
+ {
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+ afc_cli = info->afc_cli;
+ g_free (app_src);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_src = new_dst = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_rename_path (afc_cli,
+ new_src ? new_src : source,
+ new_dst ? new_dst : destination),
G_VFS_JOB(job))))
{
+ g_free (new_src);
+ g_free (new_dst);
return;
}
+ g_free (new_src);
+ g_free (new_dst);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -1523,17 +2528,54 @@ g_vfs_backend_afc_delete (GVfsBackend *backend,
const char *filename)
{
GVfsBackendAfc *self;
+ char *new_path;
+ afc_client_t afc_cli;
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),
+ if (self->mode == ACCESS_MODE_HOUSE_ARREST)
+ {
+ char *app;
+ AppInfo *info;
+
+ new_path = NULL;
+ app = g_vfs_backend_parse_house_arrest_path (self, FALSE, filename, &new_path);
+ if (app == NULL || g_str_equal (new_path, "/"))
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_PERM_DENIED, G_VFS_JOB(job));
+ return;
+ }
+
+ info = g_hash_table_lookup (self->apps, app);
+ if (info == NULL)
+ {
+ g_free (app);
+ g_free (new_path);
+ g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job));
+ return;
+ }
+
+ afc_cli = info->afc_cli;
+ g_free (app);
+ }
+ else
+ {
+ afc_cli = self->afc_cli;
+ new_path = NULL;
+ }
+
+ if (G_UNLIKELY(g_vfs_backend_afc_check (afc_remove_path (afc_cli,
+ new_path ? new_path : filename),
G_VFS_JOB(job))))
{
+ g_free (new_path);
return;
}
+ g_free (new_path);
g_vfs_job_succeeded (G_VFS_JOB(job));
}
@@ -1558,6 +2600,8 @@ g_vfs_backend_afc_init (GVfsBackendAfc *self)
/* enable full debugging */
idevice_set_debug_level (1);
}
+
+ self->apps_lock = g_mutex_new ();
}
static void