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 @@
* installation_proxy.c
* com.apple.mobile.installation_proxy service implementation.
*
- * Copyright (c) 2013 Martin Szulecki All Rights Reserved.
- * Copyright (c) 2009 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
+ * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,7 @@
#include <string.h>
#include <stdlib.h>
+#include <inttypes.h>
#include <unistd.h>
#include <plist/plist.h>
@@ -29,14 +30,156 @@
#include "property_list_service.h"
#include "common/debug.h"
+typedef enum {
+ INSTPROXY_COMMAND_TYPE_ASYNC,
+ INSTPROXY_COMMAND_TYPE_SYNC
+} instproxy_command_type_t;
+
struct instproxy_status_data {
instproxy_client_t client;
+ plist_t command;
instproxy_status_cb_t cbfunc;
- char *operation;
void *user_data;
};
/**
+ * Converts an error string identifier to a instproxy_error_t value.
+ * Used internally to get correct error codes from a response.
+ *
+ * @param name The error name to convert.
+ * @param error_detail Pointer to store error detail text if available. The
+ * caller is reponsible for freeing the allocated buffer after use. If NULL
+ * is passed no error detail will be returned.
+ *
+ * @return A matching instproxy_error_t error code or
+ * INSTPROXY_E_UNKNOWN_ERROR otherwise.
+ */
+static instproxy_error_t instproxy_strtoerr(const char* name)
+{
+ instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR;
+
+ if (strcmp(name, "AlreadyArchived") == 0) {
+ err = INSTPROXY_E_ALREADY_ARCHIVED;
+ } else if (strcmp(name, "APIInternalError") == 0) {
+ err = INSTPROXY_E_API_INTERNAL_ERROR;
+ } else if (strcmp(name, "ApplicationAlreadyInstalled") == 0) {
+ err = INSTPROXY_E_APPLICATION_ALREADY_INSTALLED;
+ } else if (strcmp(name, "ApplicationMoveFailed") == 0) {
+ err = INSTPROXY_E_APPLICATION_MOVE_FAILED;
+ } else if (strcmp(name, "ApplicationSINFCaptureFailed") == 0) {
+ err = INSTPROXY_E_APPLICATION_SINF_CAPTURE_FAILED;
+ } else if (strcmp(name, "ApplicationSandboxFailed") == 0) {
+ err = INSTPROXY_E_APPLICATION_SANDBOX_FAILED;
+ } else if (strcmp(name, "ApplicationVerificationFailed") == 0) {
+ err = INSTPROXY_E_APPLICATION_VERIFICATION_FAILED;
+ } else if (strcmp(name, "ArchiveDestructionFailed") == 0) {
+ err = INSTPROXY_E_ARCHIVE_DESTRUCTION_FAILED;
+ } else if (strcmp(name, "BundleVerificationFailed") == 0) {
+ err = INSTPROXY_E_BUNDLE_VERIFICATION_FAILED;
+ } else if (strcmp(name, "CarrierBundleCopyFailed") == 0) {
+ err = INSTPROXY_E_CARRIER_BUNDLE_COPY_FAILED;
+ } else if (strcmp(name, "CarrierBundleDirectoryCreationFailed") == 0) {
+ err = INSTPROXY_E_CARRIER_BUNDLE_DIRECTORY_CREATION_FAILED;
+ } else if (strcmp(name, "CarrierBundleMissingSupportedSIMs") == 0) {
+ err = INSTPROXY_E_CARRIER_BUNDLE_MISSING_SUPPORTED_SIMS;
+ } else if (strcmp(name, "CommCenterNotificationFailed") == 0) {
+ err = INSTPROXY_E_COMM_CENTER_NOTIFICATION_FAILED;
+ } else if (strcmp(name, "ContainerCreationFailed") == 0) {
+ err = INSTPROXY_E_CONTAINER_CREATION_FAILED;
+ } else if (strcmp(name, "ContainerP0wnFailed") == 0) {
+ err = INSTPROXY_E_CONTAINER_P0WN_FAILED;
+ } else if (strcmp(name, "ContainerRemovalFailed") == 0) {
+ err = INSTPROXY_E_CONTAINER_REMOVAL_FAILED;
+ } else if (strcmp(name, "EmbeddedProfileInstallFailed") == 0) {
+ err = INSTPROXY_E_EMBEDDED_PROFILE_INSTALL_FAILED;
+ } else if (strcmp(name, "ExecutableTwiddleFailed") == 0) {
+ err = INSTPROXY_E_EXECUTABLE_TWIDDLE_FAILED;
+ } else if (strcmp(name, "ExistenceCheckFailed") == 0) {
+ err = INSTPROXY_E_EXISTENCE_CHECK_FAILED;
+ } else if (strcmp(name, "InstallMapUpdateFailed") == 0) {
+ err = INSTPROXY_E_INSTALL_MAP_UPDATE_FAILED;
+ } else if (strcmp(name, "ManifestCaptureFailed") == 0) {
+ err = INSTPROXY_E_MANIFEST_CAPTURE_FAILED;
+ } else if (strcmp(name, "MapGenerationFailed") == 0) {
+ err = INSTPROXY_E_MAP_GENERATION_FAILED;
+ } else if (strcmp(name, "MissingBundleExecutable") == 0) {
+ err = INSTPROXY_E_MISSING_BUNDLE_EXECUTABLE;
+ } else if (strcmp(name, "MissingBundleIdentifier") == 0) {
+ err = INSTPROXY_E_MISSING_BUNDLE_IDENTIFIER;
+ } else if (strcmp(name, "MissingBundlePath") == 0) {
+ err = INSTPROXY_E_MISSING_BUNDLE_PATH;
+ } else if (strcmp(name, "MissingContainer") == 0) {
+ err = INSTPROXY_E_MISSING_CONTAINER;
+ } else if (strcmp(name, "NotificationFailed") == 0) {
+ err = INSTPROXY_E_NOTIFICATION_FAILED;
+ } else if (strcmp(name, "PackageExtractionFailed") == 0) {
+ err = INSTPROXY_E_PACKAGE_EXTRACTION_FAILED;
+ } else if (strcmp(name, "PackageInspectionFailed") == 0) {
+ err = INSTPROXY_E_PACKAGE_INSPECTION_FAILED;
+ } else if (strcmp(name, "PackageMoveFailed") == 0) {
+ err = INSTPROXY_E_PACKAGE_MOVE_FAILED;
+ } else if (strcmp(name, "PathConversionFailed") == 0) {
+ err = INSTPROXY_E_PATH_CONVERSION_FAILED;
+ } else if (strcmp(name, "RestoreContainerFailed") == 0) {
+ err = INSTPROXY_E_RESTORE_CONTAINER_FAILED;
+ } else if (strcmp(name, "SeatbeltProfileRemovalFailed") == 0) {
+ err = INSTPROXY_E_SEATBELT_PROFILE_REMOVAL_FAILED;
+ } else if (strcmp(name, "StageCreationFailed") == 0) {
+ err = INSTPROXY_E_STAGE_CREATION_FAILED;
+ } else if (strcmp(name, "SymlinkFailed") == 0) {
+ err = INSTPROXY_E_SYMLINK_FAILED;
+ } else if (strcmp(name, "UnknownCommand") == 0) {
+ err = INSTPROXY_E_UNKNOWN_COMMAND;
+ } else if (strcmp(name, "iTunesArtworkCaptureFailed") == 0) {
+ err = INSTPROXY_E_ITUNES_ARTWORK_CAPTURE_FAILED;
+ } else if (strcmp(name, "iTunesMetadataCaptureFailed") == 0) {
+ err = INSTPROXY_E_ITUNES_METADATA_CAPTURE_FAILED;
+ } else if (strcmp(name, "DeviceOSVersionTooLow") == 0) {
+ err = INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW;
+ } else if (strcmp(name, "DeviceFamilyNotSupported") == 0) {
+ err = INSTPROXY_E_DEVICE_FAMILY_NOT_SUPPORTED;
+ } else if (strcmp(name, "PackagePatchFailed") == 0) {
+ err = INSTPROXY_E_PACKAGE_PATCH_FAILED;
+ } else if (strcmp(name, "IncorrectArchitecture") == 0) {
+ err = INSTPROXY_E_INCORRECT_ARCHITECTURE;
+ } else if (strcmp(name, "PluginCopyFailed") == 0) {
+ err = INSTPROXY_E_PLUGIN_COPY_FAILED;
+ } else if (strcmp(name, "BreadcrumbFailed") == 0) {
+ err = INSTPROXY_E_BREADCRUMB_FAILED;
+ } else if (strcmp(name, "BreadcrumbUnlockFailed") == 0) {
+ err = INSTPROXY_E_BREADCRUMB_UNLOCK_FAILED;
+ } else if (strcmp(name, "GeoJSONCaptureFailed") == 0) {
+ err = INSTPROXY_E_GEOJSON_CAPTURE_FAILED;
+ } else if (strcmp(name, "NewsstandArtworkCaptureFailed") == 0) {
+ err = INSTPROXY_E_NEWSSTAND_ARTWORK_CAPTURE_FAILED;
+ } else if (strcmp(name, "MissingCommand") == 0) {
+ err = INSTPROXY_E_MISSING_COMMAND;
+ } else if (strcmp(name, "NotEntitled") == 0) {
+ err = INSTPROXY_E_NOT_ENTITLED;
+ } else if (strcmp(name, "MissingPackagePath") == 0) {
+ err = INSTPROXY_E_MISSING_PACKAGE_PATH;
+ } else if (strcmp(name, "MissingContainerPath") == 0) {
+ err = INSTPROXY_E_MISSING_CONTAINER_PATH;
+ } else if (strcmp(name, "MissingApplicationIdentifier") == 0) {
+ err = INSTPROXY_E_MISSING_APPLICATION_IDENTIFIER;
+ } else if (strcmp(name, "MissingAttributeValue") == 0) {
+ err = INSTPROXY_E_MISSING_ATTRIBUTE_VALUE;
+ } else if (strcmp(name, "LookupFailed") == 0) {
+ err = INSTPROXY_E_LOOKUP_FAILED;
+ } else if (strcmp(name, "DictCreationFailed") == 0) {
+ err = INSTPROXY_E_DICT_CREATION_FAILED;
+ } else if (strcmp(name, "InstallProhibited") == 0) {
+ err = INSTPROXY_E_INSTALL_PROHIBITED;
+ } else if (strcmp(name, "UninstallProhibited") == 0) {
+ err = INSTPROXY_E_UNINSTALL_PROHIBITED;
+ } else if (strcmp(name, "MissingBundleVersion") == 0) {
+ err = INSTPROXY_E_MISSING_BUNDLE_VERSION;
+ }
+
+ return err;
+}
+
+/**
* Locks an installation_proxy client, used for thread safety.
*
* @param client The installation_proxy client to lock
@@ -97,7 +240,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_new(idevice_t device, lo
instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private));
client_loc->parent = plistclient;
mutex_init(&client_loc->mutex);
- client_loc->status_updater = (thread_t)NULL;
+ client_loc->receive_status_thread = (thread_t)NULL;
*client = client_loc;
return INSTPROXY_E_SUCCESS;
@@ -117,11 +260,11 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_free(instproxy_client_t
property_list_service_client_free(client->parent);
client->parent = NULL;
- if (client->status_updater) {
- debug_info("joining status_updater");
- thread_join(client->status_updater);
- thread_free(client->status_updater);
- client->status_updater = (thread_t)NULL;
+ if (client->receive_status_thread) {
+ debug_info("joining receive_status_thread");
+ thread_join(client->receive_status_thread);
+ thread_free(client->receive_status_thread);
+ client->receive_status_thread = (thread_t)NULL;
}
mutex_destroy(&client->mutex);
free(client);
@@ -142,93 +285,18 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_free(instproxy_client_t
* @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
* an error occured.
*/
-static instproxy_error_t instproxy_send_command(instproxy_client_t client, const char *command, plist_t client_options, const char *appid, const char *package_path)
-{
- if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT)))
- return INSTPROXY_E_INVALID_ARG;
-
- plist_t dict = plist_new_dict();
- if (appid) {
- plist_dict_set_item(dict, "ApplicationIdentifier", plist_new_string(appid));
- }
- if (client_options && (plist_dict_get_size(client_options) > 0)) {
- plist_dict_set_item(dict, "ClientOptions", plist_copy(client_options));
- }
- plist_dict_set_item(dict, "Command", plist_new_string(command));
- if (package_path) {
- plist_dict_set_item(dict, "PackagePath", plist_new_string(package_path));
- }
-
- instproxy_error_t err = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
- plist_free(dict);
- return err;
-}
-
-LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
+static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command)
{
- if (!client || !client->parent || !result)
+ if (!client || !command)
return INSTPROXY_E_INVALID_ARG;
- instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+ instproxy_error_t res = instproxy_error(property_list_service_send_xml_plist(client->parent, command));
- instproxy_lock(client);
- res = instproxy_send_command(client, "Browse", client_options, NULL, NULL);
if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist");
- goto leave_unlock;
- }
-
- int browsing = 0;
- plist_t apps_array = plist_new_array();
- plist_t dict = NULL;
-
- do {
- browsing = 0;
- dict = NULL;
- res = instproxy_error(property_list_service_receive_plist(client->parent, &dict));
- if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) {
- break;
- }
- if (dict) {
- uint64_t i;
- uint64_t current_amount = 0;
- char *status = NULL;
- plist_t camount = plist_dict_get_item(dict, "CurrentAmount");
- plist_t pstatus = plist_dict_get_item(dict, "Status");
- if (camount) {
- plist_get_uint_val(camount, &current_amount);
- }
- if (current_amount > 0) {
- plist_t current_list = plist_dict_get_item(dict, "CurrentList");
- for (i = 0; current_list && (i < current_amount); i++) {
- plist_t item = plist_array_get_item(current_list, i);
- plist_array_append_item(apps_array, plist_copy(item));
- }
- }
- if (pstatus) {
- plist_get_string_val(pstatus, &status);
- }
- if (status) {
- if (!strcmp(status, "BrowsingApplications")) {
- browsing = 1;
- } else if (!strcmp(status, "Complete")) {
- debug_info("Browsing applications completed");
- res = INSTPROXY_E_SUCCESS;
- }
- free(status);
- }
- plist_free(dict);
- }
- } while (browsing);
-
- if (res == INSTPROXY_E_SUCCESS) {
- *result = apps_array;
- } else {
- plist_free(apps_array);
+ debug_info("could not send command plist, error %d", res);
+ return res;
}
-leave_unlock:
- instproxy_unlock(client);
return res;
}
@@ -241,78 +309,102 @@ leave_unlock:
*
* @param client The connected installation proxy client
* @param status_cb Pointer to a callback function or NULL
- * @param operation Operation name. Will be passed to the callback function
- * in async mode or shown in debug messages in sync mode.
+ * @param command Operation specificiation in plist. Will be passed to the
+ * status_cb callback.
* @param user_data Callback data passed to status_cb.
*/
-static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data)
+static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client, plist_t command, instproxy_status_cb_t status_cb, void *user_data)
{
instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
- int ok = 1;
- plist_t dict = NULL;
+ int complete = 0;
+ plist_t node = NULL;
+ char* command_name = NULL;
+ char* status_name = NULL;
+ char* error_name = NULL;
+ char* error_description = NULL;
+ uint64_t error_code = 0;
+#ifndef STRIP_DEBUG_CODE
+ int percent_complete = 0;
+#endif
+
+ instproxy_command_get_name(command, &command_name);
do {
+ /* receive status response */
instproxy_lock(client);
- res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 1000));
+ res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
instproxy_unlock(client);
+
+ /* break out if we have a communication problem */
if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) {
debug_info("could not receive plist, error %d", res);
break;
}
- if (dict) {
- /* invoke callback function */
- if (status_cb) {
- status_cb(operation, dict, user_data);
+
+ /* parse status response */
+ if (node) {
+ /* check status for possible errorĀ to allow reporting it and aborting it gracefully */
+ res = instproxy_status_get_error(node, &error_name, &error_description, &error_code);
+ if (res != INSTPROXY_E_SUCCESS) {
+ 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");
+ complete = 1;
+ }
+
+ if (error_name) {
+ free(error_name);
+ error_name = NULL;
+ }
+
+ if (error_description) {
+ free(error_description);
+ error_description = NULL;
}
- /* check for 'Error', so we can abort cleanly */
- plist_t err = plist_dict_get_item(dict, "Error");
- if (err) {
+
+ /* check status from response */
+ instproxy_status_get_name(node, &status_name);
+ if (!status_name) {
+ debug_info("failed to retrieve name from status response with error %d.", res);
+ complete = 1;
+ }
+
+ if (status_name) {
+ if (!strcmp(status_name, "Complete")) {
+ complete = 1;
+ } else {
+ res = INSTPROXY_E_OP_IN_PROGRESS;
+ }
+
#ifndef STRIP_DEBUG_CODE
- char *err_msg = NULL;
- plist_get_string_val(err, &err_msg);
- if (err_msg) {
- debug_info("(%s): ERROR: %s", operation, err_msg);
- free(err_msg);
+ percent_complete = -1;
+ instproxy_status_get_percent_complete(node, &percent_complete);
+ if (percent_complete >= 0) {
+ debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete);
+ } else {
+ debug_info("command: %s, status: %s", command_name, status_name);
}
#endif
- ok = 0;
- res = INSTPROXY_E_OP_FAILED;
+ free(status_name);
+ status_name = NULL;
}
- /* get 'Status' */
- plist_t status = plist_dict_get_item(dict, "Status");
- if (status) {
- char *status_msg = NULL;
- plist_get_string_val(status, &status_msg);
- if (status_msg) {
- if (!strcmp(status_msg, "Complete")) {
- ok = 0;
- res = INSTPROXY_E_SUCCESS;
- }
-#ifndef STRIP_DEBUG_CODE
- plist_t npercent = plist_dict_get_item(dict, "PercentComplete");
- if (npercent) {
- uint64_t val = 0;
- int percent;
- plist_get_uint_val(npercent, &val);
- percent = val;
- debug_info("(%s): %s (%d%%)", operation, status_msg, percent);
- } else {
- debug_info("(%s): %s", operation, status_msg);
- }
-#endif
- free(status_msg);
- }
+
+ /* invoke status callback function */
+ if (status_cb) {
+ status_cb(command, node, user_data);
}
- plist_free(dict);
- dict = NULL;
+
+ plist_free(node);
+ node = NULL;
}
- } while (ok && client->parent);
+ } while (!complete && client->parent);
+
+ if (command_name)
+ free(command_name);
return res;
}
/**
- * Internally used status updater thread function that will call the specified
+ * Internally used "receive status" thread function that will call the specified
* callback function when status update messages (or error messages) are
* received.
*
@@ -321,23 +413,27 @@ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client,
*
* @return Always NULL.
*/
-static void* instproxy_status_updater(void* arg)
+static void* instproxy_receive_status_loop_thread(void* arg)
{
struct instproxy_status_data *data = (struct instproxy_status_data*)arg;
- /* run until the operation is complete or an error occurs */
- (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation, data->user_data);
+ /* run until the command is complete or an error occurs */
+ (void)instproxy_receive_status_loop(data->client, data->command, data->cbfunc, data->user_data);
/* cleanup */
instproxy_lock(data->client);
+
debug_info("done, cleaning up.");
- if (data->operation) {
- free(data->operation);
+
+ if (data->command) {
+ plist_free(data->command);
}
- if (data->client->status_updater) {
- thread_free(data->client->status_updater);
- data->client->status_updater = (thread_t)NULL;
+
+ if (data->client->receive_status_thread) {
+ thread_free(data->client->receive_status_thread);
+ data->client->receive_status_thread = (thread_t)NULL;
}
+
instproxy_unlock(data->client);
free(data);
@@ -345,198 +441,454 @@ static void* instproxy_status_updater(void* arg)
}
/**
- * Internally used helper function that creates a status updater thread which
- * will call the passed callback function when status updates occur.
- * If status_cb is NULL no thread will be created, but the operation will
- * run synchronously until it completes or an error occurs.
+ * Internally used helper function that creates a "receive status" thread which
+ * will call the passed callback function when a status is received.
+ *
+ * If async is 0 no thread will be created and the command will run
+ * synchronously until it completes or an error occurs.
*
* @param client The connected installation proxy client
- * @param status_cb Pointer to a callback function or NULL
- * @param operation Operation name. Will be passed to the callback function
+ * @param command Operation name. Will be passed to the callback function
* in async mode or shown in debug messages in sync mode.
+ * @param async A boolean indicating if receive loop should be run
+ * asynchronously or block.
+ * @param status_cb Pointer to a callback function or NULL.
* @param user_data Callback data passed to status_cb.
*
* @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or
- * when the operation completed successfully (sync).
+ * when the command completed successfully (sync).
* An INSTPROXY_E_* error value is returned if an error occured.
*/
-static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data)
+static 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)
{
+ if (!client || !client->parent || !command) {
+ return INSTPROXY_E_INVALID_ARG;
+ }
+
+ if (client->receive_status_thread) {
+ return INSTPROXY_E_OP_IN_PROGRESS;
+ }
+
instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
- if (status_cb) {
+ if (async == INSTPROXY_COMMAND_TYPE_ASYNC) {
/* async mode */
struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data));
if (data) {
data->client = client;
+ data->command = plist_copy(command);
data->cbfunc = status_cb;
- data->operation = strdup(operation);
data->user_data = user_data;
- if (thread_new(&client->status_updater, instproxy_status_updater, data) == 0) {
+ if (thread_new(&client->receive_status_thread, instproxy_receive_status_loop_thread, data) == 0) {
res = INSTPROXY_E_SUCCESS;
}
}
} else {
- /* sync mode */
- res = instproxy_perform_operation(client, NULL, operation, NULL);
+ /* sync mode as a fallback */
+ res = instproxy_receive_status_loop(client, command, status_cb, user_data);
}
+
return res;
}
/**
- * Internal function used by instproxy_install and instproxy_upgrade.
+ * Internal core function to send a command and process the response.
*
* @param client The connected installation_proxy client
- * @param pkg_path Path of the installation package (inside the AFC jail)
- * @param client_options The client options to use, as PLIST_DICT, or NULL.
- * @param status_cb Callback function for progress and status information. If
- * NULL is passed, this function will run synchronously.
- * @param command The command to execute.
+ * @param command The command specification dictionary.
+ * @param async A boolean indicating whether the receive loop should be run
+ * asynchronously or block until completing the command.
+ * @param status_cb Callback function to call if a command status is received.
* @param user_data Callback data passed to status_cb.
*
* @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
* an error occured.
*/
-static 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)
+static 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)
{
- if (!client || !client->parent || !pkg_path) {
+ if (!client || !client->parent || !command) {
return INSTPROXY_E_INVALID_ARG;
}
- if (client->status_updater) {
+
+ if (client->receive_status_thread) {
return INSTPROXY_E_OP_IN_PROGRESS;
}
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ /* send command */
instproxy_lock(client);
- instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path);
+ res = instproxy_send_command(client, command);
instproxy_unlock(client);
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist, error %d", res);
- return res;
- }
+ /* loop until status or error is received */
+ res = instproxy_receive_status_loop_with_callback(client, command, async, status_cb, user_data);
- return instproxy_create_status_updater(client, status_cb, command, user_data);
+ return res;
}
-LIBIMOBILEDEVICE_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)
+LIBIMOBILEDEVICE_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)
{
- return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install", user_data);
+ if (!client || !client->parent || !status_cb)
+ return INSTPROXY_E_INVALID_ARG;
+
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Browse"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, (void*)user_data);
+
+ plist_free(command);
+
+ return res;
}
-LIBIMOBILEDEVICE_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)
+static void instproxy_append_current_list_to_result_cb(plist_t command, plist_t status, void *user_data)
{
- return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade", user_data);
+ plist_t *result_array = (plist_t*)user_data;
+ uint64_t current_amount = 0;
+ plist_t current_list = NULL;
+ uint64_t i;
+
+ instproxy_status_get_current_list(status, NULL, NULL, &current_amount, &current_list);
+
+ debug_info("current_amount: %d", current_amount);
+
+ if (current_amount > 0) {
+ for (i = 0; current_list && (i < current_amount); i++) {
+ plist_t item = plist_array_get_item(current_list, i);
+ plist_array_append_item(*result_array, plist_copy(item));
+ }
+ }
+
+ if (current_list)
+ plist_free(current_list);
}
-LIBIMOBILEDEVICE_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)
+LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
{
- if (!client || !client->parent || !appid) {
+ if (!client || !client->parent || !result)
return INSTPROXY_E_INVALID_ARG;
- }
-
- if (client->status_updater) {
- return INSTPROXY_E_OP_IN_PROGRESS;
- }
instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
- instproxy_lock(client);
- res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
- instproxy_unlock(client);
+ plist_t result_array = plist_new_array();
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist, error %d", res);
- return res;
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Browse"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_append_current_list_to_result_cb, (void*)&result_array);
+
+ if (res == INSTPROXY_E_SUCCESS) {
+ *result = result_array;
+ } else {
+ plist_free(result_array);
}
- return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data);
+ plist_free(command);
+
+ return res;
}
-LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
+static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, void *user_data)
+{
+ plist_t* result = (plist_t*)user_data;
+
+ plist_t node = plist_dict_get_item(status, "LookupResult");
+ if (node) {
+ *result = plist_copy(node);
+ }
+}
+
+LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup(instproxy_client_t client, plist_t appids, plist_t client_options, plist_t *result)
{
if (!client || !client->parent || !result)
return INSTPROXY_E_INVALID_ARG;
- instproxy_lock(client);
- instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL);
+ if (appids && (plist_get_node_type(appids) != PLIST_ARRAY && plist_get_node_type(appids) != PLIST_STRING))
+ return INSTPROXY_E_INVALID_ARG;
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist, error %d", res);
- goto leave_unlock;
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t lookup_result = NULL;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Lookup"));
+ if (appids) {
+ plist_dict_set_item(client_options, "BundleIDs", plist_copy(appids));
}
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
- res = instproxy_error(property_list_service_receive_plist(client->parent, result));
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not receive plist, error %d", res);
- goto leave_unlock;
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
+
+ if (res == INSTPROXY_E_SUCCESS) {
+ *result = lookup_result;
+ } else {
+ plist_free(lookup_result);
}
- res = INSTPROXY_E_SUCCESS;
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_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)
+{
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Install"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
+
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_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)
+{
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Upgrade"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
+
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_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)
+{
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Uninstall"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
+
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
+{
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("LookupArchives"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)result);
+
+ plist_free(command);
-leave_unlock:
- instproxy_unlock(client);
return res;
}
LIBIMOBILEDEVICE_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)
{
- if (!client || !client->parent || !appid)
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Archive"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
+
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_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)
+{
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("Restore"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
+
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_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)
+{
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("RemoveArchive"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
+
+ plist_free(command);
+
+ return res;
+}
+
+LIBIMOBILEDEVICE_API instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, plist_t capabilities, plist_t client_options, plist_t *result)
+{
+ if (!capabilities || (plist_get_node_type(capabilities) != PLIST_ARRAY && plist_get_node_type(capabilities) != PLIST_DICT))
return INSTPROXY_E_INVALID_ARG;
- if (client->status_updater) {
- return INSTPROXY_E_OP_IN_PROGRESS;
- }
+ plist_t lookup_result = NULL;
- instproxy_lock(client);
- instproxy_error_t res = instproxy_send_command(client, "Archive", client_options, appid, NULL);
- instproxy_unlock(client);
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist, error %d", res);
- return res;
+ plist_t command = plist_new_dict();
+ plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch"));
+ if (client_options)
+ plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
+ plist_dict_set_item(command, "Capabilities", plist_copy(capabilities));
+
+ res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
+
+ if (res == INSTPROXY_E_SUCCESS) {
+ *result = lookup_result;
+ } else {
+ plist_free(lookup_result);
}
- return instproxy_create_status_updater(client, status_cb, "Archive", user_data);
+
+ plist_free(command);
+
+ return res;
}
-LIBIMOBILEDEVICE_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)
+LIBIMOBILEDEVICE_API instproxy_error_t instproxy_status_get_error(plist_t status, char **name, char** description, uint64_t* code)
{
- if (!client || !client->parent || !appid)
+ instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
+
+ if (!status || !name)
return INSTPROXY_E_INVALID_ARG;
- if (client->status_updater) {
- return INSTPROXY_E_OP_IN_PROGRESS;
+ plist_t node = plist_dict_get_item(status, "Error");
+ if (node) {
+ plist_get_string_val(node, name);
+ } else {
+ /* no error here */
+ res = INSTPROXY_E_SUCCESS;
}
- instproxy_lock(client);
- instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL);
- instproxy_unlock(client);
+ if (code != NULL) {
+ *code = 0;
+ node = plist_dict_get_item(status, "ErrorDetail");
+ if (node) {
+ plist_get_uint_val(node, code);
+ *code &= 0xffffffff;
+ }
+ }
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist, error %d", res);
- return res;
+ if (description != NULL) {
+ node = plist_dict_get_item(status, "ErrorDescription");
+ if (node) {
+ plist_get_string_val(node, description);
+ }
}
- return instproxy_create_status_updater(client, status_cb, "Restore", user_data);
+
+ if (*name) {
+ res = instproxy_strtoerr(*name);
+ }
+
+ return res;
}
-LIBIMOBILEDEVICE_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)
+LIBIMOBILEDEVICE_API void instproxy_status_get_name(plist_t status, char **name)
{
- if (!client || !client->parent || !appid)
- return INSTPROXY_E_INVALID_ARG;
+ *name = NULL;
+ if (name) {
+ plist_t node = plist_dict_get_item(status, "Status");
+ if (node) {
+ plist_get_string_val(node, name);
+ }
+ }
+}
- if (client->status_updater) {
- return INSTPROXY_E_OP_IN_PROGRESS;
+LIBIMOBILEDEVICE_API void instproxy_status_get_percent_complete(plist_t status, int *percent)
+{
+ uint64_t val = 0;
+ if (percent) {
+ plist_t node = plist_dict_get_item(status, "PercentComplete");
+ if (node) {
+ plist_get_uint_val(node, &val);
+ *percent = val;
+ }
}
+}
- /* send command */
- instproxy_lock(client);
- instproxy_error_t res = instproxy_send_command(client, "RemoveArchive", client_options, appid, NULL);
- instproxy_unlock(client);
+LIBIMOBILEDEVICE_API void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list)
+{
+ plist_t node = NULL;
+
+ if (status && plist_get_node_type(status) == PLIST_DICT) {
+ /* command specific logic: parse browsed list */
+ if (list != NULL) {
+ node = plist_dict_get_item(status, "CurrentList");
+ if (node) {
+ *current_amount = plist_array_get_size(node);
+ *list = plist_copy(node);
+ }
+ }
- if (res != INSTPROXY_E_SUCCESS) {
- debug_info("could not send plist, error %d", res);
- return res;
+ if (total != NULL) {
+ node = plist_dict_get_item(status, "Total");
+ if (node) {
+ plist_get_uint_val(node, total);
+ }
+ }
+
+ if (current_amount != NULL) {
+ node = plist_dict_get_item(status, "CurrentAmount");
+ if (node) {
+ plist_get_uint_val(node, current_amount);
+ }
+ }
+
+ if (current_index != NULL) {
+ node = plist_dict_get_item(status, "CurrentIndex");
+ if (node) {
+ plist_get_uint_val(node, current_index);
+ }
+ }
+ }
+}
+
+LIBIMOBILEDEVICE_API void instproxy_command_get_name(plist_t command, char** name)
+{
+ *name = NULL;
+ plist_t node = plist_dict_get_item(command, "Command");
+ if (node) {
+ plist_get_string_val(node, name);
}
- return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data);
}
LIBIMOBILEDEVICE_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 @@
* installation_proxy.h
* com.apple.mobile.installation_proxy service header file.
*
- * Copyright (c) 2009 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
+ * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,7 +30,7 @@
struct instproxy_client_private {
property_list_service_client_t parent;
mutex_t mutex;
- thread_t status_updater;
+ thread_t receive_status_thread;
};
#endif