diff options
| author | 2008-12-11 23:05:29 +0100 | |
|---|---|---|
| committer | 2008-12-11 23:05:29 +0100 | |
| commit | 31379321cec6bf6c6d670e0738d1b1e23dc92ac1 (patch) | |
| tree | 47aad4a390bcab10ff4fc6c36b35f0b89c06c1eb /src | |
| parent | 18d1ee3b0f17325fdffe0cf3e2770a3f0f45a1b9 (diff) | |
| download | libimobiledevice-31379321cec6bf6c6d670e0738d1b1e23dc92ac1.tar.gz libimobiledevice-31379321cec6bf6c6d670e0738d1b1e23dc92ac1.tar.bz2 | |
dissect plists in three file (abstract binary xml)
Diffstat (limited to 'src')
| -rw-r--r-- | src/bplist.c | 790 | ||||
| -rw-r--r-- | src/xplist.c | 306 | 
2 files changed, 1096 insertions, 0 deletions
| diff --git a/src/bplist.c b/src/bplist.c new file mode 100644 index 0000000..6136fe9 --- /dev/null +++ b/src/bplist.c @@ -0,0 +1,790 @@ +/* + * plist.c + * Binary plist implementation + * + * Copyright (c) 2008 Jonathan Beck 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ + + +#include "plist.h" +#include <wchar.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +/* Magic marker and size. */ +#define BPLIST_MAGIC		"bplist" +#define BPLIST_MAGIC_SIZE	6 + +#define BPLIST_VERSION		"00" +#define BPLIST_VERSION_SIZE	2 + + +#define BPLIST_TRL_SIZE 	26 +#define BPLIST_TRL_OFFSIZE_IDX 	0 +#define BPLIST_TRL_PARMSIZE_IDX 1 +#define BPLIST_TRL_NUMOBJ_IDX 	2 +#define BPLIST_TRL_ROOTOBJ_IDX 	10 +#define BPLIST_TRL_OFFTAB_IDX 	18 + +enum { +	BPLIST_NULL = 0x00, +	BPLIST_TRUE = 0x08, +	BPLIST_FALSE = 0x09, +	BPLIST_FILL = 0x0F,			/* will be used for length grabbing */ +	BPLIST_UINT = 0x10, +	BPLIST_REAL = 0x20, +	BPLIST_DATE = 0x30, +	BPLIST_DATA = 0x40, +	BPLIST_STRING = 0x50, +	BPLIST_UNICODE = 0x60, +	BPLIST_UID = 0x70, +	BPLIST_ARRAY = 0xA0, +	BPLIST_SET = 0xC0, +	BPLIST_DICT = 0xD0, +	BPLIST_MASK = 0xF0 +}; + +void byte_convert(char *address, size_t size) +{ +	int i = 0, j = 0; +	char tmp = '\0'; + +	for (i = 0; i < (size / 2); i++) { +		tmp = address[i]; +		j = ((size - 1) + 0) - i; +		address[i] = address[j]; +		address[j] = tmp; +	} +} + +#include <byteswap.h> +#define swap_n_bytes(x, n) \ +		n == 8 ? bswap_64(*(uint64_t *)(x)) : \ +		(n == 4 ? bswap_32(*(uint32_t *)(x)) : \ +		(n == 2 ? bswap_16(*(uint16_t *)(x)) : *(x) )) + +#define be64dec(x) bswap_64( *(uint64_t*)(x) ) + +#define get_needed_bytes(x) (x <= 1<<8 ? 1 : ( x <= 1<<16 ? 2 : ( x <= 1<<32 ? 4 : 8))) +#define get_real_bytes(x) (x >> 32 ? 4 : 8) + +GNode *parse_uint_node(char *bnode, uint8_t size, char **next_object) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	size = 1 << size;			// make length less misleading +	switch (size) { +	case sizeof(uint8_t): +		data->intval = bnode[0]; +		break; +	case sizeof(uint16_t): +		memcpy(&data->intval, bnode, size); +		data->intval = ntohs(data->intval); +		break; +	case sizeof(uint32_t): +		memcpy(&data->intval, bnode, size); +		data->intval = ntohl(data->intval); +		break; +	case sizeof(uint64_t): +		memcpy(&data->intval, bnode, size); +		byte_convert((char *) &data->intval, size); +		break; +	default: +		free(data); +		return NULL; +	}; + +	*next_object = bnode + size; +	data->type = PLIST_UINT; +	return g_node_new(data); +} + +GNode *parse_real_node(char *bnode, uint8_t size) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	size = 1 << size;			// make length less misleading +	switch (size) { +	case sizeof(float): +		memcpy(&data->realval, bnode, size); +		byte_convert((char *) &data->realval, size); +		break; +	case sizeof(double): +		memcpy(&data->realval, bnode, size); +		byte_convert((char *) &data->realval, size); +		break; +	default: +		free(data); +		return NULL; +	} +	data->type = PLIST_REAL; +	return g_node_new(data); +} + +GNode *parse_string_node(char *bnode, uint8_t size) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	data->type = PLIST_STRING; +	data->strval = (char *) malloc(sizeof(char) * (size + 1)); +	memcpy(data->strval, bnode, size); +	data->strval[size] = '\0'; + +	return g_node_new(data); +} + +GNode *parse_unicode_node(char *bnode, uint8_t size) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	data->type = PLIST_UNICODE; +	data->unicodeval = (wchar_t *) malloc(sizeof(wchar_t) * (size + 1)); +	memcpy(data->unicodeval, bnode, size); +	data->unicodeval[size] = '\0'; + +	return g_node_new(data); +} + +GNode *parse_data_node(char *bnode, uint64_t size, uint32_t ref_size) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	data->type = PLIST_DATA; +	data->length = size; +	data->buff = (char *) malloc(sizeof(char) * size); +	memcpy(data->buff, bnode, sizeof(char) * size); + +	return g_node_new(data); +} + +GNode *parse_dict_node(char *bnode, uint64_t size, uint32_t ref_size) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	data->type = PLIST_DICT; +	data->length = size; +	data->buff = (char *) malloc(sizeof(char) * size * ref_size * 2); +	memcpy(data->buff, bnode, sizeof(char) * size * ref_size * 2); + +	return g_node_new(data); +} + +GNode *parse_array_node(char *bnode, uint64_t size, uint32_t ref_size) +{ +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	data->type = PLIST_ARRAY; +	data->length = size; +	data->buff = (char *) malloc(sizeof(char) * size * ref_size); +	memcpy(data->buff, bnode, sizeof(char) * size * ref_size); + +	return g_node_new(data); +} + + + +GNode *parse_bin_node(char *object, uint8_t dict_size, char **next_object) +{ +	if (!object) +		return NULL; + +	uint16_t type = *object & 0xF0; +	uint64_t size = *object & 0x0F; +	object++; + +	switch (type) { + +	case BPLIST_NULL: +		switch (size) { + +		case BPLIST_TRUE: +			{ +				struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); +				data->type = PLIST_BOOLEAN; +				data->boolval = TRUE; +				return g_node_new(data); +			} + +		case BPLIST_FALSE: +			{ +				struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); +				data->type = PLIST_BOOLEAN; +				data->boolval = FALSE; +				return g_node_new(data); +			} + +		case BPLIST_NULL: +		default: +			return NULL; +		} + +	case BPLIST_UINT: +		return parse_uint_node(object, size, next_object); + +	case BPLIST_REAL: +		return parse_real_node(object, size); + +	case BPLIST_DATE: +		if (3 != size) +			return NULL; +		else +			return parse_real_node(object, size); + +	case BPLIST_DATA: +		if (0x0F == size) { +			plist_t size_node = parse_bin_node(object, dict_size, &object); +			if (plist_get_node_type(size_node) != PLIST_UINT) +				return NULL; +			size = plist_get_node_uint_val(size_node); +		} +		return parse_data_node(object, size, dict_size); + +	case BPLIST_STRING: +		if (0x0F == size) { +			plist_t size_node = parse_bin_node(object, dict_size, &object); +			if (plist_get_node_type(size_node) != PLIST_UINT) +				return NULL; +			size = plist_get_node_uint_val(size_node); +		} +		return parse_string_node(object, size); + +	case BPLIST_UNICODE: +		if (0x0F == size) { +			plist_t size_node = parse_bin_node(object, dict_size, &object); +			if (plist_get_node_type(size_node) != PLIST_UINT) +				return NULL; +			size = plist_get_node_uint_val(size_node); +		} +		return parse_unicode_node(object, size); + +	case BPLIST_UID: +	case BPLIST_ARRAY: +		if (0x0F == size) { +			plist_t size_node = parse_bin_node(object, dict_size, &object); +			if (plist_get_node_type(size_node) != PLIST_UINT) +				return NULL; +			size = plist_get_node_uint_val(size_node); +		} +		return parse_array_node(object, size, dict_size); + +	case BPLIST_SET: +	case BPLIST_DICT: +		if (0x0F == size) { +			plist_t size_node = parse_bin_node(object, dict_size, &object); +			if (plist_get_node_type(size_node) != PLIST_UINT) +				return NULL; +			object++; +			size = plist_get_node_uint_val(size_node); +		} +		return parse_dict_node(object, size, dict_size); + +	} +	return NULL; +} + +gpointer copy_plist_data(gconstpointer src, gpointer data) +{ +	struct plist_data *srcdata = (struct plist_data *) src; +	struct plist_data *dstdata = (struct plist_data *) calloc(sizeof(struct plist_data), 1); + +	dstdata->type = srcdata->type; +	dstdata->length = srcdata->length; +	switch (dstdata->type) { +	case PLIST_BOOLEAN: +		dstdata->boolval = srcdata->boolval; +		break; +	case PLIST_UINT: +		dstdata->intval = srcdata->intval; +		break; +	case PLIST_DATE: +	case PLIST_REAL: +		dstdata->realval = srcdata->realval; +		break; +	case PLIST_KEY: +	case PLIST_STRING: +		dstdata->strval = strdup(srcdata->strval); +		break; +	case PLIST_UNICODE: +		dstdata->unicodeval = wcsdup(srcdata->unicodeval); +		break; +	case PLIST_DATA: +	case PLIST_ARRAY: +	case PLIST_DICT: +		dstdata->buff = (char *) malloc(sizeof(char *) * srcdata->length); +		memcpy(dstdata->buff, srcdata->buff, sizeof(char *) * srcdata->length); +		break; + +	default: +		break; +	} + +	return dstdata; +} + +void bin_to_plist(const char *plist_bin, uint32_t length, plist_t * plist) +{ +	//first check we have enough data +	if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + BPLIST_TRL_SIZE)) +		return; +	//check that plist_bin in actually a plist +	if (memcmp(plist_bin, BPLIST_MAGIC, BPLIST_MAGIC_SIZE) != 0) +		return; +	//check for known version +	if (memcmp(plist_bin + BPLIST_MAGIC_SIZE, BPLIST_VERSION, BPLIST_VERSION_SIZE) != 0) +		return; + +	//now parse trailer +	const char *trailer = plist_bin + (length - BPLIST_TRL_SIZE); + +	uint8_t offset_size = trailer[BPLIST_TRL_OFFSIZE_IDX]; +	uint8_t dict_param_size = trailer[BPLIST_TRL_PARMSIZE_IDX]; +	uint64_t num_objects = be64dec(trailer + BPLIST_TRL_NUMOBJ_IDX); +	uint64_t root_object = be64dec(trailer + BPLIST_TRL_ROOTOBJ_IDX); +	uint64_t offset_table_index = be64dec(trailer + BPLIST_TRL_OFFTAB_IDX); + +	log_debug_msg("Offset size: %i\n", offset_size); +	log_debug_msg("Ref size: %i\n", dict_param_size); +	log_debug_msg("Number of objects: %lli\n", num_objects); +	log_debug_msg("Root object index: %lli\n", root_object); +	log_debug_msg("Offset table index: %lli\n", offset_table_index); + +	if (num_objects == 0) +		return; + +	//allocate serialized array of nodes +	plist_t *nodeslist = NULL; +	nodeslist = (plist_t *) malloc(sizeof(plist_t) * num_objects); + +	if (!nodeslist) +		return; + +	//parse serialized nodes +	uint64_t i = 0; +	uint64_t current_offset = 0; +	const char *offset_table = plist_bin + offset_table_index; +	for (i = 0; i < num_objects; i++) { +		current_offset = swap_n_bytes(offset_table + i * offset_size, offset_size); + +		log_debug_msg("parse_nodes: current_offset = %i\n", current_offset); +		char *obj = plist_bin + current_offset; +		nodeslist[i] = parse_bin_node(obj, dict_param_size, &obj); +		log_debug_msg("parse_nodes: parse_raw_node done\n"); +	} + +	//setup children for structured types +	int j = 0, str_i = 0, str_j = 0; +	uint32_t index1 = 0, index2 = 0; + +	for (i = 0; i < num_objects; i++) { + +		log_debug_msg("parse_nodes: on node %i\n", i); +		struct plist_data *data = (struct plist_data *) nodeslist[i]->data; + +		switch (data->type) { +		case PLIST_DICT: +			log_debug_msg("parse_nodes: dictionary found\n"); +			for (j = 0; j < data->length; j++) { +				str_i = j * dict_param_size; +				str_j = (j + data->length) * dict_param_size; + +				index1 = swap_n_bytes(data->buff + str_i, dict_param_size); +				index2 = swap_n_bytes(data->buff + str_j, dict_param_size); + +				//first one is actually a key +				((struct plist_data *) nodeslist[index1]->data)->type = PLIST_KEY; + +				if (G_NODE_IS_ROOT(nodeslist[index1])) +					g_node_append(nodeslist[i], nodeslist[index1]); +				else +					g_node_append(nodeslist[i], g_node_copy_deep(nodeslist[index1], copy_plist_data, NULL)); + +				if (G_NODE_IS_ROOT(nodeslist[index2])) +					g_node_append(nodeslist[i], nodeslist[index2]); +				else +					g_node_append(nodeslist[i], g_node_copy_deep(nodeslist[index2], copy_plist_data, NULL)); +			} + +			free(data->buff); +			break; + +		case PLIST_ARRAY: +			log_debug_msg("parse_nodes: array found\n"); +			for (j = 0; j < data->length; j++) { +				str_j = j * dict_param_size; +				index1 = swap_n_bytes(data->buff + str_j, dict_param_size); + +				//g_node_append(nodeslist[i], nodeslist[index1]); +				if (G_NODE_IS_ROOT(nodeslist[index1])) +					g_node_append(nodeslist[i], nodeslist[index1]); +				else +					g_node_append(nodeslist[i], g_node_copy_deep(nodeslist[index1], copy_plist_data, NULL)); +			} +			free(data->buff); +			break; +		default: +			break; +		} +	} + +	*plist = nodeslist[root_object]; +} + +guint plist_data_hash(gconstpointer key) +{ +	struct plist_data *data = (struct plist_data *) ((GNode *) key)->data; + +	guint hash = data->type; +	guint i = 0; + +	char *buff = NULL; +	guint size = 0; + +	switch (data->type) { +	case PLIST_BOOLEAN: +	case PLIST_UINT: +	case PLIST_REAL: +		buff = (char *) &data->intval; +		size = 8; +		break; +	case PLIST_KEY: +	case PLIST_STRING: +		buff = data->strval; +		size = strlen(buff); +		break; +	case PLIST_UNICODE: +		buff = data->unicodeval; +		size = strlen(buff) * sizeof(wchar_t); +		break; +	case PLIST_DATA: +	case PLIST_ARRAY: +	case PLIST_DICT: +		//for these types only hash pointer +		buff = &key; +		size = sizeof(gconstpointer); +		break; +	case PLIST_DATE: +	default: +		break; +	} + +	//now perform hash +	for (i = 0; i < size; buff++, i++) +		hash = hash << 7 ^ (*buff); + +	return hash; +} + +gboolean plist_data_compare(gconstpointer a, gconstpointer b) +{ +	if (!a || !b) +		return FALSE; + +	if (!((GNode *) a)->data || !((GNode *) b)->data) +		return FALSE; + +	struct plist_data *val_a = (struct plist_data *) ((GNode *) a)->data; +	struct plist_data *val_b = (struct plist_data *) ((GNode *) b)->data; + +	if (val_a->type != val_b->type) +		return FALSE; + +	switch (val_a->type) { +	case PLIST_BOOLEAN: +	case PLIST_UINT: +	case PLIST_REAL: +		if (val_a->intval == val_b->intval)	//it is an union so this is sufficient +			return TRUE; +		else +			return FALSE; + +	case PLIST_KEY: +	case PLIST_STRING: +		if (!strcmp(val_a->strval, val_b->strval)) +			return TRUE; +		else +			return FALSE; +	case PLIST_UNICODE: +		if (!strcmp(val_a->unicodeval, val_b->unicodeval)) +			return TRUE; +		else +			return FALSE; + +	case PLIST_DATA: +	case PLIST_ARRAY: +	case PLIST_DICT: +		//compare pointer +		if (a == b) +			return TRUE; +		else +			return FALSE; +		break; +	case PLIST_DATE: +	default: +		break; +	} +	return FALSE; +} + +struct serialize_s { +	GPtrArray *objects; +	GHashTable *ref_table; +}; + +void serialize_plist(GNode * node, gpointer data) +{ +	struct serialize_s *ser = (struct serialize_s *) data; +	uint64_t current_index = ser->objects->len; + +	//first check that node is not yet in objects +	gpointer val = g_hash_table_lookup(ser->ref_table, node); +	if (val) { +		//data is already in table +		return; +	} +	//insert new ref +	g_hash_table_insert(ser->ref_table, node, GUINT_TO_POINTER(current_index)); + +	//now append current node to object array +	g_ptr_array_add(ser->objects, node); + +	//now recurse on children +	g_node_children_foreach(node, G_TRAVERSE_ALL, serialize_plist, data); +	return; +} + + + +void write_int(GByteArray * bplist, uint64_t val) +{ +	uint64_t size = get_needed_bytes(val); +	uint8_t *buff = (uint8_t *) malloc(sizeof(uint8_t) + size); +	buff[0] = BPLIST_UINT | size >> 1; +	memcpy(buff + 1, &val, size); +	swap_n_bytes(buff + 1, size); +	g_byte_array_append(bplist, buff, sizeof(uint8_t) + size); +	free(buff); +} + +void write_real(GByteArray * bplist, double val) +{ +	uint64_t size = get_real_bytes(*((uint64_t *) & val));	//cheat to know used space +	uint8_t *buff = (uint8_t *) malloc(sizeof(uint8_t) + size); +	buff[0] = BPLIST_REAL | size >> 1; +	memcpy(buff + 1, &val, size); +	swap_n_bytes(buff + 1, size); +	g_byte_array_append(bplist, buff, sizeof(uint8_t) + size); +	free(buff); +} + +void write_raw_data(GByteArray * bplist, uint8_t mark, uint8_t * val, uint64_t size) +{ +	uint8_t marker = mark | (size < 15 ? size : 0xf); +	g_byte_array_append(bplist, &marker, sizeof(uint8_t)); +	if (size >= 15) { +		GByteArray *int_buff = g_byte_array_new(); +		write_int(int_buff, size); +		g_byte_array_append(bplist, int_buff->data, int_buff->len); +		g_byte_array_free(int_buff, TRUE); +	} +	uint8_t *buff = (uint8_t *) malloc(size); +	memcpy(buff, val, size); +	g_byte_array_append(bplist, buff, size); +	free(buff); +} + +void write_data(GByteArray * bplist, uint8_t * val, uint64_t size) +{ +	write_raw_data(bplist, BPLIST_DATA, val, size); +} + +void write_string(GByteArray * bplist, char *val) +{ +	uint64_t size = strlen(val); +	write_raw_data(bplist, BPLIST_STRING, val, size); +} + +void write_array(GByteArray * bplist, GNode * node, GHashTable * ref_table, uint8_t dict_param_size) +{ +	uint64_t size = g_node_n_children(node); +	uint8_t marker = BPLIST_ARRAY | (size < 15 ? size : 0xf); +	g_byte_array_append(bplist, &marker, sizeof(uint8_t)); +	if (size >= 15) { +		GByteArray *int_buff = g_byte_array_new(); +		write_int(int_buff, size); +		g_byte_array_append(bplist, int_buff->data, int_buff->len); +		g_byte_array_free(int_buff, TRUE); +	} + +	uint64_t idx = 0; +	uint8_t *buff = (uint8_t *) malloc(size * dict_param_size); + +	GNode *cur = NULL; +	int i = 0; +	for (i = 0, cur = node->children; cur && i < size; cur = cur->next, i++) { +		idx = GPOINTER_TO_UINT(g_hash_table_lookup(ref_table, cur)); +		memcpy(buff + i * dict_param_size, &idx, dict_param_size); +		swap_n_bytes(buff + i * dict_param_size, dict_param_size); +	} + +	//now append to bplist +	g_byte_array_append(bplist, buff, size * dict_param_size); +	free(buff); + +} + +void write_dict(GByteArray * bplist, GNode * node, GHashTable * ref_table, uint8_t dict_param_size) +{ +	uint64_t size = g_node_n_children(node) / 2; +	uint8_t marker = BPLIST_ARRAY | (size < 15 ? size : 0xf); +	g_byte_array_append(bplist, &marker, sizeof(uint8_t)); +	if (size >= 15) { +		GByteArray *int_buff = g_byte_array_new(); +		write_int(int_buff, size); +		g_byte_array_append(bplist, int_buff->data, int_buff->len); +		g_byte_array_free(int_buff, TRUE); +	} + +	uint64_t idx1 = 0; +	uint64_t idx2 = 0; +	uint8_t *buff = (uint8_t *) malloc(size * 2 * dict_param_size); + +	GNode *cur = NULL; +	int i = 0; +	for (i = 0, cur = node->children; cur && i < size; cur = cur->next->next, i++) { +		idx1 = GPOINTER_TO_UINT(g_hash_table_lookup(ref_table, cur)); +		memcpy(buff + i * dict_param_size, &idx1, dict_param_size); +		swap_n_bytes(buff + i * dict_param_size, dict_param_size); + +		idx2 = GPOINTER_TO_UINT(g_hash_table_lookup(ref_table, cur->next)); +		memcpy(buff + (i + size) * dict_param_size, &idx2, dict_param_size); +		swap_n_bytes(buff + (i + size) * dict_param_size, dict_param_size); +	} + +	//now append to bplist +	g_byte_array_append(bplist, buff, size * dict_param_size); +	free(buff); + +} + +void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) +{ +	//first serialize tree + +	//list of objects +	GPtrArray *objects = g_ptr_array_new(); +	//hashtable to write only once same nodes +	GHashTable *ref_table = g_hash_table_new(plist_data_hash, plist_data_compare); + +	//serialize plist +	struct serialize_s ser_s = { objects, ref_table }; +	g_node_children_foreach(plist, G_TRAVERSE_ALL, serialize_plist, &ser_s); + +	//now stream to output buffer +	uint8_t offset_size = 0;	//unknown yet +	uint8_t dict_param_size = get_needed_bytes(objects->len); +	uint64_t num_objects = objects->len; +	uint64_t root_object = 0;	//root is first in list +	uint64_t offset_table_index = 0;	//unknown yet + +	//setup a dynamic bytes array to store bplist in +	GByteArray *bplist_buff = g_byte_array_new(); + +	//set magic number and version +	g_byte_array_append(bplist_buff, BPLIST_MAGIC, BPLIST_MAGIC_SIZE); +	g_byte_array_append(bplist_buff, BPLIST_VERSION, BPLIST_VERSION_SIZE); + +	//write objects and table +	int i = 0; +	uint8_t *buff = NULL; +	uint8_t size = 0; +	uint64_t offsets[num_objects]; +	for (i = 0; i < num_objects; i++) { + +		offsets[i] = bplist_buff->len; +		struct plist_data *data = (struct plist_data *) ((GNode *) g_ptr_array_index(objects, i))->data; + +		switch (data->type) { +		case PLIST_BOOLEAN: +			buff = (uint8_t *) malloc(sizeof(uint8_t)); +			buff[0] = data->boolval ? BPLIST_TRUE : BPLIST_FALSE; +			g_byte_array_append(bplist_buff, buff, sizeof(uint8_t)); +			free(buff); +			break; + +		case PLIST_UINT: +			write_int(bplist_buff, data->intval); +			break; + +		case PLIST_REAL: +			write_real(bplist_buff, data->realval); +			break; + +		case PLIST_KEY: +		case PLIST_STRING: +			write_string(bplist_buff, data->strval); +			break; +		case PLIST_UNICODE: +			//TODO +			break; +		case PLIST_DATA: +			write_data(bplist_buff, data->strval, data->length); +		case PLIST_ARRAY: +			write_array(bplist_buff, g_ptr_array_index(objects, i), ref_table, dict_param_size); +			break; +		case PLIST_DICT: +			write_dict(bplist_buff, g_ptr_array_index(objects, i), ref_table, dict_param_size); +			break; +		case PLIST_DATE: +			//TODO +			break; +		default: +			break; +		} +	} + +	//write offsets +	offset_size = get_needed_bytes(bplist_buff->len); +	for (i = 0; i <= num_objects; i++) { +		uint8_t *buff = (uint8_t *) malloc(offset_size); +		memcpy(buff, offsets + i, offset_size); +		swap_n_bytes(buff, offset_size); +		g_byte_array_append(bplist_buff, buff, offset_size); +		free(buff); +	} + +	//setup trailer +	num_objects = bswap_64(num_objects); +	root_object = bswap_64(root_object); +	offset_table_index = bswap_64(offset_table_index); + +	char trailer[BPLIST_TRL_SIZE]; +	memcpy(trailer + BPLIST_TRL_OFFSIZE_IDX, &offset_size, sizeof(uint8_t)); +	memcpy(trailer + BPLIST_TRL_PARMSIZE_IDX, &dict_param_size, sizeof(uint8_t)); +	memcpy(trailer + BPLIST_TRL_NUMOBJ_IDX, &num_objects, sizeof(uint64_t)); +	memcpy(trailer + BPLIST_TRL_ROOTOBJ_IDX, &root_object, sizeof(uint64_t)); +	memcpy(trailer + BPLIST_TRL_OFFTAB_IDX, &offset_table_index, sizeof(uint64_t)); + +	g_byte_array_append(bplist_buff, trailer, BPLIST_TRL_SIZE); + +	//duplicate buffer +	*plist_bin = (char *) malloc(bplist_buff->len); +	memcpy(*plist_bin, bplist_buff->data, bplist_buff->len); +	*length = bplist_buff->len; + +	g_byte_array_free(bplist_buff, TRUE); +} diff --git a/src/xplist.c b/src/xplist.c new file mode 100644 index 0000000..a87b259 --- /dev/null +++ b/src/xplist.c @@ -0,0 +1,306 @@ +/* + * plist.c + * XML plist implementation + * + * Copyright (c) 2008 Jonathan Beck 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ + + +#include <string.h> +#include <assert.h> +#include "utils.h" +#include "plist.h" +#include <wchar.h> +#include <stdlib.h> +#include <stdio.h> + + +#include <libxml/parser.h> +#include <libxml/tree.h> + + +const char *plist_base = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ +<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\ +<plist version=\"1.0\">\n\ +</plist>\0"; + + +/** Formats a block of text to be a given indentation and width. + *  + * The total width of the return string will be depth + cols. + * + * @param buf The string to format. + * @param cols The number of text columns for returned block of text. + * @param depth The number of tabs to indent the returned block of text. + * + * @return The formatted string. + */ +char *format_string(const char *buf, int cols, int depth) +{ +	int colw = depth + cols + 1; +	int len = strlen(buf); +	int nlines = len / cols + 1; +	char *new_buf = (char *) malloc(nlines * colw + depth + 1); +	int i = 0; +	int j = 0; + +	assert(cols >= 0); +	assert(depth >= 0); + +	// Inserts new lines and tabs at appropriate locations +	for (i = 0; i < nlines; i++) { +		new_buf[i * colw] = '\n'; +		for (j = 0; j < depth; j++) +			new_buf[i * colw + 1 + j] = '\t'; +		memcpy(new_buf + i * colw + 1 + depth, buf + i * cols, cols); +	} +	new_buf[len + (1 + depth) * nlines] = '\n'; + +	// Inserts final row of indentation and termination character +	for (j = 0; j < depth; j++) +		new_buf[len + (1 + depth) * nlines + 1 + j] = '\t'; +	new_buf[len + (1 + depth) * nlines + depth + 1] = '\0'; + +	return new_buf; +} + + + +struct xml_node { +	xmlNodePtr xml; +	uint32_t depth; +}; + +/** Creates a new plist XML document. + *  + * @return The plist XML document. + */ +xmlDocPtr new_plist() +{ +	char *plist = strdup(plist_base); +	xmlDocPtr plist_xml = xmlReadMemory(plist, strlen(plist), NULL, NULL, 0); + +	if (!plist_xml) +		return NULL; + +	free(plist); + +	return plist_xml; +} + +/** Destroys a previously created XML document. + * + * @param plist The XML document to destroy. + */ +void free_plist(xmlDocPtr plist) +{ +	if (!plist) +		return; + +	xmlFreeDoc(plist); +} + +void node_to_xml(GNode * node, gpointer xml_struct) +{ +	if (!node) +		return; + +	struct xml_node *xstruct = (struct xml_node *) xml_struct; +	struct plist_data *node_data = (struct plist_data *) node->data; + +	xmlNodePtr child_node = NULL; +	char isStruct = FALSE; + +	gchar *tag = NULL; +	gchar *val = NULL; + +	switch (node_data->type) { +	case PLIST_BOOLEAN: +		{ +			if (node_data->boolval) +				tag = "true"; +			else +				tag = "false"; +		} +		break; + +	case PLIST_UINT: +		tag = "integer"; +		val = g_strdup_printf("%lu", (long unsigned int) node_data->intval); +		break; + +	case PLIST_REAL: +		tag = "real"; +		val = g_strdup_printf("%Lf", (long double) node_data->realval); +		break; + +	case PLIST_STRING: +		tag = "string"; +		val = g_strdup(node_data->strval); +		break; + +	case PLIST_UNICODE: +		tag = "string"; +		val = g_strdup((gchar *) node_data->unicodeval); +		break; + +	case PLIST_KEY: +		tag = "key"; +		val = g_strdup((gchar *) node_data->strval); +		break; + +	case PLIST_DATA: +		tag = "data"; +		val = format_string(node_data->buff, 60, xstruct->depth); +		break; +	case PLIST_ARRAY: +		tag = "array"; +		isStruct = TRUE; +		break; +	case PLIST_DICT: +		tag = "dict"; +		isStruct = TRUE; +		break; +	case PLIST_DATE:			//TODO : handle date tag +	default: +		break; +	} + +	int i = 0; +	for (i = 0; i < xstruct->depth; i++) { +		xmlNodeAddContent(xstruct->xml, "\t"); +	} +	child_node = xmlNewChild(xstruct->xml, NULL, tag, val); +	xmlNodeAddContent(xstruct->xml, "\n"); +	g_free(val); + +	//add return for structured types +	if (node_data->type == PLIST_ARRAY || node_data->type == PLIST_DICT || node_data->type == PLIST_DATA) +		xmlNodeAddContent(child_node, "\n"); + +	if (isStruct) { +		struct xml_node child = { child_node, xstruct->depth + 1 }; +		g_node_children_foreach(node, G_TRAVERSE_ALL, node_to_xml, &child); +	} +	//fix indent for structured types +	if (node_data->type == PLIST_ARRAY || node_data->type == PLIST_DICT || node_data->type == PLIST_DATA) { + +		for (i = 0; i < xstruct->depth; i++) { +			xmlNodeAddContent(child_node, "\t"); +		} +	} + +	return; +} + +void xml_to_node(xmlNodePtr xml_node, GNode * plist_node) +{ +	xmlNodePtr node = NULL; + +	for (node = xml_node->children; node; node = node->next) { + +		while (node && !xmlStrcmp(node->name, "text")) +			node = node->next; +		if (!node) +			break; + +		struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); +		GNode *subnode = g_node_new(data); +		g_node_append(plist_node, subnode); + +		if (!xmlStrcmp(node->name, "true")) { +			data->boolval = 1; +			data->type = PLIST_BOOLEAN; +			continue; +		} + +		if (!xmlStrcmp(node->name, "false")) { +			data->boolval = 0; +			data->type = PLIST_BOOLEAN; +			continue; +		} + +		if (!xmlStrcmp(node->name, "integer")) { +			char *strval = xmlNodeGetContent(node); +			data->intval = atoi(strval); +			data->type = PLIST_UINT; +			continue; +		} + +		if (!xmlStrcmp(node->name, "real")) { +			char *strval = xmlNodeGetContent(node); +			data->realval = atof(strval); +			data->type = PLIST_REAL; +			continue; +		} + +		if (!xmlStrcmp(node->name, "date")) +			continue;			//TODO : handle date tag + +		if (!xmlStrcmp(node->name, "string")) { +			data->strval = strdup(xmlNodeGetContent(node)); +			data->type = PLIST_STRING; +			continue; +		} + +		if (!xmlStrcmp(node->name, "key")) { +			data->strval = strdup(xmlNodeGetContent(node)); +			data->type = PLIST_KEY; +			continue; +		} + +		if (!xmlStrcmp(node->name, "data")) { +			data->buff = strdup(xmlNodeGetContent(node)); +			data->type = PLIST_DATA; +			continue; +		} + +		if (!xmlStrcmp(node->name, "array")) { +			data->type = PLIST_ARRAY; +			xml_to_node(node, subnode); +			continue; +		} + +		if (!xmlStrcmp(node->name, "dict")) { +			data->type = PLIST_DICT; +			xml_to_node(node, subnode); +			continue; +		} +	} +} + +void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) +{ +	if (!plist || !plist_xml || *plist_xml) +		return; +	xmlDocPtr plist_doc = new_plist(); +	xmlNodePtr root_node = xmlDocGetRootElement(plist_doc); +	struct xml_node root = { root_node, 0 }; +	g_node_children_foreach(plist, G_TRAVERSE_ALL, node_to_xml, &root); +	xmlDocDumpMemory(plist_doc, (xmlChar **) plist_xml, length); +} + +void xml_to_plist(const char *plist_xml, uint32_t length, plist_t * plist) +{ +	xmlDocPtr plist_doc = xmlReadMemory(plist_xml, length, NULL, NULL, 0); +	xmlNodePtr root_node = xmlDocGetRootElement(plist_doc); + +	struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); +	*plist = g_node_new(data); +	data->type = PLIST_DICT; +	xml_to_node(root_node, *plist); +} | 
