summaryrefslogtreecommitdiffstats
path: root/tools/idevicebackup2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/idevicebackup2.c')
-rw-r--r--tools/idevicebackup2.c1037
1 files changed, 712 insertions, 325 deletions
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
index 58fda8d..f5eb1e4 100644
--- a/tools/idevicebackup2.c
+++ b/tools/idevicebackup2.c
@@ -2,8 +2,8 @@
* idevicebackup2.c
* Command line interface to use the device's backup and restore service
*
- * Copyright (c) 2010-2018 Nikias Bassen All Rights Reserved.
- * Copyright (c) 2009-2010 Martin Szulecki 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
@@ -24,9 +24,12 @@
#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>
@@ -34,6 +37,7 @@
#include <libgen.h>
#include <ctype.h>
#include <time.h>
+#include <getopt.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
@@ -42,17 +46,22 @@
#include <libimobiledevice/afc.h>
#include <libimobiledevice/installation_proxy.h>
#include <libimobiledevice/sbservices.h>
-#include "common/utils.h"
+#include <libimobiledevice/diagnostics_relay.h>
+#include <libimobiledevice-glue/utils.h>
+#include <plist/plist.h>
#include <endianness.h>
#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>
@@ -66,6 +75,7 @@
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__); };
@@ -107,6 +117,10 @@ static void notify_cb(const char *notification, void *userdata)
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);
}
@@ -118,21 +132,15 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil
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;
- }
- }
- afc_dictionary_free(fileinfo);
+ fsize = plist_dict_get_uint(fileinfo, "st_size");
+ plist_free(fileinfo);
if (fsize == 0) {
return;
@@ -165,7 +173,7 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil
static int __mkdir(const char* path, int mode)
{
-#ifdef WIN32
+#ifdef _WIN32
return mkdir(path);
#else
return mkdir(path, mode);
@@ -177,9 +185,8 @@ 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);
char *parentdir = dirname(parent);
@@ -195,7 +202,7 @@ static int mkdir_with_parents(const char *dir, int mode)
return res;
}
-#ifdef WIN32
+#ifdef _WIN32
static int win32err_to_errno(int err_value)
{
switch (err_value) {
@@ -212,7 +219,7 @@ static int win32err_to_errno(int err_value)
static int remove_file(const char* path)
{
int e = 0;
-#ifdef WIN32
+#ifdef _WIN32
if (!DeleteFile(path)) {
e = win32err_to_errno(GetLastError());
}
@@ -227,7 +234,7 @@ static int remove_file(const char* path)
static int remove_directory(const char* path)
{
int e = 0;
-#ifdef WIN32
+#ifdef _WIN32
if (!RemoveDirectory(path)) {
e = win32err_to_errno(GetLastError());
}
@@ -345,7 +352,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
char *udid_uppercase = NULL;
lockdownd_client_t lockdown = NULL;
- if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2") != LOCKDOWN_E_SUCCESS) {
+ if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
return NULL;
}
@@ -366,7 +373,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
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, "idevicebackup2") == INSTPROXY_E_SUCCESS) {
+ 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);
@@ -375,7 +382,7 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
instproxy_browse(ip, client_opts, &apps);
sbservices_client_t sbs = NULL;
- if (sbservices_client_start_service(device, &sbs, "idevicebackup2") != SBSERVICES_E_SUCCESS) {
+ if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
printf("Couldn't establish sbservices connection. Continuing anyway.\n");
}
@@ -448,7 +455,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
/* Installed Applications */
plist_dict_set_item(ret, "Installed Applications", installed_apps);
- plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 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)
@@ -545,9 +558,11 @@ static int write_restore_applications(plist_t info_plist, afc_client_t afc)
uint32_t applications_plist_xml_length = 0;
plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
- if (applications_plist) {
- plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
+ if (!applications_plist) {
+ printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
+ return 0;
}
+ plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
if (!applications_plist_xml) {
printf("Error preparing RestoreApplications.plist\n");
goto leave;
@@ -598,7 +613,7 @@ static int mb2_status_check_snapshot_state(const char *path, const char *udid, c
plist_t 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");
@@ -626,19 +641,19 @@ static void do_post_notification(idevice_t device, const char *notification)
lockdownd_client_t lockdown = NULL;
- if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2") != LOCKDOWN_E_SUCCESS) {
+ if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
return;
}
- lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
- if (service && service->port) {
+ 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("Could not start %s\n", NP_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
}
if (service) {
@@ -648,6 +663,193 @@ static void do_post_notification(idevice_t device, const char *notification)
lockdownd_client_free(lockdown);
}
+/* 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("]");
+}
+
+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';
+ }
+}
+
+/* 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();
+ }
+
+ /* 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 = "";
+ }
+
+ /* 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);
+
+ /* 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);
+ }
+
+ /* 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);
+}
+
+/*
+ * 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, ...)
+{
+ va_list ap;
+
+ if (progress_mode_active) {
+ /* Go to saved line 4 and move to a fresh line below the block */
+ printf(CURSOR_RESTORE);
+ printf("\n");
+ }
+
+ 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;
@@ -663,42 +865,55 @@ 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;
-
- if (progress > 100)
- progress = 100;
-
- print_progress_real((double)progress, 0);
-
- format_size = string_format_size(current);
- PRINT_VERBOSE(1, " (%s", format_size);
- free(format_size);
- format_size = string_format_size(total);
- PRINT_VERBOSE(1, "/%s) ", format_size);
- free(format_size);
-
- fflush(stdout);
- if (progress == 100)
- PRINT_VERBOSE(1, "\n");
+ //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();
}
-static double overall_progress = 0;
+/* 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';
+ }
+}
+#if 0
static void mb2_set_overall_progress(double progress)
{
if (progress > 0.0)
overall_progress = progress;
}
+#endif
static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
{
@@ -717,7 +932,8 @@ static void mb2_set_overall_progress_from_message(plist_t message, char* identif
if (node != NULL) {
plist_get_real_val(node, &progress);
- mb2_set_overall_progress(progress);
+ //mb2_set_overall_progress(progress);
+ overall_progress = progress;
}
}
@@ -737,8 +953,18 @@ 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;
}
}
@@ -749,7 +975,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
uint32_t bytes = 0;
char *localfile = string_build_path(backup_dir, path, NULL);
char buf[32768];
-#ifdef WIN32
+#ifdef _WIN32
struct _stati64 fst;
#else
struct stat fst;
@@ -760,7 +986,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
int errcode = -1;
int result = -1;
uint32_t length;
-#ifdef WIN32
+#ifdef _WIN32
uint64_t total;
uint64_t sent;
#else
@@ -791,23 +1017,24 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
goto leave_proto_err;
}
-#ifdef WIN32
+#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 = string_format_size(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;
@@ -816,7 +1043,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
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;
}
@@ -839,7 +1066,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
/* 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;
}
@@ -848,10 +1075,11 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
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;
@@ -881,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);
}
}
@@ -907,6 +1135,8 @@ static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t
plist_t files = plist_array_get_item(message, 1);
cnt = plist_array_get_size(files);
+ //snprintf(progress_status, sizeof(progress_status), "Sending files");
+
for (i = 0; i < cnt; i++) {
plist_t val = plist_array_get_item(files, i);
if (plist_get_node_type(val) != PLIST_STRING) {
@@ -954,12 +1184,14 @@ static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** fil
if ((nlen == 0) && (rlen == 4)) {
// a zero length means no more files to receive
return 0;
- } else if(rlen == 0) {
+ }
+ if (rlen == 0) {
// device needs more time, waiting...
continue;
- } else if (nlen > 4096) {
+ }
+ if (nlen > 4096) {
// filename length is too large
- printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
+ progress_printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
return 0;
}
@@ -973,7 +1205,7 @@ static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** fil
rlen = 0;
mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
if (rlen != nlen) {
- printf("ERROR: %s: could not read filename\n", __func__);
+ progress_printf("ERROR: %s: could not read filename\n", __func__);
return 0;
}
@@ -1014,17 +1246,24 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
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;
- nlen = mb2_receive_filename(mobilebackup2, &dname);
+ nlen = mb2_receive_filename(mobilebackup2, &dname);
if (nlen == 0) {
break;
}
+ if (nlen > 68) {
+ snprintf(transfer_info, sizeof(transfer_info), "...%s", dname + (nlen - 65));
+ } else {
+ snprintf(transfer_info, sizeof(transfer_info), "%s", dname);
+ }
nlen = mb2_receive_filename(mobilebackup2, &fname);
if (!nlen) {
@@ -1047,7 +1286,7 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
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);
@@ -1057,13 +1296,13 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
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_file(bname);
@@ -1089,7 +1328,7 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
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;
@@ -1109,7 +1348,7 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
} else {
errcode = errno_to_device_error(errno);
errdesc = strerror(errno);
- printf("Error opening '%s' for writing: %s\n", bname, errdesc);
+ progress_printf("Error opening '%s' for writing: %s\n", bname, errdesc);
break;
}
if (nlen == 0) {
@@ -1124,7 +1363,7 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
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);
}
@@ -1135,7 +1374,7 @@ static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_
/* 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);
@@ -1197,7 +1436,12 @@ static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plis
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",
- plist_new_date(st.st_mtime - MAC_EPOCH, 0));
+#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_set_item(dirlist, ep->d_name, fdict);
free(fpath);
@@ -1322,7 +1566,7 @@ static void mb2_copy_directory_by_path(const char *src, const char *dst)
}
}
-#ifdef WIN32
+#ifdef _WIN32
#define BS_CC '\b'
#define my_getch getch
#else
@@ -1349,7 +1593,8 @@ static void get_hidden_input(char *buf, int maxlen)
while ((c = my_getch())) {
if ((c == '\r') || (c == '\n')) {
break;
- } else if (isprint(c)) {
+ }
+ if (isprint(c)) {
if (pwlen < maxlen-1)
buf[pwlen++] = c;
fputc('*', stderr);
@@ -1393,53 +1638,64 @@ static char* ask_for_password(const char* msg, int type_again)
*/
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(" --full\t\tforce full backup from device.\n");
- printf(" restore\trestore last backup to the device\n");
- printf(" --system\t\trestore system files, too.\n");
- printf(" --no-reboot\t\tdo NOT reboot the system when done (default: yes).\n");
- printf(" --copy\t\tcreate a copy of backup folder before restoring.\n");
- printf(" --settings\t\trestore device settings from the backup.\n");
- printf(" --remove\t\tremove items which are not being restored\n");
- printf(" --skip-apps\t\tdo not trigger re-installation of apps after restore\n");
- printf(" --password PWD\tsupply the password of the source 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");
- printf(" encryption on|off [PWD]\tenable or disable backup encryption\n");
- printf(" NOTE: password will be requested in interactive mode if omitted\n");
- printf(" changepw [OLD NEW] change backup password on target device\n");
- printf(" NOTE: passwords will be requested in interactive mode if omitted\n");
- printf(" cloud on|off\tenable or disable cloud use (requires iCloud account)\n");
- printf("\n");
- printf("options:\n");
- printf(" -d, --debug\t\tenable communication debugging\n");
- printf(" -u, --udid UDID\ttarget specific device by UDID\n");
- printf(" -s, --source UDID\tuse backup data from device specified by UDID\n");
- printf(" -i, --interactive\trequest passwords interactively\n");
- printf(" -h, --help\t\tprints usage information\n");
- printf("\n");
- printf("Homepage: <" PACKAGE_URL ">\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;
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
- int i;
+ 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;
@@ -1453,223 +1709,257 @@ int main(int argc, char *argv[])
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] || !*argv[i]) {
- print_usage(argc, argv);
- return -1;
+ break;
+ case 'u':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
}
- udid = strdup(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--source")) {
- i++;
- if (!argv[i] || !*argv[i]) {
- print_usage(argc, argv);
- return -1;
+ 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(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--interactive")) {
+ source_udid = strdup(optarg);
+ break;
+ case 'i':
interactive_mode = 1;
- continue;
- }
- else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- print_usage(argc, argv);
+ 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")) {
+ break;
+ case OPT_REBOOT:
cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
- }
- else if (!strcmp(argv[i], "--no-reboot")) {
+ break;
+ case OPT_NO_REBOOT:
cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
- }
- else if (!strcmp(argv[i], "--copy")) {
+ 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;
- }
- else if (!strcmp(argv[i], "--remove")) {
+ break;
+ case OPT_REMOVE:
cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
- }
- else if (!strcmp(argv[i], "--skip-apps")) {
+ break;
+ case OPT_SKIP_APPS:
cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
- }
- else if (!strcmp(argv[i], "--password")) {
- i++;
- if (!argv[i]) {
- print_usage(argc, argv);
- return -1;
- }
- if (backup_password)
- free(backup_password);
- backup_password = strdup(argv[i]);
- continue;
- }
- else if (!strcmp(argv[i], "cloud")) {
- cmd = CMD_CLOUD;
- i++;
- if (!argv[i]) {
- printf("No argument given for cloud command; requires either 'on' or 'off'.\n");
- print_usage(argc, argv);
- return -1;
- }
- if (!strcmp(argv[i], "on")) {
- cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
- } else if (!strcmp(argv[i], "off")) {
- cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
- } else {
- printf("Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
- }
- continue;
- }
- else if (!strcmp(argv[i], "--full")) {
+ 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;
- }
- else if (!strcmp(argv[i], "list")) {
- cmd = CMD_LIST;
- 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], "unback")) {
- cmd = CMD_UNBACK;
+ 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[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;
+ }
+ 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 if (!strcmp(argv[i], "encryption")) {
- cmd = CMD_CHANGEPW;
- i++;
- if (!argv[i]) {
- printf("No argument given for encryption command; requires either 'on' or 'off'.\n");
- print_usage(argc, argv);
- return -1;
- }
- if (!strcmp(argv[i], "on")) {
- cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
- } else if (!strcmp(argv[i], "off")) {
- cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
- } else {
- printf("Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
- }
- // check if a password was given on the command line
- if (newpw) {
- free(newpw);
- newpw = NULL;
- }
- if (backup_password) {
- 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]);
- }
+ // 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]);
}
- continue;
}
- else if (!strcmp(argv[i], "changepw")) {
- cmd = CMD_CHANGEPW;
- cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
- // check if passwords were given on command line
- if (newpw) {
- free(newpw);
- newpw = NULL;
- }
- if (backup_password) {
- free(backup_password);
- backup_password = NULL;
- }
+ }
+ 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]) {
- backup_password = strdup(argv[i]);
- i++;
- if (!argv[i]) {
- printf("Old and new passwords have to be passed as arguments for the changepw command\n");
- print_usage(argc, argv);
- return -1;
- }
- newpw = strdup(argv[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;
}
- continue;
- }
- else if (backup_directory == NULL) {
- backup_directory = argv[i];
- }
- else {
- print_usage(argc, argv);
- return -1;
+ 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 (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
backup_directory = (char*)".this_folder_is_not_present_on_purpose";
} else {
if (backup_directory == NULL) {
- printf("No target backup directory specified.\n");
- print_usage(argc, argv);
- return -1;
+ 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) {
- printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
+ fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
return -1;
}
}
- idevice_t device = NULL;
- if (udid) {
- ret = idevice_new(&device, udid);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found with udid %s, is it plugged in?\n", udid);
- return -1;
+ 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;
}
- else
- {
- ret = idevice_new(&device, NULL);
- if (ret != IDEVICE_E_SUCCESS) {
- printf("No device found, is it plugged in?\n");
- return -1;
- }
+
+ if (!udid) {
idevice_get_udid(device, &udid);
}
@@ -1680,6 +1970,20 @@ int main(int argc, char *argv[])
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");
@@ -1700,7 +2004,7 @@ int main(int argc, char *argv[])
free(info_path);
}
plist_t manifest_plist = NULL;
- plist_read_from_filename(&manifest_plist, manifest_path);
+ plist_read_from_file(manifest_path, &manifest_plist, NULL);
if (!manifest_plist) {
idevice_free(device);
free(info_path);
@@ -1721,6 +2025,12 @@ int main(int argc, char *argv[])
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);
}
@@ -1739,8 +2049,7 @@ int main(int argc, char *argv[])
}
}
- lockdownd_client_t lockdown = NULL;
- if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2"))) {
+ 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;
@@ -1757,32 +2066,43 @@ int main(int argc, char *argv[])
node_tmp = NULL;
}
+ /* get ProductVersion */
+ int device_version = idevice_get_device_version(device);
+
/* start notification_proxy */
- np_client_t np = NULL;
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_client_t afc = NULL;
if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
/* start AFC, we need this for the lock file */
- service->port = 0;
- service->ssl_enabled = 0;
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;
}
}
@@ -1792,7 +2112,6 @@ int main(int argc, char *argv[])
}
/* start mobilebackup service and retrieve port */
- mobilebackup2_client_t mobilebackup2 = NULL;
ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
lockdownd_client_free(lockdown);
lockdown = NULL;
@@ -1827,7 +2146,7 @@ int main(int argc, char *argv[])
/* verify existing Info.plist */
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");
@@ -1842,7 +2161,6 @@ int main(int argc, char *argv[])
}
}
- uint64_t lockfile = 0;
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);
@@ -1855,15 +2173,16 @@ int main(int argc, char *argv[])
if (aerr == AFC_E_SUCCESS) {
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");
@@ -1921,7 +2240,7 @@ checkpoint:
cmd = CMD_LEAVE;
}
remove_file(info_path);
- plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
+ plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
free(info_path);
plist_free(info_plist);
@@ -1948,6 +2267,16 @@ checkpoint:
} 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");
@@ -1982,9 +2311,8 @@ checkpoint:
PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 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"));
- if (cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS)
- plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(1));
- PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "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));
}
@@ -1998,9 +2326,8 @@ checkpoint:
if (write_restore_applications(info_plist, afc) < 0) {
cmd = CMD_LEAVE;
break;
- } else {
- PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
}
+ PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
}
/* Start restore */
@@ -2055,6 +2382,12 @@ checkpoint:
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) {
@@ -2071,6 +2404,12 @@ checkpoint:
} 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 {
@@ -2108,6 +2447,32 @@ checkpoint:
}
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) {
@@ -2128,6 +2493,7 @@ checkpoint:
int operation_ok = 0;
plist_t message = NULL;
+ mobilebackup2_error_t mberr;
char *dlmsg = NULL;
int file_count = 0;
int errcode = 0;
@@ -2138,10 +2504,13 @@ checkpoint:
do {
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);
+ 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;
}
@@ -2149,6 +2518,8 @@ checkpoint:
/* device wants to download files from the computer */
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 */
mb2_set_overall_progress_from_message(message, dlmsg);
@@ -2157,7 +2528,7 @@ checkpoint:
/* device wants to know how much disk space is available on the computer */
uint64_t freespace = 0;
int res = -1;
-#ifdef WIN32
+#ifdef _WIN32
if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
res = 0;
}
@@ -2166,12 +2537,17 @@ checkpoint:
memset(&fs, '\0', sizeof(fs));
res = statvfs(backup_directory, &fs);
if (res == 0) {
- freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize;
+ 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(mobilebackup2, message, backup_directory);
@@ -2183,7 +2559,8 @@ checkpoint:
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;
@@ -2234,7 +2611,8 @@ checkpoint:
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;
@@ -2359,11 +2737,12 @@ checkpoint:
/* print status */
if ((overall_progress > 0) && !progress_finished) {
- if (overall_progress >= 100.0f) {
+ if (overall_progress >= 100.0F) {
progress_finished = 1;
+ //progress_finish();
+ //} else {
+ // print_progress_real(overall_progress, 0);
}
- print_progress_real(overall_progress, 0);
- PRINT_VERBOSE(1, " Finished\n");
}
files_out:
@@ -2388,6 +2767,8 @@ files_out:
plist_free(message);
free(dlmsg);
+ progress_finish();
+
/* report operation status to user */
switch (cmd) {
case CMD_CLOUD:
@@ -2447,12 +2828,18 @@ files_out:
}
break;
case CMD_RESTORE:
- if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
- 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 (Error Code %d).\n", -result_code);
+ 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:
@@ -2477,7 +2864,7 @@ files_out:
do_post_notification(device, NP_SYNC_DID_FINISH);
}
} else {
- printf("ERROR: Could not start service %s.\n", MOBILEBACKUP2_SERVICE_NAME);
+ printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
lockdownd_client_free(lockdown);
lockdown = NULL;
}