summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/idevicebackup2.c')
-rw-r--r--tools/idevicebackup2.c2273
1 files changed, 1671 insertions, 602 deletions
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
index 4b7e79e..f5eb1e4 100644
--- a/tools/idevicebackup2.c
+++ b/tools/idevicebackup2.c
@@ -2,27 +2,34 @@
* idevicebackup2.c
* Command line interface to use the device's backup and restore service
*
- * Copyright (c) 2009-2010 Martin Szulecki All Rights Reserved.
- * Copyright (c) 2010 Nikias Bassen All Rights Reserved.
+ * Copyright (c) 2010-2022 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2009-2010 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define TOOL_NAME "idevicebackup2"
+
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
@@ -30,37 +37,45 @@
#include <libgen.h>
#include <ctype.h>
#include <time.h>
+#include <getopt.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/mobilebackup2.h>
#include <libimobiledevice/notification_proxy.h>
#include <libimobiledevice/afc.h>
+#include <libimobiledevice/installation_proxy.h>
+#include <libimobiledevice/sbservices.h>
+#include <libimobiledevice/diagnostics_relay.h>
+#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#include <endianness.h>
-#define MOBILEBACKUP2_SERVICE_NAME "com.apple.mobilebackup2"
-#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
-
#define LOCK_ATTEMPTS 50
#define LOCK_WAIT 200000
-#ifdef WIN32
+#ifdef _WIN32
+#include <windows.h>
+#include <conio.h>
#define sleep(x) Sleep(x*1000)
+#ifndef ELOOP
+#define ELOOP 114
+#endif
+#else
+#include <termios.h>
+#include <sys/statvfs.h>
#endif
+#include <sys/stat.h>
#define CODE_SUCCESS 0x00
#define CODE_ERROR_LOCAL 0x06
#define CODE_ERROR_REMOTE 0x0b
#define CODE_FILE_DATA 0x0c
-static mobilebackup2_client_t mobilebackup2 = NULL;
-static lockdownd_client_t client = NULL;
-static afc_client_t afc = NULL;
-static idevice_t phone = NULL;
-
static int verbose = 1;
static int quit_flag = 0;
+static int passcode_requested = 0;
#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
@@ -70,70 +85,67 @@ enum cmd_mode {
CMD_INFO,
CMD_LIST,
CMD_UNBACK,
- CMD_LEAVE
-};
-
-enum plist_format_t {
- PLIST_FORMAT_XML,
- PLIST_FORMAT_BINARY
+ CMD_CHANGEPW,
+ CMD_LEAVE,
+ CMD_CLOUD
};
enum cmd_flags {
CMD_FLAG_RESTORE_SYSTEM_FILES = (1 << 1),
- CMD_FLAG_RESTORE_REBOOT = (1 << 2),
+ CMD_FLAG_RESTORE_NO_REBOOT = (1 << 2),
CMD_FLAG_RESTORE_COPY_BACKUP = (1 << 3),
- CMD_FLAG_RESTORE_SETTINGS = (1 << 4)
+ CMD_FLAG_RESTORE_SETTINGS = (1 << 4),
+ CMD_FLAG_RESTORE_REMOVE_ITEMS = (1 << 5),
+ CMD_FLAG_ENCRYPTION_ENABLE = (1 << 6),
+ CMD_FLAG_ENCRYPTION_DISABLE = (1 << 7),
+ CMD_FLAG_ENCRYPTION_CHANGEPW = (1 << 8),
+ CMD_FLAG_FORCE_FULL_BACKUP = (1 << 9),
+ CMD_FLAG_CLOUD_ENABLE = (1 << 10),
+ CMD_FLAG_CLOUD_DISABLE = (1 << 11),
+ CMD_FLAG_RESTORE_SKIP_APPS = (1 << 12)
};
+static int backup_domain_changed = 0;
+
static void notify_cb(const char *notification, void *userdata)
{
+ if (strlen(notification) == 0) {
+ return;
+ }
if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
quit_flag++;
+ } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
+ backup_domain_changed = 1;
+ } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) {
+ passcode_requested = 1;
+ } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) {
+ passcode_requested = 0;
} else {
PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
}
}
-static void free_dictionary(char **dictionary)
-{
- int i = 0;
-
- if (!dictionary)
- return;
-
- for (i = 0; dictionary[i]; i++) {
- free(dictionary[i]);
- }
- free(dictionary);
-}
-
-static void mobilebackup_afc_get_file_contents(const char *filename, char **data, uint64_t *size)
+static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
{
if (!afc || !data || !size) {
return;
}
- char **fileinfo = NULL;
+ plist_t fileinfo = NULL;
uint32_t fsize = 0;
-
- afc_get_file_info(afc, filename, &fileinfo);
+
+ afc_get_file_info_plist(afc, filename, &fileinfo);
if (!fileinfo) {
return;
}
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- fsize = atol(fileinfo[i+1]);
- break;
- }
- }
- free_dictionary(fileinfo);
+ fsize = plist_dict_get_uint(fileinfo, "st_size");
+ plist_free(fileinfo);
if (fsize == 0) {
return;
}
-
+
uint64_t f = 0;
afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
if (!f) {
@@ -145,11 +157,10 @@ static void mobilebackup_afc_get_file_contents(const char *filename, char **data
uint32_t bread = 0;
afc_file_read(afc, f, buf+done, 65536, &bread);
if (bread > 0) {
-
+ done += bread;
} else {
break;
}
- done += bread;
}
if (done == fsize) {
*size = fsize;
@@ -160,19 +171,9 @@ static void mobilebackup_afc_get_file_contents(const char *filename, char **data
afc_file_close(afc, f);
}
-static char *str_toupper(char* str)
-{
- char *res = strdup(str);
- unsigned int i;
- for (i = 0; i < strlen(res); i++) {
- res[i] = toupper(res[i]);
- }
- return res;
-}
-
static int __mkdir(const char* path, int mode)
{
-#ifdef WIN32
+#ifdef _WIN32
return mkdir(path);
#else
return mkdir(path, mode);
@@ -184,16 +185,15 @@ static int mkdir_with_parents(const char *dir, int mode)
if (!dir) return -1;
if (__mkdir(dir, mode) == 0) {
return 0;
- } else {
- if (errno == EEXIST) return 0;
}
+ if (errno == EEXIST) return 0;
int res;
char *parent = strdup(dir);
- parent = dirname(parent);
- if (parent) {
- res = mkdir_with_parents(parent, mode);
+ char *parentdir = dirname(parent);
+ if (parentdir) {
+ res = mkdir_with_parents(parentdir, mode);
} else {
- res = -1;
+ res = -1;
}
free(parent);
if (res == 0) {
@@ -202,119 +202,304 @@ static int mkdir_with_parents(const char *dir, int mode)
return res;
}
-static char* build_path(const char* elem, ...)
+#ifdef _WIN32
+static int win32err_to_errno(int err_value)
{
- if (!elem) return NULL;
- va_list args;
- int len = strlen(elem)+1;
- va_start(args, elem);
- char *arg = va_arg(args, char*);
- while (arg) {
- len += strlen(arg)+1;
- arg = va_arg(args, char*);
- }
- va_end(args);
-
- char* out = (char*)malloc(len);
- strcpy(out, elem);
-
- va_start(args, elem);
- arg = va_arg(args, char*);
- while (arg) {
- strcat(out, "/");
- strcat(out, arg);
- arg = va_arg(args, char*);
- }
- va_end(args);
- return out;
+ switch (err_value) {
+ case ERROR_FILE_NOT_FOUND:
+ return ENOENT;
+ case ERROR_ALREADY_EXISTS:
+ return EEXIST;
+ default:
+ return EFAULT;
+ }
}
+#endif
-static char* format_size_for_display(uint64_t size)
+static int remove_file(const char* path)
{
- char buf[32];
- double sz;
- if (size >= 1000000000LL) {
- sz = ((double)size / 1000000000.0f);
- sprintf(buf, "%0.1f GB", sz);
- } else if (size >= 1000000LL) {
- sz = ((double)size / 1000000.0f);
- sprintf(buf, "%0.1f MB", sz);
- } else if (size >= 1000LL) {
- sz = ((double)size / 1000.0f);
- sprintf(buf, "%0.1f kB", sz);
- } else {
- sprintf(buf, "%d Bytes", (int)size);
+ int e = 0;
+#ifdef _WIN32
+ if (!DeleteFile(path)) {
+ e = win32err_to_errno(GetLastError());
+ }
+#else
+ if (remove(path) < 0) {
+ e = errno;
+ }
+#endif
+ return e;
+}
+
+static int remove_directory(const char* path)
+{
+ int e = 0;
+#ifdef _WIN32
+ if (!RemoveDirectory(path)) {
+ e = win32err_to_errno(GetLastError());
+ }
+#else
+ if (remove(path) < 0) {
+ e = errno;
+ }
+#endif
+ return e;
+}
+
+struct entry {
+ char *name;
+ struct entry *next;
+};
+
+static void scan_directory(const char *path, struct entry **files, struct entry **directories)
+{
+ DIR* cur_dir = opendir(path);
+ if (cur_dir) {
+ struct dirent* ep;
+ while ((ep = readdir(cur_dir))) {
+ if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
+ continue;
+ }
+ char *fpath = string_build_path(path, ep->d_name, NULL);
+ if (fpath) {
+#ifdef HAVE_DIRENT_D_TYPE
+ if (ep->d_type & DT_DIR) {
+#else
+ struct stat st;
+ if (stat(fpath, &st) != 0) return;
+ if (S_ISDIR(st.st_mode)) {
+#endif
+ struct entry *ent = malloc(sizeof(struct entry));
+ if (!ent) return;
+ ent->name = fpath;
+ ent->next = *directories;
+ *directories = ent;
+ scan_directory(fpath, files, directories);
+ fpath = NULL;
+ } else {
+ struct entry *ent = malloc(sizeof(struct entry));
+ if (!ent) return;
+ ent->name = fpath;
+ ent->next = *files;
+ *files = ent;
+ fpath = NULL;
+ }
+ }
+ }
+ closedir(cur_dir);
+ }
+}
+
+static int rmdir_recursive(const char* path)
+{
+ int res = 0;
+ struct entry *files = NULL;
+ struct entry *directories = NULL;
+ struct entry *ent;
+
+ ent = malloc(sizeof(struct entry));
+ if (!ent) return ENOMEM;
+ ent->name = strdup(path);
+ ent->next = NULL;
+ directories = ent;
+
+ scan_directory(path, &files, &directories);
+
+ ent = files;
+ while (ent) {
+ struct entry *del = ent;
+ res = remove_file(ent->name);
+ free(ent->name);
+ ent = ent->next;
+ free(del);
+ }
+ ent = directories;
+ while (ent) {
+ struct entry *del = ent;
+ res = remove_directory(ent->name);
+ free(ent->name);
+ ent = ent->next;
+ free(del);
+ }
+
+ return res;
+}
+
+static char* get_uuid()
+{
+ const char *chars = "ABCDEF0123456789";
+ int i = 0;
+ char *uuid = (char*)malloc(sizeof(char) * 33);
+
+ srand(time(NULL));
+
+ for (i = 0; i < 32; i++) {
+ uuid[i] = chars[rand() % 16];
}
- return strdup(buf);
+
+ uuid[32] = '\0';
+
+ return uuid;
}
-static plist_t mobilebackup_factory_info_plist_new()
+static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t device, afc_client_t afc)
{
/* gather data from lockdown */
plist_t value_node = NULL;
plist_t root_node = NULL;
- char *udid = NULL;
+ plist_t itunes_settings = NULL;
+ plist_t min_itunes_version = NULL;
char *udid_uppercase = NULL;
+ lockdownd_client_t lockdown = NULL;
+ if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
+ return NULL;
+ }
+
plist_t ret = plist_new_dict();
/* get basic device information in one go */
- lockdownd_get_value(client, NULL, NULL, &root_node);
+ lockdownd_get_value(lockdown, NULL, NULL, &root_node);
+
+ /* get iTunes settings */
+ lockdownd_get_value(lockdown, "com.apple.iTunes", NULL, &itunes_settings);
+
+ /* get minimum iTunes version */
+ lockdownd_get_value(lockdown, "com.apple.mobile.iTunes", "MinITunesVersion", &min_itunes_version);
+
+ lockdownd_client_free(lockdown);
+
+ /* get a list of installed user applications */
+ plist_t app_dict = plist_new_dict();
+ plist_t installed_apps = plist_new_array();
+ instproxy_client_t ip = NULL;
+ if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {
+ plist_t client_opts = instproxy_client_options_new();
+ instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
+ instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL);
+
+ plist_t apps = NULL;
+ instproxy_browse(ip, client_opts, &apps);
+
+ sbservices_client_t sbs = NULL;
+ if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
+ printf("Couldn't establish sbservices connection. Continuing anyway.\n");
+ }
+
+ if (apps && (plist_get_node_type(apps) == PLIST_ARRAY)) {
+ uint32_t app_count = plist_array_get_size(apps);
+ uint32_t i;
+ for (i = 0; i < app_count; i++) {
+ plist_t app_entry = plist_array_get_item(apps, i);
+ plist_t bundle_id = plist_dict_get_item(app_entry, "CFBundleIdentifier");
+ if (bundle_id) {
+ char *bundle_id_str = NULL;
+ plist_array_append_item(installed_apps, plist_copy(bundle_id));
+
+ plist_get_string_val(bundle_id, &bundle_id_str);
+ plist_t sinf = plist_dict_get_item(app_entry, "ApplicationSINF");
+ plist_t meta = plist_dict_get_item(app_entry, "iTunesMetadata");
+ if (sinf && meta) {
+ plist_t adict = plist_new_dict();
+ plist_dict_set_item(adict, "ApplicationSINF", plist_copy(sinf));
+ if (sbs) {
+ char *pngdata = NULL;
+ uint64_t pngsize = 0;
+ sbservices_get_icon_pngdata(sbs, bundle_id_str, &pngdata, &pngsize);
+ if (pngdata) {
+ plist_dict_set_item(adict, "PlaceholderIcon", plist_new_data(pngdata, pngsize));
+ free(pngdata);
+ }
+ }
+ plist_dict_set_item(adict, "iTunesMetadata", plist_copy(meta));
+ plist_dict_set_item(app_dict, bundle_id_str, adict);
+ }
+ free(bundle_id_str);
+ }
+ }
+ }
+ plist_free(apps);
+
+ if (sbs) {
+ sbservices_client_free(sbs);
+ }
+
+ instproxy_client_options_free(client_opts);
+
+ instproxy_client_free(ip);
+ }
+
+ /* Applications */
+ plist_dict_set_item(ret, "Applications", app_dict);
/* set fields we understand */
value_node = plist_dict_get_item(root_node, "BuildVersion");
- plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
+ plist_dict_set_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));
+ plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
+ plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
- /* FIXME: How is the GUID generated? */
- plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
+ char *uuid = get_uuid();
+ plist_dict_set_item(ret, "GUID", plist_new_string(uuid));
+ free(uuid);
value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
if (value_node)
- plist_dict_insert_item(ret, "ICCID", plist_copy(value_node));
+ plist_dict_set_item(ret, "ICCID", plist_copy(value_node));
value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
if (value_node)
- plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
+ plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
+
+ /* Installed Applications */
+ plist_dict_set_item(ret, "Installed Applications", installed_apps);
- plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(time(NULL), 0));
+ plist_dict_set_item(ret, "Last Backup Date",
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(time(NULL))
+#else
+ plist_new_date(time(NULL) - MAC_EPOCH, 0)
+#endif
+ );
+
+ value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
+ if (value_node)
+ plist_dict_set_item(ret, "MEID", plist_copy(value_node));
value_node = plist_dict_get_item(root_node, "PhoneNumber");
if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
- plist_dict_insert_item(ret, "Phone Number", plist_copy(value_node));
+ plist_dict_set_item(ret, "Phone Number", plist_copy(value_node));
}
+ /* FIXME Product Name */
+
value_node = plist_dict_get_item(root_node, "ProductType");
- plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
+ plist_dict_set_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));
+ plist_dict_set_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));
+ plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
/* FIXME Sync Settings? */
value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
- idevice_get_udid(phone, &udid);
- plist_dict_insert_item(ret, "Target Identifier", plist_new_string(udid));
+ plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
- plist_dict_insert_item(ret, "Target Type", plist_new_string("Device"));
+ plist_dict_set_item(ret, "Target Type", plist_new_string("Device"));
/* uppercase */
- udid_uppercase = str_toupper(udid);
- plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
+ udid_uppercase = string_toupper((char*)udid);
+ plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
free(udid_uppercase);
- free(udid);
char *data_buf = NULL;
uint64_t data_size = 0;
- mobilebackup_afc_get_file_contents("/Books/iBooksData2.plist", &data_buf, &data_size);
+ mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
if (data_buf) {
- plist_dict_insert_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
+ plist_dict_set_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
free(data_buf);
}
@@ -326,6 +511,7 @@ static plist_t mobilebackup_factory_info_plist_new()
"PhotosFolderAlbums",
"PhotosFolderName",
"PhotosFolderPrefs",
+ "VoiceMemos.plist",
"iPhotoAlbumPrefs",
"iTunesApplicationIDs",
"iTunesPrefs",
@@ -339,120 +525,95 @@ static plist_t mobilebackup_factory_info_plist_new()
char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
strcpy(fname, "/iTunes_Control/iTunes/");
strcat(fname, itunesfiles[i]);
- mobilebackup_afc_get_file_contents(fname, &data_buf, &data_size);
+ mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
free(fname);
if (data_buf) {
- plist_dict_insert_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
+ plist_dict_set_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
free(data_buf);
}
}
- plist_dict_insert_item(ret, "iTunes Files", files);
+ plist_dict_set_item(ret, "iTunes Files", files);
- plist_t itunes_settings = plist_new_dict();
- lockdownd_get_value(client, "com.apple.iTunes", NULL, &itunes_settings);
- plist_dict_insert_item(ret, "iTunes Settings", itunes_settings);
+ plist_dict_set_item(ret, "iTunes Settings", itunes_settings ? plist_copy(itunes_settings) : plist_new_dict());
- plist_dict_insert_item(ret, "iTunes Version", plist_new_string("10.0.1"));
+ /* since we usually don't have iTunes, let's get the minimum required iTunes version from the device */
+ if (min_itunes_version) {
+ plist_dict_set_item(ret, "iTunes Version", plist_copy(min_itunes_version));
+ } else {
+ plist_dict_set_item(ret, "iTunes Version", plist_new_string("10.0.1"));
+ }
+ plist_free(itunes_settings);
+ plist_free(min_itunes_version);
plist_free(root_node);
return ret;
}
-static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
+static int write_restore_applications(plist_t info_plist, afc_client_t afc)
{
- FILE *f;
- uint64_t size;
-
- *length = 0;
-
- f = fopen(filename, "rb");
- if (!f) {
- return;
+ int res = -1;
+ uint64_t restore_applications_file = 0;
+ char * applications_plist_xml = NULL;
+ uint32_t applications_plist_xml_length = 0;
+
+ plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
+ if (!applications_plist) {
+ printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
+ return 0;
}
-
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- rewind(f);
-
- if (size == 0) {
- return;
+ plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
+ if (!applications_plist_xml) {
+ printf("Error preparing RestoreApplications.plist\n");
+ goto leave;
}
- *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, uint64_t length)
-{
- FILE *f;
-
- f = fopen(filename, "ab");
- if (!f)
- f = fopen(filename, "wb");
- if (f) {
- fwrite(buffer, sizeof(char), length, f);
- fclose(f);
+ afc_error_t afc_err = 0;
+ afc_err = afc_make_directory(afc, "/iTunesRestore");
+ if (afc_err != AFC_E_SUCCESS) {
+ printf("Error creating directory /iTunesRestore, error code %d\n", afc_err);
+ goto leave;
}
-}
-
-static int plist_read_from_filename(plist_t *plist, const char *filename)
-{
- char *buffer = NULL;
- uint64_t length;
-
- if (!filename)
- return 0;
- buffer_read_from_filename(filename, &buffer, &length);
-
- if (!buffer) {
- return 0;
+ afc_err = afc_file_open(afc, "/iTunesRestore/RestoreApplications.plist", AFC_FOPEN_WR, &restore_applications_file);
+ if (afc_err != AFC_E_SUCCESS || !restore_applications_file) {
+ printf("Error creating /iTunesRestore/RestoreApplications.plist, error code %d\n", afc_err);
+ goto leave;
}
- if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) {
- plist_from_bin(buffer, length, plist);
- } else {
- plist_from_xml(buffer, length, plist);
+ uint32_t bytes_written = 0;
+ afc_err = afc_file_write(afc, restore_applications_file, applications_plist_xml, applications_plist_xml_length, &bytes_written);
+ if (afc_err != AFC_E_SUCCESS || bytes_written != applications_plist_xml_length) {
+ printf("Error writing /iTunesRestore/RestoreApplications.plist, error code %d, wrote %u of %u bytes\n", afc_err, bytes_written, applications_plist_xml_length);
+ goto leave;
}
- 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;
-
- 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;
+ afc_err = afc_file_close(afc, restore_applications_file);
+ restore_applications_file = 0;
+ if (afc_err != AFC_E_SUCCESS) {
+ goto leave;
+ }
+ /* success */
+ res = 0;
- buffer_write_to_filename(filename, buffer, length);
+leave:
+ free(applications_plist_xml);
- free(buffer);
+ if (restore_applications_file) {
+ afc_file_close(afc, restore_applications_file);
+ restore_applications_file = 0;
+ }
- return 1;
+ return res;
}
static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
{
- int ret = -1;
+ int ret = 0;
plist_t status_plist = NULL;
- char *file_path = build_path(path, udid, "Status.plist", NULL);
+ char *file_path = string_build_path(path, udid, "Status.plist", NULL);
- plist_read_from_filename(&status_plist, file_path);
+ plist_read_from_file(file_path, &status_plist, NULL);
free(file_path);
if (!status_plist) {
printf("Could not read Status.plist!\n");
@@ -464,6 +625,7 @@ static int mb2_status_check_snapshot_state(const char *path, const char *udid, c
plist_get_string_val(node, &sval);
if (sval) {
ret = (strcmp(sval, matches) == 0) ? 1 : 0;
+ free(sval);
}
} else {
printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
@@ -472,90 +634,222 @@ static int mb2_status_check_snapshot_state(const char *path, const char *udid, c
return ret;
}
-static int mobilebackup_info_is_current_device(plist_t info)
+static void do_post_notification(idevice_t device, const char *notification)
{
- plist_t value_node = NULL;
- plist_t node = NULL;
- plist_t root_node = NULL;
- int ret = 0;
+ lockdownd_service_descriptor_t service = NULL;
+ np_client_t np;
- if (!info)
- return ret;
+ lockdownd_client_t lockdown = NULL;
- 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);
+ if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
+ return;
+ }
- /* verify UDID */
- value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
- node = plist_dict_get_item(info, "Target Identifier");
+ lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
+ if (ldret == LOCKDOWN_E_SUCCESS) {
+ np_client_new(device, service, &np);
+ if (np) {
+ np_post_notification(np, notification);
+ np_client_free(np);
+ }
+ } else {
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
+ }
- if(plist_compare_node_value(value_node, node))
- ret = 1;
- else {
- printf("Info.plist: UniqueDeviceID does not match.\n");
+ if (service) {
+ lockdownd_service_descriptor_free(service);
+ service = NULL;
}
+ lockdownd_client_free(lockdown);
+}
- /* verify SerialNumber */
- if (ret == 1) {
- value_node = plist_dict_get_item(root_node, "SerialNumber");
- node = plist_dict_get_item(info, "Serial Number");
+/* ANSI escape codes for cursor manipulation */
+#define CURSOR_UP "\033[1A"
+#define CURSOR_DOWN "\033[1B"
+#define CURSOR_SAVE "\033[s"
+#define CURSOR_RESTORE "\033[u"
+#define CURSOR_HIDE "\033[?25l"
+#define CURSOR_SHOW "\033[?25h"
+#define CLEAR_LINE "\033[2K"
+#define MOVE_COL_1 "\r"
+
+#define TRANSFER_NONE 0
+#define TRANSFER_SEND 1
+#define TRANSFER_RECEIVE 2
+
+/* Progress state - 4-line reserved area display */
+static double overall_progress = 0;
+static double file_progress = 0;
+static uint64_t file_current = 0;
+static uint64_t file_total = 0;
+static int transfer_status = 0;
+static int progress_mode_active = 0;
+static char progress_status[256] = "";
+static char transfer_info[256] = "";
+
+static void draw_progress_bar(double percent, int width)
+{
+ int i;
+ double ratio = width / 100.0;
+ printf("[");
+ for (i = 0; i < width; i++) {
+ if (i < (int)(percent * ratio)) {
+ printf("#");
+ } else {
+ printf(".");
+ }
+ }
+ printf("]");
+}
- if(plist_compare_node_value(value_node, node))
- ret = 1;
- else {
- printf("Info.plist: SerialNumber does not match.\n");
- ret = 0;
+static void draw_transfer_bar(double percent, int width)
+{
+ int i;
+ int pos = (int)((percent / 100.0) * width);
+
+ if (pos >= width)
+ pos = width - 1;
+ if (pos < 0)
+ pos = 0;
+
+ printf("[");
+ for (i = 0; i < width; i++) {
+ if (percent >= 100.0) {
+ printf("=");
+ } else if (i < pos) {
+ printf("=");
+ } else if (i == pos) {
+ printf(">");
+ } else {
+ printf(" ");
}
}
+ printf("]");
+}
+
+/* Initialize the 4-line progress area */
+static void progress_init(void)
+{
+ if (!progress_mode_active) {
+ progress_mode_active = 1;
+ /* Use current line as line 1, and reserve 3 more lines */
+ printf("\n\n\n");
+ /* Save cursor position at line 4 */
+ printf(CURSOR_SAVE);
+ progress_status[0] = '\0';
+ transfer_info[0] = '\0';
+ }
+}
- /* verify ProductVersion to prevent using backup with different OS version */
- if (ret == 1) {
- value_node = plist_dict_get_item(root_node, "ProductVersion");
- node = plist_dict_get_item(info, "Product Version");
+/* Render the 4-line progress display using current global state */
+static void progress_render(void)
+{
+ const int bar_width = 30;
+ const char *direction_label = "Sending";
+ const char *display_name = transfer_info;
+ if (!progress_mode_active) {
+ progress_init();
+ }
- if(plist_compare_node_value(value_node, node))
- ret = 1;
- else {
- printf("Info.plist: ProductVersion does not match.\n");
- ret = 0;
- }
+ /* Restore saved cursor position for line 4, then move to line 1 */
+ printf(CURSOR_RESTORE);
+ printf(CURSOR_UP CURSOR_UP CURSOR_UP);
+
+ if (transfer_status == TRANSFER_RECEIVE) {
+ direction_label = "Receiving";
+ } else if (transfer_status == TRANSFER_SEND) {
+ direction_label = "Sending";
+ } else {
+ direction_label = "";
}
- plist_free(root_node);
- root_node = NULL;
+ /* Line 1: Overall backup progress */
+ printf(CLEAR_LINE MOVE_COL_1);
+ printf("%-10s ", "Backup");
+ draw_progress_bar(overall_progress, bar_width);
+ printf(" %3.0f%%", overall_progress);
- value_node = NULL;
- node = NULL;
+ /* Line 2: Current file */
+ printf("\n");
+ printf(CLEAR_LINE MOVE_COL_1);
+ printf("%-10s ", direction_label);
+ if (display_name[0]) {
+ //printf("%s%s", direction_arrow, display_name);
+ printf("%s", display_name);
+ }
- return ret;
+ /* Line 3: Sending / Receiving progress */
+ printf("\n");
+ printf(CLEAR_LINE MOVE_COL_1);
+ printf("%-10s ", "");
+ draw_transfer_bar(file_progress, bar_width);
+ printf(" %5.1f%%", file_progress);
+ if (file_current > 0 || file_total > 0) {
+ char *format_size_current = string_format_size(file_current);
+ char *format_size_total = string_format_size(file_total);
+ if (format_size_current && format_size_total) {
+ printf(" %s / %s", format_size_current, format_size_total);
+ }
+ free(format_size_current);
+ free(format_size_total);
+ }
+ printf(" ");
+
+ /* Line 4: Status message */
+ printf("\n");
+ printf(CLEAR_LINE MOVE_COL_1);
+ printf("%-10s ", "Status");
+ if (progress_status[0]) {
+ printf("%s", progress_status);
+ }
+
+ /* Save cursor position at line 4 again for the next redraw */
+ printf(CURSOR_SAVE);
+ fflush(stdout);
}
-static void do_post_notification(const char *notification)
+/*
+ * Print a normal log line without permanently corrupting the reserved
+ * 4-line progress area. If progress mode is active, print the log message
+ * below the progress block, reserve a fresh 4-line area there, and redraw.
+ */
+static void progress_printf(const char *fmt, ...)
{
- uint16_t nport = 0;
- np_client_t np;
+ va_list ap;
- if (!client) {
- if (lockdownd_client_new_with_handshake(phone, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) {
- return;
- }
+ if (progress_mode_active) {
+ /* Go to saved line 4 and move to a fresh line below the block */
+ printf(CURSOR_RESTORE);
+ printf("\n");
}
- lockdownd_start_service(client, NP_SERVICE_NAME, &nport);
- if (nport) {
- np_client_new(phone, nport, &np);
- if (np) {
- np_post_notification(np, notification);
- np_client_free(np);
- }
- } else {
- printf("Could not start %s\n", NP_SERVICE_NAME);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+
+ if (progress_mode_active) {
+ /*
+ * Re-anchor the progress block on the current line:
+ * current line becomes line 1, plus 3 reserved lines below it.
+ */
+ printf("\n\n\n");
+ printf(CURSOR_SAVE);
+ progress_render();
}
}
+#if 0
+/* Update progress values and render */
+static void progress_update(double overall, double file, uint64_t current, uint64_t total)
+{
+ overall_progress = overall;
+ file_progress = file;
+ file_current = current;
+ file_total = total;
+ progress_render();
+}
+
+/* Legacy single-line progress for simple operations */
static void print_progress_real(double progress, int flush)
{
int i = 0;
@@ -571,42 +865,85 @@ static void print_progress_real(double progress, int flush)
if (flush > 0) {
fflush(stdout);
- if (progress == 100)
- PRINT_VERBOSE(1, "\n");
+ }
+ if (progress == 100) {
+ PRINT_VERBOSE(1, "\n");
}
}
+#endif
-static void print_progress(uint64_t current, uint64_t total)
+/* Called during file transfers - uses 3-line display */
+static void print_progress(uint64_t current, uint64_t total, int sending)
{
- char *format_size = NULL;
- double progress = ((double)current/(double)total)*100;
- if (progress < 0)
- return;
+ //double file_progress = 0;
+ /* Don't update file progress if overall backup is complete */
+ //if (overall_progress >= 100.0)
+ // return;
+ file_current = current;
+ file_total = total;
+ if (total > 0) {
+ file_progress = ((double)current / (double)total) * 100;
+ if (file_progress > 100) file_progress = 100;
+ } else {
+ file_progress = 0;
+ }
+ //progress_update(overall_progress, file_progress, current, total);
+ transfer_status = sending;
+ progress_render();
+}
- if (progress > 100)
- progress = 100;
+/* Exit progress mode - print final newline */
+static void progress_finish(void)
+{
+ if (progress_mode_active) {
+ overall_progress = 100;
+ //transfer_status = TRANSFER_NONE;
+ progress_render();
+ progress_mode_active = 0;
+ printf("\n");
+ fflush(stdout);
+ progress_status[0] = '\0';
+ //transfer_info[0] = '\0';
+ }
+}
- print_progress_real((double)progress, 0);
+#if 0
+static void mb2_set_overall_progress(double progress)
+{
+ if (progress > 0.0)
+ overall_progress = progress;
+}
+#endif
- format_size = format_size_for_display(current);
- PRINT_VERBOSE(1, " (%s", format_size);
- free(format_size);
- format_size = format_size_for_display(total);
- PRINT_VERBOSE(1, "/%s) ", format_size);
- free(format_size);
+static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
+{
+ plist_t node = NULL;
+ double progress = 0.0;
- fflush(stdout);
- if (progress == 100)
- PRINT_VERBOSE(1, "\n");
+ if (!strcmp(identifier, "DLMessageDownloadFiles")) {
+ node = plist_array_get_item(message, 3);
+ } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
+ node = plist_array_get_item(message, 2);
+ } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
+ node = plist_array_get_item(message, 3);
+ } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
+ node = plist_array_get_item(message, 3);
+ }
+
+ if (node != NULL) {
+ plist_get_real_val(node, &progress);
+ //mb2_set_overall_progress(progress);
+ overall_progress = progress;
+ }
}
static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
{
if (!status_dict) return;
plist_t filedict = plist_new_dict();
- plist_dict_insert_item(filedict, "DLFileErrorString", plist_new_string(error_message));
- plist_dict_insert_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
- plist_dict_insert_item(status_dict, path, filedict);
+ plist_dict_set_item(filedict, "DLFileErrorString", plist_new_string(error_message));
+ plist_dict_set_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
+ plist_dict_set_item(status_dict, path, filedict);
}
static int errno_to_device_error(int errno_value)
@@ -616,27 +953,46 @@ static int errno_to_device_error(int errno_value)
return -6;
case EEXIST:
return -7;
+ case ENOTDIR:
+ return -8;
+ case EISDIR:
+ return -9;
+ case ELOOP:
+ return -10;
+ case EIO:
+ return -11;
+ case ENOSPC:
+ return -15;
default:
- return -errno_value;
+ return -1;
}
}
-static int mb2_handle_send_file(const char *backup_dir, const char *path, plist_t *errplist)
+static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
{
uint32_t nlen = 0;
uint32_t pathlen = strlen(path);
uint32_t bytes = 0;
- char *localfile = build_path(backup_dir, path, NULL);
+ char *localfile = string_build_path(backup_dir, path, NULL);
char buf[32768];
+#ifdef _WIN32
+ struct _stati64 fst;
+#else
struct stat fst;
+#endif
FILE *f = NULL;
uint32_t slen = 0;
int errcode = -1;
int result = -1;
uint32_t length;
+#ifdef _WIN32
+ uint64_t total;
+ uint64_t sent;
+#else
off_t total;
off_t sent;
+#endif
mobilebackup2_error_t err;
@@ -661,18 +1017,24 @@ static int mb2_handle_send_file(const char *backup_dir, const char *path, plist_
goto leave_proto_err;
}
- if (stat(localfile, &fst) < 0) {
+#ifdef _WIN32
+ if (_stati64(localfile, &fst) < 0)
+#else
+ if (stat(localfile, &fst) < 0)
+#endif
+ {
if (errno != ENOENT)
- printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
+ progress_printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
errcode = errno;
goto leave;
}
total = fst.st_size;
- char *format_size = format_size_for_display(total);
- PRINT_VERBOSE(1, "Sending '%s' (%s)\n", path, format_size);
- free(format_size);
+ //char *format_size = string_format_size(total);
+ snprintf(transfer_info, sizeof(transfer_info), "%s", path); //"'%s' (%s)", path, format_size);
+ //free(format_size);
+ progress_render();
if (total == 0) {
errcode = 0;
@@ -681,14 +1043,14 @@ static int mb2_handle_send_file(const char *backup_dir, const char *path, plist_
f = fopen(localfile, "rb");
if (!f) {
- printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
+ progress_printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
errcode = errno;
goto leave;
}
sent = 0;
do {
- length = ((total-sent) < (off_t)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
+ length = ((total-sent) < (long long)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
/* send data size (file size + 1) */
nlen = htobe32(length+1);
memcpy(buf, &nlen, sizeof(nlen));
@@ -704,7 +1066,7 @@ static int mb2_handle_send_file(const char *backup_dir, const char *path, plist_
/* send file contents */
size_t r = fread(buf, 1, sizeof(buf), f);
if (r <= 0) {
- printf("%s: read error\n", __func__);
+ progress_printf("%s: read error\n", __func__);
errcode = errno;
goto leave;
}
@@ -713,10 +1075,11 @@ static int mb2_handle_send_file(const char *backup_dir, const char *path, plist_
goto leave_proto_err;
}
if (bytes != (uint32_t)r) {
- printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
+ progress_printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
goto leave_proto_err;
}
sent += r;
+ print_progress(sent, total, 1);
} while (sent < total);
fclose(f);
f = NULL;
@@ -736,7 +1099,7 @@ leave:
}
char *errdesc = strerror(errcode);
mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
-
+
length = strlen(errdesc);
nlen = htobe32(length+1);
memcpy(buf, &nlen, 4);
@@ -746,10 +1109,10 @@ leave:
slen += length;
err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
if (err != MOBILEBACKUP2_E_SUCCESS) {
- printf("could not send message\n");
+ progress_printf("could not send message\n");
}
if (bytes != slen) {
- printf("could only send %d from %d\n", bytes, slen);
+ progress_printf("could only send %d from %d\n", bytes, slen);
}
}
@@ -760,9 +1123,9 @@ leave_proto_err:
return result;
}
-static void mb2_handle_send_files(plist_t message, const char *backup_dir)
+static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
{
- uint32_t cnt;
+ uint32_t cnt;
uint32_t i = 0;
uint32_t sent;
plist_t errplist = NULL;
@@ -771,7 +1134,8 @@ static void mb2_handle_send_files(plist_t message, const char *backup_dir)
plist_t files = plist_array_get_item(message, 1);
cnt = plist_array_get_size(files);
- if (cnt == 0) return;
+
+ //snprintf(progress_status, sizeof(progress_status), "Sending files");
for (i = 0; i < cnt; i++) {
plist_t val = plist_array_get_item(files, i);
@@ -783,7 +1147,7 @@ static void mb2_handle_send_files(plist_t message, const char *backup_dir)
if (!str)
continue;
- if (mb2_handle_send_file(backup_dir, str, &errplist) < 0) {
+ if (mb2_handle_send_file(mobilebackup2, backup_dir, str, &errplist) < 0) {
free(str);
//printf("Error when sending file '%s' to device\n", str);
// TODO: perhaps we can continue, we've got a multi status response?!
@@ -806,7 +1170,55 @@ static void mb2_handle_send_files(plist_t message, const char *backup_dir)
}
}
-static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
+static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** filename)
+{
+ uint32_t nlen = 0;
+ uint32_t rlen = 0;
+
+ do {
+ nlen = 0;
+ rlen = 0;
+ mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
+ nlen = be32toh(nlen);
+
+ if ((nlen == 0) && (rlen == 4)) {
+ // a zero length means no more files to receive
+ return 0;
+ }
+ if (rlen == 0) {
+ // device needs more time, waiting...
+ continue;
+ }
+ if (nlen > 4096) {
+ // filename length is too large
+ progress_printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
+ return 0;
+ }
+
+ if (*filename != NULL) {
+ free(*filename);
+ *filename = NULL;
+ }
+
+ *filename = (char*)malloc(nlen+1);
+
+ rlen = 0;
+ mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
+ if (rlen != nlen) {
+ progress_printf("ERROR: %s: could not read filename\n", __func__);
+ return 0;
+ }
+
+ char* p = *filename;
+ p[rlen] = 0;
+
+ break;
+ } while(1 && !quit_flag);
+
+ return nlen;
+}
+
+static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
{
uint64_t backup_real_size = 0;
uint64_t backup_total_size = 0;
@@ -824,6 +1236,8 @@ static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
plist_t node = NULL;
FILE *f = NULL;
unsigned int file_count = 0;
+ int errcode = 0;
+ char *errdesc = NULL;
if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
@@ -832,75 +1246,66 @@ static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
plist_get_uint_val(node, &backup_total_size);
}
if (backup_total_size > 0) {
- PRINT_VERBOSE(1, "Receiving files\n");
+ //progress_printf("Receiving files\n");
+ snprintf(progress_status, sizeof(progress_status), "Receiving files"); /* Clear status when starting file transfers */
+ //transfer_info[0] = '\0'; /* Clear transfer info */
}
do {
if (quit_flag)
break;
- r = 0;
- mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
- nlen = be32toh(nlen);
+
+ nlen = mb2_receive_filename(mobilebackup2, &dname);
if (nlen == 0) {
- // we're done here
- break;
- } else if (nlen > 4096) {
- // too very long path
- printf("ERROR: %s: too long device filename (%d)!\n", __func__, nlen);
break;
}
- if (dname != NULL)
- free(dname);
- dname = (char*)malloc(nlen+1);
- r = 0;
- mobilebackup2_receive_raw(mobilebackup2, dname, nlen, &r);
- if (r != nlen) {
- printf("ERROR: %s: could not read device filename\n", __func__);
- break;
- }
- dname[r] = 0;
- nlen = 0;
- mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
- nlen = be32toh(nlen);
- if (nlen == 0) {
- printf("ERROR: %s: zero-length backup filename!\n", __func__);
- break;
- } else if (nlen > 4096) {
- printf("ERROR: %s: too long backup filename (%d)!\n", __func__, nlen);
- break;
+ if (nlen > 68) {
+ snprintf(transfer_info, sizeof(transfer_info), "...%s", dname + (nlen - 65));
+ } else {
+ snprintf(transfer_info, sizeof(transfer_info), "%s", dname);
}
- fname = (char*)malloc(nlen+1);
- mobilebackup2_receive_raw(mobilebackup2, fname, nlen, &r);
- if (r != nlen) {
- printf("ERROR: %s: could not receive backup filename!\n", __func__);
+
+ nlen = mb2_receive_filename(mobilebackup2, &fname);
+ if (!nlen) {
break;
}
- fname[r] = 0;
- if (bname != NULL)
+
+ if (bname != NULL) {
free(bname);
- bname = build_path(backup_dir, fname, NULL);
- free(fname);
+ bname = NULL;
+ }
+
+ bname = string_build_path(backup_dir, fname, NULL);
+
+ if (fname != NULL) {
+ free(fname);
+ fname = NULL;
+ }
+
+ r = 0;
nlen = 0;
mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
if (r != 4) {
- printf("ERROR: %s: could not receive code length!\n", __func__);
+ progress_printf("ERROR: %s: could not receive code length!\n", __func__);
break;
}
nlen = be32toh(nlen);
+
last_code = code;
code = 0;
+
mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
if (r != 1) {
- printf("ERROR: %s: could not receive code!\n", __func__);
+ progress_printf("ERROR: %s: could not receive code!\n", __func__);
break;
}
/* TODO remove this */
if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
- PRINT_VERBOSE(1, "Found new flag %02x\n", code);
+ progress_printf("Found new flag %02x\n", code);
}
- remove(bname);
+ remove_file(bname);
f = fopen(bname, "wb");
while (f && (code == CODE_FILE_DATA)) {
blocksize = nlen-1;
@@ -923,7 +1328,7 @@ static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
backup_real_size += blocksize;
}
if (backup_total_size > 0) {
- print_progress(backup_real_size, backup_total_size);
+ print_progress(backup_real_size, backup_total_size, TRANSFER_RECEIVE);
}
if (quit_flag)
break;
@@ -941,7 +1346,10 @@ static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
fclose(f);
file_count++;
} else {
- printf("Error opening '%s' for writing: %s\n", bname, strerror(errno));
+ errcode = errno_to_device_error(errno);
+ errdesc = strerror(errno);
+ progress_printf("Error opening '%s' for writing: %s\n", bname, errdesc);
+ break;
}
if (nlen == 0) {
break;
@@ -955,19 +1363,22 @@ static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
msg[r] = 0;
/* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
if (last_code != CODE_FILE_DATA) {
- fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
+ progress_printf("Received an error message from device: %s\n", msg);
}
free(msg);
}
} while (1);
+ if (fname != NULL)
+ free(fname);
+
/* if there are leftovers to read, finish up cleanly */
if ((int)nlen-1 > 0) {
- PRINT_VERBOSE(1, "\nDiscarding current data hunk.\n");
+ progress_printf("Discarding current data hunk.\n");
fname = (char*)malloc(nlen-1);
mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
free(fname);
- remove(bname);
+ remove_file(bname);
}
/* clean up */
@@ -977,12 +1388,14 @@ static int mb2_handle_receive_files(plist_t message, const char *backup_dir)
if (dname != NULL)
free(dname);
- // TODO error handling?!
- mobilebackup2_send_status_response(mobilebackup2, 0, NULL, plist_new_dict());
+ plist_t empty_plist = plist_new_dict();
+ mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_plist);
+ plist_free(empty_plist);
+
return file_count;
}
-static void mb2_handle_list_directory(plist_t message, const char *backup_dir)
+static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
{
if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
@@ -997,7 +1410,7 @@ static void mb2_handle_list_directory(plist_t message, const char *backup_dir)
return;
}
- char *path = build_path(backup_dir, str, NULL);
+ char *path = string_build_path(backup_dir, str, NULL);
free(str);
plist_t dirlist = plist_new_dict();
@@ -1009,7 +1422,7 @@ static void mb2_handle_list_directory(plist_t message, const char *backup_dir)
if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
continue;
}
- char *fpath = build_path(path, ep->d_name, NULL);
+ char *fpath = string_build_path(path, ep->d_name, NULL);
if (fpath) {
plist_t fdict = plist_new_dict();
struct stat st;
@@ -1020,11 +1433,17 @@ static void mb2_handle_list_directory(plist_t message, const char *backup_dir)
} else if (S_ISREG(st.st_mode)) {
ftype = "DLFileTypeRegular";
}
- plist_dict_insert_item(fdict, "DLFileType", plist_new_string(ftype));
- plist_dict_insert_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
- plist_dict_insert_item(fdict, "DLFileModificationDate", plist_new_date(st.st_mtime, 0));
+ plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
+ plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
+ plist_dict_set_item(fdict, "DLFileModificationDate",
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(st.st_mtime)
+#else
+ plist_new_date(st.st_mtime - MAC_EPOCH, 0)
+#endif
+ );
- plist_dict_insert_item(dirlist, ep->d_name, fdict);
+ plist_dict_set_item(dirlist, ep->d_name, fdict);
free(fpath);
}
}
@@ -1040,7 +1459,7 @@ static void mb2_handle_list_directory(plist_t message, const char *backup_dir)
}
}
-static void mb2_handle_make_directory(plist_t message, const char *backup_dir)
+static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
{
if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
@@ -1050,7 +1469,7 @@ static void mb2_handle_make_directory(plist_t message, const char *backup_dir)
char *errdesc = NULL;
plist_get_string_val(dir, &str);
- char *newpath = build_path(backup_dir, str, NULL);
+ char *newpath = string_build_path(backup_dir, str, NULL);
free(str);
if (mkdir_with_parents(newpath, 0755) < 0) {
@@ -1082,6 +1501,7 @@ static void mb2_copy_file_by_path(const char *src, const char *dst)
/* open destination file */
if ((to = fopen(dst, "wb")) == NULL) {
printf("Cannot open destination file '%s'.\n", dst);
+ fclose(from);
return;
}
@@ -1130,224 +1550,579 @@ static void mb2_copy_directory_by_path(const char *src, const char *dst)
if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
continue;
}
- char *srcpath = build_path(src, ep->d_name, NULL);
- char *dstpath = build_path(dst, ep->d_name, NULL);
+ char *srcpath = string_build_path(src, ep->d_name, NULL);
+ char *dstpath = string_build_path(dst, ep->d_name, NULL);
if (srcpath && dstpath) {
/* copy file */
mb2_copy_file_by_path(srcpath, dstpath);
+ }
+ if (srcpath)
free(srcpath);
+ if (dstpath)
free(dstpath);
- }
}
closedir(cur_dir);
}
}
+#ifdef _WIN32
+#define BS_CC '\b'
+#define my_getch getch
+#else
+#define BS_CC 0x7f
+static int my_getch(void)
+{
+ struct termios oldt, newt;
+ int ch;
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+ ch = getchar();
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+ return ch;
+}
+#endif
+
+static void get_hidden_input(char *buf, int maxlen)
+{
+ int pwlen = 0;
+ int c;
+
+ while ((c = my_getch())) {
+ if ((c == '\r') || (c == '\n')) {
+ break;
+ }
+ if (isprint(c)) {
+ if (pwlen < maxlen-1)
+ buf[pwlen++] = c;
+ fputc('*', stderr);
+ } else if (c == BS_CC) {
+ if (pwlen > 0) {
+ fputs("\b \b", stderr);
+ pwlen--;
+ }
+ }
+ }
+ buf[pwlen] = 0;
+}
+
+static char* ask_for_password(const char* msg, int type_again)
+{
+ char pwbuf[256];
+
+ fprintf(stderr, "%s: ", msg);
+ fflush(stderr);
+ get_hidden_input(pwbuf, 256);
+ fputc('\n', stderr);
+
+ if (type_again) {
+ char pwrep[256];
+
+ fprintf(stderr, "%s (repeat): ", msg);
+ fflush(stderr);
+ get_hidden_input(pwrep, 256);
+ fputc('\n', stderr);
+
+ if (strcmp(pwbuf, pwrep) != 0) {
+ printf("ERROR: passwords don't match\n");
+ return NULL;
+ }
+ }
+ return strdup(pwbuf);
+}
+
/**
* signal handler function for cleaning up properly
*/
static void clean_exit(int sig)
{
- fprintf(stderr, "Exiting...\n");
+ snprintf(progress_status, sizeof(progress_status), "Abort requested, hold on...");
+ progress_render();
+ //fprintf(stderr, "Exiting...\n");
quit_flag++;
}
-static void print_usage(int argc, char **argv)
+static void print_usage(int argc, char **argv, int is_error)
{
- char *name = NULL;
- name = strrchr(argv[0], '/');
- printf("Usage: %s [OPTIONS] CMD [CMDOPTIONS] 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\tcreate backup for the device\n");
- printf(" restore\trestore last backup to the device\n");
- printf(" --system\trestore system files, too.\n");
- printf(" --reboot\treboot the system when done.\n");
- printf(" --copy\tcreate a copy of backup folder before restoring.\n");
- printf(" --settings\trestore device settings from the backup.\n");
- printf(" info\t\tshow details about last completed backup of device\n");
- printf(" list\t\tlist files of last completed backup in CSV format\n");
- printf(" unback\tunpack a completed backup in DIRECTORY/_unback_/\n\n");
- printf("options:\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
+ char *name = strrchr(argv[0], '/');
+ fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
+ fprintf(is_error ? stderr : stdout,
+ "\n"
+ "Create or restore backup in/from the specified directory.\n"
+ "\n"
+ "CMD:\n"
+ " backup create backup for the device\n"
+ " --full force full backup from device.\n"
+ " restore restore last backup to the device\n"
+ " --system restore system files, too.\n"
+ " --no-reboot do NOT reboot the device when done (default: yes).\n"
+ " --copy create a copy of backup folder before restoring.\n"
+ " --settings restore device settings from the backup.\n"
+ " --remove remove items which are not being restored\n"
+ " --skip-apps do not trigger re-installation of apps after restore\n"
+ " --password PWD supply the password for the encrypted source backup\n"
+ " info show details about last completed backup of device\n"
+ " list list files of last completed backup in CSV format\n"
+ " unback unpack a completed backup in DIRECTORY/_unback_/\n"
+ " encryption on|off [PWD] enable or disable backup encryption\n"
+ " changepw [OLD NEW] change backup password on target device\n"
+ " cloud on|off enable or disable cloud use (requires iCloud account)\n"
+ "\n"
+ "NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n"
+ "be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n"
+ "See man page for further details.\n"
+ "\n"
+ "OPTIONS:\n"
+ " -u, --udid UDID target specific device by UDID\n"
+ " -s, --source UDID use backup data from device specified by UDID\n"
+ " -n, --network connect to network device\n"
+ " -i, --interactive request passwords interactively\n"
+ " -d, --debug enable communication debugging\n"
+ " -h, --help prints usage information\n"
+ " -v, --version prints version information\n"
+ "\n"
+ "Homepage: <" PACKAGE_URL ">\n"
+ "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
+ );
}
int main(int argc, char *argv[])
{
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
- int i;
- char udid[41];
- uint16_t port = 0;
- udid[0] = 0;
+ lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
+ int i = 0;
+ char* udid = NULL;
+ char* source_udid = NULL;
+ int use_network = 0;
+ lockdownd_service_descriptor_t service = NULL;
int cmd = -1;
int cmd_flags = 0;
int is_full_backup = 0;
- char *backup_directory = NULL;
+ int result_code = -1;
+ char* backup_directory = NULL;
+ int interactive_mode = 0;
+ char* backup_password = NULL;
+ char* newpw = NULL;
struct stat st;
plist_t node_tmp = NULL;
plist_t info_plist = NULL;
plist_t opts = NULL;
+
+ idevice_t device = NULL;
+ afc_client_t afc = NULL;
+ np_client_t np = NULL;
+ lockdownd_client_t lockdown = NULL;
+ mobilebackup2_client_t mobilebackup2 = NULL;
mobilebackup2_error_t err;
+ uint64_t lockfile = 0;
+
+#define OPT_SYSTEM 1
+#define OPT_REBOOT 2
+#define OPT_NO_REBOOT 3
+#define OPT_COPY 4
+#define OPT_SETTINGS 5
+#define OPT_REMOVE 6
+#define OPT_SKIP_APPS 7
+#define OPT_PASSWORD 8
+#define OPT_FULL 9
+
+ int c = 0;
+ const struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "udid", required_argument, NULL, 'u' },
+ { "source", required_argument, NULL, 's' },
+ { "interactive", no_argument, NULL, 'i' },
+ { "network", no_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'v' },
+ // command options:
+ { "system", no_argument, NULL, OPT_SYSTEM },
+ { "reboot", no_argument, NULL, OPT_REBOOT },
+ { "no-reboot", no_argument, NULL, OPT_NO_REBOOT },
+ { "copy", no_argument, NULL, OPT_COPY },
+ { "settings", no_argument, NULL, OPT_SETTINGS },
+ { "remove", no_argument, NULL, OPT_REMOVE },
+ { "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
+ { "password", required_argument, NULL, OPT_PASSWORD },
+ { "full", no_argument, NULL, OPT_FULL },
+ { NULL, 0, NULL, 0}
+ };
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
+ while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
idevice_set_debug_level(1);
- continue;
- }
- else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
- i++;
- if (!argv[i] || (strlen(argv[i]) != 40)) {
- print_usage(argc, argv);
- return 0;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- strcpy(udid, argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ udid = strdup(optarg);
+ break;
+ case 's':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ }
+ source_udid = strdup(optarg);
+ break;
+ case 'i':
+ interactive_mode = 1;
+ break;
+ case 'n':
+ use_network = 1;
+ break;
+ case 'h':
+ print_usage(argc, argv, 0);
return 0;
- }
- else if (!strcmp(argv[i], "backup")) {
- cmd = CMD_BACKUP;
- }
- else if (!strcmp(argv[i], "restore")) {
- cmd = CMD_RESTORE;
- }
- else if (!strcmp(argv[i], "--system")) {
+ case 'v':
+ printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ return 0;
+ case OPT_SYSTEM:
cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
- }
- else if (!strcmp(argv[i], "--reboot")) {
- cmd_flags |= CMD_FLAG_RESTORE_REBOOT;
- }
- else if (!strcmp(argv[i], "--copy")) {
+ break;
+ case OPT_REBOOT:
+ cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
+ break;
+ case OPT_NO_REBOOT:
+ cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
+ break;
+ case OPT_COPY:
cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
- }
- else if (!strcmp(argv[i], "--settings")) {
+ break;
+ case OPT_SETTINGS:
cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
+ break;
+ case OPT_REMOVE:
+ cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
+ break;
+ case OPT_SKIP_APPS:
+ cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
+ break;
+ case OPT_PASSWORD:
+ free(backup_password);
+ backup_password = strdup(optarg);
+ break;
+ case OPT_FULL:
+ cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
+ break;
+ default:
+ print_usage(argc, argv, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "info")) {
- cmd = CMD_INFO;
- verbose = 0;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: No command specified.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ if (!strcmp(argv[0], "backup")) {
+ cmd = CMD_BACKUP;
+ }
+ else if (!strcmp(argv[0], "restore")) {
+ cmd = CMD_RESTORE;
+ }
+ else if (!strcmp(argv[0], "cloud")) {
+ cmd = CMD_CLOUD;
+ i = 1;
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "list")) {
- cmd = CMD_LIST;
- verbose = 0;
+ if (!strcmp(argv[i], "on")) {
+ cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
+ } else if (!strcmp(argv[i], "off")) {
+ cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
+ } else {
+ fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (!strcmp(argv[i], "unback")) {
- cmd = CMD_UNBACK;
+ }
+ else if (!strcmp(argv[0], "info")) {
+ cmd = CMD_INFO;
+ verbose = 0;
+ }
+ else if (!strcmp(argv[0], "list")) {
+ cmd = CMD_LIST;
+ verbose = 0;
+ }
+ else if (!strcmp(argv[0], "unback")) {
+ cmd = CMD_UNBACK;
+ }
+ else if (!strcmp(argv[0], "encryption")) {
+ cmd = CMD_CHANGEPW;
+ i = 1;
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else if (backup_directory == NULL) {
- backup_directory = argv[i];
+ if (!strcmp(argv[i], "on")) {
+ cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
+ } else if (!strcmp(argv[i], "off")) {
+ cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
+ } else {
+ fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- else {
- print_usage(argc, argv);
- return 0;
+ // check if a password was given on the command line
+ free(newpw);
+ newpw = NULL;
+ free(backup_password);
+ backup_password = NULL;
+ i++;
+ if (argv[i]) {
+ if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ newpw = strdup(argv[i]);
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
+ backup_password = strdup(argv[i]);
+ }
+ }
+ }
+ else if (!strcmp(argv[0], "changepw")) {
+ cmd = CMD_CHANGEPW;
+ cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
+ // check if passwords were given on command line
+ free(newpw);
+ newpw = NULL;
+ free(backup_password);
+ backup_password = NULL;
+ i = 1;
+ if (argv[i]) {
+ backup_password = strdup(argv[i]);
+ i++;
+ if (!argv[i]) {
+ fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ newpw = strdup(argv[i]);
}
}
+ i++;
+ if (argv[i]) {
+ backup_directory = argv[i];
+ }
+
/* verify options */
if (cmd == -1) {
- printf("No command specified.\n");
- print_usage(argc, argv);
- return -1;
+ fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]);
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
}
- if (backup_directory == NULL) {
- printf("No target backup directory specified.\n");
- print_usage(argc, argv);
- return -1;
+ if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
+ backup_directory = (char*)".this_folder_is_not_present_on_purpose";
+ } else {
+ if (backup_directory == NULL) {
+ fprintf(stderr, "ERROR: No target backup directory specified.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+
+ /* verify if passed backup directory exists */
+ if (stat(backup_directory, &st) != 0) {
+ fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
+ 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);
+ ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
+ if (ret != IDEVICE_E_SUCCESS) {
+ if (udid) {
+ printf("No device found with udid %s.\n", udid);
+ } else {
+ printf("No device found.\n");
+ }
return -1;
}
- if (udid[0] != 0) {
- ret = idevice_new(&phone, udid);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- return -1;
- }
+ if (!udid) {
+ idevice_get_udid(device, &udid);
}
- else
- {
- ret = idevice_new(&phone, NULL);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found, is it plugged in?\n");
- return -1;
- }
- char *newudid = NULL;
- idevice_get_udid(phone, &newudid);
- strcpy(udid, newudid);
- free(newudid);
+
+ if (!source_udid) {
+ source_udid = strdup(udid);
}
- /* backup directory must contain an Info.plist */
- char *info_path = build_path(backup_directory, udid, "Info.plist", NULL);
- if (cmd == CMD_RESTORE) {
- if (stat(info_path, &st) != 0) {
- free(info_path);
- printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, udid);
+ uint8_t is_encrypted = 0;
+ char *info_path = NULL;
+ if (cmd == CMD_CHANGEPW) {
+ if (!interactive_mode) {
+ if (!newpw) {
+ newpw = getenv("BACKUP_PASSWORD_NEW");
+ if (newpw) {
+ newpw = strdup(newpw);
+ }
+ }
+ if (!backup_password) {
+ backup_password = getenv("BACKUP_PASSWORD");
+ if (backup_password) {
+ backup_password = strdup(backup_password);
+ }
+ }
+ }
+ if (!interactive_mode && !backup_password && !newpw) {
+ idevice_free(device);
+ printf("ERROR: Can't get password input in non-interactive mode. Either pass password(s) on the command line, or enable interactive mode with -i or --interactive.\n");
return -1;
}
+ } else if (cmd != CMD_CLOUD) {
+ /* backup directory must contain an Info.plist */
+ info_path = string_build_path(backup_directory, source_udid, "Info.plist", NULL);
+ if (cmd == CMD_RESTORE || cmd == CMD_UNBACK) {
+ if (stat(info_path, &st) != 0) {
+ idevice_free(device);
+ free(info_path);
+ printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
+ return -1;
+ }
+ char* manifest_path = string_build_path(backup_directory, source_udid, "Manifest.plist", NULL);
+ if (stat(manifest_path, &st) != 0) {
+ free(info_path);
+ }
+ plist_t manifest_plist = NULL;
+ plist_read_from_file(manifest_path, &manifest_plist, NULL);
+ if (!manifest_plist) {
+ idevice_free(device);
+ free(info_path);
+ free(manifest_path);
+ printf("ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
+ return -1;
+ }
+ node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
+ if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
+ plist_get_bool_val(node_tmp, &is_encrypted);
+ }
+ plist_free(manifest_plist);
+ free(manifest_path);
+ }
+ PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
}
- PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
+ if (cmd != CMD_CLOUD && is_encrypted) {
+ PRINT_VERBOSE(1, "This is an encrypted backup.\n");
+ if (backup_password == NULL) {
+ backup_password = getenv("BACKUP_PASSWORD");
+ if (backup_password) {
+ backup_password = strdup(backup_password);
+ }
+ }
+ if (backup_password == NULL) {
+ if (interactive_mode) {
+ backup_password = ask_for_password("Enter backup password", 0);
+ }
+ if (!backup_password || (strlen(backup_password) == 0)) {
+ if (backup_password) {
+ free(backup_password);
+ }
+ idevice_free(device);
+ if (cmd == CMD_RESTORE) {
+ printf("ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
+ } else if (cmd == CMD_UNBACK) {
+ printf("ERROR: a backup password is required to unback an encrypted backup. Cannot continue.\n");
+ }
+ return -1;
+ }
+ }
+ }
- if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicebackup")) {
- idevice_free(phone);
+ if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
+ printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
+ idevice_free(device);
return -1;
}
+ uint8_t willEncrypt = 0;
+ node_tmp = NULL;
+ lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
+ if (node_tmp) {
+ if (plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
+ plist_get_bool_val(node_tmp, &willEncrypt);
+ }
+ plist_free(node_tmp);
+ node_tmp = NULL;
+ }
+
+ /* get ProductVersion */
+ int device_version = idevice_get_device_version(device);
+
/* start notification_proxy */
- np_client_t np = NULL;
- ret = lockdownd_start_service(client, NP_SERVICE_NAME, &port);
- if ((ret == LOCKDOWN_E_SUCCESS) && port) {
- np_client_new(phone, port, &np);
+ ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
+ if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
+ np_client_new(device, service, &np);
np_set_notify_callback(np, notify_cb, NULL);
- const char *noties[5] = {
+ const char *noties[7] = {
NP_SYNC_CANCEL_REQUEST,
NP_SYNC_SUSPEND_REQUEST,
NP_SYNC_RESUME_REQUEST,
NP_BACKUP_DOMAIN_CHANGED,
+ "com.apple.LocalAuthentication.ui.presented",
+ "com.apple.LocalAuthentication.ui.dismissed",
NULL
};
np_observe_notifications(np, noties);
} else {
- printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
+ cmd = CMD_LEAVE;
+ goto checkpoint;
+ }
+ if (service) {
+ lockdownd_service_descriptor_free(service);
+ service = NULL;
}
- afc = NULL;
- if (cmd == CMD_BACKUP) {
+ if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
/* start AFC, we need this for the lock file */
- port = 0;
- ret = lockdownd_start_service(client, "com.apple.afc", &port);
- if ((ret == LOCKDOWN_E_SUCCESS) && port) {
- afc_client_new(phone, port, &afc);
+ ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
+ if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
+ afc_client_new(device, service, &afc);
+ } else {
+ printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
+ cmd = CMD_LEAVE;
+ goto checkpoint;
}
}
+ if (service) {
+ lockdownd_service_descriptor_free(service);
+ service = NULL;
+ }
+
/* start mobilebackup service and retrieve port */
- port = 0;
- ret = lockdownd_start_service(client, MOBILEBACKUP2_SERVICE_NAME, &port);
- if ((ret == LOCKDOWN_E_SUCCESS) && port) {
- PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, port);
- mobilebackup2_client_new(phone, port, &mobilebackup2);
+ ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
+ lockdownd_client_free(lockdown);
+ lockdown = NULL;
+ if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
+ PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
+ mobilebackup2_client_new(device, service, &mobilebackup2);
+
+ if (service) {
+ lockdownd_service_descriptor_free(service);
+ service = NULL;
+ }
/* send Hello message */
double local_versions[2] = {2.0, 2.1};
@@ -1369,20 +2144,14 @@ int main(int argc, char *argv[])
}
/* verify existing Info.plist */
- if (stat(info_path, &st) == 0) {
+ if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
- plist_read_from_filename(&info_plist, info_path);
+ plist_read_from_file(info_path, &info_plist, NULL);
if (!info_plist) {
printf("Could not read Info.plist\n");
is_full_backup = 1;
}
- if (info_plist && ((cmd == CMD_BACKUP) || (cmd == CMD_RESTORE))) {
- if (!mobilebackup_info_is_current_device(info_plist)) {
- printf("Aborting. Backup data is not compatible with the current device.\n");
- cmd = CMD_LEAVE;
- }
- }
} else {
if (cmd == CMD_RESTORE) {
printf("Aborting restore. Info.plist is missing.\n");
@@ -1392,28 +2161,28 @@ int main(int argc, char *argv[])
}
}
- uint64_t lockfile = 0;
- if (cmd == CMD_BACKUP) {
- do_post_notification(NP_SYNC_WILL_START);
+ if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
+ do_post_notification(device, NP_SYNC_WILL_START);
afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
}
if (lockfile) {
afc_error_t aerr;
- do_post_notification(NP_SYNC_LOCK_REQUEST);
+ do_post_notification(device, NP_SYNC_LOCK_REQUEST);
for (i = 0; i < LOCK_ATTEMPTS; i++) {
aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
if (aerr == AFC_E_SUCCESS) {
- do_post_notification(NP_SYNC_DID_START);
+ do_post_notification(device, NP_SYNC_DID_START);
break;
- } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
+ }
+ if (aerr == AFC_E_OP_WOULD_BLOCK) {
usleep(LOCK_WAIT);
continue;
- } else {
- fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
- afc_file_close(afc, lockfile);
- lockfile = 0;
- cmd = CMD_LEAVE;
}
+
+ fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
+ afc_file_close(afc, lockfile);
+ lockfile = 0;
+ cmd = CMD_LEAVE;
}
if (i == LOCK_ATTEMPTS) {
fprintf(stderr, "ERROR: timeout while locking for sync\n");
@@ -1426,47 +2195,95 @@ int main(int argc, char *argv[])
checkpoint:
switch(cmd) {
+ case CMD_CLOUD:
+ opts = plist_new_dict();
+ plist_dict_set_item(opts, "CloudBackupState", plist_new_bool(cmd_flags & CMD_FLAG_CLOUD_ENABLE ? 1: 0));
+ err = mobilebackup2_send_request(mobilebackup2, "EnableCloudBackup", udid, source_udid, opts);
+ plist_free(opts);
+ opts = NULL;
+ if (err != MOBILEBACKUP2_E_SUCCESS) {
+ printf("Error setting cloud backup state on device, error code %d\n", err);
+ cmd = CMD_LEAVE;
+ }
+ break;
case CMD_BACKUP:
PRINT_VERBOSE(1, "Starting backup...\n");
/* make sure backup device sub-directory exists */
- char *devbackupdir = build_path(backup_directory, udid, NULL);
+ char* devbackupdir = string_build_path(backup_directory, source_udid, NULL);
__mkdir(devbackupdir, 0755);
free(devbackupdir);
+ if (strcmp(source_udid, udid) != 0) {
+ /* handle different source backup directory */
+ // make sure target backup device sub-directory exists
+ devbackupdir = string_build_path(backup_directory, udid, NULL);
+ __mkdir(devbackupdir, 0755);
+ free(devbackupdir);
+
+ // use Info.plist path in target backup folder */
+ free(info_path);
+ info_path = string_build_path(backup_directory, udid, "Info.plist", NULL);
+ }
+
/* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
- /* TODO: verify battery on AC enough battery remaining */
+ /* TODO: verify battery on AC enough battery remaining */
/* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
if (info_plist) {
plist_free(info_plist);
info_plist = NULL;
}
- info_plist = mobilebackup_factory_info_plist_new();
- remove(info_path);
- plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
+ info_plist = mobilebackup_factory_info_plist_new(udid, device, afc);
+ if (!info_plist) {
+ fprintf(stderr, "Failed to generate Info.plist - aborting\n");
+ cmd = CMD_LEAVE;
+ }
+ remove_file(info_path);
+ plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
free(info_path);
plist_free(info_plist);
info_plist = NULL;
+ if (cmd_flags & CMD_FLAG_FORCE_FULL_BACKUP) {
+ PRINT_VERBOSE(1, "Enforcing full backup from device.\n");
+ opts = plist_new_dict();
+ plist_dict_set_item(opts, "ForceFullBackup", plist_new_bool(1));
+ }
/* request backup from device with manifest from last backup */
+ if (willEncrypt) {
+ PRINT_VERBOSE(1, "Backup will be encrypted.\n");
+ } else {
+ PRINT_VERBOSE(1, "Backup will be unencrypted.\n");
+ }
PRINT_VERBOSE(1, "Requesting backup from device...\n");
-
- err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, NULL, NULL);
+ err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, opts);
+ if (opts)
+ plist_free(opts);
if (err == MOBILEBACKUP2_E_SUCCESS) {
if (is_full_backup) {
PRINT_VERBOSE(1, "Full backup mode.\n");
} else {
PRINT_VERBOSE(1, "Incremental backup mode.\n");
}
+ if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) {
+ /* let's wait 2 second to see if the device passcode is requested */
+ int retries = 20;
+ while (retries-- > 0 && !passcode_requested) {
+ usleep(100000);
+ }
+ if (passcode_requested) {
+ printf("*** Waiting for passcode to be entered on the device ***\n");
+ }
+ }
} else {
if (err == MOBILEBACKUP2_E_BAD_VERSION) {
printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
} else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
} else {
- printf("ERROR: Could not start backup process: unspecified error occured\n");
+ printf("ERROR: Could not start backup process: unspecified error occurred\n");
}
cmd = CMD_LEAVE;
}
@@ -1475,7 +2292,7 @@ checkpoint:
/* TODO: verify battery on AC enough battery remaining */
/* verify if Status.plist says we read from an successful backup */
- if (!mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
+ if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
cmd = CMD_LEAVE;
break;
@@ -1484,18 +2301,37 @@ checkpoint:
PRINT_VERBOSE(1, "Starting Restore...\n");
opts = plist_new_dict();
- plist_dict_insert_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
+ plist_dict_set_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
PRINT_VERBOSE(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
- if ((cmd_flags & CMD_FLAG_RESTORE_REBOOT) == 0)
- plist_dict_insert_item(opts, "RestoreShouldReboot", plist_new_bool(0));
- PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_REBOOT ? "Yes":"No"));
+ if (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT)
+ plist_dict_set_item(opts, "RestoreShouldReboot", plist_new_bool(0));
+ PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT ? "No":"Yes"));
if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
- plist_dict_insert_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
+ plist_dict_set_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
- plist_dict_insert_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
- PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
+ plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
+ PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
+ plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS));
+ PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
+ if (backup_password != NULL) {
+ plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
+ }
+ PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
+
+ if (cmd_flags & CMD_FLAG_RESTORE_SKIP_APPS) {
+ PRINT_VERBOSE(1, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
+ } else {
+ /* Write /iTunesRestore/RestoreApplications.plist so that the device will start
+ * restoring applications once the rest of the restore process is finished */
+ if (write_restore_applications(info_plist, afc) < 0) {
+ cmd = CMD_LEAVE;
+ break;
+ }
+ PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
+ }
- err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, udid, opts);
+ /* Start restore */
+ err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
plist_free(opts);
if (err != MOBILEBACKUP2_E_SUCCESS) {
if (err == MOBILEBACKUP2_E_BAD_VERSION) {
@@ -1503,14 +2339,14 @@ checkpoint:
} else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
} else {
- printf("ERROR: Could not start restore process: unspecified error occured\n");
+ printf("ERROR: Could not start restore process: unspecified error occurred\n");
}
cmd = CMD_LEAVE;
}
break;
case CMD_INFO:
PRINT_VERBOSE(1, "Requesting backup info from device...\n");
- err = mobilebackup2_send_request(mobilebackup2, "Info", udid, NULL, NULL);
+ err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
if (err != MOBILEBACKUP2_E_SUCCESS) {
printf("Error requesting backup info from device, error code %d\n", err);
cmd = CMD_LEAVE;
@@ -1518,7 +2354,7 @@ checkpoint:
break;
case CMD_LIST:
PRINT_VERBOSE(1, "Requesting backup list from device...\n");
- err = mobilebackup2_send_request(mobilebackup2, "List", udid, NULL, NULL);
+ err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
if (err != MOBILEBACKUP2_E_SUCCESS) {
printf("Error requesting backup list from device, error code %d\n", err);
cmd = CMD_LEAVE;
@@ -1526,62 +2362,205 @@ checkpoint:
break;
case CMD_UNBACK:
PRINT_VERBOSE(1, "Starting to unpack backup...\n");
- err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, NULL, NULL);
+ if (backup_password != NULL) {
+ opts = plist_new_dict();
+ plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
+ }
+ PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
+ err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, source_udid, opts);
+ if (backup_password !=NULL) {
+ plist_free(opts);
+ }
if (err != MOBILEBACKUP2_E_SUCCESS) {
printf("Error requesting unback operation from device, error code %d\n", err);
cmd = CMD_LEAVE;
}
break;
+ case CMD_CHANGEPW:
+ opts = plist_new_dict();
+ plist_dict_set_item(opts, "TargetIdentifier", plist_new_string(udid));
+ if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ if (!willEncrypt) {
+ if (!newpw) {
+ newpw = getenv("BACKUP_PASSWORD");
+ if (newpw) {
+ newpw = strdup(newpw);
+ }
+ }
+ if (!newpw) {
+ newpw = ask_for_password("Enter new backup password", 1);
+ }
+ if (!newpw) {
+ printf("No backup password given. Aborting.\n");
+ }
+ } else {
+ printf("ERROR: Backup encryption is already enabled. Aborting.\n");
+ cmd = CMD_LEAVE;
+ if (newpw) {
+ free(newpw);
+ newpw = NULL;
+ }
+ }
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
+ if (willEncrypt) {
+ if (!backup_password) {
+ backup_password = getenv("BACKUP_PASSWORD");
+ if (backup_password) {
+ backup_password = strdup(backup_password);
+ }
+ }
+ if (!backup_password) {
+ backup_password = ask_for_password("Enter current backup password", 0);
+ }
+ } else {
+ printf("ERROR: Backup encryption is not enabled. Aborting.\n");
+ cmd = CMD_LEAVE;
+ if (backup_password) {
+ free(backup_password);
+ backup_password = NULL;
+ }
+ }
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
+ if (willEncrypt) {
+ if (!backup_password) {
+ backup_password = ask_for_password("Enter old backup password", 0);
+ newpw = ask_for_password("Enter new backup password", 1);
+ }
+ } else {
+ printf("ERROR: Backup encryption is not enabled so can't change password. Aborting.\n");
+ cmd = CMD_LEAVE;
+ if (newpw) {
+ free(newpw);
+ newpw = NULL;
+ }
+ if (backup_password) {
+ free(backup_password);
+ backup_password = NULL;
+ }
+ }
+ }
+ if (newpw) {
+ plist_dict_set_item(opts, "NewPassword", plist_new_string(newpw));
+ }
+ if (backup_password) {
+ plist_dict_set_item(opts, "OldPassword", plist_new_string(backup_password));
+ }
+ if (newpw || backup_password) {
+ mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
+ uint8_t passcode_hint = 0;
+ if (device_version >= IDEVICE_DEVICE_VERSION(13,0,0)) {
+ diagnostics_relay_client_t diag = NULL;
+ if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
+ plist_t dict = NULL;
+ plist_t keys = plist_new_array();
+ plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
+ if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
+ plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
+ plist_get_bool_val(node, &passcode_hint);
+ }
+ plist_free(keys);
+ plist_free(dict);
+ diagnostics_relay_goodbye(diag);
+ diagnostics_relay_client_free(diag);
+ }
+ }
+ if (passcode_hint) {
+ if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
+ PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
+ PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
+ }
+ }
+ /*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ int retr = 10;
+ while ((retr-- >= 0) && !backup_domain_changed) {
+ sleep(1);
+ }
+ }*/
+ } else {
+ cmd = CMD_LEAVE;
+ }
+ plist_free(opts);
+ break;
default:
break;
}
- /* close down the lockdown connection as it is no longer needed */
- if (client) {
- lockdownd_client_free(client);
- client = NULL;
- }
-
if (cmd != CMD_LEAVE) {
/* reset operation success status */
int operation_ok = 0;
plist_t message = NULL;
+ mobilebackup2_error_t mberr;
char *dlmsg = NULL;
int file_count = 0;
int errcode = 0;
const char *errdesc = NULL;
+ int progress_finished = 0;
/* process series of DLMessage* operations */
do {
- if (dlmsg) {
- free(dlmsg);
- dlmsg = NULL;
- }
- mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
- if (!message || !dlmsg) {
- PRINT_VERBOSE(1, "Device is not ready yet. Going to try again in 2 seconds...\n");
- sleep(2);
+ free(dlmsg);
+ dlmsg = NULL;
+ mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
+ if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) {
+ PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n");
+ goto files_out;
+ } else if (mberr != MOBILEBACKUP2_E_SUCCESS) {
+ PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr);
+ quit_flag++;
goto files_out;
}
-
+
if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
/* device wants to download files from the computer */
- mb2_handle_send_files(message, backup_directory);
+ mb2_set_overall_progress_from_message(message, dlmsg);
+ mb2_handle_send_files(mobilebackup2, message, backup_directory);
+ //snprintf(progress_status, sizeof(progress_status), "Waiting for device...");
+ //progress_render();
} else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
/* device wants to send files to the computer */
- file_count += mb2_handle_receive_files(message, backup_directory);
+ mb2_set_overall_progress_from_message(message, dlmsg);
+ file_count += mb2_handle_receive_files(mobilebackup2, message, backup_directory);
+ } else if (!strcmp(dlmsg, "DLMessageGetFreeDiskSpace")) {
+ /* device wants to know how much disk space is available on the computer */
+ uint64_t freespace = 0;
+ int res = -1;
+#ifdef _WIN32
+ if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
+ res = 0;
+ }
+#else
+ struct statvfs fs;
+ memset(&fs, '\0', sizeof(fs));
+ res = statvfs(backup_directory, &fs);
+ if (res == 0) {
+ freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;
+ }
+#endif
+ plist_t freespace_item = plist_new_uint(freespace);
+ mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
+ plist_free(freespace_item);
+ } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
+ /* device wants to purge disk space on the host - not supported */
+ plist_t empty_dict = plist_new_dict();
+ err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict);
+ plist_free(empty_dict);
} else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
/* list directory contents */
- mb2_handle_list_directory(message, backup_directory);
+ mb2_handle_list_directory(mobilebackup2, message, backup_directory);
} else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
/* make a directory */
- mb2_handle_make_directory(message, backup_directory);
- } else if (!strcmp(dlmsg, "DLMessageMoveFiles")) {
+ mb2_handle_make_directory(mobilebackup2, message, backup_directory);
+ } else if (!strcmp(dlmsg, "DLMessageMoveFiles") || !strcmp(dlmsg, "DLMessageMoveItems")) {
/* perform a series of rename operations */
+ mb2_set_overall_progress_from_message(message, dlmsg);
plist_t moves = plist_array_get_item(message, 1);
uint32_t cnt = plist_dict_get_size(moves);
- PRINT_VERBOSE(1, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
+ snprintf(progress_status, sizeof(progress_status), "Moving %d file%s", cnt, (cnt == 1) ? "" : "s");
+ progress_render();
plist_dict_iter iter = NULL;
plist_dict_new_iter(moves, &iter);
errcode = 0;
@@ -1595,11 +2574,14 @@ checkpoint:
char *str = NULL;
plist_get_string_val(val, &str);
if (str) {
- char *newpath = build_path(backup_directory, str, NULL);
+ char *newpath = string_build_path(backup_directory, str, NULL);
free(str);
- char *oldpath = build_path(backup_directory, key, NULL);
+ char *oldpath = string_build_path(backup_directory, key, NULL);
- remove(newpath);
+ if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode))
+ rmdir_recursive(newpath);
+ else
+ remove_file(newpath);
if (rename(oldpath, newpath) < 0) {
printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
errcode = errno_to_device_error(errno);
@@ -1619,14 +2601,18 @@ checkpoint:
errdesc = "Could not create dict iterator";
printf("Could not create dict iterator\n");
}
- err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
+ plist_t empty_dict = plist_new_dict();
+ err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
+ plist_free(empty_dict);
if (err != MOBILEBACKUP2_E_SUCCESS) {
printf("Could not send status response, error %d\n", err);
}
- } else if (!strcmp(dlmsg, "DLMessageRemoveFiles")) {
+ } else if (!strcmp(dlmsg, "DLMessageRemoveFiles") || !strcmp(dlmsg, "DLMessageRemoveItems")) {
+ mb2_set_overall_progress_from_message(message, dlmsg);
plist_t removes = plist_array_get_item(message, 1);
uint32_t cnt = plist_array_get_size(removes);
- PRINT_VERBOSE(1, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
+ snprintf(progress_status, sizeof(progress_status), "Removing %d file%s", cnt, (cnt == 1) ? "" : "s");
+ progress_render();
uint32_t ii = 0;
errcode = 0;
errdesc = NULL;
@@ -1636,18 +2622,34 @@ checkpoint:
char *str = NULL;
plist_get_string_val(val, &str);
if (str) {
- char *newpath = build_path(backup_directory, str, NULL);
+ const char *checkfile = strchr(str, '/');
+ int suppress_warning = 0;
+ if (checkfile) {
+ if (strcmp(checkfile+1, "Manifest.mbdx") == 0) {
+ suppress_warning = 1;
+ }
+ }
+ char *newpath = string_build_path(backup_directory, str, NULL);
free(str);
- if (remove(newpath) < 0) {
- printf("Could not remove '%s': %s (%d)\n", newpath, strerror(errno), errno);
- errcode = errno_to_device_error(errno);
- errdesc = strerror(errno);
+ int res = 0;
+ if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode)) {
+ res = rmdir_recursive(newpath);
+ } else {
+ res = remove_file(newpath);
+ }
+ if (res != 0 && res != ENOENT) {
+ if (!suppress_warning)
+ printf("Could not remove '%s': %s (%d)\n", newpath, strerror(res), res);
+ errcode = errno_to_device_error(res);
+ errdesc = strerror(res);
}
free(newpath);
}
}
}
- err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
+ plist_t empty_dict = plist_new_dict();
+ err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
+ plist_free(empty_dict);
if (err != MOBILEBACKUP2_E_SUCCESS) {
printf("Could not send status response, error %d\n", err);
}
@@ -1662,8 +2664,8 @@ checkpoint:
plist_get_string_val(srcpath, &src);
plist_get_string_val(dstpath, &dst);
if (src && dst) {
- char *oldpath = build_path(backup_directory, src, NULL);
- char *newpath = build_path(backup_directory, dst, NULL);
+ char *oldpath = string_build_path(backup_directory, src, NULL);
+ char *newpath = string_build_path(backup_directory, dst, NULL);
PRINT_VERBOSE(1, "Copying '%s' to '%s'\n", src, dst);
@@ -1680,8 +2682,9 @@ checkpoint:
free(src);
free(dst);
}
-
- err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, plist_new_dict());
+ plist_t empty_dict = plist_new_dict();
+ err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
+ plist_free(empty_dict);
if (err != MOBILEBACKUP2_E_SUCCESS) {
printf("Could not send status response, error %d\n", err);
}
@@ -1701,6 +2704,9 @@ checkpoint:
error_code = (uint32_t)ec;
if (error_code == 0) {
operation_ok = 1;
+ result_code = 0;
+ } else {
+ result_code = -error_code;
}
}
nn = plist_dict_get_item(node_tmp, "ErrorDescription");
@@ -1726,28 +2732,25 @@ checkpoint:
printf("%s", str);
free(str);
}
-
break;
}
/* print status */
- if (plist_array_get_size(message) >= 3) {
- plist_t pnode = plist_array_get_item(message, 3);
- if (pnode && (plist_get_node_type(pnode) == PLIST_REAL)) {
- double progress = 0.0;
- plist_get_real_val(pnode, &progress);
- if (progress > 0) {
- print_progress_real(progress, 0);
- PRINT_VERBOSE(1, " Finished\n");
- }
+ if ((overall_progress > 0) && !progress_finished) {
+ if (overall_progress >= 100.0F) {
+ progress_finished = 1;
+ //progress_finish();
+ //} else {
+ // print_progress_real(overall_progress, 0);
}
}
- if (message)
- plist_free(message);
+files_out:
+ plist_free(message);
message = NULL;
+ free(dlmsg);
+ dlmsg = NULL;
-files_out:
if (quit_flag > 0) {
/* need to cancel the backup here */
//mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
@@ -1761,17 +2764,37 @@ files_out:
}
} while (1);
+ plist_free(message);
+ free(dlmsg);
+
+ progress_finish();
+
/* report operation status to user */
switch (cmd) {
+ case CMD_CLOUD:
+ if (cmd_flags & CMD_FLAG_CLOUD_ENABLE) {
+ if (operation_ok) {
+ PRINT_VERBOSE(1, "Cloud backup has been enabled successfully.\n");
+ } else {
+ PRINT_VERBOSE(1, "Could not enable cloud backup.\n");
+ }
+ } else if (cmd_flags & CMD_FLAG_CLOUD_DISABLE) {
+ if (operation_ok) {
+ PRINT_VERBOSE(1, "Cloud backup has been disabled successfully.\n");
+ } else {
+ PRINT_VERBOSE(1, "Could not disable cloud backup.\n");
+ }
+ }
+ break;
case CMD_BACKUP:
PRINT_VERBOSE(1, "Received %d files from device.\n", file_count);
- if (mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
+ if (operation_ok && mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
PRINT_VERBOSE(1, "Backup Successful.\n");
} else {
if (quit_flag) {
PRINT_VERBOSE(1, "Backup Aborted.\n");
} else {
- PRINT_VERBOSE(1, "Backup Failed.\n");
+ PRINT_VERBOSE(1, "Backup Failed (Error Code %d).\n", -result_code);
}
}
break;
@@ -1783,15 +2806,41 @@ files_out:
PRINT_VERBOSE(1, "Unback Successful.\n");
}
break;
+ case CMD_CHANGEPW:
+ if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
+ if (operation_ok) {
+ PRINT_VERBOSE(1, "Backup encryption has been enabled successfully.\n");
+ } else {
+ PRINT_VERBOSE(1, "Could not enable backup encryption.\n");
+ }
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
+ if (operation_ok) {
+ PRINT_VERBOSE(1, "Backup encryption has been disabled successfully.\n");
+ } else {
+ PRINT_VERBOSE(1, "Could not disable backup encryption.\n");
+ }
+ } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
+ if (operation_ok) {
+ PRINT_VERBOSE(1, "Backup encryption password has been changed successfully.\n");
+ } else {
+ PRINT_VERBOSE(1, "Could not change backup encryption password.\n");
+ }
+ }
+ break;
case CMD_RESTORE:
- if (cmd_flags & CMD_FLAG_RESTORE_REBOOT)
- PRINT_VERBOSE(1, "The device should reboot now.\n");
if (operation_ok) {
+ if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
+ PRINT_VERBOSE(1, "The device should reboot now.\n");
PRINT_VERBOSE(1, "Restore Successful.\n");
} else {
- PRINT_VERBOSE(1, "Restore Failed.\n");
+ afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist");
+ afc_remove_path(afc, "/iTunesRestore");
+ if (quit_flag) {
+ PRINT_VERBOSE(1, "Restore Aborted.\n");
+ } else {
+ PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
+ }
}
-
break;
case CMD_INFO:
case CMD_LIST:
@@ -1811,31 +2860,51 @@ files_out:
afc_file_lock(afc, lockfile, AFC_LOCK_UN);
afc_file_close(afc, lockfile);
lockfile = 0;
- if (cmd == CMD_BACKUP)
- do_post_notification(NP_SYNC_DID_FINISH);
+ if (cmd == CMD_BACKUP || cmd == CMD_RESTORE)
+ do_post_notification(device, NP_SYNC_DID_FINISH);
}
} else {
- printf("ERROR: Could not start service %s.\n", MOBILEBACKUP2_SERVICE_NAME);
- lockdownd_client_free(client);
- client = NULL;
+ printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
+ lockdownd_client_free(lockdown);
+ lockdown = NULL;
}
- if (client) {
- lockdownd_client_free(client);
- client = NULL;
+ if (lockdown) {
+ lockdownd_client_free(lockdown);
+ lockdown = NULL;
}
- if (mobilebackup2)
+ if (mobilebackup2) {
mobilebackup2_client_free(mobilebackup2);
+ mobilebackup2 = NULL;
+ }
- if (afc)
+ if (afc) {
afc_client_free(afc);
+ afc = NULL;
+ }
- if (np)
+ if (np) {
np_client_free(np);
+ np = NULL;
+ }
+
+ idevice_free(device);
+ device = NULL;
- idevice_free(phone);
+ if (backup_password) {
+ free(backup_password);
+ }
+
+ if (udid) {
+ free(udid);
+ udid = NULL;
+ }
+ if (source_udid) {
+ free(source_udid);
+ source_udid = NULL;
+ }
- return 0;
+ return result_code;
}