summaryrefslogtreecommitdiff
path: root/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces
diff options
context:
space:
mode:
authoreportnov <eportnov@ibs.ru>2022-09-02 15:32:25 +0300
committereportnov <eportnov@ibs.ru>2022-09-02 15:33:40 +0300
commite48fffd96e3623adf0a3756aaf869970bdc936dd (patch)
tree6b2912b88bf4ac5c44b6783140d81382c43a13a6 /meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces
parent6c8befb66b45f29e8e287a249a2d1d2b22bebe8f (diff)
downloadopenbmc-e48fffd96e3623adf0a3756aaf869970bdc936dd.tar.xz
Add smtp client
Diffstat (limited to 'meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces')
-rw-r--r--meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/00010-Add-smtp-client.patch637
-rw-r--r--meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend1
2 files changed, 638 insertions, 0 deletions
diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/00010-Add-smtp-client.patch b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/00010-Add-smtp-client.patch
new file mode 100644
index 0000000000..7b0172c881
--- /dev/null
+++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb/00010-Add-smtp-client.patch
@@ -0,0 +1,637 @@
+From 77a6599736c27aae08f1b91f3b36c9b04808f563 Mon Sep 17 00:00:00 2001
+From: eportnov <eportnov@ibs.ru>
+Date: Fri, 2 Sep 2022 15:11:28 +0300
+Subject: [PATCH] Add smtp client
+
+---
+ meson.build | 4 +
+ redfish-core/include/redfish.hpp | 4 +-
+ .../include/registries/privilege_registry.hpp | 8 +
+ .../utils/smtp/change_parameters_parser.hpp | 27 +++
+ .../utils/smtp/send_message_parser.hpp | 29 +++
+ .../include/utils/smtp/types/iparser.hpp | 21 ++
+ redfish-core/lib/service_root.hpp | 1 +
+ redfish-core/lib/smtp.hpp | 191 ++++++++++++++++++
+ .../utils/smtp/change_parameters_parser.cpp | 62 ++++++
+ .../src/utils/smtp/send_message_parser.cpp | 76 +++++++
+ redfish-core/src/utils/smtp/types/iparser.cpp | 35 ++++
+ redfish-core/ut/smtp_reader_parser_test.cpp | 45 +++++
+ 12 files changed, 502 insertions(+), 1 deletion(-)
+ create mode 100644 redfish-core/include/utils/smtp/change_parameters_parser.hpp
+ create mode 100644 redfish-core/include/utils/smtp/send_message_parser.hpp
+ create mode 100644 redfish-core/include/utils/smtp/types/iparser.hpp
+ create mode 100644 redfish-core/lib/smtp.hpp
+ create mode 100644 redfish-core/src/utils/smtp/change_parameters_parser.cpp
+ create mode 100644 redfish-core/src/utils/smtp/send_message_parser.cpp
+ create mode 100644 redfish-core/src/utils/smtp/types/iparser.cpp
+ create mode 100644 redfish-core/ut/smtp_reader_parser_test.cpp
+
+diff --git a/meson.build b/meson.build
+index 45d440ef..3787e145 100644
+--- a/meson.build
++++ b/meson.build
+@@ -367,6 +367,9 @@ install_subdir('static', install_dir : 'share/www', strip_directory : true)
+ srcfiles_bmcweb = [
+ 'redfish-core/src/error_messages.cpp',
+ 'redfish-core/src/utils/json_utils.cpp',
++ 'redfish-core/src/utils/smtp/send_message_parser.cpp',
++ 'redfish-core/src/utils/smtp/change_parameters_parser.cpp',
++ 'redfish-core/src/utils/smtp/types/iparser.cpp',
+ 'src/boost_asio_ssl.cpp',
+ 'src/boost_asio.cpp',
+ 'src/boost_beast.cpp',
+@@ -409,6 +412,7 @@ srcfiles_unittest = [
+ 'redfish-core/ut/hex_utils_test.cpp',
+ 'redfish-core/ut/ip_utils_test.cpp',
+ 'redfish-core/ut/json_utils_test.cpp',
++ 'redfish-core/ut/smtp_reader_parser_test.cpp',
+ 'redfish-core/ut/lock_test.cpp',
+ 'redfish-core/ut/privileges_test.cpp',
+ 'redfish-core/ut/registries_test.cpp',
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index ab635f91..2854a745 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -47,6 +47,7 @@
+ #include "../lib/trigger.hpp"
+ #include "../lib/update_service.hpp"
+ #include "../lib/virtual_media.hpp"
++#include "../lib/smtp.hpp"
+
+ namespace redfish
+ {
+@@ -222,7 +223,8 @@ class RedfishService
+ requestRoutesMetricReport(app);
+ requestRoutesTriggerCollection(app);
+ requestRoutesTrigger(app);
+-
++ requestRoutesSmtp(app);
++ requestSmtpFunctions(app);
+ // Note, this must be the last route registered
+ requestRoutesRedfish(app);
+ }
+diff --git a/redfish-core/include/registries/privilege_registry.hpp b/redfish-core/include/registries/privilege_registry.hpp
+index 035aee4a..5ec62749 100644
+--- a/redfish-core/include/registries/privilege_registry.hpp
++++ b/redfish-core/include/registries/privilege_registry.hpp
+@@ -1363,6 +1363,14 @@ const static auto& postSimpleStorageCollection = privilegeSetConfigureComponents
+ const static auto& putSimpleStorageCollection = privilegeSetConfigureComponents;
+ const static auto& deleteSimpleStorageCollection = privilegeSetConfigureComponents;
+
++// SmtpCollection
++const static auto& getSmtpCollection = privilegeSetLogin;
++const static auto& headSmtpCollection = privilegeSetLogin;
++const static auto& patchSmtpCollection = privilegeSetConfigureComponents;
++const static auto& postSmtpCollection = privilegeSetConfigureComponents;
++const static auto& putSmtpCollection = privilegeSetConfigureComponents;
++const static auto& deleteSmtpCollection = privilegeSetConfigureComponents;
++
+ // SoftwareInventory
+ const static auto& getSoftwareInventory = privilegeSetLogin;
+ const static auto& headSoftwareInventory = privilegeSetLogin;
+diff --git a/redfish-core/include/utils/smtp/change_parameters_parser.hpp b/redfish-core/include/utils/smtp/change_parameters_parser.hpp
+new file mode 100644
+index 00000000..ee18b57b
+--- /dev/null
++++ b/redfish-core/include/utils/smtp/change_parameters_parser.hpp
+@@ -0,0 +1,27 @@
++#pragma once
++
++#include "types/iparser.hpp"
++
++namespace redfish::smtp::parser
++{
++ class ChangeParameters : public types::IParser
++ {
++ public:
++ explicit ChangeParameters( std::string const& function_with_parameters );
++ ~ChangeParameters() override = default;
++
++ std::string GetUser() const;
++ std::string GetPassword() const;
++ std::string GetHost() const;
++ std::string GetPort() const;
++
++ std::string GetMethodName() const override;
++ private:
++ void FillParams( types::ParserResult const& params );
++
++ std::string mUser;
++ std::string mPassword;
++ std::string mHost;
++ std::string mPort;
++ };
++}
+diff --git a/redfish-core/include/utils/smtp/send_message_parser.hpp b/redfish-core/include/utils/smtp/send_message_parser.hpp
+new file mode 100644
+index 00000000..8ae009cf
+--- /dev/null
++++ b/redfish-core/include/utils/smtp/send_message_parser.hpp
+@@ -0,0 +1,29 @@
++#pragma once
++
++#include "types/iparser.hpp"
++
++namespace redfish::smtp::parser
++{
++ class SendMessage : public types::IParser
++ {
++ public:
++ explicit SendMessage( std::string const& function_with_parameters );
++ ~SendMessage() override = default;
++
++ std::string GetFrom() const;
++ std::string GetTo() const;
++ std::list<std::string> GetCc() const;
++ std::string GetSubject() const;
++ std::string GetText() const;
++
++ std::string GetMethodName() const override;
++ private:
++ void FillParams( types::ParserResult const& params );
++
++ std::string mFrom;
++ std::string mTo;
++ std::list<std::string> mCc;
++ std::string mSubject;
++ std::string mText;
++ };
++}
+diff --git a/redfish-core/include/utils/smtp/types/iparser.hpp b/redfish-core/include/utils/smtp/types/iparser.hpp
+new file mode 100644
+index 00000000..0d9fe511
+--- /dev/null
++++ b/redfish-core/include/utils/smtp/types/iparser.hpp
+@@ -0,0 +1,21 @@
++#pragma once
++
++#include <string>
++#include <list>
++#include <unordered_map>
++
++namespace redfish::smtp::parser::types
++{
++ using ParserResult = std::unordered_multimap<std::string, std::string>;
++
++ class IParser
++ {
++ public:
++ virtual ~IParser() = default;
++
++ virtual std::string GetMethodName() const = 0;
++ protected:
++ std::list<std::string> ParseLine( std::string const& line, std::string const& delimiter );
++ ParserResult BuildParams(std::list<std::string> const& parsed_data);
++ };
++}
+diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp
+index 387b6141..aa97c91d 100644
+--- a/redfish-core/lib/service_root.hpp
++++ b/redfish-core/lib/service_root.hpp
+@@ -63,6 +63,7 @@ inline void
+ asyncResp->res.jsonValue["TelemetryService"]["@odata.id"] =
+ "/redfish/v1/TelemetryService";
+ asyncResp->res.jsonValue["Cables"]["@odata.id"] = "/redfish/v1/Cables";
++ asyncResp->res.jsonValue["Smtp"]["@odata.id"] = "/redfish/v1/Smtp";
+
+ nlohmann::json& protocolFeatures =
+ asyncResp->res.jsonValue["ProtocolFeaturesSupported"];
+diff --git a/redfish-core/lib/smtp.hpp b/redfish-core/lib/smtp.hpp
+new file mode 100644
+index 00000000..1bd798f7
+--- /dev/null
++++ b/redfish-core/lib/smtp.hpp
+@@ -0,0 +1,191 @@
++#include <app.hpp>
++#include <registries/privilege_registry.hpp>
++#include <query.hpp>
++
++#include <iostream>
++
++#include "utils/smtp/send_message_parser.hpp"
++#include "utils/smtp/change_parameters_parser.hpp"
++
++namespace redfish
++{
++ constexpr const char* SERVICE_PATH = "xyz.openbmc_project.SMTP";
++ constexpr const char* OBJECT_PATH = "/xyz/openbmc_project/SMTP";
++ constexpr const char* INTERFACE_PATH = "xyz.openbmc_project.SMTP";
++
++ namespace smtp
++ {
++ inline std::optional<std::string> GetFunctionName( std::string const& function_with_parameters )
++ {
++ static constexpr char FIRST_DELIMITER = '&';
++ static constexpr int ZERO_POSITION = 0;
++
++ auto position = function_with_parameters.find(FIRST_DELIMITER);
++ if( position == std::string::npos )
++ {
++ return {};
++ }
++ return function_with_parameters.substr( ZERO_POSITION, position );
++ }
++
++ inline void SendMail( std::string const& function_with_parameters, std::shared_ptr<bmcweb::AsyncResp> const& asyncResp )
++ {
++ auto parsing_result = parser::SendMessage{function_with_parameters};
++
++ crow::connections::systemBus->async_method_call([asyncResp](const boost::system::error_code& error_code, bool result){
++ if (error_code.value() == EBADR ||
++ error_code == boost::system::errc::host_unreachable)
++ {
++ BMCWEB_LOG_ERROR << "host unreachable " << error_code;
++ std::cout << "host unreachabler" << std::endl;
++ return;
++ }
++ if (error_code)
++ {
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << error_code;
++ std::cout << "respHandler DBus error" << std::endl;
++ return;
++ }
++ asyncResp->res.jsonValue["result"] = result;
++ },
++ SERVICE_PATH, OBJECT_PATH, INTERFACE_PATH,
++ parsing_result.GetMethodName(),
++ parsing_result.GetFrom(),
++ parsing_result.GetTo(),
++ parsing_result.GetCc(),
++ parsing_result.GetSubject(),
++ parsing_result.GetText());
++ }
++
++ inline void ChangeParameters( std::string const& function_with_parameters, std::shared_ptr<bmcweb::AsyncResp> const& asyncResp )
++ {
++ auto parsing_result = parser::ChangeParameters{function_with_parameters};
++
++ crow::connections::systemBus->async_method_call([asyncResp](const boost::system::error_code& error_code, bool result){
++ if (error_code.value() == EBADR ||
++ error_code == boost::system::errc::host_unreachable)
++ {
++ BMCWEB_LOG_ERROR << "host unreachable " << error_code;
++ std::cout << "host unreachable" << std::endl;
++ return;
++ }
++ if (error_code)
++ {
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << error_code;
++ std::cout << "respHandler DBus error" << std::endl;
++ return;
++ }
++ asyncResp->res.jsonValue["result"] = result;
++ },
++ SERVICE_PATH, OBJECT_PATH, INTERFACE_PATH,
++ parsing_result.GetMethodName(),
++ parsing_result.GetUser(),
++ parsing_result.GetPassword(),
++ parsing_result.GetHost(),
++ parsing_result.GetPort());
++ }
++ }
++
++ inline void FillResp(crow::Response& resp, const dbus::utility::DBusPropertiesMap& properties)
++ {
++ for (const auto& [propKey, propVariant] : properties)
++ {
++ if (propKey == "Host" )
++ {
++ auto host_as_string = std::get_if<std::string>(&propVariant);
++ if(host_as_string)
++ {
++ resp.jsonValue["host"] = *host_as_string;
++ }
++ }
++ if (propKey == "Port" )
++ {
++ auto port_as_string = std::get_if<std::string>(&propVariant);
++ if(port_as_string)
++ {
++ resp.jsonValue["port"] = *port_as_string;
++ }
++ }
++ if (propKey == "User" )
++ {
++ auto user_as_string = std::get_if<std::string>(&propVariant);
++ if(user_as_string)
++ {
++ resp.jsonValue["user"] = *user_as_string;
++ }
++ }
++ }
++ }
++
++ inline void requestRoutesSmtp(App& app)
++ {
++ BMCWEB_ROUTE(app, "/redfish/v1/Smtp/")
++ .privileges(redfish::privileges::getSmtpCollection)
++ .methods(boost::beast::http::verb::get)(
++ [&app](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
++ {
++ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
++ {
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call([asyncResp](const boost::system::error_code& error_code,
++ const dbus::utility::DBusPropertiesMap& properties)
++ {
++ if (error_code)
++ {
++ BMCWEB_LOG_DEBUG << "DBUS response error " << error_code;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ FillResp(asyncResp->res, properties);
++ }, SERVICE_PATH, OBJECT_PATH, "org.freedesktop.DBus.Properties",
++ "GetAll", "xyz.openbmc_project.SMTP");
++
++ asyncResp->res.jsonValue["@odata.type"] ="#SmtpCollection";
++ asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Smtp";
++ asyncResp->res.jsonValue["Name"] = "Smtp Collection";
++ });
++ }
++
++
++
++ inline void requestSmtpFunctions(App& app)
++ {
++ BMCWEB_ROUTE(app, "/redfish/v1/Smtp/<str>/")
++ .privileges(redfish::privileges::getSmtpCollection)
++ .methods(boost::beast::http::verb::get)(
++ [&app](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& function_with_parameters)
++ {
++ static constexpr std::string_view SEND_MAIL_METHOD_NAME = "SendMail";
++ static constexpr std::string_view CHANGE_PARAMETERS_METHOD_NAME = "ChangeParameters";
++
++ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
++ {
++ return;
++ }
++ auto function_name = smtp::GetFunctionName(function_with_parameters);
++ if(!function_name)
++ {
++ return;
++ }
++
++ asyncResp->res.jsonValue["@odata.type"] ="#SmtpCollection";
++ asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Smtp/" + *function_name;
++ asyncResp->res.jsonValue["Name"] = "Smtp Collection";
++
++ if(*function_name == SEND_MAIL_METHOD_NAME)
++ {
++ smtp::SendMail(function_with_parameters, asyncResp);
++ }
++ else if(*function_name == CHANGE_PARAMETERS_METHOD_NAME)
++ {
++ smtp::ChangeParameters(function_with_parameters, asyncResp);
++ }
++ });
++ }
++}
+diff --git a/redfish-core/src/utils/smtp/change_parameters_parser.cpp b/redfish-core/src/utils/smtp/change_parameters_parser.cpp
+new file mode 100644
+index 00000000..f78b9482
+--- /dev/null
++++ b/redfish-core/src/utils/smtp/change_parameters_parser.cpp
+@@ -0,0 +1,62 @@
++#include "utils/smtp/change_parameters_parser.hpp"
++
++namespace redfish::smtp::parser
++{
++ ChangeParameters::ChangeParameters( std::string const& function_with_parameters )
++ {
++ static const std::string DELIMITER_ON_LINE = "&";
++
++ auto params = BuildParams(ParseLine(function_with_parameters, DELIMITER_ON_LINE));
++ FillParams( params );
++ }
++
++ std::string ChangeParameters::GetUser() const
++ {
++ return mUser;
++ }
++ std::string ChangeParameters::GetPassword() const
++ {
++ return mPassword;
++ }
++ std::string ChangeParameters::GetHost() const
++ {
++ return mHost;
++ }
++ std::string ChangeParameters::GetPort() const
++ {
++ return mPort;
++ }
++
++ std::string ChangeParameters::GetMethodName() const
++ {
++ constexpr const char* CHANGE_METHOD_NAME = "ChangeParameters";
++ return CHANGE_METHOD_NAME;
++ }
++
++ void ChangeParameters::FillParams( types::ParserResult const& params )
++ {
++ auto user = params.find("user");
++ if(user != params.end())
++ {
++ mUser = (*user).second;
++ }
++
++ auto password = params.find("password");
++ if(password != params.end())
++ {
++ mPassword = (*password).second;
++ }
++
++ auto host = params.find("host");
++ if(host != params.end())
++ {
++ mHost = (*host).second;
++ }
++
++ auto port = params.find("port");
++ if(port != params.end())
++ {
++ mPort = (*port).second;
++ }
++ }
++}
+diff --git a/redfish-core/src/utils/smtp/send_message_parser.cpp b/redfish-core/src/utils/smtp/send_message_parser.cpp
+new file mode 100644
+index 00000000..0904a3e4
+--- /dev/null
++++ b/redfish-core/src/utils/smtp/send_message_parser.cpp
+@@ -0,0 +1,76 @@
++#include "utils/smtp/send_message_parser.hpp"
++
++namespace redfish::smtp::parser
++{
++ SendMessage::SendMessage( std::string const& function_with_parameters )
++ {
++ static const std::string DELIMITER_ON_LINE = "&";
++
++ auto params = BuildParams(ParseLine(function_with_parameters, DELIMITER_ON_LINE));
++ FillParams( params );
++ }
++
++ std::string SendMessage::GetFrom() const
++ {
++ return mFrom;
++ }
++
++ std::string SendMessage::GetTo() const
++ {
++ return mTo;
++ }
++
++ std::list<std::string> SendMessage::GetCc() const
++ {
++ return mCc;
++ }
++
++ std::string SendMessage::GetSubject() const
++ {
++ return mSubject;
++ }
++
++ std::string SendMessage::GetText() const
++ {
++ return mText;
++ }
++
++ std::string SendMessage::GetMethodName() const
++ {
++ constexpr const char* SEND_METHOD_NAME = "SendMail";
++ return SEND_METHOD_NAME;
++ }
++
++ void SendMessage::FillParams( types::ParserResult const& params )
++ {
++ auto from = params.find("from");
++ if(from != params.end())
++ {
++ mFrom = (*from).second;
++ }
++
++ auto to = params.find("to");
++ if(to != params.end())
++ {
++ mTo = (*to).second;
++ }
++
++ auto cc = params.equal_range("cc");
++ for(auto it = cc.first; it != cc.second; ++it)
++ {
++ mCc.push_back( (*it).second );
++ }
++
++ auto subject = params.find("subject");
++ if(subject != params.end())
++ {
++ mSubject = (*subject).second;
++ }
++
++ auto text = params.find("text");
++ if(text != params.end())
++ {
++ mText = (*text).second;
++ }
++ }
++}
+diff --git a/redfish-core/src/utils/smtp/types/iparser.cpp b/redfish-core/src/utils/smtp/types/iparser.cpp
+new file mode 100644
+index 00000000..ac95b967
+--- /dev/null
++++ b/redfish-core/src/utils/smtp/types/iparser.cpp
+@@ -0,0 +1,35 @@
++#include "utils/smtp/types/iparser.hpp"
++
++namespace redfish::smtp::parser::types
++{
++ std::list<std::string> IParser::ParseLine( std::string const& line, std::string const& delimiter )
++ {
++ std::list<std::string> result;
++
++ auto start = 0U;
++ auto end = line.find(delimiter);
++ while(end != std::string::npos)
++ {
++ auto parsed = line.substr(start, end - start);
++ result.push_back(parsed);
++ start = end + delimiter.length();
++ end = line.find(delimiter, start);
++ }
++ auto parsed = line.substr(start, end);
++ result.push_back(parsed);
++ return result;
++ }
++
++ ParserResult IParser::BuildParams(std::list<std::string> const& parsed_data)
++ {
++ static const std::string DELIMITER_ON_PARAMETER = "=";
++
++ std::unordered_multimap<std::string, std::string> result;
++ for(const auto& not_parsed_param : parsed_data)
++ {
++ auto param_pair = ParseLine( not_parsed_param, DELIMITER_ON_PARAMETER );
++ result.insert( {param_pair.front(), param_pair.back()} );
++ }
++ return result;
++ }
++}
+diff --git a/redfish-core/ut/smtp_reader_parser_test.cpp b/redfish-core/ut/smtp_reader_parser_test.cpp
+new file mode 100644
+index 00000000..67a6645c
+--- /dev/null
++++ b/redfish-core/ut/smtp_reader_parser_test.cpp
+@@ -0,0 +1,45 @@
++#include "utils/smtp/send_message_parser.hpp"
++#include "utils/smtp/change_parameters_parser.hpp"
++#include "smtp.hpp"
++
++#include <string>
++
++#include <gmock/gmock.h>
++#include <gtest/gtest.h>
++
++namespace redfish::json_util
++{
++namespace
++{
++
++using ::testing::ElementsAre;
++
++TEST(SmtpReaderParser, SendMessage)
++{
++ smtp::parser::SendMessage message_parser{"SendMail&from=claiff@mail.ru&to=claiff1990@gmail.com&cc=claiff@mail.ru&cc=claiff@mail.com&subject=theme&text=text"};
++ EXPECT_EQ(message_parser.GetFrom(), "claiff@mail.ru");
++ ASSERT_THAT(message_parser.GetCc(), ElementsAre("claiff@mail.com", "claiff@mail.ru"));
++ EXPECT_EQ(message_parser.GetTo(), "claiff1990@gmail.com");
++ EXPECT_EQ(message_parser.GetSubject(), "theme");
++ EXPECT_EQ(message_parser.GetText(), "text");
++ EXPECT_EQ(message_parser.GetMethodName(), "SendMail");
++}
++
++TEST(SmtpReaderParser, ChangeParameters)
++{
++ smtp::parser::ChangeParameters message_parser{"ChangeParameters&user=claiff@mail.ru&password=32311&host=smtp.mail.ru&port=465"};
++ EXPECT_EQ(message_parser.GetUser(), "claiff@mail.ru");
++ EXPECT_EQ(message_parser.GetPassword(), "32311");
++ EXPECT_EQ(message_parser.GetHost(), "smtp.mail.ru");
++ EXPECT_EQ(message_parser.GetPort(), "465");
++}
++
++TEST(SmtpReaderParser, GetFunctionName)
++{
++ static constexpr std::string_view MESSAGE = "ChangeParameters&user=claiff@mail.ru&password=32311&host=smtp.mail.ru&port=465";
++ auto result = smtp::GetFunctionName(MESSAGE.data());
++ EXPECT_EQ(result, "ChangeParameters");
++}
++
++} // namespace
++} // namespace redfish::json_util
diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend
index 1671177ac5..29a8481044 100644
--- a/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend
+++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/interfaces/bmcweb_%.bbappend
@@ -10,6 +10,7 @@ SRC_URI += "\
file://0007-Removed-non-working-boot-override-modes.patch \
file://0008-Add-pcie-device-names.patch \
file://0009-fix-circular-buffer-telemetry.patch \
+ file://00010-Add-smtp-client.patch \
"
EXTRA_OEMESON += "\