From 79fcf91cc86574257539fb45b0b6d1a1c64852c5 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Sun, 24 Jan 2010 02:45:22 +0100 Subject: Add service code and a backup tool for mobilebackup support --- include/Makefile.am | 3 +- include/libiphone/mobilebackup.h | 55 ++++++ src/Makefile.am | 3 +- src/mobilebackup.c | 131 ++++++++++++++ src/mobilebackup.h | 31 ++++ tools/Makefile.am | 8 +- tools/iphonebackup.c | 380 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 608 insertions(+), 3 deletions(-) create mode 100644 include/libiphone/mobilebackup.h create mode 100644 src/mobilebackup.c create mode 100644 src/mobilebackup.h create mode 100644 tools/iphonebackup.c diff --git a/include/Makefile.am b/include/Makefile.am index 54dae0e..a4246cf 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -4,4 +4,5 @@ nobase_include_HEADERS = libiphone/libiphone.h \ libiphone/notification_proxy.h \ libiphone/installation_proxy.h \ libiphone/sbservices.h \ - libiphone/mobilesync.h + libiphone/mobilesync.h \ + libiphone/mobilebackup.h diff --git a/include/libiphone/mobilebackup.h b/include/libiphone/mobilebackup.h new file mode 100644 index 0000000..8db6758 --- /dev/null +++ b/include/libiphone/mobilebackup.h @@ -0,0 +1,55 @@ +/** + * @file libiphone/mobilebackup.h + * @brief MobileBackup Implementation + * \internal + * + * Copyright (c) 2009 Martin Szulecki All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IMOBILEBACKUP_H +#define IMOBILEBACKUP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Error Codes */ +#define MOBILEBACKUP_E_SUCCESS 0 +#define MOBILEBACKUP_E_INVALID_ARG -1 +#define MOBILEBACKUP_E_PLIST_ERROR -2 +#define MOBILEBACKUP_E_MUX_ERROR -3 +#define MOBILEBACKUP_E_BAD_VERSION -4 + +#define MOBILEBACKUP_E_UNKNOWN_ERROR -256 + +typedef int16_t mobilebackup_error_t; + +struct mobilebackup_client_int; +typedef struct mobilebackup_client_int *mobilebackup_client_t; + +mobilebackup_error_t mobilebackup_client_new(iphone_device_t device, uint16_t port, mobilebackup_client_t * client); +mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client); +mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t *plist); +mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 2e92382..6c29020 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,4 +14,5 @@ libiphone_la_SOURCES = iphone.c iphone.h \ notification_proxy.c notification_proxy.h\ installation_proxy.c installation_proxy.h\ sbservices.c sbservices.h\ - mobilesync.c mobilesync.h + mobilesync.c mobilesync.h\ + mobilebackup.c mobilebackup.h diff --git a/src/mobilebackup.c b/src/mobilebackup.c new file mode 100644 index 0000000..5b81c7f --- /dev/null +++ b/src/mobilebackup.c @@ -0,0 +1,131 @@ +/* + * mobilebackup.c + * Contains functions for the built-in MobileBackup client. + * + * Copyright (c) 2009 Martin Szulecki All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "mobilebackup.h" +#include "device_link_service.h" +#include "debug.h" + +#define MBACKUP_VERSION_INT1 100 +#define MBACKUP_VERSION_INT2 0 + +/** + * Convert an device_link_service_error_t value to an mobilebackup_error_t value. + * Used internally to get correct error codes when using device_link_service stuff. + * + * @param err An device_link_service_error_t error code + * + * @return A matching mobilebackup_error_t error code, + * MOBILEBACKUP_E_UNKNOWN_ERROR otherwise. + */ +static mobilebackup_error_t mobilebackup_error(device_link_service_error_t err) +{ + switch (err) { + case DEVICE_LINK_SERVICE_E_SUCCESS: + return MOBILEBACKUP_E_SUCCESS; + case DEVICE_LINK_SERVICE_E_INVALID_ARG: + return MOBILEBACKUP_E_INVALID_ARG; + case DEVICE_LINK_SERVICE_E_PLIST_ERROR: + return MOBILEBACKUP_E_PLIST_ERROR; + case DEVICE_LINK_SERVICE_E_MUX_ERROR: + return MOBILEBACKUP_E_MUX_ERROR; + case DEVICE_LINK_SERVICE_E_BAD_VERSION: + return MOBILEBACKUP_E_BAD_VERSION; + default: + break; + } + return MOBILEBACKUP_E_UNKNOWN_ERROR; +} + +mobilebackup_error_t mobilebackup_client_new(iphone_device_t device, uint16_t port, + mobilebackup_client_t * client) +{ + if (!device || port == 0 || !client || *client) + return MOBILEBACKUP_E_INVALID_ARG; + + device_link_service_client_t dlclient = NULL; + mobilebackup_error_t ret = mobilebackup_error(device_link_service_client_new(device, port, &dlclient)); + if (ret != MOBILEBACKUP_E_SUCCESS) { + return ret; + } + + mobilebackup_client_t client_loc = (mobilebackup_client_t) malloc(sizeof(struct mobilebackup_client_int)); + client_loc->parent = dlclient; + + /* perform handshake */ + ret = mobilebackup_error(device_link_service_version_exchange(dlclient, MBACKUP_VERSION_INT1, MBACKUP_VERSION_INT2)); + if (ret != MOBILEBACKUP_E_SUCCESS) { + debug_info("version exchange failed, error %d", ret); + mobilebackup_client_free(client_loc); + return ret; + } + + *client = client_loc; + + return ret; +} + +mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client) +{ + if (!client) + return MOBILEBACKUP_E_INVALID_ARG; + device_link_service_disconnect(client->parent); + mobilebackup_error_t err = mobilebackup_error(device_link_service_client_free(client->parent)); + free(client); + return err; +} + +/** Polls the iPhone for MobileBackup data. + * + * @param client The MobileBackup client + * @param plist A pointer to the location where the plist should be stored + * + * @return an error code + */ +mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist) +{ + if (!client) + return MOBILEBACKUP_E_INVALID_ARG; + mobilebackup_error_t ret = mobilebackup_error(device_link_service_receive(client->parent, plist)); + return ret; +} + +/** Sends MobileBackup data to the iPhone + * + * @note This function is low-level and should only be used if you need to send + * a new type of message. + * + * @param client The MobileBackup client + * @param plist The location of the plist to send + * + * @return an error code + */ +mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist) +{ + if (!client || !plist) + return MOBILEBACKUP_E_INVALID_ARG; + return mobilebackup_error(device_link_service_send(client->parent, plist)); +} + diff --git a/src/mobilebackup.h b/src/mobilebackup.h new file mode 100644 index 0000000..04ebc45 --- /dev/null +++ b/src/mobilebackup.h @@ -0,0 +1,31 @@ + /* + * mobilebackup.h + * Definitions for the mobilebackup service + * + * Copyright (c) 2009 Martin Szulecki All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef MOBILEBACKUP_H +#define MOBILEBACKUP_H + +#include "libiphone/mobilebackup.h" +#include "device_link_service.h" + +struct mobilebackup_client_int { + device_link_service_client_t parent; +}; + +#endif diff --git a/tools/Makefile.am b/tools/Makefile.am index 7f1be1c..d19ef0c 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -3,7 +3,7 @@ INCLUDES = -I$(top_srcdir)/include AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS) AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) -bin_PROGRAMS = iphone_id iphoneinfo iphonesyslog +bin_PROGRAMS = iphone_id iphoneinfo iphonesyslog iphonebackup iphoneinfo_SOURCES = iphoneinfo.c iphoneinfo_CFLAGS = $(AM_CFLAGS) @@ -19,3 +19,9 @@ iphone_id_SOURCES = iphone_id.c iphone_id_CFLAGS = $(AM_CFLAGS) iphone_id_LDFLAGS = $(AM_LDFLAGS) iphone_id_LDADD = ../src/libiphone.la + +iphonebackup_SOURCES = iphonebackup.c +iphonebackup_CFLAGS = $(AM_CFLAGS) +iphonebackup_LDFLAGS = $(AM_LDFLAGS) +iphonebackup_LDADD = ../src/libiphone.la + diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c new file mode 100644 index 0000000..8e29160 --- /dev/null +++ b/tools/iphonebackup.c @@ -0,0 +1,380 @@ +/* + * iphonebackup.c + * Command line interface to use the device's backup and restore service + * + * Copyright (c) 2009 Martin Szulecki All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" + +static int quit_flag = 0; + +enum cmd_mode { + CMD_BACKUP, + CMD_RESTORE +}; + +/* +Backup Process Communication: +-------------------------------------------------------------------------------- +* Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt +* Verify battery on AC enough battery remaining +* ValidatePair with device for TrustedHost ability +> DLMessageVersionExchange +< DLMessageVersionExchange - DLVersionsOk +> DLMessageDeviceReady +< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupRequest +> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupReplyOK +... +> DLSendFile +< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFileReceived +... +> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFinished + + +*/ + +/* +Restore Process Communication: +-------------------------------------------------------------------------------- +* Verify battery on AC enough battery remaining +* ValidatePair with device for TrustedHost ability +> DLMessageVersionExchange +< DLMessageVersionExchange - DLVersionsOk +> DLMessageDeviceReady +< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreMigrate +... +DLSendFile +... +< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreReplyOK +*/ + +static plist_t mobilebackup_factory_metadata() +{ +/* +Metadata key is: + + Path + Library/SMS/sms.db + Version + 3.0 + Greylist + + Domain + HomeDomain + +*/ +/* + + Metadata + + YnBsaXN0MDDUAQIDBAUGBwhUUGF0aFdWZXJzaW9uWEdyZXlsaXN0VkRvbWFp + bl8QEkxpYnJhcnkvU01TL3Ntcy5kYlMzLjAIWkhvbWVEb21haW4IERYeJy5D + R0gAAAAAAAABAQAAAAAAAAAJAAAAAAAAAAAAAAAAAAAAUw== + + StorageVersion + 1.0 + Version + 3.0 + AuthVersion + 1.0 + IsEncrypted + + +*/ +} + +/** + * Generates a manifest data plist with all files and corresponding hashes + */ +static plist_t mobilebackup_factory_manifest_data_plist() +{ + plist_t manifest_data = plist_dict_new(); + /* + File hash is: + sha1(-) + */ + /* + Data hash is: + sha1() + */ + +/* + + DeviceId + 7a7b570ee169f02c43d3893f0d661cf7a32e1cf5 + Version + 6.2 + Files + + 3d0d7e5fb2ce288813306e4d4636395e047a3d28 + + ModificationTime + 2009-12-29T02:12:17Z + FileLength + 131072 + Domain + HomeDomain + DataHash + + MfpSk+qw+RAJqLNTJI81tntvrwc= + + Group ID + 501 + User ID + 501 + Mode + 420 + + + DeviceICCID + 89492060399209300736 + +*/ + + return manifest_data; +} + +/** + * Generates a manifest plist with all needed information and hashes + */ +static plist_t mobilebackup_factory_manifest_plist() +{ + plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); + plist_t manifest = plist_dict_new(); + + /* + AuthSignature Hash is: + sha1() + */ + +/* + + BackupManifestKey + + AuthVersion + 2.0 + AuthSignature + + WBdfjcZWg/u/Bpn7aKDJC68UZF4= + + IsEncrypted + 0 + Data + + ... + + + BackupComputerBasePathKey + / + BackupMessageTypeKey + BackupMessageBackupRequest + BackupProtocolVersion + 1.6 + +*/ + + return manifest; +} + +/** + * signal handler function for cleaning up properly + */ +static void clean_exit(int sig) +{ + fprintf(stderr, "Exiting...\n"); + quit_flag++; +} + +static void print_usage(int argc, char **argv) +{ + char *name = NULL; + name = strrchr(argv[0], '/'); + printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0])); + printf("Create or restore backup from the current or specified directory.\n\n"); + printf("commands:\n"); + printf(" backup\tSaves a device backup into DIRECTORY\n"); + printf(" restore\tRestores a device backup from DIRECTORY.\n\n"); + printf("options:\n"); + printf(" -d, --debug\t\tenable communication debugging\n"); + printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); + printf(" -h, --help\t\tprints usage information\n"); + printf("\n"); +} + +static mobilebackup_client_t mobilebackup = NULL; +static lockdownd_client_t client = NULL; +static iphone_device_t phone = NULL; + +int main(int argc, char *argv[]) +{ + iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; + int i; + char uuid[41]; + uint16_t port = 0; + uuid[0] = 0; + int cmd = -1; + char *backup_directory = NULL; + + /* we need to exit cleanly on running backups and restores or we cause havok */ + signal(SIGINT, clean_exit); + signal(SIGQUIT, clean_exit); + signal(SIGTERM, clean_exit); + signal(SIGPIPE, SIG_IGN); + + /* parse cmdline args */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { + iphone_set_debug_level(1); + continue; + } + else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { + i++; + if (!argv[i] || (strlen(argv[i]) != 40)) { + print_usage(argc, argv); + return 0; + } + strcpy(uuid, argv[i]); + continue; + } + else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { + print_usage(argc, argv); + return 0; + } + else if (!strcmp(argv[i], "backup")) { + cmd = CMD_BACKUP; + } + else if (!strcmp(argv[i], "restore")) { + cmd = CMD_RESTORE; + } + else if (backup_directory == NULL) { + backup_directory = argv[i]; + } + else { + print_usage(argc, argv); + return 0; + } + } + + /* verify options */ + if (cmd == -1) { + printf("No command specified.\n"); + print_usage(argc, argv); + return -1; + } + + if (backup_directory == NULL) { + printf("No target backup directory specified.\n"); + print_usage(argc, argv); + return -1; + } + + printf("Backup directory is \"%s\"\n", backup_directory); + + if (uuid[0] != 0) { + ret = iphone_device_new(&phone, uuid); + if (ret != IPHONE_E_SUCCESS) { + printf("No device found with uuid %s, is it plugged in?\n", uuid); + return -1; + } + } + else + { + ret = iphone_device_new(&phone, NULL); + if (ret != IPHONE_E_SUCCESS) { + printf("No device found, is it plugged in?\n"); + return -1; + } + } + + if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "iphonebackup")) { + iphone_device_free(phone); + return -1; + } + + /* start syslog_relay service and retrieve port */ + ret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &port); + if ((ret == LOCKDOWN_E_SUCCESS) && port) { + printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); + mobilebackup_client_new(phone, port, &mobilebackup); + + /* TODO: Command implementations */ + switch(cmd) { + case CMD_BACKUP: + printf("TODO: Creating backup...\n"); +/* +Create target directory: +MobileSync/Backup/-YYYYMMDD-HHMMSS/ +*/ + +/* +Create: Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) +Create:Manifest.plist (backup manifest (backup state)) +*/ + lockdownd_client_free(client); +/* +Receive: +... +.mddata (Raw filedata) +.mdinfo (backup file information) +... +Create: Status.plist (Info on how the backup process turned out) +*/ + +/* +- Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt +- Verify battery on AC enough battery remaining +- Request backup from device with manifest (BackupMessageBackupRequest) +- Receive and save DLSendFile files and metadata, ACK each +- Wait until received final backup finished message +*/ + break; + case CMD_RESTORE: + printf("TODO: Restoring backup...\n"); +/* +- Verify battery on AC enough battery remaining +- Request restore from device (BackupMessageRestoreMigrate) +- Read mddata files and send to devices using DLSendFile +- Signal restore finished message to device +*/ + lockdownd_client_free(client); + break; + default: + break; + } + } else { + printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); + lockdownd_client_free(client); + } + + if (mobilebackup) + mobilebackup_client_free(mobilebackup); + iphone_device_free(phone); + + return 0; +} + -- cgit v1.1-32-gdbae From 4ded564cbc58bd513ece9ff9f8c5a6948759bfb7 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Sun, 24 Jan 2010 02:45:51 +0100 Subject: Remove duplicated dumping of plists in mobilesync --- src/mobilesync.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/mobilesync.c b/src/mobilesync.c index 3492673..15614b5 100644 --- a/src/mobilesync.c +++ b/src/mobilesync.c @@ -109,16 +109,6 @@ mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plis if (!client) return MOBILESYNC_E_INVALID_ARG; mobilesync_error_t ret = mobilesync_error(device_link_service_receive(client->parent, plist)); -#ifndef STRIP_DEBUG_CODE - if (ret != MOBILESYNC_E_SUCCESS) { - return ret; - } - char *XMLContent = NULL; - uint32_t length = 0; - plist_to_xml(*plist, &XMLContent, &length); - debug_info("plist size: %i\nbuffer :\n%s", length, XMLContent); - free(XMLContent); -#endif return ret; } @@ -136,13 +126,5 @@ mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist) { if (!client || !plist) return MOBILESYNC_E_INVALID_ARG; - -#ifndef STRIP_DEBUG_CODE - char *XMLContent = NULL; - uint32_t length = 0; - plist_to_xml(plist, &XMLContent, &length); - debug_info("plist size: %i\nbuffer :\n%s", length, XMLContent); - free(XMLContent); -#endif return mobilesync_error(device_link_service_send(client->parent, plist)); } -- cgit v1.1-32-gdbae From 9a6fa170d226d1557e89f5e5252d1e0c61158ff5 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Sun, 24 Jan 2010 02:46:38 +0100 Subject: Add function to handle the ProcessMessage in the DeviceLink service --- src/device_link_service.c | 20 ++++++++++++++++++++ src/device_link_service.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/device_link_service.c b/src/device_link_service.c index e1155a5..c791b03 100644 --- a/src/device_link_service.c +++ b/src/device_link_service.c @@ -252,6 +252,26 @@ device_link_service_error_t device_link_service_disconnect(device_link_service_c return err; } +device_link_service_error_t device_link_service_process_message(device_link_service_client_t client, plist_t message) +{ + if (!client || !message) + return DEVICE_LINK_SERVICE_E_INVALID_ARG; + + if (plist_get_node_type(message) != PLIST_DICT) + return DEVICE_LINK_SERVICE_E_INVALID_ARG; + + plist_t array = plist_new_array(); + plist_array_append_item(array, plist_new_string("DLMessageProcessMessage")); + plist_array_append_item(array, message); + + device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; + if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + err = DEVICE_LINK_SERVICE_E_MUX_ERROR; + } + plist_free(array); + return err; +} + /** * Generic device link service send function. * diff --git a/src/device_link_service.h b/src/device_link_service.h index e14d897..8345d57 100644 --- a/src/device_link_service.h +++ b/src/device_link_service.h @@ -44,6 +44,7 @@ typedef int16_t device_link_service_error_t; device_link_service_error_t device_link_service_client_new(iphone_device_t device, uint16_t port, device_link_service_client_t *client); device_link_service_error_t device_link_service_client_free(device_link_service_client_t client); device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor); +device_link_service_error_t device_link_service_process_message(device_link_service_client_t client, plist_t message); device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client); device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist); device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist); -- cgit v1.1-32-gdbae From 8f133b0e0e89f4a44b80a442f70ef28cc7efa3df Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Sun, 24 Jan 2010 21:53:28 +0100 Subject: Initial backup implementation, device is sending the backup now --- tools/iphonebackup.c | 504 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 360 insertions(+), 144 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 8e29160..611c0f2 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,10 @@ #define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" +static mobilebackup_client_t mobilebackup = NULL; +static lockdownd_client_t client = NULL; +static iphone_device_t phone = NULL; + static int quit_flag = 0; enum cmd_mode { @@ -38,43 +43,68 @@ enum cmd_mode { CMD_RESTORE }; -/* -Backup Process Communication: --------------------------------------------------------------------------------- -* Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt -* Verify battery on AC enough battery remaining -* ValidatePair with device for TrustedHost ability -> DLMessageVersionExchange -< DLMessageVersionExchange - DLVersionsOk -> DLMessageDeviceReady -< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupRequest -> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupReplyOK -... -> DLSendFile -< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFileReceived -... -> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFinished +static plist_t mobilebackup_factory_info_plist() +{ + /* gather data from lockdown */ + GTimeVal tv = {0, 0}; + plist_t value_node = NULL; + plist_t root_node = NULL; + char *uuid = NULL; + char *uuid_uppercase = NULL; + plist_t ret = plist_new_dict(); -*/ + /* get basic device information in one go */ + lockdownd_get_value(client, NULL, NULL, &root_node); -/* -Restore Process Communication: --------------------------------------------------------------------------------- -* Verify battery on AC enough battery remaining -* ValidatePair with device for TrustedHost ability -> DLMessageVersionExchange -< DLMessageVersionExchange - DLVersionsOk -> DLMessageDeviceReady -< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreMigrate -... -DLSendFile -... -< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreReplyOK -*/ + /* set fields we understand */ + value_node = plist_dict_get_item(root_node, "BuildVersion"); + plist_dict_insert_item(ret, "Build Version", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "DeviceName"); + plist_dict_insert_item(ret, "Device Name", plist_copy(value_node)); + plist_dict_insert_item(ret, "Display Name", plist_copy(value_node)); + + /* FIXME: How is the GUID generated? */ + plist_dict_insert_item(ret, "GUID", plist_new_string("---")); + + value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity"); + if (value_node) + plist_dict_insert_item(ret, "IMEI", plist_copy(value_node)); + + g_get_current_time(&tv); + plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec)); -static plist_t mobilebackup_factory_metadata() + value_node = plist_dict_get_item(root_node, "ProductType"); + plist_dict_insert_item(ret, "Product Type", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "ProductVersion"); + plist_dict_insert_item(ret, "Product Version", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "SerialNumber"); + plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); + iphone_device_get_uuid(phone, &uuid); + plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid)); + + /* uppercase */ + uuid_uppercase = g_ascii_strup(uuid, -1); + plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase)); + free(uuid_uppercase); + free(uuid); + + plist_t files = plist_new_dict(); + /* FIXME: Embed files as nodes */ + plist_dict_insert_item(ret, "iTunes Files", files); + plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2")); + + return ret; +} + +static plist_t mobilebackup_factory_metadata_plist() { + plist_t ret = NULL; /* Metadata key is: @@ -106,6 +136,7 @@ Metadata key is: */ + return ret; } /** @@ -113,92 +144,165 @@ Metadata key is: */ static plist_t mobilebackup_factory_manifest_data_plist() { - plist_t manifest_data = plist_dict_new(); - /* - File hash is: - sha1(-) - */ - /* - Data hash is: - sha1() - */ + plist_t ret = NULL; + plist_t value_node = NULL; + char *uuid = NULL; + GTimeVal tv = {0, 0}; -/* - - DeviceId - 7a7b570ee169f02c43d3893f0d661cf7a32e1cf5 - Version - 6.2 - Files - - 3d0d7e5fb2ce288813306e4d4636395e047a3d28 - - ModificationTime - 2009-12-29T02:12:17Z - FileLength - 131072 - Domain - HomeDomain - DataHash - - MfpSk+qw+RAJqLNTJI81tntvrwc= - - Group ID - 501 - User ID - 501 - Mode - 420 - - - DeviceICCID - 89492060399209300736 - -*/ + ret = plist_new_dict(); - return manifest_data; + /* get basic device information in one go */ + lockdownd_get_value(client, NULL, "IntegratedCircuitCardIdentity", &value_node); + + iphone_device_get_uuid(phone, &uuid); + plist_dict_insert_item(ret, "DeviceId", plist_new_string(uuid)); + free(uuid); + + plist_dict_insert_item(ret, "Version", plist_new_string("6.2")); + + /* TODO: add all Applications */ + + /* TODO: add all Files */ + plist_t files = plist_new_dict(); + + /* single file entry */ + plist_t info_node = plist_new_dict(); + g_get_current_time(&tv); + plist_dict_insert_item(info_node, "ModificationTime", plist_new_date(tv.tv_sec, tv.tv_usec)); + plist_dict_insert_item(info_node, "FileLength", plist_new_uint(131072)); + plist_dict_insert_item(info_node, "Domain", plist_new_string("HomeDomain")); + + /* FIXME: calculate correct data hash */ + /* Data hash is: sha1() */ + plist_dict_insert_item(info_node, "DataHash", plist_new_data(NULL, 0)); + plist_dict_insert_item(info_node, "Group ID", plist_new_uint(501)); + plist_dict_insert_item(info_node, "User ID", plist_new_uint(501)); + plist_dict_insert_item(info_node, "Mode ID", plist_new_uint(420)); + + /* FIXME: calculate correct file hash */ + /* File hash is: sha1(-) */ + plist_dict_insert_item(files, "3d0d7e5fb2ce288813306e4d4636395e047a3d28", info_node); + plist_dict_insert_item(ret, "Files", files); + + /* last node with ICCID */ + if (value_node) + plist_dict_insert_item(ret, "DeviceICCID", &value_node); + + return ret; } /** * Generates a manifest plist with all needed information and hashes */ -static plist_t mobilebackup_factory_manifest_plist() +static plist_t mobilebackup_factory_manifest_plist(plist_t manifest_data) { - plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); - plist_t manifest = plist_dict_new(); + char *buffer = NULL; + char *s = NULL; + uint32_t length; + unsigned char sha1[20]; + gsize sha1_len; + GChecksum *checksum; + plist_t ret = NULL; - /* - AuthSignature Hash is: - sha1() - */ + if (!manifest_data) + return ret; -/* - - BackupManifestKey - - AuthVersion - 2.0 - AuthSignature - - WBdfjcZWg/u/Bpn7aKDJC68UZF4= - - IsEncrypted - 0 - Data - - ... - - - BackupComputerBasePathKey - / - BackupMessageTypeKey - BackupMessageBackupRequest - BackupProtocolVersion - 1.6 - -*/ + ret = plist_new_dict(); + plist_dict_insert_item(ret, "AuthVersion", plist_new_string("2.0")); - return manifest; + /* AuthSignature Hash is: sha1() */ + plist_to_bin(manifest_data, &buffer, &length); + + sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1); + checksum = g_checksum_new(G_CHECKSUM_SHA1); + g_checksum_update(checksum, (guchar *)buffer, length); + g_checksum_get_digest(checksum, sha1, &sha1_len); + s = (char *)g_checksum_get_string(checksum); + printf("SHA1 AuthSignature: %s\n", s); + plist_dict_insert_item(ret, "AuthSignature", plist_new_data((char*)sha1, sha1_len)); + g_checksum_free(checksum); + + + plist_dict_insert_item(ret, "IsEncrypted", plist_new_uint(0)); + plist_dict_insert_item(ret, "Data", plist_new_data(buffer, length)); + + free(buffer); + + return ret; +} + +enum plist_format_t { + PLIST_FORMAT_XML, + PLIST_FORMAT_BINARY +}; + +static int plist_read_from_filename(char *filename, plist_t *plist) +{ + return 1; +} + +static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format) +{ + char *buffer = NULL; + uint32_t length; + FILE *f; + + if (!plist || !filename) + return 0; + + if (format == PLIST_FORMAT_XML) + plist_to_xml(plist, &buffer, &length); + else if (format == PLIST_FORMAT_BINARY) + plist_to_bin(plist, &buffer, &length); + else + return 0; + + f = fopen(filename, "wb"); + fwrite(buffer, sizeof(char), length, f); + fclose(f); + + free(buffer); + + return 1; +} + +static int plist_strcmp(plist_t node, const char *str) +{ + char *buffer = NULL; + int ret = 0; + + if (plist_get_node_type(node) != PLIST_STRING) + return ret; + + plist_get_string_val(node, &buffer); + ret = strcmp(buffer, str); + free(buffer); + + return ret; +} + +static void mobilebackup_write_status(char *path, int status) +{ + plist_t status_plist = plist_new_dict(); + plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); + char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL); + plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); + g_free(file_path); + plist_free(status_plist); +} + +static void debug_plist(plist_t plist) +{ + char *buffer = NULL; + uint32_t length = 0; + + if (!plist) + return; + + plist_to_xml(plist, &buffer, &length); + + printf("Printing %i bytes plist:\n%s\n", length, buffer); + free(buffer); } /** @@ -226,10 +330,6 @@ static void print_usage(int argc, char **argv) printf("\n"); } -static mobilebackup_client_t mobilebackup = NULL; -static lockdownd_client_t client = NULL; -static iphone_device_t phone = NULL; - int main(int argc, char *argv[]) { iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; @@ -239,6 +339,8 @@ int main(int argc, char *argv[]) uuid[0] = 0; int cmd = -1; char *backup_directory = NULL; + struct stat st; + plist_t node = NULL; /* we need to exit cleanly on running backups and restores or we cause havok */ signal(SIGINT, clean_exit); @@ -293,6 +395,22 @@ int main(int argc, char *argv[]) return -1; } + /* verify if passed backup directory exists */ + if (stat(backup_directory, &st) != 0) { + printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory); + return -1; + } + + /* restore directory must contain an Info.plist */ + char *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Info.plist", NULL); + if (cmd == CMD_RESTORE) { + if (stat(info_path, &st) != 0) { + g_free(info_path); + printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory); + return -1; + } + } + printf("Backup directory is \"%s\"\n", backup_directory); if (uuid[0] != 0) { @@ -322,46 +440,140 @@ int main(int argc, char *argv[]) printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); mobilebackup_client_new(phone, port, &mobilebackup); - /* TODO: Command implementations */ switch(cmd) { case CMD_BACKUP: - printf("TODO: Creating backup...\n"); -/* -Create target directory: -MobileSync/Backup/-YYYYMMDD-HHMMSS/ -*/ - -/* -Create: Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) -Create:Manifest.plist (backup manifest (backup state)) -*/ + printf("Starting backup...\n"); + /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ + /* TODO: verify battery on AC enough battery remaining */ + + /* ????: create target directory: MobileSync/Backup/-YYYYMMDD-HHMMSS/ */ + + /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ + printf("Creating \"%s/Info.plist\".\n", backup_directory); + plist_t info_plist = mobilebackup_factory_info_plist(); + plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); + g_free(info_path); + + /* create Manifest.plist (backup manifest (backup state)) */ + printf("Creating \"%s/Manifest.plist\".\n", backup_directory); + char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); + plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); + plist_t manifest_plist = mobilebackup_factory_manifest_plist(manifest_data); + plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); + g_free(manifest_path); + + /* create Status.plist with failed status for now */ + mobilebackup_write_status(backup_directory, 0); + + /* close down lockdown connection as it is no longer needed */ lockdownd_client_free(client); -/* -Receive: -... -.mddata (Raw filedata) -.mdinfo (backup file information) -... -Create: Status.plist (Info on how the backup process turned out) -*/ + client = NULL; + + /* request backup from device with manifest */ + printf("Sending manifest and requesting backup.\n"); + + node = plist_new_dict(); + plist_dict_insert_item(node, "BackupManifestKey", manifest_plist); + plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/")); + plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); + plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6")); + + plist_t message = plist_new_array(); + plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); + plist_array_append_item(message, node); + + mobilebackup_send(mobilebackup, message); + plist_free(message); + message = NULL; + + /* get response */ + int backup_ok = 0; + mobilebackup_receive(mobilebackup, &message); + node = plist_array_get_item(message, 0); + if (!plist_strcmp(node, "DLMessageProcessMessage")) { + node = plist_array_get_item(message, 1); + node = plist_dict_get_item(node, "BackupMessageTypeKey"); + if (node && !plist_strcmp(node, "BackupMessageBackupReplyOK")) { + printf("Device accepts manifest and will send backup data now...\n"); + backup_ok = 1; + printf("Acknowledging...\n"); + /* send it back for ACK */ + mobilebackup_send(mobilebackup, message); + } + } else { + printf("Unhandled message received!\n"); + debug_plist(message); + } + plist_free(message); + message = NULL; + + if (!backup_ok) { + printf("ERROR: Device rejected to start the backup process.\n"); + break; + } + + /* receive and save DLSendFile files and metadata, ACK each */ + int file_index = 0; + do { + mobilebackup_receive(mobilebackup, &message); + node = plist_array_get_item(message, 0); + if (plist_strcmp(node, "DLSendFile")) + break; + + printf("Receiving file %d...\n", file_index); + /* TODO: save .mdinfo */ + /* TODO: save .mddata */ + debug_plist(message); + plist_free(message); + message = NULL; + + if (quit_flag) { + /* FIXME: need to cancel the backup here */ + break; + } + + /* acknowlegdge that we received the file */ + node = plist_new_dict(); + plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived")); + + message = plist_new_array(); + plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); + plist_array_append_item(message, node); + mobilebackup_send(mobilebackup, message); + + plist_free(message); + message = NULL; + + file_index++; + } while (!plist_strcmp(node, "DLSendFile")); + + printf("Received %d files from device.\n", file_index); + + if (!plist_strcmp(node, "DLMessageProcessMessage")) { + node = plist_array_get_item(message, 1); + node = plist_dict_get_item(node, "BackupMessageTypeKey"); + /* wait until received final backup finished message */ + if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) { + /* backup finished */ + /* create: Status.plist (Info on how the backup process turned out) */ + printf("Backup Successful.\n"); + mobilebackup_write_status(backup_directory, 1); + } + } + + if (node) + plist_free(node); -/* -- Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt -- Verify battery on AC enough battery remaining -- Request backup from device with manifest (BackupMessageBackupRequest) -- Receive and save DLSendFile files and metadata, ACK each -- Wait until received final backup finished message -*/ break; case CMD_RESTORE: - printf("TODO: Restoring backup...\n"); -/* -- Verify battery on AC enough battery remaining -- Request restore from device (BackupMessageRestoreMigrate) -- Read mddata files and send to devices using DLSendFile -- Signal restore finished message to device -*/ + printf("Restoring backup...\n"); + /* verify battery on AC enough battery remaining */ + /* request restore from device (BackupMessageRestoreMigrate) */ + /* read mddata files and send to devices using DLSendFile */ + /* signal restore finished message to device */ + /* close down lockdown connection as it is no longer needed */ lockdownd_client_free(client); + client = NULL; break; default: break; @@ -369,8 +581,12 @@ Create: Status.plist (Info on how the backup process turned out) } else { printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); lockdownd_client_free(client); + client = NULL; } + if (client) + lockdownd_client_free(client); + if (mobilebackup) mobilebackup_client_free(mobilebackup); iphone_device_free(phone); -- cgit v1.1-32-gdbae From 9f1677222b06f41c4ab309918511f3c3918a822e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 01:31:10 +0100 Subject: Implement storing received files as backup including new Manifest --- tools/iphonebackup.c | 315 +++++++++++++++++++++++++-------------------------- 1 file changed, 157 insertions(+), 158 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 611c0f2..1ad4116 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -40,7 +40,8 @@ static int quit_flag = 0; enum cmd_mode { CMD_BACKUP, - CMD_RESTORE + CMD_RESTORE, + CMD_LEAVE }; static plist_t mobilebackup_factory_info_plist() @@ -102,150 +103,24 @@ static plist_t mobilebackup_factory_info_plist() return ret; } -static plist_t mobilebackup_factory_metadata_plist() -{ - plist_t ret = NULL; -/* -Metadata key is: - - Path - Library/SMS/sms.db - Version - 3.0 - Greylist - - Domain - HomeDomain - -*/ -/* - - Metadata - - YnBsaXN0MDDUAQIDBAUGBwhUUGF0aFdWZXJzaW9uWEdyZXlsaXN0VkRvbWFp - bl8QEkxpYnJhcnkvU01TL3Ntcy5kYlMzLjAIWkhvbWVEb21haW4IERYeJy5D - R0gAAAAAAAABAQAAAAAAAAAJAAAAAAAAAAAAAAAAAAAAUw== - - StorageVersion - 1.0 - Version - 3.0 - AuthVersion - 1.0 - IsEncrypted - - -*/ - return ret; -} - -/** - * Generates a manifest data plist with all files and corresponding hashes - */ -static plist_t mobilebackup_factory_manifest_data_plist() -{ - plist_t ret = NULL; - plist_t value_node = NULL; - char *uuid = NULL; - GTimeVal tv = {0, 0}; - - ret = plist_new_dict(); - - /* get basic device information in one go */ - lockdownd_get_value(client, NULL, "IntegratedCircuitCardIdentity", &value_node); - - iphone_device_get_uuid(phone, &uuid); - plist_dict_insert_item(ret, "DeviceId", plist_new_string(uuid)); - free(uuid); - - plist_dict_insert_item(ret, "Version", plist_new_string("6.2")); - - /* TODO: add all Applications */ - - /* TODO: add all Files */ - plist_t files = plist_new_dict(); - - /* single file entry */ - plist_t info_node = plist_new_dict(); - g_get_current_time(&tv); - plist_dict_insert_item(info_node, "ModificationTime", plist_new_date(tv.tv_sec, tv.tv_usec)); - plist_dict_insert_item(info_node, "FileLength", plist_new_uint(131072)); - plist_dict_insert_item(info_node, "Domain", plist_new_string("HomeDomain")); - - /* FIXME: calculate correct data hash */ - /* Data hash is: sha1() */ - plist_dict_insert_item(info_node, "DataHash", plist_new_data(NULL, 0)); - plist_dict_insert_item(info_node, "Group ID", plist_new_uint(501)); - plist_dict_insert_item(info_node, "User ID", plist_new_uint(501)); - plist_dict_insert_item(info_node, "Mode ID", plist_new_uint(420)); - - /* FIXME: calculate correct file hash */ - /* File hash is: sha1(-) */ - plist_dict_insert_item(files, "3d0d7e5fb2ce288813306e4d4636395e047a3d28", info_node); - plist_dict_insert_item(ret, "Files", files); - - /* last node with ICCID */ - if (value_node) - plist_dict_insert_item(ret, "DeviceICCID", &value_node); - - return ret; -} - -/** - * Generates a manifest plist with all needed information and hashes - */ -static plist_t mobilebackup_factory_manifest_plist(plist_t manifest_data) -{ - char *buffer = NULL; - char *s = NULL; - uint32_t length; - unsigned char sha1[20]; - gsize sha1_len; - GChecksum *checksum; - plist_t ret = NULL; - - if (!manifest_data) - return ret; - - ret = plist_new_dict(); - plist_dict_insert_item(ret, "AuthVersion", plist_new_string("2.0")); - - /* AuthSignature Hash is: sha1() */ - plist_to_bin(manifest_data, &buffer, &length); - - sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1); - checksum = g_checksum_new(G_CHECKSUM_SHA1); - g_checksum_update(checksum, (guchar *)buffer, length); - g_checksum_get_digest(checksum, sha1, &sha1_len); - s = (char *)g_checksum_get_string(checksum); - printf("SHA1 AuthSignature: %s\n", s); - plist_dict_insert_item(ret, "AuthSignature", plist_new_data((char*)sha1, sha1_len)); - g_checksum_free(checksum); - - - plist_dict_insert_item(ret, "IsEncrypted", plist_new_uint(0)); - plist_dict_insert_item(ret, "Data", plist_new_data(buffer, length)); - - free(buffer); - - return ret; -} - enum plist_format_t { PLIST_FORMAT_XML, PLIST_FORMAT_BINARY }; -static int plist_read_from_filename(char *filename, plist_t *plist) +static void buffer_to_filename(char *filename, char *buffer, uint32_t length) { - return 1; + FILE *f; + + f = fopen(filename, "ab"); + fwrite(buffer, sizeof(char), length, f); + fclose(f); } static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format) { char *buffer = NULL; uint32_t length; - FILE *f; if (!plist || !filename) return 0; @@ -257,9 +132,7 @@ static int plist_write_to_filename(plist_t plist, char *filename, enum plist_for else return 0; - f = fopen(filename, "wb"); - fwrite(buffer, sizeof(char), length, f); - fclose(f); + buffer_to_filename(filename, buffer, length); free(buffer); @@ -281,25 +154,50 @@ static int plist_strcmp(plist_t node, const char *str) return ret; } +static plist_t device_link_message_factory_process_message_new(plist_t content) +{ + plist_t ret = plist_new_array(); + plist_array_append_item(ret, plist_new_string("DLMessageProcessMessage")); + plist_array_append_item(ret, content); + return ret; +} + +static void mobilebackup_cancel_backup_with_error(const char *reason) +{ + plist_t node = plist_new_dict(); + plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageError")); + plist_dict_insert_item(node, "BackupErrorReasonKey", plist_new_string(reason)); + + plist_t message = device_link_message_factory_process_message_new(node); + + mobilebackup_send(mobilebackup, message); + + plist_free(message); + message = NULL; +} + static void mobilebackup_write_status(char *path, int status) { + struct stat st; plist_t status_plist = plist_new_dict(); plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL); + if (stat(file_path, &st) == 0) + remove(file_path); plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); g_free(file_path); plist_free(status_plist); } -static void debug_plist(plist_t plist) +static void debug_plist(plist_t a) { char *buffer = NULL; uint32_t length = 0; - if (!plist) + if (a == NULL) return; - plist_to_xml(plist, &buffer, &length); + plist_to_xml(a, &buffer, &length); printf("Printing %i bytes plist:\n%s\n", length, buffer); free(buffer); @@ -341,6 +239,9 @@ int main(int argc, char *argv[]) char *backup_directory = NULL; struct stat st; plist_t node = NULL; + plist_t node_tmp = NULL; + char *buffer = NULL; + uint64_t length = 0; /* we need to exit cleanly on running backups and restores or we cause havok */ signal(SIGINT, clean_exit); @@ -440,6 +341,11 @@ int main(int argc, char *argv[]) printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); mobilebackup_client_new(phone, port, &mobilebackup); + if (quit_flag > 0) { + printf("Aborting backup. Cancelled by user.\n"); + cmd = CMD_LEAVE; + } + switch(cmd) { case CMD_BACKUP: printf("Starting backup...\n"); @@ -451,29 +357,32 @@ int main(int argc, char *argv[]) /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ printf("Creating \"%s/Info.plist\".\n", backup_directory); plist_t info_plist = mobilebackup_factory_info_plist(); + if (stat(info_path, &st) == 0) + remove(info_path); plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); g_free(info_path); + if (client) { + lockdownd_client_free(client); + client = NULL; + } + /* create Manifest.plist (backup manifest (backup state)) */ printf("Creating \"%s/Manifest.plist\".\n", backup_directory); char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); - plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); - plist_t manifest_plist = mobilebackup_factory_manifest_plist(manifest_data); - plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); - g_free(manifest_path); + plist_t manifest_plist = NULL; + if (stat(manifest_path, &st) == 0) + remove(manifest_path); /* create Status.plist with failed status for now */ mobilebackup_write_status(backup_directory, 0); - /* close down lockdown connection as it is no longer needed */ - lockdownd_client_free(client); - client = NULL; - - /* request backup from device with manifest */ + /* request backup from device with manifest from last backup */ printf("Sending manifest and requesting backup.\n"); node = plist_new_dict(); - plist_dict_insert_item(node, "BackupManifestKey", manifest_plist); + if (manifest_plist) + plist_dict_insert_item(node, "BackupManifestKey", manifest_plist); plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/")); plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6")); @@ -514,21 +423,66 @@ int main(int argc, char *argv[]) /* receive and save DLSendFile files and metadata, ACK each */ int file_index = 0; + char *file_path = NULL; + char *file_ext = NULL; + char *filename_mdinfo = NULL; + char *filename_mddata = NULL; + char *filename_source = NULL; do { mobilebackup_receive(mobilebackup, &message); node = plist_array_get_item(message, 0); if (plist_strcmp(node, "DLSendFile")) break; - printf("Receiving file %d...\n", file_index); - /* TODO: save .mdinfo */ - /* TODO: save .mddata */ - debug_plist(message); + /* get source filename and print it */ + node_tmp = plist_array_get_item(message, 2); + node = plist_dict_get_item(node_tmp, "DLFileSource"); + plist_get_string_val(node, &filename_source); + printf("Received file %s...", filename_source); + free(filename_source); + + /* save .mdinfo */ + node = plist_dict_get_item(node_tmp, "BackupFileInfo"); + if (node) { + node = plist_dict_get_item(node_tmp, "DLFileDest"); + plist_get_string_val(node, &file_path); + file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL); + filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + node = plist_dict_get_item(node_tmp, "BackupFileInfo"); + plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY); + g_free(file_ext); + g_free(filename_mdinfo); + } + + /* save .mddata */ + node = plist_dict_get_item(node_tmp, "BackupFileInfo"); + if (node_tmp && file_path) { + node = plist_dict_get_item(node_tmp, "DLFileDest"); + plist_get_string_val(node, &file_path); + file_ext = (char *)g_strconcat(file_path, ".mddata", NULL); + filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + node_tmp = plist_array_get_item(message, 1); + plist_get_data_val(node_tmp, &buffer, &length); + buffer_to_filename(filename_mddata, buffer, length); + free(buffer); + buffer = NULL; + g_free(filename_mddata); + } + + printf("DONE\n"); + + if (file_ext) + free(file_ext); + plist_free(message); message = NULL; - if (quit_flag) { - /* FIXME: need to cancel the backup here */ + if (quit_flag > 0) { + /* need to cancel the backup here */ + mobilebackup_cancel_backup_with_error("Cancelling DLSendFile"); + + plist_free(message); + message = NULL; break; } @@ -550,17 +504,58 @@ int main(int argc, char *argv[]) printf("Received %d files from device.\n", file_index); if (!plist_strcmp(node, "DLMessageProcessMessage")) { - node = plist_array_get_item(message, 1); - node = plist_dict_get_item(node, "BackupMessageTypeKey"); + node_tmp = plist_array_get_item(message, 1); + node = plist_dict_get_item(node_tmp, "BackupMessageTypeKey"); /* wait until received final backup finished message */ if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) { /* backup finished */ + + /* process BackupFilesToDeleteKey */ + node = plist_dict_get_item(node_tmp, "BackupFilesToDeleteKey"); + if (node) { + length = plist_array_get_size(node); + i = 0; + while ((node_tmp = plist_array_get_item(node, i++)) != NULL) { + plist_get_string_val(node_tmp, &file_path); + + file_ext = (char *)g_strconcat(file_path, ".mddata", NULL); + filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + g_free(file_ext); + printf("Removing \"%s\"... ", filename_mddata); + if (!remove( filename_mddata )) { + printf("DONE\n"); + } else + printf("FAILED\n"); + + file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL); + filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + g_free(file_ext); + printf("Removing \"%s\"... ", filename_mdinfo); + if (!remove( filename_mdinfo )) { + printf("DONE\n"); + } else + printf("FAILED\n"); + } + } + + /* save new Manifest.plist */ + node_tmp = plist_array_get_item(message, 1); + manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey"); + if (manifest_plist) { + if (stat(manifest_path, &st) != 0) + remove(manifest_path); + plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); + } + /* create: Status.plist (Info on how the backup process turned out) */ printf("Backup Successful.\n"); mobilebackup_write_status(backup_directory, 1); } } + if (manifest_path) + g_free(manifest_path); + if (node) plist_free(node); @@ -575,6 +570,7 @@ int main(int argc, char *argv[]) lockdownd_client_free(client); client = NULL; break; + case CMD_LEAVE: default: break; } @@ -584,11 +580,14 @@ int main(int argc, char *argv[]) client = NULL; } - if (client) + if (client) { lockdownd_client_free(client); + client = NULL; + } if (mobilebackup) mobilebackup_client_free(mobilebackup); + iphone_device_free(phone); return 0; -- cgit v1.1-32-gdbae From a6bb8a89e42b6b301214d5f6e52522e5be7870eb Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 02:14:40 +0100 Subject: Print progress of backup --- tools/iphonebackup.c | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 1ad4116..927d195 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -242,6 +242,8 @@ int main(int argc, char *argv[]) plist_t node_tmp = NULL; char *buffer = NULL; uint64_t length = 0; + uint64_t backup_total_size = 0; + uint64_t c = 0; /* we need to exit cleanly on running backups and restores or we cause havok */ signal(SIGINT, clean_exit); @@ -352,8 +354,6 @@ int main(int argc, char *argv[]) /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ /* TODO: verify battery on AC enough battery remaining */ - /* ????: create target directory: MobileSync/Backup/-YYYYMMDD-HHMMSS/ */ - /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ printf("Creating \"%s/Info.plist\".\n", backup_directory); plist_t info_plist = mobilebackup_factory_info_plist(); @@ -370,6 +370,7 @@ int main(int argc, char *argv[]) /* create Manifest.plist (backup manifest (backup state)) */ printf("Creating \"%s/Manifest.plist\".\n", backup_directory); char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); + /* FIXME: We should read the last Manifest.plist and send it to the device */ plist_t manifest_plist = NULL; if (stat(manifest_path, &st) == 0) remove(manifest_path); @@ -406,6 +407,7 @@ int main(int argc, char *argv[]) printf("Device accepts manifest and will send backup data now...\n"); backup_ok = 1; printf("Acknowledging...\n"); + printf("Please wait. Device prepares backup data...\n"); /* send it back for ACK */ mobilebackup_send(mobilebackup, message); } @@ -423,23 +425,51 @@ int main(int argc, char *argv[]) /* receive and save DLSendFile files and metadata, ACK each */ int file_index = 0; + uint64_t backup_real_size = 0; char *file_path = NULL; char *file_ext = NULL; char *filename_mdinfo = NULL; char *filename_mddata = NULL; char *filename_source = NULL; + char *format_size = NULL; do { mobilebackup_receive(mobilebackup, &message); node = plist_array_get_item(message, 0); if (plist_strcmp(node, "DLSendFile")) break; - /* get source filename and print it */ node_tmp = plist_array_get_item(message, 2); - node = plist_dict_get_item(node_tmp, "DLFileSource"); - plist_get_string_val(node, &filename_source); - printf("Received file %s...", filename_source); - free(filename_source); + + /* first message contains total backup size */ + if (file_index == 0) { + node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey"); + plist_get_uint_val(node, &backup_total_size); + format_size = g_format_size_for_display(backup_total_size); + printf("Backup will need %s on disk.\n", format_size); + g_free(format_size); + } + + /* print out "received" if DLFileStatusKey is 2 (last file piece) */ + node = plist_dict_get_item(node_tmp, "DLFileStatusKey"); + plist_get_uint_val(node, &c); + if (c == 2) { + node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); + node = plist_dict_get_item(node, "FileSize"); + plist_get_uint_val(node, &length); + backup_real_size += length; + format_size = g_format_size_for_display(backup_real_size); + printf("(%s", format_size); + g_free(format_size); + format_size = g_format_size_for_display(backup_total_size); + printf("/%s): ", format_size); + g_free(format_size); + + /* get source filename and print it */ + node = plist_dict_get_item(node_tmp, "DLFileSource"); + plist_get_string_val(node, &filename_source); + printf("Received file %s... ", filename_source); + free(filename_source); + } /* save .mdinfo */ node = plist_dict_get_item(node_tmp, "BackupFileInfo"); @@ -469,7 +499,8 @@ int main(int argc, char *argv[]) g_free(filename_mddata); } - printf("DONE\n"); + if (c == 2) + printf("DONE\n"); if (file_ext) free(file_ext); -- cgit v1.1-32-gdbae From d6cbfafa5372557d41bb4f97eb3f8e33ee92fa8e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 02:48:13 +0100 Subject: Do not print progress for Manifest.plist --- tools/iphonebackup.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 927d195..f7be8ca 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -432,6 +432,7 @@ int main(int argc, char *argv[]) char *filename_mddata = NULL; char *filename_source = NULL; char *format_size = NULL; + gboolean is_manifest = FALSE; do { mobilebackup_receive(mobilebackup, &message); node = plist_array_get_item(message, 0); @@ -452,23 +453,33 @@ int main(int argc, char *argv[]) /* print out "received" if DLFileStatusKey is 2 (last file piece) */ node = plist_dict_get_item(node_tmp, "DLFileStatusKey"); plist_get_uint_val(node, &c); + + /* get source filename */ + node = plist_dict_get_item(node_tmp, "DLFileSource"); + plist_get_string_val(node, &filename_source); + + if (!strcmp(filename_source, "/tmp/Manifest.plist")) + is_manifest = TRUE; + else + is_manifest = FALSE; + if (c == 2) { node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); node = plist_dict_get_item(node, "FileSize"); plist_get_uint_val(node, &length); + + /* increased received size for each completed file */ backup_real_size += length; - format_size = g_format_size_for_display(backup_real_size); - printf("(%s", format_size); - g_free(format_size); - format_size = g_format_size_for_display(backup_total_size); - printf("/%s): ", format_size); - g_free(format_size); - /* get source filename and print it */ - node = plist_dict_get_item(node_tmp, "DLFileSource"); - plist_get_string_val(node, &filename_source); - printf("Received file %s... ", filename_source); - free(filename_source); + if (!is_manifest) { + format_size = g_format_size_for_display(backup_real_size); + printf("(%s", format_size); + g_free(format_size); + format_size = g_format_size_for_display(backup_total_size); + printf("/%s): ", format_size); + g_free(format_size); + printf("Received file %s... ", filename_source); + } } /* save .mdinfo */ @@ -499,8 +510,12 @@ int main(int argc, char *argv[]) g_free(filename_mddata); } - if (c == 2) + if ((c == 2) && (!is_manifest)) { printf("DONE\n"); + } + + if (filename_source) + free(filename_source); if (file_ext) free(file_ext); -- cgit v1.1-32-gdbae From 736d600aa7ea50dc79201be8b35c7e6767f12a6d Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 02:55:16 +0100 Subject: Make sure to print accurate backup progress information; was wrong so far --- tools/iphonebackup.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index f7be8ca..28a8949 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -464,14 +464,15 @@ int main(int argc, char *argv[]) is_manifest = FALSE; if (c == 2) { - node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); - node = plist_dict_get_item(node, "FileSize"); - plist_get_uint_val(node, &length); - /* increased received size for each completed file */ - backup_real_size += length; - if (!is_manifest) { + node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); + node = plist_dict_get_item(node, "FileSize"); + plist_get_uint_val(node, &length); + + backup_real_size += length; + file_index++; + format_size = g_format_size_for_display(backup_real_size); printf("(%s", format_size); g_free(format_size); @@ -543,8 +544,6 @@ int main(int argc, char *argv[]) plist_free(message); message = NULL; - - file_index++; } while (!plist_strcmp(node, "DLSendFile")); printf("Received %d files from device.\n", file_index); -- cgit v1.1-32-gdbae From d25503b6a865d78eba373e7a71ba84f3e6a5a8cb Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 04:55:27 +0100 Subject: Use device_link_message_factory where appropriate in iphonebackup --- tools/iphonebackup.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 28a8949..5124ea5 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -388,10 +388,7 @@ int main(int argc, char *argv[]) plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6")); - plist_t message = plist_new_array(); - plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); - plist_array_append_item(message, node); - + plist_t message = device_link_message_factory_process_message_new(node); mobilebackup_send(mobilebackup, message); plist_free(message); message = NULL; @@ -537,9 +534,7 @@ int main(int argc, char *argv[]) node = plist_new_dict(); plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived")); - message = plist_new_array(); - plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); - plist_array_append_item(message, node); + message = device_link_message_factory_process_message_new(node); mobilebackup_send(mobilebackup, message); plist_free(message); -- cgit v1.1-32-gdbae From 57d2586abd946b019095841038afb323026bfc16 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 04:56:45 +0100 Subject: Rename each received atomic temporary manifest to active one on backups --- tools/iphonebackup.c | 72 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 5124ea5..c5ac49d 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -430,6 +430,7 @@ int main(int argc, char *argv[]) char *filename_source = NULL; char *format_size = NULL; gboolean is_manifest = FALSE; + uint8_t b = 0; do { mobilebackup_receive(mobilebackup, &message); node = plist_array_get_item(message, 0); @@ -452,32 +453,36 @@ int main(int argc, char *argv[]) plist_get_uint_val(node, &c); /* get source filename */ - node = plist_dict_get_item(node_tmp, "DLFileSource"); - plist_get_string_val(node, &filename_source); - - if (!strcmp(filename_source, "/tmp/Manifest.plist")) - is_manifest = TRUE; - else - is_manifest = FALSE; - - if (c == 2) { - /* increased received size for each completed file */ - if (!is_manifest) { - node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); - node = plist_dict_get_item(node, "FileSize"); - plist_get_uint_val(node, &length); - - backup_real_size += length; - file_index++; - - format_size = g_format_size_for_display(backup_real_size); - printf("(%s", format_size); - g_free(format_size); - format_size = g_format_size_for_display(backup_total_size); - printf("/%s): ", format_size); - g_free(format_size); - printf("Received file %s... ", filename_source); - } + node = plist_dict_get_item(node_tmp, "BackupManifestKey"); + b = 0; + if (node) { + plist_get_bool_val(node, &b); + } + is_manifest = (b == 1) ? TRUE: FALSE; + + /* increased received size for each completed file */ + if ((c == 2) && (!is_manifest)) { + /* get source filename */ + node = plist_dict_get_item(node_tmp, "DLFileSource"); + plist_get_string_val(node, &filename_source); + + node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); + node = plist_dict_get_item(node, "FileSize"); + plist_get_uint_val(node, &length); + + backup_real_size += length; + file_index++; + + format_size = g_format_size_for_display(backup_real_size); + printf("(%s", format_size); + g_free(format_size); + format_size = g_format_size_for_display(backup_total_size); + printf("/%s): ", format_size); + g_free(format_size); + printf("Received file %s... ", filename_source); + + if (filename_source) + free(filename_source); } /* save .mdinfo */ @@ -498,11 +503,21 @@ int main(int argc, char *argv[]) if (node_tmp && file_path) { node = plist_dict_get_item(node_tmp, "DLFileDest"); plist_get_string_val(node, &file_path); - file_ext = (char *)g_strconcat(file_path, ".mddata", NULL); + + if (!is_manifest) + file_ext = (char *)g_strconcat(file_path, ".mddata", NULL); + else + file_ext = g_strdup(file_path); + filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); node_tmp = plist_array_get_item(message, 1); plist_get_data_val(node_tmp, &buffer, &length); + + /* activate currently sent manifest */ buffer_to_filename(filename_mddata, buffer, length); + if ((c == 2) && (is_manifest)) { + rename(filename_mddata, manifest_path); + } free(buffer); buffer = NULL; g_free(filename_mddata); @@ -512,9 +527,6 @@ int main(int argc, char *argv[]) printf("DONE\n"); } - if (filename_source) - free(filename_source); - if (file_ext) free(file_ext); -- cgit v1.1-32-gdbae From 0f4ad590f2ca1d87e6383cd423db9446a01f5441 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:35:07 +0100 Subject: iphonebackup: Add _new to Info.plist factory helper name This better reflects that a newly allocated plist_t is returned. --- tools/iphonebackup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index c5ac49d..c073616 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -44,7 +44,7 @@ enum cmd_mode { CMD_LEAVE }; -static plist_t mobilebackup_factory_info_plist() +static plist_t mobilebackup_factory_info_plist_new() { /* gather data from lockdown */ GTimeVal tv = {0, 0}; @@ -356,7 +356,7 @@ int main(int argc, char *argv[]) /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ printf("Creating \"%s/Info.plist\".\n", backup_directory); - plist_t info_plist = mobilebackup_factory_info_plist(); + plist_t info_plist = mobilebackup_factory_info_plist_new(); if (stat(info_path, &st) == 0) remove(info_path); plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); -- cgit v1.1-32-gdbae From db62ad45f6881a019dbd15ed8df799fd1c589476 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:37:06 +0100 Subject: Output that restore command is not implemented in iphonebackup --- tools/iphonebackup.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index c073616..789dd21 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -613,10 +613,10 @@ int main(int argc, char *argv[]) break; case CMD_RESTORE: - printf("Restoring backup...\n"); + printf("Restoring backup is NOT IMPLEMENTED.\n"); /* verify battery on AC enough battery remaining */ - /* request restore from device (BackupMessageRestoreMigrate) */ - /* read mddata files and send to devices using DLSendFile */ + /* request restore from device with manifest (BackupMessageRestoreMigrate) */ + /* read mddata/mdinfo files and send to devices using DLSendFile */ /* signal restore finished message to device */ /* close down lockdown connection as it is no longer needed */ lockdownd_client_free(client); -- cgit v1.1-32-gdbae From e5d42da1321fa6c29363c97188d7e6148083ae64 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:39:01 +0100 Subject: Improve some code comments for iphonebackup --- tools/iphonebackup.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 789dd21..f7f1ff1 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -355,20 +355,20 @@ int main(int argc, char *argv[]) /* TODO: verify battery on AC enough battery remaining */ /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ - printf("Creating \"%s/Info.plist\".\n", backup_directory); + printf("Creating Info.plist.\n"); plist_t info_plist = mobilebackup_factory_info_plist_new(); if (stat(info_path, &st) == 0) remove(info_path); plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); g_free(info_path); + /* close down the lockdown connection as it is no longer needed */ if (client) { lockdownd_client_free(client); client = NULL; } /* create Manifest.plist (backup manifest (backup state)) */ - printf("Creating \"%s/Manifest.plist\".\n", backup_directory); char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); /* FIXME: We should read the last Manifest.plist and send it to the device */ plist_t manifest_plist = NULL; @@ -379,7 +379,7 @@ int main(int argc, char *argv[]) mobilebackup_write_status(backup_directory, 0); /* request backup from device with manifest from last backup */ - printf("Sending manifest and requesting backup.\n"); + printf("Requesting backup from device...\n"); node = plist_new_dict(); if (manifest_plist) @@ -513,8 +513,9 @@ int main(int argc, char *argv[]) node_tmp = plist_array_get_item(message, 1); plist_get_data_val(node_tmp, &buffer, &length); - /* activate currently sent manifest */ buffer_to_filename(filename_mddata, buffer, length); + + /* activate currently sent manifest */ if ((c == 2) && (is_manifest)) { rename(filename_mddata, manifest_path); } @@ -558,7 +559,7 @@ int main(int argc, char *argv[]) if (!plist_strcmp(node, "DLMessageProcessMessage")) { node_tmp = plist_array_get_item(message, 1); node = plist_dict_get_item(node_tmp, "BackupMessageTypeKey"); - /* wait until received final backup finished message */ + /* check if we received the final "backup finished" message */ if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) { /* backup finished */ @@ -590,12 +591,13 @@ int main(int argc, char *argv[]) } } - /* save new Manifest.plist */ + /* save last valid Manifest.plist */ node_tmp = plist_array_get_item(message, 1); manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey"); if (manifest_plist) { if (stat(manifest_path, &st) != 0) remove(manifest_path); + printf("Storing Manifest.plist...\n"); plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); } -- cgit v1.1-32-gdbae From 3e4694043b0767acede31102f403b4ee8cbb076e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:40:47 +0100 Subject: Use backup_ok to depict final backup success status in iphonebackup --- tools/iphonebackup.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index f7f1ff1..3fe3a3f 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -420,6 +420,9 @@ int main(int argc, char *argv[]) break; } + /* reset backup status */ + backup_ok = 0; + /* receive and save DLSendFile files and metadata, ACK each */ int file_index = 0; uint64_t backup_real_size = 0; @@ -600,13 +603,19 @@ int main(int argc, char *argv[]) printf("Storing Manifest.plist...\n"); plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); } - - /* create: Status.plist (Info on how the backup process turned out) */ - printf("Backup Successful.\n"); - mobilebackup_write_status(backup_directory, 1); + + backup_ok = 1; } } + if (backup_ok) { + /* create: Status.plist (Info on how the backup process turned out) */ + printf("Backup Successful.\n"); + mobilebackup_write_status(backup_directory, 1); + } else { + printf("Backup Failed.\n"); + } + if (manifest_path) g_free(manifest_path); -- cgit v1.1-32-gdbae From 33a7e44b20f44adafe663d11dc36f704b248f3f2 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:41:40 +0100 Subject: Improve check for BackupTotalSizeKey and improve message reporting total size --- tools/iphonebackup.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 3fe3a3f..2628871 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -445,10 +445,12 @@ int main(int argc, char *argv[]) /* first message contains total backup size */ if (file_index == 0) { node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey"); - plist_get_uint_val(node, &backup_total_size); - format_size = g_format_size_for_display(backup_total_size); - printf("Backup will need %s on disk.\n", format_size); - g_free(format_size); + if (node) { + plist_get_uint_val(node, &backup_total_size); + format_size = g_format_size_for_display(backup_total_size); + printf("Backup data requires %s on the disk.\n", format_size); + g_free(format_size); + } } /* print out "received" if DLFileStatusKey is 2 (last file piece) */ -- cgit v1.1-32-gdbae From 34a4e3e15cb30b1e786776695f96e5f0352b3c2e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:43:10 +0100 Subject: Do not append to .mdinfo files on backup. Write them only once. --- tools/iphonebackup.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 2628871..d444e9b 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -488,19 +488,19 @@ int main(int argc, char *argv[]) if (filename_source) free(filename_source); - } - /* save .mdinfo */ - node = plist_dict_get_item(node_tmp, "BackupFileInfo"); - if (node) { - node = plist_dict_get_item(node_tmp, "DLFileDest"); - plist_get_string_val(node, &file_path); - file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL); - filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + /* save .mdinfo */ node = plist_dict_get_item(node_tmp, "BackupFileInfo"); - plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY); - g_free(file_ext); - g_free(filename_mdinfo); + if (node) { + node = plist_dict_get_item(node_tmp, "DLFileDest"); + plist_get_string_val(node, &file_path); + file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL); + filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + node = plist_dict_get_item(node_tmp, "BackupFileInfo"); + plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY); + g_free(file_ext); + g_free(filename_mdinfo); + } } /* save .mddata */ -- cgit v1.1-32-gdbae From de9b690877c4581a4e846743efa26aa75759d074 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:44:32 +0100 Subject: Also count received hunks so the total backup size message appears once --- tools/iphonebackup.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index d444e9b..c0f2276 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -425,6 +425,7 @@ int main(int argc, char *argv[]) /* receive and save DLSendFile files and metadata, ACK each */ int file_index = 0; + int hunk_index = 0; uint64_t backup_real_size = 0; char *file_path = NULL; char *file_ext = NULL; @@ -442,8 +443,8 @@ int main(int argc, char *argv[]) node_tmp = plist_array_get_item(message, 2); - /* first message contains total backup size */ - if (file_index == 0) { + /* first message hunk contains total backup size */ + if (hunk_index == 0) { node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey"); if (node) { plist_get_uint_val(node, &backup_total_size); @@ -533,6 +534,8 @@ int main(int argc, char *argv[]) printf("DONE\n"); } + hunk_index++; + if (file_ext) free(file_ext); -- cgit v1.1-32-gdbae From ec67ba628c688d8c0a01c7f5901155797be7b510 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 15:45:01 +0100 Subject: Remove the manifest file if it is supplied in the last backup message --- tools/iphonebackup.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index c0f2276..15a0495 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -603,8 +603,7 @@ int main(int argc, char *argv[]) node_tmp = plist_array_get_item(message, 1); manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey"); if (manifest_plist) { - if (stat(manifest_path, &st) != 0) - remove(manifest_path); + remove(manifest_path); printf("Storing Manifest.plist...\n"); plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); } -- cgit v1.1-32-gdbae From cc1d6b734369961e686c8c04f9cce2b820fdb543 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 22:05:28 +0100 Subject: Remove debug_plist helper in iphonebackup --- tools/iphonebackup.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 15a0495..a0b91f3 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -189,20 +189,6 @@ static void mobilebackup_write_status(char *path, int status) plist_free(status_plist); } -static void debug_plist(plist_t a) -{ - char *buffer = NULL; - uint32_t length = 0; - - if (a == NULL) - return; - - plist_to_xml(a, &buffer, &length); - - printf("Printing %i bytes plist:\n%s\n", length, buffer); - free(buffer); -} - /** * signal handler function for cleaning up properly */ @@ -409,8 +395,7 @@ int main(int argc, char *argv[]) mobilebackup_send(mobilebackup, message); } } else { - printf("Unhandled message received!\n"); - debug_plist(message); + printf("ERROR: Unhandled message received!\n"); } plist_free(message); message = NULL; -- cgit v1.1-32-gdbae From 16a17385369696c69cdcbb40042515c6c0b211d5 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 22:17:28 +0100 Subject: Remove obsolete DLSendFile loop check and make it while(1) The checked node was already changed within the loop. --- tools/iphonebackup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index a0b91f3..c24c161 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -545,7 +545,7 @@ int main(int argc, char *argv[]) plist_free(message); message = NULL; - } while (!plist_strcmp(node, "DLSendFile")); + } while (1); printf("Received %d files from device.\n", file_index); -- cgit v1.1-32-gdbae From 4f5b839827625adaf444a4c5fbfd6145367da473 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 22:19:42 +0100 Subject: Do not send BackupFileReceived on every hunk, rather the last hunk of a file This hammered the device with BackupFileReceived messages and caused the backup to fail in the middle of the backup process. --- tools/iphonebackup.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index c24c161..19acc4e 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -176,6 +176,13 @@ static void mobilebackup_cancel_backup_with_error(const char *reason) message = NULL; } +static plist_t mobilebackup_factory_backup_file_received_new() +{ + plist_t node = plist_new_dict(); + plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived")); + return device_link_message_factory_process_message_new(node); +} + static void mobilebackup_write_status(char *path, int status) { struct stat st; @@ -515,36 +522,31 @@ int main(int argc, char *argv[]) g_free(filename_mddata); } - if ((c == 2) && (!is_manifest)) { - printf("DONE\n"); - } - hunk_index++; if (file_ext) free(file_ext); - plist_free(message); + if (message) + plist_free(message); message = NULL; - if (quit_flag > 0) { - /* need to cancel the backup here */ - mobilebackup_cancel_backup_with_error("Cancelling DLSendFile"); + if (c == 2) { + if (!is_manifest) + printf("DONE\n"); + /* acknowlegdge that we received the file */ + message = mobilebackup_factory_backup_file_received_new(); + mobilebackup_send(mobilebackup, message); plist_free(message); message = NULL; - break; } - /* acknowlegdge that we received the file */ - node = plist_new_dict(); - plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived")); - - message = device_link_message_factory_process_message_new(node); - mobilebackup_send(mobilebackup, message); - - plist_free(message); - message = NULL; + if (quit_flag > 0) { + /* need to cancel the backup here */ + mobilebackup_cancel_backup_with_error("Cancelling DLSendFile"); + break; + } } while (1); printf("Received %d files from device.\n", file_index); -- cgit v1.1-32-gdbae From 90af94845ba841c693e80ac0eec317130c1c416e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Mon, 25 Jan 2010 23:36:24 +0100 Subject: Auto-pair devices within lockdownd_client_new_with_handshake() This brings back the automatic pairing feature if not yet paired. --- src/lockdown.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/lockdown.c b/src/lockdown.c index 792dd33..108b558 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -661,6 +661,14 @@ lockdownd_error_t lockdownd_client_new_with_handshake(iphone_device_t device, lo /* in any case, we need to validate pairing to receive trusted host status */ ret = lockdownd_validate_pair(client_loc, NULL); + /* if not paired yet, let's do it now */ + if (LOCKDOWN_E_INVALID_HOST_ID == ret) { + ret = lockdownd_pair(client_loc, NULL); + if (LOCKDOWN_E_SUCCESS == ret) { + ret = lockdownd_validate_pair(client_loc, NULL); + } + } + if (LOCKDOWN_E_SUCCESS == ret) { ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); if (LOCKDOWN_E_SUCCESS != ret) { @@ -825,11 +833,16 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ if (error_node) { char *value = NULL; plist_get_string_val(error_node, &value); - /* the first pairing fails if the device is password protected */ - if (value && !strcmp(value, "PasswordProtected")) { - ret = LOCKDOWN_E_PASSWORD_PROTECTED; + if (value) { + /* the first pairing fails if the device is password protected */ + if (!strcmp(value, "PasswordProtected")) { + ret = LOCKDOWN_E_PASSWORD_PROTECTED; + } else if (!strcmp(value, "InvalidHostID")) { + ret = LOCKDOWN_E_INVALID_HOST_ID; + } free(value); } + plist_free(error_node); error_node = NULL; } -- cgit v1.1-32-gdbae From b369efa426307bb6e9828c755ccc50c4f213c2e8 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Tue, 26 Jan 2010 02:03:21 +0100 Subject: Refactor iphonebackup and implement incremental backup support --- tools/iphonebackup.c | 293 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 237 insertions(+), 56 deletions(-) diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c index 19acc4e..f0cfa7a 100644 --- a/tools/iphonebackup.c +++ b/tools/iphonebackup.c @@ -44,6 +44,17 @@ enum cmd_mode { CMD_LEAVE }; +enum plist_format_t { + PLIST_FORMAT_XML, + PLIST_FORMAT_BINARY +}; + +enum device_link_file_status_t { + DEVICE_LINK_FILE_STATUS_NONE = 0, + DEVICE_LINK_FILE_STATUS_HUNK, + DEVICE_LINK_FILE_STATUS_LAST_HUNK +}; + static plist_t mobilebackup_factory_info_plist_new() { /* gather data from lockdown */ @@ -95,20 +106,50 @@ static plist_t mobilebackup_factory_info_plist_new() free(uuid_uppercase); free(uuid); - plist_t files = plist_new_dict(); /* FIXME: Embed files as nodes */ + plist_t files = plist_new_dict(); plist_dict_insert_item(ret, "iTunes Files", files); plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2")); + plist_free(root_node); + return ret; } -enum plist_format_t { - PLIST_FORMAT_XML, - PLIST_FORMAT_BINARY -}; +static void mobilebackup_info_update_last_backup_date(plist_t info_plist) +{ + GTimeVal tv = {0, 0}; + plist_t node = NULL; -static void buffer_to_filename(char *filename, char *buffer, uint32_t length) + if (!info_plist) + return; + + g_get_current_time(&tv); + node = plist_dict_get_item(info_plist, "Last Backup Date"); + plist_set_date_val(node, tv.tv_sec, tv.tv_usec); + + node = NULL; +} + +static void buffer_read_from_filename(const char *filename, char **buffer, uint32_t *length) +{ + FILE *f; + uint64_t size; + + f = fopen(filename, "rb"); + + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + + *buffer = (char*)malloc(sizeof(char)*size); + fread(*buffer, sizeof(char), size, f); + fclose(f); + + *length = size; +} + +static void buffer_write_to_filename(const char *filename, const char *buffer, uint32_t length) { FILE *f; @@ -117,7 +158,32 @@ static void buffer_to_filename(char *filename, char *buffer, uint32_t length) fclose(f); } -static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format) +static int plist_read_from_filename(plist_t *plist, const char *filename) +{ + char *buffer = NULL; + uint32_t length; + + if (!filename) + return 0; + + buffer_read_from_filename(filename, &buffer, &length); + + if (!buffer) { + return 0; + } + + if (memcmp(buffer, "bplist00", 8) == 0) { + plist_from_bin(buffer, length, plist); + } else { + plist_from_xml(buffer, length, plist); + } + + free(buffer); + + return 1; +} + +static int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format) { char *buffer = NULL; uint32_t length; @@ -132,7 +198,7 @@ static int plist_write_to_filename(plist_t plist, char *filename, enum plist_for else return 0; - buffer_to_filename(filename, buffer, length); + buffer_write_to_filename(filename, buffer, length); free(buffer); @@ -183,17 +249,100 @@ static plist_t mobilebackup_factory_backup_file_received_new() return device_link_message_factory_process_message_new(node); } -static void mobilebackup_write_status(char *path, int status) +static gchar *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension) +{ + gchar *filename = g_strconcat(name, extension, NULL); + gchar *path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, filename, NULL); + g_free(filename); + return path; +} + +static void mobilebackup_write_status(const char *path, int status) { struct stat st; plist_t status_plist = plist_new_dict(); plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); - char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL); + gchar *file_path = mobilebackup_build_path(path, "Status", ".plist"); + if (stat(file_path, &st) == 0) remove(file_path); + plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); - g_free(file_path); + plist_free(status_plist); + status_plist = NULL; + + g_free(file_path); +} + +static int mobilebackup_info_is_current_device(plist_t info) +{ + plist_t value_node = NULL; + plist_t node = NULL; + plist_t root_node = NULL; + int ret = 0; + + if (!info) + return ret; + + if (plist_get_node_type(info) != PLIST_DICT) + return ret; + + /* get basic device information in one go */ + lockdownd_get_value(client, NULL, NULL, &root_node); + + /* verify UUID */ + value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); + node = plist_dict_get_item(info, "Target Identifier"); + + if(plist_compare_node_value(value_node, node)) + ret = 1; + + /* verify SerialNumber */ + if (ret == 1) { + value_node = plist_dict_get_item(root_node, "SerialNumber"); + node = plist_dict_get_item(info, "Serial Number"); + + if(plist_compare_node_value(value_node, node)) + ret = 1; + else + ret = 0; + } + + plist_free(root_node); + root_node = NULL; + + value_node = NULL; + node = NULL; + + return ret; +} + +static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash) +{ + int ret = 0; + gchar *path = mobilebackup_build_path(backup_directory, hash, ".mddata"); + printf("Removing \"%s\"... ", path); + if (!remove( path )) + ret = 1; + else + ret = 0; + + g_free(path); + + if (!ret) + return ret; + + path = mobilebackup_build_path(backup_directory, hash, ".mdinfo"); + printf("Removing \"%s\"... ", path); + if (!remove( path )) + ret = 1; + else + ret = 0; + + g_free(path); + + return ret; } /** @@ -229,13 +378,17 @@ int main(int argc, char *argv[]) uint16_t port = 0; uuid[0] = 0; int cmd = -1; + int is_full_backup = 0; char *backup_directory = NULL; struct stat st; plist_t node = NULL; plist_t node_tmp = NULL; + plist_t manifest_plist = NULL; + plist_t info_plist = NULL; char *buffer = NULL; uint64_t length = 0; uint64_t backup_total_size = 0; + enum device_link_file_status_t file_status; uint64_t c = 0; /* we need to exit cleanly on running backups and restores or we cause havok */ @@ -298,7 +451,7 @@ int main(int argc, char *argv[]) } /* restore directory must contain an Info.plist */ - char *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Info.plist", NULL); + char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist"); if (cmd == CMD_RESTORE) { if (stat(info_path, &st) != 0) { g_free(info_path); @@ -347,27 +500,45 @@ int main(int argc, char *argv[]) /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ /* TODO: verify battery on AC enough battery remaining */ - /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ - printf("Creating Info.plist.\n"); - plist_t info_plist = mobilebackup_factory_info_plist_new(); - if (stat(info_path, &st) == 0) - remove(info_path); - plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); + /* Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ + + /* read existing Info.plist or create new one */ + if (stat(info_path, &st) == 0) { + printf("Reading Info.plist from existing backup.\n"); + plist_read_from_filename(&info_plist, info_path); + + if(!is_full_backup) { + /* update the last backup time within Info.plist */ + mobilebackup_info_update_last_backup_date(info_plist); + remove(info_path); + plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); + } + } else { + printf("Creating Info.plist for new backup.\n"); + info_plist = mobilebackup_factory_info_plist_new(); + plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); + is_full_backup = 1; + } + g_free(info_path); + /* Manifest.plist (backup manifest (backup state)) */ + char *manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist"); + /* read the last Manifest.plist if the current backup is for this device */ + if (!is_full_backup && mobilebackup_info_is_current_device(info_plist)) { + printf("Reading existing Manifest.\n"); + plist_read_from_filename(&manifest_plist, manifest_path); + } + + plist_free(info_plist); + info_plist = NULL; + /* close down the lockdown connection as it is no longer needed */ if (client) { lockdownd_client_free(client); client = NULL; } - /* create Manifest.plist (backup manifest (backup state)) */ - char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); - /* FIXME: We should read the last Manifest.plist and send it to the device */ - plist_t manifest_plist = NULL; - if (stat(manifest_path, &st) == 0) - remove(manifest_path); - /* create Status.plist with failed status for now */ mobilebackup_write_status(backup_directory, 0); @@ -375,8 +546,10 @@ int main(int argc, char *argv[]) printf("Requesting backup from device...\n"); node = plist_new_dict(); + if (manifest_plist) plist_dict_insert_item(node, "BackupManifestKey", manifest_plist); + plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/")); plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6")); @@ -389,6 +562,7 @@ int main(int argc, char *argv[]) /* get response */ int backup_ok = 0; mobilebackup_receive(mobilebackup, &message); + node = plist_array_get_item(message, 0); if (!plist_strcmp(node, "DLMessageProcessMessage")) { node = plist_array_get_item(message, 1); @@ -397,6 +571,10 @@ int main(int argc, char *argv[]) printf("Device accepts manifest and will send backup data now...\n"); backup_ok = 1; printf("Acknowledging...\n"); + if (is_full_backup) + printf("Full backup mode.\n"); + else + printf("Incremental backup mode.\n"); printf("Please wait. Device prepares backup data...\n"); /* send it back for ACK */ mobilebackup_send(mobilebackup, message); @@ -427,9 +605,13 @@ int main(int argc, char *argv[]) char *format_size = NULL; gboolean is_manifest = FALSE; uint8_t b = 0; + + /* process series of DLSendFile messages */ do { mobilebackup_receive(mobilebackup, &message); node = plist_array_get_item(message, 0); + + /* get out if we don't get a DLSendFile */ if (plist_strcmp(node, "DLSendFile")) break; @@ -446,9 +628,10 @@ int main(int argc, char *argv[]) } } - /* print out "received" if DLFileStatusKey is 2 (last file piece) */ + /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */ node = plist_dict_get_item(node_tmp, "DLFileStatusKey"); plist_get_uint_val(node, &c); + file_status = c; /* get source filename */ node = plist_dict_get_item(node_tmp, "BackupManifestKey"); @@ -458,25 +641,26 @@ int main(int argc, char *argv[]) } is_manifest = (b == 1) ? TRUE: FALSE; - /* increased received size for each completed file */ - if ((c == 2) && (!is_manifest)) { + /* check if we completed a file */ + if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (!is_manifest)) { /* get source filename */ node = plist_dict_get_item(node_tmp, "DLFileSource"); plist_get_string_val(node, &filename_source); + /* increase received size */ node = plist_dict_get_item(node_tmp, "DLFileAttributesKey"); node = plist_dict_get_item(node, "FileSize"); plist_get_uint_val(node, &length); - backup_real_size += length; - file_index++; format_size = g_format_size_for_display(backup_real_size); printf("(%s", format_size); g_free(format_size); + format_size = g_format_size_for_display(backup_total_size); printf("/%s): ", format_size); g_free(format_size); + printf("Received file %s... ", filename_source); if (filename_source) @@ -487,13 +671,20 @@ int main(int argc, char *argv[]) if (node) { node = plist_dict_get_item(node_tmp, "DLFileDest"); plist_get_string_val(node, &file_path); - file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL); - filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + + filename_mdinfo = mobilebackup_build_path(backup_directory, file_path, ".mdinfo"); + + /* remove any existing file */ + if (stat(filename_mdinfo, &st) != 0) + remove(filename_mdinfo); + node = plist_dict_get_item(node_tmp, "BackupFileInfo"); plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY); - g_free(file_ext); + g_free(filename_mdinfo); } + + file_index++; } /* save .mddata */ @@ -502,23 +693,26 @@ int main(int argc, char *argv[]) node = plist_dict_get_item(node_tmp, "DLFileDest"); plist_get_string_val(node, &file_path); - if (!is_manifest) - file_ext = (char *)g_strconcat(file_path, ".mddata", NULL); - else - file_ext = g_strdup(file_path); + filename_mddata = mobilebackup_build_path(backup_directory, file_path, is_manifest ? NULL: ".mddata"); + + /* if this is the first hunk, remove any existing file */ + if (stat(filename_mddata, &st) != 0) + remove(filename_mddata); - filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); + /* get file data hunk */ node_tmp = plist_array_get_item(message, 1); plist_get_data_val(node_tmp, &buffer, &length); - buffer_to_filename(filename_mddata, buffer, length); + buffer_write_to_filename(filename_mddata, buffer, length); /* activate currently sent manifest */ - if ((c == 2) && (is_manifest)) { + if ((file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) && (is_manifest)) { rename(filename_mddata, manifest_path); } + free(buffer); buffer = NULL; + g_free(filename_mddata); } @@ -531,7 +725,7 @@ int main(int argc, char *argv[]) plist_free(message); message = NULL; - if (c == 2) { + if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) { if (!is_manifest) printf("DONE\n"); @@ -566,20 +760,7 @@ int main(int argc, char *argv[]) while ((node_tmp = plist_array_get_item(node, i++)) != NULL) { plist_get_string_val(node_tmp, &file_path); - file_ext = (char *)g_strconcat(file_path, ".mddata", NULL); - filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); - g_free(file_ext); - printf("Removing \"%s\"... ", filename_mddata); - if (!remove( filename_mddata )) { - printf("DONE\n"); - } else - printf("FAILED\n"); - - file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL); - filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL); - g_free(file_ext); - printf("Removing \"%s\"... ", filename_mdinfo); - if (!remove( filename_mdinfo )) { + if (mobilebackup_delete_backup_file_by_hash(backup_directory, file_path)) { printf("DONE\n"); } else printf("FAILED\n"); @@ -600,7 +781,7 @@ int main(int argc, char *argv[]) } if (backup_ok) { - /* create: Status.plist (Info on how the backup process turned out) */ + /* Status.plist (Info on how the backup process turned out) */ printf("Backup Successful.\n"); mobilebackup_write_status(backup_directory, 1); } else { -- cgit v1.1-32-gdbae