diff options
Diffstat (limited to 'Swiften/Parser')
-rw-r--r-- | Swiften/Parser/Attribute.h | 8 | ||||
-rw-r--r-- | Swiften/Parser/AttributeMap.cpp | 4 | ||||
-rw-r--r-- | Swiften/Parser/AttributeMap.h | 1 | ||||
-rw-r--r-- | Swiften/Parser/ExpatParser.cpp | 86 | ||||
-rw-r--r-- | Swiften/Parser/ExpatParser.h | 6 | ||||
-rw-r--r-- | Swiften/Parser/IQParser.cpp | 4 | ||||
-rw-r--r-- | Swiften/Parser/LibXMLParser.cpp | 82 | ||||
-rw-r--r-- | Swiften/Parser/LibXMLParser.h | 9 | ||||
-rw-r--r-- | Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h | 2 | ||||
-rw-r--r-- | Swiften/Parser/PlatformXMLParserFactory.cpp | 6 | ||||
-rw-r--r-- | Swiften/Parser/PlatformXMLParserFactory.h | 2 | ||||
-rw-r--r-- | Swiften/Parser/PresenceParser.cpp | 4 | ||||
-rw-r--r-- | Swiften/Parser/StreamErrorParser.cpp | 6 | ||||
-rw-r--r-- | Swiften/Parser/UnitTest/AttributeMapTest.cpp | 17 | ||||
-rw-r--r-- | Swiften/Parser/UnitTest/XMLParserTest.cpp | 156 | ||||
-rw-r--r-- | Swiften/Parser/XMLParser.cpp | 2 | ||||
-rw-r--r-- | Swiften/Parser/XMLParser.h | 11 | ||||
-rw-r--r-- | Swiften/Parser/XMLParserClient.cpp | 11 | ||||
-rw-r--r-- | Swiften/Parser/XMLParserClient.h | 18 | ||||
-rw-r--r-- | Swiften/Parser/XMLParserFactory.h | 2 |
20 files changed, 363 insertions, 74 deletions
diff --git a/Swiften/Parser/Attribute.h b/Swiften/Parser/Attribute.h index f54317e..07e63b4 100644 --- a/Swiften/Parser/Attribute.h +++ b/Swiften/Parser/Attribute.h @@ -14,6 +14,9 @@ namespace Swift { Attribute(const std::string& name, const std::string& ns) : name(name), ns(ns) { } + Attribute(const std::string& name, const std::string& ns, const std::string& prefix) : name(name), ns(ns), prefix(prefix) { + } + const std::string& getName() const { return name; } @@ -22,6 +25,10 @@ namespace Swift { return ns; } + const std::string& getPrefix() const { + return prefix; + } + bool operator==(const Attribute& o) const { return o.name == name && o.ns == ns; } @@ -29,5 +36,6 @@ namespace Swift { private: std::string name; std::string ns; + std::string prefix; }; } diff --git a/Swiften/Parser/AttributeMap.cpp b/Swiften/Parser/AttributeMap.cpp index f6767de..7814a64 100644 --- a/Swiften/Parser/AttributeMap.cpp +++ b/Swiften/Parser/AttributeMap.cpp @@ -54,3 +54,7 @@ boost::optional<std::string> AttributeMap::getAttributeValue(const std::string& void AttributeMap::addAttribute(const std::string& name, const std::string& ns, const std::string& value) { attributes.push_back(Entry(Attribute(name, ns), value)); } + +void AttributeMap::addAttribute(const std::string& name, const std::string& ns, const std::string& prefix, const std::string& value) { + attributes.push_back(Entry(Attribute(name, ns, prefix), value)); +} diff --git a/Swiften/Parser/AttributeMap.h b/Swiften/Parser/AttributeMap.h index 804d6aa..26d5826 100644 --- a/Swiften/Parser/AttributeMap.h +++ b/Swiften/Parser/AttributeMap.h @@ -43,6 +43,7 @@ namespace Swift { boost::optional<std::string> getAttributeValue(const std::string&) const; void addAttribute(const std::string& name, const std::string& ns, const std::string& value); + void addAttribute(const std::string& name, const std::string& ns, const std::string& prefix, const std::string& value); const std::vector<Entry>& getEntries() const { return attributes; diff --git a/Swiften/Parser/ExpatParser.cpp b/Swiften/Parser/ExpatParser.cpp index 8415c42..32d4f53 100644 --- a/Swiften/Parser/ExpatParser.cpp +++ b/Swiften/Parser/ExpatParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -11,6 +11,8 @@ #include <memory> #include <string> +#include <boost/algorithm/string.hpp> + #include <expat.h> #include <Swiften/Base/String.h> @@ -18,6 +20,33 @@ #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +namespace { +struct XmlInfo { + std::string prefix; + std::string uri; + std::string name; +}; + +XmlInfo splitExpatInfo(const std::string& s, char sep) { + // name + // uri|name + // uri|name|prefix + std::vector<std::string> v; + boost::split(v, s, [sep](char c) {return c == sep; }); + switch (v.size()) { + case 1: + return{ "", "", std::move(v[0]) }; + case 2: + return{ "", std::move(v[0]), std::move(v[1]) }; + case 3: + return{ std::move(v[2]), std::move(v[0]), std::move(v[1]) }; + default: + return{ "", "", "" }; + } +} +} + + namespace Swift { static const char NAMESPACE_SEPARATOR = '\x01'; @@ -27,33 +56,24 @@ struct ExpatParser::Private { }; static void handleStartElement(void* parser, const XML_Char* name, const XML_Char** attributes) { - std::pair<std::string,std::string> nsTagPair = String::getSplittedAtFirst(name, NAMESPACE_SEPARATOR); - if (nsTagPair.second == "") { - nsTagPair.second = nsTagPair.first; - nsTagPair.first = ""; - } + auto elemInfo = splitExpatInfo(name, NAMESPACE_SEPARATOR); + AttributeMap attributeValues; const XML_Char** currentAttribute = attributes; while (*currentAttribute) { - std::pair<std::string,std::string> nsAttributePair = String::getSplittedAtFirst(*currentAttribute, NAMESPACE_SEPARATOR); - if (nsAttributePair.second == "") { - nsAttributePair.second = nsAttributePair.first; - nsAttributePair.first = ""; - } - attributeValues.addAttribute(nsAttributePair.second, nsAttributePair.first, std::string(*(currentAttribute+1))); + auto attribInfo = splitExpatInfo(*currentAttribute, NAMESPACE_SEPARATOR); + attributeValues.addAttribute(attribInfo.name, attribInfo.uri, attribInfo.prefix, std::string(*(currentAttribute+1))); currentAttribute += 2; } - static_cast<XMLParser*>(parser)->getClient()->handleStartElement(nsTagPair.second, nsTagPair.first, attributeValues); + auto* client = static_cast<XMLParser*>(parser)->getClient(); + client->handleStartElementPrefix(elemInfo.prefix, elemInfo.uri, elemInfo.name, attributeValues); + client->handleStartElement(elemInfo.name, elemInfo.uri, attributeValues); } static void handleEndElement(void* parser, const XML_Char* name) { - std::pair<std::string,std::string> nsTagPair = String::getSplittedAtFirst(name, NAMESPACE_SEPARATOR); - if (nsTagPair.second == "") { - nsTagPair.second = nsTagPair.first; - nsTagPair.first = ""; - } - static_cast<XMLParser*>(parser)->getClient()->handleEndElement(nsTagPair.second, nsTagPair.first); + auto elemInfo = splitExpatInfo(name, NAMESPACE_SEPARATOR); + static_cast<XMLParser*>(parser)->getClient()->handleEndElement(elemInfo.name, elemInfo.uri); } static void handleCharacterData(void* parser, const XML_Char* data, int len) { @@ -64,29 +84,51 @@ static void handleCharacterData(void* parser, const XML_Char* data, int len) { static void handleXMLDeclaration(void*, const XML_Char*, const XML_Char*, int) { } +static void handleNamespaceDeclaration(void* parser, const XML_Char* prefix, const XML_Char* uri) { + static_cast<XMLParser*>(parser)->getClient()->handleNamespaceDeclaration(std::string(prefix ? prefix : ""), std::string(uri ? uri : "")); +} + static void handleEntityDeclaration(void* parser, const XML_Char*, int, const XML_Char*, int, const XML_Char*, const XML_Char*, const XML_Char*, const XML_Char*) { static_cast<ExpatParser*>(parser)->stopParser(); } +static void handleComment(void* parser, const XML_Char* /*data*/) { + if (!static_cast<ExpatParser*>(parser)->allowsComments()) { + static_cast<ExpatParser*>(parser)->stopParser(); + } +} + +static void handleProcessingInstruction(void* parser, const XML_Char* /*target*/, const XML_Char* /*data*/) { + static_cast<ExpatParser*>(parser)->stopParser(); +} + +static void handleDoctypeDeclaration(void* parser, const XML_Char* /*doctypeName*/, const XML_Char* /*sysid*/, const XML_Char* /*pubid*/, int /*has_internal_subset*/) { + static_cast<ExpatParser*>(parser)->stopParser(); +} -ExpatParser::ExpatParser(XMLParserClient* client) : XMLParser(client), p(new Private()) { +ExpatParser::ExpatParser(XMLParserClient* client, bool allowComments) : XMLParser(client, allowComments), p(new Private()) { p->parser_ = XML_ParserCreateNS("UTF-8", NAMESPACE_SEPARATOR); + XML_SetReturnNSTriplet(p->parser_, true); XML_SetUserData(p->parser_, this); XML_SetElementHandler(p->parser_, handleStartElement, handleEndElement); XML_SetCharacterDataHandler(p->parser_, handleCharacterData); XML_SetXmlDeclHandler(p->parser_, handleXMLDeclaration); XML_SetEntityDeclHandler(p->parser_, handleEntityDeclaration); + XML_SetNamespaceDeclHandler(p->parser_, handleNamespaceDeclaration, nullptr); + XML_SetCommentHandler(p->parser_, handleComment); + XML_SetProcessingInstructionHandler(p->parser_, handleProcessingInstruction); + XML_SetDoctypeDeclHandler(p->parser_, handleDoctypeDeclaration, nullptr); } ExpatParser::~ExpatParser() { XML_ParserFree(p->parser_); } -bool ExpatParser::parse(const std::string& data) { +bool ExpatParser::parse(const std::string& data, bool finalData) { if (data.size() > std::numeric_limits<int>::max()) { return false; } - bool success = XML_Parse(p->parser_, data.c_str(), static_cast<int>(data.size()), false) == XML_STATUS_OK; + bool success = XML_Parse(p->parser_, data.c_str(), static_cast<int>(data.size()), finalData) == XML_STATUS_OK; /*if (!success) { std::cout << "ERROR: " << XML_ErrorString(XML_GetErrorCode(p->parser_)) << " while parsing " << data << std::endl; }*/ diff --git a/Swiften/Parser/ExpatParser.h b/Swiften/Parser/ExpatParser.h index 12df463..34d790d 100644 --- a/Swiften/Parser/ExpatParser.h +++ b/Swiften/Parser/ExpatParser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -16,10 +16,10 @@ namespace Swift { class SWIFTEN_API ExpatParser : public XMLParser, public boost::noncopyable { public: - ExpatParser(XMLParserClient* client); + ExpatParser(XMLParserClient* client, bool allowComments = false); ~ExpatParser(); - bool parse(const std::string& data); + bool parse(const std::string& data, bool finalData = false); void stopParser(); diff --git a/Swiften/Parser/IQParser.cpp b/Swiften/Parser/IQParser.cpp index 5cfae34..363f7ec 100644 --- a/Swiften/Parser/IQParser.cpp +++ b/Swiften/Parser/IQParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -32,7 +32,7 @@ void IQParser::handleStanzaAttributes(const AttributeMap& attributes) { getStanzaGeneric()->setType(IQ::Error); } else { - SWIFT_LOG(warning) << "Unknown IQ type: " << *type << std::endl; + SWIFT_LOG(warning) << "Unknown IQ type: " << *type; getStanzaGeneric()->setType(IQ::Get); } } diff --git a/Swiften/Parser/LibXMLParser.cpp b/Swiften/Parser/LibXMLParser.cpp index 5bd3737..32b91a1 100644 --- a/Swiften/Parser/LibXMLParser.cpp +++ b/Swiften/Parser/LibXMLParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -17,6 +17,12 @@ #include <Swiften/Base/Log.h> #include <Swiften/Parser/XMLParserClient.h> +namespace { +std::string asString(const unsigned char* s) { + return s ? std::string(reinterpret_cast<const char*>(s)) : std::string(); +} +} + namespace Swift { struct LibXMLParser::Private { @@ -24,29 +30,39 @@ struct LibXMLParser::Private { xmlParserCtxtPtr context_; }; -static void handleStartElement(void* parser, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) { +static void handleStartElement(void* parser, const xmlChar* name, const xmlChar* prefix, const xmlChar* xmlns, int nbNamespaces, const xmlChar** namespaces, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) { AttributeMap attributeValues; if (nbDefaulted != 0) { // Just because i don't understand what this means yet :-) - SWIFT_LOG(error) << "Unexpected nbDefaulted on XML element" << std::endl; + SWIFT_LOG(error) << "Unexpected nbDefaulted on XML element"; } for (int i = 0; i < nbAttributes*5; i += 5) { - std::string attributeNS = ""; - if (attributes[i+2]) { - attributeNS = std::string(reinterpret_cast<const char*>(attributes[i+2])); - } + std::string attributeName = asString(attributes[i]); + std::string attributePrefix = asString(attributes[i+1]); + std::string attributeNS = asString(attributes[i+2]); assert(attributes[i+4] >= attributes[i+3]); attributeValues.addAttribute( - std::string(reinterpret_cast<const char*>(attributes[i])), + attributeName, attributeNS, + attributePrefix, std::string(reinterpret_cast<const char*>(attributes[i+3]), static_cast<size_t>(attributes[i+4]-attributes[i+3]))); } - static_cast<XMLParser*>(parser)->getClient()->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()), attributeValues); + auto* client = static_cast<XMLParser*>(parser)->getClient(); + for (auto i = 0; i < nbNamespaces * 2; i += 2) { + const auto prefix = asString(namespaces[i]); + const auto uri = asString(namespaces[i + 1]); + client->handleNamespaceDeclaration(prefix, uri); + } + auto nameStr = asString(name); + auto xmlsnsStr = asString(xmlns); + auto prefixStr = asString(prefix); + client->handleStartElementPrefix(prefixStr, xmlsnsStr, nameStr, attributeValues); + client->handleStartElement(nameStr, xmlsnsStr, attributeValues); } static void handleEndElement(void *parser, const xmlChar* name, const xmlChar*, const xmlChar* xmlns) { - static_cast<XMLParser*>(parser)->getClient()->handleEndElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string())); + static_cast<XMLParser*>(parser)->getClient()->handleEndElement(asString(name), asString(xmlns)); } static void handleCharacterData(void* parser, const xmlChar* data, int len) { @@ -54,6 +70,24 @@ static void handleCharacterData(void* parser, const xmlChar* data, int len) { static_cast<XMLParser*>(parser)->getClient()->handleCharacterData(std::string(reinterpret_cast<const char*>(data), static_cast<size_t>(len))); } +static void handleComment(void* parser, const xmlChar* /*data*/) { + if (!static_cast<LibXMLParser*>(parser)->allowsComments()) { + static_cast<LibXMLParser*>(parser)->stopParser(); + } +} + +static void handleEntityDeclaration(void * parser, const xmlChar* /*name*/, int /*type*/, const xmlChar* /*publicId*/, const xmlChar* /*systemId*/, xmlChar* /*content*/) { + static_cast<LibXMLParser*>(parser)->stopParser(); +} + +static void handleProcessingInstruction(void* parser, const xmlChar* /*target*/, const xmlChar* /*data*/) { + static_cast<LibXMLParser*>(parser)->stopParser(); +} + +static void handleExternalSubset(void* parser, const xmlChar * /*name*/, const xmlChar * /*ExternalID*/, const xmlChar * /*SystemID*/) { + static_cast<LibXMLParser*>(parser)->stopParser(); +} + static void handleError(void*, const char* /*m*/, ... ) { /* va_list args; @@ -66,12 +100,20 @@ static void handleError(void*, const char* /*m*/, ... ) { static void handleWarning(void*, const char*, ... ) { } +static void handleGenericError(void*, const char*, ... ) { +} + +static void handleStructuredError(void*, xmlErrorPtr) { +} + bool LibXMLParser::initialized = false; -LibXMLParser::LibXMLParser(XMLParserClient* client) : XMLParser(client), p(new Private()) { +LibXMLParser::LibXMLParser(XMLParserClient* client, bool allowComments) : XMLParser(client, allowComments), p(new Private()) { // Initialize libXML for multithreaded applications if (!initialized) { xmlInitParser(); + xmlSetGenericErrorFunc(nullptr, handleGenericError); + xmlSetStructuredErrorFunc(nullptr, handleStructuredError); initialized = true; } @@ -82,6 +124,10 @@ LibXMLParser::LibXMLParser(XMLParserClient* client) : XMLParser(client), p(new P p->handler_.characters = &handleCharacterData; p->handler_.warning = &handleWarning; p->handler_.error = &handleError; + p->handler_.comment = &handleComment; + p->handler_.entityDecl = &handleEntityDeclaration; + p->handler_.processingInstruction = &handleProcessingInstruction; + p->handler_.externalSubset = &handleExternalSubset; p->context_ = xmlCreatePushParserCtxt(&p->handler_, this, nullptr, 0, nullptr); xmlCtxtUseOptions(p->context_, XML_PARSE_NOENT); @@ -94,15 +140,16 @@ LibXMLParser::~LibXMLParser() { } } -bool LibXMLParser::parse(const std::string& data) { +bool LibXMLParser::parse(const std::string& data, bool finalData) { if (data.size() > std::numeric_limits<int>::max()) { return false; } - if (xmlParseChunk(p->context_, data.c_str(), static_cast<int>(data.size()), false) == XML_ERR_OK) { + auto error = xmlParseChunk(p->context_, data.c_str(), static_cast<int>(data.size()), finalData); + if (error == XML_ERR_OK) { return true; } - xmlError* error = xmlCtxtGetLastError(p->context_); - if (error->code == XML_WAR_NS_URI || error->code == XML_WAR_NS_URI_RELATIVE) { + if (stopped_) return false; + if (error == XML_WAR_NS_URI || error == XML_WAR_NS_URI_RELATIVE) { xmlCtxtResetLastError(p->context_); p->context_->errNo = XML_ERR_OK; return true; @@ -110,4 +157,9 @@ bool LibXMLParser::parse(const std::string& data) { return false; } +void LibXMLParser::stopParser() { + stopped_ = true; + xmlStopParser(p->context_); +} + } diff --git a/Swiften/Parser/LibXMLParser.h b/Swiften/Parser/LibXMLParser.h index 9f752ce..e21770d 100644 --- a/Swiften/Parser/LibXMLParser.h +++ b/Swiften/Parser/LibXMLParser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -19,13 +19,16 @@ namespace Swift { */ class LibXMLParser : public XMLParser, public boost::noncopyable { public: - LibXMLParser(XMLParserClient* client); + LibXMLParser(XMLParserClient* client, bool allowComments = false); virtual ~LibXMLParser(); - bool parse(const std::string& data); + bool parse(const std::string& data, bool finalData = false); + + void stopParser(); private: static bool initialized; + bool stopped_ = false; struct Private; const std::unique_ptr<Private> p; diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h b/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h index dcdbffa..8f9e0e1 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h +++ b/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h @@ -19,7 +19,7 @@ namespace Swift { class PayloadsParserTester : public XMLParserClient { public: PayloadsParserTester() : level(0) { - xmlParser = PlatformXMLParserFactory().createXMLParser(this); + xmlParser = PlatformXMLParserFactory().createXMLParser(this, false); } bool parse(const std::string& data) { diff --git a/Swiften/Parser/PlatformXMLParserFactory.cpp b/Swiften/Parser/PlatformXMLParserFactory.cpp index bf66734..a424aca 100644 --- a/Swiften/Parser/PlatformXMLParserFactory.cpp +++ b/Swiften/Parser/PlatformXMLParserFactory.cpp @@ -20,11 +20,11 @@ namespace Swift { PlatformXMLParserFactory::PlatformXMLParserFactory() { } -std::unique_ptr<XMLParser> PlatformXMLParserFactory::createXMLParser(XMLParserClient* client) { +std::unique_ptr<XMLParser> PlatformXMLParserFactory::createXMLParser(XMLParserClient* client, bool allowComments) { #ifdef HAVE_LIBXML - return std::make_unique<LibXMLParser>(client); + return std::make_unique<LibXMLParser>(client, allowComments); #else - return std::make_unique<ExpatParser>(client); + return std::make_unique<ExpatParser>(client, allowComments); #endif } diff --git a/Swiften/Parser/PlatformXMLParserFactory.h b/Swiften/Parser/PlatformXMLParserFactory.h index fa3ca19..d72a513 100644 --- a/Swiften/Parser/PlatformXMLParserFactory.h +++ b/Swiften/Parser/PlatformXMLParserFactory.h @@ -14,6 +14,6 @@ namespace Swift { public: PlatformXMLParserFactory(); - virtual std::unique_ptr<XMLParser> createXMLParser(XMLParserClient*); + virtual std::unique_ptr<XMLParser> createXMLParser(XMLParserClient*, bool allowComments = false); }; } diff --git a/Swiften/Parser/PresenceParser.cpp b/Swiften/Parser/PresenceParser.cpp index 0235a12..f73e9d8 100644 --- a/Swiften/Parser/PresenceParser.cpp +++ b/Swiften/Parser/PresenceParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -41,7 +41,7 @@ void PresenceParser::handleStanzaAttributes(const AttributeMap& attributes) { getStanzaGeneric()->setType(Presence::Error); } else { - SWIFT_LOG(error) << "Unknown Presence type: " << *type << std::endl; + SWIFT_LOG(error) << "Unknown Presence type: " << *type; getStanzaGeneric()->setType(Presence::Available); } } diff --git a/Swiften/Parser/StreamErrorParser.cpp b/Swiften/Parser/StreamErrorParser.cpp index 64e0681..e89af58 100644 --- a/Swiften/Parser/StreamErrorParser.cpp +++ b/Swiften/Parser/StreamErrorParser.cpp @@ -48,9 +48,6 @@ void StreamErrorParser::handleEndElement(const std::string& element, const std:: else if(element == "invalid-from") { getElementGeneric()->setType(StreamError::InvalidFrom); } - else if(element == "invalid-id") { - getElementGeneric()->setType(StreamError::InvalidID); - } else if(element == "invalid-namespace") { getElementGeneric()->setType(StreamError::InvalidNamespace); } @@ -90,6 +87,9 @@ void StreamErrorParser::handleEndElement(const std::string& element, const std:: else if(element == "unsupported-encoding") { getElementGeneric()->setType(StreamError::UnsupportedEncoding); } + else if(element == "unsupported-feature") { + getElementGeneric()->setType(StreamError::UnsupportedFeature); + } else if(element == "unsupported-stanza-type") { getElementGeneric()->setType(StreamError::UnsupportedStanzaType); } diff --git a/Swiften/Parser/UnitTest/AttributeMapTest.cpp b/Swiften/Parser/UnitTest/AttributeMapTest.cpp index 4529eac..d9335c1 100644 --- a/Swiften/Parser/UnitTest/AttributeMapTest.cpp +++ b/Swiften/Parser/UnitTest/AttributeMapTest.cpp @@ -15,6 +15,7 @@ class AttributeMapTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(AttributeMapTest); CPPUNIT_TEST(testGetAttribute_Namespaced); + CPPUNIT_TEST(testGetAttribute_Namespaced_Prefix); CPPUNIT_TEST(testGetBoolAttribute_True); CPPUNIT_TEST(testGetBoolAttribute_1); CPPUNIT_TEST(testGetBoolAttribute_False); @@ -34,6 +35,22 @@ class AttributeMapTest : public CppUnit::TestFixture CPPUNIT_ASSERT_EQUAL(std::string("en"), testling.getAttribute("lang", "http://www.w3.org/XML/1998/namespace")); } + void testGetAttribute_Namespaced_Prefix() { + AttributeMap testling; + testling.addAttribute("lang", "", "prefix", "nl"); + testling.addAttribute("lang", "http://www.w3.org/XML/1998/namespace", "prefix", "en"); + testling.addAttribute("lang", "", "prefix", "fr"); + + CPPUNIT_ASSERT_EQUAL(std::string("en"), testling.getAttribute("lang", "http://www.w3.org/XML/1998/namespace")); + const auto& entries = testling.getEntries(); + auto it = std::find_if(entries.begin(), entries.end(), [](const AttributeMap::Entry& e) { + return e.getValue() == "en"; + }); + const bool found = it != entries.end(); + CPPUNIT_ASSERT_EQUAL(true, found); + CPPUNIT_ASSERT_EQUAL(std::string("prefix"), it->getAttribute().getPrefix()); + } + void testGetBoolAttribute_True() { AttributeMap testling; testling.addAttribute("foo", "", "true"); diff --git a/Swiften/Parser/UnitTest/XMLParserTest.cpp b/Swiften/Parser/UnitTest/XMLParserTest.cpp index b593aa7..89229c9 100644 --- a/Swiften/Parser/UnitTest/XMLParserTest.cpp +++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp @@ -6,6 +6,7 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> +#include <unordered_map> #include <vector> #include <string> @@ -34,10 +35,17 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_TEST(testParse_WhitespaceInAttribute); CPPUNIT_TEST(testParse_AttributeWithoutNamespace); CPPUNIT_TEST(testParse_AttributeWithNamespace); + CPPUNIT_TEST(testParse_AttributeWithNamespaceNoPrefix); CPPUNIT_TEST(testParse_BillionLaughs); CPPUNIT_TEST(testParse_InternalEntity); //CPPUNIT_TEST(testParse_UndefinedPrefix); //CPPUNIT_TEST(testParse_UndefinedAttributePrefix); + CPPUNIT_TEST(testParse_AllowCommentsInXML); + CPPUNIT_TEST(testParse_DisallowCommentsInXML); + CPPUNIT_TEST(testParse_Doctype); + CPPUNIT_TEST(testParse_ProcessingInstructions); + CPPUNIT_TEST(testParse_ProcessingPrefixedElement); + CPPUNIT_TEST(testParse_InvalidlyEncodedInput); CPPUNIT_TEST_SUITE_END(); public: @@ -61,6 +69,9 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[1].data); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].ns); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[1].namespaces.size()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[1].namespaces.count("")); + CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].namespaces[""]); CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type); CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[2].data); @@ -85,10 +96,13 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[0].data); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[0].ns); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].namespaces.size()); + CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[0].namespaces[""]); CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type); CPPUNIT_ASSERT_EQUAL(std::string("name"), client_.events[1].data); CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].ns); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].namespaces.size()); CPPUNIT_ASSERT_EQUAL(Client::CharacterData, client_.events[2].type); CPPUNIT_ASSERT_EQUAL(std::string("Swift"), client_.events[2].data); @@ -161,6 +175,8 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type); CPPUNIT_ASSERT_EQUAL(std::string("x"), client_.events[0].data); CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[0].ns); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].namespaces.size()); + CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[0].namespaces["p"]); CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type); CPPUNIT_ASSERT_EQUAL(std::string("y"), client_.events[1].data); @@ -176,7 +192,7 @@ class XMLParserTest : public CppUnit::TestFixture { } void testParse_UnhandledXML() { - ParserType testling(&client_); + ParserType testling(&client_, true); CPPUNIT_ASSERT(testling.parse("<iq><!-- Testing --></iq>")); @@ -217,6 +233,15 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("iq"), client_.events[1].data); } + void testParse_CompleteDocument() { + ParserType testling(&client_); + + CPPUNIT_ASSERT(!testling.parse("<iq", true)); + CPPUNIT_ASSERT(!testling.parse("<iq>", true)); + CPPUNIT_ASSERT(!testling.parse("<iq><child>foo</child>", true)); + CPPUNIT_ASSERT(testling.parse("<iq><child>foo</child></iq>", true)); + } + void testParse_WhitespaceInAttribute() { ParserType testling(&client_); @@ -242,6 +267,7 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName()); CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace()); + CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getPrefix()); } void testParse_AttributeWithNamespace() { @@ -253,6 +279,25 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName()); CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace()); + CPPUNIT_ASSERT_EQUAL(std::string("f"), client_.events[0].attributes.getEntries()[0].getAttribute().getPrefix()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events[0].namespaces.size()); + CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im"), client_.events[0].namespaces[""]); + CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].namespaces["f"]); + } + + void testParse_AttributeWithNamespaceNoPrefix() { + ParserType testling(&client_); + + CPPUNIT_ASSERT(testling.parse( + "<query xmlns='http://swift.im' xmlns:f='http://swift.im/f' attr='3'/>")); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); + CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName()); + CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace()); + CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getPrefix()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events[0].namespaces.size()); + CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im"), client_.events[0].namespaces[""]); + CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].namespaces["f"]); } void testParse_BillionLaughs() { @@ -292,6 +337,7 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type); CPPUNIT_ASSERT_EQUAL(std::string("foo:bar"), client_.events[0].data); CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].ns); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].namespaces.size()); CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type); CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[1].data); @@ -309,48 +355,136 @@ class XMLParserTest : public CppUnit::TestFixture { void testParse_UndefinedAttributePrefix() { ParserType testling(&client_); - CPPUNIT_ASSERT(testling.parse( - "<foo bar:baz='bla'/>")); + CPPUNIT_ASSERT(testling.parse("<foo bar:baz='bla'/>")); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("bar:baz"), client_.events[0].attributes.getEntries()[0].getAttribute().getName()); } + void testParse_AllowCommentsInXML() { + ParserType testling(&client_, true); + + CPPUNIT_ASSERT(testling.parse("<message><!-- Some More Comments Testing --></message>")); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events.size()); + + CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type); + CPPUNIT_ASSERT_EQUAL(std::string("message"), client_.events[0].data); + + CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[1].type); + CPPUNIT_ASSERT_EQUAL(std::string("message"), client_.events[1].data); + } + + void testParse_DisallowCommentsInXML() { + ParserType testling(&client_); + + CPPUNIT_ASSERT(!testling.parse("<message><!-- Some More Comments Testing --></message>")); + } + + void testParse_Doctype() { + ParserType testling(&client_); + + CPPUNIT_ASSERT(!testling.parse("<!DOCTYPE greeting SYSTEM \"hello.dtd\">")); + } + + void testParse_ProcessingInstructions() { + ParserType testling(&client_); + + CPPUNIT_ASSERT(!testling.parse("<?xml-stylesheet type=\"text/xsl\" href=\"Sample.xsl\"?>")); + } + + void testParse_ProcessingPrefixedElement() { + client_.testingStartElementPrefix = true; + ParserType testling(&client_); + + CPPUNIT_ASSERT(testling.parse("<prefix:message xmlns='uri' xmlns:prefix='uriPrefix'/>")); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events.size()); + + CPPUNIT_ASSERT_EQUAL(Client::StartElementPrefix, client_.events[0].type); + CPPUNIT_ASSERT_EQUAL(std::string("message"), client_.events[0].data); + CPPUNIT_ASSERT_EQUAL(std::string("uriPrefix"), client_.events[0].ns); + CPPUNIT_ASSERT_EQUAL(std::string("prefix"), client_.events[0].prefix); + + CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[1].type); + CPPUNIT_ASSERT_EQUAL(std::string("message"), client_.events[1].data); + CPPUNIT_ASSERT_EQUAL(std::string("uriPrefix"), client_.events[1].ns); + } + + void testParse_InvalidlyEncodedInput() { + ParserType testling(&client_); + + // The following input was generated by a fuzzer, and triggered a crash in the LibXML2 parser because + // some types of error (buffer I/O errors, for instance) will not update the error in the parser context, + // and the code used to rely on that error always being set if parsing failed. + // This particular input will trick the parser into believing the encoding is UTF-16LE, which eventually will lead + // to two invalid encodings, followed by an I/O error. The latter will end parsing without updating the + // error in the parsing context, which used to trigger a crash. + testling.parse(std::string("<\0?\0\x80q type='get' id='aab9a'<<query xmlns='jabber:iq:roster'/>\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9br:i><quq:private'><storage xml s='s'\x00\x10</query></iq>", 271)); + testling.parse("<iq type='get'\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e to='ad5d8d2b25' ext='ca cs min@wonderland.t' id='aabda'><vCard xmlnr='vcard-temp'/>O/iq>"); + testling.parse("<\xff\xff\xff\x7fype:'get' to='won\x84" "erland.lit' id='aabea'><tuery xmlns='\xd8Vtp://jabber.org/p\x88ot\x8b" "col/disco#info'/>abber.org/protocol/disco#Nnfo'/></iq>"); + } + private: class Client : public XMLParserClient { public: - enum Type { StartElement, EndElement, CharacterData }; + using NamespaceMap = std::unordered_map<std::string /* prefix */, std::string /* uri */>; + enum Type { StartElement, StartElementPrefix, EndElement, CharacterData, NamespaceDefined }; struct Event { Event( Type type, const std::string& data, const std::string& ns, - const AttributeMap& attributes) - : type(type), data(data), ns(ns), attributes(attributes) {} + const std::string& prefix, + const AttributeMap& attributes, + NamespaceMap namespaces) + : type(type), data(data), ns(ns), prefix(prefix), attributes(attributes), namespaces(std::move(namespaces)) {} + Event( + Type type, + const std::string& data, + const std::string& ns, + const AttributeMap& attributes, + NamespaceMap namespaces = {}) + : Event(type, data, ns, {}, attributes, std::move(namespaces)) {} Event(Type type, const std::string& data, const std::string& ns = std::string()) - : type(type), data(data), ns(ns) {} + : Event(type, data, ns, "", AttributeMap(), NamespaceMap()) {} Type type; std::string data; std::string ns; + std::string prefix; AttributeMap attributes; + NamespaceMap namespaces; }; Client() {} - virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) { - events.push_back(Event(StartElement, element, ns, attributes)); + void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) override { + if (testingStartElementPrefix) return; + events.push_back(Event(StartElement, element, ns, attributes, std::move(namespaces_))); } - virtual void handleEndElement(const std::string& element, const std::string& ns) { + void handleStartElementPrefix(const std::string& prefix, const std::string& uri, const std::string& name, const AttributeMap&) override { + if (!testingStartElementPrefix) return; + events.push_back(Event(StartElementPrefix, name, uri, prefix, AttributeMap(), NamespaceMap())); + } + + void handleEndElement(const std::string& element, const std::string& ns) override { events.push_back(Event(EndElement, element, ns)); } - virtual void handleCharacterData(const std::string& data) { + void handleCharacterData(const std::string& data) override { events.push_back(Event(CharacterData, data)); } + void handleNamespaceDeclaration(const std::string& prefix, const std::string& uri) override { + namespaces_[prefix] = uri; + } + std::vector<Event> events; + bool testingStartElementPrefix = false; + private: + NamespaceMap namespaces_; } client_; }; diff --git a/Swiften/Parser/XMLParser.cpp b/Swiften/Parser/XMLParser.cpp index 8e92fe4..8a0799f 100644 --- a/Swiften/Parser/XMLParser.cpp +++ b/Swiften/Parser/XMLParser.cpp @@ -8,7 +8,7 @@ namespace Swift { -XMLParser::XMLParser(XMLParserClient* client) : client_(client) { +XMLParser::XMLParser(XMLParserClient* client, bool allowComments) : client_(client), allowComments_(allowComments){ } XMLParser::~XMLParser() { diff --git a/Swiften/Parser/XMLParser.h b/Swiften/Parser/XMLParser.h index 8a73c3f..3b09d22 100644 --- a/Swiften/Parser/XMLParser.h +++ b/Swiften/Parser/XMLParser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -15,16 +15,21 @@ namespace Swift { class SWIFTEN_API XMLParser { public: - XMLParser(XMLParserClient* client); + XMLParser(XMLParserClient* client, bool allowComments = false); virtual ~XMLParser(); - virtual bool parse(const std::string& data) = 0; + virtual bool parse(const std::string& data, bool finalData = false) = 0; XMLParserClient* getClient() const { return client_; } + bool allowsComments() const { + return allowComments_; + } + private: XMLParserClient* client_; + const bool allowComments_ = false; }; } diff --git a/Swiften/Parser/XMLParserClient.cpp b/Swiften/Parser/XMLParserClient.cpp index 6dc6db6..6698900 100644 --- a/Swiften/Parser/XMLParserClient.cpp +++ b/Swiften/Parser/XMLParserClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -11,5 +11,14 @@ namespace Swift { XMLParserClient::~XMLParserClient() { } +void XMLParserClient::handleStartElement(const std::string&, const std::string&, const AttributeMap&) { +} + +void XMLParserClient::handleStartElementPrefix(const std::string&, const std::string&, const std::string&, const AttributeMap&) { +} + +void XMLParserClient::handleNamespaceDeclaration(const std::string&, const std::string&) { +} + } diff --git a/Swiften/Parser/XMLParserClient.h b/Swiften/Parser/XMLParserClient.h index e4346f6..2f0bc9e 100644 --- a/Swiften/Parser/XMLParserClient.h +++ b/Swiften/Parser/XMLParserClient.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -14,8 +14,22 @@ namespace Swift { public: virtual ~XMLParserClient(); - virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) = 0; + /** + * Client will have to implement only one of the following methods depending on whether + * he is interested in processing the element prefix or not. + */ + virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes); + virtual void handleStartElementPrefix(const std::string& prefix, const std::string& uri, const std::string& element, const AttributeMap& attributes); + virtual void handleEndElement(const std::string& element, const std::string& ns) = 0; virtual void handleCharacterData(const std::string& data) = 0; + + /** + * Signal that a namespace prefix has been declared + * This callback might be called multiple times for a single element, + * and will trigger before the corresponding \ref handleStartElement + * is called. + */ + virtual void handleNamespaceDeclaration(const std::string& prefix, const std::string& uri); }; } diff --git a/Swiften/Parser/XMLParserFactory.h b/Swiften/Parser/XMLParserFactory.h index 595512b..ae3c90e 100644 --- a/Swiften/Parser/XMLParserFactory.h +++ b/Swiften/Parser/XMLParserFactory.h @@ -18,6 +18,6 @@ namespace Swift { public: virtual ~XMLParserFactory(); - virtual std::unique_ptr<XMLParser> createXMLParser(XMLParserClient*) = 0; + virtual std::unique_ptr<XMLParser> createXMLParser(XMLParserClient*, bool allowComments = false) = 0; }; } |