Save-area file upload to BMC

This change includes:
1. New compiler option for the IBM management console specific functionalities
2. New REST path handler for the IBM path /ibm/v1/Host/ConfigFiles
3. Save-Area file Upload through REST interface PUT command

Tested by :
curl -k -H "X-Auth-Token: $bmc_token" -X PUT https://${bmc}/ibm/v1/Host/ConfigFiles/<filename> --data-binary "@<filepath>"

Signed-off-by: Sunitha Harish <sunithaharish04@gmail.com>
Change-Id: I939938009b2de447aea9af5bb3c53bdb845c4084
diff --git a/include/ibm/management_console_rest.hpp b/include/ibm/management_console_rest.hpp
index 01771b3..3d2bbc2 100644
--- a/include/ibm/management_console_rest.hpp
+++ b/include/ibm/management_console_rest.hpp
@@ -3,11 +3,147 @@
 #include <tinyxml2.h>
 
 #include <async_resp.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/container/flat_set.hpp>
+#include <filesystem>
+#include <fstream>
+#include <regex>
+#include <sdbusplus/message/types.hpp>
+
+#define MAX_SAVE_AREA_FILESIZE 200000
 
 namespace crow
 {
 namespace ibm_mc
 {
+constexpr const char *methodNotAllowedMsg = "Method Not Allowed";
+constexpr const char *resourceNotFoundMsg = "Resource Not Found";
+constexpr const char *contentNotAcceptableMsg = "Content Not Acceptable";
+constexpr const char *internalServerError = "Internal Server Error";
+
+bool createSaveAreaPath(crow::Response &res)
+{
+    // The path /var/lib/obmc will be created by initrdscripts
+    // Create the directories for the save-area files, when we get
+    // first file upload request
+    std::error_code ec;
+    if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
+    {
+        std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
+    }
+    if (ec)
+    {
+        res.result(boost::beast::http::status::internal_server_error);
+        res.jsonValue["Description"] = internalServerError;
+        BMCWEB_LOG_DEBUG
+            << "handleIbmPost: Failed to prepare save-area directory. ec : "
+            << ec;
+        return false;
+    }
+
+    if (!std::filesystem::is_directory(
+            "/var/lib/obmc/bmc-console-mgmt/save-area", ec))
+    {
+        std::filesystem::create_directory(
+            "/var/lib/obmc/bmc-console-mgmt/save-area", ec);
+    }
+    if (ec)
+    {
+        res.result(boost::beast::http::status::internal_server_error);
+        res.jsonValue["Description"] = internalServerError;
+        BMCWEB_LOG_DEBUG
+            << "handleIbmPost: Failed to prepare save-area directory. ec : "
+            << ec;
+        return false;
+    }
+    return true;
+}
+void handleFilePut(const crow::Request &req, crow::Response &res,
+                   const std::string &objectPath,
+                   const std::string &destProperty)
+{
+    // Check the content-type of the request
+    std::string_view contentType = req.getHeaderValue("content-type");
+    if (boost::starts_with(contentType, "multipart/form-data"))
+    {
+        BMCWEB_LOG_DEBUG
+            << "This is multipart/form-data. Invalid content for PUT";
+
+        res.result(boost::beast::http::status::not_acceptable);
+        res.jsonValue["Description"] = contentNotAcceptableMsg;
+        return;
+    }
+    else
+    {
+        BMCWEB_LOG_DEBUG << "Not a multipart/form-data. Continue..";
+    }
+    std::size_t pos = objectPath.find("ConfigFiles/");
+    if (pos != std::string::npos)
+    {
+        BMCWEB_LOG_DEBUG
+            << "handleIbmPut: Request to create/update the save-area file";
+        if (!createSaveAreaPath(res))
+        {
+            res.result(boost::beast::http::status::not_found);
+            res.jsonValue["Description"] = resourceNotFoundMsg;
+            return;
+        }
+        // Extract the file name from the objectPath
+        std::string fileId = objectPath.substr(pos + strlen("ConfigFiles/"));
+        // Create the file
+        std::ofstream file;
+        std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
+        loc /= fileId;
+
+        std::string data = std::move(req.body);
+        BMCWEB_LOG_DEBUG << "data capaticty : " << data.capacity();
+        if (data.capacity() > MAX_SAVE_AREA_FILESIZE)
+        {
+            res.result(boost::beast::http::status::bad_request);
+            res.jsonValue["Description"] =
+                "File size exceeds 200KB. Maximum allowed size is 200KB";
+            return;
+        }
+
+        BMCWEB_LOG_DEBUG << "Creating file " << loc;
+        file.open(loc, std::ofstream::out);
+        if (file.fail())
+        {
+            BMCWEB_LOG_DEBUG << "Error while opening the file for writing";
+            res.result(boost::beast::http::status::internal_server_error);
+            res.jsonValue["Description"] = "Error while creating the file";
+            return;
+        }
+        else
+        {
+            file << data;
+            BMCWEB_LOG_DEBUG << "save-area file is created";
+            res.jsonValue["Description"] = "File Created";
+        }
+    }
+    else
+    {
+        BMCWEB_LOG_DEBUG << "Bad URI";
+        res.result(boost::beast::http::status::not_found);
+        res.jsonValue["Description"] = resourceNotFoundMsg;
+    }
+    return;
+}
+
+inline void handleFileUrl(const crow::Request &req, crow::Response &res,
+                          std::string &objectPath)
+{
+    std::string destProperty = "";
+    if (req.method() == "PUT"_method)
+    {
+        handleFilePut(req, res, objectPath, destProperty);
+        res.end();
+        return;
+    }
+    res.result(boost::beast::http::status::method_not_allowed);
+    res.jsonValue["Description"] = methodNotAllowedMsg;
+    res.end();
+}
 
 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
 {
@@ -28,6 +164,14 @@
                     {"@odata.id", "/ibm/v1/HMC/LockService"}};
                 res.end();
             });
+
+    BMCWEB_ROUTE(app, "/ibm/v1/Host/<path>")
+        .requires({"ConfigureComponents", "ConfigureManager"})
+        .methods("PUT"_method)([](const crow::Request &req, crow::Response &res,
+                                  const std::string &path) {
+            std::string objectPath = "/ibm/v1/Host/" + path;
+            handleFileUrl(req, res, objectPath);
+        });
 }
 
 } // namespace ibm_mc