summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/Makefile.am1
-rw-r--r--include/libimobiledevice/mobile_image_mounter.h56
-rw-r--r--src/Makefile.am1
-rw-r--r--src/mobile_image_mounter.c271
-rw-r--r--src/mobile_image_mounter.h34
-rw-r--r--tools/Makefile.am6
-rw-r--r--tools/ideviceimagemounter.c525
7 files changed, 893 insertions, 1 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index 2e20332..0cb3c50 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -5,5 +5,6 @@ nobase_include_HEADERS = libimobiledevice/libimobiledevice.h \
libimobiledevice/notification_proxy.h \
libimobiledevice/installation_proxy.h \
libimobiledevice/sbservices.h \
+ libimobiledevice/mobile_image_mounter.h \
libimobiledevice/mobilesync.h \
libimobiledevice/mobilebackup.h
diff --git a/include/libimobiledevice/mobile_image_mounter.h b/include/libimobiledevice/mobile_image_mounter.h
new file mode 100644
index 0000000..63dcb14
--- /dev/null
+++ b/include/libimobiledevice/mobile_image_mounter.h
@@ -0,0 +1,56 @@
+/**
+ * @file libimobiledevice/mobile_image_mounter.h
+ * @brief Implementation of the mobile image mounter service.
+ * \internal
+ *
+ * Copyright (c) 2010 Nikias Bassen All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MOBILE_IMAGE_MOUNTER_H
+#define MOBILE_IMAGE_MOUNTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libimobiledevice/libimobiledevice.h>
+
+/* Error Codes */
+#define MOBILE_IMAGE_MOUNTER_E_SUCCESS 0
+#define MOBILE_IMAGE_MOUNTER_E_INVALID_ARG -1
+#define MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR -2
+#define MOBILE_IMAGE_MOUNTER_E_CONN_FAILED -3
+
+#define MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR -256
+
+typedef int16_t mobile_image_mounter_error_t;
+
+struct mobile_image_mounter_client_int;
+typedef struct mobile_image_mounter_client_int *mobile_image_mounter_client_t;
+
+/* Interface */
+mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, uint16_t port, mobile_image_mounter_client_t *client);
+mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client);
+mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result);
+mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *image_signature, uint16_t signature_length, const char *image_type, plist_t *result);
+mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index b0093fa..e847d7c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,5 +16,6 @@ libimobiledevice_la_SOURCES = idevice.c idevice.h \
notification_proxy.c notification_proxy.h\
installation_proxy.c installation_proxy.h\
sbservices.c sbservices.h\
+ mobile_image_mounter.c mobile_image_mounter.h\
mobilesync.c mobilesync.h\
mobilebackup.c mobilebackup.h
diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c
new file mode 100644
index 0000000..7fe0a6d
--- /dev/null
+++ b/src/mobile_image_mounter.c
@@ -0,0 +1,271 @@
+/*
+ * mobile_image_mounter.c
+ * MobileImageMounter implementation.
+ *
+ * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <plist/plist.h>
+
+#include "mobile_image_mounter.h"
+#include "property_list_service.h"
+#include "debug.h"
+
+/** Locks an MobileImageMounter client, done for thread safety stuff.
+ *
+ * @param client The MIM to lock
+ */
+static void mobile_image_mounter_lock(mobile_image_mounter_client_t client)
+{
+ g_mutex_lock(client->mutex);
+}
+
+/** Unlocks an MIM client, done for thread safety stuff.
+ *
+ * @param client The MIM to unlock
+ */
+static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client)
+{
+ g_mutex_unlock(client->mutex);
+}
+
+/**
+ * Convert a property_list_service_error_t value to a
+ * mobile_image_mounter_error_t value.
+ * Used internally to get correct error codes.
+ *
+ * @param err A property_list_service_error_t error code
+ *
+ * @return A matching mobile_image_mounter_error_t error code,
+ * MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR otherwise.
+ */
+static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_service_error_t err)
+{
+ switch (err) {
+ case PROPERTY_LIST_SERVICE_E_SUCCESS:
+ return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
+ case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
+ case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
+ return MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR;
+ case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
+ return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED;
+ default:
+ break;
+ }
+ return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
+}
+
+/**
+ * Makes a connection to the MobileImageMounter service on the phone.
+ *
+ * @param device The device to connect to.
+ * @param port Destination port (usually given by lockdownd_start_service).
+ * @param client Pointer that will be set to a newly allocated
+ * mobile_image_mounter_client_t upon successful return.
+ *
+ * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
+ * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG when device is NULL,
+ * or MOBILE_IMAGE_MOUNTER_E_CONN_FAILED when the connection to the
+ * device could not be established.
+ */
+mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, uint16_t port, mobile_image_mounter_client_t *client)
+{
+ /* makes sure thread environment is available */
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+
+ if (!device)
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
+
+ property_list_service_client_t plistclient = NULL;
+ if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED;
+ }
+
+ mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_int));
+ client_loc->parent = plistclient;
+
+ client_loc->mutex = g_mutex_new();
+
+ *client = client_loc;
+ return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
+}
+
+/**
+ * Disconnects an MobileImageMounter client from the device.
+ *
+ * @param client The client to disconnect.
+ *
+ * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
+ * or MOBILE_IMAGE_MOUNTER_E_INVALID_ARG when client is NULL.
+ */
+mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client)
+{
+ if (!client)
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
+
+ property_list_service_client_free(client->parent);
+ client->parent = NULL;
+ if (client->mutex) {
+ g_mutex_free(client->mutex);
+ }
+ free(client);
+
+ return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
+}
+
+/**
+ * Tells if the image of ImageType is already mounted.
+ *
+ * @param client The client use
+ * @param image_type The type of the image to look up
+ * @param result Pointer to a plist that will receive the result of the
+ * operation.
+ *
+ * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
+ * operation has failed. Check the resulting plist for further information.
+ *
+ * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, or an error code on error
+ */
+mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result)
+{
+ if (!client || !image_type || !result) {
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
+ }
+ mobile_image_mounter_lock(client);
+
+ plist_t dict = plist_new_dict();
+ plist_dict_insert_item(dict,"Command", plist_new_string("LookupImage"));
+ plist_dict_insert_item(dict,"ImageType", plist_new_string(image_type));
+
+ mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
+ plist_free(dict);
+
+ if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ debug_info("%s: Error sending XML plist to device!", __func__);
+ goto leave_unlock;
+ }
+
+ res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
+ if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ debug_info("%s: Error receiving response from device!", __func__);
+ }
+
+leave_unlock:
+ mobile_image_mounter_unlock(client);
+ return res;
+}
+
+/**
+ * Mounts an image on the device.
+ *
+ * @param client The connected MobileImageMounter client.
+ * @param image_path The absolute path of the image to mount. The image must
+ * be present before calling this function.
+ * @param image_signature Pointer to a buffer holding the images' signature
+ * @param signature_length Length of the signature image_signature points to
+ * @param image_type Type of image to mount
+ * @param result Pointer to a plist that will receive the result of the
+ * operation.
+ *
+ * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
+ * operation has failed. Check the resulting plist for further information.
+ *
+ * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
+ * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG when on ore more parameters are
+ * invalid, or another error code otherwise.
+ */
+mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *image_signature, uint16_t signature_length, const char *image_type, plist_t *result)
+{
+ if (!client || !image_path || !image_signature || (signature_length == 0) || !image_type || !result) {
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
+ }
+ mobile_image_mounter_lock(client);
+
+ plist_t dict = plist_new_dict();
+ plist_dict_insert_item(dict, "Command", plist_new_string("MountImage"));
+ plist_dict_insert_item(dict, "ImagePath", plist_new_string(image_path));
+ plist_dict_insert_item(dict, "ImageSignature", plist_new_data(image_signature, signature_length));
+ plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type));
+
+ mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
+ plist_free(dict);
+
+ if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ debug_info("%s: Error sending XML plist to device!", __func__);
+ goto leave_unlock;
+ }
+
+ res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
+ if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ debug_info("%s: Error receiving response from device!", __func__);
+ }
+
+leave_unlock:
+ mobile_image_mounter_unlock(client);
+ return res;
+}
+
+/**
+ * Hangs up the connection to the MobileImageMounter service.
+ * This functions has to be called before freeing up a mobile_image_mounter
+ * instance. If not, errors appear in the device's syslog.
+ *
+ * @param client The client to hang up
+ *
+ * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
+ * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid,
+ * or another error code otherwise.
+ */
+mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)
+{
+ if (!client) {
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
+ }
+ mobile_image_mounter_lock(client);
+
+ plist_t dict = plist_new_dict();
+ plist_dict_insert_item(dict, "Command", plist_new_string("Hangup"));
+
+ mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
+ plist_free(dict);
+
+ if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ debug_info("%s: Error sending XML plist to device!", __func__);
+ goto leave_unlock;
+ }
+
+ dict = NULL;
+ res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &dict));
+ if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ debug_info("%s: Error receiving response from device!", __func__);
+ }
+ if (dict) {
+ debug_plist(dict);
+ plist_free(dict);
+ }
+
+leave_unlock:
+ mobile_image_mounter_unlock(client);
+ return res;
+}
diff --git a/src/mobile_image_mounter.h b/src/mobile_image_mounter.h
new file mode 100644
index 0000000..85dd8bc
--- /dev/null
+++ b/src/mobile_image_mounter.h
@@ -0,0 +1,34 @@
+/*
+ * mobile_image_mounter.h
+ * Mobile Image Mounter header file.
+ *
+ * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef IMOBILE_IMAGE_MOUNTER_H
+#define IMOBILE_IMAGE_MOUNTER_H
+
+#include <glib.h>
+
+#include "libimobiledevice/mobile_image_mounter.h"
+#include "property_list_service.h"
+
+struct mobile_image_mounter_client_int {
+ property_list_service_client_t parent;
+ GMutex *mutex;
+};
+
+#endif
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 9b6a6e8..b4fa69e 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -3,7 +3,7 @@ INCLUDES = -I$(top_srcdir)/include
AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS)
AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS)
-bin_PROGRAMS = idevice_id ideviceinfo idevicesyslog idevicebackup
+bin_PROGRAMS = idevice_id ideviceinfo idevicesyslog idevicebackup ideviceimagemounter
ideviceinfo_SOURCES = ideviceinfo.c
ideviceinfo_CFLAGS = $(AM_CFLAGS)
@@ -25,3 +25,7 @@ idevicebackup_CFLAGS = $(AM_CFLAGS)
idevicebackup_LDFLAGS = $(AM_LDFLAGS)
idevicebackup_LDADD = ../src/libimobiledevice.la
+ideviceimagemounter_SOURCES = ideviceimagemounter.c
+ideviceimagemounter_CFLAGS = $(AM_CFLAGS)
+ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS)
+ideviceimagemounter_LDADD = ../src/libimobiledevice.la
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
new file mode 100644
index 0000000..03fe112
--- /dev/null
+++ b/tools/ideviceimagemounter.c
@@ -0,0 +1,525 @@
+/**
+ * ideviceimagemounter -- Mount developer/debug disk images on the iPhone/iPod
+ *
+ * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more profile.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include <stdlib.h>
+#define _GNU_SOURCE 1
+#define __USE_GNU 1
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <glib.h>
+
+#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/lockdown.h>
+#include <libimobiledevice/afc.h>
+#include <libimobiledevice/notification_proxy.h>
+#include <libimobiledevice/mobile_image_mounter.h>
+
+static int indent_level = 0;
+
+static int list_mode = 0;
+static int xml_mode = 0;
+static char *uuid = NULL;
+static char *imagetype = NULL;
+
+static const char PKG_PATH[] = "PublicStaging";
+static const char PATH_PREFIX[] = "/private/var/mobile/Media";
+
+static void print_usage(int argc, char **argv)
+{
+ char *name = NULL;
+
+ name = strrchr(argv[0], '/');
+ printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0]));
+ printf("Mounts the specified disk image on the device.\n\n");
+ printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
+ printf(" -l, --list\t\tList mount information\n");
+ printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n");
+ printf(" -x, --xml\t\tUse XML output\n");
+ printf(" -d, --debug\t\tenable communication debugging\n");
+ printf(" -h, --help\t\tprints usage information\n");
+ printf("\n");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ static struct option longopts[] = {
+ {"help", 0, NULL, 'h'},
+ {"uuid", 0, NULL, 'u'},
+ {"list", 0, NULL, 'l'},
+ {"imagetype", 0, NULL, 't'},
+ {"xml", 0, NULL, 'x'},
+ {"debug", 0, NULL, 'd'},
+ {NULL, 0, NULL, 0}
+ };
+ int c;
+
+ while (1) {
+ c = getopt_long(argc, argv, "hu:lt:xd", longopts,
+ (int *) 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_usage(argc, argv);
+ exit(0);
+ case 'u':
+ if (strlen(optarg) != 40) {
+ printf("%s: invalid UUID specified (length != 40)\n",
+ argv[0]);
+ print_usage(argc, argv);
+ exit(2);
+ }
+ uuid = strdup(optarg);
+ break;
+ case 'l':
+ list_mode = 1;
+ break;
+ case 't':
+ imagetype = strdup(optarg);
+ break;
+ case 'x':
+ xml_mode = 1;
+ break;
+ case 'd':
+ idevice_set_debug_level(1);
+ break;
+ default:
+ print_usage(argc, argv);
+ exit(2);
+ }
+ }
+}
+
+static void plist_node_to_string(plist_t node);
+
+static void plist_array_to_string(plist_t node)
+{
+ /* iterate over items */
+ int i, count;
+ plist_t subnode = NULL;
+
+ count = plist_array_get_size(node);
+
+ for (i = 0; i < count; i++) {
+ subnode = plist_array_get_item(node, i);
+ printf("%*s", indent_level, "");
+ printf("%d: ", i);
+ plist_node_to_string(subnode);
+ }
+}
+
+static void plist_dict_to_string(plist_t node)
+{
+ /* iterate over key/value pairs */
+ plist_dict_iter it = NULL;
+
+ char* key = NULL;
+ plist_t subnode = NULL;
+ plist_dict_new_iter(node, &it);
+ plist_dict_next_item(node, it, &key, &subnode);
+ while (subnode)
+ {
+ printf("%*s", indent_level, "");
+ printf("%s", key);
+ if (plist_get_node_type(subnode) == PLIST_ARRAY)
+ printf("[%d]: ", plist_array_get_size(subnode));
+ else
+ printf(": ");
+ free(key);
+ key = NULL;
+ plist_node_to_string(subnode);
+ plist_dict_next_item(node, it, &key, &subnode);
+ }
+ free(it);
+}
+
+static void plist_node_to_string(plist_t node)
+{
+ char *s = NULL;
+ char *data = NULL;
+ double d;
+ uint8_t b;
+ uint64_t u = 0;
+ GTimeVal tv = { 0, 0 };
+
+ plist_type t;
+
+ if (!node)
+ return;
+
+ t = plist_get_node_type(node);
+
+ switch (t) {
+ case PLIST_BOOLEAN:
+ plist_get_bool_val(node, &b);
+ printf("%s\n", (b ? "true" : "false"));
+ break;
+
+ case PLIST_UINT:
+ plist_get_uint_val(node, &u);
+ printf("%llu\n", (long long)u);
+ break;
+
+ case PLIST_REAL:
+ plist_get_real_val(node, &d);
+ printf("%f\n", d);
+ break;
+
+ case PLIST_STRING:
+ plist_get_string_val(node, &s);
+ printf("%s\n", s);
+ free(s);
+ break;
+
+ case PLIST_KEY:
+ plist_get_key_val(node, &s);
+ printf("%s: ", s);
+ free(s);
+ break;
+
+ case PLIST_DATA:
+ plist_get_data_val(node, &data, &u);
+ uint64_t i;
+ for (i = 0; i < u; i++) {
+ printf("%02x", (unsigned char)data[i]);
+ }
+ free(data);
+ printf("\n");
+ g_free(s);
+ break;
+
+ case PLIST_DATE:
+ plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec);
+ s = g_time_val_to_iso8601(&tv);
+ printf("%s\n", s);
+ free(s);
+ break;
+
+ case PLIST_ARRAY:
+ printf("\n");
+ indent_level++;
+ plist_array_to_string(node);
+ indent_level--;
+ break;
+
+ case PLIST_DICT:
+ printf("\n");
+ indent_level++;
+ plist_dict_to_string(node);
+ indent_level--;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void print_xml(plist_t node)
+{
+ char *xml = NULL;
+ uint32_t len = 0;
+ plist_to_xml(node, &xml, &len);
+ if (xml)
+ puts(xml);
+}
+
+int main(int argc, char **argv)
+{
+ idevice_t device = NULL;
+ lockdownd_client_t lckd = NULL;
+ mobile_image_mounter_client_t mim = NULL;
+ afc_client_t afc = NULL;
+ uint16_t port = 0;
+ int res = -1;
+ char *image_path = NULL;
+ char *image_sig_path = NULL;
+
+ parse_opts(argc, argv);
+
+ argc -= optind;
+ argv += optind;
+
+ if (!list_mode) {
+ if (argc < 1) {
+ printf("ERROR: No IMAGE_FILE has been given!\n");
+ return -1;
+ }
+ image_path = strdup(argv[0]);
+ if (argc >= 2) {
+ image_sig_path = strdup(argv[1]);
+ } else {
+ if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
+ printf("Out of memory?!\n");
+ return -1;
+ }
+ }
+ }
+
+ if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) {
+ printf("No device found, is it plugged in?\n");
+ return -1;
+ }
+
+ if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) {
+ printf("ERROR: could not connect to lockdown. Exiting.\n");
+ goto leave;
+ }
+
+ lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port);
+
+ if (port == 0) {
+ printf("ERROR: Could not start mobile_image_mounter service!\n");
+ goto leave;
+ }
+
+ if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ printf("ERROR: Could not connect to mobile_image_mounter!\n");
+ goto leave;
+ }
+
+ if (!list_mode) {
+ struct stat fst;
+ port = 0;
+ if ((lockdownd_start_service(lckd, "com.apple.afc", &port) !=
+ LOCKDOWN_E_SUCCESS) || !port) {
+ fprintf(stderr, "Could not start com.apple.afc!\n");
+ goto leave;
+ }
+ if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) {
+ fprintf(stderr, "Could not connect to AFC!\n");
+ goto leave;
+ }
+ if (stat(image_path, &fst) != 0) {
+ fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
+ goto leave;
+ }
+ if (stat(image_sig_path, &fst) != 0) {
+ fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
+ goto leave;
+ }
+ }
+
+ lockdownd_client_free(lckd);
+ lckd = NULL;
+
+ mobile_image_mounter_error_t err;
+ plist_t result = NULL;
+
+ if (list_mode) {
+ /* list mounts mode */
+ if (!imagetype) {
+ imagetype = strdup("Developer");
+ }
+ err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
+ free(imagetype);
+ if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ res = 0;
+ if (xml_mode) {
+ print_xml(result);
+ } else {
+ plist_dict_to_string(result);
+ }
+ } else {
+ printf("Error: lookup_image returned %d\n", err);
+ }
+ } else {
+ char sig[8192];
+ size_t sig_length = 0;
+ FILE *f = fopen(image_sig_path, "r");
+ if (!f) {
+ fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
+ goto leave;
+ }
+ sig_length = fread(sig, 1, sizeof(sig), f);
+ fclose(f);
+ if (sig_length == 0) {
+ fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
+ goto leave;
+ }
+
+ f = fopen(image_path, "r");
+ if (!f) {
+ fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
+ goto leave;
+ }
+
+ char *targetname = NULL;
+ if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) {
+ fprintf(stderr, "Out of memory!?\n");
+ goto leave;
+ }
+ char *mountname = NULL;
+ if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
+ fprintf(stderr, "Out of memory!?\n");
+ goto leave;
+ }
+
+ printf("Copying '%s' --> '%s'\n", image_path, targetname);
+
+ char **strs = NULL;
+ if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
+ if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
+ fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
+ }
+ }
+ if (strs) {
+ int i = 0;
+ while (strs[i]) {
+ free(strs[i]);
+ i++;
+ }
+ free(strs);
+ }
+
+ uint64_t af = 0;
+ if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
+ AFC_E_SUCCESS) || !af) {
+ fclose(f);
+ fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
+ goto leave;
+ }
+
+ char buf[8192];
+ size_t amount = 0;
+ do {
+ amount = fread(buf, 1, sizeof(buf), f);
+ if (amount > 0) {
+ uint32_t written, total = 0;
+ while (total < amount) {
+ written = 0;
+ if (afc_file_write(afc, af, buf, amount, &written) !=
+ AFC_E_SUCCESS) {
+ fprintf(stderr, "AFC Write error!\n");
+ break;
+ }
+ total += written;
+ }
+ if (total != amount) {
+ fprintf(stderr, "Error: wrote only %d of %d\n", total,
+ amount);
+ afc_file_close(afc, af);
+ fclose(f);
+ goto leave;
+ }
+ }
+ }
+ while (amount > 0);
+
+ afc_file_close(afc, af);
+ fclose(f);
+
+ printf("done.\n");
+
+ printf("Mounting...\n");
+ if (!imagetype) {
+ imagetype = strdup("Developer");
+ }
+ err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
+ free(imagetype);
+ if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
+ if (result) {
+ plist_t node = plist_dict_get_item(result, "Status");
+ if (node) {
+ char *status = NULL;
+ plist_get_string_val(node, &status);
+ if (status) {
+ if (!strcmp(status, "Complete")) {
+ printf("Done.\n");
+ res = 0;
+ } else {
+ printf("unexpected status value:\n");
+ if (xml_mode) {
+ print_xml(result);
+ } else {
+ plist_dict_to_string(result);
+ }
+ }
+ free(status);
+ } else {
+ printf("unexpected result:\n");
+ if (xml_mode) {
+ print_xml(result);
+ } else {
+ plist_dict_to_string(result);
+ }
+ }
+ }
+ node = plist_dict_get_item(result, "Error");
+ if (node) {
+ char *error = NULL;
+ plist_get_string_val(node, &error);
+ if (error) {
+ printf("Error: %s\n", error);
+ free(error);
+ } else {
+ printf("unexpected result:\n");
+ if (xml_mode) {
+ print_xml(result);
+ } else {
+ plist_dict_to_string(result);
+ }
+ }
+
+ } else {
+ if (xml_mode) {
+ print_xml(result);
+ } else {
+ plist_dict_to_string(result);
+ }
+ }
+ }
+ } else {
+ printf("Error: mount_image returned %d\n", err);
+
+ }
+ }
+
+ if (result) {
+ plist_free(result);
+ }
+
+ /* perform hangup command */
+ mobile_image_mounter_hangup(mim);
+ /* free client */
+ mobile_image_mounter_free(mim);
+
+leave:
+ if (afc) {
+ afc_client_free(afc);
+ }
+ if (lckd) {
+ lockdownd_client_free(lckd);
+ }
+ idevice_free(device);
+
+ if (image_path)
+ free(image_path);
+ if (image_sig_path)
+ free(image_sig_path);
+
+ return res;
+}