diff options
Diffstat (limited to 'src/xplist.c')
| -rw-r--r-- | src/xplist.c | 683 |
1 files changed, 448 insertions, 235 deletions
diff --git a/src/xplist.c b/src/xplist.c index a7d52e5..de5227a 100644 --- a/src/xplist.c +++ b/src/xplist.c @@ -39,14 +39,15 @@ #include <float.h> #include <math.h> #include <limits.h> +#include <errno.h> #include <node.h> -#include <node_list.h> #include "plist.h" #include "base64.h" #include "strbuf.h" #include "time64.h" +#include "hashtable.h" #define XPLIST_KEY "key" #define XPLIST_KEY_LEN 3 @@ -71,7 +72,7 @@ #define MAC_EPOCH 978307200 -#define MAX_DATA_BYTES_PER_LINE(__i) (((76 - (__i << 3)) >> 2) * 3) +#define MAX_DATA_BYTES_PER_LINE(__i) (((76 - ((__i) << 3)) >> 2) * 3) static const char XML_PLIST_PROLOG[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\ @@ -81,8 +82,10 @@ static const char XML_PLIST_EPILOG[] = "</plist>\n"; #ifdef DEBUG static int plist_xml_debug = 0; #define PLIST_XML_ERR(...) if (plist_xml_debug) { fprintf(stderr, "libplist[xmlparser] ERROR: " __VA_ARGS__); } +#define PLIST_XML_WRITE_ERR(...) if (plist_xml_debug) { fprintf(stderr, "libplist[xmlwriter] ERROR: " __VA_ARGS__); } #else #define PLIST_XML_ERR(...) +#define PLIST_XML_WRITE_ERR(...) #endif void plist_xml_init(void) @@ -101,6 +104,13 @@ void plist_xml_deinit(void) /* deinit XML stuff */ } +void plist_xml_set_debug(int debug) +{ +#if DEBUG + plist_xml_debug = debug; +#endif +} + static size_t dtostr(char *buf, size_t bufsize, double realval) { size_t len = 0; @@ -113,7 +123,7 @@ static size_t dtostr(char *buf, size_t bufsize, double realval) } else { size_t i = 0; len = snprintf(buf, bufsize, "%.*g", 17, realval); - for (i = 0; i < len; i++) { + for (i = 0; buf && i < len; i++) { if (buf[i] == ',') { buf[i] = '.'; break; @@ -125,7 +135,7 @@ static size_t dtostr(char *buf, size_t bufsize, double realval) return len; } -static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) +static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth) { plist_data_t node_data = NULL; @@ -134,13 +144,15 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) const char *tag = NULL; size_t tag_len = 0; - char *val = NULL; + char val[64] = { 0 }; size_t val_len = 0; uint32_t i = 0; - if (!node) - return; + if (!node) { + PLIST_XML_WRITE_ERR("Encountered invalid empty node in property list\n"); + return PLIST_ERR_INVALID_ARG; + } node_data = plist_get_data(node); @@ -158,22 +170,20 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) } break; - case PLIST_UINT: + case PLIST_INT: tag = XPLIST_INT; tag_len = XPLIST_INT_LEN; - val = (char*)malloc(64); if (node_data->length == 16) { - val_len = snprintf(val, 64, "%"PRIu64, node_data->intval); + val_len = snprintf(val, sizeof(val), "%" PRIu64, node_data->intval); } else { - val_len = snprintf(val, 64, "%"PRIi64, node_data->intval); + val_len = snprintf(val, sizeof(val), "%" PRIi64, node_data->intval); } break; case PLIST_REAL: tag = XPLIST_REAL; tag_len = XPLIST_REAL_LEN; - val = (char*)malloc(64); - val_len = dtostr(val, 64, node_data->realval); + val_len = dtostr(val, sizeof(val), node_data->realval); break; case PLIST_STRING: @@ -211,14 +221,11 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) struct TM _btime; struct TM *btime = gmtime64_r(&timev, &_btime); if (btime) { - val = (char*)malloc(24); - memset(val, 0, 24); struct tm _tmcopy; copy_TM64_to_tm(btime, &_tmcopy); - val_len = strftime(val, 24, "%Y-%m-%dT%H:%M:%SZ", &_tmcopy); + val_len = strftime(val, sizeof(val), "%Y-%m-%dT%H:%M:%SZ", &_tmcopy); if (val_len <= 0) { - free (val); - val = NULL; + snprintf(val, sizeof(val), "1970-01-01T00:00:00Z"); } } } @@ -226,15 +233,17 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) case PLIST_UID: tag = XPLIST_DICT; tag_len = XPLIST_DICT_LEN; - val = (char*)malloc(64); if (node_data->length == 16) { - val_len = snprintf(val, 64, "%"PRIu64, node_data->intval); + val_len = snprintf(val, sizeof(val), "%" PRIu64, node_data->intval); } else { - val_len = snprintf(val, 64, "%"PRIi64, node_data->intval); + val_len = snprintf(val, sizeof(val), "%" PRIi64, node_data->intval); } break; + case PLIST_NULL: + PLIST_XML_WRITE_ERR("PLIST_NULL type is not valid for XML format\n"); + return PLIST_ERR_FORMAT; default: - break; + return PLIST_ERR_UNKNOWN; } for (i = 0; i < depth; i++) { @@ -244,7 +253,7 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) /* append tag */ str_buf_append(*outbuf, "<", 1); str_buf_append(*outbuf, tag, tag_len); - if (node_data->type == PLIST_STRING || node_data->type == PLIST_KEY) { + if ((node_data->type == PLIST_STRING || node_data->type == PLIST_KEY) && node_data->length > 0) { size_t j; size_t len; off_t start = 0; @@ -331,7 +340,7 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) for (i = 0; i < depth; i++) { str_buf_append(*outbuf, "\t", 1); } - } else if (val) { + } else if (val_len > 0) { str_buf_append(*outbuf, ">", 1); tagOpen = TRUE; str_buf_append(*outbuf, val, val_len); @@ -342,7 +351,6 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) tagOpen = FALSE; str_buf_append(*outbuf, "/>", 2); } - free(val); if (isStruct) { /* add newline for structured types */ @@ -352,9 +360,10 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) if (node_data->type == PLIST_DICT && node->children) { assert((node->children->count % 2) == 0); } - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - node_to_xml(ch, outbuf, depth+1); + plist_err_t res = node_to_xml(ch, outbuf, depth+1); + if (res < 0) return res; } /* fix indent for structured types */ @@ -370,28 +379,47 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) str_buf_append(*outbuf, ">", 1); } str_buf_append(*outbuf, "\n", 1); - - return; + return PLIST_ERR_SUCCESS; } -static void parse_date(const char *strval, struct TM *btime) +static int parse_date(const char *strval, struct TM *btime) { - if (!btime) return; - memset(btime, 0, sizeof(struct tm)); - if (!strval) return; + if (!btime) return -1; + memset(btime, 0, sizeof(*btime)); + if (!strval) return -1; #ifdef HAVE_STRPTIME - strptime((char*)strval, "%Y-%m-%dT%H:%M:%SZ", btime); +#ifdef USE_TM64 + struct tm t = { 0 }; + char* r = strptime((char*)strval, "%Y-%m-%dT%H:%M:%SZ", &t); + if (!r || *r != '\0') { + return -1; + } + copy_tm_to_TM64(&t, btime); +#else + char* r = strptime((char*)strval, "%Y-%m-%dT%H:%M:%SZ", btime); + if (!r || *r != '\0') { + return -1; + } +#endif #else #ifdef USE_TM64 #define PLIST_SSCANF_FORMAT "%lld-%d-%dT%d:%d:%dZ" #else #define PLIST_SSCANF_FORMAT "%d-%d-%dT%d:%d:%dZ" #endif - sscanf(strval, PLIST_SSCANF_FORMAT, &btime->tm_year, &btime->tm_mon, &btime->tm_mday, &btime->tm_hour, &btime->tm_min, &btime->tm_sec); + int n = 0; + if (sscanf(strval, PLIST_SSCANF_FORMAT "%n", &btime->tm_year, &btime->tm_mon, &btime->tm_mday, &btime->tm_hour, &btime->tm_min, &btime->tm_sec, &n) != 6) return -1; + if (strval[n] != '\0') return -1; + if (btime->tm_mon < 1 || btime->tm_mon > 12) return -1; + if (btime->tm_mday < 1 || btime->tm_mday > 31) return -1; + if (btime->tm_hour < 0 || btime->tm_hour > 23) return -1; + if (btime->tm_min < 0 || btime->tm_min > 59) return -1; + if (btime->tm_sec < 0 || btime->tm_sec > 59) return -1; btime->tm_year-=1900; btime->tm_mon--; #endif btime->tm_isdst=0; + return 0; } #define PO10i_LIMIT (INT64_MAX/10) @@ -403,7 +431,7 @@ static int num_digits_i(int64_t i) int64_t po10; n=1; if (i < 0) { - i = -i; + i = (i == INT64_MIN) ? INT64_MAX : -i; n++; } po10=10; @@ -432,17 +460,34 @@ static int num_digits_u(uint64_t i) return n; } -static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) +static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, hashtable_t *visited) { plist_data_t data; if (!node) { - return; + return PLIST_ERR_INVALID_ARG; + } + + if (depth > PLIST_MAX_NESTING_DEPTH) { + PLIST_XML_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); + return PLIST_ERR_MAX_NESTING; } + + if (hash_table_lookup(visited, node)) { + PLIST_XML_WRITE_ERR("circular reference detected\n"); + return PLIST_ERR_CIRCULAR_REF; + } + + // mark as visited + hash_table_insert(visited, node, (void*)1); + data = plist_get_data(node); if (node->children) { - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - node_estimate_size(ch, size, depth + 1); + plist_err_t err = _node_estimate_size(ch, size, depth + 1, visited); + if (err != PLIST_ERR_SUCCESS) { + return err; + } } switch (data->type) { case PLIST_DICT: @@ -473,7 +518,7 @@ static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) *size += data->length; *size += (XPLIST_KEY_LEN << 1) + 6; break; - case PLIST_UINT: + case PLIST_INT: if (data->length == 16) { *size += num_digits_u(data->intval); } else { @@ -482,7 +527,7 @@ static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) *size += (XPLIST_INT_LEN << 1) + 6; break; case PLIST_REAL: - *size += num_digits_i((int64_t)data->realval) + 7; + *size += dtostr(NULL, 0, data->realval); *size += (XPLIST_REAL_LEN << 1) + 6; break; case PLIST_DATE: @@ -505,49 +550,84 @@ static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) *size += 18; /* <key>CF$UID</key> */ *size += (XPLIST_INT_LEN << 1) + 6; break; + case PLIST_NULL: + PLIST_XML_WRITE_ERR("PLIST_NULL type is not valid for XML format\n"); + return PLIST_ERR_FORMAT; default: - break; + PLIST_XML_WRITE_ERR("invalid node type encountered\n"); + return PLIST_ERR_UNKNOWN; } *size += indent; } + return PLIST_ERR_SUCCESS; +} + +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) +{ + hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL); + if (!visited) return PLIST_ERR_NO_MEM; + plist_err_t err = _node_estimate_size(node, size, depth, visited); + hash_table_destroy(visited); + return err; } -PLIST_API void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) +plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) { uint64_t size = 0; - node_estimate_size(plist, &size, 0); + plist_err_t res; + + if (!plist || !plist_xml || !length) { + return PLIST_ERR_INVALID_ARG; + } + + res = node_estimate_size((node_t)plist, &size, 0); + if (res < 0) { + return res; + } size += sizeof(XML_PLIST_PROLOG) + sizeof(XML_PLIST_EPILOG) - 1; strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { + PLIST_XML_WRITE_ERR("Could not allocate output buffer\n"); + return PLIST_ERR_NO_MEM; + } str_buf_append(outbuf, XML_PLIST_PROLOG, sizeof(XML_PLIST_PROLOG)-1); - node_to_xml(plist, &outbuf, 0); + res = node_to_xml((node_t)plist, &outbuf, 0); + if (res < 0) { + str_buf_free(outbuf); + *plist_xml = NULL; + *length = 0; + return res; + } str_buf_append(outbuf, XML_PLIST_EPILOG, sizeof(XML_PLIST_EPILOG)); - *plist_xml = outbuf->data; + *plist_xml = (char*)outbuf->data; *length = outbuf->len - 1; outbuf->data = NULL; str_buf_free(outbuf); -} -PLIST_API void plist_to_xml_free(char *plist_xml) -{ - free(plist_xml); + return PLIST_ERR_SUCCESS; } struct _parse_ctx { const char *pos; const char *end; - int err; + plist_err_t err; }; typedef struct _parse_ctx* parse_ctx; +static inline int is_xml_ws(unsigned char c) +{ + return (c == ' ' || c == '\t' || c == '\r' || c == '\n'); +} + static void parse_skip_ws(parse_ctx ctx) { - while (ctx->pos < ctx->end && ((*(ctx->pos) == ' ') || (*(ctx->pos) == '\t') || (*(ctx->pos) == '\r') || (*(ctx->pos) == '\n'))) { + while (ctx->pos < ctx->end && is_xml_ws(*(ctx->pos))) { ctx->pos++; } } @@ -638,14 +718,14 @@ static void text_parts_free(text_part_t *tp) { while (tp) { text_part_t *tmp = tp; - tp = tp->next; + tp = (text_part_t*)tp->next; free(tmp); } } static text_part_t* text_part_append(text_part_t* parts, const char *begin, size_t length, int is_cdata) { - text_part_t* newpart = malloc(sizeof(text_part_t)); + text_part_t* newpart = (text_part_t*)malloc(sizeof(text_part_t)); assert(newpart); parts->next = text_part_init(newpart, begin, length, is_cdata); return newpart; @@ -665,21 +745,21 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le find_char(ctx, '<', 0); if (ctx->pos >= ctx->end || *ctx->pos != '<') { PLIST_XML_ERR("EOF while looking for closing tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } q = ctx->pos; ctx->pos++; if (ctx->pos >= ctx->end) { PLIST_XML_ERR("EOF while parsing '%s'\n", p); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } if (*ctx->pos == '!') { ctx->pos++; if (ctx->pos >= ctx->end-1) { PLIST_XML_ERR("EOF while parsing <! special tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } if (*ctx->pos == '-' && *(ctx->pos+1) == '-') { @@ -692,7 +772,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le find_str(ctx, "-->", 3, 0); if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3) != 0) { PLIST_XML_ERR("EOF while looking for end of comment\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } ctx->pos += 3; @@ -700,7 +780,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le ctx->pos++; if (ctx->pos >= ctx->end - 8) { PLIST_XML_ERR("EOF while parsing <[ tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } if (strncmp(ctx->pos, "CDATA[", 6) == 0) { @@ -716,7 +796,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le find_str(ctx, "]]>", 3, 0); if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "]]>", 3) != 0) { PLIST_XML_ERR("EOF while looking for end of CDATA block\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } q = ctx->pos; @@ -730,14 +810,14 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le p = ctx->pos; find_next(ctx, " \r\n\t>", 5, 1); PLIST_XML_ERR("Invalid special tag <[%.*s> encountered inside <%s> tag\n", (int)(ctx->pos - p), p, tag); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } } else { p = ctx->pos; find_next(ctx, " \r\n\t>", 5, 1); PLIST_XML_ERR("Invalid special tag <!%.*s> encountered inside <%s> tag\n", (int)(ctx->pos - p), p, tag); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } } else if (*ctx->pos == '/') { @@ -746,30 +826,29 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le p = ctx->pos; find_next(ctx, " \r\n\t>", 5, 1); PLIST_XML_ERR("Invalid tag <%.*s> encountered inside <%s> tag\n", (int)(ctx->pos - p), p, tag); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } } while (1); ctx->pos++; - if (ctx->pos >= ctx->end-tag_len || strncmp(ctx->pos, tag, tag_len)) { + if (ctx->pos >= ctx->end-tag_len || strncmp(ctx->pos, tag, tag_len) != 0) { PLIST_XML_ERR("EOF or end tag mismatch\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } ctx->pos+=tag_len; parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { PLIST_XML_ERR("EOF while parsing closing tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } else if (*ctx->pos != '>') { PLIST_XML_ERR("Invalid closing tag; expected '>', found '%c'\n", *ctx->pos); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; return NULL; } ctx->pos++; - - if (q-p > 0) { + if (q > p) { if (last) { last = text_part_append(last, p, q-p, 0); } else if (parts) { @@ -793,42 +872,50 @@ static int unescape_entities(char *str, size_t *length) PLIST_XML_ERR("Invalid entity sequence encountered (missing terminating ';')\n"); return -1; } - if (str+i >= entp+1) { - int entlen = str+i - entp; - int bytelen = 1; - if (!strncmp(entp, "amp", 3)) { + size_t entlen = (size_t)(str+i - entp); + if (entlen > 0) { + if (entlen > 256) { + PLIST_XML_ERR("Rejecting absurdly large entity at &%.*s...\n", 8, entp); + return -1; + } + size_t bytelen = 1; + if (entlen == 3 && memcmp(entp, "amp", 3) == 0) { /* the '&' is already there */ - } else if (!strncmp(entp, "apos", 4)) { + } else if (entlen == 4 && memcmp(entp, "apos", 4) == 0) { *(entp-1) = '\''; - } else if (!strncmp(entp, "quot", 4)) { + } else if (entlen == 4 && memcmp(entp, "quot", 4) == 0) { *(entp-1) = '"'; - } else if (!strncmp(entp, "lt", 2)) { + } else if (entlen == 2 && memcmp(entp, "lt", 2) == 0) { *(entp-1) = '<'; - } else if (!strncmp(entp, "gt", 2)) { + } else if (entlen == 2 && memcmp(entp, "gt", 2) == 0) { *(entp-1) = '>'; } else if (*entp == '#') { /* numerical character reference */ uint64_t val = 0; char* ep = NULL; if (entlen > 8) { - PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too long: &%.*s;\n", entlen, entp); + PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too long: &%.*s;\n", (int)entlen, entp); return -1; } - if (*(entp+1) == 'x' || *(entp+1) == 'X') { + if (entlen >= 2 && (entp[1] == 'x' || entp[1] == 'X')) { if (entlen < 3) { - PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); + PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", (int)entlen, entp); return -1; } val = strtoull(entp+2, &ep, 16); } else { if (entlen < 2) { - PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); + PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", (int)entlen, entp); return -1; } val = strtoull(entp+1, &ep, 10); } - if (val == 0 || val > 0x10FFFF || ep-entp != entlen) { - PLIST_XML_ERR("Invalid numerical character reference found: &%.*s;\n", entlen, entp); + if (val == 0 || val > 0x10FFFF || (size_t)(ep-entp) != entlen) { + PLIST_XML_ERR("Invalid numerical character reference found: &%.*s;\n", (int)entlen, entp); + return -1; + } + if (val >= 0xD800 && val <= 0xDFFF) { + PLIST_XML_ERR("Invalid numerical character reference found (surrogate): &%.*s;\n", (int)entlen, entp); return -1; } /* convert to UTF8 */ @@ -858,12 +945,18 @@ static int unescape_entities(char *str, size_t *length) *(entp-1) = (char)(val & 0x7F); } } else { - PLIST_XML_ERR("Invalid entity encountered: &%.*s;\n", entlen, entp); + PLIST_XML_ERR("Invalid entity encountered: &%.*s;\n", (int)entlen, entp); return -1; } - memmove(entp, str+i+1, len - i); - i -= entlen+1 - bytelen; - len -= entlen+2 - bytelen; + memmove(entp, str+i+1, len - i); /* include '\0' */ + size_t dec = entlen + 1 - bytelen; + size_t shrink = entlen + 2 - bytelen; + if (i < dec || len < shrink) { + PLIST_XML_ERR("Internal error: length underflow?!\n"); + return -1; + } + i -= dec; + len -= shrink; continue; } else { PLIST_XML_ERR("Invalid empty entity sequence &;\n"); @@ -876,7 +969,7 @@ static int unescape_entities(char *str, size_t *length) return 0; } -static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t *length, int *requires_free) +static char* text_parts_get_content(text_part_t *tp, int unesc_entities, int trim_ws, size_t *length, int *requires_free) { char *str = NULL; size_t total_length = 0; @@ -885,27 +978,25 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t return NULL; } char *p; - if (requires_free && !tp->next) { - if (tp->is_cdata || !unesc_entities) { - *requires_free = 0; - if (length) { - *length = tp->length; - } - return (char*)tp->begin; + if (requires_free && !tp->next && !unesc_entities && !trim_ws) { + *requires_free = 0; + if (length) { + *length = tp->length; } + return (char*)tp->begin; } text_part_t *tmp = tp; while (tp && tp->begin) { total_length += tp->length; - tp = tp->next; + tp = (text_part_t*)tp->next; } - str = malloc(total_length + 1); + str = (char*)malloc(total_length + 1); assert(str); p = str; tp = tmp; while (tp && tp->begin) { size_t len = tp->length; - strncpy(p, tp->begin, len); + memcpy(p, tp->begin, len); p[len] = '\0'; if (!tp->is_cdata && unesc_entities) { if (unescape_entities(p, &len) < 0) { @@ -914,9 +1005,24 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t } } p += len; - tp = tp->next; + tp = (text_part_t*)tp->next; } *p = '\0'; + if (trim_ws) { + char* start = str; + char* end = p; + while (start < end && is_xml_ws((unsigned char)start[0])) start++; + while (end > start && is_xml_ws((unsigned char)end[-1])) end--; + if (start != str) { + size_t newlen = (size_t)(end - start); + memmove(str, start, newlen); + str[newlen] = '\0'; + p = str + newlen; + } else { + *end = '\0'; + p = end; + } + } if (length) { *length = p - str; } @@ -926,20 +1032,20 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t return str; } -static void node_from_xml(parse_ctx ctx, plist_t *plist) +static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) { - char *tag = NULL; + char tag[16] = { 0 }; char *keyname = NULL; plist_t subnode = NULL; const char *p = NULL; plist_t parent = NULL; - int has_content = 0; struct node_path_item { const char *type; - void *prev; + struct node_path_item *prev; }; struct node_path_item* node_path = NULL; + int depth = 0; while (ctx->pos < ctx->end && !ctx->err) { parse_skip_ws(ctx); @@ -950,13 +1056,13 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) p = ctx->pos; find_next(ctx, " \t\r\n", 4, 0); PLIST_XML_ERR("Expected: opening tag, found: %.*s\n", (int)(ctx->pos - p), p); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos++; if (ctx->pos >= ctx->end) { PLIST_XML_ERR("EOF while parsing tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } @@ -964,12 +1070,12 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) find_str(ctx, "?>", 2, 1); if (ctx->pos > ctx->end-2) { PLIST_XML_ERR("EOF while looking for <? tag closing marker\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } - if (strncmp(ctx->pos, "?>", 2)) { + if (strncmp(ctx->pos, "?>", 2) != 0) { PLIST_XML_ERR("Couldn't find <? tag closing marker\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos += 2; @@ -978,10 +1084,10 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) /* comment or DTD */ if (((ctx->end - ctx->pos) > 3) && !strncmp(ctx->pos, "!--", 3)) { ctx->pos += 3; - find_str(ctx,"-->", 3, 0); - if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3)) { + find_str(ctx, "-->", 3, 0); + if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3) != 0) { PLIST_XML_ERR("Couldn't find end of comment\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos+=3; @@ -992,7 +1098,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) find_next(ctx, " \t\r\n[>", 6, 1); if (ctx->pos >= ctx->end) { PLIST_XML_ERR("EOF while parsing !DOCTYPE\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (*ctx->pos == '[') { @@ -1008,9 +1114,9 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } if (embedded_dtd) { find_str(ctx, "]>", 2, 1); - if (ctx->pos > ctx->end-2 || strncmp(ctx->pos, "]>", 2)) { + if (ctx->pos > ctx->end-2 || strncmp(ctx->pos, "]>", 2) != 0) { PLIST_XML_ERR("Couldn't find end of DOCTYPE\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos += 2; @@ -1019,7 +1125,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) p = ctx->pos; find_next(ctx, " \r\n\t>", 5, 1); PLIST_XML_ERR("Invalid or incomplete special tag <%.*s> encountered\n", (int)(ctx->pos - p), p); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } continue; @@ -1027,55 +1133,57 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) int is_empty = 0; int closing_tag = 0; p = ctx->pos; - find_next(ctx," \r\n\t<>", 6, 0); + find_next(ctx, " \r\n\t<>", 6, 0); if (ctx->pos >= ctx->end) { PLIST_XML_ERR("Unexpected EOF while parsing XML\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } - int taglen = ctx->pos - p; - tag = malloc(taglen + 1); - strncpy(tag, p, taglen); + size_t taglen = ctx->pos - p; + if (taglen >= sizeof(tag)) { + PLIST_XML_ERR("Unexpected tag <%.*s> encountered\n", (int)taglen, p); + ctx->pos = ctx->end; + ctx->err = PLIST_ERR_PARSE; + goto err_out; + } + memcpy(tag, p, taglen); tag[taglen] = '\0'; if (*ctx->pos != '>') { find_next(ctx, "<>", 2, 1); } if (ctx->pos >= ctx->end) { PLIST_XML_ERR("Unexpected EOF while parsing XML\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (*ctx->pos != '>') { PLIST_XML_ERR("Missing '>' for tag <%s\n", tag); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (*(ctx->pos-1) == '/') { - int idx = ctx->pos - p - 1; + size_t idx = ctx->pos - p - 1; if (idx < taglen) tag[idx] = '\0'; is_empty = 1; } ctx->pos++; if (!strcmp(tag, "plist")) { - free(tag); - tag = NULL; - has_content = 0; - if (!node_path && *plist) { - /* we don't allow another top-level <plist> */ - break; + PLIST_XML_ERR("Multiple top-level <plist> elements encountered\n"); + ctx->err = PLIST_ERR_PARSE; + goto err_out; } if (is_empty) { PLIST_XML_ERR("Empty plist tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } - struct node_path_item *path_item = malloc(sizeof(struct node_path_item)); + struct node_path_item *path_item = (struct node_path_item*)malloc(sizeof(struct node_path_item)); if (!path_item) { PLIST_XML_ERR("out of memory when allocating node path item\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } path_item->type = "plist"; @@ -1084,34 +1192,42 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) continue; } else if (!strcmp(tag, "/plist")) { - if (!has_content) { + if (!*plist) { PLIST_XML_ERR("encountered empty plist tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (!node_path) { PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (strcmp(node_path->type, tag+1) != 0) { PLIST_XML_ERR("mismatching closing tag <%s> found for opening tag <%s>\n", tag, node_path->type); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } struct node_path_item *path_item = node_path; - node_path = node_path->prev; + node_path = (struct node_path_item*)node_path->prev; free(path_item); - - free(tag); - tag = NULL; - continue; } - + if (tag[0] == '/') { + closing_tag = 1; + goto handle_closing; + } plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_XML_ERR("failed to allocate plist data\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } subnode = plist_new_node(data); - has_content = 1; + if (!subnode) { + PLIST_XML_ERR("failed to create node\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } if (!strcmp(tag, XPLIST_DICT)) { data->type = PLIST_DICT; @@ -1123,17 +1239,16 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (tp->begin) { - int requires_free = 0; - char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); + char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } char *str = str_content; @@ -1144,7 +1259,30 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } str++; } - data->intval = strtoull((char*)str, NULL, 0); + errno = 0; + char* endp = NULL; + data->intval = strtoull(str, &endp, 0); + if (errno == ERANGE) { + PLIST_XML_ERR("Integer overflow detected while parsing '%.20s'\n", str_content); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; + free(str_content); + goto err_out; + } + if (endp == str || *endp != '\0') { + PLIST_XML_ERR("Invalid characters while parsing integer value '%.20s'\n", str_content); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; + free(str_content); + goto err_out; + } + if (is_negative && data->intval > ((uint64_t)INT64_MAX + 1)) { + PLIST_XML_ERR("Signed integer value out of range while parsing '%.20s'\n", str_content); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; + free(str_content); + goto err_out; + } if (is_negative || (data->intval <= INT64_MAX)) { uint64_t v = data->intval; if (is_negative) { @@ -1155,44 +1293,71 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } else { data->length = 16; } - if (requires_free) { - free(str_content); - } + free(str_content); } else { is_empty = 1; } - text_parts_free(tp->next); + text_parts_free((text_part_t*)first_part.next); } if (is_empty) { - data->intval = 0; - data->length = 8; + PLIST_XML_ERR("Encountered empty " XPLIST_INT " tag\n"); + ctx->err = PLIST_ERR_PARSE; + goto err_out; } - data->type = PLIST_UINT; + data->type = PLIST_INT; } else if (!strcmp(tag, XPLIST_REAL)) { if (!is_empty) { text_part_t first_part = { NULL, 0, 0, NULL }; text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (tp->begin) { - int requires_free = 0; - char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); + char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } - data->realval = atof(str_content); - if (requires_free) { + errno = 0; + char *endp = NULL; + data->realval = strtod(str_content, &endp); + if (errno == ERANGE) { + PLIST_XML_ERR("Invalid range while parsing value for '%s' node\n", tag); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; free(str_content); + goto err_out; } + if (endp == str_content || *endp != '\0') { + PLIST_XML_ERR("Could not parse value for '%s' node\n", tag); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; + free(str_content); + goto err_out; + + } + if (!isfinite(data->realval)) { + PLIST_XML_ERR("Invalid real value while parsing '%.20s'\n", str_content); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; + free(str_content); + goto err_out; + } + free(str_content); + } else { + is_empty = 1; } - text_parts_free(tp->next); + text_parts_free((text_part_t*)first_part.next); + } + if (is_empty) { + PLIST_XML_ERR("Encountered empty " XPLIST_REAL " tag\n"); + ctx->err = PLIST_ERR_PARSE; + goto err_out; } data->type = PLIST_REAL; data->length = 8; @@ -1218,21 +1383,19 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) size_t length = 0; if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } - str = text_parts_get_content(tp, 1, &length, NULL); - text_parts_free(first_part.next); + str = text_parts_get_content(tp, 1, 0, &length, NULL); + text_parts_free((text_part_t*)first_part.next); if (!str) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) { keyname = str; - free(tag); - tag = NULL; plist_free(subnode); subnode = NULL; continue; @@ -1251,22 +1414,28 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (tp->begin) { int requires_free = 0; - char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); + char *str_content = text_parts_get_content(tp, 0, 0, NULL, &requires_free); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } size_t size = tp->length; if (size > 0) { data->buff = base64decode(str_content, &size); + if (!data->buff) { + text_parts_free((text_part_t*)first_part.next); + PLIST_XML_ERR("failed to decode base64 stream\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } data->length = size; } @@ -1274,7 +1443,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) free(str_content); } } - text_parts_free(tp->next); + text_parts_free((text_part_t*)first_part.next); } data->type = PLIST_DATA; } else if (!strcmp(tag, XPLIST_DATE)) { @@ -1283,66 +1452,65 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } Time64_T timev = 0; if (tp->begin) { - int requires_free = 0; - size_t length = 0; - char *str_content = text_parts_get_content(tp, 0, &length, &requires_free); + char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); - ctx->err++; + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; goto err_out; } - - if ((length >= 11) && (length < 32)) { - /* we need to copy here and 0-terminate because sscanf will read the entire string (whole rest of XML data) which can be huge */ - char strval[32]; - struct TM btime; - strncpy(strval, str_content, length); - strval[tp->length] = '\0'; - parse_date(strval, &btime); - timev = timegm64(&btime); - } else { - PLIST_XML_ERR("Invalid text content in date node\n"); - } - if (requires_free) { + struct TM btime; + if (parse_date(str_content, &btime) < 0) { + PLIST_XML_ERR("Failed to parse date node\n"); + text_parts_free((text_part_t*)first_part.next); + ctx->err = PLIST_ERR_PARSE; free(str_content); + goto err_out; } + timev = timegm64(&btime); + free(str_content); + } else { + is_empty = 1; } - text_parts_free(tp->next); + text_parts_free((text_part_t*)first_part.next); data->realval = (double)(timev - MAC_EPOCH); } + if (is_empty) { + PLIST_XML_ERR("Encountered empty " XPLIST_DATE " tag\n"); + ctx->err = PLIST_ERR_PARSE; + goto err_out; + } data->length = sizeof(double); data->type = PLIST_DATE; - } else if (tag[0] == '/') { - closing_tag = 1; } else { PLIST_XML_ERR("Unexpected tag <%s%s> encountered\n", tag, (is_empty) ? "/" : ""); ctx->pos = ctx->end; - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (subnode && !closing_tag) { if (!*plist) { - /* first node, make this node the parent node */ + /* first value node inside <plist> */ *plist = subnode; - if (data->type != PLIST_DICT && data->type != PLIST_ARRAY) { - /* if the first node is not a structered node, we're done */ - subnode = NULL; - goto err_out; + + if (data->type == PLIST_DICT || data->type == PLIST_ARRAY) { + parent = subnode; + } else { + /* scalar root: keep parsing until </plist> */ + parent = NULL; } - parent = subnode; } else if (parent) { switch (plist_get_node_type(parent)) { case PLIST_DICT: if (!keyname) { PLIST_XML_ERR("missing key name while adding dict item\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } plist_dict_set_item(parent, keyname, subnode); @@ -1353,47 +1521,76 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) default: /* should not happen */ PLIST_XML_ERR("parent is not a structured node\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } + } else { + /* We already produced root, and we're not inside a container */ + PLIST_XML_ERR("Unexpected tag <%s> found while </plist> is expected\n", tag); + ctx->err = PLIST_ERR_PARSE; + goto err_out; } if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) { - struct node_path_item *path_item = malloc(sizeof(struct node_path_item)); + if (depth >= PLIST_MAX_NESTING_DEPTH) { + PLIST_XML_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); + ctx->err = PLIST_ERR_MAX_NESTING; + goto err_out; + } + struct node_path_item *path_item = (struct node_path_item*)malloc(sizeof(struct node_path_item)); if (!path_item) { PLIST_XML_ERR("out of memory when allocating node path item\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } path_item->type = (data->type == PLIST_DICT) ? XPLIST_DICT : XPLIST_ARRAY; path_item->prev = node_path; node_path = path_item; + depth++; parent = subnode; + } else { + /* If we inserted a child scalar into a container, nothing to push. */ } subnode = NULL; - } else if (closing_tag) { + } +handle_closing: + if (closing_tag) { if (!node_path) { PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (strcmp(node_path->type, tag+1) != 0) { PLIST_XML_ERR("unexpected %s found (for opening %s)\n", tag, node_path->type); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } - struct node_path_item *path_item = node_path; - node_path = node_path->prev; - free(path_item); - parent = ((node_t*)parent)->parent; - if (!parent) { - goto err_out; + /* When closing a dictionary, convert a single-entry + { "CF$UID" : <integer> } dictionary into a PLIST_UID node. + Perform the conversion before moving to the parent node. */ + if (!strcmp(node_path->type, XPLIST_DICT) && parent && plist_get_node_type(parent) == PLIST_DICT) { + if (plist_dict_get_size(parent) == 1) { + plist_t uid = plist_dict_get_item(parent, "CF$UID"); + if (uid) { + uint64_t val = 0; + if (plist_get_node_type(uid) != PLIST_INT) { + ctx->err = PLIST_ERR_PARSE; + PLIST_XML_ERR("Invalid node type for CF$UID dict entry (must be PLIST_INT)\n"); + goto err_out; + } + plist_get_uint_val(uid, &val); + plist_set_uid_val(parent, val); + } + } } - } - free(tag); - tag = NULL; + if (depth > 0) depth--; + struct node_path_item *path_item = node_path; + node_path = (struct node_path_item*)node_path->prev; + free(path_item); + parent = (parent) ? ((node_t)parent)->parent : NULL; + } free(keyname); keyname = NULL; plist_free(subnode); @@ -1403,35 +1600,51 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) if (node_path) { PLIST_XML_ERR("EOF encountered while </%s> was expected\n", node_path->type); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; } err_out: - free(tag); free(keyname); plist_free(subnode); /* clean up node_path if required */ while (node_path) { struct node_path_item *path_item = node_path; - node_path = path_item->prev; + node_path = (struct node_path_item*)path_item->prev; free(path_item); } - if (ctx->err) { + if (ctx->err != PLIST_ERR_SUCCESS) { plist_free(*plist); *plist = NULL; + return ctx->err; + } + + /* check if we have a UID "dict" so we can replace it with a proper UID node */ + if (PLIST_IS_DICT(*plist) && plist_dict_get_size(*plist) == 1) { + plist_t value = plist_dict_get_item(*plist, "CF$UID"); + if (PLIST_IS_INT(value)) { + uint64_t u64val = 0; + plist_get_uint_val(value, &u64val); + plist_free(*plist); + *plist = plist_new_uid(u64val); + } } + + return PLIST_ERR_SUCCESS; } -PLIST_API void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist) +plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist) { + if (!plist) { + return PLIST_ERR_INVALID_ARG; + } + *plist = NULL; if (!plist_xml || (length == 0)) { - *plist = NULL; - return; + return PLIST_ERR_INVALID_ARG; } - struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 }; + struct _parse_ctx ctx = { plist_xml, plist_xml + length, PLIST_ERR_SUCCESS }; - node_from_xml(&ctx, plist); + return node_from_xml(&ctx, plist); } |
