From f6fb85ba98fdd6601c4b8323c51c8367ccc4b52e Mon Sep 17 00:00:00 2001 From: Edwin Mons Date: Mon, 22 Jul 2019 10:54:55 +0200 Subject: Signal namespace declarations to ParserClients Prior to calling handleStartElement, the ParserClient handleNamespaceDeclaration will fire for each namespace declared on the element. Test-Information: Unit tests pass on Debian 9 for both expat and libxml2 Change-Id: Ic42e83aee83edfbb2aa5c971997808eb6e133223 diff --git a/Swiften/Parser/ExpatParser.cpp b/Swiften/Parser/ExpatParser.cpp index e4e66f2..a50949b 100644 --- a/Swiften/Parser/ExpatParser.cpp +++ b/Swiften/Parser/ExpatParser.cpp @@ -64,6 +64,10 @@ 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(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(parser)->stopParser(); } @@ -76,6 +80,7 @@ ExpatParser::ExpatParser(XMLParserClient* client) : XMLParser(client), p(new Pri XML_SetCharacterDataHandler(p->parser_, handleCharacterData); XML_SetXmlDeclHandler(p->parser_, handleXMLDeclaration); XML_SetEntityDeclHandler(p->parser_, handleEntityDeclaration); + XML_SetNamespaceDeclHandler(p->parser_, handleNamespaceDeclaration, nullptr); } ExpatParser::~ExpatParser() { diff --git a/Swiften/Parser/LibXMLParser.cpp b/Swiften/Parser/LibXMLParser.cpp index c9f3a07..192f44b 100644 --- a/Swiften/Parser/LibXMLParser.cpp +++ b/Swiften/Parser/LibXMLParser.cpp @@ -24,7 +24,7 @@ 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*, 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 :-) @@ -42,6 +42,11 @@ static void handleStartElement(void* parser, const xmlChar* name, const xmlChar* std::string(reinterpret_cast(attributes[i+3]), static_cast(attributes[i+4]-attributes[i+3]))); } + for (auto i = 0; i < nbNamespaces * 2; i += 2) { + const auto prefix = namespaces[i] ? std::string(reinterpret_cast(namespaces[i])) : ""; + const auto uri = std::string(reinterpret_cast(namespaces[i + 1])); + static_cast(parser)->getClient()->handleNamespaceDeclaration(prefix, uri); + } static_cast(parser)->getClient()->handleStartElement(reinterpret_cast(name), (xmlns ? reinterpret_cast(xmlns) : std::string()), attributeValues); } diff --git a/Swiften/Parser/UnitTest/XMLParserTest.cpp b/Swiften/Parser/UnitTest/XMLParserTest.cpp index 9e9012b..c026b4b 100644 --- a/Swiften/Parser/UnitTest/XMLParserTest.cpp +++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -61,6 +62,9 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[1].data); CPPUNIT_ASSERT_EQUAL(static_cast(0), client_.events[1].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].ns); + CPPUNIT_ASSERT_EQUAL(static_cast(1), client_.events[1].namespaces.size()); + CPPUNIT_ASSERT_EQUAL(static_cast(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 +89,13 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[0].data); CPPUNIT_ASSERT_EQUAL(static_cast(0), client_.events[0].attributes.getEntries().size()); CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[0].ns); + CPPUNIT_ASSERT_EQUAL(static_cast(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(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 +168,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(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); @@ -262,6 +271,9 @@ class XMLParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(static_cast(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(static_cast(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() { @@ -301,6 +313,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(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); @@ -328,14 +341,16 @@ class XMLParserTest : public CppUnit::TestFixture { private: class Client : public XMLParserClient { public: - enum Type { StartElement, EndElement, CharacterData }; + using NamespaceMap = std::unordered_map; + enum Type { StartElement, 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 AttributeMap& attributes, + NamespaceMap namespaces = {}) + : type(type), data(data), ns(ns), attributes(attributes), namespaces(std::move(namespaces)) {} Event(Type type, const std::string& data, const std::string& ns = std::string()) : type(type), data(data), ns(ns) {} @@ -343,23 +358,29 @@ class XMLParserTest : public CppUnit::TestFixture { std::string data; std::string ns; 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 { + events.push_back(Event(StartElement, element, ns, attributes, std::move(namespaces_))); } - virtual void handleEndElement(const std::string& element, const std::string& ns) { + 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 events; + private: + NamespaceMap namespaces_; } client_; }; diff --git a/Swiften/Parser/XMLParserClient.cpp b/Swiften/Parser/XMLParserClient.cpp index 6dc6db6..40be4e8 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,8 @@ namespace Swift { XMLParserClient::~XMLParserClient() { } +void XMLParserClient::handleNamespaceDeclaration(const std::string&, const std::string&) { +} + } diff --git a/Swiften/Parser/XMLParserClient.h b/Swiften/Parser/XMLParserClient.h index e4346f6..0682320 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. */ @@ -17,5 +17,13 @@ namespace Swift { virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) = 0; 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); }; } -- cgit v0.10.2-6-g49f6