summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2010-01-28 22:18:41 +0100
committerGravatar Martin Szulecki2010-01-29 02:16:00 +0100
commit96101a1231a4ddfeb40fd738a24e108a3a904048 (patch)
tree65a8f54354d9acbbba93dac2c8602d07e469482c /tools/idevicebackup.c
parent45b88ae3956de089fdc35605910f1359a1d3961c (diff)
downloadlibimobiledevice-96101a1231a4ddfeb40fd738a24e108a3a904048.tar.gz
libimobiledevice-96101a1231a4ddfeb40fd738a24e108a3a904048.tar.bz2
Global renames due to project rename to libimobiledevice
Diffstat (limited to 'tools/idevicebackup.c')
-rw-r--r--tools/idevicebackup.c953
1 files changed, 953 insertions, 0 deletions
diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c
new file mode 100644
index 0000000..d3b3ccc
--- /dev/null
+++ b/tools/idevicebackup.c
@@ -0,0 +1,953 @@
1/*
2 * idevicebackup.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
30#include <libimobiledevice/libimobiledevice.h>
31#include <libimobiledevice/lockdown.h>
32#include <libimobiledevice/mobilebackup.h>
33#include <libimobiledevice/notification_proxy.h>
34#include <libimobiledevice/afc.h>
35
36#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
37#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
38
39static mobilebackup_client_t mobilebackup = NULL;
40static lockdownd_client_t client = NULL;
41static idevice_t phone = NULL;
42
43static int quit_flag = 0;
44
45enum cmd_mode {
46 CMD_BACKUP,
47 CMD_RESTORE,
48 CMD_LEAVE
49};
50
51enum plist_format_t {
52 PLIST_FORMAT_XML,
53 PLIST_FORMAT_BINARY
54};
55
56enum device_link_file_status_t {
57 DEVICE_LINK_FILE_STATUS_NONE = 0,
58 DEVICE_LINK_FILE_STATUS_HUNK,
59 DEVICE_LINK_FILE_STATUS_LAST_HUNK
60};
61
62static void notify_cb(const char *notification)
63{
64 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
65 printf("User has aborted on-device\n");
66 quit_flag++;
67 } else {
68 printf("unhandled notification '%s' (TODO: implement)\n", notification);
69 }
70}
71
72static plist_t mobilebackup_factory_info_plist_new()
73{
74 /* gather data from lockdown */
75 GTimeVal tv = {0, 0};
76 plist_t value_node = NULL;
77 plist_t root_node = NULL;
78 char *uuid = NULL;
79 char *uuid_uppercase = NULL;
80
81 plist_t ret = plist_new_dict();
82
83 /* get basic device information in one go */
84 lockdownd_get_value(client, NULL, NULL, &root_node);
85
86 /* set fields we understand */
87 value_node = plist_dict_get_item(root_node, "BuildVersion");
88 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
89
90 value_node = plist_dict_get_item(root_node, "DeviceName");
91 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node));
92 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node));
93
94 /* FIXME: How is the GUID generated? */
95 plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
96
97 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
98 if (value_node)
99 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
100
101 g_get_current_time(&tv);
102 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec));
103
104 value_node = plist_dict_get_item(root_node, "ProductType");
105 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
106
107 value_node = plist_dict_get_item(root_node, "ProductVersion");
108 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node));
109
110 value_node = plist_dict_get_item(root_node, "SerialNumber");
111 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node));
112
113 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
114 idevice_get_uuid(phone, &uuid);
115 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
116
117 /* uppercase */
118 uuid_uppercase = g_ascii_strup(uuid, -1);
119 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase));
120 free(uuid_uppercase);
121 free(uuid);
122
123 /* FIXME: Embed files as <data> nodes */
124 plist_t files = plist_new_dict();
125 plist_dict_insert_item(ret, "iTunes Files", files);
126 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2"));
127
128 plist_free(root_node);
129
130 return ret;
131}
132
133static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
134{
135 GTimeVal tv = {0, 0};
136 plist_t node = NULL;
137
138 if (!info_plist)
139 return;
140
141 g_get_current_time(&tv);
142 node = plist_dict_get_item(info_plist, "Last Backup Date");
143 plist_set_date_val(node, tv.tv_sec, tv.tv_usec);
144
145 node = NULL;
146}
147
148static void buffer_read_from_filename(const char *filename, char **buffer, uint32_t *length)
149{
150 FILE *f;
151 uint64_t size;
152
153 f = fopen(filename, "rb");
154
155 fseek(f, 0, SEEK_END);
156 size = ftell(f);
157 rewind(f);
158
159 *buffer = (char*)malloc(sizeof(char)*size);
160 fread(*buffer, sizeof(char), size, f);
161 fclose(f);
162
163 *length = size;
164}
165
166static void buffer_write_to_filename(const char *filename, const char *buffer, uint32_t length)
167{
168 FILE *f;
169
170 f = fopen(filename, "ab");
171 fwrite(buffer, sizeof(char), length, f);
172 fclose(f);
173}
174
175static int plist_read_from_filename(plist_t *plist, const char *filename)
176{
177 char *buffer = NULL;
178 uint32_t length;
179
180 if (!filename)
181 return 0;
182
183 buffer_read_from_filename(filename, &buffer, &length);
184
185 if (!buffer) {
186 return 0;
187 }
188
189 if (memcmp(buffer, "bplist00", 8) == 0) {
190 plist_from_bin(buffer, length, plist);
191 } else {
192 plist_from_xml(buffer, length, plist);
193 }
194
195 free(buffer);
196
197 return 1;
198}
199
200static int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format)
201{
202 char *buffer = NULL;
203 uint32_t length;
204
205 if (!plist || !filename)
206 return 0;
207
208 if (format == PLIST_FORMAT_XML)
209 plist_to_xml(plist, &buffer, &length);
210 else if (format == PLIST_FORMAT_BINARY)
211 plist_to_bin(plist, &buffer, &length);
212 else
213 return 0;
214
215 buffer_write_to_filename(filename, buffer, length);
216
217 free(buffer);
218
219 return 1;
220}
221
222static int plist_strcmp(plist_t node, const char *str)
223{
224 char *buffer = NULL;
225 int ret = 0;
226
227 if (plist_get_node_type(node) != PLIST_STRING)
228 return ret;
229
230 plist_get_string_val(node, &buffer);
231 ret = strcmp(buffer, str);
232 free(buffer);
233
234 return ret;
235}
236
237static plist_t device_link_message_factory_process_message_new(plist_t content)
238{
239 plist_t ret = plist_new_array();
240 plist_array_append_item(ret, plist_new_string("DLMessageProcessMessage"));
241 plist_array_append_item(ret, content);
242 return ret;
243}
244
245static void mobilebackup_cancel_backup_with_error(const char *reason)
246{
247 plist_t node = plist_new_dict();
248 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageError"));
249 plist_dict_insert_item(node, "BackupErrorReasonKey", plist_new_string(reason));
250
251 plist_t message = device_link_message_factory_process_message_new(node);
252
253 mobilebackup_send(mobilebackup, message);
254
255 plist_free(message);
256 message = NULL;
257}
258
259static plist_t mobilebackup_factory_backup_file_received_new()
260{
261 plist_t node = plist_new_dict();
262 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived"));
263 return device_link_message_factory_process_message_new(node);
264}
265
266static gchar *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension)
267{
268 gchar *filename = g_strconcat(name, extension, NULL);
269 gchar *path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, filename, NULL);
270 g_free(filename);
271 return path;
272}
273
274static void mobilebackup_write_status(const char *path, int status)
275{
276 struct stat st;
277 plist_t status_plist = plist_new_dict();
278 plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status));
279 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist");
280
281 if (stat(file_path, &st) == 0)
282 remove(file_path);
283
284 plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML);
285
286 plist_free(status_plist);
287 status_plist = NULL;
288
289 g_free(file_path);
290}
291
292static int mobilebackup_info_is_current_device(plist_t info)
293{
294 plist_t value_node = NULL;
295 plist_t node = NULL;
296 plist_t root_node = NULL;
297 int ret = 0;
298
299 if (!info)
300 return ret;
301
302 if (plist_get_node_type(info) != PLIST_DICT)
303 return ret;
304
305 /* get basic device information in one go */
306 lockdownd_get_value(client, NULL, NULL, &root_node);
307
308 /* verify UUID */
309 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
310 node = plist_dict_get_item(info, "Target Identifier");
311
312 if(plist_compare_node_value(value_node, node))
313 ret = 1;
314 else {
315 printf("Info.plist: UniqueDeviceID does not match.\n");
316 }
317
318 /* verify SerialNumber */
319 if (ret == 1) {
320 value_node = plist_dict_get_item(root_node, "SerialNumber");
321 node = plist_dict_get_item(info, "Serial Number");
322
323 if(plist_compare_node_value(value_node, node))
324 ret = 1;
325 else {
326 printf("Info.plist: SerialNumber does not match.\n");
327 ret = 0;
328 }
329 }
330
331 /* verify ProductVersion to prevent using backup with different OS version */
332 if (ret == 1) {
333 value_node = plist_dict_get_item(root_node, "ProductVersion");
334 node = plist_dict_get_item(info, "Product Version");
335
336 if(plist_compare_node_value(value_node, node))
337 ret = 1;
338 else {
339 printf("Info.plist: ProductVersion does not match.\n");
340 ret = 0;
341 }
342 }
343
344 plist_free(root_node);
345 root_node = NULL;
346
347 value_node = NULL;
348 node = NULL;
349
350 return ret;
351}
352
353static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash)
354{
355 int ret = 0;
356 gchar *path = mobilebackup_build_path(backup_directory, hash, ".mddata");
357 printf("Removing \"%s\"... ", path);
358 if (!remove( path ))
359 ret = 1;
360 else
361 ret = 0;
362
363 g_free(path);
364
365 if (!ret)
366 return ret;
367
368 path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
369 printf("Removing \"%s\"... ", path);
370 if (!remove( path ))
371 ret = 1;
372 else
373 ret = 0;
374
375 g_free(path);
376
377 return ret;
378}
379
380static void do_post_notification(const char *notification)
381{
382 uint16_t nport = 0;
383 np_client_t np;
384
385 if (!client) {
386 if (lockdownd_client_new_with_handshake(phone, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) {
387 return;
388 }
389 }
390
391 lockdownd_start_service(client, NP_SERVICE_NAME, &nport);
392 if (nport) {
393 np_client_new(phone, nport, &np);
394 if (np) {
395 np_post_notification(np, notification);
396 np_client_free(np);
397 }
398 } else {
399 printf("Could not start %s\n", NP_SERVICE_NAME);
400 }
401}
402
403/**
404 * signal handler function for cleaning up properly
405 */
406static void clean_exit(int sig)
407{
408 fprintf(stderr, "Exiting...\n");
409 quit_flag++;
410}
411
412static void print_usage(int argc, char **argv)
413{
414 char *name = NULL;
415 name = strrchr(argv[0], '/');
416 printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0]));
417 printf("Create or restore backup from the current or specified directory.\n\n");
418 printf("commands:\n");
419 printf(" backup\tSaves a device backup into DIRECTORY\n");
420 printf(" restore\tRestores a device backup from DIRECTORY.\n\n");
421 printf("options:\n");
422 printf(" -d, --debug\t\tenable communication debugging\n");
423 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
424 printf(" -h, --help\t\tprints usage information\n");
425 printf("\n");
426}
427
428int main(int argc, char *argv[])
429{
430 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
431 int i;
432 char uuid[41];
433 uint16_t port = 0;
434 uuid[0] = 0;
435 int cmd = -1;
436 int is_full_backup = 0;
437 char *backup_directory = NULL;
438 struct stat st;
439 plist_t node = NULL;
440 plist_t node_tmp = NULL;
441 plist_t manifest_plist = NULL;
442 plist_t info_plist = NULL;
443 char *buffer = NULL;
444 uint64_t length = 0;
445 uint64_t backup_total_size = 0;
446 enum device_link_file_status_t file_status;
447 uint64_t c = 0;
448
449 /* we need to exit cleanly on running backups and restores or we cause havok */
450 signal(SIGINT, clean_exit);
451 signal(SIGQUIT, clean_exit);
452 signal(SIGTERM, clean_exit);
453 signal(SIGPIPE, SIG_IGN);
454
455 /* parse cmdline args */
456 for (i = 1; i < argc; i++) {
457 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
458 idevice_set_debug_level(1);
459 continue;
460 }
461 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) {
462 i++;
463 if (!argv[i] || (strlen(argv[i]) != 40)) {
464 print_usage(argc, argv);
465 return 0;
466 }
467 strcpy(uuid, argv[i]);
468 continue;
469 }
470 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
471 print_usage(argc, argv);
472 return 0;
473 }
474 else if (!strcmp(argv[i], "backup")) {
475 cmd = CMD_BACKUP;
476 }
477 else if (!strcmp(argv[i], "restore")) {
478 cmd = CMD_RESTORE;
479 }
480 else if (backup_directory == NULL) {
481 backup_directory = argv[i];
482 }
483 else {
484 print_usage(argc, argv);
485 return 0;
486 }
487 }
488
489 /* verify options */
490 if (cmd == -1) {
491 printf("No command specified.\n");
492 print_usage(argc, argv);
493 return -1;
494 }
495
496 if (backup_directory == NULL) {
497 printf("No target backup directory specified.\n");
498 print_usage(argc, argv);
499 return -1;
500 }
501
502 /* verify if passed backup directory exists */
503 if (stat(backup_directory, &st) != 0) {
504 printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
505 return -1;
506 }
507
508 /* restore directory must contain an Info.plist */
509 char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist");
510 if (cmd == CMD_RESTORE) {
511 if (stat(info_path, &st) != 0) {
512 g_free(info_path);
513 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
514 return -1;
515 }
516 }
517
518 printf("Backup directory is \"%s\"\n", backup_directory);
519
520 if (uuid[0] != 0) {
521 ret = idevice_new(&phone, uuid);
522 if (ret != IDEVICE_E_SUCCESS) {
523 printf("No device found with uuid %s, is it plugged in?\n", uuid);
524 return -1;
525 }
526 }
527 else
528 {
529 ret = idevice_new(&phone, NULL);
530 if (ret != IDEVICE_E_SUCCESS) {
531 printf("No device found, is it plugged in?\n");
532 return -1;
533 }
534 }
535
536 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicebackup")) {
537 idevice_free(phone);
538 return -1;
539 }
540
541 /* start notification_proxy */
542 np_client_t np = NULL;
543 ret = lockdownd_start_service(client, NP_SERVICE_NAME, &port);
544 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
545 np_client_new(phone, port, &np);
546 np_set_notify_callback(np, notify_cb);
547 const char *noties[5] = {
548 NP_SYNC_CANCEL_REQUEST,
549 NP_SYNC_SUSPEND_REQUEST,
550 NP_SYNC_RESUME_REQUEST,
551 NP_BACKUP_DOMAIN_CHANGED,
552 NULL
553 };
554 np_observe_notifications(np, noties);
555 } else {
556 printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
557 }
558
559 /* start AFC, we need this for the lock file */
560 afc_client_t afc = NULL;
561 port = 0;
562 ret = lockdownd_start_service(client, "com.apple.afc", &port);
563 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
564 afc_client_new(phone, port, &afc);
565 }
566
567 /* start syslog_relay service and retrieve port */
568 port = 0;
569 ret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &port);
570 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
571 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port);
572 mobilebackup_client_new(phone, port, &mobilebackup);
573
574 /* check abort conditions */
575 if (quit_flag > 0) {
576 printf("Aborting backup. Cancelled by user.\n");
577 cmd = CMD_LEAVE;
578 }
579
580 /* verify existing Info.plist */
581 if (stat(info_path, &st) == 0) {
582 printf("Reading Info.plist from backup.\n");
583 plist_read_from_filename(&info_plist, info_path);
584
585 if (cmd == CMD_BACKUP) {
586 if (mobilebackup_info_is_current_device(info_plist)) {
587 /* update the last backup time within Info.plist */
588 mobilebackup_info_update_last_backup_date(info_plist);
589 remove(info_path);
590 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
591 } else {
592 printf("Aborting backup. Backup is not compatible with the current device.\n");
593 cmd = CMD_LEAVE;
594 }
595 }
596 } else {
597 is_full_backup = 1;
598 }
599
600 do_post_notification(NP_SYNC_WILL_START);
601 uint64_t lockfile = 0;
602 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
603 if (lockfile) {
604 do_post_notification(NP_SYNC_LOCK_REQUEST);
605 if (afc_file_lock(afc, lockfile, AFC_LOCK_EX) == AFC_E_SUCCESS) {
606 do_post_notification(NP_SYNC_DID_START);
607 } else {
608 afc_file_close(afc, lockfile);
609 lockfile = 0;
610 }
611 }
612
613 switch(cmd) {
614 case CMD_BACKUP:
615 printf("Starting backup...\n");
616 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
617 /* TODO: verify battery on AC enough battery remaining */
618
619 /* Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
620 /* create new Info.plist on new backups */
621 if (is_full_backup) {
622 printf("Creating Info.plist for new backup.\n");
623 info_plist = mobilebackup_factory_info_plist_new();
624 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
625 }
626
627 g_free(info_path);
628
629 /* Manifest.plist (backup manifest (backup state)) */
630 char *manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist");
631
632 /* read the last Manifest.plist */
633 if (!is_full_backup) {
634 printf("Reading existing Manifest.\n");
635 plist_read_from_filename(&manifest_plist, manifest_path);
636 }
637
638 plist_free(info_plist);
639 info_plist = NULL;
640
641 /* close down the lockdown connection as it is no longer needed */
642 if (client) {
643 lockdownd_client_free(client);
644 client = NULL;
645 }
646
647 /* create Status.plist with failed status for now */
648 mobilebackup_write_status(backup_directory, 0);
649
650 /* request backup from device with manifest from last backup */
651 printf("Requesting backup from device...\n");
652
653 node = plist_new_dict();
654
655 if (manifest_plist)
656 plist_dict_insert_item(node, "BackupManifestKey", manifest_plist);
657
658 plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/"));
659 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest"));
660 plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6"));
661
662 plist_t message = device_link_message_factory_process_message_new(node);
663 mobilebackup_send(mobilebackup, message);
664 plist_free(message);
665 message = NULL;
666 node = NULL;
667
668 /* get response */
669 int backup_ok = 0;
670 mobilebackup_receive(mobilebackup, &message);
671
672 node = plist_array_get_item(message, 0);
673 if (!plist_strcmp(node, "DLMessageProcessMessage")) {
674 node = plist_array_get_item(message, 1);
675 node = plist_dict_get_item(node, "BackupMessageTypeKey");
676 if (node && !plist_strcmp(node, "BackupMessageBackupReplyOK")) {
677 printf("Device accepts manifest and will send backup data now...\n");
678 backup_ok = 1;
679 printf("Acknowledging...\n");
680 if (is_full_backup)
681 printf("Full backup mode.\n");
682 else
683 printf("Incremental backup mode.\n");
684 printf("Please wait. Device prepares backup data...\n");
685 /* send it back for ACK */
686 mobilebackup_send(mobilebackup, message);
687 }
688 } else {
689 printf("ERROR: Unhandled message received!\n");
690 }
691 plist_free(message);
692 message = NULL;
693
694 if (!backup_ok) {
695 printf("ERROR: Device rejected to start the backup process.\n");
696 break;
697 }
698
699 /* reset backup status */
700 backup_ok = 0;
701
702 /* receive and save DLSendFile files and metadata, ACK each */
703 int file_index = 0;
704 int hunk_index = 0;
705 uint64_t backup_real_size = 0;
706 char *file_path = NULL;
707 char *file_ext = NULL;
708 char *filename_mdinfo = NULL;
709 char *filename_mddata = NULL;
710 char *filename_source = NULL;
711 char *format_size = NULL;
712 gboolean is_manifest = FALSE;
713 uint8_t b = 0;
714
715 /* process series of DLSendFile messages */
716 do {
717 mobilebackup_receive(mobilebackup, &message);
718 node = plist_array_get_item(message, 0);
719
720 /* get out if we don't get a DLSendFile */
721 if (plist_strcmp(node, "DLSendFile"))
722 break;
723
724 node_tmp = plist_array_get_item(message, 2);
725
726 /* first message hunk contains total backup size */
727 if (hunk_index == 0) {
728 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey");
729 if (node) {
730 plist_get_uint_val(node, &backup_total_size);
731 format_size = g_format_size_for_display(backup_total_size);
732 printf("Backup data requires %s on the disk.\n", format_size);
733 g_free(format_size);
734 }
735 }
736
737 /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */
738 node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
739 plist_get_uint_val(node, &c);
740 file_status = c;
741
742 /* get source filename */
743 node = plist_dict_get_item(node_tmp, "BackupManifestKey");
744 b = 0;
745 if (node) {
746 plist_get_bool_val(node, &b);
747 }
748 is_manifest = (b == 1) ? TRUE: FALSE;
749
750 /* check if we completed a file */
751 if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (!is_manifest)) {
752 /* get source filename */
753 node = plist_dict_get_item(node_tmp, "DLFileSource");
754 plist_get_string_val(node, &filename_source);
755
756 /* increase received size */
757 node = plist_dict_get_item(node_tmp, "DLFileAttributesKey");
758 node = plist_dict_get_item(node, "FileSize");
759 plist_get_uint_val(node, &length);
760 backup_real_size += length;
761
762 format_size = g_format_size_for_display(backup_real_size);
763 printf("(%s", format_size);
764 g_free(format_size);
765
766 format_size = g_format_size_for_display(backup_total_size);
767 printf("/%s): ", format_size);
768 g_free(format_size);
769
770 printf("Received file %s... ", filename_source);
771
772 if (filename_source)
773 free(filename_source);
774
775 /* save <hash>.mdinfo */
776 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
777 if (node) {
778 node = plist_dict_get_item(node_tmp, "DLFileDest");
779 plist_get_string_val(node, &file_path);
780
781 filename_mdinfo = mobilebackup_build_path(backup_directory, file_path, ".mdinfo");
782
783 /* remove any existing file */
784 if (stat(filename_mdinfo, &st) != 0)
785 remove(filename_mdinfo);
786
787 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
788 plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY);
789
790 g_free(filename_mdinfo);
791 }
792
793 file_index++;
794 }
795
796 /* save <hash>.mddata */
797 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
798 if (node_tmp && file_path) {
799 node = plist_dict_get_item(node_tmp, "DLFileDest");
800 plist_get_string_val(node, &file_path);
801
802 filename_mddata = mobilebackup_build_path(backup_directory, file_path, is_manifest ? NULL: ".mddata");
803
804 /* if this is the first hunk, remove any existing file */
805 if (stat(filename_mddata, &st) != 0)
806 remove(filename_mddata);
807
808 /* get file data hunk */
809 node_tmp = plist_array_get_item(message, 1);
810 plist_get_data_val(node_tmp, &buffer, &length);
811
812 buffer_write_to_filename(filename_mddata, buffer, length);
813
814 /* activate currently sent manifest */
815 if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (is_manifest)) {
816 rename(filename_mddata, manifest_path);
817 }
818
819 free(buffer);
820 buffer = NULL;
821
822 g_free(filename_mddata);
823 }
824
825 hunk_index++;
826
827 if (file_ext)
828 free(file_ext);
829
830 if (message)
831 plist_free(message);
832 message = NULL;
833
834 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
835 if (!is_manifest)
836 printf("DONE\n");
837
838 /* acknowlegdge that we received the file */
839 message = mobilebackup_factory_backup_file_received_new();
840 mobilebackup_send(mobilebackup, message);
841 plist_free(message);
842 message = NULL;
843 }
844
845 if (quit_flag > 0) {
846 /* need to cancel the backup here */
847 mobilebackup_cancel_backup_with_error("Cancelling DLSendFile");
848
849 /* remove any atomic Manifest.plist.tmp */
850 if (manifest_path)
851 g_free(manifest_path);
852
853 manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
854 if (stat(manifest_path, &st) == 0)
855 remove(manifest_path);
856 break;
857 }
858 } while (1);
859
860 printf("Received %d files from device.\n", file_index);
861
862 if (!quit_flag && !plist_strcmp(node, "DLMessageProcessMessage")) {
863 node_tmp = plist_array_get_item(message, 1);
864 node = plist_dict_get_item(node_tmp, "BackupMessageTypeKey");
865 /* check if we received the final "backup finished" message */
866 if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) {
867 /* backup finished */
868
869 /* process BackupFilesToDeleteKey */
870 node = plist_dict_get_item(node_tmp, "BackupFilesToDeleteKey");
871 if (node) {
872 length = plist_array_get_size(node);
873 i = 0;
874 while ((node_tmp = plist_array_get_item(node, i++)) != NULL) {
875 plist_get_string_val(node_tmp, &file_path);
876
877 if (mobilebackup_delete_backup_file_by_hash(backup_directory, file_path)) {
878 printf("DONE\n");
879 } else
880 printf("FAILED\n");
881 }
882 }
883
884 /* save last valid Manifest.plist */
885 node_tmp = plist_array_get_item(message, 1);
886 manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey");
887 if (manifest_plist) {
888 remove(manifest_path);
889 printf("Storing Manifest.plist...\n");
890 plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML);
891 }
892
893 backup_ok = 1;
894 }
895 }
896
897 if (backup_ok) {
898 /* Status.plist (Info on how the backup process turned out) */
899 printf("Backup Successful.\n");
900 mobilebackup_write_status(backup_directory, 1);
901 } else {
902 printf("Backup Failed.\n");
903 }
904
905 if (manifest_path)
906 g_free(manifest_path);
907
908 break;
909 case CMD_RESTORE:
910 printf("Restoring backup is NOT IMPLEMENTED.\n");
911 /* verify battery on AC enough battery remaining */
912 /* request restore from device with manifest (BackupMessageRestoreMigrate) */
913 /* read mddata/mdinfo files and send to devices using DLSendFile */
914 /* signal restore finished message to device */
915 /* close down lockdown connection as it is no longer needed */
916 lockdownd_client_free(client);
917 client = NULL;
918 break;
919 case CMD_LEAVE:
920 default:
921 break;
922 }
923 if (lockfile) {
924 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
925 afc_file_close(afc, lockfile);
926 lockfile = 0;
927 do_post_notification(NP_SYNC_DID_FINISH);
928 }
929 } else {
930 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME);
931 lockdownd_client_free(client);
932 client = NULL;
933 }
934
935 if (client) {
936 lockdownd_client_free(client);
937 client = NULL;
938 }
939
940 if (afc)
941 afc_client_free(afc);
942
943 if (np)
944 np_client_free(np);
945
946 if (mobilebackup)
947 mobilebackup_client_free(mobilebackup);
948
949 idevice_free(phone);
950
951 return 0;
952}
953