summaryrefslogtreecommitdiffstats
path: root/src/restore.c
diff options
context:
space:
mode:
authorGravatar Martin Szulecki2010-05-19 23:48:37 +0200
committerGravatar Martin Szulecki2010-05-19 23:48:37 +0200
commitd5e18975a3905682f5db090b4ddffeafb4f5fde4 (patch)
tree6ea0194a67dc606243730318493e0e81e7e44cde /src/restore.c
parenta78323c5135a33851e7056a2f2fce3c83b5acfbc (diff)
downloadlibimobiledevice-d5e18975a3905682f5db090b4ddffeafb4f5fde4.tar.gz
libimobiledevice-d5e18975a3905682f5db090b4ddffeafb4f5fde4.tar.bz2
Implement restored API
Diffstat (limited to 'src/restore.c')
-rw-r--r--src/restore.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/src/restore.c b/src/restore.c
new file mode 100644
index 0000000..8acefef
--- /dev/null
+++ b/src/restore.c
@@ -0,0 +1,408 @@
1/*
2 * restore.c
3 * com.apple.mobile.restored service implementation.
4 *
5 * Copyright (c) 2010 Joshua Hill. 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 <arpa/inet.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include <glib.h>
27#include <plist/plist.h>
28
29#include "property_list_service.h"
30#include "restore.h"
31#include "idevice.h"
32#include "debug.h"
33
34#define RESULT_SUCCESS 0
35#define RESULT_FAILURE 1
36
37/**
38 * Internally used function for checking the result from restore's answer
39 * plist to a previously sent request.
40 *
41 * @param dict The plist to evaluate.
42 * @param query_match Name of the request to match.
43 *
44 * @return RESULT_SUCCESS when the result is 'Success',
45 * RESULT_FAILURE when the result is 'Failure',
46 * or a negative value if an error occured during evaluation.
47 */
48static int restored_check_result(plist_t dict)
49{
50 int ret = -1;
51 plist_t result_node = plist_dict_get_item(dict, "Result");
52 if (!result_node) {
53 return ret;
54 }
55
56 plist_type result_type = plist_get_node_type(result_node);
57
58 if (result_type == PLIST_STRING) {
59
60 char *result_value = NULL;
61
62 plist_get_string_val(result_node, &result_value);
63
64 if (result_value) {
65 if (!strcmp(result_value, "Success")) {
66 ret = RESULT_SUCCESS;
67 } else if (!strcmp(result_value, "Failure")) {
68 ret = RESULT_FAILURE;
69 } else {
70 debug_info("ERROR: unknown result value '%s'", result_value);
71 }
72 }
73 if (result_value)
74 free(result_value);
75 }
76 return ret;
77}
78
79/**
80 * Adds a label key with the passed value to a plist dict node.
81 *
82 * @param plist The plist to add the key to
83 * @param label The value for the label key
84 *
85 */
86static void plist_dict_add_label(plist_t plist, const char *label)
87{
88 if (plist && label) {
89 if (plist_get_node_type(plist) == PLIST_DICT)
90 plist_dict_insert_item(plist, "Label", plist_new_string(label));
91 }
92}
93
94/**
95 * Closes the restored client session if one is running and frees up the
96 * restored_client struct.
97 *
98 * @param client The restore client
99 *
100 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
101 */
102restored_error_t restored_client_free(restored_client_t client)
103{
104 if (!client)
105 return RESTORE_E_INVALID_ARG;
106 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
107
108 if (client->parent) {
109 restored_goodbye(client);
110
111 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
112 ret = RESTORE_E_SUCCESS;
113 }
114 }
115
116 if (client->uuid) {
117 free(client->uuid);
118 }
119 if (client->label) {
120 free(client->label);
121 }
122
123 free(client);
124 return ret;
125}
126
127/**
128 * Sets the label to send for requests to restored.
129 *
130 * @param client The restore client
131 * @param label The label to set or NULL to disable sending a label
132 *
133 */
134void restored_client_set_label(restored_client_t client, const char *label)
135{
136 if (client) {
137 if (client->label)
138 free(client->label);
139
140 client->label = (label != NULL) ? strdup(label): NULL;
141 }
142}
143
144/**
145 * Receives a plist from restored.
146 *
147 * @param client The restored client
148 * @param plist The plist to store the received data
149 *
150 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client or
151 * plist is NULL
152 */
153restored_error_t restored_receive(restored_client_t client, plist_t *plist)
154{
155 if (!client || !plist || (plist && *plist))
156 return RESTORE_E_INVALID_ARG;
157 restored_error_t ret = RESTORE_E_SUCCESS;
158 property_list_service_error_t err;
159
160 err = property_list_service_receive_plist(client->parent, plist);
161 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
162 ret = RESTORE_E_UNKNOWN_ERROR;
163 }
164
165 if (!*plist)
166 ret = RESTORE_E_PLIST_ERROR;
167
168 return ret;
169}
170
171/**
172 * Sends a plist to restored.
173 *
174 * @note This function is low-level and should only be used if you need to send
175 * a new type of message.
176 *
177 * @param client The restored client
178 * @param plist The plist to send
179 *
180 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client or
181 * plist is NULL
182 */
183restored_error_t restored_send(restored_client_t client, plist_t plist)
184{
185 if (!client || !plist)
186 return RESTORE_E_INVALID_ARG;
187
188 restored_error_t ret = RESTORE_E_SUCCESS;
189 idevice_error_t err;
190
191 err = property_list_service_send_xml_plist(client->parent, plist);
192 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
193 ret = RESTORE_E_UNKNOWN_ERROR;
194 }
195 return ret;
196}
197
198/**
199 * Query the type of the service daemon. Depending on whether the device is
200 * queried in normal mode or restore mode, different types will be returned.
201 *
202 * @param client The restored client
203 * @param type The type returned by the service daemon. Pass NULL to ignore.
204 * @param version The restore protocol version. Pass NULL to ignore.
205 *
206 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
207 */
208restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version)
209{
210 if (!client)
211 return RESTORE_E_INVALID_ARG;
212
213 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
214
215 plist_t dict = plist_new_dict();
216 plist_dict_add_label(dict, client->label);
217 plist_dict_insert_item(dict,"Request", plist_new_string("QueryType"));
218
219 debug_info("called");
220 ret = restored_send(client, dict);
221
222 plist_free(dict);
223 dict = NULL;
224
225 ret = restored_receive(client, &dict);
226
227 if (RESTORE_E_SUCCESS != ret)
228 return ret;
229
230 ret = RESTORE_E_UNKNOWN_ERROR;
231 if (restored_check_result(dict) == RESULT_SUCCESS) {
232 /* return the type if requested */
233 if (type != NULL) {
234 plist_t type_node = plist_dict_get_item(dict, "Type");
235 plist_get_string_val(type_node, type);
236 }
237 debug_info("success with type %s", *type);
238 ret = RESTORE_E_SUCCESS;
239
240 /* fetch the restore protocol version */
241 plist_t version_node = plist_dict_get_item(dict, "RestoreProtocolVersion");
242 if (version_node && version) {
243 plist_get_uint_val(version_node, version);
244 debug_info("restored protocol version %llu", *version);
245 ret = RESTORE_E_SUCCESS;
246 } else
247 ret = RESTORE_E_UNKNOWN_ERROR;
248
249 }
250 plist_free(dict);
251 dict = NULL;
252
253 return ret;
254}
255
256/**
257 * Creates a new restored client for the device.
258 *
259 * @param device The device to create a restored client for
260 * @param client The pointer to the location of the new restored_client
261 * @param label The label to use for communication. Usually the program name.
262 *
263 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
264 */
265restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label)
266{
267 if (!client)
268 return RESTORE_E_INVALID_ARG;
269
270 restored_error_t ret = RESTORE_E_SUCCESS;
271
272 property_list_service_client_t plistclient = NULL;
273 if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
274 debug_info("could not connect to restored (device %s)", device->uuid);
275 return RESTORE_E_MUX_ERROR;
276 }
277
278 restored_client_t client_loc = (restored_client_t) malloc(sizeof(struct restored_client_private));
279 client_loc->parent = plistclient;
280 client_loc->uuid = NULL;
281 client_loc->label = NULL;
282 if (label != NULL)
283 client_loc->label = strdup(label);
284
285 ret = idevice_get_uuid(device, &client_loc->uuid);
286 if (RESTORE_E_SUCCESS != ret) {
287 debug_info("failed to get device uuid.");
288 }
289 debug_info("device uuid: %s", client_loc->uuid);
290
291 if (RESTORE_E_SUCCESS == ret) {
292 *client = client_loc;
293 } else {
294 restored_client_free(client_loc);
295 }
296
297 return ret;
298}
299
300/**
301 * Sends the Goodbye request to restored signaling the end of communication.
302 *
303 * @param client The restore client
304 *
305 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
306 * RESTORE_E_PLIST_ERROR if the device did not acknowledge the request
307 */
308restored_error_t restored_goodbye(restored_client_t client)
309{
310 if (!client)
311 return RESTORE_E_INVALID_ARG;
312
313 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
314
315 plist_t dict = plist_new_dict();
316 plist_dict_add_label(dict, client->label);
317 plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye"));
318
319 debug_info("called");
320
321 ret = restored_send(client, dict);
322 plist_free(dict);
323 dict = NULL;
324
325 ret = restored_receive(client, &dict);
326 if (!dict) {
327 debug_info("did not get goodbye response back");
328 return RESTORE_E_PLIST_ERROR;
329 }
330
331 if (restored_check_result(dict) == RESULT_SUCCESS) {
332 debug_info("success");
333 ret = RESTORE_E_SUCCESS;
334 }
335 plist_free(dict);
336 dict = NULL;
337 return ret;
338}
339
340/**
341 * Requests to start a restore and retrieve it's port on success.
342 *
343 * @param client The restored client
344 *
345 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter
346 * is NULL, RESTORE_E_START_RESTORE_FAILED if the request fails
347 */
348restored_error_t restored_start_restore(restored_client_t client)
349{
350 if (!client)
351 return RESTORE_E_INVALID_ARG;
352
353 plist_t dict = NULL;
354 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
355
356 dict = plist_new_dict();
357 plist_dict_add_label(dict, client->label);
358 plist_dict_insert_item(dict,"Request", plist_new_string("StartRestore"));
359 plist_dict_insert_item(dict,"RestoreProtocolVersion", plist_new_uint(2));
360
361 /* send to device */
362 ret = restored_send(client, dict);
363 plist_free(dict);
364 dict = NULL;
365
366 return ret;
367}
368
369/**
370 * Requests device to reboot.
371 *
372 * @param client The restored client
373 *
374 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter
375 * is NULL
376 */
377restored_error_t restored_reboot(restored_client_t client)
378{
379 if (!client)
380 return RESTORE_E_INVALID_ARG;
381
382 plist_t dict = NULL;
383 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
384
385 dict = plist_new_dict();
386 plist_dict_add_label(dict, client->label);
387 plist_dict_insert_item(dict,"Request", plist_new_string("Reboot"));
388
389 /* send to device */
390 ret = restored_send(client, dict);
391 plist_free(dict);
392 dict = NULL;
393
394 if (RESTORE_E_SUCCESS != ret)
395 return ret;
396
397 ret = restored_receive(client, &dict);
398 if (RESTORE_E_SUCCESS != ret)
399 return ret;
400
401 if (!dict)
402 return RESTORE_E_PLIST_ERROR;
403
404 plist_free(dict);
405 dict = NULL;
406 return ret;
407}
408