summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup4.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2010-09-05 20:15:43 +0200
committerGravatar Martin Szulecki2011-04-11 17:05:38 +0200
commitca7dba1c618ed499d0693427e89701cda1731ca9 (patch)
treeeb5f1d6de2779f43940c8cbd578b1b9de7c1745a /tools/idevicebackup4.c
parentc5fe346717a449a6bfcdbd7477724d95cdeb85d5 (diff)
downloadlibimobiledevice-ca7dba1c618ed499d0693427e89701cda1731ca9.tar.gz
libimobiledevice-ca7dba1c618ed499d0693427e89701cda1731ca9.tar.bz2
Add initial mobilebackup2 support and idevicebackup4 tool
Diffstat (limited to 'tools/idevicebackup4.c')
-rw-r--r--tools/idevicebackup4.c2142
1 files changed, 2142 insertions, 0 deletions
diff --git a/tools/idevicebackup4.c b/tools/idevicebackup4.c
new file mode 100644
index 0000000..4b39510
--- /dev/null
+++ b/tools/idevicebackup4.c
@@ -0,0 +1,2142 @@
1/*
2 * idevicebackup4.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#include <stdio.h>
24#include <string.h>
25#include <errno.h>
26#include <stdlib.h>
27#include <signal.h>
28#include <glib.h>
29#include <gcrypt.h>
30#include <unistd.h>
31
32#include <libimobiledevice/libimobiledevice.h>
33#include <libimobiledevice/lockdown.h>
34#include <libimobiledevice/mobilebackup2.h>
35#include <libimobiledevice/notification_proxy.h>
36#include <libimobiledevice/afc.h>
37
38#define MOBILEBACKUP2_SERVICE_NAME "com.apple.mobilebackup2"
39#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
40
41#define LOCK_ATTEMPTS 50
42#define LOCK_WAIT 200000
43
44#define CODE_NULL 0x00
45#define CODE_ERROR_MSG 0x0b
46#define CODE_FILE_DATA 0x0c
47
48static mobilebackup2_client_t mobilebackup2 = NULL;
49static lockdownd_client_t client = NULL;
50static idevice_t phone = NULL;
51
52static int quit_flag = 0;
53
54enum cmd_mode {
55 CMD_BACKUP,
56 CMD_RESTORE,
57 CMD_LEAVE
58};
59
60enum plist_format_t {
61 PLIST_FORMAT_XML,
62 PLIST_FORMAT_BINARY
63};
64
65enum device_link_file_status_t {
66 DEVICE_LINK_FILE_STATUS_NONE = 0,
67 DEVICE_LINK_FILE_STATUS_HUNK,
68 DEVICE_LINK_FILE_STATUS_LAST_HUNK
69};
70
71static void sha1_of_data(const char *input, uint32_t size, unsigned char *hash_out)
72{
73 gcry_md_hash_buffer(GCRY_MD_SHA1, hash_out, input, size);
74}
75
76static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len)
77{
78 int i;
79 for (i = 0; i < hash_len; i++) {
80 if (hash1[i] != hash2[i]) {
81 return 0;
82 }
83 }
84 return 1;
85}
86
87static 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)
88{
89 gcry_md_hd_t hd = NULL;
90 gcry_md_open(&hd, GCRY_MD_SHA1, 0);
91 if (!hd) {
92 printf("ERROR: Could not initialize libgcrypt/SHA1\n");
93 return;
94 }
95 gcry_md_reset(hd);
96
97 FILE *f = fopen(path, "rb");
98 if (f) {
99 unsigned char buf[16384];
100 size_t len;
101 while ((len = fread(buf, 1, 16384, f)) > 0) {
102 gcry_md_write(hd, buf, len);
103 }
104 fclose(f);
105 gcry_md_write(hd, destpath, strlen(destpath));
106 gcry_md_write(hd, ";", 1);
107 if (greylist == 1) {
108 gcry_md_write(hd, "true", 4);
109 } else {
110 gcry_md_write(hd, "false", 5);
111 }
112 gcry_md_write(hd, ";", 1);
113 if (domain) {
114 gcry_md_write(hd, domain, strlen(domain));
115 } else {
116 gcry_md_write(hd, "(null)", 6);
117 }
118 gcry_md_write(hd, ";", 1);
119 if (appid) {
120 gcry_md_write(hd, appid, strlen(appid));
121 } else {
122 gcry_md_write(hd, "(null)", 6);
123 }
124 gcry_md_write(hd, ";", 1);
125 if (version) {
126 gcry_md_write(hd, version, strlen(version));
127 } else {
128 gcry_md_write(hd, "(null)", 6);
129 }
130 unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1);
131 memcpy(hash_out, newhash, 20);
132 }
133 gcry_md_close(hd);
134}
135
136static void print_hash(const unsigned char *hash, int len)
137{
138 int i;
139 for (i = 0; i < len; i++) {
140 printf("%02x", hash[i]);
141 }
142}
143
144static void notify_cb(const char *notification, void *userdata)
145{
146 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
147 printf("User has aborted on-device\n");
148 quit_flag++;
149 } else {
150 printf("unhandled notification '%s' (TODO: implement)\n", notification);
151 }
152}
153
154static void debug_buf(const char *data, const int length)
155{
156 int i;
157 int j;
158 unsigned char c;
159
160 for (i = 0; i < length; i += 16) {
161 fprintf(stdout, "%04x: ", i);
162 for (j = 0; j < 16; j++) {
163 if (i + j >= length) {
164 fprintf(stdout, " ");
165 continue;
166 }
167 fprintf(stdout, "%02hhx ", *(data + i + j));
168 }
169 fprintf(stdout, " | ");
170 for (j = 0; j < 16; j++) {
171 if (i + j >= length)
172 break;
173 c = *(data + i + j);
174 if ((c < 32) || (c > 127)) {
175 fprintf(stdout, ".");
176 continue;
177 }
178 fprintf(stdout, "%c", c);
179 }
180 fprintf(stdout, "\n");
181 }
182 fprintf(stdout, "\n");
183}
184
185static plist_t mobilebackup_factory_info_plist_new()
186{
187 /* gather data from lockdown */
188 GTimeVal tv = {0, 0};
189 plist_t value_node = NULL;
190 plist_t root_node = NULL;
191 char *uuid = NULL;
192 char *uuid_uppercase = NULL;
193
194 plist_t ret = plist_new_dict();
195
196 /* get basic device information in one go */
197 lockdownd_get_value(client, NULL, NULL, &root_node);
198
199 /* set fields we understand */
200 value_node = plist_dict_get_item(root_node, "BuildVersion");
201 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
202
203 value_node = plist_dict_get_item(root_node, "DeviceName");
204 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node));
205 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node));
206
207 /* FIXME: How is the GUID generated? */
208 plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
209
210 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
211 if (value_node)
212 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
213
214 g_get_current_time(&tv);
215 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec));
216
217 value_node = plist_dict_get_item(root_node, "ProductType");
218 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
219
220 value_node = plist_dict_get_item(root_node, "ProductVersion");
221 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node));
222
223 value_node = plist_dict_get_item(root_node, "SerialNumber");
224 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node));
225
226 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
227 idevice_get_uuid(phone, &uuid);
228 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
229
230 /* uppercase */
231 uuid_uppercase = g_ascii_strup(uuid, -1);
232 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase));
233 free(uuid_uppercase);
234 free(uuid);
235
236 /* FIXME: Embed files as <data> nodes */
237 plist_t files = plist_new_dict();
238 plist_dict_insert_item(ret, "iTunes Files", files);
239 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2"));
240
241 plist_free(root_node);
242
243 return ret;
244}
245
246static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
247{
248 GTimeVal tv = {0, 0};
249 plist_t node = NULL;
250
251 if (!info_plist)
252 return;
253
254 g_get_current_time(&tv);
255 node = plist_dict_get_item(info_plist, "Last Backup Date");
256 plist_set_date_val(node, tv.tv_sec, tv.tv_usec);
257
258 node = NULL;
259}
260
261static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
262{
263 FILE *f;
264 uint64_t size;
265
266 *length = 0;
267
268 f = fopen(filename, "rb");
269 if (!f) {
270 return;
271 }
272
273 fseek(f, 0, SEEK_END);
274 size = ftell(f);
275 rewind(f);
276
277 if (size == 0) {
278 return;
279 }
280
281 *buffer = (char*)malloc(sizeof(char)*size);
282 fread(*buffer, sizeof(char), size, f);
283 fclose(f);
284
285 *length = size;
286}
287
288static void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length)
289{
290 FILE *f;
291
292 f = fopen(filename, "ab");
293 if (!f)
294 f = fopen(filename, "wb");
295 if (f) {
296 fwrite(buffer, sizeof(char), length, f);
297 fclose(f);
298 }
299}
300
301static int plist_read_from_filename(plist_t *plist, const char *filename)
302{
303 char *buffer = NULL;
304 uint64_t length;
305
306 if (!filename)
307 return 0;
308
309 buffer_read_from_filename(filename, &buffer, &length);
310
311 if (!buffer) {
312 return 0;
313 }
314
315 if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) {
316 plist_from_bin(buffer, length, plist);
317 } else {
318 plist_from_xml(buffer, length, plist);
319 }
320
321 free(buffer);
322
323 return 1;
324}
325
326static int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format)
327{
328 char *buffer = NULL;
329 uint32_t length;
330
331 if (!plist || !filename)
332 return 0;
333
334 if (format == PLIST_FORMAT_XML)
335 plist_to_xml(plist, &buffer, &length);
336 else if (format == PLIST_FORMAT_BINARY)
337 plist_to_bin(plist, &buffer, &length);
338 else
339 return 0;
340
341 buffer_write_to_filename(filename, buffer, length);
342
343 free(buffer);
344
345 return 1;
346}
347
348static int plist_strcmp(plist_t node, const char *str)
349{
350 char *buffer = NULL;
351 int ret = 0;
352
353 if (plist_get_node_type(node) != PLIST_STRING)
354 return ret;
355
356 plist_get_string_val(node, &buffer);
357 ret = strcmp(buffer, str);
358 free(buffer);
359
360 return ret;
361}
362
363static gchar *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension)
364{
365 gchar *filename = g_strconcat(name, extension, NULL);
366 gchar *path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, filename, NULL);
367 g_free(filename);
368 return path;
369}
370
371/*static void mobilebackup_write_status(const char *path, int status)
372{
373 struct stat st;
374 plist_t status_plist = plist_new_dict();
375 plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status));
376 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist");
377
378 if (stat(file_path, &st) == 0)
379 remove(file_path);
380
381 plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML);
382
383 plist_free(status_plist);
384 status_plist = NULL;
385
386 g_free(file_path);
387}*/
388
389static int mobilebackup_read_status(const char *path)
390{
391 int ret = -1;
392 plist_t status_plist = NULL;
393 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist");
394
395 plist_read_from_filename(&status_plist, file_path);
396 g_free(file_path);
397 if (!status_plist) {
398 printf("Could not read Status.plist!\n");
399 return ret;
400 }
401 plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
402 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
403 char *str = NULL;
404 plist_get_string_val(node, &str);
405 if (!strcmp(str, "finished")) {
406 ret = 1;
407 } else {
408 ret = 0;
409 }
410 g_free(str);
411 } else {
412 printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
413 }
414 plist_free(status_plist);
415 return ret;
416}
417
418static int mobilebackup_info_is_current_device(plist_t info)
419{
420 plist_t value_node = NULL;
421 plist_t node = NULL;
422 plist_t root_node = NULL;
423 int ret = 0;
424
425 if (!info)
426 return ret;
427
428 if (plist_get_node_type(info) != PLIST_DICT)
429 return ret;
430
431 /* get basic device information in one go */
432 lockdownd_get_value(client, NULL, NULL, &root_node);
433
434 /* verify UUID */
435 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
436 node = plist_dict_get_item(info, "Target Identifier");
437
438 if(plist_compare_node_value(value_node, node))
439 ret = 1;
440 else {
441 printf("Info.plist: UniqueDeviceID does not match.\n");
442 }
443
444 /* verify SerialNumber */
445 if (ret == 1) {
446 value_node = plist_dict_get_item(root_node, "SerialNumber");
447 node = plist_dict_get_item(info, "Serial Number");
448
449 if(plist_compare_node_value(value_node, node))
450 ret = 1;
451 else {
452 printf("Info.plist: SerialNumber does not match.\n");
453 ret = 0;
454 }
455 }
456
457 /* verify ProductVersion to prevent using backup with different OS version */
458 if (ret == 1) {
459 value_node = plist_dict_get_item(root_node, "ProductVersion");
460 node = plist_dict_get_item(info, "Product Version");
461
462 if(plist_compare_node_value(value_node, node))
463 ret = 1;
464 else {
465 printf("Info.plist: ProductVersion does not match.\n");
466 ret = 0;
467 }
468 }
469
470 plist_free(root_node);
471 root_node = NULL;
472
473 value_node = NULL;
474 node = NULL;
475
476 return ret;
477}
478
479/*static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash)
480{
481 int ret = 0;
482 gchar *path = mobilebackup_build_path(backup_directory, hash, ".mddata");
483 printf("Removing \"%s\" ", path);
484 if (!remove( path ))
485 ret = 1;
486 else
487 ret = 0;
488
489 g_free(path);
490
491 if (!ret)
492 return ret;
493
494 path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
495 printf("and \"%s\"... ", path);
496 if (!remove( path ))
497 ret = 1;
498 else
499 ret = 0;
500
501 g_free(path);
502
503 return ret;
504}*/
505
506static int mobilebackup_check_file_integrity(const char *backup_directory, const char *hash, plist_t filedata)
507{
508 char *datapath;
509 char *infopath;
510 plist_t mdinfo = NULL;
511 struct stat st;
512 unsigned char file_hash[20];
513
514 datapath = mobilebackup_build_path(backup_directory, hash, ".mddata");
515 if (stat(datapath, &st) != 0) {
516 printf("\r\n");
517 printf("ERROR: '%s.mddata' is missing!\n", hash);
518 free(datapath);
519 return 0;
520 }
521
522 infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
523 plist_read_from_filename(&mdinfo, infopath);
524 free(infopath);
525 if (!mdinfo) {
526 printf("\r\n");
527 printf("ERROR: '%s.mdinfo' is missing or corrupted!\n", hash);
528 free(datapath);
529 return 0;
530 }
531
532 /* sha1 hash verification */
533 plist_t node = plist_dict_get_item(filedata, "DataHash");
534 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
535 printf("\r\n");
536 printf("ERROR: Could not get DataHash for file entry '%s'\n", hash);
537 plist_free(mdinfo);
538 free(datapath);
539 return 0;
540 }
541
542 node = plist_dict_get_item(mdinfo, "Metadata");
543 if (!node && (plist_get_node_type(node) != PLIST_DATA)) {
544 printf("\r\n");
545 printf("ERROR: Could not find Metadata in plist '%s.mdinfo'\n", hash);
546 plist_free(mdinfo);
547 free(datapath);
548 return 0;
549 }
550
551 char *meta_bin = NULL;
552 uint64_t meta_bin_size = 0;
553 plist_get_data_val(node, &meta_bin, &meta_bin_size);
554 plist_t metadata = NULL;
555 if (meta_bin) {
556 plist_from_bin(meta_bin, (uint32_t)meta_bin_size, &metadata);
557 }
558 if (!metadata) {
559 printf("\r\n");
560 printf("ERROR: Could not get Metadata from plist '%s.mdinfo'\n", hash);
561 plist_free(mdinfo);
562 free(datapath);
563 return 0;
564 }
565
566 char *version = NULL;
567 node = plist_dict_get_item(metadata, "Version");
568 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
569 plist_get_string_val(node, &version);
570 }
571
572 char *destpath = NULL;
573 node = plist_dict_get_item(metadata, "Path");
574 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
575 plist_get_string_val(node, &destpath);
576 }
577
578 uint8_t greylist = 0;
579 node = plist_dict_get_item(metadata, "Greylist");
580 if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
581 plist_get_bool_val(node, &greylist);
582 }
583
584 char *domain = NULL;
585 node = plist_dict_get_item(metadata, "Domain");
586 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
587 plist_get_string_val(node, &domain);
588 }
589
590 char *fnstr = malloc(strlen(domain) + 1 + strlen(destpath) + 1);
591 strcpy(fnstr, domain);
592 strcat(fnstr, "-");
593 strcat(fnstr, destpath);
594 unsigned char fnhash[20];
595 char fnamehash[41];
596 char *p = fnamehash;
597 sha1_of_data(fnstr, strlen(fnstr), fnhash);
598 free(fnstr);
599 int i;
600 for ( i = 0; i < 20; i++, p += 2 ) {
601 snprintf (p, 3, "%02x", (unsigned char)fnhash[i] );
602 }
603 if (strcmp(fnamehash, hash)) {
604 printf("\r\n");
605 printf("WARNING: filename hash does not match for entry '%s'\n", hash);
606 }
607
608 char *auth_version = NULL;
609 node = plist_dict_get_item(mdinfo, "AuthVersion");
610 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
611 plist_get_string_val(node, &auth_version);
612 }
613
614 if (strcmp(auth_version, "1.0")) {
615 printf("\r\n");
616 printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version);
617 }
618
619 node = plist_dict_get_item(filedata, "DataHash");
620 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
621 printf("\r\n");
622 printf("WARNING: Could not get DataHash key from file info data for entry '%s'\n", hash);
623 }
624
625 int res = 1;
626 unsigned char *data_hash = NULL;
627 uint64_t data_hash_len = 0;
628 plist_get_data_val(node, (char**)&data_hash, &data_hash_len);
629 int hash_ok = 0;
630 if (data_hash && (data_hash_len == 20)) {
631 compute_datahash(datapath, destpath, greylist, domain, NULL, version, file_hash);
632 hash_ok = compare_hash(data_hash, file_hash, 20);
633 } else if (data_hash_len == 0) {
634 /* no datahash present */
635 hash_ok = 1;
636 }
637
638 g_free(domain);
639 g_free(version);
640 g_free(destpath);
641
642 if (!hash_ok) {
643 printf("\r\n");
644 printf("ERROR: The hash for '%s.mddata' does not match DataHash entry in Manifest\n", hash);
645 printf("datahash: ");
646 print_hash(data_hash, 20);
647 printf("\nfilehash: ");
648 print_hash(file_hash, 20);
649 printf("\n");
650 res = 0;
651 }
652 g_free(data_hash);
653 plist_free(mdinfo);
654 return res;
655}
656
657static void do_post_notification(const char *notification)
658{
659 uint16_t nport = 0;
660 np_client_t np;
661
662 if (!client) {
663 if (lockdownd_client_new_with_handshake(phone, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) {
664 return;
665 }
666 }
667
668 lockdownd_start_service(client, NP_SERVICE_NAME, &nport);
669 if (nport) {
670 np_client_new(phone, nport, &np);
671 if (np) {
672 np_post_notification(np, notification);
673 np_client_free(np);
674 }
675 } else {
676 printf("Could not start %s\n", NP_SERVICE_NAME);
677 }
678}
679
680static void print_progress(double progress)
681{
682 int i = 0;
683 if (progress < 0)
684 return;
685
686 if (progress > 100)
687 progress = 100;
688
689 printf("\r[");
690 for(i = 0; i < 50; i++) {
691 if(i < progress / 2) {
692 printf("=");
693 } else {
694 printf(" ");
695 }
696 }
697 printf("] %3.0f%%", progress);
698 fflush(stdout);
699 if (progress == 100)
700 printf("\n");
701}
702
703static void multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
704{
705 if (!status_dict) return;
706 plist_t filedict = plist_new_dict();
707 plist_dict_insert_item(filedict, "DLFileErrorString", plist_new_string(error_message));
708 plist_dict_insert_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
709 plist_dict_insert_item(status_dict, path, filedict);
710}
711
712static int errno_to_device_error(int errno_value)
713{
714 switch (errno_value) {
715 case ENOENT:
716 return -6;
717 case EEXIST:
718 return -7;
719 default:
720 return -errno_value;
721 }
722}
723
724static int handle_send_file(const char *backup_dir, const char *path, plist_t *errplist)
725{
726 uint32_t nlen = 0;
727 uint32_t pathlen = strlen(path);
728 int bytes = 0;
729 gchar *localfile = g_build_path(G_DIR_SEPARATOR_S, backup_dir, path, NULL);
730 char buf[16384];
731 struct stat fst;
732
733 FILE *f = NULL;
734 uint32_t slen = 0;
735 int errcode = -1;
736 int e = -1;
737
738 mobilebackup2_error_t err;
739
740 /* send path length */
741 nlen = GUINT32_TO_BE(pathlen);
742 err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes);
743 if (err != MOBILEBACKUP2_E_SUCCESS) {
744 goto leave_proto_err;
745 }
746 if (bytes != sizeof(nlen)) {
747 err = MOBILEBACKUP2_E_MUX_ERROR;
748 goto leave_proto_err;
749 }
750
751 /* send path */
752 err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, (uint32_t*)&bytes);
753 if (err != MOBILEBACKUP2_E_SUCCESS) {
754 goto leave_proto_err;
755 }
756 if ((uint32_t)bytes != pathlen) {
757 err = MOBILEBACKUP2_E_MUX_ERROR;
758 goto leave_proto_err;
759 }
760
761 if (stat(localfile, &fst) < 0) {
762 printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
763 errcode = errno;
764 goto leave;
765 }
766
767 f = fopen(localfile, "rb");
768 if (!f) {
769 printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
770 errcode = errno;
771 goto leave;
772 }
773
774 printf("Sending file '%s'\n", path);
775
776 /* send data size (file size + 1) */
777 uint32_t length = (uint32_t)fst.st_size;
778 nlen = GUINT32_TO_BE(length+1);
779 memcpy(buf, &nlen, sizeof(nlen));
780 /* unknown special byte */
781 buf[4] = 0x0C;
782 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, (uint32_t*)&bytes);
783 if (err != MOBILEBACKUP2_E_SUCCESS) {
784 goto leave_proto_err;
785 }
786 if (bytes != 5) {
787 goto leave_proto_err;
788 }
789
790 /* send file contents */
791 uint32_t sent = 0;
792 while (sent < length) {
793 size_t r = fread(buf, 1, sizeof(buf), f);
794 if (r <= 0) {
795 printf("%s: read error\n", __func__);
796 errcode = errno;
797 goto leave;
798 }
799 err = mobilebackup2_send_raw(mobilebackup2, buf, r, (uint32_t*)&bytes);
800 if (err != MOBILEBACKUP2_E_SUCCESS) {
801 goto leave_proto_err;
802 }
803 if ((uint32_t)bytes != r) {
804 printf("sent only %d from %d\n", bytes, r);
805 goto leave_proto_err;
806 }
807 sent += r;
808 }
809 fclose(f);
810 f = NULL;
811 errcode = 0;
812
813leave:
814 if (errcode == 0) {
815 e = 0;
816 nlen = 1;
817 nlen = GUINT32_TO_BE(nlen);
818 memcpy(buf, &nlen, 4);
819 buf[4] = 0;
820 mobilebackup2_send_raw(mobilebackup2, buf, 5, &sent);
821 } else {
822 if (!*errplist) {
823 *errplist = plist_new_dict();
824 }
825 char *errdesc = strerror(errcode);
826 multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
827
828 length = strlen(errdesc);
829 nlen = GUINT32_TO_BE(length+1);
830 memcpy(buf, &nlen, 4);
831 buf[4] = 0x06;
832 slen = 5;
833 memcpy(buf+slen, errdesc, length);
834 slen += length;
835 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, (uint32_t*)&bytes);
836 if (err != MOBILEBACKUP2_E_SUCCESS) {
837 printf("could not send message\n");
838 }
839 if ((uint32_t)bytes != slen) {
840 printf("could only send %d from %d\n", bytes, slen);
841 }
842 }
843
844leave_proto_err:
845 if (f)
846 fclose(f);
847 g_free(localfile);
848 return e;
849}
850
851static void handle_send_files(plist_t message, const char *backup_dir)
852{
853 uint32_t cnt;
854 uint32_t i = 0;
855 uint32_t sent;
856 plist_t errplist = NULL;
857
858 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
859
860 plist_t files = plist_array_get_item(message, 1);
861 cnt = plist_array_get_size(files);
862 if (cnt == 0) return;
863
864 for (i = 0; i < cnt; i++) {
865 plist_t val = plist_array_get_item(files, i);
866 if (plist_get_node_type(val) != PLIST_STRING) {
867 continue;
868 }
869 char *str = NULL;
870 plist_get_string_val(val, &str);
871 if (!str)
872 continue;
873
874 if (handle_send_file(backup_dir, str, &errplist) < 0) {
875 //printf("Error when sending file '%s' to device\n", str);
876 // TODO: perhaps we can continue, we've got a multi status response?!
877 break;
878 }
879 }
880
881 /* send terminating 0 dword */
882 uint32_t zero = 0;
883 mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
884
885 if (!errplist) {
886 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, plist_new_dict());
887 } else {
888 mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
889 plist_free(errplist);
890 }
891}
892
893static int handle_receive_files(plist_t message, const char *backup_dir)
894{
895 uint64_t backup_real_size = 0;
896 uint64_t backup_total_size = 0;
897 uint32_t blocksize;
898 uint32_t bdone;
899 uint32_t rlen;
900 uint32_t nlen = 0;
901 uint32_t r;
902 char buf[32768];
903 char *fname = NULL;
904 gchar *bname = NULL;
905 char code = 0;
906 plist_t node = NULL;
907 FILE *f = NULL;
908 unsigned int file_count = 0;
909
910 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
911
912 node = plist_array_get_item(message, 3);
913 if (plist_get_node_type(node) == PLIST_UINT) {
914 plist_get_uint_val(node, &backup_total_size);
915 }
916 if (backup_total_size > 0) {
917 gchar *format_size = g_format_size_for_display(backup_total_size);
918 printf("Receiving backup data (%s)\n", format_size);
919 g_free(format_size);
920 }
921
922 do {
923 if (quit_flag)
924 break;
925 r = 0;
926 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
927 nlen = GUINT32_FROM_BE(nlen);
928 if (nlen == 0) {
929 // we're done here
930 break;
931 }
932 fname = (char*)malloc(nlen+1);
933 r = 0;
934 mobilebackup2_receive_raw(mobilebackup2, fname, nlen, &r);
935 fname[r] = 0;
936 // we don't need this name
937 //printf("\n%s\n", fname);
938 free(fname);
939 nlen = 0;
940 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
941 nlen = GUINT32_FROM_BE(nlen);
942 fname = (char*)malloc(nlen+1);
943 mobilebackup2_receive_raw(mobilebackup2, fname, nlen, &r);
944 if (r != nlen) {
945 fprintf(stderr, "hmmm.... received %d from %d\n", r, nlen);
946 }
947 fname[r] = 0;
948 bname = g_build_path(G_DIR_SEPARATOR_S, backup_dir, fname, NULL);
949 free(fname);
950 nlen = 0;
951 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
952 nlen = GUINT32_FROM_BE(nlen);
953 code = 0;
954 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
955
956 /* TODO remove this */
957 if ((code != 0) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_MSG)) {
958 printf("Found new flag %02x\n", code);
959 }
960
961 remove(bname);
962 f = fopen(bname, "wb");
963 while (code == CODE_FILE_DATA) {
964 blocksize = nlen-1;
965 bdone = 0;
966 rlen = 0;
967 while (bdone < blocksize) {
968 if ((blocksize - bdone) < sizeof(buf)) {
969 rlen = blocksize - bdone;
970 } else {
971 rlen = sizeof(buf);
972 }
973 mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
974 if ((int)r <= 0) {
975 break;
976 }
977 fwrite(buf, 1, r, f);
978 bdone += r;
979 }
980 if (bdone == blocksize) {
981 backup_real_size += blocksize;
982 }
983 if (quit_flag)
984 break;
985 nlen = 0;
986 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
987 nlen = GUINT32_FROM_BE(nlen);
988 if (nlen > 0) {
989 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
990 } else {
991 break;
992 }
993 }
994 fclose(f);
995
996 file_count++;
997 if (backup_total_size > 0)
998 print_progress(((double)backup_real_size/(double)backup_total_size)*100);
999 if (nlen == 0) {
1000 break;
1001 }
1002
1003 /* check if an error message was received */
1004 if (code == CODE_ERROR_MSG) {
1005 /* error message */
1006 char *msg = (char*)malloc(nlen);
1007 mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
1008 msg[r] = 0;
1009 fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
1010 free(msg);
1011 }
1012 } while (1);
1013 if ((int)nlen-1 > 0) {
1014 printf("trashing padding\n");
1015 fname = (char*)malloc(nlen-1);
1016 mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
1017 debug_buf(fname, r);
1018 free(fname);
1019 }
1020 // TODO error handling?!
1021 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, plist_new_dict());
1022 return file_count;
1023}
1024
1025static void handle_list_directory(plist_t message, const char *backup_dir)
1026{
1027 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1028
1029 /* TODO implement, for now we just return an empty listing */
1030 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, plist_new_dict());
1031 if (err != MOBILEBACKUP2_E_SUCCESS) {
1032 printf("Could not send status response, error %d\n", err);
1033 }
1034}
1035
1036static void handle_make_directory(plist_t message, const char *backup_dir)
1037{
1038 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1039
1040 plist_t dir = plist_array_get_item(message, 1);
1041 char *str = NULL;
1042 int errcode = 0;
1043 char *errdesc = NULL;
1044 plist_get_string_val(dir, &str);
1045
1046 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_dir, str, NULL);
1047 g_free(str);
1048
1049 if (mkdir(newpath, 0755) < 0) {
1050 errdesc = strerror(errno);
1051 if (errno != EEXIST) {
1052 printf("mkdir: %s (%d)\n", errdesc, errno);
1053 }
1054 errcode = errno_to_device_error(errno);
1055 }
1056 g_free(newpath);
1057 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
1058 if (err != MOBILEBACKUP2_E_SUCCESS) {
1059 printf("Could not send status response, error %d\n", err);
1060 }
1061}
1062
1063/**
1064 * signal handler function for cleaning up properly
1065 */
1066static void clean_exit(int sig)
1067{
1068 fprintf(stderr, "Exiting...\n");
1069 quit_flag++;
1070}
1071
1072static void print_usage(int argc, char **argv)
1073{
1074 char *name = NULL;
1075 name = strrchr(argv[0], '/');
1076 printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0]));
1077 printf("Create or restore backup from the current or specified directory.\n\n");
1078 printf("commands:\n");
1079 printf(" backup\tSaves a device backup into DIRECTORY\n");
1080 printf(" restore\tRestores a device backup from DIRECTORY.\n\n");
1081 printf("options:\n");
1082 printf(" -d, --debug\t\tenable communication debugging\n");
1083 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
1084 printf(" -h, --help\t\tprints usage information\n");
1085 printf("\n");
1086}
1087
1088int main(int argc, char *argv[])
1089{
1090 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
1091 int i;
1092 char uuid[41];
1093 uint16_t port = 0;
1094 uuid[0] = 0;
1095 int cmd = -1;
1096 int is_full_backup = 0;
1097 char *backup_directory = NULL;
1098 struct stat st;
1099 //plist_t node = NULL;
1100 plist_t node_tmp = NULL;
1101 //plist_t manifest_plist = NULL;
1102 plist_t info_plist = NULL;
1103 //char *buffer = NULL;
1104 //char *file_path = NULL;
1105 //uint64_t length = 0;
1106 //uint64_t backup_total_size = 0;
1107 //enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE;
1108// uint64_t c = 0;
1109 mobilebackup2_error_t err;
1110 char *manifest_path = NULL;
1111
1112 /* we need to exit cleanly on running backups and restores or we cause havok */
1113 signal(SIGINT, clean_exit);
1114 signal(SIGQUIT, clean_exit);
1115 signal(SIGTERM, clean_exit);
1116 signal(SIGPIPE, SIG_IGN);
1117
1118 /* parse cmdline args */
1119 for (i = 1; i < argc; i++) {
1120 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
1121 idevice_set_debug_level(1);
1122 continue;
1123 }
1124 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) {
1125 i++;
1126 if (!argv[i] || (strlen(argv[i]) != 40)) {
1127 print_usage(argc, argv);
1128 return 0;
1129 }
1130 strcpy(uuid, argv[i]);
1131 continue;
1132 }
1133 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1134 print_usage(argc, argv);
1135 return 0;
1136 }
1137 else if (!strcmp(argv[i], "backup")) {
1138 cmd = CMD_BACKUP;
1139 }
1140 else if (!strcmp(argv[i], "restore")) {
1141 cmd = CMD_RESTORE;
1142 }
1143 else if (backup_directory == NULL) {
1144 backup_directory = argv[i];
1145 }
1146 else {
1147 print_usage(argc, argv);
1148 return 0;
1149 }
1150 }
1151
1152 /* verify options */
1153 if (cmd == -1) {
1154 printf("No command specified.\n");
1155 print_usage(argc, argv);
1156 return -1;
1157 }
1158
1159 if (backup_directory == NULL) {
1160 printf("No target backup directory specified.\n");
1161 print_usage(argc, argv);
1162 return -1;
1163 }
1164
1165 /* verify if passed backup directory exists */
1166 if (stat(backup_directory, &st) != 0) {
1167 printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
1168 return -1;
1169 }
1170
1171 if (uuid[0] != 0) {
1172 ret = idevice_new(&phone, uuid);
1173 if (ret != IDEVICE_E_SUCCESS) {
1174 printf("No device found with uuid %s, is it plugged in?\n", uuid);
1175 return -1;
1176 }
1177 }
1178 else
1179 {
1180 ret = idevice_new(&phone, NULL);
1181 if (ret != IDEVICE_E_SUCCESS) {
1182 printf("No device found, is it plugged in?\n");
1183 return -1;
1184 }
1185 char *newuuid = NULL;
1186 idevice_get_uuid(phone, &newuuid);
1187 strcpy(uuid, newuuid);
1188 free(newuuid);
1189 }
1190
1191 /* backup directory must contain an Info.plist */
1192 gchar *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, uuid, "Info.plist", NULL);
1193 if (cmd == CMD_RESTORE) {
1194 if (stat(info_path, &st) != 0) {
1195 g_free(info_path);
1196 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
1197 return -1;
1198 }
1199 } else if (cmd == CMD_BACKUP) {
1200 is_full_backup = 1;
1201 }
1202
1203 printf("Backup directory is \"%s\"\n", backup_directory);
1204
1205 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicebackup")) {
1206 idevice_free(phone);
1207 return -1;
1208 }
1209
1210 /* start notification_proxy */
1211 np_client_t np = NULL;
1212 ret = lockdownd_start_service(client, NP_SERVICE_NAME, &port);
1213 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
1214 np_client_new(phone, port, &np);
1215 np_set_notify_callback(np, notify_cb, NULL);
1216 const char *noties[5] = {
1217 NP_SYNC_CANCEL_REQUEST,
1218 NP_SYNC_SUSPEND_REQUEST,
1219 NP_SYNC_RESUME_REQUEST,
1220 NP_BACKUP_DOMAIN_CHANGED,
1221 NULL
1222 };
1223 np_observe_notifications(np, noties);
1224 } else {
1225 printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
1226 }
1227
1228 afc_client_t afc = NULL;
1229 if (cmd == CMD_BACKUP) {
1230 /* start AFC, we need this for the lock file */
1231 port = 0;
1232 ret = lockdownd_start_service(client, "com.apple.afc", &port);
1233 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
1234 afc_client_new(phone, port, &afc);
1235 }
1236 }
1237
1238 /* start mobilebackup service and retrieve port */
1239 port = 0;
1240 ret = lockdownd_start_service(client, MOBILEBACKUP2_SERVICE_NAME, &port);
1241 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
1242 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, port);
1243 mobilebackup2_client_new(phone, port, &mobilebackup2);
1244
1245 /* send Hello message */
1246 err = mobilebackup2_version_exchange(mobilebackup2);
1247 if (err != MOBILEBACKUP2_E_SUCCESS) {
1248 printf("Could not perform backup protocol version exchange, error code %d\n", err);
1249 cmd = CMD_LEAVE;
1250 goto checkpoint;
1251 }
1252
1253 /* check abort conditions */
1254 if (quit_flag > 0) {
1255 printf("Aborting backup. Cancelled by user.\n");
1256 cmd = CMD_LEAVE;
1257 goto checkpoint;
1258 }
1259
1260 /* verify existing Info.plist */
1261 if (stat(info_path, &st) == 0) {
1262 printf("Reading Info.plist from backup.\n");
1263 plist_read_from_filename(&info_plist, info_path);
1264
1265 if (!info_plist) {
1266 printf("Could not read Info.plist\n");
1267 is_full_backup = 1;
1268 }
1269 if (info_plist && (cmd == CMD_BACKUP)) {
1270 if (mobilebackup_info_is_current_device(info_plist)) {
1271 /* update the last backup time within Info.plist */
1272 mobilebackup_info_update_last_backup_date(info_plist);
1273 remove(info_path);
1274 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
1275 } else {
1276 printf("Aborting backup. Backup is not compatible with the current device.\n");
1277 cmd = CMD_LEAVE;
1278 }
1279 } else if (info_plist && (cmd == CMD_RESTORE)) {
1280 if (!mobilebackup_info_is_current_device(info_plist)) {
1281 printf("Aborting restore. Backup data is not compatible with the current device.\n");
1282 cmd = CMD_LEAVE;
1283 }
1284 }
1285 } else {
1286 if (cmd == CMD_RESTORE) {
1287 printf("Aborting restore. Info.plist is missing.\n");
1288 cmd = CMD_LEAVE;
1289 } else {
1290 is_full_backup = 1;
1291 }
1292 }
1293
1294 uint64_t lockfile = 0;
1295 if (cmd == CMD_BACKUP) {
1296 do_post_notification(NP_SYNC_WILL_START);
1297 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
1298 }
1299 if (lockfile) {
1300 afc_error_t aerr;
1301 do_post_notification(NP_SYNC_LOCK_REQUEST);
1302 for (i = 0; i < LOCK_ATTEMPTS; i++) {
1303 aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
1304 if (aerr == AFC_E_SUCCESS) {
1305 do_post_notification(NP_SYNC_DID_START);
1306 break;
1307 } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
1308 usleep(LOCK_WAIT);
1309 continue;
1310 } else {
1311 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
1312 afc_file_close(afc, lockfile);
1313 lockfile = 0;
1314 cmd = CMD_LEAVE;
1315 }
1316 }
1317 if (i == LOCK_ATTEMPTS) {
1318 fprintf(stderr, "ERROR: timeout while locking for sync\n");
1319 afc_file_close(afc, lockfile);
1320 lockfile = 0;
1321 cmd = CMD_LEAVE;
1322 }
1323 }
1324
1325 /* Manifest.plist (backup manifest (backup state)) */
1326 manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, uuid, "Manifest.plist", NULL);
1327
1328checkpoint:
1329
1330 switch(cmd) {
1331 case CMD_BACKUP:
1332 printf("Starting backup...\n");
1333 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
1334 /* TODO: verify battery on AC enough battery remaining */
1335 gchar *device_backup_dir = g_build_path(G_DIR_SEPARATOR_S, backup_directory, uuid, NULL);
1336 if (mobilebackup_read_status(device_backup_dir) <= 0) {
1337 gchar *status_plist = g_build_path(G_DIR_SEPARATOR_S, backup_directory, uuid, "Status.plist", NULL);
1338 remove(status_plist);
1339 g_free(status_plist);
1340 }
1341 g_free(device_backup_dir);
1342
1343 /* read the last Manifest.plist */
1344 /*
1345 if (!is_full_backup) {
1346 printf("Reading existing Manifest.\n");
1347 plist_read_from_filename(&manifest_plist, manifest_path);
1348 if (!manifest_plist) {
1349 printf("Could not read Manifest.plist, switching to full backup mode.\n");
1350 is_full_backup = 1;
1351 }
1352 }*/
1353
1354 /* Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
1355
1356 /* create new Info.plist on new backups */
1357 if (is_full_backup) {
1358 if (info_plist) {
1359 plist_free(info_plist);
1360 info_plist = NULL;
1361 }
1362 remove(info_path);
1363 printf("Creating Info.plist for new backup.\n");
1364 info_plist = mobilebackup_factory_info_plist_new();
1365 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
1366 }
1367 g_free(info_path);
1368
1369 plist_free(info_plist);
1370 info_plist = NULL;
1371
1372 /* close down the lockdown connection as it is no longer needed */
1373 if (client) {
1374 lockdownd_client_free(client);
1375 client = NULL;
1376 }
1377
1378 /* create Status.plist with failed status for now */
1379 //mobilebackup_write_status(backup_directory, 0);
1380
1381 /* request backup from device with manifest from last backup */
1382 printf("Requesting backup from device...\n");
1383
1384 err = mobilebackup2_request_backup(mobilebackup2, uuid);
1385 if (err == MOBILEBACKUP2_E_SUCCESS) {
1386 /*if (is_full_backup)
1387 printf("Full backup mode.\n");
1388 else
1389 printf("Incremental backup mode.\n");*/
1390 //printf("Please wait. Device is preparing backup data...\n");
1391 } else {
1392 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
1393 printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
1394 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
1395 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
1396 } else {
1397 printf("ERROR: Could not start backup process: unspecified error occured\n");
1398 }
1399 break;
1400 }
1401
1402 /* reset backup status */
1403 int backup_ok = 0;
1404 plist_t message = NULL;
1405
1406 /* TODO receive and save DLSendFile files and metadata, ACK each */
1407 char *dlmsg = NULL;
1408 /*uint64_t file_size = 0;
1409 uint64_t file_size_current = 0;*/
1410 int file_count = 0;
1411 /*int hunk_index = 0;
1412 uint64_t backup_real_size = 0;
1413 char *file_ext = NULL;
1414 char *filename_mdinfo = NULL;
1415 char *filename_mddata = NULL;
1416 char *filename_source = NULL;
1417 char *format_size = NULL;
1418 gboolean is_manifest = FALSE;
1419 uint8_t b = 0;*/
1420 int errcode = 0;
1421 const char *errdesc = NULL;
1422
1423 /* process series of DLMessage* operations */
1424 do {
1425 if (dlmsg) {
1426 free(dlmsg);
1427 dlmsg = NULL;
1428 }
1429 mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
1430 if (!message || !dlmsg) {
1431 printf("Device is not ready yet. Going to try again in 2 seconds...\n");
1432 sleep(2);
1433 goto files_out;
1434 }
1435
1436 //node = plist_array_get_item(message, 0);
1437
1438 if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
1439 /* device wants to download files from the computer */
1440 handle_send_files(message, backup_directory);
1441 } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
1442 /* device wants to send files to the computer */
1443 file_count += handle_receive_files(message, backup_directory);
1444 } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
1445 /* list directory contents */
1446 handle_list_directory(message, backup_directory);
1447 } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
1448 /* make a directory */
1449 handle_make_directory(message, backup_directory);
1450 } else if (!strcmp(dlmsg, "DLMessageMoveFiles")) {
1451 /* perform a series of rename operations */
1452 plist_t moves = plist_array_get_item(message, 1);
1453 uint32_t cnt = plist_dict_get_size(moves);
1454 printf("Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
1455 plist_dict_iter iter = NULL;
1456 plist_dict_new_iter(moves, &iter);
1457 errcode = 0;
1458 errdesc = NULL;
1459 if (iter) {
1460 char *key = NULL;
1461 plist_t val = NULL;
1462 do {
1463 plist_dict_next_item(moves, iter, &key, &val);
1464 if (key && (plist_get_node_type(val) == PLIST_STRING)) {
1465 char *str = NULL;
1466 plist_get_string_val(val, &str);
1467 if (str) {
1468 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, str, NULL);
1469 g_free(str);
1470 gchar *oldpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, key, NULL);
1471
1472 remove(newpath);
1473 //fprintf(stderr, "Moving '%s' to '%s'\n", oldpath, newpath);
1474 if (rename(oldpath, newpath) < 0) {
1475 printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
1476 errcode = -errno;
1477 errdesc = strerror(errno);
1478 break;
1479 }
1480 g_free(oldpath);
1481 g_free(newpath);
1482 }
1483 free(key);
1484 key = NULL;
1485 }
1486 } while (val);
1487 free(iter);
1488 } else {
1489 errcode = -1;
1490 errdesc = "Could not create dict iterator";
1491 printf("Could not create dict iterator\n");
1492 }
1493 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
1494 if (err != MOBILEBACKUP2_E_SUCCESS) {
1495 printf("Could not send status response, error %d\n", err);
1496 }
1497 } else if (!strcmp(dlmsg, "DLMessageRemoveFiles")) {
1498 plist_t removes = plist_array_get_item(message, 1);
1499 uint32_t cnt = plist_array_get_size(removes);
1500 printf("Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
1501 uint32_t ii = 0;
1502 errcode = 0;
1503 errdesc = NULL;
1504 for (ii = 0; ii < cnt; ii++) {
1505 plist_t val = plist_array_get_item(removes, ii);
1506 if (plist_get_node_type(val) == PLIST_STRING) {
1507 char *str = NULL;
1508 plist_get_string_val(val, &str);
1509 if (str) {
1510 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, str, NULL);
1511 g_free(str);
1512 if (remove(newpath) < 0) {
1513 printf("Could not remove '%s': %s (%d)\n", newpath, strerror(errno), errno);
1514 //errcode = -errno;
1515 //errdesc = strerror(errno);
1516 }
1517 g_free(newpath);
1518 }
1519 }
1520 }
1521 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
1522 if (err != MOBILEBACKUP2_E_SUCCESS) {
1523 printf("Could not send status response, error %d\n", err);
1524 }
1525 } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
1526 node_tmp = plist_array_get_item(message, 1);
1527 if (plist_get_node_type(node_tmp) != PLIST_DICT) {
1528 printf("Unknown message received!\n");
1529 }
1530 plist_t nn;
1531 int error_code = -1;
1532 nn = plist_dict_get_item(node_tmp, "ErrorCode");
1533 if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
1534 uint64_t ec = 0;
1535 plist_get_uint_val(nn, &ec);
1536 error_code = (uint32_t)ec;
1537 if (error_code == 0) {
1538 backup_ok = 1;
1539 }
1540 }
1541 nn = plist_dict_get_item(node_tmp, "ErrorDescription");
1542 char *str = NULL;
1543 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
1544 plist_get_string_val(nn, &str);
1545 }
1546 if (error_code != 0) {
1547 if (str) {
1548 printf("ErrorCode %d: Description: %s\n", error_code, str);
1549 } else {
1550 printf("ErrorCode %d: (Unknown)\n", error_code);
1551 }
1552 }
1553 if (str) {
1554 free(str);
1555 }
1556 break;
1557 }
1558
1559 /* print status */
1560 /*if (plist_array_get_size(message) >= 4) {
1561 plist_t pnode = plist_array_get_item(message, 4);
1562 if (pnode && (plist_get_node_type(pnode) == PLIST_REAL)) {
1563 double progress = 0.0;
1564 plist_get_real_val(pnode, &progress);
1565 printf("Progress: %f\n", progress);
1566 }
1567 }*/
1568
1569#if 0
1570 /* get out if we don't get a DLSendFile */
1571 if (plist_strcmp(node, "DLSendFile"))
1572 break;
1573
1574 node_tmp = plist_array_get_item(message, 2);
1575
1576 /* first message hunk contains total backup size */
1577 if ((hunk_index == 0) && (file_index == 0)) {
1578 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey");
1579 if (node) {
1580 plist_get_uint_val(node, &backup_total_size);
1581 format_size = g_format_size_for_display(backup_total_size);
1582 printf("Backup data requires %s on the disk.\n", format_size);
1583 g_free(format_size);
1584 }
1585 }
1586
1587 /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */
1588 node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
1589 plist_get_uint_val(node, &c);
1590 file_status = c;
1591
1592 /* get source filename */
1593 node = plist_dict_get_item(node_tmp, "BackupManifestKey");
1594 b = 0;
1595 if (node) {
1596 plist_get_bool_val(node, &b);
1597 }
1598 is_manifest = (b == 1) ? TRUE: FALSE;
1599
1600 if ((hunk_index == 0) && (!is_manifest)) {
1601 /* get source filename */
1602 node = plist_dict_get_item(node_tmp, "DLFileSource");
1603 plist_get_string_val(node, &filename_source);
1604
1605 /* increase received size */
1606 node = plist_dict_get_item(node_tmp, "DLFileAttributesKey");
1607 node = plist_dict_get_item(node, "FileSize");
1608 plist_get_uint_val(node, &file_size);
1609 backup_real_size += file_size;
1610
1611 format_size = g_format_size_for_display(backup_real_size);
1612 printf("(%s", format_size);
1613 g_free(format_size);
1614
1615 format_size = g_format_size_for_display(backup_total_size);
1616 printf("/%s): ", format_size);
1617 g_free(format_size);
1618
1619 format_size = g_format_size_for_display(file_size);
1620 printf("Receiving file %s (%s)... \n", filename_source, format_size);
1621 g_free(format_size);
1622
1623 if (filename_source)
1624 free(filename_source);
1625 }
1626
1627 /* check if we completed a file */
1628 if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (!is_manifest)) {
1629 /* save <hash>.mdinfo */
1630 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1631 if (node) {
1632 node = plist_dict_get_item(node_tmp, "DLFileDest");
1633 plist_get_string_val(node, &file_path);
1634
1635 filename_mdinfo = mobilebackup_build_path(backup_directory, file_path, ".mdinfo");
1636
1637 /* remove any existing file */
1638 if (stat(filename_mdinfo, &st) == 0)
1639 remove(filename_mdinfo);
1640
1641 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1642 plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY);
1643
1644 g_free(filename_mdinfo);
1645 }
1646
1647 file_index++;
1648 }
1649
1650 /* save <hash>.mddata */
1651 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1652 if (node_tmp) {
1653 node = plist_dict_get_item(node_tmp, "DLFileDest");
1654 plist_get_string_val(node, &file_path);
1655
1656 filename_mddata = mobilebackup_build_path(backup_directory, file_path, is_manifest ? NULL: ".mddata");
1657
1658 /* if this is the first hunk, remove any existing file */
1659 if ((hunk_index == 0) && (stat(filename_mddata, &st) == 0))
1660 remove(filename_mddata);
1661
1662 /* get file data hunk */
1663 node_tmp = plist_array_get_item(message, 1);
1664 plist_get_data_val(node_tmp, &buffer, &length);
1665
1666 buffer_write_to_filename(filename_mddata, buffer, length);
1667 if (!is_manifest)
1668 file_size_current += length;
1669
1670 /* activate currently sent manifest */
1671 if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (is_manifest)) {
1672 rename(filename_mddata, manifest_path);
1673 }
1674
1675 free(buffer);
1676 buffer = NULL;
1677
1678 g_free(filename_mddata);
1679 }
1680
1681 if ((!is_manifest)) {
1682 if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1683 print_progress(100);
1684 } else {
1685 if (file_size > 0)
1686 print_progress((double)((file_size_current*100)/file_size));
1687 }
1688 }
1689
1690 hunk_index++;
1691
1692 if (file_ext)
1693 free(file_ext);
1694#endif
1695 if (message)
1696 plist_free(message);
1697 message = NULL;
1698
1699#if 0
1700 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1701 /* acknowlegdge that we received the file */
1702 mobilebackup_send_backup_file_received(mobilebackup);
1703 /* reset hunk_index */
1704 hunk_index = 0;
1705 if (!is_manifest) {
1706 file_size_current = 0;
1707 file_size = 0;
1708 }
1709 }
1710#endif
1711
1712files_out:
1713 if (quit_flag > 0) {
1714 /* need to cancel the backup here */
1715 //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
1716
1717 /* remove any atomic Manifest.plist.tmp */
1718 if (manifest_path)
1719 g_free(manifest_path);
1720
1721 /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
1722 if (stat(manifest_path, &st) == 0)
1723 remove(manifest_path);*/
1724 break;
1725 }
1726 } while (1);
1727
1728 printf("Received %d files from device.\n", file_count);
1729#if 0
1730 if (!quit_flag && !plist_strcmp(node, "DLMessageProcessMessage")) {
1731 node_tmp = plist_array_get_item(message, 1);
1732 node = plist_dict_get_item(node_tmp, "BackupMessageTypeKey");
1733 /* check if we received the final "backup finished" message */
1734 if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) {
1735 /* backup finished */
1736
1737 /* process BackupFilesToDeleteKey */
1738 node = plist_dict_get_item(node_tmp, "BackupFilesToDeleteKey");
1739 if (node) {
1740 length = plist_array_get_size(node);
1741 i = 0;
1742 while ((node_tmp = plist_array_get_item(node, i++)) != NULL) {
1743 plist_get_string_val(node_tmp, &file_path);
1744
1745 if (mobilebackup_delete_backup_file_by_hash(backup_directory, file_path)) {
1746 printf("DONE\n");
1747 } else
1748 printf("FAILED\n");
1749 }
1750 }
1751
1752 /* save last valid Manifest.plist */
1753 node_tmp = plist_array_get_item(message, 1);
1754 manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey");
1755 if (manifest_plist) {
1756 remove(manifest_path);
1757 printf("Storing Manifest.plist...\n");
1758 plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML);
1759 }
1760
1761 backup_ok = 1;
1762 }
1763 }
1764#endif
1765 if (backup_ok) {
1766 // /* Status.plist (Info on how the backup process turned out) */
1767 printf("Backup Successful.\n");
1768 //mobilebackup_write_status(backup_directory, 1);
1769 } else {
1770 printf("Backup Failed.\n");
1771 }
1772
1773 break;
1774 case CMD_RESTORE:
1775 /* close down the lockdown connection as it is no longer needed */
1776 if (client) {
1777 lockdownd_client_free(client);
1778 client = NULL;
1779 }
1780
1781 /* TODO: verify battery on AC enough battery remaining */
1782
1783 /* verify if Status.plist says we read from an successful backup */
1784#if 0
1785 if (mobilebackup_read_status(backup_directory) <= 0) {
1786 printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
1787 break;
1788 }
1789 /* now make sure backup integrity is ok! verify all files */
1790 printf("Reading existing Manifest.\n");
1791 plist_read_from_filename(&manifest_plist, manifest_path);
1792 if (!manifest_plist) {
1793 printf("Could not read Manifest.plist. Aborting.\n");
1794 break;
1795 }
1796
1797 printf("Verifying backup integrity, please wait.\n");
1798 char *bin = NULL;
1799 uint64_t binsize = 0;
1800 node = plist_dict_get_item(manifest_plist, "Data");
1801 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
1802 printf("Could not read Data key from Manifest.plist!\n");
1803 break;
1804 }
1805 plist_get_data_val(node, &bin, &binsize);
1806 plist_t backup_data = NULL;
1807 if (bin) {
1808 char *auth_ver = NULL;
1809 unsigned char *auth_sig = NULL;
1810 uint64_t auth_sig_len = 0;
1811 /* verify AuthSignature */
1812 node = plist_dict_get_item(manifest_plist, "AuthVersion");
1813 plist_get_string_val(node, &auth_ver);
1814 if (auth_ver && (strcmp(auth_ver, "2.0") == 0)) {
1815 node = plist_dict_get_item(manifest_plist, "AuthSignature");
1816 if (node && (plist_get_node_type(node) == PLIST_DATA)) {
1817 plist_get_data_val(node, (char**)&auth_sig, &auth_sig_len);
1818 }
1819 if (auth_sig && (auth_sig_len == 20)) {
1820 /* calculate the sha1, then compare */
1821 unsigned char data_sha1[20];
1822 sha1_of_data(bin, binsize, data_sha1);
1823 if (compare_hash(auth_sig, data_sha1, 20)) {
1824 printf("AuthSignature is valid\n");
1825 } else {
1826 printf("ERROR: AuthSignature is NOT VALID\n");
1827 }
1828 } else {
1829 printf("Could not get AuthSignature from manifest!\n");
1830 }
1831 g_free(auth_sig);
1832 } else if (auth_ver) {
1833 printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver);
1834 }
1835 plist_from_bin(bin, (uint32_t)binsize, &backup_data);
1836 g_free(bin);
1837 }
1838 if (!backup_data) {
1839 printf("Could not read plist from Manifest.plist Data key!\n");
1840 break;
1841 }
1842 plist_t files = plist_dict_get_item(backup_data, "Files");
1843 if (files && (plist_get_node_type(files) == PLIST_DICT)) {
1844 plist_dict_iter iter = NULL;
1845 plist_dict_new_iter(files, &iter);
1846 if (iter) {
1847 /* loop over Files entries in Manifest data plist */
1848 char *hash = NULL;
1849 int file_ok = 0;
1850 int total_files = plist_dict_get_size(files);
1851 int cur_file = 1;
1852 node = NULL;
1853 plist_dict_next_item(files, iter, &hash, &node);
1854 while (node) {
1855 printf("Verifying file %d/%d (%d%%) \r", cur_file, total_files, (cur_file*100/total_files));
1856 cur_file++;
1857 /* make sure both .mddata/.mdinfo files are available for each entry */
1858 file_ok = mobilebackup_check_file_integrity(backup_directory, hash, node);
1859 node = NULL;
1860 free(hash);
1861 hash = NULL;
1862 if (!file_ok) {
1863 break;
1864 }
1865 plist_dict_next_item(files, iter, &hash, &node);
1866 }
1867 printf("\n");
1868 free(iter);
1869 if (!file_ok) {
1870 plist_free(backup_data);
1871 break;
1872 }
1873 printf("All backup files appear to be valid\n");
1874 }
1875 }
1876
1877 printf("Requesting restore from device...\n");
1878
1879 /* request restore from device with manifest (BackupMessageRestoreMigrate) */
1880 int restore_flags = MB_RESTORE_NOTIFY_SPRINGBOARD | MB_RESTORE_PRESERVE_SETTINGS | MB_RESTORE_PRESERVE_CAMERA_ROLL;
1881 err = mobilebackup_request_restore(mobilebackup, manifest_plist, restore_flags, "1.6");
1882 if (err != MOBILEBACKUP_E_SUCCESS) {
1883 if (err == MOBILEBACKUP_E_BAD_VERSION) {
1884 printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
1885 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
1886 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
1887 } else {
1888 printf("ERROR: Could not start restore process: unspecified error occured (%d)\n", err);
1889 }
1890 plist_free(backup_data);
1891 break;
1892 }
1893
1894 printf("Entered restore mode.\n");
1895
1896 int restore_ok = 0;
1897
1898 if (files && (plist_get_node_type(files) == PLIST_DICT)) {
1899 plist_dict_iter iter = NULL;
1900 plist_dict_new_iter(files, &iter);
1901 if (iter) {
1902 /* loop over Files entries in Manifest data plist */
1903 char *hash = NULL;
1904 plist_t file_info = NULL;
1905 char *file_info_path = NULL;
1906 int total_files = plist_dict_get_size(files);
1907 int cur_file = 0;
1908 uint64_t file_offset = 0;
1909 uint8_t is_encrypted = 0;
1910 plist_t tmp_node = NULL;
1911 plist_t file_path_node = NULL;
1912 plist_t send_file_node = NULL;
1913 node = NULL;
1914 plist_dict_next_item(files, iter, &hash, &node);
1915 while (node) {
1916 /* TODO: read mddata/mdinfo files and send to device using DLSendFile */
1917 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
1918 plist_read_from_filename(&file_info, file_info_path);
1919
1920 /* get encryption state */
1921 tmp_node = plist_dict_get_item(file_info, "IsEncrypted");
1922 plist_get_bool_val(tmp_node, &is_encrypted);
1923 tmp_node = NULL;
1924
1925 /* get real file path from metadata */
1926 tmp_node = plist_dict_get_item(file_info, "Metadata");
1927 plist_get_data_val(tmp_node, &buffer, &length);
1928 tmp_node = NULL;
1929 plist_from_bin(buffer, length, &tmp_node);
1930 file_path_node = plist_dict_get_item(tmp_node, "Path");
1931 plist_get_string_val(file_path_node, &file_path);
1932
1933 printf("Restoring file %s %d/%d (%d%%)... ", file_path, cur_file, total_files, (cur_file*100/total_files));
1934
1935 /* add additional device link file information keys */
1936 plist_dict_insert_item(file_info, "DLFileAttributesKey", plist_copy(node));
1937 plist_dict_insert_item(file_info, "DLFileSource", plist_new_string(file_info_path));
1938 plist_dict_insert_item(file_info, "DLFileDest", plist_new_string("/tmp/RestoreFile.plist"));
1939 plist_dict_insert_item(file_info, "DLFileIsEncrypted", plist_new_bool(is_encrypted));
1940 plist_dict_insert_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1941 plist_dict_insert_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1942
1943 /* read data from file */
1944 free(file_info_path);
1945 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata");
1946 buffer_read_from_filename(file_info_path, &buffer, &length);
1947 free(file_info_path);
1948
1949 /* send DLSendFile messages */
1950 file_offset = 0;
1951 do {
1952 if ((length-file_offset) <= 8192)
1953 file_status = DEVICE_LINK_FILE_STATUS_LAST_HUNK;
1954 else
1955 file_status = DEVICE_LINK_FILE_STATUS_HUNK;
1956
1957 plist_dict_remove_item(file_info, "DLFileOffsetKey");
1958 plist_dict_insert_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1959
1960 plist_dict_remove_item(file_info, "DLFileStatusKey");
1961 plist_dict_insert_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1962
1963 send_file_node = plist_new_array();
1964
1965 plist_array_append_item(send_file_node, plist_new_string("DLSendFile"));
1966
1967 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK)
1968 plist_array_append_item(send_file_node, plist_new_data(buffer+file_offset, length-file_offset));
1969 else
1970 plist_array_append_item(send_file_node, plist_new_data(buffer+file_offset, 8192));
1971
1972 plist_array_append_item(send_file_node, plist_copy(file_info));
1973
1974 err = mobilebackup_send(mobilebackup, send_file_node);
1975 if (err != MOBILEBACKUP_E_SUCCESS) {
1976 printf("ERROR: Unable to send file hunk due to error %d. Aborting...\n", err);
1977 file_status = DEVICE_LINK_FILE_STATUS_NONE;
1978 }
1979
1980 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1981 /* TODO: if all hunks of a file are sent, device must send ack */
1982 err = mobilebackup_receive_restore_file_received(mobilebackup, NULL);
1983 if (err != MOBILEBACKUP_E_SUCCESS) {
1984 printf("ERROR: Did not receive an ack for the sent file due to error %d. Aborting...\n", err);
1985 file_status = DEVICE_LINK_FILE_STATUS_NONE;
1986 }
1987 }
1988
1989 file_offset += 8192;
1990
1991 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK)
1992 printf("DONE\n");
1993
1994 plist_free(send_file_node);
1995
1996 if (file_status == DEVICE_LINK_FILE_STATUS_NONE)
1997 break;
1998
1999 } while((file_offset < length));
2000
2001 free(hash);
2002 node = NULL;
2003 hash = NULL;
2004
2005 restore_ok = 1;
2006 if (file_status == DEVICE_LINK_FILE_STATUS_NONE) {
2007 restore_ok = 0;
2008 break;
2009 }
2010
2011 cur_file++;
2012 plist_dict_next_item(files, iter, &hash, &node);
2013 }
2014 free(iter);
2015
2016 printf("Restored %d files on device.\n", cur_file);
2017 }
2018 }
2019 /* TODO: observe notification_proxy id com.apple.mobile.application_installed */
2020 /* TODO: loop over Applications entries in Manifest data plist */
2021 plist_t applications = plist_dict_get_item(backup_data, "Applications");
2022 if (applications && (plist_get_node_type(applications) == PLIST_DICT) && restore_ok) {
2023 plist_dict_iter iter = NULL;
2024 plist_dict_new_iter(applications, &iter);
2025 if (iter) {
2026 /* loop over Application entries in Manifest data plist */
2027 char *hash = NULL;
2028 int total_files = plist_dict_get_size(applications);
2029 int cur_file = 1;
2030 plist_t tmp_node = NULL;
2031 plist_t dict = NULL;
2032 plist_t array = NULL;
2033 node = NULL;
2034 plist_dict_next_item(applications, iter, &hash, &node);
2035 while (node) {
2036 printf("Restoring Application %s %d/%d (%d%%)...", hash, cur_file, total_files, (cur_file*100/total_files));
2037 /* FIXME: receive com.apple.mobile.application_installed notification */
2038 /* send AppInfo entry */
2039 tmp_node = plist_dict_get_item(node, "AppInfo");
2040
2041 dict = plist_new_dict();
2042 plist_dict_insert_item(dict, "AppInfo", plist_copy(tmp_node));
2043 plist_dict_insert_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageRestoreApplicationSent"));
2044
2045 array = plist_new_array();
2046 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
2047 plist_array_append_item(array, dict);
2048
2049 err = mobilebackup_send(mobilebackup, array);
2050 if (err != MOBILEBACKUP_E_SUCCESS) {
2051 printf("ERROR: Unable to restore application %s due to error %d. Aborting...\n", hash, err);
2052 restore_ok = 0;
2053 }
2054
2055 plist_free(array);
2056 array = NULL;
2057 dict = NULL;
2058
2059 /* receive BackupMessageRestoreApplicationReceived from device */
2060 if (restore_ok) {
2061 err = mobilebackup_receive_restore_application_received(mobilebackup, NULL);
2062 if (err != MOBILEBACKUP_E_SUCCESS) {
2063 printf("ERROR: Failed to receive an ack from the device for this application due to error %d. Aborting...\n", err);
2064 restore_ok = 0;
2065 }
2066 }
2067
2068 tmp_node = NULL;
2069 node = NULL;
2070 free(hash);
2071 hash = NULL;
2072
2073 if (restore_ok) {
2074 printf("DONE\n");
2075 cur_file++;
2076 plist_dict_next_item(applications, iter, &hash, &node);
2077 } else
2078 break;
2079 }
2080 free(iter);
2081
2082 if (restore_ok)
2083 printf("All applications restored.\n");
2084 else
2085 printf("Failed to restore applications.\n");
2086 }
2087 }
2088
2089 plist_free(backup_data);
2090
2091 /* signal restore finished message to device; BackupMessageRestoreComplete */
2092 if (restore_ok) {
2093 err = mobilebackup_send_restore_complete(mobilebackup);
2094 if (err != MOBILEBACKUP_E_SUCCESS) {
2095 printf("ERROR: Could not send BackupMessageRestoreComplete, error code %d\n", err);
2096 }
2097 }
2098
2099 if (restore_ok) {
2100 printf("Restore Successful.\n");
2101 } else {
2102 printf("Restore Failed.\n");
2103 }
2104#endif
2105 break;
2106 case CMD_LEAVE:
2107 default:
2108 break;
2109 }
2110 if (lockfile) {
2111 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
2112 afc_file_close(afc, lockfile);
2113 lockfile = 0;
2114 do_post_notification(NP_SYNC_DID_FINISH);
2115 }
2116 if (manifest_path)
2117 g_free(manifest_path);
2118 } else {
2119 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP2_SERVICE_NAME);
2120 lockdownd_client_free(client);
2121 client = NULL;
2122 }
2123
2124 if (client) {
2125 lockdownd_client_free(client);
2126 client = NULL;
2127 }
2128
2129 if (afc)
2130 afc_client_free(afc);
2131
2132 if (np)
2133 np_client_free(np);
2134
2135 if (mobilebackup2)
2136 mobilebackup2_client_free(mobilebackup2);
2137
2138 idevice_free(phone);
2139
2140 return 0;
2141}
2142