summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Martin Szulecki2010-01-24 21:53:28 +0100
committerGravatar Martin Szulecki2010-01-24 21:53:28 +0100
commit8f133b0e0e89f4a44b80a442f70ef28cc7efa3df (patch)
tree6066f5ab921c3494ef2d7ad4a7002d5a4294cd57
parent9a6fa170d226d1557e89f5e5252d1e0c61158ff5 (diff)
downloadlibimobiledevice-8f133b0e0e89f4a44b80a442f70ef28cc7efa3df.tar.gz
libimobiledevice-8f133b0e0e89f4a44b80a442f70ef28cc7efa3df.tar.bz2
Initial backup implementation, device is sending the backup now
-rw-r--r--tools/iphonebackup.c504
1 files 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 <errno.h>
#include <stdlib.h>
#include <signal.h>
+#include <glib.h>
#include <libiphone/libiphone.h>
#include <libiphone/lockdown.h>
@@ -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 <data> 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:
<dict>
@@ -106,6 +136,7 @@ Metadata key is:
<false/>
</dict>
*/
+ 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(<Domain>-<Relative File Path>)
- */
- /*
- Data hash is:
- sha1(<file>)
- */
+ plist_t ret = NULL;
+ plist_t value_node = NULL;
+ char *uuid = NULL;
+ GTimeVal tv = {0, 0};
-/*
-<dict>
- <key>DeviceId</key>
- <string>7a7b570ee169f02c43d3893f0d661cf7a32e1cf5</string>
- <key>Version</key>
- <string>6.2</string>
- <key>Files</key>
- <dict>
- <key>3d0d7e5fb2ce288813306e4d4636395e047a3d28</key>
- <dict>
- <key>ModificationTime</key>
- <date>2009-12-29T02:12:17Z</date>
- <key>FileLength</key>
- <integer>131072</integer>
- <key>Domain</key>
- <string>HomeDomain</string>
- <key>DataHash</key>
- <data>
- MfpSk+qw+RAJqLNTJI81tntvrwc=
- </data>
- <key>Group ID</key>
- <integer>501</integer>
- <key>User ID</key>
- <integer>501</integer>
- <key>Mode</key>
- <integer>420</integer>
- </dict>
- </dict>
- <key>DeviceICCID</key>
- <string>89492060399209300736</string>
-</dict>
-*/
+ 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(<file>) */
+ 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(<Domain>-<Relative File Path>) */
+ 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(<manifest_data>)
- */
+ if (!manifest_data)
+ return ret;
-/*
-<dict>
- <key>BackupManifestKey</key>
- <dict>
- <key>AuthVersion</key>
- <string>2.0</string>
- <key>AuthSignature</key>
- <data>
- WBdfjcZWg/u/Bpn7aKDJC68UZF4=
- </data>
- <key>IsEncrypted</key>
- <integer>0</integer>
- <key>Data</key>
- <data><!-- binary plist -->
- ...
- </data>
- </dict>
- <key>BackupComputerBasePathKey</key>
- <string>/</string>
- <key>BackupMessageTypeKey</key>
- <string>BackupMessageBackupRequest</string>
- <key>BackupProtocolVersion</key>
- <string>1.6</string>
-</dict>
-*/
+ ret = plist_new_dict();
+ plist_dict_insert_item(ret, "AuthVersion", plist_new_string("2.0"));
- return manifest;
+ /* AuthSignature Hash is: sha1(<manifest_data>) */
+ 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/<uuid>-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/<uuid>-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:
-...
-<hash>.mddata (Raw filedata)
-<hash>.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 <hash>.mdinfo */
+ /* TODO: save <hash>.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);