diff options
Diffstat (limited to 'tools/ideviceprovision.c')
-rw-r--r-- | tools/ideviceprovision.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/tools/ideviceprovision.c b/tools/ideviceprovision.c new file mode 100644 index 0000000..4080a28 --- /dev/null +++ b/tools/ideviceprovision.c | |||
@@ -0,0 +1,689 @@ | |||
1 | /* | ||
2 | * ideviceprovision.c | ||
3 | * Simple utility to install, get, or remove provisioning profiles | ||
4 | * to/from idevices | ||
5 | * | ||
6 | * Copyright (c) 2012-2016 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 | #ifdef HAVE_CONFIG_H | ||
24 | #include <config.h> | ||
25 | #endif | ||
26 | |||
27 | #define TOOL_NAME "ideviceprovision" | ||
28 | |||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <getopt.h> | ||
33 | #include <sys/stat.h> | ||
34 | #include <errno.h> | ||
35 | #ifndef WIN32 | ||
36 | #include <signal.h> | ||
37 | #endif | ||
38 | |||
39 | #ifdef WIN32 | ||
40 | #include <windows.h> | ||
41 | #else | ||
42 | #include <arpa/inet.h> | ||
43 | #endif | ||
44 | |||
45 | #include <libimobiledevice/libimobiledevice.h> | ||
46 | #include <libimobiledevice/lockdown.h> | ||
47 | #include <libimobiledevice/misagent.h> | ||
48 | #include <plist/plist.h> | ||
49 | |||
50 | static void print_usage(int argc, char **argv, int is_error) | ||
51 | { | ||
52 | char *name = strrchr(argv[0], '/'); | ||
53 | fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0])); | ||
54 | fprintf(is_error ? stderr : stdout, | ||
55 | "\n" | ||
56 | "Manage provisioning profiles on a device.\n" | ||
57 | "\n" | ||
58 | "Where COMMAND is one of:\n" | ||
59 | " install FILE Installs the provisioning profile specified by FILE.\n" | ||
60 | " A valid .mobileprovision file is expected.\n" | ||
61 | " list Get a list of all provisioning profiles on the device.\n" | ||
62 | " copy PATH Retrieves all provisioning profiles from the device and\n" | ||
63 | " stores them into the existing directory specified by PATH.\n" | ||
64 | " The files will be stored as UUID.mobileprovision\n" | ||
65 | " copy UUID PATH Retrieves the provisioning profile identified by UUID\n" | ||
66 | " from the device and stores it into the existing directory\n" | ||
67 | " specified by PATH. The file will be stored as UUID.mobileprovision.\n" | ||
68 | " remove UUID Removes the provisioning profile identified by UUID.\n" | ||
69 | " remove-all Removes all installed provisioning profiles.\n" | ||
70 | " dump FILE Prints detailed information about the provisioning profile\n" | ||
71 | " specified by FILE.\n" | ||
72 | "\n" | ||
73 | "The following OPTIONS are accepted:\n" | ||
74 | " -u, --udid UDID target specific device by UDID\n" | ||
75 | " -n, --network connect to network device\n" | ||
76 | " -x, --xml print XML output when using the 'dump' command\n" | ||
77 | " -d, --debug enable communication debugging\n" | ||
78 | " -h, --help prints usage information\n" | ||
79 | " -v, --version prints version information\n" | ||
80 | "\n" | ||
81 | "Homepage: <" PACKAGE_URL ">\n" | ||
82 | "Bug Reports: <" PACKAGE_BUGREPORT ">\n" | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | enum { | ||
87 | OP_INSTALL, | ||
88 | OP_LIST, | ||
89 | OP_COPY, | ||
90 | OP_REMOVE, | ||
91 | OP_DUMP, | ||
92 | NUM_OPS | ||
93 | }; | ||
94 | |||
95 | #define ASN1_SEQUENCE 0x30 | ||
96 | #define ASN1_CONTAINER 0xA0 | ||
97 | #define ASN1_OBJECT_IDENTIFIER 0x06 | ||
98 | #define ASN1_OCTET_STRING 0x04 | ||
99 | |||
100 | static void asn1_next_item(unsigned char** p) | ||
101 | { | ||
102 | char bsize = *(*p+1); | ||
103 | if (bsize & 0x80) { | ||
104 | *p += 2 + (bsize & 0xF); | ||
105 | } else { | ||
106 | *p += 3; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static size_t asn1_item_get_size(const unsigned char* p) | ||
111 | { | ||
112 | size_t res = 0; | ||
113 | char bsize = *(p+1); | ||
114 | if (bsize & 0x80) { | ||
115 | uint16_t ws = 0; | ||
116 | uint32_t ds = 0; | ||
117 | switch (bsize & 0xF) { | ||
118 | case 2: | ||
119 | ws = *(uint16_t*)(p+2); | ||
120 | res = ntohs(ws); | ||
121 | break; | ||
122 | case 3: | ||
123 | ds = *(uint32_t*)(p+2); | ||
124 | res = ntohl(ds) >> 8; | ||
125 | break; | ||
126 | case 4: | ||
127 | ds = *(uint32_t*)(p+2); | ||
128 | res = ntohl(ds); | ||
129 | break; | ||
130 | default: | ||
131 | fprintf(stderr, "ERROR: Invalid or unimplemented byte size %d\n", bsize & 0xF); | ||
132 | break; | ||
133 | } | ||
134 | } else { | ||
135 | res = (int)bsize; | ||
136 | } | ||
137 | return res; | ||
138 | } | ||
139 | |||
140 | static void asn1_skip_item(unsigned char** p) | ||
141 | { | ||
142 | size_t sz = asn1_item_get_size(*p); | ||
143 | *p += 2; | ||
144 | *p += sz; | ||
145 | } | ||
146 | |||
147 | static plist_t profile_get_embedded_plist(plist_t profile) | ||
148 | { | ||
149 | if (plist_get_node_type(profile) != PLIST_DATA) { | ||
150 | fprintf(stderr, "%s: unexpected plist node type for profile (PLIST_DATA expected)\n", __func__); | ||
151 | return NULL; | ||
152 | } | ||
153 | char* bbuf = NULL; | ||
154 | uint64_t blen = 0; | ||
155 | plist_get_data_val(profile, &bbuf, &blen); | ||
156 | if (!bbuf) { | ||
157 | fprintf(stderr, "%s: could not get data value from plist node\n", __func__); | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | unsigned char* pp = (unsigned char*)bbuf; | ||
162 | |||
163 | if (*pp != ASN1_SEQUENCE) { | ||
164 | free(bbuf); | ||
165 | fprintf(stderr, "%s: unexpected profile data (0)\n", __func__); | ||
166 | return NULL; | ||
167 | } | ||
168 | size_t slen = asn1_item_get_size(pp); | ||
169 | char bsize = *(pp+1); | ||
170 | if (bsize & 0x80) { | ||
171 | slen += 2 + (bsize & 0xF); | ||
172 | } else { | ||
173 | slen += 3; | ||
174 | } | ||
175 | if (slen != blen) { | ||
176 | free(bbuf); | ||
177 | fprintf(stderr, "%s: unexpected profile data (1)\n", __func__); | ||
178 | return NULL; | ||
179 | } | ||
180 | asn1_next_item(&pp); | ||
181 | |||
182 | if (*pp != ASN1_OBJECT_IDENTIFIER) { | ||
183 | free(bbuf); | ||
184 | fprintf(stderr, "%s: unexpected profile data (2)\n", __func__); | ||
185 | return NULL; | ||
186 | } | ||
187 | asn1_skip_item(&pp); | ||
188 | |||
189 | if (*pp != ASN1_CONTAINER) { | ||
190 | free(bbuf); | ||
191 | fprintf(stderr, "%s: unexpected profile data (3)\n", __func__); | ||
192 | return NULL; | ||
193 | } | ||
194 | asn1_next_item(&pp); | ||
195 | |||
196 | if (*pp != ASN1_SEQUENCE) { | ||
197 | free(bbuf); | ||
198 | fprintf(stderr, "%s: unexpected profile data (4)\n", __func__); | ||
199 | return NULL; | ||
200 | } | ||
201 | asn1_next_item(&pp); | ||
202 | |||
203 | int k = 0; | ||
204 | // go to the 3rd element (skip 2) | ||
205 | while (k < 2) { | ||
206 | asn1_skip_item(&pp); | ||
207 | k++; | ||
208 | } | ||
209 | if (*pp != ASN1_SEQUENCE) { | ||
210 | free(bbuf); | ||
211 | fprintf(stderr, "%s: unexpected profile data (5)\n", __func__); | ||
212 | return NULL; | ||
213 | } | ||
214 | asn1_next_item(&pp); | ||
215 | |||
216 | if (*pp != ASN1_OBJECT_IDENTIFIER) { | ||
217 | free(bbuf); | ||
218 | fprintf(stderr, "%s: unexpected profile data (6)\n", __func__); | ||
219 | return NULL; | ||
220 | } | ||
221 | asn1_skip_item(&pp); | ||
222 | |||
223 | if (*pp != ASN1_CONTAINER) { | ||
224 | free(bbuf); | ||
225 | fprintf(stderr, "%s: unexpected profile data (7)\n", __func__); | ||
226 | return NULL; | ||
227 | } | ||
228 | asn1_next_item(&pp); | ||
229 | |||
230 | if (*pp != ASN1_OCTET_STRING) { | ||
231 | free(bbuf); | ||
232 | fprintf(stderr, "%s: unexpected profile data (8)\n", __func__); | ||
233 | return NULL; | ||
234 | } | ||
235 | slen = asn1_item_get_size(pp); | ||
236 | asn1_next_item(&pp); | ||
237 | |||
238 | plist_t pl = NULL; | ||
239 | plist_from_xml((char*)pp, slen, &pl); | ||
240 | free(bbuf); | ||
241 | |||
242 | return pl; | ||
243 | } | ||
244 | |||
245 | static int profile_read_from_file(const char* path, unsigned char **profile_data, unsigned int *profile_size) | ||
246 | { | ||
247 | FILE* f = fopen(path, "rb"); | ||
248 | if (!f) { | ||
249 | fprintf(stderr, "Could not open file '%s'\n", path); | ||
250 | return -1; | ||
251 | } | ||
252 | fseek(f, 0, SEEK_END); | ||
253 | long int size = ftell(f); | ||
254 | fseek(f, 0, SEEK_SET); | ||
255 | |||
256 | if (size >= 0x1000000) { | ||
257 | fprintf(stderr, "The file '%s' is too large for processing.\n", path); | ||
258 | fclose(f); | ||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | unsigned char* buf = malloc(size); | ||
263 | if (!buf) { | ||
264 | fprintf(stderr, "Could not allocate memory...\n"); | ||
265 | fclose(f); | ||
266 | return -1; | ||
267 | } | ||
268 | |||
269 | long int cur = 0; | ||
270 | while (cur < size) { | ||
271 | ssize_t r = fread(buf+cur, 1, 512, f); | ||
272 | if (r <= 0) { | ||
273 | break; | ||
274 | } | ||
275 | cur += r; | ||
276 | } | ||
277 | fclose(f); | ||
278 | |||
279 | if (cur != size) { | ||
280 | free(buf); | ||
281 | fprintf(stderr, "Could not read in file '%s' (size %ld read %ld)\n", path, size, cur); | ||
282 | return -1; | ||
283 | } | ||
284 | |||
285 | *profile_data = buf; | ||
286 | *profile_size = (unsigned int)size; | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | int main(int argc, char *argv[]) | ||
292 | { | ||
293 | lockdownd_client_t client = NULL; | ||
294 | lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
295 | lockdownd_service_descriptor_t service = NULL; | ||
296 | idevice_t device = NULL; | ||
297 | idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; | ||
298 | int res = 0; | ||
299 | int i; | ||
300 | int op = -1; | ||
301 | int output_xml = 0; | ||
302 | const char* udid = NULL; | ||
303 | const char* param = NULL; | ||
304 | const char* param2 = NULL; | ||
305 | int use_network = 0; | ||
306 | int c = 0; | ||
307 | const struct option longopts[] = { | ||
308 | { "debug", no_argument, NULL, 'd' }, | ||
309 | { "help", no_argument, NULL, 'h' }, | ||
310 | { "udid", required_argument, NULL, 'u' }, | ||
311 | { "network", no_argument, NULL, 'n' }, | ||
312 | { "version", no_argument, NULL, 'v' }, | ||
313 | { "xml", no_argument, NULL, 'x' }, | ||
314 | { NULL, 0, NULL, 0} | ||
315 | }; | ||
316 | |||
317 | #ifndef WIN32 | ||
318 | signal(SIGPIPE, SIG_IGN); | ||
319 | #endif | ||
320 | /* parse cmdline args */ | ||
321 | while ((c = getopt_long(argc, argv, "dhu:nvx", longopts, NULL)) != -1) { | ||
322 | switch (c) { | ||
323 | case 'd': | ||
324 | idevice_set_debug_level(1); | ||
325 | break; | ||
326 | case 'u': | ||
327 | if (!*optarg) { | ||
328 | fprintf(stderr, "ERROR: UDID argument must not be empty!\n"); | ||
329 | print_usage(argc, argv, 1); | ||
330 | return 2; | ||
331 | } | ||
332 | udid = optarg; | ||
333 | break; | ||
334 | case 'n': | ||
335 | use_network = 1; | ||
336 | break; | ||
337 | case 'h': | ||
338 | print_usage(argc, argv, 0); | ||
339 | return 0; | ||
340 | case 'v': | ||
341 | printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); | ||
342 | return 0; | ||
343 | case 'x': | ||
344 | output_xml = 1; | ||
345 | break; | ||
346 | default: | ||
347 | print_usage(argc, argv, 1); | ||
348 | return 2; | ||
349 | } | ||
350 | } | ||
351 | argc -= optind; | ||
352 | argv += optind; | ||
353 | |||
354 | if (!argv[0]) { | ||
355 | fprintf(stderr, "ERROR: Missing command.\n"); | ||
356 | print_usage(argc+optind, argv-optind, 1); | ||
357 | return 2; | ||
358 | } | ||
359 | |||
360 | i = 0; | ||
361 | if (!strcmp(argv[i], "install")) { | ||
362 | op = OP_INSTALL; | ||
363 | i++; | ||
364 | if (!argv[i] || !*argv[i]) { | ||
365 | fprintf(stderr, "Missing argument for 'install' command.\n"); | ||
366 | print_usage(argc+optind, argv-optind, 1); | ||
367 | return 2; | ||
368 | } | ||
369 | param = argv[i]; | ||
370 | } | ||
371 | else if (!strcmp(argv[i], "list")) { | ||
372 | op = OP_LIST; | ||
373 | } | ||
374 | else if (!strcmp(argv[i], "copy")) { | ||
375 | op = OP_COPY; | ||
376 | i++; | ||
377 | if (!argv[i] || !*argv[i]) { | ||
378 | fprintf(stderr, "Missing argument for 'copy' command.\n"); | ||
379 | print_usage(argc+optind, argv-optind, 1); | ||
380 | return 2; | ||
381 | } | ||
382 | param = argv[i]; | ||
383 | i++; | ||
384 | if (argv[i] && (strlen(argv[i]) > 0)) { | ||
385 | param2 = argv[i]; | ||
386 | } | ||
387 | } | ||
388 | else if (!strcmp(argv[i], "remove")) { | ||
389 | op = OP_REMOVE; | ||
390 | i++; | ||
391 | if (!argv[i] || !*argv[i]) { | ||
392 | fprintf(stderr, "Missing argument for 'remove' command.\n"); | ||
393 | print_usage(argc+optind, argv-optind, 1); | ||
394 | return 2; | ||
395 | } | ||
396 | param = argv[i]; | ||
397 | } | ||
398 | else if (!strcmp(argv[i], "remove-all")) { | ||
399 | op = OP_REMOVE; | ||
400 | } | ||
401 | else if (!strcmp(argv[i], "dump")) { | ||
402 | op = OP_DUMP; | ||
403 | i++; | ||
404 | if (!argv[i] || !*argv[i]) { | ||
405 | fprintf(stderr, "Missing argument for 'remove' command.\n"); | ||
406 | print_usage(argc+optind, argv-optind, 1); | ||
407 | return 2; | ||
408 | } | ||
409 | param = argv[i]; | ||
410 | } | ||
411 | if ((op == -1) || (op >= NUM_OPS)) { | ||
412 | fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]); | ||
413 | print_usage(argc+optind, argv-optind, 1); | ||
414 | return 2; | ||
415 | } | ||
416 | |||
417 | if (op == OP_DUMP) { | ||
418 | unsigned char* profile_data = NULL; | ||
419 | unsigned int profile_size = 0; | ||
420 | if (profile_read_from_file(param, &profile_data, &profile_size) != 0) { | ||
421 | return -1; | ||
422 | } | ||
423 | plist_t pdata = plist_new_data((char*)profile_data, profile_size); | ||
424 | plist_t pl = profile_get_embedded_plist(pdata); | ||
425 | plist_free(pdata); | ||
426 | free(profile_data); | ||
427 | |||
428 | if (pl) { | ||
429 | if (output_xml) { | ||
430 | char* xml = NULL; | ||
431 | uint32_t xlen = 0; | ||
432 | plist_to_xml(pl, &xml, &xlen); | ||
433 | if (xml) { | ||
434 | printf("%s\n", xml); | ||
435 | free(xml); | ||
436 | } | ||
437 | } else { | ||
438 | if (pl && (plist_get_node_type(pl) == PLIST_DICT)) { | ||
439 | plist_write_to_stream(pl, stdout, PLIST_FORMAT_LIMD, 0); | ||
440 | } else { | ||
441 | fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n"); | ||
442 | res = -1; | ||
443 | } | ||
444 | } | ||
445 | } else { | ||
446 | fprintf(stderr, "ERROR: could not extract embedded plist from profile!\n"); | ||
447 | } | ||
448 | plist_free(pl); | ||
449 | |||
450 | return res; | ||
451 | } | ||
452 | |||
453 | if (op == OP_COPY) { | ||
454 | struct stat st; | ||
455 | const char *checkdir = (param2) ? param2 : param; | ||
456 | if ((stat(checkdir, &st) < 0) || !S_ISDIR(st.st_mode)) { | ||
457 | fprintf(stderr, "ERROR: %s does not exist or is not a directory!\n", checkdir); | ||
458 | return -1; | ||
459 | } | ||
460 | } | ||
461 | |||
462 | ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); | ||
463 | if (ret != IDEVICE_E_SUCCESS) { | ||
464 | if (udid) { | ||
465 | printf("No device found with udid %s.\n", udid); | ||
466 | } else { | ||
467 | printf("No device found.\n"); | ||
468 | } | ||
469 | return -1; | ||
470 | } | ||
471 | |||
472 | if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) { | ||
473 | fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret); | ||
474 | idevice_free(device); | ||
475 | return -1; | ||
476 | } | ||
477 | |||
478 | plist_t pver = NULL; | ||
479 | char *pver_s = NULL; | ||
480 | lockdownd_get_value(client, NULL, "ProductVersion", &pver); | ||
481 | if (pver && plist_get_node_type(pver) == PLIST_STRING) { | ||
482 | plist_get_string_val(pver, &pver_s); | ||
483 | } | ||
484 | plist_free(pver); | ||
485 | int product_version_major = 0; | ||
486 | int product_version_minor = 0; | ||
487 | int product_version_patch = 0; | ||
488 | if (pver_s) { | ||
489 | sscanf(pver_s, "%d.%d.%d", &product_version_major, &product_version_minor, &product_version_patch); | ||
490 | free(pver_s); | ||
491 | } | ||
492 | if (product_version_major == 0) { | ||
493 | fprintf(stderr, "ERROR: Could not determine the device's ProductVersion\n"); | ||
494 | lockdownd_client_free(client); | ||
495 | idevice_free(device); | ||
496 | return -1; | ||
497 | } | ||
498 | int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF); | ||
499 | |||
500 | lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service); | ||
501 | if (lerr != LOCKDOWN_E_SUCCESS) { | ||
502 | fprintf(stderr, "Could not start service %s: %s\n", MISAGENT_SERVICE_NAME, lockdownd_strerror(lerr)); | ||
503 | lockdownd_client_free(client); | ||
504 | idevice_free(device); | ||
505 | return -1; | ||
506 | } | ||
507 | lockdownd_client_free(client); | ||
508 | client = NULL; | ||
509 | |||
510 | misagent_client_t mis = NULL; | ||
511 | if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) { | ||
512 | fprintf(stderr, "Could not connect to %s on device\n", MISAGENT_SERVICE_NAME); | ||
513 | if (service) | ||
514 | lockdownd_service_descriptor_free(service); | ||
515 | lockdownd_client_free(client); | ||
516 | idevice_free(device); | ||
517 | return -1; | ||
518 | } | ||
519 | |||
520 | if (service) | ||
521 | lockdownd_service_descriptor_free(service); | ||
522 | |||
523 | switch (op) { | ||
524 | case OP_INSTALL: | ||
525 | { | ||
526 | unsigned char* profile_data = NULL; | ||
527 | unsigned int profile_size = 0; | ||
528 | if (profile_read_from_file(param, &profile_data, &profile_size) != 0) { | ||
529 | break; | ||
530 | } | ||
531 | |||
532 | uint64_t psize = profile_size; | ||
533 | plist_t pdata = plist_new_data((const char*)profile_data, psize); | ||
534 | free(profile_data); | ||
535 | |||
536 | if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) { | ||
537 | printf("Profile '%s' installed successfully.\n", param); | ||
538 | } else { | ||
539 | int sc = misagent_get_status_code(mis); | ||
540 | fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc); | ||
541 | } | ||
542 | } | ||
543 | break; | ||
544 | case OP_LIST: | ||
545 | case OP_COPY: | ||
546 | { | ||
547 | plist_t profiles = NULL; | ||
548 | misagent_error_t merr; | ||
549 | if (product_version < 0x090300) { | ||
550 | merr = misagent_copy(mis, &profiles); | ||
551 | } else { | ||
552 | merr = misagent_copy_all(mis, &profiles); | ||
553 | } | ||
554 | if (merr == MISAGENT_E_SUCCESS) { | ||
555 | int found_match = 0; | ||
556 | uint32_t num_profiles = plist_array_get_size(profiles); | ||
557 | if (op == OP_LIST || !param2) { | ||
558 | printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles"); | ||
559 | } | ||
560 | uint32_t j; | ||
561 | for (j = 0; !found_match && j < num_profiles; j++) { | ||
562 | char* p_name = NULL; | ||
563 | char* p_uuid = NULL; | ||
564 | plist_t profile = plist_array_get_item(profiles, j); | ||
565 | plist_t pl = profile_get_embedded_plist(profile); | ||
566 | if (pl && (plist_get_node_type(pl) == PLIST_DICT)) { | ||
567 | plist_t node; | ||
568 | node = plist_dict_get_item(pl, "Name"); | ||
569 | if (node && (plist_get_node_type(node) == PLIST_STRING)) { | ||
570 | plist_get_string_val(node, &p_name); | ||
571 | } | ||
572 | node = plist_dict_get_item(pl, "UUID"); | ||
573 | if (node && (plist_get_node_type(node) == PLIST_STRING)) { | ||
574 | plist_get_string_val(node, &p_uuid); | ||
575 | } | ||
576 | } | ||
577 | if (param2) { | ||
578 | if (p_uuid && !strcmp(p_uuid, param)) { | ||
579 | found_match = 1; | ||
580 | } else { | ||
581 | free(p_uuid); | ||
582 | free(p_name); | ||
583 | continue; | ||
584 | } | ||
585 | } | ||
586 | printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)"); | ||
587 | if (op == OP_COPY) { | ||
588 | char pfname[512]; | ||
589 | if (p_uuid) { | ||
590 | sprintf(pfname, "%s/%s.mobileprovision", (param2) ? param2 : param, p_uuid); | ||
591 | } else { | ||
592 | sprintf(pfname, "%s/profile%d.mobileprovision", (param2) ? param2 : param, j); | ||
593 | } | ||
594 | FILE* f = fopen(pfname, "wb"); | ||
595 | if (f) { | ||
596 | char* dt = NULL; | ||
597 | uint64_t ds = 0; | ||
598 | plist_get_data_val(profile, &dt, &ds); | ||
599 | fwrite(dt, 1, ds, f); | ||
600 | fclose(f); | ||
601 | printf(" => %s\n", pfname); | ||
602 | } else { | ||
603 | fprintf(stderr, "Could not open '%s' for writing: %s\n", pfname, strerror(errno)); | ||
604 | } | ||
605 | } | ||
606 | free(p_uuid); | ||
607 | free(p_name); | ||
608 | } | ||
609 | if (param2 && !found_match) { | ||
610 | fprintf(stderr, "Profile '%s' was not found on the device.\n", param); | ||
611 | res = -1; | ||
612 | } | ||
613 | } else { | ||
614 | int sc = misagent_get_status_code(mis); | ||
615 | fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc); | ||
616 | res = -1; | ||
617 | } | ||
618 | plist_free(profiles); | ||
619 | } | ||
620 | break; | ||
621 | case OP_REMOVE: | ||
622 | if (param) { | ||
623 | /* remove specified provisioning profile */ | ||
624 | if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) { | ||
625 | printf("Profile '%s' removed.\n", param); | ||
626 | } else { | ||
627 | int sc = misagent_get_status_code(mis); | ||
628 | fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc); | ||
629 | } | ||
630 | } else { | ||
631 | /* remove all provisioning profiles */ | ||
632 | plist_t profiles = NULL; | ||
633 | misagent_error_t merr; | ||
634 | if (product_version < 0x090300) { | ||
635 | merr = misagent_copy(mis, &profiles); | ||
636 | } else { | ||
637 | merr = misagent_copy_all(mis, &profiles); | ||
638 | } | ||
639 | if (merr == MISAGENT_E_SUCCESS) { | ||
640 | uint32_t j; | ||
641 | uint32_t num_removed = 0; | ||
642 | for (j = 0; j < plist_array_get_size(profiles); j++) { | ||
643 | char* p_name = NULL; | ||
644 | char* p_uuid = NULL; | ||
645 | plist_t profile = plist_array_get_item(profiles, j); | ||
646 | plist_t pl = profile_get_embedded_plist(profile); | ||
647 | if (pl && (plist_get_node_type(pl) == PLIST_DICT)) { | ||
648 | plist_t node; | ||
649 | node = plist_dict_get_item(pl, "Name"); | ||
650 | if (node && (plist_get_node_type(node) == PLIST_STRING)) { | ||
651 | plist_get_string_val(node, &p_name); | ||
652 | } | ||
653 | node = plist_dict_get_item(pl, "UUID"); | ||
654 | if (node && (plist_get_node_type(node) == PLIST_STRING)) { | ||
655 | plist_get_string_val(node, &p_uuid); | ||
656 | } | ||
657 | } | ||
658 | if (p_uuid) { | ||
659 | if (misagent_remove(mis, p_uuid) == MISAGENT_E_SUCCESS) { | ||
660 | printf("OK profile removed: %s - %s\n", p_uuid, (p_name) ? p_name : "(no name)"); | ||
661 | num_removed++; | ||
662 | } else { | ||
663 | int sc = misagent_get_status_code(mis); | ||
664 | printf("FAIL profile not removed: %s - %s (status code 0x%x)\n", p_uuid, (p_name) ? p_name : "(no name)", sc); | ||
665 | } | ||
666 | } | ||
667 | free(p_name); | ||
668 | free(p_uuid); | ||
669 | } | ||
670 | printf("%d profiles removed.\n", num_removed); | ||
671 | } else { | ||
672 | int sc = misagent_get_status_code(mis); | ||
673 | fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc); | ||
674 | res = -1; | ||
675 | } | ||
676 | plist_free(profiles); | ||
677 | } | ||
678 | break; | ||
679 | default: | ||
680 | break; | ||
681 | } | ||
682 | |||
683 | misagent_client_free(mis); | ||
684 | |||
685 | idevice_free(device); | ||
686 | |||
687 | return res; | ||
688 | } | ||
689 | |||