diff options
Diffstat (limited to 'tools/idevicebackup2.c')
| -rw-r--r-- | tools/idevicebackup2.c | 715 | 
1 files changed, 440 insertions, 275 deletions
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c index 58fda8d..12d6083 100644 --- a/tools/idevicebackup2.c +++ b/tools/idevicebackup2.c @@ -2,8 +2,8 @@   * idevicebackup2.c   * Command line interface to use the device's backup and restore service   * - * Copyright (c) 2010-2018 Nikias Bassen All Rights Reserved. - * Copyright (c) 2009-2010 Martin Szulecki All Rights Reserved. + * Copyright (c) 2010-2022 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2009-2010 Martin Szulecki, All Rights Reserved.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -24,6 +24,8 @@  #include <config.h>  #endif +#define TOOL_NAME "idevicebackup2" +  #include <stdio.h>  #include <string.h>  #include <errno.h> @@ -34,6 +36,7 @@  #include <libgen.h>  #include <ctype.h>  #include <time.h> +#include <getopt.h>  #include <libimobiledevice/libimobiledevice.h>  #include <libimobiledevice/lockdown.h> @@ -42,17 +45,22 @@  #include <libimobiledevice/afc.h>  #include <libimobiledevice/installation_proxy.h>  #include <libimobiledevice/sbservices.h> -#include "common/utils.h" +#include <libimobiledevice/diagnostics_relay.h> +#include <libimobiledevice-glue/utils.h> +#include <plist/plist.h>  #include <endianness.h>  #define LOCK_ATTEMPTS 50  #define LOCK_WAIT 200000 -#ifdef WIN32 +#ifdef _WIN32  #include <windows.h>  #include <conio.h>  #define sleep(x) Sleep(x*1000) +#ifndef ELOOP +#define ELOOP 114 +#endif  #else  #include <termios.h>  #include <sys/statvfs.h> @@ -66,6 +74,7 @@  static int verbose = 1;  static int quit_flag = 0; +static int passcode_requested = 0;  #define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); }; @@ -107,6 +116,10 @@ static void notify_cb(const char *notification, void *userdata)  		quit_flag++;  	} else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {  		backup_domain_changed = 1; +	} else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) { +		passcode_requested = 1; +	} else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) { +		passcode_requested = 0;  	} else {  		PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);  	} @@ -118,21 +131,15 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil  		return;  	} -	char **fileinfo = NULL; +	plist_t fileinfo = NULL;  	uint32_t fsize = 0; -	afc_get_file_info(afc, filename, &fileinfo); +	afc_get_file_info_plist(afc, filename, &fileinfo);  	if (!fileinfo) {  		return;  	} -	int i; -	for (i = 0; fileinfo[i]; i+=2) { -		if (!strcmp(fileinfo[i], "st_size")) { -			fsize = atol(fileinfo[i+1]); -			break; -		} -	} -	afc_dictionary_free(fileinfo); +	fsize = plist_dict_get_uint(fileinfo, "st_size"); +	plist_free(fileinfo);  	if (fsize == 0) {  		return; @@ -165,7 +172,7 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil  static int __mkdir(const char* path, int mode)  { -#ifdef WIN32 +#ifdef _WIN32  	return mkdir(path);  #else  	return mkdir(path, mode); @@ -177,9 +184,8 @@ static int mkdir_with_parents(const char *dir, int mode)  	if (!dir) return -1;  	if (__mkdir(dir, mode) == 0) {  		return 0; -	} else { -		if (errno == EEXIST) return 0;  	} +	if (errno == EEXIST) return 0;  	int res;  	char *parent = strdup(dir);  	char *parentdir = dirname(parent); @@ -195,7 +201,7 @@ static int mkdir_with_parents(const char *dir, int mode)  	return res;  } -#ifdef WIN32 +#ifdef _WIN32  static int win32err_to_errno(int err_value)  {  	switch (err_value) { @@ -212,7 +218,7 @@ static int win32err_to_errno(int err_value)  static int remove_file(const char* path)  {  	int e = 0; -#ifdef WIN32 +#ifdef _WIN32  	if (!DeleteFile(path)) {  		e = win32err_to_errno(GetLastError());  	} @@ -227,7 +233,7 @@ static int remove_file(const char* path)  static int remove_directory(const char* path)  {  	int e = 0; -#ifdef WIN32 +#ifdef _WIN32  	if (!RemoveDirectory(path)) {  		e = win32err_to_errno(GetLastError());  	} @@ -345,7 +351,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d  	char *udid_uppercase = NULL;  	lockdownd_client_t lockdown = NULL; -	if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2") != LOCKDOWN_E_SUCCESS) { +	if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {  		return NULL;  	} @@ -366,7 +372,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d  	plist_t app_dict = plist_new_dict();  	plist_t installed_apps = plist_new_array();  	instproxy_client_t ip = NULL; -	if (instproxy_client_start_service(device, &ip, "idevicebackup2") == INSTPROXY_E_SUCCESS) { +	if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {  		plist_t client_opts = instproxy_client_options_new();  		instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);  		instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL); @@ -375,7 +381,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d  		instproxy_browse(ip, client_opts, &apps);  		sbservices_client_t sbs = NULL; -		if (sbservices_client_start_service(device, &sbs, "idevicebackup2") != SBSERVICES_E_SUCCESS) { +		if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {  			printf("Couldn't establish sbservices connection. Continuing anyway.\n");  		} @@ -448,7 +454,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d  	/* Installed Applications */  	plist_dict_set_item(ret, "Installed Applications", installed_apps); -	plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0)); +	plist_dict_set_item(ret, "Last Backup Date", +#ifdef HAVE_PLIST_UNIX_DATE +		plist_new_unix_date(time(NULL)) +#else +		plist_new_date(time(NULL) - MAC_EPOCH, 0) +#endif +	);  	value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");  	if (value_node) @@ -545,9 +557,11 @@ static int write_restore_applications(plist_t info_plist, afc_client_t afc)  	uint32_t applications_plist_xml_length = 0;  	plist_t applications_plist = plist_dict_get_item(info_plist, "Applications"); -	if (applications_plist) { -		plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length); +	if (!applications_plist) { +		printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n"); +		return 0;  	} +	plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);  	if (!applications_plist_xml) {  		printf("Error preparing RestoreApplications.plist\n");  		goto leave; @@ -598,7 +612,7 @@ static int mb2_status_check_snapshot_state(const char *path, const char *udid, c  	plist_t status_plist = NULL;  	char *file_path = string_build_path(path, udid, "Status.plist", NULL); -	plist_read_from_filename(&status_plist, file_path); +	plist_read_from_file(file_path, &status_plist, NULL);  	free(file_path);  	if (!status_plist) {  		printf("Could not read Status.plist!\n"); @@ -626,19 +640,19 @@ static void do_post_notification(idevice_t device, const char *notification)  	lockdownd_client_t lockdown = NULL; -	if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2") != LOCKDOWN_E_SUCCESS) { +	if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {  		return;  	} -	lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service); -	if (service && service->port) { +	lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service); +	if (ldret == LOCKDOWN_E_SUCCESS) {  		np_client_new(device, service, &np);  		if (np) {  			np_post_notification(np, notification);  			np_client_free(np);  		}  	} else { -		printf("Could not start %s\n", NP_SERVICE_NAME); +		printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));  	}  	if (service) { @@ -737,8 +751,18 @@ static int errno_to_device_error(int errno_value)  			return -6;  		case EEXIST:  			return -7; +		case ENOTDIR: +			return -8; +		case EISDIR: +			return -9; +		case ELOOP: +			return -10; +		case EIO: +			return -11; +		case ENOSPC: +			return -15;  		default: -			return -errno_value; +			return -1;  	}  } @@ -749,7 +773,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char  	uint32_t bytes = 0;  	char *localfile = string_build_path(backup_dir, path, NULL);  	char buf[32768]; -#ifdef WIN32 +#ifdef _WIN32  	struct _stati64 fst;  #else  	struct stat fst; @@ -760,7 +784,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char  	int errcode = -1;  	int result = -1;  	uint32_t length; -#ifdef WIN32 +#ifdef _WIN32  	uint64_t total;  	uint64_t sent;  #else @@ -791,7 +815,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char  		goto leave_proto_err;  	} -#ifdef WIN32 +#ifdef _WIN32  	if (_stati64(localfile, &fst) < 0)  #else  	if (stat(localfile, &fst) < 0) @@ -954,10 +978,12 @@ static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** fil  		if ((nlen == 0) && (rlen == 4)) {  			// a zero length means no more files to receive  			return 0; -		} else if(rlen == 0) { +		} +		if (rlen == 0) {  			// device needs more time, waiting...  			continue; -		} else if (nlen > 4096) { +		} +		if (nlen > 4096) {  			// filename length is too large  			printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);  			return 0; @@ -1197,7 +1223,12 @@ static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plis  				plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));  				plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));  				plist_dict_set_item(fdict, "DLFileModificationDate", -						    plist_new_date(st.st_mtime - MAC_EPOCH, 0)); +#ifdef HAVE_PLIST_UNIX_DATE +						    plist_new_unix_date(st.st_mtime) +#else +						    plist_new_date(st.st_mtime - MAC_EPOCH, 0) +#endif +				);  				plist_dict_set_item(dirlist, ep->d_name, fdict);  				free(fpath); @@ -1322,7 +1353,7 @@ static void mb2_copy_directory_by_path(const char *src, const char *dst)  	}  } -#ifdef WIN32 +#ifdef _WIN32  #define BS_CC '\b'  #define my_getch getch  #else @@ -1349,7 +1380,8 @@ static void get_hidden_input(char *buf, int maxlen)  	while ((c = my_getch())) {  		if ((c == '\r') || (c == '\n')) {  			break; -		} else if (isprint(c)) { +		} +		if (isprint(c)) {  			if (pwlen < maxlen-1)  				buf[pwlen++] = c;  			fputc('*', stderr); @@ -1397,49 +1429,58 @@ static void clean_exit(int sig)  	quit_flag++;  } -static void print_usage(int argc, char **argv) +static void print_usage(int argc, char **argv, int is_error)  { -	char *name = NULL; -	name = strrchr(argv[0], '/'); -	printf("Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0])); -	printf("Create or restore backup from the current or specified directory.\n\n"); -	printf("commands:\n"); -	printf("  backup\tcreate backup for the device\n"); -	printf("    --full\t\tforce full backup from device.\n"); -	printf("  restore\trestore last backup to the device\n"); -	printf("    --system\t\trestore system files, too.\n"); -	printf("    --no-reboot\t\tdo NOT reboot the system when done (default: yes).\n"); -	printf("    --copy\t\tcreate a copy of backup folder before restoring.\n"); -	printf("    --settings\t\trestore device settings from the backup.\n"); -	printf("    --remove\t\tremove items which are not being restored\n"); -	printf("    --skip-apps\t\tdo not trigger re-installation of apps after restore\n"); -	printf("    --password PWD\tsupply the password of the source backup\n"); -	printf("  info\t\tshow details about last completed backup of device\n"); -	printf("  list\t\tlist files of last completed backup in CSV format\n"); -	printf("  unback\tunpack a completed backup in DIRECTORY/_unback_/\n"); -	printf("  encryption on|off [PWD]\tenable or disable backup encryption\n"); -	printf("    NOTE: password will be requested in interactive mode if omitted\n"); -	printf("  changepw [OLD NEW]  change backup password on target device\n"); -	printf("    NOTE: passwords will be requested in interactive mode if omitted\n"); -	printf("  cloud on|off\tenable or disable cloud use (requires iCloud account)\n"); -	printf("\n"); -	printf("options:\n"); -	printf("  -d, --debug\t\tenable communication debugging\n"); -	printf("  -u, --udid UDID\ttarget specific device by UDID\n"); -	printf("  -s, --source UDID\tuse backup data from device specified by UDID\n"); -	printf("  -i, --interactive\trequest passwords interactively\n"); -	printf("  -h, --help\t\tprints usage information\n"); -	printf("\n"); -	printf("Homepage: <" PACKAGE_URL ">\n"); +	char *name = strrchr(argv[0], '/'); +	fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0])); +	fprintf(is_error ? stderr : stdout, +		"\n" +		"Create or restore backup in/from the specified directory.\n" +		"\n" +		"CMD:\n" +		"  backup        create backup for the device\n" +		"    --full              force full backup from device.\n" +		"  restore       restore last backup to the device\n" +		"    --system            restore system files, too.\n" +		"    --no-reboot         do NOT reboot the device when done (default: yes).\n" +		"    --copy              create a copy of backup folder before restoring.\n" +		"    --settings          restore device settings from the backup.\n" +		"    --remove            remove items which are not being restored\n" +		"    --skip-apps         do not trigger re-installation of apps after restore\n" +		"    --password PWD      supply the password for the encrypted source backup\n" +		"  info          show details about last completed backup of device\n" +		"  list          list files of last completed backup in CSV format\n" +		"  unback        unpack a completed backup in DIRECTORY/_unback_/\n" +		"  encryption on|off [PWD]       enable or disable backup encryption\n" +		"  changepw [OLD NEW]    change backup password on target device\n" +		"  cloud on|off          enable or disable cloud use (requires iCloud account)\n" +		"\n" +		"NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n" +		"be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n" +		"See man page for further details.\n" +		"\n" +		"OPTIONS:\n" +		"  -u, --udid UDID       target specific device by UDID\n" +		"  -s, --source UDID     use backup data from device specified by UDID\n" +		"  -n, --network         connect to network device\n" +		"  -i, --interactive     request passwords interactively\n" +		"  -d, --debug           enable communication debugging\n" +		"  -h, --help            prints usage information\n" +		"  -v, --version         prints version information\n" +		"\n" +		"Homepage:    <" PACKAGE_URL ">\n" +		"Bug Reports: <" PACKAGE_BUGREPORT ">\n" +	);  }  int main(int argc, char *argv[])  {  	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;  	lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR; -	int i; +	int i = 0;  	char* udid = NULL;  	char* source_udid = NULL; +	int use_network = 0;  	lockdownd_service_descriptor_t service = NULL;  	int cmd = -1;  	int cmd_flags = 0; @@ -1453,223 +1494,257 @@ int main(int argc, char *argv[])  	plist_t node_tmp = NULL;  	plist_t info_plist = NULL;  	plist_t opts = NULL; + +	idevice_t device = NULL; +	afc_client_t afc = NULL; +	np_client_t np = NULL; +	lockdownd_client_t lockdown = NULL; +	mobilebackup2_client_t mobilebackup2 = NULL;  	mobilebackup2_error_t err; +	uint64_t lockfile = 0; + +#define OPT_SYSTEM 1 +#define OPT_REBOOT 2 +#define OPT_NO_REBOOT 3 +#define OPT_COPY 4 +#define OPT_SETTINGS 5 +#define OPT_REMOVE 6 +#define OPT_SKIP_APPS 7 +#define OPT_PASSWORD 8 +#define OPT_FULL 9 + +	int c = 0; +	const struct option longopts[] = { +		{ "debug", no_argument, NULL, 'd' }, +		{ "help", no_argument, NULL, 'h' }, +		{ "udid", required_argument, NULL, 'u' }, +		{ "source", required_argument, NULL, 's' }, +		{ "interactive", no_argument, NULL, 'i' }, +		{ "network", no_argument, NULL, 'n' }, +		{ "version", no_argument, NULL, 'v' }, +		// command options: +		{ "system", no_argument, NULL, OPT_SYSTEM }, +		{ "reboot", no_argument, NULL, OPT_REBOOT }, +		{ "no-reboot", no_argument, NULL, OPT_NO_REBOOT }, +		{ "copy", no_argument, NULL, OPT_COPY }, +		{ "settings", no_argument, NULL, OPT_SETTINGS }, +		{ "remove", no_argument, NULL, OPT_REMOVE }, +		{ "skip-apps", no_argument, NULL, OPT_SKIP_APPS }, +		{ "password", required_argument, NULL, OPT_PASSWORD }, +		{ "full", no_argument, NULL, OPT_FULL }, +		{ NULL, 0, NULL, 0} +	};  	/* we need to exit cleanly on running backups and restores or we cause havok */  	signal(SIGINT, clean_exit);  	signal(SIGTERM, clean_exit); -#ifndef WIN32 +#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")) { +	while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) { +		switch (c) { +		case 'd':  			idevice_set_debug_level(1); -			continue; -		} -		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) { -			i++; -			if (!argv[i] || !*argv[i]) { -				print_usage(argc, argv); -				return -1; +			break; +		case 'u': +			if (!*optarg) { +				fprintf(stderr, "ERROR: UDID argument must not be empty!\n"); +				print_usage(argc, argv, 1); +				return 2;  			} -			udid = strdup(argv[i]); -			continue; -		} -		else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--source")) { -			i++; -			if (!argv[i] || !*argv[i]) { -				print_usage(argc, argv); -				return -1; +			udid = strdup(optarg); +			break; +		case 's': +			if (!*optarg) { +				fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n"); +				print_usage(argc, argv, 1); +				return 2;  			} -			source_udid = strdup(argv[i]); -			continue; -		} -		else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--interactive")) { +			source_udid = strdup(optarg); +			break; +		case 'i':  			interactive_mode = 1; -			continue; -		} -		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { -			print_usage(argc, argv); +			break; +		case 'n': +			use_network = 1; +			break; +		case 'h': +			print_usage(argc, argv, 0);  			return 0; -		} -		else if (!strcmp(argv[i], "backup")) { -			cmd = CMD_BACKUP; -		} -		else if (!strcmp(argv[i], "restore")) { -			cmd = CMD_RESTORE; -		} -		else if (!strcmp(argv[i], "--system")) { +		case 'v': +			printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); +			return 0; +		case OPT_SYSTEM:  			cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES; -		} -		else if (!strcmp(argv[i], "--reboot")) { +			break; +		case OPT_REBOOT:  			cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT; -		} -		else if (!strcmp(argv[i], "--no-reboot")) { +			break; +		case OPT_NO_REBOOT:  			cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT; -		} -		else if (!strcmp(argv[i], "--copy")) { +			break; +		case OPT_COPY:  			cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP; -		} -		else if (!strcmp(argv[i], "--settings")) { +			break; +		case OPT_SETTINGS:  			cmd_flags |= CMD_FLAG_RESTORE_SETTINGS; -		} -		else if (!strcmp(argv[i], "--remove")) { +			break; +		case OPT_REMOVE:  			cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS; -		} -		else if (!strcmp(argv[i], "--skip-apps")) { +			break; +		case OPT_SKIP_APPS:  			cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS; -		} -		else if (!strcmp(argv[i], "--password")) { -			i++; -			if (!argv[i]) { -				print_usage(argc, argv); -				return -1; -			} -			if (backup_password) -				free(backup_password); -			backup_password = strdup(argv[i]); -			continue; -		} -		else if (!strcmp(argv[i], "cloud")) { -			cmd = CMD_CLOUD; -			i++; -			if (!argv[i]) { -				printf("No argument given for cloud command; requires either 'on' or 'off'.\n"); -				print_usage(argc, argv); -				return -1; -			} -			if (!strcmp(argv[i], "on")) { -				cmd_flags |= CMD_FLAG_CLOUD_ENABLE; -			} else if (!strcmp(argv[i], "off")) { -				cmd_flags |= CMD_FLAG_CLOUD_DISABLE; -			} else { -				printf("Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]); -			} -			continue; -		} -		else if (!strcmp(argv[i], "--full")) { +			break; +		case OPT_PASSWORD: +			free(backup_password); +			backup_password = strdup(optarg); +			break; +		case OPT_FULL:  			cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP; +			break; +		default: +			print_usage(argc, argv, 1); +			return 2;  		} -		else if (!strcmp(argv[i], "info")) { -			cmd = CMD_INFO; -			verbose = 0; -		} -		else if (!strcmp(argv[i], "list")) { -			cmd = CMD_LIST; -			verbose = 0; +	} +	argc -= optind; +	argv += optind; + +	if (!argv[0]) { +		fprintf(stderr, "ERROR: No command specified.\n"); +		print_usage(argc+optind, argv-optind, 1); +		return 2; +	} + +	if (!strcmp(argv[0], "backup")) { +		cmd = CMD_BACKUP; +	} +	else if (!strcmp(argv[0], "restore")) { +		cmd = CMD_RESTORE; +	} +	else if (!strcmp(argv[0], "cloud")) { +		cmd = CMD_CLOUD; +		i = 1; +		if (!argv[i]) { +			fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n"); +			print_usage(argc+optind, argv-optind, 1); +			return 2;  		} -		else if (!strcmp(argv[i], "unback")) { -			cmd = CMD_UNBACK; +		if (!strcmp(argv[i], "on")) { +			cmd_flags |= CMD_FLAG_CLOUD_ENABLE; +		} else if (!strcmp(argv[i], "off")) { +			cmd_flags |= CMD_FLAG_CLOUD_DISABLE; +		} else { +			fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]); +			print_usage(argc+optind, argv-optind, 1); +			return 2; +		} +	} +	else if (!strcmp(argv[0], "info")) { +		cmd = CMD_INFO; +		verbose = 0; +	} +	else if (!strcmp(argv[0], "list")) { +		cmd = CMD_LIST; +		verbose = 0; +	} +	else if (!strcmp(argv[0], "unback")) { +		cmd = CMD_UNBACK; +	} +	else if (!strcmp(argv[0], "encryption")) { +		cmd = CMD_CHANGEPW; +		i = 1; +		if (!argv[i]) { +			fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n"); +			print_usage(argc+optind, argv-optind, 1); +			return 2; +		} +		if (!strcmp(argv[i], "on")) { +			cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE; +		} else if (!strcmp(argv[i], "off")) { +			cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE; +		} else { +			fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]); +			print_usage(argc+optind, argv-optind, 1); +			return 2;  		} -		else if (!strcmp(argv[i], "encryption")) { -			cmd = CMD_CHANGEPW; -			i++; -			if (!argv[i]) { -				printf("No argument given for encryption command; requires either 'on' or 'off'.\n"); -				print_usage(argc, argv); -				return -1; -			} -			if (!strcmp(argv[i], "on")) { -				cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE; -			} else if (!strcmp(argv[i], "off")) { -				cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE; -			} else { -				printf("Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]); -			} -			// check if a password was given on the command line -			if (newpw) { -				free(newpw); -				newpw = NULL; -			} -			if (backup_password) { -				free(backup_password); -				backup_password = NULL; -			} -			i++; -			if (argv[i]) { -				if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) { -					newpw = strdup(argv[i]); -				} else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) { -					backup_password = strdup(argv[i]); -				} +		// check if a password was given on the command line +		free(newpw); +		newpw = NULL; +		free(backup_password); +		backup_password = NULL; +		i++; +		if (argv[i]) { +			if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) { +				newpw = strdup(argv[i]); +			} else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) { +				backup_password = strdup(argv[i]);  			} -			continue;  		} -		else if (!strcmp(argv[i], "changepw")) { -			cmd = CMD_CHANGEPW; -			cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW; -			// check if passwords were given on command line -			if (newpw) { -				free(newpw); -				newpw = NULL; -			} -			if (backup_password) { -				free(backup_password); -				backup_password = NULL; -			} +	} +	else if (!strcmp(argv[0], "changepw")) { +		cmd = CMD_CHANGEPW; +		cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW; +		// check if passwords were given on command line +		free(newpw); +		newpw = NULL; +		free(backup_password); +		backup_password = NULL; +		i = 1; +		if (argv[i]) { +			backup_password = strdup(argv[i]);  			i++; -			if (argv[i]) { -				backup_password = strdup(argv[i]); -				i++; -				if (!argv[i]) { -					printf("Old and new passwords have to be passed as arguments for the changepw command\n"); -					print_usage(argc, argv); -					return -1; -				} -				newpw = strdup(argv[i]); +			if (!argv[i]) { +				fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n"); +				print_usage(argc+optind, argv-optind, 1); +				return 2;  			} -			continue; -		} -		else if (backup_directory == NULL) { -			backup_directory = argv[i]; -		} -		else { -			print_usage(argc, argv); -			return -1; +			newpw = strdup(argv[i]);  		}  	} +	i++; +	if (argv[i]) { +		backup_directory = argv[i]; +	} +  	/* verify options */  	if (cmd == -1) { -		printf("No command specified.\n"); -		print_usage(argc, argv); -		return -1; +		fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]); +		print_usage(argc+optind, argv-optind, 1); +		return 2;  	}  	if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {  		backup_directory = (char*)".this_folder_is_not_present_on_purpose";  	} else {  		if (backup_directory == NULL) { -			printf("No target backup directory specified.\n"); -			print_usage(argc, argv); -			return -1; +			fprintf(stderr, "ERROR: No target backup directory specified.\n"); +			print_usage(argc+optind, argv-optind, 1); +			return 2;  		}  		/* verify if passed backup directory exists */  		if (stat(backup_directory, &st) != 0) { -			printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory); +			fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);  			return -1;  		}  	} -	idevice_t device = NULL; -	if (udid) { -		ret = idevice_new(&device, udid); -		if (ret != IDEVICE_E_SUCCESS) { -			printf("No device found with udid %s, is it plugged in?\n", udid); -			return -1; +	ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); +	if (ret != IDEVICE_E_SUCCESS) { +		if (udid) { +			printf("No device found with udid %s.\n", udid); +		} else { +			printf("No device found.\n");  		} +		return -1;  	} -	else -	{ -		ret = idevice_new(&device, NULL); -		if (ret != IDEVICE_E_SUCCESS) { -			printf("No device found, is it plugged in?\n"); -			return -1; -		} + +	if (!udid) {  		idevice_get_udid(device, &udid);  	} @@ -1680,6 +1755,20 @@ int main(int argc, char *argv[])  	uint8_t is_encrypted = 0;  	char *info_path = NULL;  	if (cmd == CMD_CHANGEPW) { +		if (!interactive_mode) { +			if (!newpw) { +				newpw = getenv("BACKUP_PASSWORD_NEW"); +				if (newpw) { +					newpw = strdup(newpw); +				} +			} +			if (!backup_password) { +				backup_password = getenv("BACKUP_PASSWORD"); +				if (backup_password) { +					backup_password = strdup(backup_password); +				} +			} +		}  		if (!interactive_mode && !backup_password && !newpw) {  			idevice_free(device);  			printf("ERROR: Can't get password input in non-interactive mode. Either pass password(s) on the command line, or enable interactive mode with -i or --interactive.\n"); @@ -1700,7 +1789,7 @@ int main(int argc, char *argv[])  				free(info_path);  			}  			plist_t manifest_plist = NULL; -			plist_read_from_filename(&manifest_plist, manifest_path); +			plist_read_from_file(manifest_path, &manifest_plist, NULL);  			if (!manifest_plist) {  				idevice_free(device);  				free(info_path); @@ -1721,6 +1810,12 @@ int main(int argc, char *argv[])  	if (cmd != CMD_CLOUD && is_encrypted) {  		PRINT_VERBOSE(1, "This is an encrypted backup.\n");  		if (backup_password == NULL) { +			backup_password = getenv("BACKUP_PASSWORD"); +			if (backup_password) { +				backup_password = strdup(backup_password); +			} +		} +		if (backup_password == NULL) {  			if (interactive_mode) {  				backup_password = ask_for_password("Enter backup password", 0);  			} @@ -1739,8 +1834,7 @@ int main(int argc, char *argv[])  		}  	} -	lockdownd_client_t lockdown = NULL; -	if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2"))) { +	if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {  		printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);  		idevice_free(device);  		return -1; @@ -1757,32 +1851,43 @@ int main(int argc, char *argv[])  		node_tmp = NULL;  	} +	/* get ProductVersion */ +	int device_version = idevice_get_device_version(device); +  	/* start notification_proxy */ -	np_client_t np = NULL;  	ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);  	if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {  		np_client_new(device, service, &np);  		np_set_notify_callback(np, notify_cb, NULL); -		const char *noties[5] = { +		const char *noties[7] = {  			NP_SYNC_CANCEL_REQUEST,  			NP_SYNC_SUSPEND_REQUEST,  			NP_SYNC_RESUME_REQUEST,  			NP_BACKUP_DOMAIN_CHANGED, +			"com.apple.LocalAuthentication.ui.presented", +			"com.apple.LocalAuthentication.ui.dismissed",  			NULL  		};  		np_observe_notifications(np, noties);  	} else { -		printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME); +		printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret)); +		cmd = CMD_LEAVE; +		goto checkpoint; +	} +	if (service) { +		lockdownd_service_descriptor_free(service); +		service = NULL;  	} -	afc_client_t afc = NULL;  	if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {  		/* start AFC, we need this for the lock file */ -		service->port = 0; -		service->ssl_enabled = 0;  		ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);  		if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {  			afc_client_new(device, service, &afc); +		} else { +			printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret)); +			cmd = CMD_LEAVE; +			goto checkpoint;  		}  	} @@ -1792,7 +1897,6 @@ int main(int argc, char *argv[])  	}  	/* start mobilebackup service and retrieve port */ -	mobilebackup2_client_t mobilebackup2 = NULL;  	ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);  	lockdownd_client_free(lockdown);  	lockdown = NULL; @@ -1827,7 +1931,7 @@ int main(int argc, char *argv[])  		/* verify existing Info.plist */  		if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {  			PRINT_VERBOSE(1, "Reading Info.plist from backup.\n"); -			plist_read_from_filename(&info_plist, info_path); +			plist_read_from_file(info_path, &info_plist, NULL);  			if (!info_plist) {  				printf("Could not read Info.plist\n"); @@ -1842,7 +1946,6 @@ int main(int argc, char *argv[])  			}  		} -		uint64_t lockfile = 0;  		if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {  			do_post_notification(device, NP_SYNC_WILL_START);  			afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile); @@ -1855,15 +1958,16 @@ int main(int argc, char *argv[])  				if (aerr == AFC_E_SUCCESS) {  					do_post_notification(device, NP_SYNC_DID_START);  					break; -				} else if (aerr == AFC_E_OP_WOULD_BLOCK) { +				} +				if (aerr == AFC_E_OP_WOULD_BLOCK) {  					usleep(LOCK_WAIT);  					continue; -				} else { -					fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr); -					afc_file_close(afc, lockfile); -					lockfile = 0; -					cmd = CMD_LEAVE;  				} + +				fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr); +				afc_file_close(afc, lockfile); +				lockfile = 0; +				cmd = CMD_LEAVE;  			}  			if (i == LOCK_ATTEMPTS) {  				fprintf(stderr, "ERROR: timeout while locking for sync\n"); @@ -1921,7 +2025,7 @@ checkpoint:  				cmd = CMD_LEAVE;  			}  			remove_file(info_path); -			plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); +			plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);  			free(info_path);  			plist_free(info_plist); @@ -1948,6 +2052,16 @@ checkpoint:  				}	else {  					PRINT_VERBOSE(1, "Incremental backup mode.\n");  				} +				if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) { +					/* let's wait 2 second to see if the device passcode is requested */ +					int retries = 20; +					while (retries-- > 0 && !passcode_requested) { +						usleep(100000); +					} +					if (passcode_requested) { +						printf("*** Waiting for passcode to be entered on the device ***\n"); +					} +				}  			} else {  				if (err == MOBILEBACKUP2_E_BAD_VERSION) {  					printf("ERROR: Could not start backup process: backup protocol version mismatch!\n"); @@ -1982,9 +2096,8 @@ checkpoint:  			PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));  			plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));  			PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No")); -			if (cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) -				plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(1)); -				PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No")); +			plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS)); +			PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));  			if (backup_password != NULL) {  				plist_dict_set_item(opts, "Password", plist_new_string(backup_password));  			} @@ -1998,9 +2111,8 @@ checkpoint:  				if (write_restore_applications(info_plist, afc) < 0) {  					cmd = CMD_LEAVE;  					break; -				} else { -					PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");  				} +				PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");  			}  			/* Start restore */ @@ -2055,6 +2167,12 @@ checkpoint:  			if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {  				if (!willEncrypt) {  					if (!newpw) { +						newpw = getenv("BACKUP_PASSWORD"); +						if (newpw) { +							newpw = strdup(newpw); +						} +					} +					if (!newpw) {  						newpw = ask_for_password("Enter new backup password", 1);  					}  					if (!newpw) { @@ -2071,6 +2189,12 @@ checkpoint:  			} else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {  				if (willEncrypt) {  					if (!backup_password) { +						backup_password = getenv("BACKUP_PASSWORD"); +						if (backup_password) { +							backup_password = strdup(backup_password); +						} +					} +					if (!backup_password) {  						backup_password = ask_for_password("Enter current backup password", 0);  					}  				} else { @@ -2108,6 +2232,32 @@ checkpoint:  			}  			if (newpw || backup_password) {  				mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts); +				uint8_t passcode_hint = 0; +				if (device_version >= IDEVICE_DEVICE_VERSION(13,0,0)) { +					diagnostics_relay_client_t diag = NULL; +					if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) { +						plist_t dict = NULL; +						plist_t keys = plist_new_array(); +						plist_array_append_item(keys, plist_new_string("PasswordConfigured")); +						if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) { +							plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured"); +							plist_get_bool_val(node, &passcode_hint); +						} +						plist_free(keys); +						plist_free(dict); +						diagnostics_relay_goodbye(diag); +						diagnostics_relay_client_free(diag); +					} +				} +				if (passcode_hint) { +					if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) { +						PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n"); +					} else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) { +						PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n"); +					} else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) { +						PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n"); +					} +				}  				/*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {  					int retr = 10;  					while ((retr-- >= 0) && !backup_domain_changed) { @@ -2128,6 +2278,7 @@ checkpoint:  			int operation_ok = 0;  			plist_t message = NULL; +			mobilebackup2_error_t mberr;  			char *dlmsg = NULL;  			int file_count = 0;  			int errcode = 0; @@ -2138,10 +2289,13 @@ checkpoint:  			do {  				free(dlmsg);  				dlmsg = NULL; -				mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg); -				if (!message || !dlmsg) { -					PRINT_VERBOSE(1, "Device is not ready yet. Going to try again in 2 seconds...\n"); -					sleep(2); +				mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg); +				if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) { +					PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n"); +					goto files_out; +				} else if (mberr != MOBILEBACKUP2_E_SUCCESS) { +					PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr); +					quit_flag++;  					goto files_out;  				} @@ -2157,7 +2311,7 @@ checkpoint:  					/* device wants to know how much disk space is available on the computer */  					uint64_t freespace = 0;  					int res = -1; -#ifdef WIN32 +#ifdef _WIN32  					if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {  						res = 0;  					} @@ -2166,12 +2320,17 @@ checkpoint:  					memset(&fs, '\0', sizeof(fs));  					res = statvfs(backup_directory, &fs);  					if (res == 0) { -						freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize; +						freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;  					}  #endif  					plist_t freespace_item = plist_new_uint(freespace);  					mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);  					plist_free(freespace_item); +				} else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) { +					/* device wants to purge disk space on the host - not supported */ +					plist_t empty_dict = plist_new_dict(); +					err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict); +					plist_free(empty_dict);  				} else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {  					/* list directory contents */  					mb2_handle_list_directory(mobilebackup2, message, backup_directory); @@ -2359,7 +2518,7 @@ checkpoint:  				/* print status */  				if ((overall_progress > 0) && !progress_finished) { -					if (overall_progress >= 100.0f) { +					if (overall_progress >= 100.0F) {  						progress_finished = 1;  					}  					print_progress_real(overall_progress, 0); @@ -2447,12 +2606,18 @@ files_out:  				}  				break;  				case CMD_RESTORE: -				if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0) -					PRINT_VERBOSE(1, "The device should reboot now.\n");  				if (operation_ok) { +					if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0) +						PRINT_VERBOSE(1, "The device should reboot now.\n");  					PRINT_VERBOSE(1, "Restore Successful.\n");  				} else { -					PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code); +					afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist"); +					afc_remove_path(afc, "/iTunesRestore"); +					if (quit_flag) { +						PRINT_VERBOSE(1, "Restore Aborted.\n"); +					} else { +						PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code); +					}  				}  				break;  				case CMD_INFO: @@ -2477,7 +2642,7 @@ files_out:  				do_post_notification(device, NP_SYNC_DID_FINISH);  		}  	} else { -		printf("ERROR: Could not start service %s.\n", MOBILEBACKUP2_SERVICE_NAME); +		printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));  		lockdownd_client_free(lockdown);  		lockdown = NULL;  	}  | 
