int plist_data_compare(const void *a, const void *b) { plist_data_t val_a = NULL; plist_data_t val_b = NULL; if (!a || !b) return FALSE; if (!((node_t*) a)->data || !((node_t*) b)->data) return FALSE; val_a = plist_get_data((plist_t) a); val_b = plist_get_data((plist_t) b); if (val_a->type != val_b->type) return FALSE; switch (val_a->type) { case PLIST_BOOLEAN: case PLIST_UINT: case PLIST_REAL: case PLIST_UID: 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_DATA: if (val_a->length != val_b->length) return FALSE; if (!memcmp(val_a->buff, val_b->buff, val_a->length)) return TRUE; else return FALSE; case PLIST_ARRAY: case PLIST_DICT: //compare pointer if (a == b) return TRUE; else return FALSE; break; case PLIST_DATE: if (!memcmp(&(val_a->timeval), &(val_b->timeval), sizeof(struct timeval))) return TRUE; else return FALSE; default: break; } return FALSE; }
void plist_set_type(plist_t node, plist_type type) { if ( node_n_children(node) == 0 ) { plist_data_t data = plist_get_data(node); plist_free_data( data ); data = plist_new_plist_data(); data->type = type; switch (type) { case PLIST_BOOLEAN: data->length = sizeof(uint8_t); break; case PLIST_UINT: case PLIST_UID: data->length = sizeof(uint64_t); break; case PLIST_REAL: data->length = sizeof(double); break; case PLIST_DATE: data->length = sizeof(struct timeval); break; default: data->length = 0; break; } } }
plist_t plist_dict_get_item(plist_t node, const char* key) { plist_t ret = NULL; if (node && PLIST_DICT == plist_get_node_type(node)) { plist_t current = NULL; for (current = (plist_t)node_first_child(node); current; current = (plist_t)node_next_sibling(node_next_sibling(current))) { plist_data_t data = plist_get_data(current); assert( PLIST_KEY == plist_get_node_type(current) ); if (data && !strcmp(key, data->strval)) { ret = (plist_t)node_next_sibling(current); break; } } } return ret; }
static void plist_copy_node(GNode * node, gpointer parent_node_ptr) { plist_t newnode = NULL; plist_data_t data = plist_get_data(node); plist_data_t newdata = plist_new_plist_data(); assert(data); // plist should always have data memcpy(newdata, data, sizeof(struct plist_data_s)); plist_type node_type = plist_get_node_type(node); if (node_type == PLIST_DATA || node_type == PLIST_STRING || node_type == PLIST_KEY) { switch (node_type) { case PLIST_DATA: newdata->buff = (uint8_t *) malloc(data->length); memcpy(newdata->buff, data->buff, data->length); case PLIST_KEY: case PLIST_STRING: newdata->strval = strdup((char *) data->strval); default: break; } } newnode = plist_new_node(newdata); if (*(plist_t*)parent_node_ptr) { g_node_append(*(plist_t*)parent_node_ptr, newnode); } else { *(plist_t*)parent_node_ptr = newnode; } g_node_children_foreach(node, G_TRAVERSE_ALL, plist_copy_node, &newnode); }
plist_type plist_get_node_type(plist_t node) { if (node) { plist_data_t data = plist_get_data(node); if (data) return data->type; } return PLIST_NONE; }
static void plist_set_element_val(plist_t node, plist_type type, const void *value, uint64_t length) { //free previous allocated buffer plist_data_t data = plist_get_data(node); assert(data); // a node should always have data attached switch (data->type) { case PLIST_KEY: case PLIST_STRING: free(data->strval); data->strval = NULL; break; case PLIST_DATA: free(data->buff); data->buff = NULL; break; default: break; } //now handle value data->type = type; data->length = length; switch (type) { case PLIST_BOOLEAN: data->boolval = *((char *) value); break; case PLIST_UINT: case PLIST_UID: data->intval = *((uint64_t *) value); break; case PLIST_REAL: data->realval = *((double *) value); break; case PLIST_KEY: case PLIST_STRING: data->strval = strdup((char *) value); break; case PLIST_DATA: data->buff = (uint8_t *) malloc(length); memcpy(data->buff, value, length); break; case PLIST_DATE: data->timeval.tv_sec = ((struct timeval*) value)->tv_sec; data->timeval.tv_usec = ((struct timeval*) value)->tv_usec; break; case PLIST_ARRAY: case PLIST_DICT: default: break; } }
static plist_t parse_date_node(char *bnode, uint8_t size) { plist_t node = parse_real_node(bnode, size); plist_data_t data = plist_get_data(node); double time_real = data->realval; data->timeval.tv_sec = (glong) time_real; data->timeval.tv_usec = (time_real - (glong) time_real) * G_USEC_PER_SEC; data->type = PLIST_DATE; data->length = sizeof(GTimeVal); return node; }
static void plist_copy_node(node_t *node, void *parent_node_ptr) { plist_type node_type = PLIST_NONE; plist_t newnode = NULL; plist_data_t data = plist_get_data(node); plist_data_t newdata = plist_new_plist_data(); assert(data); // plist should always have data memcpy(newdata, data, sizeof(struct plist_data_s)); node_type = plist_get_node_type(node); if (node_type == PLIST_DATA || node_type == PLIST_STRING || node_type == PLIST_KEY) { switch (node_type) { case PLIST_DATA: newdata->buff = (uint8_t *) malloc(data->length); memcpy(newdata->buff, data->buff, data->length); break; case PLIST_KEY: case PLIST_STRING: newdata->strval = strdup((char *) data->strval); break; default: break; } } newnode = plist_new_node(newdata); if (*(plist_t*)parent_node_ptr) { node_attach(*(plist_t*)parent_node_ptr, newnode); } else { *(plist_t*)parent_node_ptr = newnode; } node_iterator_t *ni = node_iterator_create(node->children); node_t *ch; while ((ch = node_iterator_next(ni))) { plist_copy_node(ch, &newnode); } node_iterator_destroy(ni); }
static guint plist_data_hash(gconstpointer key) { plist_data_t data = plist_get_data((plist_t) key); 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; //works also for real as we use an union size = 8; break; case PLIST_KEY: case PLIST_STRING: buff = data->strval; size = strlen(buff); break; case PLIST_DATA: case PLIST_ARRAY: case PLIST_DICT: //for these types only hash pointer buff = (char *) &key; size = sizeof(gconstpointer); break; case PLIST_DATE: buff = (char *) &(data->timeval); size = data->length; break; default: break; } //now perform hash for (i = 0; i < size; buff++, i++) hash = hash << 7 ^ (*buff); return hash; }
static int plist_free_node(node_t* node) { plist_data_t data = NULL; int index = node_detach(node->parent, node); data = plist_get_data(node); plist_free_data(data); node->data = NULL; node_iterator_t *ni = node_iterator_create(node->children); node_t *ch; while ((ch = node_iterator_next(ni))) { plist_free_node(ch); } node_iterator_destroy(ni); node_destroy(node); return index; }
static void plist_get_type_and_value(plist_t node, plist_type * type, void *value, uint64_t * length) { plist_data_t data = NULL; if (!node) return; data = plist_get_data(node); *type = data->type; *length = data->length; switch (*type) { case PLIST_BOOLEAN: *((char *) value) = data->boolval; break; case PLIST_UINT: case PLIST_UID: *((uint64_t *) value) = data->intval; break; case PLIST_REAL: *((double *) value) = data->realval; break; case PLIST_KEY: case PLIST_STRING: *((char **) value) = strdup(data->strval); break; case PLIST_DATA: *((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t)); memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t)); break; case PLIST_DATE: //exception : here we use memory on the stack since it is just a temporary buffer ((struct timeval*) value)->tv_sec = data->timeval.tv_sec; ((struct timeval*) value)->tv_usec = data->timeval.tv_usec; break; case PLIST_ARRAY: case PLIST_DICT: default: break; } }
static void plist_free_node(GNode * node, gpointer none) { g_node_unlink(node); plist_data_t data = plist_get_data(node); if (data) { switch (data->type) { case PLIST_KEY: case PLIST_STRING: free(data->strval); break; case PLIST_DATA: free(data->buff); break; default: break; } free(data); } node->data = NULL; g_node_children_foreach(node, G_TRAVERSE_ALL, plist_free_node, NULL); }
static plist_t plist_find_node(plist_t plist, plist_type type, const void *value, uint64_t length) { plist_t current = NULL; if (!plist) return NULL; for (current = plist_get_first_child(plist); current; current = plist_get_next_sibling(current)) { plist_data_t data = plist_get_data(current); if (data->type == type && data->length == length && compare_node_value(type, data, value, length)) { return current; } if (data->type == PLIST_DICT || data->type == PLIST_ARRAY) { plist_t sub = plist_find_node(current, type, value, length); if (sub) return sub; } } return NULL; }
static void xml_to_node(xmlNodePtr xml_node, plist_t * plist_node) { xmlNodePtr node = NULL; plist_data_t data = NULL; plist_t subnode = NULL; //for string long len = 0; int type = 0; if (!xml_node) return; for (node = xml_node->children; node; node = node->next) { while (node && !xmlStrcmp(node->name, XPLIST_TEXT)) node = node->next; if (!node) break; if (!xmlStrcmp(node->name, BAD_CAST("comment"))) { continue; } data = plist_new_plist_data(); subnode = plist_new_node(data); if (*plist_node) node_attach(*plist_node, subnode); else *plist_node = subnode; if (!xmlStrcmp(node->name, XPLIST_TRUE)) { data->boolval = TRUE; data->type = PLIST_BOOLEAN; data->length = 1; continue; } if (!xmlStrcmp(node->name, XPLIST_FALSE)) { data->boolval = FALSE; data->type = PLIST_BOOLEAN; data->length = 1; continue; } if (!xmlStrcmp(node->name, XPLIST_INT)) { xmlChar *strval = xmlNodeGetContent(node); int is_negative = 0; char *str = (char*)strval; if ((str[0] == '-') || (str[0] == '+')) { if (str[0] == '-') { is_negative = 1; } str++; } char* endp = NULL; data->intval = strtoull((char*)str, &endp, 0); if ((endp != NULL) && (strlen(endp) > 0)) { fprintf(stderr, "%s: integer parse error: string contains invalid characters: '%s'\n", __func__, endp); } if (is_negative || (data->intval <= INT64_MAX)) { int64_t v = data->intval; if (is_negative) { v = -v; } data->intval = (uint64_t)v; data->length = 8; } else { data->length = 16; } data->type = PLIST_UINT; xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_REAL)) { xmlChar *strval = xmlNodeGetContent(node); data->realval = atof((char *) strval); data->type = PLIST_REAL; data->length = 8; xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_DATE)) { xmlChar *strval = xmlNodeGetContent(node); time_t timev = 0; if (strlen((const char*)strval) >= 11) { struct tm btime; struct tm* tm_utc; parse_date((const char*)strval, &btime); timev = mktime(&btime); tm_utc = gmtime(&timev); timev -= (mktime(tm_utc) - timev); } data->timeval.tv_sec = (long)(timev - MAC_EPOCH); data->timeval.tv_usec = 0; data->type = PLIST_DATE; data->length = sizeof(struct timeval); xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_STRING)) { xmlChar *strval = xmlNodeGetContent(node); len = strlen((char *) strval); type = xmlDetectCharEncoding(strval, len); if (XML_CHAR_ENCODING_UTF8 == type || XML_CHAR_ENCODING_ASCII == type || XML_CHAR_ENCODING_NONE == type) { data->strval = strdup((char *) strval); data->type = PLIST_STRING; data->length = strlen(data->strval); } xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_KEY)) { xmlChar *strval = xmlNodeGetContent(node); len = strlen((char *) strval); type = xmlDetectCharEncoding(strval, len); if (XML_CHAR_ENCODING_UTF8 == type || XML_CHAR_ENCODING_ASCII == type || XML_CHAR_ENCODING_NONE == type) { data->strval = strdup((char *) strval); data->type = PLIST_KEY; data->length = strlen(data->strval); } xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_DATA)) { xmlChar *strval = xmlNodeGetContent(node); size_t size = 0; unsigned char *dec = base64decode((char*)strval, &size); data->buff = (uint8_t *) malloc(size * sizeof(uint8_t)); memcpy(data->buff, dec, size * sizeof(uint8_t)); free(dec); data->length = size; data->type = PLIST_DATA; xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_ARRAY)) { data->type = PLIST_ARRAY; xml_to_node(node, &subnode); continue; } if (!xmlStrcmp(node->name, XPLIST_DICT)) { data->type = PLIST_DICT; xml_to_node(node, &subnode); if (plist_get_node_type(subnode) == PLIST_DICT) { if (plist_dict_get_size(subnode) == 1) { plist_t uid = plist_dict_get_item(subnode, "CF$UID"); if (uid) { uint64_t val = 0; plist_get_uint_val(uid, &val); plist_dict_remove_item(subnode, "CF$UID"); plist_data_t nodedata = plist_get_data((node_t*)subnode); free(nodedata->buff); nodedata->type = PLIST_UID; nodedata->length = sizeof(uint64_t); nodedata->intval = val; } } } continue; } } }
static void node_to_xml(node_t* node, void *xml_struct) { struct xml_node *xstruct = NULL; plist_data_t node_data = NULL; xmlNodePtr child_node = NULL; char isStruct = FALSE; char isUIDNode = FALSE; const xmlChar *tag = NULL; char *val = NULL; //for base64 char *valtmp = NULL; uint32_t i = 0; if (!node) return; xstruct = (struct xml_node *) xml_struct; node_data = plist_get_data(node); switch (node_data->type) { case PLIST_BOOLEAN: { if (node_data->boolval) tag = XPLIST_TRUE; else tag = XPLIST_FALSE; } break; case PLIST_UINT: tag = XPLIST_INT; val = (char*)malloc(64); if (node_data->length == 16) { (void)snprintf(val, 64, "%"PRIu64, node_data->intval); } else { (void)snprintf(val, 64, "%"PRIi64, node_data->intval); } break; case PLIST_REAL: tag = XPLIST_REAL; val = (char*)malloc(64); (void)snprintf(val, 64, "%f", node_data->realval); break; case PLIST_STRING: tag = XPLIST_STRING; val = strdup((char*) node_data->strval); break; case PLIST_KEY: tag = XPLIST_KEY; val = strdup((char*) node_data->strval); break; case PLIST_DATA: tag = XPLIST_DATA; if (node_data->length) { size_t len = node_data->length; valtmp = base64encode(node_data->buff, &len); val = format_string(valtmp, len, 68, xstruct->depth); free(valtmp); } break; case PLIST_ARRAY: tag = XPLIST_ARRAY; isStruct = TRUE; break; case PLIST_DICT: tag = XPLIST_DICT; isStruct = TRUE; break; case PLIST_DATE: tag = XPLIST_DATE; { time_t timev = (time_t)node_data->timeval.tv_sec + MAC_EPOCH; struct tm *btime = gmtime(&timev); if (btime) { val = (char*)malloc(24); memset(val, 0, 24); if (strftime(val, 24, "%Y-%m-%dT%H:%M:%SZ", btime) <= 0) { free (val); val = NULL; } } } break; case PLIST_UID: // special case for keyed encoding tag = XPLIST_DICT; isStruct = TRUE; isUIDNode = TRUE; node_data->type = PLIST_DICT; node_attach(node, new_key_node("CF$UID")); node_attach(node, new_uint_node(node_data->intval)); break; default: break; } for (i = 0; i < xstruct->depth; i++) { xmlNodeAddContent(xstruct->xml, BAD_CAST("\t")); } if (node_data->type == PLIST_STRING || node_data->type == PLIST_KEY) { /* make sure we convert the following predefined xml entities */ /* < = < > = > ' = ' " = " & = & */ child_node = xmlNewTextChild(xstruct->xml, NULL, tag, BAD_CAST(val)); } else child_node = xmlNewChild(xstruct->xml, NULL, tag, BAD_CAST(val)); xmlNodeAddContent(xstruct->xml, BAD_CAST("\n")); if (val) { free(val); } //add return for structured types if (node_data->type == PLIST_ARRAY || node_data->type == PLIST_DICT) xmlNodeAddContent(child_node, BAD_CAST("\n")); //make sure we don't produce <data/> if it's empty if ((node_data->type == PLIST_DATA) && !val) { xmlNodeAddContent(child_node, BAD_CAST("\n")); for (i = 0; i < xstruct->depth; i++) { xmlNodeAddContent(child_node, BAD_CAST("\t")); } } if (isStruct) { struct xml_node child = { child_node, xstruct->depth + 1 }; node_iterator_t *ni = node_iterator_create(node->children); node_t *ch; while ((ch = node_iterator_next(ni))) { node_to_xml(ch, &child); } node_iterator_destroy(ni); } //fix indent for structured types if (node_data->type == PLIST_ARRAY || node_data->type == PLIST_DICT) { for (i = 0; i < xstruct->depth; i++) { xmlNodeAddContent(child_node, BAD_CAST("\t")); } } if (isUIDNode) { unsigned int num = node_n_children(node); unsigned int j; for (j = num; j > 0; j--) { node_t* ch = node_nth_child(node, j-1); node_detach(node, ch); node_destroy(ch); } node_data->type = PLIST_UID; } return; }
static void xml_to_node(xmlNodePtr xml_node, plist_t * plist_node) { xmlNodePtr node = NULL; plist_data_t data = NULL; plist_t subnode = NULL; //for string long len = 0; int type = 0; if (!xml_node) return; for (node = xml_node->children; node; node = node->next) { while (node && !xmlStrcmp(node->name, XPLIST_TEXT)) node = node->next; if (!node) break; if (!xmlStrcmp(node->name, BAD_CAST("comment"))) { continue; } data = plist_new_plist_data(); subnode = plist_new_node(data); if (*plist_node) node_attach(*plist_node, subnode); else *plist_node = subnode; if (!xmlStrcmp(node->name, XPLIST_TRUE)) { data->boolval = TRUE; data->type = PLIST_BOOLEAN; data->length = 1; continue; } if (!xmlStrcmp(node->name, XPLIST_FALSE)) { data->boolval = FALSE; data->type = PLIST_BOOLEAN; data->length = 1; continue; } if (!xmlStrcmp(node->name, XPLIST_INT)) { xmlChar *strval = xmlNodeGetContent(node); data->intval = strtoull((char*)strval, NULL, 0); data->type = PLIST_UINT; data->length = 8; xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_REAL)) { xmlChar *strval = xmlNodeGetContent(node); data->realval = atof((char *) strval); data->type = PLIST_REAL; data->length = 8; xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_DATE)) { xmlChar *strval = xmlNodeGetContent(node); time_t time = 0; if (strlen((const char*)strval) >= 11) { struct tm btime; parse_date((const char*)strval, &btime); time = mktime(&btime); } data->timeval.tv_sec = (long)time; data->timeval.tv_usec = 0; data->type = PLIST_DATE; data->length = sizeof(struct timeval); xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_STRING)) { xmlChar *strval = xmlNodeGetContent(node); len = strlen((char *) strval); type = xmlDetectCharEncoding(strval, len); if (XML_CHAR_ENCODING_UTF8 == type || XML_CHAR_ENCODING_ASCII == type || XML_CHAR_ENCODING_NONE == type) { data->strval = strdup((char *) strval); data->type = PLIST_STRING; data->length = strlen(data->strval); } xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_KEY)) { xmlChar *strval = xmlNodeGetContent(node); data->strval = strdup((char *) strval); data->type = PLIST_KEY; data->length = strlen(data->strval); xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_DATA)) { xmlChar *strval = xmlNodeGetContent(node); size_t size = 0; unsigned char *dec = base64decode((char*)strval, &size); data->buff = (uint8_t *) malloc(size * sizeof(uint8_t)); memcpy(data->buff, dec, size * sizeof(uint8_t)); free(dec); data->length = size; data->type = PLIST_DATA; xmlFree(strval); continue; } if (!xmlStrcmp(node->name, XPLIST_ARRAY)) { data->type = PLIST_ARRAY; xml_to_node(node, &subnode); continue; } if (!xmlStrcmp(node->name, XPLIST_DICT)) { data->type = PLIST_DICT; xml_to_node(node, &subnode); if (plist_get_node_type(subnode) == PLIST_DICT) { if (plist_dict_get_size(subnode) == 1) { plist_t uid = plist_dict_get_item(subnode, "CF$UID"); if (uid) { uint64_t val = 0; plist_get_uint_val(uid, &val); plist_dict_remove_item(subnode, "CF$UID"); plist_data_t nodedata = plist_get_data((node_t*)subnode); free(nodedata->buff); nodedata->type = PLIST_UID; nodedata->length = sizeof(uint64_t); nodedata->intval = val; } } } continue; } } }
void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist) { char *trailer = NULL; uint8_t offset_size = 0; uint8_t dict_param_size = 0; uint64_t num_objects = 0; uint64_t root_object = 0; uint64_t offset_table_index = 0; plist_t *nodeslist = NULL; uint64_t i = 0; uint64_t current_offset = 0; char *offset_table = NULL; uint32_t j = 0, str_i = 0, str_j = 0; uint32_t index1 = 0, index2 = 0; //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 trailer = (char *) (plist_bin + (length - BPLIST_TRL_SIZE)); offset_size = trailer[BPLIST_TRL_OFFSIZE_IDX]; dict_param_size = trailer[BPLIST_TRL_PARMSIZE_IDX]; num_objects = be64dec(trailer + BPLIST_TRL_NUMOBJ_IDX); root_object = be64dec(trailer + BPLIST_TRL_ROOTOBJ_IDX); offset_table_index = be64dec(trailer + BPLIST_TRL_OFFTAB_IDX); if (num_objects == 0) return; //allocate serialized array of nodes nodeslist = (plist_t *) malloc(sizeof(plist_t) * num_objects); if (!nodeslist) return; //parse serialized nodes offset_table = (char *) (plist_bin + offset_table_index); for (i = 0; i < num_objects; i++) { char *obj = NULL; current_offset = UINT_TO_HOST(offset_table + i * offset_size, offset_size); obj = (char *) (plist_bin + current_offset); nodeslist[i] = parse_bin_node(obj, dict_param_size, &obj); } //setup children for structured types for (i = 0; i < num_objects; i++) { plist_data_t data = plist_get_data(nodeslist[i]); switch (data->type) { case PLIST_DICT: for (j = 0; j < data->length; j++) { str_i = j * dict_param_size; str_j = (j + data->length) * dict_param_size; index1 = UINT_TO_HOST(data->buff + str_i, dict_param_size); index2 = UINT_TO_HOST(data->buff + str_j, dict_param_size); //first one is actually a key plist_get_data(nodeslist[index1])->type = PLIST_KEY; if (index1 < num_objects) { 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 (index2 < num_objects) { 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: for (j = 0; j < data->length; j++) { str_j = j * dict_param_size; index1 = UINT_TO_HOST(data->buff + str_j, dict_param_size); if (index1 < num_objects) { 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]; free(nodeslist); }
void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) { GPtrArray *objects = NULL; GHashTable *ref_table = NULL; struct serialize_s ser_s; uint8_t offset_size = 0; uint8_t dict_param_size = 0; uint64_t num_objects = 0; uint64_t root_object = 0; uint64_t offset_table_index = 0; GByteArray *bplist_buff = NULL; uint64_t i = 0; uint8_t *buff = NULL; uint64_t *offsets = NULL; uint8_t pad[6] = { 0, 0, 0, 0, 0, 0 }; uint8_t trailer[BPLIST_TRL_SIZE]; //for string glong len = 0; int type = 0; glong items_read = 0; glong items_written = 0; GError *error = NULL; gunichar2 *unicodestr = NULL; //check for valid input if (!plist || !plist_bin || *plist_bin || !length) return; //list of objects objects = g_ptr_array_new(); //hashtable to write only once same nodes ref_table = g_hash_table_new(plist_data_hash, plist_data_compare); //serialize plist ser_s.objects = objects; ser_s.ref_table = ref_table; serialize_plist(plist, &ser_s); //now stream to output buffer offset_size = 0; //unknown yet dict_param_size = get_needed_bytes(objects->len); num_objects = objects->len; root_object = 0; //root is first in list offset_table_index = 0; //unknown yet //setup a dynamic bytes array to store bplist in 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 offsets = (uint64_t *) malloc(num_objects * sizeof(uint64_t)); for (i = 0; i < num_objects; i++) { plist_data_t data = plist_get_data(g_ptr_array_index(objects, i)); offsets[i] = bplist_buff->len; 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: len = strlen(data->strval); if ( is_ascii_string(data->strval, len) ) { write_string(bplist_buff, data->strval); } else { unicodestr = g_utf8_to_utf16(data->strval, len, &items_read, &items_written, &error); write_unicode(bplist_buff, unicodestr, items_written); g_free(unicodestr); } break; case PLIST_DATA: write_data(bplist_buff, data->buff, 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: write_date(bplist_buff, data->timeval.tv_sec + (double) data->timeval.tv_usec / G_USEC_PER_SEC); break; default: break; } } //free intermediate objects g_hash_table_foreach_remove(ref_table, free_index, NULL); g_ptr_array_free(objects, TRUE); g_hash_table_destroy(ref_table); //write offsets offset_size = get_needed_bytes(bplist_buff->len); offset_table_index = bplist_buff->len; for (i = 0; i < num_objects; i++) { uint8_t *offsetbuff = (uint8_t *) malloc(offset_size); #if G_BYTE_ORDER == G_BIG_ENDIAN offsets[i] = offsets[i] << ((sizeof(uint64_t) - offset_size) * 8); #endif memcpy(offsetbuff, &offsets[i], offset_size); byte_convert(offsetbuff, offset_size); g_byte_array_append(bplist_buff, offsetbuff, offset_size); free(offsetbuff); } //experimental pad to reflect apple's files g_byte_array_append(bplist_buff, pad, 6); //setup trailer num_objects = GUINT64_FROM_BE(num_objects); root_object = GUINT64_FROM_BE(root_object); offset_table_index = GUINT64_FROM_BE(offset_table_index); 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); free(offsets); }