summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2010-09-05 20:15:43 +0200
committerGravatar Martin Szulecki2011-04-11 17:05:38 +0200
commitca7dba1c618ed499d0693427e89701cda1731ca9 (patch)
treeeb5f1d6de2779f43940c8cbd578b1b9de7c1745a /src
parentc5fe346717a449a6bfcdbd7477724d95cdeb85d5 (diff)
downloadlibimobiledevice-ca7dba1c618ed499d0693427e89701cda1731ca9.tar.gz
libimobiledevice-ca7dba1c618ed499d0693427e89701cda1731ca9.tar.bz2
Add initial mobilebackup2 support and idevicebackup4 tool
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/mobilebackup2.c396
-rw-r--r--src/mobilebackup2.h31
3 files changed, 428 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 70dc895..f42ac08 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,4 +21,5 @@ libimobiledevice_la_SOURCES = idevice.c idevice.h \
21 mobilesync.c mobilesync.h\ 21 mobilesync.c mobilesync.h\
22 mobilebackup.c mobilebackup.h\ 22 mobilebackup.c mobilebackup.h\
23 house_arrest.c house_arrest.h\ 23 house_arrest.c house_arrest.h\
24 mobilebackup2.c mobilebackup2.h\
24 restore.c restore.h 25 restore.c restore.h
diff --git a/src/mobilebackup2.c b/src/mobilebackup2.c
new file mode 100644
index 0000000..7b6ea33
--- /dev/null
+++ b/src/mobilebackup2.c
@@ -0,0 +1,396 @@
1/*
2 * mobilebackup2.c
3 * Contains functions for the built-in MobileBackup2 client (iOS4+ only)
4 *
5 * Copyright (c) 2010 Nikias Bassen 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
26#include "mobilebackup2.h"
27#include "device_link_service.h"
28#include "debug.h"
29
30#define MBACKUP2_VERSION_INT1 100
31#define MBACKUP2_VERSION_INT2 0
32
33#define IS_FLAG_SET(x, y) ((x & y) == y)
34
35/**
36 * Convert an device_link_service_error_t value to an mobilebackup2_error_t value.
37 * Used internally to get correct error codes from the underlying
38 * device_link_service.
39 *
40 * @param err An device_link_service_error_t error code
41 *
42 * @return A matching mobilebackup2_error_t error code,
43 * MOBILEBACKUP2_E_UNKNOWN_ERROR otherwise.
44 */
45static mobilebackup2_error_t mobilebackup2_error(device_link_service_error_t err)
46{
47 switch (err) {
48 case DEVICE_LINK_SERVICE_E_SUCCESS:
49 return MOBILEBACKUP2_E_SUCCESS;
50 case DEVICE_LINK_SERVICE_E_INVALID_ARG:
51 return MOBILEBACKUP2_E_INVALID_ARG;
52 case DEVICE_LINK_SERVICE_E_PLIST_ERROR:
53 return MOBILEBACKUP2_E_PLIST_ERROR;
54 case DEVICE_LINK_SERVICE_E_MUX_ERROR:
55 return MOBILEBACKUP2_E_MUX_ERROR;
56 case DEVICE_LINK_SERVICE_E_BAD_VERSION:
57 return MOBILEBACKUP2_E_BAD_VERSION;
58 default:
59 break;
60 }
61 return MOBILEBACKUP2_E_UNKNOWN_ERROR;
62}
63
64/**
65 * Connects to the mobilebackup2 service on the specified device.
66 *
67 * @param device The device to connect to.
68 * @param port Destination port (usually given by lockdownd_start_service).
69 * @param client Pointer that will be set to a newly allocated
70 * mobilebackup2_client_t upon successful return.
71 *
72 * @return MOBILEBACKUP2_E_SUCCESS on success, MOBILEBACKUP2_E_INVALID ARG
73 * if one or more parameter is invalid, or MOBILEBACKUP2_E_BAD_VERSION
74 * if the mobilebackup2 version on the device is newer.
75 */
76mobilebackup2_error_t mobilebackup2_client_new(idevice_t device, uint16_t port,
77 mobilebackup2_client_t * client)
78{
79 if (!device || port == 0 || !client || *client)
80 return MOBILEBACKUP2_E_INVALID_ARG;
81
82 device_link_service_client_t dlclient = NULL;
83 mobilebackup2_error_t ret = mobilebackup2_error(device_link_service_client_new(device, port, &dlclient));
84 if (ret != MOBILEBACKUP2_E_SUCCESS) {
85 return ret;
86 }
87
88 mobilebackup2_client_t client_loc = (mobilebackup2_client_t) malloc(sizeof(struct mobilebackup2_client_private));
89 client_loc->parent = dlclient;
90
91 /* perform handshake */
92 ret = mobilebackup2_error(device_link_service_version_exchange(dlclient, MBACKUP2_VERSION_INT1, MBACKUP2_VERSION_INT2));
93 if (ret != MOBILEBACKUP2_E_SUCCESS) {
94 debug_info("version exchange failed, error %d", ret);
95 mobilebackup2_client_free(client_loc);
96 return ret;
97 }
98
99 *client = client_loc;
100
101 return ret;
102}
103
104/**
105 * Disconnects a mobilebackup2 client from the device and frees up the
106 * mobilebackup2 client data.
107 *
108 * @param client The mobilebackup2 client to disconnect and free.
109 *
110 * @return MOBILEBACKUP2_E_SUCCESS on success, or MOBILEBACKUP2_E_INVALID_ARG
111 * if client is NULL.
112 */
113mobilebackup2_error_t mobilebackup2_client_free(mobilebackup2_client_t client)
114{
115 if (!client)
116 return MOBILEBACKUP2_E_INVALID_ARG;
117 mobilebackup2_error_t err = MOBILEBACKUP2_E_SUCCESS;
118 if (client->parent) {
119 device_link_service_disconnect(client->parent);
120 err = mobilebackup2_error(device_link_service_client_free(client->parent));
121 }
122 free(client);
123 return err;
124}
125
126/**
127 * Sends a backup message plist.
128 *
129 * @param client The connected MobileBackup client to use.
130 * @param message The message to send. This will be inserted into the request
131 * plist as value for MessageName. If this parameter is NULL,
132 * the plist passed in the options parameter will be sent directly.
133 * @param options Additional options as PLIST_DICT to add to the request.
134 * The MessageName key with the value passed in the message parameter
135 * will be inserted into this plist before sending it. This parameter
136 * can be NULL if message is not NULL.
137 */
138static mobilebackup2_error_t internal_mobilebackup2_send_message(mobilebackup2_client_t client, const char *message, plist_t options)
139{
140 if (!client || !client->parent || (!message && !options))
141 return MOBILEBACKUP2_E_INVALID_ARG;
142
143 if (options && (plist_get_node_type(options) != PLIST_DICT)) {
144 return MOBILEBACKUP2_E_INVALID_ARG;
145 }
146
147 mobilebackup2_error_t err;
148
149 if (message) {
150 plist_t dict = NULL;
151 if (options) {
152 dict = plist_copy(options);
153 } else {
154 dict = plist_new_dict();
155 }
156 plist_dict_insert_item(dict, "MessageName", plist_new_string(message));
157
158 /* send it as DLMessageProcessMessage */
159 err = mobilebackup2_error(device_link_service_send_process_message(client->parent, dict));
160 plist_free(dict);
161 } else {
162 err = mobilebackup2_error(device_link_service_send_process_message(client->parent, options));
163 }
164 if (err != MOBILEBACKUP2_E_SUCCESS) {
165 debug_info("ERROR: Could not send message '%s' (%d)!", message, err);
166 }
167 return err;
168}
169
170/**
171 * Receives a plist from the device and checks if the value for the
172 * MessageName key matches the value passed in the message parameter.
173 *
174 * @param client The connected MobileBackup client to use.
175 * @param message The expected message to check.
176 * @param result Pointer to a plist_t that will be set to the received plist
177 * for further processing. The caller has to free it using plist_free().
178 * Note that it will be set to NULL if the operation itself fails due to
179 * a communication or plist error.
180 * If this parameter is NULL, it will be ignored.
181 *
182 * @return MOBILEBACKUP2_E_SUCCESS on success, MOBILEBACKUP2_E_INVALID_ARG if
183 * client or message is invalid, MOBILEBACKUP2_E_REPLY_NOT_OK if the
184 * expected message could not be received, MOBILEBACKUP2_E_PLIST_ERROR if
185 * the received message is not a valid backup message plist (i.e. the
186 * MessageName key is not present), or MOBILEBACKUP2_E_MUX_ERROR
187 * if a communication error occurs.
188 */
189static mobilebackup2_error_t internal_mobilebackup2_receive_message(mobilebackup2_client_t client, const char *message, plist_t *result)
190{
191 if (!client || !client->parent || !message)
192 return MOBILEBACKUP2_E_INVALID_ARG;
193
194 if (result)
195 *result = NULL;
196 mobilebackup2_error_t err;
197
198 plist_t dict = NULL;
199
200 /* receive DLMessageProcessMessage */
201 err = mobilebackup2_error(device_link_service_receive_process_message(client->parent, &dict));
202 if (err != MOBILEBACKUP2_E_SUCCESS) {
203 goto leave;
204 }
205
206 plist_t node = plist_dict_get_item(dict, "MessageName");
207 if (!node) {
208 debug_info("ERROR: MessageName key not found in plist!");
209 err = MOBILEBACKUP2_E_PLIST_ERROR;
210 goto leave;
211 }
212
213 char *str = NULL;
214 plist_get_string_val(node, &str);
215 if (str && (strcmp(str, message) == 0)) {
216 err = MOBILEBACKUP2_E_SUCCESS;
217 } else {
218 debug_info("ERROR: MessageName value does not match '%s'!", message);
219 err = MOBILEBACKUP2_E_REPLY_NOT_OK;
220 }
221 if (str)
222 free(str);
223
224 if (result) {
225 *result = dict;
226 dict = NULL;
227 }
228leave:
229 if (dict) {
230 plist_free(dict);
231 }
232
233 return err;
234}
235
236/**
237 * TODO
238 */
239mobilebackup2_error_t mobilebackup2_receive_message(mobilebackup2_client_t client, plist_t *msg_plist, char **dlmessage)
240{
241 return mobilebackup2_error(device_link_service_receive_message(client->parent, msg_plist, dlmessage));
242}
243
244mobilebackup2_error_t mobilebackup2_send_raw(mobilebackup2_client_t client, const char *data, uint32_t length, uint32_t *bytes)
245{
246 if (!client || !client->parent)
247 return MOBILEBACKUP2_E_INVALID_ARG;
248
249 *bytes = 0;
250
251 idevice_connection_t conn = client->parent->parent->connection;
252
253 int bytes_loc = 0;
254 uint32_t sent = 0;
255 do {
256 bytes_loc = 0;
257 idevice_connection_send(conn, data+sent, length-sent, (uint32_t*)&bytes_loc);
258 if (bytes_loc <= 0)
259 break;
260 sent += bytes_loc;
261 } while (sent < length);
262 if (sent > 0) {
263 *bytes = sent;
264 return MOBILEBACKUP2_E_SUCCESS;
265 } else {
266 return MOBILEBACKUP2_E_MUX_ERROR;
267 }
268}
269
270mobilebackup2_error_t mobilebackup2_receive_raw(mobilebackup2_client_t client, char *data, uint32_t length, uint32_t *bytes)
271{
272 if (!client || !client->parent)
273 return MOBILEBACKUP2_E_INVALID_ARG;
274
275 idevice_connection_t conn = client->parent->parent->connection;
276
277 *bytes = 0;
278
279 int bytes_loc = 0;
280 uint32_t received = 0;
281 do {
282 bytes_loc = 0;
283 idevice_connection_receive(conn, data+received, length-received, (uint32_t*)&bytes_loc);
284 if (bytes_loc <= 0) break;
285 received += bytes_loc;
286 } while (received < length);
287 if (received > 0) {
288 *bytes = received;
289 return MOBILEBACKUP2_E_SUCCESS;
290 } else if (received == 0) {
291 return MOBILEBACKUP2_E_SUCCESS;
292 } else {
293 return MOBILEBACKUP2_E_MUX_ERROR;
294 }
295}
296
297/**
298 * TODO
299 */
300mobilebackup2_error_t mobilebackup2_version_exchange(mobilebackup2_client_t client)
301{
302 if (!client || !client->parent)
303 return MOBILEBACKUP2_E_INVALID_ARG;
304
305 plist_t dict = plist_new_dict();
306 plist_t array = plist_new_array();
307 plist_array_append_item(array, plist_new_real(2.0));
308 plist_array_append_item(array, plist_new_real(2.1));
309 plist_dict_insert_item(dict, "SupportedProtocolVersions", array);
310
311 mobilebackup2_error_t err = internal_mobilebackup2_send_message(client, "Hello", dict);
312 plist_free(dict);
313
314 if (err != MOBILEBACKUP2_E_SUCCESS)
315 goto leave;
316
317 dict = NULL;
318 err = internal_mobilebackup2_receive_message(client, "Response", &dict);
319 if (err != MOBILEBACKUP2_E_SUCCESS)
320 goto leave;
321
322 plist_t node = plist_dict_get_item(dict, "ErrorCode");
323 if (!node || (plist_get_node_type(node) != PLIST_UINT)) {
324 err = MOBILEBACKUP2_E_PLIST_ERROR;
325 goto leave;
326 }
327
328 uint64_t val = 0;
329 plist_get_uint_val(node, &val);
330
331 if (val != 0) {
332 err = MOBILEBACKUP2_E_REPLY_NOT_OK;
333 goto leave;
334 }
335
336 node = plist_dict_get_item(dict, "ProtocolVersion");
337 if (!node || (plist_get_node_type(node) != PLIST_REAL)) {
338 err = MOBILEBACKUP2_E_PLIST_ERROR;
339 goto leave;
340 }
341
342 double rval = 0.0;
343 plist_get_real_val(node, &rval);
344
345 debug_info("using protocol version %f\n", rval);
346
347 // TODO version check ??
348 // if version does not match
349 // err = MOBILEBACKUP2_E_BAD_VERSION
350
351leave:
352 if (dict)
353 plist_free(dict);
354 return err;
355}
356
357/**
358 * TODO
359 */
360mobilebackup2_error_t mobilebackup2_request_backup(mobilebackup2_client_t client, const char *uuid)
361{
362 if (!client || !client->parent)
363 return MOBILEBACKUP2_E_INVALID_ARG;
364
365 plist_t dict = plist_new_dict();
366 plist_dict_insert_item(dict, "TargetIdentifier", plist_new_string(uuid));
367 mobilebackup2_error_t err = internal_mobilebackup2_send_message(client, "Backup", dict);
368 plist_free(dict);
369
370 return err;
371}
372
373mobilebackup2_error_t mobilebackup2_send_status_response(mobilebackup2_client_t client, int status_code, const char *status1, plist_t status2)
374{
375 if (!client || !client->parent)
376 return MOBILEBACKUP2_E_INVALID_ARG;
377
378 plist_t array = plist_new_array();
379 plist_array_append_item(array, plist_new_string("DLMessageStatusResponse"));
380 plist_array_append_item(array, plist_new_uint(status_code));
381 if (status1) {
382 plist_array_append_item(array, plist_new_string(status1));
383 } else {
384 plist_array_append_item(array, plist_new_string("___EmptyParameterString___"));
385 }
386 if (status2) {
387 plist_array_append_item(array, plist_copy(status2));
388 } else {
389 plist_array_append_item(array, plist_new_string("___EmptyParameterString___"));
390 }
391
392 mobilebackup2_error_t err = mobilebackup2_error(device_link_service_send(client->parent, array));
393 plist_free(array);
394
395 return err;
396}
diff --git a/src/mobilebackup2.h b/src/mobilebackup2.h
new file mode 100644
index 0000000..f96e400
--- /dev/null
+++ b/src/mobilebackup2.h
@@ -0,0 +1,31 @@
1 /*
2 * mobilebackup2.h
3 * Definitions for the mobilebackup2 service (iOS4+)
4 *
5 * Copyright (c) 2010 Nikias Bassen 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 MOBILEBACKUP2_H
22#define MOBILEBACKUP2_H
23
24#include "libimobiledevice/mobilebackup2.h"
25#include "device_link_service.h"
26
27struct mobilebackup2_client_private {
28 device_link_service_client_t parent;
29};
30
31#endif