diff options
| -rw-r--r-- | src/ideviceinstaller.c | 333 |
1 files changed, 212 insertions, 121 deletions
diff --git a/src/ideviceinstaller.c b/src/ideviceinstaller.c index 67e06f9..acf2b6b 100644 --- a/src/ideviceinstaller.c +++ b/src/ideviceinstaller.c | |||
| @@ -212,6 +212,7 @@ static void print_usage(int argc, char **argv) | |||
| 212 | " -o list_all\t- list all types of apps\n" | 212 | " -o list_all\t- list all types of apps\n" |
| 213 | " -o xml\t\t- print full output as xml plist\n" | 213 | " -o xml\t\t- print full output as xml plist\n" |
| 214 | " -i, --install ARCHIVE\tInstall app from package file specified by ARCHIVE.\n" | 214 | " -i, --install ARCHIVE\tInstall app from package file specified by ARCHIVE.\n" |
| 215 | " \tARCHIVE can also be a .ipcc file for carrier bundles.\n" | ||
| 215 | " -u, --uninstall APPID\tUninstall app specified by APPID.\n" | 216 | " -u, --uninstall APPID\tUninstall app specified by APPID.\n" |
| 216 | " -g, --upgrade APPID\tUpgrade app specified by APPID.\n" | 217 | " -g, --upgrade APPID\tUpgrade app specified by APPID.\n" |
| 217 | " -L, --list-archives\tList archived applications, possible options:\n" | 218 | " -L, --list-archives\tList archived applications, possible options:\n" |
| @@ -525,152 +526,242 @@ run_again: | |||
| 525 | goto leave_cleanup; | 526 | goto leave_cleanup; |
| 526 | } | 527 | } |
| 527 | 528 | ||
| 528 | /* extract iTunesMetadata.plist from package */ | 529 | plist_t client_opts = instproxy_client_options_new(); |
| 529 | char *zbuf = NULL; | ||
| 530 | uint32_t len = 0; | ||
| 531 | if (zip_f_get_contents(zf, "iTunesMetadata.plist", 0, &zbuf, &len) == 0) { | ||
| 532 | meta = plist_new_data(zbuf, len); | ||
| 533 | } | ||
| 534 | if (zbuf) { | ||
| 535 | free(zbuf); | ||
| 536 | } | ||
| 537 | 530 | ||
| 538 | /* we need to get the CFBundleName first */ | 531 | if ((strlen(appid) > 5) && (strcmp(&appid[strlen(appid)-5], ".ipcc") == 0)) { |
| 539 | plist_t info = NULL; | 532 | char* ipcc = strdup(appid); |
| 540 | zbuf = NULL; | 533 | if ((asprintf(&pkgname, "%s/%s", PKG_PATH, basename(ipcc)) > 0) && pkgname) { |
| 541 | len = 0; | 534 | afc_make_directory(afc, pkgname); |
| 542 | if (zip_f_get_contents(zf, "Info.plist", ZIP_FL_NODIR, &zbuf, &len) < 0) { | 535 | } |
| 543 | zip_unchange_all(zf); | ||
| 544 | zip_close(zf); | ||
| 545 | goto leave_cleanup; | ||
| 546 | } | ||
| 547 | if (memcmp(zbuf, "bplist00", 8) == 0) { | ||
| 548 | plist_from_bin(zbuf, len, &info); | ||
| 549 | } else { | ||
| 550 | plist_from_xml(zbuf, len, &info); | ||
| 551 | } | ||
| 552 | free(zbuf); | ||
| 553 | 536 | ||
| 554 | if (!info) { | 537 | printf("Uploading %s package contents...\n", basename(ipcc)); |
| 555 | fprintf(stderr, "Could not parse Info.plist!\n"); | 538 | |
| 556 | zip_unchange_all(zf); | 539 | /* extract the contents of the .ipcc file to PublicStaging/<name>.ipcc directory */ |
| 557 | zip_close(zf); | 540 | zip_uint64_t numzf = zip_get_num_entries(zf, 0); |
| 558 | goto leave_cleanup; | 541 | zip_uint64_t i = 0; |
| 559 | } | 542 | for (i = 0; numzf > 0 && i < numzf; i++) { |
| 543 | const char* zname = zip_get_name(zf, i, 0); | ||
| 544 | char* dstpath = NULL; | ||
| 545 | if (!zname) continue; | ||
| 546 | if (zname[strlen(zname)-1] == '/') { | ||
| 547 | // directory | ||
| 548 | if ((asprintf(&dstpath, "%s/%s/%s", PKG_PATH, basename(ipcc), zname) > 0) && dstpath) { | ||
| 549 | afc_make_directory(afc, dstpath); } | ||
| 550 | free(dstpath); | ||
| 551 | dstpath = NULL; | ||
| 552 | } else { | ||
| 553 | // file | ||
| 554 | struct zip_file* zfile = zip_fopen_index(zf, i, 0); | ||
| 555 | if (!zfile) continue; | ||
| 556 | |||
| 557 | if ((asprintf(&dstpath, "%s/%s/%s", PKG_PATH, basename(ipcc), zname) <= 0) || !dstpath || (afc_file_open(afc, dstpath, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS)) { | ||
| 558 | fprintf(stderr, "ERROR: can't open afc://%s for writing\n", dstpath); | ||
| 559 | free(dstpath); | ||
| 560 | dstpath = NULL; | ||
| 561 | zip_fclose(zfile); | ||
| 562 | continue; | ||
| 563 | } | ||
| 560 | 564 | ||
| 561 | char *bundlename = NULL; | 565 | struct zip_stat zs; |
| 566 | zip_stat_init(&zs); | ||
| 567 | if (zip_stat_index(zf, i, 0, &zs) != 0) { | ||
| 568 | fprintf(stderr, "ERROR: zip_stat_index %ld failed!\n", i); | ||
| 569 | free(dstpath); | ||
| 570 | dstpath = NULL; | ||
| 571 | zip_fclose(zfile); | ||
| 572 | continue; | ||
| 573 | } | ||
| 562 | 574 | ||
| 563 | plist_t bname = plist_dict_get_item(info, "CFBundleName"); | 575 | free(dstpath); |
| 564 | if (bname) { | 576 | dstpath = NULL; |
| 565 | plist_get_string_val(bname, &bundlename); | 577 | |
| 566 | } | 578 | zip_uint64_t zfsize = 0; |
| 567 | plist_free(info); | 579 | while (zfsize < zs.size) { |
| 580 | zip_int64_t amount = zip_fread(zfile, buf, sizeof(buf)); | ||
| 581 | if (amount == 0) { | ||
| 582 | break; | ||
| 583 | } | ||
| 584 | |||
| 585 | if (amount > 0) { | ||
| 586 | uint32_t written, total = 0; | ||
| 587 | while (total < amount) { | ||
| 588 | written = 0; | ||
| 589 | if (afc_file_write(afc, af, buf, amount, &written) != | ||
| 590 | AFC_E_SUCCESS) { | ||
| 591 | fprintf(stderr, "AFC Write error!\n"); | ||
| 592 | break; | ||
| 593 | } | ||
| 594 | total += written; | ||
| 595 | } | ||
| 596 | if (total != amount) { | ||
| 597 | fprintf(stderr, "Error: wrote only %d of %zu\n", total, amount); | ||
| 598 | afc_file_close(afc, af); | ||
| 599 | zip_fclose(zfile); | ||
| 600 | free(dstpath); | ||
| 601 | goto leave_cleanup; | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | zfsize += amount; | ||
| 606 | } | ||
| 568 | 607 | ||
| 569 | if (!bundlename) { | 608 | afc_file_close(afc, af); |
| 570 | fprintf(stderr, "Could not determine CFBundleName!\n"); | 609 | af = 0; |
| 571 | zip_unchange_all(zf); | ||
| 572 | zip_close(zf); | ||
| 573 | goto leave_cleanup; | ||
| 574 | } | ||
| 575 | 610 | ||
| 576 | char *sinfname = NULL; | 611 | zip_fclose(zfile); |
| 577 | if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundlename, bundlename) < 0) { | 612 | } |
| 578 | fprintf(stderr, "Out of memory!?\n"); | 613 | } |
| 579 | goto leave_cleanup; | 614 | free(ipcc); |
| 580 | } | 615 | printf("done.\n"); |
| 581 | free(bundlename); | ||
| 582 | 616 | ||
| 583 | /* extract .sinf from package */ | 617 | instproxy_client_options_add(client_opts, "PackageType", "CarrierBundle", NULL); |
| 584 | zbuf = NULL; | 618 | } else { |
| 585 | len = 0; | 619 | /* extract iTunesMetadata.plist from package */ |
| 586 | if (zip_f_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) { | 620 | char *zbuf = NULL; |
| 587 | sinf = plist_new_data(zbuf, len); | 621 | uint32_t len = 0; |
| 588 | } | 622 | if (zip_f_get_contents(zf, "iTunesMetadata.plist", 0, &zbuf, &len) == 0) { |
| 589 | free(sinfname); | 623 | meta = plist_new_data(zbuf, len); |
| 590 | if (zbuf) { | 624 | } |
| 625 | if (zbuf) { | ||
| 626 | free(zbuf); | ||
| 627 | } | ||
| 628 | |||
| 629 | /* we need to get the CFBundleName first */ | ||
| 630 | plist_t info = NULL; | ||
| 631 | zbuf = NULL; | ||
| 632 | len = 0; | ||
| 633 | if (zip_f_get_contents(zf, "Info.plist", ZIP_FL_NODIR, &zbuf, &len) < 0) { | ||
| 634 | zip_unchange_all(zf); | ||
| 635 | zip_close(zf); | ||
| 636 | goto leave_cleanup; | ||
| 637 | } | ||
| 638 | if (memcmp(zbuf, "bplist00", 8) == 0) { | ||
| 639 | plist_from_bin(zbuf, len, &info); | ||
| 640 | } else { | ||
| 641 | plist_from_xml(zbuf, len, &info); | ||
| 642 | } | ||
| 591 | free(zbuf); | 643 | free(zbuf); |
| 592 | } | ||
| 593 | 644 | ||
| 594 | zip_unchange_all(zf); | 645 | if (!info) { |
| 595 | zip_close(zf); | 646 | fprintf(stderr, "Could not parse Info.plist!\n"); |
| 647 | zip_unchange_all(zf); | ||
| 648 | zip_close(zf); | ||
| 649 | goto leave_cleanup; | ||
| 650 | } | ||
| 596 | 651 | ||
| 597 | /* copy archive to device */ | 652 | char *bundlename = NULL; |
| 598 | f = fopen(appid, "r"); | ||
| 599 | if (!f) { | ||
| 600 | fprintf(stderr, "fopen: %s: %s\n", appid, strerror(errno)); | ||
| 601 | goto leave_cleanup; | ||
| 602 | } | ||
| 603 | 653 | ||
| 604 | pkgname = NULL; | 654 | plist_t bname = plist_dict_get_item(info, "CFBundleName"); |
| 605 | if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) { | 655 | if (bname) { |
| 606 | fprintf(stderr, "Out of memory!?\n"); | 656 | plist_get_string_val(bname, &bundlename); |
| 607 | goto leave_cleanup; | 657 | } |
| 608 | } | 658 | plist_free(info); |
| 609 | 659 | ||
| 610 | printf("Copying '%s' --> '%s'\n", appid, pkgname); | 660 | if (!bundlename) { |
| 661 | fprintf(stderr, "Could not determine CFBundleName!\n"); | ||
| 662 | zip_unchange_all(zf); | ||
| 663 | zip_close(zf); | ||
| 664 | goto leave_cleanup; | ||
| 665 | } | ||
| 611 | 666 | ||
| 612 | char **strs = NULL; | 667 | char *sinfname = NULL; |
| 613 | if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { | 668 | if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundlename, bundlename) < 0) { |
| 614 | if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { | 669 | fprintf(stderr, "Out of memory!?\n"); |
| 615 | fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH); | 670 | goto leave_cleanup; |
| 616 | } | 671 | } |
| 617 | } | 672 | free(bundlename); |
| 618 | if (strs) { | 673 | |
| 619 | int i = 0; | 674 | /* extract .sinf from package */ |
| 620 | while (strs[i]) { | 675 | zbuf = NULL; |
| 621 | free(strs[i]); | 676 | len = 0; |
| 622 | i++; | 677 | if (zip_f_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) { |
| 678 | sinf = plist_new_data(zbuf, len); | ||
| 679 | } | ||
| 680 | free(sinfname); | ||
| 681 | if (zbuf) { | ||
| 682 | free(zbuf); | ||
| 623 | } | 683 | } |
| 624 | free(strs); | ||
| 625 | } | ||
| 626 | 684 | ||
| 627 | if ((afc_file_open(afc, pkgname, AFC_FOPEN_WRONLY, &af) != | 685 | /* copy archive to device */ |
| 628 | AFC_E_SUCCESS) || !af) { | 686 | f = fopen(appid, "r"); |
| 629 | fclose(f); | 687 | if (!f) { |
| 630 | fprintf(stderr, "afc_file_open on '%s' failed!\n", pkgname); | 688 | fprintf(stderr, "fopen: %s: %s\n", appid, strerror(errno)); |
| 631 | free(pkgname); | 689 | goto leave_cleanup; |
| 632 | goto leave_cleanup; | 690 | } |
| 633 | } | ||
| 634 | 691 | ||
| 635 | size_t amount = 0; | 692 | pkgname = NULL; |
| 636 | do { | 693 | if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) { |
| 637 | amount = fread(buf, 1, sizeof(buf), f); | 694 | fprintf(stderr, "Out of memory!?\n"); |
| 638 | if (amount > 0) { | 695 | goto leave_cleanup; |
| 639 | uint32_t written, total = 0; | 696 | } |
| 640 | while (total < amount) { | 697 | |
| 641 | written = 0; | 698 | printf("Copying '%s' --> '%s'\n", appid, pkgname); |
| 642 | if (afc_file_write(afc, af, buf, amount, &written) != | 699 | |
| 643 | AFC_E_SUCCESS) { | 700 | char **strs = NULL; |
| 644 | fprintf(stderr, "AFC Write error!\n"); | 701 | if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { |
| 645 | break; | 702 | if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { |
| 646 | } | 703 | fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH); |
| 647 | total += written; | 704 | } |
| 705 | } | ||
| 706 | if (strs) { | ||
| 707 | int i = 0; | ||
| 708 | while (strs[i]) { | ||
| 709 | free(strs[i]); | ||
| 710 | i++; | ||
| 648 | } | 711 | } |
| 649 | if (total != amount) { | 712 | free(strs); |
| 650 | fprintf(stderr, "Error: wrote only %d of %zu\n", total, | 713 | } |
| 714 | |||
| 715 | if ((afc_file_open(afc, pkgname, AFC_FOPEN_WRONLY, &af) != | ||
| 716 | AFC_E_SUCCESS) || !af) { | ||
| 717 | fclose(f); | ||
| 718 | fprintf(stderr, "afc_file_open on '%s' failed!\n", pkgname); | ||
| 719 | free(pkgname); | ||
| 720 | goto leave_cleanup; | ||
| 721 | } | ||
| 722 | |||
| 723 | size_t amount = 0; | ||
| 724 | do { | ||
| 725 | amount = fread(buf, 1, sizeof(buf), f); | ||
| 726 | if (amount > 0) { | ||
| 727 | uint32_t written, total = 0; | ||
| 728 | while (total < amount) { | ||
| 729 | written = 0; | ||
| 730 | if (afc_file_write(afc, af, buf, amount, &written) != | ||
| 731 | AFC_E_SUCCESS) { | ||
| 732 | fprintf(stderr, "AFC Write error!\n"); | ||
| 733 | break; | ||
| 734 | } | ||
| 735 | total += written; | ||
| 736 | } | ||
| 737 | if (total != amount) { | ||
| 738 | fprintf(stderr, "Error: wrote only %d of %zu\n", total, | ||
| 651 | amount); | 739 | amount); |
| 652 | afc_file_close(afc, af); | 740 | afc_file_close(afc, af); |
| 653 | fclose(f); | 741 | fclose(f); |
| 654 | free(pkgname); | 742 | free(pkgname); |
| 655 | goto leave_cleanup; | 743 | goto leave_cleanup; |
| 744 | } | ||
| 656 | } | 745 | } |
| 657 | } | 746 | } |
| 658 | } | 747 | while (amount > 0); |
| 659 | while (amount > 0); | ||
| 660 | 748 | ||
| 661 | afc_file_close(afc, af); | 749 | afc_file_close(afc, af); |
| 662 | fclose(f); | 750 | fclose(f); |
| 663 | 751 | ||
| 664 | printf("done.\n"); | 752 | printf("done.\n"); |
| 665 | 753 | ||
| 666 | /* perform installation or upgrade */ | 754 | if (sinf) { |
| 667 | plist_t client_opts = instproxy_client_options_new(); | 755 | instproxy_client_options_add(client_opts, "ApplicationSINF", sinf, NULL); |
| 668 | if (sinf) { | 756 | } |
| 669 | instproxy_client_options_add(client_opts, "ApplicationSINF", sinf, NULL); | 757 | if (meta) { |
| 670 | } | 758 | instproxy_client_options_add(client_opts, "iTunesMetadata", meta, NULL); |
| 671 | if (meta) { | 759 | } |
| 672 | instproxy_client_options_add(client_opts, "iTunesMetadata", meta, NULL); | ||
| 673 | } | 760 | } |
| 761 | zip_unchange_all(zf); | ||
| 762 | zip_close(zf); | ||
| 763 | |||
| 764 | /* perform installation or upgrade */ | ||
| 674 | if (install_mode) { | 765 | if (install_mode) { |
| 675 | printf("Installing '%s'\n", pkgname); | 766 | printf("Installing '%s'\n", pkgname); |
| 676 | #ifdef HAVE_LIBIMOBILEDEVICE_1_1 | 767 | #ifdef HAVE_LIBIMOBILEDEVICE_1_1 |
