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