diff options
| -rw-r--r-- | include/Makefile.am | 1 | ||||
| -rw-r--r-- | include/libimobiledevice/mobile_image_mounter.h | 56 | ||||
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/mobile_image_mounter.c | 271 | ||||
| -rw-r--r-- | src/mobile_image_mounter.h | 34 | ||||
| -rw-r--r-- | tools/Makefile.am | 6 | ||||
| -rw-r--r-- | tools/ideviceimagemounter.c | 525 | 
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; +} | 
