diff options
-rw-r--r-- | Swiften/Parser/BOSHBodyExtractor.cpp | 132 | ||||
-rw-r--r-- | Swiften/Parser/BOSHBodyExtractor.h | 34 | ||||
-rw-r--r-- | Swiften/Parser/SConscript | 1 | ||||
-rw-r--r-- | Swiften/Parser/UnitTest/BOSHBodyExtractorTest.cpp | 104 | ||||
-rw-r--r-- | Swiften/SConscript | 1 |
5 files changed, 272 insertions, 0 deletions
diff --git a/Swiften/Parser/BOSHBodyExtractor.cpp b/Swiften/Parser/BOSHBodyExtractor.cpp new file mode 100644 index 0000000..d8759a3 --- /dev/null +++ b/Swiften/Parser/BOSHBodyExtractor.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/BOSHBodyExtractor.h> + +#include <boost/shared_ptr.hpp> + +#include <Swiften/Parser/XMLParserClient.h> +#include <Swiften/Parser/XMLParser.h> +#include <Swiften/Parser/XMLParserFactory.h> + +namespace Swift { + +class BOSHBodyParserClient : public XMLParserClient { + public: + BOSHBodyParserClient(BOSHBodyExtractor* bodyExtractor) : bodyExtractor(bodyExtractor) { + } + + virtual void handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) { + bodyExtractor->body->attributes = attributes; + } + + virtual void handleEndElement(const std::string&, const std::string&) { + } + + virtual void handleCharacterData(const std::string&) { + } + + private: + BOSHBodyExtractor* bodyExtractor; +}; + +inline bool isWhitespace(char c) { + return c == ' ' || c == '\n' || c == '\t' || c == '\r'; +} + +BOSHBodyExtractor::BOSHBodyExtractor(XMLParserFactory* parserFactory, const ByteArray& data) { + // Look for the opening body element + ByteArray::const_iterator i = data.begin(); + while (i < data.end() && isWhitespace(*i)) { + ++i; + } + if (std::distance(i, data.end()) < 6 || *i != '<' || *(i+1) != 'b' || *(i+2) != 'o' || *(i+3) != 'd' || *(i+4) != 'y' || !(isWhitespace(*(i+5)) || *(i+5) == '>' || *(i+5) == '/')) { + return; + } + i += 5; + + // Parse until end of element + bool inSingleQuote = false; + bool inDoubleQuote = false; + bool endStartTagSeen = false; + bool endElementSeen = false; + for (; i != data.end(); ++i) { + char c = static_cast<char>(*i); + if (inSingleQuote) { + if (c == '\'') { + inSingleQuote = false; + } + } + else if (inDoubleQuote) { + if (c == '"') { + inDoubleQuote = false; + } + } + else if (c == '\'') { + inSingleQuote = true; + } + else if (c == '"') { + inDoubleQuote = true; + } + else if (c == '/') { + if (i + 1 == data.end() || *(i+1) != '>') { + return; + } + else { + endElementSeen = true; + endStartTagSeen = true; + i += 2; + break; + } + } + else if (c == '>') { + endStartTagSeen = true; + i += 1; + break; + } + } + + if (!endStartTagSeen) { + return; + } + + // Look for the end of the element + ByteArray::const_reverse_iterator j = data.rbegin(); + if (!endElementSeen) { + while (isWhitespace(*j) && j < data.rend()) { + ++j; + } + + if (j == data.rend() || *j != '>') { + return; + } + ++j; + + while (j < data.rend() && isWhitespace(*j)) { + ++j; + } + + if (std::distance(j, data.rend()) < 6 || *(j+5) != '<' || *(j+4) != '/' || *(j+3) != 'b' || *(j+2) != 'o' || *(j+1) != 'd' || *j != 'y') { + return; + } + j += 6; + } + + body = BOSHBody(); + if (!endElementSeen) { + body->content = std::string(reinterpret_cast<const char*>(vecptr(data) + std::distance(data.begin(), i)), std::distance(i, j.base())); + } + + // Parse the body element + BOSHBodyParserClient parserClient(this); + boost::shared_ptr<XMLParser> parser(parserFactory->createXMLParser(&parserClient)); + if (!parser->parse(std::string(reinterpret_cast<const char*>(vecptr(data)), std::distance(data.begin(), i)))) { + body = boost::optional<BOSHBody>(); + return; + } +} + +} diff --git a/Swiften/Parser/BOSHBodyExtractor.h b/Swiften/Parser/BOSHBodyExtractor.h new file mode 100644 index 0000000..d1266c6 --- /dev/null +++ b/Swiften/Parser/BOSHBodyExtractor.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/optional.hpp> + +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Parser/XMLParserClient.h> + +namespace Swift { + struct XMLParserFactory; + + class BOSHBodyExtractor { + friend class BOSHBodyParserClient; + public: + struct BOSHBody { + AttributeMap attributes; + std::string content; + }; + + BOSHBodyExtractor(XMLParserFactory* parserFactory, const ByteArray& data); + + const boost::optional<BOSHBody>& getBody() { + return body; + } + + private: + boost::optional<BOSHBody> body; + }; +} diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index 2a5d2ee..1cef02c 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -17,6 +17,7 @@ sources = [ "MessageParser.cpp", "PayloadParser.cpp", "StanzaAckParser.cpp", + "BOSHBodyExtractor.cpp", "ComponentHandshakeParser.cpp", "PayloadParserFactory.cpp", "PayloadParserFactoryCollection.cpp", diff --git a/Swiften/Parser/UnitTest/BOSHBodyExtractorTest.cpp b/Swiften/Parser/UnitTest/BOSHBodyExtractorTest.cpp new file mode 100644 index 0000000..fc7d17b --- /dev/null +++ b/Swiften/Parser/UnitTest/BOSHBodyExtractorTest.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Parser/PlatformXMLParserFactory.h> +#include <Swiften/Parser/BOSHBodyExtractor.h> + +using namespace Swift; + +class BOSHBodyExtractorTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BOSHBodyExtractorTest); + CPPUNIT_TEST(testGetBody); + CPPUNIT_TEST(testGetBody_EmptyContent); + CPPUNIT_TEST(testGetBody_EmptyContent2); + CPPUNIT_TEST(testGetBody_EmptyElementEmptyContent); + CPPUNIT_TEST(testGetBody_InvalidStartTag); + CPPUNIT_TEST(testGetBody_InvalidStartTag2); + CPPUNIT_TEST(testGetBody_IncompleteStartTag); + CPPUNIT_TEST(testGetBody_InvalidEndTag); + CPPUNIT_TEST(testGetBody_InvalidEndTag2); + CPPUNIT_TEST_SUITE_END(); + + public: + void testGetBody() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body a1='a\"1' a2=\"a'2\" boo='bar' >" + "foo <message> <body> bar" + "</body > ")); + + CPPUNIT_ASSERT(testling.getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("a\"1"), testling.getBody()->attributes.getAttribute("a1")); + CPPUNIT_ASSERT_EQUAL(std::string("foo <message> <body> bar"), testling.getBody()->content); + } + + void testGetBody_EmptyContent() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body foo='bar'/>")); + + CPPUNIT_ASSERT(testling.getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getBody()->attributes.getAttribute("foo")); + CPPUNIT_ASSERT(testling.getBody()->content.empty()); + } + + void testGetBody_EmptyContent2() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body foo='bar'></body>")); + + CPPUNIT_ASSERT(testling.getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getBody()->attributes.getAttribute("foo")); + CPPUNIT_ASSERT(testling.getBody()->content.empty()); + } + + void testGetBody_EmptyElementEmptyContent() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body/>")); + + CPPUNIT_ASSERT(testling.getBody()); + } + + void testGetBody_InvalidStartTag() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<bodi></body>")); + + CPPUNIT_ASSERT(!testling.getBody()); + } + + void testGetBody_InvalidStartTag2() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<bodyy></body>")); + + CPPUNIT_ASSERT(!testling.getBody()); + } + + void testGetBody_IncompleteStartTag() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body")); + + CPPUNIT_ASSERT(!testling.getBody()); + } + + void testGetBody_InvalidEndTag() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body></bodi>")); + + CPPUNIT_ASSERT(!testling.getBody()); + } + + void testGetBody_InvalidEndTag2() { + BOSHBodyExtractor testling(&parserFactory, createByteArray( + "<body><b/body>")); + + CPPUNIT_ASSERT(!testling.getBody()); + } + + private: + PlatformXMLParserFactory parserFactory; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BOSHBodyExtractorTest); diff --git a/Swiften/SConscript b/Swiften/SConscript index 6e129a3..e650e2a 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -307,6 +307,7 @@ if env["SCONS_STAGE"] == "build" : File("Parser/PayloadParsers/UnitTest/ReplaceTest.cpp"), File("Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/MUCUserPayloadParserTest.cpp"), + File("Parser/UnitTest/BOSHBodyExtractorTest.cpp"), File("Parser/UnitTest/AttributeMapTest.cpp"), File("Parser/UnitTest/IQParserTest.cpp"), File("Parser/UnitTest/GenericPayloadTreeParserTest.cpp"), |