diff options
| author | 2010-01-24 21:53:28 +0100 | |
|---|---|---|
| committer | 2010-01-24 21:53:28 +0100 | |
| commit | 8f133b0e0e89f4a44b80a442f70ef28cc7efa3df (patch) | |
| tree | 6066f5ab921c3494ef2d7ad4a7002d5a4294cd57 /tools/iphonebackup.c | |
| parent | 9a6fa170d226d1557e89f5e5252d1e0c61158ff5 (diff) | |
| download | libimobiledevice-8f133b0e0e89f4a44b80a442f70ef28cc7efa3df.tar.gz libimobiledevice-8f133b0e0e89f4a44b80a442f70ef28cc7efa3df.tar.bz2 | |
Initial backup implementation, device is sending the backup now
Diffstat (limited to 'tools/iphonebackup.c')
| -rw-r--r-- | tools/iphonebackup.c | 504 |
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 @@ | |||
| 24 | #include <errno.h> | 24 | #include <errno.h> |
| 25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
| 26 | #include <signal.h> | 26 | #include <signal.h> |
| 27 | #include <glib.h> | ||
| 27 | 28 | ||
| 28 | #include <libiphone/libiphone.h> | 29 | #include <libiphone/libiphone.h> |
| 29 | #include <libiphone/lockdown.h> | 30 | #include <libiphone/lockdown.h> |
| @@ -31,6 +32,10 @@ | |||
| 31 | 32 | ||
| 32 | #define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" | 33 | #define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" |
| 33 | 34 | ||
| 35 | static mobilebackup_client_t mobilebackup = NULL; | ||
| 36 | static lockdownd_client_t client = NULL; | ||
| 37 | static iphone_device_t phone = NULL; | ||
| 38 | |||
| 34 | static int quit_flag = 0; | 39 | static int quit_flag = 0; |
| 35 | 40 | ||
| 36 | enum cmd_mode { | 41 | enum cmd_mode { |
| @@ -38,43 +43,68 @@ enum cmd_mode { | |||
| 38 | CMD_RESTORE | 43 | CMD_RESTORE |
| 39 | }; | 44 | }; |
| 40 | 45 | ||
| 41 | /* | 46 | static plist_t mobilebackup_factory_info_plist() |
| 42 | Backup Process Communication: | 47 | { |
| 43 | -------------------------------------------------------------------------------- | 48 | /* gather data from lockdown */ |
| 44 | * Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt | 49 | GTimeVal tv = {0, 0}; |
| 45 | * Verify battery on AC enough battery remaining | 50 | plist_t value_node = NULL; |
| 46 | * ValidatePair with device for TrustedHost ability | 51 | plist_t root_node = NULL; |
| 47 | > DLMessageVersionExchange | 52 | char *uuid = NULL; |
| 48 | < DLMessageVersionExchange - DLVersionsOk | 53 | char *uuid_uppercase = NULL; |
| 49 | > DLMessageDeviceReady | ||
| 50 | < DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupRequest | ||
| 51 | > DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupReplyOK | ||
| 52 | ... | ||
| 53 | > DLSendFile | ||
| 54 | < DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFileReceived | ||
| 55 | ... | ||
| 56 | > DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFinished | ||
| 57 | 54 | ||
| 55 | plist_t ret = plist_new_dict(); | ||
| 58 | 56 | ||
| 59 | */ | 57 | /* get basic device information in one go */ |
| 58 | lockdownd_get_value(client, NULL, NULL, &root_node); | ||
| 60 | 59 | ||
| 61 | /* | 60 | /* set fields we understand */ |
| 62 | Restore Process Communication: | 61 | value_node = plist_dict_get_item(root_node, "BuildVersion"); |
| 63 | -------------------------------------------------------------------------------- | 62 | plist_dict_insert_item(ret, "Build Version", plist_copy(value_node)); |
| 64 | * Verify battery on AC enough battery remaining | 63 | |
| 65 | * ValidatePair with device for TrustedHost ability | 64 | value_node = plist_dict_get_item(root_node, "DeviceName"); |
| 66 | > DLMessageVersionExchange | 65 | plist_dict_insert_item(ret, "Device Name", plist_copy(value_node)); |
| 67 | < DLMessageVersionExchange - DLVersionsOk | 66 | plist_dict_insert_item(ret, "Display Name", plist_copy(value_node)); |
| 68 | > DLMessageDeviceReady | 67 | |
| 69 | < DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreMigrate | 68 | /* FIXME: How is the GUID generated? */ |
| 70 | ... | 69 | plist_dict_insert_item(ret, "GUID", plist_new_string("---")); |
| 71 | DLSendFile | 70 | |
| 72 | ... | 71 | value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity"); |
| 73 | < DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreReplyOK | 72 | if (value_node) |
| 74 | */ | 73 | plist_dict_insert_item(ret, "IMEI", plist_copy(value_node)); |
| 74 | |||
| 75 | g_get_current_time(&tv); | ||
| 76 | plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec)); | ||
| 75 | 77 | ||
| 76 | static plist_t mobilebackup_factory_metadata() | 78 | value_node = plist_dict_get_item(root_node, "ProductType"); |
| 79 | plist_dict_insert_item(ret, "Product Type", plist_copy(value_node)); | ||
| 80 | |||
| 81 | value_node = plist_dict_get_item(root_node, "ProductVersion"); | ||
| 82 | plist_dict_insert_item(ret, "Product Version", plist_copy(value_node)); | ||
| 83 | |||
| 84 | value_node = plist_dict_get_item(root_node, "SerialNumber"); | ||
| 85 | plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node)); | ||
| 86 | |||
| 87 | value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); | ||
| 88 | iphone_device_get_uuid(phone, &uuid); | ||
| 89 | plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid)); | ||
| 90 | |||
| 91 | /* uppercase */ | ||
| 92 | uuid_uppercase = g_ascii_strup(uuid, -1); | ||
| 93 | plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase)); | ||
| 94 | free(uuid_uppercase); | ||
| 95 | free(uuid); | ||
| 96 | |||
| 97 | plist_t files = plist_new_dict(); | ||
| 98 | /* FIXME: Embed files as <data> nodes */ | ||
| 99 | plist_dict_insert_item(ret, "iTunes Files", files); | ||
| 100 | plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2")); | ||
| 101 | |||
| 102 | return ret; | ||
| 103 | } | ||
| 104 | |||
| 105 | static plist_t mobilebackup_factory_metadata_plist() | ||
| 77 | { | 106 | { |
| 107 | plist_t ret = NULL; | ||
| 78 | /* | 108 | /* |
| 79 | Metadata key is: | 109 | Metadata key is: |
| 80 | <dict> | 110 | <dict> |
| @@ -106,6 +136,7 @@ Metadata key is: | |||
| 106 | <false/> | 136 | <false/> |
| 107 | </dict> | 137 | </dict> |
| 108 | */ | 138 | */ |
| 139 | return ret; | ||
| 109 | } | 140 | } |
| 110 | 141 | ||
| 111 | /** | 142 | /** |
| @@ -113,92 +144,165 @@ Metadata key is: | |||
| 113 | */ | 144 | */ |
| 114 | static plist_t mobilebackup_factory_manifest_data_plist() | 145 | static plist_t mobilebackup_factory_manifest_data_plist() |
| 115 | { | 146 | { |
| 116 | plist_t manifest_data = plist_dict_new(); | 147 | plist_t ret = NULL; |
| 117 | /* | 148 | plist_t value_node = NULL; |
| 118 | File hash is: | 149 | char *uuid = NULL; |
| 119 | sha1(<Domain>-<Relative File Path>) | 150 | GTimeVal tv = {0, 0}; |
| 120 | */ | ||
| 121 | /* | ||
| 122 | Data hash is: | ||
| 123 | sha1(<file>) | ||
| 124 | */ | ||
| 125 | 151 | ||
| 126 | /* | 152 | ret = plist_new_dict(); |
| 127 | <dict> | ||
| 128 | <key>DeviceId</key> | ||
| 129 | <string>7a7b570ee169f02c43d3893f0d661cf7a32e1cf5</string> | ||
| 130 | <key>Version</key> | ||
| 131 | <string>6.2</string> | ||
| 132 | <key>Files</key> | ||
| 133 | <dict> | ||
| 134 | <key>3d0d7e5fb2ce288813306e4d4636395e047a3d28</key> | ||
| 135 | <dict> | ||
| 136 | <key>ModificationTime</key> | ||
| 137 | <date>2009-12-29T02:12:17Z</date> | ||
| 138 | <key>FileLength</key> | ||
| 139 | <integer>131072</integer> | ||
| 140 | <key>Domain</key> | ||
| 141 | <string>HomeDomain</string> | ||
| 142 | <key>DataHash</key> | ||
| 143 | <data> | ||
| 144 | MfpSk+qw+RAJqLNTJI81tntvrwc= | ||
| 145 | </data> | ||
| 146 | <key>Group ID</key> | ||
| 147 | <integer>501</integer> | ||
| 148 | <key>User ID</key> | ||
| 149 | <integer>501</integer> | ||
| 150 | <key>Mode</key> | ||
| 151 | <integer>420</integer> | ||
| 152 | </dict> | ||
| 153 | </dict> | ||
| 154 | <key>DeviceICCID</key> | ||
| 155 | <string>89492060399209300736</string> | ||
| 156 | </dict> | ||
| 157 | */ | ||
| 158 | 153 | ||
| 159 | return manifest_data; | 154 | /* get basic device information in one go */ |
| 155 | lockdownd_get_value(client, NULL, "IntegratedCircuitCardIdentity", &value_node); | ||
| 156 | |||
| 157 | iphone_device_get_uuid(phone, &uuid); | ||
| 158 | plist_dict_insert_item(ret, "DeviceId", plist_new_string(uuid)); | ||
| 159 | free(uuid); | ||
| 160 | |||
| 161 | plist_dict_insert_item(ret, "Version", plist_new_string("6.2")); | ||
| 162 | |||
| 163 | /* TODO: add all Applications */ | ||
| 164 | |||
| 165 | /* TODO: add all Files */ | ||
| 166 | plist_t files = plist_new_dict(); | ||
| 167 | |||
| 168 | /* single file entry */ | ||
| 169 | plist_t info_node = plist_new_dict(); | ||
| 170 | g_get_current_time(&tv); | ||
| 171 | plist_dict_insert_item(info_node, "ModificationTime", plist_new_date(tv.tv_sec, tv.tv_usec)); | ||
| 172 | plist_dict_insert_item(info_node, "FileLength", plist_new_uint(131072)); | ||
| 173 | plist_dict_insert_item(info_node, "Domain", plist_new_string("HomeDomain")); | ||
| 174 | |||
| 175 | /* FIXME: calculate correct data hash */ | ||
| 176 | /* Data hash is: sha1(<file>) */ | ||
| 177 | plist_dict_insert_item(info_node, "DataHash", plist_new_data(NULL, 0)); | ||
| 178 | plist_dict_insert_item(info_node, "Group ID", plist_new_uint(501)); | ||
| 179 | plist_dict_insert_item(info_node, "User ID", plist_new_uint(501)); | ||
| 180 | plist_dict_insert_item(info_node, "Mode ID", plist_new_uint(420)); | ||
| 181 | |||
| 182 | /* FIXME: calculate correct file hash */ | ||
| 183 | /* File hash is: sha1(<Domain>-<Relative File Path>) */ | ||
| 184 | plist_dict_insert_item(files, "3d0d7e5fb2ce288813306e4d4636395e047a3d28", info_node); | ||
| 185 | plist_dict_insert_item(ret, "Files", files); | ||
| 186 | |||
| 187 | /* last node with ICCID */ | ||
| 188 | if (value_node) | ||
| 189 | plist_dict_insert_item(ret, "DeviceICCID", &value_node); | ||
| 190 | |||
| 191 | return ret; | ||
| 160 | } | 192 | } |
| 161 | 193 | ||
| 162 | /** | 194 | /** |
| 163 | * Generates a manifest plist with all needed information and hashes | 195 | * Generates a manifest plist with all needed information and hashes |
| 164 | */ | 196 | */ |
| 165 | static plist_t mobilebackup_factory_manifest_plist() | 197 | static plist_t mobilebackup_factory_manifest_plist(plist_t manifest_data) |
| 166 | { | 198 | { |
| 167 | plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); | 199 | char *buffer = NULL; |
| 168 | plist_t manifest = plist_dict_new(); | 200 | char *s = NULL; |
| 201 | uint32_t length; | ||
| 202 | unsigned char sha1[20]; | ||
| 203 | gsize sha1_len; | ||
| 204 | GChecksum *checksum; | ||
| 205 | plist_t ret = NULL; | ||
| 169 | 206 | ||
| 170 | /* | 207 | if (!manifest_data) |
| 171 | AuthSignature Hash is: | 208 | return ret; |
| 172 | sha1(<manifest_data>) | ||
| 173 | */ | ||
| 174 | 209 | ||
| 175 | /* | 210 | ret = plist_new_dict(); |
| 176 | <dict> | 211 | plist_dict_insert_item(ret, "AuthVersion", plist_new_string("2.0")); |
| 177 | <key>BackupManifestKey</key> | ||
| 178 | <dict> | ||
| 179 | <key>AuthVersion</key> | ||
| 180 | <string>2.0</string> | ||
| 181 | <key>AuthSignature</key> | ||
| 182 | <data> | ||
| 183 | WBdfjcZWg/u/Bpn7aKDJC68UZF4= | ||
| 184 | </data> | ||
| 185 | <key>IsEncrypted</key> | ||
| 186 | <integer>0</integer> | ||
| 187 | <key>Data</key> | ||
| 188 | <data><!-- binary plist --> | ||
| 189 | ... | ||
| 190 | </data> | ||
| 191 | </dict> | ||
| 192 | <key>BackupComputerBasePathKey</key> | ||
| 193 | <string>/</string> | ||
| 194 | <key>BackupMessageTypeKey</key> | ||
| 195 | <string>BackupMessageBackupRequest</string> | ||
| 196 | <key>BackupProtocolVersion</key> | ||
| 197 | <string>1.6</string> | ||
| 198 | </dict> | ||
| 199 | */ | ||
| 200 | 212 | ||
| 201 | return manifest; | 213 | /* AuthSignature Hash is: sha1(<manifest_data>) */ |
| 214 | plist_to_bin(manifest_data, &buffer, &length); | ||
| 215 | |||
| 216 | sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1); | ||
| 217 | checksum = g_checksum_new(G_CHECKSUM_SHA1); | ||
| 218 | g_checksum_update(checksum, (guchar *)buffer, length); | ||
| 219 | g_checksum_get_digest(checksum, sha1, &sha1_len); | ||
| 220 | s = (char *)g_checksum_get_string(checksum); | ||
| 221 | printf("SHA1 AuthSignature: %s\n", s); | ||
| 222 | plist_dict_insert_item(ret, "AuthSignature", plist_new_data((char*)sha1, sha1_len)); | ||
| 223 | g_checksum_free(checksum); | ||
| 224 | |||
| 225 | |||
| 226 | plist_dict_insert_item(ret, "IsEncrypted", plist_new_uint(0)); | ||
| 227 | plist_dict_insert_item(ret, "Data", plist_new_data(buffer, length)); | ||
| 228 | |||
| 229 | free(buffer); | ||
| 230 | |||
| 231 | return ret; | ||
| 232 | } | ||
| 233 | |||
| 234 | enum plist_format_t { | ||
| 235 | PLIST_FORMAT_XML, | ||
| 236 | PLIST_FORMAT_BINARY | ||
| 237 | }; | ||
| 238 | |||
| 239 | static int plist_read_from_filename(char *filename, plist_t *plist) | ||
| 240 | { | ||
| 241 | return 1; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format) | ||
| 245 | { | ||
| 246 | char *buffer = NULL; | ||
| 247 | uint32_t length; | ||
| 248 | FILE *f; | ||
| 249 | |||
| 250 | if (!plist || !filename) | ||
| 251 | return 0; | ||
| 252 | |||
| 253 | if (format == PLIST_FORMAT_XML) | ||
| 254 | plist_to_xml(plist, &buffer, &length); | ||
| 255 | else if (format == PLIST_FORMAT_BINARY) | ||
| 256 | plist_to_bin(plist, &buffer, &length); | ||
| 257 | else | ||
| 258 | return 0; | ||
| 259 | |||
| 260 | f = fopen(filename, "wb"); | ||
| 261 | fwrite(buffer, sizeof(char), length, f); | ||
| 262 | fclose(f); | ||
| 263 | |||
| 264 | free(buffer); | ||
| 265 | |||
| 266 | return 1; | ||
| 267 | } | ||
| 268 | |||
| 269 | static int plist_strcmp(plist_t node, const char *str) | ||
| 270 | { | ||
| 271 | char *buffer = NULL; | ||
| 272 | int ret = 0; | ||
| 273 | |||
| 274 | if (plist_get_node_type(node) != PLIST_STRING) | ||
| 275 | return ret; | ||
| 276 | |||
| 277 | plist_get_string_val(node, &buffer); | ||
| 278 | ret = strcmp(buffer, str); | ||
| 279 | free(buffer); | ||
| 280 | |||
| 281 | return ret; | ||
| 282 | } | ||
| 283 | |||
| 284 | static void mobilebackup_write_status(char *path, int status) | ||
| 285 | { | ||
| 286 | plist_t status_plist = plist_new_dict(); | ||
| 287 | plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); | ||
| 288 | char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL); | ||
| 289 | plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); | ||
| 290 | g_free(file_path); | ||
| 291 | plist_free(status_plist); | ||
| 292 | } | ||
| 293 | |||
| 294 | static void debug_plist(plist_t plist) | ||
| 295 | { | ||
| 296 | char *buffer = NULL; | ||
| 297 | uint32_t length = 0; | ||
| 298 | |||
| 299 | if (!plist) | ||
| 300 | return; | ||
| 301 | |||
| 302 | plist_to_xml(plist, &buffer, &length); | ||
| 303 | |||
| 304 | printf("Printing %i bytes plist:\n%s\n", length, buffer); | ||
| 305 | free(buffer); | ||
| 202 | } | 306 | } |
| 203 | 307 | ||
| 204 | /** | 308 | /** |
| @@ -226,10 +330,6 @@ static void print_usage(int argc, char **argv) | |||
| 226 | printf("\n"); | 330 | printf("\n"); |
| 227 | } | 331 | } |
| 228 | 332 | ||
| 229 | static mobilebackup_client_t mobilebackup = NULL; | ||
| 230 | static lockdownd_client_t client = NULL; | ||
| 231 | static iphone_device_t phone = NULL; | ||
| 232 | |||
| 233 | int main(int argc, char *argv[]) | 333 | int main(int argc, char *argv[]) |
| 234 | { | 334 | { |
| 235 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | 335 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; |
| @@ -239,6 +339,8 @@ int main(int argc, char *argv[]) | |||
| 239 | uuid[0] = 0; | 339 | uuid[0] = 0; |
| 240 | int cmd = -1; | 340 | int cmd = -1; |
| 241 | char *backup_directory = NULL; | 341 | char *backup_directory = NULL; |
| 342 | struct stat st; | ||
| 343 | plist_t node = NULL; | ||
| 242 | 344 | ||
| 243 | /* we need to exit cleanly on running backups and restores or we cause havok */ | 345 | /* we need to exit cleanly on running backups and restores or we cause havok */ |
| 244 | signal(SIGINT, clean_exit); | 346 | signal(SIGINT, clean_exit); |
| @@ -293,6 +395,22 @@ int main(int argc, char *argv[]) | |||
| 293 | return -1; | 395 | return -1; |
| 294 | } | 396 | } |
| 295 | 397 | ||
| 398 | /* verify if passed backup directory exists */ | ||
| 399 | if (stat(backup_directory, &st) != 0) { | ||
| 400 | printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory); | ||
| 401 | return -1; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* restore directory must contain an Info.plist */ | ||
| 405 | char *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Info.plist", NULL); | ||
| 406 | if (cmd == CMD_RESTORE) { | ||
| 407 | if (stat(info_path, &st) != 0) { | ||
| 408 | g_free(info_path); | ||
| 409 | printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory); | ||
| 410 | return -1; | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 296 | printf("Backup directory is \"%s\"\n", backup_directory); | 414 | printf("Backup directory is \"%s\"\n", backup_directory); |
| 297 | 415 | ||
| 298 | if (uuid[0] != 0) { | 416 | if (uuid[0] != 0) { |
| @@ -322,46 +440,140 @@ int main(int argc, char *argv[]) | |||
| 322 | printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); | 440 | printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); |
| 323 | mobilebackup_client_new(phone, port, &mobilebackup); | 441 | mobilebackup_client_new(phone, port, &mobilebackup); |
| 324 | 442 | ||
| 325 | /* TODO: Command implementations */ | ||
| 326 | switch(cmd) { | 443 | switch(cmd) { |
| 327 | case CMD_BACKUP: | 444 | case CMD_BACKUP: |
| 328 | printf("TODO: Creating backup...\n"); | 445 | printf("Starting backup...\n"); |
| 329 | /* | 446 | /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ |
| 330 | Create target directory: | 447 | /* TODO: verify battery on AC enough battery remaining */ |
| 331 | MobileSync/Backup/<uuid>-YYYYMMDD-HHMMSS/ | 448 | |
| 332 | */ | 449 | /* ????: create target directory: MobileSync/Backup/<uuid>-YYYYMMDD-HHMMSS/ */ |
| 333 | 450 | ||
| 334 | /* | 451 | /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ |
| 335 | Create: Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) | 452 | printf("Creating \"%s/Info.plist\".\n", backup_directory); |
| 336 | Create:Manifest.plist (backup manifest (backup state)) | 453 | plist_t info_plist = mobilebackup_factory_info_plist(); |
| 337 | */ | 454 | plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); |
| 455 | g_free(info_path); | ||
| 456 | |||
| 457 | /* create Manifest.plist (backup manifest (backup state)) */ | ||
| 458 | printf("Creating \"%s/Manifest.plist\".\n", backup_directory); | ||
| 459 | char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); | ||
| 460 | plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); | ||
| 461 | plist_t manifest_plist = mobilebackup_factory_manifest_plist(manifest_data); | ||
| 462 | plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); | ||
| 463 | g_free(manifest_path); | ||
| 464 | |||
| 465 | /* create Status.plist with failed status for now */ | ||
| 466 | mobilebackup_write_status(backup_directory, 0); | ||
| 467 | |||
| 468 | /* close down lockdown connection as it is no longer needed */ | ||
| 338 | lockdownd_client_free(client); | 469 | lockdownd_client_free(client); |
| 339 | /* | 470 | client = NULL; |
| 340 | Receive: | 471 | |
| 341 | ... | 472 | /* request backup from device with manifest */ |
| 342 | <hash>.mddata (Raw filedata) | 473 | printf("Sending manifest and requesting backup.\n"); |
| 343 | <hash>.mdinfo (backup file information) | 474 | |
| 344 | ... | 475 | node = plist_new_dict(); |
| 345 | Create: Status.plist (Info on how the backup process turned out) | 476 | plist_dict_insert_item(node, "BackupManifestKey", manifest_plist); |
| 346 | */ | 477 | plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/")); |
| 478 | plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); | ||
| 479 | plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6")); | ||
| 480 | |||
| 481 | plist_t message = plist_new_array(); | ||
| 482 | plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); | ||
| 483 | plist_array_append_item(message, node); | ||
| 484 | |||
| 485 | mobilebackup_send(mobilebackup, message); | ||
| 486 | plist_free(message); | ||
| 487 | message = NULL; | ||
| 488 | |||
| 489 | /* get response */ | ||
| 490 | int backup_ok = 0; | ||
| 491 | mobilebackup_receive(mobilebackup, &message); | ||
| 492 | node = plist_array_get_item(message, 0); | ||
| 493 | if (!plist_strcmp(node, "DLMessageProcessMessage")) { | ||
| 494 | node = plist_array_get_item(message, 1); | ||
| 495 | node = plist_dict_get_item(node, "BackupMessageTypeKey"); | ||
| 496 | if (node && !plist_strcmp(node, "BackupMessageBackupReplyOK")) { | ||
| 497 | printf("Device accepts manifest and will send backup data now...\n"); | ||
| 498 | backup_ok = 1; | ||
| 499 | printf("Acknowledging...\n"); | ||
| 500 | /* send it back for ACK */ | ||
| 501 | mobilebackup_send(mobilebackup, message); | ||
| 502 | } | ||
| 503 | } else { | ||
| 504 | printf("Unhandled message received!\n"); | ||
| 505 | debug_plist(message); | ||
| 506 | } | ||
| 507 | plist_free(message); | ||
| 508 | message = NULL; | ||
| 509 | |||
| 510 | if (!backup_ok) { | ||
| 511 | printf("ERROR: Device rejected to start the backup process.\n"); | ||
| 512 | break; | ||
| 513 | } | ||
| 514 | |||
| 515 | /* receive and save DLSendFile files and metadata, ACK each */ | ||
| 516 | int file_index = 0; | ||
| 517 | do { | ||
| 518 | mobilebackup_receive(mobilebackup, &message); | ||
| 519 | node = plist_array_get_item(message, 0); | ||
| 520 | if (plist_strcmp(node, "DLSendFile")) | ||
| 521 | break; | ||
| 522 | |||
| 523 | printf("Receiving file %d...\n", file_index); | ||
| 524 | /* TODO: save <hash>.mdinfo */ | ||
| 525 | /* TODO: save <hash>.mddata */ | ||
| 526 | debug_plist(message); | ||
| 527 | plist_free(message); | ||
| 528 | message = NULL; | ||
| 529 | |||
| 530 | if (quit_flag) { | ||
| 531 | /* FIXME: need to cancel the backup here */ | ||
| 532 | break; | ||
| 533 | } | ||
| 534 | |||
| 535 | /* acknowlegdge that we received the file */ | ||
| 536 | node = plist_new_dict(); | ||
| 537 | plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived")); | ||
| 538 | |||
| 539 | message = plist_new_array(); | ||
| 540 | plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); | ||
| 541 | plist_array_append_item(message, node); | ||
| 542 | mobilebackup_send(mobilebackup, message); | ||
| 543 | |||
| 544 | plist_free(message); | ||
| 545 | message = NULL; | ||
| 546 | |||
| 547 | file_index++; | ||
| 548 | } while (!plist_strcmp(node, "DLSendFile")); | ||
| 549 | |||
| 550 | printf("Received %d files from device.\n", file_index); | ||
| 551 | |||
| 552 | if (!plist_strcmp(node, "DLMessageProcessMessage")) { | ||
| 553 | node = plist_array_get_item(message, 1); | ||
| 554 | node = plist_dict_get_item(node, "BackupMessageTypeKey"); | ||
| 555 | /* wait until received final backup finished message */ | ||
| 556 | if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) { | ||
| 557 | /* backup finished */ | ||
| 558 | /* create: Status.plist (Info on how the backup process turned out) */ | ||
| 559 | printf("Backup Successful.\n"); | ||
| 560 | mobilebackup_write_status(backup_directory, 1); | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | if (node) | ||
| 565 | plist_free(node); | ||
| 347 | 566 | ||
| 348 | /* | ||
| 349 | - Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt | ||
| 350 | - Verify battery on AC enough battery remaining | ||
| 351 | - Request backup from device with manifest (BackupMessageBackupRequest) | ||
| 352 | - Receive and save DLSendFile files and metadata, ACK each | ||
| 353 | - Wait until received final backup finished message | ||
| 354 | */ | ||
| 355 | break; | 567 | break; |
| 356 | case CMD_RESTORE: | 568 | case CMD_RESTORE: |
| 357 | printf("TODO: Restoring backup...\n"); | 569 | printf("Restoring backup...\n"); |
| 358 | /* | 570 | /* verify battery on AC enough battery remaining */ |
| 359 | - Verify battery on AC enough battery remaining | 571 | /* request restore from device (BackupMessageRestoreMigrate) */ |
| 360 | - Request restore from device (BackupMessageRestoreMigrate) | 572 | /* read mddata files and send to devices using DLSendFile */ |
| 361 | - Read mddata files and send to devices using DLSendFile | 573 | /* signal restore finished message to device */ |
| 362 | - Signal restore finished message to device | 574 | /* close down lockdown connection as it is no longer needed */ |
| 363 | */ | ||
| 364 | lockdownd_client_free(client); | 575 | lockdownd_client_free(client); |
| 576 | client = NULL; | ||
| 365 | break; | 577 | break; |
| 366 | default: | 578 | default: |
| 367 | break; | 579 | break; |
| @@ -369,8 +581,12 @@ Create: Status.plist (Info on how the backup process turned out) | |||
| 369 | } else { | 581 | } else { |
| 370 | printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); | 582 | printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); |
| 371 | lockdownd_client_free(client); | 583 | lockdownd_client_free(client); |
| 584 | client = NULL; | ||
| 372 | } | 585 | } |
| 373 | 586 | ||
| 587 | if (client) | ||
| 588 | lockdownd_client_free(client); | ||
| 589 | |||
| 374 | if (mobilebackup) | 590 | if (mobilebackup) |
| 375 | mobilebackup_client_free(mobilebackup); | 591 | mobilebackup_client_free(mobilebackup); |
| 376 | iphone_device_free(phone); | 592 | iphone_device_free(phone); |
