afc: Add afc_get_file_info_plist and afc_get_device_info_plist functions

These functions are like afc_get_file_info/afc_get_device_info but the
data is returned as a plist_t dictionary instead of a string list.
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_get_device_info(afc_client_t client, char ***device_information);
 
 /**
+ * Get device information for a connected client. The device information
+ * returned is the device model as well as the free space, the total capacity
+ * and blocksize on the accessed disk partition.
+ *
+ * @param client The client to get device info for.
+ * @param device_information A pointer to a plist_t that will be populated
+ *        with key-value pairs (dictionary) representing the device’s
+ *        storage and model information. Free with plist_free().
+ *
+ * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
+ */
+LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information);
+
+/**
  * Gets a directory listing of the directory requested.
  *
  * @param client The client to get a directory listing from.
@@ -163,7 +177,7 @@
  * @param client The client to use to get the information of the file.
  * @param path The fully-qualified path to the file.
  * @param file_information Pointer to a buffer that will be filled with a
- *        NULL-terminated list of strings with the file information. Set to NULL
+ *        NULL-terminated list of strings with the file attributes. Set to NULL
  *        before calling this function. Free with afc_dictionary_free().
  *
  * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
@@ -171,6 +185,19 @@
 LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information);
 
 /**
+ * Gets information about a specific file.
+ *
+ * @param client The client to use to get the information of the file.
+ * @param path The fully-qualified path to the file.
+ * @param file_information A pointer to a plist_t that will be populated
+ *        with key-value pairs (dictionary) representing the file attributes.
+ *        Free with plist_free().
+ *
+ * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
+ */
+LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information);
+
+/**
  * Opens a file on the device.
  *
  * @param client The client to use to open the file.
diff --git a/src/afc.c b/src/afc.c
index c0314bc..c7eed5c 100644
--- a/src/afc.c
+++ b/src/afc.c
@@ -402,6 +402,50 @@
 	return list;
 }
 
+static plist_t *make_dictionary(char *tokens, size_t length)
+{
+	size_t j = 0;
+	plist_t dict = NULL;
+
+	if (!tokens || !length)
+		return NULL;
+
+	dict = plist_new_dict();
+
+	while (j < length) {
+		size_t key_len = strnlen(tokens + j, length - j);
+		if (j + key_len >= length) {
+			plist_free(dict);
+			return NULL;
+		}
+		char* key = tokens + j;
+		j += key_len + 1;
+
+		if (j >= length) {
+			plist_free(dict);
+			return NULL;
+		}
+
+		size_t val_len = strnlen(tokens + j, length - j);
+		if (j + val_len >= length) {
+			plist_free(dict);
+			return NULL;
+		}
+		char* val = tokens + j;
+		j += val_len + 1;
+
+		char* endp = NULL;
+		unsigned long long u64val = strtoull(val, &endp, 10);
+		if (endp && *endp == '\0') {
+			plist_dict_set_item(dict, key, plist_new_uint(u64val));
+		} else {
+			plist_dict_set_item(dict, key, plist_new_string(val));
+		}
+	}
+
+	return dict;
+}
+
 static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len)
 {
 	if (data_len > client->packet_extra) {
@@ -498,6 +542,40 @@
 	return ret;
 }
 
+afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information)
+{
+	uint32_t bytes = 0;
+	char *data = NULL;
+	afc_error_t ret = AFC_E_UNKNOWN_ERROR;
+
+	if (!client || !device_information)
+		return AFC_E_INVALID_ARG;
+
+	afc_lock(client);
+
+	/* Send the command */
+	ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes);
+	if (ret != AFC_E_SUCCESS) {
+		afc_unlock(client);
+		return AFC_E_NOT_ENOUGH_DATA;
+	}
+	/* Receive the data */
+	ret = afc_receive_data(client, &data, &bytes);
+	if (ret != AFC_E_SUCCESS) {
+		if (data)
+			free(data);
+		afc_unlock(client);
+		return ret;
+	}
+	/* Parse the data */
+	*device_information = make_dictionary(data, bytes);
+	free(data);
+
+	afc_unlock(client);
+
+	return ret;
+}
+
 afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value)
 {
 	afc_error_t ret = AFC_E_INTERNAL_ERROR;
@@ -647,8 +725,6 @@
 		return AFC_E_NO_MEM;
 	}
 
-	debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR);
-
 	/* Send command */
 	memcpy(AFC_PACKET_DATA_PTR, path, data_len);
 	ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes);
@@ -669,6 +745,44 @@
 	return ret;
 }
 
+afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information)
+{
+	char *received = NULL;
+	uint32_t bytes = 0;
+	afc_error_t ret = AFC_E_UNKNOWN_ERROR;
+
+	if (!client || !path || !file_information)
+		return AFC_E_INVALID_ARG;
+
+	afc_lock(client);
+
+	uint32_t data_len = (uint32_t)strlen(path)+1;
+	if (_afc_check_packet_buffer(client, data_len) < 0) {
+		afc_unlock(client);
+		debug_info("Failed to realloc packet buffer");
+		return AFC_E_NO_MEM;
+	}
+
+	/* Send command */
+	memcpy(AFC_PACKET_DATA_PTR, path, data_len);
+	ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes);
+	if (ret != AFC_E_SUCCESS) {
+		afc_unlock(client);
+		return AFC_E_NOT_ENOUGH_DATA;
+	}
+
+	/* Receive data */
+	ret = afc_receive_data(client, &received, &bytes);
+	if (received) {
+		*file_information = make_dictionary(received, bytes);
+		free(received);
+	}
+
+	afc_unlock(client);
+
+	return ret;
+}
+
 afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle)
 {
 	if (!client || !client->parent || !client->afc_packet)