summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/Makefile.am3
-rw-r--r--include/libiphone/mobilebackup.h55
-rw-r--r--src/Makefile.am3
-rw-r--r--src/device_link_service.c20
-rw-r--r--src/device_link_service.h1
-rw-r--r--src/mobilebackup.c131
-rw-r--r--src/mobilebackup.h31
-rw-r--r--src/mobilesync.c18
-rw-r--r--tools/Makefile.am8
-rw-r--r--tools/iphonebackup.c640
10 files changed, 889 insertions, 21 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index f871d28..aced258 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -5,4 +5,5 @@ nobase_include_HEADERS = libiphone/libiphone.h \
5 libiphone/notification_proxy.h \ 5 libiphone/notification_proxy.h \
6 libiphone/installation_proxy.h \ 6 libiphone/installation_proxy.h \
7 libiphone/sbservices.h \ 7 libiphone/sbservices.h \
8 libiphone/mobilesync.h 8 libiphone/mobilesync.h \
9 libiphone/mobilebackup.h
diff --git a/include/libiphone/mobilebackup.h b/include/libiphone/mobilebackup.h
new file mode 100644
index 0000000..8db6758
--- /dev/null
+++ b/include/libiphone/mobilebackup.h
@@ -0,0 +1,55 @@
1/**
2 * @file libiphone/mobilebackup.h
3 * @brief MobileBackup Implementation
4 * \internal
5 *
6 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifndef IMOBILEBACKUP_H
24#define IMOBILEBACKUP_H
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#include <libiphone/libiphone.h>
31
32/* Error Codes */
33#define MOBILEBACKUP_E_SUCCESS 0
34#define MOBILEBACKUP_E_INVALID_ARG -1
35#define MOBILEBACKUP_E_PLIST_ERROR -2
36#define MOBILEBACKUP_E_MUX_ERROR -3
37#define MOBILEBACKUP_E_BAD_VERSION -4
38
39#define MOBILEBACKUP_E_UNKNOWN_ERROR -256
40
41typedef int16_t mobilebackup_error_t;
42
43struct mobilebackup_client_int;
44typedef struct mobilebackup_client_int *mobilebackup_client_t;
45
46mobilebackup_error_t mobilebackup_client_new(iphone_device_t device, uint16_t port, mobilebackup_client_t * client);
47mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client);
48mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t *plist);
49mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist);
50
51#ifdef __cplusplus
52}
53#endif
54
55#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index f68f579..93cfbaf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,4 +15,5 @@ libiphone_la_SOURCES = iphone.c iphone.h \
15 notification_proxy.c notification_proxy.h\ 15 notification_proxy.c notification_proxy.h\
16 installation_proxy.c installation_proxy.h\ 16 installation_proxy.c installation_proxy.h\
17 sbservices.c sbservices.h\ 17 sbservices.c sbservices.h\
18 mobilesync.c mobilesync.h 18 mobilesync.c mobilesync.h\
19 mobilebackup.c mobilebackup.h
diff --git a/src/device_link_service.c b/src/device_link_service.c
index b7d9ee8..9998fd0 100644
--- a/src/device_link_service.c
+++ b/src/device_link_service.c
@@ -253,6 +253,26 @@ device_link_service_error_t device_link_service_disconnect(device_link_service_c
253 return err; 253 return err;
254} 254}
255 255
256device_link_service_error_t device_link_service_process_message(device_link_service_client_t client, plist_t message)
257{
258 if (!client || !message)
259 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
260
261 if (plist_get_node_type(message) != PLIST_DICT)
262 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
263
264 plist_t array = plist_new_array();
265 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
266 plist_array_append_item(array, message);
267
268 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS;
269 if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
270 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
271 }
272 plist_free(array);
273 return err;
274}
275
256/** 276/**
257 * Generic device link service send function. 277 * Generic device link service send function.
258 * 278 *
diff --git a/src/device_link_service.h b/src/device_link_service.h
index e14d897..8345d57 100644
--- a/src/device_link_service.h
+++ b/src/device_link_service.h
@@ -44,6 +44,7 @@ typedef int16_t device_link_service_error_t;
44device_link_service_error_t device_link_service_client_new(iphone_device_t device, uint16_t port, device_link_service_client_t *client); 44device_link_service_error_t device_link_service_client_new(iphone_device_t device, uint16_t port, device_link_service_client_t *client);
45device_link_service_error_t device_link_service_client_free(device_link_service_client_t client); 45device_link_service_error_t device_link_service_client_free(device_link_service_client_t client);
46device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor); 46device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor);
47device_link_service_error_t device_link_service_process_message(device_link_service_client_t client, plist_t message);
47device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client); 48device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client);
48device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist); 49device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist);
49device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist); 50device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist);
diff --git a/src/mobilebackup.c b/src/mobilebackup.c
new file mode 100644
index 0000000..5b81c7f
--- /dev/null
+++ b/src/mobilebackup.c
@@ -0,0 +1,131 @@
1/*
2 * mobilebackup.c
3 * Contains functions for the built-in MobileBackup client.
4 *
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <plist/plist.h>
23#include <string.h>
24#include <stdlib.h>
25#include <arpa/inet.h>
26
27#include "mobilebackup.h"
28#include "device_link_service.h"
29#include "debug.h"
30
31#define MBACKUP_VERSION_INT1 100
32#define MBACKUP_VERSION_INT2 0
33
34/**
35 * Convert an device_link_service_error_t value to an mobilebackup_error_t value.
36 * Used internally to get correct error codes when using device_link_service stuff.
37 *
38 * @param err An device_link_service_error_t error code
39 *
40 * @return A matching mobilebackup_error_t error code,
41 * MOBILEBACKUP_E_UNKNOWN_ERROR otherwise.
42 */
43static mobilebackup_error_t mobilebackup_error(device_link_service_error_t err)
44{
45 switch (err) {
46 case DEVICE_LINK_SERVICE_E_SUCCESS:
47 return MOBILEBACKUP_E_SUCCESS;
48 case DEVICE_LINK_SERVICE_E_INVALID_ARG:
49 return MOBILEBACKUP_E_INVALID_ARG;
50 case DEVICE_LINK_SERVICE_E_PLIST_ERROR:
51 return MOBILEBACKUP_E_PLIST_ERROR;
52 case DEVICE_LINK_SERVICE_E_MUX_ERROR:
53 return MOBILEBACKUP_E_MUX_ERROR;
54 case DEVICE_LINK_SERVICE_E_BAD_VERSION:
55 return MOBILEBACKUP_E_BAD_VERSION;
56 default:
57 break;
58 }
59 return MOBILEBACKUP_E_UNKNOWN_ERROR;
60}
61
62mobilebackup_error_t mobilebackup_client_new(iphone_device_t device, uint16_t port,
63 mobilebackup_client_t * client)
64{
65 if (!device || port == 0 || !client || *client)
66 return MOBILEBACKUP_E_INVALID_ARG;
67
68 device_link_service_client_t dlclient = NULL;
69 mobilebackup_error_t ret = mobilebackup_error(device_link_service_client_new(device, port, &dlclient));
70 if (ret != MOBILEBACKUP_E_SUCCESS) {
71 return ret;
72 }
73
74 mobilebackup_client_t client_loc = (mobilebackup_client_t) malloc(sizeof(struct mobilebackup_client_int));
75 client_loc->parent = dlclient;
76
77 /* perform handshake */
78 ret = mobilebackup_error(device_link_service_version_exchange(dlclient, MBACKUP_VERSION_INT1, MBACKUP_VERSION_INT2));
79 if (ret != MOBILEBACKUP_E_SUCCESS) {
80 debug_info("version exchange failed, error %d", ret);
81 mobilebackup_client_free(client_loc);
82 return ret;
83 }
84
85 *client = client_loc;
86
87 return ret;
88}
89
90mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client)
91{
92 if (!client)
93 return MOBILEBACKUP_E_INVALID_ARG;
94 device_link_service_disconnect(client->parent);
95 mobilebackup_error_t err = mobilebackup_error(device_link_service_client_free(client->parent));
96 free(client);
97 return err;
98}
99
100/** Polls the iPhone for MobileBackup data.
101 *
102 * @param client The MobileBackup client
103 * @param plist A pointer to the location where the plist should be stored
104 *
105 * @return an error code
106 */
107mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist)
108{
109 if (!client)
110 return MOBILEBACKUP_E_INVALID_ARG;
111 mobilebackup_error_t ret = mobilebackup_error(device_link_service_receive(client->parent, plist));
112 return ret;
113}
114
115/** Sends MobileBackup data to the iPhone
116 *
117 * @note This function is low-level and should only be used if you need to send
118 * a new type of message.
119 *
120 * @param client The MobileBackup client
121 * @param plist The location of the plist to send
122 *
123 * @return an error code
124 */
125mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist)
126{
127 if (!client || !plist)
128 return MOBILEBACKUP_E_INVALID_ARG;
129 return mobilebackup_error(device_link_service_send(client->parent, plist));
130}
131
diff --git a/src/mobilebackup.h b/src/mobilebackup.h
new file mode 100644
index 0000000..04ebc45
--- /dev/null
+++ b/src/mobilebackup.h
@@ -0,0 +1,31 @@
1 /*
2 * mobilebackup.h
3 * Definitions for the mobilebackup service
4 *
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21#ifndef MOBILEBACKUP_H
22#define MOBILEBACKUP_H
23
24#include "libiphone/mobilebackup.h"
25#include "device_link_service.h"
26
27struct mobilebackup_client_int {
28 device_link_service_client_t parent;
29};
30
31#endif
diff --git a/src/mobilesync.c b/src/mobilesync.c
index 3492673..15614b5 100644
--- a/src/mobilesync.c
+++ b/src/mobilesync.c
@@ -109,16 +109,6 @@ mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plis
109 if (!client) 109 if (!client)
110 return MOBILESYNC_E_INVALID_ARG; 110 return MOBILESYNC_E_INVALID_ARG;
111 mobilesync_error_t ret = mobilesync_error(device_link_service_receive(client->parent, plist)); 111 mobilesync_error_t ret = mobilesync_error(device_link_service_receive(client->parent, plist));
112#ifndef STRIP_DEBUG_CODE
113 if (ret != MOBILESYNC_E_SUCCESS) {
114 return ret;
115 }
116 char *XMLContent = NULL;
117 uint32_t length = 0;
118 plist_to_xml(*plist, &XMLContent, &length);
119 debug_info("plist size: %i\nbuffer :\n%s", length, XMLContent);
120 free(XMLContent);
121#endif
122 return ret; 112 return ret;
123} 113}
124 114
@@ -136,13 +126,5 @@ mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist)
136{ 126{
137 if (!client || !plist) 127 if (!client || !plist)
138 return MOBILESYNC_E_INVALID_ARG; 128 return MOBILESYNC_E_INVALID_ARG;
139
140#ifndef STRIP_DEBUG_CODE
141 char *XMLContent = NULL;
142 uint32_t length = 0;
143 plist_to_xml(plist, &XMLContent, &length);
144 debug_info("plist size: %i\nbuffer :\n%s", length, XMLContent);
145 free(XMLContent);
146#endif
147 return mobilesync_error(device_link_service_send(client->parent, plist)); 129 return mobilesync_error(device_link_service_send(client->parent, plist));
148} 130}
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 7f1be1c..d19ef0c 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -3,7 +3,7 @@ INCLUDES = -I$(top_srcdir)/include
3AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS) 3AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS)
4AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) 4AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS)
5 5
6bin_PROGRAMS = iphone_id iphoneinfo iphonesyslog 6bin_PROGRAMS = iphone_id iphoneinfo iphonesyslog iphonebackup
7 7
8iphoneinfo_SOURCES = iphoneinfo.c 8iphoneinfo_SOURCES = iphoneinfo.c
9iphoneinfo_CFLAGS = $(AM_CFLAGS) 9iphoneinfo_CFLAGS = $(AM_CFLAGS)
@@ -19,3 +19,9 @@ iphone_id_SOURCES = iphone_id.c
19iphone_id_CFLAGS = $(AM_CFLAGS) 19iphone_id_CFLAGS = $(AM_CFLAGS)
20iphone_id_LDFLAGS = $(AM_LDFLAGS) 20iphone_id_LDFLAGS = $(AM_LDFLAGS)
21iphone_id_LDADD = ../src/libiphone.la 21iphone_id_LDADD = ../src/libiphone.la
22
23iphonebackup_SOURCES = iphonebackup.c
24iphonebackup_CFLAGS = $(AM_CFLAGS)
25iphonebackup_LDFLAGS = $(AM_LDFLAGS)
26iphonebackup_LDADD = ../src/libiphone.la
27
diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c
new file mode 100644
index 0000000..28a8949
--- /dev/null
+++ b/tools/iphonebackup.c
@@ -0,0 +1,640 @@
1/*
2 * iphonebackup.c
3 * Command line interface to use the device's backup and restore service
4 *
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <signal.h>
27#include <glib.h>
28
29#include <libiphone/libiphone.h>
30#include <libiphone/lockdown.h>
31#include <libiphone/mobilebackup.h>
32
33#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
34
35static mobilebackup_client_t mobilebackup = NULL;
36static lockdownd_client_t client = NULL;
37static iphone_device_t phone = NULL;
38
39static int quit_flag = 0;
40
41enum cmd_mode {
42 CMD_BACKUP,
43 CMD_RESTORE,
44 CMD_LEAVE
45};
46
47static plist_t mobilebackup_factory_info_plist()
48{
49 /* gather data from lockdown */
50 GTimeVal tv = {0, 0};
51 plist_t value_node = NULL;
52 plist_t root_node = NULL;
53 char *uuid = NULL;
54 char *uuid_uppercase = NULL;
55
56 plist_t ret = plist_new_dict();
57
58 /* get basic device information in one go */
59 lockdownd_get_value(client, NULL, NULL, &root_node);
60
61 /* set fields we understand */
62 value_node = plist_dict_get_item(root_node, "BuildVersion");
63 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
64
65 value_node = plist_dict_get_item(root_node, "DeviceName");
66 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node));
67 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node));
68
69 /* FIXME: How is the GUID generated? */
70 plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
71
72 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
73 if (value_node)
74 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
75
76 g_get_current_time(&tv);
77 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec));
78
79 value_node = plist_dict_get_item(root_node, "ProductType");
80 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
81
82 value_node = plist_dict_get_item(root_node, "ProductVersion");
83 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node));
84
85 value_node = plist_dict_get_item(root_node, "SerialNumber");
86 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node));
87
88 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
89 iphone_device_get_uuid(phone, &uuid);
90 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
91
92 /* uppercase */
93 uuid_uppercase = g_ascii_strup(uuid, -1);
94 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase));
95 free(uuid_uppercase);
96 free(uuid);
97
98 plist_t files = plist_new_dict();
99 /* FIXME: Embed files as <data> nodes */
100 plist_dict_insert_item(ret, "iTunes Files", files);
101 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2"));
102
103 return ret;
104}
105
106enum plist_format_t {
107 PLIST_FORMAT_XML,
108 PLIST_FORMAT_BINARY
109};
110
111static void buffer_to_filename(char *filename, char *buffer, uint32_t length)
112{
113 FILE *f;
114
115 f = fopen(filename, "ab");
116 fwrite(buffer, sizeof(char), length, f);
117 fclose(f);
118}
119
120static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format)
121{
122 char *buffer = NULL;
123 uint32_t length;
124
125 if (!plist || !filename)
126 return 0;
127
128 if (format == PLIST_FORMAT_XML)
129 plist_to_xml(plist, &buffer, &length);
130 else if (format == PLIST_FORMAT_BINARY)
131 plist_to_bin(plist, &buffer, &length);
132 else
133 return 0;
134
135 buffer_to_filename(filename, buffer, length);
136
137 free(buffer);
138
139 return 1;
140}
141
142static int plist_strcmp(plist_t node, const char *str)
143{
144 char *buffer = NULL;
145 int ret = 0;
146
147 if (plist_get_node_type(node) != PLIST_STRING)
148 return ret;
149
150 plist_get_string_val(node, &buffer);
151 ret = strcmp(buffer, str);
152 free(buffer);
153
154 return ret;
155}
156
157static plist_t device_link_message_factory_process_message_new(plist_t content)
158{
159 plist_t ret = plist_new_array();
160 plist_array_append_item(ret, plist_new_string("DLMessageProcessMessage"));
161 plist_array_append_item(ret, content);
162 return ret;
163}
164
165static void mobilebackup_cancel_backup_with_error(const char *reason)
166{
167 plist_t node = plist_new_dict();
168 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageError"));
169 plist_dict_insert_item(node, "BackupErrorReasonKey", plist_new_string(reason));
170
171 plist_t message = device_link_message_factory_process_message_new(node);
172
173 mobilebackup_send(mobilebackup, message);
174
175 plist_free(message);
176 message = NULL;
177}
178
179static void mobilebackup_write_status(char *path, int status)
180{
181 struct stat st;
182 plist_t status_plist = plist_new_dict();
183 plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status));
184 char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL);
185 if (stat(file_path, &st) == 0)
186 remove(file_path);
187 plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML);
188 g_free(file_path);
189 plist_free(status_plist);
190}
191
192static void debug_plist(plist_t a)
193{
194 char *buffer = NULL;
195 uint32_t length = 0;
196
197 if (a == NULL)
198 return;
199
200 plist_to_xml(a, &buffer, &length);
201
202 printf("Printing %i bytes plist:\n%s\n", length, buffer);
203 free(buffer);
204}
205
206/**
207 * signal handler function for cleaning up properly
208 */
209static void clean_exit(int sig)
210{
211 fprintf(stderr, "Exiting...\n");
212 quit_flag++;
213}
214
215static void print_usage(int argc, char **argv)
216{
217 char *name = NULL;
218 name = strrchr(argv[0], '/');
219 printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0]));
220 printf("Create or restore backup from the current or specified directory.\n\n");
221 printf("commands:\n");
222 printf(" backup\tSaves a device backup into DIRECTORY\n");
223 printf(" restore\tRestores a device backup from DIRECTORY.\n\n");
224 printf("options:\n");
225 printf(" -d, --debug\t\tenable communication debugging\n");
226 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
227 printf(" -h, --help\t\tprints usage information\n");
228 printf("\n");
229}
230
231int main(int argc, char *argv[])
232{
233 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
234 int i;
235 char uuid[41];
236 uint16_t port = 0;
237 uuid[0] = 0;
238 int cmd = -1;
239 char *backup_directory = NULL;
240 struct stat st;
241 plist_t node = NULL;
242 plist_t node_tmp = NULL;
243 char *buffer = NULL;
244 uint64_t length = 0;
245 uint64_t backup_total_size = 0;
246 uint64_t c = 0;
247
248 /* we need to exit cleanly on running backups and restores or we cause havok */
249 signal(SIGINT, clean_exit);
250 signal(SIGQUIT, clean_exit);
251 signal(SIGTERM, clean_exit);
252 signal(SIGPIPE, SIG_IGN);
253
254 /* parse cmdline args */
255 for (i = 1; i < argc; i++) {
256 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
257 iphone_set_debug_level(1);
258 continue;
259 }
260 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) {
261 i++;
262 if (!argv[i] || (strlen(argv[i]) != 40)) {
263 print_usage(argc, argv);
264 return 0;
265 }
266 strcpy(uuid, argv[i]);
267 continue;
268 }
269 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
270 print_usage(argc, argv);
271 return 0;
272 }
273 else if (!strcmp(argv[i], "backup")) {
274 cmd = CMD_BACKUP;
275 }
276 else if (!strcmp(argv[i], "restore")) {
277 cmd = CMD_RESTORE;
278 }
279 else if (backup_directory == NULL) {
280 backup_directory = argv[i];
281 }
282 else {
283 print_usage(argc, argv);
284 return 0;
285 }
286 }
287
288 /* verify options */
289 if (cmd == -1) {
290 printf("No command specified.\n");
291 print_usage(argc, argv);
292 return -1;
293 }
294
295 if (backup_directory == NULL) {
296 printf("No target backup directory specified.\n");
297 print_usage(argc, argv);
298 return -1;
299 }
300
301 /* verify if passed backup directory exists */
302 if (stat(backup_directory, &st) != 0) {
303 printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
304 return -1;
305 }
306
307 /* restore directory must contain an Info.plist */
308 char *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Info.plist", NULL);
309 if (cmd == CMD_RESTORE) {
310 if (stat(info_path, &st) != 0) {
311 g_free(info_path);
312 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
313 return -1;
314 }
315 }
316
317 printf("Backup directory is \"%s\"\n", backup_directory);
318
319 if (uuid[0] != 0) {
320 ret = iphone_device_new(&phone, uuid);
321 if (ret != IPHONE_E_SUCCESS) {
322 printf("No device found with uuid %s, is it plugged in?\n", uuid);
323 return -1;
324 }
325 }
326 else
327 {
328 ret = iphone_device_new(&phone, NULL);
329 if (ret != IPHONE_E_SUCCESS) {
330 printf("No device found, is it plugged in?\n");
331 return -1;
332 }
333 }
334
335 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "iphonebackup")) {
336 iphone_device_free(phone);
337 return -1;
338 }
339
340 /* start syslog_relay service and retrieve port */
341 ret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &port);
342 if ((ret == LOCKDOWN_E_SUCCESS) && port) {
343 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port);
344 mobilebackup_client_new(phone, port, &mobilebackup);
345
346 if (quit_flag > 0) {
347 printf("Aborting backup. Cancelled by user.\n");
348 cmd = CMD_LEAVE;
349 }
350
351 switch(cmd) {
352 case CMD_BACKUP:
353 printf("Starting backup...\n");
354 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
355 /* TODO: verify battery on AC enough battery remaining */
356
357 /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
358 printf("Creating \"%s/Info.plist\".\n", backup_directory);
359 plist_t info_plist = mobilebackup_factory_info_plist();
360 if (stat(info_path, &st) == 0)
361 remove(info_path);
362 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
363 g_free(info_path);
364
365 if (client) {
366 lockdownd_client_free(client);
367 client = NULL;
368 }
369
370 /* create Manifest.plist (backup manifest (backup state)) */
371 printf("Creating \"%s/Manifest.plist\".\n", backup_directory);
372 char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL);
373 /* FIXME: We should read the last Manifest.plist and send it to the device */
374 plist_t manifest_plist = NULL;
375 if (stat(manifest_path, &st) == 0)
376 remove(manifest_path);
377
378 /* create Status.plist with failed status for now */
379 mobilebackup_write_status(backup_directory, 0);
380
381 /* request backup from device with manifest from last backup */
382 printf("Sending manifest and requesting backup.\n");
383
384 node = plist_new_dict();
385 if (manifest_plist)
386 plist_dict_insert_item(node, "BackupManifestKey", manifest_plist);
387 plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/"));
388 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest"));
389 plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6"));
390
391 plist_t message = plist_new_array();
392 plist_array_append_item(message, plist_new_string("DLMessageProcessMessage"));
393 plist_array_append_item(message, node);
394
395 mobilebackup_send(mobilebackup, message);
396 plist_free(message);
397 message = NULL;
398
399 /* get response */
400 int backup_ok = 0;
401 mobilebackup_receive(mobilebackup, &message);
402 node = plist_array_get_item(message, 0);
403 if (!plist_strcmp(node, "DLMessageProcessMessage")) {
404 node = plist_array_get_item(message, 1);
405 node = plist_dict_get_item(node, "BackupMessageTypeKey");
406 if (node && !plist_strcmp(node, "BackupMessageBackupReplyOK")) {
407 printf("Device accepts manifest and will send backup data now...\n");
408 backup_ok = 1;
409 printf("Acknowledging...\n");
410 printf("Please wait. Device prepares backup data...\n");
411 /* send it back for ACK */
412 mobilebackup_send(mobilebackup, message);
413 }
414 } else {
415 printf("Unhandled message received!\n");
416 debug_plist(message);
417 }
418 plist_free(message);
419 message = NULL;
420
421 if (!backup_ok) {
422 printf("ERROR: Device rejected to start the backup process.\n");
423 break;
424 }
425
426 /* receive and save DLSendFile files and metadata, ACK each */
427 int file_index = 0;
428 uint64_t backup_real_size = 0;
429 char *file_path = NULL;
430 char *file_ext = NULL;
431 char *filename_mdinfo = NULL;
432 char *filename_mddata = NULL;
433 char *filename_source = NULL;
434 char *format_size = NULL;
435 gboolean is_manifest = FALSE;
436 do {
437 mobilebackup_receive(mobilebackup, &message);
438 node = plist_array_get_item(message, 0);
439 if (plist_strcmp(node, "DLSendFile"))
440 break;
441
442 node_tmp = plist_array_get_item(message, 2);
443
444 /* first message contains total backup size */
445 if (file_index == 0) {
446 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey");
447 plist_get_uint_val(node, &backup_total_size);
448 format_size = g_format_size_for_display(backup_total_size);
449 printf("Backup will need %s on disk.\n", format_size);
450 g_free(format_size);
451 }
452
453 /* print out "received" if DLFileStatusKey is 2 (last file piece) */
454 node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
455 plist_get_uint_val(node, &c);
456
457 /* get source filename */
458 node = plist_dict_get_item(node_tmp, "DLFileSource");
459 plist_get_string_val(node, &filename_source);
460
461 if (!strcmp(filename_source, "/tmp/Manifest.plist"))
462 is_manifest = TRUE;
463 else
464 is_manifest = FALSE;
465
466 if (c == 2) {
467 /* increased received size for each completed file */
468 if (!is_manifest) {
469 node = plist_dict_get_item(node_tmp, "DLFileAttributesKey");
470 node = plist_dict_get_item(node, "FileSize");
471 plist_get_uint_val(node, &length);
472
473 backup_real_size += length;
474 file_index++;
475
476 format_size = g_format_size_for_display(backup_real_size);
477 printf("(%s", format_size);
478 g_free(format_size);
479 format_size = g_format_size_for_display(backup_total_size);
480 printf("/%s): ", format_size);
481 g_free(format_size);
482 printf("Received file %s... ", filename_source);
483 }
484 }
485
486 /* save <hash>.mdinfo */
487 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
488 if (node) {
489 node = plist_dict_get_item(node_tmp, "DLFileDest");
490 plist_get_string_val(node, &file_path);
491 file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL);
492 filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL);
493 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
494 plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY);
495 g_free(file_ext);
496 g_free(filename_mdinfo);
497 }
498
499 /* save <hash>.mddata */
500 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
501 if (node_tmp && file_path) {
502 node = plist_dict_get_item(node_tmp, "DLFileDest");
503 plist_get_string_val(node, &file_path);
504 file_ext = (char *)g_strconcat(file_path, ".mddata", NULL);
505 filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL);
506 node_tmp = plist_array_get_item(message, 1);
507 plist_get_data_val(node_tmp, &buffer, &length);
508 buffer_to_filename(filename_mddata, buffer, length);
509 free(buffer);
510 buffer = NULL;
511 g_free(filename_mddata);
512 }
513
514 if ((c == 2) && (!is_manifest)) {
515 printf("DONE\n");
516 }
517
518 if (filename_source)
519 free(filename_source);
520
521 if (file_ext)
522 free(file_ext);
523
524 plist_free(message);
525 message = NULL;
526
527 if (quit_flag > 0) {
528 /* need to cancel the backup here */
529 mobilebackup_cancel_backup_with_error("Cancelling DLSendFile");
530
531 plist_free(message);
532 message = NULL;
533 break;
534 }
535
536 /* acknowlegdge that we received the file */
537 node = plist_new_dict();
538 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived"));
539
540 message = plist_new_array();
541 plist_array_append_item(message, plist_new_string("DLMessageProcessMessage"));
542 plist_array_append_item(message, node);
543 mobilebackup_send(mobilebackup, message);
544
545 plist_free(message);
546 message = NULL;
547 } while (!plist_strcmp(node, "DLSendFile"));
548
549 printf("Received %d files from device.\n", file_index);
550
551 if (!plist_strcmp(node, "DLMessageProcessMessage")) {
552 node_tmp = plist_array_get_item(message, 1);
553 node = plist_dict_get_item(node_tmp, "BackupMessageTypeKey");
554 /* wait until received final backup finished message */
555 if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) {
556 /* backup finished */
557
558 /* process BackupFilesToDeleteKey */
559 node = plist_dict_get_item(node_tmp, "BackupFilesToDeleteKey");
560 if (node) {
561 length = plist_array_get_size(node);
562 i = 0;
563 while ((node_tmp = plist_array_get_item(node, i++)) != NULL) {
564 plist_get_string_val(node_tmp, &file_path);
565
566 file_ext = (char *)g_strconcat(file_path, ".mddata", NULL);
567 filename_mddata = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL);
568 g_free(file_ext);
569 printf("Removing \"%s\"... ", filename_mddata);
570 if (!remove( filename_mddata )) {
571 printf("DONE\n");
572 } else
573 printf("FAILED\n");
574
575 file_ext = (char *)g_strconcat(file_path, ".mdinfo", NULL);
576 filename_mdinfo = g_build_path(G_DIR_SEPARATOR_S, backup_directory, file_ext, NULL);
577 g_free(file_ext);
578 printf("Removing \"%s\"... ", filename_mdinfo);
579 if (!remove( filename_mdinfo )) {
580 printf("DONE\n");
581 } else
582 printf("FAILED\n");
583 }
584 }
585
586 /* save new Manifest.plist */
587 node_tmp = plist_array_get_item(message, 1);
588 manifest_plist = plist_dict_get_item(node_tmp, "BackupManifestKey");
589 if (manifest_plist) {
590 if (stat(manifest_path, &st) != 0)
591 remove(manifest_path);
592 plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML);
593 }
594
595 /* create: Status.plist (Info on how the backup process turned out) */
596 printf("Backup Successful.\n");
597 mobilebackup_write_status(backup_directory, 1);
598 }
599 }
600
601 if (manifest_path)
602 g_free(manifest_path);
603
604 if (node)
605 plist_free(node);
606
607 break;
608 case CMD_RESTORE:
609 printf("Restoring backup...\n");
610 /* verify battery on AC enough battery remaining */
611 /* request restore from device (BackupMessageRestoreMigrate) */
612 /* read mddata files and send to devices using DLSendFile */
613 /* signal restore finished message to device */
614 /* close down lockdown connection as it is no longer needed */
615 lockdownd_client_free(client);
616 client = NULL;
617 break;
618 case CMD_LEAVE:
619 default:
620 break;
621 }
622 } else {
623 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME);
624 lockdownd_client_free(client);
625 client = NULL;
626 }
627
628 if (client) {
629 lockdownd_client_free(client);
630 client = NULL;
631 }
632
633 if (mobilebackup)
634 mobilebackup_client_free(mobilebackup);
635
636 iphone_device_free(phone);
637
638 return 0;
639}
640