summaryrefslogtreecommitdiffstats
path: root/src/ideviceinstaller.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ideviceinstaller.c')
-rw-r--r--src/ideviceinstaller.c264
1 files changed, 127 insertions, 137 deletions
diff --git a/src/ideviceinstaller.c b/src/ideviceinstaller.c
index d80f04d..55c10d0 100644
--- a/src/ideviceinstaller.c
+++ b/src/ideviceinstaller.c
@@ -94,7 +94,7 @@ const char APPARCH_PATH[] = "ApplicationArchives";
94 94
95char *udid = NULL; 95char *udid = NULL;
96char *options = NULL; 96char *options = NULL;
97char *appid = NULL; 97char *cmdarg = NULL;
98 98
99enum cmd_mode { 99enum cmd_mode {
100 CMD_NONE = 0, 100 CMD_NONE = 0,
@@ -147,7 +147,7 @@ static void print_apps(plist_t apps)
147 plist_get_string_val(p_bundle_identifier, &s_bundle_identifier); 147 plist_get_string_val(p_bundle_identifier, &s_bundle_identifier);
148 } 148 }
149 if (!s_bundle_identifier) { 149 if (!s_bundle_identifier) {
150 fprintf(stderr, "ERROR: Failed to get APPID!\n"); 150 fprintf(stderr, "ERROR: Failed to get bundle identifier!\n");
151 break; 151 break;
152 } 152 }
153 153
@@ -387,50 +387,49 @@ static void idevice_wait_for_command_to_complete()
387 idevice_event_unsubscribe(); 387 idevice_event_unsubscribe();
388} 388}
389 389
390static void print_usage(int argc, char **argv) 390static void print_usage(int argc, char **argv, int is_error)
391{ 391{
392 char *name = NULL; 392 char *name = strrchr(argv[0], '/');
393 393 fprintf((is_error) ? stderr : stdout, "Usage: %s OPTIONS\n", (name ? name + 1 : argv[0]));
394 name = strrchr(argv[0], '/'); 394 fprintf((is_error) ? stderr : stdout,
395 printf("Usage: %s OPTIONS\n", (name ? name + 1 : argv[0])); 395 "\n"
396 printf("\n"); 396 "Manage apps on iOS devices.\n"
397 printf("Manage apps on iOS devices.\n"); 397 "\n"
398 printf("\n"); 398 "COMMANDS:\n"
399 printf( 399 " list List installed apps\n"
400 "OPTIONS:\n" 400 " -b, --bundle-identifier Only query for given bundle identifier\n"
401 " -u, --udid UDID\tTarget specific device by UDID.\n" 401 " (can be passed multiple times)\n"
402 " -n, --network\t\tConnect to network device.\n" 402 " -o list_user list user apps only (this is the default)\n"
403 " -l, --list-apps\tList apps, possible options:\n" 403 " -o list_system list system apps only\n"
404 " -b, --bundle-identifier\tOnly query for given bundle identifier\n" 404 " -o list_all list all types of apps\n"
405 " (can be passed multiple times)\n" 405 " -o xml print full output as xml plist\n"
406 " -o list_user\t- list user apps only (this is the default)\n" 406 " install PATH Install app from package file specified by PATH.\n"
407 " -o list_system\t- list system apps only\n" 407 " PATH can also be a .ipcc file for carrier bundles.\n"
408 " -o list_all\t- list all types of apps\n" 408 " uninstall BUNDLEID Uninstall app specified by BUNDLEID.\n"
409 " -o xml\t\t- print full output as xml plist\n" 409 " upgrade PATH Upgrade app from package file specified by PATH.\n"
410 " -i, --install ARCHIVE\tInstall app from package file specified by ARCHIVE.\n" 410 " list-archives List archived apps\n"
411 " \tARCHIVE can also be a .ipcc file for carrier bundles.\n" 411 " -o xml print full output as xml plist\n"
412 " -U, --uninstall APPID\tUninstall app specified by APPID.\n" 412 " archive BUNDLEID Archive app specified by BUNDLEID, possible options:\n"
413 " -g, --upgrade ARCHIVE\tUpgrade app from package file specified by ARCHIVE.\n" 413 " -o uninstall uninstall the package after making an archive\n"
414 " -L, --list-archives\tList archived applications, possible options:\n" 414 " -o app_only archive application data only\n"
415 " -o xml\t\t- print full output as xml plist\n" 415 " -o docs_only archive documents (user data) only\n"
416 " -a, --archive APPID\tArchive app specified by APPID, possible options:\n" 416 " -o copy=PATH copy the app archive to directory PATH when done\n"
417 " -o uninstall\t- uninstall the package after making an archive\n" 417 " -o remove only valid when copy=PATH is used: remove after copy\n"
418 " -o app_only\t- archive application data only\n" 418 " restore BUNDLEID Restore archived app specified by BUNDLEID\n"
419 " -o docs_only\t- archive documents (user data) only\n" 419 " remove-archive BUNDLEID Remove app archive specified by BUNDLEID\n"
420 " -o copy=PATH\t- copy the app archive to directory PATH when done\n" 420 "\n"
421 " -o remove\t- only valid when copy=PATH is used: remove after copy\n" 421 "OPTIONS:\n"
422 " -r, --restore APPID\tRestore archived app specified by APPID\n" 422 " -u, --udid UDID Target specific device by UDID\n"
423 " -R, --remove-archive APPID Remove app archive specified by APPID\n" 423 " -n, --network Connect to network device\n"
424 " -o, --options\t\tPass additional options to the specified command.\n" 424 " -w, --notify-wait Wait for app installed/uninstalled notification\n"
425 " -w, --notify-wait\t\tWait for app installed/uninstalled notification\n" 425 " to before reporting success of operation\n"
426 " \t\tto before reporting success of operation\n" 426 " -h, --help Print usage information\n"
427 " -h, --help\t\tprints usage information\n" 427 " -d, --debug Enable communication debugging\n"
428 " -d, --debug\t\tenable communication debugging\n" 428 " -v, --version Print version information\n"
429 " -v, --version\t\tprint version information\n" 429 "\n"
430 "\n" 430 "Homepage: <" PACKAGE_URL ">\n"
431 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
431 ); 432 );
432 printf("Homepage: <" PACKAGE_URL ">\n");
433 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
434} 433}
435 434
436static void parse_opts(int argc, char **argv) 435static void parse_opts(int argc, char **argv)
@@ -439,14 +438,6 @@ static void parse_opts(int argc, char **argv)
439 { "help", no_argument, NULL, 'h' }, 438 { "help", no_argument, NULL, 'h' },
440 { "udid", required_argument, NULL, 'u' }, 439 { "udid", required_argument, NULL, 'u' },
441 { "network", no_argument, NULL, 'n' }, 440 { "network", no_argument, NULL, 'n' },
442 { "list-apps", no_argument, NULL, 'l' },
443 { "install", required_argument, NULL, 'i' },
444 { "uninstall", required_argument, NULL, 'U' },
445 { "upgrade", required_argument, NULL, 'g' },
446 { "list-archives", no_argument, NULL, 'L' },
447 { "archive", required_argument, NULL, 'a' },
448 { "restore", required_argument, NULL, 'r' },
449 { "remove-archive", required_argument, NULL, 'R' },
450 { "options", required_argument, NULL, 'o' }, 441 { "options", required_argument, NULL, 'o' },
451 { "notify-wait", no_argument, NULL, 'w' }, 442 { "notify-wait", no_argument, NULL, 'w' },
452 { "debug", no_argument, NULL, 'd' }, 443 { "debug", no_argument, NULL, 'd' },
@@ -457,39 +448,19 @@ static void parse_opts(int argc, char **argv)
457 int c; 448 int c;
458 449
459 while (1) { 450 while (1) {
460 c = getopt_long(argc, argv, "hU:li:u:g:La:r:R:o:nwdvb:", longopts, 451 c = getopt_long(argc, argv, "hu:o:nwdvb:", longopts, (int*)0);
461 (int *) 0);
462 if (c == -1) { 452 if (c == -1) {
463 break; 453 break;
464 } 454 }
465 455
466 /* verify if multiple modes have been supplied */
467 switch (c) {
468 case 'l':
469 case 'i':
470 case 'g':
471 case 'L':
472 case 'a':
473 case 'r':
474 case 'R':
475 if (cmd != CMD_NONE) {
476 printf("ERROR: A mode has already been supplied. Multiple modes are not supported.\n");
477 print_usage(argc, argv);
478 exit(2);
479 }
480 break;
481 default:
482 break;
483 }
484
485 switch (c) { 456 switch (c) {
486 case 'h': 457 case 'h':
487 print_usage(argc, argv); 458 print_usage(argc, argv, 0);
488 exit(0); 459 exit(0);
489 case 'u': 460 case 'u':
490 if (!*optarg) { 461 if (!*optarg) {
491 printf("ERROR: UDID must not be empty!\n"); 462 printf("ERROR: UDID must not be empty!\n");
492 print_usage(argc, argv); 463 print_usage(argc, argv, 1);
493 exit(2); 464 exit(2);
494 } 465 }
495 udid = strdup(optarg); 466 udid = strdup(optarg);
@@ -500,7 +471,7 @@ static void parse_opts(int argc, char **argv)
500 case 'b': 471 case 'b':
501 if (!*optarg) { 472 if (!*optarg) {
502 printf("ERROR: bundle identifier must not be empty!\n"); 473 printf("ERROR: bundle identifier must not be empty!\n");
503 print_usage(argc, argv); 474 print_usage(argc, argv, 1);
504 exit(2); 475 exit(2);
505 } 476 }
506 if (bundle_ids == NULL) { 477 if (bundle_ids == NULL) {
@@ -508,36 +479,6 @@ static void parse_opts(int argc, char **argv)
508 } 479 }
509 plist_array_append_item(bundle_ids, plist_new_string(optarg)); 480 plist_array_append_item(bundle_ids, plist_new_string(optarg));
510 break; 481 break;
511 case 'l':
512 cmd = CMD_LIST_APPS;
513 break;
514 case 'i':
515 cmd = CMD_INSTALL;
516 appid = strdup(optarg);
517 break;
518 case 'U':
519 cmd = CMD_UNINSTALL;
520 appid = strdup(optarg);
521 break;
522 case 'g':
523 cmd = CMD_UPGRADE;
524 appid = strdup(optarg);
525 break;
526 case 'L':
527 cmd = CMD_LIST_ARCHIVES;
528 break;
529 case 'a':
530 cmd = CMD_ARCHIVE;
531 appid = strdup(optarg);
532 break;
533 case 'r':
534 cmd = CMD_RESTORE;
535 appid = strdup(optarg);
536 break;
537 case 'R':
538 cmd = CMD_REMOVE_ARCHIVE;
539 appid = strdup(optarg);
540 break;
541 case 'o': 482 case 'o':
542 if (!options) { 483 if (!options) {
543 options = strdup(optarg); 484 options = strdup(optarg);
@@ -560,19 +501,69 @@ static void parse_opts(int argc, char **argv)
560 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); 501 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
561 exit(0); 502 exit(0);
562 default: 503 default:
563 print_usage(argc, argv); 504 print_usage(argc, argv, 1);
564 exit(2); 505 exit(2);
565 } 506 }
566 } 507 }
567 508
568 if (cmd == CMD_NONE) { 509 argv += optind;
569 printf("ERROR: No mode/command was supplied.\n"); 510 argc -= optind;
570 }
571 511
572 if (cmd == CMD_NONE || optind <= 1 || (argc - optind > 0)) { 512 if (argc == 0) {
573 print_usage(argc, argv); 513 fprintf(stderr, "ERROR: Missing command.\n\n");
514 print_usage(argc+optind, argv-optind, 1);
574 exit(2); 515 exit(2);
575 } 516 }
517
518 char *cmdstr = argv[0];
519
520 if (!strcmp(cmdstr, "list")) {
521 cmd = CMD_LIST_APPS;
522 } else if (!strcmp(cmdstr, "install")) {
523 cmd = CMD_INSTALL;
524 } else if (!strcmp(cmdstr, "upgrade")) {
525 cmd = CMD_UPGRADE;
526 } else if (!strcmp(cmdstr, "uninstall") || !strcmp(cmdstr, "remove")) {
527 cmd = CMD_UNINSTALL;
528 } else if (!strcmp(cmdstr, "archives") || !strcmp(cmdstr, "list-archives")) {
529 cmd = CMD_LIST_ARCHIVES;
530 } else if (!strcmp(cmdstr, "archive")) {
531 cmd = CMD_ARCHIVE;
532 } else if (!strcmp(cmdstr, "restore")) {
533 cmd = CMD_RESTORE;
534 } else if (!strcmp(cmdstr, "remove-archive")) {
535 cmd = CMD_REMOVE_ARCHIVE;
536 }
537
538 switch (cmd) {
539 case CMD_LIST_APPS:
540 case CMD_LIST_ARCHIVES:
541 break;
542 case CMD_INSTALL:
543 case CMD_UPGRADE:
544 if (argc < 2) {
545 fprintf(stderr, "ERROR: Missing filename for '%s' command.\n\n", cmdstr);
546 print_usage(argc+optind, argv-optind, 1);
547 exit(2);
548 }
549 cmdarg = argv[1];
550 break;
551 case CMD_UNINSTALL:
552 case CMD_ARCHIVE:
553 case CMD_RESTORE:
554 case CMD_REMOVE_ARCHIVE:
555 if (argc < 2) {
556 fprintf(stderr, "ERROR: Missing bundle ID for '%s' command.\n\n", cmdstr);
557 print_usage(argc+optind, argv-optind, 1);
558 exit(2);
559 }
560 cmdarg = argv[1];
561 break;
562 default:
563 fprintf(stderr, "ERROR: Invalid command '%s'.\n\n", cmdstr);
564 print_usage(argc+optind, argv-optind, 1);
565 exit(2);
566 }
576} 567}
577 568
578static int afc_upload_file(afc_client_t afc, const char* filename, const char* dstfn) 569static int afc_upload_file(afc_client_t afc, const char* filename, const char* dstfn)
@@ -583,7 +574,7 @@ static int afc_upload_file(afc_client_t afc, const char* filename, const char* d
583 574
584 f = fopen(filename, "rb"); 575 f = fopen(filename, "rb");
585 if (!f) { 576 if (!f) {
586 fprintf(stderr, "fopen: %s: %s\n", appid, strerror(errno)); 577 fprintf(stderr, "fopen: %s: %s\n", filename, strerror(errno));
587 return -1; 578 return -1;
588 } 579 }
589 580
@@ -866,8 +857,8 @@ run_again:
866 goto leave_cleanup; 857 goto leave_cleanup;
867 } 858 }
868 859
869 if (stat(appid, &fst) != 0) { 860 if (stat(cmdarg, &fst) != 0) {
870 fprintf(stderr, "ERROR: stat: %s: %s\n", appid, strerror(errno)); 861 fprintf(stderr, "ERROR: stat: %s: %s\n", cmdarg, strerror(errno));
871 goto leave_cleanup; 862 goto leave_cleanup;
872 } 863 }
873 864
@@ -892,14 +883,14 @@ run_again:
892 int errp = 0; 883 int errp = 0;
893 struct zip *zf = NULL; 884 struct zip *zf = NULL;
894 885
895 if ((strlen(appid) > 5) && (strcmp(&appid[strlen(appid)-5], ".ipcc") == 0)) { 886 if ((strlen(cmdarg) > 5) && (strcmp(&cmdarg[strlen(cmdarg)-5], ".ipcc") == 0)) {
896 zf = zip_open(appid, 0, &errp); 887 zf = zip_open(cmdarg, 0, &errp);
897 if (!zf) { 888 if (!zf) {
898 fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp); 889 fprintf(stderr, "ERROR: zip_open: %s: %d\n", cmdarg, errp);
899 goto leave_cleanup; 890 goto leave_cleanup;
900 } 891 }
901 892
902 char* ipcc = strdup(appid); 893 char* ipcc = strdup(cmdarg);
903 if ((asprintf(&pkgname, "%s/%s", PKG_PATH, basename(ipcc)) > 0) && pkgname) { 894 if ((asprintf(&pkgname, "%s/%s", PKG_PATH, basename(ipcc)) > 0) && pkgname) {
904 afc_make_directory(afc, pkgname); 895 afc_make_directory(afc, pkgname);
905 } 896 }
@@ -989,20 +980,20 @@ run_again:
989 /* upload developer app directory */ 980 /* upload developer app directory */
990 instproxy_client_options_add(client_opts, "PackageType", "Developer", NULL); 981 instproxy_client_options_add(client_opts, "PackageType", "Developer", NULL);
991 982
992 if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) { 983 if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(cmdarg)) < 0) {
993 fprintf(stderr, "ERROR: Out of memory allocating pkgname!?\n"); 984 fprintf(stderr, "ERROR: Out of memory allocating pkgname!?\n");
994 goto leave_cleanup; 985 goto leave_cleanup;
995 } 986 }
996 987
997 printf("Uploading %s package contents... ", basename(appid)); 988 printf("Uploading %s package contents... ", basename(cmdarg));
998 afc_upload_dir(afc, appid, pkgname); 989 afc_upload_dir(afc, cmdarg, pkgname);
999 printf("DONE.\n"); 990 printf("DONE.\n");
1000 991
1001 /* extract the CFBundleIdentifier from the package */ 992 /* extract the CFBundleIdentifier from the package */
1002 993
1003 /* construct full filename to Info.plist */ 994 /* construct full filename to Info.plist */
1004 char *filename = (char*)malloc(strlen(appid)+11+1); 995 char *filename = (char*)malloc(strlen(cmdarg)+11+1);
1005 strcpy(filename, appid); 996 strcpy(filename, cmdarg);
1006 strcat(filename, "/Info.plist"); 997 strcat(filename, "/Info.plist");
1007 998
1008 struct stat st; 999 struct stat st;
@@ -1044,9 +1035,9 @@ run_again:
1044 plist_free(info); 1035 plist_free(info);
1045 info = NULL; 1036 info = NULL;
1046 } else { 1037 } else {
1047 zf = zip_open(appid, 0, &errp); 1038 zf = zip_open(cmdarg, 0, &errp);
1048 if (!zf) { 1039 if (!zf) {
1049 fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp); 1040 fprintf(stderr, "ERROR: zip_open: %s: %d\n", cmdarg, errp);
1050 goto leave_cleanup; 1041 goto leave_cleanup;
1051 } 1042 }
1052 1043
@@ -1153,9 +1144,9 @@ run_again:
1153 goto leave_cleanup; 1144 goto leave_cleanup;
1154 } 1145 }
1155 1146
1156 printf("Copying '%s' to device... ", appid); 1147 printf("Copying '%s' to device... ", cmdarg);
1157 1148
1158 if (afc_upload_file(afc, appid, pkgname) < 0) { 1149 if (afc_upload_file(afc, cmdarg, pkgname) < 0) {
1159 printf("FAILED\n"); 1150 printf("FAILED\n");
1160 free(pkgname); 1151 free(pkgname);
1161 goto leave_cleanup; 1152 goto leave_cleanup;
@@ -1191,8 +1182,8 @@ run_again:
1191 wait_for_command_complete = 1; 1182 wait_for_command_complete = 1;
1192 notification_expected = 1; 1183 notification_expected = 1;
1193 } else if (cmd == CMD_UNINSTALL) { 1184 } else if (cmd == CMD_UNINSTALL) {
1194 printf("Uninstalling '%s'\n", appid); 1185 printf("Uninstalling '%s'\n", cmdarg);
1195 instproxy_uninstall(ipc, appid, NULL, status_cb, NULL); 1186 instproxy_uninstall(ipc, cmdarg, NULL, status_cb, NULL);
1196 wait_for_command_complete = 1; 1187 wait_for_command_complete = 1;
1197 notification_expected = 0; 1188 notification_expected = 0;
1198 } else if (cmd == CMD_LIST_ARCHIVES) { 1189 } else if (cmd == CMD_LIST_ARCHIVES) {
@@ -1353,7 +1344,7 @@ run_again:
1353 } 1344 }
1354 } 1345 }
1355 1346
1356 instproxy_archive(ipc, appid, client_opts, status_cb, NULL); 1347 instproxy_archive(ipc, cmdarg, client_opts, status_cb, NULL);
1357 1348
1358 instproxy_client_options_free(client_opts); 1349 instproxy_client_options_free(client_opts);
1359 wait_for_command_complete = 1; 1350 wait_for_command_complete = 1;
@@ -1375,7 +1366,7 @@ run_again:
1375 uint64_t af = 0; 1366 uint64_t af = 0;
1376 /* local filename */ 1367 /* local filename */
1377 char *localfile = NULL; 1368 char *localfile = NULL;
1378 if (asprintf(&localfile, "%s/%s.ipa", copy_path, appid) < 0) { 1369 if (asprintf(&localfile, "%s/%s.ipa", copy_path, cmdarg) < 0) {
1379 fprintf(stderr, "Out of memory!?\n"); 1370 fprintf(stderr, "Out of memory!?\n");
1380 goto leave_cleanup; 1371 goto leave_cleanup;
1381 } 1372 }
@@ -1390,7 +1381,7 @@ run_again:
1390 1381
1391 /* remote filename */ 1382 /* remote filename */
1392 char *remotefile = NULL; 1383 char *remotefile = NULL;
1393 if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, appid) < 0) { 1384 if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, cmdarg) < 0) {
1394 fprintf(stderr, "Out of memory!?\n"); 1385 fprintf(stderr, "Out of memory!?\n");
1395 goto leave_cleanup; 1386 goto leave_cleanup;
1396 } 1387 }
@@ -1475,7 +1466,7 @@ run_again:
1475 1466
1476 if (remove_after_copy) { 1467 if (remove_after_copy) {
1477 /* remove archive if requested */ 1468 /* remove archive if requested */
1478 printf("Removing '%s'\n", appid); 1469 printf("Removing '%s'\n", cmdarg);
1479 cmd = CMD_REMOVE_ARCHIVE; 1470 cmd = CMD_REMOVE_ARCHIVE;
1480 free(options); 1471 free(options);
1481 options = NULL; 1472 options = NULL;
@@ -1488,11 +1479,11 @@ run_again:
1488 } 1479 }
1489 goto leave_cleanup; 1480 goto leave_cleanup;
1490 } else if (cmd == CMD_RESTORE) { 1481 } else if (cmd == CMD_RESTORE) {
1491 instproxy_restore(ipc, appid, NULL, status_cb, NULL); 1482 instproxy_restore(ipc, cmdarg, NULL, status_cb, NULL);
1492 wait_for_command_complete = 1; 1483 wait_for_command_complete = 1;
1493 notification_expected = 1; 1484 notification_expected = 1;
1494 } else if (cmd == CMD_REMOVE_ARCHIVE) { 1485 } else if (cmd == CMD_REMOVE_ARCHIVE) {
1495 instproxy_remove_archive(ipc, appid, NULL, status_cb, NULL); 1486 instproxy_remove_archive(ipc, cmdarg, NULL, status_cb, NULL);
1496 wait_for_command_complete = 1; 1487 wait_for_command_complete = 1;
1497 } else { 1488 } else {
1498 printf("ERROR: no command selected?! This should not be reached!\n"); 1489 printf("ERROR: no command selected?! This should not be reached!\n");
@@ -1515,7 +1506,6 @@ leave_cleanup:
1515 idevice_free(device); 1506 idevice_free(device);
1516 1507
1517 free(udid); 1508 free(udid);
1518 free(appid);
1519 free(options); 1509 free(options);
1520 free(bundleidentifier); 1510 free(bundleidentifier);
1521 plist_free(bundle_ids); 1511 plist_free(bundle_ids);