summaryrefslogtreecommitdiffstats
path: root/src/installation_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/installation_proxy.c')
-rw-r--r--src/installation_proxy.c740
1 files changed, 740 insertions, 0 deletions
diff --git a/src/installation_proxy.c b/src/installation_proxy.c
new file mode 100644
index 0000000..a99b5cb
--- /dev/null
+++ b/src/installation_proxy.c
@@ -0,0 +1,740 @@
1/*
2 * installation_proxy.c
3 * Installation Proxy implementation.
4 *
5 * Copyright (c) 2009 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 <string.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <arpa/inet.h>
26#include <plist/plist.h>
27
28#include "installation_proxy.h"
29#include "property_list_service.h"
30#include "debug.h"
31
32struct instproxy_status_data {
33 instproxy_client_t client;
34 instproxy_status_cb_t cbfunc;
35 char *operation;
36};
37
38/** Locks an installation_proxy client, done for thread safety stuff.
39 *
40 * @param client The installation_proxy client to lock
41 */
42static void instproxy_lock(instproxy_client_t client)
43{
44 debug_info("InstallationProxy: Locked");
45 g_mutex_lock(client->mutex);
46}
47
48/** Unlocks an installation_proxy client, done for thread safety stuff.
49 *
50 * @param client The installation_proxy client to lock
51 */
52static void instproxy_unlock(instproxy_client_t client)
53{
54 debug_info("InstallationProxy: Unlocked");
55 g_mutex_unlock(client->mutex);
56}
57
58/**
59 * Convert a property_list_service_error_t value to an instproxy_error_t value.
60 * Used internally to get correct error codes.
61 *
62 * @param err A property_list_service_error_t error code
63 *
64 * @return A matching instproxy_error_t error code,
65 * INSTPROXY_E_UNKNOWN_ERROR otherwise.
66 */
67static instproxy_error_t instproxy_error(property_list_service_error_t err)
68{
69 switch (err) {
70 case PROPERTY_LIST_SERVICE_E_SUCCESS:
71 return INSTPROXY_E_SUCCESS;
72 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
73 return INSTPROXY_E_INVALID_ARG;
74 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
75 return INSTPROXY_E_PLIST_ERROR;
76 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
77 return INSTPROXY_E_CONN_FAILED;
78 default:
79 break;
80 }
81 return INSTPROXY_E_UNKNOWN_ERROR;
82}
83
84/**
85 * Creates a new installation_proxy client
86 *
87 * @param device The device to connect to
88 * @param port Destination port (usually given by lockdownd_start_service).
89 * @param client Pointer that will be set to a newly allocated
90 * instproxy_client_t upon successful return.
91 *
92 * @return INSTPROXY_E_SUCCESS on success, or an INSTPROXY_E_* error value
93 * when an error occured.
94 */
95instproxy_error_t instproxy_client_new(iphone_device_t device, uint16_t port, instproxy_client_t *client)
96{
97 /* makes sure thread environment is available */
98 if (!g_thread_supported())
99 g_thread_init(NULL);
100
101 if (!device)
102 return INSTPROXY_E_INVALID_ARG;
103
104 property_list_service_client_t plistclient = NULL;
105 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
106 return INSTPROXY_E_CONN_FAILED;
107 }
108
109 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_int));
110 client_loc->parent = plistclient;
111 client_loc->mutex = g_mutex_new();
112 client_loc->status_updater = NULL;
113
114 *client = client_loc;
115 return INSTPROXY_E_SUCCESS;
116}
117
118/**
119 * Frees an installation_proxy client.
120 *
121 * @param client The installation_proxy client to free.
122 *
123 * @return INSTPROXY_E_SUCCESS on success
124 * or INSTPROXY_E_INVALID_ARG if client is NULL.
125 */
126instproxy_error_t instproxy_client_free(instproxy_client_t client)
127{
128 if (!client)
129 return INSTPROXY_E_INVALID_ARG;
130
131 property_list_service_client_free(client->parent);
132 client->parent = NULL;
133 if (client->status_updater) {
134 debug_info("joining status_updater");
135 g_thread_join(client->status_updater);
136 }
137 if (client->mutex) {
138 g_mutex_free(client->mutex);
139 }
140 free(client);
141
142 return INSTPROXY_E_SUCCESS;
143}
144
145/**
146 * List installed applications. This function runs synchronously.
147 *
148 * @param client The connected installation_proxy client
149 * @param apptype The type of applications to list.
150 * @param result Pointer that will be set to a plist that will hold an array
151 * of PLIST_DICT holding information about the applications found.
152 *
153 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
154 * an error occured.
155 */
156instproxy_error_t instproxy_browse(instproxy_client_t client, instproxy_apptype_t apptype, plist_t *result)
157{
158 if (!client || !client->parent || !result)
159 return INSTPROXY_E_INVALID_ARG;
160
161 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
162 int browsing = 0;
163 plist_t apps_array = NULL;
164
165 plist_t dict = plist_new_dict();
166 if (apptype != INSTPROXY_APPTYPE_ALL) {
167 plist_t client_opts = plist_new_dict();
168 plist_t p_apptype = NULL;
169 switch (apptype) {
170 case INSTPROXY_APPTYPE_SYSTEM:
171 p_apptype = plist_new_string("System");
172 break;
173 case INSTPROXY_APPTYPE_USER:
174 p_apptype = plist_new_string("User");
175 break;
176 default:
177 debug_info("unknown apptype %d given, using INSTPROXY_APPTYPE_USER instead", apptype);
178 p_apptype = plist_new_string("User");
179 break;
180 }
181 plist_dict_insert_item(client_opts, "ApplicationType", p_apptype);
182 plist_dict_insert_item(dict, "ClientOptions", client_opts);
183 }
184 plist_dict_insert_item(dict, "Command", plist_new_string("Browse"));
185
186 instproxy_lock(client);
187 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
188 plist_free(dict);
189 if (res != INSTPROXY_E_SUCCESS) {
190 debug_info("could not send plist");
191 goto leave_unlock;
192 }
193
194 apps_array = plist_new_array();
195
196 do {
197 browsing = 0;
198 dict = NULL;
199 res = instproxy_error(property_list_service_receive_plist(client->parent, &dict));
200 if (res != INSTPROXY_E_SUCCESS) {
201 break;
202 }
203 if (dict) {
204 uint64_t i;
205 uint64_t current_amount = 0;
206 char *status = NULL;
207 plist_t camount = plist_dict_get_item(dict, "CurrentAmount");
208 plist_t pstatus = plist_dict_get_item(dict, "Status");
209 if (camount) {
210 plist_get_uint_val(camount, &current_amount);
211 }
212 if (current_amount > 0) {
213 plist_t current_list = plist_dict_get_item(dict, "CurrentList");
214 for (i = 0; current_list && (i < current_amount); i++) {
215 plist_t item = plist_array_get_item(current_list, i);
216 plist_array_append_item(apps_array, plist_copy(item));
217 }
218 }
219 if (pstatus) {
220 plist_get_string_val(pstatus, &status);
221 }
222 if (status) {
223 if (!strcmp(status, "BrowsingApplications")) {
224 browsing = 1;
225 } else if (!strcmp(status, "Complete")) {
226 debug_info("Browsing applications completed");
227 res = INSTPROXY_E_SUCCESS;
228 }
229 free(status);
230 }
231 plist_free(dict);
232 }
233 } while (browsing);
234
235 if (res == INSTPROXY_E_SUCCESS) {
236 *result = apps_array;
237 }
238
239leave_unlock:
240 instproxy_unlock(client);
241 return res;
242}
243
244/**
245 * Internally used function that will synchronously receive messages from
246 * the specified installation_proxy until it completes or an error occurs.
247 *
248 * If status_cb is not NULL, the callback function will be called each time
249 * a status update or error message is received.
250 *
251 * @param client The connected installation proxy client
252 * @param status_cb Pointer to a callback function or NULL
253 * @param operation Operation name. Will be passed to the callback function
254 * in async mode or shown in debug messages in sync mode.
255 */
256static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation)
257{
258 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
259 int ok = 1;
260 plist_t dict = NULL;
261
262 do {
263 instproxy_lock(client);
264 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 30000));
265 instproxy_unlock(client);
266 if (res != INSTPROXY_E_SUCCESS) {
267 debug_info("could not receive plist, error %d", res);
268 break;
269 }
270 if (dict) {
271 /* invoke callback function */
272 if (status_cb) {
273 status_cb(operation, dict);
274 }
275 /* check for 'Error', so we can abort cleanly */
276 plist_t err = plist_dict_get_item(dict, "Error");
277 if (err) {
278#ifndef STRIP_DEBUG_CODE
279 char *err_msg = NULL;
280 plist_get_string_val(err, &err_msg);
281 if (err_msg) {
282 debug_info("(%s): ERROR: %s", operation, err_msg);
283 free(err_msg);
284 }
285#endif
286 ok = 0;
287 res = INSTPROXY_E_OP_FAILED;
288 }
289 /* get 'Status' */
290 plist_t status = plist_dict_get_item(dict, "Status");
291 if (status) {
292 char *status_msg = NULL;
293 plist_get_string_val(status, &status_msg);
294 if (status_msg) {
295 if (!strcmp(status_msg, "Complete")) {
296 ok = 0;
297 res = INSTPROXY_E_SUCCESS;
298 }
299#ifndef STRIP_DEBUG_CODE
300 plist_t npercent = plist_dict_get_item(dict, "PercentComplete");
301 if (npercent) {
302 uint64_t val = 0;
303 int percent;
304 plist_get_uint_val(npercent, &val);
305 percent = val;
306 debug_info("(%s): %s (%d%%)", operation, status_msg, percent);
307 } else {
308 debug_info("(%s): %s", operation, status_msg);
309 }
310#endif
311 free(status_msg);
312 }
313 }
314 plist_free(dict);
315 dict = NULL;
316 }
317 } while (ok && client->parent);
318
319 return res;
320}
321
322/**
323 * Internally used status updater thread function that will call the specified
324 * callback function when status update messages (or error messages) are
325 * received.
326 *
327 * @param arg Pointer to an allocated struct instproxy_status_data that holds
328 * the required data about the connected client and the callback function.
329 *
330 * @return Always NULL.
331 */
332static gpointer instproxy_status_updater(gpointer arg)
333{
334 struct instproxy_status_data *data = (struct instproxy_status_data*)arg;
335
336 /* run until the operation is complete or an error occurs */
337 (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation);
338
339 /* cleanup */
340 instproxy_lock(data->client);
341 debug_info("done, cleaning up.");
342 if (data->operation) {
343 free(data->operation);
344 }
345 data->client->status_updater = NULL;
346 instproxy_unlock(data->client);
347 free(data);
348
349 return NULL;
350}
351
352/**
353 * Internally used helper function that creates a status updater thread which
354 * will call the passed callback function when status updates occur.
355 * If status_cb is NULL no thread will be created, but the operation will
356 * run synchronously until it completes or an error occurs.
357 *
358 * @param client The connected installation proxy client
359 * @param status_cb Pointer to a callback function or NULL
360 * @param operation Operation name. Will be passed to the callback function
361 * in async mode or shown in debug messages in sync mode.
362 *
363 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or
364 * when the operation completed successfully (sync).
365 * An INSTPROXY_E_* error value is returned if an error occured.
366 */
367static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation)
368{
369 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
370 if (status_cb) {
371 /* async mode */
372 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data));
373 if (data) {
374 data->client = client;
375 data->cbfunc = status_cb;
376 data->operation = strdup(operation);
377
378 client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL);
379 if (client->status_updater) {
380 res = INSTPROXY_E_SUCCESS;
381 }
382 }
383 } else {
384 /* sync mode */
385 res = instproxy_perform_operation(client, NULL, operation);
386 }
387 return res;
388}
389
390
391/**
392 * Internal function used by instproxy_install and instproxy_upgrade.
393 *
394 * @param client The connected installation_proxy client
395 * @param pkg_path Path of the installation package (inside the AFC jail)
396 * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
397 * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
398 * @param status_cb Callback function for progress and status information. If
399 * NULL is passed, this function will run synchronously.
400 * @param command The command to execute.
401 *
402 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
403 * an error occured.
404 */
405static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb, const char *command)
406{
407 if (!client || !client->parent || !pkg_path) {
408 return INSTPROXY_E_INVALID_ARG;
409 }
410 if (sinf && (plist_get_node_type(sinf) != PLIST_DATA)) {
411 debug_info("(%s): ERROR: sinf data is not a PLIST_DATA node!", command);
412 return INSTPROXY_E_INVALID_ARG;
413 }
414 if (metadata && (plist_get_node_type(metadata) != PLIST_DATA)) {
415 debug_info("(%s): ERROR: metadata is not a PLIST_DATA node!", command);
416 return INSTPROXY_E_INVALID_ARG;
417 }
418
419 if (client->status_updater) {
420 return INSTPROXY_E_OP_IN_PROGRESS;
421 }
422
423 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
424
425 plist_t dict = plist_new_dict();
426 if (sinf && metadata) {
427 plist_t client_opts = plist_new_dict();
428 plist_dict_insert_item(client_opts, "ApplicationSINF", plist_copy(sinf));
429 plist_dict_insert_item(client_opts, "iTunesMetadata", plist_copy(metadata));
430 plist_dict_insert_item(dict, "ClientOptions", client_opts);
431 }
432 plist_dict_insert_item(dict, "Command", plist_new_string(command));
433 plist_dict_insert_item(dict, "PackagePath", plist_new_string(pkg_path));
434
435 instproxy_lock(client);
436 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
437 instproxy_unlock(client);
438
439 plist_free(dict);
440
441 if (res != INSTPROXY_E_SUCCESS) {
442 debug_info("could not send plist, error %d", res);
443 return res;
444 }
445
446 return instproxy_create_status_updater(client, status_cb, command);
447}
448
449/**
450 * Install an application on the device.
451 *
452 * @param client The connected installation_proxy client
453 * @param pkg_path Path of the installation package (inside the AFC jail)
454 * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
455 * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
456 * @param status_cb Callback function for progress and status information. If
457 * NULL is passed, this function will run synchronously.
458 *
459 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
460 * an error occured.
461 *
462 * @note If a callback function is given (async mode), this function returns
463 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
464 * created successfully; any error occuring during the operation has to be
465 * handled inside the specified callback function.
466 */
467instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb)
468{
469 return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Install");
470}
471
472/**
473 * Upgrade an application on the device. This function is nearly the same as
474 * instproxy_install; the difference is that the installation progress on the
475 * device is faster if the application is already installed.
476 *
477 * @param client The connected installation_proxy client
478 * @param pkg_path Path of the installation package (inside the AFC jail)
479 * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
480 * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
481 * @param status_cb Callback function for progress and status information. If
482 * NULL is passed, this function will run synchronously.
483 *
484 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
485 * an error occured.
486 *
487 * @note If a callback function is given (async mode), this function returns
488 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
489 * created successfully; any error occuring during the operation has to be
490 * handled inside the specified callback function.
491 */
492instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb)
493{
494 return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Upgrade");
495}
496
497/**
498 * Uninstall an application from the device.
499 *
500 * @param client The connected installation proxy client
501 * @param appid ApplicationIdentifier of the app to uninstall
502 * @param status_cb Callback function for progress and status information. If
503 * NULL is passed, this function will run synchronously.
504 *
505 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
506 * an error occured.
507 *
508 * @note If a callback function is given (async mode), this function returns
509 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
510 * created successfully; any error occuring during the operation has to be
511 * handled inside the specified callback function.
512 */
513instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
514{
515 if (!client || !client->parent || !appid) {
516 return INSTPROXY_E_INVALID_ARG;
517 }
518
519 if (client->status_updater) {
520 return INSTPROXY_E_OP_IN_PROGRESS;
521 }
522
523 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
524 plist_t dict = plist_new_dict();
525 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
526 plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall"));
527
528 instproxy_lock(client);
529 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
530 instproxy_unlock(client);
531
532 plist_free(dict);
533
534 if (res != INSTPROXY_E_SUCCESS) {
535 debug_info("could not send plist, error %d", res);
536 return res;
537 }
538
539 return instproxy_create_status_updater(client, status_cb, "Uninstall");
540}
541
542/**
543 * List archived applications. This function runs synchronously.
544 *
545 * @see instproxy_archive
546 *
547 * @param client The connected installation_proxy client
548 * @param result Pointer that will be set to a plist containing a PLIST_DICT
549 * holding information about the archived applications found.
550 *
551 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
552 * an error occured.
553 */
554instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t *result)
555{
556 if (!client || !client->parent || !result)
557 return INSTPROXY_E_INVALID_ARG;
558
559 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
560
561 plist_t dict = plist_new_dict();
562 plist_dict_insert_item(dict, "Command", plist_new_string("LookupArchives"));
563
564 instproxy_lock(client);
565
566 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
567 plist_free(dict);
568
569 if (res != INSTPROXY_E_SUCCESS) {
570 debug_info("could not send plist, error %d", res);
571 goto leave_unlock;
572 }
573
574 res = instproxy_error(property_list_service_receive_plist(client->parent, result));
575 if (res != INSTPROXY_E_SUCCESS) {
576 debug_info("could not receive plist, error %d", res);
577 goto leave_unlock;
578 }
579
580 res = INSTPROXY_E_SUCCESS;
581
582leave_unlock:
583 instproxy_unlock(client);
584 return res;
585}
586
587/**
588 * Archive an application on the device.
589 * This function tells the device to make an archive of the specified
590 * application. This results in the device creating a ZIP archive in the
591 * 'ApplicationArchives' directory and uninstalling the application.
592 *
593 * @param client The connected installation proxy client
594 * @param appid ApplicationIdentifier of the app to archive.
595 * @param options This is either 0 for default behaviour (make an archive
596 * including app/user settings etc. AND uninstall the application),
597 * or one or a combination of the following options:
598 * INSTPROXY_ARCHIVE_APP_ONLY (1)
599 * INSTPROXY_ARCHIVE_SKIP_UNINSTALL (2)
600 * @param status_cb Callback function for progress and status information. If
601 * NULL is passed, this function will run synchronously.
602 *
603 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
604 * an error occured.
605 *
606 * @note If a callback function is given (async mode), this function returns
607 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
608 * created successfully; any error occuring during the operation has to be
609 * handled inside the specified callback function.
610 */
611instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, uint32_t options, instproxy_status_cb_t status_cb)
612{
613 if (!client || !client->parent || !appid)
614 return INSTPROXY_E_INVALID_ARG;
615
616 if (client->status_updater) {
617 return INSTPROXY_E_OP_IN_PROGRESS;
618 }
619
620 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
621
622 plist_t dict = plist_new_dict();
623 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
624 if (options > 0) {
625 plist_t client_opts = plist_new_dict();
626 if (options & INSTPROXY_ARCHIVE_APP_ONLY) {
627 plist_dict_insert_item(client_opts, "ArchiveType", plist_new_string("ApplicationOnly"));
628 }
629 if (options & INSTPROXY_ARCHIVE_SKIP_UNINSTALL) {
630 plist_dict_insert_item(client_opts, "SkipUninstall", plist_new_bool(1));
631 }
632 plist_dict_insert_item(dict, "ClientOptions", client_opts);
633 }
634 plist_dict_insert_item(dict, "Command", plist_new_string("Archive"));
635
636 instproxy_lock(client);
637 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
638 instproxy_unlock(client);
639
640 plist_free(dict);
641
642 if (res != INSTPROXY_E_SUCCESS) {
643 debug_info("could not send plist, error %d", res);
644 return res;
645 }
646 return instproxy_create_status_updater(client, status_cb, "Archive");
647}
648
649/**
650 * Restore a previously archived application on the device.
651 * This function is the counterpart to instproxy_archive.
652 * @see instproxy_archive
653 *
654 * @param client The connected installation proxy client
655 * @param appid ApplicationIdentifier of the app to restore.
656 * @param status_cb Callback function for progress and status information. If
657 * NULL is passed, this function will run synchronously.
658 *
659 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
660 * an error occured.
661 *
662 * @note If a callback function is given (async mode), this function returns
663 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
664 * created successfully; any error occuring during the operation has to be
665 * handled inside the specified callback function.
666 */
667instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
668{
669 if (!client || !client->parent || !appid)
670 return INSTPROXY_E_INVALID_ARG;
671
672 if (client->status_updater) {
673 return INSTPROXY_E_OP_IN_PROGRESS;
674 }
675
676 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
677
678 plist_t dict = plist_new_dict();
679 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
680 plist_dict_insert_item(dict, "Command", plist_new_string("Restore"));
681
682 instproxy_lock(client);
683 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
684 instproxy_unlock(client);
685
686 plist_free(dict);
687
688 if (res != INSTPROXY_E_SUCCESS) {
689 debug_info("could not send plist, error %d", res);
690 return res;
691 }
692 return instproxy_create_status_updater(client, status_cb, "Restore");
693}
694
695/**
696 * Removes a previously archived application from the device.
697 * This function removes the ZIP archive from the 'ApplicationArchives'
698 * directory.
699 *
700 * @param client The connected installation proxy client
701 * @param appid ApplicationIdentifier of the archived app to remove.
702 * @param status_cb Callback function for progress and status information. If
703 * NULL is passed, this function will run synchronously.
704 *
705 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
706 * an error occured.
707 *
708 * @note If a callback function is given (async mode), this function returns
709 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
710 * created successfully; any error occuring during the operation has to be
711 * handled inside the specified callback function.
712 */
713instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
714{
715 if (!client || !client->parent || !appid)
716 return INSTPROXY_E_INVALID_ARG;
717
718 if (client->status_updater) {
719 return INSTPROXY_E_OP_IN_PROGRESS;
720 }
721
722 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
723
724 plist_t dict = plist_new_dict();
725 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
726 plist_dict_insert_item(dict, "Command", plist_new_string("RemoveArchive"));
727
728 instproxy_lock(client);
729 res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
730 instproxy_unlock(client);
731
732 plist_free(dict);
733
734 if (res != INSTPROXY_E_SUCCESS) {
735 debug_info("could not send plist, error %d", res);
736 return res;
737 }
738 return instproxy_create_status_updater(client, status_cb, "RemoveArchive");
739}
740