summaryrefslogtreecommitdiffstats
path: root/src/ideviceinstaller.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ideviceinstaller.c')
-rw-r--r--src/ideviceinstaller.c189
1 files changed, 83 insertions, 106 deletions
diff --git a/src/ideviceinstaller.c b/src/ideviceinstaller.c
index ecf63c6..dfa3e13 100644
--- a/src/ideviceinstaller.c
+++ b/src/ideviceinstaller.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * ideviceinstaller - Manage apps on iOS devices. 2 * ideviceinstaller - Manage apps on iOS devices.
3 * 3 *
4 * Copyright (C) 2010-2019 Nikias Bassen <nikias@gmx.li> 4 * Copyright (C) 2010-2023 Nikias Bassen <nikias@gmx.li>
5 * Copyright (C) 2010-2015 Martin Szulecki <m.szulecki@libimobiledevice.org> 5 * Copyright (C) 2010-2015 Martin Szulecki <m.szulecki@libimobiledevice.org>
6 * 6 *
7 * Licensed under the GNU General Public License Version 2 7 * Licensed under the GNU General Public License Version 2
@@ -93,7 +93,6 @@ const char PKG_PATH[] = "PublicStaging";
93const char APPARCH_PATH[] = "ApplicationArchives"; 93const char APPARCH_PATH[] = "ApplicationArchives";
94 94
95char *udid = NULL; 95char *udid = NULL;
96char *options = NULL;
97char *cmdarg = NULL; 96char *cmdarg = NULL;
98 97
99enum cmd_mode { 98enum cmd_mode {
@@ -122,7 +121,14 @@ int err_occurred = 0;
122int notified = 0; 121int notified = 0;
123plist_t bundle_ids = NULL; 122plist_t bundle_ids = NULL;
124plist_t return_attrs = NULL; 123plist_t return_attrs = NULL;
125int return_attrs_have_CFBundleIdentifier = 0; 124int xml_mode = 0;
125int opt_list_user = 0;
126int opt_list_system = 0;
127char *copy_path = NULL;
128int remove_after_copy = 0;
129int skip_uninstall = 1;
130int app_only = 0;
131int docs_only = 0;
126 132
127static void print_apps_header() 133static void print_apps_header()
128{ 134{
@@ -414,30 +420,30 @@ static void print_usage(int argc, char **argv, int is_error)
414 "Manage apps on iOS devices.\n" 420 "Manage apps on iOS devices.\n"
415 "\n" 421 "\n"
416 "COMMANDS:\n" 422 "COMMANDS:\n"
417 " list List installed apps\n" 423 " list List installed apps. Options:\n"
424 " --user List user apps only (this is the default)\n"
425 " --system List system apps only\n"
426 " --all List all types of apps\n"
427 " --xml Print output as XML Property List\n"
418 " -a, --attribute ATTR Specify attribute to return - see man page\n" 428 " -a, --attribute ATTR Specify attribute to return - see man page\n"
419 " (can be passed multiple times)\n" 429 " (can be passed multiple times)\n"
420 " -b, --bundle-identifier BUNDLEID Only query given bundle identifier\n" 430 " -b, --bundle-identifier BUNDLEID Only query given bundle identifier\n"
421 " (can be passed multiple times)\n" 431 " (can be passed multiple times)\n"
422 " -o list_user list user apps only (this is the default)\n"
423 " -o list_system list system apps only\n"
424 " -o list_all list all types of apps\n"
425 " -o xml print full output as xml plist\n"
426 " install PATH Install app from package file specified by PATH.\n" 432 " install PATH Install app from package file specified by PATH.\n"
427 " PATH can also be a .ipcc file for carrier bundles.\n" 433 " PATH can also be a .ipcc file for carrier bundles.\n"
428 " uninstall BUNDLEID Uninstall app specified by BUNDLEID.\n" 434 " uninstall BUNDLEID Uninstall app specified by BUNDLEID.\n"
429 " upgrade PATH Upgrade app from package file specified by PATH.\n" 435 " upgrade PATH Upgrade app from package file specified by PATH.\n"
430 "\n" 436 "\n"
431 "LEGACY COMMANDS (non-functional with iOS 7 or later):\n" 437 "LEGACY COMMANDS (non-functional with iOS 7 or later):\n"
432 " archive BUNDLEID Archive app specified by BUNDLEID, possible options:\n" 438 " archive BUNDLEID Archive app specified by BUNDLEID. Options:\n"
433 " -o uninstall uninstall the package after making an archive\n" 439 " --uninstall Uninstall the package after making an archive\n"
434 " -o app_only archive application data only\n" 440 " --app-only Archive application data only\n"
435 " -o docs_only archive documents (user data) only\n" 441 " --docs-only Archive documents (user data) only\n"
436 " -o copy=PATH copy the app archive to directory PATH when done\n" 442 " --copy=PATH Copy the app archive to directory PATH when done\n"
437 " -o remove only valid when copy=PATH is used: remove after copy\n" 443 " --remove Only valid when copy=PATH is used: remove after copy\n"
438 " restore BUNDLEID Restore archived app specified by BUNDLEID\n" 444 " restore BUNDLEID Restore archived app specified by BUNDLEID\n"
439 " list-archives List archived apps\n" 445 " list-archives List archived apps. Options:\n"
440 " -o xml print full output as xml plist\n" 446 " --xml Print output as XML Property List\n"
441 " remove-archive BUNDLEID Remove app archive specified by BUNDLEID\n" 447 " remove-archive BUNDLEID Remove app archive specified by BUNDLEID\n"
442 "\n" 448 "\n"
443 "OPTIONS:\n" 449 "OPTIONS:\n"
@@ -454,24 +460,44 @@ static void print_usage(int argc, char **argv, int is_error)
454 ); 460 );
455} 461}
456 462
463enum numerical_opts {
464 LIST_USER = 1,
465 LIST_SYSTEM,
466 LIST_ALL,
467 ARCHIVE_UNINSTALL,
468 ARCHIVE_APP_ONLY,
469 ARCHIVE_DOCS_ONLY,
470 ARCHIVE_COPY_PATH,
471 ARCHIVE_COPY_REMOVE,
472 OUTPUT_XML
473};
474
457static void parse_opts(int argc, char **argv) 475static void parse_opts(int argc, char **argv)
458{ 476{
459 static struct option longopts[] = { 477 static struct option longopts[] = {
460 { "help", no_argument, NULL, 'h' }, 478 { "help", no_argument, NULL, 'h' },
461 { "udid", required_argument, NULL, 'u' }, 479 { "udid", required_argument, NULL, 'u' },
462 { "network", no_argument, NULL, 'n' }, 480 { "network", no_argument, NULL, 'n' },
463 { "options", required_argument, NULL, 'o' },
464 { "notify-wait", no_argument, NULL, 'w' }, 481 { "notify-wait", no_argument, NULL, 'w' },
465 { "debug", no_argument, NULL, 'd' }, 482 { "debug", no_argument, NULL, 'd' },
466 { "version", no_argument, NULL, 'v' }, 483 { "version", no_argument, NULL, 'v' },
467 { "bundle-identifier", required_argument, NULL, 'b' }, 484 { "bundle-identifier", required_argument, NULL, 'b' },
468 { "attribute", required_argument, NULL, 'a' }, 485 { "attribute", required_argument, NULL, 'a' },
486 { "user", no_argument, NULL, LIST_USER },
487 { "system", no_argument, NULL, LIST_SYSTEM },
488 { "all", no_argument, NULL, LIST_ALL },
489 { "xml", no_argument, NULL, OUTPUT_XML },
490 { "uninstall", no_argument, NULL, ARCHIVE_UNINSTALL },
491 { "app-only", no_argument, NULL, ARCHIVE_APP_ONLY },
492 { "docs-only", no_argument, NULL, ARCHIVE_DOCS_ONLY },
493 { "copy", required_argument, NULL, ARCHIVE_COPY_PATH },
494 { "remove", no_argument, NULL, ARCHIVE_COPY_REMOVE },
469 { NULL, 0, NULL, 0 } 495 { NULL, 0, NULL, 0 }
470 }; 496 };
471 int c; 497 int c;
472 498
473 while (1) { 499 while (1) {
474 c = getopt_long(argc, argv, "hu:o:nwdvb:a:", longopts, (int*)0); 500 c = getopt_long(argc, argv, "hu:nwdvb:a:", longopts, (int*)0);
475 if (c == -1) { 501 if (c == -1) {
476 break; 502 break;
477 } 503 }
@@ -501,9 +527,6 @@ static void parse_opts(int argc, char **argv)
501 return_attrs = plist_new_array(); 527 return_attrs = plist_new_array();
502 } 528 }
503 plist_array_append_item(return_attrs, plist_new_string(optarg)); 529 plist_array_append_item(return_attrs, plist_new_string(optarg));
504 if (!strcmp(optarg, "CFBundleIdentifier")) {
505 return_attrs_have_CFBundleIdentifier = 1;
506 }
507 break; 530 break;
508 case 'b': 531 case 'b':
509 if (!*optarg) { 532 if (!*optarg) {
@@ -516,18 +539,6 @@ static void parse_opts(int argc, char **argv)
516 } 539 }
517 plist_array_append_item(bundle_ids, plist_new_string(optarg)); 540 plist_array_append_item(bundle_ids, plist_new_string(optarg));
518 break; 541 break;
519 case 'o':
520 if (!options) {
521 options = strdup(optarg);
522 } else {
523 char *newopts = malloc(strlen(options) + strlen(optarg) + 2);
524 strcpy(newopts, options);
525 free(options);
526 strcat(newopts, ",");
527 strcat(newopts, optarg);
528 options = newopts;
529 }
530 break;
531 case 'w': 542 case 'w':
532 use_notifier = 1; 543 use_notifier = 1;
533 break; 544 break;
@@ -537,6 +548,36 @@ static void parse_opts(int argc, char **argv)
537 case 'v': 548 case 'v':
538 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); 549 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
539 exit(0); 550 exit(0);
551 case LIST_USER:
552 opt_list_user = 1;
553 break;
554 case LIST_SYSTEM:
555 opt_list_system = 1;
556 break;
557 case LIST_ALL:
558 opt_list_user = 1;
559 opt_list_system = 1;
560 break;
561 case OUTPUT_XML:
562 xml_mode = 1;
563 break;
564 case ARCHIVE_UNINSTALL:
565 skip_uninstall = 0;
566 break;
567 case ARCHIVE_APP_ONLY:
568 app_only = 1;
569 docs_only = 0;
570 break;
571 case ARCHIVE_DOCS_ONLY:
572 docs_only = 1;
573 app_only = 0;
574 break;
575 case ARCHIVE_COPY_PATH:
576 copy_path = strdup(optarg);
577 break;
578 case ARCHIVE_COPY_REMOVE:
579 remove_after_copy = 1;
580 break;
540 default: 581 default:
541 print_usage(argc, argv, 1); 582 print_usage(argc, argv, 1);
542 exit(2); 583 exit(2);
@@ -795,46 +836,30 @@ run_again:
795 notification_expected = 0; 836 notification_expected = 0;
796 837
797 if (cmd == CMD_LIST_APPS) { 838 if (cmd == CMD_LIST_APPS) {
798 int xml_mode = 0;
799 plist_t client_opts = instproxy_client_options_new(); 839 plist_t client_opts = instproxy_client_options_new();
800 instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL); 840 instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
801 plist_t apps = NULL; 841 plist_t apps = NULL;
802 842
803 /* look for options */ 843 if (opt_list_system && opt_list_user) {
804 if (options) { 844 plist_dict_remove_item(client_opts, "ApplicationType");
805 char *opts = strdup(options); 845 } else if (opt_list_system) {
806 char *elem = strtok(opts, ","); 846 instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL);
807 while (elem) { 847 } else if (opt_list_user) {
808 if (!strcmp(elem, "list_system")) { 848 instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
809 instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL);
810 } else if (!strcmp(elem, "list_all")) {
811 plist_dict_remove_item(client_opts, "ApplicationType");
812 } else if (!strcmp(elem, "list_user")) {
813 /* do nothing, we're already set */
814 } else if (!strcmp(elem, "xml")) {
815 xml_mode = 1;
816 }
817 elem = strtok(NULL, ",");
818 }
819 free(opts);
820 } 849 }
821 850
822 if (bundle_ids) { 851 if (bundle_ids) {
823 plist_dict_set_item(client_opts, "BundleIDs", plist_copy(bundle_ids)); 852 plist_dict_set_item(client_opts, "BundleIDs", plist_copy(bundle_ids));
824 } 853 }
825 854
826 if (!xml_mode && !return_attrs) { 855 if (!xml_mode) {
827 return_attrs = plist_new_array(); 856 return_attrs = plist_new_array();
828 plist_array_append_item(return_attrs, plist_new_string("CFBundleIdentifier")); 857 plist_array_append_item(return_attrs, plist_new_string("CFBundleIdentifier"));
829 return_attrs_have_CFBundleIdentifier = 1;
830 plist_array_append_item(return_attrs, plist_new_string("CFBundleShortVersionString")); 858 plist_array_append_item(return_attrs, plist_new_string("CFBundleShortVersionString"));
831 plist_array_append_item(return_attrs, plist_new_string("CFBundleDisplayName")); 859 plist_array_append_item(return_attrs, plist_new_string("CFBundleDisplayName"));
832 } 860 }
833 861
834 if (return_attrs) { 862 if (return_attrs) {
835 if (!return_attrs_have_CFBundleIdentifier) {
836 plist_array_insert_item(return_attrs, plist_new_string("CFBundleIdentifier"), 0);
837 }
838 instproxy_client_options_add(client_opts, "ReturnAttributes", return_attrs, NULL); 863 instproxy_client_options_add(client_opts, "ReturnAttributes", return_attrs, NULL);
839 } 864 }
840 865
@@ -845,7 +870,6 @@ run_again:
845 fprintf(stderr, "ERROR: instproxy_browse returnd an invalid plist!\n"); 870 fprintf(stderr, "ERROR: instproxy_browse returnd an invalid plist!\n");
846 goto leave_cleanup; 871 goto leave_cleanup;
847 } 872 }
848
849 char *xml = NULL; 873 char *xml = NULL;
850 uint32_t len = 0; 874 uint32_t len = 0;
851 875
@@ -1228,21 +1252,8 @@ run_again:
1228 wait_for_command_complete = 1; 1252 wait_for_command_complete = 1;
1229 notification_expected = 0; 1253 notification_expected = 0;
1230 } else if (cmd == CMD_LIST_ARCHIVES) { 1254 } else if (cmd == CMD_LIST_ARCHIVES) {
1231 int xml_mode = 0;
1232 plist_t dict = NULL; 1255 plist_t dict = NULL;
1233 1256
1234 /* look for options */
1235 if (options) {
1236 char *opts = strdup(options);
1237 char *elem = strtok(opts, ",");
1238 while (elem) {
1239 if (!strcmp(elem, "xml")) {
1240 xml_mode = 1;
1241 }
1242 elem = strtok(NULL, ",");
1243 }
1244 }
1245
1246 err = instproxy_lookup_archives(ipc, NULL, &dict); 1257 err = instproxy_lookup_archives(ipc, NULL, &dict);
1247 if (err != INSTPROXY_E_SUCCESS) { 1258 if (err != INSTPROXY_E_SUCCESS) {
1248 fprintf(stderr, "ERROR: lookup_archives returned %d\n", err); 1259 fprintf(stderr, "ERROR: lookup_archives returned %d\n", err);
@@ -1257,7 +1268,6 @@ run_again:
1257 if (xml_mode) { 1268 if (xml_mode) {
1258 char *xml = NULL; 1269 char *xml = NULL;
1259 uint32_t len = 0; 1270 uint32_t len = 0;
1260
1261 plist_to_xml(dict, &xml, &len); 1271 plist_to_xml(dict, &xml, &len);
1262 if (xml) { 1272 if (xml) {
1263 puts(xml); 1273 puts(xml);
@@ -1310,35 +1320,8 @@ run_again:
1310 while (node); 1320 while (node);
1311 plist_free(dict); 1321 plist_free(dict);
1312 } else if (cmd == CMD_ARCHIVE) { 1322 } else if (cmd == CMD_ARCHIVE) {
1313 char *copy_path = NULL;
1314 int remove_after_copy = 0;
1315 int skip_uninstall = 1;
1316 int app_only = 0;
1317 int docs_only = 0;
1318 plist_t client_opts = NULL; 1323 plist_t client_opts = NULL;
1319 1324
1320 /* look for options */
1321 if (options) {
1322 char *opts = strdup(options);
1323 char *elem = strtok(opts, ",");
1324 while (elem) {
1325 if (!strcmp(elem, "uninstall")) {
1326 skip_uninstall = 0;
1327 } else if (!strcmp(elem, "app_only")) {
1328 app_only = 1;
1329 docs_only = 0;
1330 } else if (!strcmp(elem, "docs_only")) {
1331 docs_only = 1;
1332 app_only = 0;
1333 } else if ((strlen(elem) > 5) && !strncmp(elem, "copy=", 5)) {
1334 copy_path = strdup(elem+5);
1335 } else if (!strcmp(elem, "remove")) {
1336 remove_after_copy = 1;
1337 }
1338 elem = strtok(NULL, ",");
1339 }
1340 }
1341
1342 if (skip_uninstall || app_only || docs_only) { 1325 if (skip_uninstall || app_only || docs_only) {
1343 client_opts = instproxy_client_options_new(); 1326 client_opts = instproxy_client_options_new();
1344 if (skip_uninstall) { 1327 if (skip_uninstall) {
@@ -1355,13 +1338,11 @@ run_again:
1355 struct stat fst; 1338 struct stat fst;
1356 if (stat(copy_path, &fst) != 0) { 1339 if (stat(copy_path, &fst) != 0) {
1357 fprintf(stderr, "ERROR: stat: %s: %s\n", copy_path, strerror(errno)); 1340 fprintf(stderr, "ERROR: stat: %s: %s\n", copy_path, strerror(errno));
1358 free(copy_path);
1359 goto leave_cleanup; 1341 goto leave_cleanup;
1360 } 1342 }
1361 1343
1362 if (!S_ISDIR(fst.st_mode)) { 1344 if (!S_ISDIR(fst.st_mode)) {
1363 fprintf(stderr, "ERROR: '%s' is not a directory as expected.\n", copy_path); 1345 fprintf(stderr, "ERROR: '%s' is not a directory as expected.\n", copy_path);
1364 free(copy_path);
1365 goto leave_cleanup; 1346 goto leave_cleanup;
1366 } 1347 }
1367 1348
@@ -1372,7 +1353,6 @@ run_again:
1372 1353
1373 if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || !service) { 1354 if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || !service) {
1374 fprintf(stderr, "Could not start com.apple.afc!\n"); 1355 fprintf(stderr, "Could not start com.apple.afc!\n");
1375 free(copy_path);
1376 goto leave_cleanup; 1356 goto leave_cleanup;
1377 } 1357 }
1378 1358
@@ -1411,7 +1391,6 @@ run_again:
1411 fprintf(stderr, "Out of memory!?\n"); 1391 fprintf(stderr, "Out of memory!?\n");
1412 goto leave_cleanup; 1392 goto leave_cleanup;
1413 } 1393 }
1414 free(copy_path);
1415 1394
1416 f = fopen(localfile, "wb"); 1395 f = fopen(localfile, "wb");
1417 if (!f) { 1396 if (!f) {
@@ -1509,8 +1488,6 @@ run_again:
1509 /* remove archive if requested */ 1488 /* remove archive if requested */
1510 printf("Removing '%s'\n", cmdarg); 1489 printf("Removing '%s'\n", cmdarg);
1511 cmd = CMD_REMOVE_ARCHIVE; 1490 cmd = CMD_REMOVE_ARCHIVE;
1512 free(options);
1513 options = NULL;
1514 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &client, "ideviceinstaller")) { 1491 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &client, "ideviceinstaller")) {
1515 fprintf(stderr, "Could not connect to lockdownd. Exiting.\n"); 1492 fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
1516 goto leave_cleanup; 1493 goto leave_cleanup;
@@ -1547,7 +1524,7 @@ leave_cleanup:
1547 idevice_free(device); 1524 idevice_free(device);
1548 1525
1549 free(udid); 1526 free(udid);
1550 free(options); 1527 free(copy_path);
1551 free(bundleidentifier); 1528 free(bundleidentifier);
1552 plist_free(bundle_ids); 1529 plist_free(bundle_ids);
1553 plist_free(return_attrs); 1530 plist_free(return_attrs);