summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/Parser/ExpatParser.cpp5
-rw-r--r--Swiften/Parser/LibXMLParser.cpp7
-rw-r--r--Swiften/Parser/UnitTest/XMLParserTest.cpp35
-rw-r--r--Swiften/Parser/XMLParserClient.cpp5
-rw-r--r--Swiften/Parser/XMLParserClient.h10
5 files changed, 52 insertions, 10 deletions
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
@@ -58,30 +58,35 @@ static void handleEndElement(void* parser, const XML_Char* name) {
static void handleCharacterData(void* parser, const XML_Char* data, int len) {
assert(len >= 0);
static_cast<XMLParser*>(parser)->getClient()->handleCharacterData(std::string(data, static_cast<size_t>(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();
}
ExpatParser::ExpatParser(XMLParserClient* client) : XMLParser(client), p(new Private()) {
p->parser_ = XML_ParserCreateNS("UTF-8", NAMESPACE_SEPARATOR);
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);
}
ExpatParser::~ExpatParser() {
XML_ParserFree(p->parser_);
}
bool ExpatParser::parse(const std::string& data, bool finalData) {
if (data.size() > std::numeric_limits<int>::max()) {
return false;
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
@@ -18,36 +18,41 @@
#include <Swiften/Parser/XMLParserClient.h>
namespace Swift {
struct LibXMLParser::Private {
xmlSAXHandler handler_;
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 :-)
SWIFT_LOG(error) << "Unexpected nbDefaulted on XML element" << std::endl;
}
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]));
}
assert(attributes[i+4] >= attributes[i+3]);
attributeValues.addAttribute(
std::string(reinterpret_cast<const char*>(attributes[i])),
attributeNS,
std::string(reinterpret_cast<const char*>(attributes[i+3]),
static_cast<size_t>(attributes[i+4]-attributes[i+3])));
}
+ for (auto i = 0; i < nbNamespaces * 2; i += 2) {
+ const auto prefix = namespaces[i] ? std::string(reinterpret_cast<const char*>(namespaces[i])) : "";
+ const auto uri = std::string(reinterpret_cast<const char*>(namespaces[i + 1]));
+ static_cast<XMLParser*>(parser)->getClient()->handleNamespaceDeclaration(prefix, uri);
+ }
static_cast<XMLParser*>(parser)->getClient()->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()), 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 void handleCharacterData(void* parser, const xmlChar* data, int len) {
assert(len >= 0);
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
@@ -1,17 +1,18 @@
/*
* Copyright (c) 2010 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <unordered_map>
#include <vector>
#include <string>
#include <Swiften/Parser/XMLParserClient.h>
#ifdef HAVE_EXPAT
#include <Swiften/Parser/ExpatParser.h>
#endif
#ifdef HAVE_LIBXML
#include <Swiften/Parser/LibXMLParser.h>
@@ -55,18 +56,21 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("iq"), client_.events[0].data);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size());
CPPUNIT_ASSERT_EQUAL(std::string("get"), client_.events[0].attributes.getAttribute("type"));
CPPUNIT_ASSERT_EQUAL(std::string(), client_.events[0].ns);
CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
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);
CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[2].ns);
CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
CPPUNIT_ASSERT_EQUAL(std::string("iq"), client_.events[3].data);
CPPUNIT_ASSERT_EQUAL(std::string(), client_.events[3].ns);
}
@@ -79,22 +83,25 @@ class XMLParserTest : public CppUnit::TestFixture {
"<name>Swift</name>"
"</query>"));
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), client_.events.size());
CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
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);
CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
CPPUNIT_ASSERT_EQUAL(std::string("name"), client_.events[3].data);
CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[3].ns);
CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[4].type);
@@ -155,18 +162,20 @@ class XMLParserTest : public CppUnit::TestFixture {
ParserType testling(&client_);
CPPUNIT_ASSERT(testling.parse("<p:x xmlns:p='bla'><p:y/></p:x>"));
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), client_.events.size());
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);
CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[1].ns);
CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
CPPUNIT_ASSERT_EQUAL(std::string("y"), client_.events[2].data);
CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[2].ns);
@@ -256,18 +265,21 @@ class XMLParserTest : public CppUnit::TestFixture {
void testParse_AttributeWithNamespace() {
ParserType testling(&client_);
CPPUNIT_ASSERT(testling.parse(
"<query xmlns='http://swift.im' xmlns:f='http://swift.im/f' 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("http://swift.im/f"), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace());
+ 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() {
ParserType testling(&client_);
CPPUNIT_ASSERT(!testling.parse(
"<?xml version=\"1.0\"?>"
"<!DOCTYPE lolz ["
" <!ENTITY lol \"lol\">"
@@ -295,18 +307,19 @@ class XMLParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(testling.parse(
"<foo:bar><bla/></foo:bar>"));
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), client_.events.size());
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);
CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[1].ns);
CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
CPPUNIT_ASSERT_EQUAL(std::string("bla"), client_.events[2].data);
CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[2].ns);
@@ -322,50 +335,58 @@ class XMLParserTest : public CppUnit::TestFixture {
"<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());
}
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, 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) {}
Type type;
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<Event> events;
+ private:
+ NamespaceMap namespaces_;
} client_;
};
#ifdef HAVE_EXPAT
CPPUNIT_TEST_SUITE_REGISTRATION(XMLParserTest<ExpatParser>);
#endif
#ifdef HAVE_LIBXML
CPPUNIT_TEST_SUITE_REGISTRATION(XMLParserTest<LibXMLParser>);
#endif
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,15 +1,18 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/Parser/XMLParserClient.h>
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,21 +1,29 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <Swiften/Base/API.h>
#include <Swiften/Parser/AttributeMap.h>
namespace Swift {
class SWIFTEN_API XMLParserClient {
public:
virtual ~XMLParserClient();
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);
};
}