From 957383e3e3f002b64683026af97a1c2579739035 Mon Sep 17 00:00:00 2001 From: firewave Date: Sun, 5 Apr 2020 23:03:20 +0200 Subject: [PATCH] updated TinyXML2 to 8.0.0 --- externals/tinyxml/tinyxml2.cpp | 265 ++++++++++++++++++++++++++------- externals/tinyxml/tinyxml2.h | 212 +++++++++++++++++--------- 2 files changed, 353 insertions(+), 124 deletions(-) diff --git a/externals/tinyxml/tinyxml2.cpp b/externals/tinyxml/tinyxml2.cpp index 05c164f713e..9e7ff1a62ab 100644 --- a/externals/tinyxml/tinyxml2.cpp +++ b/externals/tinyxml/tinyxml2.cpp @@ -45,14 +45,14 @@ distribution. { va_list va; va_start( va, format ); - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); va_end( va ); return result; } static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) { - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); return result; } @@ -101,9 +101,9 @@ distribution. #endif -static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF +static const char LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF static const char LF = LINE_FEED; -static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out +static const char CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out static const char CR = CARRIAGE_RETURN; static const char SINGLE_QUOTE = '\''; static const char DOUBLE_QUOTE = '\"'; @@ -197,7 +197,7 @@ char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLin TIXMLASSERT(curLineNumPtr); char* start = p; - char endChar = *endTag; + const char endChar = *endTag; size_t length = strlen( endTag ); // Inner loop of text parsing. @@ -310,7 +310,7 @@ const char* StrPair::GetStr() const int buflen = 10; char buf[buflen] = { 0 }; int len = 0; - char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + const char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); if ( adjusted == 0 ) { *q = *p; ++p; @@ -430,22 +430,22 @@ void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length switch (*length) { case 4: --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 3: --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 2: --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 1: --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); + *output = static_cast(input | FIRST_BYTE_MARK[*length]); break; default: TIXMLASSERT( false ); @@ -582,12 +582,17 @@ void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) } -void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) +void XMLUtil::ToStr( int64_t v, char* buffer, int bufferSize ) { // horrible syntax trick to make the compiler happy about %lld - TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); + TIXML_SNPRINTF(buffer, bufferSize, "%lld", static_cast(v)); } +void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize ) +{ + // horrible syntax trick to make the compiler happy about %llu + TIXML_SNPRINTF(buffer, bufferSize, "%llu", (long long)v); +} bool XMLUtil::ToInt( const char* str, int* value ) { @@ -612,13 +617,20 @@ bool XMLUtil::ToBool( const char* str, bool* value ) *value = (ival==0) ? false : true; return true; } - if ( StringEqual( str, "true" ) ) { - *value = true; - return true; + static const char* TRUE_VALS[] = { "true", "True", "TRUE", 0 }; + static const char* FALSE_VALS[] = { "false", "False", "FALSE", 0 }; + + for (int i = 0; TRUE_VALS[i]; ++i) { + if (StringEqual(str, TRUE_VALS[i])) { + *value = true; + return true; + } } - else if ( StringEqual( str, "false" ) ) { - *value = false; - return true; + for (int i = 0; FALSE_VALS[i]; ++i) { + if (StringEqual(str, FALSE_VALS[i])) { + *value = false; + return true; + } } return false; } @@ -646,13 +658,23 @@ bool XMLUtil::ToInt64(const char* str, int64_t* value) { long long v = 0; // horrible syntax trick to make the compiler happy about %lld if (TIXML_SSCANF(str, "%lld", &v) == 1) { - *value = (int64_t)v; + *value = static_cast(v); return true; } return false; } +bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { + unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu + if(TIXML_SSCANF(str, "%llu", &v) == 1) { + *value = (uint64_t)v; + return true; + } + return false; +} + + char* XMLDocument::Identify( char* p, XMLNode** node ) { TIXMLASSERT( node ); @@ -1004,7 +1026,11 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) // 'endTag' is the end tag for this node, it is returned by a call to a child. // 'parentEnd' is the end tag for the parent, which is filled in and returned. - while( p && *p ) { + XMLDocument::DepthTracker tracker(_document); + if (_document->Error()) + return 0; + + while( p && *p ) { XMLNode* node = 0; p = _document->Identify( p, &node ); @@ -1013,7 +1039,7 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) break; } - int initialLineNum = node->_parseLineNum; + const int initialLineNum = node->_parseLineNum; StrPair endTag; p = node->ParseDeep( p, &endTag, curLineNumPtr ); @@ -1025,18 +1051,28 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) break; } - XMLDeclaration* decl = node->ToDeclaration(); + const XMLDeclaration* const decl = node->ToDeclaration(); if ( decl ) { // Declarations are only allowed at document level - bool wellLocated = ( ToDocument() != 0 ); - if ( wellLocated ) { - // Multiple declarations are allowed but all declarations - // must occur before anything else - for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) { - if ( !existingNode->ToDeclaration() ) { - wellLocated = false; - break; - } + // + // Multiple declarations are allowed but all declarations + // must occur before anything else. + // + // Optimized due to a security test case. If the first node is + // a declaration, and the last node is a declaration, then only + // declarations have so far been added. + bool wellLocated = false; + + if (ToDocument()) { + if (FirstChild()) { + wellLocated = + FirstChild() && + FirstChild()->ToDeclaration() && + LastChild() && + LastChild()->ToDeclaration(); + } + else { + wellLocated = true; } } if ( !wellLocated ) { @@ -1359,7 +1395,7 @@ char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr return 0; } - char endTag[2] = { *p, 0 }; + const char endTag[2] = { *p, 0 }; ++p; // move past opening quote p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); @@ -1400,6 +1436,15 @@ XMLError XMLAttribute::QueryInt64Value(int64_t* value) const } +XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const +{ + if(XMLUtil::ToUnsigned64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + XMLError XMLAttribute::QueryBoolValue( bool* value ) const { if ( XMLUtil::ToBool( Value(), value )) { @@ -1456,6 +1501,12 @@ void XMLAttribute::SetAttribute(int64_t v) _value.SetStr(buf); } +void XMLAttribute::SetAttribute(uint64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} void XMLAttribute::SetAttribute( bool v ) @@ -1542,6 +1593,13 @@ int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const return i; } +uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const +{ + uint64_t i = defaultValue; + QueryUnsigned64Attribute(name, &i); + return i; +} + bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const { bool b = defaultValue; @@ -1606,6 +1664,12 @@ void XMLElement::SetText(int64_t v) SetText(buf); } +void XMLElement::SetText(uint64_t v) { + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + void XMLElement::SetText( bool v ) { @@ -1670,6 +1734,19 @@ XMLError XMLElement::QueryInt64Text(int64_t* ival) const } +XMLError XMLElement::QueryUnsigned64Text(uint64_t* ival) const +{ + if(FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if(XMLUtil::ToUnsigned64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + XMLError XMLElement::QueryBoolText( bool* bval ) const { if ( FirstChild() && FirstChild()->ToText() ) { @@ -1729,6 +1806,13 @@ int64_t XMLElement::Int64Text(int64_t defaultValue) const return i; } +uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const +{ + uint64_t i = defaultValue; + QueryUnsigned64Text(&i); + return i; +} + bool XMLElement::BoolText(bool defaultValue) const { bool b = defaultValue; @@ -1816,7 +1900,7 @@ char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) TIXMLASSERT( attrib ); attrib->_parseLineNum = _document->_parseCurLineNum; - int attrLineNum = attrib->_parseLineNum; + const int attrLineNum = attrib->_parseLineNum; p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); if ( !p || Attribute( attrib->Name() ) ) { @@ -1877,6 +1961,39 @@ XMLAttribute* XMLElement::CreateAttribute() return attrib; } + +XMLElement* XMLElement::InsertNewChildElement(const char* name) +{ + XMLElement* node = _document->NewElement(name); + return InsertEndChild(node) ? node : 0; +} + +XMLComment* XMLElement::InsertNewComment(const char* comment) +{ + XMLComment* node = _document->NewComment(comment); + return InsertEndChild(node) ? node : 0; +} + +XMLText* XMLElement::InsertNewText(const char* text) +{ + XMLText* node = _document->NewText(text); + return InsertEndChild(node) ? node : 0; +} + +XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text) +{ + XMLDeclaration* node = _document->NewDeclaration(text); + return InsertEndChild(node) ? node : 0; +} + +XMLUnknown* XMLElement::InsertNewUnknown(const char* text) +{ + XMLUnknown* node = _document->NewUnknown(text); + return InsertEndChild(node) ? node : 0; +} + + + // // // foobar @@ -1973,10 +2090,8 @@ const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { "XML_ERROR_FILE_NOT_FOUND", "XML_ERROR_FILE_COULD_NOT_BE_OPENED", "XML_ERROR_FILE_READ_ERROR", - "UNUSED_XML_ERROR_ELEMENT_MISMATCH", "XML_ERROR_PARSING_ELEMENT", "XML_ERROR_PARSING_ATTRIBUTE", - "UNUSED_XML_ERROR_IDENTIFYING_TAG", "XML_ERROR_PARSING_TEXT", "XML_ERROR_PARSING_CDATA", "XML_ERROR_PARSING_COMMENT", @@ -1986,7 +2101,8 @@ const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { "XML_ERROR_MISMATCHED_ELEMENT", "XML_ERROR_PARSING", "XML_CAN_NOT_CONVERT_TEXT", - "XML_NO_TEXT_NODE" + "XML_NO_TEXT_NODE", + "XML_ELEMENT_DEPTH_EXCEEDED" }; @@ -2000,6 +2116,7 @@ XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : _errorLineNum( 0 ), _charBuffer( 0 ), _parseCurLineNum( 0 ), + _parsingDepth(0), _unlinked(), _elementPool(), _attributePool(), @@ -2017,7 +2134,7 @@ XMLDocument::~XMLDocument() } -void XMLDocument::MarkInUse(XMLNode* node) +void XMLDocument::MarkInUse(const XMLNode* const node) { TIXMLASSERT(node); TIXMLASSERT(node->_parent == 0); @@ -2044,6 +2161,7 @@ void XMLDocument::Clear() delete [] _charBuffer; _charBuffer = 0; + _parsingDepth = 0; #if 0 _textPool.Trace( "text" ); @@ -2121,7 +2239,7 @@ static FILE* callfopen( const char* filepath, const char* mode ) TIXMLASSERT( mode ); #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) FILE* fp = 0; - errno_t err = fopen_s( &fp, filepath, mode ); + const errno_t err = fopen_s( &fp, filepath, mode ); if ( err ) { return 0; } @@ -2179,7 +2297,7 @@ template struct LongFitsIntoSizeTMinusOne { static bool Fits( unsigned long value ) { - return value < (size_t)-1; + return value < static_cast(-1); } }; @@ -2224,7 +2342,7 @@ XMLError XMLDocument::LoadFile( FILE* fp ) const size_t size = filelength; TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[size+1]; - size_t read = fread( _charBuffer, 1, size, fp ); + const size_t read = fread( _charBuffer, 1, size, fp ); if ( read != size ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; @@ -2275,7 +2393,7 @@ XMLError XMLDocument::Parse( const char* p, size_t len ) SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } - if ( len == (size_t)(-1) ) { + if ( len == static_cast(-1) ) { len = strlen( p ); } TIXMLASSERT( _charBuffer == 0 ); @@ -2317,9 +2435,10 @@ void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... _errorLineNum = lineNum; _errorStr.Reset(); - size_t BUFFER_SIZE = 1000; + const size_t BUFFER_SIZE = 1000; char* buffer = new char[BUFFER_SIZE]; + TIXMLASSERT(sizeof(error) <= sizeof(int)); TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); if (format) { @@ -2377,6 +2496,20 @@ void XMLDocument::Parse() ParseDeep(p, 0, &_parseCurLineNum ); } +void XMLDocument::PushDepth() +{ + _parsingDepth++; + if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) { + SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." ); + } +} + +void XMLDocument::PopDepth() +{ + TIXMLASSERT(_parsingDepth > 0); + --_parsingDepth; +} + XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : _elementJustOpened( false ), _stack(), @@ -2394,13 +2527,13 @@ XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : } for( int i=0; i(entityValue); TIXMLASSERT( flagIndex < ENTITY_RANGE ); _entityFlag[flagIndex] = true; } - _restrictedEntityFlag[(unsigned char)'&'] = true; - _restrictedEntityFlag[(unsigned char)'<'] = true; - _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice + _restrictedEntityFlag[static_cast('&')] = true; + _restrictedEntityFlag[static_cast('<')] = true; + _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice _buffer.Push( 0 ); } @@ -2475,10 +2608,10 @@ void XMLPrinter::PrintString( const char* p, bool restricted ) // Check for entities. If one is found, flush // the stream up until the entity, write the // entity, and keep looking. - if ( flag[(unsigned char)(*q)] ) { + if ( flag[static_cast(*q)] ) { while ( p < q ) { const size_t delta = q - p; - const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast(delta); Write( p, toPrint ); p += toPrint; } @@ -2502,14 +2635,16 @@ void XMLPrinter::PrintString( const char* p, bool restricted ) ++q; TIXMLASSERT( p <= q ); } + // Flush the remaining string. This will be the entire + // string if an entity wasn't found. + if ( p < q ) { + const size_t delta = q - p; + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast(delta); + Write( p, toPrint ); + } } - // Flush the remaining string. This will be the entire - // string if an entity wasn't found. - TIXMLASSERT( p <= q ); - if ( !_processEntities || ( p < q ) ) { - const size_t delta = q - p; - const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; - Write( p, toPrint ); + else { + Write( p ); } } @@ -2533,8 +2668,6 @@ void XMLPrinter::OpenElement( const char* name, bool compactMode ) if ( _textDepth < 0 && !_firstElement && !compactMode ) { Putc( '\n' ); - } - if ( !compactMode ) { PrintSpace( _depth ); } @@ -2582,6 +2715,14 @@ void XMLPrinter::PushAttribute(const char* name, int64_t v) } +void XMLPrinter::PushAttribute(const char* name, uint64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + void XMLPrinter::PushAttribute( const char* name, bool v ) { char buf[BUF_SIZE]; @@ -2651,6 +2792,7 @@ void XMLPrinter::PushText( const char* text, bool cdata ) } } + void XMLPrinter::PushText( int64_t value ) { char buf[BUF_SIZE]; @@ -2658,6 +2800,15 @@ void XMLPrinter::PushText( int64_t value ) PushText( buf, false ); } + +void XMLPrinter::PushText( uint64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(value, buf, BUF_SIZE); + PushText(buf, false); +} + + void XMLPrinter::PushText( int value ) { char buf[BUF_SIZE]; diff --git a/externals/tinyxml/tinyxml2.h b/externals/tinyxml/tinyxml2.h index a22f791d64b..1beadaa543e 100644 --- a/externals/tinyxml/tinyxml2.h +++ b/externals/tinyxml/tinyxml2.h @@ -98,14 +98,21 @@ distribution. /* Versioning, past 1.0.14: http://semver.org/ */ -static const int TIXML2_MAJOR_VERSION = 6; -static const int TIXML2_MINOR_VERSION = 1; +static const int TIXML2_MAJOR_VERSION = 8; +static const int TIXML2_MINOR_VERSION = 0; static const int TIXML2_PATCH_VERSION = 0; -#define TINYXML2_MAJOR_VERSION 6 -#define TINYXML2_MINOR_VERSION 1 +#define TINYXML2_MAJOR_VERSION 8 +#define TINYXML2_MINOR_VERSION 0 #define TINYXML2_PATCH_VERSION 0 +// A fixed element depth limit is problematic. There needs to be a +// limit to avoid a stack overflow. However, that limit varies per +// system, and the capacity of the stack. On the other hand, it's a trivial +// attack that can result from ill, malicious, or even correctly formed XML, +// so there needs to be a limit in place. +static const int TINYXML2_MAX_ELEMENT_DEPTH = 100; + namespace tinyxml2 { class XMLDocument; @@ -122,8 +129,10 @@ class XMLPrinter; pointers into the XML file itself, and will apply normalization and entity translation if actually read. Can also store (and memory manage) a traditional char[] + + Isn't clear why TINYXML2_LIB is needed; but seems to fix #719 */ -class StrPair +class TINYXML2_LIB StrPair { public: enum { @@ -183,7 +192,7 @@ class StrPair char* _end; StrPair( const StrPair& other ); // not supported - void operator=( StrPair& other ); // not supported, use TransferTo() + void operator=( const StrPair& other ); // not supported, use TransferTo() }; @@ -281,7 +290,7 @@ class DynArray return _mem; } - T* Mem() { + T* Mem() { TIXMLASSERT( _mem ); return _mem; } @@ -294,7 +303,7 @@ class DynArray TIXMLASSERT( cap > 0 ); if ( cap > _allocated ) { TIXMLASSERT( cap <= INT_MAX / 2 ); - int newAllocated = cap * 2; + const int newAllocated = cap * 2; T* newMem = new T[newAllocated]; TIXMLASSERT( newAllocated >= _size ); memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs @@ -327,7 +336,6 @@ class MemPool virtual void* Alloc() = 0; virtual void Free( void* ) = 0; virtual void SetTracked() = 0; - virtual void Clear() = 0; }; @@ -340,9 +348,9 @@ class MemPoolT : public MemPool public: MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} ~MemPoolT() { - Clear(); + MemPoolT< ITEM_SIZE >::Clear(); } - + void Clear() { // Delete the blocks. while( !_blockPtrs.Empty()) { @@ -388,7 +396,7 @@ class MemPoolT : public MemPool ++_nUntracked; return result; } - + virtual void Free( void* mem ) { if ( !mem ) { return; @@ -518,10 +526,8 @@ enum XMLError { XML_ERROR_FILE_NOT_FOUND, XML_ERROR_FILE_COULD_NOT_BE_OPENED, XML_ERROR_FILE_READ_ERROR, - UNUSED_XML_ERROR_ELEMENT_MISMATCH, // remove at next major version XML_ERROR_PARSING_ELEMENT, XML_ERROR_PARSING_ATTRIBUTE, - UNUSED_XML_ERROR_IDENTIFYING_TAG, // remove at next major version XML_ERROR_PARSING_TEXT, XML_ERROR_PARSING_CDATA, XML_ERROR_PARSING_COMMENT, @@ -532,6 +538,7 @@ enum XMLError { XML_ERROR_PARSING, XML_CAN_NOT_CONVERT_TEXT, XML_NO_TEXT_NODE, + XML_ELEMENT_DEPTH_EXCEEDED, XML_ERROR_COUNT }; @@ -555,7 +562,7 @@ class TINYXML2_LIB XMLUtil TIXMLASSERT( p ); return p; } - static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) { + static char* SkipWhiteSpace( char* const p, int* curLineNumPtr ) { return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); } @@ -564,7 +571,7 @@ class TINYXML2_LIB XMLUtil static bool IsWhiteSpace( char p ) { return !IsUTF8Continuation(p) && isspace( static_cast(p) ); } - + inline static bool IsNameStartChar( unsigned char ch ) { if ( ch >= 128 ) { // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() @@ -575,7 +582,7 @@ class TINYXML2_LIB XMLUtil } return ch == ':' || ch == '_'; } - + inline static bool IsNameChar( unsigned char ch ) { return IsNameStartChar( ch ) || isdigit( ch ) @@ -592,8 +599,8 @@ class TINYXML2_LIB XMLUtil TIXMLASSERT( nChar >= 0 ); return strncmp( p, q, nChar ) == 0; } - - inline static bool IsUTF8Continuation( char p ) { + + inline static bool IsUTF8Continuation( const char p ) { return ( p & 0x80 ) != 0; } @@ -610,6 +617,7 @@ class TINYXML2_LIB XMLUtil static void ToStr( float v, char* buffer, int bufferSize ); static void ToStr( double v, char* buffer, int bufferSize ); static void ToStr(int64_t v, char* buffer, int bufferSize); + static void ToStr(uint64_t v, char* buffer, int bufferSize); // converts strings to primitive types static bool ToInt( const char* str, int* value ); @@ -618,7 +626,7 @@ class TINYXML2_LIB XMLUtil static bool ToFloat( const char* str, float* value ); static bool ToDouble( const char* str, double* value ); static bool ToInt64(const char* str, int64_t* value); - + static bool ToUnsigned64(const char* str, uint64_t* value); // Changes what is serialized for a boolean value. // Default to "true" and "false". Shouldn't be changed // unless you have a special testing or compatibility need. @@ -874,11 +882,11 @@ class TINYXML2_LIB XMLNode Make a copy of this node and all its children. If the 'target' is null, then the nodes will - be allocated in the current document. If 'target' - is specified, the memory will be allocated is the + be allocated in the current document. If 'target' + is specified, the memory will be allocated is the specified XMLDocument. - NOTE: This is probably not the correct tool to + NOTE: This is probably not the correct tool to copy a document, since XMLDocuments can have multiple top level XMLNodes. You probably want to use XMLDocument::DeepCopy() @@ -917,8 +925,8 @@ class TINYXML2_LIB XMLNode */ virtual bool Accept( XMLVisitor* visitor ) const = 0; - /** - Set user data into the XMLNode. TinyXML-2 in + /** + Set user data into the XMLNode. TinyXML-2 in no way processes or interprets user data. It is initially 0. */ @@ -932,7 +940,7 @@ class TINYXML2_LIB XMLNode void* GetUserData() const { return _userData; } protected: - XMLNode( XMLDocument* ); + explicit XMLNode( XMLDocument* ); virtual ~XMLNode(); virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); @@ -1000,7 +1008,7 @@ class TINYXML2_LIB XMLText : public XMLNode virtual bool ShallowEqual( const XMLNode* compare ) const; protected: - XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); @@ -1031,7 +1039,7 @@ class TINYXML2_LIB XMLComment : public XMLNode virtual bool ShallowEqual( const XMLNode* compare ) const; protected: - XMLComment( XMLDocument* doc ); + explicit XMLComment( XMLDocument* doc ); virtual ~XMLComment(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); @@ -1070,7 +1078,7 @@ class TINYXML2_LIB XMLDeclaration : public XMLNode virtual bool ShallowEqual( const XMLNode* compare ) const; protected: - XMLDeclaration( XMLDocument* doc ); + explicit XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); @@ -1105,7 +1113,7 @@ class TINYXML2_LIB XMLUnknown : public XMLNode virtual bool ShallowEqual( const XMLNode* compare ) const; protected: - XMLUnknown( XMLDocument* doc ); + explicit XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); @@ -1157,6 +1165,12 @@ class TINYXML2_LIB XMLAttribute return i; } + uint64_t Unsigned64Value() const { + uint64_t i = 0; + QueryUnsigned64Value(&i); + return i; + } + /// Query as an unsigned integer. See IntValue() unsigned UnsignedValue() const { unsigned i=0; @@ -1191,6 +1205,8 @@ class TINYXML2_LIB XMLAttribute XMLError QueryUnsignedValue( unsigned int* value ) const; /// See QueryIntValue XMLError QueryInt64Value(int64_t* value) const; + /// See QueryIntValue + XMLError QueryUnsigned64Value(uint64_t* value) const; /// See QueryIntValue XMLError QueryBoolValue( bool* value ) const; /// See QueryIntValue @@ -1206,7 +1222,9 @@ class TINYXML2_LIB XMLAttribute void SetAttribute( unsigned value ); /// Set the attribute to value. void SetAttribute(int64_t value); - /// Set the attribute to value. + /// Set the attribute to value. + void SetAttribute(uint64_t value); + /// Set the attribute to value. void SetAttribute( bool value ); /// Set the attribute to value. void SetAttribute( double value ); @@ -1294,6 +1312,8 @@ class TINYXML2_LIB XMLElement : public XMLNode unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; /// See IntAttribute() int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; + /// See IntAttribute() + uint64_t Unsigned64Attribute(const char* name, uint64_t defaultValue = 0) const; /// See IntAttribute() bool BoolAttribute(const char* name, bool defaultValue = false) const; /// See IntAttribute() @@ -1340,6 +1360,15 @@ class TINYXML2_LIB XMLElement : public XMLNode return a->QueryInt64Value(value); } + /// See QueryIntAttribute() + XMLError QueryUnsigned64Attribute(const char* name, uint64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if(!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsigned64Value(value); + } + /// See QueryIntAttribute() XMLError QueryBoolAttribute( const char* name, bool* value ) const { const XMLAttribute* a = FindAttribute( name ); @@ -1376,14 +1405,14 @@ class TINYXML2_LIB XMLElement : public XMLNode } - + /** Given an attribute name, QueryAttribute() returns XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion can't be performed, or XML_NO_ATTRIBUTE if the attribute doesn't exist. It is overloaded for the primitive types, and is a generally more convenient replacement of QueryIntAttribute() and related functions. - + If successful, the result of the conversion will be written to 'value'. If not successful, nothing will be written to 'value'. This allows you to provide default @@ -1394,27 +1423,31 @@ class TINYXML2_LIB XMLElement : public XMLNode QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 @endverbatim */ - int QueryAttribute( const char* name, int* value ) const { + XMLError QueryAttribute( const char* name, int* value ) const { return QueryIntAttribute( name, value ); } - int QueryAttribute( const char* name, unsigned int* value ) const { + XMLError QueryAttribute( const char* name, unsigned int* value ) const { return QueryUnsignedAttribute( name, value ); } - int QueryAttribute(const char* name, int64_t* value) const { + XMLError QueryAttribute(const char* name, int64_t* value) const { return QueryInt64Attribute(name, value); } - int QueryAttribute( const char* name, bool* value ) const { + XMLError QueryAttribute(const char* name, uint64_t* value) const { + return QueryUnsigned64Attribute(name, value); + } + + XMLError QueryAttribute( const char* name, bool* value ) const { return QueryBoolAttribute( name, value ); } - int QueryAttribute( const char* name, double* value ) const { + XMLError QueryAttribute( const char* name, double* value ) const { return QueryDoubleAttribute( name, value ); } - int QueryAttribute( const char* name, float* value ) const { + XMLError QueryAttribute( const char* name, float* value ) const { return QueryFloatAttribute( name, value ); } @@ -1440,7 +1473,13 @@ class TINYXML2_LIB XMLElement : public XMLNode a->SetAttribute(value); } - /// Sets the named attribute to value. + /// Sets the named attribute to value. + void SetAttribute(const char* name, uint64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); @@ -1522,7 +1561,7 @@ class TINYXML2_LIB XMLElement : public XMLNode @verbatim Hullaballoo!This is text @endverbatim - + For this XML: @verbatim @@ -1536,15 +1575,17 @@ class TINYXML2_LIB XMLElement : public XMLNode /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( int value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( unsigned value ); + void SetText( unsigned value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText(int64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(uint64_t value); /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( bool value ); + void SetText( bool value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( double value ); + void SetText( double value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( float value ); + void SetText( float value ); /** Convenience method to query the value of a child text node. This is probably best @@ -1578,6 +1619,8 @@ class TINYXML2_LIB XMLElement : public XMLNode /// See QueryIntText() XMLError QueryInt64Text(int64_t* uval) const; /// See QueryIntText() + XMLError QueryUnsigned64Text(uint64_t* uval) const; + /// See QueryIntText() XMLError QueryBoolText( bool* bval ) const; /// See QueryIntText() XMLError QueryDoubleText( double* dval ) const; @@ -1590,12 +1633,29 @@ class TINYXML2_LIB XMLElement : public XMLNode unsigned UnsignedText(unsigned defaultValue = 0) const; /// See QueryIntText() int64_t Int64Text(int64_t defaultValue = 0) const; + /// See QueryIntText() + uint64_t Unsigned64Text(uint64_t defaultValue = 0) const; /// See QueryIntText() bool BoolText(bool defaultValue = false) const; /// See QueryIntText() double DoubleText(double defaultValue = 0) const; /// See QueryIntText() - float FloatText(float defaultValue = 0) const; + float FloatText(float defaultValue = 0) const; + + /** + Convenience method to create a new XMLElement and add it as last (right) + child of this node. Returns the created and inserted element. + */ + XMLElement* InsertNewChildElement(const char* name); + /// See InsertNewChildElement() + XMLComment* InsertNewComment(const char* comment); + /// See InsertNewChildElement() + XMLText* InsertNewText(const char* text); + /// See InsertNewChildElement() + XMLDeclaration* InsertNewDeclaration(const char* text); + /// See InsertNewChildElement() + XMLUnknown* InsertNewUnknown(const char* text); + // internal: enum ElementClosingType { @@ -1618,11 +1678,7 @@ class TINYXML2_LIB XMLElement : public XMLNode XMLElement( const XMLElement& ); // not supported void operator=( const XMLElement& ); // not supported - XMLAttribute* FindAttribute( const char* name ) { - return const_cast(const_cast(this)->FindAttribute( name )); - } XMLAttribute* FindOrCreateAttribute( const char* name ); - //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p, int* curLineNumPtr ); static void DeleteAttribute( XMLAttribute* attribute ); XMLAttribute* CreateAttribute(); @@ -1650,9 +1706,9 @@ enum Whitespace { class TINYXML2_LIB XMLDocument : public XMLNode { friend class XMLElement; - // Gives access to SetError, but over-access for everything else. + // Gives access to SetError and Push/PopDepth, but over-access for everything else. // Wishing C++ had "internal" scope. - friend class XMLNode; + friend class XMLNode; friend class XMLText; friend class XMLComment; friend class XMLDeclaration; @@ -1681,7 +1737,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode specified, TinyXML-2 will assume 'xml' points to a null terminated string. */ - XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); + XMLError Parse( const char* xml, size_t nBytes=static_cast(-1) ); /** Load an XML file from disk. @@ -1692,8 +1748,8 @@ class TINYXML2_LIB XMLDocument : public XMLNode /** Load an XML file from disk. You are responsible - for providing and closing the FILE*. - + for providing and closing the FILE*. + NOTE: The file should be opened as binary ("rb") not text in order for TinyXML-2 to correctly do newline normalization. @@ -1823,7 +1879,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode const char* ErrorName() const; static const char* ErrorIDToName(XMLError errorID); - /** Returns a "long form" error description. A hopefully helpful + /** Returns a "long form" error description. A hopefully helpful diagnostic with location, line number, and/or additional info. */ const char* ErrorStr() const; @@ -1836,7 +1892,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode { return _errorLineNum; } - + /// Clear the document, resetting it to the initial state. void Clear(); @@ -1853,7 +1909,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode char* Identify( char* p, XMLNode** node ); // internal - void MarkInUse(XMLNode*); + void MarkInUse(const XMLNode* const); virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { return 0; @@ -1874,6 +1930,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode int _errorLineNum; char* _charBuffer; int _parseCurLineNum; + int _parsingDepth; // Memory tracking does add some overhead. // However, the code assumes that you don't // have a bunch of unlinked nodes around. @@ -1893,6 +1950,24 @@ class TINYXML2_LIB XMLDocument : public XMLNode void SetError( XMLError error, int lineNum, const char* format, ... ); + // Something of an obvious security hole, once it was discovered. + // Either an ill-formed XML or an excessively deep one can overflow + // the stack. Track stack depth, and error out if needed. + class DepthTracker { + public: + explicit DepthTracker(XMLDocument * document) { + this->_document = document; + document->PushDepth(); + } + ~DepthTracker() { + _document->PopDepth(); + } + private: + XMLDocument * _document; + }; + void PushDepth(); + void PopDepth(); + template NodeType* CreateUnlinkedNode( MemPoolT& pool ); }; @@ -1969,10 +2044,10 @@ class TINYXML2_LIB XMLHandle { public: /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - XMLHandle( XMLNode* node ) : _node( node ) { + explicit XMLHandle( XMLNode* node ) : _node( node ) { } /// Create a handle from a node. - XMLHandle( XMLNode& node ) : _node( &node ) { + explicit XMLHandle( XMLNode& node ) : _node( &node ) { } /// Copy constructor XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { @@ -2049,9 +2124,9 @@ class TINYXML2_LIB XMLHandle class TINYXML2_LIB XMLConstHandle { public: - XMLConstHandle( const XMLNode* node ) : _node( node ) { + explicit XMLConstHandle( const XMLNode* node ) : _node( node ) { } - XMLConstHandle( const XMLNode& node ) : _node( &node ) { + explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) { } XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { } @@ -2172,7 +2247,8 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor void PushAttribute( const char* name, const char* value ); void PushAttribute( const char* name, int value ); void PushAttribute( const char* name, unsigned value ); - void PushAttribute(const char* name, int64_t value); + void PushAttribute( const char* name, int64_t value ); + void PushAttribute( const char* name, uint64_t value ); void PushAttribute( const char* name, bool value ); void PushAttribute( const char* name, double value ); /// If streaming, close the Element. @@ -2184,8 +2260,10 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor void PushText( int value ); /// Add a text node from an unsigned. void PushText( unsigned value ); - /// Add a text node from an unsigned. - void PushText(int64_t value); + /// Add a text node from a signed 64bit integer. + void PushText( int64_t value ); + /// Add a text node from an unsigned 64bit integer. + void PushText( uint64_t value ); /// Add a text node from a bool. void PushText( bool value ); /// Add a text node from a float. @@ -2231,10 +2309,10 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor If in print to memory mode, reset the buffer to the beginning. */ - void ClearBuffer() { + void ClearBuffer( bool resetToFirstElement = true ) { _buffer.Clear(); _buffer.Push(0); - _firstElement = true; + _firstElement = resetToFirstElement; } protected: