diff options
-rw-r--r-- | include/libimobiledevice/afc.h | 29 | ||||
-rw-r--r-- | src/afc.c | 118 |
2 files changed, 144 insertions, 3 deletions
diff --git a/include/libimobiledevice/afc.h b/include/libimobiledevice/afc.h index 4ad3dbd..3dcb5da 100644 --- a/include/libimobiledevice/afc.h +++ b/include/libimobiledevice/afc.h | |||
@@ -145,6 +145,20 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_free(afc_client_t client); | |||
145 | LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char ***device_information); | 145 | LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char ***device_information); |
146 | 146 | ||
147 | /** | 147 | /** |
148 | * Get device information for a connected client. The device information | ||
149 | * returned is the device model as well as the free space, the total capacity | ||
150 | * and blocksize on the accessed disk partition. | ||
151 | * | ||
152 | * @param client The client to get device info for. | ||
153 | * @param device_information A pointer to a plist_t that will be populated | ||
154 | * with key-value pairs (dictionary) representing the device’s | ||
155 | * storage and model information. Free with plist_free(). | ||
156 | * | ||
157 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
158 | */ | ||
159 | LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information); | ||
160 | |||
161 | /** | ||
148 | * Gets a directory listing of the directory requested. | 162 | * Gets a directory listing of the directory requested. |
149 | * | 163 | * |
150 | * @param client The client to get a directory listing from. | 164 | * @param client The client to get a directory listing from. |
@@ -163,7 +177,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c | |||
163 | * @param client The client to use to get the information of the file. | 177 | * @param client The client to use to get the information of the file. |
164 | * @param path The fully-qualified path to the file. | 178 | * @param path The fully-qualified path to the file. |
165 | * @param file_information Pointer to a buffer that will be filled with a | 179 | * @param file_information Pointer to a buffer that will be filled with a |
166 | * NULL-terminated list of strings with the file information. Set to NULL | 180 | * NULL-terminated list of strings with the file attributes. Set to NULL |
167 | * before calling this function. Free with afc_dictionary_free(). | 181 | * before calling this function. Free with afc_dictionary_free(). |
168 | * | 182 | * |
169 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 183 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. |
@@ -171,6 +185,19 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c | |||
171 | LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information); | 185 | LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information); |
172 | 186 | ||
173 | /** | 187 | /** |
188 | * Gets information about a specific file. | ||
189 | * | ||
190 | * @param client The client to use to get the information of the file. | ||
191 | * @param path The fully-qualified path to the file. | ||
192 | * @param file_information A pointer to a plist_t that will be populated | ||
193 | * with key-value pairs (dictionary) representing the file attributes. | ||
194 | * Free with plist_free(). | ||
195 | * | ||
196 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
197 | */ | ||
198 | LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information); | ||
199 | |||
200 | /** | ||
174 | * Opens a file on the device. | 201 | * Opens a file on the device. |
175 | * | 202 | * |
176 | * @param client The client to use to open the file. | 203 | * @param client The client to use to open the file. |
@@ -402,6 +402,50 @@ static char **make_strings_list(char *tokens, uint32_t length) | |||
402 | return list; | 402 | return list; |
403 | } | 403 | } |
404 | 404 | ||
405 | static plist_t *make_dictionary(char *tokens, size_t length) | ||
406 | { | ||
407 | size_t j = 0; | ||
408 | plist_t dict = NULL; | ||
409 | |||
410 | if (!tokens || !length) | ||
411 | return NULL; | ||
412 | |||
413 | dict = plist_new_dict(); | ||
414 | |||
415 | while (j < length) { | ||
416 | size_t key_len = strnlen(tokens + j, length - j); | ||
417 | if (j + key_len >= length) { | ||
418 | plist_free(dict); | ||
419 | return NULL; | ||
420 | } | ||
421 | char* key = tokens + j; | ||
422 | j += key_len + 1; | ||
423 | |||
424 | if (j >= length) { | ||
425 | plist_free(dict); | ||
426 | return NULL; | ||
427 | } | ||
428 | |||
429 | size_t val_len = strnlen(tokens + j, length - j); | ||
430 | if (j + val_len >= length) { | ||
431 | plist_free(dict); | ||
432 | return NULL; | ||
433 | } | ||
434 | char* val = tokens + j; | ||
435 | j += val_len + 1; | ||
436 | |||
437 | char* endp = NULL; | ||
438 | unsigned long long u64val = strtoull(val, &endp, 10); | ||
439 | if (endp && *endp == '\0') { | ||
440 | plist_dict_set_item(dict, key, plist_new_uint(u64val)); | ||
441 | } else { | ||
442 | plist_dict_set_item(dict, key, plist_new_string(val)); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | return dict; | ||
447 | } | ||
448 | |||
405 | static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len) | 449 | static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len) |
406 | { | 450 | { |
407 | if (data_len > client->packet_extra) { | 451 | if (data_len > client->packet_extra) { |
@@ -498,6 +542,40 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***device_information) | |||
498 | return ret; | 542 | return ret; |
499 | } | 543 | } |
500 | 544 | ||
545 | afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information) | ||
546 | { | ||
547 | uint32_t bytes = 0; | ||
548 | char *data = NULL; | ||
549 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | ||
550 | |||
551 | if (!client || !device_information) | ||
552 | return AFC_E_INVALID_ARG; | ||
553 | |||
554 | afc_lock(client); | ||
555 | |||
556 | /* Send the command */ | ||
557 | ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes); | ||
558 | if (ret != AFC_E_SUCCESS) { | ||
559 | afc_unlock(client); | ||
560 | return AFC_E_NOT_ENOUGH_DATA; | ||
561 | } | ||
562 | /* Receive the data */ | ||
563 | ret = afc_receive_data(client, &data, &bytes); | ||
564 | if (ret != AFC_E_SUCCESS) { | ||
565 | if (data) | ||
566 | free(data); | ||
567 | afc_unlock(client); | ||
568 | return ret; | ||
569 | } | ||
570 | /* Parse the data */ | ||
571 | *device_information = make_dictionary(data, bytes); | ||
572 | free(data); | ||
573 | |||
574 | afc_unlock(client); | ||
575 | |||
576 | return ret; | ||
577 | } | ||
578 | |||
501 | afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) | 579 | afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) |
502 | { | 580 | { |
503 | afc_error_t ret = AFC_E_INTERNAL_ERROR; | 581 | afc_error_t ret = AFC_E_INTERNAL_ERROR; |
@@ -647,8 +725,6 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***fil | |||
647 | return AFC_E_NO_MEM; | 725 | return AFC_E_NO_MEM; |
648 | } | 726 | } |
649 | 727 | ||
650 | debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR); | ||
651 | |||
652 | /* Send command */ | 728 | /* Send command */ |
653 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); | 729 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
654 | ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); | 730 | ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); |
@@ -669,6 +745,44 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***fil | |||
669 | return ret; | 745 | return ret; |
670 | } | 746 | } |
671 | 747 | ||
748 | afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information) | ||
749 | { | ||
750 | char *received = NULL; | ||
751 | uint32_t bytes = 0; | ||
752 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | ||
753 | |||
754 | if (!client || !path || !file_information) | ||
755 | return AFC_E_INVALID_ARG; | ||
756 | |||
757 | afc_lock(client); | ||
758 | |||
759 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
760 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
761 | afc_unlock(client); | ||
762 | debug_info("Failed to realloc packet buffer"); | ||
763 | return AFC_E_NO_MEM; | ||
764 | } | ||
765 | |||
766 | /* Send command */ | ||
767 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); | ||
768 | ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); | ||
769 | if (ret != AFC_E_SUCCESS) { | ||
770 | afc_unlock(client); | ||
771 | return AFC_E_NOT_ENOUGH_DATA; | ||
772 | } | ||
773 | |||
774 | /* Receive data */ | ||
775 | ret = afc_receive_data(client, &received, &bytes); | ||
776 | if (received) { | ||
777 | *file_information = make_dictionary(received, bytes); | ||
778 | free(received); | ||
779 | } | ||
780 | |||
781 | afc_unlock(client); | ||
782 | |||
783 | return ret; | ||
784 | } | ||
785 | |||
672 | afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) | 786 | afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) |
673 | { | 787 | { |
674 | if (!client || !client->parent || !client->afc_packet) | 788 | if (!client || !client->parent || !client->afc_packet) |