int main(int argc, char* argv[]) {
	idevice_t device = NULL;
	lockdownd_client_t lockdownd = NULL;
	afc_client_t afc = NULL;

	idevice_error_t device_error = IDEVICE_E_SUCCESS;
	lockdownd_error_t lockdownd_error = LOCKDOWN_E_SUCCESS;
	afc_error_t afc_error = AFC_E_SUCCESS;

	int i;
	const char* udid = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--extract")) {
			extract_raw_crash_reports = 1;
			continue;
		}
		else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keep")) {
			keep_crash_reports = 1;
			continue;
		}
		else if (target_directory == NULL) {
			target_directory = argv[i];
			continue;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	/* ensure a target directory was supplied */
	if (!target_directory) {
		print_usage(argc, argv);
		return 0;
	}

	/* check if target directory exists */
	if (!file_exists(target_directory)) {
		fprintf(stderr, "ERROR: Directory '%s' does not exist.\n", target_directory);
		print_usage(argc, argv);
		return 0;
	}

	device_error = idevice_new(&device, udid);
	if (device_error != IDEVICE_E_SUCCESS) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	lockdownd_error = lockdownd_client_new_with_handshake(device, &lockdownd, "idevicecrashreport");
	if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
		fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lockdownd_error);
		idevice_free(device);
		return -1;
	}

	/* start crash log mover service */
	lockdownd_service_descriptor_t service = NULL;
	lockdownd_error = lockdownd_start_service(lockdownd, "com.apple.crashreportmover", &service);
	if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
		lockdownd_client_free(lockdownd);
		idevice_free(device);
		return -1;
	}

	/* trigger move operation on device */
	idevice_connection_t connection = NULL;
	device_error = idevice_connect(device, service->port, &connection);
	if(device_error != IDEVICE_E_SUCCESS) {
		lockdownd_client_free(lockdownd);
		idevice_free(device);
		return -1;
	}

	/* read "ping" message which indicates the crash logs have been moved to a safe harbor */
	char *ping = malloc(4);
	int attempts = 0;
	while ((strncmp(ping, "ping", 4) != 0) && (attempts > 10)) {
		uint32_t bytes = 0;
		device_error = idevice_connection_receive_timeout(connection, ping, 4, &bytes, 2000);
		if ((bytes == 0) && (device_error == IDEVICE_E_SUCCESS)) {
			attempts++;
			continue;
		} else if (device_error < 0) {
			fprintf(stderr, "ERROR: Crash logs could not be moved. Connection interrupted.\n");
			break;
		}
	}
	idevice_disconnect(connection);
	free(ping);

	if (service) {
		lockdownd_service_descriptor_free(service);
		service = NULL;
	}

	if (device_error != IDEVICE_E_SUCCESS || attempts > 10) {
		fprintf(stderr, "ERROR: Failed to receive ping message from crash report mover.\n");
		lockdownd_client_free(lockdownd);
		idevice_free(device);
		return -1;
	}

	lockdownd_error = lockdownd_start_service(lockdownd, "com.apple.crashreportcopymobile", &service);
	if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
		lockdownd_client_free(lockdownd);
		idevice_free(device);
		return -1;
	}
	lockdownd_client_free(lockdownd);

	afc = NULL;
	afc_error = afc_client_new(device, service, &afc);
	if(afc_error != AFC_E_SUCCESS) {
		lockdownd_client_free(lockdownd);
		idevice_free(device);
		return -1;
	}

	if (service) {
		lockdownd_service_descriptor_free(service);
		service = NULL;
	}

	/* recursively copy crash reports from the device to a local directory */
	if (afc_client_copy_and_remove_crash_reports(afc, ".", target_directory) < 0) {
		fprintf(stderr, "ERROR: Failed to get crash reports from device.\n");
		afc_client_free(afc);
		idevice_free(device);
		return -1;
	}

	printf("Done.\n");

	afc_client_free(afc);
	idevice_free(device);

	return 0;
}
int main(int argc, char *argv[])
{
	int i;

	signal(SIGINT, clean_exit);
	signal(SIGTERM, clean_exit);
#ifndef WIN32
	signal(SIGQUIT, clean_exit);
	signal(SIGPIPE, SIG_IGN);
#endif

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = _strdup(argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	int num = 0;
	char **devices = NULL;
	idevice_get_device_list(&devices, &num);
	idevice_device_list_free(devices);
	if (num == 0) {
		if (!udid) {
			fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
			return -1;
		} else {
			fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
		}
	}

	idevice_event_subscribe(device_event_cb, NULL);

	while (!quit_flag) {
		sleep(1);
	}
	idevice_event_unsubscribe();
	stop_logging();

	if (udid) {
		free(udid);
	}

	return 0;
}
Exemple #3
0
int main(int argc, char *argv[])
{
	lockdownd_client_t client = NULL;
	idevice_t phone = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	int i;
	char udid[41];
	time_t setdate = 0;
	plist_t node = NULL;
	udid[0] = 0;
	uint64_t datetime = 0;
	time_t rawtime;
	struct tm * tmp;
	char const *format = NULL;
	char buffer[80];

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			strcpy(udid, argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--set")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) <= 1)) {
				print_usage(argc, argv);
				return 0;
			}
			setdate = atoi(argv[i]);
			if (setdate == 0) {
				printf("ERROR: Invalid timestamp value.\n");
				print_usage(argc, argv);
				return 0;
			}
			continue;
		}
		else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--sync")) {
			i++;
			/* get current time */
			setdate = time(NULL);
			/* convert it to local time which sets timezone/daylight variables */
			tmp = localtime(&setdate);
			/* recalculate to make it UTC */
			setdate = mktime(tmp);
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	/* determine a date format */
	if (!format) {
		format = DATE_FMT_LANGINFO ();
		if (!*format) {
			format = "%a %b %e %H:%M:%S %Z %Y";
		}
	}

	if (udid[0] != 0) {
		ret = idevice_new(&phone, udid);
		if (ret != IDEVICE_E_SUCCESS) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
			return -1;
		}
	}
	else
	{
		ret = idevice_new(&phone, NULL);
		if (ret != IDEVICE_E_SUCCESS) {
			printf("No device found, is it plugged in?\n");
			return -1;
		}
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicedate")) {
		idevice_free(phone);
		return -1;
	}

	/* get or set? */
	if (setdate == 0) {
		/* get time value from device */
		if(lockdownd_get_value(client, NULL, "TimeIntervalSince1970", &node) == LOCKDOWN_E_SUCCESS) {
			if (node) {
				plist_get_uint_val(node, &datetime);
				plist_free(node);
				node = NULL;

				/* date/time calculations */
				rawtime = (time_t)datetime;
				tmp = localtime(&rawtime);

				/* finally we format and print the current date */
				strftime(buffer, 80, format, tmp);
				puts(buffer);
			}
		}
	} else {
		datetime = setdate;

		if(lockdownd_set_value(client, NULL, "TimeIntervalSince1970", plist_new_uint(datetime)) == LOCKDOWN_E_SUCCESS) {
			tmp = localtime(&setdate);
			strftime(buffer, 80, format, tmp);
			puts(buffer);
		} else {
			printf("ERROR: Failed to set date on device.\n");
		}
	}

	lockdownd_client_free(client);
	idevice_free(phone);

	return 0;
}
int main(int argc, char *argv[])
{
	lockdownd_client_t client = NULL;
	lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
	lockdownd_service_descriptor_t service = NULL;
	idevice_t device = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	int i;
	int op = -1;
	int output_xml = 0;
	const char* udid = NULL;
	const char* param = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "install")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_INSTALL;
			continue;
		}
		else if (!strcmp(argv[i], "list")) {
			op = OP_LIST;
		}
		else if (!strcmp(argv[i], "copy")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_COPY;
			continue;
		}
		else if (!strcmp(argv[i], "remove")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_REMOVE;
			continue;
		}
		else if (!strcmp(argv[i], "dump")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_DUMP;
			continue;
		}
		else if (!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml")) {
			output_xml = 1;
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	if ((op == -1) || (op >= NUM_OPS)) {
		print_usage(argc, argv);
		return 0;
	}

	if (op == OP_DUMP) {
		int res = 0;
		unsigned char* profile_data = NULL;
		unsigned int profile_size = 0;
		if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
			return -1;
		}
		plist_t pdata = plist_new_data((char*)profile_data, profile_size);
		plist_t pl = profile_get_embedded_plist(pdata);
		plist_free(pdata);
		free(profile_data);

		if (pl) {
			if (output_xml) {
				char* xml = NULL;
				uint32_t xlen = 0;
				plist_to_xml(pl, &xml, &xlen);
				if (xml) {
					printf("%s\n", xml);
					free(xml);
				}
			} else {
				if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
					plist_print_to_stream(pl, stdout);
				} else {
					fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n");
					res = -1;
				}
			}
		} else {
			fprintf(stderr, "ERROR: could not extract embedded plist from profile!\n");
		}
		plist_free(pl);

		return res;
	}

	ret = idevice_new(&device, udid);
	if (ret != IDEVICE_E_SUCCESS) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, "ideviceprovision"))) {
		fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
		idevice_free(device);
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_start_service(client, "com.apple.misagent", &service)) {
		fprintf(stderr, "Could not start service \"com.apple.misagent\"\n");
		lockdownd_client_free(client);
		idevice_free(device);
		return -1;
	}
	lockdownd_client_free(client);
	client = NULL;

	misagent_client_t mis = NULL;
	if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
		fprintf(stderr, "Could not connect to \"com.apple.misagent\" on device\n");
		if (service)
			lockdownd_service_descriptor_free(service);
		lockdownd_client_free(client);
		idevice_free(device);
		return -1;
	}

	if (service)
		lockdownd_service_descriptor_free(service);

	switch (op) {
		case OP_INSTALL:
		{
			unsigned char* profile_data = NULL;
			unsigned int profile_size = 0;
			if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
				break;
			}

			uint64_t psize = profile_size;
			plist_t pdata = plist_new_data((const char*)profile_data, psize);
			free(profile_data);

			if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) {
				printf("Profile '%s' installed successfully.\n", param);
			} else {
				int sc = misagent_get_status_code(mis);
				fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc);
			}
		}
			break;
		case OP_LIST:
		case OP_COPY:
		{
			plist_t profiles = NULL;
			if (misagent_copy(mis, &profiles) == MISAGENT_E_SUCCESS) {
				uint32_t num_profiles = plist_array_get_size(profiles);
				printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles");
				uint32_t j;
				for (j = 0; j < num_profiles; j++) {
					char* p_name = NULL;
					char* p_uuid = NULL;
					plist_t profile = plist_array_get_item(profiles, j);
					plist_t pl = profile_get_embedded_plist(profile);
					if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
						plist_t node;
						node = plist_dict_get_item(pl, "Name");
						if (node && (plist_get_node_type(node) == PLIST_STRING)) {
							plist_get_string_val(node, &p_name);
						}
						node = plist_dict_get_item(pl, "UUID");
						if (node && (plist_get_node_type(node) == PLIST_STRING)) {
							plist_get_string_val(node, &p_uuid);
						}
					}
					printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)");
					if (op == OP_COPY) {
						char pfname[512];
						if (p_uuid) {
							sprintf(pfname, "%s/%s.mobileprovision", param, p_uuid);
						} else {
							sprintf(pfname, "%s/profile%d.mobileprovision", param, j);
						}
						FILE* f = fopen(pfname, "wb");
						if (f) {
							char* dt = NULL;
							uint64_t ds = 0;
							plist_get_data_val(profile, &dt, &ds);
							fwrite(dt, 1, ds, f);
							fclose(f);
							printf(" => %s\n", pfname);
						} else {
							fprintf(stderr, "Could not open '%s' for writing\n", pfname);
						}
					}
					if (p_uuid) {
						free(p_uuid);
					}
					if (p_name) {
						free(p_name);
					}
				}
			} else {
				int sc = misagent_get_status_code(mis);
				fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
			}
		}
			break;
		case OP_REMOVE:
			if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) {
				printf("Profile '%s' removed.\n", param);
			} else {
				int sc = misagent_get_status_code(mis);
				fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc);
			}
			break;
		default:
			break;
	}

	misagent_client_free(mis);

	idevice_free(device);

	return 0;
}
int main(int argc, char* argv[]) {
	int opt = 0;
	int optindex = 0;
	char* ipsw = NULL;
	char* uuid = NULL;
	int tss_enabled = 0;
	int shsh_only = 0;
	char* shsh_dir = NULL;
	use_apple_server=1;

	// create an instance of our context
	struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t));
	if (client == NULL) {
		error("ERROR: Out of memory\n");
		return -1;
	}
	memset(client, '\0', sizeof(struct idevicerestore_client_t));

	while ((opt = getopt_long(argc, argv, "dhcesxtpi:u:", longopts, &optindex)) > 0) {
		switch (opt) {
		case 'h':
			usage(argc, argv);
			return 0;

		case 'd':
			client->flags |= FLAG_DEBUG;
			idevicerestore_debug = 1;
			break;

		case 'e':
			client->flags |= FLAG_ERASE;
			break;

		case 'c':
			client->flags |= FLAG_CUSTOM;
			break;

		case 's':
			use_apple_server=0;
			break;

		case 'x':
			client->flags |= FLAG_EXCLUDE;
			break;

		case 'i':
			if (optarg) {
				char* tail = NULL;
				client->ecid = strtoull(optarg, &tail, 16);
				if (tail && (tail[0] != '\0')) {
					client->ecid = 0;
				}
				if (client->ecid == 0) {
					error("ERROR: Could not parse ECID from '%s'\n", optarg);
					return -1;
				}
			}
			break;

		case 'u':
			uuid = optarg;
			break;

		case 't':
			shsh_only = 1;
			break;

		case 'p':
			client->flags |= FLAG_PWN;
			break;

		default:
			usage(argc, argv);
			return -1;
		}
	}

	if (((argc-optind) == 1) || (client->flags & FLAG_PWN)) {
		argc -= optind;
		argv += optind;

		ipsw = argv[0];
	} else {
		usage(argc, argv);
		return -1;
	}

	if (client->flags & FLAG_DEBUG) {
		idevice_set_debug_level(1);
		irecv_set_debug_level(1);
	}

	client->uuid = uuid;
	client->ipsw = ipsw;

	// update version data (from cache, or apple if too old)
	load_version_data(client);

	// check which mode the device is currently in so we know where to start
	if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) {
		error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n");
		return -1;
	}
	info("Found device in %s mode\n", client->mode->string);

	if (client->mode->index == MODE_WTF) {
		int cpid = 0;

		if (dfu_client_new(client) != 0) {
			error("ERROR: Could not open device in WTF mode\n");
			return -1;
		}
		if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { 
			error("ERROR: Could not get CPID for WTF mode device\n");
			dfu_client_free(client);
			return -1;
		}

		char* s_wtfurl = NULL;
		plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL");
		if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) {
			plist_get_string_val(wtfurl, &s_wtfurl);
		}
		if (!s_wtfurl) {
			info("Using hardcoded x12220000_5_Recovery.ipsw URL\n");
			s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw");
		}

		// make a local file name
		char* fnpart = strrchr(s_wtfurl, '/');
		if (!fnpart) {
			fnpart = "x12220000_5_Recovery.ipsw";
		} else {
			fnpart++;
		}
		struct stat fst;
		char wtfipsw[256];
		sprintf(wtfipsw, "cache/%s", fnpart);
		if (stat(wtfipsw, &fst) != 0) {
			__mkdir("cache", 0755);
			download_to_file(s_wtfurl, wtfipsw);
		}

		char wtfname[256];
		sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid);
		char* wtftmp = NULL;
		uint32_t wtfsize = 0;
		ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize);
		if (!wtftmp) {
			error("ERROR: Could not extract WTF\n");
		} else {
			if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) {
				error("ERROR: Could not send WTF...\n");
			}
		}
		dfu_client_free(client);

		sleep(1);

		free(wtftmp);
		client->mode = &idevicerestore_modes[MODE_DFU];
	}

	// discover the device type
	if (check_device(client) < 0 || client->device->index == DEVICE_UNKNOWN) {
		error("ERROR: Unable to discover device type\n");
		return -1;
	}
	info("Identified device as %s\n", client->device->product);

	if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) {
		error("ERROR: you need to put your device into DFU mode to pwn it.\n");
		return -1;
	}

	if (client->flags & FLAG_PWN) {
		recovery_client_free(client);

		info("connecting to DFU\n");
		if (dfu_client_new(client) < 0) {
			return -1;
		}
		info("exploiting with limera1n...\n");
		// TODO: check for non-limera1n device and fail
		if (limera1n_exploit(client->device, client->dfu->client) != 0) {
			error("ERROR: limera1n exploit failed\n");
			dfu_client_free(client);
			return -1;
		}
		dfu_client_free(client);
		info("Device should be in pwned DFU state now.\n");

		return 0;
	}

	if (client->mode->index == MODE_RESTORE) {
		if (restore_reboot(client) < 0) {
			error("ERROR: Unable to exit restore mode\n");
			return -1;
		}
	}

	// extract buildmanifest
	plist_t buildmanifest = NULL;
	if (client->flags & FLAG_CUSTOM) {
		info("Extracting Restore.plist from IPSW\n");
		if (ipsw_extract_restore_plist(ipsw, &buildmanifest) < 0) {
			error("ERROR: Unable to extract Restore.plist from %s\n", ipsw);
			return -1;
		}
	} else {
		info("Extracting BuildManifest from IPSW\n");
		if (ipsw_extract_build_manifest(ipsw, &buildmanifest, &tss_enabled) < 0) {
			error("ERROR: Unable to extract BuildManifest from %s\n", ipsw);
			return -1;
		}
	}

	/* check if device type is supported by the given build manifest */
	if (build_manifest_check_compatibility(buildmanifest, client->device->product) < 0) {
		error("ERROR: could not make sure this firmware is suitable for the current device. refusing to continue.\n");
		return -1;
	}

	/* print iOS information from the manifest */
	build_manifest_get_version_information(buildmanifest, &client->version, &client->build);

	info("Product Version: %s\n", client->version);
	info("Product Build: %s\n", client->build);

	if (client->flags & FLAG_CUSTOM) {
		/* prevent signing custom firmware */
		tss_enabled = 0;
		info("Custom firmware requested. Disabled TSS request.\n");
	}

	// choose whether this is an upgrade or a restore (default to upgrade)
	client->tss = NULL;
	plist_t build_identity = NULL;
	if (client->flags & FLAG_CUSTOM) {
		build_identity = plist_new_dict();
		{
			plist_t node;
			plist_t comp;
			plist_t info;
			plist_t manifest;

			info = plist_new_dict();
			plist_dict_insert_item(info, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update"));
			plist_dict_insert_item(info, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)"));
			plist_dict_insert_item(build_identity, "Info", info);

			manifest = plist_new_dict();

			char tmpstr[256];
			char p_all_flash[128];
			char lcmodel[8];
			strcpy(lcmodel, client->device->model);
			int x = 0;
			while (lcmodel[x]) {
				lcmodel[x] = tolower(lcmodel[x]);
				x++;
			}

			sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production");
			strcpy(tmpstr, p_all_flash);
			strcat(tmpstr, "/manifest");

			// get all_flash file manifest
			char *files[16];
			char *fmanifest = NULL;
			uint32_t msize = 0;
			if (ipsw_extract_to_memory(ipsw, tmpstr, &fmanifest, &msize) < 0) {
				error("ERROR: could not extract %s from IPSW\n", tmpstr);
				return -1;
			}

			char *tok = strtok(fmanifest, "\r\n");
			int fc = 0;
			while (tok) {
				files[fc++] = strdup(tok);
				if (fc >= 16) {
					break;
				}
				tok = strtok(NULL, "\r\n");
			}
			free(fmanifest);

			for (x = 0; x < fc; x++) {
				info = plist_new_dict();
				strcpy(tmpstr, p_all_flash);
				strcat(tmpstr, "/");
				strcat(tmpstr, files[x]);
				plist_dict_insert_item(info, "Path", plist_new_string(tmpstr));
				comp = plist_new_dict();
				plist_dict_insert_item(comp, "Info", info);
				const char* compname = get_component_name(files[x]);
				if (compname) {
					plist_dict_insert_item(manifest, compname, comp);
					if (!strncmp(files[x], "DeviceTree", 10)) {
						plist_dict_insert_item(manifest, "RestoreDeviceTree", plist_copy(comp));
					}
				} else {
					error("WARNING: unhandled component %s\n", files[x]);
					plist_free(comp);
				}
				free(files[x]);
				files[x] = NULL;
			}

			// add iBSS
			sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE");
			info = plist_new_dict();
			plist_dict_insert_item(info, "Path", plist_new_string(tmpstr));
			comp = plist_new_dict();
			plist_dict_insert_item(comp, "Info", info);
			plist_dict_insert_item(manifest, "iBSS", comp);

			// add iBEC
			sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE");
			info = plist_new_dict();
			plist_dict_insert_item(info, "Path", plist_new_string(tmpstr));
			comp = plist_new_dict();
			plist_dict_insert_item(comp, "Info", info);
			plist_dict_insert_item(manifest, "iBEC", comp);

			// add kernel cache
			node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget");
			if (node && (plist_get_node_type(node) == PLIST_DICT)) {
				char tt[4];
				strncpy(tt, lcmodel, 3);
				tt[3] = 0;
				plist_t kdict = plist_dict_get_item(node, tt);
				if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) {
					plist_t kc = plist_dict_get_item(kdict, "Release");
					if (kc && (plist_get_node_type(kc) == PLIST_STRING)) {
						info = plist_new_dict();
						plist_dict_insert_item(info, "Path", plist_copy(kc));
						comp = plist_new_dict();
						plist_dict_insert_item(comp, "Info", info);
						plist_dict_insert_item(manifest, "KernelCache", comp);
						plist_dict_insert_item(manifest, "RestoreKernelCache", plist_copy(comp));

					}
				}
			}

			// add ramdisk
			node = plist_dict_get_item(buildmanifest, "RestoreRamDisks");
			if (node && (plist_get_node_type(node) == PLIST_DICT)) {
				plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update");
				if (rd && (plist_get_node_type(rd) == PLIST_STRING)) {
					info = plist_new_dict();
					plist_dict_insert_item(info, "Path", plist_copy(rd));
					comp = plist_new_dict();
					plist_dict_insert_item(comp, "Info", info);
					plist_dict_insert_item(manifest, "RestoreRamDisk", comp);
				}
			}

			// add OS filesystem
			node = plist_dict_get_item(buildmanifest, "SystemRestoreImages");
			if (!node) {
				error("ERROR: missing SystemRestoreImages in Restore.plist\n");
			}
			plist_t os = plist_dict_get_item(node, "User");
			if (!os) {
				error("ERROR: missing filesystem in Restore.plist\n");
			} else {
				info = plist_new_dict();
				plist_dict_insert_item(info, "Path", plist_copy(os));
				comp = plist_new_dict();
				plist_dict_insert_item(comp, "Info", info);
				plist_dict_insert_item(manifest, "OS", comp);
			}

			// finally add manifest
			plist_dict_insert_item(build_identity, "Manifest", manifest);
		}
	} else if (client->flags & FLAG_ERASE) {
		build_identity = build_manifest_get_build_identity(buildmanifest, 0);
		if (build_identity == NULL) {
			error("ERROR: Unable to find any build identities\n");
			plist_free(buildmanifest);
			return -1;
		}
	} else {
		// loop through all build identities in the build manifest
		// and list the valid ones
		int i = 0;
		int valid_builds = 0;
		int build_count = build_manifest_get_identity_count(buildmanifest);
		for (i = 0; i < build_count; i++) {
			build_identity = build_manifest_get_build_identity(buildmanifest, i);
			valid_builds++;
		}
	}

	/* print information about current build identity */
	build_identity_print_information(build_identity);

	/* retrieve shsh blobs if required */
	if (tss_enabled) {
		debug("Getting device's ECID for TSS request\n");
		/* fetch the device's ECID for the TSS request */
		if (get_ecid(client, &client->ecid) < 0) {
			error("ERROR: Unable to find device ECID\n");
			return -1;
		}
		info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid);

		if (get_shsh_blobs(client, client->ecid, NULL, 0, build_identity, &client->tss) < 0) {
			error("ERROR: Unable to get SHSH blobs for this device\n");
			return -1;
		}
	}

	if (shsh_only) {
		if (!tss_enabled) {
			info("This device does not require a TSS record");
			return 0;
		}
		if (!client->tss) {
			error("ERROR: could not fetch TSS record");
			plist_free(buildmanifest);
			return -1;
		} else {
			char *bin = NULL;
			uint32_t blen = 0;
			plist_to_bin(client->tss, &bin, &blen);
			if (bin) {
				char zfn[512];
				sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product, client->version);
				__mkdir("shsh", 0755);
				struct stat fst;
				if (stat(zfn, &fst) != 0) {
					gzFile zf = gzopen(zfn, "wb");
					gzwrite(zf, bin, blen);
					gzclose(zf);
					info("SHSH saved to '%s'\n", zfn);
				} else {
					info("SHSH '%s' already present.\n", zfn);
				}
				free(bin);
			} else {
				error("ERROR: could not get TSS record data\n");
			}
			plist_free(client->tss);
			plist_free(buildmanifest);
			return 0;
		}
	}

	/* verify if we have tss records if required */
	if ((tss_enabled) && (client->tss == NULL)) {
		error("ERROR: Unable to proceed without a TSS record.\n");
		plist_free(buildmanifest);
		return -1;
	}

	if ((tss_enabled) && client->tss) {
		/* fix empty dicts */
		fixup_tss(client->tss);
	}

	// Extract filesystem from IPSW and return its name
	char* filesystem = NULL;
	if (ipsw_extract_filesystem(client->ipsw, build_identity, &filesystem) < 0) {
		error("ERROR: Unable to extract filesystem from IPSW\n");
		if (client->tss)
			plist_free(client->tss);
		plist_free(buildmanifest);
		return -1;
	}

	// if the device is in normal mode, place device into recovery mode
	if (client->mode->index == MODE_NORMAL) {
		info("Entering recovery mode...\n");
		if (normal_enter_recovery(client) < 0) {
			error("ERROR: Unable to place device into recovery mode\n");
			if (client->tss)
				plist_free(client->tss);
			plist_free(buildmanifest);
			return -1;
		}
	}

	// if the device is in DFU mode, place device into recovery mode
	if (client->mode->index == MODE_DFU) {
		recovery_client_free(client);
		if (client->flags & FLAG_CUSTOM) {
			info("connecting to DFU\n");
			if (dfu_client_new(client) < 0) {
				return -1;
			}
			info("exploiting with limera1n\n");
			// TODO: check for non-limera1n device and fail
			if (limera1n_exploit(client->device, client->dfu->client) != 0) {
				error("ERROR: limera1n exploit failed\n");
				dfu_client_free(client);
				return -1;
			}
			dfu_client_free(client);
			info("exploited\n");
		}
		if (dfu_enter_recovery(client, build_identity) < 0) {
			error("ERROR: Unable to place device into recovery mode\n");
			plist_free(buildmanifest);
			if (client->tss)
				plist_free(client->tss);
			return -1;
		}
	}

	if (client->mode->index == MODE_DFU) {
		client->mode = &idevicerestore_modes[MODE_RECOVERY];
	} else {
		/* now we load the iBEC */
		if (recovery_send_ibec(client, build_identity) < 0) {
			error("ERROR: Unable to send iBEC\n");
			return -1;
		}
		recovery_client_free(client);
	
		/* this must be long enough to allow the device to run the iBEC */
		/* FIXME: Probably better to detect if the device is back then */
		sleep(7);
	}

	if (client->build[0] > '8') {
		// we need another tss request with nonce.
		unsigned char* nonce = NULL;
		int nonce_size = 0;
		int nonce_changed = 0;
		if (get_nonce(client, &nonce, &nonce_size) < 0) {
			error("ERROR: Unable to get nonce from device!\n");
			recovery_send_reset(client);
			return -1;
		}

		if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) {
			nonce_changed = 1;
			if (client->nonce) {
				free(client->nonce);
			}
			client->nonce = nonce;
			client->nonce_size = nonce_size;
		} else {
			free(nonce);
		}

		info("Nonce: ");
		int i;
		for (i = 0; i < client->nonce_size; i++) {
			info("%02x ", client->nonce[i]);
		}
		info("\n");

		if (nonce_changed && !(client->flags & FLAG_CUSTOM)) {
			// Welcome iOS5. We have to re-request the TSS with our nonce.
			plist_free(client->tss);
			if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) {
				error("ERROR: Unable to get SHSH blobs for this device\n");
				return -1;
			}
			if (!client->tss) {
				error("ERROR: can't continue without TSS\n");
				return -1;
			}
			fixup_tss(client->tss);
		}
	}

	// now finally do the magic to put the device into restore mode
	if (client->mode->index == MODE_RECOVERY) {
		if (client->srnm == NULL) {
			error("ERROR: could not retrieve device serial number. Can't continue.\n");
			return -1;
		}
		if (recovery_enter_restore(client, build_identity) < 0) {
			error("ERROR: Unable to place device into restore mode\n");
			plist_free(buildmanifest);
			if (client->tss)
				plist_free(client->tss);
			return -1;
		}
	}

	// device is finally in restore mode, let's do this
	if (client->mode->index == MODE_RESTORE) {
		info("About to restore device... \n");
		if (restore_device(client, build_identity, filesystem) < 0) {
			error("ERROR: Unable to restore device\n");
			return -1;
		}
	}

	info("Cleaning up...\n");
	if (filesystem)
		unlink(filesystem);

	info("DONE\n");
	return 0;
}
int main(int argc, char *argv[])
{
	lockdownd_client_t client = NULL;
	lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
	idevice_t device = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	int i;
	const char* udid = NULL;
	time_t setdate = 0;
	plist_t node = NULL;
	int node_type = -1;
	uint64_t datetime = 0;
	time_t rawtime;
	struct tm * tmp;
	char const *format = NULL;
	char buffer[80];
	int result = 0;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--set")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) <= 1)) {
				print_usage(argc, argv);
				return 0;
			}
			setdate = atoi(argv[i]);
			if (setdate == 0) {
				printf("ERROR: Invalid timestamp value.\n");
				print_usage(argc, argv);
				return 0;
			}
			continue;
		}
		else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--sync")) {
			i++;
			/* get current time */
			setdate = time(NULL);
			/* convert it to local time which sets timezone/daylight variables */
			tmp = localtime(&setdate);
			/* recalculate to make it UTC */
			setdate = mktime(tmp);
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	/* determine a date format */
	if (!format) {
		format = DATE_FMT_LANGINFO ();
		if (!*format) {
			format = "%a %b %e %H:%M:%S %Z %Y";
		}
	}

	ret = idevice_new(&device, udid);
	if (ret != IDEVICE_E_SUCCESS) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, "idevicedate"))) {
		fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
		result = -1;
		goto cleanup;
	}

	if(lockdownd_get_value(client, NULL, "TimeIntervalSince1970", &node) != LOCKDOWN_E_SUCCESS) {
		fprintf(stderr, "ERROR: Unable to retrieve 'TimeIntervalSince1970' node from device.\n");
		result = -1;
		goto cleanup;
	}

	if (node == NULL) {
		fprintf(stderr, "ERROR: Empty node for 'TimeIntervalSince1970' received.\n");
		result = -1;
		goto cleanup;
	}

	node_type = plist_get_node_type(node);

	/* get or set? */
	if (setdate == 0) {
		/* get time value from device */
		switch (node_type) {
			case PLIST_UINT:
				plist_get_uint_val(node, &datetime);
				break;
			case PLIST_REAL:
				{
					double rv = 0;
					plist_get_real_val(node, &rv);
					datetime = rv;
				}
				break;
			default:
				fprintf(stderr, "ERROR: Unexpected node type for 'TimeIntervalSince1970'\n");
				break;
		}
		plist_free(node);
		node = NULL;

		/* date/time calculations */
		rawtime = (time_t)datetime;
		tmp = localtime(&rawtime);

		/* finally we format and print the current date */
		strftime(buffer, 80, format, tmp);
		puts(buffer);
	} else {
		datetime = setdate;

		plist_free(node);
		node = NULL;

		switch (node_type) {
			case PLIST_UINT:
				node = plist_new_uint(datetime);
				break;
			case PLIST_REAL:
				node = plist_new_real((double)datetime);
				break;
			default:
				fprintf(stderr, "ERROR: Unexpected node type for 'TimeIntervalSince1970'\n");
				break;
		}

		if(lockdownd_set_value(client, NULL, "TimeIntervalSince1970", node) == LOCKDOWN_E_SUCCESS) {
			tmp = localtime(&setdate);
			strftime(buffer, 80, format, tmp);
			puts(buffer);
		} else {
			printf("ERROR: Failed to set date on device.\n");
		}
		node = NULL;
	}

cleanup:
	if (client)
		lockdownd_client_free(client);

	if (device)
		idevice_free(device);

	return result;
}
int main(int argc, char *argv[])
{
	lockdownd_client_t client = NULL;
	idevice_t phone = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	int i;
	int simple = 0;
	int format = FORMAT_KEY_VALUE;
	char uuid[41];
	char *domain = NULL;
	char *key = NULL;
	char *xml_doc = NULL;
	uint32_t xml_length;
	plist_t node = NULL;
	plist_type node_type;
	uuid[0] = 0;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			strcpy(uuid, argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--domain")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 4)) {
				print_usage(argc, argv);
				return 0;
			}
			if (!is_domain_known(argv[i])) {
				fprintf(stderr, "WARNING: Sending query with unknown domain \"%s\".\n", argv[i]);
			}
			domain = strdup(argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--key")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) <= 1)) {
				print_usage(argc, argv);
				return 0;
			}
			key = strdup(argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml")) {
			format = FORMAT_XML;
			continue;
		}
		else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--simple")) {
			simple = 1;
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	if (uuid[0] != 0) {
		ret = idevice_new(&phone, uuid);
		if (ret != IDEVICE_E_SUCCESS) {
			printf("No device found with uuid %s, is it plugged in?\n", uuid);
			return -1;
		}
	}
	else
	{
		ret = idevice_new(&phone, NULL);
		if (ret != IDEVICE_E_SUCCESS) {
			printf("No device found, is it plugged in?\n");
			return -1;
		}
	}

	if (LOCKDOWN_E_SUCCESS != (simple ?
			lockdownd_client_new(phone, &client, "ideviceinfo"):
			lockdownd_client_new_with_handshake(phone, &client, "ideviceinfo"))) {
		idevice_free(phone);
		return -1;
	}

	/* run query and output information */
	if(lockdownd_get_value(client, domain, key, &node) == LOCKDOWN_E_SUCCESS) {
		if (node) {
			switch (format) {
			case FORMAT_XML:
				plist_to_xml(node, &xml_doc, &xml_length);
				printf("%s", xml_doc);
				free(xml_doc);
				break;
			case FORMAT_KEY_VALUE:
				node_type = plist_get_node_type(node);
				if (node_type == PLIST_DICT) {
					plist_dict_to_string(node);
				} else if (node_type == PLIST_ARRAY) {
					plist_array_to_string(node);
					break;
				}
			default:
				if (key != NULL)
					plist_node_to_string(node);
			break;
			}
			plist_free(node);
			node = NULL;
		}
	}

	if (domain != NULL)
		free(domain);
	lockdownd_client_free(client);
	idevice_free(phone);

	return 0;
}
int main(int argc, char **argv)
{
	idevice_t device = NULL;
	lockdownd_client_t lockdown_client = NULL;
	diagnostics_relay_client_t diagnostics_client = NULL;
	lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
	lockdownd_service_descriptor_t service = NULL;
	int result = -1;
	int i;
	const char *udid = NULL;
	int cmd = CMD_NONE;
	char* cmd_arg = NULL;
	plist_t node = NULL;
	plist_t keys = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				result = 0;
				goto cleanup;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			result = 0;
			goto cleanup;
		}
		else if (!strcmp(argv[i], "sleep")) {
			cmd = CMD_SLEEP;
		}
		else if (!strcmp(argv[i], "restart")) {
			cmd = CMD_RESTART;
		}
		else if (!strcmp(argv[i], "shutdown")) {
			cmd = CMD_SHUTDOWN;
		}
		else if (!strcmp(argv[i], "diagnostics")) {
			cmd = CMD_DIAGNOSTICS;
			/*  read type */
			i++;
			if (!argv[i] || ((strcmp(argv[i], "All") != 0) && (strcmp(argv[i], "WiFi") != 0) && (strcmp(argv[i], "GasGauge") != 0) && (strcmp(argv[i], "NAND") != 0))) {
				if (argv[i] == NULL) {
					cmd_arg = strdup("All");
					continue;
				}

				if (!strncmp(argv[i], "-", 1)) {
					cmd_arg = strdup("All");
					i--;
					continue;
				}

				printf("Unknown TYPE %s\n", argv[i]);
				print_usage(argc, argv);
				goto cleanup;
			}

			cmd_arg = strdup(argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "mobilegestalt")) {
			cmd = CMD_MOBILEGESTALT;
			/*  read keys */
			i++;

			if (!argv[i] || argv[i] == NULL || (!strncmp(argv[i], "-", 1))) {
				printf("Please supply the key to query.\n");
				print_usage(argc, argv);
				goto cleanup;
			}

			keys = plist_new_array();
			while(1) {
				if (argv[i] && (strlen(argv[i]) >= 2) && (strncmp(argv[i], "-", 1) != 0)) {
					plist_array_append_item(keys, plist_new_string(argv[i]));
					i++;
				} else {
					i--;
					break;
				}
			}
			continue;
		}
		else if (!strcmp(argv[i], "ioreg")) {
			cmd = CMD_IOREGISTRY;
			/*  read plane */
			i++;
			if (argv[i]) {
				cmd_arg = strdup(argv[i]);
			}
			continue;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	/* verify options */
	if (cmd == CMD_NONE) {
		print_usage(argc, argv);
		goto cleanup;
	}

	if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);	
		} else {
			printf("No device found, is it plugged in?\n");
		}
		goto cleanup;
	}

	if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &lockdown_client, "idevicediagnostics"))) {
		idevice_free(device);
		printf("ERROR: Could not connect to lockdownd, error code %d\n", ret);
		goto cleanup;
	}

	/*  attempt to use newer diagnostics service available on iOS 5 and later */
	ret = lockdownd_start_service(lockdown_client, "com.apple.mobile.diagnostics_relay", &service);
	if (ret != LOCKDOWN_E_SUCCESS) {
		/*  attempt to use older diagnostics service */
		ret = lockdownd_start_service(lockdown_client, "com.apple.iosdiagnostics.relay", &service);
	}

	lockdownd_client_free(lockdown_client);

	if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) {
		if (diagnostics_relay_client_new(device, service, &diagnostics_client) != DIAGNOSTICS_RELAY_E_SUCCESS) {
			printf("Could not connect to diagnostics_relay!\n");
			result = -1;
		} else {
			switch (cmd) {
				case CMD_SLEEP:
					if (diagnostics_relay_sleep(diagnostics_client) == DIAGNOSTICS_RELAY_E_SUCCESS) {
						printf("Putting device into deep sleep mode.\n");
						result = EXIT_SUCCESS;
					} else {
						printf("Failed to put device into deep sleep mode.\n");
					}
				break;
				case CMD_RESTART:
					if (diagnostics_relay_restart(diagnostics_client, DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) == DIAGNOSTICS_RELAY_E_SUCCESS) {
						printf("Restarting device.\n");
						result = EXIT_SUCCESS;
					} else {
						printf("Failed to restart device.\n");
					}
				break;
				case CMD_SHUTDOWN:
					if (diagnostics_relay_shutdown(diagnostics_client, DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) == DIAGNOSTICS_RELAY_E_SUCCESS) {
						printf("Shutting down device.\n");
						result = EXIT_SUCCESS;
					} else {
						printf("Failed to shutdown device.\n");
					}
				break;
				case CMD_MOBILEGESTALT:
					if (diagnostics_relay_query_mobilegestalt(diagnostics_client, keys, &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
						if (node) {
							print_xml(node);
							result = EXIT_SUCCESS;
						}
					} else {
						printf("Unable to query mobilegestalt keys.\n");
					}
				break;
				case CMD_IOREGISTRY:
					if (diagnostics_relay_query_ioregistry_plane(diagnostics_client, cmd_arg == NULL ? "": cmd_arg, &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
						if (node) {
							print_xml(node);
							result = EXIT_SUCCESS;
						}
					} else {
						printf("Unable to retrieve IORegistry from device.\n");
					}
					break;
				case CMD_DIAGNOSTICS:
				default:
					if (diagnostics_relay_request_diagnostics(diagnostics_client, cmd_arg, &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
						if (node) {
							print_xml(node);
							result = EXIT_SUCCESS;
						}
					} else {
						printf("Unable to retrieve diagnostics from device.\n");
					}
					break;
			}

			diagnostics_relay_goodbye(diagnostics_client);
			diagnostics_relay_client_free(diagnostics_client);
		}
	} else {
		printf("Could not start diagnostics service!\n");
	}

	if (service) {
		lockdownd_service_descriptor_free(service);
		service = NULL;
	}

	idevice_free(device);

cleanup:
	if (node) {
		plist_free(node);
	}
	if (keys) {
		plist_free(keys);
	}
	if (cmd_arg) {
		free(cmd_arg);
	}
	return result;
}
int idevicerestore_start(struct idevicerestore_client_t* client)
{
	int tss_enabled = 0;
	int result = 0;

	if (!client) {
		return -1;
	}

	if ((client->flags & FLAG_LATEST) && (client->flags & FLAG_CUSTOM)) {
		error("ERROR: FLAG_LATEST cannot be used with FLAG_CUSTOM.\n");
		return -1;
	}

	if (!client->ipsw && !(client->flags & FLAG_PWN) && !(client->flags & FLAG_LATEST)) {
		error("ERROR: no ipsw file given\n");
		return -1;
	}

	if (client->flags & FLAG_DEBUG) {
		idevice_set_debug_level(1);
		irecv_set_debug_level(1);
		idevicerestore_debug = 1;
	}

	idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.0);

	// update version data (from cache, or apple if too old)
	load_version_data(client);

	// check which mode the device is currently in so we know where to start
	if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) {
		error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n");
		return -1;
	}
	idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.1);
	info("Found device in %s mode\n", client->mode->string);

	if (client->mode->index == MODE_WTF) {
		unsigned int cpid = 0;

		if (dfu_client_new(client) != 0) {
			error("ERROR: Could not open device in WTF mode\n");
			return -1;
		}
		if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { 
			error("ERROR: Could not get CPID for WTF mode device\n");
			dfu_client_free(client);
			return -1;
		}

		char wtfname[256];
		sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid);
		unsigned char* wtftmp = NULL;
		unsigned int wtfsize = 0;

		// Prefer to get WTF file from the restore IPSW
		ipsw_extract_to_memory(client->ipsw, wtfname, &wtftmp, &wtfsize);
		if (!wtftmp) {
			// Download WTF IPSW
			char* s_wtfurl = NULL;
			plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL");
			if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) {
				plist_get_string_val(wtfurl, &s_wtfurl);
			}
			if (!s_wtfurl) {
				info("Using hardcoded x12220000_5_Recovery.ipsw URL\n");
				s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw");
			}

			// make a local file name
			char* fnpart = strrchr(s_wtfurl, '/');
			if (!fnpart) {
				fnpart = "x12220000_5_Recovery.ipsw";
			} else {
				fnpart++;
			}
			struct stat fst;
			char wtfipsw[1024];
			if (client->cache_dir) {
				if (stat(client->cache_dir, &fst) < 0) {
					mkdir_with_parents(client->cache_dir, 0755);
				}
				strcpy(wtfipsw, client->cache_dir);
				strcat(wtfipsw, "/");
				strcat(wtfipsw, fnpart);
			} else {
				strcpy(wtfipsw, fnpart);
			}
			if (stat(wtfipsw, &fst) != 0) {
				download_to_file(s_wtfurl, wtfipsw, 0);
			}

			ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize);
			if (!wtftmp) {
				error("ERROR: Could not extract WTF\n");
			}
		}

		if (wtftmp) {
			if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) {
				error("ERROR: Could not send WTF...\n");
			}
		}
		dfu_client_free(client);

		sleep(1);

		free(wtftmp);
		client->mode = &idevicerestore_modes[MODE_DFU];
	}

	// discover the device type
	if (check_product_type(client) == NULL || client->device == NULL) {
		error("ERROR: Unable to discover device type\n");
		return -1;
	}
	idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.2);
	info("Identified device as %s\n", client->device->product_type);

	if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) {
		error("ERROR: you need to put your device into DFU mode to pwn it.\n");
		return -1;
	}

	if (client->flags & FLAG_PWN) {
		recovery_client_free(client);

		info("connecting to DFU\n");
		if (dfu_client_new(client) < 0) {
			return -1;
		}
		info("exploiting with limera1n...\n");
		// TODO: check for non-limera1n device and fail
		if (limera1n_exploit(client->device, &client->dfu->client) != 0) {
			error("ERROR: limera1n exploit failed\n");
			dfu_client_free(client);
			return -1;
		}
		dfu_client_free(client);
		info("Device should be in pwned DFU state now.\n");

		return 0;
	}

	if (client->flags & FLAG_LATEST) {
		char* ipsw = NULL;
		int res = ipsw_download_latest_fw(client->version_data, client->device->product_type, "cache", &ipsw);
		if (res != 0) {
			if (ipsw) {
				free(ipsw);
			}
			return res;
		} else {
			client->ipsw = ipsw;
		}
	}
	idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6);

	if (client->flags & FLAG_NOACTION) {
		return 0;
	}

	if (client->mode->index == MODE_RESTORE) {
		if (restore_reboot(client) < 0) {
			error("ERROR: Unable to exit restore mode\n");
			return -2;
		}

		// we need to refresh the current mode again
		check_mode(client);
		info("Found device in %s mode\n", client->mode->string);
	}

	// verify if ipsw file exists
	if (access(client->ipsw, F_OK) < 0) {
		error("ERROR: Firmware file %s does not exist.\n", client->ipsw);
		return -1;
	}

	// extract buildmanifest
	plist_t buildmanifest = NULL;
	if (client->flags & FLAG_CUSTOM) {
		info("Extracting Restore.plist from IPSW\n");
		if (ipsw_extract_restore_plist(client->ipsw, &buildmanifest) < 0) {
			error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw);
			return -1;
		}
	} else {
		info("Extracting BuildManifest from IPSW\n");
		if (ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &tss_enabled) < 0) {
			error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw);
			return -1;
		}
	}
	idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8);

	/* check if device type is supported by the given build manifest */
	if (build_manifest_check_compatibility(buildmanifest, client->device->product_type) < 0) {
		error("ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n");
		return -1;
	}

	/* print iOS information from the manifest */
	build_manifest_get_version_information(buildmanifest, client);

	info("Product Version: %s\n", client->version);
	info("Product Build: %s Major: %d\n", client->build, client->build_major);

	if (client->flags & FLAG_CUSTOM) {
		/* prevent signing custom firmware */
		tss_enabled = 0;
		info("Custom firmware requested. Disabled TSS request.\n");
	}

	// choose whether this is an upgrade or a restore (default to upgrade)
	client->tss = NULL;
	plist_t build_identity = NULL;
	if (client->flags & FLAG_CUSTOM) {
		build_identity = plist_new_dict();
		{
			plist_t node;
			plist_t comp;
			plist_t inf;
			plist_t manifest;

			char tmpstr[256];
			char p_all_flash[128];
			char lcmodel[8];
			strcpy(lcmodel, client->device->hardware_model);
			int x = 0;
			while (lcmodel[x]) {
				lcmodel[x] = tolower(lcmodel[x]);
				x++;
			}

			sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production");
			strcpy(tmpstr, p_all_flash);
			strcat(tmpstr, "/manifest");

			// get all_flash file manifest
			char *files[16];
			char *fmanifest = NULL;
			uint32_t msize = 0;
			if (ipsw_extract_to_memory(client->ipsw, tmpstr, (unsigned char**)&fmanifest, &msize) < 0) {
				error("ERROR: could not extract %s from IPSW\n", tmpstr);
				return -1;
			}

			char *tok = strtok(fmanifest, "\r\n");
			int fc = 0;
			while (tok) {
				files[fc++] = strdup(tok);
				if (fc >= 16) {
					break;
				}
				tok = strtok(NULL, "\r\n");
			}
			free(fmanifest);

			manifest = plist_new_dict();

			for (x = 0; x < fc; x++) {
				inf = plist_new_dict();
				strcpy(tmpstr, p_all_flash);
				strcat(tmpstr, "/");
				strcat(tmpstr, files[x]);
				plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr));
				comp = plist_new_dict();
				plist_dict_insert_item(comp, "Info", inf);
				const char* compname = get_component_name(files[x]);
				if (compname) {
					plist_dict_insert_item(manifest, compname, comp);
					if (!strncmp(files[x], "DeviceTree", 10)) {
						plist_dict_insert_item(manifest, "RestoreDeviceTree", plist_copy(comp));
					}
				} else {
					error("WARNING: unhandled component %s\n", files[x]);
					plist_free(comp);
				}
				free(files[x]);
				files[x] = NULL;
			}

			// add iBSS
			sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE");
			inf = plist_new_dict();
			plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr));
			comp = plist_new_dict();
			plist_dict_insert_item(comp, "Info", inf);
			plist_dict_insert_item(manifest, "iBSS", comp);

			// add iBEC
			sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE");
			inf = plist_new_dict();
			plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr));
			comp = plist_new_dict();
			plist_dict_insert_item(comp, "Info", inf);
			plist_dict_insert_item(manifest, "iBEC", comp);

			// add kernel cache
			plist_t kdict = NULL;

			node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget");
			if (node && (plist_get_node_type(node) == PLIST_DICT)) {
				char tt[4];
				strncpy(tt, lcmodel, 3);
				tt[3] = 0;
				kdict = plist_dict_get_item(node, tt);
			} else {
				// Populated in older iOS IPSWs
				kdict = plist_dict_get_item(buildmanifest, "RestoreKernelCaches");
			}
			if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) {
				plist_t kc = plist_dict_get_item(kdict, "Release");
				if (kc && (plist_get_node_type(kc) == PLIST_STRING)) {
					inf = plist_new_dict();
					plist_dict_insert_item(inf, "Path", plist_copy(kc));
					comp = plist_new_dict();
					plist_dict_insert_item(comp, "Info", inf);
					plist_dict_insert_item(manifest, "KernelCache", comp);
					plist_dict_insert_item(manifest, "RestoreKernelCache", plist_copy(comp));
				}
			}

			// add ramdisk
			node = plist_dict_get_item(buildmanifest, "RestoreRamDisks");
			if (node && (plist_get_node_type(node) == PLIST_DICT)) {
				plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update");
				// if no "Update" ram disk entry is found try "User" ram disk instead
				if (!rd && !(client->flags & FLAG_ERASE)) {
					rd = plist_dict_get_item(node, "User");
					// also, set the ERASE flag since we actually change the restore variant
					client->flags |= FLAG_ERASE;
				}
				if (rd && (plist_get_node_type(rd) == PLIST_STRING)) {
					inf = plist_new_dict();
					plist_dict_insert_item(inf, "Path", plist_copy(rd));
					comp = plist_new_dict();
					plist_dict_insert_item(comp, "Info", inf);
					plist_dict_insert_item(manifest, "RestoreRamDisk", comp);
				}
			}

			// add OS filesystem
			node = plist_dict_get_item(buildmanifest, "SystemRestoreImages");
			if (!node) {
				error("ERROR: missing SystemRestoreImages in Restore.plist\n");
			}
			plist_t os = plist_dict_get_item(node, "User");
			if (!os) {
				error("ERROR: missing filesystem in Restore.plist\n");
			} else {
				inf = plist_new_dict();
				plist_dict_insert_item(inf, "Path", plist_copy(os));
				comp = plist_new_dict();
				plist_dict_insert_item(comp, "Info", inf);
				plist_dict_insert_item(manifest, "OS", comp);
			}

			// add info
			inf = plist_new_dict();
			plist_dict_insert_item(inf, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update"));
			plist_dict_insert_item(inf, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)"));
			plist_dict_insert_item(build_identity, "Info", inf);

			// finally add manifest
			plist_dict_insert_item(build_identity, "Manifest", manifest);
		}
	} else if (client->flags & FLAG_ERASE) {
		build_identity = build_manifest_get_build_identity(buildmanifest, 0);
		if (build_identity == NULL) {
			error("ERROR: Unable to find any build identities\n");
			plist_free(buildmanifest);
			return -1;
		}
	} else {
		// loop through all build identities in the build manifest
		// and list the valid ones
		int i = 0;
		int valid_builds = 0;
		int build_count = build_manifest_get_identity_count(buildmanifest);
		for (i = 0; i < build_count; i++) {
			build_identity = build_manifest_get_build_identity(buildmanifest, i);
			valid_builds++;
		}
	}

	/* print information about current build identity */
	build_identity_print_information(build_identity);

	idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.0);
	/* retrieve shsh blobs if required */
	if (tss_enabled) {
		debug("Getting device's ECID for TSS request\n");
		/* fetch the device's ECID for the TSS request */
		if (get_ecid(client, &client->ecid) < 0) {
			error("ERROR: Unable to find device ECID\n");
			return -1;
		}
		info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid);

		if (client->build_major > 8) {
			unsigned char* nonce = NULL;
			int nonce_size = 0;
			int nonce_changed = 0;
			if (get_nonce(client, &nonce, &nonce_size) < 0) {
				/* the first nonce request with older firmware releases can fail and it's OK */
				info("NOTE: Unable to get nonce from device\n");
			}

			if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) {
				nonce_changed = 1;
				if (client->nonce) {
					free(client->nonce);
				}
				client->nonce = nonce;
				client->nonce_size = nonce_size;
			} else {
				free(nonce);
			}
		}

		if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) {
			error("ERROR: Unable to get SHSH blobs for this device\n");
			return -1;
		}
	}

	if (client->flags & FLAG_SHSHONLY) {
		if (!tss_enabled) {
			info("This device does not require a TSS record");
			return 0;
		}
		if (!client->tss) {
			error("ERROR: could not fetch TSS record");
			plist_free(buildmanifest);
			return -1;
		} else {
			char *bin = NULL;
			uint32_t blen = 0;
			plist_to_bin(client->tss, &bin, &blen);
			if (bin) {
				char zfn[1024];
				if (client->cache_dir) {
					strcpy(zfn, client->cache_dir);
					strcat(zfn, "/shsh");
				} else {
					strcpy(zfn, "shsh");
				}
				mkdir_with_parents(zfn, 0755);
				sprintf(zfn+strlen(zfn), "/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product_type, client->version);
				struct stat fst;
				if (stat(zfn, &fst) != 0) {
					gzFile zf = gzopen(zfn, "wb");
					gzwrite(zf, bin, blen);
					gzclose(zf);
					info("SHSH saved to '%s'\n", zfn);
				} else {
					info("SHSH '%s' already present.\n", zfn);
				}
				free(bin);
			} else {
				error("ERROR: could not get TSS record data\n");
			}
			plist_free(client->tss);
			plist_free(buildmanifest);
			return 0;
		}
	}

	/* verify if we have tss records if required */
	if ((tss_enabled) && (client->tss == NULL)) {
		error("ERROR: Unable to proceed without a TSS record.\n");
		plist_free(buildmanifest);
		return -1;
	}

	if ((tss_enabled) && client->tss) {
		/* fix empty dicts */
		fixup_tss(client->tss);
	}
	idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.1);

	// if the device is in normal mode, place device into recovery mode
	if (client->mode->index == MODE_NORMAL) {
		info("Entering recovery mode...\n");
		if (normal_enter_recovery(client) < 0) {
			error("ERROR: Unable to place device into recovery mode from %s mode\n", client->mode->string);
			if (client->tss)
				plist_free(client->tss);
			plist_free(buildmanifest);
			return -5;
		}
	}

	// Get filesystem name from build identity
	char* fsname = NULL;
	if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) {
		error("ERROR: Unable get path for filesystem component\n");
		return -1;
	}

	// check if we already have an extracted filesystem
	int delete_fs = 0;
	char* filesystem = NULL;
	struct stat st;
	memset(&st, '\0', sizeof(struct stat));
	char tmpf[1024];
	if (client->cache_dir) {
		if (stat(client->cache_dir, &st) < 0) {
			mkdir_with_parents(client->cache_dir, 0755);
		}
		strcpy(tmpf, client->cache_dir);
		strcat(tmpf, "/");
		char *ipswtmp = strdup(client->ipsw);
		strcat(tmpf, basename(ipswtmp));
		free(ipswtmp);
	} else {
		strcpy(tmpf, client->ipsw);
	}
	char* p = strrchr((const char*)tmpf, '.');
	if (p) {
		*p = '\0';
	}

	if (stat(tmpf, &st) < 0) {
		__mkdir(tmpf, 0755);
	}
	strcat(tmpf, "/");
	strcat(tmpf, fsname);

	memset(&st, '\0', sizeof(struct stat));
	if (stat(tmpf, &st) == 0) {
		off_t fssize = 0;
		ipsw_get_file_size(client->ipsw, fsname, &fssize);
		if ((fssize > 0) && (st.st_size == fssize)) {
			info("Using cached filesystem from '%s'\n", tmpf);
			filesystem = strdup(tmpf);
		}
	}

	if (!filesystem) {
		char extfn[1024];
		strcpy(extfn, tmpf);
		strcat(extfn, ".extract");
		char lockfn[1024];
		strcpy(lockfn, tmpf);
		strcat(lockfn, ".lock");
		lock_info_t li;

		lock_file(lockfn, &li);
		FILE* extf = NULL;
		if (access(extfn, F_OK) != 0) {
			extf = fopen(extfn, "w");
		}
		unlock_file(&li);
		if (!extf) {
			// use temp filename
			filesystem = tempnam(NULL, "ipsw_");
			if (!filesystem) {
				error("WARNING: Could not get temporary filename, using '%s' in current directory\n", fsname);
				filesystem = strdup(fsname);
			}
			delete_fs = 1;
		} else {
			// use <fsname>.extract as filename
			filesystem = strdup(extfn);
			fclose(extf);
		}
		remove(lockfn);

		// Extract filesystem from IPSW
		info("Extracting filesystem from IPSW\n");
		if (ipsw_extract_to_file(client->ipsw, fsname, filesystem) < 0) {
			error("ERROR: Unable to extract filesystem from IPSW\n");
			if (client->tss)
				plist_free(client->tss);
			plist_free(buildmanifest);
			return -1;
		}

		if (strstr(filesystem, ".extract")) {
			// rename <fsname>.extract to <fsname>
			rename(filesystem, tmpf);
			free(filesystem);
			filesystem = strdup(tmpf); 
		}
	}

	idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3);

	// if the device is in DFU mode, place device into recovery mode
	if (client->mode->index == MODE_DFU) {
		recovery_client_free(client);
		if ((client->flags & FLAG_CUSTOM) && limera1n_is_supported(client->device)) {
			info("connecting to DFU\n");
			if (dfu_client_new(client) < 0) {
				if (delete_fs && filesystem)
					unlink(filesystem);
				return -1;
			}
			info("exploiting with limera1n\n");
			// TODO: check for non-limera1n device and fail
			if (limera1n_exploit(client->device, &client->dfu->client) != 0) {
				error("ERROR: limera1n exploit failed\n");
				dfu_client_free(client);
				if (delete_fs && filesystem)
					unlink(filesystem);
				return -1;
			}
			dfu_client_free(client);
			info("exploited\n");
		}
		if (dfu_enter_recovery(client, build_identity) < 0) {
			error("ERROR: Unable to place device into recovery mode from %s mode\n", client->mode->string);
			plist_free(buildmanifest);
			if (client->tss)
				plist_free(client->tss);
			if (delete_fs && filesystem)
				unlink(filesystem);
			return -2;
		}
	}

	if (client->mode->index == MODE_DFU) {
		client->mode = &idevicerestore_modes[MODE_RECOVERY];
	} else {
		if ((client->build_major > 8) && !(client->flags & FLAG_CUSTOM)) {
			/* send ApTicket */
			if (recovery_send_ticket(client) < 0) {
				error("WARNING: Unable to send APTicket\n");
			}
		}

		/* now we load the iBEC */
		if (recovery_send_ibec(client, build_identity) < 0) {
			error("ERROR: Unable to send iBEC\n");
			if (delete_fs && filesystem)
				unlink(filesystem);
			return -2;
		}
		recovery_client_free(client);
	
		/* this must be long enough to allow the device to run the iBEC */
		/* FIXME: Probably better to detect if the device is back then */
		sleep(7);
	}
	idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5);

	if (client->build_major > 8) {
		// we need another tss request with nonce.
		unsigned char* nonce = NULL;
		int nonce_size = 0;
		int nonce_changed = 0;
		if (get_nonce(client, &nonce, &nonce_size) < 0) {
			error("ERROR: Unable to get nonce from device!\n");
			recovery_send_reset(client);
			if (delete_fs && filesystem)
				unlink(filesystem);
			return -2;
		}

		if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) {
			nonce_changed = 1;
			if (client->nonce) {
				free(client->nonce);
			}
			client->nonce = nonce;
			client->nonce_size = nonce_size;
		} else {
			free(nonce);
		}

		if (nonce_changed && !(client->flags & FLAG_CUSTOM)) {
			// Welcome iOS5. We have to re-request the TSS with our nonce.
			plist_free(client->tss);
			if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) {
				error("ERROR: Unable to get SHSH blobs for this device\n");
				if (delete_fs && filesystem)
					unlink(filesystem);
				return -1;
			}
			if (!client->tss) {
				error("ERROR: can't continue without TSS\n");
				if (delete_fs && filesystem)
					unlink(filesystem);
				return -1;
			}
			fixup_tss(client->tss);
		}
	}
	idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7);

	// now finally do the magic to put the device into restore mode
	if (client->mode->index == MODE_RECOVERY) {
		if (client->srnm == NULL) {
			error("ERROR: could not retrieve device serial number. Can't continue.\n");
			if (delete_fs && filesystem)
				unlink(filesystem);
			return -1;
		}
		if (recovery_enter_restore(client, build_identity) < 0) {
			error("ERROR: Unable to place device into restore mode\n");
			plist_free(buildmanifest);
			if (client->tss)
				plist_free(client->tss);
			if (delete_fs && filesystem)
				unlink(filesystem);
			return -2;
		}
		recovery_client_free(client);
	}
	idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.9);

	// device is finally in restore mode, let's do this
	if (client->mode->index == MODE_RESTORE) {
		info("About to restore device... \n");
		result = restore_device(client, build_identity, filesystem);
		if (result < 0) {
			error("ERROR: Unable to restore device\n");
			if (delete_fs && filesystem)
				unlink(filesystem);
			return result;
		}
	}

	info("Cleaning up...\n");
	if (delete_fs && filesystem)
		unlink(filesystem);

	/* special handling of AppleTVs */
	if (strncmp(client->device->product_type, "AppleTV", 7) == 0) {
		if (recovery_client_new(client) == 0) {
			if (recovery_set_autoboot(client, 1) == 0) {
				recovery_send_reset(client);
			} else {
				error("Setting auto-boot failed?!\n");
			}
		} else {
			error("Could not connect to device in recovery mode.\n");
		}
	}

	info("DONE\n");

	if (result == 0) {
		idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0);
	}

	return result;
}
int main(int argc, char *argv[])
{
	heartbeat_client_t heartbeat = NULL;
	idevice_t device = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	int i;
	const char* udid = NULL;

	signal(SIGINT, clean_exit);
	signal(SIGTERM, clean_exit);
#ifndef WIN32
	signal(SIGQUIT, clean_exit);
	signal(SIGPIPE, SIG_IGN);
#endif
	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	ret = idevice_new(&device, udid);
	if (ret != IDEVICE_E_SUCCESS) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	/* start heartbeat service on device */
	heartbeat_client_start_service(device, &heartbeat, "ideviceheartbeat");
	if (heartbeat) {
		printf("< heartbeat started, listening...\n");
	} else {
		printf("Failed to start heartbeat service\n");
		idevice_free(device);
		return -1;
	}

	/* main loop */
	uint8_t b = 0;
	uint64_t interval = 10000;
	plist_t message = NULL;
	plist_t node = NULL;
	do {
		/* await a "ping" message from the device every interval seconds */
		heartbeat_receive_with_timeout(heartbeat, &message, (uint32_t)interval);
		if (message) {
			/* report device beat settings */
			node = plist_dict_get_item(message, "SupportsSleepyTime");
			if (node && plist_get_node_type(node) == PLIST_BOOLEAN) {
				plist_get_bool_val(node, &b);
			}
			node = plist_dict_get_item(message, "Interval");
			if (node && plist_get_node_type(node) == PLIST_UINT) {
				plist_get_uint_val(node, &interval);
			}

			printf("> marco: supports_sleepy_time %d, interval %llu\n", b, interval);

			plist_free(message);
			message = NULL;

			/* answer with a "pong" message */
			message = plist_new_dict();
			plist_dict_insert_item(message, "Command", plist_new_string("Polo"));
			heartbeat_send(heartbeat, message);

			printf("< polo\n");

			if (message) {
				plist_free(message);
				message = NULL;
			}
		}
	} while(!quit_flag);

	heartbeat_client_free(heartbeat);

	idevice_free(device);

	return 0;
}
int main(int argc, char *argv[])
{
	unsigned int bytes = 0;
	uint16_t i = 0;
	lockdownd_service_descriptor_t service = NULL;
	lockdownd_client_t client = NULL;
	idevice_t phone = NULL;
	uint64_t lockfile = 0;
	np_client_t gnp = NULL;

	if (argc > 1 && !strcasecmp(argv[1], "--debug")) {
		idevice_set_debug_level(1);
	} else {
		idevice_set_debug_level(0);
	}

	if (IDEVICE_E_SUCCESS != idevice_new(&phone, NULL)) {
		printf("No device found, is it plugged in?\n");
		return -1;
	}

	char *udid = NULL;
	if (IDEVICE_E_SUCCESS == idevice_get_udid(phone, &udid)) {
		printf("DeviceUniqueID : %s\n", udid);
	}
	if (udid)
		free(udid);

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "ideviceclient")) {
		idevice_free(phone);
		printf("Exiting.\n");
		return -1;
	}

	char *nnn = NULL;
	if (LOCKDOWN_E_SUCCESS == lockdownd_get_device_name(client, &nnn)) {
		printf("DeviceName : %s\n", nnn);
		free(nnn);
	}

	lockdownd_start_service(client, "com.apple.afc", &service);

	if (service && service->port) {
		afc_client_t afc = NULL;
		afc_client_new(phone, service, &afc);

		if (afc) {
			service->port = 0;
			service->ssl_enabled = 0;
			lockdownd_start_service(client, "com.apple.mobile.notification_proxy", &service);
			if (service->port) {
				printf("Notification Proxy started.\n");
				np_client_new(phone, service, &gnp);
			} else {
				printf("ERROR: Notification proxy could not be started.\n");
			}

			if (gnp) {
				const char *nspec[5] = {
					NP_SYNC_CANCEL_REQUEST,
					NP_SYNC_SUSPEND_REQUEST,
					NP_SYNC_RESUME_REQUEST,
					NP_ITDBPREP_DID_END,
					NULL
				};
				np_observe_notifications(gnp, nspec);
				np_set_notify_callback(gnp, notifier, NULL);
			}

			perform_notification(phone, client, NP_SYNC_WILL_START);

			afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
			if (lockfile) {
				printf("locking file\n");
				afc_file_lock(afc, lockfile, AFC_LOCK_EX);

				perform_notification(phone, client, NP_SYNC_DID_START);
			}

			char **dirs = NULL;
			afc_read_directory(afc, "/eafaedf", &dirs);
			if (!dirs)
				afc_read_directory(afc, "/", &dirs);
			printf("Directory time.\n");
			for (i = 0; dirs[i]; i++) {
				printf("/%s\n", dirs[i]);
				free(dirs[i]);
			}
			if (dirs)
				free(dirs);

			dirs = NULL;
			afc_get_device_info(afc, &dirs);
			if (dirs) {
				for (i = 0; dirs[i]; i += 2) {
					printf("%s: %s\n", dirs[i], dirs[i + 1]);
					free(dirs[i]);
				}
				free(dirs);
			}

			uint64_t my_file = 0;
			char **info = NULL;
			uint64_t fsize = 0;
			if (AFC_E_SUCCESS == afc_get_file_info(afc, "/readme.libimobiledevice.fx", &info) && info) {
				for (i = 0; info[i]; i += 2) {
					printf("%s: %s\n", info[i], info[i+1]);
					if (!strcmp(info[i], "st_size")) {
						fsize = atoll(info[i+1]);
					}
				}
			}

			if (AFC_E_SUCCESS ==
				afc_file_open(afc, "/readme.libimobiledevice.fx", AFC_FOPEN_RDONLY, &my_file) && my_file) {
				printf("A file size: %llu\n", (long long)fsize);
				char *file_data = (char *) malloc(sizeof(char) * fsize);
				afc_file_read(afc, my_file, file_data, fsize, &bytes);
				if (bytes > 0) {
					printf("The file's data:\n");
					fwrite(file_data, 1, bytes, stdout);
				}
				printf("\nClosing my file.\n");
				afc_file_close(afc, my_file);
				free(file_data);
			} else
				printf("couldn't open a file\n");

			afc_file_open(afc, "/readme.libimobiledevice.fx", AFC_FOPEN_WR, &my_file);
			if (my_file) {
				char *outdatafile = strdup("this is a bitchin text file\n");
				afc_file_write(afc, my_file, outdatafile, strlen(outdatafile), &bytes);
				free(outdatafile);
				if (bytes > 0)
					printf("Wrote a surprise. ;)\n");
				else
					printf("I wanted to write a surprise, but... :(\n");
				afc_file_close(afc, my_file);
			}
			printf("Deleting a file...\n");
			bytes = afc_remove_path(afc, "/delme");
			if (bytes)
				printf("Success.\n");
			else
				printf("Failure. (expected unless you have a /delme file on your phone)\n");

			printf("Renaming a file...\n");
			bytes = afc_rename_path(afc, "/renme", "/renme2");
			if (bytes > 0)
				printf("Success.\n");
			else
				printf("Failure. (expected unless you have a /renme file on your phone)\n");

			printf("Seek & read\n");
			afc_file_open(afc, "/readme.libimobiledevice.fx", AFC_FOPEN_RDONLY, &my_file);
			if (AFC_E_SUCCESS != afc_file_seek(afc, my_file, 5, SEEK_CUR))
				printf("WARN: SEEK DID NOT WORK\n");
			char *threeletterword = (char *) malloc(sizeof(char) * 5);
			afc_file_read(afc, my_file, threeletterword, 3, &bytes);
			threeletterword[3] = '\0';
			if (bytes > 0)
				printf("Result: %s\n", threeletterword);
			else
				printf("Couldn't read!\n");
			free(threeletterword);
			afc_file_close(afc, my_file);
		}

		if (gnp && lockfile) {
			printf("XXX sleeping\n");
			sleep(5);

			printf("XXX unlocking file\n");
			afc_file_lock(afc, lockfile, AFC_LOCK_UN);

			printf("XXX closing file\n");
			afc_file_close(afc, lockfile);

			printf("XXX sleeping\n");
			sleep(5);
			//perform_notification(phone, client, NP_SYNC_DID_FINISH);
		}

		if (gnp) {
			np_client_free(gnp);
			gnp = NULL;
		}

		afc_client_free(afc);

		lockdownd_service_descriptor_free(service);
		service = NULL;
	} else {
		printf("Start service failure.\n");
	}

	printf("All done.\n");

	lockdownd_client_free(client);
	idevice_free(phone);

	return 0;
}
Exemple #12
0
bool ibrowserAPI::init(F_SUCC,F_ERRO)
{
    
    lockdownd_service_descriptor_t service = NULL;
    
    if (NULL == device)
    {
        if (IDEVICE_E_SUCCESS != idevice_new(&device, NULL)) {
            ERRO("idevice_new");
        }
        idevice_set_debug_level(1);
    }
    
    if (NULL == lockdownd_client)
    {
        if (LOCKDOWN_E_SUCCESS != (lockdownd_client_new_with_handshake(device, &lockdownd_client, CLIENT_LABEL))) {
            ERRO("lockdownd_client_new_with_handshake");
        }
    }
    
    if (NULL == instproxy_client)
    {
        if(LOCKDOWN_E_SUCCESS != (lockdownd_start_service(lockdownd_client,"com.apple.mobile.installation_proxy",&service) || !service->port))
        {
            ERRO("lockdownd_start_service com.apple.mobile.installation_proxy");
        }
        
        if(INSTPROXY_E_SUCCESS != instproxy_client_new(device,service,&instproxy_client) )
        {
            ERRO("instproxy_client_new");
        }
    }
    
    
    if (NULL == afc_client)
    {
        if(LOCKDOWN_E_SUCCESS != (lockdownd_start_service(lockdownd_client,"com.apple.afc",&service)) || !service->port)
        {
            ERRO("lockdownd_start_service com.apple.afc");
        }
        
        if (afc_client_new(device, service, &afc_client) != AFC_E_SUCCESS) {
            ERRO("afc_client_new");
        }
    }
    
    if (NULL == sbservices_client)
    {
        if(LOCKDOWN_E_SUCCESS != (lockdownd_start_service(lockdownd_client,"com.apple.springboardservices",&service)) || !service->port)
        {
            ERRO("lockdownd_start_service com.apple.springboardservices");
        }
        
        if (sbservices_client_new(device, service, &sbservices_client) != AFC_E_SUCCESS) {
            ERRO("sbservices_client_new");
        }
    }
    
    return true;

}
int main(int argc, char *argv[])
{
	lockdownd_client_t lockdown = NULL;
	idevice_t device = NULL;
	idevice_connection_t connection = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	thread_t th;
	const char* udid = NULL;
	uint16_t port = 0;
	uint16_t local_port = 0;
	int result = EXIT_SUCCESS;
	int i;

	/* bind signals */
	signal(SIGINT, clean_exit);
	signal(SIGTERM, clean_exit);
#ifndef WIN32
	signal(SIGQUIT, clean_exit);
	signal(SIGPIPE, SIG_IGN);
#endif

	/* parse cmdline arguments */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			debug_mode = 1;
			idevice_set_debug_level(1);
			socket_set_verbose(3);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return EXIT_SUCCESS;
		}
		else if (atoi(argv[i]) > 0) {
			local_port = atoi(argv[i]);
			continue;
		}
		else {
			print_usage(argc, argv);
			return EXIT_SUCCESS;
		}
	}

	/* a PORT is mandatory */
	if (!local_port) {
		fprintf(stderr, "Please specify a PORT.\n");
		print_usage(argc, argv);
		goto leave_cleanup;
	}

	/* start services and connect to device */
	ret = idevice_new(&device, udid);
	if (ret != IDEVICE_E_SUCCESS) {
		if (udid) {
			fprintf(stderr, "No device found with udid %s, is it plugged in?\n", udid);
		} else {
			fprintf(stderr, "No device found, is it plugged in?\n");
		}
		result = EXIT_FAILURE;
		goto leave_cleanup;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lockdown, "idevicedebugserverproxy")) {
		fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
		result = EXIT_FAILURE;
		goto leave_cleanup;
	}

	if ((lockdownd_start_service(lockdown, "com.apple.debugserver", &port) != LOCKDOWN_E_SUCCESS) || !port) {
		fprintf(stderr, "Could not start com.apple.debugserver!\nPlease make sure to mount the developer disk image first.\n");
		result = EXIT_FAILURE;
		goto leave_cleanup;
	}

	if (idevice_connect(device, port, &connection) != IDEVICE_E_SUCCESS) {
		fprintf(stderr, "Connection to debugserver port %d failed!\n", (int)port);
		result = EXIT_FAILURE;
		goto leave_cleanup;
	}

	/* free lockdown connection if running as it is not needed anymore */
	if (lockdown) {
		lockdownd_client_free(lockdown);
		lockdown = NULL;
	}

	/* setup and create socket endpoint */
	socket_info_t socket_info;

	socket_info.device_connection = connection;
	socket_info.local_port = local_port;
	socket_info.remote_port = port;

	/* create local socket */
	socket_info.server_fd = socket_create(socket_info.local_port);
	if (socket_info.server_fd < 0) {
		fprintf(stderr, "Could not create socket\n");
		result = EXIT_FAILURE;
		goto leave_cleanup;
	}

	while (!quit_flag) {
		debug("%s: Waiting for connection on local port %d\n", __func__, socket_info.local_port);

		/* wait for client */
		socket_info.client_fd = socket_accept(socket_info.server_fd, socket_info.local_port);
		if (socket_info.client_fd < 0) {
			debug("%s: Continuing...\n", __func__);
			continue;
		}

		debug("%s: Handling new client connection...\n", __func__);

		if (thread_create(&th, connection_handler, (void*)&socket_info) != 0) {
			fprintf(stderr, "Could not start connection handler.\n");
			socket_shutdown(socket_info.server_fd, SHUT_RDWR);
			socket_close(socket_info.server_fd);
		}
	}

	debug("%s: Shutting down debugserver proxy...\n", __func__);

leave_cleanup:
	if (connection) {
		idevice_disconnect(connection);
	}
	if (lockdown) {
		lockdownd_client_free(lockdown);
	}
	if (device) {
		idevice_free(device);
	}

	return result;
}
Exemple #14
0
int main(int argc, char **argv)
{
	idevice_t device = NULL;
	lockdownd_client_t client = NULL;
	char **dev_list = NULL;
	char *device_name = NULL;
	int ret = 0;
	int i;
	int mode = MODE_SHOW_ID;
	const char* udid = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--list")) {
			mode = MODE_LIST_DEVICES;
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
	}

	/* check if udid was passed */
	if (mode == MODE_SHOW_ID) {
		i--;
		if (!argv[i] || (strlen(argv[i]) != 40)) {
			print_usage(argc, argv);
			return 0;
		}
		udid = argv[i];
	}

	switch (mode) {
	case MODE_SHOW_ID:
		idevice_new(&device, udid);
		if (!device) {
			fprintf(stderr, "ERROR: No device with UDID=%s attached.\n", udid);
			return -2;
		}

		if (LOCKDOWN_E_SUCCESS != lockdownd_client_new(device, &client, "idevice_id")) {
			idevice_free(device);
			fprintf(stderr, "ERROR: Connecting to device failed!\n");
			return -2;
		}

		if ((LOCKDOWN_E_SUCCESS != lockdownd_get_device_name(client, &device_name)) || !device_name) {
			fprintf(stderr, "ERROR: Could not get device name!\n");
			ret = -2;
		}

		lockdownd_client_free(client);
		idevice_free(device);

		if (ret == 0) {
			printf("%s\n", device_name);
		}

		if (device_name) {
			free(device_name);
		}

		return ret;
	case MODE_LIST_DEVICES:
	default:
		if (idevice_get_device_list(&dev_list, &i) < 0) {
			fprintf(stderr, "ERROR: Unable to retrieve device list!\n");
			return -1;
		}
		for (i = 0; dev_list[i] != NULL; i++) {
			printf("%s\n", dev_list[i]);
		}
		idevice_device_list_free(dev_list);
		return 0;
	}
}
int main(int argc, char **argv)
{
	idevice_t device = NULL;
	lockdownd_client_t lckd = NULL;
	screenshotr_client_t shotr = NULL;
	uint16_t port = 0;
	int result = -1;
	int i;
	const char *udid = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, NULL)) {
		idevice_free(device);
		printf("Exiting.\n");
		return -1;
	}

	lockdownd_start_service(lckd, "com.apple.mobile.screenshotr", &port);
	lockdownd_client_free(lckd);
	if (port > 0) {
		if (screenshotr_client_new(device, port, &shotr) != SCREENSHOTR_E_SUCCESS) {
			printf("Could not connect to screenshotr!\n");
		} else {
			char *imgdata = NULL;
			char filename[36];
			uint64_t imgsize = 0;
			time_t now = time(NULL);
			strftime(filename, 36, "screenshot-%Y-%m-%d-%H-%M-%S.tiff", gmtime(&now));
			if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) {
				FILE *f = fopen(filename, "wb");
				if (f) {
					if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
						printf("Screenshot saved to %s\n", filename);
						result = 0;
					} else {
						printf("Could not save screenshot to file %s!\n", filename);
					}
					fclose(f);
				} else {
					printf("Could not open %s for writing: %s\n", filename, strerror(errno));
				}
			} else {
				printf("Could not get screenshot!\n");
			}
			screenshotr_client_free(shotr);
		}
	} else {
		printf("Could not start screenshotr service! Remember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n");
	}
	idevice_free(device);
	
	return result;
}
int main(int argc, char* argv[]) {
	int opt = 0;
	int optindex = 0;
	char* ipsw = NULL;
	char* uuid = NULL;
	int tss_enabled = 0;

	// create an instance of our context
	struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t));
	if (client == NULL) {
		error("ERROR: Out of memory\n");
		return -1;
	}
	memset(client, '\0', sizeof(struct idevicerestore_client_t));

	while ((opt = getopt_long(argc, argv, "dhcexu:", longopts, &optindex)) > 0) {
		switch (opt) {
		case 'h':
			usage(argc, argv);
			return 0;

		case 'd':
			client->flags |= FLAG_DEBUG;
			idevicerestore_debug = 1;
			break;

		case 'e':
			client->flags |= FLAG_ERASE;
			break;

		case 'c':
			client->flags |= FLAG_CUSTOM;
			break;

		case 'x':
			client->flags |= FLAG_EXCLUDE;
			break;

		case 'u':
			uuid = optarg;
			break;

		default:
			usage(argc, argv);
			return -1;
		}
	}

	if ((argc-optind) == 1) {
		argc -= optind;
		argv += optind;

		ipsw = argv[0];
	} else {
		usage(argc, argv);
		return -1;
	}

	if (client->flags & FLAG_DEBUG) {
		idevice_set_debug_level(1);
		irecv_set_debug_level(1);
	}

	client->uuid = uuid;
	client->ipsw = ipsw;

	// check which mode the device is currently in so we know where to start
	if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) {
		error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n");
		return -1;
	}
	info("Found device in %s mode\n", client->mode->string);

	// discover the device type
	if (check_device(client) < 0 || client->device->index == DEVICE_UNKNOWN) {
		error("ERROR: Unable to discover device type\n");
		return -1;
	}
	info("Identified device as %s\n", client->device->product);

	if (client->mode->index == MODE_RESTORE) {
		if (restore_reboot(client) < 0) {
			error("ERROR: Unable to exit restore mode\n");
			return -1;
		}
	}

	// extract buildmanifest
	plist_t buildmanifest = NULL;
	info("Extracting BuildManifest from IPSW\n");
	if (ipsw_extract_build_manifest(ipsw, &buildmanifest, &tss_enabled) < 0) {
		error("ERROR: Unable to extract BuildManifest from %s\n", ipsw);
		return -1;
	}

	/* print iOS information from the manifest */
	build_manifest_print_information(buildmanifest);

	if (client->flags & FLAG_CUSTOM) {
		/* prevent signing custom firmware */
		tss_enabled = 0;
		info("Custom firmware requested. Disabled TSS request.\n");
	}

	// choose whether this is an upgrade or a restore (default to upgrade)
	client->tss = NULL;
	plist_t build_identity = NULL;
	if (client->flags & FLAG_ERASE) {
		build_identity = build_manifest_get_build_identity(buildmanifest, 0);
		if (build_identity == NULL) {
			error("ERROR: Unable to find any build identities\n");
			plist_free(buildmanifest);
			return -1;
		}
	} else {
		// loop through all build identities in the build manifest
		// and list the valid ones
		int i = 0;
		int valid_builds = 0;
		int build_count = build_manifest_get_identity_count(buildmanifest);
		for (i = 0; i < build_count; i++) {
			build_identity = build_manifest_get_build_identity(buildmanifest, i);
			valid_builds++;
		}
	}

	/* print information about current build identity */
	build_identity_print_information(build_identity);

	/* retrieve shsh blobs if required */
	if (tss_enabled) {
		debug("Getting device's ECID for TSS request\n");
		/* fetch the device's ECID for the TSS request */
		if (get_ecid(client, &client->ecid) < 0) {
			error("ERROR: Unable to find device ECID\n");
			return -1;
		}
		info("Found ECID %llu\n", client->ecid);

		if (get_shsh_blobs(client, client->ecid, build_identity, &client->tss) < 0) {
			error("ERROR: Unable to get SHSH blobs for this device\n");
			return -1;
		}
	}

	/* verify if we have tss records if required */
	if ((tss_enabled) && (client->tss == NULL)) {
		error("ERROR: Unable to proceed without a TSS record.\n");
		plist_free(buildmanifest);
		return -1;
	}

	// Extract filesystem from IPSW and return its name
	char* filesystem = NULL;
	if (ipsw_extract_filesystem(client->ipsw, build_identity, &filesystem) < 0) {
		error("ERROR: Unable to extract filesystem from IPSW\n");
		if (client->tss)
			plist_free(client->tss);
		plist_free(buildmanifest);
		return -1;
	}

	// if the device is in normal mode, place device into recovery mode
	if (client->mode->index == MODE_NORMAL) {
		info("Entering recovery mode...\n");
		if (normal_enter_recovery(client) < 0) {
			error("ERROR: Unable to place device into recovery mode\n");
			if (client->tss)
				plist_free(client->tss);
			plist_free(buildmanifest);
			return -1;
		}
	}

	// if the device is in DFU mode, place device into recovery mode
	if (client->mode->index == MODE_DFU) {
		if (dfu_enter_recovery(client, build_identity) < 0) {
			error("ERROR: Unable to place device into recovery mode\n");
			plist_free(buildmanifest);
			if (client->tss)
				plist_free(client->tss);
			return -1;
		}
	}

	// if the device is in recovery mode, place device into restore mode
	if (client->mode->index == MODE_RECOVERY) {
		if (recovery_enter_restore(client, build_identity) < 0) {
			error("ERROR: Unable to place device into restore mode\n");
			plist_free(buildmanifest);
			if (client->tss)
				plist_free(client->tss);
			return -1;
		}
	}

	// device is finally in restore mode, let's do this
	if (client->mode->index == MODE_RESTORE) {
		info("Restoring device... \n");
		if (restore_device(client, build_identity, filesystem) < 0) {
			error("ERROR: Unable to restore device\n");
			return -1;
		}
	}

	info("Cleaning up...\n");
	if (filesystem)
		unlink(filesystem);

	info("DONE\n");
	return 0;
}
Exemple #17
0
int main(int argc, const char **argv)
{
	char *errmsg = "";
	idevice_t device = NULL;
	lockdownd_client_t client = NULL;
	lockdownd_service_descriptor_t service = NULL;
	house_arrest_client_t hac = NULL;
	const char *service_name = "com.apple.afc";
    const char *appid = NULL;
	char *device_name = NULL;
	int result = 0;
	char* udid = NULL;
	int cmd = CMD_INTERACTIVE;
	const char *cmdstr = NULL;
	int i;

	cwd = strdup("/");

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (str_is_equal(argv[i], "-d") || str_is_equal(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (str_is_equal(argv[i], "-u") || str_is_equal(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				exit(EXIT_FAILURE);
			}
			udid = strdup(argv[i]);
			continue;
		}
		else if (str_is_equal(argv[i], "-2") || str_is_equal(argv[i], "--afc2")) {
			service_name = "com.apple.afc2";
			continue;
		}
        else if (str_is_equal(argv[i], "-a") || str_is_equal(argv[i], "--appid")) {
            if (++i >=  argc) {
                print_usage(argc, argv);
                exit(EXIT_FAILURE);
            }
            appid = argv[i];
        }
		else if (str_is_equal(argv[i], "-h") || str_is_equal(argv[i], "--help")) {
			print_usage(argc, argv);
			exit(EXIT_SUCCESS);
		}
		else if ((cmd = str_to_cmd(argv[i])) != CMD_UNKNOWN) {
			cmdstr = argv[i];
			i++;
			break;
		}
	}
	argc -= i;
	argv += i;

	/* Connect to device */
	if (udid) {
		result = idevice_new(&device, udid);
		if (result != IDEVICE_E_SUCCESS)
			errx(EXIT_FAILURE, "No device found with udid %s, is it plugged in?", udid);
	}
	else {
		result = idevice_new(&device, NULL);
		if (result != IDEVICE_E_SUCCESS)
			errx(EXIT_FAILURE, "No device found, is it plugged in?");
		idevice_get_udid(device, &udid);
	}

	/* Connect to lockdownd */
	result = lockdownd_client_new_with_handshake(device, &client, "afccl");
	if (result != LOCKDOWN_E_SUCCESS) {
		asprintf(&errmsg, "ERROR: Connecting to lockdownd service failed!");
		goto bail;
	}

	result = lockdownd_get_device_name(client, &device_name);
	if ((result != LOCKDOWN_E_SUCCESS) || !device_name) {
		asprintf(&errmsg, "ERROR: Could not get device name!");
		goto bail;
	}

    if (appid) {
        result = lockdownd_start_service(client, "com.apple.mobile.house_arrest", &service);
        if (result != LOCKDOWN_E_SUCCESS || !service || !service->port) {
			asprintf(&errmsg, "error starting house arrest service: (%d) %s", result, afc_strerror(result));
			goto bail;
        }
        if (client) {
            lockdownd_client_free(client);
            client = NULL;
        }
        
        if (house_arrest_client_new(device, service, &hac) != HOUSE_ARREST_E_SUCCESS) {
            asprintf(&errmsg, "could not connect to house_arrest service!\n");
			goto bail;
        }
        
        if (service) {
            lockdownd_service_descriptor_free(service);
            service = NULL;
        }
        
        result = house_arrest_send_command(hac, "VendDocuments", appid);
        if (result != HOUSE_ARREST_E_SUCCESS) {
            asprintf(&errmsg, "error %d when trying to get VendDocuments\n", result);
			goto bail;
        }
        
        plist_t dict = NULL;
        if (house_arrest_get_result(hac, &dict) != HOUSE_ARREST_E_SUCCESS) {
            if (house_arrest_get_result(hac, &dict) != HOUSE_ARREST_E_SUCCESS) {
                asprintf(&errmsg, "hmmm....\n");
				goto bail;
            }
        }
        
        plist_t node = plist_dict_get_item(dict, "Error");
        if (node) {
            char *str = NULL;
            plist_get_string_val(node, &str);
            asprintf(&errmsg, "Error: %s\n", str);
            if (str) free(str);
            plist_free(dict);
            dict = NULL;
			goto bail;
		}
        node = plist_dict_get_item(dict, "Status");
        if (node) {
            char *str = NULL;
            plist_get_string_val(node, &str);
            if (str && (strcmp(str, "Complete") != 0)) {
                printf("Warning: Status is not 'Complete' but '%s'\n", str);
            }
            if (str) free(str);
        }
        if (dict) {
            plist_free(dict);
        }
        
        afc_error_t ae = afc_client_new_from_house_arrest_client(hac, &afc);
        if (ae != AFC_E_SUCCESS) {
            printf("afc error %d\n", ae);
        }

    }
    else {
        result = lockdownd_start_service(client, service_name, &service);
        if (result != LOCKDOWN_E_SUCCESS || !service || !service->port) {
            asprintf(&errmsg, "error starting AFC service: (%d) %s", result, afc_strerror(result));
			goto bail;
        }

        /* Connect to AFC */
        result = afc_client_new(device, service, &afc);
        lockdownd_client_free(client);
        idevice_free(device);
        if (result != AFC_E_SUCCESS) {
            errx(EXIT_FAILURE, "AFC connection failed (%d) %s", result, afc_strerror(result));
        }
	}
	result = do_cmd(cmd, argc, argv);

	if (hac)
		house_arrest_client_free(hac);

	afc_client_free(afc);

	exit(result == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
    
bail:
    if (hac)
		house_arrest_client_free(hac);

	if (service)
		lockdownd_service_descriptor_free(service);

    if (client)
		lockdownd_client_free(client);

    if (device)
		idevice_free(device);

	errx(EXIT_FAILURE, "%s", errmsg);
}
Exemple #18
0
int main(int argc, char *argv[])
{
	lockdownd_client_t client = NULL;
	idevice_t phone = NULL;

	idevice_set_debug_level(1);

	if (IDEVICE_E_SUCCESS != idevice_new(&phone, NULL)) {
		printf("No device found, is it plugged in?\n");
		return -1;
	}

	char *udid = NULL;
	if (IDEVICE_E_SUCCESS == idevice_get_udid(phone, &udid)) {
		printf("DeviceUniqueID : %s\n", udid);
	}
	if (udid)
		free(udid);

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "lckdclient")) {
		idevice_free(phone);
		return -1;
	}

	using_history();
	int loop = 1;
	while (loop) {
		char *cmd = readline("> ");
		if (cmd) {

			char **args = get_tokens(cmd);

			int len = 0;
			while (args && args[len]) {
				len++;
			}

			if (len > 0) {
				add_history(cmd);
				if (!strcmp(*args, "quit"))
					loop = 0;

				if (!strcmp(*args, "get") && len >= 2) {
					plist_t value = NULL;
					if (LOCKDOWN_E_SUCCESS == lockdownd_get_value(client, len == 3 ? *(args + 1):NULL,  len == 3 ? *(args + 2):*(args + 1), &value))
					{
						char *xml = NULL;
						uint32_t length;
						plist_to_xml(value, &xml, &length);
						printf("Success : value = %s\n", xml);
						free(xml);
					}
					else
						printf("Error\n");

					if (value)
						plist_free(value);
				}

				if (!strcmp(*args, "start") && len == 2) {
					uint16_t port = 0;
					if(LOCKDOWN_E_SUCCESS == lockdownd_start_service(client, *(args + 1), &port)) {
						printf("started service %s on port %i\n", *(args + 1), port);
					}
					else
					{
						printf("failed to start service %s on device.\n", *(args + 1));
					}
				}
			}
			strfreev(args);
		}
		free(cmd);
		cmd = NULL;
	}
	clear_history();
	lockdownd_client_free(client);
	idevice_free(phone);

	return 0;
}
int main(int argc, char **argv)
{
	idevice_t device = NULL;
	lockdownd_client_t lckd = NULL;
	lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
	screenshotr_client_t shotr = NULL;
	lockdownd_service_descriptor_t service = NULL;
	int result = -1;
	int i;
	const char *udid = NULL;
	char *filename = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else if (argv[i][0] != '-' && !filename) {
			filename = strdup(argv[i]);
			continue;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, NULL))) {
		idevice_free(device);
		printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
		return -1;
	}

	lockdownd_start_service(lckd, "com.apple.mobile.screenshotr", &service);
	lockdownd_client_free(lckd);
	if (service && service->port > 0) {
		if (screenshotr_client_new(device, service, &shotr) != SCREENSHOTR_E_SUCCESS) {
			printf("Could not connect to screenshotr!\n");
		} else {
			char *imgdata = NULL;
			uint64_t imgsize = 0;
			if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) {
				if (!filename) {
					const char *fileext = NULL;
					if (memcmp(imgdata, "\x89PNG", 4) == 0) {
						fileext = ".png";
					} else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
						fileext = ".tiff";
					} else {
						printf("WARNING: screenshot data has unexpected image format.\n");
						fileext = ".dat";
					}
					time_t now = time(NULL);
					filename = (char*)malloc(36);
					size_t pos = strftime(filename, 36, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
					sprintf(filename+pos, "%s", fileext);
				}
				FILE *f = fopen(filename, "wb");
				if (f) {
					if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
						printf("Screenshot saved to %s\n", filename);
						result = 0;
					} else {
						printf("Could not save screenshot to file %s!\n", filename);
					}
					fclose(f);
				} else {
					printf("Could not open %s for writing: %s\n", filename, strerror(errno));
				}
			} else {
				printf("Could not get screenshot!\n");
			}
			screenshotr_client_free(shotr);
		}
	} else {
		printf("Could not start screenshotr service! Remember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n");
	}

	if (service)
		lockdownd_service_descriptor_free(service);

	idevice_free(device);
	free(filename);

	return result;
}
int main(int argc, char **argv)
{
	idevice_t dev = NULL;
	lockdownd_client_t client = NULL;
	house_arrest_client_t hac = NULL;
	house_arrest_error_t res;
	int i;
	char *udid = NULL;
	const char *appid = NULL;
	int test_file_io = 0;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = strdup(argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--test")) {
			test_file_io = 1;
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			appid = argv[i];
			break;
		}
	}

	if (!appid) {
		print_usage(argc, argv);
		return 0;
	}

	if (idevice_new(&dev, udid) != IDEVICE_E_SUCCESS) {
		printf("no device connected?!\n");
		goto leave_cleanup;
	}

	if (lockdownd_client_new_with_handshake(dev, &client, NULL) != LOCKDOWN_E_SUCCESS) {
		printf("could not connect to lockdownd!\n");
		goto leave_cleanup;
	}

	uint16_t port = 0;
	if (lockdownd_start_service(client, "com.apple.mobile.house_arrest", &port) != LOCKDOWN_E_SUCCESS) {
		printf("could not start house_arrest service!\n");
		goto leave_cleanup;
	}

	if (client) {
		lockdownd_client_free(client);
		client = NULL;
	}

	if (house_arrest_client_new(dev, port, &hac) != HOUSE_ARREST_E_SUCCESS) {
		printf("could not connect to house_arrest service!\n");
		goto leave_cleanup;
	}

	res = house_arrest_send_command(hac, "VendDocuments", appid);
	if (res != HOUSE_ARREST_E_SUCCESS) {
		printf("error %d when trying to get VendDocuments\n", res);
		goto leave_cleanup;
	}

	plist_t dict = NULL;
	if (house_arrest_get_result(hac, &dict) != HOUSE_ARREST_E_SUCCESS) {
		if (house_arrest_get_result(hac, &dict) != HOUSE_ARREST_E_SUCCESS) {
			printf("hmmm....\n");
			goto leave_cleanup;
		}
	}

	plist_t node = plist_dict_get_item(dict, "Error");
	if (node) {
		char *str = NULL;
		plist_get_string_val(node, &str);
		printf("Error: %s\n", str);
		if (str) free(str);
		plist_free(dict);
		dict = NULL;
		goto leave_cleanup;
	}
	node = plist_dict_get_item(dict, "Status");
	if (node) {
		char *str = NULL;
		plist_get_string_val(node, &str);
		if (str && (strcmp(str, "Complete") != 0)) {
			printf("Warning: Status is not 'Complete' but '%s'\n", str);
		}
		if (str) free(str);
		plist_free(dict);
		dict = NULL;
	}
	if (dict) {
		plist_free(dict);
	}

	afc_client_t afc = NULL;
	afc_error_t ae = afc_client_new_from_house_arrest_client(hac, &afc);
	if (ae != AFC_E_SUCCESS) {
		printf("afc error %d\n", ae);
	}
	if (ae == AFC_E_SUCCESS) {
		char **list = NULL;
		afc_read_directory(afc, "/", &list);
		printf("Directory contents:\n");
		if (list) {
			while (list[0]) {
				if (strcmp(list[0], ".") && strcmp(list[0], "..")) {
					puts(list[0]);
				}
				list++;
			}
		}

		if (test_file_io) {
			uint64_t tf = 0;
			printf("\n==== Performing file tests ====\n");
			printf("Opening file 'foobar' for writing: ");
			if (afc_file_open(afc, "/foobar", AFC_FOPEN_RW, &tf) == AFC_E_SUCCESS) {
				uint32_t wb = 0;
				printf("OK\n");

				printf("Writing to file: ");
				if (afc_file_write(afc, tf, "test\r\n", 6, &wb) != AFC_E_SUCCESS) {
					printf("ERROR\n");
				} else {
					printf("OK\n");
				}
				afc_file_close(afc, tf);
				printf("Deleting file 'foobar': ");
				if (afc_remove_path(afc, "/foobar") == AFC_E_SUCCESS) {
					printf("OK\n");
				} else {
					printf("ERROR\n");
				}
			} else {
				printf("ERROR\n");
			}
		}
		afc_client_free(afc);
	} else {
		printf("failed to connect to afc service, error %d\n", ae);
	}

leave_cleanup:
	if (hac) {
		house_arrest_client_free(hac);
	}
	if (client) {
		lockdownd_client_free(client);
	}
	if (dev) {
		idevice_free(dev);
	}

	return 0;
}
int main(int argc, char *argv[])
{
	lockdownd_client_t client = NULL;
	lockdownd_service_descriptor_t service = NULL;
	idevice_t device = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	int i;
	int op = -1;
	const char* udid = NULL;
	const char* param = NULL;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = argv[i];
			continue;
		}
		else if (!strcmp(argv[i], "install")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_INSTALL;
			continue;
		}
		else if (!strcmp(argv[i], "list")) {
			op = OP_LIST;
		}
		else if (!strcmp(argv[i], "copy")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_COPY;
			continue;
		}
		else if (!strcmp(argv[i], "remove")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) < 1)) {
				print_usage(argc, argv);
				return 0;
			}
			param = argv[i];
			op = OP_REMOVE;
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			print_usage(argc, argv);
			return 0;
		}
	}

	if ((op == -1) || (op >= NUM_OPS)) {
		print_usage(argc, argv);
		return 0;
	}

	ret = idevice_new(&device, udid);
	if (ret != IDEVICE_E_SUCCESS) {
		if (udid) {
			printf("No device found with udid %s, is it plugged in?\n", udid);
		} else {
			printf("No device found, is it plugged in?\n");
		}
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &client, "ideviceprovision")) {
		idevice_free(device);
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_start_service(client, "com.apple.misagent", &service)) {
		fprintf(stderr, "Could not start service \"com.apple.misagent\"\n");
		lockdownd_client_free(client);
		idevice_free(device);
		return -1;
	}
	lockdownd_client_free(client);
	client = NULL;

	misagent_client_t mis = NULL;
	if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
		fprintf(stderr, "Could not connect to \"com.apple.misagent\" on device\n");
		if (service)
			lockdownd_service_descriptor_free(service);
		lockdownd_client_free(client);
		idevice_free(device);
		return -1;
	}

	if (service)
		lockdownd_service_descriptor_free(service);

	switch (op) {
		case OP_INSTALL:
		{
			FILE* f = fopen(param, "rb");
			if (!f) {
				fprintf(stderr, "Could not open file '%s'\n", param);
				break;
			}
			fseek(f, 0, SEEK_END);
			long int size = ftell(f);
			fseek(f, 0, SEEK_SET);

			if (size >= 0x1000000) {
				fprintf(stderr, "The file '%s' is too large for processing.\n", param);
				fclose(f);
				break;
			}

			char* buf = malloc(size);
			if (!buf) {
				fprintf(stderr, "Could not allocate memory...\n");
				fclose(f);
				break;
			}

			long int cur = 0;
			while (cur < size) {
				ssize_t r = fread(buf+cur, 1, 512, f);
				if (r <= 0) {
					break;
				}
				cur += r;
			}
			fclose(f);

			if (cur != size) {
				free(buf);
				fprintf(stderr, "Could not read in file '%s' (size %ld read %ld)\n", param, size, cur);
				break;
			}

			uint64_t psize = size;
			plist_t pdata = plist_new_data(buf, psize);

			if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) {
				printf("Profile '%s' installed successfully.\n", param);
			} else {
				int sc = misagent_get_status_code(mis);
				fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc);
			}
			free(buf);
		}
			break;
		case OP_LIST:
		case OP_COPY:
		{
			plist_t profiles = NULL;
			if (misagent_copy(mis, &profiles) == MISAGENT_E_SUCCESS) {
				uint32_t num_profiles = plist_array_get_size(profiles);
				printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles");
				uint32_t j;
				for (j = 0; j < num_profiles; j++) {
					char* p_name = NULL;
					char* p_uuid = NULL;
					plist_t profile = plist_array_get_item(profiles, j);
					plist_t pl = profile_get_embedded_plist(profile);
					if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
						plist_t node;
						node = plist_dict_get_item(pl, "Name");
						if (node && (plist_get_node_type(node) == PLIST_STRING)) {
							plist_get_string_val(node, &p_name);
						}
						node = plist_dict_get_item(pl, "UUID");
						if (node && (plist_get_node_type(node) == PLIST_STRING)) {
							plist_get_string_val(node, &p_uuid);
						}
					}
					printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)");
					if (op == OP_COPY) {
						char pfname[512];
						if (p_uuid) {
							sprintf(pfname, "%s/%s.mobileprovision", param, p_uuid);
						} else {
							sprintf(pfname, "%s/profile%d.mobileprovision", param, j);
						}
						FILE* f = fopen(pfname, "wb");
						if (f) {
							char* dt = NULL;
							uint64_t ds = 0;
							plist_get_data_val(profile, &dt, &ds);
							fwrite(dt, 1, ds, f);
							fclose(f);
							printf(" => %s\n", pfname);
						} else {
							fprintf(stderr, "Could not open '%s' for writing\n", pfname);
						}
					}
					if (p_uuid) {
						free(p_uuid);
					}
					if (p_name) {
						free(p_name);
					}
				}
			} else {
				int sc = misagent_get_status_code(mis);
				fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
			}
		}
			break;
		case OP_REMOVE:
			if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) {
				printf("Profile '%s' removed.\n", param);
			} else {
				int sc = misagent_get_status_code(mis);
				fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc);
			}
			break;
		default:
			break;
	}

	misagent_client_free(mis);

	idevice_free(device);

	return 0;
}
int main(int argc, char *argv[])
{
    int res = -1;
    idevice_t device = NULL;
    idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
    instproxy_client_t instproxy_client = NULL;
    debugserver_client_t debugserver_client = NULL;
    int i;
    int debug_level = 0;
    int cmd = CMD_NONE;
    const char* udid = NULL;
    const char* bundle_identifier = NULL;
    char* path = NULL;
    char* working_directory = NULL;
    char **newlist = NULL;
    char** environment = NULL;
    int environment_index = 0;
    int environment_count = 0;
    char* response = NULL;
    debugserver_command_t command = NULL;
    debugserver_error_t dres = DEBUGSERVER_E_UNKNOWN_ERROR;

    /* map signals */
    signal(SIGINT, on_signal);
    signal(SIGTERM, on_signal);
#ifndef WIN32
    signal(SIGQUIT, on_signal);
    signal(SIGPIPE, SIG_IGN);
#endif

    /* parse command line arguments */
    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
            debug_level++;
            idevice_set_debug_level(debug_level);
            continue;
        } else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
            i++;
            if (!argv[i] || (strlen(argv[i]) != 40)) {
                print_usage(argc, argv);
                res = 0;
                goto cleanup;
            }
            udid = argv[i];
            continue;
        } else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--env")) {
            i++;
            if (!argv[i] || (strlen(argv[i]) <= 1) || strchr(argv[i], '=') == NULL) {
                print_usage(argc, argv);
                res = 0;
                goto cleanup;
            }
            /* add environment variable */
            if (!newlist)
                newlist = malloc((environment_count + 1) * sizeof(char*));
            else
                newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
            newlist[environment_count++] = strdup(argv[i]);
            environment = newlist;
            continue;
        } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
            print_usage(argc, argv);
            res = 0;
            goto cleanup;
        } else if (!strcmp(argv[i], "run")) {
            cmd = CMD_RUN;

            i++;
            if (!argv[i]) {
                /* make sure at least the bundle identifier was provided */
                printf("Please supply the bundle identifier of the app to run.\n");
                print_usage(argc, argv);
                res = 0;
                goto cleanup;
            }
            /*  read bundle identifier */
            bundle_identifier = argv[i];
            break;
        } else {
            print_usage(argc, argv);
            res = 0;
            goto cleanup;
        }
    }

    if (environment) {
        newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
        newlist[environment_count] = NULL;
        environment = newlist;
    }

    /* verify options */
    if (cmd == CMD_NONE) {
        print_usage(argc, argv);
        goto cleanup;
    }

    /* connect to the device */
    ret = idevice_new(&device, udid);
    if (ret != IDEVICE_E_SUCCESS) {
        if (udid) {
            printf("No device found with udid %s, is it plugged in?\n", udid);
        } else {
            printf("No device found, is it plugged in?\n");
        }
        goto cleanup;
    }

    switch (cmd) {
    case CMD_RUN:
    default:
        /* get the path to the app and it's working directory */
        if (instproxy_client_start_service(device, &instproxy_client, "idevicerun") != INSTPROXY_E_SUCCESS) {
            fprintf(stderr, "Could not start installation proxy service.\n");
            goto cleanup;
        }
        plist_t container = NULL;
        instproxy_client_get_object_by_key_from_info_directionary_for_bundle_identifier(instproxy_client, bundle_identifier, "Container", &container);
        instproxy_client_get_path_for_bundle_identifier(instproxy_client, bundle_identifier, &path);
        instproxy_client_free(instproxy_client);
        instproxy_client = NULL;

        if (container && (plist_get_node_type(container) == PLIST_STRING)) {
            plist_get_string_val(container, &working_directory);
            debug_info("working_directory: %s\n", working_directory);
            plist_free(container);
        } else {
            plist_free(container);
            fprintf(stderr, "Could not determine container path for bundle identifier %s.\n", bundle_identifier);
            goto cleanup;
        }

        /* start and connect to debugserver */
        if (debugserver_client_start_service(device, &debugserver_client, "idevicerun") != DEBUGSERVER_E_SUCCESS) {
            fprintf(stderr,
                    "Could not start com.apple.debugserver!\n"
                    "Please make sure to mount the developer disk image first:\n"
                    "  1) Get the iOS version from `ideviceinfo -k ProductVersion`.\n"
                    "  2) Find the matching iPhoneOS DeveloperDiskImage.dmg files.\n"
                    "  3) Run `ideviceimagemounter` with the above path.\n");
            goto cleanup;
        }

        /* enable logging for the session in debug mode */
        if (debug_level) {
            debug_info("Setting logging bitmask...");
            debugserver_command_new("QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE|LOG_RNB_PACKETS", 0, NULL, &command);
            dres = debugserver_client_send_command(debugserver_client, command, &response);
            debugserver_command_free(command);
            command = NULL;
            if (response) {
                if (strncmp(response, "OK", 2)) {
                    debugserver_client_handle_response(debugserver_client, &response, 0);
                    goto cleanup;
                }
                free(response);
                response = NULL;
            }
        }

        /* set maximum packet size */
        debug_info("Setting maximum packet size...");
        char* packet_size[2] = {strdup("1024"), NULL};
        debugserver_command_new("QSetMaxPacketSize:", 1, packet_size, &command);
        free(packet_size[0]);
        dres = debugserver_client_send_command(debugserver_client, command, &response);
        debugserver_command_free(command);
        command = NULL;
        if (response) {
            if (strncmp(response, "OK", 2)) {
                debugserver_client_handle_response(debugserver_client, &response, 0);
                goto cleanup;
            }
            free(response);
            response = NULL;
        }

        /* set working directory */
        debug_info("Setting working directory...");
        char* working_dir[2] = {working_directory, NULL};
        debugserver_command_new("QSetWorkingDir:", 1, working_dir, &command);
        dres = debugserver_client_send_command(debugserver_client, command, &response);
        debugserver_command_free(command);
        command = NULL;
        if (response) {
            if (strncmp(response, "OK", 2)) {
                debugserver_client_handle_response(debugserver_client, &response, 0);
                goto cleanup;
            }
            free(response);
            response = NULL;
        }

        /* set environment */
        if (environment) {
            debug_info("Setting environment...");
            for (environment_index = 0; environment_index < environment_count; environment_index++) {
                debug_info("setting environment variable: %s", environment[environment_index]);
                debugserver_client_set_environment_hex_encoded(debugserver_client, environment[environment_index], NULL);
            }
        }

        /* set arguments and run app */
        debug_info("Setting argv...");
        i++; /* i is the offset of the bundle identifier, thus skip it */
        int app_argc = (argc - i + 2);
        char **app_argv = (char**)malloc(sizeof(char*) * app_argc);
        app_argv[0] = path;
        debug_info("app_argv[%d] = %s", 0, app_argv[0]);
        app_argc = 1;
        while (i < argc && argv && argv[i]) {
            debug_info("app_argv[%d] = %s", app_argc, argv[i]);
            app_argv[app_argc++] = argv[i];
            i++;
        }
        app_argv[app_argc] = NULL;
        debugserver_client_set_argv(debugserver_client, app_argc, app_argv, NULL);
        free(app_argv);

        /* check if launch succeeded */
        debug_info("Checking if launch succeeded...");
        debugserver_command_new("qLaunchSuccess", 0, NULL, &command);
        dres = debugserver_client_send_command(debugserver_client, command, &response);
        debugserver_command_free(command);
        command = NULL;
        if (response) {
            if (strncmp(response, "OK", 2)) {
                debugserver_client_handle_response(debugserver_client, &response, 0);
                goto cleanup;
            }
            free(response);
            response = NULL;
        }

        /* set thread */
        debug_info("Setting thread...");
        debugserver_command_new("Hc0", 0, NULL, &command);
        dres = debugserver_client_send_command(debugserver_client, command, &response);
        debugserver_command_free(command);
        command = NULL;
        if (response) {
            if (strncmp(response, "OK", 2)) {
                debugserver_client_handle_response(debugserver_client, &response, 0);
                goto cleanup;
            }
            free(response);
            response = NULL;
        }

        /* continue running process */
        debug_info("Continue running process...");
        debugserver_command_new("c", 0, NULL, &command);
        dres = debugserver_client_send_command(debugserver_client, command, &response);
        debugserver_command_free(command);
        command = NULL;

        /* main loop which is parsing/handling packets during the run */
        debug_info("Entering run loop...");
        while (!quit_flag) {
            if (dres != DEBUGSERVER_E_SUCCESS) {
                debug_info("failed to receive response");
                break;
            }

            if (response) {
                debug_info("response: %s", response);
                dres = debugserver_client_handle_response(debugserver_client, &response, 1);
            }

            sleep(1);
        }

        /* kill process after we finished */
        debug_info("Killing process...");
        debugserver_command_new("k", 0, NULL, &command);
        dres = debugserver_client_send_command(debugserver_client, command, &response);
        debugserver_command_free(command);
        command = NULL;
        if (response) {
            if (strncmp(response, "OK", 2)) {
                debugserver_client_handle_response(debugserver_client, &response, 0);
                goto cleanup;
            }
            free(response);
            response = NULL;
        }

        res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1;
        break;
    }

cleanup:
    /* cleanup the house */
    if (environment) {
        for (environment_index = 0; environment_index < environment_count; environment_index++) {
            free(environment[environment_index]);
        }
        free(environment);
    }

    if (working_directory)
        free(working_directory);

    if (path)
        free(path);

    if (response)
        free(response);

    if (debugserver_client)
        debugserver_client_free(debugserver_client);

    if (device)
        idevice_free(device);

    return res;
}
int main(int argc, char** argv)
{
	int res = -1;
	char* udid = NULL;

	int c = 0;
	int optidx = 0;
	const struct option longopts[] = {
		{ "udid", required_argument, NULL, 'u' },
		{ "help", no_argument, NULL, 'h' },
		{ NULL, 0, NULL, 0}
	};

	while ((c = getopt_long(argc, argv, "du:h", longopts, &optidx)) != -1) {
		switch (c) {
		case 'u':
			udid = strdup(optarg);
			break;
		case 'h':
			print_usage();
			return 0;
		case 'd':
			idevice_set_debug_level(1);
			break;
		default:
			print_usage();
			return -1;
		}
	}

	argc -= optind;
	argv += optind;

	if (argc > 1) {
		print_usage();
		return -1;
	}

	idevice_t device = NULL;
	if (idevice_new(&device, udid) != IDEVICE_E_SUCCESS) {
		fprintf(stderr, "ERROR: Could not connect to device\n");
		return -1;
	}

	lockdownd_client_t lockdown = NULL;
	lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicename");
	if (lerr != LOCKDOWN_E_SUCCESS) {
		idevice_free(device);
		fprintf(stderr, "ERROR: lockdown connection failed, lockdown error %d\n", lerr);
		return -1;
	}

	if (argc == 0) {
		// getting device name
		char* name = NULL;
		lerr = lockdownd_get_device_name(lockdown, &name);
		if (name) {
			printf("%s\n", name);
			free(name);
			res = 0;
		} else {
			fprintf(stderr, "ERROR: Could not get device name, lockdown error %d\n", lerr);
		}
	} else {
		// setting device name
		lerr = lockdownd_set_value(lockdown, NULL, "DeviceName", plist_new_string(argv[0]));
		if (lerr == LOCKDOWN_E_SUCCESS) {
			printf("device name set to '%s'\n", argv[0]);
			res = 0;
		} else {
			fprintf(stderr, "ERROR: Could not set device name, lockdown error %d\n", lerr);
		}
	}

	lockdownd_client_free(lockdown);
	idevice_free(device);

	if (udid) {
		free(udid);
	}

	return res;
}
static void parse_opts(int argc, char **argv)
{
	static struct option longopts[] = {
		{"help", 0, NULL, 'h'},
		{"udid", 1, NULL, 'u'},
		{"list-apps", 0, NULL, 'l'},
		{"install", 1, NULL, 'i'},
		{"uninstall", 1, NULL, 'U'},
		{"upgrade", 1, NULL, 'g'},
		{"list-archives", 0, NULL, 'L'},
		{"archive", 1, NULL, 'a'},
		{"restore", 1, NULL, 'r'},
		{"remove-archive", 1, NULL, 'R'},
		{"options", 1, 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;
		}

		/* verify if multiple modes have been supplied */
		switch (c) {
		case 'l':
		case 'i':
		case 'g':
		case 'L':
		case 'a':
		case 'r':
		case 'R':
			if (cmd != CMD_NONE) {
				printf("ERROR: A mode has already been supplied. Multiple modes are not supported.\n");
				print_usage(argc, argv);
				exit(2);
			}
			break;
		default:
			break;
		}

		switch (c) {
		case 'h':
			print_usage(argc, argv);
			exit(0);
		case 'u':
			if (str_is_udid(optarg) == 0) {
				udid = strdup(optarg);
				break;
			}
			if (strchr(optarg, '.') != NULL) {
				fprintf(stderr, "WARNING: Using \"-u\" for \"--uninstall\" is deprecated. Please use \"-U\" instead.\n");
				cmd = CMD_UNINSTALL;
				appid = strdup(optarg);
			} else {
				printf("ERROR: Invalid UDID specified\n");
				print_usage(argc, argv);
				exit(2);
			}
			break;
		case 'l':
			cmd = CMD_LIST_APPS;
			break;
		case 'i':
			cmd = CMD_INSTALL;
			appid = strdup(optarg);
			break;
		case 'U':
			if (str_is_udid(optarg) == 0) {
				fprintf(stderr, "WARNING: Using \"-U\" for \"--udid\" is deprecated. Please use \"-u\" instead.\n");
				udid = strdup(optarg);
				break;
			}
			cmd = CMD_UNINSTALL;
			appid = strdup(optarg);
			break;
		case 'g':
			cmd = CMD_UPGRADE;
			appid = strdup(optarg);
			break;
		case 'L':
			cmd = CMD_LIST_ARCHIVES;
			break;
		case 'a':
			cmd = CMD_ARCHIVE;
			appid = strdup(optarg);
			break;
		case 'r':
			cmd = CMD_RESTORE;
			appid = strdup(optarg);
			break;
		case 'R':
			cmd = CMD_REMOVE_ARCHIVE;
			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':
			idevice_set_debug_level(1);
			break;
		default:
			print_usage(argc, argv);
			exit(2);
		}
	}

	if (cmd == CMD_NONE) {
		printf("ERROR: No mode/operation was supplied.\n");
	}

	if (cmd == CMD_NONE || optind <= 1 || (argc - optind > 0)) {
		print_usage(argc, argv);
		exit(2);
	}
}