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 \
5 libimobiledevice/notification_proxy.h \ 5 libimobiledevice/notification_proxy.h \
6 libimobiledevice/installation_proxy.h \ 6 libimobiledevice/installation_proxy.h \
7 libimobiledevice/sbservices.h \ 7 libimobiledevice/sbservices.h \
8 libimobiledevice/mobile_image_mounter.h \
8 libimobiledevice/mobilesync.h \ 9 libimobiledevice/mobilesync.h \
9 libimobiledevice/mobilebackup.h 10 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 @@
1/**
2 * @file libimobiledevice/mobile_image_mounter.h
3 * @brief Implementation of the mobile image mounter service.
4 * \internal
5 *
6 * Copyright (c) 2010 Nikias Bassen All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifndef MOBILE_IMAGE_MOUNTER_H
24#define MOBILE_IMAGE_MOUNTER_H
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#include <libimobiledevice/libimobiledevice.h>
31
32/* Error Codes */
33#define MOBILE_IMAGE_MOUNTER_E_SUCCESS 0
34#define MOBILE_IMAGE_MOUNTER_E_INVALID_ARG -1
35#define MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR -2
36#define MOBILE_IMAGE_MOUNTER_E_CONN_FAILED -3
37
38#define MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR -256
39
40typedef int16_t mobile_image_mounter_error_t;
41
42struct mobile_image_mounter_client_int;
43typedef struct mobile_image_mounter_client_int *mobile_image_mounter_client_t;
44
45/* Interface */
46mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, uint16_t port, mobile_image_mounter_client_t *client);
47mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client);
48mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result);
49mobile_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);
50mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client);
51
52#ifdef __cplusplus
53}
54#endif
55
56#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 \
16 notification_proxy.c notification_proxy.h\ 16 notification_proxy.c notification_proxy.h\
17 installation_proxy.c installation_proxy.h\ 17 installation_proxy.c installation_proxy.h\
18 sbservices.c sbservices.h\ 18 sbservices.c sbservices.h\
19 mobile_image_mounter.c mobile_image_mounter.h\
19 mobilesync.c mobilesync.h\ 20 mobilesync.c mobilesync.h\
20 mobilebackup.c mobilebackup.h 21 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 @@
1/*
2 * mobile_image_mounter.c
3 * MobileImageMounter implementation.
4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <string.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <arpa/inet.h>
27#include <plist/plist.h>
28
29#include "mobile_image_mounter.h"
30#include "property_list_service.h"
31#include "debug.h"
32
33/** Locks an MobileImageMounter client, done for thread safety stuff.
34 *
35 * @param client The MIM to lock
36 */
37static void mobile_image_mounter_lock(mobile_image_mounter_client_t client)
38{
39 g_mutex_lock(client->mutex);
40}
41
42/** Unlocks an MIM client, done for thread safety stuff.
43 *
44 * @param client The MIM to unlock
45 */
46static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client)
47{
48 g_mutex_unlock(client->mutex);
49}
50
51/**
52 * Convert a property_list_service_error_t value to a
53 * mobile_image_mounter_error_t value.
54 * Used internally to get correct error codes.
55 *
56 * @param err A property_list_service_error_t error code
57 *
58 * @return A matching mobile_image_mounter_error_t error code,
59 * MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR otherwise.
60 */
61static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_service_error_t err)
62{
63 switch (err) {
64 case PROPERTY_LIST_SERVICE_E_SUCCESS:
65 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
66 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
67 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
68 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
69 return MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR;
70 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
71 return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED;
72 default:
73 break;
74 }
75 return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
76}
77
78/**
79 * Makes a connection to the MobileImageMounter service on the phone.
80 *
81 * @param device The device to connect to.
82 * @param port Destination port (usually given by lockdownd_start_service).
83 * @param client Pointer that will be set to a newly allocated
84 * mobile_image_mounter_client_t upon successful return.
85 *
86 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
87 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG when device is NULL,
88 * or MOBILE_IMAGE_MOUNTER_E_CONN_FAILED when the connection to the
89 * device could not be established.
90 */
91mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, uint16_t port, mobile_image_mounter_client_t *client)
92{
93 /* makes sure thread environment is available */
94 if (!g_thread_supported())
95 g_thread_init(NULL);
96
97 if (!device)
98 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
99
100 property_list_service_client_t plistclient = NULL;
101 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
102 return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED;
103 }
104
105 mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_int));
106 client_loc->parent = plistclient;
107
108 client_loc->mutex = g_mutex_new();
109
110 *client = client_loc;
111 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
112}
113
114/**
115 * Disconnects an MobileImageMounter client from the device.
116 *
117 * @param client The client to disconnect.
118 *
119 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
120 * or MOBILE_IMAGE_MOUNTER_E_INVALID_ARG when client is NULL.
121 */
122mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client)
123{
124 if (!client)
125 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
126
127 property_list_service_client_free(client->parent);
128 client->parent = NULL;
129 if (client->mutex) {
130 g_mutex_free(client->mutex);
131 }
132 free(client);
133
134 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
135}
136
137/**
138 * Tells if the image of ImageType is already mounted.
139 *
140 * @param client The client use
141 * @param image_type The type of the image to look up
142 * @param result Pointer to a plist that will receive the result of the
143 * operation.
144 *
145 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
146 * operation has failed. Check the resulting plist for further information.
147 *
148 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, or an error code on error
149 */
150mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result)
151{
152 if (!client || !image_type || !result) {
153 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
154 }
155 mobile_image_mounter_lock(client);
156
157 plist_t dict = plist_new_dict();
158 plist_dict_insert_item(dict,"Command", plist_new_string("LookupImage"));
159 plist_dict_insert_item(dict,"ImageType", plist_new_string(image_type));
160
161 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
162 plist_free(dict);
163
164 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
165 debug_info("%s: Error sending XML plist to device!", __func__);
166 goto leave_unlock;
167 }
168
169 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
170 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
171 debug_info("%s: Error receiving response from device!", __func__);
172 }
173
174leave_unlock:
175 mobile_image_mounter_unlock(client);
176 return res;
177}
178
179/**
180 * Mounts an image on the device.
181 *
182 * @param client The connected MobileImageMounter client.
183 * @param image_path The absolute path of the image to mount. The image must
184 * be present before calling this function.
185 * @param image_signature Pointer to a buffer holding the images' signature
186 * @param signature_length Length of the signature image_signature points to
187 * @param image_type Type of image to mount
188 * @param result Pointer to a plist that will receive the result of the
189 * operation.
190 *
191 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
192 * operation has failed. Check the resulting plist for further information.
193 *
194 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
195 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG when on ore more parameters are
196 * invalid, or another error code otherwise.
197 */
198mobile_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)
199{
200 if (!client || !image_path || !image_signature || (signature_length == 0) || !image_type || !result) {
201 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
202 }
203 mobile_image_mounter_lock(client);
204
205 plist_t dict = plist_new_dict();
206 plist_dict_insert_item(dict, "Command", plist_new_string("MountImage"));
207 plist_dict_insert_item(dict, "ImagePath", plist_new_string(image_path));
208 plist_dict_insert_item(dict, "ImageSignature", plist_new_data(image_signature, signature_length));
209 plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type));
210
211 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
212 plist_free(dict);
213
214 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
215 debug_info("%s: Error sending XML plist to device!", __func__);
216 goto leave_unlock;
217 }
218
219 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
220 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
221 debug_info("%s: Error receiving response from device!", __func__);
222 }
223
224leave_unlock:
225 mobile_image_mounter_unlock(client);
226 return res;
227}
228
229/**
230 * Hangs up the connection to the MobileImageMounter service.
231 * This functions has to be called before freeing up a mobile_image_mounter
232 * instance. If not, errors appear in the device's syslog.
233 *
234 * @param client The client to hang up
235 *
236 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
237 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid,
238 * or another error code otherwise.
239 */
240mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)
241{
242 if (!client) {
243 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
244 }
245 mobile_image_mounter_lock(client);
246
247 plist_t dict = plist_new_dict();
248 plist_dict_insert_item(dict, "Command", plist_new_string("Hangup"));
249
250 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
251 plist_free(dict);
252
253 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
254 debug_info("%s: Error sending XML plist to device!", __func__);
255 goto leave_unlock;
256 }
257
258 dict = NULL;
259 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &dict));
260 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
261 debug_info("%s: Error receiving response from device!", __func__);
262 }
263 if (dict) {
264 debug_plist(dict);
265 plist_free(dict);
266 }
267
268leave_unlock:
269 mobile_image_mounter_unlock(client);
270 return res;
271}
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 @@
1/*
2 * mobile_image_mounter.h
3 * Mobile Image Mounter header file.
4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21#ifndef IMOBILE_IMAGE_MOUNTER_H
22#define IMOBILE_IMAGE_MOUNTER_H
23
24#include <glib.h>
25
26#include "libimobiledevice/mobile_image_mounter.h"
27#include "property_list_service.h"
28
29struct mobile_image_mounter_client_int {
30 property_list_service_client_t parent;
31 GMutex *mutex;
32};
33
34#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
3AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS) 3AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS)
4AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) 4AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS)
5 5
6bin_PROGRAMS = idevice_id ideviceinfo idevicesyslog idevicebackup 6bin_PROGRAMS = idevice_id ideviceinfo idevicesyslog idevicebackup ideviceimagemounter
7 7
8ideviceinfo_SOURCES = ideviceinfo.c 8ideviceinfo_SOURCES = ideviceinfo.c
9ideviceinfo_CFLAGS = $(AM_CFLAGS) 9ideviceinfo_CFLAGS = $(AM_CFLAGS)
@@ -25,3 +25,7 @@ idevicebackup_CFLAGS = $(AM_CFLAGS)
25idevicebackup_LDFLAGS = $(AM_LDFLAGS) 25idevicebackup_LDFLAGS = $(AM_LDFLAGS)
26idevicebackup_LDADD = ../src/libimobiledevice.la 26idevicebackup_LDADD = ../src/libimobiledevice.la
27 27
28ideviceimagemounter_SOURCES = ideviceimagemounter.c
29ideviceimagemounter_CFLAGS = $(AM_CFLAGS)
30ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS)
31ideviceimagemounter_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 @@
1/**
2 * ideviceimagemounter -- Mount developer/debug disk images on the iPhone/iPod
3 *
4 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
5 *
6 * Licensed under the GNU General Public License Version 2
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more profile.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24#include <stdlib.h>
25#define _GNU_SOURCE 1
26#define __USE_GNU 1
27#include <stdio.h>
28#include <string.h>
29#include <getopt.h>
30#include <errno.h>
31#include <glib.h>
32
33#include <libimobiledevice/libimobiledevice.h>
34#include <libimobiledevice/lockdown.h>
35#include <libimobiledevice/afc.h>
36#include <libimobiledevice/notification_proxy.h>
37#include <libimobiledevice/mobile_image_mounter.h>
38
39static int indent_level = 0;
40
41static int list_mode = 0;
42static int xml_mode = 0;
43static char *uuid = NULL;
44static char *imagetype = NULL;
45
46static const char PKG_PATH[] = "PublicStaging";
47static const char PATH_PREFIX[] = "/private/var/mobile/Media";
48
49static void print_usage(int argc, char **argv)
50{
51 char *name = NULL;
52
53 name = strrchr(argv[0], '/');
54 printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0]));
55 printf("Mounts the specified disk image on the device.\n\n");
56 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
57 printf(" -l, --list\t\tList mount information\n");
58 printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n");
59 printf(" -x, --xml\t\tUse XML output\n");
60 printf(" -d, --debug\t\tenable communication debugging\n");
61 printf(" -h, --help\t\tprints usage information\n");
62 printf("\n");
63}
64
65static void parse_opts(int argc, char **argv)
66{
67 static struct option longopts[] = {
68 {"help", 0, NULL, 'h'},
69 {"uuid", 0, NULL, 'u'},
70 {"list", 0, NULL, 'l'},
71 {"imagetype", 0, NULL, 't'},
72 {"xml", 0, NULL, 'x'},
73 {"debug", 0, NULL, 'd'},
74 {NULL, 0, NULL, 0}
75 };
76 int c;
77
78 while (1) {
79 c = getopt_long(argc, argv, "hu:lt:xd", longopts,
80 (int *) 0);
81 if (c == -1) {
82 break;
83 }
84
85 switch (c) {
86 case 'h':
87 print_usage(argc, argv);
88 exit(0);
89 case 'u':
90 if (strlen(optarg) != 40) {
91 printf("%s: invalid UUID specified (length != 40)\n",
92 argv[0]);
93 print_usage(argc, argv);
94 exit(2);
95 }
96 uuid = strdup(optarg);
97 break;
98 case 'l':
99 list_mode = 1;
100 break;
101 case 't':
102 imagetype = strdup(optarg);
103 break;
104 case 'x':
105 xml_mode = 1;
106 break;
107 case 'd':
108 idevice_set_debug_level(1);
109 break;
110 default:
111 print_usage(argc, argv);
112 exit(2);
113 }
114 }
115}
116
117static void plist_node_to_string(plist_t node);
118
119static void plist_array_to_string(plist_t node)
120{
121 /* iterate over items */
122 int i, count;
123 plist_t subnode = NULL;
124
125 count = plist_array_get_size(node);
126
127 for (i = 0; i < count; i++) {
128 subnode = plist_array_get_item(node, i);
129 printf("%*s", indent_level, "");
130 printf("%d: ", i);
131 plist_node_to_string(subnode);
132 }
133}
134
135static void plist_dict_to_string(plist_t node)
136{
137 /* iterate over key/value pairs */
138 plist_dict_iter it = NULL;
139
140 char* key = NULL;
141 plist_t subnode = NULL;
142 plist_dict_new_iter(node, &it);
143 plist_dict_next_item(node, it, &key, &subnode);
144 while (subnode)
145 {
146 printf("%*s", indent_level, "");
147 printf("%s", key);
148 if (plist_get_node_type(subnode) == PLIST_ARRAY)
149 printf("[%d]: ", plist_array_get_size(subnode));
150 else
151 printf(": ");
152 free(key);
153 key = NULL;
154 plist_node_to_string(subnode);
155 plist_dict_next_item(node, it, &key, &subnode);
156 }
157 free(it);
158}
159
160static void plist_node_to_string(plist_t node)
161{
162 char *s = NULL;
163 char *data = NULL;
164 double d;
165 uint8_t b;
166 uint64_t u = 0;
167 GTimeVal tv = { 0, 0 };
168
169 plist_type t;
170
171 if (!node)
172 return;
173
174 t = plist_get_node_type(node);
175
176 switch (t) {
177 case PLIST_BOOLEAN:
178 plist_get_bool_val(node, &b);
179 printf("%s\n", (b ? "true" : "false"));
180 break;
181
182 case PLIST_UINT:
183 plist_get_uint_val(node, &u);
184 printf("%llu\n", (long long)u);
185 break;
186
187 case PLIST_REAL:
188 plist_get_real_val(node, &d);
189 printf("%f\n", d);
190 break;
191
192 case PLIST_STRING:
193 plist_get_string_val(node, &s);
194 printf("%s\n", s);
195 free(s);
196 break;
197
198 case PLIST_KEY:
199 plist_get_key_val(node, &s);
200 printf("%s: ", s);
201 free(s);
202 break;
203
204 case PLIST_DATA:
205 plist_get_data_val(node, &data, &u);
206 uint64_t i;
207 for (i = 0; i < u; i++) {
208 printf("%02x", (unsigned char)data[i]);
209 }
210 free(data);
211 printf("\n");
212 g_free(s);
213 break;
214
215 case PLIST_DATE:
216 plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec);
217 s = g_time_val_to_iso8601(&tv);
218 printf("%s\n", s);
219 free(s);
220 break;
221
222 case PLIST_ARRAY:
223 printf("\n");
224 indent_level++;
225 plist_array_to_string(node);
226 indent_level--;
227 break;
228
229 case PLIST_DICT:
230 printf("\n");
231 indent_level++;
232 plist_dict_to_string(node);
233 indent_level--;
234 break;
235
236 default:
237 break;
238 }
239}
240
241static void print_xml(plist_t node)
242{
243 char *xml = NULL;
244 uint32_t len = 0;
245 plist_to_xml(node, &xml, &len);
246 if (xml)
247 puts(xml);
248}
249
250int main(int argc, char **argv)
251{
252 idevice_t device = NULL;
253 lockdownd_client_t lckd = NULL;
254 mobile_image_mounter_client_t mim = NULL;
255 afc_client_t afc = NULL;
256 uint16_t port = 0;
257 int res = -1;
258 char *image_path = NULL;
259 char *image_sig_path = NULL;
260
261 parse_opts(argc, argv);
262
263 argc -= optind;
264 argv += optind;
265
266 if (!list_mode) {
267 if (argc < 1) {
268 printf("ERROR: No IMAGE_FILE has been given!\n");
269 return -1;
270 }
271 image_path = strdup(argv[0]);
272 if (argc >= 2) {
273 image_sig_path = strdup(argv[1]);
274 } else {
275 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
276 printf("Out of memory?!\n");
277 return -1;
278 }
279 }
280 }
281
282 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) {
283 printf("No device found, is it plugged in?\n");
284 return -1;
285 }
286
287 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) {
288 printf("ERROR: could not connect to lockdown. Exiting.\n");
289 goto leave;
290 }
291
292 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port);
293
294 if (port == 0) {
295 printf("ERROR: Could not start mobile_image_mounter service!\n");
296 goto leave;
297 }
298
299 if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
300 printf("ERROR: Could not connect to mobile_image_mounter!\n");
301 goto leave;
302 }
303
304 if (!list_mode) {
305 struct stat fst;
306 port = 0;
307 if ((lockdownd_start_service(lckd, "com.apple.afc", &port) !=
308 LOCKDOWN_E_SUCCESS) || !port) {
309 fprintf(stderr, "Could not start com.apple.afc!\n");
310 goto leave;
311 }
312 if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) {
313 fprintf(stderr, "Could not connect to AFC!\n");
314 goto leave;
315 }
316 if (stat(image_path, &fst) != 0) {
317 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
318 goto leave;
319 }
320 if (stat(image_sig_path, &fst) != 0) {
321 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
322 goto leave;
323 }
324 }
325
326 lockdownd_client_free(lckd);
327 lckd = NULL;
328
329 mobile_image_mounter_error_t err;
330 plist_t result = NULL;
331
332 if (list_mode) {
333 /* list mounts mode */
334 if (!imagetype) {
335 imagetype = strdup("Developer");
336 }
337 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
338 free(imagetype);
339 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
340 res = 0;
341 if (xml_mode) {
342 print_xml(result);
343 } else {
344 plist_dict_to_string(result);
345 }
346 } else {
347 printf("Error: lookup_image returned %d\n", err);
348 }
349 } else {
350 char sig[8192];
351 size_t sig_length = 0;
352 FILE *f = fopen(image_sig_path, "r");
353 if (!f) {
354 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
355 goto leave;
356 }
357 sig_length = fread(sig, 1, sizeof(sig), f);
358 fclose(f);
359 if (sig_length == 0) {
360 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
361 goto leave;
362 }
363
364 f = fopen(image_path, "r");
365 if (!f) {
366 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
367 goto leave;
368 }
369
370 char *targetname = NULL;
371 if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) {
372 fprintf(stderr, "Out of memory!?\n");
373 goto leave;
374 }
375 char *mountname = NULL;
376 if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
377 fprintf(stderr, "Out of memory!?\n");
378 goto leave;
379 }
380
381 printf("Copying '%s' --> '%s'\n", image_path, targetname);
382
383 char **strs = NULL;
384 if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
385 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
386 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
387 }
388 }
389 if (strs) {
390 int i = 0;
391 while (strs[i]) {
392 free(strs[i]);
393 i++;
394 }
395 free(strs);
396 }
397
398 uint64_t af = 0;
399 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
400 AFC_E_SUCCESS) || !af) {
401 fclose(f);
402 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
403 goto leave;
404 }
405
406 char buf[8192];
407 size_t amount = 0;
408 do {
409 amount = fread(buf, 1, sizeof(buf), f);
410 if (amount > 0) {
411 uint32_t written, total = 0;
412 while (total < amount) {
413 written = 0;
414 if (afc_file_write(afc, af, buf, amount, &written) !=
415 AFC_E_SUCCESS) {
416 fprintf(stderr, "AFC Write error!\n");
417 break;
418 }
419 total += written;
420 }
421 if (total != amount) {
422 fprintf(stderr, "Error: wrote only %d of %d\n", total,
423 amount);
424 afc_file_close(afc, af);
425 fclose(f);
426 goto leave;
427 }
428 }
429 }
430 while (amount > 0);
431
432 afc_file_close(afc, af);
433 fclose(f);
434
435 printf("done.\n");
436
437 printf("Mounting...\n");
438 if (!imagetype) {
439 imagetype = strdup("Developer");
440 }
441 err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
442 free(imagetype);
443 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
444 if (result) {
445 plist_t node = plist_dict_get_item(result, "Status");
446 if (node) {
447 char *status = NULL;
448 plist_get_string_val(node, &status);
449 if (status) {
450 if (!strcmp(status, "Complete")) {
451 printf("Done.\n");
452 res = 0;
453 } else {
454 printf("unexpected status value:\n");
455 if (xml_mode) {
456 print_xml(result);
457 } else {
458 plist_dict_to_string(result);
459 }
460 }
461 free(status);
462 } else {
463 printf("unexpected result:\n");
464 if (xml_mode) {
465 print_xml(result);
466 } else {
467 plist_dict_to_string(result);
468 }
469 }
470 }
471 node = plist_dict_get_item(result, "Error");
472 if (node) {
473 char *error = NULL;
474 plist_get_string_val(node, &error);
475 if (error) {
476 printf("Error: %s\n", error);
477 free(error);
478 } else {
479 printf("unexpected result:\n");
480 if (xml_mode) {
481 print_xml(result);
482 } else {
483 plist_dict_to_string(result);
484 }
485 }
486
487 } else {
488 if (xml_mode) {
489 print_xml(result);
490 } else {
491 plist_dict_to_string(result);
492 }
493 }
494 }
495 } else {
496 printf("Error: mount_image returned %d\n", err);
497
498 }
499 }
500
501 if (result) {
502 plist_free(result);
503 }
504
505 /* perform hangup command */
506 mobile_image_mounter_hangup(mim);
507 /* free client */
508 mobile_image_mounter_free(mim);
509
510leave:
511 if (afc) {
512 afc_client_free(afc);
513 }
514 if (lckd) {
515 lockdownd_client_free(lckd);
516 }
517 idevice_free(device);
518
519 if (image_path)
520 free(image_path);
521 if (image_sig_path)
522 free(image_sig_path);
523
524 return res;
525}