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