1 /*
2  * idevicebackup.c
3  * Command line interface to use the device's backup and restore service
4  *
5  * Copyright (c) 2009-2010 Martin Szulecki All Rights Reserved.
6  * Copyright (c) 2010      Nikias Bassen All Rights Reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #define TOOL_NAME "idevicebackup"
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <signal.h>
34 #ifdef HAVE_OPENSSL
35 #include <openssl/sha.h>
36 #else
37 #include <gcrypt.h>
38 #endif
39 #include <unistd.h>
40 #include <ctype.h>
41 #include <time.h>
42 
43 #include <libimobiledevice/libimobiledevice.h>
44 #include <libimobiledevice/lockdown.h>
45 #include <libimobiledevice/mobilebackup.h>
46 #include <libimobiledevice/notification_proxy.h>
47 #include <libimobiledevice/afc.h>
48 #include "common/utils.h"
49 
50 #define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
51 #define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
52 
53 #define LOCK_ATTEMPTS 50
54 #define LOCK_WAIT 200000
55 
56 #ifdef WIN32
57 #include <windows.h>
58 #define sleep(x) Sleep(x*1000)
59 #endif
60 
61 static mobilebackup_client_t mobilebackup = NULL;
62 static lockdownd_client_t client = NULL;
63 static idevice_t device = NULL;
64 
65 static int quit_flag = 0;
66 
67 enum cmd_mode {
68 	CMD_BACKUP,
69 	CMD_RESTORE,
70 	CMD_LEAVE
71 };
72 
73 enum device_link_file_status_t {
74 	DEVICE_LINK_FILE_STATUS_NONE = 0,
75 	DEVICE_LINK_FILE_STATUS_HUNK,
76 	DEVICE_LINK_FILE_STATUS_LAST_HUNK
77 };
78 
sha1_of_data(const char * input,uint32_t size,unsigned char * hash_out)79 static void sha1_of_data(const char *input, uint32_t size, unsigned char *hash_out)
80 {
81 #ifdef HAVE_OPENSSL
82 	SHA1((const unsigned char*)input, size, hash_out);
83 #else
84 	gcry_md_hash_buffer(GCRY_MD_SHA1, hash_out, input, size);
85 #endif
86 }
87 
compare_hash(const unsigned char * hash1,const unsigned char * hash2,int hash_len)88 static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len)
89 {
90 	int i;
91 	for (i = 0; i < hash_len; i++) {
92 		if (hash1[i] != hash2[i]) {
93 			return 0;
94 		}
95 	}
96 	return 1;
97 }
98 
compute_datahash(const char * path,const char * destpath,uint8_t greylist,const char * domain,const char * appid,const char * version,unsigned char * hash_out)99 static void compute_datahash(const char *path, const char *destpath, uint8_t greylist, const char *domain, const char *appid, const char *version, unsigned char *hash_out)
100 {
101 #ifdef HAVE_OPENSSL
102 	SHA_CTX sha1;
103 	SHA1_Init(&sha1);
104 #else
105 	gcry_md_hd_t hd = NULL;
106 	gcry_md_open(&hd, GCRY_MD_SHA1, 0);
107 	if (!hd) {
108 		printf("ERROR: Could not initialize libgcrypt/SHA1\n");
109 		return;
110 	}
111 	gcry_md_reset(hd);
112 #endif
113 	FILE *f = fopen(path, "rb");
114 	if (f) {
115 		unsigned char buf[16384];
116 		size_t len;
117 		while ((len = fread(buf, 1, 16384, f)) > 0) {
118 #ifdef HAVE_OPENSSL
119 			SHA1_Update(&sha1, buf, len);
120 #else
121 			gcry_md_write(hd, buf, len);
122 #endif
123 		}
124 		fclose(f);
125 #ifdef HAVE_OPENSSL
126 		SHA1_Update(&sha1, destpath, strlen(destpath));
127 		SHA1_Update(&sha1, ";", 1);
128 #else
129 		gcry_md_write(hd, destpath, strlen(destpath));
130 		gcry_md_write(hd, ";", 1);
131 #endif
132 		if (greylist == 1) {
133 #ifdef HAVE_OPENSSL
134 			SHA1_Update(&sha1, "true", 4);
135 #else
136 			gcry_md_write(hd, "true", 4);
137 #endif
138 		} else {
139 #ifdef HAVE_OPENSSL
140 			SHA1_Update(&sha1, "false", 5);
141 #else
142 			gcry_md_write(hd, "false", 5);
143 #endif
144 		}
145 #ifdef HAVE_OPENSSL
146 		SHA1_Update(&sha1, ";", 1);
147 #else
148 		gcry_md_write(hd, ";", 1);
149 #endif
150 		if (domain) {
151 #ifdef HAVE_OPENSSL
152 			SHA1_Update(&sha1, domain, strlen(domain));
153 #else
154 			gcry_md_write(hd, domain, strlen(domain));
155 #endif
156 		} else {
157 #ifdef HAVE_OPENSSL
158 			SHA1_Update(&sha1, "(null)", 6);
159 #else
160 			gcry_md_write(hd, "(null)", 6);
161 #endif
162 		}
163 #ifdef HAVE_OPENSSL
164 		SHA1_Update(&sha1, ";", 1);
165 #else
166 		gcry_md_write(hd, ";", 1);
167 #endif
168 		if (appid) {
169 #ifdef HAVE_OPENSSL
170 			SHA1_Update(&sha1, appid, strlen(appid));
171 #else
172 			gcry_md_write(hd, appid, strlen(appid));
173 #endif
174 		} else {
175 #ifdef HAVE_OPENSSL
176 			SHA1_Update(&sha1, "(null)", 6);
177 #else
178 			gcry_md_write(hd, "(null)", 6);
179 #endif
180 		}
181 #ifdef HAVE_OPENSSL
182 		SHA1_Update(&sha1, ";", 1);
183 #else
184 		gcry_md_write(hd, ";", 1);
185 #endif
186 		if (version) {
187 #ifdef HAVE_OPENSSL
188 			SHA1_Update(&sha1, version, strlen(version));
189 #else
190 			gcry_md_write(hd, version, strlen(version));
191 #endif
192 		} else {
193 #ifdef HAVE_OPENSSL
194 			SHA1_Update(&sha1, "(null)", 6);
195 #else
196 			gcry_md_write(hd, "(null)", 6);
197 #endif
198 		}
199 #ifdef HAVE_OPENSSL
200 		SHA1_Final(hash_out, &sha1);
201 #else
202 		unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1);
203 		memcpy(hash_out, newhash, 20);
204 #endif
205 	}
206 #ifndef HAVE_OPENSSL
207 	gcry_md_close(hd);
208 #endif
209 }
210 
print_hash(const unsigned char * hash,int len)211 static void print_hash(const unsigned char *hash, int len)
212 {
213 	int i;
214 	for (i = 0; i < len; i++) {
215 		printf("%02x", hash[i]);
216 	}
217 }
218 
notify_cb(const char * notification,void * userdata)219 static void notify_cb(const char *notification, void *userdata)
220 {
221 	if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
222 		printf("User has aborted on-device\n");
223 		quit_flag++;
224 	} else {
225 		printf("unhandled notification '%s' (TODO: implement)\n", notification);
226 	}
227 }
228 
mobilebackup_factory_info_plist_new(const char * udid)229 static plist_t mobilebackup_factory_info_plist_new(const char* udid)
230 {
231 	/* gather data from lockdown */
232 	plist_t value_node = NULL;
233 	plist_t root_node = NULL;
234 	char *udid_uppercase = NULL;
235 
236 	plist_t ret = plist_new_dict();
237 
238 	/* get basic device information in one go */
239 	lockdownd_get_value(client, NULL, NULL, &root_node);
240 
241 	/* set fields we understand */
242 	value_node = plist_dict_get_item(root_node, "BuildVersion");
243 	plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
244 
245 	value_node = plist_dict_get_item(root_node, "DeviceName");
246 	plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
247 	plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
248 
249 	/* FIXME: How is the GUID generated? */
250 	plist_dict_set_item(ret, "GUID", plist_new_string("---"));
251 
252 	value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
253 	if (value_node)
254 		plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
255 
256 	plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
257 
258 	value_node = plist_dict_get_item(root_node, "ProductType");
259 	plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
260 
261 	value_node = plist_dict_get_item(root_node, "ProductVersion");
262 	plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
263 
264 	value_node = plist_dict_get_item(root_node, "SerialNumber");
265 	plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
266 
267 	value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
268 	plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
269 
270 	/* uppercase */
271 	udid_uppercase = string_toupper((char*)udid);
272 	plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
273 	free(udid_uppercase);
274 
275 	/* FIXME: Embed files as <data> nodes */
276 	plist_t files = plist_new_dict();
277 	plist_dict_set_item(ret, "iTunes Files", files);
278 	plist_dict_set_item(ret, "iTunes Version", plist_new_string("9.0.2"));
279 
280 	plist_free(root_node);
281 
282 	return ret;
283 }
284 
mobilebackup_info_update_last_backup_date(plist_t info_plist)285 static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
286 {
287 	plist_t node = NULL;
288 
289 	if (!info_plist)
290 		return;
291 
292 	node = plist_dict_get_item(info_plist, "Last Backup Date");
293 	plist_set_date_val(node, time(NULL) - MAC_EPOCH, 0);
294 
295 	node = NULL;
296 }
297 
plist_strcmp(plist_t node,const char * str)298 static int plist_strcmp(plist_t node, const char *str)
299 {
300 	char *buffer = NULL;
301 	int ret = 0;
302 
303 	if (plist_get_node_type(node) != PLIST_STRING)
304 		return ret;
305 
306 	plist_get_string_val(node, &buffer);
307 	ret = strcmp(buffer, str);
308 	free(buffer);
309 
310 	return ret;
311 }
312 
mobilebackup_build_path(const char * backup_directory,const char * name,const char * extension)313 static char *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension)
314 {
315 	char* filename = (char*)malloc(strlen(name)+(extension == NULL ? 0: strlen(extension))+1);
316 	strcpy(filename, name);
317 	if (extension != NULL)
318 		strcat(filename, extension);
319 	char *path = string_build_path(backup_directory, filename, NULL);
320 	free(filename);
321 	return path;
322 }
323 
mobilebackup_write_status(const char * path,int status)324 static void mobilebackup_write_status(const char *path, int status)
325 {
326 	struct stat st;
327 	plist_t status_plist = plist_new_dict();
328 	plist_dict_set_item(status_plist, "Backup Success", plist_new_bool(status));
329 	char *file_path = mobilebackup_build_path(path, "Status", ".plist");
330 
331 	if (stat(file_path, &st) == 0)
332 		remove(file_path);
333 
334 	plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML);
335 
336 	plist_free(status_plist);
337 	status_plist = NULL;
338 
339 	free(file_path);
340 }
341 
mobilebackup_read_status(const char * path)342 static int mobilebackup_read_status(const char *path)
343 {
344 	int ret = -1;
345 	plist_t status_plist = NULL;
346 	char *file_path = mobilebackup_build_path(path, "Status", ".plist");
347 
348 	plist_read_from_filename(&status_plist, file_path);
349 	free(file_path);
350 	if (!status_plist) {
351 		printf("Could not read Status.plist!\n");
352 		return ret;
353 	}
354 	plist_t node = plist_dict_get_item(status_plist, "Backup Success");
355 	if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
356 		uint8_t bval = 0;
357 		plist_get_bool_val(node, &bval);
358 		ret = bval;
359 	} else {
360 		printf("%s: ERROR could not get Backup Success key from Status.plist!\n", __func__);
361 	}
362 	plist_free(status_plist);
363 	return ret;
364 }
365 
mobilebackup_info_is_current_device(plist_t info)366 static int mobilebackup_info_is_current_device(plist_t info)
367 {
368 	plist_t value_node = NULL;
369 	plist_t node = NULL;
370 	plist_t root_node = NULL;
371 	int ret = 0;
372 
373 	if (!info)
374 		return ret;
375 
376 	if (plist_get_node_type(info) != PLIST_DICT)
377 		return ret;
378 
379 	/* get basic device information in one go */
380 	lockdownd_get_value(client, NULL, NULL, &root_node);
381 
382 	/* verify UDID */
383 	value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
384 	node = plist_dict_get_item(info, "Target Identifier");
385 
386 	if(plist_compare_node_value(value_node, node))
387 		ret = 1;
388 	else {
389 		printf("Info.plist: UniqueDeviceID does not match.\n");
390 	}
391 
392 	/* verify SerialNumber */
393 	if (ret == 1) {
394 		value_node = plist_dict_get_item(root_node, "SerialNumber");
395 		node = plist_dict_get_item(info, "Serial Number");
396 
397 		if(plist_compare_node_value(value_node, node))
398 			ret = 1;
399 		else {
400 			printf("Info.plist: SerialNumber does not match.\n");
401 			ret = 0;
402 		}
403 	}
404 
405 	/* verify ProductVersion to prevent using backup with different OS version */
406 	if (ret == 1) {
407 		value_node = plist_dict_get_item(root_node, "ProductVersion");
408 		node = plist_dict_get_item(info, "Product Version");
409 
410 		if(plist_compare_node_value(value_node, node))
411 			ret = 1;
412 		else {
413 			printf("Info.plist: ProductVersion does not match.\n");
414 			ret = 0;
415 		}
416 	}
417 
418 	plist_free(root_node);
419 	root_node = NULL;
420 
421 	value_node = NULL;
422 	node = NULL;
423 
424 	return ret;
425 }
426 
mobilebackup_delete_backup_file_by_hash(const char * backup_directory,const char * hash)427 static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash)
428 {
429 	int ret = 0;
430 	char *path = mobilebackup_build_path(backup_directory, hash, ".mddata");
431 	printf("Removing \"%s\" ", path);
432 	if (!remove( path ))
433 		ret = 1;
434 	else
435 		ret = 0;
436 
437 	free(path);
438 
439 	if (!ret)
440 		return ret;
441 
442 	path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
443 	printf("and \"%s\"... ", path);
444 	if (!remove( path ))
445 		ret = 1;
446 	else
447 		ret = 0;
448 
449 	free(path);
450 
451 	return ret;
452 }
453 
mobilebackup_check_file_integrity(const char * backup_directory,const char * hash,plist_t filedata)454 static int mobilebackup_check_file_integrity(const char *backup_directory, const char *hash, plist_t filedata)
455 {
456 	char *datapath;
457 	char *infopath;
458 	plist_t mdinfo = NULL;
459 	struct stat st;
460 	unsigned char file_hash[20];
461 
462 	datapath = mobilebackup_build_path(backup_directory, hash, ".mddata");
463 	if (stat(datapath, &st) != 0) {
464 		printf("\r\n");
465 		printf("ERROR: '%s.mddata' is missing!\n", hash);
466 		free(datapath);
467 		return 0;
468 	}
469 
470 	infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
471 	plist_read_from_filename(&mdinfo, infopath);
472 	free(infopath);
473 	if (!mdinfo) {
474 		printf("\r\n");
475 		printf("ERROR: '%s.mdinfo' is missing or corrupted!\n", hash);
476 		free(datapath);
477 		return 0;
478 	}
479 
480 	/* sha1 hash verification */
481 	plist_t node = plist_dict_get_item(filedata, "DataHash");
482 	if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
483 		printf("\r\n");
484 		printf("ERROR: Could not get DataHash for file entry '%s'\n", hash);
485 		plist_free(mdinfo);
486 		free(datapath);
487 		return 0;
488 	}
489 
490 	node = plist_dict_get_item(mdinfo, "Metadata");
491 	if (!node && (plist_get_node_type(node) != PLIST_DATA)) {
492 		printf("\r\n");
493 		printf("ERROR: Could not find Metadata in plist '%s.mdinfo'\n", hash);
494 		plist_free(mdinfo);
495 		free(datapath);
496 		return 0;
497 	}
498 
499 	char *meta_bin = NULL;
500 	uint64_t meta_bin_size = 0;
501 	plist_get_data_val(node, &meta_bin, &meta_bin_size);
502 	plist_t metadata = NULL;
503 	if (meta_bin) {
504 		plist_from_bin(meta_bin, (uint32_t)meta_bin_size, &metadata);
505 	}
506 	if (!metadata) {
507 		printf("\r\n");
508 		printf("ERROR: Could not get Metadata from plist '%s.mdinfo'\n", hash);
509 		plist_free(mdinfo);
510 		free(datapath);
511 		return 0;
512 	}
513 
514 	char *version = NULL;
515 	node = plist_dict_get_item(metadata, "Version");
516 	if (node && (plist_get_node_type(node) == PLIST_STRING)) {
517 		plist_get_string_val(node, &version);
518 	}
519 
520 	char *destpath = NULL;
521 	node = plist_dict_get_item(metadata, "Path");
522 	if (node && (plist_get_node_type(node) == PLIST_STRING)) {
523 		plist_get_string_val(node, &destpath);
524 	}
525 
526 	uint8_t greylist = 0;
527 	node = plist_dict_get_item(metadata, "Greylist");
528 	if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
529 		plist_get_bool_val(node, &greylist);
530 	}
531 
532 	char *domain = NULL;
533 	node = plist_dict_get_item(metadata, "Domain");
534 	if (node && (plist_get_node_type(node) == PLIST_STRING)) {
535 		plist_get_string_val(node, &domain);
536 	}
537 
538 	char *fnstr = malloc(strlen(domain) + 1 + strlen(destpath) + 1);
539 	strcpy(fnstr, domain);
540 	strcat(fnstr, "-");
541 	strcat(fnstr, destpath);
542 	unsigned char fnhash[20];
543 	char fnamehash[41];
544 	char *p = fnamehash;
545 	sha1_of_data(fnstr, strlen(fnstr), fnhash);
546 	free(fnstr);
547 	int i;
548 	for ( i = 0; i < 20; i++, p += 2 ) {
549 		snprintf (p, 3, "%02x", (unsigned char)fnhash[i] );
550 	}
551 	if (strcmp(fnamehash, hash)) {
552 		printf("\r\n");
553 		printf("WARNING: filename hash does not match for entry '%s'\n", hash);
554 	}
555 
556 	char *auth_version = NULL;
557 	node = plist_dict_get_item(mdinfo, "AuthVersion");
558 	if (node && (plist_get_node_type(node) == PLIST_STRING)) {
559 		plist_get_string_val(node, &auth_version);
560 	}
561 
562 	if (strcmp(auth_version, "1.0")) {
563 		printf("\r\n");
564 		printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version);
565 	}
566 
567 	node = plist_dict_get_item(filedata, "DataHash");
568 	if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
569 		printf("\r\n");
570 		printf("WARNING: Could not get DataHash key from file info data for entry '%s'\n", hash);
571 	}
572 
573 	int res = 1;
574 	unsigned char *data_hash = NULL;
575 	uint64_t data_hash_len = 0;
576 	plist_get_data_val(node, (char**)&data_hash, &data_hash_len);
577 	int hash_ok = 0;
578 	if (data_hash && (data_hash_len == 20)) {
579 		compute_datahash(datapath, destpath, greylist, domain, NULL, version, file_hash);
580 		hash_ok = compare_hash(data_hash, file_hash, 20);
581 	} else if (data_hash_len == 0) {
582 		/* no datahash present */
583 		hash_ok = 1;
584 	}
585 
586 	free(domain);
587 	free(version);
588 	free(destpath);
589 
590 	if (!hash_ok) {
591 		printf("\r\n");
592 		printf("ERROR: The hash for '%s.mddata' does not match DataHash entry in Manifest\n", hash);
593 		printf("datahash: ");
594 		print_hash(data_hash, 20);
595 		printf("\nfilehash: ");
596 		print_hash(file_hash, 20);
597 		printf("\n");
598 		res = 0;
599 	}
600 	free(data_hash);
601 	plist_free(mdinfo);
602 	return res;
603 }
604 
do_post_notification(const char * notification)605 static void do_post_notification(const char *notification)
606 {
607 	lockdownd_service_descriptor_t service = NULL;
608 	np_client_t np;
609 
610 	if (!client) {
611 		if (lockdownd_client_new_with_handshake(device, &client, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
612 			return;
613 		}
614 	}
615 
616 	lockdownd_start_service(client, NP_SERVICE_NAME, &service);
617 	if (service && service->port) {
618 		np_client_new(device, service, &np);
619 		if (np) {
620 			np_post_notification(np, notification);
621 			np_client_free(np);
622 		}
623 	} else {
624 		printf("Could not start %s\n", NP_SERVICE_NAME);
625 	}
626 
627 	if (service) {
628 		lockdownd_service_descriptor_free(service);
629 		service = NULL;
630 	}
631 }
632 
print_progress(double progress)633 static void print_progress(double progress)
634 {
635 	int i = 0;
636 	if (progress < 0)
637 		return;
638 
639 	if (progress > 100)
640 		progress = 100;
641 
642 	printf("\r[");
643 	for(i = 0; i < 50; i++) {
644 		if(i < progress / 2) {
645 			printf("=");
646 		} else {
647 			printf(" ");
648 		}
649 	}
650 	printf("] %3.0f%%", progress);
651 	fflush(stdout);
652 	if (progress == 100)
653 		printf("\n");
654 }
655 
656 /**
657  * signal handler function for cleaning up properly
658  */
clean_exit(int sig)659 static void clean_exit(int sig)
660 {
661 	fprintf(stderr, "Exiting...\n");
662 	quit_flag++;
663 }
664 
print_usage(int argc,char ** argv)665 static void print_usage(int argc, char **argv)
666 {
667 	char *name = NULL;
668 	name = strrchr(argv[0], '/');
669 	printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0]));
670 	printf("\n");
671 	printf("Create or restore backup from the current or specified directory.\n");
672 	printf("\n");
673 	printf("CMD:\n");
674 	printf("  backup\tSaves a device backup into DIRECTORY\n");
675 	printf("  restore\tRestores a device backup from DIRECTORY.\n");
676 	printf("\n");
677 	printf("OPTIONS:\n");
678 	printf("  -u, --udid UDID\ttarget specific device by UDID\n");
679 	printf("  -n, --network\t\tconnect to network device\n");
680 	printf("  -d, --debug\t\tenable communication debugging\n");
681 	printf("  -h, --help\t\tprints usage information\n");
682 	printf("  -v, --version\t\tprints version information\n");
683 	printf("\n");
684 	printf("Homepage:    <" PACKAGE_URL ">\n");
685 	printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
686 }
687 
main(int argc,char * argv[])688 int main(int argc, char *argv[])
689 {
690 	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
691 	lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
692 	int i;
693 	char* udid = NULL;
694 	int use_network = 0;
695 	lockdownd_service_descriptor_t service = NULL;
696 	int cmd = -1;
697 	int is_full_backup = 0;
698 	char *backup_directory = NULL;
699 	struct stat st;
700 	plist_t node = NULL;
701 	plist_t node_tmp = NULL;
702 	plist_t manifest_plist = NULL;
703 	plist_t info_plist = NULL;
704 	char *buffer = NULL;
705 	char *file_path = NULL;
706 	uint64_t length = 0;
707 	uint64_t backup_total_size = 0;
708 	enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE;
709 	uint64_t c = 0;
710 
711 	/* we need to exit cleanly on running backups and restores or we cause havok */
712 	signal(SIGINT, clean_exit);
713 	signal(SIGTERM, clean_exit);
714 #ifndef WIN32
715 	signal(SIGQUIT, clean_exit);
716 	signal(SIGPIPE, SIG_IGN);
717 #endif
718 
719 	/* parse cmdline args */
720 	for (i = 1; i < argc; i++) {
721 		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
722 			idevice_set_debug_level(1);
723 			continue;
724 		}
725 		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
726 			i++;
727 			if (!argv[i] || !*argv[i]) {
728 				print_usage(argc, argv);
729 				return 0;
730 			}
731 			udid = strdup(argv[i]);
732 			continue;
733 		}
734 		else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--network")) {
735 			use_network = 1;
736 			continue;
737 		}
738 		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
739 			print_usage(argc, argv);
740 			return 0;
741 		}
742 		else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
743 			printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
744 			return 0;
745 		}
746 		else if (!strcmp(argv[i], "backup")) {
747 			cmd = CMD_BACKUP;
748 		}
749 		else if (!strcmp(argv[i], "restore")) {
750 			cmd = CMD_RESTORE;
751 		}
752 		else if (backup_directory == NULL) {
753 			backup_directory = argv[i];
754 		}
755 		else {
756 			print_usage(argc, argv);
757 			return 0;
758 		}
759 	}
760 
761 	/* verify options */
762 	if (cmd == -1) {
763 		printf("No command specified.\n");
764 		print_usage(argc, argv);
765 		return -1;
766 	}
767 
768 	if (backup_directory == NULL) {
769 		printf("No target backup directory specified.\n");
770 		print_usage(argc, argv);
771 		return -1;
772 	}
773 
774 	/* verify if passed backup directory exists */
775 	if (stat(backup_directory, &st) != 0) {
776 		printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
777 		return -1;
778 	}
779 
780 	/* restore directory must contain an Info.plist */
781 	char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist");
782 	if (cmd == CMD_RESTORE) {
783 		if (stat(info_path, &st) != 0) {
784 			free(info_path);
785 			printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
786 			return -1;
787 		}
788 	}
789 
790 	printf("Backup directory is \"%s\"\n", backup_directory);
791 
792 	ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
793 	if (ret != IDEVICE_E_SUCCESS) {
794 		if (udid) {
795 			printf("No device found with udid %s.\n", udid);
796 		} else {
797 			printf("No device found.\n");
798 		}
799 		return -1;
800 	}
801 
802 	if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
803 		printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
804 		idevice_free(device);
805 		return -1;
806 	}
807 
808 	node = NULL;
809 	lockdownd_get_value(client, NULL, "ProductVersion", &node);
810 	if (node) {
811 		char* str = NULL;
812 		if (plist_get_node_type(node) == PLIST_STRING) {
813 			plist_get_string_val(node, &str);
814 		}
815 		plist_free(node);
816 		node = NULL;
817 		if (str) {
818 			int maj = strtol(str, NULL, 10);
819 			free(str);
820 			if (maj > 3) {
821 				printf("ERROR: This tool is only compatible with iOS 3 or below. For newer iOS versions please use the idevicebackup2 tool.\n");
822 				lockdownd_client_free(client);
823 				idevice_free(device);
824 				return -1;
825 			}
826 		}
827 	}
828 
829 	/* start notification_proxy */
830 	np_client_t np = NULL;
831 	ldret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
832 	if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
833 		np_client_new(device, service, &np);
834 		np_set_notify_callback(np, notify_cb, NULL);
835 		const char *noties[5] = {
836 			NP_SYNC_CANCEL_REQUEST,
837 			NP_SYNC_SUSPEND_REQUEST,
838 			NP_SYNC_RESUME_REQUEST,
839 			NP_BACKUP_DOMAIN_CHANGED,
840 			NULL
841 		};
842 		np_observe_notifications(np, noties);
843 	} else {
844 		printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
845 	}
846 
847 	afc_client_t afc = NULL;
848 	if (cmd == CMD_BACKUP) {
849 		/* start AFC, we need this for the lock file */
850 		service->port = 0;
851 		service->ssl_enabled = 0;
852 		ldret = lockdownd_start_service(client, "com.apple.afc", &service);
853 		if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
854 			afc_client_new(device, service, &afc);
855 		}
856 	}
857 
858 	if (service) {
859 		lockdownd_service_descriptor_free(service);
860 		service = NULL;
861 	}
862 
863 	/* start mobilebackup service and retrieve port */
864 	ldret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &service);
865 	if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
866 		printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, service->port);
867 		mobilebackup_client_new(device, service, &mobilebackup);
868 
869 		if (service) {
870 			lockdownd_service_descriptor_free(service);
871 			service = NULL;
872 		}
873 
874 		/* check abort conditions */
875 		if (quit_flag > 0) {
876 			printf("Aborting backup. Cancelled by user.\n");
877 			cmd = CMD_LEAVE;
878 		}
879 
880 		/* verify existing Info.plist */
881 		if (stat(info_path, &st) == 0) {
882 			printf("Reading Info.plist from backup.\n");
883 			plist_read_from_filename(&info_plist, info_path);
884 
885 			if (!info_plist) {
886 				printf("Could not read Info.plist\n");
887 				is_full_backup = 1;
888 			}
889 			if (info_plist && (cmd == CMD_BACKUP)) {
890 				if (mobilebackup_info_is_current_device(info_plist)) {
891 					/* update the last backup time within Info.plist */
892 					mobilebackup_info_update_last_backup_date(info_plist);
893 					remove(info_path);
894 					plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
895 				} else {
896 					printf("Aborting backup. Backup is not compatible with the current device.\n");
897 					cmd = CMD_LEAVE;
898 				}
899 			} else if (info_plist && (cmd == CMD_RESTORE)) {
900 				if (!mobilebackup_info_is_current_device(info_plist)) {
901 					printf("Aborting restore. Backup data is not compatible with the current device.\n");
902 					cmd = CMD_LEAVE;
903 				}
904 			}
905 		} else {
906 			if (cmd == CMD_RESTORE) {
907 				printf("Aborting restore. Info.plist is missing.\n");
908 				cmd = CMD_LEAVE;
909 			} else {
910 				is_full_backup = 1;
911 			}
912 		}
913 
914 		uint64_t lockfile = 0;
915 		if (cmd == CMD_BACKUP) {
916 			do_post_notification(NP_SYNC_WILL_START);
917 			afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
918 		}
919 		if (lockfile) {
920 			afc_error_t aerr;
921 			do_post_notification(NP_SYNC_LOCK_REQUEST);
922 			for (i = 0; i < LOCK_ATTEMPTS; i++) {
923 				aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
924 				if (aerr == AFC_E_SUCCESS) {
925 					do_post_notification(NP_SYNC_DID_START);
926 					break;
927 				} else if (aerr == AFC_E_OP_WOULD_BLOCK) {
928 					usleep(LOCK_WAIT);
929 					continue;
930 				} else {
931 					fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
932 					afc_file_close(afc, lockfile);
933 					lockfile = 0;
934 					cmd = CMD_LEAVE;
935 				}
936 			}
937 			if (i == LOCK_ATTEMPTS) {
938 				fprintf(stderr, "ERROR: timeout while locking for sync\n");
939 				afc_file_close(afc, lockfile);
940 				lockfile = 0;
941 				cmd = CMD_LEAVE;
942 			}
943 		}
944 
945 		mobilebackup_error_t err;
946 
947 		/* Manifest.plist (backup manifest (backup state)) */
948 		char *manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist");
949 
950 		switch(cmd) {
951 			case CMD_BACKUP:
952 			printf("Starting backup...\n");
953 			/* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
954 			/* TODO: verify battery on AC enough battery remaining */
955 
956 			/* read the last Manifest.plist */
957 			if (!is_full_backup) {
958 				printf("Reading existing Manifest.\n");
959 				plist_read_from_filename(&manifest_plist, manifest_path);
960 				if (!manifest_plist) {
961 					printf("Could not read Manifest.plist, switching to full backup mode.\n");
962 					is_full_backup = 1;
963 				}
964 			}
965 
966 			/* Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
967 
968 			/* create new Info.plist on new backups */
969 			if (is_full_backup) {
970 				if (info_plist) {
971 					plist_free(info_plist);
972 					info_plist = NULL;
973 				}
974 				remove(info_path);
975 				printf("Creating Info.plist for new backup.\n");
976 				info_plist = mobilebackup_factory_info_plist_new(udid);
977 				plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
978 			}
979 			free(info_path);
980 
981 			plist_free(info_plist);
982 			info_plist = NULL;
983 
984 			/* close down the lockdown connection as it is no longer needed */
985 			if (client) {
986 				lockdownd_client_free(client);
987 				client = NULL;
988 			}
989 
990 			/* create Status.plist with failed status for now */
991 			mobilebackup_write_status(backup_directory, 0);
992 
993 			/* request backup from device with manifest from last backup */
994 			printf("Requesting backup from device...\n");
995 
996 			err = mobilebackup_request_backup(mobilebackup, manifest_plist, "/", "1.6");
997 			if (err == MOBILEBACKUP_E_SUCCESS) {
998 				if (is_full_backup)
999 					printf("Full backup mode.\n");
1000 				else
1001 					printf("Incremental backup mode.\n");
1002 				printf("Please wait. Device is preparing backup data...\n");
1003 			} else {
1004 				if (err == MOBILEBACKUP_E_BAD_VERSION) {
1005 					printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
1006 				} else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
1007 					printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
1008 				} else {
1009 					printf("ERROR: Could not start backup process: unspecified error occurred\n");
1010 				}
1011 				break;
1012 			}
1013 
1014 			/* reset backup status */
1015 			int backup_ok = 0;
1016 			plist_t message = NULL;
1017 
1018 			/* receive and save DLSendFile files and metadata, ACK each */
1019 			uint64_t file_size = 0;
1020 			uint64_t file_size_current = 0;
1021 			int file_index = 0;
1022 			int hunk_index = 0;
1023 			uint64_t backup_real_size = 0;
1024 			char *file_ext = NULL;
1025 			char *filename_mdinfo = NULL;
1026 			char *filename_mddata = NULL;
1027 			char *filename_source = NULL;
1028 			char *format_size = NULL;
1029 			int is_manifest = 0;
1030 			uint8_t b = 0;
1031 
1032 			/* process series of DLSendFile messages */
1033 			do {
1034 				mobilebackup_receive(mobilebackup, &message);
1035 				if (!message) {
1036 					printf("Device is not ready yet. Going to try again in 2 seconds...\n");
1037 					sleep(2);
1038 					goto files_out;
1039 				}
1040 
1041 				node = plist_array_get_item(message, 0);
1042 
1043 				/* get out if we don't get a DLSendFile */
1044 				if (plist_strcmp(node, "DLSendFile"))
1045 					break;
1046 
1047 				node_tmp = plist_array_get_item(message, 2);
1048 
1049 				/* first message hunk contains total backup size */
1050 				if ((hunk_index == 0) && (file_index == 0)) {
1051 					node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey");
1052 					if (node) {
1053 						plist_get_uint_val(node, &backup_total_size);
1054 						format_size = string_format_size(backup_total_size);
1055 						printf("Backup data requires %s on the disk.\n", format_size);
1056 						free(format_size);
1057 					}
1058 				}
1059 
1060 				/* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */
1061 				node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
1062 				plist_get_uint_val(node, &c);
1063 				file_status = c;
1064 
1065 				/* get source filename */
1066 				node = plist_dict_get_item(node_tmp, "BackupManifestKey");
1067 				b = 0;
1068 				if (node) {
1069 					plist_get_bool_val(node, &b);
1070 				}
1071 				is_manifest = (b == 1) ? 1 : 0;
1072 
1073 				if ((hunk_index == 0) && (!is_manifest)) {
1074 					/* get source filename */
1075 					node = plist_dict_get_item(node_tmp, "DLFileSource");
1076 					plist_get_string_val(node, &filename_source);
1077 
1078 					/* increase received size */
1079 					node = plist_dict_get_item(node_tmp, "DLFileAttributesKey");
1080 					node = plist_dict_get_item(node, "FileSize");
1081 					plist_get_uint_val(node, &file_size);
1082 					backup_real_size += file_size;
1083 
1084 					format_size = string_format_size(backup_real_size);
1085 					printf("(%s", format_size);
1086 					free(format_size);
1087 
1088 					format_size = string_format_size(backup_total_size);
1089 					printf("/%s): ", format_size);
1090 					free(format_size);
1091 
1092 					format_size = string_format_size(file_size);
1093 					printf("Receiving file %s (%s)... \n", filename_source, format_size);
1094 					free(format_size);
1095 
1096 					if (filename_source)
1097 						free(filename_source);
1098 				}
1099 
1100 				/* check if we completed a file */
1101 				if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (!is_manifest)) {
1102 					/* save <hash>.mdinfo */
1103 					node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1104 					if (node) {
1105 						node = plist_dict_get_item(node_tmp, "DLFileDest");
1106 						plist_get_string_val(node, &file_path);
1107 
1108 						filename_mdinfo = mobilebackup_build_path(backup_directory, file_path, ".mdinfo");
1109 
1110 						/* remove any existing file */
1111 						if (stat(filename_mdinfo, &st) == 0)
1112 							remove(filename_mdinfo);
1113 
1114 						node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1115 						plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY);
1116 
1117 						free(filename_mdinfo);
1118 					}
1119 
1120 					file_index++;
1121 				}
1122 
1123 				/* save <hash>.mddata */
1124 				node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1125 				if (node_tmp) {
1126 					node = plist_dict_get_item(node_tmp, "DLFileDest");
1127 					plist_get_string_val(node, &file_path);
1128 
1129 					filename_mddata = mobilebackup_build_path(backup_directory, file_path, is_manifest ? NULL: ".mddata");
1130 
1131 					/* if this is the first hunk, remove any existing file */
1132 					if ((hunk_index == 0) && (stat(filename_mddata, &st) == 0))
1133 						remove(filename_mddata);
1134 
1135 					/* get file data hunk */
1136 					node_tmp = plist_array_get_item(message, 1);
1137 					plist_get_data_val(node_tmp, &buffer, &length);
1138 
1139 					buffer_write_to_filename(filename_mddata, buffer, length);
1140 					if (!is_manifest)
1141 						file_size_current += length;
1142 
1143 					/* activate currently sent manifest */
1144 					if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (is_manifest)) {
1145 						rename(filename_mddata, manifest_path);
1146 					}
1147 
1148 					free(buffer);
1149 					buffer = NULL;
1150 
1151 					free(filename_mddata);
1152 				}
1153 
1154 				if ((!is_manifest)) {
1155 					if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1156 							print_progress(100);
1157 					} else {
1158 						if (file_size > 0)
1159 							print_progress((double)((file_size_current*100)/file_size));
1160 					}
1161 				}
1162 
1163 				hunk_index++;
1164 
1165 				if (file_ext)
1166 					free(file_ext);
1167 
1168 				if (message)
1169 					plist_free(message);
1170 				message = NULL;
1171 
1172 				if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1173 					/* acknowlegdge that we received the file */
1174 					mobilebackup_send_backup_file_received(mobilebackup);
1175 					/* reset hunk_index */
1176 					hunk_index = 0;
1177 					if (!is_manifest) {
1178 						file_size_current = 0;
1179 						file_size = 0;
1180 					}
1181 				}
1182 files_out:
1183 				if (quit_flag > 0) {
1184 					/* need to cancel the backup here */
1185 					mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
1186 
1187 					/* remove any atomic Manifest.plist.tmp */
1188 					if (manifest_path)
1189 						free(manifest_path);
1190 
1191 					manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
1192 					if (stat(manifest_path, &st) == 0)
1193 						remove(manifest_path);
1194 					break;
1195 				}
1196 			} while (1);
1197 
1198 			printf("Received %d files from device.\n", file_index);
1199 
1200 			if (!quit_flag && !plist_strcmp(node, "DLMessageProcessMessage")) {
1201 				node_tmp = plist_array_get_item(message, 1);
1202 				node = plist_dict_get_item(node_tmp, "BackupMessageTypeKey");
1203 				/* check if we received the final "backup finished" message */
1204 				if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) {
1205 					/* backup finished */
1206 
1207 					/* process BackupFilesToDeleteKey */
1208 					node = plist_dict_get_item(node_tmp, "BackupFilesToDeleteKey");
1209 					if (node) {
1210 						length = plist_array_get_size(node);
1211 						i = 0;
1212 						while ((node_tmp = plist_array_get_item(node, i++)) != NULL) {
1213 							plist_get_string_val(node_tmp, &file_path);
1214 
1215 							if (mobilebackup_delete_backup_file_by_hash(backup_directory, file_path)) {
1216 								printf("DONE\n");
1217 							} else
1218 								printf("FAILED\n");
1219 						}
1220 					}
1221 
1222 					/* save last valid Manifest.plist */
1223 					node_tmp = plist_array_get_item(message, 1);
1224 					manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey");
1225 					if (manifest_plist) {
1226 						remove(manifest_path);
1227 						printf("Storing Manifest.plist...\n");
1228 						plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML);
1229 					}
1230 
1231 					backup_ok = 1;
1232 				}
1233 			}
1234 
1235 			if (backup_ok) {
1236 				/* Status.plist (Info on how the backup process turned out) */
1237 				printf("Backup Successful.\n");
1238 				mobilebackup_write_status(backup_directory, 1);
1239 			} else {
1240 				printf("Backup Failed.\n");
1241 			}
1242 			break;
1243 			case CMD_RESTORE:
1244 			/* close down the lockdown connection as it is no longer needed */
1245 			if (client) {
1246 				lockdownd_client_free(client);
1247 				client = NULL;
1248 			}
1249 
1250 			/* TODO: verify battery on AC enough battery remaining */
1251 
1252 			/* verify if Status.plist says we read from an successful backup */
1253 			if (mobilebackup_read_status(backup_directory) <= 0) {
1254 				printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
1255 				break;
1256 			}
1257 			/* now make sure backup integrity is ok! verify all files */
1258 			printf("Reading existing Manifest.\n");
1259 			plist_read_from_filename(&manifest_plist, manifest_path);
1260 			if (!manifest_plist) {
1261 				printf("Could not read Manifest.plist. Aborting.\n");
1262 				break;
1263 			}
1264 
1265 			printf("Verifying backup integrity, please wait.\n");
1266 			char *bin = NULL;
1267 			uint64_t binsize = 0;
1268 			node = plist_dict_get_item(manifest_plist, "Data");
1269 			if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
1270 				printf("Could not read Data key from Manifest.plist!\n");
1271 				break;
1272 			}
1273 			plist_get_data_val(node, &bin, &binsize);
1274 			plist_t backup_data = NULL;
1275 			if (bin) {
1276 				char *auth_ver = NULL;
1277 				unsigned char *auth_sig = NULL;
1278 				uint64_t auth_sig_len = 0;
1279 				/* verify AuthSignature */
1280 				node = plist_dict_get_item(manifest_plist, "AuthVersion");
1281 				plist_get_string_val(node, &auth_ver);
1282 				if (auth_ver && (strcmp(auth_ver, "2.0") == 0)) {
1283 					node = plist_dict_get_item(manifest_plist, "AuthSignature");
1284 					if (node && (plist_get_node_type(node) == PLIST_DATA)) {
1285 						plist_get_data_val(node, (char**)&auth_sig, &auth_sig_len);
1286 					}
1287 					if (auth_sig && (auth_sig_len == 20)) {
1288 						/* calculate the sha1, then compare */
1289 						unsigned char data_sha1[20];
1290 						sha1_of_data(bin, binsize, data_sha1);
1291 						if (compare_hash(auth_sig, data_sha1, 20)) {
1292 							printf("AuthSignature is valid\n");
1293 						} else {
1294 							printf("ERROR: AuthSignature is NOT VALID\n");
1295 						}
1296 					} else {
1297 						printf("Could not get AuthSignature from manifest!\n");
1298 					}
1299 					free(auth_sig);
1300 				} else if (auth_ver) {
1301 					printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver);
1302 				}
1303 				plist_from_bin(bin, (uint32_t)binsize, &backup_data);
1304 				free(bin);
1305 			}
1306 			if (!backup_data) {
1307 				printf("Could not read plist from Manifest.plist Data key!\n");
1308 				break;
1309 			}
1310 			plist_t files = plist_dict_get_item(backup_data, "Files");
1311 			if (files && (plist_get_node_type(files) == PLIST_DICT)) {
1312 				plist_dict_iter iter = NULL;
1313 				plist_dict_new_iter(files, &iter);
1314 				if (iter) {
1315 					/* loop over Files entries in Manifest data plist */
1316 					char *hash = NULL;
1317 					int file_ok = 0;
1318 					int total_files = plist_dict_get_size(files);
1319 					int cur_file = 1;
1320 					node = NULL;
1321 					plist_dict_next_item(files, iter, &hash, &node);
1322 					while (node) {
1323 						printf("Verifying file %d/%d (%d%%) \r", cur_file, total_files, (cur_file*100/total_files));
1324 						cur_file++;
1325 						/* make sure both .mddata/.mdinfo files are available for each entry */
1326 						file_ok = mobilebackup_check_file_integrity(backup_directory, hash, node);
1327 						node = NULL;
1328 						free(hash);
1329 						hash = NULL;
1330 						if (!file_ok) {
1331 							break;
1332 						}
1333 						plist_dict_next_item(files, iter, &hash, &node);
1334 					}
1335 					printf("\n");
1336 					free(iter);
1337 					if (!file_ok) {
1338 						plist_free(backup_data);
1339 						break;
1340 					}
1341 					printf("All backup files appear to be valid\n");
1342 				}
1343 			}
1344 
1345 			printf("Requesting restore from device...\n");
1346 
1347 			/* request restore from device with manifest (BackupMessageRestoreMigrate) */
1348 			int restore_flags = MB_RESTORE_NOTIFY_SPRINGBOARD | MB_RESTORE_PRESERVE_SETTINGS | MB_RESTORE_PRESERVE_CAMERA_ROLL;
1349 			err = mobilebackup_request_restore(mobilebackup, manifest_plist, restore_flags, "1.6");
1350 			if (err != MOBILEBACKUP_E_SUCCESS) {
1351 				if (err == MOBILEBACKUP_E_BAD_VERSION) {
1352 					printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
1353 				} else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
1354 					printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
1355 				} else {
1356 					printf("ERROR: Could not start restore process: unspecified error occurred (%d)\n", err);
1357 				}
1358 				plist_free(backup_data);
1359 				break;
1360 			}
1361 
1362 			printf("Entered restore mode.\n");
1363 
1364 			int restore_ok = 0;
1365 
1366 			if (files && (plist_get_node_type(files) == PLIST_DICT)) {
1367 				plist_dict_iter iter = NULL;
1368 				plist_dict_new_iter(files, &iter);
1369 				if (iter) {
1370 					/* loop over Files entries in Manifest data plist */
1371 					char *hash = NULL;
1372 					plist_t file_info = NULL;
1373 					char *file_info_path = NULL;
1374 					int total_files = plist_dict_get_size(files);
1375 					int cur_file = 0;
1376 					uint64_t file_offset = 0;
1377 					uint8_t is_encrypted = 0;
1378 					plist_t tmp_node = NULL;
1379 					plist_t file_path_node = NULL;
1380 					plist_t send_file_node = NULL;
1381 					node = NULL;
1382 					plist_dict_next_item(files, iter, &hash, &node);
1383 					while (node) {
1384 						/* TODO: read mddata/mdinfo files and send to device using DLSendFile */
1385 						file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
1386 						plist_read_from_filename(&file_info, file_info_path);
1387 
1388 						/* get encryption state */
1389 						tmp_node = plist_dict_get_item(file_info, "IsEncrypted");
1390 						plist_get_bool_val(tmp_node, &is_encrypted);
1391 						tmp_node = NULL;
1392 
1393 						/* get real file path from metadata */
1394 						tmp_node = plist_dict_get_item(file_info, "Metadata");
1395 						plist_get_data_val(tmp_node, &buffer, &length);
1396 						tmp_node = NULL;
1397 						plist_from_bin(buffer, length, &tmp_node);
1398 						file_path_node = plist_dict_get_item(tmp_node, "Path");
1399 						plist_get_string_val(file_path_node, &file_path);
1400 
1401 						printf("Restoring file %s %d/%d (%d%%)... ", file_path, cur_file, total_files, (cur_file*100/total_files));
1402 
1403 						/* add additional device link file information keys */
1404 						plist_dict_set_item(file_info, "DLFileAttributesKey", plist_copy(node));
1405 						plist_dict_set_item(file_info, "DLFileSource", plist_new_string(file_info_path));
1406 						plist_dict_set_item(file_info, "DLFileDest", plist_new_string("/tmp/RestoreFile.plist"));
1407 						plist_dict_set_item(file_info, "DLFileIsEncrypted", plist_new_bool(is_encrypted));
1408 						plist_dict_set_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1409 						plist_dict_set_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1410 
1411 						/* read data from file */
1412 						free(file_info_path);
1413 						file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata");
1414 
1415 						/* determine file size */
1416 #ifdef WIN32
1417 						struct _stati64 fst;
1418 						if (_stati64(file_info_path, &fst) != 0)
1419 #else
1420 						struct stat fst;
1421 						if (stat(file_info_path, &fst) != 0)
1422 #endif
1423 						{
1424 							printf("ERROR: stat() failed for '%s': %s\n", file_info_path, strerror(errno));
1425 							free(file_info_path);
1426 							break;
1427 						}
1428 						length = fst.st_size;
1429 
1430 						FILE *f = fopen(file_info_path, "rb");
1431 						if (!f) {
1432 							printf("ERROR: could not open local file '%s': %s\n", file_info_path, strerror(errno));
1433 							free(file_info_path);
1434 							break;
1435 						}
1436 						free(file_info_path);
1437 
1438 						/* send DLSendFile messages */
1439 						file_offset = 0;
1440 						do {
1441 							char buf[8192];
1442 							size_t len = fread(buf, 1, sizeof(buf), f);
1443 
1444 							if ((length-file_offset) <= sizeof(buf))
1445 								file_status = DEVICE_LINK_FILE_STATUS_LAST_HUNK;
1446 							else
1447 								file_status = DEVICE_LINK_FILE_STATUS_HUNK;
1448 
1449 							plist_dict_remove_item(file_info, "DLFileOffsetKey");
1450 							plist_dict_set_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1451 
1452 							plist_dict_remove_item(file_info, "DLFileStatusKey");
1453 							plist_dict_set_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1454 
1455 							send_file_node = plist_new_array();
1456 
1457 							plist_array_append_item(send_file_node, plist_new_string("DLSendFile"));
1458 
1459 							plist_array_append_item(send_file_node, plist_new_data(buf, len));
1460 							plist_array_append_item(send_file_node, plist_copy(file_info));
1461 
1462 							err = mobilebackup_send(mobilebackup, send_file_node);
1463 							if (err != MOBILEBACKUP_E_SUCCESS) {
1464 								printf("ERROR: Unable to send file hunk due to error %d. Aborting...\n", err);
1465 								file_status = DEVICE_LINK_FILE_STATUS_NONE;
1466 							}
1467 
1468 							if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1469 								/* TODO: if all hunks of a file are sent, device must send ack */
1470 								err = mobilebackup_receive_restore_file_received(mobilebackup, NULL);
1471 								if (err != MOBILEBACKUP_E_SUCCESS) {
1472 									printf("ERROR: Did not receive an ack for the sent file due to error %d. Aborting...\n", err);
1473 									file_status = DEVICE_LINK_FILE_STATUS_NONE;
1474 								}
1475 							}
1476 
1477 							file_offset += len;
1478 
1479 							if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK)
1480 								printf("DONE\n");
1481 
1482 							plist_free(send_file_node);
1483 
1484 							if (file_status == DEVICE_LINK_FILE_STATUS_NONE)
1485 								break;
1486 
1487 						} while((file_offset < length));
1488 
1489 						free(hash);
1490 						node = NULL;
1491 						hash = NULL;
1492 
1493 						restore_ok = 1;
1494 						if (file_status == DEVICE_LINK_FILE_STATUS_NONE) {
1495 							restore_ok = 0;
1496 							break;
1497 						}
1498 
1499 						cur_file++;
1500 						plist_dict_next_item(files, iter, &hash, &node);
1501 					}
1502 					free(iter);
1503 
1504 					printf("Restored %d files on device.\n", cur_file);
1505 				}
1506 			}
1507 			/* TODO: observe notification_proxy id com.apple.mobile.application_installed */
1508 			/* TODO: loop over Applications entries in Manifest data plist */
1509 			plist_t applications = plist_dict_get_item(backup_data, "Applications");
1510 			if (applications && (plist_get_node_type(applications) == PLIST_DICT) && restore_ok) {
1511 				plist_dict_iter iter = NULL;
1512 				plist_dict_new_iter(applications, &iter);
1513 				if (iter) {
1514 					/* loop over Application entries in Manifest data plist */
1515 					char *hash = NULL;
1516 					int total_files = plist_dict_get_size(applications);
1517 					int cur_file = 1;
1518 					plist_t tmp_node = NULL;
1519 					plist_t dict = NULL;
1520 					plist_t array = NULL;
1521 					node = NULL;
1522 					plist_dict_next_item(applications, iter, &hash, &node);
1523 					while (node) {
1524 						printf("Restoring Application %s %d/%d (%d%%)...", hash, cur_file, total_files, (cur_file*100/total_files));
1525 						/* FIXME: receive com.apple.mobile.application_installed notification */
1526 						/* send AppInfo entry */
1527 						tmp_node = plist_dict_get_item(node, "AppInfo");
1528 
1529 						dict = plist_new_dict();
1530 						plist_dict_set_item(dict, "AppInfo", plist_copy(tmp_node));
1531 						plist_dict_set_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageRestoreApplicationSent"));
1532 
1533 						array = plist_new_array();
1534 						plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
1535 						plist_array_append_item(array, dict);
1536 
1537 						err = mobilebackup_send(mobilebackup, array);
1538 						if (err != MOBILEBACKUP_E_SUCCESS) {
1539 							printf("ERROR: Unable to restore application %s due to error %d. Aborting...\n", hash, err);
1540 							restore_ok = 0;
1541 						}
1542 
1543 						plist_free(array);
1544 						array = NULL;
1545 						dict = NULL;
1546 
1547 						/* receive BackupMessageRestoreApplicationReceived from device */
1548 						if (restore_ok) {
1549 							err = mobilebackup_receive_restore_application_received(mobilebackup, NULL);
1550 							if (err != MOBILEBACKUP_E_SUCCESS) {
1551 								printf("ERROR: Failed to receive an ack from the device for this application due to error %d. Aborting...\n", err);
1552 								restore_ok = 0;
1553 							}
1554 						}
1555 
1556 						tmp_node = NULL;
1557 						node = NULL;
1558 						free(hash);
1559 						hash = NULL;
1560 
1561 						if (restore_ok) {
1562 							printf("DONE\n");
1563 							cur_file++;
1564 							plist_dict_next_item(applications, iter, &hash, &node);
1565 						} else
1566 							break;
1567 					}
1568 					free(iter);
1569 
1570 					if (restore_ok)
1571 						printf("All applications restored.\n");
1572 					else
1573 						printf("Failed to restore applications.\n");
1574 				}
1575 			}
1576 
1577 			plist_free(backup_data);
1578 
1579 			/* signal restore finished message to device; BackupMessageRestoreComplete */
1580 			if (restore_ok) {
1581 				err = mobilebackup_send_restore_complete(mobilebackup);
1582 				if (err != MOBILEBACKUP_E_SUCCESS) {
1583 					printf("ERROR: Could not send BackupMessageRestoreComplete, error code %d\n", err);
1584 					}
1585 			}
1586 
1587 			if (restore_ok) {
1588 				printf("Restore Successful.\n");
1589 			} else {
1590 				printf("Restore Failed.\n");
1591 			}
1592 			break;
1593 			case CMD_LEAVE:
1594 			default:
1595 			break;
1596 		}
1597 		if (lockfile) {
1598 			afc_file_lock(afc, lockfile, AFC_LOCK_UN);
1599 			afc_file_close(afc, lockfile);
1600 			lockfile = 0;
1601 			do_post_notification(NP_SYNC_DID_FINISH);
1602 		}
1603 		if (manifest_path)
1604 			free(manifest_path);
1605 	} else {
1606 		printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME);
1607 		lockdownd_client_free(client);
1608 		client = NULL;
1609 	}
1610 
1611 	if (client) {
1612 		lockdownd_client_free(client);
1613 		client = NULL;
1614 	}
1615 
1616 	if (afc)
1617 		afc_client_free(afc);
1618 
1619 	if (np)
1620 		np_client_free(np);
1621 
1622 	if (mobilebackup)
1623 		mobilebackup_client_free(mobilebackup);
1624 
1625 	idevice_free(device);
1626 
1627 	if (udid) {
1628 		free(udid);
1629 	}
1630 
1631 	return 0;
1632 }
1633 
1634