Implement MIME parsing

This commit adds two core features to bmcweb:

1. A multipart mime parser that can read multipart form requests into
bmcweb.  This is implemented as a generic parser that identifies the
content-type strings and parses them into structures.

2. A /login route that can be logged into with a multipart form.  This
is to allow changing the login screen to a purely forms based
implementation, thus removing the very large whitelist we currently have
to maintain, and removing javascript from our threat envelope.

More testing is still needed, as this is a parser that exists outside of
the secured areas, but in this simple example, it seems to work well.

Tested: curl -vvvvv --insecure -X POST -F 'username=root' -F
'password=0penBmc' https://<bmc ip address>:18080/login

Returned; { "data": "User 'root' logged in", "message": "200 OK",
"status": "ok" }

Change-Id: Icc3f4c082d584170b65b9e82f7876926cd38035d
Signed-off-by: Ed Tanous<ed@tanous.net>
Signed-off-by: George Liu <liuxiwei@inspur.com>
diff --git a/include/ut/multipart_test.cpp b/include/ut/multipart_test.cpp
new file mode 100644
index 0000000..35e05e2
--- /dev/null
+++ b/include/ut/multipart_test.cpp
@@ -0,0 +1,237 @@
+#include <http_utility.hpp>
+#include <multipart_parser.hpp>
+
+#include <map>
+
+#include "gmock/gmock.h"
+
+class MultipartTest : public ::testing::Test
+{
+  public:
+    boost::beast::http::request<boost::beast::http::string_body> req{};
+    MultipartParser parser;
+    std::error_code ec;
+};
+
+TEST_F(MultipartTest, TestGoodMultipartParser)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
+                 "111111111111111111111111112222222222222222222222222222222\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "{\r\n-----------------------------d74496d66958873e123456\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test3\"\r\n\r\n"
+                 "{\r\n--------d74496d6695887}\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ParserError rc = parser.parse(reqIn);
+    ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
+
+    ASSERT_EQ(parser.boundary,
+              "\r\n-----------------------------d74496d66958873e");
+    ASSERT_EQ(parser.mime_fields.size(), 3);
+
+    ASSERT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
+              "form-data; name=\"Test1\"");
+    ASSERT_EQ(parser.mime_fields[0].content,
+              "111111111111111111111111112222222222222222222222222222222");
+
+    ASSERT_EQ(parser.mime_fields[1].fields.at("Content-Disposition"),
+              "form-data; name=\"Test2\"");
+    ASSERT_EQ(parser.mime_fields[1].content,
+              "{\r\n-----------------------------d74496d66958873e123456");
+    ASSERT_EQ(parser.mime_fields[2].fields.at("Content-Disposition"),
+              "form-data; name=\"Test3\"");
+    ASSERT_EQ(parser.mime_fields[2].content, "{\r\n--------d74496d6695887}");
+}
+
+TEST_F(MultipartTest, TestBadMultipartParser1)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
+                 "1234567890\r\n"
+                 "-----------------------------d74496d66958873e\r-\r\n";
+
+    crow::Request reqIn(req, ec);
+    ParserError rc = parser.parse(reqIn);
+    ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
+
+    ASSERT_EQ(parser.boundary,
+              "\r\n-----------------------------d74496d66958873e");
+    ASSERT_EQ(parser.mime_fields.size(), 1);
+}
+
+TEST_F(MultipartTest, TestBadMultipartParser2)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
+                 "abcd\r\n"
+                 "-----------------------------d74496d66958873e-\r\n";
+
+    crow::Request reqIn(req, ec);
+    ParserError rc = parser.parse(reqIn);
+    ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
+
+    ASSERT_EQ(parser.boundary,
+              "\r\n-----------------------------d74496d66958873e");
+    ASSERT_EQ(parser.mime_fields.size(), 1);
+}
+
+TEST_F(MultipartTest, TestErrorBoundaryFormat)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary+=-----------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
+                 "{\"Key1\": 11223333333333333333333333333333333333333333}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_FORMAT);
+}
+
+TEST_F(MultipartTest, TestErrorBoundaryCR)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_CR);
+}
+
+TEST_F(MultipartTest, TestErrorBoundaryLF)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_LF);
+}
+
+TEST_F(MultipartTest, TestErrorBoundaryData)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d7449sd6d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_DATA);
+}
+
+TEST_F(MultipartTest, TestErrorEmptyHeader)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 ": form-data; name=\"Test1\"\r\n"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_EMPTY_HEADER);
+}
+
+TEST_F(MultipartTest, TestErrorHeaderName)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-!!Disposition: form-data; name=\"Test1\"\r\n"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_NAME);
+}
+
+TEST_F(MultipartTest, TestErrorHeaderValue)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_VALUE);
+}
+
+TEST_F(MultipartTest, TestErrorHeaderEnding)
+{
+    req.set("Content-Type",
+            "multipart/form-data; "
+            "boundary=---------------------------d74496d66958873e");
+
+    req.body() = "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test1\"\r\n\r"
+                 "{\"Key1\": 112233}\r\n"
+                 "-----------------------------d74496d66958873e\r\n"
+                 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
+                 "123456\r\n"
+                 "-----------------------------d74496d66958873e--\r\n";
+
+    crow::Request reqIn(req, ec);
+    ASSERT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_ENDING);
+}