summaryrefslogtreecommitdiffstats
path: root/src/xplist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xplist.c')
-rw-r--r--src/xplist.c683
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);
}