summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/idevicebackup2.c')
-rw-r--r--tools/idevicebackup2.c2684
1 files changed, 2684 insertions, 0 deletions
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
new file mode 100644
index 0000000..c73b269
--- /dev/null
+++ b/tools/idevicebackup2.c
@@ -0,0 +1,2684 @@
1/*
2 * idevicebackup2.c
3 * Command line interface to use the device's backup and restore service
4 *
5 * Copyright (c) 2010-2022 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2009-2010 Martin Szulecki, 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 "idevicebackup2"
28
29#include <stdio.h>
30#include <string.h>
31#include <errno.h>
32#include <stdlib.h>
33#include <signal.h>
34#include <unistd.h>
35#include <dirent.h>
36#include <libgen.h>
37#include <ctype.h>
38#include <time.h>
39#include <getopt.h>
40
41#include <libimobiledevice/libimobiledevice.h>
42#include <libimobiledevice/lockdown.h>
43#include <libimobiledevice/mobilebackup2.h>
44#include <libimobiledevice/notification_proxy.h>
45#include <libimobiledevice/afc.h>
46#include <libimobiledevice/installation_proxy.h>
47#include <libimobiledevice/sbservices.h>
48#include <libimobiledevice/diagnostics_relay.h>
49#include <libimobiledevice-glue/utils.h>
50#include <plist/plist.h>
51
52#include <endianness.h>
53
54#define LOCK_ATTEMPTS 50
55#define LOCK_WAIT 200000
56
57#ifdef WIN32
58#include <windows.h>
59#include <conio.h>
60#define sleep(x) Sleep(x*1000)
61#ifndef ELOOP
62#define ELOOP 114
63#endif
64#else
65#include <termios.h>
66#include <sys/statvfs.h>
67#endif
68#include <sys/stat.h>
69
70#define CODE_SUCCESS 0x00
71#define CODE_ERROR_LOCAL 0x06
72#define CODE_ERROR_REMOTE 0x0b
73#define CODE_FILE_DATA 0x0c
74
75static int verbose = 1;
76static int quit_flag = 0;
77
78#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
79
80enum cmd_mode {
81 CMD_BACKUP,
82 CMD_RESTORE,
83 CMD_INFO,
84 CMD_LIST,
85 CMD_UNBACK,
86 CMD_CHANGEPW,
87 CMD_LEAVE,
88 CMD_CLOUD
89};
90
91enum cmd_flags {
92 CMD_FLAG_RESTORE_SYSTEM_FILES = (1 << 1),
93 CMD_FLAG_RESTORE_NO_REBOOT = (1 << 2),
94 CMD_FLAG_RESTORE_COPY_BACKUP = (1 << 3),
95 CMD_FLAG_RESTORE_SETTINGS = (1 << 4),
96 CMD_FLAG_RESTORE_REMOVE_ITEMS = (1 << 5),
97 CMD_FLAG_ENCRYPTION_ENABLE = (1 << 6),
98 CMD_FLAG_ENCRYPTION_DISABLE = (1 << 7),
99 CMD_FLAG_ENCRYPTION_CHANGEPW = (1 << 8),
100 CMD_FLAG_FORCE_FULL_BACKUP = (1 << 9),
101 CMD_FLAG_CLOUD_ENABLE = (1 << 10),
102 CMD_FLAG_CLOUD_DISABLE = (1 << 11),
103 CMD_FLAG_RESTORE_SKIP_APPS = (1 << 12)
104};
105
106static int backup_domain_changed = 0;
107
108static void notify_cb(const char *notification, void *userdata)
109{
110 if (strlen(notification) == 0) {
111 return;
112 }
113 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
114 PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
115 quit_flag++;
116 } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
117 backup_domain_changed = 1;
118 } else {
119 PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
120 }
121}
122
123static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
124{
125 if (!afc || !data || !size) {
126 return;
127 }
128
129 char **fileinfo = NULL;
130 uint32_t fsize = 0;
131
132 afc_get_file_info(afc, filename, &fileinfo);
133 if (!fileinfo) {
134 return;
135 }
136 int i;
137 for (i = 0; fileinfo[i]; i+=2) {
138 if (!strcmp(fileinfo[i], "st_size")) {
139 fsize = atol(fileinfo[i+1]);
140 break;
141 }
142 }
143 afc_dictionary_free(fileinfo);
144
145 if (fsize == 0) {
146 return;
147 }
148
149 uint64_t f = 0;
150 afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
151 if (!f) {
152 return;
153 }
154 char *buf = (char*)malloc((uint32_t)fsize);
155 uint32_t done = 0;
156 while (done < fsize) {
157 uint32_t bread = 0;
158 afc_file_read(afc, f, buf+done, 65536, &bread);
159 if (bread > 0) {
160 done += bread;
161 } else {
162 break;
163 }
164 }
165 if (done == fsize) {
166 *size = fsize;
167 *data = buf;
168 } else {
169 free(buf);
170 }
171 afc_file_close(afc, f);
172}
173
174static int __mkdir(const char* path, int mode)
175{
176#ifdef WIN32
177 return mkdir(path);
178#else
179 return mkdir(path, mode);
180#endif
181}
182
183static int mkdir_with_parents(const char *dir, int mode)
184{
185 if (!dir) return -1;
186 if (__mkdir(dir, mode) == 0) {
187 return 0;
188 }
189 if (errno == EEXIST) return 0;
190 int res;
191 char *parent = strdup(dir);
192 char *parentdir = dirname(parent);
193 if (parentdir) {
194 res = mkdir_with_parents(parentdir, mode);
195 } else {
196 res = -1;
197 }
198 free(parent);
199 if (res == 0) {
200 mkdir_with_parents(dir, mode);
201 }
202 return res;
203}
204
205#ifdef WIN32
206static int win32err_to_errno(int err_value)
207{
208 switch (err_value) {
209 case ERROR_FILE_NOT_FOUND:
210 return ENOENT;
211 case ERROR_ALREADY_EXISTS:
212 return EEXIST;
213 default:
214 return EFAULT;
215 }
216}
217#endif
218
219static int remove_file(const char* path)
220{
221 int e = 0;
222#ifdef WIN32
223 if (!DeleteFile(path)) {
224 e = win32err_to_errno(GetLastError());
225 }
226#else
227 if (remove(path) < 0) {
228 e = errno;
229 }
230#endif
231 return e;
232}
233
234static int remove_directory(const char* path)
235{
236 int e = 0;
237#ifdef WIN32
238 if (!RemoveDirectory(path)) {
239 e = win32err_to_errno(GetLastError());
240 }
241#else
242 if (remove(path) < 0) {
243 e = errno;
244 }
245#endif
246 return e;
247}
248
249struct entry {
250 char *name;
251 struct entry *next;
252};
253
254static void scan_directory(const char *path, struct entry **files, struct entry **directories)
255{
256 DIR* cur_dir = opendir(path);
257 if (cur_dir) {
258 struct dirent* ep;
259 while ((ep = readdir(cur_dir))) {
260 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
261 continue;
262 }
263 char *fpath = string_build_path(path, ep->d_name, NULL);
264 if (fpath) {
265#ifdef HAVE_DIRENT_D_TYPE
266 if (ep->d_type & DT_DIR) {
267#else
268 struct stat st;
269 if (stat(fpath, &st) != 0) return;
270 if (S_ISDIR(st.st_mode)) {
271#endif
272 struct entry *ent = malloc(sizeof(struct entry));
273 if (!ent) return;
274 ent->name = fpath;
275 ent->next = *directories;
276 *directories = ent;
277 scan_directory(fpath, files, directories);
278 fpath = NULL;
279 } else {
280 struct entry *ent = malloc(sizeof(struct entry));
281 if (!ent) return;
282 ent->name = fpath;
283 ent->next = *files;
284 *files = ent;
285 fpath = NULL;
286 }
287 }
288 }
289 closedir(cur_dir);
290 }
291}
292
293static int rmdir_recursive(const char* path)
294{
295 int res = 0;
296 struct entry *files = NULL;
297 struct entry *directories = NULL;
298 struct entry *ent;
299
300 ent = malloc(sizeof(struct entry));
301 if (!ent) return ENOMEM;
302 ent->name = strdup(path);
303 ent->next = NULL;
304 directories = ent;
305
306 scan_directory(path, &files, &directories);
307
308 ent = files;
309 while (ent) {
310 struct entry *del = ent;
311 res = remove_file(ent->name);
312 free(ent->name);
313 ent = ent->next;
314 free(del);
315 }
316 ent = directories;
317 while (ent) {
318 struct entry *del = ent;
319 res = remove_directory(ent->name);
320 free(ent->name);
321 ent = ent->next;
322 free(del);
323 }
324
325 return res;
326}
327
328static char* get_uuid()
329{
330 const char *chars = "ABCDEF0123456789";
331 int i = 0;
332 char *uuid = (char*)malloc(sizeof(char) * 33);
333
334 srand(time(NULL));
335
336 for (i = 0; i < 32; i++) {
337 uuid[i] = chars[rand() % 16];
338 }
339
340 uuid[32] = '\0';
341
342 return uuid;
343}
344
345static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t device, afc_client_t afc)
346{
347 /* gather data from lockdown */
348 plist_t value_node = NULL;
349 plist_t root_node = NULL;
350 plist_t itunes_settings = NULL;
351 plist_t min_itunes_version = NULL;
352 char *udid_uppercase = NULL;
353
354 lockdownd_client_t lockdown = NULL;
355 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
356 return NULL;
357 }
358
359 plist_t ret = plist_new_dict();
360
361 /* get basic device information in one go */
362 lockdownd_get_value(lockdown, NULL, NULL, &root_node);
363
364 /* get iTunes settings */
365 lockdownd_get_value(lockdown, "com.apple.iTunes", NULL, &itunes_settings);
366
367 /* get minimum iTunes version */
368 lockdownd_get_value(lockdown, "com.apple.mobile.iTunes", "MinITunesVersion", &min_itunes_version);
369
370 lockdownd_client_free(lockdown);
371
372 /* get a list of installed user applications */
373 plist_t app_dict = plist_new_dict();
374 plist_t installed_apps = plist_new_array();
375 instproxy_client_t ip = NULL;
376 if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {
377 plist_t client_opts = instproxy_client_options_new();
378 instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
379 instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL);
380
381 plist_t apps = NULL;
382 instproxy_browse(ip, client_opts, &apps);
383
384 sbservices_client_t sbs = NULL;
385 if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
386 printf("Couldn't establish sbservices connection. Continuing anyway.\n");
387 }
388
389 if (apps && (plist_get_node_type(apps) == PLIST_ARRAY)) {
390 uint32_t app_count = plist_array_get_size(apps);
391 uint32_t i;
392 for (i = 0; i < app_count; i++) {
393 plist_t app_entry = plist_array_get_item(apps, i);
394 plist_t bundle_id = plist_dict_get_item(app_entry, "CFBundleIdentifier");
395 if (bundle_id) {
396 char *bundle_id_str = NULL;
397 plist_array_append_item(installed_apps, plist_copy(bundle_id));
398
399 plist_get_string_val(bundle_id, &bundle_id_str);
400 plist_t sinf = plist_dict_get_item(app_entry, "ApplicationSINF");
401 plist_t meta = plist_dict_get_item(app_entry, "iTunesMetadata");
402 if (sinf && meta) {
403 plist_t adict = plist_new_dict();
404 plist_dict_set_item(adict, "ApplicationSINF", plist_copy(sinf));
405 if (sbs) {
406 char *pngdata = NULL;
407 uint64_t pngsize = 0;
408 sbservices_get_icon_pngdata(sbs, bundle_id_str, &pngdata, &pngsize);
409 if (pngdata) {
410 plist_dict_set_item(adict, "PlaceholderIcon", plist_new_data(pngdata, pngsize));
411 free(pngdata);
412 }
413 }
414 plist_dict_set_item(adict, "iTunesMetadata", plist_copy(meta));
415 plist_dict_set_item(app_dict, bundle_id_str, adict);
416 }
417 free(bundle_id_str);
418 }
419 }
420 }
421 plist_free(apps);
422
423 if (sbs) {
424 sbservices_client_free(sbs);
425 }
426
427 instproxy_client_options_free(client_opts);
428
429 instproxy_client_free(ip);
430 }
431
432 /* Applications */
433 plist_dict_set_item(ret, "Applications", app_dict);
434
435 /* set fields we understand */
436 value_node = plist_dict_get_item(root_node, "BuildVersion");
437 plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
438
439 value_node = plist_dict_get_item(root_node, "DeviceName");
440 plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
441 plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
442
443 char *uuid = get_uuid();
444 plist_dict_set_item(ret, "GUID", plist_new_string(uuid));
445 free(uuid);
446
447 value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
448 if (value_node)
449 plist_dict_set_item(ret, "ICCID", plist_copy(value_node));
450
451 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
452 if (value_node)
453 plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
454
455 /* Installed Applications */
456 plist_dict_set_item(ret, "Installed Applications", installed_apps);
457
458 plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
459
460 value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
461 if (value_node)
462 plist_dict_set_item(ret, "MEID", plist_copy(value_node));
463
464 value_node = plist_dict_get_item(root_node, "PhoneNumber");
465 if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
466 plist_dict_set_item(ret, "Phone Number", plist_copy(value_node));
467 }
468
469 /* FIXME Product Name */
470
471 value_node = plist_dict_get_item(root_node, "ProductType");
472 plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
473
474 value_node = plist_dict_get_item(root_node, "ProductVersion");
475 plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
476
477 value_node = plist_dict_get_item(root_node, "SerialNumber");
478 plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
479
480 /* FIXME Sync Settings? */
481
482 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
483 plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
484
485 plist_dict_set_item(ret, "Target Type", plist_new_string("Device"));
486
487 /* uppercase */
488 udid_uppercase = string_toupper((char*)udid);
489 plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
490 free(udid_uppercase);
491
492 char *data_buf = NULL;
493 uint64_t data_size = 0;
494 mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
495 if (data_buf) {
496 plist_dict_set_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
497 free(data_buf);
498 }
499
500 plist_t files = plist_new_dict();
501 const char *itunesfiles[] = {
502 "ApertureAlbumPrefs",
503 "IC-Info.sidb",
504 "IC-Info.sidv",
505 "PhotosFolderAlbums",
506 "PhotosFolderName",
507 "PhotosFolderPrefs",
508 "VoiceMemos.plist",
509 "iPhotoAlbumPrefs",
510 "iTunesApplicationIDs",
511 "iTunesPrefs",
512 "iTunesPrefs.plist",
513 NULL
514 };
515 int i = 0;
516 for (i = 0; itunesfiles[i]; i++) {
517 data_buf = NULL;
518 data_size = 0;
519 char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
520 strcpy(fname, "/iTunes_Control/iTunes/");
521 strcat(fname, itunesfiles[i]);
522 mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
523 free(fname);
524 if (data_buf) {
525 plist_dict_set_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
526 free(data_buf);
527 }
528 }
529 plist_dict_set_item(ret, "iTunes Files", files);
530
531 plist_dict_set_item(ret, "iTunes Settings", itunes_settings ? plist_copy(itunes_settings) : plist_new_dict());
532
533 /* since we usually don't have iTunes, let's get the minimum required iTunes version from the device */
534 if (min_itunes_version) {
535 plist_dict_set_item(ret, "iTunes Version", plist_copy(min_itunes_version));
536 } else {
537 plist_dict_set_item(ret, "iTunes Version", plist_new_string("10.0.1"));
538 }
539
540 plist_free(itunes_settings);
541 plist_free(min_itunes_version);
542 plist_free(root_node);
543
544 return ret;
545}
546
547static int write_restore_applications(plist_t info_plist, afc_client_t afc)
548{
549 int res = -1;
550 uint64_t restore_applications_file = 0;
551 char * applications_plist_xml = NULL;
552 uint32_t applications_plist_xml_length = 0;
553
554 plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
555 if (!applications_plist) {
556 printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
557 return 0;
558 }
559 plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
560 if (!applications_plist_xml) {
561 printf("Error preparing RestoreApplications.plist\n");
562 goto leave;
563 }
564
565 afc_error_t afc_err = 0;
566 afc_err = afc_make_directory(afc, "/iTunesRestore");
567 if (afc_err != AFC_E_SUCCESS) {
568 printf("Error creating directory /iTunesRestore, error code %d\n", afc_err);
569 goto leave;
570 }
571
572 afc_err = afc_file_open(afc, "/iTunesRestore/RestoreApplications.plist", AFC_FOPEN_WR, &restore_applications_file);
573 if (afc_err != AFC_E_SUCCESS || !restore_applications_file) {
574 printf("Error creating /iTunesRestore/RestoreApplications.plist, error code %d\n", afc_err);
575 goto leave;
576 }
577
578 uint32_t bytes_written = 0;
579 afc_err = afc_file_write(afc, restore_applications_file, applications_plist_xml, applications_plist_xml_length, &bytes_written);
580 if (afc_err != AFC_E_SUCCESS || bytes_written != applications_plist_xml_length) {
581 printf("Error writing /iTunesRestore/RestoreApplications.plist, error code %d, wrote %u of %u bytes\n", afc_err, bytes_written, applications_plist_xml_length);
582 goto leave;
583 }
584
585 afc_err = afc_file_close(afc, restore_applications_file);
586 restore_applications_file = 0;
587 if (afc_err != AFC_E_SUCCESS) {
588 goto leave;
589 }
590 /* success */
591 res = 0;
592
593leave:
594 free(applications_plist_xml);
595
596 if (restore_applications_file) {
597 afc_file_close(afc, restore_applications_file);
598 restore_applications_file = 0;
599 }
600
601 return res;
602}
603
604static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
605{
606 int ret = 0;
607 plist_t status_plist = NULL;
608 char *file_path = string_build_path(path, udid, "Status.plist", NULL);
609
610 plist_read_from_file(file_path, &status_plist, NULL);
611 free(file_path);
612 if (!status_plist) {
613 printf("Could not read Status.plist!\n");
614 return ret;
615 }
616 plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
617 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
618 char* sval = NULL;
619 plist_get_string_val(node, &sval);
620 if (sval) {
621 ret = (strcmp(sval, matches) == 0) ? 1 : 0;
622 free(sval);
623 }
624 } else {
625 printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
626 }
627 plist_free(status_plist);
628 return ret;
629}
630
631static void do_post_notification(idevice_t device, const char *notification)
632{
633 lockdownd_service_descriptor_t service = NULL;
634 np_client_t np;
635
636 lockdownd_client_t lockdown = NULL;
637
638 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
639 return;
640 }
641
642 lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
643 if (ldret == LOCKDOWN_E_SUCCESS) {
644 np_client_new(device, service, &np);
645 if (np) {
646 np_post_notification(np, notification);
647 np_client_free(np);
648 }
649 } else {
650 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
651 }
652
653 if (service) {
654 lockdownd_service_descriptor_free(service);
655 service = NULL;
656 }
657 lockdownd_client_free(lockdown);
658}
659
660static void print_progress_real(double progress, int flush)
661{
662 int i = 0;
663 PRINT_VERBOSE(1, "\r[");
664 for(i = 0; i < 50; i++) {
665 if(i < progress / 2) {
666 PRINT_VERBOSE(1, "=");
667 } else {
668 PRINT_VERBOSE(1, " ");
669 }
670 }
671 PRINT_VERBOSE(1, "] %3.0f%%", progress);
672
673 if (flush > 0) {
674 fflush(stdout);
675 if (progress == 100)
676 PRINT_VERBOSE(1, "\n");
677 }
678}
679
680static void print_progress(uint64_t current, uint64_t total)
681{
682 char *format_size = NULL;
683 double progress = ((double)current/(double)total)*100;
684 if (progress < 0)
685 return;
686
687 if (progress > 100)
688 progress = 100;
689
690 print_progress_real((double)progress, 0);
691
692 format_size = string_format_size(current);
693 PRINT_VERBOSE(1, " (%s", format_size);
694 free(format_size);
695 format_size = string_format_size(total);
696 PRINT_VERBOSE(1, "/%s) ", format_size);
697 free(format_size);
698
699 fflush(stdout);
700 if (progress == 100)
701 PRINT_VERBOSE(1, "\n");
702}
703
704static double overall_progress = 0;
705
706static void mb2_set_overall_progress(double progress)
707{
708 if (progress > 0.0)
709 overall_progress = progress;
710}
711
712static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
713{
714 plist_t node = NULL;
715 double progress = 0.0;
716
717 if (!strcmp(identifier, "DLMessageDownloadFiles")) {
718 node = plist_array_get_item(message, 3);
719 } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
720 node = plist_array_get_item(message, 2);
721 } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
722 node = plist_array_get_item(message, 3);
723 } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
724 node = plist_array_get_item(message, 3);
725 }
726
727 if (node != NULL) {
728 plist_get_real_val(node, &progress);
729 mb2_set_overall_progress(progress);
730 }
731}
732
733static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
734{
735 if (!status_dict) return;
736 plist_t filedict = plist_new_dict();
737 plist_dict_set_item(filedict, "DLFileErrorString", plist_new_string(error_message));
738 plist_dict_set_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
739 plist_dict_set_item(status_dict, path, filedict);
740}
741
742static int errno_to_device_error(int errno_value)
743{
744 switch (errno_value) {
745 case ENOENT:
746 return -6;
747 case EEXIST:
748 return -7;
749 case ENOTDIR:
750 return -8;
751 case EISDIR:
752 return -9;
753 case ELOOP:
754 return -10;
755 case EIO:
756 return -11;
757 case ENOSPC:
758 return -15;
759 default:
760 return -1;
761 }
762}
763
764static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
765{
766 uint32_t nlen = 0;
767 uint32_t pathlen = strlen(path);
768 uint32_t bytes = 0;
769 char *localfile = string_build_path(backup_dir, path, NULL);
770 char buf[32768];
771#ifdef WIN32
772 struct _stati64 fst;
773#else
774 struct stat fst;
775#endif
776
777 FILE *f = NULL;
778 uint32_t slen = 0;
779 int errcode = -1;
780 int result = -1;
781 uint32_t length;
782#ifdef WIN32
783 uint64_t total;
784 uint64_t sent;
785#else
786 off_t total;
787 off_t sent;
788#endif
789
790 mobilebackup2_error_t err;
791
792 /* send path length */
793 nlen = htobe32(pathlen);
794 err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
795 if (err != MOBILEBACKUP2_E_SUCCESS) {
796 goto leave_proto_err;
797 }
798 if (bytes != (uint32_t)sizeof(nlen)) {
799 err = MOBILEBACKUP2_E_MUX_ERROR;
800 goto leave_proto_err;
801 }
802
803 /* send path */
804 err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
805 if (err != MOBILEBACKUP2_E_SUCCESS) {
806 goto leave_proto_err;
807 }
808 if (bytes != pathlen) {
809 err = MOBILEBACKUP2_E_MUX_ERROR;
810 goto leave_proto_err;
811 }
812
813#ifdef WIN32
814 if (_stati64(localfile, &fst) < 0)
815#else
816 if (stat(localfile, &fst) < 0)
817#endif
818 {
819 if (errno != ENOENT)
820 printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
821 errcode = errno;
822 goto leave;
823 }
824
825 total = fst.st_size;
826
827 char *format_size = string_format_size(total);
828 PRINT_VERBOSE(1, "Sending '%s' (%s)\n", path, format_size);
829 free(format_size);
830
831 if (total == 0) {
832 errcode = 0;
833 goto leave;
834 }
835
836 f = fopen(localfile, "rb");
837 if (!f) {
838 printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
839 errcode = errno;
840 goto leave;
841 }
842
843 sent = 0;
844 do {
845 length = ((total-sent) < (long long)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
846 /* send data size (file size + 1) */
847 nlen = htobe32(length+1);
848 memcpy(buf, &nlen, sizeof(nlen));
849 buf[4] = CODE_FILE_DATA;
850 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
851 if (err != MOBILEBACKUP2_E_SUCCESS) {
852 goto leave_proto_err;
853 }
854 if (bytes != 5) {
855 goto leave_proto_err;
856 }
857
858 /* send file contents */
859 size_t r = fread(buf, 1, sizeof(buf), f);
860 if (r <= 0) {
861 printf("%s: read error\n", __func__);
862 errcode = errno;
863 goto leave;
864 }
865 err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
866 if (err != MOBILEBACKUP2_E_SUCCESS) {
867 goto leave_proto_err;
868 }
869 if (bytes != (uint32_t)r) {
870 printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
871 goto leave_proto_err;
872 }
873 sent += r;
874 } while (sent < total);
875 fclose(f);
876 f = NULL;
877 errcode = 0;
878
879leave:
880 if (errcode == 0) {
881 result = 0;
882 nlen = 1;
883 nlen = htobe32(nlen);
884 memcpy(buf, &nlen, 4);
885 buf[4] = CODE_SUCCESS;
886 mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
887 } else {
888 if (!*errplist) {
889 *errplist = plist_new_dict();
890 }
891 char *errdesc = strerror(errcode);
892 mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
893
894 length = strlen(errdesc);
895 nlen = htobe32(length+1);
896 memcpy(buf, &nlen, 4);
897 buf[4] = CODE_ERROR_LOCAL;
898 slen = 5;
899 memcpy(buf+slen, errdesc, length);
900 slen += length;
901 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
902 if (err != MOBILEBACKUP2_E_SUCCESS) {
903 printf("could not send message\n");
904 }
905 if (bytes != slen) {
906 printf("could only send %d from %d\n", bytes, slen);
907 }
908 }
909
910leave_proto_err:
911 if (f)
912 fclose(f);
913 free(localfile);
914 return result;
915}
916
917static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
918{
919 uint32_t cnt;
920 uint32_t i = 0;
921 uint32_t sent;
922 plist_t errplist = NULL;
923
924 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
925
926 plist_t files = plist_array_get_item(message, 1);
927 cnt = plist_array_get_size(files);
928
929 for (i = 0; i < cnt; i++) {
930 plist_t val = plist_array_get_item(files, i);
931 if (plist_get_node_type(val) != PLIST_STRING) {
932 continue;
933 }
934 char *str = NULL;
935 plist_get_string_val(val, &str);
936 if (!str)
937 continue;
938
939 if (mb2_handle_send_file(mobilebackup2, backup_dir, str, &errplist) < 0) {
940 free(str);
941 //printf("Error when sending file '%s' to device\n", str);
942 // TODO: perhaps we can continue, we've got a multi status response?!
943 break;
944 }
945 free(str);
946 }
947
948 /* send terminating 0 dword */
949 uint32_t zero = 0;
950 mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
951
952 if (!errplist) {
953 plist_t emptydict = plist_new_dict();
954 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, emptydict);
955 plist_free(emptydict);
956 } else {
957 mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
958 plist_free(errplist);
959 }
960}
961
962static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** filename)
963{
964 uint32_t nlen = 0;
965 uint32_t rlen = 0;
966
967 do {
968 nlen = 0;
969 rlen = 0;
970 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
971 nlen = be32toh(nlen);
972
973 if ((nlen == 0) && (rlen == 4)) {
974 // a zero length means no more files to receive
975 return 0;
976 }
977 if (rlen == 0) {
978 // device needs more time, waiting...
979 continue;
980 }
981 if (nlen > 4096) {
982 // filename length is too large
983 printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
984 return 0;
985 }
986
987 if (*filename != NULL) {
988 free(*filename);
989 *filename = NULL;
990 }
991
992 *filename = (char*)malloc(nlen+1);
993
994 rlen = 0;
995 mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
996 if (rlen != nlen) {
997 printf("ERROR: %s: could not read filename\n", __func__);
998 return 0;
999 }
1000
1001 char* p = *filename;
1002 p[rlen] = 0;
1003
1004 break;
1005 } while(1 && !quit_flag);
1006
1007 return nlen;
1008}
1009
1010static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1011{
1012 uint64_t backup_real_size = 0;
1013 uint64_t backup_total_size = 0;
1014 uint32_t blocksize;
1015 uint32_t bdone;
1016 uint32_t rlen;
1017 uint32_t nlen = 0;
1018 uint32_t r;
1019 char buf[32768];
1020 char *fname = NULL;
1021 char *dname = NULL;
1022 char *bname = NULL;
1023 char code = 0;
1024 char last_code = 0;
1025 plist_t node = NULL;
1026 FILE *f = NULL;
1027 unsigned int file_count = 0;
1028 int errcode = 0;
1029 char *errdesc = NULL;
1030
1031 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
1032
1033 node = plist_array_get_item(message, 3);
1034 if (plist_get_node_type(node) == PLIST_UINT) {
1035 plist_get_uint_val(node, &backup_total_size);
1036 }
1037 if (backup_total_size > 0) {
1038 PRINT_VERBOSE(1, "Receiving files\n");
1039 }
1040
1041 do {
1042 if (quit_flag)
1043 break;
1044
1045 nlen = mb2_receive_filename(mobilebackup2, &dname);
1046 if (nlen == 0) {
1047 break;
1048 }
1049
1050 nlen = mb2_receive_filename(mobilebackup2, &fname);
1051 if (!nlen) {
1052 break;
1053 }
1054
1055 if (bname != NULL) {
1056 free(bname);
1057 bname = NULL;
1058 }
1059
1060 bname = string_build_path(backup_dir, fname, NULL);
1061
1062 if (fname != NULL) {
1063 free(fname);
1064 fname = NULL;
1065 }
1066
1067 r = 0;
1068 nlen = 0;
1069 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1070 if (r != 4) {
1071 printf("ERROR: %s: could not receive code length!\n", __func__);
1072 break;
1073 }
1074 nlen = be32toh(nlen);
1075
1076 last_code = code;
1077 code = 0;
1078
1079 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1080 if (r != 1) {
1081 printf("ERROR: %s: could not receive code!\n", __func__);
1082 break;
1083 }
1084
1085 /* TODO remove this */
1086 if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
1087 PRINT_VERBOSE(1, "Found new flag %02x\n", code);
1088 }
1089
1090 remove_file(bname);
1091 f = fopen(bname, "wb");
1092 while (f && (code == CODE_FILE_DATA)) {
1093 blocksize = nlen-1;
1094 bdone = 0;
1095 rlen = 0;
1096 while (bdone < blocksize) {
1097 if ((blocksize - bdone) < sizeof(buf)) {
1098 rlen = blocksize - bdone;
1099 } else {
1100 rlen = sizeof(buf);
1101 }
1102 mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
1103 if ((int)r <= 0) {
1104 break;
1105 }
1106 fwrite(buf, 1, r, f);
1107 bdone += r;
1108 }
1109 if (bdone == blocksize) {
1110 backup_real_size += blocksize;
1111 }
1112 if (backup_total_size > 0) {
1113 print_progress(backup_real_size, backup_total_size);
1114 }
1115 if (quit_flag)
1116 break;
1117 nlen = 0;
1118 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1119 nlen = be32toh(nlen);
1120 if (nlen > 0) {
1121 last_code = code;
1122 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1123 } else {
1124 break;
1125 }
1126 }
1127 if (f) {
1128 fclose(f);
1129 file_count++;
1130 } else {
1131 errcode = errno_to_device_error(errno);
1132 errdesc = strerror(errno);
1133 printf("Error opening '%s' for writing: %s\n", bname, errdesc);
1134 break;
1135 }
1136 if (nlen == 0) {
1137 break;
1138 }
1139
1140 /* check if an error message was received */
1141 if (code == CODE_ERROR_REMOTE) {
1142 /* error message */
1143 char *msg = (char*)malloc(nlen);
1144 mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
1145 msg[r] = 0;
1146 /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
1147 if (last_code != CODE_FILE_DATA) {
1148 fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
1149 }
1150 free(msg);
1151 }
1152 } while (1);
1153
1154 if (fname != NULL)
1155 free(fname);
1156
1157 /* if there are leftovers to read, finish up cleanly */
1158 if ((int)nlen-1 > 0) {
1159 PRINT_VERBOSE(1, "\nDiscarding current data hunk.\n");
1160 fname = (char*)malloc(nlen-1);
1161 mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
1162 free(fname);
1163 remove_file(bname);
1164 }
1165
1166 /* clean up */
1167 if (bname != NULL)
1168 free(bname);
1169
1170 if (dname != NULL)
1171 free(dname);
1172
1173 plist_t empty_plist = plist_new_dict();
1174 mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_plist);
1175 plist_free(empty_plist);
1176
1177 return file_count;
1178}
1179
1180static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1181{
1182 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1183
1184 plist_t node = plist_array_get_item(message, 1);
1185 char *str = NULL;
1186 if (plist_get_node_type(node) == PLIST_STRING) {
1187 plist_get_string_val(node, &str);
1188 }
1189 if (!str) {
1190 printf("ERROR: Malformed DLContentsOfDirectory message\n");
1191 // TODO error handling
1192 return;
1193 }
1194
1195 char *path = string_build_path(backup_dir, str, NULL);
1196 free(str);
1197
1198 plist_t dirlist = plist_new_dict();
1199
1200 DIR* cur_dir = opendir(path);
1201 if (cur_dir) {
1202 struct dirent* ep;
1203 while ((ep = readdir(cur_dir))) {
1204 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1205 continue;
1206 }
1207 char *fpath = string_build_path(path, ep->d_name, NULL);
1208 if (fpath) {
1209 plist_t fdict = plist_new_dict();
1210 struct stat st;
1211 stat(fpath, &st);
1212 const char *ftype = "DLFileTypeUnknown";
1213 if (S_ISDIR(st.st_mode)) {
1214 ftype = "DLFileTypeDirectory";
1215 } else if (S_ISREG(st.st_mode)) {
1216 ftype = "DLFileTypeRegular";
1217 }
1218 plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
1219 plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
1220 plist_dict_set_item(fdict, "DLFileModificationDate",
1221 plist_new_date(st.st_mtime - MAC_EPOCH, 0));
1222
1223 plist_dict_set_item(dirlist, ep->d_name, fdict);
1224 free(fpath);
1225 }
1226 }
1227 closedir(cur_dir);
1228 }
1229 free(path);
1230
1231 /* TODO error handling */
1232 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
1233 plist_free(dirlist);
1234 if (err != MOBILEBACKUP2_E_SUCCESS) {
1235 printf("Could not send status response, error %d\n", err);
1236 }
1237}
1238
1239static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1240{
1241 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1242
1243 plist_t dir = plist_array_get_item(message, 1);
1244 char *str = NULL;
1245 int errcode = 0;
1246 char *errdesc = NULL;
1247 plist_get_string_val(dir, &str);
1248
1249 char *newpath = string_build_path(backup_dir, str, NULL);
1250 free(str);
1251
1252 if (mkdir_with_parents(newpath, 0755) < 0) {
1253 errdesc = strerror(errno);
1254 if (errno != EEXIST) {
1255 printf("mkdir: %s (%d)\n", errdesc, errno);
1256 }
1257 errcode = errno_to_device_error(errno);
1258 }
1259 free(newpath);
1260 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
1261 if (err != MOBILEBACKUP2_E_SUCCESS) {
1262 printf("Could not send status response, error %d\n", err);
1263 }
1264}
1265
1266static void mb2_copy_file_by_path(const char *src, const char *dst)
1267{
1268 FILE *from, *to;
1269 char buf[BUFSIZ];
1270 size_t length;
1271
1272 /* open source file */
1273 if ((from = fopen(src, "rb")) == NULL) {
1274 printf("Cannot open source path '%s'.\n", src);
1275 return;
1276 }
1277
1278 /* open destination file */
1279 if ((to = fopen(dst, "wb")) == NULL) {
1280 printf("Cannot open destination file '%s'.\n", dst);
1281 fclose(from);
1282 return;
1283 }
1284
1285 /* copy the file */
1286 while ((length = fread(buf, 1, BUFSIZ, from)) != 0) {
1287 fwrite(buf, 1, length, to);
1288 }
1289
1290 if(fclose(from) == EOF) {
1291 printf("Error closing source file.\n");
1292 }
1293
1294 if(fclose(to) == EOF) {
1295 printf("Error closing destination file.\n");
1296 }
1297}
1298
1299static void mb2_copy_directory_by_path(const char *src, const char *dst)
1300{
1301 if (!src || !dst) {
1302 return;
1303 }
1304
1305 struct stat st;
1306
1307 /* if src does not exist */
1308 if ((stat(src, &st) < 0) || !S_ISDIR(st.st_mode)) {
1309 printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
1310 return;
1311 }
1312
1313 /* if dst directory does not exist */
1314 if ((stat(dst, &st) < 0) || !S_ISDIR(st.st_mode)) {
1315 /* create it */
1316 if (mkdir_with_parents(dst, 0755) < 0) {
1317 printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
1318 return;
1319 }
1320 }
1321
1322 /* loop over src directory contents */
1323 DIR *cur_dir = opendir(src);
1324 if (cur_dir) {
1325 struct dirent* ep;
1326 while ((ep = readdir(cur_dir))) {
1327 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1328 continue;
1329 }
1330 char *srcpath = string_build_path(src, ep->d_name, NULL);
1331 char *dstpath = string_build_path(dst, ep->d_name, NULL);
1332 if (srcpath && dstpath) {
1333 /* copy file */
1334 mb2_copy_file_by_path(srcpath, dstpath);
1335 }
1336
1337 if (srcpath)
1338 free(srcpath);
1339 if (dstpath)
1340 free(dstpath);
1341 }
1342 closedir(cur_dir);
1343 }
1344}
1345
1346#ifdef WIN32
1347#define BS_CC '\b'
1348#define my_getch getch
1349#else
1350#define BS_CC 0x7f
1351static int my_getch(void)
1352{
1353 struct termios oldt, newt;
1354 int ch;
1355 tcgetattr(STDIN_FILENO, &oldt);
1356 newt = oldt;
1357 newt.c_lflag &= ~(ICANON | ECHO);
1358 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1359 ch = getchar();
1360 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
1361 return ch;
1362}
1363#endif
1364
1365static void get_hidden_input(char *buf, int maxlen)
1366{
1367 int pwlen = 0;
1368 int c;
1369
1370 while ((c = my_getch())) {
1371 if ((c == '\r') || (c == '\n')) {
1372 break;
1373 }
1374 if (isprint(c)) {
1375 if (pwlen < maxlen-1)
1376 buf[pwlen++] = c;
1377 fputc('*', stderr);
1378 } else if (c == BS_CC) {
1379 if (pwlen > 0) {
1380 fputs("\b \b", stderr);
1381 pwlen--;
1382 }
1383 }
1384 }
1385 buf[pwlen] = 0;
1386}
1387
1388static char* ask_for_password(const char* msg, int type_again)
1389{
1390 char pwbuf[256];
1391
1392 fprintf(stderr, "%s: ", msg);
1393 fflush(stderr);
1394 get_hidden_input(pwbuf, 256);
1395 fputc('\n', stderr);
1396
1397 if (type_again) {
1398 char pwrep[256];
1399
1400 fprintf(stderr, "%s (repeat): ", msg);
1401 fflush(stderr);
1402 get_hidden_input(pwrep, 256);
1403 fputc('\n', stderr);
1404
1405 if (strcmp(pwbuf, pwrep) != 0) {
1406 printf("ERROR: passwords don't match\n");
1407 return NULL;
1408 }
1409 }
1410 return strdup(pwbuf);
1411}
1412
1413/**
1414 * signal handler function for cleaning up properly
1415 */
1416static void clean_exit(int sig)
1417{
1418 fprintf(stderr, "Exiting...\n");
1419 quit_flag++;
1420}
1421
1422static void print_usage(int argc, char **argv, int is_error)
1423{
1424 char *name = strrchr(argv[0], '/');
1425 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
1426 fprintf(is_error ? stderr : stdout,
1427 "\n"
1428 "Create or restore backup in/from the specified directory.\n"
1429 "\n"
1430 "CMD:\n"
1431 " backup create backup for the device\n"
1432 " --full force full backup from device.\n"
1433 " restore restore last backup to the device\n"
1434 " --system restore system files, too.\n"
1435 " --no-reboot do NOT reboot the device when done (default: yes).\n"
1436 " --copy create a copy of backup folder before restoring.\n"
1437 " --settings restore device settings from the backup.\n"
1438 " --remove remove items which are not being restored\n"
1439 " --skip-apps do not trigger re-installation of apps after restore\n"
1440 " --password PWD supply the password for the encrypted source backup\n"
1441 " info show details about last completed backup of device\n"
1442 " list list files of last completed backup in CSV format\n"
1443 " unback unpack a completed backup in DIRECTORY/_unback_/\n"
1444 " encryption on|off [PWD] enable or disable backup encryption\n"
1445 " changepw [OLD NEW] change backup password on target device\n"
1446 " cloud on|off enable or disable cloud use (requires iCloud account)\n"
1447 "\n"
1448 "NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n"
1449 "be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n"
1450 "See man page for further details.\n"
1451 "\n"
1452 "OPTIONS:\n"
1453 " -u, --udid UDID target specific device by UDID\n"
1454 " -s, --source UDID use backup data from device specified by UDID\n"
1455 " -n, --network connect to network device\n"
1456 " -i, --interactive request passwords interactively\n"
1457 " -d, --debug enable communication debugging\n"
1458 " -h, --help prints usage information\n"
1459 " -v, --version prints version information\n"
1460 "\n"
1461 "Homepage: <" PACKAGE_URL ">\n"
1462 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
1463 );
1464}
1465
1466#define DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF))
1467
1468int main(int argc, char *argv[])
1469{
1470 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
1471 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1472 int i = 0;
1473 char* udid = NULL;
1474 char* source_udid = NULL;
1475 int use_network = 0;
1476 lockdownd_service_descriptor_t service = NULL;
1477 int cmd = -1;
1478 int cmd_flags = 0;
1479 int is_full_backup = 0;
1480 int result_code = -1;
1481 char* backup_directory = NULL;
1482 int interactive_mode = 0;
1483 char* backup_password = NULL;
1484 char* newpw = NULL;
1485 struct stat st;
1486 plist_t node_tmp = NULL;
1487 plist_t info_plist = NULL;
1488 plist_t opts = NULL;
1489
1490 idevice_t device = NULL;
1491 afc_client_t afc = NULL;
1492 np_client_t np = NULL;
1493 lockdownd_client_t lockdown = NULL;
1494 mobilebackup2_client_t mobilebackup2 = NULL;
1495 mobilebackup2_error_t err;
1496 uint64_t lockfile = 0;
1497
1498#define OPT_SYSTEM 1
1499#define OPT_REBOOT 2
1500#define OPT_NO_REBOOT 3
1501#define OPT_COPY 4
1502#define OPT_SETTINGS 5
1503#define OPT_REMOVE 6
1504#define OPT_SKIP_APPS 7
1505#define OPT_PASSWORD 8
1506#define OPT_FULL 9
1507
1508 int c = 0;
1509 const struct option longopts[] = {
1510 { "debug", no_argument, NULL, 'd' },
1511 { "help", no_argument, NULL, 'h' },
1512 { "udid", required_argument, NULL, 'u' },
1513 { "source", required_argument, NULL, 's' },
1514 { "interactive", no_argument, NULL, 'i' },
1515 { "network", no_argument, NULL, 'n' },
1516 { "version", no_argument, NULL, 'v' },
1517 // command options:
1518 { "system", no_argument, NULL, OPT_SYSTEM },
1519 { "reboot", no_argument, NULL, OPT_REBOOT },
1520 { "no-reboot", no_argument, NULL, OPT_NO_REBOOT },
1521 { "copy", no_argument, NULL, OPT_COPY },
1522 { "settings", no_argument, NULL, OPT_SETTINGS },
1523 { "remove", no_argument, NULL, OPT_REMOVE },
1524 { "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
1525 { "password", required_argument, NULL, OPT_PASSWORD },
1526 { "full", no_argument, NULL, OPT_FULL },
1527 { NULL, 0, NULL, 0}
1528 };
1529
1530 /* we need to exit cleanly on running backups and restores or we cause havok */
1531 signal(SIGINT, clean_exit);
1532 signal(SIGTERM, clean_exit);
1533#ifndef WIN32
1534 signal(SIGQUIT, clean_exit);
1535 signal(SIGPIPE, SIG_IGN);
1536#endif
1537
1538 /* parse cmdline args */
1539 while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) {
1540 switch (c) {
1541 case 'd':
1542 idevice_set_debug_level(1);
1543 break;
1544 case 'u':
1545 if (!*optarg) {
1546 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
1547 print_usage(argc, argv, 1);
1548 return 2;
1549 }
1550 udid = strdup(optarg);
1551 break;
1552 case 's':
1553 if (!*optarg) {
1554 fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n");
1555 print_usage(argc, argv, 1);
1556 return 2;
1557 }
1558 source_udid = strdup(optarg);
1559 break;
1560 case 'i':
1561 interactive_mode = 1;
1562 break;
1563 case 'n':
1564 use_network = 1;
1565 break;
1566 case 'h':
1567 print_usage(argc, argv, 0);
1568 return 0;
1569 case 'v':
1570 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
1571 return 0;
1572 case OPT_SYSTEM:
1573 cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
1574 break;
1575 case OPT_REBOOT:
1576 cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
1577 break;
1578 case OPT_NO_REBOOT:
1579 cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
1580 break;
1581 case OPT_COPY:
1582 cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
1583 break;
1584 case OPT_SETTINGS:
1585 cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
1586 break;
1587 case OPT_REMOVE:
1588 cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
1589 break;
1590 case OPT_SKIP_APPS:
1591 cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
1592 break;
1593 case OPT_PASSWORD:
1594 free(backup_password);
1595 backup_password = strdup(optarg);
1596 break;
1597 case OPT_FULL:
1598 cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
1599 break;
1600 default:
1601 print_usage(argc, argv, 1);
1602 return 2;
1603 }
1604 }
1605 argc -= optind;
1606 argv += optind;
1607
1608 if (!argv[0]) {
1609 fprintf(stderr, "ERROR: No command specified.\n");
1610 print_usage(argc+optind, argv-optind, 1);
1611 return 2;
1612 }
1613
1614 if (!strcmp(argv[0], "backup")) {
1615 cmd = CMD_BACKUP;
1616 }
1617 else if (!strcmp(argv[0], "restore")) {
1618 cmd = CMD_RESTORE;
1619 }
1620 else if (!strcmp(argv[0], "cloud")) {
1621 cmd = CMD_CLOUD;
1622 i = 1;
1623 if (!argv[i]) {
1624 fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n");
1625 print_usage(argc+optind, argv-optind, 1);
1626 return 2;
1627 }
1628 if (!strcmp(argv[i], "on")) {
1629 cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
1630 } else if (!strcmp(argv[i], "off")) {
1631 cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
1632 } else {
1633 fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
1634 print_usage(argc+optind, argv-optind, 1);
1635 return 2;
1636 }
1637 }
1638 else if (!strcmp(argv[0], "info")) {
1639 cmd = CMD_INFO;
1640 verbose = 0;
1641 }
1642 else if (!strcmp(argv[0], "list")) {
1643 cmd = CMD_LIST;
1644 verbose = 0;
1645 }
1646 else if (!strcmp(argv[0], "unback")) {
1647 cmd = CMD_UNBACK;
1648 }
1649 else if (!strcmp(argv[0], "encryption")) {
1650 cmd = CMD_CHANGEPW;
1651 i = 1;
1652 if (!argv[i]) {
1653 fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n");
1654 print_usage(argc+optind, argv-optind, 1);
1655 return 2;
1656 }
1657 if (!strcmp(argv[i], "on")) {
1658 cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
1659 } else if (!strcmp(argv[i], "off")) {
1660 cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
1661 } else {
1662 fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
1663 print_usage(argc+optind, argv-optind, 1);
1664 return 2;
1665 }
1666 // check if a password was given on the command line
1667 free(newpw);
1668 newpw = NULL;
1669 free(backup_password);
1670 backup_password = NULL;
1671 i++;
1672 if (argv[i]) {
1673 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
1674 newpw = strdup(argv[i]);
1675 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
1676 backup_password = strdup(argv[i]);
1677 }
1678 }
1679 }
1680 else if (!strcmp(argv[0], "changepw")) {
1681 cmd = CMD_CHANGEPW;
1682 cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
1683 // check if passwords were given on command line
1684 free(newpw);
1685 newpw = NULL;
1686 free(backup_password);
1687 backup_password = NULL;
1688 i = 1;
1689 if (argv[i]) {
1690 backup_password = strdup(argv[i]);
1691 i++;
1692 if (!argv[i]) {
1693 fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n");
1694 print_usage(argc+optind, argv-optind, 1);
1695 return 2;
1696 }
1697 newpw = strdup(argv[i]);
1698 }
1699 }
1700
1701 i++;
1702 if (argv[i]) {
1703 backup_directory = argv[i];
1704 }
1705
1706 /* verify options */
1707 if (cmd == -1) {
1708 fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]);
1709 print_usage(argc+optind, argv-optind, 1);
1710 return 2;
1711 }
1712
1713 if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
1714 backup_directory = (char*)".this_folder_is_not_present_on_purpose";
1715 } else {
1716 if (backup_directory == NULL) {
1717 fprintf(stderr, "ERROR: No target backup directory specified.\n");
1718 print_usage(argc+optind, argv-optind, 1);
1719 return 2;
1720 }
1721
1722 /* verify if passed backup directory exists */
1723 if (stat(backup_directory, &st) != 0) {
1724 fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
1725 return -1;
1726 }
1727 }
1728
1729 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1730 if (ret != IDEVICE_E_SUCCESS) {
1731 if (udid) {
1732 printf("No device found with udid %s.\n", udid);
1733 } else {
1734 printf("No device found.\n");
1735 }
1736 return -1;
1737 }
1738
1739 if (!udid) {
1740 idevice_get_udid(device, &udid);
1741 }
1742
1743 if (!source_udid) {
1744 source_udid = strdup(udid);
1745 }
1746
1747 uint8_t is_encrypted = 0;
1748 char *info_path = NULL;
1749 if (cmd == CMD_CHANGEPW) {
1750 if (!interactive_mode) {
1751 if (!newpw) {
1752 newpw = getenv("BACKUP_PASSWORD_NEW");
1753 if (newpw) {
1754 newpw = strdup(newpw);
1755 }
1756 }
1757 if (!backup_password) {
1758 backup_password = getenv("BACKUP_PASSWORD");
1759 if (backup_password) {
1760 backup_password = strdup(backup_password);
1761 }
1762 }
1763 }
1764 if (!interactive_mode && !backup_password && !newpw) {
1765 idevice_free(device);
1766 printf("ERROR: Can't get password input in non-interactive mode. Either pass password(s) on the command line, or enable interactive mode with -i or --interactive.\n");
1767 return -1;
1768 }
1769 } else if (cmd != CMD_CLOUD) {
1770 /* backup directory must contain an Info.plist */
1771 info_path = string_build_path(backup_directory, source_udid, "Info.plist", NULL);
1772 if (cmd == CMD_RESTORE || cmd == CMD_UNBACK) {
1773 if (stat(info_path, &st) != 0) {
1774 idevice_free(device);
1775 free(info_path);
1776 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
1777 return -1;
1778 }
1779 char* manifest_path = string_build_path(backup_directory, source_udid, "Manifest.plist", NULL);
1780 if (stat(manifest_path, &st) != 0) {
1781 free(info_path);
1782 }
1783 plist_t manifest_plist = NULL;
1784 plist_read_from_file(manifest_path, &manifest_plist, NULL);
1785 if (!manifest_plist) {
1786 idevice_free(device);
1787 free(info_path);
1788 free(manifest_path);
1789 printf("ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
1790 return -1;
1791 }
1792 node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
1793 if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
1794 plist_get_bool_val(node_tmp, &is_encrypted);
1795 }
1796 plist_free(manifest_plist);
1797 free(manifest_path);
1798 }
1799 PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
1800 }
1801
1802 if (cmd != CMD_CLOUD && is_encrypted) {
1803 PRINT_VERBOSE(1, "This is an encrypted backup.\n");
1804 if (backup_password == NULL) {
1805 backup_password = getenv("BACKUP_PASSWORD");
1806 if (backup_password) {
1807 backup_password = strdup(backup_password);
1808 }
1809 }
1810 if (backup_password == NULL) {
1811 if (interactive_mode) {
1812 backup_password = ask_for_password("Enter backup password", 0);
1813 }
1814 if (!backup_password || (strlen(backup_password) == 0)) {
1815 if (backup_password) {
1816 free(backup_password);
1817 }
1818 idevice_free(device);
1819 if (cmd == CMD_RESTORE) {
1820 printf("ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
1821 } else if (cmd == CMD_UNBACK) {
1822 printf("ERROR: a backup password is required to unback an encrypted backup. Cannot continue.\n");
1823 }
1824 return -1;
1825 }
1826 }
1827 }
1828
1829 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
1830 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
1831 idevice_free(device);
1832 return -1;
1833 }
1834
1835 uint8_t willEncrypt = 0;
1836 node_tmp = NULL;
1837 lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
1838 if (node_tmp) {
1839 if (plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
1840 plist_get_bool_val(node_tmp, &willEncrypt);
1841 }
1842 plist_free(node_tmp);
1843 node_tmp = NULL;
1844 }
1845
1846 /* get ProductVersion */
1847 char *product_version = NULL;
1848 int device_version = 0;
1849 node_tmp = NULL;
1850 lockdownd_get_value(lockdown, NULL, "ProductVersion", &node_tmp);
1851 if (node_tmp) {
1852 if (plist_get_node_type(node_tmp) == PLIST_STRING) {
1853 plist_get_string_val(node_tmp, &product_version);
1854 }
1855 plist_free(node_tmp);
1856 node_tmp = NULL;
1857 }
1858 if (product_version) {
1859 int vers[3] = { 0, 0, 0 };
1860 if (sscanf(product_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
1861 device_version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
1862 }
1863 }
1864
1865 /* start notification_proxy */
1866 ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
1867 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
1868 np_client_new(device, service, &np);
1869 np_set_notify_callback(np, notify_cb, NULL);
1870 const char *noties[5] = {
1871 NP_SYNC_CANCEL_REQUEST,
1872 NP_SYNC_SUSPEND_REQUEST,
1873 NP_SYNC_RESUME_REQUEST,
1874 NP_BACKUP_DOMAIN_CHANGED,
1875 NULL
1876 };
1877 np_observe_notifications(np, noties);
1878 } else {
1879 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
1880 cmd = CMD_LEAVE;
1881 goto checkpoint;
1882 }
1883 if (service) {
1884 lockdownd_service_descriptor_free(service);
1885 service = NULL;
1886 }
1887
1888 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
1889 /* start AFC, we need this for the lock file */
1890 ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
1891 if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
1892 afc_client_new(device, service, &afc);
1893 } else {
1894 printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
1895 cmd = CMD_LEAVE;
1896 goto checkpoint;
1897 }
1898 }
1899
1900 if (service) {
1901 lockdownd_service_descriptor_free(service);
1902 service = NULL;
1903 }
1904
1905 /* start mobilebackup service and retrieve port */
1906 ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
1907 lockdownd_client_free(lockdown);
1908 lockdown = NULL;
1909 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
1910 PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
1911 mobilebackup2_client_new(device, service, &mobilebackup2);
1912
1913 if (service) {
1914 lockdownd_service_descriptor_free(service);
1915 service = NULL;
1916 }
1917
1918 /* send Hello message */
1919 double local_versions[2] = {2.0, 2.1};
1920 double remote_version = 0.0;
1921 err = mobilebackup2_version_exchange(mobilebackup2, local_versions, 2, &remote_version);
1922 if (err != MOBILEBACKUP2_E_SUCCESS) {
1923 printf("Could not perform backup protocol version exchange, error code %d\n", err);
1924 cmd = CMD_LEAVE;
1925 goto checkpoint;
1926 }
1927
1928 PRINT_VERBOSE(1, "Negotiated Protocol Version %.1f\n", remote_version);
1929
1930 /* check abort conditions */
1931 if (quit_flag > 0) {
1932 PRINT_VERBOSE(1, "Aborting as requested by user...\n");
1933 cmd = CMD_LEAVE;
1934 goto checkpoint;
1935 }
1936
1937 /* verify existing Info.plist */
1938 if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
1939 PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
1940 plist_read_from_file(info_path, &info_plist, NULL);
1941
1942 if (!info_plist) {
1943 printf("Could not read Info.plist\n");
1944 is_full_backup = 1;
1945 }
1946 } else {
1947 if (cmd == CMD_RESTORE) {
1948 printf("Aborting restore. Info.plist is missing.\n");
1949 cmd = CMD_LEAVE;
1950 } else {
1951 is_full_backup = 1;
1952 }
1953 }
1954
1955 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
1956 do_post_notification(device, NP_SYNC_WILL_START);
1957 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
1958 }
1959 if (lockfile) {
1960 afc_error_t aerr;
1961 do_post_notification(device, NP_SYNC_LOCK_REQUEST);
1962 for (i = 0; i < LOCK_ATTEMPTS; i++) {
1963 aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
1964 if (aerr == AFC_E_SUCCESS) {
1965 do_post_notification(device, NP_SYNC_DID_START);
1966 break;
1967 }
1968 if (aerr == AFC_E_OP_WOULD_BLOCK) {
1969 usleep(LOCK_WAIT);
1970 continue;
1971 }
1972
1973 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
1974 afc_file_close(afc, lockfile);
1975 lockfile = 0;
1976 cmd = CMD_LEAVE;
1977 }
1978 if (i == LOCK_ATTEMPTS) {
1979 fprintf(stderr, "ERROR: timeout while locking for sync\n");
1980 afc_file_close(afc, lockfile);
1981 lockfile = 0;
1982 cmd = CMD_LEAVE;
1983 }
1984 }
1985
1986checkpoint:
1987
1988 switch(cmd) {
1989 case CMD_CLOUD:
1990 opts = plist_new_dict();
1991 plist_dict_set_item(opts, "CloudBackupState", plist_new_bool(cmd_flags & CMD_FLAG_CLOUD_ENABLE ? 1: 0));
1992 err = mobilebackup2_send_request(mobilebackup2, "EnableCloudBackup", udid, source_udid, opts);
1993 plist_free(opts);
1994 opts = NULL;
1995 if (err != MOBILEBACKUP2_E_SUCCESS) {
1996 printf("Error setting cloud backup state on device, error code %d\n", err);
1997 cmd = CMD_LEAVE;
1998 }
1999 break;
2000 case CMD_BACKUP:
2001 PRINT_VERBOSE(1, "Starting backup...\n");
2002
2003 /* make sure backup device sub-directory exists */
2004 char* devbackupdir = string_build_path(backup_directory, source_udid, NULL);
2005 __mkdir(devbackupdir, 0755);
2006 free(devbackupdir);
2007
2008 if (strcmp(source_udid, udid) != 0) {
2009 /* handle different source backup directory */
2010 // make sure target backup device sub-directory exists
2011 devbackupdir = string_build_path(backup_directory, udid, NULL);
2012 __mkdir(devbackupdir, 0755);
2013 free(devbackupdir);
2014
2015 // use Info.plist path in target backup folder */
2016 free(info_path);
2017 info_path = string_build_path(backup_directory, udid, "Info.plist", NULL);
2018 }
2019
2020 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
2021 /* TODO: verify battery on AC enough battery remaining */
2022
2023 /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
2024 if (info_plist) {
2025 plist_free(info_plist);
2026 info_plist = NULL;
2027 }
2028 info_plist = mobilebackup_factory_info_plist_new(udid, device, afc);
2029 if (!info_plist) {
2030 fprintf(stderr, "Failed to generate Info.plist - aborting\n");
2031 cmd = CMD_LEAVE;
2032 }
2033 remove_file(info_path);
2034 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
2035 free(info_path);
2036
2037 plist_free(info_plist);
2038 info_plist = NULL;
2039
2040 if (cmd_flags & CMD_FLAG_FORCE_FULL_BACKUP) {
2041 PRINT_VERBOSE(1, "Enforcing full backup from device.\n");
2042 opts = plist_new_dict();
2043 plist_dict_set_item(opts, "ForceFullBackup", plist_new_bool(1));
2044 }
2045 /* request backup from device with manifest from last backup */
2046 if (willEncrypt) {
2047 PRINT_VERBOSE(1, "Backup will be encrypted.\n");
2048 } else {
2049 PRINT_VERBOSE(1, "Backup will be unencrypted.\n");
2050 }
2051 PRINT_VERBOSE(1, "Requesting backup from device...\n");
2052 err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, opts);
2053 if (opts)
2054 plist_free(opts);
2055 if (err == MOBILEBACKUP2_E_SUCCESS) {
2056 if (is_full_backup) {
2057 PRINT_VERBOSE(1, "Full backup mode.\n");
2058 } else {
2059 PRINT_VERBOSE(1, "Incremental backup mode.\n");
2060 }
2061 } else {
2062 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2063 printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
2064 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2065 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
2066 } else {
2067 printf("ERROR: Could not start backup process: unspecified error occurred\n");
2068 }
2069 cmd = CMD_LEAVE;
2070 }
2071 break;
2072 case CMD_RESTORE:
2073 /* TODO: verify battery on AC enough battery remaining */
2074
2075 /* verify if Status.plist says we read from an successful backup */
2076 if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
2077 printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
2078 cmd = CMD_LEAVE;
2079 break;
2080 }
2081
2082 PRINT_VERBOSE(1, "Starting Restore...\n");
2083
2084 opts = plist_new_dict();
2085 plist_dict_set_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
2086 PRINT_VERBOSE(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
2087 if (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT)
2088 plist_dict_set_item(opts, "RestoreShouldReboot", plist_new_bool(0));
2089 PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT ? "No":"Yes"));
2090 if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
2091 plist_dict_set_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
2092 PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
2093 plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
2094 PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
2095 plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS));
2096 PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
2097 if (backup_password != NULL) {
2098 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2099 }
2100 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2101
2102 if (cmd_flags & CMD_FLAG_RESTORE_SKIP_APPS) {
2103 PRINT_VERBOSE(1, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
2104 } else {
2105 /* Write /iTunesRestore/RestoreApplications.plist so that the device will start
2106 * restoring applications once the rest of the restore process is finished */
2107 if (write_restore_applications(info_plist, afc) < 0) {
2108 cmd = CMD_LEAVE;
2109 break;
2110 }
2111 PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
2112 }
2113
2114 /* Start restore */
2115 err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
2116 plist_free(opts);
2117 if (err != MOBILEBACKUP2_E_SUCCESS) {
2118 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2119 printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
2120 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2121 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
2122 } else {
2123 printf("ERROR: Could not start restore process: unspecified error occurred\n");
2124 }
2125 cmd = CMD_LEAVE;
2126 }
2127 break;
2128 case CMD_INFO:
2129 PRINT_VERBOSE(1, "Requesting backup info from device...\n");
2130 err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
2131 if (err != MOBILEBACKUP2_E_SUCCESS) {
2132 printf("Error requesting backup info from device, error code %d\n", err);
2133 cmd = CMD_LEAVE;
2134 }
2135 break;
2136 case CMD_LIST:
2137 PRINT_VERBOSE(1, "Requesting backup list from device...\n");
2138 err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
2139 if (err != MOBILEBACKUP2_E_SUCCESS) {
2140 printf("Error requesting backup list from device, error code %d\n", err);
2141 cmd = CMD_LEAVE;
2142 }
2143 break;
2144 case CMD_UNBACK:
2145 PRINT_VERBOSE(1, "Starting to unpack backup...\n");
2146 if (backup_password != NULL) {
2147 opts = plist_new_dict();
2148 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2149 }
2150 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2151 err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, source_udid, opts);
2152 if (backup_password !=NULL) {
2153 plist_free(opts);
2154 }
2155 if (err != MOBILEBACKUP2_E_SUCCESS) {
2156 printf("Error requesting unback operation from device, error code %d\n", err);
2157 cmd = CMD_LEAVE;
2158 }
2159 break;
2160 case CMD_CHANGEPW:
2161 opts = plist_new_dict();
2162 plist_dict_set_item(opts, "TargetIdentifier", plist_new_string(udid));
2163 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2164 if (!willEncrypt) {
2165 if (!newpw) {
2166 newpw = getenv("BACKUP_PASSWORD");
2167 if (newpw) {
2168 newpw = strdup(newpw);
2169 }
2170 }
2171 if (!newpw) {
2172 newpw = ask_for_password("Enter new backup password", 1);
2173 }
2174 if (!newpw) {
2175 printf("No backup password given. Aborting.\n");
2176 }
2177 } else {
2178 printf("ERROR: Backup encryption is already enabled. Aborting.\n");
2179 cmd = CMD_LEAVE;
2180 if (newpw) {
2181 free(newpw);
2182 newpw = NULL;
2183 }
2184 }
2185 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2186 if (willEncrypt) {
2187 if (!backup_password) {
2188 backup_password = getenv("BACKUP_PASSWORD");
2189 if (backup_password) {
2190 backup_password = strdup(backup_password);
2191 }
2192 }
2193 if (!backup_password) {
2194 backup_password = ask_for_password("Enter current backup password", 0);
2195 }
2196 } else {
2197 printf("ERROR: Backup encryption is not enabled. Aborting.\n");
2198 cmd = CMD_LEAVE;
2199 if (backup_password) {
2200 free(backup_password);
2201 backup_password = NULL;
2202 }
2203 }
2204 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2205 if (willEncrypt) {
2206 if (!backup_password) {
2207 backup_password = ask_for_password("Enter old backup password", 0);
2208 newpw = ask_for_password("Enter new backup password", 1);
2209 }
2210 } else {
2211 printf("ERROR: Backup encryption is not enabled so can't change password. Aborting.\n");
2212 cmd = CMD_LEAVE;
2213 if (newpw) {
2214 free(newpw);
2215 newpw = NULL;
2216 }
2217 if (backup_password) {
2218 free(backup_password);
2219 backup_password = NULL;
2220 }
2221 }
2222 }
2223 if (newpw) {
2224 plist_dict_set_item(opts, "NewPassword", plist_new_string(newpw));
2225 }
2226 if (backup_password) {
2227 plist_dict_set_item(opts, "OldPassword", plist_new_string(backup_password));
2228 }
2229 if (newpw || backup_password) {
2230 mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
2231 uint8_t passcode_hint = 0;
2232 if (device_version >= DEVICE_VERSION(13,0,0)) {
2233 diagnostics_relay_client_t diag = NULL;
2234 if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2235 plist_t dict = NULL;
2236 plist_t keys = plist_new_array();
2237 plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
2238 if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2239 plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
2240 plist_get_bool_val(node, &passcode_hint);
2241 }
2242 plist_free(keys);
2243 plist_free(dict);
2244 diagnostics_relay_goodbye(diag);
2245 diagnostics_relay_client_free(diag);
2246 }
2247 }
2248 if (passcode_hint) {
2249 if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2250 PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
2251 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2252 PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
2253 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2254 PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
2255 }
2256 }
2257 /*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2258 int retr = 10;
2259 while ((retr-- >= 0) && !backup_domain_changed) {
2260 sleep(1);
2261 }
2262 }*/
2263 } else {
2264 cmd = CMD_LEAVE;
2265 }
2266 plist_free(opts);
2267 break;
2268 default:
2269 break;
2270 }
2271
2272 if (cmd != CMD_LEAVE) {
2273 /* reset operation success status */
2274 int operation_ok = 0;
2275 plist_t message = NULL;
2276
2277 mobilebackup2_error_t mberr;
2278 char *dlmsg = NULL;
2279 int file_count = 0;
2280 int errcode = 0;
2281 const char *errdesc = NULL;
2282 int progress_finished = 0;
2283
2284 /* process series of DLMessage* operations */
2285 do {
2286 free(dlmsg);
2287 dlmsg = NULL;
2288 mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
2289 if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) {
2290 PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n");
2291 goto files_out;
2292 } else if (mberr != MOBILEBACKUP2_E_SUCCESS) {
2293 PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr);
2294 quit_flag++;
2295 goto files_out;
2296 }
2297
2298 if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
2299 /* device wants to download files from the computer */
2300 mb2_set_overall_progress_from_message(message, dlmsg);
2301 mb2_handle_send_files(mobilebackup2, message, backup_directory);
2302 } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
2303 /* device wants to send files to the computer */
2304 mb2_set_overall_progress_from_message(message, dlmsg);
2305 file_count += mb2_handle_receive_files(mobilebackup2, message, backup_directory);
2306 } else if (!strcmp(dlmsg, "DLMessageGetFreeDiskSpace")) {
2307 /* device wants to know how much disk space is available on the computer */
2308 uint64_t freespace = 0;
2309 int res = -1;
2310#ifdef WIN32
2311 if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
2312 res = 0;
2313 }
2314#else
2315 struct statvfs fs;
2316 memset(&fs, '\0', sizeof(fs));
2317 res = statvfs(backup_directory, &fs);
2318 if (res == 0) {
2319 freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize;
2320 }
2321#endif
2322 plist_t freespace_item = plist_new_uint(freespace);
2323 mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
2324 plist_free(freespace_item);
2325 } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
2326 /* device wants to purge disk space on the host - not supported */
2327 plist_t empty_dict = plist_new_dict();
2328 err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict);
2329 plist_free(empty_dict);
2330 } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
2331 /* list directory contents */
2332 mb2_handle_list_directory(mobilebackup2, message, backup_directory);
2333 } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
2334 /* make a directory */
2335 mb2_handle_make_directory(mobilebackup2, message, backup_directory);
2336 } else if (!strcmp(dlmsg, "DLMessageMoveFiles") || !strcmp(dlmsg, "DLMessageMoveItems")) {
2337 /* perform a series of rename operations */
2338 mb2_set_overall_progress_from_message(message, dlmsg);
2339 plist_t moves = plist_array_get_item(message, 1);
2340 uint32_t cnt = plist_dict_get_size(moves);
2341 PRINT_VERBOSE(1, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
2342 plist_dict_iter iter = NULL;
2343 plist_dict_new_iter(moves, &iter);
2344 errcode = 0;
2345 errdesc = NULL;
2346 if (iter) {
2347 char *key = NULL;
2348 plist_t val = NULL;
2349 do {
2350 plist_dict_next_item(moves, iter, &key, &val);
2351 if (key && (plist_get_node_type(val) == PLIST_STRING)) {
2352 char *str = NULL;
2353 plist_get_string_val(val, &str);
2354 if (str) {
2355 char *newpath = string_build_path(backup_directory, str, NULL);
2356 free(str);
2357 char *oldpath = string_build_path(backup_directory, key, NULL);
2358
2359 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode))
2360 rmdir_recursive(newpath);
2361 else
2362 remove_file(newpath);
2363 if (rename(oldpath, newpath) < 0) {
2364 printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
2365 errcode = errno_to_device_error(errno);
2366 errdesc = strerror(errno);
2367 break;
2368 }
2369 free(oldpath);
2370 free(newpath);
2371 }
2372 free(key);
2373 key = NULL;
2374 }
2375 } while (val);
2376 free(iter);
2377 } else {
2378 errcode = -1;
2379 errdesc = "Could not create dict iterator";
2380 printf("Could not create dict iterator\n");
2381 }
2382 plist_t empty_dict = plist_new_dict();
2383 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2384 plist_free(empty_dict);
2385 if (err != MOBILEBACKUP2_E_SUCCESS) {
2386 printf("Could not send status response, error %d\n", err);
2387 }
2388 } else if (!strcmp(dlmsg, "DLMessageRemoveFiles") || !strcmp(dlmsg, "DLMessageRemoveItems")) {
2389 mb2_set_overall_progress_from_message(message, dlmsg);
2390 plist_t removes = plist_array_get_item(message, 1);
2391 uint32_t cnt = plist_array_get_size(removes);
2392 PRINT_VERBOSE(1, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
2393 uint32_t ii = 0;
2394 errcode = 0;
2395 errdesc = NULL;
2396 for (ii = 0; ii < cnt; ii++) {
2397 plist_t val = plist_array_get_item(removes, ii);
2398 if (plist_get_node_type(val) == PLIST_STRING) {
2399 char *str = NULL;
2400 plist_get_string_val(val, &str);
2401 if (str) {
2402 const char *checkfile = strchr(str, '/');
2403 int suppress_warning = 0;
2404 if (checkfile) {
2405 if (strcmp(checkfile+1, "Manifest.mbdx") == 0) {
2406 suppress_warning = 1;
2407 }
2408 }
2409 char *newpath = string_build_path(backup_directory, str, NULL);
2410 free(str);
2411 int res = 0;
2412 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2413 res = rmdir_recursive(newpath);
2414 } else {
2415 res = remove_file(newpath);
2416 }
2417 if (res != 0 && res != ENOENT) {
2418 if (!suppress_warning)
2419 printf("Could not remove '%s': %s (%d)\n", newpath, strerror(res), res);
2420 errcode = errno_to_device_error(res);
2421 errdesc = strerror(res);
2422 }
2423 free(newpath);
2424 }
2425 }
2426 }
2427 plist_t empty_dict = plist_new_dict();
2428 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2429 plist_free(empty_dict);
2430 if (err != MOBILEBACKUP2_E_SUCCESS) {
2431 printf("Could not send status response, error %d\n", err);
2432 }
2433 } else if (!strcmp(dlmsg, "DLMessageCopyItem")) {
2434 plist_t srcpath = plist_array_get_item(message, 1);
2435 plist_t dstpath = plist_array_get_item(message, 2);
2436 errcode = 0;
2437 errdesc = NULL;
2438 if ((plist_get_node_type(srcpath) == PLIST_STRING) && (plist_get_node_type(dstpath) == PLIST_STRING)) {
2439 char *src = NULL;
2440 char *dst = NULL;
2441 plist_get_string_val(srcpath, &src);
2442 plist_get_string_val(dstpath, &dst);
2443 if (src && dst) {
2444 char *oldpath = string_build_path(backup_directory, src, NULL);
2445 char *newpath = string_build_path(backup_directory, dst, NULL);
2446
2447 PRINT_VERBOSE(1, "Copying '%s' to '%s'\n", src, dst);
2448
2449 /* check that src exists */
2450 if ((stat(oldpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2451 mb2_copy_directory_by_path(oldpath, newpath);
2452 } else if ((stat(oldpath, &st) == 0) && S_ISREG(st.st_mode)) {
2453 mb2_copy_file_by_path(oldpath, newpath);
2454 }
2455
2456 free(newpath);
2457 free(oldpath);
2458 }
2459 free(src);
2460 free(dst);
2461 }
2462 plist_t empty_dict = plist_new_dict();
2463 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2464 plist_free(empty_dict);
2465 if (err != MOBILEBACKUP2_E_SUCCESS) {
2466 printf("Could not send status response, error %d\n", err);
2467 }
2468 } else if (!strcmp(dlmsg, "DLMessageDisconnect")) {
2469 break;
2470 } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
2471 node_tmp = plist_array_get_item(message, 1);
2472 if (plist_get_node_type(node_tmp) != PLIST_DICT) {
2473 printf("Unknown message received!\n");
2474 }
2475 plist_t nn;
2476 int error_code = -1;
2477 nn = plist_dict_get_item(node_tmp, "ErrorCode");
2478 if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
2479 uint64_t ec = 0;
2480 plist_get_uint_val(nn, &ec);
2481 error_code = (uint32_t)ec;
2482 if (error_code == 0) {
2483 operation_ok = 1;
2484 result_code = 0;
2485 } else {
2486 result_code = -error_code;
2487 }
2488 }
2489 nn = plist_dict_get_item(node_tmp, "ErrorDescription");
2490 char *str = NULL;
2491 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2492 plist_get_string_val(nn, &str);
2493 }
2494 if (error_code != 0) {
2495 if (str) {
2496 printf("ErrorCode %d: %s\n", error_code, str);
2497 } else {
2498 printf("ErrorCode %d: (Unknown)\n", error_code);
2499 }
2500 }
2501 if (str) {
2502 free(str);
2503 }
2504 nn = plist_dict_get_item(node_tmp, "Content");
2505 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2506 str = NULL;
2507 plist_get_string_val(nn, &str);
2508 PRINT_VERBOSE(1, "Content:\n");
2509 printf("%s", str);
2510 free(str);
2511 }
2512 break;
2513 }
2514
2515 /* print status */
2516 if ((overall_progress > 0) && !progress_finished) {
2517 if (overall_progress >= 100.0F) {
2518 progress_finished = 1;
2519 }
2520 print_progress_real(overall_progress, 0);
2521 PRINT_VERBOSE(1, " Finished\n");
2522 }
2523
2524files_out:
2525 plist_free(message);
2526 message = NULL;
2527 free(dlmsg);
2528 dlmsg = NULL;
2529
2530 if (quit_flag > 0) {
2531 /* need to cancel the backup here */
2532 //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
2533
2534 /* remove any atomic Manifest.plist.tmp */
2535
2536 /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
2537 if (stat(manifest_path, &st) == 0)
2538 remove(manifest_path);*/
2539 break;
2540 }
2541 } while (1);
2542
2543 plist_free(message);
2544 free(dlmsg);
2545
2546 /* report operation status to user */
2547 switch (cmd) {
2548 case CMD_CLOUD:
2549 if (cmd_flags & CMD_FLAG_CLOUD_ENABLE) {
2550 if (operation_ok) {
2551 PRINT_VERBOSE(1, "Cloud backup has been enabled successfully.\n");
2552 } else {
2553 PRINT_VERBOSE(1, "Could not enable cloud backup.\n");
2554 }
2555 } else if (cmd_flags & CMD_FLAG_CLOUD_DISABLE) {
2556 if (operation_ok) {
2557 PRINT_VERBOSE(1, "Cloud backup has been disabled successfully.\n");
2558 } else {
2559 PRINT_VERBOSE(1, "Could not disable cloud backup.\n");
2560 }
2561 }
2562 break;
2563 case CMD_BACKUP:
2564 PRINT_VERBOSE(1, "Received %d files from device.\n", file_count);
2565 if (operation_ok && mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
2566 PRINT_VERBOSE(1, "Backup Successful.\n");
2567 } else {
2568 if (quit_flag) {
2569 PRINT_VERBOSE(1, "Backup Aborted.\n");
2570 } else {
2571 PRINT_VERBOSE(1, "Backup Failed (Error Code %d).\n", -result_code);
2572 }
2573 }
2574 break;
2575 case CMD_UNBACK:
2576 if (quit_flag) {
2577 PRINT_VERBOSE(1, "Unback Aborted.\n");
2578 } else {
2579 PRINT_VERBOSE(1, "The files can now be found in the \"_unback_\" directory.\n");
2580 PRINT_VERBOSE(1, "Unback Successful.\n");
2581 }
2582 break;
2583 case CMD_CHANGEPW:
2584 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2585 if (operation_ok) {
2586 PRINT_VERBOSE(1, "Backup encryption has been enabled successfully.\n");
2587 } else {
2588 PRINT_VERBOSE(1, "Could not enable backup encryption.\n");
2589 }
2590 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2591 if (operation_ok) {
2592 PRINT_VERBOSE(1, "Backup encryption has been disabled successfully.\n");
2593 } else {
2594 PRINT_VERBOSE(1, "Could not disable backup encryption.\n");
2595 }
2596 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2597 if (operation_ok) {
2598 PRINT_VERBOSE(1, "Backup encryption password has been changed successfully.\n");
2599 } else {
2600 PRINT_VERBOSE(1, "Could not change backup encryption password.\n");
2601 }
2602 }
2603 break;
2604 case CMD_RESTORE:
2605 if (operation_ok) {
2606 if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
2607 PRINT_VERBOSE(1, "The device should reboot now.\n");
2608 PRINT_VERBOSE(1, "Restore Successful.\n");
2609 } else {
2610 afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist");
2611 afc_remove_path(afc, "/iTunesRestore");
2612 if (quit_flag) {
2613 PRINT_VERBOSE(1, "Restore Aborted.\n");
2614 } else {
2615 PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
2616 }
2617 }
2618 break;
2619 case CMD_INFO:
2620 case CMD_LIST:
2621 case CMD_LEAVE:
2622 default:
2623 if (quit_flag) {
2624 PRINT_VERBOSE(1, "Operation Aborted.\n");
2625 } else if (cmd == CMD_LEAVE) {
2626 PRINT_VERBOSE(1, "Operation Failed.\n");
2627 } else {
2628 PRINT_VERBOSE(1, "Operation Successful.\n");
2629 }
2630 break;
2631 }
2632 }
2633 if (lockfile) {
2634 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
2635 afc_file_close(afc, lockfile);
2636 lockfile = 0;
2637 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE)
2638 do_post_notification(device, NP_SYNC_DID_FINISH);
2639 }
2640 } else {
2641 printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
2642 lockdownd_client_free(lockdown);
2643 lockdown = NULL;
2644 }
2645
2646 if (lockdown) {
2647 lockdownd_client_free(lockdown);
2648 lockdown = NULL;
2649 }
2650
2651 if (mobilebackup2) {
2652 mobilebackup2_client_free(mobilebackup2);
2653 mobilebackup2 = NULL;
2654 }
2655
2656 if (afc) {
2657 afc_client_free(afc);
2658 afc = NULL;
2659 }
2660
2661 if (np) {
2662 np_client_free(np);
2663 np = NULL;
2664 }
2665
2666 idevice_free(device);
2667 device = NULL;
2668
2669 if (backup_password) {
2670 free(backup_password);
2671 }
2672
2673 if (udid) {
2674 free(udid);
2675 udid = NULL;
2676 }
2677 if (source_udid) {
2678 free(source_udid);
2679 source_udid = NULL;
2680 }
2681
2682 return result_code;
2683}
2684