summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/idevicebackup2.c')
-rw-r--r--tools/idevicebackup2.c1674
1 files changed, 1674 insertions, 0 deletions
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
new file mode 100644
index 0000000..b9187e9
--- /dev/null
+++ b/tools/idevicebackup2.c
@@ -0,0 +1,1674 @@
1/*
2 * idevicebackup2.c
3 * Command line interface to use the device's backup and restore service
4 *
5 * Copyright (c) 2009-2010 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2010 Nikias Bassen All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include <stdio.h>
24#include <string.h>
25#include <errno.h>
26#include <stdlib.h>
27#include <signal.h>
28#include <glib.h>
29#include <glib/gstdio.h>
30#include <gcrypt.h>
31#include <unistd.h>
32
33#include <libimobiledevice/libimobiledevice.h>
34#include <libimobiledevice/lockdown.h>
35#include <libimobiledevice/mobilebackup2.h>
36#include <libimobiledevice/notification_proxy.h>
37#include <libimobiledevice/afc.h>
38
39#define MOBILEBACKUP2_SERVICE_NAME "com.apple.mobilebackup2"
40#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
41
42#define LOCK_ATTEMPTS 50
43#define LOCK_WAIT 200000
44
45#define CODE_SUCCESS 0x00
46#define CODE_ERROR_LOCAL 0x06
47#define CODE_ERROR_REMOTE 0x0b
48#define CODE_FILE_DATA 0x0c
49
50static mobilebackup2_client_t mobilebackup2 = NULL;
51static lockdownd_client_t client = NULL;
52static afc_client_t afc = NULL;
53static idevice_t phone = NULL;
54
55static int verbose = 1;
56static int quit_flag = 0;
57
58#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
59
60enum cmd_mode {
61 CMD_BACKUP,
62 CMD_RESTORE,
63 CMD_INFO,
64 CMD_LIST,
65 CMD_LEAVE
66};
67
68enum plist_format_t {
69 PLIST_FORMAT_XML,
70 PLIST_FORMAT_BINARY
71};
72
73static void notify_cb(const char *notification, void *userdata)
74{
75 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
76 PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
77 quit_flag++;
78 } else {
79 PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
80 }
81}
82
83static void free_dictionary(char **dictionary)
84{
85 int i = 0;
86
87 if (!dictionary)
88 return;
89
90 for (i = 0; dictionary[i]; i++) {
91 free(dictionary[i]);
92 }
93 free(dictionary);
94}
95
96static void mobilebackup_afc_get_file_contents(const char *filename, char **data, uint64_t *size)
97{
98 if (!afc || !data || !size) {
99 return;
100 }
101
102 char **fileinfo = NULL;
103 uint32_t fsize = 0;
104
105 afc_get_file_info(afc, filename, &fileinfo);
106 if (!fileinfo) {
107 return;
108 }
109 int i;
110 for (i = 0; fileinfo[i]; i+=2) {
111 if (!strcmp(fileinfo[i], "st_size")) {
112 fsize = atol(fileinfo[i+1]);
113 break;
114 }
115 }
116 free_dictionary(fileinfo);
117
118 if (fsize == 0) {
119 return;
120 }
121
122 uint64_t f = 0;
123 afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
124 if (!f) {
125 return;
126 }
127 char *buf = (char*)malloc((uint32_t)fsize);
128 uint32_t done = 0;
129 while (done < fsize) {
130 uint32_t bread = 0;
131 afc_file_read(afc, f, buf+done, 65536, &bread);
132 if (bread > 0) {
133
134 } else {
135 break;
136 }
137 done += bread;
138 }
139 if (done == fsize) {
140 *size = fsize;
141 *data = buf;
142 } else {
143 free(buf);
144 }
145 afc_file_close(afc, f);
146}
147
148static plist_t mobilebackup_factory_info_plist_new()
149{
150 /* gather data from lockdown */
151 GTimeVal tv = {0, 0};
152 plist_t value_node = NULL;
153 plist_t root_node = NULL;
154 char *uuid = NULL;
155 char *uuid_uppercase = NULL;
156
157 plist_t ret = plist_new_dict();
158
159 /* get basic device information in one go */
160 lockdownd_get_value(client, NULL, NULL, &root_node);
161
162 /* set fields we understand */
163 value_node = plist_dict_get_item(root_node, "BuildVersion");
164 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
165
166 value_node = plist_dict_get_item(root_node, "DeviceName");
167 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node));
168 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node));
169
170 /* FIXME: How is the GUID generated? */
171 plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
172
173 value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
174 if (value_node)
175 plist_dict_insert_item(ret, "ICCID", plist_copy(value_node));
176
177 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
178 if (value_node)
179 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
180
181 g_get_current_time(&tv);
182 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, 0));
183
184 value_node = plist_dict_get_item(root_node, "PhoneNumber");
185 if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
186 plist_dict_insert_item(ret, "Phone Number", plist_copy(value_node));
187 }
188
189 value_node = plist_dict_get_item(root_node, "ProductType");
190 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
191
192 value_node = plist_dict_get_item(root_node, "ProductVersion");
193 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node));
194
195 value_node = plist_dict_get_item(root_node, "SerialNumber");
196 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node));
197
198 /* FIXME Sync Settings? */
199
200 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
201 idevice_get_uuid(phone, &uuid);
202 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
203
204 plist_dict_insert_item(ret, "Target Type", plist_new_string("Device"));
205
206 /* uppercase */
207 uuid_uppercase = g_ascii_strup(uuid, -1);
208 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase));
209 free(uuid_uppercase);
210 free(uuid);
211
212 char *data_buf = NULL;
213 uint64_t data_size = 0;
214 mobilebackup_afc_get_file_contents("/Books/iBooksData2.plist", &data_buf, &data_size);
215 if (data_buf) {
216 plist_dict_insert_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
217 free(data_buf);
218 }
219
220 plist_t files = plist_new_dict();
221 const char *itunesfiles[] = {
222 "ApertureAlbumPrefs",
223 "IC-Info.sidb",
224 "IC-Info.sidv",
225 "PhotosFolderAlbums",
226 "PhotosFolderName",
227 "PhotosFolderPrefs",
228 "iPhotoAlbumPrefs",
229 "iTunesApplicationIDs",
230 "iTunesPrefs",
231 "iTunesPrefs.plist",
232 NULL
233 };
234 int i = 0;
235 for (i = 0; itunesfiles[i]; i++) {
236 data_buf = NULL;
237 data_size = 0;
238 gchar *fname = g_strconcat("/iTunes_Control/iTunes/", itunesfiles[i], NULL);
239 mobilebackup_afc_get_file_contents(fname, &data_buf, &data_size);
240 g_free(fname);
241 if (data_buf) {
242 plist_dict_insert_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
243 free(data_buf);
244 }
245 }
246 plist_dict_insert_item(ret, "iTunes Files", files);
247
248 plist_t itunes_settings = plist_new_dict();
249 lockdownd_get_value(client, "com.apple.iTunes", NULL, &itunes_settings);
250 plist_dict_insert_item(ret, "iTunes Settings", itunes_settings);
251
252 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("10.0.1"));
253
254 plist_free(root_node);
255
256 return ret;
257}
258
259static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
260{
261 FILE *f;
262 uint64_t size;
263
264 *length = 0;
265
266 f = fopen(filename, "rb");
267 if (!f) {
268 return;
269 }
270
271 fseek(f, 0, SEEK_END);
272 size = ftell(f);
273 rewind(f);
274
275 if (size == 0) {
276 return;
277 }
278
279 *buffer = (char*)malloc(sizeof(char)*size);
280 fread(*buffer, sizeof(char), size, f);
281 fclose(f);
282
283 *length = size;
284}
285
286static void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length)
287{
288 FILE *f;
289
290 f = fopen(filename, "ab");
291 if (!f)
292 f = fopen(filename, "wb");
293 if (f) {
294 fwrite(buffer, sizeof(char), length, f);
295 fclose(f);
296 }
297}
298
299static int plist_read_from_filename(plist_t *plist, const char *filename)
300{
301 char *buffer = NULL;
302 uint64_t length;
303
304 if (!filename)
305 return 0;
306
307 buffer_read_from_filename(filename, &buffer, &length);
308
309 if (!buffer) {
310 return 0;
311 }
312
313 if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) {
314 plist_from_bin(buffer, length, plist);
315 } else {
316 plist_from_xml(buffer, length, plist);
317 }
318
319 free(buffer);
320
321 return 1;
322}
323
324static int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format)
325{
326 char *buffer = NULL;
327 uint32_t length;
328
329 if (!plist || !filename)
330 return 0;
331
332 if (format == PLIST_FORMAT_XML)
333 plist_to_xml(plist, &buffer, &length);
334 else if (format == PLIST_FORMAT_BINARY)
335 plist_to_bin(plist, &buffer, &length);
336 else
337 return 0;
338
339 buffer_write_to_filename(filename, buffer, length);
340
341 free(buffer);
342
343 return 1;
344}
345
346static int mb2_status_check_snapshot_state(const char *path, const char *uuid, const char *matches)
347{
348 int ret = -1;
349 plist_t status_plist = NULL;
350 gchar *file_path = g_build_path(G_DIR_SEPARATOR_S, path, uuid, "Status.plist", NULL);
351
352 plist_read_from_filename(&status_plist, file_path);
353 g_free(file_path);
354 if (!status_plist) {
355 printf("Could not read Status.plist!\n");
356 return ret;
357 }
358 plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
359 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
360 char* sval = NULL;
361 plist_get_string_val(node, &sval);
362 if (sval) {
363 ret = (strcmp(sval, matches) == 0) ? 1 : 0;
364 }
365 } else {
366 printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
367 }
368 plist_free(status_plist);
369 return ret;
370}
371
372static int mobilebackup_info_is_current_device(plist_t info)
373{
374 plist_t value_node = NULL;
375 plist_t node = NULL;
376 plist_t root_node = NULL;
377 int ret = 0;
378
379 if (!info)
380 return ret;
381
382 if (plist_get_node_type(info) != PLIST_DICT)
383 return ret;
384
385 /* get basic device information in one go */
386 lockdownd_get_value(client, NULL, NULL, &root_node);
387
388 /* verify UUID */
389 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
390 node = plist_dict_get_item(info, "Target Identifier");
391
392 if(plist_compare_node_value(value_node, node))
393 ret = 1;
394 else {
395 printf("Info.plist: UniqueDeviceID does not match.\n");
396 }
397
398 /* verify SerialNumber */
399 if (ret == 1) {
400 value_node = plist_dict_get_item(root_node, "SerialNumber");
401 node = plist_dict_get_item(info, "Serial Number");
402
403 if(plist_compare_node_value(value_node, node))
404 ret = 1;
405 else {
406 printf("Info.plist: SerialNumber does not match.\n");
407 ret = 0;
408 }
409 }
410
411 /* verify ProductVersion to prevent using backup with different OS version */
412 if (ret == 1) {
413 value_node = plist_dict_get_item(root_node, "ProductVersion");
414 node = plist_dict_get_item(info, "Product Version");
415
416 if(plist_compare_node_value(value_node, node))
417 ret = 1;
418 else {
419 printf("Info.plist: ProductVersion does not match.\n");
420 ret = 0;
421 }
422 }
423
424 plist_free(root_node);
425 root_node = NULL;
426
427 value_node = NULL;
428 node = NULL;
429
430 return ret;
431}
432
433static void do_post_notification(const char *notification)
434{
435 uint16_t nport = 0;
436 np_client_t np;
437
438 if (!client) {
439 if (lockdownd_client_new_with_handshake(phone, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) {
440 return;
441 }
442 }
443
444 lockdownd_start_service(client, NP_SERVICE_NAME, &nport);
445 if (nport) {
446 np_client_new(phone, nport, &np);
447 if (np) {
448 np_post_notification(np, notification);
449 np_client_free(np);
450 }
451 } else {
452 printf("Could not start %s\n", NP_SERVICE_NAME);
453 }
454}
455
456static void print_progress_real(double progress, int flush)
457{
458 int i = 0;
459 PRINT_VERBOSE(1, "\r[");
460 for(i = 0; i < 50; i++) {
461 if(i < progress / 2) {
462 PRINT_VERBOSE(1, "=");
463 } else {
464 PRINT_VERBOSE(1, " ");
465 }
466 }
467 PRINT_VERBOSE(1, "] %3.0f%%", progress);
468
469 if (flush > 0) {
470 fflush(stdout);
471 if (progress == 100)
472 PRINT_VERBOSE(1, "\n");
473 }
474}
475
476static void print_progress(uint64_t current, uint64_t total)
477{
478 gchar *format_size = NULL;
479 double progress = ((double)current/(double)total)*100;
480 if (progress < 0)
481 return;
482
483 if (progress > 100)
484 progress = 100;
485
486 print_progress_real((double)progress, 0);
487
488 format_size = g_format_size_for_display(current);
489 PRINT_VERBOSE(1, " (%s", format_size);
490 g_free(format_size);
491 format_size = g_format_size_for_display(total);
492 PRINT_VERBOSE(1, "/%s) ", format_size);
493 g_free(format_size);
494
495 fflush(stdout);
496 if (progress == 100)
497 PRINT_VERBOSE(1, "\n");
498}
499
500static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
501{
502 if (!status_dict) return;
503 plist_t filedict = plist_new_dict();
504 plist_dict_insert_item(filedict, "DLFileErrorString", plist_new_string(error_message));
505 plist_dict_insert_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
506 plist_dict_insert_item(status_dict, path, filedict);
507}
508
509static int errno_to_device_error(int errno_value)
510{
511 switch (errno_value) {
512 case ENOENT:
513 return -6;
514 case EEXIST:
515 return -7;
516 default:
517 return -errno_value;
518 }
519}
520
521static int mb2_handle_send_file(const char *backup_dir, const char *path, plist_t *errplist)
522{
523 uint32_t nlen = 0;
524 uint32_t pathlen = strlen(path);
525 uint32_t bytes = 0;
526 gchar *localfile = g_build_path(G_DIR_SEPARATOR_S, backup_dir, path, NULL);
527 char buf[32768];
528 struct stat fst;
529
530 FILE *f = NULL;
531 uint32_t slen = 0;
532 int errcode = -1;
533 int result = -1;
534 uint32_t length;
535 off_t total;
536 off_t sent;
537
538 mobilebackup2_error_t err;
539
540 /* send path length */
541 nlen = GUINT32_TO_BE(pathlen);
542 err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
543 if (err != MOBILEBACKUP2_E_SUCCESS) {
544 goto leave_proto_err;
545 }
546 if (bytes != (uint32_t)sizeof(nlen)) {
547 err = MOBILEBACKUP2_E_MUX_ERROR;
548 goto leave_proto_err;
549 }
550
551 /* send path */
552 err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
553 if (err != MOBILEBACKUP2_E_SUCCESS) {
554 goto leave_proto_err;
555 }
556 if (bytes != pathlen) {
557 err = MOBILEBACKUP2_E_MUX_ERROR;
558 goto leave_proto_err;
559 }
560
561 if (stat(localfile, &fst) < 0) {
562 if (errno != ENOENT)
563 printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
564 errcode = errno;
565 goto leave;
566 }
567
568 total = fst.st_size;
569
570 gchar *format_size = g_format_size_for_display(total);
571 PRINT_VERBOSE(1, "Sending file '%s': (%s)\n", path, format_size);
572 g_free(format_size);
573
574 if (total == 0) {
575 errcode = 0;
576 goto leave;
577 }
578
579 f = fopen(localfile, "rb");
580 if (!f) {
581 printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
582 errcode = errno;
583 goto leave;
584 }
585
586 sent = 0;
587 do {
588 length = ((total-sent) < (off_t)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
589 /* send data size (file size + 1) */
590 nlen = GUINT32_TO_BE(length+1);
591 memcpy(buf, &nlen, sizeof(nlen));
592 buf[4] = CODE_FILE_DATA;
593 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
594 if (err != MOBILEBACKUP2_E_SUCCESS) {
595 goto leave_proto_err;
596 }
597 if (bytes != 5) {
598 goto leave_proto_err;
599 }
600
601 /* send file contents */
602 size_t r = fread(buf, 1, sizeof(buf), f);
603 if (r <= 0) {
604 printf("%s: read error\n", __func__);
605 errcode = errno;
606 goto leave;
607 }
608 err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
609 if (err != MOBILEBACKUP2_E_SUCCESS) {
610 goto leave_proto_err;
611 }
612 if (bytes != (uint32_t)r) {
613 printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
614 goto leave_proto_err;
615 }
616 sent += r;
617 } while (sent < total);
618 fclose(f);
619 f = NULL;
620 errcode = 0;
621
622leave:
623 if (errcode == 0) {
624 result = 0;
625 nlen = 1;
626 nlen = GUINT32_TO_BE(nlen);
627 memcpy(buf, &nlen, 4);
628 buf[4] = CODE_SUCCESS;
629 mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
630 } else {
631 if (!*errplist) {
632 *errplist = plist_new_dict();
633 }
634 char *errdesc = strerror(errcode);
635 mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
636
637 length = strlen(errdesc);
638 nlen = GUINT32_TO_BE(length+1);
639 memcpy(buf, &nlen, 4);
640 buf[4] = CODE_ERROR_LOCAL;
641 slen = 5;
642 memcpy(buf+slen, errdesc, length);
643 slen += length;
644 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
645 if (err != MOBILEBACKUP2_E_SUCCESS) {
646 printf("could not send message\n");
647 }
648 if (bytes != slen) {
649 printf("could only send %d from %d\n", bytes, slen);
650 }
651 }
652
653leave_proto_err:
654 if (f)
655 fclose(f);
656 g_free(localfile);
657 return result;
658}
659
660static void mb2_handle_send_files(plist_t message, const char *backup_dir)
661{
662 uint32_t cnt;
663 uint32_t i = 0;
664 uint32_t sent;
665 plist_t errplist = NULL;
666 double progress = 0;
667
668 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
669
670 plist_t files = plist_array_get_item(message, 1);
671 cnt = plist_array_get_size(files);
672 if (cnt == 0) return;
673
674 plist_t val = plist_array_get_item(message, 3);
675 plist_get_real_val(val, &progress);
676 val = NULL;
677
678 for (i = 0; i < cnt; i++) {
679 val = plist_array_get_item(files, i);
680 if (plist_get_node_type(val) != PLIST_STRING) {
681 continue;
682 }
683 char *str = NULL;
684 plist_get_string_val(val, &str);
685 if (!str)
686 continue;
687
688 if (mb2_handle_send_file(backup_dir, str, &errplist) < 0) {
689 //printf("Error when sending file '%s' to device\n", str);
690 // TODO: perhaps we can continue, we've got a multi status response?!
691 break;
692 }
693 }
694
695 /* send terminating 0 dword */
696 uint32_t zero = 0;
697 mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
698
699 if (!errplist) {
700 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, plist_new_dict());
701 } else {
702 mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
703 plist_free(errplist);
704 }
705
706 if (progress > 0) {
707 print_progress_real(progress, 1);
708 PRINT_VERBOSE(1, "\n");
709 }
710}
711
712static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
713{
714 uint64_t backup_real_size = 0;
715 uint64_t backup_total_size = 0;
716 uint32_t blocksize;
717 uint32_t bdone;
718 uint32_t rlen;
719 uint32_t nlen = 0;
720 uint32_t r;
721 char buf[32768];
722 char *fname = NULL;
723 char *dname = NULL;
724 gchar *bname = NULL;
725 char code = 0;
726 char last_code = 0;
727 plist_t node = NULL;
728 FILE *f = NULL;
729 unsigned int file_count = 0;
730
731 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
732
733 node = plist_array_get_item(message, 3);
734 if (plist_get_node_type(node) == PLIST_UINT) {
735 plist_get_uint_val(node, &backup_total_size);
736 }
737 if (backup_total_size > 0) {
738 PRINT_VERBOSE(1, "Receiving backup data\n");
739 }
740
741 do {
742 if (quit_flag)
743 break;
744 r = 0;
745 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
746 nlen = GUINT32_FROM_BE(nlen);
747 if (nlen == 0) {
748 // we're done here
749 break;
750 } else if (nlen > 4096) {
751 // too very long path
752 printf("ERROR: %s: too long device filename (%d)!\n", __func__, nlen);
753 break;
754 }
755 if (dname != NULL)
756 free(dname);
757 dname = (char*)malloc(nlen+1);
758 r = 0;
759 mobilebackup2_receive_raw(mobilebackup2, dname, nlen, &r);
760 if (r != nlen) {
761 printf("ERROR: %s: could not read device filename\n", __func__);
762 break;
763 }
764 dname[r] = 0;
765 nlen = 0;
766 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
767 nlen = GUINT32_FROM_BE(nlen);
768 if (nlen == 0) {
769 printf("ERROR: %s: zero-length backup filename!\n", __func__);
770 break;
771 } else if (nlen > 4096) {
772 printf("ERROR: %s: too long backup filename (%d)!\n", __func__, nlen);
773 break;
774 }
775 fname = (char*)malloc(nlen+1);
776 mobilebackup2_receive_raw(mobilebackup2, fname, nlen, &r);
777 if (r != nlen) {
778 printf("ERROR: %s: could not receive backup filename!\n", __func__);
779 break;
780 }
781 fname[r] = 0;
782 if (bname != NULL)
783 g_free(bname);
784 bname = g_build_path(G_DIR_SEPARATOR_S, backup_dir, fname, NULL);
785 free(fname);
786 nlen = 0;
787 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
788 if (r != 4) {
789 printf("ERROR: %s: could not receive code length!\n", __func__);
790 break;
791 }
792 nlen = GUINT32_FROM_BE(nlen);
793 last_code = code;
794 code = 0;
795 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
796 if (r != 1) {
797 printf("ERROR: %s: could not receive code!\n", __func__);
798 break;
799 }
800
801 /* TODO remove this */
802 if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
803 PRINT_VERBOSE(1, "Found new flag %02x\n", code);
804 }
805
806 remove(bname);
807 f = fopen(bname, "wb");
808 while (f && (code == CODE_FILE_DATA)) {
809 blocksize = nlen-1;
810 bdone = 0;
811 rlen = 0;
812 while (bdone < blocksize) {
813 if ((blocksize - bdone) < sizeof(buf)) {
814 rlen = blocksize - bdone;
815 } else {
816 rlen = sizeof(buf);
817 }
818 mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
819 if ((int)r <= 0) {
820 break;
821 }
822 fwrite(buf, 1, r, f);
823 bdone += r;
824 }
825 if (bdone == blocksize) {
826 backup_real_size += blocksize;
827 }
828 if (backup_total_size > 0) {
829 print_progress(backup_real_size, backup_total_size);
830 }
831 if (quit_flag)
832 break;
833 nlen = 0;
834 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
835 nlen = GUINT32_FROM_BE(nlen);
836 if (nlen > 0) {
837 last_code = code;
838 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
839 } else {
840 break;
841 }
842 }
843 if (f) {
844 fclose(f);
845 file_count++;
846 } else {
847 printf("Error opening '%s' for writing: %s\n", bname, strerror(errno));
848 }
849 if (nlen == 0) {
850 break;
851 }
852
853 /* check if an error message was received */
854 if (code == CODE_ERROR_REMOTE) {
855 /* error message */
856 char *msg = (char*)malloc(nlen);
857 mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
858 msg[r] = 0;
859 /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
860 if (last_code != CODE_FILE_DATA) {
861 fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
862 }
863 free(msg);
864 }
865 } while (1);
866
867 /* if there are leftovers to read, finish up cleanly */
868 if ((int)nlen-1 > 0) {
869 PRINT_VERBOSE(1, "\nDiscarding current data hunk.\n");
870 fname = (char*)malloc(nlen-1);
871 mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
872 free(fname);
873 remove(bname);
874 }
875
876 /* clean up */
877 if (bname != NULL)
878 g_free(bname);
879
880 if (dname != NULL)
881 free(dname);
882
883 // TODO error handling?!
884 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, plist_new_dict());
885 return file_count;
886}
887
888static void mb2_handle_list_directory(plist_t message, const char *backup_dir)
889{
890 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
891
892 plist_t node = plist_array_get_item(message, 1);
893 char *str = NULL;
894 if (plist_get_node_type(node) == PLIST_STRING) {
895 plist_get_string_val(node, &str);
896 }
897 if (!str) {
898 printf("ERROR: Malformed DLContentsOfDirectory message\n");
899 // TODO error handling
900 return;
901 }
902
903 gchar *path = g_build_path(G_DIR_SEPARATOR_S, backup_dir, str, NULL);
904 free(str);
905
906 plist_t dirlist = plist_new_dict();
907
908 GDir *cur_dir = g_dir_open(path, 0, NULL);
909 if (cur_dir) {
910 gchar *dir_file;
911 while ((dir_file = (gchar *)g_dir_read_name(cur_dir))) {
912 gchar *fpath = g_build_filename(path, dir_file, NULL);
913 if (fpath) {
914 plist_t fdict = plist_new_dict();
915 GStatBuf st;
916 g_stat(fpath, &st);
917 const char *ftype = "DLFileTypeUnknown";
918 if (g_file_test(fpath, G_FILE_TEST_IS_DIR)) {
919 ftype = "DLFileTypeDirectory";
920 } else if (g_file_test(fpath, G_FILE_TEST_IS_REGULAR)) {
921 ftype = "DLFileTypeRegular";
922 }
923 plist_dict_insert_item(fdict, "DLFileType", plist_new_string(ftype));
924 plist_dict_insert_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
925 plist_dict_insert_item(fdict, "DLFileModificationDate", plist_new_date(st.st_mtime, 0));
926
927 plist_dict_insert_item(dirlist, dir_file, fdict);
928 g_free(fpath);
929 }
930 }
931 g_dir_close(cur_dir);
932 }
933 g_free(path);
934
935 /* TODO error handling */
936 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
937 plist_free(dirlist);
938 if (err != MOBILEBACKUP2_E_SUCCESS) {
939 printf("Could not send status response, error %d\n", err);
940 }
941}
942
943static void mb2_handle_make_directory(plist_t message, const char *backup_dir)
944{
945 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
946
947 plist_t dir = plist_array_get_item(message, 1);
948 char *str = NULL;
949 int errcode = 0;
950 char *errdesc = NULL;
951 plist_get_string_val(dir, &str);
952
953 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_dir, str, NULL);
954 g_free(str);
955
956 if (mkdir(newpath, 0755) < 0) {
957 errdesc = strerror(errno);
958 if (errno != EEXIST) {
959 printf("mkdir: %s (%d)\n", errdesc, errno);
960 }
961 errcode = errno_to_device_error(errno);
962 }
963 g_free(newpath);
964 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
965 if (err != MOBILEBACKUP2_E_SUCCESS) {
966 printf("Could not send status response, error %d\n", err);
967 }
968}
969
970static void mb2_copy_file_by_path(const gchar *src, const gchar *dst)
971{
972 FILE *from, *to;
973 char ch;
974
975 /* open source file */
976 if ((from = fopen(src, "rb")) == NULL) {
977 printf("Cannot open source path '%s'.\n", src);
978 return;
979 }
980
981 /* open destination file */
982 if ((to = fopen(dst, "wb")) == NULL) {
983 printf("Cannot open destination file '%s'.\n", dst);
984 return;
985 }
986
987 /* copy the file */
988 while(!feof(from)) {
989 ch = fgetc(from);
990 if(ferror(from)) {
991 printf("Error reading source file.\n");
992 break;
993 }
994 if(!feof(from))
995 fputc(ch, to);
996
997 if(ferror(to)) {
998 printf("Error writing destination file.\n");
999 break;
1000 }
1001 }
1002
1003 if(fclose(from) == EOF) {
1004 printf("Error closing source file.\n");
1005 }
1006
1007 if(fclose(to) == EOF) {
1008 printf("Error closing destination file.\n");
1009 }
1010}
1011
1012static void mb2_copy_directory_by_path(const gchar *src, const gchar *dst)
1013{
1014 if (!src || !dst) {
1015 return;
1016 }
1017
1018 /* if src does not exist */
1019 if (!g_file_test(src, G_FILE_TEST_EXISTS)) {
1020 printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
1021 return;
1022 }
1023
1024 /* if dst directory does not exist */
1025 if (!g_file_test(dst, G_FILE_TEST_IS_DIR)) {
1026 /* create it */
1027 if (g_mkdir_with_parents(dst, 0755) < 0) {
1028 printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
1029 return;
1030 }
1031 }
1032
1033 /* loop over src directory contents */
1034 GDir *cur_dir = g_dir_open(src, 0, NULL);
1035 if (cur_dir) {
1036 gchar *dir_file;
1037 while ((dir_file = (gchar *)g_dir_read_name(cur_dir))) {
1038 gchar *srcpath = g_build_filename(src, dir_file, NULL);
1039 gchar *dstpath = g_build_filename(dst, dir_file, NULL);
1040 if (srcpath && dstpath) {
1041 /* copy file */
1042 mb2_copy_file_by_path(srcpath, dstpath);
1043
1044 g_free(srcpath);
1045 g_free(dstpath);
1046 }
1047 }
1048 g_dir_close(cur_dir);
1049 }
1050}
1051
1052/**
1053 * signal handler function for cleaning up properly
1054 */
1055static void clean_exit(int sig)
1056{
1057 fprintf(stderr, "Exiting...\n");
1058 quit_flag++;
1059}
1060
1061static void print_usage(int argc, char **argv)
1062{
1063 char *name = NULL;
1064 name = strrchr(argv[0], '/');
1065 printf("Usage: %s [OPTIONS] CMD DIRECTORY\n", (name ? name + 1: argv[0]));
1066 printf("Create or restore backup from the current or specified directory.\n\n");
1067 printf("commands:\n");
1068 printf(" backup\tcreate backup for the device\n");
1069 printf(" restore\trestore last backup to the device\n");
1070 printf(" info\t\tshow details about last completed backup of device\n");
1071 printf(" list\t\tlist files of last completed backup in CSV format\n\n");
1072 printf("options:\n");
1073 printf(" -d, --debug\t\tenable communication debugging\n");
1074 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
1075 printf(" -h, --help\t\tprints usage information\n");
1076 printf("\n");
1077}
1078
1079int main(int argc, char *argv[])
1080{
1081 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
1082 int i;
1083 char uuid[41];
1084 uint16_t port = 0;
1085 uuid[0] = 0;
1086 int cmd = -1;
1087 int is_full_backup = 0;
1088 char *backup_directory = NULL;
1089 struct stat st;
1090 plist_t node_tmp = NULL;
1091 plist_t info_plist = NULL;
1092 mobilebackup2_error_t err;
1093
1094 /* we need to exit cleanly on running backups and restores or we cause havok */
1095 signal(SIGINT, clean_exit);
1096 signal(SIGQUIT, clean_exit);
1097 signal(SIGTERM, clean_exit);
1098 signal(SIGPIPE, SIG_IGN);
1099
1100 /* parse cmdline args */
1101 for (i = 1; i < argc; i++) {
1102 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
1103 idevice_set_debug_level(1);
1104 continue;
1105 }
1106 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) {
1107 i++;
1108 if (!argv[i] || (strlen(argv[i]) != 40)) {
1109 print_usage(argc, argv);
1110 return 0;
1111 }
1112 strcpy(uuid, argv[i]);
1113 continue;
1114 }
1115 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1116 print_usage(argc, argv);
1117 return 0;
1118 }
1119 else if (!strcmp(argv[i], "backup")) {
1120 cmd = CMD_BACKUP;
1121 }
1122 else if (!strcmp(argv[i], "restore")) {
1123 cmd = CMD_RESTORE;
1124 }
1125 else if (!strcmp(argv[i], "info")) {
1126 cmd = CMD_INFO;
1127 verbose = 0;
1128 }
1129 else if (!strcmp(argv[i], "list")) {
1130 cmd = CMD_LIST;
1131 verbose = 0;
1132 }
1133 else if (backup_directory == NULL) {
1134 backup_directory = argv[i];
1135 }
1136 else {
1137 print_usage(argc, argv);
1138 return 0;
1139 }
1140 }
1141
1142 /* verify options */
1143 if (cmd == -1) {
1144 printf("No command specified.\n");
1145 print_usage(argc, argv);
1146 return -1;
1147 }
1148
1149 if (backup_directory == NULL) {
1150 printf("No target backup directory specified.\n");
1151 print_usage(argc, argv);
1152 return -1;
1153 }
1154
1155 /* verify if passed backup directory exists */
1156 if (stat(backup_directory, &st) != 0) {
1157 printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
1158 return -1;
1159 }
1160
1161 if (uuid[0] != 0) {
1162 ret = idevice_new(&phone, uuid);
1163 if (ret != IDEVICE_E_SUCCESS) {
1164 printf("No device found with uuid %s, is it plugged in?\n", uuid);
1165 return -1;
1166 }
1167 }
1168 else
1169 {
1170 ret = idevice_new(&phone, NULL);
1171 if (ret != IDEVICE_E_SUCCESS) {
1172 printf("No device found, is it plugged in?\n");
1173 return -1;
1174 }
1175 char *newuuid = NULL;
1176 idevice_get_uuid(phone, &newuuid);
1177 strcpy(uuid, newuuid);
1178 free(newuuid);
1179 }
1180
1181 /* backup directory must contain an Info.plist */
1182 gchar *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, uuid, "Info.plist", NULL);
1183 if (cmd == CMD_RESTORE) {
1184 if (stat(info_path, &st) != 0) {
1185 g_free(info_path);
1186 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UUID %s.\n", backup_directory, uuid);
1187 return -1;
1188 }
1189 }
1190
1191 PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
1192
1193 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicebackup")) {
1194 idevice_free(phone);
1195 return -1;
1196 }
1197
1198 /* start notification_proxy */
1199 np_client_t np = NULL;
1200 ret = lockdownd_start_service(client, NP_SERVICE_NAME, &port);
1201 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
1202 np_client_new(phone, port, &np);
1203 np_set_notify_callback(np, notify_cb, NULL);
1204 const char *noties[5] = {
1205 NP_SYNC_CANCEL_REQUEST,
1206 NP_SYNC_SUSPEND_REQUEST,
1207 NP_SYNC_RESUME_REQUEST,
1208 NP_BACKUP_DOMAIN_CHANGED,
1209 NULL
1210 };
1211 np_observe_notifications(np, noties);
1212 } else {
1213 printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
1214 }
1215
1216 afc = NULL;
1217 if (cmd == CMD_BACKUP) {
1218 /* start AFC, we need this for the lock file */
1219 port = 0;
1220 ret = lockdownd_start_service(client, "com.apple.afc", &port);
1221 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
1222 afc_client_new(phone, port, &afc);
1223 }
1224 }
1225
1226 /* start mobilebackup service and retrieve port */
1227 port = 0;
1228 ret = lockdownd_start_service(client, MOBILEBACKUP2_SERVICE_NAME, &port);
1229 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
1230 PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, port);
1231 mobilebackup2_client_new(phone, port, &mobilebackup2);
1232
1233 /* send Hello message */
1234 err = mobilebackup2_version_exchange(mobilebackup2);
1235 if (err != MOBILEBACKUP2_E_SUCCESS) {
1236 printf("Could not perform backup protocol version exchange, error code %d\n", err);
1237 cmd = CMD_LEAVE;
1238 goto checkpoint;
1239 }
1240
1241 /* check abort conditions */
1242 if (quit_flag > 0) {
1243 PRINT_VERBOSE(1, "Aborting as requested by user...\n");
1244 cmd = CMD_LEAVE;
1245 goto checkpoint;
1246 }
1247
1248 /* verify existing Info.plist */
1249 if (stat(info_path, &st) == 0) {
1250 PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
1251 plist_read_from_filename(&info_plist, info_path);
1252
1253 if (!info_plist) {
1254 printf("Could not read Info.plist\n");
1255 is_full_backup = 1;
1256 }
1257 if (info_plist && (cmd == CMD_BACKUP)) {
1258 if (mobilebackup_info_is_current_device(info_plist)) {
1259 /* update the last backup time within Info.plist */
1260 //mobilebackup_info_update_last_backup_date(info_plist);
1261 //remove(info_path);
1262 //plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
1263 } else {
1264 printf("Aborting backup. Backup is not compatible with the current device.\n");
1265 cmd = CMD_LEAVE;
1266 }
1267 } else if (info_plist && (cmd == CMD_RESTORE)) {
1268 if (!mobilebackup_info_is_current_device(info_plist)) {
1269 printf("Aborting restore. Backup data is not compatible with the current device.\n");
1270 cmd = CMD_LEAVE;
1271 }
1272 }
1273 } else {
1274 if (cmd == CMD_RESTORE) {
1275 printf("Aborting restore. Info.plist is missing.\n");
1276 cmd = CMD_LEAVE;
1277 } else {
1278 is_full_backup = 1;
1279 }
1280 }
1281
1282 uint64_t lockfile = 0;
1283 if (cmd == CMD_BACKUP) {
1284 do_post_notification(NP_SYNC_WILL_START);
1285 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
1286 }
1287 if (lockfile) {
1288 afc_error_t aerr;
1289 do_post_notification(NP_SYNC_LOCK_REQUEST);
1290 for (i = 0; i < LOCK_ATTEMPTS; i++) {
1291 aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
1292 if (aerr == AFC_E_SUCCESS) {
1293 do_post_notification(NP_SYNC_DID_START);
1294 break;
1295 } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
1296 usleep(LOCK_WAIT);
1297 continue;
1298 } else {
1299 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
1300 afc_file_close(afc, lockfile);
1301 lockfile = 0;
1302 cmd = CMD_LEAVE;
1303 }
1304 }
1305 if (i == LOCK_ATTEMPTS) {
1306 fprintf(stderr, "ERROR: timeout while locking for sync\n");
1307 afc_file_close(afc, lockfile);
1308 lockfile = 0;
1309 cmd = CMD_LEAVE;
1310 }
1311 }
1312
1313checkpoint:
1314
1315 switch(cmd) {
1316 case CMD_BACKUP:
1317 PRINT_VERBOSE(1, "Starting backup...\n");
1318
1319 /* make sure backup device sub-directory exists */
1320 gchar *devbackupdir = g_build_path(G_DIR_SEPARATOR_S, backup_directory, uuid, NULL);
1321 g_mkdir(devbackupdir, 0755);
1322 g_free(devbackupdir);
1323
1324 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
1325 /* TODO: verify battery on AC enough battery remaining */
1326
1327 /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
1328 if (info_plist) {
1329 plist_free(info_plist);
1330 info_plist = NULL;
1331 }
1332 info_plist = mobilebackup_factory_info_plist_new();
1333 remove(info_path);
1334 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
1335 g_free(info_path);
1336
1337 plist_free(info_plist);
1338 info_plist = NULL;
1339
1340 /* request backup from device with manifest from last backup */
1341 PRINT_VERBOSE(1, "Requesting backup from device...\n");
1342
1343 err = mobilebackup2_send_request(mobilebackup2, "Backup", uuid, NULL, NULL);
1344 if (err == MOBILEBACKUP2_E_SUCCESS) {
1345 if (is_full_backup) {
1346 PRINT_VERBOSE(1, "Full backup mode.\n");
1347 } else {
1348 PRINT_VERBOSE(1, "Incremental backup mode.\n");
1349 }
1350 } else {
1351 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
1352 printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
1353 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
1354 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
1355 } else {
1356 printf("ERROR: Could not start backup process: unspecified error occured\n");
1357 }
1358 cmd = CMD_LEAVE;
1359 }
1360 break;
1361 case CMD_RESTORE:
1362 /* TODO: verify battery on AC enough battery remaining */
1363
1364 /* verify if Status.plist says we read from an successful backup */
1365 if (!mb2_status_check_snapshot_state(backup_directory, uuid, "finished")) {
1366 printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
1367 cmd = CMD_LEAVE;
1368 break;
1369 }
1370
1371 PRINT_VERBOSE(1, "Starting Restore...\n");
1372
1373 plist_t opts = plist_new_dict();
1374 plist_dict_insert_item(opts, "shouldRestoreSystemFiles", plist_new_bool(0));
1375 err = mobilebackup2_send_request(mobilebackup2, "Restore", uuid, uuid, opts);
1376 plist_free(opts);
1377 if (err != MOBILEBACKUP2_E_SUCCESS) {
1378 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
1379 printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
1380 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
1381 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
1382 } else {
1383 printf("ERROR: Could not start restore process: unspecified error occured\n");
1384 }
1385 cmd = CMD_LEAVE;
1386 }
1387 break;
1388 case CMD_INFO:
1389 PRINT_VERBOSE(1, "Requesting backup info from device...\n");
1390 err = mobilebackup2_send_request(mobilebackup2, "Info", uuid, NULL, NULL);
1391 if (err != MOBILEBACKUP2_E_SUCCESS) {
1392 printf("Error requesting backup info from device, error code %d\n", err);
1393 cmd = CMD_LEAVE;
1394 }
1395 break;
1396 case CMD_LIST:
1397 PRINT_VERBOSE(1, "Requesting backup list from device...\n");
1398 err = mobilebackup2_send_request(mobilebackup2, "List", uuid, NULL, NULL);
1399 if (err != MOBILEBACKUP2_E_SUCCESS) {
1400 printf("Error requesting backup list from device, error code %d\n", err);
1401 cmd = CMD_LEAVE;
1402 }
1403 break;
1404 default:
1405 break;
1406 }
1407
1408 /* close down the lockdown connection as it is no longer needed */
1409 if (client) {
1410 lockdownd_client_free(client);
1411 client = NULL;
1412 }
1413
1414 if (cmd != CMD_LEAVE) {
1415 /* reset backup status */
1416 int backup_ok = 0;
1417 plist_t message = NULL;
1418
1419 char *dlmsg = NULL;
1420 int file_count = 0;
1421 int errcode = 0;
1422 const char *errdesc = NULL;
1423
1424 /* process series of DLMessage* operations */
1425 do {
1426 if (dlmsg) {
1427 free(dlmsg);
1428 dlmsg = NULL;
1429 }
1430 mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
1431 if (!message || !dlmsg) {
1432 PRINT_VERBOSE(1, "Device is not ready yet. Going to try again in 2 seconds...\n");
1433 sleep(2);
1434 goto files_out;
1435 }
1436
1437 if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
1438 /* device wants to download files from the computer */
1439 mb2_handle_send_files(message, backup_directory);
1440 } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
1441 /* device wants to send files to the computer */
1442 file_count += mb2_handle_receive_files(message, backup_directory);
1443 } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
1444 /* list directory contents */
1445 mb2_handle_list_directory(message, backup_directory);
1446 } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
1447 /* make a directory */
1448 mb2_handle_make_directory(message, backup_directory);
1449 } else if (!strcmp(dlmsg, "DLMessageMoveFiles")) {
1450 /* perform a series of rename operations */
1451 plist_t moves = plist_array_get_item(message, 1);
1452 uint32_t cnt = plist_dict_get_size(moves);
1453 PRINT_VERBOSE(1, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
1454 plist_dict_iter iter = NULL;
1455 plist_dict_new_iter(moves, &iter);
1456 errcode = 0;
1457 errdesc = NULL;
1458 if (iter) {
1459 char *key = NULL;
1460 plist_t val = NULL;
1461 do {
1462 plist_dict_next_item(moves, iter, &key, &val);
1463 if (key && (plist_get_node_type(val) == PLIST_STRING)) {
1464 char *str = NULL;
1465 plist_get_string_val(val, &str);
1466 if (str) {
1467 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, str, NULL);
1468 g_free(str);
1469 gchar *oldpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, key, NULL);
1470
1471 remove(newpath);
1472 //fprintf(stderr, "Moving '%s' to '%s'\n", oldpath, newpath);
1473 if (rename(oldpath, newpath) < 0) {
1474 printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
1475 errcode = -errno;
1476 errdesc = strerror(errno);
1477 break;
1478 }
1479 g_free(oldpath);
1480 g_free(newpath);
1481 }
1482 free(key);
1483 key = NULL;
1484 }
1485 } while (val);
1486 free(iter);
1487 } else {
1488 errcode = -1;
1489 errdesc = "Could not create dict iterator";
1490 printf("Could not create dict iterator\n");
1491 }
1492 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
1493 if (err != MOBILEBACKUP2_E_SUCCESS) {
1494 printf("Could not send status response, error %d\n", err);
1495 }
1496 } else if (!strcmp(dlmsg, "DLMessageRemoveFiles")) {
1497 plist_t removes = plist_array_get_item(message, 1);
1498 uint32_t cnt = plist_array_get_size(removes);
1499 PRINT_VERBOSE(1, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
1500 uint32_t ii = 0;
1501 errcode = 0;
1502 errdesc = NULL;
1503 for (ii = 0; ii < cnt; ii++) {
1504 plist_t val = plist_array_get_item(removes, ii);
1505 if (plist_get_node_type(val) == PLIST_STRING) {
1506 char *str = NULL;
1507 plist_get_string_val(val, &str);
1508 if (str) {
1509 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, str, NULL);
1510 g_free(str);
1511 if (remove(newpath) < 0) {
1512 printf("Could not remove '%s': %s (%d)\n", newpath, strerror(errno), errno);
1513 //errcode = -errno;
1514 //errdesc = strerror(errno);
1515 }
1516 g_free(newpath);
1517 }
1518 }
1519 }
1520 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
1521 if (err != MOBILEBACKUP2_E_SUCCESS) {
1522 printf("Could not send status response, error %d\n", err);
1523 }
1524 } else if (!strcmp(dlmsg, "DLMessageCopyItem")) {
1525 plist_t srcpath = plist_array_get_item(message, 1);
1526 plist_t dstpath = plist_array_get_item(message, 2);
1527 errcode = 0;
1528 errdesc = NULL;
1529 if ((plist_get_node_type(srcpath) == PLIST_STRING) && (plist_get_node_type(dstpath) == PLIST_STRING)) {
1530 char *src = NULL;
1531 char *dst = NULL;
1532 plist_get_string_val(srcpath, &src);
1533 plist_get_string_val(dstpath, &dst);
1534 if (src && dst) {
1535 gchar *oldpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, src, NULL);
1536 gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, backup_directory, dst, NULL);
1537
1538 PRINT_VERBOSE(1, "Copying '%s' to '%s'\n", src, dst);
1539
1540 /* check that src exists */
1541 if (g_file_test(oldpath, G_FILE_TEST_IS_DIR)) {
1542 mb2_copy_directory_by_path(oldpath, newpath);
1543 } else if (g_file_test(oldpath, G_FILE_TEST_IS_REGULAR)) {
1544 mb2_copy_file_by_path(oldpath, newpath);
1545 }
1546
1547 g_free(newpath);
1548 g_free(oldpath);
1549 }
1550 g_free(src);
1551 g_free(dst);
1552 }
1553
1554 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
1555 if (err != MOBILEBACKUP2_E_SUCCESS) {
1556 printf("Could not send status response, error %d\n", err);
1557 }
1558 } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
1559 node_tmp = plist_array_get_item(message, 1);
1560 if (plist_get_node_type(node_tmp) != PLIST_DICT) {
1561 printf("Unknown message received!\n");
1562 }
1563 plist_t nn;
1564 int error_code = -1;
1565 nn = plist_dict_get_item(node_tmp, "ErrorCode");
1566 if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
1567 uint64_t ec = 0;
1568 plist_get_uint_val(nn, &ec);
1569 error_code = (uint32_t)ec;
1570 if (error_code == 0) {
1571 backup_ok = 1;
1572 }
1573 }
1574 nn = plist_dict_get_item(node_tmp, "ErrorDescription");
1575 char *str = NULL;
1576 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
1577 plist_get_string_val(nn, &str);
1578 }
1579 if (error_code != 0) {
1580 if (str) {
1581 printf("ErrorCode %d: %s\n", error_code, str);
1582 } else {
1583 printf("ErrorCode %d: (Unknown)\n", error_code);
1584 }
1585 }
1586 if (str) {
1587 free(str);
1588 }
1589 nn = plist_dict_get_item(node_tmp, "Content");
1590 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
1591 str = NULL;
1592 plist_get_string_val(nn, &str);
1593 PRINT_VERBOSE(1, "Content:\n");
1594 printf("%s", str);
1595 free(str);
1596 }
1597 break;
1598 }
1599
1600 /* print status */
1601 /*if (plist_array_get_size(message) >= 4) {
1602 plist_t pnode = plist_array_get_item(message, 4);
1603 if (pnode && (plist_get_node_type(pnode) == PLIST_REAL)) {
1604 double progress = 0.0;
1605 plist_get_real_val(pnode, &progress);
1606 printf("Progress: %f\n", progress);
1607 }
1608 }*/
1609
1610 if (message)
1611 plist_free(message);
1612 message = NULL;
1613
1614files_out:
1615 if (quit_flag > 0) {
1616 /* need to cancel the backup here */
1617 //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
1618
1619 /* remove any atomic Manifest.plist.tmp */
1620
1621 /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
1622 if (stat(manifest_path, &st) == 0)
1623 remove(manifest_path);*/
1624 break;
1625 }
1626 } while (1);
1627
1628 if (cmd == CMD_BACKUP) {
1629 PRINT_VERBOSE(1, "Received %d files from device.\n", file_count);
1630 if (mb2_status_check_snapshot_state(backup_directory, uuid, "finished")) {
1631 PRINT_VERBOSE(1, "Backup Successful.\n");
1632 } else {
1633 if (quit_flag) {
1634 PRINT_VERBOSE(1, "Backup Aborted.\n");
1635 } else {
1636 PRINT_VERBOSE(1, "Backup Failed.\n");
1637 }
1638 }
1639 } else if (cmd == CMD_RESTORE) {
1640 // TODO: check for success/failure
1641 PRINT_VERBOSE(1, "The device should reboot now to complete the process.\nRestore Successful.\n");
1642 }
1643 }
1644 if (lockfile) {
1645 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
1646 afc_file_close(afc, lockfile);
1647 lockfile = 0;
1648 do_post_notification(NP_SYNC_DID_FINISH);
1649 }
1650 } else {
1651 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP2_SERVICE_NAME);
1652 lockdownd_client_free(client);
1653 client = NULL;
1654 }
1655
1656 if (client) {
1657 lockdownd_client_free(client);
1658 client = NULL;
1659 }
1660
1661 if (afc)
1662 afc_client_free(afc);
1663
1664 if (np)
1665 np_client_free(np);
1666
1667 if (mobilebackup2)
1668 mobilebackup2_client_free(mobilebackup2);
1669
1670 idevice_free(phone);
1671
1672 return 0;
1673}
1674