summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/Parser/BOSHBodyExtractor.cpp132
-rw-r--r--Swiften/Parser/BOSHBodyExtractor.h34
-rw-r--r--Swiften/Parser/SConscript1
-rw-r--r--Swiften/Parser/UnitTest/BOSHBodyExtractorTest.cpp104
-rw-r--r--Swiften/SConscript1
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"),