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"), | 
 Swift
 Swift