diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Array.cpp | 51 | ||||
| -rw-r--r-- | src/Boolean.cpp | 2 | ||||
| -rw-r--r-- | src/Data.cpp | 13 | ||||
| -rw-r--r-- | src/Date.cpp | 24 | ||||
| -rw-r--r-- | src/Dictionary.cpp | 10 | ||||
| -rw-r--r-- | src/Integer.cpp | 5 | ||||
| -rw-r--r-- | src/Key.cpp | 4 | ||||
| -rw-r--r-- | src/Node.cpp | 2 | ||||
| -rw-r--r-- | src/Real.cpp | 2 | ||||
| -rw-r--r-- | src/String.cpp | 25 | ||||
| -rw-r--r-- | src/Structure.cpp | 27 | ||||
| -rw-r--r-- | src/Uid.cpp | 2 | ||||
| -rw-r--r-- | src/base64.c | 2 | ||||
| -rw-r--r-- | src/bplist.c | 195 | ||||
| -rw-r--r-- | src/hashtable.c | 10 | ||||
| -rw-r--r-- | src/hashtable.h | 2 | ||||
| -rw-r--r-- | src/jplist.c | 81 | ||||
| -rw-r--r-- | src/oplist.c | 98 | ||||
| -rw-r--r-- | src/out-default.c | 34 | ||||
| -rw-r--r-- | src/out-limd.c | 34 | ||||
| -rw-r--r-- | src/out-plutil.c | 34 | ||||
| -rw-r--r-- | src/plist.c | 442 | ||||
| -rw-r--r-- | src/plist.h | 17 | ||||
| -rw-r--r-- | src/time64.c | 1 | ||||
| -rw-r--r-- | src/xplist.c | 151 |
25 files changed, 986 insertions, 282 deletions
diff --git a/src/Array.cpp b/src/Array.cpp index bc448d3..784df7c 100644 --- a/src/Array.cpp +++ b/src/Array.cpp @@ -40,7 +40,9 @@ static void array_fill(Array *_this, std::vector<Node*> &array, plist_t node) do { subnode = NULL; plist_array_next_item(node, iter, &subnode); - array.push_back( Node::FromPlist(subnode, _this) ); + if (subnode) { + array.push_back( Node::FromPlist(subnode, _this) ); + } } while (subnode); free(iter); } @@ -51,7 +53,7 @@ Array::Array(plist_t node, Node* parent) : Structure(parent) array_fill(this, _array, _node); } -Array::Array(const PList::Array& a) +Array::Array(const PList::Array& a) : Structure(a.GetParent()) { _array.clear(); _node = plist_copy(a.GetPlist()); @@ -60,6 +62,8 @@ Array::Array(const PList::Array& a) Array& Array::operator=(const PList::Array& a) { + if (this == &a) return *this; + plist_free(_node); for (size_t it = 0; it < _array.size(); it++) { delete _array.at(it); @@ -88,6 +92,26 @@ Node* Array::operator[](unsigned int array_index) return _array.at(array_index); } +Node* Array::Back() +{ + return _array.back(); +} + +Node* Array::back() +{ + return _array.back(); +} + +Node* Array::Front() +{ + return _array.front(); +} + +Node* Array::front() +{ + return _array.front(); +} + Array::iterator Array::Begin() { return _array.begin(); @@ -132,7 +156,7 @@ size_t Array::size() const { return _array.size(); } -void Array::Append(Node* node) +void Array::Append(const Node* node) { if (node) { @@ -143,7 +167,12 @@ void Array::Append(Node* node) } } -void Array::Insert(Node* node, unsigned int pos) +void Array::Append(const Node& node) +{ + Append(&node); +} + +void Array::Insert(const Node* node, unsigned int pos) { if (node) { @@ -156,6 +185,11 @@ void Array::Insert(Node* node, unsigned int pos) } } +void Array::Insert(const Node &node, unsigned int pos) +{ + Insert(&node, pos); +} + void Array::Remove(Node* node) { if (node) @@ -168,7 +202,7 @@ void Array::Remove(Node* node) std::vector<Node*>::iterator it = _array.begin(); it += pos; _array.erase(it); - delete node; + free(node); } } @@ -181,10 +215,15 @@ void Array::Remove(unsigned int pos) _array.erase(it); } -unsigned int Array::GetNodeIndex(Node* node) const +unsigned int Array::GetNodeIndex(const Node* node) const { std::vector<Node*>::const_iterator it = std::find(_array.begin(), _array.end(), node); return std::distance (_array.begin(), it); } +unsigned int Array::GetNodeIndex(const Node& node) const +{ + return GetNodeIndex(&node); +} + } // namespace PList diff --git a/src/Boolean.cpp b/src/Boolean.cpp index 9ec1a63..2a5e303 100644 --- a/src/Boolean.cpp +++ b/src/Boolean.cpp @@ -40,6 +40,8 @@ Boolean::Boolean(const PList::Boolean& b) : Node(PLIST_BOOLEAN) Boolean& Boolean::operator=(const PList::Boolean& b) { + if (this == &b) return *this; + plist_free(_node); _node = plist_copy(b.GetPlist()); return *this; diff --git a/src/Data.cpp b/src/Data.cpp index a96fc50..94767c9 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -40,6 +40,8 @@ Data::Data(const PList::Data& d) : Node(PLIST_DATA) Data& Data::operator=(const PList::Data& b) { + if (this == &b) return *this; + plist_free(_node); _node = plist_copy(b.GetPlist()); return *this; @@ -50,6 +52,11 @@ Data::Data(const std::vector<char>& buff) : Node(PLIST_DATA) plist_set_data_val(_node, &buff[0], buff.size()); } +Data::Data(const char* buff, uint64_t size) : Node(PLIST_DATA) +{ + plist_set_data_val(_node, buff, size); +} + Data::~Data() { } @@ -66,14 +73,10 @@ void Data::SetValue(const std::vector<char>& buff) std::vector<char> Data::GetValue() const { - char* buff = NULL; uint64_t length = 0; - plist_get_data_val(_node, &buff, &length); + const char* buff = plist_get_data_ptr(_node, &length); std::vector<char> ret(buff, buff + length); - delete buff; return ret; } - - } // namespace PList diff --git a/src/Date.cpp b/src/Date.cpp index 8b8e650..214220b 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -34,20 +34,22 @@ Date::Date(plist_t node, Node* parent) : Node(node, parent) Date::Date(const PList::Date& d) : Node(PLIST_DATE) { - timeval t = d.GetValue(); - plist_set_date_val(_node, t.tv_sec, t.tv_usec); + int64_t t = d.GetValue(); + plist_set_unix_date_val(_node, t); } Date& Date::operator=(const PList::Date& d) { + if (this == &d) return *this; + plist_free(_node); _node = plist_copy(d.GetPlist()); return *this; } -Date::Date(timeval t) : Node(PLIST_DATE) +Date::Date(int64_t t) : Node(PLIST_DATE) { - plist_set_date_val(_node, t.tv_sec, t.tv_usec); + plist_set_unix_date_val(_node, t); } Date::~Date() @@ -59,18 +61,16 @@ Node* Date::Clone() const return new Date(*this); } -void Date::SetValue(timeval t) +void Date::SetValue(int64_t t) { - plist_set_date_val(_node, t.tv_sec, t.tv_usec); + plist_set_unix_date_val(_node, t); } -timeval Date::GetValue() const +int64_t Date::GetValue() const { - int32_t tv_sec = 0; - int32_t tv_usec = 0; - plist_get_date_val(_node, &tv_sec, &tv_usec); - timeval t = {tv_sec, tv_usec}; - return t; + int64_t sec = 0; + plist_get_unix_date_val(_node, &sec); + return sec; } } // namespace PList diff --git a/src/Dictionary.cpp b/src/Dictionary.cpp index 30c20b6..b354945 100644 --- a/src/Dictionary.cpp +++ b/src/Dictionary.cpp @@ -40,7 +40,7 @@ static void dictionary_fill(Dictionary *_this, std::map<std::string,Node*> &map, plist_dict_next_item(node, it, &key, &subnode); if (key && subnode) map[std::string(key)] = Node::FromPlist(subnode, _this); - delete key; + free(key); } while (subnode); free(it); } @@ -51,7 +51,7 @@ Dictionary::Dictionary(plist_t node, Node* parent) : Structure(parent) dictionary_fill(this, _map, _node); } -Dictionary::Dictionary(const PList::Dictionary& d) +Dictionary::Dictionary(const PList::Dictionary& d) : Structure(d.GetParent()) { for (Dictionary::iterator it = _map.begin(); it != _map.end(); it++) { @@ -65,6 +65,8 @@ Dictionary::Dictionary(const PList::Dictionary& d) Dictionary& Dictionary::operator=(const PList::Dictionary& d) { + if (this == &d) return *this; + for (Dictionary::iterator it = _map.begin(); it != _map.end(); it++) { plist_free(it->second->GetPlist()); @@ -176,9 +178,9 @@ void Dictionary::Remove(Node* node) plist_dict_get_item_key(node->GetPlist(), &key); plist_dict_remove_item(_node, key); std::string skey = key; - delete key; + free(key); _map.erase(skey); - delete node; + free(node); } } diff --git a/src/Integer.cpp b/src/Integer.cpp index 30a5405..26e7ccf 100644 --- a/src/Integer.cpp +++ b/src/Integer.cpp @@ -35,11 +35,14 @@ Integer::Integer(plist_t node, Node* parent) : Node(node, parent) Integer::Integer(const PList::Integer& i) : Node(PLIST_INT) { - plist_set_uint_val(_node, i.GetValue()); + plist_free(_node); + _node = plist_copy(i.GetPlist()); } Integer& Integer::operator=(const PList::Integer& i) { + if (this == &i) return *this; + plist_free(_node); _node = plist_copy(i.GetPlist()); return *this; diff --git a/src/Key.cpp b/src/Key.cpp index 79265d5..459227a 100644 --- a/src/Key.cpp +++ b/src/Key.cpp @@ -40,6 +40,8 @@ Key::Key(const PList::Key& k) : Node(PLIST_INT) Key& Key::operator=(const PList::Key& k) { + if (this == &k) return *this; + plist_free(_node); _node = plist_copy(k.GetPlist()); return *this; @@ -69,7 +71,7 @@ std::string Key::GetValue() const char* s = NULL; plist_get_key_val(_node, &s); std::string ret = s ? s : ""; - delete s; + free(s); return ret; } diff --git a/src/Node.cpp b/src/Node.cpp index 0bd428a..1043b31 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -73,7 +73,7 @@ Node::Node(plist_type type, Node* parent) : _parent(parent) _node = plist_new_data(NULL,0); break; case PLIST_DATE: - _node = plist_new_date(0,0); + _node = plist_new_unix_date(0); break; case PLIST_ARRAY: _node = plist_new_array(); diff --git a/src/Real.cpp b/src/Real.cpp index 02d1d9b..b743ab5 100644 --- a/src/Real.cpp +++ b/src/Real.cpp @@ -39,6 +39,8 @@ Real::Real(const PList::Real& d) : Node(PLIST_INT) Real& Real::operator=(const PList::Real& d) { + if (this == &d) return *this; + plist_free(_node); _node = plist_copy(d.GetPlist()); return *this; diff --git a/src/String.cpp b/src/String.cpp index 2ddc28b..bcd5e2e 100644 --- a/src/String.cpp +++ b/src/String.cpp @@ -40,16 +40,37 @@ String::String(const PList::String& s) : Node(PLIST_INT) String& String::operator=(const PList::String& s) { + if (this == &s) return *this; + plist_free(_node); _node = plist_copy(s.GetPlist()); return *this; } +String& String::operator=(const std::string& s) +{ + plist_free(_node); + _node = plist_new_string(s.c_str()); + return *this; +} + +String& String::operator=(const char* s) +{ + plist_free(_node); + _node = plist_new_string(s); + return *this; +} + String::String(const std::string& s) : Node(PLIST_STRING) { plist_set_string_val(_node, s.c_str()); } +String::String(const char *s) : Node(PLIST_STRING) +{ + plist_set_string_val(_node, s); +} + String::~String() { } @@ -66,10 +87,8 @@ void String::SetValue(const std::string& s) std::string String::GetValue() const { - char* s = NULL; - plist_get_string_val(_node, &s); + const char* s = plist_get_string_ptr(_node, NULL); std::string ret = s ? s : ""; - delete s; return ret; } diff --git a/src/Structure.cpp b/src/Structure.cpp index 670cce6..65e5ca8 100644 --- a/src/Structure.cpp +++ b/src/Structure.cpp @@ -57,7 +57,7 @@ std::string Structure::ToXml() const uint32_t length = 0; plist_to_xml(_node, &xml, &length); std::string ret(xml, xml+length); - delete xml; + free(xml); return ret; } @@ -67,7 +67,7 @@ std::vector<char> Structure::ToBin() const uint32_t length = 0; plist_to_bin(_node, &bin, &length); std::vector<char> ret(bin, bin+length); - delete bin; + free(bin); return ret; } @@ -77,7 +77,7 @@ void Structure::UpdateNodeParent(Node* node) if ( NULL != node->_parent ) { plist_type type = plist_get_node_type(node->_parent); - if (PLIST_ARRAY ==type || PLIST_DICT == type ) + if (PLIST_ARRAY == type || PLIST_DICT == type) { Structure* s = static_cast<Structure*>(node->_parent); s->Remove(node); @@ -117,8 +117,27 @@ Structure* Structure::FromBin(const std::vector<char>& bin) plist_from_bin(&bin[0], bin.size(), &root); return ImportStruct(root); +} + +Structure* Structure::FromBin(const char* bin, uint64_t size) +{ + plist_t root = NULL; + plist_from_bin(bin, size, &root); + return ImportStruct(root); } -} // namespace PList +Structure* Structure::FromMemory(const std::vector<char>& buf, plist_format_t *format) +{ + return Structure::FromMemory(&buf[0], buf.size(), format); +} +Structure* Structure::FromMemory(const char* buf, uint64_t size, plist_format_t *format) +{ + plist_t root = NULL; + plist_from_memory(buf, size, &root, format); + return ImportStruct(root); +} + + +} // namespace PList diff --git a/src/Uid.cpp b/src/Uid.cpp index 8c73c80..d73f777 100644 --- a/src/Uid.cpp +++ b/src/Uid.cpp @@ -40,6 +40,8 @@ Uid::Uid(const PList::Uid& i) : Node(PLIST_UID) Uid& Uid::operator=(const PList::Uid& i) { + if (this == &i) return *this; + plist_free(_node); _node = plist_copy(i.GetPlist()); return *this; diff --git a/src/base64.c b/src/base64.c index ee02356..76990b9 100644 --- a/src/base64.c +++ b/src/base64.c @@ -78,7 +78,7 @@ unsigned char *base64decode(const char *buf, size_t *size) if (len <= 0) return NULL; unsigned char *outbuf = (unsigned char*)malloc((len/4)*3+3); const char *ptr = buf; - int p = 0; + size_t p = 0; int wv, w1, w2, w3, w4; int tmpval[4]; int tmpcnt = 0; diff --git a/src/bplist.c b/src/bplist.c index 93f0bc6..8d50f2e 100644 --- a/src/bplist.c +++ b/src/bplist.c @@ -87,6 +87,28 @@ union plist_uint_ptr uint64_t *u64ptr; }; +#ifdef _MSC_VER +uint64_t get_unaligned_64(uint64_t *ptr) +{ + uint64_t temp; + memcpy(&temp, ptr, sizeof(temp)); + return temp; +} + +uint32_t get_unaligned_32(uint32_t *ptr) +{ + uint32_t temp; + memcpy(&temp, ptr, sizeof(temp)); + return temp; +} + +uint16_t get_unaligned_16(uint16_t *ptr) +{ + uint16_t temp; + memcpy(&temp, ptr, sizeof(temp)); + return temp; +} +#else #define get_unaligned(ptr) \ ({ \ struct __attribute__((packed)) { \ @@ -94,6 +116,7 @@ union plist_uint_ptr } *__p = (void *) (ptr); \ __p->__v; \ }) +#endif #ifndef bswap16 @@ -148,17 +171,31 @@ union plist_uint_ptr #define beNtoh(x,n) be64toh((x) << ((8-(n)) << 3)) #endif +#ifdef _MSC_VER +static uint64_t UINT_TO_HOST(const void* x, uint8_t n) +{ + union plist_uint_ptr __up; + __up.src = (n > 8) ? (const char*)x + (n - 8) : (const char*)x; + return (n >= 8 ? be64toh( get_unaligned_64(__up.u64ptr) ) : + (n == 4 ? be32toh( get_unaligned_32(__up.u32ptr) ) : + (n == 2 ? be16toh( get_unaligned_16(__up.u16ptr) ) : + (n == 1 ? *__up.u8ptr : + beNtoh( get_unaligned_64(__up.u64ptr), n) + )))); +} +#else #define UINT_TO_HOST(x, n) \ ({ \ union plist_uint_ptr __up; \ - __up.src = ((n) > 8) ? (x) + ((n) - 8) : (x); \ + __up.src = ((n) > 8) ? (const char*)(x) + ((n) - 8) : (const char*)(x); \ ((n) >= 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \ ((n) == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \ ((n) == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \ - ((n) == 1 ? *__up.u8ptr : \ + ((n) == 1 ? *__up.u8ptr : \ beNtoh( get_unaligned(__up.u64ptr), n) \ )))); \ }) +#endif #define get_needed_bytes(x) \ ( ((uint64_t)(x)) < (1ULL << 8) ? 1 : \ @@ -168,15 +205,13 @@ union plist_uint_ptr #define get_real_bytes(x) ((x) == (float) (x) ? sizeof(float) : sizeof(double)) -#if (defined(__LITTLE_ENDIAN__) \ - && !defined(__FLOAT_WORD_ORDER__)) \ - || (defined(__FLOAT_WORD_ORDER__) \ - && __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#define float_bswap64(x) bswap64(x) -#define float_bswap32(x) bswap32(x) -#else +#if (defined(__BIG_ENDIAN__) && !defined(__FLOAT_WORD_ORDER__)) \ + || (defined(__FLOAT_WORD_ORDER__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) #define float_bswap64(x) (x) #define float_bswap32(x) (x) +#else +#define float_bswap64(x) bswap64(x) +#define float_bswap32(x) bswap32(x) #endif #ifndef __has_builtin @@ -204,13 +239,16 @@ struct bplist_data { const char* offset_table; uint32_t level; ptrarray_t* used_indexes; + plist_err_t err; }; #ifdef DEBUG static int plist_bin_debug = 0; #define PLIST_BIN_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binparser] ERROR: " __VA_ARGS__); } +#define PLIST_BIN_WRITE_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binwriter] ERROR: " __VA_ARGS__); } #else #define PLIST_BIN_ERR(...) +#define PLIST_BIN_WRITE_ERR(...) #endif void plist_bin_init(void) @@ -271,19 +309,30 @@ static plist_t parse_int_node(const char **bnode, uint8_t size) static plist_t parse_real_node(const char **bnode, uint8_t size) { plist_data_t data = plist_new_plist_data(); - uint8_t buf[8]; size = 1 << size; // make length less misleading switch (size) { case sizeof(uint32_t): - *(uint32_t*)buf = float_bswap32(get_unaligned((uint32_t*)*bnode)); - data->realval = *(float *) buf; - break; + { + uint32_t ival; + memcpy(&ival, *bnode, sizeof(uint32_t)); + ival = float_bswap32(ival); + float fval; + memcpy(&fval, &ival, sizeof(float)); + data->realval = fval; + } + break; + case sizeof(uint64_t): - *(uint64_t*)buf = float_bswap64(get_unaligned((uint64_t*)*bnode)); - data->realval = *(double *) buf; + { + uint64_t ival; + memcpy(&ival, *bnode, sizeof(uint64_t)); + ival = float_bswap64(ival); + memcpy(&data->realval, &ival, sizeof(double)); break; + } + default: free(data); PLIST_BIN_ERR("%s: Invalid byte size for real node\n", __func__); @@ -323,13 +372,13 @@ static plist_t parse_string_node(const char **bnode, uint64_t size) return node_create(NULL, data); } -static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, long *items_written) +static char *plist_utf16be_to_utf8(uint16_t *unistr, size_t len, size_t *items_read, size_t *items_written) { if (!unistr || (len <= 0)) return NULL; char* outbuf; char* outbuf_new; - int p = 0; - long i = 0; + size_t p = 0; + size_t i = 0; uint16_t wc; uint32_t w; @@ -343,7 +392,7 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, } while (i < len) { - wc = be16toh(get_unaligned(unistr + i)); + wc = UINT_TO_HOST(unistr + i, sizeof(wc)); i++; if (wc >= 0xD800 && wc <= 0xDBFF) { if (!read_lead_surrogate) { @@ -397,8 +446,8 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, static plist_t parse_unicode_node(const char **bnode, uint64_t size) { plist_data_t data = plist_new_plist_data(); - long items_read = 0; - long items_written = 0; + size_t items_read = 0; + size_t items_written = 0; data->type = PLIST_STRING; data->strval = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written); @@ -739,6 +788,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node if (node_index >= bplist->num_objects) { PLIST_BIN_ERR("node index (%u) must be smaller than the number of objects (%" PRIu64 ")\n", node_index, bplist->num_objects); + bplist->err = PLIST_ERR_PARSE; return NULL; } @@ -746,6 +796,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node if (idx_ptr < bplist->offset_table || idx_ptr >= bplist->offset_table + bplist->num_objects * bplist->offset_size) { PLIST_BIN_ERR("node index %u points outside of valid range\n", node_index); + bplist->err = PLIST_ERR_PARSE; return NULL; } @@ -753,6 +804,14 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node /* make sure the node offset is in a sane range */ if ((ptr < bplist->data+BPLIST_MAGIC_SIZE+BPLIST_VERSION_SIZE) || (ptr >= bplist->offset_table)) { PLIST_BIN_ERR("offset for node index %u points outside of valid range\n", node_index); + bplist->err = PLIST_ERR_PARSE; + return NULL; + } + + /* check nesting depth */ + if (bplist->level > PLIST_MAX_NESTING_DEPTH) { + PLIST_BIN_ERR("maximum nesting depth (%u) exceeded\n",(unsigned)PLIST_MAX_NESTING_DEPTH); + bplist->err = PLIST_ERR_MAX_NESTING; return NULL; } @@ -772,6 +831,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node void *node_level = ptr_array_index(bplist->used_indexes, bplist->level); if (node_i == node_level) { PLIST_BIN_ERR("recursion detected in binary plist\n"); + bplist->err = PLIST_ERR_CIRCULAR_REF; return NULL; } } @@ -830,7 +890,14 @@ plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * pli ref_size = trailer->ref_size; num_objects = be64toh(trailer->num_objects); root_object = be64toh(trailer->root_object_index); - offset_table = (char *)(plist_bin + be64toh(trailer->offset_table_offset)); + + uint64_t offset_table_offset = be64toh(trailer->offset_table_offset); + uint64_t max_valid_offset = (uint64_t)length - sizeof(bplist_trailer_t); + if (offset_table_offset > max_valid_offset) { + PLIST_BIN_ERR("offset table offset outside of valid range\n"); + return PLIST_ERR_PARSE; + } + offset_table = (char *)(plist_bin + offset_table_offset); if (num_objects == 0) { PLIST_BIN_ERR("number of objects must be larger than 0\n"); @@ -876,6 +943,7 @@ plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * pli bplist.offset_table = offset_table; bplist.level = 0; bplist.used_indexes = ptr_array_new(16); + bplist.err = PLIST_ERR_SUCCESS; if (!bplist.used_indexes) { PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n"); @@ -887,7 +955,7 @@ plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * pli ptr_array_free(bplist.used_indexes); if (!*plist) { - return PLIST_ERR_PARSE; + return (bplist.err != PLIST_ERR_SUCCESS) ? bplist.err : PLIST_ERR_PARSE; } return PLIST_ERR_SUCCESS; @@ -944,35 +1012,58 @@ struct serialize_s { ptrarray_t* objects; hashtable_t* ref_table; + hashtable_t* in_stack; }; -static void serialize_plist(node_t node, void* data) +static plist_err_t serialize_plist(node_t node, void* data, uint32_t depth) { uint64_t *index_val = NULL; struct serialize_s *ser = (struct serialize_s *) data; - uint64_t current_index = ser->objects->len; - //first check that node is not yet in objects + if (depth > PLIST_MAX_NESTING_DEPTH) { + PLIST_BIN_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); + return PLIST_ERR_MAX_NESTING; + } + + // circular reference check: is node on current recursion stack? + if (hash_table_lookup(ser->in_stack, node)) { + PLIST_BIN_WRITE_ERR("circular reference detected\n"); + return PLIST_ERR_CIRCULAR_REF; + } + + // first check that node is not yet in objects void* val = hash_table_lookup(ser->ref_table, node); - if (val) - { - //data is already in table - return; + if (val) { + // data is already in table + return PLIST_ERR_SUCCESS; } - //insert new ref + + // mark as active + hash_table_insert(ser->in_stack, node, (void*)1); + + // insert new ref index_val = (uint64_t *) malloc(sizeof(uint64_t)); assert(index_val != NULL); - *index_val = current_index; + *index_val = ser->objects->len; hash_table_insert(ser->ref_table, node, index_val); - //now append current node to object array + // now append current node to object array ptr_array_add(ser->objects, node); - //now recurse on children + // now recurse on children node_t ch; + plist_err_t err = PLIST_ERR_SUCCESS; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - serialize_plist(ch, data); + err = serialize_plist(ch, data, depth+1); + if (err != PLIST_ERR_SUCCESS) { + break; + } } + + // leave recursion stack + hash_table_remove(ser->in_stack, node); + + return err; } #define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0))) @@ -1051,11 +1142,11 @@ static void write_string(bytearray_t * bplist, char *val, uint64_t size) write_raw_data(bplist, BPLIST_STRING, (uint8_t *) val, size); } -static uint16_t *plist_utf8_to_utf16be(char *unistr, long size, long *items_read, long *items_written) +static uint16_t *plist_utf8_to_utf16be(char *unistr, size_t size, size_t *items_read, size_t *items_written) { uint16_t *outbuf; - int p = 0; - long i = 0; + size_t p = 0; + size_t i = 0; unsigned char c0; unsigned char c1; @@ -1095,7 +1186,7 @@ static uint16_t *plist_utf8_to_utf16be(char *unistr, long size, long *items_read i+=1; } else { // invalid character - PLIST_BIN_ERR("%s: invalid utf8 sequence in string at index %lu\n", __func__, i); + PLIST_BIN_ERR("%s: invalid utf8 sequence in string at index %zu\n", __func__, i); break; } } @@ -1110,10 +1201,10 @@ static uint16_t *plist_utf8_to_utf16be(char *unistr, long size, long *items_read return outbuf; } -static void write_unicode(bytearray_t * bplist, char *val, uint64_t size) +static void write_unicode(bytearray_t * bplist, char *val, size_t size) { - long items_read = 0; - long items_written = 0; + size_t items_read = 0; + size_t items_written = 0; uint16_t *unicodestr = NULL; unicodestr = plist_utf8_to_utf16be(val, size, &items_read, &items_written); @@ -1198,6 +1289,7 @@ plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) { ptrarray_t* objects = NULL; hashtable_t* ref_table = NULL; + hashtable_t* in_stack = NULL; struct serialize_s ser_s; uint8_t offset_size = 0; uint8_t ref_size = 0; @@ -1227,11 +1319,28 @@ plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) ptr_array_free(objects); return PLIST_ERR_NO_MEM; } + //hashtable for circular reference detection + in_stack = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL); + if (!in_stack) { + ptr_array_free(objects); + hash_table_destroy(ref_table); + return PLIST_ERR_NO_MEM; + } //serialize plist ser_s.objects = objects; ser_s.ref_table = ref_table; - serialize_plist((node_t)plist, &ser_s); + ser_s.in_stack = in_stack; + plist_err_t err = serialize_plist((node_t)plist, &ser_s, 0); + if (err != PLIST_ERR_SUCCESS) { + ptr_array_free(objects); + hash_table_destroy(ref_table); + hash_table_destroy(in_stack); + return err; + } + //no longer needed + hash_table_destroy(in_stack); + ser_s.in_stack = NULL; //now stream to output buffer offset_size = 0; //unknown yet diff --git a/src/hashtable.c b/src/hashtable.c index 86dae82..dd6dbfc 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -47,7 +47,7 @@ void hash_table_destroy(hashtable_t *ht) ht->free_func(e->value); } hashentry_t* old = e; - e = (hashentry_t*)e->next; + e = e->next; free(old); } } @@ -71,7 +71,7 @@ void hash_table_insert(hashtable_t* ht, void *key, void *value) e->value = value; return; } - e = (hashentry_t*)e->next; + e = e->next; } // if we get here, the element is not yet in the list. @@ -103,7 +103,7 @@ void* hash_table_lookup(hashtable_t* ht, void *key) if (ht->compare_func(e->key, key)) { return e->value; } - e = (hashentry_t*)e->next; + e = e->next; } return NULL; } @@ -124,7 +124,7 @@ void hash_table_remove(hashtable_t* ht, void *key) // found element, remove it from the list hashentry_t* old = e; if (e == ht->entries[idx0]) { - ht->entries[idx0] = (hashentry_t*)e->next; + ht->entries[idx0] = e->next; } else { last->next = e->next; } @@ -135,6 +135,6 @@ void hash_table_remove(hashtable_t* ht, void *key) return; } last = e; - e = (hashentry_t*)e->next; + e = e->next; } } diff --git a/src/hashtable.h b/src/hashtable.h index 42d7b93..514cfec 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -25,7 +25,7 @@ typedef struct hashentry_t { void *key; void *value; - void *next; + struct hashentry_t *next; } hashentry_t; typedef unsigned int(*hash_func_t)(const void* key); diff --git a/src/jplist.c b/src/jplist.c index 782d2b3..9a40844 100644 --- a/src/jplist.c +++ b/src/jplist.c @@ -38,6 +38,7 @@ #include "plist.h" #include "strbuf.h" #include "jsmn.h" +#include "hashtable.h" #ifdef DEBUG static int plist_json_debug = 0; @@ -72,8 +73,10 @@ void plist_json_set_debug(int debug) } #ifndef HAVE_STRNDUP +#ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" +#endif static char* strndup(const char* str, size_t len) { char *newstr = (char *)malloc(len+1); @@ -83,8 +86,10 @@ static char* strndup(const char* str, size_t len) } return newstr; } +#ifndef _MSC_VER #pragma GCC diagnostic pop #endif +#endif static size_t dtostr(char *buf, size_t bufsize, double realval) { @@ -311,18 +316,32 @@ static int num_digits_u(uint64_t i) return n; } -static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) +static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, hashtable_t *visited) { plist_data_t data; if (!node) { return PLIST_ERR_INVALID_ARG; } + + if (depth > PLIST_MAX_NESTING_DEPTH) { + PLIST_JSON_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_JSON_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; unsigned int n_children = node_n_children(node); for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - plist_err_t res = node_estimate_size(ch, size, depth + 1, prettify); + plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, visited); if (res < 0) { return res; } @@ -398,6 +417,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept return PLIST_ERR_SUCCESS; } +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) +{ + 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, prettify, visited); + hash_table_destroy(visited); + return err; +} + plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify) { uint64_t size = 0; @@ -448,6 +476,7 @@ plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, in typedef struct { jsmntok_t* tokens; int count; + plist_err_t err; } jsmntok_info_t; static int64_t parse_decimal(const char* str, const char* str_end, char** endp) @@ -460,6 +489,8 @@ static int64_t parse_decimal(const char* str, const char* str_end, char** endp) if (str[0] == '-') { is_neg = 1; (*endp)++; + } else if (str[0] == '+') { + (*endp)++; } if (is_neg) { MAX++; @@ -522,7 +553,7 @@ static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) } else { val = plist_new_uint((uint64_t)intpart); } - } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || ((*(endp+1) == '-') && endp+2 < str_end && isdigit(*(endp+2)))))) { + } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || (((*(endp+1) == '-') || (*(endp+1) == '+')) && endp+2 < str_end && isdigit(*(endp+2)))))) { /* floating point */ double dval = (double)intpart; char* fendp = endp; @@ -546,7 +577,7 @@ static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) if (fendp >= str_end) { break; } - if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || ((*(fendp+1) == '-') && fendp+2 < str_end && isdigit(*(fendp+2))))) { + if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || (((*(fendp+1) == '-') || (*(fendp+1) == '+')) && fendp+2 < str_end && isdigit(*(fendp+2))))) { int64_t exp = parse_decimal(fendp+1, str_end, &fendp); dval = dval * pow(10, (double)exp); } else { @@ -673,12 +704,18 @@ static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index) return node; } -static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index); +static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth); -static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index) +static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth) { if (ti->tokens[*index].type != JSMN_ARRAY) { PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__); + ti->err = PLIST_ERR_PARSE; + return NULL; + } + if (depth > PLIST_MAX_NESTING_DEPTH) { + PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH); + ti->err = PLIST_ERR_MAX_NESTING; return NULL; } plist_t arr = plist_new_array(); @@ -689,15 +726,16 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index) if (j >= ti->count) { PLIST_JSON_ERR("%s: token index out of valid range\n", __func__); plist_free(arr); + ti->err = PLIST_ERR_PARSE; return NULL; } plist_t val = NULL; switch (ti->tokens[j].type) { case JSMN_OBJECT: - val = parse_object(js, ti, &j); + val = parse_object(js, ti, &j, depth+1); break; case JSMN_ARRAY: - val = parse_array(js, ti, &j); + val = parse_array(js, ti, &j, depth+1); break; case JSMN_STRING: val = parse_string(js, ti, &j); @@ -712,6 +750,7 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index) plist_array_append_item(arr, val); } else { plist_free(arr); + ti->err = PLIST_ERR_PARSE; return NULL; } } @@ -719,10 +758,16 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index) return arr; } -static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index) +static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth) { if (ti->tokens[*index].type != JSMN_OBJECT) { PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__); + ti->err = PLIST_ERR_PARSE; + return NULL; + } + if (depth > PLIST_MAX_NESTING_DEPTH) { + PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH); + ti->err = PLIST_ERR_MAX_NESTING; return NULL; } int num_tokens = ti->tokens[*index].size; @@ -730,6 +775,7 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index) int j = (*index)+1; if (num_tokens % 2 != 0) { PLIST_JSON_ERR("%s: number of children must be even\n", __func__); + ti->err = PLIST_ERR_PARSE; return NULL; } plist_t obj = plist_new_dict(); @@ -737,12 +783,14 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index) if (j+1 >= ti->count) { PLIST_JSON_ERR("%s: token index out of valid range\n", __func__); plist_free(obj); + ti->err = PLIST_ERR_PARSE; return NULL; } if (ti->tokens[j].type == JSMN_STRING) { char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL); if (!key) { plist_free(obj); + ti->err = PLIST_ERR_PARSE; return NULL; } plist_t val = NULL; @@ -750,10 +798,10 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index) num++; switch (ti->tokens[j].type) { case JSMN_OBJECT: - val = parse_object(js, ti, &j); + val = parse_object(js, ti, &j, depth+1); break; case JSMN_ARRAY: - val = parse_array(js, ti, &j); + val = parse_array(js, ti, &j, depth+1); break; case JSMN_STRING: val = parse_string(js, ti, &j); @@ -769,12 +817,14 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index) } else { free(key); plist_free(obj); + ti->err = PLIST_ERR_PARSE; return NULL; } free(key); } else { PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__); plist_free(obj); + ti->err = PLIST_ERR_PARSE; return NULL; } } @@ -834,7 +884,7 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) } int startindex = 0; - jsmntok_info_t ti = { tokens, parser.toknext }; + jsmntok_info_t ti = { tokens, parser.toknext, PLIST_ERR_SUCCESS }; switch (tokens[startindex].type) { case JSMN_PRIMITIVE: *plist = parse_primitive(json, &ti, &startindex); @@ -843,14 +893,17 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) *plist = parse_string(json, &ti, &startindex); break; case JSMN_ARRAY: - *plist = parse_array(json, &ti, &startindex); + *plist = parse_array(json, &ti, &startindex, 0); break; case JSMN_OBJECT: - *plist = parse_object(json, &ti, &startindex); + *plist = parse_object(json, &ti, &startindex, 0); break; default: break; } free(tokens); + if (!*plist) { + return (ti.err != PLIST_ERR_SUCCESS) ? ti.err : PLIST_ERR_PARSE; + } return PLIST_ERR_SUCCESS; } diff --git a/src/oplist.c b/src/oplist.c index 6ab6603..0eea27a 100644 --- a/src/oplist.c +++ b/src/oplist.c @@ -37,6 +37,7 @@ #include "plist.h" #include "strbuf.h" +#include "hashtable.h" #ifdef DEBUG static int plist_ostep_debug = 0; @@ -71,8 +72,10 @@ void plist_ostep_set_debug(int debug) } #ifndef HAVE_STRNDUP +#ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" +#endif static char* strndup(const char* str, size_t len) { char *newstr = (char *)malloc(len+1); @@ -82,8 +85,10 @@ static char* strndup(const char* str, size_t len) } return newstr; } +#ifndef _MSC_VER #pragma GCC diagnostic pop #endif +#endif static size_t dtostr(char *buf, size_t bufsize, double realval) { @@ -355,18 +360,32 @@ static int num_digits_u(uint64_t i) return n; } -static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) +static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, hashtable_t *visited) { plist_data_t data; if (!node) { return PLIST_ERR_INVALID_ARG; } + + if (depth > PLIST_MAX_NESTING_DEPTH) { + PLIST_OSTEP_WRITE_ERR("node tree is nested too deeply\n"); + return PLIST_ERR_MAX_NESTING; + } + + if (hash_table_lookup(visited, node)) { + PLIST_OSTEP_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; unsigned int n_children = node_n_children(node); for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - plist_err_t res = node_estimate_size(ch, size, depth + 1, prettify); + plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, visited); if (res < 0) { return res; } @@ -442,6 +461,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept return PLIST_ERR_SUCCESS; } +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) +{ + 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, prettify, visited); + hash_table_destroy(visited); + return err; +} + plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify) { uint64_t size = 0; @@ -488,7 +516,7 @@ struct _parse_ctx { const char *start; const char *pos; const char *end; - int err; + plist_err_t err; uint32_t depth; }; typedef struct _parse_ctx* parse_ctx; @@ -545,50 +573,50 @@ static void parse_dict_data(parse_ctx ctx, plist_t dict) } key = NULL; ctx->err = node_from_openstep(ctx, &key); - if (ctx->err != 0) { + if (ctx->err != PLIST_ERR_SUCCESS) { break; } if (!PLIST_IS_STRING(key)) { PLIST_OSTEP_ERR("Invalid type for dictionary key at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing dictionary '=' delimiter at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (*ctx->pos != '=') { PLIST_OSTEP_ERR("Missing '=' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } ctx->pos++; if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } val = NULL; ctx->err = node_from_openstep(ctx, &val); - if (ctx->err != 0) { + if (ctx->err != PLIST_ERR_SUCCESS) { break; } if (!val) { PLIST_OSTEP_ERR("Missing value for dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing dictionary item terminator ';' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (*ctx->pos != ';') { PLIST_OSTEP_ERR("Missing terminating ';' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } @@ -608,10 +636,10 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) plist_t subnode = NULL; const char *p = NULL; ctx->depth++; - if (ctx->depth > 1000) { + if (ctx->depth > PLIST_MAX_NESTING_DEPTH) { PLIST_OSTEP_ERR("Too many levels of recursion (%u) at offset %ld\n", ctx->depth, (long int)(ctx->pos - ctx->start)); - ctx->err++; - return PLIST_ERR_PARSE; + ctx->err = PLIST_ERR_MAX_NESTING; + return ctx->err; } while (ctx->pos < ctx->end && !ctx->err) { parse_skip_ws(ctx); @@ -629,12 +657,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) } if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing dictionary terminator '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (*ctx->pos != '}') { PLIST_OSTEP_ERR("Missing terminating '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos++; @@ -652,11 +680,11 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) break; } ctx->err = node_from_openstep(ctx, &tmp); - if (ctx->err != 0) { + if (ctx->err != PLIST_ERR_SUCCESS) { break; } if (!tmp) { - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } plist_array_append_item(subnode, tmp); @@ -664,7 +692,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing array item delimiter ',' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (*ctx->pos != ',') { @@ -674,17 +702,17 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) } plist_free(tmp); tmp = NULL; - if (ctx->err) { + if (ctx->err != PLIST_ERR_SUCCESS) { goto err_out; } if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing array terminator ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (*ctx->pos != ')') { PLIST_OSTEP_ERR("Missing terminating ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos++; @@ -699,7 +727,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (*ctx->pos == '>') { @@ -707,19 +735,19 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) } if (!isxdigit(*ctx->pos)) { PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } uint8_t b = HEX_DIGIT(*ctx->pos); ctx->pos++; if (ctx->pos >= ctx->end) { PLIST_OSTEP_ERR("Unexpected end of data at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } if (!isxdigit(*ctx->pos)) { PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } b = (b << 4) + HEX_DIGIT(*ctx->pos); @@ -735,14 +763,14 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) byte_array_free(bytes); plist_free_data(data); PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (*ctx->pos != '>') { byte_array_free(bytes); plist_free_data(data); PLIST_OSTEP_ERR("Missing terminating '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos++; @@ -757,7 +785,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) char c = *ctx->pos; ctx->pos++; p = ctx->pos; - int num_escapes = 0; + size_t num_escapes = 0; while (ctx->pos < ctx->end) { if (*ctx->pos == '\\') { num_escapes++; @@ -770,13 +798,13 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) if (ctx->pos >= ctx->end) { plist_free_data(data); PLIST_OSTEP_ERR("EOF while parsing quoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (*ctx->pos != c) { plist_free_data(data); PLIST_OSTEP_ERR("Missing closing quote (%c) at offset %ld\n", c, (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } size_t slen = ctx->pos - p; @@ -877,7 +905,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) } else { plist_free_data(data); PLIST_OSTEP_ERR("Unexpected character when parsing unquoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; break; } } @@ -886,11 +914,11 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) ctx->depth--; err_out: - if (ctx->err) { + if (ctx->err != PLIST_ERR_SUCCESS) { plist_free(subnode); plist_free(*plist); *plist = NULL; - return PLIST_ERR_PARSE; + return ctx->err; } return PLIST_ERR_SUCCESS; } diff --git a/src/out-default.c b/src/out-default.c index 266070b..fb57bcf 100644 --- a/src/out-default.c +++ b/src/out-default.c @@ -38,6 +38,7 @@ #include "plist.h" #include "strbuf.h" #include "time64.h" +#include "hashtable.h" #define MAC_EPOCH 978307200 @@ -310,19 +311,37 @@ static int num_digits_u(uint64_t i) return n; } -static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data) +static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data, hashtable_t *visited) { plist_data_t data; if (!node) { return PLIST_ERR_INVALID_ARG; } + + if (depth > PLIST_MAX_NESTING_DEPTH) { +#if DEBUG + fprintf(stderr, "libplist: ERROR: maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); +#endif + return PLIST_ERR_MAX_NESTING; + } + + if (hash_table_lookup(visited, node)) { +#if DEBUG + fprintf(stderr, "libplist: ERROR: circular reference detected\n"); +#endif + 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; unsigned int n_children = node_n_children(node); for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - plist_err_t res = node_estimate_size(ch, size, depth + 1, indent, partial_data); - if (res < 0) { + plist_err_t res = _node_estimate_size(ch, size, depth + 1, indent, partial_data, visited); + if (res != PLIST_ERR_SUCCESS) { return res; } } @@ -401,6 +420,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept return PLIST_ERR_SUCCESS; } +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data) +{ + 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, indent, partial_data, visited); + hash_table_destroy(visited); + return err; +} + static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) { uint8_t indent = 0; diff --git a/src/out-limd.c b/src/out-limd.c index 7d861f8..35247fb 100644 --- a/src/out-limd.c +++ b/src/out-limd.c @@ -40,6 +40,7 @@ #include "strbuf.h" #include "time64.h" #include "base64.h" +#include "hashtable.h" #define MAC_EPOCH 978307200 @@ -278,19 +279,37 @@ static int num_digits_u(uint64_t i) return n; } -static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent) +static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, hashtable_t *visited) { plist_data_t data; if (!node) { return PLIST_ERR_INVALID_ARG; } + + if (depth > PLIST_MAX_NESTING_DEPTH) { +#if DEBUG + fprintf(stderr, "libplist: ERROR: maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); +#endif + return PLIST_ERR_MAX_NESTING; + } + + if (hash_table_lookup(visited, node)) { +#if DEBUG + fprintf(stderr, "libplist: ERROR: circular reference detected\n"); +#endif + 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; unsigned int n_children = node_n_children(node); for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - plist_err_t res = node_estimate_size(ch, size, depth + 1, indent); - if (res < 0) { + plist_err_t res = _node_estimate_size(ch, size, depth + 1, indent, visited); + if (res != PLIST_ERR_SUCCESS) { return res; } } @@ -359,6 +378,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept return PLIST_ERR_SUCCESS; } +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent) +{ + 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, indent, visited); + hash_table_destroy(visited); + return err; +} + static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) { uint8_t indent = 0; diff --git a/src/out-plutil.c b/src/out-plutil.c index d85f22c..bf9e72e 100644 --- a/src/out-plutil.c +++ b/src/out-plutil.c @@ -38,6 +38,7 @@ #include "plist.h" #include "strbuf.h" #include "time64.h" +#include "hashtable.h" #define MAC_EPOCH 978307200 @@ -304,19 +305,37 @@ static int num_digits_u(uint64_t i) return n; } -static plist_err_t 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 PLIST_ERR_INVALID_ARG; } + + if (depth > PLIST_MAX_NESTING_DEPTH) { +#if DEBUG + fprintf(stderr, "libplist: ERROR: maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); +#endif + return PLIST_ERR_MAX_NESTING; + } + + if (hash_table_lookup(visited, node)) { +#if DEBUG + fprintf(stderr, "libplist: ERROR: circular reference detected\n"); +#endif + 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; unsigned int n_children = node_n_children(node); for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - plist_err_t res = node_estimate_size(ch, size, depth + 1); - if (res < 0) { + plist_err_t res = _node_estimate_size(ch, size, depth + 1, visited); + if (res != PLIST_ERR_SUCCESS) { return res; } } @@ -388,6 +407,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept 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; +} + static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) { plist_err_t res = node_to_string((node_t)plist, &outbuf, 0); diff --git a/src/plist.c b/src/plist.c index 2078520..17b8419 100644 --- a/src/plist.c +++ b/src/plist.c @@ -35,11 +35,10 @@ #include <limits.h> #include <float.h> #include <ctype.h> +#include <inttypes.h> #ifdef WIN32 #include <windows.h> -#else -#include <pthread.h> #endif #include <node.h> @@ -47,10 +46,92 @@ #include <hashtable.h> #include <ptrarray.h> +#define MAC_EPOCH 978307200 + #ifdef _MSC_VER typedef SSIZE_T ssize_t; #endif +#ifdef DEBUG +static int plist_debug = 0; +#define PLIST_ERR(...) if (plist_debug > 0) { fprintf(stderr, "libplist ERROR: " __VA_ARGS__); } +#else +#define PLIST_ERR(...) +#endif + +#ifndef bswap16 +#define bswap16(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) +#endif + +#ifndef bswap32 +#define bswap32(x) ((((x) & 0xFF000000) >> 24) \ + | (((x) & 0x00FF0000) >> 8) \ + | (((x) & 0x0000FF00) << 8) \ + | (((x) & 0x000000FF) << 24)) +#endif + +#ifndef bswap64 +#define bswap64(x) ((((x) & 0xFF00000000000000ull) >> 56) \ + | (((x) & 0x00FF000000000000ull) >> 40) \ + | (((x) & 0x0000FF0000000000ull) >> 24) \ + | (((x) & 0x000000FF00000000ull) >> 8) \ + | (((x) & 0x00000000FF000000ull) << 8) \ + | (((x) & 0x0000000000FF0000ull) << 24) \ + | (((x) & 0x000000000000FF00ull) << 40) \ + | (((x) & 0x00000000000000FFull) << 56)) +#endif + +#ifndef le16toh +#ifdef __BIG_ENDIAN__ +#define le16toh(x) bswap16(x) +#else +#define le16toh(x) (x) +#endif +#endif + +#ifndef le32toh +#ifdef __BIG_ENDIAN__ +#define le32toh(x) bswap32(x) +#else +#define le32toh(x) (x) +#endif +#endif + +#ifndef le64toh +#ifdef __BIG_ENDIAN__ +#define le64toh(x) bswap64(x) +#else +#define le64toh(x) (x) +#endif +#endif + +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif +#else + #define INITIALIZER(f) \ + static void f(void) __attribute__((__constructor__)); \ + static void f(void) +#endif + extern void plist_xml_init(void); extern void plist_xml_deinit(void); extern void plist_bin_init(void); @@ -60,14 +141,6 @@ extern void plist_json_deinit(void); extern void plist_ostep_init(void); extern void plist_ostep_deinit(void); -static void internal_plist_init(void) -{ - plist_bin_init(); - plist_xml_init(); - plist_json_init(); - plist_ostep_init(); -} - static void internal_plist_deinit(void) { plist_bin_deinit(); @@ -76,66 +149,14 @@ static void internal_plist_deinit(void) plist_ostep_deinit(); } -#ifdef WIN32 -typedef volatile struct { - LONG lock; - int state; -} thread_once_t; - -static thread_once_t init_once = {0, 0}; -static thread_once_t deinit_once = {0, 0}; - -static void thread_once(thread_once_t *once_control, void (*init_routine)(void)) +INITIALIZER(internal_plist_init) { - while (InterlockedExchange(&(once_control->lock), 1) != 0) { - Sleep(1); - } - if (!once_control->state) { - once_control->state = 1; - init_routine(); - } - InterlockedExchange(&(once_control->lock), 0); -} -#else -static pthread_once_t init_once = PTHREAD_ONCE_INIT; -static pthread_once_t deinit_once = PTHREAD_ONCE_INIT; -#define thread_once pthread_once -#endif - -#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR - #if defined(__llvm__) || defined(__GNUC__) - #define HAVE_ATTRIBUTE_CONSTRUCTOR - #endif -#endif - -#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR -static void __attribute__((constructor)) libplist_initialize(void) -{ - thread_once(&init_once, internal_plist_init); -} - -static void __attribute__((destructor)) libplist_deinitialize(void) -{ - thread_once(&deinit_once, internal_plist_deinit); -} -#elif defined(WIN32) -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) -{ - switch (dwReason) { - case DLL_PROCESS_ATTACH: - thread_once(&init_once, internal_plist_init); - break; - case DLL_PROCESS_DETACH: - thread_once(&deinit_once, internal_plist_deinit); - break; - default: - break; - } - return 1; + plist_bin_init(); + plist_xml_init(); + plist_json_init(); + plist_ostep_init(); + atexit(internal_plist_deinit); } -#else -#warning No compiler support for constructor/destructor attributes, some features might not be available. -#endif #ifndef HAVE_MEMMEM // see https://sourceware.org/legacy-ml/libc-alpha/2007-12/msg00000.html @@ -337,7 +358,7 @@ plist_data_t plist_get_data(plist_t node) plist_data_t plist_new_plist_data(void) { - plist_data_t data = (plist_data_t) calloc(sizeof(struct plist_data_s), 1); + plist_data_t data = (plist_data_t) calloc(1, sizeof(struct plist_data_s)); return data; } @@ -494,8 +515,10 @@ plist_t plist_new_data(const char *val, uint64_t length) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_DATA; +if (val && length) { data->buff = (uint8_t *) malloc(length); memcpy(data->buff, val, length); +} data->length = length; return plist_new_node(data); } @@ -509,6 +532,15 @@ plist_t plist_new_date(int32_t sec, int32_t usec) return plist_new_node(data); } +plist_t plist_new_unix_date(int64_t sec) +{ + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_DATE; + data->realval = (double)sec - MAC_EPOCH; + data->length = sizeof(double); + return plist_new_node(data); +} + plist_t plist_new_null(void) { plist_data_t data = plist_new_plist_data(); @@ -957,6 +989,195 @@ void plist_dict_merge(plist_t *target, plist_t source) free(it); } +uint8_t plist_dict_get_bool(plist_t dict, const char *key) +{ + uint8_t bval = 0; + uint64_t uintval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return 0; + } + switch (plist_get_node_type(node)) { + case PLIST_BOOLEAN: + plist_get_bool_val(node, &bval); + break; + case PLIST_INT: + plist_get_uint_val(node, &uintval); + bval = (uintval) ? 1 : 0; + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + if (strcmp(strval, "true")) { + bval = 1; + } else if (strcmp(strval, "false")) { + bval = 0; + } else { + PLIST_ERR("%s: invalid string '%s' for string to boolean conversion\n", __func__, strval); + } + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 1) { + bval = (strval[0]) ? 1 : 0; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return bval; +} + +int64_t plist_dict_get_int(plist_t dict, const char *key) +{ + int64_t intval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return intval; + } + switch (plist_get_node_type(node)) { + case PLIST_INT: + plist_get_int_val(node, &intval); + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + intval = strtoll(strval, NULL, 0); + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 8) { + intval = le64toh(*(int64_t*)strval); + } else if (strsz == 4) { + intval = le32toh(*(int32_t*)strval); + } else if (strsz == 2) { + intval = le16toh(*(int16_t*)strval); + } else if (strsz == 1) { + intval = strval[0]; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return intval; +} + + +uint64_t plist_dict_get_uint(plist_t dict, const char *key) +{ + uint64_t uintval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return uintval; + } + switch (plist_get_node_type(node)) { + case PLIST_INT: + plist_get_uint_val(node, &uintval); + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + uintval = strtoull(strval, NULL, 0); + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 8) { + uintval = le64toh(*(uint64_t*)strval); + } else if (strsz == 4) { + uintval = le32toh(*(uint32_t*)strval); + } else if (strsz == 2) { + uintval = le16toh(*(uint16_t*)strval); + } else if (strsz == 1) { + uintval = strval[0]; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return uintval; +} + +plist_err_t plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + uint8_t bval = plist_dict_get_bool(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_bool(bval)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_int(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + int64_t i64val = plist_dict_get_int(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_int(i64val)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + uint64_t u64val = plist_dict_get_uint(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_uint(u64val)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!PLIST_IS_DATA(node)) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!PLIST_IS_STRING(node)) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) { plist_t current = plist; @@ -1184,20 +1405,40 @@ void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) } } +void plist_get_unix_date_val(plist_t node, int64_t *sec) +{ + if (!node || !sec) + return; + plist_type type = plist_get_node_type(node); + uint64_t length = 0; + double val = 0; + if (PLIST_DATE != type) + return; + plist_get_type_and_value(node, &type, (void *) &val, &length); + assert(length == sizeof(double)); + *sec = (int64_t)val + MAC_EPOCH; +} + 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 (a == b) + return TRUE; - if (!((node_t) a)->data || !((node_t) b)->data) + if (!a || !b) return FALSE; val_a = plist_get_data((plist_t) a); val_b = plist_get_data((plist_t) b); + if (val_a == NULL && val_b == NULL) + return TRUE; + + if (val_a == NULL || val_b == NULL) + return FALSE; + if (val_a->type != val_b->type) return FALSE; @@ -1209,28 +1450,30 @@ int plist_data_compare(const void *a, const void *b) case PLIST_REAL: case PLIST_DATE: case PLIST_UID: - if (val_a->length != val_b->length) - return FALSE; - return val_a->intval == val_b->intval; //it is an union so this is sufficient + return val_a->length == val_b->length + && val_a->intval == val_b->intval; // it is a union so this is sufficient case PLIST_KEY: case PLIST_STRING: + if (!val_a->strval || !val_b->strval) + return val_a->strval == val_b->strval; return strcmp(val_a->strval, val_b->strval) == 0; - case PLIST_DATA: + case PLIST_DATA: { if (val_a->length != val_b->length) return FALSE; + if (val_a->length == 0) + return TRUE; return memcmp(val_a->buff, val_b->buff, val_a->length) == 0; - + } case PLIST_ARRAY: case PLIST_DICT: //compare pointer return a == b; default: - break; + return FALSE; } - return FALSE; } char plist_compare_node_value(plist_t node_l, plist_t node_r) @@ -1340,7 +1583,13 @@ void plist_set_data_val(plist_t node, const char *val, uint64_t length) void plist_set_date_val(plist_t node, int32_t sec, int32_t usec) { double val = (double)sec + (double)usec / 1000000; - plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval)); + plist_set_element_val(node, PLIST_DATE, &val, sizeof(double)); +} + +void plist_set_unix_date_val(plist_t node, int64_t sec) +{ + double val = (double)(sec - MAC_EPOCH); + plist_set_element_val(node, PLIST_DATE, &val, sizeof(double)); } int plist_bool_val_is_true(plist_t boolnode) @@ -1462,9 +1711,12 @@ int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) if (!PLIST_IS_DATE(datenode)) { return -1; } - int32_t sec = 0; - int32_t usec = 0; - plist_get_date_val(datenode, &sec, &usec); + plist_data_t data = plist_get_data(datenode); + assert(data->length == sizeof(double)); + double val = data->realval; + int32_t sec = (int32_t)val; + val = fabs((val - (int64_t)val) * 1000000); + int32_t usec = (int32_t)val; uint64_t dateval = ((int64_t)sec << 32) | usec; uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec; if (dateval == cmpval) { @@ -1478,6 +1730,24 @@ int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) return 1; } +int plist_unix_date_val_compare(plist_t datenode, int64_t cmpval) +{ + if (!PLIST_IS_DATE(datenode)) { + return -1; + } + int64_t dateval = 0; + plist_get_unix_date_val(datenode, &dateval); + if (dateval == cmpval) { + return 0; + } + + if (dateval < cmpval) { + return -1; + } + + return 1; +} + int plist_string_val_compare(plist_t strnode, const char* cmpval) { if (!PLIST_IS_STRING(strnode)) { @@ -1577,6 +1847,9 @@ extern void plist_ostep_set_debug(int debug); void plist_set_debug(int debug) { +#if DEBUG + plist_debug = debug; +#endif plist_xml_set_debug(debug); plist_bin_set_debug(debug); plist_json_set_debug(debug); @@ -1597,6 +1870,9 @@ void plist_sort(plist_t plist) } else if (PLIST_IS_DICT(plist)) { node_t node = (node_t)plist; node_t ch; + if (!node_first_child(node)) { + return; + } for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { ch = node_next_sibling(ch); plist_sort((plist_t)ch); diff --git a/src/plist.h b/src/plist.h index a993e3a..e310bcc 100644 --- a/src/plist.h +++ b/src/plist.h @@ -49,6 +49,10 @@ #endif #endif +#ifndef PLIST_MAX_NESTING_DEPTH +#define PLIST_MAX_NESTING_DEPTH 512 +#endif + #include "plist/plist.h" struct plist_data_s @@ -81,4 +85,17 @@ extern plist_err_t plist_write_to_stream_default(plist_t plist, FILE *stream, pl extern plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options); extern plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options); +static inline unsigned int plist_node_ptr_hash(const void *ptr) +{ + uintptr_t h = (uintptr_t)ptr; + h ^= (h >> 16); + h *= 0x85ebca6b; + return (unsigned int)h; +} + +static inline int plist_node_ptr_compare(const void *a, const void *b) +{ + return a == b; +} + #endif diff --git a/src/time64.c b/src/time64.c index 07639df..cfbe7ac 100644 --- a/src/time64.c +++ b/src/time64.c @@ -224,6 +224,7 @@ Time64_T timegm64(const struct TM *date) { Time64_T days = 0; Time64_T seconds = 0; Year year; + assert(date != NULL); Year orig_year = (Year)date->tm_year; int cycles = 0; diff --git a/src/xplist.c b/src/xplist.c index 66e1dba..9870104 100644 --- a/src/xplist.c +++ b/src/xplist.c @@ -46,6 +46,7 @@ #include "base64.h" #include "strbuf.h" #include "time64.h" +#include "hashtable.h" #define XPLIST_KEY "key" #define XPLIST_KEY_LEN 3 @@ -256,7 +257,7 @@ static plist_err_t 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; @@ -444,17 +445,34 @@ static int num_digits_u(uint64_t i) return n; } -static plist_err_t 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 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; 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: @@ -529,6 +547,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept 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_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) { uint64_t size = 0; @@ -574,7 +601,7 @@ plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) struct _parse_ctx { const char *pos; const char *end; - int err; + plist_err_t err; }; typedef struct _parse_ctx* parse_ctx; @@ -698,21 +725,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) == '-') { @@ -725,7 +752,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; @@ -733,7 +760,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) { @@ -749,7 +776,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; @@ -763,14 +790,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 == '/') { @@ -779,25 +806,25 @@ 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) != 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++; @@ -973,6 +1000,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) void *prev; }; struct node_path_item* node_path = NULL; + int depth = 0; while (ctx->pos < ctx->end && !ctx->err) { parse_skip_ws(ctx); @@ -983,13 +1011,13 @@ static plist_err_t 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; } @@ -997,12 +1025,12 @@ static plist_err_t 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) != 0) { PLIST_XML_ERR("Couldn't find <? tag closing marker\n"); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } ctx->pos += 2; @@ -1014,7 +1042,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) 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; @@ -1025,7 +1053,7 @@ static plist_err_t 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 == '[') { @@ -1043,7 +1071,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) find_str(ctx, "]>", 2, 1); 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; @@ -1052,7 +1080,7 @@ static plist_err_t 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; @@ -1063,10 +1091,10 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) 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; + size_t taglen = ctx->pos - p; tag = (char*)malloc(taglen + 1); strncpy(tag, p, taglen); tag[taglen] = '\0'; @@ -1075,16 +1103,16 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) } 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; @@ -1101,14 +1129,14 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) } 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 = (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"; @@ -1119,17 +1147,17 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) } else if (!strcmp(tag, "/plist")) { if (!has_content) { 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; @@ -1157,7 +1185,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (tp->begin) { @@ -1166,7 +1194,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } char *str = str_content; @@ -1208,7 +1236,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (tp->begin) { @@ -1217,7 +1245,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } data->realval = atof(str_content); @@ -1252,14 +1280,14 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } str = text_parts_get_content(tp, 1, &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)) { @@ -1274,6 +1302,14 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) data->length = length; } } else { + if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) { + keyname = strdup(""); + free(tag); + tag = NULL; + plist_free(subnode); + subnode = NULL; + continue; + } data->strval = strdup(""); data->length = 0; } @@ -1285,7 +1321,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } if (tp->begin) { @@ -1294,7 +1330,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } size_t size = tp->length; @@ -1317,7 +1353,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } Time64_T timev = 0; @@ -1328,7 +1364,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err++; + ctx->err = PLIST_ERR_PARSE; goto err_out; } @@ -1357,7 +1393,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) } 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) { @@ -1375,7 +1411,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) 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); @@ -1386,35 +1422,42 @@ static plist_err_t 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; } } if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) { + 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; } subnode = NULL; } else 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; } + if (depth > 0) depth--; struct node_path_item *path_item = node_path; node_path = (struct node_path_item*)node_path->prev; free(path_item); @@ -1436,7 +1479,7 @@ static plist_err_t 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: @@ -1451,10 +1494,10 @@ err_out: free(path_item); } - if (ctx->err) { + if (ctx->err != PLIST_ERR_SUCCESS) { plist_free(*plist); *plist = NULL; - return PLIST_ERR_PARSE; + return ctx->err; } /* check if we have a UID "dict" so we can replace it with a proper UID node */ @@ -1481,7 +1524,7 @@ plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * pli 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 }; return node_from_xml(&ctx, plist); } |
