diff options
-rw-r--r-- | include/libimobiledevice/mobile_image_mounter.h | 5 | ||||
-rw-r--r-- | src/mobile_image_mounter.c | 122 |
2 files changed, 127 insertions, 0 deletions
diff --git a/include/libimobiledevice/mobile_image_mounter.h b/include/libimobiledevice/mobile_image_mounter.h index ceaf0c2..560fec6 100644 --- a/include/libimobiledevice/mobile_image_mounter.h +++ b/include/libimobiledevice/mobile_image_mounter.h @@ -38,6 +38,7 @@ extern "C" { #define MOBILE_IMAGE_MOUNTER_E_INVALID_ARG -1 #define MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR -2 #define MOBILE_IMAGE_MOUNTER_E_CONN_FAILED -3 +#define MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED -4 #define MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR -256 /*@}*/ @@ -48,12 +49,16 @@ typedef int16_t mobile_image_mounter_error_t; typedef struct mobile_image_mounter_client_private mobile_image_mounter_client_private; typedef mobile_image_mounter_client_private *mobile_image_mounter_client_t; /**< The client handle. */ +/** callback for image upload */ +typedef ssize_t (*mobile_image_mounter_upload_cb_t) (void* buffer, size_t length, void *user_data); + /* Interface */ mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t service, mobile_image_mounter_client_t *client); mobile_image_mounter_error_t mobile_image_mounter_start_service(idevice_t device, mobile_image_mounter_client_t* client, const char* label); mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client); mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result); +mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata); mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *image_signature, uint16_t signature_length, const char *image_type, plist_t *result); mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client); diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c index bc439f9..9e6d8e9 100644 --- a/src/mobile_image_mounter.c +++ b/src/mobile_image_mounter.c @@ -190,6 +190,128 @@ leave_unlock: } /** + * Uploads an image to the device. + * + * @param client The connected mobile_image_mounter client. + * @param image_type Type of image that is being uploaded. + * @param image_size Total size of the image. + * @param upload_cb Callback function that gets the data chunks for uploading + * the image. + * @param userdata User defined data for the upload callback function. + * + * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on succes, or a + * MOBILE_IMAGE_MOUNTER_E_* error code otherwise. + */ +mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata) +{ + if (!client || !image_type || (image_size == 0) || !upload_cb) { + return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; + } + mobile_image_mounter_lock(client); + plist_t result = NULL; + + plist_t dict = plist_new_dict(); + plist_dict_insert_item(dict, "Command", plist_new_string("ReceiveBytes")); + plist_dict_insert_item(dict, "ImageSize", plist_new_uint(image_size)); + plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type)); + + mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); + plist_free(dict); + + if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + debug_info("Error sending XML plist to device!"); + goto leave_unlock; + } + + res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); + if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + debug_info("Error receiving response from device!"); + goto leave_unlock; + } + res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; + + char* strval = NULL; + plist_t node = plist_dict_get_item(result, "Status"); + if (node && plist_get_node_type(node) == PLIST_STRING) { + plist_get_string_val(node, &strval); + } + if (!strval) { + debug_info("Error: Unexpected response received!"); + goto leave_unlock; + } + if (strcmp(strval, "ReceiveBytesAck") != 0) { + debug_info("Error: didn't get ReceiveBytesAck but %s", strval); + free(strval); + goto leave_unlock; + } + free(strval); + + size_t tx = 0; + size_t bufsize = 65536; + unsigned char *buf = (unsigned char*)malloc(bufsize); + if (!buf) { + debug_info("Out of memory"); + res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; + goto leave_unlock; + } + debug_info("uploading image (%d bytes)", (int)image_size); + while (tx < image_size) { + size_t remaining = image_size - tx; + size_t amount = (remaining < bufsize) ? remaining : bufsize; + ssize_t r = upload_cb(buf, amount, userdata); + if (r < 0) { + debug_info("upload_cb returned %d", (int)r); + break; + } + uint32_t sent = 0; + if (service_send(client->parent->parent, (const char*)buf, (uint32_t)r, &sent) != SERVICE_E_SUCCESS) { + debug_info("service_send failed"); + break; + } + tx += r; + } + free(buf); + if (tx < image_size) { + debug_info("Error: failed to upload image"); + goto leave_unlock; + } + debug_info("image uploaded"); + + res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); + if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + debug_info("Error receiving response from device!"); + goto leave_unlock; + } + res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; + + strval = NULL; + node = plist_dict_get_item(result, "Status"); + if (node && plist_get_node_type(node) == PLIST_STRING) { + plist_get_string_val(node, &strval); + } + if (!strval) { + debug_info("Error: Unexpected response received!"); + goto leave_unlock; + } + if (strcmp(strval, "Complete") != 0) { + debug_info("Error: didn't get Complete but %s", strval); + free(strval); + goto leave_unlock; + } else { + res = MOBILE_IMAGE_MOUNTER_E_SUCCESS; + } + free(strval); + + +leave_unlock: + mobile_image_mounter_unlock(client); + if (result) + plist_free(result); + return res; + +} + +/** * Mounts an image on the device. * * @param client The connected mobile_image_mounter client. |