diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/installation_proxy.c | 830 | ||||
| -rw-r--r-- | src/installation_proxy.h | 5 |
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 | ||
| 33 | typedef enum { | ||
| 34 | INSTPROXY_COMMAND_TYPE_ASYNC, | ||
| 35 | INSTPROXY_COMMAND_TYPE_SYNC | ||
| 36 | } instproxy_command_type_t; | ||
| 37 | |||
| 32 | struct instproxy_status_data { | 38 | struct 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 | */ | ||
| 57 | static 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 | */ |
| 145 | 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) | 288 | static 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 | |||
| 167 | LIBIMOBILEDEVICE_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, ¤t_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 | ||
| 230 | leave_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 | */ |
| 248 | static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) | 316 | static 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 | */ |
| 324 | static void* instproxy_status_updater(void* arg) | 416 | static 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 | */ |
| 363 | static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) | 462 | 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) |
| 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 | */ |
| 400 | 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) | 507 | 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) |
| 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 | ||
| 421 | 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) | 530 | 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) |
| 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 | ||
| 426 | 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) | 549 | static 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, ¤t_amount, ¤t_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 | ||
| 431 | 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) | 571 | LIBIMOBILEDEVICE_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 | ||
| 455 | LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) | 598 | static 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 | |||
| 608 | LIBIMOBILEDEVICE_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 | |||
| 641 | 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) | ||
| 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 | |||
| 658 | 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) | ||
| 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 | |||
| 675 | 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) | ||
| 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 | |||
| 692 | LIBIMOBILEDEVICE_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 | ||
| 476 | leave_unlock: | ||
| 477 | instproxy_unlock(client); | ||
| 478 | return res; | 705 | return res; |
| 479 | } | 706 | } |
| 480 | 707 | ||
| 481 | 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) | 708 | 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) |
| 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 | |||
| 725 | 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) | ||
| 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 | |||
| 742 | 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) | ||
| 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 | |||
| 759 | LIBIMOBILEDEVICE_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 | ||
| 501 | 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) | 787 | LIBIMOBILEDEVICE_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 | ||
| 521 | 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) | 825 | LIBIMOBILEDEVICE_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) { | 836 | LIBIMOBILEDEVICE_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 */ | 848 | 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) |
| 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 | |||
| 885 | LIBIMOBILEDEVICE_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 | ||
| 542 | LIBIMOBILEDEVICE_API plist_t instproxy_client_options_new() | 894 | 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 @@ | |||
| 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 @@ | |||
| 29 | struct instproxy_client_private { | 30 | struct 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 |
