summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/installation_proxy.c830
-rw-r--r--src/installation_proxy.h5
2 files changed, 594 insertions, 241 deletions
diff --git a/src/installation_proxy.c b/src/installation_proxy.c
index 3d9d314..b7326dc 100644
--- a/src/installation_proxy.c
+++ b/src/installation_proxy.c
@@ -2,8 +2,8 @@
2 * installation_proxy.c 2 * installation_proxy.c
3 * com.apple.mobile.installation_proxy service implementation. 3 * com.apple.mobile.installation_proxy service implementation.
4 * 4 *
5 * Copyright (c) 2013 Martin Szulecki All Rights Reserved. 5 * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. 6 * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved.
7 * 7 *
8 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,7 @@
22 22
23#include <string.h> 23#include <string.h>
24#include <stdlib.h> 24#include <stdlib.h>
25#include <inttypes.h>
25#include <unistd.h> 26#include <unistd.h>
26#include <plist/plist.h> 27#include <plist/plist.h>
27 28
@@ -29,14 +30,156 @@
29#include "property_list_service.h" 30#include "property_list_service.h"
30#include "common/debug.h" 31#include "common/debug.h"
31 32
33typedef enum {
34 INSTPROXY_COMMAND_TYPE_ASYNC,
35 INSTPROXY_COMMAND_TYPE_SYNC
36} instproxy_command_type_t;
37
32struct instproxy_status_data { 38struct instproxy_status_data {
33 instproxy_client_t client; 39 instproxy_client_t client;
40 plist_t command;
34 instproxy_status_cb_t cbfunc; 41 instproxy_status_cb_t cbfunc;
35 char *operation;
36 void *user_data; 42 void *user_data;
37}; 43};
38 44
39/** 45/**
46 * Converts an error string identifier to a instproxy_error_t value.
47 * Used internally to get correct error codes from a response.
48 *
49 * @param name The error name to convert.
50 * @param error_detail Pointer to store error detail text if available. The
51 * caller is reponsible for freeing the allocated buffer after use. If NULL
52 * is passed no error detail will be returned.
53 *
54 * @return A matching instproxy_error_t error code or
55 * INSTPROXY_E_UNKNOWN_ERROR otherwise.
56 */
57static instproxy_error_t instproxy_strtoerr(const char* name)
58{
59 instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR;
60
61 if (strcmp(name, "AlreadyArchived") == 0) {
62 err = INSTPROXY_E_ALREADY_ARCHIVED;
63 } else if (strcmp(name, "APIInternalError") == 0) {
64 err = INSTPROXY_E_API_INTERNAL_ERROR;
65 } else if (strcmp(name, "ApplicationAlreadyInstalled") == 0) {
66 err = INSTPROXY_E_APPLICATION_ALREADY_INSTALLED;
67 } else if (strcmp(name, "ApplicationMoveFailed") == 0) {
68 err = INSTPROXY_E_APPLICATION_MOVE_FAILED;
69 } else if (strcmp(name, "ApplicationSINFCaptureFailed") == 0) {
70 err = INSTPROXY_E_APPLICATION_SINF_CAPTURE_FAILED;
71 } else if (strcmp(name, "ApplicationSandboxFailed") == 0) {
72 err = INSTPROXY_E_APPLICATION_SANDBOX_FAILED;
73 } else if (strcmp(name, "ApplicationVerificationFailed") == 0) {
74 err = INSTPROXY_E_APPLICATION_VERIFICATION_FAILED;
75 } else if (strcmp(name, "ArchiveDestructionFailed") == 0) {
76 err = INSTPROXY_E_ARCHIVE_DESTRUCTION_FAILED;
77 } else if (strcmp(name, "BundleVerificationFailed") == 0) {
78 err = INSTPROXY_E_BUNDLE_VERIFICATION_FAILED;
79 } else if (strcmp(name, "CarrierBundleCopyFailed") == 0) {
80 err = INSTPROXY_E_CARRIER_BUNDLE_COPY_FAILED;
81 } else if (strcmp(name, "CarrierBundleDirectoryCreationFailed") == 0) {
82 err = INSTPROXY_E_CARRIER_BUNDLE_DIRECTORY_CREATION_FAILED;
83 } else if (strcmp(name, "CarrierBundleMissingSupportedSIMs") == 0) {
84 err = INSTPROXY_E_CARRIER_BUNDLE_MISSING_SUPPORTED_SIMS;
85 } else if (strcmp(name, "CommCenterNotificationFailed") == 0) {
86 err = INSTPROXY_E_COMM_CENTER_NOTIFICATION_FAILED;
87 } else if (strcmp(name, "ContainerCreationFailed") == 0) {
88 err = INSTPROXY_E_CONTAINER_CREATION_FAILED;
89 } else if (strcmp(name, "ContainerP0wnFailed") == 0) {
90 err = INSTPROXY_E_CONTAINER_P0WN_FAILED;
91 } else if (strcmp(name, "ContainerRemovalFailed") == 0) {
92 err = INSTPROXY_E_CONTAINER_REMOVAL_FAILED;
93 } else if (strcmp(name, "EmbeddedProfileInstallFailed") == 0) {
94 err = INSTPROXY_E_EMBEDDED_PROFILE_INSTALL_FAILED;
95 } else if (strcmp(name, "ExecutableTwiddleFailed") == 0) {
96 err = INSTPROXY_E_EXECUTABLE_TWIDDLE_FAILED;
97 } else if (strcmp(name, "ExistenceCheckFailed") == 0) {
98 err = INSTPROXY_E_EXISTENCE_CHECK_FAILED;
99 } else if (strcmp(name, "InstallMapUpdateFailed") == 0) {
100 err = INSTPROXY_E_INSTALL_MAP_UPDATE_FAILED;
101 } else if (strcmp(name, "ManifestCaptureFailed") == 0) {
102 err = INSTPROXY_E_MANIFEST_CAPTURE_FAILED;
103 } else if (strcmp(name, "MapGenerationFailed") == 0) {
104 err = INSTPROXY_E_MAP_GENERATION_FAILED;
105 } else if (strcmp(name, "MissingBundleExecutable") == 0) {
106 err = INSTPROXY_E_MISSING_BUNDLE_EXECUTABLE;
107 } else if (strcmp(name, "MissingBundleIdentifier") == 0) {
108 err = INSTPROXY_E_MISSING_BUNDLE_IDENTIFIER;
109 } else if (strcmp(name, "MissingBundlePath") == 0) {
110 err = INSTPROXY_E_MISSING_BUNDLE_PATH;
111 } else if (strcmp(name, "MissingContainer") == 0) {
112 err = INSTPROXY_E_MISSING_CONTAINER;
113 } else if (strcmp(name, "NotificationFailed") == 0) {
114 err = INSTPROXY_E_NOTIFICATION_FAILED;
115 } else if (strcmp(name, "PackageExtractionFailed") == 0) {
116 err = INSTPROXY_E_PACKAGE_EXTRACTION_FAILED;
117 } else if (strcmp(name, "PackageInspectionFailed") == 0) {
118 err = INSTPROXY_E_PACKAGE_INSPECTION_FAILED;
119 } else if (strcmp(name, "PackageMoveFailed") == 0) {
120 err = INSTPROXY_E_PACKAGE_MOVE_FAILED;
121 } else if (strcmp(name, "PathConversionFailed") == 0) {
122 err = INSTPROXY_E_PATH_CONVERSION_FAILED;
123 } else if (strcmp(name, "RestoreContainerFailed") == 0) {
124 err = INSTPROXY_E_RESTORE_CONTAINER_FAILED;
125 } else if (strcmp(name, "SeatbeltProfileRemovalFailed") == 0) {
126 err = INSTPROXY_E_SEATBELT_PROFILE_REMOVAL_FAILED;
127 } else if (strcmp(name, "StageCreationFailed") == 0) {
128 err = INSTPROXY_E_STAGE_CREATION_FAILED;
129 } else if (strcmp(name, "SymlinkFailed") == 0) {
130 err = INSTPROXY_E_SYMLINK_FAILED;
131 } else if (strcmp(name, "UnknownCommand") == 0) {
132 err = INSTPROXY_E_UNKNOWN_COMMAND;
133 } else if (strcmp(name, "iTunesArtworkCaptureFailed") == 0) {
134 err = INSTPROXY_E_ITUNES_ARTWORK_CAPTURE_FAILED;
135 } else if (strcmp(name, "iTunesMetadataCaptureFailed") == 0) {
136 err = INSTPROXY_E_ITUNES_METADATA_CAPTURE_FAILED;
137 } else if (strcmp(name, "DeviceOSVersionTooLow") == 0) {
138 err = INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW;
139 } else if (strcmp(name, "DeviceFamilyNotSupported") == 0) {
140 err = INSTPROXY_E_DEVICE_FAMILY_NOT_SUPPORTED;
141 } else if (strcmp(name, "PackagePatchFailed") == 0) {
142 err = INSTPROXY_E_PACKAGE_PATCH_FAILED;
143 } else if (strcmp(name, "IncorrectArchitecture") == 0) {
144 err = INSTPROXY_E_INCORRECT_ARCHITECTURE;
145 } else if (strcmp(name, "PluginCopyFailed") == 0) {
146 err = INSTPROXY_E_PLUGIN_COPY_FAILED;
147 } else if (strcmp(name, "BreadcrumbFailed") == 0) {
148 err = INSTPROXY_E_BREADCRUMB_FAILED;
149 } else if (strcmp(name, "BreadcrumbUnlockFailed") == 0) {
150 err = INSTPROXY_E_BREADCRUMB_UNLOCK_FAILED;
151 } else if (strcmp(name, "GeoJSONCaptureFailed") == 0) {
152 err = INSTPROXY_E_GEOJSON_CAPTURE_FAILED;
153 } else if (strcmp(name, "NewsstandArtworkCaptureFailed") == 0) {
154 err = INSTPROXY_E_NEWSSTAND_ARTWORK_CAPTURE_FAILED;
155 } else if (strcmp(name, "MissingCommand") == 0) {
156 err = INSTPROXY_E_MISSING_COMMAND;
157 } else if (strcmp(name, "NotEntitled") == 0) {
158 err = INSTPROXY_E_NOT_ENTITLED;
159 } else if (strcmp(name, "MissingPackagePath") == 0) {
160 err = INSTPROXY_E_MISSING_PACKAGE_PATH;
161 } else if (strcmp(name, "MissingContainerPath") == 0) {
162 err = INSTPROXY_E_MISSING_CONTAINER_PATH;
163 } else if (strcmp(name, "MissingApplicationIdentifier") == 0) {
164 err = INSTPROXY_E_MISSING_APPLICATION_IDENTIFIER;
165 } else if (strcmp(name, "MissingAttributeValue") == 0) {
166 err = INSTPROXY_E_MISSING_ATTRIBUTE_VALUE;
167 } else if (strcmp(name, "LookupFailed") == 0) {
168 err = INSTPROXY_E_LOOKUP_FAILED;
169 } else if (strcmp(name, "DictCreationFailed") == 0) {
170 err = INSTPROXY_E_DICT_CREATION_FAILED;
171 } else if (strcmp(name, "InstallProhibited") == 0) {
172 err = INSTPROXY_E_INSTALL_PROHIBITED;
173 } else if (strcmp(name, "UninstallProhibited") == 0) {
174 err = INSTPROXY_E_UNINSTALL_PROHIBITED;
175 } else if (strcmp(name, "MissingBundleVersion") == 0) {
176 err = INSTPROXY_E_MISSING_BUNDLE_VERSION;
177 }
178
179 return err;
180}
181
182/**
40 * Locks an installation_proxy client, used for thread safety. 183 * Locks an installation_proxy client, used for thread safety.
41 * 184 *
42 * @param client The installation_proxy client to lock 185 * @param client The installation_proxy client to lock
@@ -97,7 +240,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_new(idevice_t device, lo
97 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); 240 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private));
98 client_loc->parent = plistclient; 241 client_loc->parent = plistclient;
99 mutex_init(&client_loc->mutex); 242 mutex_init(&client_loc->mutex);
100 client_loc->status_updater = (thread_t)NULL; 243 client_loc->receive_status_thread = (thread_t)NULL;
101 244
102 *client = client_loc; 245 *client = client_loc;
103 return INSTPROXY_E_SUCCESS; 246 return INSTPROXY_E_SUCCESS;
@@ -117,11 +260,11 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_free(instproxy_client_t
117 260
118 property_list_service_client_free(client->parent); 261 property_list_service_client_free(client->parent);
119 client->parent = NULL; 262 client->parent = NULL;
120 if (client->status_updater) { 263 if (client->receive_status_thread) {
121 debug_info("joining status_updater"); 264 debug_info("joining receive_status_thread");
122 thread_join(client->status_updater); 265 thread_join(client->receive_status_thread);
123 thread_free(client->status_updater); 266 thread_free(client->receive_status_thread);
124 client->status_updater = (thread_t)NULL; 267 client->receive_status_thread = (thread_t)NULL;
125 } 268 }
126 mutex_destroy(&client->mutex); 269 mutex_destroy(&client->mutex);
127 free(client); 270 free(client);
@@ -142,93 +285,18 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_free(instproxy_client_t
142 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if 285 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
143 * an error occured. 286 * an error occured.
144 */ 287 */
145static instproxy_error_t instproxy_send_command(instproxy_client_t client, const char *command, plist_t client_options, const char *appid, const char *package_path) 288static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command)
146{
147 if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT)))
148 return INSTPROXY_E_INVALID_ARG;
149
150 plist_t dict = plist_new_dict();
151 if (appid) {
152 plist_dict_set_item(dict, "ApplicationIdentifier", plist_new_string(appid));
153 }
154 if (client_options && (plist_dict_get_size(client_options) > 0)) {
155 plist_dict_set_item(dict, "ClientOptions", plist_copy(client_options));
156 }
157 plist_dict_set_item(dict, "Command", plist_new_string(command));
158 if (package_path) {
159 plist_dict_set_item(dict, "PackagePath", plist_new_string(package_path));
160 }
161
162 instproxy_error_t err = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
163 plist_free(dict);
164 return err;
165}
166
167LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
168{ 289{
169 if (!client || !client->parent || !result) 290 if (!client || !command)
170 return INSTPROXY_E_INVALID_ARG; 291 return INSTPROXY_E_INVALID_ARG;
171 292
172 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 293 instproxy_error_t res = instproxy_error(property_list_service_send_xml_plist(client->parent, command));
173 294
174 instproxy_lock(client);
175 res = instproxy_send_command(client, "Browse", client_options, NULL, NULL);
176 if (res != INSTPROXY_E_SUCCESS) { 295 if (res != INSTPROXY_E_SUCCESS) {
177 debug_info("could not send plist"); 296 debug_info("could not send command plist, error %d", res);
178 goto leave_unlock; 297 return res;
179 }
180
181 int browsing = 0;
182 plist_t apps_array = plist_new_array();
183 plist_t dict = NULL;
184
185 do {
186 browsing = 0;
187 dict = NULL;
188 res = instproxy_error(property_list_service_receive_plist(client->parent, &dict));
189 if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) {
190 break;
191 }
192 if (dict) {
193 uint64_t i;
194 uint64_t current_amount = 0;
195 char *status = NULL;
196 plist_t camount = plist_dict_get_item(dict, "CurrentAmount");
197 plist_t pstatus = plist_dict_get_item(dict, "Status");
198 if (camount) {
199 plist_get_uint_val(camount, &current_amount);
200 }
201 if (current_amount > 0) {
202 plist_t current_list = plist_dict_get_item(dict, "CurrentList");
203 for (i = 0; current_list && (i < current_amount); i++) {
204 plist_t item = plist_array_get_item(current_list, i);
205 plist_array_append_item(apps_array, plist_copy(item));
206 }
207 }
208 if (pstatus) {
209 plist_get_string_val(pstatus, &status);
210 }
211 if (status) {
212 if (!strcmp(status, "BrowsingApplications")) {
213 browsing = 1;
214 } else if (!strcmp(status, "Complete")) {
215 debug_info("Browsing applications completed");
216 res = INSTPROXY_E_SUCCESS;
217 }
218 free(status);
219 }
220 plist_free(dict);
221 }
222 } while (browsing);
223
224 if (res == INSTPROXY_E_SUCCESS) {
225 *result = apps_array;
226 } else {
227 plist_free(apps_array);
228 } 298 }
229 299
230leave_unlock:
231 instproxy_unlock(client);
232 return res; 300 return res;
233} 301}
234 302
@@ -241,78 +309,102 @@ leave_unlock:
241 * 309 *
242 * @param client The connected installation proxy client 310 * @param client The connected installation proxy client
243 * @param status_cb Pointer to a callback function or NULL 311 * @param status_cb Pointer to a callback function or NULL
244 * @param operation Operation name. Will be passed to the callback function 312 * @param command Operation specificiation in plist. Will be passed to the
245 * in async mode or shown in debug messages in sync mode. 313 * status_cb callback.
246 * @param user_data Callback data passed to status_cb. 314 * @param user_data Callback data passed to status_cb.
247 */ 315 */
248static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) 316static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client, plist_t command, instproxy_status_cb_t status_cb, void *user_data)
249{ 317{
250 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 318 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
251 int ok = 1; 319 int complete = 0;
252 plist_t dict = NULL; 320 plist_t node = NULL;
321 char* command_name = NULL;
322 char* status_name = NULL;
323 char* error_name = NULL;
324 char* error_description = NULL;
325 uint64_t error_code = 0;
326#ifndef STRIP_DEBUG_CODE
327 int percent_complete = 0;
328#endif
329
330 instproxy_command_get_name(command, &command_name);
253 331
254 do { 332 do {
333 /* receive status response */
255 instproxy_lock(client); 334 instproxy_lock(client);
256 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 1000)); 335 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
257 instproxy_unlock(client); 336 instproxy_unlock(client);
337
338 /* break out if we have a communication problem */
258 if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) { 339 if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) {
259 debug_info("could not receive plist, error %d", res); 340 debug_info("could not receive plist, error %d", res);
260 break; 341 break;
261 } 342 }
262 if (dict) { 343
263 /* invoke callback function */ 344 /* parse status response */
264 if (status_cb) { 345 if (node) {
265 status_cb(operation, dict, user_data); 346 /* check status for possible error to allow reporting it and aborting it gracefully */
347 res = instproxy_status_get_error(node, &error_name, &error_description, &error_code);
348 if (res != INSTPROXY_E_SUCCESS) {
349 debug_info("command: %s, error %d, code 0x%08"PRIx64", name: %s, description: \"%s\"", command_name, res, error_code, error_name, error_description ? error_description: "N/A");
350 complete = 1;
351 }
352
353 if (error_name) {
354 free(error_name);
355 error_name = NULL;
356 }
357
358 if (error_description) {
359 free(error_description);
360 error_description = NULL;
266 } 361 }
267 /* check for 'Error', so we can abort cleanly */ 362
268 plist_t err = plist_dict_get_item(dict, "Error"); 363 /* check status from response */
269 if (err) { 364 instproxy_status_get_name(node, &status_name);
365 if (!status_name) {
366 debug_info("failed to retrieve name from status response with error %d.", res);
367 complete = 1;
368 }
369
370 if (status_name) {
371 if (!strcmp(status_name, "Complete")) {
372 complete = 1;
373 } else {
374 res = INSTPROXY_E_OP_IN_PROGRESS;
375 }
376
270#ifndef STRIP_DEBUG_CODE 377#ifndef STRIP_DEBUG_CODE
271 char *err_msg = NULL; 378 percent_complete = -1;
272 plist_get_string_val(err, &err_msg); 379 instproxy_status_get_percent_complete(node, &percent_complete);
273 if (err_msg) { 380 if (percent_complete >= 0) {
274 debug_info("(%s): ERROR: %s", operation, err_msg); 381 debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete);
275 free(err_msg); 382 } else {
383 debug_info("command: %s, status: %s", command_name, status_name);
276 } 384 }
277#endif 385#endif
278 ok = 0; 386 free(status_name);
279 res = INSTPROXY_E_OP_FAILED; 387 status_name = NULL;
280 } 388 }
281 /* get 'Status' */ 389
282 plist_t status = plist_dict_get_item(dict, "Status"); 390 /* invoke status callback function */
283 if (status) { 391 if (status_cb) {
284 char *status_msg = NULL; 392 status_cb(command, node, user_data);
285 plist_get_string_val(status, &status_msg);
286 if (status_msg) {
287 if (!strcmp(status_msg, "Complete")) {
288 ok = 0;
289 res = INSTPROXY_E_SUCCESS;
290 }
291#ifndef STRIP_DEBUG_CODE
292 plist_t npercent = plist_dict_get_item(dict, "PercentComplete");
293 if (npercent) {
294 uint64_t val = 0;
295 int percent;
296 plist_get_uint_val(npercent, &val);
297 percent = val;
298 debug_info("(%s): %s (%d%%)", operation, status_msg, percent);
299 } else {
300 debug_info("(%s): %s", operation, status_msg);
301 }
302#endif
303 free(status_msg);
304 }
305 } 393 }
306 plist_free(dict); 394
307 dict = NULL; 395 plist_free(node);
396 node = NULL;
308 } 397 }
309 } while (ok && client->parent); 398 } while (!complete && client->parent);
399
400 if (command_name)
401 free(command_name);
310 402
311 return res; 403 return res;
312} 404}
313 405
314/** 406/**
315 * Internally used status updater thread function that will call the specified 407 * Internally used "receive status" thread function that will call the specified
316 * callback function when status update messages (or error messages) are 408 * callback function when status update messages (or error messages) are
317 * received. 409 * received.
318 * 410 *
@@ -321,23 +413,27 @@ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client,
321 * 413 *
322 * @return Always NULL. 414 * @return Always NULL.
323 */ 415 */
324static void* instproxy_status_updater(void* arg) 416static void* instproxy_receive_status_loop_thread(void* arg)
325{ 417{
326 struct instproxy_status_data *data = (struct instproxy_status_data*)arg; 418 struct instproxy_status_data *data = (struct instproxy_status_data*)arg;
327 419
328 /* run until the operation is complete or an error occurs */ 420 /* run until the command is complete or an error occurs */
329 (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation, data->user_data); 421 (void)instproxy_receive_status_loop(data->client, data->command, data->cbfunc, data->user_data);
330 422
331 /* cleanup */ 423 /* cleanup */
332 instproxy_lock(data->client); 424 instproxy_lock(data->client);
425
333 debug_info("done, cleaning up."); 426 debug_info("done, cleaning up.");
334 if (data->operation) { 427
335 free(data->operation); 428 if (data->command) {
429 plist_free(data->command);
336 } 430 }
337 if (data->client->status_updater) { 431
338 thread_free(data->client->status_updater); 432 if (data->client->receive_status_thread) {
339 data->client->status_updater = (thread_t)NULL; 433 thread_free(data->client->receive_status_thread);
434 data->client->receive_status_thread = (thread_t)NULL;
340 } 435 }
436
341 instproxy_unlock(data->client); 437 instproxy_unlock(data->client);
342 free(data); 438 free(data);
343 439
@@ -345,198 +441,454 @@ static void* instproxy_status_updater(void* arg)
345} 441}
346 442
347/** 443/**
348 * Internally used helper function that creates a status updater thread which 444 * Internally used helper function that creates a "receive status" thread which
349 * will call the passed callback function when status updates occur. 445 * will call the passed callback function when a status is received.
350 * If status_cb is NULL no thread will be created, but the operation will 446 *
351 * run synchronously until it completes or an error occurs. 447 * If async is 0 no thread will be created and the command will run
448 * synchronously until it completes or an error occurs.
352 * 449 *
353 * @param client The connected installation proxy client 450 * @param client The connected installation proxy client
354 * @param status_cb Pointer to a callback function or NULL 451 * @param command Operation name. Will be passed to the callback function
355 * @param operation Operation name. Will be passed to the callback function
356 * in async mode or shown in debug messages in sync mode. 452 * in async mode or shown in debug messages in sync mode.
453 * @param async A boolean indicating if receive loop should be run
454 * asynchronously or block.
455 * @param status_cb Pointer to a callback function or NULL.
357 * @param user_data Callback data passed to status_cb. 456 * @param user_data Callback data passed to status_cb.
358 * 457 *
359 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or 458 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or
360 * when the operation completed successfully (sync). 459 * when the command completed successfully (sync).
361 * An INSTPROXY_E_* error value is returned if an error occured. 460 * An INSTPROXY_E_* error value is returned if an error occured.
362 */ 461 */
363static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) 462static instproxy_error_t instproxy_receive_status_loop_with_callback(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data)
364{ 463{
464 if (!client || !client->parent || !command) {
465 return INSTPROXY_E_INVALID_ARG;
466 }
467
468 if (client->receive_status_thread) {
469 return INSTPROXY_E_OP_IN_PROGRESS;
470 }
471
365 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 472 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
366 if (status_cb) { 473 if (async == INSTPROXY_COMMAND_TYPE_ASYNC) {
367 /* async mode */ 474 /* async mode */
368 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); 475 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data));
369 if (data) { 476 if (data) {
370 data->client = client; 477 data->client = client;
478 data->command = plist_copy(command);
371 data->cbfunc = status_cb; 479 data->cbfunc = status_cb;
372 data->operation = strdup(operation);
373 data->user_data = user_data; 480 data->user_data = user_data;
374 481
375 if (thread_new(&client->status_updater, instproxy_status_updater, data) == 0) { 482 if (thread_new(&client->receive_status_thread, instproxy_receive_status_loop_thread, data) == 0) {
376 res = INSTPROXY_E_SUCCESS; 483 res = INSTPROXY_E_SUCCESS;
377 } 484 }
378 } 485 }
379 } else { 486 } else {
380 /* sync mode */ 487 /* sync mode as a fallback */
381 res = instproxy_perform_operation(client, NULL, operation, NULL); 488 res = instproxy_receive_status_loop(client, command, status_cb, user_data);
382 } 489 }
490
383 return res; 491 return res;
384} 492}
385 493
386/** 494/**
387 * Internal function used by instproxy_install and instproxy_upgrade. 495 * Internal core function to send a command and process the response.
388 * 496 *
389 * @param client The connected installation_proxy client 497 * @param client The connected installation_proxy client
390 * @param pkg_path Path of the installation package (inside the AFC jail) 498 * @param command The command specification dictionary.
391 * @param client_options The client options to use, as PLIST_DICT, or NULL. 499 * @param async A boolean indicating whether the receive loop should be run
392 * @param status_cb Callback function for progress and status information. If 500 * asynchronously or block until completing the command.
393 * NULL is passed, this function will run synchronously. 501 * @param status_cb Callback function to call if a command status is received.
394 * @param command The command to execute.
395 * @param user_data Callback data passed to status_cb. 502 * @param user_data Callback data passed to status_cb.
396 * 503 *
397 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if 504 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
398 * an error occured. 505 * an error occured.
399 */ 506 */
400static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, const char *command, void *user_data) 507static instproxy_error_t instproxy_perform_command(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data)
401{ 508{
402 if (!client || !client->parent || !pkg_path) { 509 if (!client || !client->parent || !command) {
403 return INSTPROXY_E_INVALID_ARG; 510 return INSTPROXY_E_INVALID_ARG;
404 } 511 }
405 if (client->status_updater) { 512
513 if (client->receive_status_thread) {
406 return INSTPROXY_E_OP_IN_PROGRESS; 514 return INSTPROXY_E_OP_IN_PROGRESS;
407 } 515 }
408 516
517 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
518
519 /* send command */
409 instproxy_lock(client); 520 instproxy_lock(client);
410 instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path); 521 res = instproxy_send_command(client, command);
411 instproxy_unlock(client); 522 instproxy_unlock(client);
412 523
413 if (res != INSTPROXY_E_SUCCESS) { 524 /* loop until status or error is received */
414 debug_info("could not send plist, error %d", res); 525 res = instproxy_receive_status_loop_with_callback(client, command, async, status_cb, user_data);
415 return res;
416 }
417 526
418 return instproxy_create_status_updater(client, status_cb, command, user_data); 527 return res;
419} 528}
420 529
421LIBIMOBILEDEVICE_API instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 530LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse_with_callback(instproxy_client_t client, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
422{ 531{
423 return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install", user_data); 532 if (!client || !client->parent || !status_cb)
533 return INSTPROXY_E_INVALID_ARG;
534
535 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
536
537 plist_t command = plist_new_dict();
538 plist_dict_set_item(command, "Command", plist_new_string("Browse"));
539 if (client_options)
540 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
541
542 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, (void*)user_data);
543
544 plist_free(command);
545
546 return res;
424} 547}
425 548
426LIBIMOBILEDEVICE_API instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 549static void instproxy_append_current_list_to_result_cb(plist_t command, plist_t status, void *user_data)
427{ 550{
428 return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade", user_data); 551 plist_t *result_array = (plist_t*)user_data;
552 uint64_t current_amount = 0;
553 plist_t current_list = NULL;
554 uint64_t i;
555
556 instproxy_status_get_current_list(status, NULL, NULL, &current_amount, &current_list);
557
558 debug_info("current_amount: %d", current_amount);
559
560 if (current_amount > 0) {
561 for (i = 0; current_list && (i < current_amount); i++) {
562 plist_t item = plist_array_get_item(current_list, i);
563 plist_array_append_item(*result_array, plist_copy(item));
564 }
565 }
566
567 if (current_list)
568 plist_free(current_list);
429} 569}
430 570
431LIBIMOBILEDEVICE_API instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 571LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
432{ 572{
433 if (!client || !client->parent || !appid) { 573 if (!client || !client->parent || !result)
434 return INSTPROXY_E_INVALID_ARG; 574 return INSTPROXY_E_INVALID_ARG;
435 }
436
437 if (client->status_updater) {
438 return INSTPROXY_E_OP_IN_PROGRESS;
439 }
440 575
441 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 576 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
442 577
443 instproxy_lock(client); 578 plist_t result_array = plist_new_array();
444 res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
445 instproxy_unlock(client);
446 579
447 if (res != INSTPROXY_E_SUCCESS) { 580 plist_t command = plist_new_dict();
448 debug_info("could not send plist, error %d", res); 581 plist_dict_set_item(command, "Command", plist_new_string("Browse"));
449 return res; 582 if (client_options)
583 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
584
585 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_append_current_list_to_result_cb, (void*)&result_array);
586
587 if (res == INSTPROXY_E_SUCCESS) {
588 *result = result_array;
589 } else {
590 plist_free(result_array);
450 } 591 }
451 592
452 return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data); 593 plist_free(command);
594
595 return res;
453} 596}
454 597
455LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) 598static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, void *user_data)
599{
600 plist_t* result = (plist_t*)user_data;
601
602 plist_t node = plist_dict_get_item(status, "LookupResult");
603 if (node) {
604 *result = plist_copy(node);
605 }
606}
607
608LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup(instproxy_client_t client, plist_t appids, plist_t client_options, plist_t *result)
456{ 609{
457 if (!client || !client->parent || !result) 610 if (!client || !client->parent || !result)
458 return INSTPROXY_E_INVALID_ARG; 611 return INSTPROXY_E_INVALID_ARG;
459 612
460 instproxy_lock(client); 613 if (appids && (plist_get_node_type(appids) != PLIST_ARRAY && plist_get_node_type(appids) != PLIST_STRING))
461 instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL); 614 return INSTPROXY_E_INVALID_ARG;
462 615
463 if (res != INSTPROXY_E_SUCCESS) { 616 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
464 debug_info("could not send plist, error %d", res); 617
465 goto leave_unlock; 618 plist_t lookup_result = NULL;
619
620 plist_t command = plist_new_dict();
621 plist_dict_set_item(command, "Command", plist_new_string("Lookup"));
622 if (appids) {
623 plist_dict_set_item(client_options, "BundleIDs", plist_copy(appids));
466 } 624 }
625 if (client_options)
626 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
467 627
468 res = instproxy_error(property_list_service_receive_plist(client->parent, result)); 628 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
469 if (res != INSTPROXY_E_SUCCESS) { 629
470 debug_info("could not receive plist, error %d", res); 630 if (res == INSTPROXY_E_SUCCESS) {
471 goto leave_unlock; 631 *result = lookup_result;
632 } else {
633 plist_free(lookup_result);
472 } 634 }
473 635
474 res = INSTPROXY_E_SUCCESS; 636 plist_free(command);
637
638 return res;
639}
640
641LIBIMOBILEDEVICE_API instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
642{
643 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
644
645 plist_t command = plist_new_dict();
646 plist_dict_set_item(command, "Command", plist_new_string("Install"));
647 if (client_options)
648 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
649 plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
650
651 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
652
653 plist_free(command);
654
655 return res;
656}
657
658LIBIMOBILEDEVICE_API instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
659{
660 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
661
662 plist_t command = plist_new_dict();
663 plist_dict_set_item(command, "Command", plist_new_string("Upgrade"));
664 if (client_options)
665 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
666 plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
667
668 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
669
670 plist_free(command);
671
672 return res;
673}
674
675LIBIMOBILEDEVICE_API instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
676{
677 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
678
679 plist_t command = plist_new_dict();
680 plist_dict_set_item(command, "Command", plist_new_string("Uninstall"));
681 if (client_options)
682 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
683 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
684
685 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
686
687 plist_free(command);
688
689 return res;
690}
691
692LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
693{
694 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
695
696 plist_t command = plist_new_dict();
697 plist_dict_set_item(command, "Command", plist_new_string("LookupArchives"));
698 if (client_options)
699 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
700
701 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)result);
702
703 plist_free(command);
475 704
476leave_unlock:
477 instproxy_unlock(client);
478 return res; 705 return res;
479} 706}
480 707
481LIBIMOBILEDEVICE_API instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 708LIBIMOBILEDEVICE_API instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
482{ 709{
483 if (!client || !client->parent || !appid) 710 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
711
712 plist_t command = plist_new_dict();
713 plist_dict_set_item(command, "Command", plist_new_string("Archive"));
714 if (client_options)
715 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
716 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
717
718 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
719
720 plist_free(command);
721
722 return res;
723}
724
725LIBIMOBILEDEVICE_API instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
726{
727 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
728
729 plist_t command = plist_new_dict();
730 plist_dict_set_item(command, "Command", plist_new_string("Restore"));
731 if (client_options)
732 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
733 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
734
735 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
736
737 plist_free(command);
738
739 return res;
740}
741
742LIBIMOBILEDEVICE_API instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
743{
744 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
745
746 plist_t command = plist_new_dict();
747 plist_dict_set_item(command, "Command", plist_new_string("RemoveArchive"));
748 if (client_options)
749 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
750 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
751
752 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
753
754 plist_free(command);
755
756 return res;
757}
758
759LIBIMOBILEDEVICE_API instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, plist_t capabilities, plist_t client_options, plist_t *result)
760{
761 if (!capabilities || (plist_get_node_type(capabilities) != PLIST_ARRAY && plist_get_node_type(capabilities) != PLIST_DICT))
484 return INSTPROXY_E_INVALID_ARG; 762 return INSTPROXY_E_INVALID_ARG;
485 763
486 if (client->status_updater) { 764 plist_t lookup_result = NULL;
487 return INSTPROXY_E_OP_IN_PROGRESS;
488 }
489 765
490 instproxy_lock(client); 766 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
491 instproxy_error_t res = instproxy_send_command(client, "Archive", client_options, appid, NULL);
492 instproxy_unlock(client);
493 767
494 if (res != INSTPROXY_E_SUCCESS) { 768 plist_t command = plist_new_dict();
495 debug_info("could not send plist, error %d", res); 769 plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch"));
496 return res; 770 if (client_options)
771 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
772 plist_dict_set_item(command, "Capabilities", plist_copy(capabilities));
773
774 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
775
776 if (res == INSTPROXY_E_SUCCESS) {
777 *result = lookup_result;
778 } else {
779 plist_free(lookup_result);
497 } 780 }
498 return instproxy_create_status_updater(client, status_cb, "Archive", user_data); 781
782 plist_free(command);
783
784 return res;
499} 785}
500 786
501LIBIMOBILEDEVICE_API instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 787LIBIMOBILEDEVICE_API instproxy_error_t instproxy_status_get_error(plist_t status, char **name, char** description, uint64_t* code)
502{ 788{
503 if (!client || !client->parent || !appid) 789 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
790
791 if (!status || !name)
504 return INSTPROXY_E_INVALID_ARG; 792 return INSTPROXY_E_INVALID_ARG;
505 793
506 if (client->status_updater) { 794 plist_t node = plist_dict_get_item(status, "Error");
507 return INSTPROXY_E_OP_IN_PROGRESS; 795 if (node) {
796 plist_get_string_val(node, name);
797 } else {
798 /* no error here */
799 res = INSTPROXY_E_SUCCESS;
508 } 800 }
509 801
510 instproxy_lock(client); 802 if (code != NULL) {
511 instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL); 803 *code = 0;
512 instproxy_unlock(client); 804 node = plist_dict_get_item(status, "ErrorDetail");
805 if (node) {
806 plist_get_uint_val(node, code);
807 *code &= 0xffffffff;
808 }
809 }
513 810
514 if (res != INSTPROXY_E_SUCCESS) { 811 if (description != NULL) {
515 debug_info("could not send plist, error %d", res); 812 node = plist_dict_get_item(status, "ErrorDescription");
516 return res; 813 if (node) {
814 plist_get_string_val(node, description);
815 }
517 } 816 }
518 return instproxy_create_status_updater(client, status_cb, "Restore", user_data); 817
818 if (*name) {
819 res = instproxy_strtoerr(*name);
820 }
821
822 return res;
519} 823}
520 824
521LIBIMOBILEDEVICE_API instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 825LIBIMOBILEDEVICE_API void instproxy_status_get_name(plist_t status, char **name)
522{ 826{
523 if (!client || !client->parent || !appid) 827 *name = NULL;
524 return INSTPROXY_E_INVALID_ARG; 828 if (name) {
829 plist_t node = plist_dict_get_item(status, "Status");
830 if (node) {
831 plist_get_string_val(node, name);
832 }
833 }
834}
525 835
526 if (client->status_updater) { 836LIBIMOBILEDEVICE_API void instproxy_status_get_percent_complete(plist_t status, int *percent)
527 return INSTPROXY_E_OP_IN_PROGRESS; 837{
838 uint64_t val = 0;
839 if (percent) {
840 plist_t node = plist_dict_get_item(status, "PercentComplete");
841 if (node) {
842 plist_get_uint_val(node, &val);
843 *percent = val;
844 }
528 } 845 }
846}
529 847
530 /* send command */ 848LIBIMOBILEDEVICE_API void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list)
531 instproxy_lock(client); 849{
532 instproxy_error_t res = instproxy_send_command(client, "RemoveArchive", client_options, appid, NULL); 850 plist_t node = NULL;
533 instproxy_unlock(client); 851
852 if (status && plist_get_node_type(status) == PLIST_DICT) {
853 /* command specific logic: parse browsed list */
854 if (list != NULL) {
855 node = plist_dict_get_item(status, "CurrentList");
856 if (node) {
857 *current_amount = plist_array_get_size(node);
858 *list = plist_copy(node);
859 }
860 }
534 861
535 if (res != INSTPROXY_E_SUCCESS) { 862 if (total != NULL) {
536 debug_info("could not send plist, error %d", res); 863 node = plist_dict_get_item(status, "Total");
537 return res; 864 if (node) {
865 plist_get_uint_val(node, total);
866 }
867 }
868
869 if (current_amount != NULL) {
870 node = plist_dict_get_item(status, "CurrentAmount");
871 if (node) {
872 plist_get_uint_val(node, current_amount);
873 }
874 }
875
876 if (current_index != NULL) {
877 node = plist_dict_get_item(status, "CurrentIndex");
878 if (node) {
879 plist_get_uint_val(node, current_index);
880 }
881 }
882 }
883}
884
885LIBIMOBILEDEVICE_API void instproxy_command_get_name(plist_t command, char** name)
886{
887 *name = NULL;
888 plist_t node = plist_dict_get_item(command, "Command");
889 if (node) {
890 plist_get_string_val(node, name);
538 } 891 }
539 return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data);
540} 892}
541 893
542LIBIMOBILEDEVICE_API plist_t instproxy_client_options_new() 894LIBIMOBILEDEVICE_API plist_t instproxy_client_options_new()
diff --git a/src/installation_proxy.h b/src/installation_proxy.h
index 4f79c12..15dbb84 100644
--- a/src/installation_proxy.h
+++ b/src/installation_proxy.h
@@ -2,7 +2,8 @@
2 * installation_proxy.h 2 * installation_proxy.h
3 * com.apple.mobile.installation_proxy service header file. 3 * com.apple.mobile.installation_proxy service header file.
4 * 4 *
5 * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
@@ -29,7 +30,7 @@
29struct instproxy_client_private { 30struct instproxy_client_private {
30 property_list_service_client_t parent; 31 property_list_service_client_t parent;
31 mutex_t mutex; 32 mutex_t mutex;
32 thread_t status_updater; 33 thread_t receive_status_thread;
33}; 34};
34 35
35#endif 36#endif