summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Array.cpp51
-rw-r--r--src/Boolean.cpp2
-rw-r--r--src/Data.cpp13
-rw-r--r--src/Date.cpp24
-rw-r--r--src/Dictionary.cpp10
-rw-r--r--src/Integer.cpp5
-rw-r--r--src/Key.cpp4
-rw-r--r--src/Node.cpp2
-rw-r--r--src/Real.cpp2
-rw-r--r--src/String.cpp25
-rw-r--r--src/Structure.cpp27
-rw-r--r--src/Uid.cpp2
-rw-r--r--src/base64.c2
-rw-r--r--src/bplist.c333
-rw-r--r--src/hashtable.c10
-rw-r--r--src/hashtable.h2
-rw-r--r--src/jplist.c104
-rw-r--r--src/jsmn.c43
-rw-r--r--src/jsmn.h18
-rw-r--r--src/oplist.c98
-rw-r--r--src/out-default.c34
-rw-r--r--src/out-limd.c34
-rw-r--r--src/out-plutil.c34
-rw-r--r--src/plist.c652
-rw-r--r--src/plist.h17
-rw-r--r--src/time64.c5
-rw-r--r--src/time64.h4
-rw-r--r--src/xplist.c469
28 files changed, 1452 insertions, 574 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..f0c44fc 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,72 +1142,87 @@ 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(const unsigned char *unistr, size_t size, size_t *items_read, size_t *items_written)
{
- uint16_t *outbuf;
- int p = 0;
- long i = 0;
-
- unsigned char c0;
- unsigned char c1;
- unsigned char c2;
- unsigned char c3;
-
- uint32_t w;
-
- outbuf = (uint16_t*)malloc(((size*2)+1)*sizeof(uint16_t));
- if (!outbuf) {
- PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)((size*2)+1)*sizeof(uint16_t));
- return NULL;
- }
+ uint16_t *outbuf;
+ size_t p = 0;
+ size_t i = 0;
+
+ unsigned char c0;
+ unsigned char c1;
+ unsigned char c2;
+ unsigned char c3;
+
+ outbuf = (uint16_t*)malloc(((size*2)+1)*sizeof(uint16_t));
+ if (!outbuf) {
+ PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)((size*2)+1)*sizeof(uint16_t));
+ return NULL;
+ }
- while (i < size) {
- c0 = unistr[i];
- c1 = (i < size-1) ? unistr[i+1] : 0;
- c2 = (i < size-2) ? unistr[i+2] : 0;
- c3 = (i < size-3) ? unistr[i+3] : 0;
- if ((c0 >= 0xF0) && (i < size-3) && (c1 >= 0x80) && (c2 >= 0x80) && (c3 >= 0x80)) {
- // 4 byte sequence. Need to generate UTF-16 surrogate pair
- w = ((((c0 & 7) << 18) + ((c1 & 0x3F) << 12) + ((c2 & 0x3F) << 6) + (c3 & 0x3F)) & 0x1FFFFF) - 0x010000;
- outbuf[p++] = be16toh(0xD800 + (w >> 10));
- outbuf[p++] = be16toh(0xDC00 + (w & 0x3FF));
- i+=4;
- } else if ((c0 >= 0xE0) && (i < size-2) && (c1 >= 0x80) && (c2 >= 0x80)) {
- // 3 byte sequence
- outbuf[p++] = be16toh(((c2 & 0x3F) + ((c1 & 3) << 6)) + (((c1 >> 2) & 15) << 8) + ((c0 & 15) << 12));
- i+=3;
- } else if ((c0 >= 0xC0) && (i < size-1) && (c1 >= 0x80)) {
- // 2 byte sequence
- outbuf[p++] = be16toh(((c1 & 0x3F) + ((c0 & 3) << 6)) + (((c0 >> 2) & 7) << 8));
- i+=2;
- } else if (c0 < 0x80) {
- // 1 byte sequence
- outbuf[p++] = be16toh(c0);
- i+=1;
- } else {
- // invalid character
- PLIST_BIN_ERR("%s: invalid utf8 sequence in string at index %lu\n", __func__, i);
- break;
- }
- }
- if (items_read) {
- *items_read = i;
- }
- if (items_written) {
- *items_written = p;
- }
- outbuf[p] = 0;
+ while (i < size) {
+ c0 = unistr[i];
+ c1 = (i+1 < size) ? unistr[i+1] : 0;
+ c2 = (i+2 < size) ? unistr[i+2] : 0;
+ c3 = (i+3 < size) ? unistr[i+3] : 0;
+ if ((c0 >= 0xF0 && c0 <= 0xF4) && (i+3 < size) && ((c1 & 0xC0) == 0x80) && ((c2 & 0xC0) == 0x80) && ((c3 & 0xC0) == 0x80)) {
+ // 4 byte sequence. Need to generate UTF-16 surrogate pair
+ /* lead-specific second-byte constraints */
+ if ((c0 == 0xF0 && c1 < 0x90) || /* overlong (< U+10000) */
+ (c0 == 0xF4 && c1 > 0x8F)) /* > U+10FFFF */
+ {
+ break;
+ }
+ uint32_t w = ((uint32_t)(c3 & 0x3F)) | ((uint32_t)(c2 & 0x3F) << 6) | ((uint32_t)(c1 & 0x3F) << 12) | ((uint32_t)(c0 & 0x07) << 18);
+ if (w < 0x10000 || w > 0x10FFFF) break;
+ w -= 0x10000;
+ outbuf[p++] = be16toh((uint16_t)(0xD800 + (w >> 10)));
+ outbuf[p++] = be16toh((uint16_t)(0xDC00 + (w & 0x3FF)));
+ i+=4;
+ } else if (((c0 & 0xF0) == 0xE0) && (i+2 < size) && ((c1 & 0xC0) == 0x80) && ((c2 & 0xC0) == 0x80)) {
+ // 3 byte sequence
+ if ((c0 == 0xE0 && c1 < 0xA0) || /* overlong (< U+0800) */
+ (c0 == 0xED && c1 > 0x9F)) /* UTF-16 surrogate range */
+ {
+ break;
+ }
+ uint32_t w = ((uint32_t)(c2 & 0x3F)) | ((uint32_t)(c1 & 0x3F) << 6) | ((uint32_t)(c0 & 0x0F) << 12);
+ if (w < 0x800) break;
+ if (w >= 0xD800 && w <= 0xDFFF) break; // invalid Unicode scalar values
+ outbuf[p++] = be16toh((uint16_t)w);
+ i+=3;
+ } else if ((c0 >= 0xC2 && c0 <= 0xDF) && (i+1 < size) && ((c1 & 0xC0) == 0x80)) {
+ // 2 byte sequence
+ uint32_t w = ((uint32_t)(c1 & 0x3F)) | ((uint32_t)(c0 & 0x1F) << 6);
+ outbuf[p++] = be16toh((uint16_t)w);
+ i+=2;
+ } else if (c0 < 0x80) {
+ // 1 byte sequence
+ outbuf[p++] = be16toh((uint16_t)c0);
+ i+=1;
+ } else {
+ // invalid character
+ PLIST_BIN_ERR("%s: invalid utf8 sequence in string at index %zu\n", __func__, i);
+ break;
+ }
+ }
+ if (items_read) {
+ *items_read = i;
+ }
+ if (items_written) {
+ *items_written = p;
+ }
+ outbuf[p] = 0;
- return outbuf;
+ 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);
+ unicodestr = plist_utf8_to_utf16be((const unsigned char *)val, size, &items_read, &items_written);
write_raw_data(bplist, BPLIST_UNICODE, (uint8_t*)unicodestr, items_written);
free(unicodestr);
}
@@ -1180,24 +1286,24 @@ static void write_uid(bytearray_t * bplist, uint64_t val)
byte_array_append(bplist, (uint8_t*)&val + (8-size), size);
}
-static int is_ascii_string(char* s, int len)
+static int is_ascii_string(const char* s, size_t len)
{
- int ret = 1, i = 0;
- for(i = 0; i < len; i++)
- {
- if ( !isascii( s[i] ) )
- {
- ret = 0;
- break;
- }
- }
- return ret;
+ int ret = 1;
+ size_t i = 0;
+ for (i = 0; i < len; i++) {
+ if ( !isascii( s[i] ) ) {
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
}
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 +1333,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..2c88756 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,31 +704,38 @@ 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();
- int num_tokens = ti->tokens[*index].size;
- int num;
+ size_t num_tokens = ti->tokens[*index].size;
+ size_t num;
int j = (*index)+1;
for (num = 0; num < num_tokens; num++) {
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,17 +758,24 @@ 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;
- int num;
+ size_t num_tokens = ti->tokens[*index].size;
+ size_t num;
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;
}
}
@@ -794,14 +844,15 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
jsmn_parser parser;
jsmn_init(&parser);
- int maxtoks = 256;
- int curtoks = 0;
+ unsigned int maxtoks = 256;
+ unsigned int curtoks = 0;
int r = 0;
jsmntok_t *tokens = NULL;
do {
jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks);
if (!newtokens) {
+ free(tokens);
PLIST_JSON_ERR("%s: Out of memory\n", __func__);
return PLIST_ERR_NO_MEM;
}
@@ -811,8 +862,14 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
r = jsmn_parse(&parser, json, length, tokens, maxtoks);
if (r == JSMN_ERROR_NOMEM) {
+ if (maxtoks > (unsigned int)INT_MAX - 16) {
+ free(tokens);
+ return PLIST_ERR_NO_MEM;
+ }
maxtoks+=16;
continue;
+ } else if (r < 0) {
+ break;
}
} while (r == JSMN_ERROR_NOMEM);
@@ -829,12 +886,16 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__);
free(tokens);
return PLIST_ERR_PARSE;
+ case JSMN_ERROR_LIMIT:
+ PLIST_JSON_ERR("%s: Input data too large\n", __func__);
+ free(tokens);
+ return PLIST_ERR_PARSE;
default:
break;
}
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 +904,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/jsmn.c b/src/jsmn.c
index f190312..889b8d7 100644
--- a/src/jsmn.c
+++ b/src/jsmn.c
@@ -3,6 +3,8 @@
* Simple JSON parser
*
* Copyright (c) 2010 Serge A. Zaitsev
+ * Updated to use size_t for token offsets and harden against overflows.
+ * (Nikias Bassen, January 2026)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,20 +26,25 @@
*/
#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <assert.h>
#include "jsmn.h"
+#define JSMN_POS_INVALID ((size_t)SIZE_MAX)
+
/**
* Allocates a fresh unused token from the token pull.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
- jsmntok_t *tokens, int num_tokens) {
+ jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *tok;
- if (parser->toknext >= num_tokens) {
+ if ((unsigned int)parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
- tok->start = tok->end = -1;
+ tok->start = tok->end = JSMN_POS_INVALID;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
@@ -49,7 +56,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
- int start, int end) {
+ size_t start, size_t end) {
token->type = type;
token->start = start;
token->end = end;
@@ -60,9 +67,9 @@ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
* Fills next available token with JSON primitive.
*/
static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
- jsmntok_t *tokens, int num_tokens) {
+ jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *token;
- int start;
+ size_t start;
start = parser->pos;
@@ -107,10 +114,10 @@ found:
* Fills next token with JSON string.
*/
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
- jsmntok_t *tokens, int num_tokens) {
+ jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *token;
- int start = parser->pos;
+ size_t start = parser->pos;
parser->pos++;
@@ -162,7 +169,7 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
/**
* Parse JSON string and fill tokens.
*/
-jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, jsmntok_t *tokens,
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t length, jsmntok_t *tokens,
unsigned int num_tokens) {
jsmnerr_t r;
int i;
@@ -170,6 +177,13 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
parser->end = length;
+ if (num_tokens >= INT_MAX) {
+ return JSMN_ERROR_LIMIT;
+ }
+ if (length > SIZE_MAX / 2) {
+ return JSMN_ERROR_LIMIT;
+ }
+
for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) {
char c;
jsmntype_t type;
@@ -198,10 +212,13 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
}
token = &tokens[parser->toknext - 1];
for (;;) {
- if (token->start != -1 && token->end == -1) {
+ if (token->start != JSMN_POS_INVALID && token->end == JSMN_POS_INVALID) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
+ if (parser->pos == SIZE_MAX) {
+ return JSMN_ERROR_INVAL;
+ }
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
@@ -214,7 +231,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
- if (token->start != -1 && token->end == -1) {
+ if (token->start != JSMN_POS_INVALID && token->end == JSMN_POS_INVALID) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
@@ -227,7 +244,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
if (i == -1) return JSMN_ERROR_INVAL;
for (; i >= 0; i--) {
token = &tokens[i];
- if (token->start != -1 && token->end == -1) {
+ if (token->start != JSMN_POS_INVALID && token->end == JSMN_POS_INVALID) {
parser->toksuper = i;
break;
}
@@ -268,7 +285,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
- if (tokens[i].start != -1 && tokens[i].end == -1) {
+ if (tokens[i].start != JSMN_POS_INVALID && tokens[i].end == JSMN_POS_INVALID) {
return JSMN_ERROR_PART;
}
}
diff --git a/src/jsmn.h b/src/jsmn.h
index 380744d..629a0dd 100644
--- a/src/jsmn.h
+++ b/src/jsmn.h
@@ -3,6 +3,8 @@
* Simple JSON parser (header file)
*
* Copyright (c) 2010 Serge A. Zaitsev
+ * Updated to use size_t for token offsets and harden against overflows.
+ * (Nikias Bassen, January 2026)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +27,8 @@
#ifndef __JSMN_H_
#define __JSMN_H_
+#include <stddef.h>
+
/**
* JSON type identifier. Basic types are:
* o Object
@@ -46,6 +50,8 @@ typedef enum {
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3,
+ /* Input exceeds implementation-defined limits */
+ JSMN_ERROR_LIMIT = -4,
/* Everything was fine */
JSMN_SUCCESS = 0
} jsmnerr_t;
@@ -58,9 +64,9 @@ typedef enum {
*/
typedef struct {
jsmntype_t type;
- int start;
- int end;
- int size;
+ size_t start;
+ size_t end;
+ size_t size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
@@ -71,8 +77,8 @@ typedef struct {
* the string being parsed now and current position in that string
*/
typedef struct {
- unsigned int pos; /* offset in the JSON string */
- unsigned int end; /* offset after last character of JSON string */
+ size_t pos; /* offset in the JSON string */
+ size_t end; /* offset after last character of JSON string */
int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;
@@ -86,7 +92,7 @@ void jsmn_init(jsmn_parser *parser);
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
* a single JSON object.
*/
-jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length,
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t length,
jsmntok_t *tokens, unsigned int num_tokens);
#endif /* __JSMN_H_ */
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..a9199ee 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
@@ -189,7 +210,7 @@ void* memmem(const void* haystack, size_t haystack_len, const void* needle, size
int plist_is_binary(const char *plist_data, uint32_t length)
{
- if (length < 8) {
+ if (plist_data == NULL || length < 8) {
return 0;
}
@@ -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();
@@ -549,12 +581,27 @@ static plist_t plist_copy_node(node_t node)
node_type = plist_get_node_type(node);
switch (node_type) {
case PLIST_DATA:
- newdata->buff = (uint8_t *) malloc(data->length);
- memcpy(newdata->buff, data->buff, data->length);
+ if (data->buff) {
+ newdata->buff = (uint8_t *) malloc(data->length);
+ assert(newdata->buff);
+ memcpy(newdata->buff, data->buff, data->length);
+ } else {
+ newdata->buff = NULL;
+ newdata->length = 0;
+ }
break;
case PLIST_KEY:
case PLIST_STRING:
- newdata->strval = strdup(data->strval);
+ if (data->strval) {
+ size_t n = strlen(data->strval);
+ newdata->strval = (char*)malloc(n+1);
+ assert(newdata->strval);
+ memcpy(newdata->strval, data->strval, n+1);
+ newdata->length = (uint64_t)n;
+ } else {
+ newdata->strval = NULL;
+ newdata->length = 0;
+ }
break;
case PLIST_ARRAY:
if (data->hashtable) {
@@ -666,50 +713,62 @@ static void _plist_array_post_insert(plist_t node, plist_t item, long n)
void plist_array_set_item(plist_t node, plist_t item, uint32_t n)
{
- if (!item) {
+ if (!PLIST_IS_ARRAY(node) || !item || n >= INT_MAX) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, item=%p, n=%u)\n", __func__, node, item, n);
return;
}
- if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX)
+ node_t it = (node_t)item;
+ if (it->parent != NULL) {
+ assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first");
+ PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__);
+ return;
+ }
+ plist_t old_item = plist_array_get_item(node, n);
+ if (old_item)
{
- plist_t old_item = plist_array_get_item(node, n);
- if (old_item)
- {
- int idx = plist_free_node((node_t)old_item);
- assert(idx >= 0);
- if (idx < 0) {
- return;
- }
- node_insert((node_t)node, idx, (node_t)item);
- ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
- if (pa) {
- ptr_array_set(pa, item, idx);
- }
+ int idx = plist_free_node((node_t)old_item);
+ assert(idx >= 0);
+ if (idx < 0) {
+ return;
+ }
+ node_insert((node_t)node, idx, (node_t)item);
+ ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
+ if (pa) {
+ ptr_array_set(pa, item, idx);
}
}
}
void plist_array_append_item(plist_t node, plist_t item)
{
- if (!item) {
+ if (!PLIST_IS_ARRAY(node) || !item) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, item=%p)\n", __func__, node, item);
return;
}
- if (node && PLIST_ARRAY == plist_get_node_type(node))
- {
- node_attach((node_t)node, (node_t)item);
- _plist_array_post_insert(node, item, -1);
+ node_t it = (node_t)item;
+ if (it->parent != NULL) {
+ assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first");
+ PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__);
+ return;
}
+ node_attach((node_t)node, (node_t)item);
+ _plist_array_post_insert(node, item, -1);
}
void plist_array_insert_item(plist_t node, plist_t item, uint32_t n)
{
- if (!item) {
+ if (!PLIST_IS_ARRAY(node) || !item || n >= INT_MAX) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, item=%p, n=%u)\n", __func__, node, item, n);
return;
}
- if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX)
- {
- node_insert((node_t)node, n, (node_t)item);
- _plist_array_post_insert(node, item, (long)n);
+ node_t it = (node_t)item;
+ if (it->parent != NULL) {
+ assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first");
+ PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__);
+ return;
}
+ node_insert((node_t)node, n, (node_t)item);
+ _plist_array_post_insert(node, item, (long)n);
}
void plist_array_remove_item(plist_t node, uint32_t n)
@@ -841,31 +900,39 @@ plist_t plist_dict_item_get_key(plist_t node)
plist_t plist_dict_get_item(plist_t node, const char* key)
{
plist_t ret = NULL;
-
- if (node && PLIST_DICT == plist_get_node_type(node))
- {
- plist_data_t data = plist_get_data(node);
- hashtable_t *ht = (hashtable_t*)data->hashtable;
- if (ht) {
- struct plist_data_s sdata;
- sdata.strval = (char*)key;
- sdata.length = strlen(key);
- ret = (plist_t)hash_table_lookup(ht, &sdata);
- } else {
- plist_t current = NULL;
- for (current = (plist_t)node_first_child((node_t)node);
- current;
- current = (plist_t)node_next_sibling(node_next_sibling((node_t)current)))
- {
- data = plist_get_data(current);
- assert( PLIST_KEY == plist_get_node_type(current) );
-
- if (data && !strcmp(key, data->strval))
- {
- ret = (plist_t)node_next_sibling((node_t)current);
- break;
- }
+ if (!PLIST_IS_DICT(node) || !key) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, key=%p)\n", __func__, node, key);
+ return NULL;
+ }
+ plist_data_t data = plist_get_data(node);
+ assert(data);
+ if (!data) {
+ PLIST_ERR("%s: invalid node\n", __func__);
+ return NULL;
+ }
+ size_t keylen = strlen(key);
+ hashtable_t *ht = (hashtable_t*)data->hashtable;
+ if (ht) {
+ struct plist_data_s sdata = { 0 };
+ sdata.strval = (char*)key;
+ sdata.length = keylen;
+ return (plist_t)hash_table_lookup(ht, &sdata);
+ } else {
+ plist_t k = NULL;
+ for (k = (plist_t)node_first_child((node_t)node); k; ) {
+ plist_t v = (plist_t)node_next_sibling(k);
+ if (!v) break;
+ data = plist_get_data(k);
+ assert(PLIST_IS_KEY(k));
+ if (!PLIST_IS_KEY(k) || !data || !data->strval) {
+ PLIST_ERR("invalid key node at %p\n", k);
+ break;
+ }
+ if (data->length == keylen && !memcmp(key, data->strval, keylen+1)) {
+ ret = v;
+ break;
}
+ k = node_next_sibling(v);
}
}
return ret;
@@ -873,44 +940,49 @@ plist_t plist_dict_get_item(plist_t node, const char* key)
void plist_dict_set_item(plist_t node, const char* key, plist_t item)
{
- if (!item) {
+ if (!PLIST_IS_DICT(node) || !key || !item) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, key=%p, item=%p)\n", __func__, node, key, item);
return;
}
- if (node && PLIST_DICT == plist_get_node_type(node)) {
- plist_t old_item = plist_dict_get_item(node, key);
- plist_t key_node = NULL;
+ node_t it = (node_t)item;
+ if (it->parent != NULL) {
+ assert(it->parent == NULL && "item already has a parent");
+ PLIST_ERR("%s: item already has a parent\n", __func__);
+ return;
+ }
+ plist_t old_item = plist_dict_get_item(node, key);
+ plist_t key_node = NULL;
if (old_item) {
- int idx = plist_free_node((node_t)old_item);
- assert(idx >= 0);
- if (idx < 0) {
- return;
- }
- node_insert((node_t)node, idx, (node_t)item);
- key_node = node_prev_sibling((node_t)item);
- } else {
- key_node = plist_new_key(key);
- node_attach((node_t)node, (node_t)key_node);
- node_attach((node_t)node, (node_t)item);
+ int idx = plist_free_node((node_t)old_item);
+ assert(idx >= 0);
+ if (idx < 0) {
+ return;
}
+ node_insert((node_t)node, idx, (node_t)item);
+ key_node = node_prev_sibling((node_t)item);
+ } else {
+ key_node = plist_new_key(key);
+ node_attach((node_t)node, (node_t)key_node);
+ node_attach((node_t)node, (node_t)item);
+ }
- hashtable_t *ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable;
- if (ht) {
- /* store pointer to item in hash table */
- hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item);
- } else {
- if (((node_t)node)->count > 500) {
- /* make new hash table */
- ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
- /* calculate the hashes for all entries we have so far */
- plist_t current = NULL;
- for (current = (plist_t)node_first_child((node_t)node);
- ht && current;
- current = (plist_t)node_next_sibling(node_next_sibling((node_t)current)))
- {
- hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current));
- }
- ((plist_data_t)((node_t)node)->data)->hashtable = ht;
+ hashtable_t *ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable;
+ if (ht) {
+ /* store pointer to item in hash table */
+ hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item);
+ } else {
+ if (((node_t)node)->count > 500) {
+ /* make new hash table */
+ ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
+ /* calculate the hashes for all entries we have so far */
+ plist_t current = NULL;
+ for (current = (plist_t)node_first_child((node_t)node);
+ ht && current;
+ current = (plist_t)node_next_sibling(node_next_sibling((node_t)current)))
+ {
+ hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current));
}
+ ((plist_data_t)((node_t)node)->data)->hashtable = ht;
}
}
}
@@ -957,6 +1029,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 +1445,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 +1490,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 +1623,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 +1751,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 +1770,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 +1887,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 +1910,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..218088e 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;
@@ -442,7 +443,7 @@ void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
}
-#ifndef HAVE_LOCALTIME_R
+#if !defined(HAVE_LOCALTIME_R) && !defined(_WIN32)
/* Simulate localtime_r() to the best of our ability */
static struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
const struct tm *static_result = localtime(time);
@@ -461,7 +462,7 @@ static struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
#endif
-#ifndef HAVE_GMTIME_R
+#if !defined(HAVE_GMTIME_R) && !defined(_WIN32)
/* Simulate gmtime_r() to the best of our ability */
static struct tm * fake_gmtime_r(const time_t *time, struct tm *result) {
const struct tm *static_result = gmtime(time);
diff --git a/src/time64.h b/src/time64.h
index efdc716..28968c0 100644
--- a/src/time64.h
+++ b/src/time64.h
@@ -58,11 +58,15 @@ Time64_T timelocal64 (struct TM *);
/* Not everyone has gm/localtime_r(), provide a replacement */
#ifdef HAVE_LOCALTIME_R
# define LOCALTIME_R(clock, result) localtime_r(clock, result)
+#elif defined(_WIN32)
+# define LOCALTIME_R(clock, result) (localtime_s(result, clock) ? NULL : result)
#else
# define LOCALTIME_R(clock, result) fake_localtime_r(clock, result)
#endif
#ifdef HAVE_GMTIME_R
# define GMTIME_R(clock, result) gmtime_r(clock, result)
+#elif defined (_WIN32)
+# define GMTIME_R(clock, result) (gmtime_s(result, clock) ? NULL : result)
#else
# define GMTIME_R(clock, result) fake_gmtime_r(clock, result)
#endif
diff --git a/src/xplist.c b/src/xplist.c
index 66e1dba..6100afc 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -39,6 +39,7 @@
#include <float.h>
#include <math.h>
#include <limits.h>
+#include <errno.h>
#include <node.h>
@@ -46,6 +47,7 @@
#include "base64.h"
#include "strbuf.h"
#include "time64.h"
+#include "hashtable.h"
#define XPLIST_KEY "key"
#define XPLIST_KEY_LEN 3
@@ -142,7 +144,7 @@ static plist_err_t 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;
@@ -171,19 +173,17 @@ static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth
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:
@@ -221,13 +221,11 @@ static plist_err_t 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*)calloc(1, 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");
}
}
}
@@ -235,11 +233,10 @@ static plist_err_t 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:
@@ -256,7 +253,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;
@@ -343,7 +340,7 @@ static plist_err_t 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);
@@ -354,7 +351,6 @@ static plist_err_t 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 */
@@ -386,24 +382,44 @@ static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth
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)
@@ -444,17 +460,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 +562,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,13 +616,18 @@ 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;
+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++;
}
}
@@ -698,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) == '-') {
@@ -725,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;
@@ -733,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) {
@@ -749,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;
@@ -763,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 == '/') {
@@ -779,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) != 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) {
@@ -826,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 */
@@ -891,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");
@@ -909,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;
@@ -918,14 +978,12 @@ 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) {
@@ -938,7 +996,7 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t
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) {
@@ -950,6 +1008,21 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t
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;
}
@@ -961,18 +1034,18 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t
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);
@@ -983,13 +1056,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 +1070,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;
@@ -1011,10 +1084,10 @@ static plist_err_t 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);
+ 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 +1098,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 +1116,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 +1125,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;
@@ -1060,55 +1133,56 @@ static plist_err_t 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 = (char*)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;
}
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";
@@ -1117,34 +1191,32 @@ static plist_err_t 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 = (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();
subnode = plist_new_node(data);
- has_content = 1;
if (!strcmp(tag, XPLIST_DICT)) {
data->type = PLIST_DICT;
@@ -1157,16 +1229,15 @@ 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) {
- 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((text_part_t*)first_part.next);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
char *str = str_content;
@@ -1177,7 +1248,30 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
}
str++;
}
- data->intval = strtoull(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) {
@@ -1188,17 +1282,16 @@ static plist_err_t 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((text_part_t*)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_INT;
} else if (!strcmp(tag, XPLIST_REAL)) {
@@ -1208,24 +1301,52 @@ 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) {
- 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((text_part_t*)first_part.next);
- ctx->err++;
+ 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((text_part_t*)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;
@@ -1252,20 +1373,18 @@ 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);
+ 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;
@@ -1274,6 +1393,12 @@ 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("");
+ plist_free(subnode);
+ subnode = NULL;
+ continue;
+ }
data->strval = strdup("");
data->length = 0;
}
@@ -1285,16 +1410,16 @@ 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) {
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((text_part_t*)first_part.next);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
size_t size = tp->length;
@@ -1307,7 +1432,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
free(str_content);
}
}
- text_parts_free((text_part_t*)tp->next);
+ text_parts_free((text_part_t*)first_part.next);
}
data->type = PLIST_DATA;
} else if (!strcmp(tag, XPLIST_DATE)) {
@@ -1317,47 +1442,45 @@ 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;
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((text_part_t*)first_part.next);
- ctx->err++;
+ 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((text_part_t*)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) {
@@ -1375,7 +1498,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,47 +1509,50 @@ 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) {
+ }
+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;
}
+ 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 = ((node_t)parent)->parent;
- if (!parent) {
- goto err_out;
- }
+ parent = (parent) ? ((node_t)parent)->parent : NULL;
+ /* parent can be NULL when we just closed the root node; keep parsing */
}
-
- free(tag);
- tag = NULL;
free(keyname);
keyname = NULL;
plist_free(subnode);
@@ -1436,11 +1562,10 @@ 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:
- free(tag);
free(keyname);
plist_free(subnode);
@@ -1451,16 +1576,16 @@ 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 */
if (PLIST_IS_DICT(*plist) && plist_dict_get_size(*plist) == 1) {
plist_t value = plist_dict_get_item(*plist, "CF$UID");
- if (PLIST_IS_UINT(value)) {
+ if (PLIST_IS_INT(value)) {
uint64_t u64val = 0;
plist_get_uint_val(value, &u64val);
plist_free(*plist);
@@ -1481,7 +1606,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);
}