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