/* * Copyright (c) 2011-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Parser/BOSHBodyExtractor.h> #include <memory> #include <boost/numeric/conversion/cast.hpp> #include <Swiften/Parser/XMLParser.h> #include <Swiften/Parser/XMLParserClient.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(unsigned 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)), boost::numeric_cast<size_t>(std::distance(i, j.base()))); } // Parse the body element BOSHBodyParserClient parserClient(this); std::shared_ptr<XMLParser> parser(parserFactory->createXMLParser(&parserClient)); if (!parser->parse(std::string( reinterpret_cast<const char*>(vecptr(data)), boost::numeric_cast<size_t>(std::distance(data.begin(), i))))) { /* TODO: This needs to be only validating the BOSH <body> element, so that XMPP parsing errors are caught at the correct higher layer */ body = boost::optional<BOSHBody>(); return; } } }