From 4f61e116c83be5e38a8406ac3285a07b84712ec1 Mon Sep 17 00:00:00 2001
From: Tarun Gupta <tarun1995gupta@gmail.com>
Date: Sat, 24 Jun 2017 21:39:39 +0530
Subject: Adds MIX Create Element, its Parser and Serializer

License:
This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.

Test-Information:
Added tests for MIX Create Parser and Serializer based on
examples in XEP 0369, which passes.

Change-Id: I33b85c8243d55cd293c886f2607bdd9dec6c7bdb

diff --git a/Swiften/Elements/MIXCreate.h b/Swiften/Elements/MIXCreate.h
new file mode 100644
index 0000000..0fab463
--- /dev/null
+++ b/Swiften/Elements/MIXCreate.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Form.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+    class SWIFTEN_API MIXCreate : public Payload {
+        public:
+            using ref = std::shared_ptr<MIXCreate>;
+
+        public:
+
+            MIXCreate() {}
+
+            std::shared_ptr<Form> getData() const {
+                return data_;
+            }
+
+            void setData(std::shared_ptr<Form> value) {
+                this->data_ = value ;
+            }
+
+            const boost::optional<std::string>& getChannel() const {
+                return channel_;
+            }
+
+            void setChannel(const std::string& channel) {
+                this->channel_ = channel;
+            }
+
+        private:
+            boost::optional<std::string> channel_;
+            std::shared_ptr<Form> data_;
+    };
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 76827bb..bd9bdaa 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -52,6 +52,7 @@
 #include <Swiften/Parser/PayloadParsers/MAMQueryParser.h>
 #include <Swiften/Parser/PayloadParsers/MAMResultParser.h>
 #include <Swiften/Parser/PayloadParsers/MIXParticipantParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/MIXCreateParser.h>
 #include <Swiften/Parser/PayloadParsers/MIXDestroyParser.h>
 #include <Swiften/Parser/PayloadParsers/MIXJoinParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h>
@@ -134,6 +135,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
     factories_.push_back(std::make_shared<ClientStateParserFactory>());
     factories_.push_back(std::make_shared<MIXParticipantParserFactory>());
     factories_.push_back(std::make_shared<GenericPayloadParserFactory<MIXDestroyParser> >("destroy", "urn:xmpp:mix:1"));
+    factories_.push_back(std::make_shared<GenericPayloadParserFactory<MIXCreateParser> >("create", "urn:xmpp:mix:1"));
     factories_.push_back(std::make_shared<MUCUserPayloadParserFactory>(this));
     factories_.push_back(std::make_shared<MUCOwnerPayloadParserFactory>(this));
     factories_.push_back(std::make_shared<GenericPayloadParserFactory<MUCInvitationPayloadParser> >("x", "jabber:x:conference"));
diff --git a/Swiften/Parser/PayloadParsers/MIXCreateParser.cpp b/Swiften/Parser/PayloadParsers/MIXCreateParser.cpp
new file mode 100644
index 0000000..8c4ec8a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MIXCreateParser.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/MIXCreateParser.h>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/FormParser.h>
+
+using namespace Swift;
+
+MIXCreateParser::MIXCreateParser() : level_(0) {
+}
+
+MIXCreateParser::~MIXCreateParser() {
+}
+
+void MIXCreateParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+    if (level_ == 0) {
+        if (boost::optional<std::string> attributeValue = attributes.getAttributeValue("channel")) {
+            getPayloadInternal()->setChannel(*attributeValue);
+        }
+    }
+
+    if (level_ == 1) {
+        if (element == "x" && ns == "jabber:x:data") {
+            currentPayloadParser_ = std::make_shared<FormParser>();
+        }
+    }
+
+    if (level_ >= 1 && currentPayloadParser_) {
+        currentPayloadParser_->handleStartElement(element, ns, attributes);
+    }
+    ++level_;
+}
+
+void MIXCreateParser::handleEndElement(const std::string& element, const std::string& ns) {
+    --level_;
+    if (currentPayloadParser_) {
+        if (level_ >= 1) {
+            currentPayloadParser_->handleEndElement(element, ns);
+        }
+
+        if (level_ == 1) {
+            if (element == "x" && ns == "jabber:x:data") {
+                getPayloadInternal()->setData(std::dynamic_pointer_cast<Form>(currentPayloadParser_->getPayload()));
+            }
+            currentPayloadParser_.reset();
+        }
+    }
+}
+
+void MIXCreateParser::handleCharacterData(const std::string& data) {
+    if (level_ > 1 && currentPayloadParser_) {
+        currentPayloadParser_->handleCharacterData(data);
+    }
+}
diff --git a/Swiften/Parser/PayloadParsers/MIXCreateParser.h b/Swiften/Parser/PayloadParsers/MIXCreateParser.h
new file mode 100644
index 0000000..60e3040
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MIXCreateParser.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Base/Override.h>
+#include <Swiften/Elements/MIXCreate.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+    class PayloadParser;
+
+    class SWIFTEN_API MIXCreateParser : public GenericPayloadParser<MIXCreate> {
+        public:
+            MIXCreateParser();
+            virtual ~MIXCreateParser();
+
+            virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) SWIFTEN_OVERRIDE;
+            virtual void handleEndElement(const std::string& element, const std::string&) SWIFTEN_OVERRIDE;
+            virtual void handleCharacterData(const std::string& data) SWIFTEN_OVERRIDE;
+
+        private:
+            int level_;
+            std::shared_ptr<PayloadParser> currentPayloadParser_;
+    };
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/MIXCreateParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/MIXCreateParserTest.cpp
new file mode 100644
index 0000000..85c79c6
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/MIXCreateParserTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <gtest/gtest.h>
+
+#include <Swiften/Elements/MIXCreate.h>
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
+
+using namespace Swift;
+
+TEST(MIXCreateParserTest, XEP0369_Example68) {
+    PayloadsParserTester parser;
+    ASSERT_TRUE(parser.parse(
+                "<create xmlns=\"urn:xmpp:mix:1\"/>"
+                ));
+
+    auto payload = parser.getPayload<MIXCreate>();
+    ASSERT_TRUE(payload);
+
+    ASSERT_FALSE(payload->getData());
+    ASSERT_FALSE(payload->getChannel());
+}
+
+TEST(MIXCreateParserTest, XEP0369_Example66) {
+    PayloadsParserTester parser;
+    ASSERT_TRUE(parser.parse(
+                "<create channel=\"coven\" xmlns=\"urn:xmpp:mix:1\"/>"
+                ));
+
+    auto payload = parser.getPayload<MIXCreate>();
+    ASSERT_TRUE(payload);
+
+    ASSERT_FALSE(payload->getData());
+
+    ASSERT_TRUE(payload->getChannel());
+    ASSERT_EQ(std::string("coven"), *payload->getChannel());
+}
+
+TEST(MIXCreateParserTest, XEP0369_Example67) {
+    PayloadsParserTester parser;
+    ASSERT_TRUE(parser.parse(
+                 "<create channel=\"coven\" xmlns=\"urn:xmpp:mix:1\">"
+                    "<x xmlns=\"jabber:x:data\" type=\"result\">"
+                        "<field var=\"FORM_TYPE\" type=\"hidden\">"
+                            "<value>urn:xmpp:mix:1</value>"
+                        "</field>"
+                        "<field var=\"Owner\">"
+                            "<value>hecate@shakespeare.lit</value>"
+                            "<value>greymalkin@shakespeare.lit</value>"
+                        "</field>"
+                        "<field var=\"Messages Node Subscription\">"
+                            "<value>allowed</value>"
+                         "</field>"
+                        "<field var=\"JID Visibility\">"
+                            "<value>jid-mandatory-visible</value>"
+                        "</field>"
+                        "<field var=\"No Private Messages\">"
+                            "<value>true</value>"
+                        "</field>"
+                    "</x>"
+                "</create>"
+                ));
+
+    auto payload = parser.getPayload<MIXCreate>();
+    ASSERT_TRUE(payload);
+
+    ASSERT_TRUE(payload->getData());
+    ASSERT_EQ(Form::Type::ResultType, payload->getData()->getType());
+    std::shared_ptr<FormField> fieldType = payload->getData()->getField("FORM_TYPE");
+    ASSERT_TRUE(fieldType);
+
+    std::shared_ptr<FormField> fieldJIDVisibility = payload->getData()->getField("JID Visibility");
+    ASSERT_TRUE(fieldJIDVisibility);
+    ASSERT_EQ(std::string("jid-mandatory-visible"), fieldJIDVisibility->getTextSingleValue());
+
+    std::shared_ptr<FormField> fieldprivateMessages = payload->getData()->getField("No Private Messages");
+    ASSERT_TRUE(fieldprivateMessages);
+    ASSERT_EQ(std::string("true"), fieldprivateMessages->getTextSingleValue());
+
+    std::shared_ptr<FormField> nodeSubs = payload->getData()->getField("Messages Node Subscription");
+    ASSERT_TRUE(nodeSubs);
+    ASSERT_EQ(std::string("allowed"), nodeSubs->getTextSingleValue());
+
+    ASSERT_TRUE(payload->getChannel());
+    ASSERT_EQ(std::string("coven"), *payload->getChannel());
+}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 55d7eaf..eccc941 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -69,6 +69,7 @@ sources = [
         "PayloadParsers/DelayParser.cpp",
         "PayloadParsers/MIXParticipantParser.cpp",
         "PayloadParsers/MIXDestroyParser.cpp",
+        "PayloadParsers/MIXCreateParser.cpp",
         "PayloadParsers/MIXJoinParser.cpp",
         "PayloadParsers/MIXSubscribeParser.cpp",
         "PayloadParsers/MUCUserPayloadParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 8d00db1..6e30b26 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -209,6 +209,7 @@ if env["SCONS_STAGE"] == "build" :
             "Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp",
             "Serializer/PayloadSerializers/MIXParticipantSerializer.cpp",
             "Serializer/PayloadSerializers/MIXDestroySerializer.cpp",
+            "Serializer/PayloadSerializers/MIXCreateSerializer.cpp",
             "Serializer/PayloadSerializers/MIXJoinSerializer.cpp",
             "Serializer/PayloadSerializers/MIXSubscribeSerializer.cpp",
             "Serializer/PayloadSerializers/MUCPayloadSerializer.cpp",
@@ -436,6 +437,7 @@ if env["SCONS_STAGE"] == "build" :
             File("Parser/PayloadParsers/UnitTest/JingleParserTest.cpp"),
             File("Parser/PayloadParsers/UnitTest/MIXParticipantParserTest.cpp"),
             File("Parser/PayloadParsers/UnitTest/MIXDestroyParserTest.cpp"),
+            File("Parser/PayloadParsers/UnitTest/MIXCreateParserTest.cpp"),
             File("Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp"),
             File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"),
             File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"),
@@ -518,6 +520,7 @@ if env["SCONS_STAGE"] == "build" :
             File("Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp"),
             File("Serializer/PayloadSerializers/UnitTest/MIXParticipantSerializerTest.cpp"),
             File("Serializer/PayloadSerializers/UnitTest/MIXDestroySerializerTest.cpp"),
+            File("Serializer/PayloadSerializers/UnitTest/MIXCreateSerializerTest.cpp"),
             File("Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp"),
             File("Serializer/PayloadSerializers/UnitTest/DeliveryReceiptSerializerTest.cpp"),
             File("Serializer/PayloadSerializers/UnitTest/IdleSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 72b6128..4c3cf66 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -48,6 +48,7 @@
 #include <Swiften/Serializer/PayloadSerializers/MIXJoinSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MIXDestroySerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MIXParticipantSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MUCDestroyPayloadSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.h>
@@ -158,6 +159,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
     serializers_.push_back(new ResultSetSerializer());
     serializers_.push_back(new ForwardedSerializer(this));
     serializers_.push_back(new MIXParticipantSerializer());
+    serializers_.push_back(new MIXCreateSerializer());
     serializers_.push_back(new MIXJoinSerializer());
     serializers_.push_back(new MIXDestroySerializer());
     serializers_.push_back(new MAMResultSerializer(this));
diff --git a/Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.cpp
new file mode 100644
index 0000000..9476df2
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.h>
+
+#include <memory>
+
+#include <Swiften/Serializer/PayloadSerializers/FormSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+using namespace Swift;
+
+MIXCreateSerializer::MIXCreateSerializer() {
+}
+
+MIXCreateSerializer::~MIXCreateSerializer() {
+}
+
+std::string MIXCreateSerializer::serializePayload(std::shared_ptr<MIXCreate> payload) const {
+    if (!payload) {
+        return "";
+    }
+    XMLElement element("create", "urn:xmpp:mix:1");
+
+    if(payload->getChannel()) {
+        element.setAttribute("channel", *payload->getChannel());
+    }
+
+    if(payload->getData()) {
+        element.addNode(std::make_shared<XMLRawTextNode>(FormSerializer().serialize(payload->getData())));
+    }
+
+    return element.serialize();
+}
diff --git a/Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.h b/Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.h
new file mode 100644
index 0000000..23cc65d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Base/Override.h>
+#include <Swiften/Elements/MIXCreate.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+
+namespace Swift {
+
+    class SWIFTEN_API MIXCreateSerializer : public GenericPayloadSerializer<MIXCreate> {
+        public:
+            MIXCreateSerializer();
+            virtual ~MIXCreateSerializer();
+
+            virtual std::string serializePayload(std::shared_ptr<MIXCreate>) const SWIFTEN_OVERRIDE;
+    };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/MIXCreateSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/MIXCreateSerializerTest.cpp
new file mode 100644
index 0000000..78424be
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/MIXCreateSerializerTest.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 Tarun Gupta
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <gtest/gtest.h>
+
+#include <Swiften/Serializer/PayloadSerializers/MIXCreateSerializer.h>
+
+using namespace Swift;
+
+TEST(MIXCreateSerializerTest, XEP0369_Example31) {
+    MIXCreateSerializer testling;
+
+    auto create = std::make_shared<MIXCreate>();
+    std::string expectedResult = "<create xmlns=\"urn:xmpp:mix:1\"/>";
+    ASSERT_EQ(expectedResult, testling.serialize(create));
+}
+
+TEST(MIXCreateSerializerTest, XEP0369_Example66) {
+    MIXCreateSerializer testling;
+
+    auto create = std::make_shared<MIXCreate>();
+    create->setChannel(std::string("coven"));
+
+    std::string expectedResult = "<create channel=\"coven\" xmlns=\"urn:xmpp:mix:1\"/>";
+    ASSERT_EQ(expectedResult, testling.serialize(create));
+}
-- 
cgit v0.10.2-6-g49f6