summaryrefslogtreecommitdiffstats
path: root/src/iphoneinstaller.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2010-02-01 21:13:53 +0100
committerGravatar Martin Szulecki2010-02-02 11:24:28 +0100
commit375a10385cfb1267d57f6ea717b235c9e2978d5a (patch)
treec8db9a56b4793cce8d8862acdbc484819e7dcd8a /src/iphoneinstaller.c
parent1f9bf779543bf7fde74905c1f52119d234e629fe (diff)
downloadideviceinstaller-375a10385cfb1267d57f6ea717b235c9e2978d5a.tar.gz
ideviceinstaller-375a10385cfb1267d57f6ea717b235c9e2978d5a.tar.bz2
Rename project and use libimobiledevice API
Diffstat (limited to 'src/iphoneinstaller.c')
-rw-r--r--src/iphoneinstaller.c1009
1 files changed, 0 insertions, 1009 deletions
diff --git a/src/iphoneinstaller.c b/src/iphoneinstaller.c
deleted file mode 100644
index 04ac341..0000000
--- a/src/iphoneinstaller.c
+++ /dev/null
@@ -1,1009 +0,0 @@
-/**
- * iphoneinstaller -- Manage iPhone/iPod apps
- *
- * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
- *
- * Licensed under the GNU General Public License Version 2
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more profile.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
- * USA
- */
-#include <stdlib.h>
-#define _GNU_SOURCE 1
-#define __USE_GNU 1
-#include <stdio.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <time.h>
-
-#include <libiphone/libiphone.h>
-#include <libiphone/lockdown.h>
-#include <libiphone/installation_proxy.h>
-#include <libiphone/notification_proxy.h>
-#include <libiphone/afc.h>
-
-#include <plist/plist.h>
-
-#include <zip.h>
-
-const char PKG_PATH[] = "PublicStaging";
-const char APPARCH_PATH[] = "ApplicationArchives";
-
-char *uuid = NULL;
-char *options = NULL;
-char *appid = NULL;
-
-int list_apps_mode = 0;
-int install_mode = 0;
-int uninstall_mode = 0;
-int upgrade_mode = 0;
-int list_archives_mode = 0;
-int archive_mode = 0;
-int restore_mode = 0;
-int remove_archive_mode = 0;
-
-char *last_status = NULL;
-int wait_for_op_complete = 0;
-int notification_expected = 0;
-int op_completed = 0;
-int err_occured = 0;
-int notified = 0;
-
-
-static void notifier(const char *notification)
-{
- /* printf("notification received: %s\n", notification);*/
- notified = 1;
-}
-
-static void status_cb(const char *operation, plist_t status)
-{
- if (status && operation) {
- plist_t npercent = plist_dict_get_item(status, "PercentComplete");
- plist_t nstatus = plist_dict_get_item(status, "Status");
- plist_t nerror = plist_dict_get_item(status, "Error");
- int percent = 0;
- char *status_msg = NULL;
- if (npercent) {
- uint64_t val = 0;
- plist_get_uint_val(npercent, &val);
- percent = val;
- }
- if (nstatus) {
- plist_get_string_val(nstatus, &status_msg);
- if (!strcmp(status_msg, "Complete")) {
- op_completed = 1;
- }
- }
- if (!nerror) {
- if (last_status && (strcmp(last_status, status_msg))) {
- printf("\n");
- }
-
- if (!npercent) {
- printf("%s - %s\n", operation, status_msg);
- } else {
- printf("%s - %s (%d%%)\r", operation, status_msg, percent);
- }
- } else {
- char *err_msg = NULL;
- plist_get_string_val(nerror, &err_msg);
- printf("%s - Error occured: %s\n", operation, err_msg);
- free(err_msg);
- err_occured = 1;
- }
- if (last_status) {
- free(last_status);
- last_status = NULL;
- }
- if (status_msg) {
- last_status = strdup(status_msg);
- free(status_msg);
- }
- } else {
- printf("%s: called with invalid data!\n", __func__);
- }
-}
-
-static int zip_f_get_contents(struct zip *zf, const char *filename, int locate_flags, char **buffer, uint32_t *len)
-{
- struct zip_stat zs;
- struct zip_file *zfile;
- int zindex = zip_name_locate(zf, filename, locate_flags);
-
- *buffer = NULL;
- *len = 0;
-
- if (zindex < 0) {
- fprintf(stderr, "ERROR: could not locate %s in archive!\n", filename);
- return -1;
- }
-
- zip_stat_init(&zs);
-
- if (zip_stat_index(zf, zindex, 0, &zs) != 0) {
- fprintf(stderr, "ERROR: zip_stat_index '%s' failed!\n", filename);
- return -2;
- }
-
- if (zs.size > 10485760) {
- fprintf(stderr, "ERROR: file '%s' is too large!\n", filename);
- return -3;
- }
-
- zfile = zip_fopen_index(zf, zindex, 0);
- if (!zfile) {
- fprintf(stderr, "ERROR: zip_fopen '%s' failed!\n", filename);
- return -4;
- }
-
- *buffer = malloc(zs.size);
- if (zip_fread(zfile, *buffer, zs.size) != zs.size) {
- fprintf(stderr, "ERROR: zip_fread %ld bytes from '%s'\n", zs.size, filename);
- free(*buffer);
- *buffer = NULL;
- zip_fclose(zfile);
- return -5;
- }
- *len = zs.size;
- zip_fclose(zfile);
- return 0;
-}
-
-static void do_wait_when_needed()
-{
- int i = 0;
- struct timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = 500000000;
-
- /* wait for operation to complete */
- while (wait_for_op_complete && !op_completed && !err_occured
- && !notified && (i < 60)) {
- nanosleep(&ts, NULL);
- i++;
- }
-
- /* wait some time if a notification is expected */
- while (notification_expected && !notified && !err_occured && (i < 10)) {
- nanosleep(&ts, NULL);
- i++;
- }
-}
-
-static void print_usage(int argc, char **argv)
-{
- char *name = NULL;
-
- name = strrchr(argv[0], '/');
- printf("Usage: %s OPTIONS\n", (name ? name + 1 : argv[0]));
- printf
- (" -U|--uuid UUID\tTarget specific device by its 40-digit device UUID.\n"
- " -l|--list-apps\tList apps, possible options:\n"
- " -o list_user\t- list user apps only (this is the default)\n"
- " -o list_system\t- list system apps only\n"
- " -o list_all\t- list all types of apps\n"
- " -o xml\t\t- print full output as xml plist\n"
- " -i|--install ARCHIVE\tInstall app from package file specified by ARCHIVE.\n"
- " -u|--uninstall APPID\tUninstall app specified by APPID.\n"
- " -g|--upgrade APPID\tUpgrade app specified by APPID.\n"
- " -L|--list-archives\tList archived applications, possible options:\n"
- " -o xml\t\t- print full output as xml plist\n"
- " -a|--archive APPID\tArchive app specified by APPID, possible options:\n"
- " -o uninstall\t- uninstall the package after making an archive\n"
- " -o app_only\t- archive application data only\n"
- " -o copy=PATH\t- copy the app archive to directory PATH when done\n"
- " -o remove\t- only valid when copy=PATH is used: remove after copy\n"
- " -r|--restore APPID\tRestore archived app specified by APPID\n"
- " -R|--remove-archive APPID Remove app archive specified by APPID\n"
- " -o|--options\t\tPass additional options to the specified command.\n"
- " -h|--help\t\tprints usage information\n"
- " -D|--debug\t\tenable communication debugging\n" "\n");
-}
-
-static void parse_opts(int argc, char **argv)
-{
- static struct option longopts[] = {
- {"help", 0, NULL, 'h'},
- {"uuid", 0, NULL, 'U'},
- {"list-apps", 0, NULL, 'l'},
- {"install", 0, NULL, 'i'},
- {"uninstall", 0, NULL, 'u'},
- {"upgrade", 0, NULL, 'g'},
- {"list-archives", 0, NULL, 'L'},
- {"archive", 0, NULL, 'a'},
- {"restore", 0, NULL, 'r'},
- {"remove-archive", 0, NULL, 'R'},
- {"options", 0, NULL, 'o'},
- {"debug", 0, NULL, 'D'},
- {NULL, 0, NULL, 0}
- };
- int c;
-
- while (1) {
- c = getopt_long(argc, argv, "hU:li:u:g:La:r:R:o:D", longopts,
- (int *) 0);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'h':
- print_usage(argc, argv);
- exit(0);
- case 'U':
- if (strlen(optarg) != 40) {
- printf("%s: invalid UUID specified (length != 40)\n",
- argv[0]);
- print_usage(argc, argv);
- exit(2);
- }
- uuid = strdup(optarg);
- break;
- case 'l':
- list_apps_mode = 1;
- break;
- case 'i':
- install_mode = 1;
- appid = strdup(optarg);
- break;
- case 'u':
- uninstall_mode = 1;
- appid = strdup(optarg);
- break;
- case 'g':
- upgrade_mode = 1;
- appid = strdup(optarg);
- break;
- case 'L':
- list_archives_mode = 1;
- break;
- case 'a':
- archive_mode = 1;
- appid = strdup(optarg);
- break;
- case 'r':
- restore_mode = 1;
- appid = strdup(optarg);
- break;
- case 'R':
- remove_archive_mode = 1;
- appid = strdup(optarg);
- break;
- case 'o':
- if (!options) {
- options = strdup(optarg);
- } else {
- char *newopts = malloc(strlen(options) + strlen(optarg) + 2);
- strcpy(newopts, options);
- free(options);
- strcat(newopts, ",");
- strcat(newopts, optarg);
- options = newopts;
- }
- break;
- case 'D':
- iphone_set_debug_level(1);
- break;
- default:
- print_usage(argc, argv);
- exit(2);
- }
- }
-
- if (optind <= 1 || (argc - optind > 0)) {
- print_usage(argc, argv);
- exit(2);
- }
-}
-
-int main(int argc, char **argv)
-{
- iphone_device_t phone = NULL;
- lockdownd_client_t client = NULL;
- instproxy_client_t ipc = NULL;
- np_client_t np = NULL;
- afc_client_t afc = NULL;
- uint16_t port = 0;
- int res = 0;
-
- parse_opts(argc, argv);
-
- argc -= optind;
- argv += optind;
-
- if (IPHONE_E_SUCCESS != iphone_device_new(&phone, uuid)) {
- fprintf(stderr, "No iPhone found, is it plugged in?\n");
- return -1;
- }
-
- if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "iphoneinstaller")) {
- fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
- goto leave_cleanup;
- }
-
- if ((lockdownd_start_service
- (client, "com.apple.mobile.notification_proxy",
- &port) != LOCKDOWN_E_SUCCESS) || !port) {
- fprintf(stderr,
- "Could not start com.apple.mobile.notification_proxy!\n");
- goto leave_cleanup;
- }
-
- if (np_client_new(phone, port, &np) != NP_E_SUCCESS) {
- fprintf(stderr, "Could not connect to notification_proxy!\n");
- goto leave_cleanup;
- }
-
- np_set_notify_callback(np, notifier);
-
- const char *noties[3] = { NP_APP_INSTALLED, NP_APP_UNINSTALLED, NULL };
-
- np_observe_notifications(np, noties);
-
-run_again:
- port = 0;
- if ((lockdownd_start_service
- (client, "com.apple.mobile.installation_proxy",
- &port) != LOCKDOWN_E_SUCCESS) || !port) {
- fprintf(stderr,
- "Could not start com.apple.mobile.installation_proxy!\n");
- goto leave_cleanup;
- }
-
- if (instproxy_client_new(phone, port, &ipc) != INSTPROXY_E_SUCCESS) {
- fprintf(stderr, "Could not connect to installation_proxy!\n");
- goto leave_cleanup;
- }
-
- setbuf(stdout, NULL);
-
- if (last_status) {
- free(last_status);
- last_status = NULL;
- }
- notification_expected = 0;
-
- if (list_apps_mode) {
- int xml_mode = 0;
- plist_t client_opts = instproxy_client_options_new();
- instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
- instproxy_error_t err;
- plist_t apps = NULL;
-
- /* look for options */
- if (options) {
- char *opts = strdup(options);
- char *elem = strtok(opts, ",");
- while (elem) {
- if (!strcmp(elem, "list_system")) {
- if (!client_opts) {
- client_opts = instproxy_client_options_new();
- }
- instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL);
- } else if (!strcmp(elem, "list_all")) {
- instproxy_client_options_free(client_opts);
- client_opts = NULL;
- } else if (!strcmp(elem, "list_user")) {
- /* do nothing, we're already set */
- } else if (!strcmp(elem, "xml")) {
- xml_mode = 1;
- }
- elem = strtok(NULL, ",");
- }
- }
-
- err = instproxy_browse(ipc, client_opts, &apps);
- instproxy_client_options_free(client_opts);
- if (err != INSTPROXY_E_SUCCESS) {
- fprintf(stderr, "ERROR: instproxy_browse returned %d\n", err);
- goto leave_cleanup;
- }
- if (!apps || (plist_get_node_type(apps) != PLIST_ARRAY)) {
- fprintf(stderr,
- "ERROR: instproxy_browse returnd an invalid plist!\n");
- goto leave_cleanup;
- }
- if (xml_mode) {
- char *xml = NULL;
- uint32_t len = 0;
-
- plist_to_xml(apps, &xml, &len);
- if (xml) {
- puts(xml);
- free(xml);
- }
- plist_free(apps);
- goto leave_cleanup;
- }
- printf("Total: %d apps\n", plist_array_get_size(apps));
- uint32_t i = 0;
- for (i = 0; i < plist_array_get_size(apps); i++) {
- plist_t app = plist_array_get_item(apps, i);
- plist_t p_appid =
- plist_dict_get_item(app, "CFBundleIdentifier");
- char *s_appid = NULL;
- char *s_dispName = NULL;
- char *s_version = NULL;
- plist_t dispName =
- plist_dict_get_item(app, "CFBundleDisplayName");
- plist_t version = plist_dict_get_item(app, "CFBundleVersion");
-
- if (p_appid) {
- plist_get_string_val(p_appid, &s_appid);
- }
- if (!s_appid) {
- fprintf(stderr, "ERROR: Failed to get APPID!\n");
- break;
- }
-
- if (dispName) {
- plist_get_string_val(dispName, &s_dispName);
- }
- if (version) {
- plist_get_string_val(version, &s_version);
- }
-
- if (!s_dispName) {
- s_dispName = strdup(s_appid);
- }
- if (s_version) {
- printf("%s - %s %s\n", s_appid, s_dispName, s_version);
- free(s_version);
- } else {
- printf("%s - %s\n", s_appid, s_dispName);
- }
- free(s_dispName);
- free(s_appid);
- }
- plist_free(apps);
- } else if (install_mode || upgrade_mode) {
- plist_t sinf = NULL;
- plist_t meta = NULL;
- char *pkgname = NULL;
- struct stat fst;
- FILE *f = NULL;
- uint64_t af = 0;
- char buf[8192];
-
- port = 0;
- if ((lockdownd_start_service(client, "com.apple.afc", &port) !=
- LOCKDOWN_E_SUCCESS) || !port) {
- fprintf(stderr, "Could not start com.apple.afc!\n");
- goto leave_cleanup;
- }
-
- lockdownd_client_free(client);
- client = NULL;
-
- if (afc_client_new(phone, port, &afc) != INSTPROXY_E_SUCCESS) {
- fprintf(stderr, "Could not connect to AFC!\n");
- goto leave_cleanup;
- }
-
- if (stat(appid, &fst) != 0) {
- fprintf(stderr, "ERROR: stat: %s: %s\n", appid, strerror(errno));
- goto leave_cleanup;
- }
-
- /* open install package */
- int errp = 0;
- struct zip *zf = zip_open(appid, 0, &errp);
- if (!zf) {
- fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp);
- goto leave_cleanup;
- }
-
- /* extract iTunesMetadata.plist from package */
- char *zbuf = NULL;
- uint32_t len = 0;
- if (zip_f_get_contents(zf, "iTunesMetadata.plist", 0, &zbuf, &len) < 0) {
- zip_unchange_all(zf);
- zip_close(zf);
- goto leave_cleanup;
- }
- meta = plist_new_data(zbuf, len);
- free(zbuf);
-
- /* we need to get the CFBundleName first */
- plist_t info = NULL;
- zbuf = NULL;
- len = 0;
- if (zip_f_get_contents(zf, "Info.plist", ZIP_FL_NODIR, &zbuf, &len) < 0) {
- zip_unchange_all(zf);
- zip_close(zf);
- goto leave_cleanup;
- }
- if (memcmp(zbuf, "bplist00", 8) == 0) {
- plist_from_bin(zbuf, len, &info);
- } else {
- plist_from_xml(zbuf, len, &info);
- }
- free(zbuf);
-
- if (!info) {
- fprintf(stderr, "Could not parse Info.plist!\n");
- zip_unchange_all(zf);
- zip_close(zf);
- goto leave_cleanup;
- }
-
- char *bundlename = NULL;
-
- plist_t bname = plist_dict_get_item(info, "CFBundleName");
- if (bname) {
- plist_get_string_val(bname, &bundlename);
- }
- plist_free(info);
-
- if (!bundlename) {
- fprintf(stderr, "Could not determine CFBundleName!\n");
- zip_unchange_all(zf);
- zip_close(zf);
- goto leave_cleanup;
- }
-
- char *sinfname = NULL;
- if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundlename, bundlename) < 0) {
- fprintf(stderr, "Out of memory!?\n");
- goto leave_cleanup;
- }
- free(bundlename);
-
- /* extract .sinf from package */
- zbuf = NULL;
- len = 0;
- if (zip_f_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) {
- sinf = plist_new_data(zbuf, len);
- }
- free(sinfname);
- if (zbuf) {
- free(zbuf);
- }
-
- zip_unchange_all(zf);
- zip_close(zf);
-
- /* copy archive to device */
- f = fopen(appid, "r");
- if (!f) {
- fprintf(stderr, "fopen: %s: %s\n", appid, strerror(errno));
- goto leave_cleanup;
- }
-
- pkgname = NULL;
- if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) {
- fprintf(stderr, "Out of memory!?\n");
- goto leave_cleanup;
- }
-
- printf("Copying '%s' --> '%s'\n", appid, pkgname);
-
- char **strs = NULL;
- if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
- if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
- fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
- }
- }
- if (strs) {
- int i = 0;
- while (strs[i]) {
- free(strs[i]);
- i++;
- }
- free(strs);
- }
-
- if ((afc_file_open(afc, pkgname, AFC_FOPEN_WRONLY, &af) !=
- AFC_E_SUCCESS) || !af) {
- fclose(f);
- fprintf(stderr, "afc_file_open on '%s' failed!\n", pkgname);
- free(pkgname);
- goto leave_cleanup;
- }
-
- size_t amount = 0;
- do {
- amount = fread(buf, 1, sizeof(buf), f);
- if (amount > 0) {
- uint32_t written, total = 0;
- while (total < amount) {
- written = 0;
- if (afc_file_write(afc, af, buf, amount, &written) !=
- AFC_E_SUCCESS) {
- fprintf(stderr, "AFC Write error!\n");
- break;
- }
- total += written;
- }
- if (total != amount) {
- fprintf(stderr, "Error: wrote only %d of %d\n", total,
- amount);
- afc_file_close(afc, af);
- fclose(f);
- free(pkgname);
- goto leave_cleanup;
- }
- }
- }
- while (amount > 0);
-
- afc_file_close(afc, af);
- fclose(f);
-
- printf("done.\n");
-
- /* perform installation or upgrade */
- plist_t client_opts = instproxy_client_options_new();
- if (sinf) {
- instproxy_client_options_add(client_opts, "ApplicationSINF", sinf, NULL);
- }
- if (meta) {
- instproxy_client_options_add(client_opts, "iTunesMetadata", meta, NULL);
- }
- if (install_mode) {
- printf("Installing '%s'\n", pkgname);
- instproxy_install(ipc, pkgname, client_opts, status_cb);
- } else {
- printf("Upgrading '%s'\n", pkgname);
- instproxy_upgrade(ipc, pkgname, client_opts, status_cb);
- }
- instproxy_client_options_free(client_opts);
- free(pkgname);
- wait_for_op_complete = 1;
- notification_expected = 1;
- } else if (uninstall_mode) {
- instproxy_uninstall(ipc, appid, NULL, status_cb);
- wait_for_op_complete = 1;
- notification_expected = 1;
- } else if (list_archives_mode) {
- int xml_mode = 0;
- plist_t dict = NULL;
- plist_t lres = NULL;
- instproxy_error_t err;
-
- /* look for options */
- if (options) {
- char *opts = strdup(options);
- char *elem = strtok(opts, ",");
- while (elem) {
- if (!strcmp(elem, "xml")) {
- xml_mode = 1;
- }
- elem = strtok(NULL, ",");
- }
- }
-
- err = instproxy_lookup_archives(ipc, NULL, &dict);
- if (err != INSTPROXY_E_SUCCESS) {
- fprintf(stderr, "ERROR: lookup_archives returned %d\n", err);
- goto leave_cleanup;
- }
- if (!dict) {
- fprintf(stderr,
- "ERROR: lookup_archives did not return a plist!?\n");
- goto leave_cleanup;
- }
-
- lres = plist_dict_get_item(dict, "LookupResult");
- if (!lres || (plist_get_node_type(lres) != PLIST_DICT)) {
- plist_free(dict);
- fprintf(stderr, "ERROR: Could not get dict 'LookupResult'\n");
- goto leave_cleanup;
- }
-
- if (xml_mode) {
- char *xml = NULL;
- uint32_t len = 0;
-
- plist_to_xml(lres, &xml, &len);
- if (xml) {
- puts(xml);
- free(xml);
- }
- plist_free(dict);
- goto leave_cleanup;
- }
- plist_dict_iter iter = NULL;
- plist_t node = NULL;
- char *key = NULL;
-
- printf("Total: %d archived apps\n", plist_dict_get_size(lres));
- plist_dict_new_iter(lres, &iter);
- if (!iter) {
- plist_free(dict);
- fprintf(stderr, "ERROR: Could not create plist_dict_iter!\n");
- goto leave_cleanup;
- }
- do {
- key = NULL;
- node = NULL;
- plist_dict_next_item(lres, iter, &key, &node);
- if (key && (plist_get_node_type(node) == PLIST_DICT)) {
- char *s_dispName = NULL;
- char *s_version = NULL;
- plist_t dispName =
- plist_dict_get_item(node, "CFBundleDisplayName");
- plist_t version =
- plist_dict_get_item(node, "CFBundleVersion");
- if (dispName) {
- plist_get_string_val(dispName, &s_dispName);
- }
- if (version) {
- plist_get_string_val(version, &s_version);
- }
- if (!s_dispName) {
- s_dispName = strdup(key);
- }
- if (s_version) {
- printf("%s - %s %s\n", key, s_dispName, s_version);
- free(s_version);
- } else {
- printf("%s - %s\n", key, s_dispName);
- }
- free(s_dispName);
- free(key);
- }
- }
- while (node);
- plist_free(dict);
- } else if (archive_mode) {
- char *copy_path = NULL;
- int remove_after_copy = 0;
- int skip_uninstall = 1;
- int app_only = 0;
- plist_t client_opts = NULL;
-
- /* look for options */
- if (options) {
- char *opts = strdup(options);
- char *elem = strtok(opts, ",");
- while (elem) {
- if (!strcmp(elem, "uninstall")) {
- skip_uninstall = 0;
- } else if (!strcmp(elem, "app_only")) {
- app_only = 1;
- } else if ((strlen(elem) > 5) && !strncmp(elem, "copy=", 5)) {
- copy_path = strdup(elem+5);
- } else if (!strcmp(elem, "remove")) {
- remove_after_copy = 1;
- }
- elem = strtok(NULL, ",");
- }
- }
-
- if (skip_uninstall || app_only) {
- client_opts = instproxy_client_options_new();
- if (skip_uninstall) {
- instproxy_client_options_add(client_opts, "SkipUninstall", 1, NULL);
- }
- if (app_only) {
- instproxy_client_options_add(client_opts, "ArchiveType", "ApplicationOnly", NULL);
- }
- }
-
- if (copy_path) {
- struct stat fst;
- if (stat(copy_path, &fst) != 0) {
- fprintf(stderr, "ERROR: stat: %s: %s\n", copy_path, strerror(errno));
- free(copy_path);
- goto leave_cleanup;
- }
-
- if (!S_ISDIR(fst.st_mode)) {
- fprintf(stderr, "ERROR: '%s' is not a directory as expected.\n", copy_path);
- free(copy_path);
- goto leave_cleanup;
- }
-
- port = 0;
- if ((lockdownd_start_service(client, "com.apple.afc", &port) != LOCKDOWN_E_SUCCESS) || !port) {
- fprintf(stderr, "Could not start com.apple.afc!\n");
- free(copy_path);
- goto leave_cleanup;
- }
-
- lockdownd_client_free(client);
- client = NULL;
-
- if (afc_client_new(phone, port, &afc) != INSTPROXY_E_SUCCESS) {
- fprintf(stderr, "Could not connect to AFC!\n");
- goto leave_cleanup;
- }
- }
-
- instproxy_archive(ipc, appid, client_opts, status_cb);
- instproxy_client_options_free(client_opts);
- wait_for_op_complete = 1;
- if (skip_uninstall) {
- notification_expected = 0;
- } else {
- notification_expected = 1;
- }
-
- do_wait_when_needed();
-
- if (copy_path) {
- if (err_occured) {
- afc_client_free(afc);
- afc = NULL;
- goto leave_cleanup;
- }
- FILE *f = NULL;
- uint64_t af = 0;
- /* local filename */
- char *localfile = NULL;
- if (asprintf(&localfile, "%s/%s.ipa", copy_path, appid) < 0) {
- fprintf(stderr, "Out of memory!?\n");
- goto leave_cleanup;
- }
- free(copy_path);
-
- f = fopen(localfile, "w");
- if (!f) {
- fprintf(stderr, "ERROR: fopen: %s: %s\n", localfile, strerror(errno));
- free(localfile);
- goto leave_cleanup;
- }
-
- /* remote filename */
- char *remotefile = NULL;
- if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, appid) < 0) {
- fprintf(stderr, "Out of memory!?\n");
- goto leave_cleanup;
- }
-
- uint32_t fsize = 0;
- char **fileinfo = NULL;
- if ((afc_get_file_info(afc, remotefile, &fileinfo) != AFC_E_SUCCESS) || !fileinfo) {
- fprintf(stderr, "ERROR getting AFC file info for '%s' on device!\n", remotefile);
- fclose(f);
- free(remotefile);
- free(localfile);
- goto leave_cleanup;
- }
-
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- fsize = atoi(fileinfo[i+1]);
- break;
- }
- }
- i = 0;
- while (fileinfo[i]) {
- free(fileinfo[i]);
- i++;
- }
- free(fileinfo);
-
- if (fsize == 0) {
- fprintf(stderr, "Hm... remote file length could not be determined. Cannot copy.\n");
- fclose(f);
- free(remotefile);
- free(localfile);
- goto leave_cleanup;
- }
-
- if ((afc_file_open(afc, remotefile, AFC_FOPEN_RDONLY, &af) != AFC_E_SUCCESS) || !af) {
- fclose(f);
- fprintf(stderr, "ERROR: could not open '%s' on device for reading!\n", remotefile);
- free(remotefile);
- free(localfile);
- goto leave_cleanup;
- }
-
- /* copy file over */
- printf("Copying '%s' --> '%s'\n", remotefile, localfile);
- free(remotefile);
- free(localfile);
-
- uint32_t amount = 0;
- uint32_t total = 0;
- char buf[8192];
-
- do {
- if (afc_file_read(afc, af, buf, sizeof(buf), &amount) != AFC_E_SUCCESS) {
- fprintf(stderr, "AFC Read error!\n");
- break;
- }
-
- if (amount > 0) {
- size_t written = fwrite(buf, 1, amount, f);
- if (written != amount) {
- fprintf(stderr, "Error when writing %d bytes to local file!\n", amount);
- break;
- }
- total += written;
- }
- } while (amount > 0);
-
- afc_file_close(afc, af);
- fclose(f);
-
- printf("done.\n");
- if (total != fsize) {
- fprintf(stderr, "WARNING: remote and local file sizes don't match (%d != %d)\n", fsize, total);
- if (remove_after_copy) {
- fprintf(stderr, "NOTE: archive file will NOT be removed from device\n");
- remove_after_copy = 0;
- }
- }
-
- if (remove_after_copy) {
- /* remove archive if requested */
- printf("Removing '%s'\n", appid);
- archive_mode = 0;
- remove_archive_mode = 1;
- free(options);
- options = NULL;
- if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "iphoneinstaller")) {
- fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
- goto leave_cleanup;
- }
- goto run_again;
- }
- }
- goto leave_cleanup;
- } else if (restore_mode) {
- instproxy_restore(ipc, appid, NULL, status_cb);
- wait_for_op_complete = 1;
- notification_expected = 1;
- } else if (remove_archive_mode) {
- instproxy_remove_archive(ipc, appid, NULL, status_cb);
- wait_for_op_complete = 1;
- } else {
- printf
- ("ERROR: no operation selected?! This should not be reached!\n");
- res = -2;
- goto leave_cleanup;
- }
-
- if (client) {
- /* not needed anymore */
- lockdownd_client_free(client);
- client = NULL;
- }
-
- do_wait_when_needed();
-
- leave_cleanup:
- if (np) {
- np_client_free(np);
- }
- if (ipc) {
- instproxy_client_free(ipc);
- }
- if (afc) {
- afc_client_free(afc);
- }
- if (client) {
- lockdownd_client_free(client);
- }
- iphone_device_free(phone);
-
- if (uuid) {
- free(uuid);
- }
- if (appid) {
- free(appid);
- }
- if (options) {
- free(options);
- }
-
- return res;
-}