From 3476833ca17c19dba0a31645d689f216039107dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sat, 1 Oct 2011 12:37:45 +0200
Subject: Added <body/> extractor code.


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"),
-- 
cgit v0.10.2-6-g49f6