diff options
68 files changed, 16973 insertions, 13975 deletions
diff --git a/.clang-format b/.clang-format index b98e24fe47..dd27708378 100644 --- a/.clang-format +++ b/.clang-format @@ -1,97 +1,98 @@ --- -BasedOnStyle: Google -AccessModifierOffset: -1 +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: true +AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: true +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true IndentBraces: false BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach +BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: true +PointerAlignment: Left DisableFormat: false ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup IncludeCategories: - - Regex: '^[<"](crow)' - Priority: 5 - - Regex: '^[<"](boost)' - Priority: 6 - Regex: '^[<"](gtest|gmock)' - Priority: 7 - - Regex: '^<.*\.h>' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' Priority: 1 - - Regex: '^<.*\.hpp>' + - Regex: '^<.*\.h>' Priority: 2 - Regex: '^<.*' Priority: 3 - Regex: '.*' Priority: 4 IndentCaseLabels: true -IndentWidth: 2 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: false +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: false -PenaltyBreakBeforeFirstCallParameter: 1 +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 200 -PointerAlignment: Left +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right ReflowComments: true SortIncludes: true SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 +SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Auto -TabWidth: 8 +Standard: Cpp11 +TabWidth: 4 UseTab: Never ... - diff --git a/crow/include/crow.h b/crow/include/crow.h index edc0d7f924..7e31cf335c 100644 --- a/crow/include/crow.h +++ b/crow/include/crow.h @@ -1,4 +1,6 @@ #pragma once +#include "boost/beast/core.hpp" + #include "crow/app.h" #include "crow/common.h" #include "crow/http_connection.h" @@ -13,4 +15,3 @@ #include "crow/timer_queue.h" #include "crow/utility.h" #include "crow/websocket.h" -#include "boost/beast/core.hpp" diff --git a/crow/include/crow/app.h b/crow/include/crow/app.h index 78d99df0e1..7051ec869f 100644 --- a/crow/include/crow/app.h +++ b/crow/include/crow/app.h @@ -7,6 +7,7 @@ #include <memory> #include <string> #include <utility> + #include "crow/http_request.h" #include "crow/http_server.h" #include "crow/logging.h" @@ -14,198 +15,236 @@ #include "crow/routing.h" #include "crow/utility.h" -#define BMCWEB_ROUTE(app, url) \ - app.template route<crow::black_magic::get_parameter_tag(url)>(url) +#define BMCWEB_ROUTE(app, url) \ + app.template route<crow::black_magic::get_parameter_tag(url)>(url) -namespace crow { +namespace crow +{ #ifdef BMCWEB_ENABLE_SSL using ssl_context_t = boost::asio::ssl::context; #endif -template <typename... Middlewares> -class Crow { - public: - using self_t = Crow; - using server_t = Server<Crow, SocketAdaptor, Middlewares...>; +template <typename... Middlewares> class Crow +{ + public: + using self_t = Crow; + using server_t = Server<Crow, SocketAdaptor, Middlewares...>; #ifdef BMCWEB_ENABLE_SSL - using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>; + using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>; #endif - explicit Crow(std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : io(std::move(io)) {} - ~Crow() { this->stop(); } - - template <typename Adaptor> - void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) { - router.handleUpgrade(req, res, adaptor); - } - - void handle(const Request& req, Response& res) { router.handle(req, res); } - - DynamicRule& routeDynamic(std::string&& rule) { - return router.newRuleDynamic(rule); - } - - template <uint64_t Tag> - auto route(std::string&& rule) -> typename std::result_of< - decltype (&Router::newRuleTagged<Tag>)(Router, std::string&&)>::type { - return router.newRuleTagged<Tag>(std::move(rule)); - } - - self_t& socket(int existing_socket) { - socketFd = existing_socket; - return *this; - } - - self_t& port(std::uint16_t port) { - portUint = port; - return *this; - } - - self_t& bindaddr(std::string bindaddr) { - bindaddrStr = bindaddr; - return *this; - } - - void validate() { router.validate(); } - - void run() { - validate(); + explicit Crow(std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + io(std::move(io)) + { + } + ~Crow() + { + this->stop(); + } + + template <typename Adaptor> + void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) + { + router.handleUpgrade(req, res, adaptor); + } + + void handle(const Request& req, Response& res) + { + router.handle(req, res); + } + + DynamicRule& routeDynamic(std::string&& rule) + { + return router.newRuleDynamic(rule); + } + + template <uint64_t Tag> + auto route(std::string&& rule) -> typename std::result_of< + decltype (&Router::newRuleTagged<Tag>)(Router, std::string&&)>::type + { + return router.newRuleTagged<Tag>(std::move(rule)); + } + + self_t& socket(int existing_socket) + { + socketFd = existing_socket; + return *this; + } + + self_t& port(std::uint16_t port) + { + portUint = port; + return *this; + } + + self_t& bindaddr(std::string bindaddr) + { + bindaddrStr = bindaddr; + return *this; + } + + void validate() + { + router.validate(); + } + + void run() + { + validate(); #ifdef BMCWEB_ENABLE_SSL - if (useSsl) { - if (-1 == socketFd) { - sslServer = std::move(std::make_unique<ssl_server_t>( - this, bindaddrStr, portUint, &middlewares, &sslContext, io)); - } else { - sslServer = std::move(std::make_unique<ssl_server_t>( - this, socketFd, &middlewares, &sslContext, io)); - } - sslServer->setTickFunction(tickInterval, tickFunction); - sslServer->run(); - } else + if (useSsl) + { + if (-1 == socketFd) + { + sslServer = std::move(std::make_unique<ssl_server_t>( + this, bindaddrStr, portUint, &middlewares, &sslContext, + io)); + } + else + { + sslServer = std::move(std::make_unique<ssl_server_t>( + this, socketFd, &middlewares, &sslContext, io)); + } + sslServer->setTickFunction(tickInterval, tickFunction); + sslServer->run(); + } + else #endif + { + if (-1 == socketFd) + { + server = std::move(std::make_unique<server_t>( + this, bindaddrStr, portUint, &middlewares, nullptr, io)); + } + else + { + server = std::move(std::make_unique<server_t>( + this, socketFd, &middlewares, nullptr, io)); + } + server->setTickFunction(tickInterval, tickFunction); + server->run(); + } + } + + void stop() { - if (-1 == socketFd) { - server = std::move(std::make_unique<server_t>( - this, bindaddrStr, portUint, &middlewares, nullptr, io)); - } else { - server = std::move(std::make_unique<server_t>( - this, socketFd, &middlewares, nullptr, io)); - } - server->setTickFunction(tickInterval, tickFunction); - server->run(); - } - } - - void stop() { io->stop(); } - - void debugPrint() { - BMCWEB_LOG_DEBUG << "Routing:"; - router.debugPrint(); - } - - std::vector<const std::string*> getRoutes() { - // TODO(ed) Should this be /? - const std::string root(""); - return router.getRoutes(root); - } - std::vector<const std::string*> getRoutes(const std::string& parent) { - return router.getRoutes(parent); - } + io->stop(); + } + + void debugPrint() + { + BMCWEB_LOG_DEBUG << "Routing:"; + router.debugPrint(); + } + + std::vector<const std::string*> getRoutes() + { + // TODO(ed) Should this be /? + const std::string root(""); + return router.getRoutes(root); + } + std::vector<const std::string*> getRoutes(const std::string& parent) + { + return router.getRoutes(parent); + } #ifdef BMCWEB_ENABLE_SSL - self_t& sslFile(const std::string& crt_filename, - const std::string& key_filename) { - useSsl = true; - sslContext.set_verify_mode(boost::asio::ssl::verify_peer); - sslContext.use_certificate_file(crt_filename, ssl_context_t::pem); - sslContext.use_private_key_file(key_filename, ssl_context_t::pem); - sslContext.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3); - return *this; - } - - self_t& sslFile(const std::string& pem_filename) { - useSsl = true; - sslContext.set_verify_mode(boost::asio::ssl::verify_peer); - sslContext.load_verify_file(pem_filename); - sslContext.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3); - return *this; - } - - self_t& ssl(boost::asio::ssl::context&& ctx) { - useSsl = true; - sslContext = std::move(ctx); - return *this; - } - - bool useSsl{false}; - ssl_context_t sslContext{boost::asio::ssl::context::sslv23}; + self_t& sslFile(const std::string& crt_filename, + const std::string& key_filename) + { + useSsl = true; + sslContext.set_verify_mode(boost::asio::ssl::verify_peer); + sslContext.use_certificate_file(crt_filename, ssl_context_t::pem); + sslContext.use_private_key_file(key_filename, ssl_context_t::pem); + sslContext.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3); + return *this; + } + + self_t& sslFile(const std::string& pem_filename) + { + useSsl = true; + sslContext.set_verify_mode(boost::asio::ssl::verify_peer); + sslContext.load_verify_file(pem_filename); + sslContext.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3); + return *this; + } + + self_t& ssl(boost::asio::ssl::context&& ctx) + { + useSsl = true; + sslContext = std::move(ctx); + return *this; + } + + bool useSsl{false}; + ssl_context_t sslContext{boost::asio::ssl::context::sslv23}; #else - template <typename T, typename... Remain> - self_t& ssl_file(T&&, Remain&&...) { - // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is defined. - static_assert( - // make static_assert dependent to T; always false - std::is_base_of<T, void>::value, - "Define BMCWEB_ENABLE_SSL to enable ssl support."); - return *this; - } - - template <typename T> - self_t& ssl(T&&) { - // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is defined. - static_assert( - // make static_assert dependent to T; always false - std::is_base_of<T, void>::value, - "Define BMCWEB_ENABLE_SSL to enable ssl support."); - return *this; - } + template <typename T, typename... Remain> self_t& ssl_file(T&&, Remain&&...) + { + // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is + // defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of<T, void>::value, + "Define BMCWEB_ENABLE_SSL to enable ssl support."); + return *this; + } + + template <typename T> self_t& ssl(T&&) + { + // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is + // defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of<T, void>::value, + "Define BMCWEB_ENABLE_SSL to enable ssl support."); + return *this; + } #endif - // middleware - using context_t = detail::Context<Middlewares...>; - template <typename T> - typename T::Context& getContext(const Request& req) { - static_assert(black_magic::Contains<T, Middlewares...>::value, - "App doesn't have the specified middleware type."); - auto& ctx = *reinterpret_cast<context_t*>(req.middlewareContext); - return ctx.template get<T>(); - } - - template <typename T> - T& getMiddleware() { - return utility::getElementByType<T, Middlewares...>(middlewares); - } - - template <typename Duration, typename Func> - self_t& tick(Duration d, Func f) { - tickInterval = std::chrono::duration_cast<std::chrono::milliseconds>(d); - tickFunction = f; - return *this; - } - - private: - std::shared_ptr<asio::io_service> io; - uint16_t portUint = 80; - std::string bindaddrStr = "::"; - int socketFd = -1; - Router router; - - std::chrono::milliseconds tickInterval{}; - std::function<void()> tickFunction; - - std::tuple<Middlewares...> middlewares; + // middleware + using context_t = detail::Context<Middlewares...>; + template <typename T> typename T::Context& getContext(const Request& req) + { + static_assert(black_magic::Contains<T, Middlewares...>::value, + "App doesn't have the specified middleware type."); + auto& ctx = *reinterpret_cast<context_t*>(req.middlewareContext); + return ctx.template get<T>(); + } + + template <typename T> T& getMiddleware() + { + return utility::getElementByType<T, Middlewares...>(middlewares); + } + + template <typename Duration, typename Func> self_t& tick(Duration d, Func f) + { + tickInterval = std::chrono::duration_cast<std::chrono::milliseconds>(d); + tickFunction = f; + return *this; + } + + private: + std::shared_ptr<asio::io_service> io; + uint16_t portUint = 80; + std::string bindaddrStr = "::"; + int socketFd = -1; + Router router; + + std::chrono::milliseconds tickInterval{}; + std::function<void()> tickFunction; + + std::tuple<Middlewares...> middlewares; #ifdef BMCWEB_ENABLE_SSL - std::unique_ptr<ssl_server_t> sslServer; + std::unique_ptr<ssl_server_t> sslServer; #endif - std::unique_ptr<server_t> server; + std::unique_ptr<server_t> server; }; -template <typename... Middlewares> -using App = Crow<Middlewares...>; +template <typename... Middlewares> using App = Crow<Middlewares...>; using SimpleApp = Crow<>; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/common.h b/crow/include/crow/common.h index e155da6da9..b3e424f994 100644 --- a/crow/include/crow/common.h +++ b/crow/include/crow/common.h @@ -1,127 +1,140 @@ #pragma once +#include <boost/beast/http/verb.hpp> #include <iostream> #include <stdexcept> #include <string> #include <vector> + #include "crow/utility.h" -#include <boost/beast/http/verb.hpp> -namespace crow { -enum class HTTPMethod { +namespace crow +{ +enum class HTTPMethod +{ #ifndef DELETE - DELETE = 0, - GET, - HEAD, - POST, - PUT, - CONNECT, - OPTIONS, - TRACE, - PATCH = 24, // see http_parser_merged.h line 118 for why it is 24 + DELETE = 0, + GET, + HEAD, + POST, + PUT, + CONNECT, + OPTIONS, + TRACE, + PATCH = 24, // see http_parser_merged.h line 118 for why it is 24 #endif - Delete = 0, - Get, - Head, - Post, - Put, - Connect, - Options, - Trace, - Patch = 24, + Delete = 0, + Get, + Head, + Post, + Put, + Connect, + Options, + Trace, + Patch = 24, }; -inline std::string methodName(boost::beast::http::verb method) { - switch (method) { - case boost::beast::http::verb::delete_: - return "DELETE"; - case boost::beast::http::verb::get: - return "GET"; - case boost::beast::http::verb::head: - return "HEAD"; - case boost::beast::http::verb::post: - return "POST"; - case boost::beast::http::verb::put: - return "PUT"; - case boost::beast::http::verb::connect: - return "CONNECT"; - case boost::beast::http::verb::options: - return "OPTIONS"; - case boost::beast::http::verb::trace: - return "TRACE"; - case boost::beast::http::verb::patch: - return "PATCH"; - } - return "invalid"; +inline std::string methodName(boost::beast::http::verb method) +{ + switch (method) + { + case boost::beast::http::verb::delete_: + return "DELETE"; + case boost::beast::http::verb::get: + return "GET"; + case boost::beast::http::verb::head: + return "HEAD"; + case boost::beast::http::verb::post: + return "POST"; + case boost::beast::http::verb::put: + return "PUT"; + case boost::beast::http::verb::connect: + return "CONNECT"; + case boost::beast::http::verb::options: + return "OPTIONS"; + case boost::beast::http::verb::trace: + return "TRACE"; + case boost::beast::http::verb::patch: + return "PATCH"; + } + return "invalid"; } -enum class ParamType { - INT, - UINT, - DOUBLE, - STRING, - PATH, +enum class ParamType +{ + INT, + UINT, + DOUBLE, + STRING, + PATH, - MAX + MAX }; -struct RoutingParams { - std::vector<int64_t> intParams; - std::vector<uint64_t> uintParams; - std::vector<double> doubleParams; - std::vector<std::string> stringParams; +struct RoutingParams +{ + std::vector<int64_t> intParams; + std::vector<uint64_t> uintParams; + std::vector<double> doubleParams; + std::vector<std::string> stringParams; - void debugPrint() const { - std::cerr << "RoutingParams" << std::endl; - for (auto i : intParams) { - std::cerr << i << ", "; - } - std::cerr << std::endl; - for (auto i : uintParams) { - std::cerr << i << ", "; + void debugPrint() const + { + std::cerr << "RoutingParams" << std::endl; + for (auto i : intParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; + for (auto i : uintParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; + for (auto i : doubleParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; + for (auto& i : stringParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; } - std::cerr << std::endl; - for (auto i : doubleParams) { - std::cerr << i << ", "; - } - std::cerr << std::endl; - for (auto& i : stringParams) { - std::cerr << i << ", "; - } - std::cerr << std::endl; - } - template <typename T> - T get(unsigned) const; + template <typename T> T get(unsigned) const; }; -template <> -inline int64_t RoutingParams::get<int64_t>(unsigned index) const { - return intParams[index]; +template <> inline int64_t RoutingParams::get<int64_t>(unsigned index) const +{ + return intParams[index]; } -template <> -inline uint64_t RoutingParams::get<uint64_t>(unsigned index) const { - return uintParams[index]; +template <> inline uint64_t RoutingParams::get<uint64_t>(unsigned index) const +{ + return uintParams[index]; } -template <> -inline double RoutingParams::get<double>(unsigned index) const { - return doubleParams[index]; +template <> inline double RoutingParams::get<double>(unsigned index) const +{ + return doubleParams[index]; } template <> -inline std::string RoutingParams::get<std::string>(unsigned index) const { - return stringParams[index]; +inline std::string RoutingParams::get<std::string>(unsigned index) const +{ + return stringParams[index]; } -} // namespace crow +} // namespace crow constexpr boost::beast::http::verb operator"" _method(const char* str, - size_t /*len*/) { - using verb = boost::beast::http::verb; - // clang-format off + size_t /*len*/) +{ + using verb = boost::beast::http::verb; + // clang-format off return crow::black_magic::isEquP(str, "GET", 3) ? verb::get : crow::black_magic::isEquP(str, "DELETE", 6) ? verb::delete_ : @@ -134,5 +147,5 @@ constexpr boost::beast::http::verb operator"" _method(const char* str, crow::black_magic::isEquP(str, "PATCH", 5) ? verb::patch : crow::black_magic::isEquP(str, "PURGE", 5) ? verb::purge : throw std::runtime_error("invalid http method"); - // clang-format on + // clang-format on }
\ No newline at end of file diff --git a/crow/include/crow/http_connection.h b/crow/include/crow/http_connection.h index 5f2f719a9b..6d62c85bc4 100644 --- a/crow/include/crow/http_connection.h +++ b/crow/include/crow/http_connection.h @@ -1,249 +1,279 @@ #pragma once +#include "http_utility.hpp" + #include <array> #include <atomic> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/asio.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/websocket.hpp> #include <chrono> #include <regex> #include <vector> -#include "http_utility.hpp" + #include "crow/http_response.h" #include "crow/logging.h" #include "crow/middleware_context.h" #include "crow/socket_adaptors.h" #include "crow/timer_queue.h" -#include <boost/algorithm/string.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/asio.hpp> -#include <boost/beast/core.hpp> -#include <boost/beast/http.hpp> -#include <boost/beast/websocket.hpp> #ifdef BMCWEB_ENABLE_SSL #include <boost/asio/ssl.hpp> #endif -namespace crow { - -inline void escapeHtml(std::string& data) { - std::string buffer; - // less than 5% of characters should be larger, so reserve a buffer of the - // right size - buffer.reserve(data.size() * 1.05); - for (size_t pos = 0; pos != data.size(); ++pos) { - switch (data[pos]) { - case '&': - buffer.append("&"); - break; - case '\"': - buffer.append("""); - break; - case '\'': - buffer.append("'"); - break; - case '<': - buffer.append("<"); - break; - case '>': - buffer.append(">"); - break; - default: - buffer.append(&data[pos], 1); - break; +namespace crow +{ + +inline void escapeHtml(std::string& data) +{ + std::string buffer; + // less than 5% of characters should be larger, so reserve a buffer of the + // right size + buffer.reserve(data.size() * 1.05); + for (size_t pos = 0; pos != data.size(); ++pos) + { + switch (data[pos]) + { + case '&': + buffer.append("&"); + break; + case '\"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + break; + default: + buffer.append(&data[pos], 1); + break; + } } - } - data.swap(buffer); + data.swap(buffer); } -inline void convertToLinks(std::string& s) { - const static std::regex r{ - "("@odata\\.((id)|(Context))"[ \\n]*:[ " - "\\n]*)("((?!").*)")"}; - s = std::regex_replace(s, r, "$1<a href=\"$6\">$5</a>"); +inline void convertToLinks(std::string& s) +{ + const static std::regex r{"("@odata\\.((id)|(Context))"[ \\n]*:[ " + "\\n]*)("((?!").*)")"}; + s = std::regex_replace(s, r, "$1<a href=\"$6\">$5</a>"); } -inline void prettyPrintJson(crow::Response& res) { - std::string value = res.jsonValue.dump(4); - escapeHtml(value); - convertToLinks(value); - res.body() = - "<html>\n" - "<head>\n" - "<title>Redfish API</title>\n" - "<link rel=\"stylesheet\" type=\"text/css\" " - "href=\"/styles/default.css\">\n" - "<script src=\"/highlight.pack.js\"></script>" - "<script>hljs.initHighlightingOnLoad();</script>" - "</head>\n" - "<body>\n" - "<div style=\"max-width: 576px;margin:0 auto;\">\n" - "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" " - "height=\"406px\" " - "width=\"576px\">\n" - "<br>\n" - "<pre>\n" - "<code class=\"json\">" + - value + - "</code>\n" - "</pre>\n" - "</div>\n" - "</body>\n" - "</html>\n"; +inline void prettyPrintJson(crow::Response& res) +{ + std::string value = res.jsonValue.dump(4); + escapeHtml(value); + convertToLinks(value); + res.body() = "<html>\n" + "<head>\n" + "<title>Redfish API</title>\n" + "<link rel=\"stylesheet\" type=\"text/css\" " + "href=\"/styles/default.css\">\n" + "<script src=\"/highlight.pack.js\"></script>" + "<script>hljs.initHighlightingOnLoad();</script>" + "</head>\n" + "<body>\n" + "<div style=\"max-width: 576px;margin:0 auto;\">\n" + "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" " + "height=\"406px\" " + "width=\"576px\">\n" + "<br>\n" + "<pre>\n" + "<code class=\"json\">" + + value + + "</code>\n" + "</pre>\n" + "</div>\n" + "</body>\n" + "</html>\n"; } using namespace boost; using tcp = asio::ip::tcp; -namespace detail { -template <typename MW> -struct CheckBeforeHandleArity3Const { - template <typename T, void (T::*)(Request&, Response&, typename MW::Context&) - const = &T::beforeHandle> - struct Get {}; +namespace detail +{ +template <typename MW> struct CheckBeforeHandleArity3Const +{ + template <typename T, + void (T::*)(Request&, Response&, typename MW::Context&) const = + &T::beforeHandle> + struct Get + { + }; }; -template <typename MW> -struct CheckBeforeHandleArity3 { - template <typename T, void (T::*)(Request&, Response&, - typename MW::Context&) = &T::beforeHandle> - struct Get {}; +template <typename MW> struct CheckBeforeHandleArity3 +{ + template <typename T, void (T::*)(Request&, Response&, + typename MW::Context&) = &T::beforeHandle> + struct Get + { + }; }; -template <typename MW> -struct CheckAfterHandleArity3Const { - template <typename T, void (T::*)(Request&, Response&, typename MW::Context&) - const = &T::afterHandle> - struct Get {}; +template <typename MW> struct CheckAfterHandleArity3Const +{ + template <typename T, + void (T::*)(Request&, Response&, typename MW::Context&) const = + &T::afterHandle> + struct Get + { + }; }; -template <typename MW> -struct CheckAfterHandleArity3 { - template <typename T, void (T::*)(Request&, Response&, - typename MW::Context&) = &T::afterHandle> - struct Get {}; +template <typename MW> struct CheckAfterHandleArity3 +{ + template <typename T, void (T::*)(Request&, Response&, + typename MW::Context&) = &T::afterHandle> + struct Get + { + }; }; -template <typename T> -struct IsBeforeHandleArity3Impl { - template <typename C> - static std::true_type f( - typename CheckBeforeHandleArity3Const<T>::template Get<C>*); +template <typename T> struct IsBeforeHandleArity3Impl +{ + template <typename C> + static std::true_type + f(typename CheckBeforeHandleArity3Const<T>::template Get<C>*); - template <typename C> - static std::true_type f( - typename CheckBeforeHandleArity3<T>::template Get<C>*); + template <typename C> + static std::true_type + f(typename CheckBeforeHandleArity3<T>::template Get<C>*); - template <typename C> - static std::false_type f(...); + template <typename C> static std::false_type f(...); - public: - static const bool value = decltype(f<T>(nullptr))::value; + public: + static const bool value = decltype(f<T>(nullptr))::value; }; -template <typename T> -struct IsAfterHandleArity3Impl { - template <typename C> - static std::true_type f( - typename CheckAfterHandleArity3Const<T>::template Get<C>*); +template <typename T> struct IsAfterHandleArity3Impl +{ + template <typename C> + static std::true_type + f(typename CheckAfterHandleArity3Const<T>::template Get<C>*); - template <typename C> - static std::true_type f(typename CheckAfterHandleArity3<T>::template Get<C>*); + template <typename C> + static std::true_type + f(typename CheckAfterHandleArity3<T>::template Get<C>*); - template <typename C> - static std::false_type f(...); + template <typename C> static std::false_type f(...); - public: - static const bool value = decltype(f<T>(nullptr))::value; + public: + static const bool value = decltype(f<T>(nullptr))::value; }; template <typename MW, typename Context, typename ParentContext> typename std::enable_if<!IsBeforeHandleArity3Impl<MW>::value>::type -beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.beforeHandle(req, res, ctx.template get<MW>(), ctx); + beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.beforeHandle(req, res, ctx.template get<MW>(), ctx); } template <typename MW, typename Context, typename ParentContext> typename std::enable_if<IsBeforeHandleArity3Impl<MW>::value>::type -beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.beforeHandle(req, res, ctx.template get<MW>()); + beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.beforeHandle(req, res, ctx.template get<MW>()); } template <typename MW, typename Context, typename ParentContext> typename std::enable_if<!IsAfterHandleArity3Impl<MW>::value>::type -afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.afterHandle(req, res, ctx.template get<MW>(), ctx); + afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.afterHandle(req, res, ctx.template get<MW>(), ctx); } template <typename MW, typename Context, typename ParentContext> typename std::enable_if<IsAfterHandleArity3Impl<MW>::value>::type -afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.afterHandle(req, res, ctx.template get<MW>()); + afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.afterHandle(req, res, ctx.template get<MW>()); } template <int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares> bool middlewareCallHelper(Container& middlewares, Request& req, Response& res, - Context& ctx) { - using parent_context_t = typename Context::template partial<N - 1>; - beforeHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); - - if (res.isCompleted()) { - afterHandlerCall<CurrentMW, Context, parent_context_t>( + Context& ctx) +{ + using parent_context_t = typename Context::template partial<N - 1>; + beforeHandlerCall<CurrentMW, Context, parent_context_t>( std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - return true; - } - if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>( - middlewares, req, res, ctx)) { - afterHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); - return true; - } + if (res.isCompleted()) + { + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); + return true; + } + + if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>( + middlewares, req, res, ctx)) + { + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); + return true; + } - return false; + return false; } template <int N, typename Context, typename Container> bool middlewareCallHelper(Container& /*middlewares*/, Request& /*req*/, - Response& /*res*/, Context& /*ctx*/) { - return false; + Response& /*res*/, Context& /*ctx*/) +{ + return false; } template <int N, typename Context, typename Container> -typename std::enable_if<(N < 0)>::type afterHandlersCallHelper( - Container& /*middlewares*/, Context& /*Context*/, Request& /*req*/, - Response& /*res*/) {} +typename std::enable_if<(N < 0)>::type + afterHandlersCallHelper(Container& /*middlewares*/, Context& /*Context*/, + Request& /*req*/, Response& /*res*/) +{ +} template <int N, typename Context, typename Container> -typename std::enable_if<(N == 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res) { - using parent_context_t = typename Context::template partial<N - 1>; - using CurrentMW = typename std::tuple_element< - N, typename std::remove_reference<Container>::type>::type; - afterHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); +typename std::enable_if<(N == 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req, + Response& res) +{ + using parent_context_t = typename Context::template partial<N - 1>; + using CurrentMW = typename std::tuple_element< + N, typename std::remove_reference<Container>::type>::type; + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); } template <int N, typename Context, typename Container> -typename std::enable_if<(N > 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res) { - using parent_context_t = typename Context::template partial<N - 1>; - using CurrentMW = typename std::tuple_element< - N, typename std::remove_reference<Container>::type>::type; - afterHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); - afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req, - res); +typename std::enable_if<(N > 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req, + Response& res) +{ + using parent_context_t = typename Context::template partial<N - 1>; + using CurrentMW = typename std::tuple_element< + N, typename std::remove_reference<Container>::type>::type; + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); + afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req, + res); } -} // namespace detail +} // namespace detail #ifdef BMCWEB_ENABLE_DEBUG static std::atomic<int> connectionCount; @@ -253,331 +283,382 @@ static std::atomic<int> connectionCount; constexpr unsigned int httpReqBodyLimit = 1024 * 1024 * 30; template <typename Adaptor, typename Handler, typename... Middlewares> -class Connection { - public: - Connection(boost::asio::io_service& ioService, Handler* handler, - const std::string& server_name, - std::tuple<Middlewares...>* middlewares, - std::function<std::string()>& get_cached_date_str_f, - detail::TimerQueue& timerQueue, - typename Adaptor::context* adaptorCtx) - : adaptor(ioService, adaptorCtx), - handler(handler), - serverName(server_name), - middlewares(middlewares), - getCachedDateStr(get_cached_date_str_f), - timerQueue(timerQueue) { - parser.emplace(std::piecewise_construct, std::make_tuple()); - // Temporarily changed to 30MB; Need to modify uploading/authentication - // mechanism - parser->body_limit(httpReqBodyLimit); - req.emplace(parser->get()); +class Connection +{ + public: + Connection(boost::asio::io_service& ioService, Handler* handler, + const std::string& server_name, + std::tuple<Middlewares...>* middlewares, + std::function<std::string()>& get_cached_date_str_f, + detail::TimerQueue& timerQueue, + typename Adaptor::context* adaptorCtx) : + adaptor(ioService, adaptorCtx), + handler(handler), serverName(server_name), middlewares(middlewares), + getCachedDateStr(get_cached_date_str_f), timerQueue(timerQueue) + { + parser.emplace(std::piecewise_construct, std::make_tuple()); + // Temporarily changed to 30MB; Need to modify uploading/authentication + // mechanism + parser->body_limit(httpReqBodyLimit); + req.emplace(parser->get()); #ifdef BMCWEB_ENABLE_DEBUG - connectionCount++; - BMCWEB_LOG_DEBUG << this << " Connection open, total " << connectionCount; + connectionCount++; + BMCWEB_LOG_DEBUG << this << " Connection open, total " + << connectionCount; #endif - } + } - ~Connection() { - res.completeRequestHandler = nullptr; - cancelDeadlineTimer(); + ~Connection() + { + res.completeRequestHandler = nullptr; + cancelDeadlineTimer(); #ifdef BMCWEB_ENABLE_DEBUG - connectionCount--; - BMCWEB_LOG_DEBUG << this << " Connection closed, total " << connectionCount; + connectionCount--; + BMCWEB_LOG_DEBUG << this << " Connection closed, total " + << connectionCount; #endif - } - - decltype(std::declval<Adaptor>().rawSocket()) & socket() { - return adaptor.rawSocket(); - } - - void start() { - adaptor.start([this](const boost::system::error_code& ec) { - if (!ec) { - startDeadline(); - - doReadHeaders(); - } else { - checkDestroy(); - } - }); - } - - void handle() { - cancelDeadlineTimer(); - bool isInvalidRequest = false; - const boost::string_view connection = - req->getHeaderValue(boost::beast::http::field::connection); - - // Check for HTTP version 1.1. - if (req->version() == 11) { - if (req->getHeaderValue(boost::beast::http::field::host).empty()) { - isInvalidRequest = true; - res = Response(boost::beast::http::status::bad_request); - } } - BMCWEB_LOG_INFO << "Request: " << adaptor.remoteEndpoint() << " " << this - << " HTTP/" << req->version() / 10 << "." - << req->version() % 10 << ' ' << req->methodString() << " " - << req->target(); - - needToCallAfterHandlers = false; - - if (!isInvalidRequest) { - res.completeRequestHandler = [] {}; - res.isAliveHelper = [this]() -> bool { return adaptor.isOpen(); }; - - ctx = detail::Context<Middlewares...>(); - req->middlewareContext = (void*)&ctx; - req->ioService = &adaptor.getIoService(); - detail::middlewareCallHelper<0, decltype(ctx), decltype(*middlewares), - Middlewares...>(*middlewares, *req, res, - ctx); - - if (!res.completed) { - if (req->isUpgrade() && - boost::iequals( - req->getHeaderValue(boost::beast::http::field::upgrade), - "websocket")) { - handler->handleUpgrade(*req, res, std::move(adaptor)); - return; + decltype(std::declval<Adaptor>().rawSocket())& socket() + { + return adaptor.rawSocket(); + } + + void start() + { + adaptor.start([this](const boost::system::error_code& ec) { + if (!ec) + { + startDeadline(); + + doReadHeaders(); + } + else + { + checkDestroy(); + } + }); + } + + void handle() + { + cancelDeadlineTimer(); + bool isInvalidRequest = false; + const boost::string_view connection = + req->getHeaderValue(boost::beast::http::field::connection); + + // Check for HTTP version 1.1. + if (req->version() == 11) + { + if (req->getHeaderValue(boost::beast::http::field::host).empty()) + { + isInvalidRequest = true; + res = Response(boost::beast::http::status::bad_request); + } } - res.completeRequestHandler = [this] { this->completeRequest(); }; - needToCallAfterHandlers = true; - handler->handle(*req, res); - if (req->keepAlive()) { - res.addHeader("connection", "Keep-Alive"); + + BMCWEB_LOG_INFO << "Request: " << adaptor.remoteEndpoint() << " " + << this << " HTTP/" << req->version() / 10 << "." + << req->version() % 10 << ' ' << req->methodString() + << " " << req->target(); + + needToCallAfterHandlers = false; + + if (!isInvalidRequest) + { + res.completeRequestHandler = [] {}; + res.isAliveHelper = [this]() -> bool { return adaptor.isOpen(); }; + + ctx = detail::Context<Middlewares...>(); + req->middlewareContext = (void*)&ctx; + req->ioService = &adaptor.getIoService(); + detail::middlewareCallHelper< + 0, decltype(ctx), decltype(*middlewares), Middlewares...>( + *middlewares, *req, res, ctx); + + if (!res.completed) + { + if (req->isUpgrade() && + boost::iequals( + req->getHeaderValue(boost::beast::http::field::upgrade), + "websocket")) + { + handler->handleUpgrade(*req, res, std::move(adaptor)); + return; + } + res.completeRequestHandler = [this] { + this->completeRequest(); + }; + needToCallAfterHandlers = true; + handler->handle(*req, res); + if (req->keepAlive()) + { + res.addHeader("connection", "Keep-Alive"); + } + } + else + { + completeRequest(); + } + } + else + { + completeRequest(); } - } else { - completeRequest(); - } - } else { - completeRequest(); } - } - void completeRequest() { - BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' ' - << res.resultInt() << " keepalive=" << req->keepAlive(); + void completeRequest() + { + BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' ' + << res.resultInt() << " keepalive=" << req->keepAlive(); + + if (needToCallAfterHandlers) + { + needToCallAfterHandlers = false; - if (needToCallAfterHandlers) { - needToCallAfterHandlers = false; + // call all afterHandler of middlewares + detail::afterHandlersCallHelper<((int)sizeof...(Middlewares) - 1), + decltype(ctx), + decltype(*middlewares)>( + *middlewares, ctx, *req, res); + } - // call all afterHandler of middlewares - detail::afterHandlersCallHelper<((int)sizeof...(Middlewares) - 1), - decltype(ctx), decltype(*middlewares)>( - *middlewares, ctx, *req, res); + // auto self = this->shared_from_this(); + res.completeRequestHandler = nullptr; + + if (!adaptor.isOpen()) + { + // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " << + // isReading + // << ' ' << isWriting; + // delete this; + return; + } + if (res.body().empty() && !res.jsonValue.empty()) + { + if (http_helpers::requestPrefersHtml(*req)) + { + prettyPrintJson(res); + } + else + { + res.jsonMode(); + res.body() = res.jsonValue.dump(2); + } + } + + if (res.resultInt() >= 400 && res.body().empty()) + { + res.body() = std::string(res.reason()); + } + res.addHeader(boost::beast::http::field::server, serverName); + res.addHeader(boost::beast::http::field::date, getCachedDateStr()); + + res.keepAlive(req->keepAlive()); + + doWrite(); } - // auto self = this->shared_from_this(); - res.completeRequestHandler = nullptr; + private: + void doReadHeaders() + { + // auto self = this->shared_from_this(); + isReading = true; + BMCWEB_LOG_DEBUG << this << " doReadHeaders"; + + // Clean up any previous Connection. + boost::beast::http::async_read_header( + adaptor.socket(), buffer, *parser, + [this](const boost::system::error_code& ec, + std::size_t bytes_transferred) { + isReading = false; + BMCWEB_LOG_ERROR << this << " async_read_header " + << bytes_transferred << " Bytes"; + bool errorWhileReading = false; + if (ec) + { + errorWhileReading = true; + BMCWEB_LOG_ERROR + << this << " Error while reading: " << ec.message(); + } + else + { + // if the adaptor isn't open anymore, and wasn't handed to a + // websocket, treat as an error + if (!adaptor.isOpen() && !req->isUpgrade()) + { + errorWhileReading = true; + } + } + + if (errorWhileReading) + { + cancelDeadlineTimer(); + adaptor.close(); + BMCWEB_LOG_DEBUG << this << " from read(1)"; + checkDestroy(); + return; + } + + // Compute the url parameters for the request + req->url = req->target(); + std::size_t index = req->url.find("?"); + if (index != boost::string_view::npos) + { + req->url = req->url.substr(0, index - 1); + } + req->urlParams = QueryString(std::string(req->target())); + doRead(); + }); + } - if (!adaptor.isOpen()) { - // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " << isReading - // << ' ' << isWriting; - // delete this; - return; + void doRead() + { + // auto self = this->shared_from_this(); + isReading = true; + BMCWEB_LOG_DEBUG << this << " doRead"; + + boost::beast::http::async_read( + adaptor.socket(), buffer, *parser, + [this](const boost::system::error_code& ec, + std::size_t bytes_transferred) { + BMCWEB_LOG_ERROR << this << " async_read " << bytes_transferred + << " Bytes"; + isReading = false; + + bool errorWhileReading = false; + if (ec) + { + BMCWEB_LOG_ERROR << "Error while reading: " << ec.message(); + errorWhileReading = true; + } + else + { + if (!adaptor.isOpen()) + { + errorWhileReading = true; + } + } + if (errorWhileReading) + { + cancelDeadlineTimer(); + adaptor.close(); + BMCWEB_LOG_DEBUG << this << " from read(1)"; + checkDestroy(); + return; + } + handle(); + }); } - if (res.body().empty() && !res.jsonValue.empty()) { - if (http_helpers::requestPrefersHtml(*req)) { - prettyPrintJson(res); - } else { - res.jsonMode(); - res.body() = res.jsonValue.dump(2); - } + + void doWrite() + { + // auto self = this->shared_from_this(); + isWriting = true; + BMCWEB_LOG_DEBUG << "Doing Write"; + res.preparePayload(); + serializer.emplace(*res.stringResponse); + boost::beast::http::async_write( + adaptor.socket(), *serializer, + [&](const boost::system::error_code& ec, + std::size_t bytes_transferred) { + isWriting = false; + BMCWEB_LOG_DEBUG << this << " Wrote " << bytes_transferred + << " bytes"; + + if (ec) + { + BMCWEB_LOG_DEBUG << this << " from write(2)"; + checkDestroy(); + return; + } + if (!req->keepAlive()) + { + adaptor.close(); + BMCWEB_LOG_DEBUG << this << " from write(1)"; + checkDestroy(); + return; + } + + serializer.reset(); + BMCWEB_LOG_DEBUG << this << " Clearing response"; + res.clear(); + parser.emplace(std::piecewise_construct, std::make_tuple()); + parser->body_limit(httpReqBodyLimit); // reset body limit for + // newly created parser + buffer.consume(buffer.size()); + + req.emplace(parser->get()); + doReadHeaders(); + }); } - if (res.resultInt() >= 400 && res.body().empty()) { - res.body() = std::string(res.reason()); + void checkDestroy() + { + BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting " + << isWriting; + if (!isReading && !isWriting) + { + BMCWEB_LOG_DEBUG << this << " delete (idle) "; + delete this; + } } - res.addHeader(boost::beast::http::field::server, serverName); - res.addHeader(boost::beast::http::field::date, getCachedDateStr()); - - res.keepAlive(req->keepAlive()); - - doWrite(); - } - - private: - void doReadHeaders() { - // auto self = this->shared_from_this(); - isReading = true; - BMCWEB_LOG_DEBUG << this << " doReadHeaders"; - - // Clean up any previous Connection. - boost::beast::http::async_read_header( - adaptor.socket(), buffer, *parser, - [this](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - isReading = false; - BMCWEB_LOG_ERROR << this << " async_read_header " << bytes_transferred - << " Bytes"; - bool errorWhileReading = false; - if (ec) { - errorWhileReading = true; - BMCWEB_LOG_ERROR << this - << " Error while reading: " << ec.message(); - } else { - // if the adaptor isn't open anymore, and wasn't handed to a - // websocket, treat as an error - if (!adaptor.isOpen() && !req->isUpgrade()) { - errorWhileReading = true; - } - } - if (errorWhileReading) { - cancelDeadlineTimer(); - adaptor.close(); - BMCWEB_LOG_DEBUG << this << " from read(1)"; - checkDestroy(); - return; - } - - // Compute the url parameters for the request - req->url = req->target(); - std::size_t index = req->url.find("?"); - if (index != boost::string_view::npos) { - req->url = req->url.substr(0, index - 1); - } - req->urlParams = QueryString(std::string(req->target())); - doRead(); - }); - } - - void doRead() { - // auto self = this->shared_from_this(); - isReading = true; - BMCWEB_LOG_DEBUG << this << " doRead"; - - boost::beast::http::async_read( - adaptor.socket(), buffer, *parser, - [this](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - BMCWEB_LOG_ERROR << this << " async_read " << bytes_transferred - << " Bytes"; - isReading = false; - - bool errorWhileReading = false; - if (ec) { - BMCWEB_LOG_ERROR << "Error while reading: " << ec.message(); - errorWhileReading = true; - } else { - if (!adaptor.isOpen()) { - errorWhileReading = true; + void cancelDeadlineTimer() + { + BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' ' + << timerCancelKey; + timerQueue.cancel(timerCancelKey); + } + + void startDeadline() + { + cancelDeadlineTimer(); + + timerCancelKey = timerQueue.add([this] { + if (!adaptor.isOpen()) + { + return; } - } - if (errorWhileReading) { - cancelDeadlineTimer(); adaptor.close(); - BMCWEB_LOG_DEBUG << this << " from read(1)"; - checkDestroy(); - return; - } - handle(); - }); - } - - void doWrite() { - // auto self = this->shared_from_this(); - isWriting = true; - BMCWEB_LOG_DEBUG << "Doing Write"; - res.preparePayload(); - serializer.emplace(*res.stringResponse); - boost::beast::http::async_write( - adaptor.socket(), *serializer, - [&](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - isWriting = false; - BMCWEB_LOG_DEBUG << this << " Wrote " << bytes_transferred - << " bytes"; - - if (ec) { - BMCWEB_LOG_DEBUG << this << " from write(2)"; - checkDestroy(); - return; - } - if (!req->keepAlive()) { - adaptor.close(); - BMCWEB_LOG_DEBUG << this << " from write(1)"; - checkDestroy(); - return; - } - - serializer.reset(); - BMCWEB_LOG_DEBUG << this << " Clearing response"; - res.clear(); - parser.emplace(std::piecewise_construct, std::make_tuple()); - parser->body_limit(httpReqBodyLimit); // reset body limit for - // newly created parser - buffer.consume(buffer.size()); - - req.emplace(parser->get()); - doReadHeaders(); }); - } - - void checkDestroy() { - BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting " - << isWriting; - if (!isReading && !isWriting) { - BMCWEB_LOG_DEBUG << this << " delete (idle) "; - delete this; + BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' ' + << timerCancelKey; } - } - - void cancelDeadlineTimer() { - BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' ' - << timerCancelKey; - timerQueue.cancel(timerCancelKey); - } - - void startDeadline() { - cancelDeadlineTimer(); - - timerCancelKey = timerQueue.add([this] { - if (!adaptor.isOpen()) { - return; - } - adaptor.close(); - }); - BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' ' - << timerCancelKey; - } - - private: - Adaptor adaptor; - Handler* handler; - - // Making this a boost::optional allows it to be efficiently destroyed and - // re-created on Connection reset - boost::optional< - boost::beast::http::request_parser<boost::beast::http::string_body>> - parser; - - boost::beast::flat_buffer buffer{8192}; - - boost::optional< - boost::beast::http::response_serializer<boost::beast::http::string_body>> - serializer; - - boost::optional<crow::Request> req; - crow::Response res; - - const std::string& serverName; - - int timerCancelKey{-1}; - - bool isReading{}; - bool isWriting{}; - bool needToCallAfterHandlers{}; - bool needToStartReadAfterComplete{}; - bool addKeepAlive{}; - - std::tuple<Middlewares...>* middlewares; - detail::Context<Middlewares...> ctx; - - std::function<std::string()>& getCachedDateStr; - detail::TimerQueue& timerQueue; -}; // namespace crow -} // namespace crow + + private: + Adaptor adaptor; + Handler* handler; + + // Making this a boost::optional allows it to be efficiently destroyed and + // re-created on Connection reset + boost::optional< + boost::beast::http::request_parser<boost::beast::http::string_body>> + parser; + + boost::beast::flat_buffer buffer{8192}; + + boost::optional<boost::beast::http::response_serializer< + boost::beast::http::string_body>> + serializer; + + boost::optional<crow::Request> req; + crow::Response res; + + const std::string& serverName; + + int timerCancelKey{-1}; + + bool isReading{}; + bool isWriting{}; + bool needToCallAfterHandlers{}; + bool needToStartReadAfterComplete{}; + bool addKeepAlive{}; + + std::tuple<Middlewares...>* middlewares; + detail::Context<Middlewares...> ctx; + + std::function<std::string()>& getCachedDateStr; + detail::TimerQueue& timerQueue; +}; // namespace crow +} // namespace crow diff --git a/crow/include/crow/http_request.h b/crow/include/crow/http_request.h index 7e95bc6669..0e6dd12932 100644 --- a/crow/include/crow/http_request.h +++ b/crow/include/crow/http_request.h @@ -7,42 +7,66 @@ #include "crow/common.h" #include "crow/query_string.h" -namespace crow { - -struct Request { - boost::string_view url{}; - QueryString urlParams{}; - bool isSecure{false}; - - const std::string& body; - - void* middlewareContext{}; - boost::asio::io_service* ioService{}; - - Request(boost::beast::http::request<boost::beast::http::string_body>& req) - : req(req), body(req.body()) {} - - const boost::beast::http::verb method() const { return req.method(); } - - const boost::string_view getHeaderValue(boost::string_view key) const { - return req[key]; - } - - const boost::string_view getHeaderValue(boost::beast::http::field key) const { - return req[key]; - } - - const boost::string_view methodString() const { return req.method_string(); } - - const boost::string_view target() const { return req.target(); } - - unsigned version() { return req.version(); } - - bool isUpgrade() { return boost::beast::websocket::is_upgrade(req); } - - bool keepAlive() { return req.keep_alive(); } - - boost::beast::http::request<boost::beast::http::string_body>& req; +namespace crow +{ + +struct Request +{ + boost::string_view url{}; + QueryString urlParams{}; + bool isSecure{false}; + + const std::string& body; + + void* middlewareContext{}; + boost::asio::io_service* ioService{}; + + Request(boost::beast::http::request<boost::beast::http::string_body>& req) : + req(req), body(req.body()) + { + } + + const boost::beast::http::verb method() const + { + return req.method(); + } + + const boost::string_view getHeaderValue(boost::string_view key) const + { + return req[key]; + } + + const boost::string_view getHeaderValue(boost::beast::http::field key) const + { + return req[key]; + } + + const boost::string_view methodString() const + { + return req.method_string(); + } + + const boost::string_view target() const + { + return req.target(); + } + + unsigned version() + { + return req.version(); + } + + bool isUpgrade() + { + return boost::beast::websocket::is_upgrade(req); + } + + bool keepAlive() + { + return req.keep_alive(); + } + + boost::beast::http::request<boost::beast::http::string_body>& req; }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/http_response.h b/crow/include/crow/http_response.h index 560eef46e8..d66bf47046 100644 --- a/crow/include/crow/http_response.h +++ b/crow/include/crow/http_response.h @@ -1,121 +1,173 @@ #pragma once +#include "nlohmann/json.hpp" + +#include <boost/beast/http.hpp> #include <string> -#include "nlohmann/json.hpp" #include "crow/http_request.h" #include "crow/logging.h" -#include <boost/beast/http.hpp> -namespace crow { +namespace crow +{ template <typename Adaptor, typename Handler, typename... Middlewares> class Connection; -struct Response { - template <typename Adaptor, typename Handler, typename... Middlewares> - friend class crow::Connection; - using response_type = - boost::beast::http::response<boost::beast::http::string_body>; - - boost::optional<response_type> stringResponse; - - nlohmann::json jsonValue; +struct Response +{ + template <typename Adaptor, typename Handler, typename... Middlewares> + friend class crow::Connection; + using response_type = + boost::beast::http::response<boost::beast::http::string_body>; - void addHeader(const boost::string_view key, const boost::string_view value) { - stringResponse->set(key, value); - } + boost::optional<response_type> stringResponse; - void addHeader(boost::beast::http::field key, boost::string_view value) { - stringResponse->set(key, value); - } + nlohmann::json jsonValue; - Response() : stringResponse(response_type{}) {} + void addHeader(const boost::string_view key, const boost::string_view value) + { + stringResponse->set(key, value); + } - explicit Response(boost::beast::http::status code) - : stringResponse(response_type{}) {} + void addHeader(boost::beast::http::field key, boost::string_view value) + { + stringResponse->set(key, value); + } - explicit Response(boost::string_view body_) - : stringResponse(response_type{}) { - stringResponse->body() = std::string(body_); - } + Response() : stringResponse(response_type{}) + { + } - Response(boost::beast::http::status code, boost::string_view s) - : stringResponse(response_type{}) { - stringResponse->result(code); - stringResponse->body() = std::string(s); - } + explicit Response(boost::beast::http::status code) : + stringResponse(response_type{}) + { + } - Response(Response&& r) { - BMCWEB_LOG_DEBUG << "Moving response containers"; - *this = std::move(r); - } + explicit Response(boost::string_view body_) : + stringResponse(response_type{}) + { + stringResponse->body() = std::string(body_); + } - ~Response() { BMCWEB_LOG_DEBUG << this << " Destroying response"; } + Response(boost::beast::http::status code, boost::string_view s) : + stringResponse(response_type{}) + { + stringResponse->result(code); + stringResponse->body() = std::string(s); + } - Response& operator=(const Response& r) = delete; + Response(Response&& r) + { + BMCWEB_LOG_DEBUG << "Moving response containers"; + *this = std::move(r); + } - Response& operator=(Response&& r) noexcept { - BMCWEB_LOG_DEBUG << "Moving response containers"; - stringResponse = std::move(r.stringResponse); - r.stringResponse.emplace(response_type{}); - jsonValue = std::move(r.jsonValue); - completed = r.completed; - return *this; - } + ~Response() + { + BMCWEB_LOG_DEBUG << this << " Destroying response"; + } - void result(boost::beast::http::status v) { stringResponse->result(v); } + Response& operator=(const Response& r) = delete; - boost::beast::http::status result() { return stringResponse->result(); } + Response& operator=(Response&& r) noexcept + { + BMCWEB_LOG_DEBUG << "Moving response containers"; + stringResponse = std::move(r.stringResponse); + r.stringResponse.emplace(response_type{}); + jsonValue = std::move(r.jsonValue); + completed = r.completed; + return *this; + } - unsigned resultInt() { return stringResponse->result_int(); } + void result(boost::beast::http::status v) + { + stringResponse->result(v); + } - boost::string_view reason() { return stringResponse->reason(); } + boost::beast::http::status result() + { + return stringResponse->result(); + } - bool isCompleted() const noexcept { return completed; } + unsigned resultInt() + { + return stringResponse->result_int(); + } - std::string& body() { return stringResponse->body(); } + boost::string_view reason() + { + return stringResponse->reason(); + } - void keepAlive(bool k) { stringResponse->keep_alive(k); } + bool isCompleted() const noexcept + { + return completed; + } - void preparePayload() { stringResponse->prepare_payload(); }; + std::string& body() + { + return stringResponse->body(); + } - void clear() { - BMCWEB_LOG_DEBUG << this << " Clearing response containers"; - stringResponse.emplace(response_type{}); - jsonValue.clear(); - completed = false; - } + void keepAlive(bool k) + { + stringResponse->keep_alive(k); + } - void write(boost::string_view body_part) { - stringResponse->body() += std::string(body_part); - } + void preparePayload() + { + stringResponse->prepare_payload(); + }; + + void clear() + { + BMCWEB_LOG_DEBUG << this << " Clearing response containers"; + stringResponse.emplace(response_type{}); + jsonValue.clear(); + completed = false; + } - void end() { - if (completed) { - BMCWEB_LOG_ERROR << "Response was ended twice"; - return; + void write(boost::string_view body_part) + { + stringResponse->body() += std::string(body_part); } - completed = true; - BMCWEB_LOG_DEBUG << "calling completion handler"; - if (completeRequestHandler) { - BMCWEB_LOG_DEBUG << "completion handler was valid"; - completeRequestHandler(); + + void end() + { + if (completed) + { + BMCWEB_LOG_ERROR << "Response was ended twice"; + return; + } + completed = true; + BMCWEB_LOG_DEBUG << "calling completion handler"; + if (completeRequestHandler) + { + BMCWEB_LOG_DEBUG << "completion handler was valid"; + completeRequestHandler(); + } } - } - void end(boost::string_view body_part) { - write(body_part); - end(); - } + void end(boost::string_view body_part) + { + write(body_part); + end(); + } - bool isAlive() { return isAliveHelper && isAliveHelper(); } + bool isAlive() + { + return isAliveHelper && isAliveHelper(); + } - private: - bool completed{}; - std::function<void()> completeRequestHandler; - std::function<bool()> isAliveHelper; + private: + bool completed{}; + std::function<void()> completeRequestHandler; + std::function<bool()> isAliveHelper; - // In case of a JSON object, set the Content-Type header - void jsonMode() { addHeader("Content-Type", "application/json"); } + // In case of a JSON object, set the Content-Type header + void jsonMode() + { + addHeader("Content-Type", "application/json"); + } }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/http_server.h b/crow/include/crow/http_server.h index 96b85dc264..173d0d1ffb 100644 --- a/crow/include/crow/http_server.h +++ b/crow/include/crow/http_server.h @@ -1,183 +1,205 @@ #pragma once #include <atomic> +#include <boost/asio.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> #include <chrono> #include <cstdint> #include <future> #include <memory> #include <utility> #include <vector> + #include "crow/http_connection.h" #include "crow/logging.h" #include "crow/timer_queue.h" -#include <boost/asio.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> #ifdef BMCWEB_ENABLE_SSL #include <boost/asio/ssl.hpp> #endif -namespace crow { +namespace crow +{ using namespace boost; using tcp = asio::ip::tcp; template <typename Handler, typename Adaptor = SocketAdaptor, typename... Middlewares> -class Server { - public: - Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor, - std::tuple<Middlewares...>* middlewares = nullptr, - typename Adaptor::context* adaptor_ctx = nullptr, - std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : ioService(std::move(io)), - acceptor(std::move(acceptor)), - signals(*ioService, SIGINT, SIGTERM), - tickTimer(*ioService), - handler(handler), - middlewares(middlewares), - adaptorCtx(adaptor_ctx) {} - - Server(Handler* handler, const std::string& bindaddr, uint16_t port, - std::tuple<Middlewares...>* middlewares = nullptr, - typename Adaptor::context* adaptor_ctx = nullptr, - std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : Server(handler, +class Server +{ + public: + Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor, + std::tuple<Middlewares...>* middlewares = nullptr, + typename Adaptor::context* adaptor_ctx = nullptr, + std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + ioService(std::move(io)), + acceptor(std::move(acceptor)), signals(*ioService, SIGINT, SIGTERM), + tickTimer(*ioService), handler(handler), middlewares(middlewares), + adaptorCtx(adaptor_ctx) + { + } + + Server(Handler* handler, const std::string& bindaddr, uint16_t port, + std::tuple<Middlewares...>* middlewares = nullptr, + typename Adaptor::context* adaptor_ctx = nullptr, + std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + Server(handler, std::make_unique<tcp::acceptor>( *io, tcp::endpoint( boost::asio::ip::address::from_string(bindaddr), port)), - middlewares, adaptor_ctx, io) {} - - Server(Handler* handler, int existing_socket, - std::tuple<Middlewares...>* middlewares = nullptr, - typename Adaptor::context* adaptor_ctx = nullptr, - std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : Server(handler, + middlewares, adaptor_ctx, io) + { + } + + Server(Handler* handler, int existing_socket, + std::tuple<Middlewares...>* middlewares = nullptr, + typename Adaptor::context* adaptor_ctx = nullptr, + std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + Server(handler, std::make_unique<tcp::acceptor>(*io, boost::asio::ip::tcp::v6(), existing_socket), - middlewares, adaptor_ctx, io) {} - - void setTickFunction(std::chrono::milliseconds d, std::function<void()> f) { - tickInterval = d; - tickFunction = f; - } - - void onTick() { - tickFunction(); - tickTimer.expires_from_now( - boost::posix_time::milliseconds(tickInterval.count())); - tickTimer.async_wait([this](const boost::system::error_code& ec) { - if (ec) { - return; - } - onTick(); - }); - } - - void updateDateStr() { - auto lastTimeT = time(0); - tm myTm{}; + middlewares, adaptor_ctx, io) + { + } + + void setTickFunction(std::chrono::milliseconds d, std::function<void()> f) + { + tickInterval = d; + tickFunction = f; + } + + void onTick() + { + tickFunction(); + tickTimer.expires_from_now( + boost::posix_time::milliseconds(tickInterval.count())); + tickTimer.async_wait([this](const boost::system::error_code& ec) { + if (ec) + { + return; + } + onTick(); + }); + } + + void updateDateStr() + { + auto lastTimeT = time(0); + tm myTm{}; #ifdef _MSC_VER - gmtime_s(&my_tm, &last_time_t); + gmtime_s(&my_tm, &last_time_t); #else - gmtime_r(&lastTimeT, &myTm); + gmtime_r(&lastTimeT, &myTm); #endif - dateStr.resize(100); - size_t dateStrSz = - strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm); - dateStr.resize(dateStrSz); - }; - - void run() { - updateDateStr(); - - getCachedDateStr = [this]() -> std::string { - static std::chrono::time_point<std::chrono::steady_clock> lastDateUpdate = - std::chrono::steady_clock::now(); - if (std::chrono::steady_clock::now() - lastDateUpdate >= - std::chrono::seconds(10)) { - lastDateUpdate = std::chrono::steady_clock::now(); - updateDateStr(); - } - return this->dateStr; + dateStr.resize(100); + size_t dateStrSz = + strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm); + dateStr.resize(dateStrSz); }; - boost::asio::deadline_timer timer(*ioService); - timer.expires_from_now(boost::posix_time::seconds(1)); - - std::function<void(const boost::system::error_code& ec)> handler; - handler = [&](const boost::system::error_code& ec) { - if (ec) { - return; - } - timerQueue.process(); - timer.expires_from_now(boost::posix_time::seconds(1)); - timer.async_wait(handler); - }; - timer.async_wait(handler); - - if (tickFunction && tickInterval.count() > 0) { - tickTimer.expires_from_now( - boost::posix_time::milliseconds(tickInterval.count())); - tickTimer.async_wait([this](const boost::system::error_code& ec) { - if (ec) { - return; + void run() + { + updateDateStr(); + + getCachedDateStr = [this]() -> std::string { + static std::chrono::time_point<std::chrono::steady_clock> + lastDateUpdate = std::chrono::steady_clock::now(); + if (std::chrono::steady_clock::now() - lastDateUpdate >= + std::chrono::seconds(10)) + { + lastDateUpdate = std::chrono::steady_clock::now(); + updateDateStr(); + } + return this->dateStr; + }; + + boost::asio::deadline_timer timer(*ioService); + timer.expires_from_now(boost::posix_time::seconds(1)); + + std::function<void(const boost::system::error_code& ec)> handler; + handler = [&](const boost::system::error_code& ec) { + if (ec) + { + return; + } + timerQueue.process(); + timer.expires_from_now(boost::posix_time::seconds(1)); + timer.async_wait(handler); + }; + timer.async_wait(handler); + + if (tickFunction && tickInterval.count() > 0) + { + tickTimer.expires_from_now( + boost::posix_time::milliseconds(tickInterval.count())); + tickTimer.async_wait([this](const boost::system::error_code& ec) { + if (ec) + { + return; + } + onTick(); + }); } - onTick(); - }); - } - BMCWEB_LOG_INFO << serverName << " server is running, local endpoint " - << acceptor->local_endpoint(); + BMCWEB_LOG_INFO << serverName << " server is running, local endpoint " + << acceptor->local_endpoint(); - signals.async_wait([&](const boost::system::error_code& /*error*/, - int /*signal_number*/) { stop(); }); + signals.async_wait([&](const boost::system::error_code& /*error*/, + int /*signal_number*/) { stop(); }); - doAccept(); - } + doAccept(); + } - void stop() { ioService->stop(); } + void stop() + { + ioService->stop(); + } - void doAccept() { - auto p = new Connection<Adaptor, Handler, Middlewares...>( - *ioService, handler, serverName, middlewares, getCachedDateStr, - timerQueue, adaptorCtx); - acceptor->async_accept(p->socket(), - [this, p](boost::system::error_code ec) { - if (!ec) { - this->ioService->post([p] { p->start(); }); - } else { - delete p; - } - doAccept(); - }); - } + void doAccept() + { + auto p = new Connection<Adaptor, Handler, Middlewares...>( + *ioService, handler, serverName, middlewares, getCachedDateStr, + timerQueue, adaptorCtx); + acceptor->async_accept( + p->socket(), [this, p](boost::system::error_code ec) { + if (!ec) + { + this->ioService->post([p] { p->start(); }); + } + else + { + delete p; + } + doAccept(); + }); + } - private: - std::shared_ptr<asio::io_service> ioService; - detail::TimerQueue timerQueue; - std::function<std::string()> getCachedDateStr; - std::unique_ptr<tcp::acceptor> acceptor; - boost::asio::signal_set signals; - boost::asio::deadline_timer tickTimer; + private: + std::shared_ptr<asio::io_service> ioService; + detail::TimerQueue timerQueue; + std::function<std::string()> getCachedDateStr; + std::unique_ptr<tcp::acceptor> acceptor; + boost::asio::signal_set signals; + boost::asio::deadline_timer tickTimer; - std::string dateStr; + std::string dateStr; - Handler* handler; - std::string serverName = "iBMC"; + Handler* handler; + std::string serverName = "iBMC"; - std::chrono::milliseconds tickInterval{}; - std::function<void()> tickFunction; + std::chrono::milliseconds tickInterval{}; + std::function<void()> tickFunction; - std::tuple<Middlewares...>* middlewares; + std::tuple<Middlewares...>* middlewares; #ifdef BMCWEB_ENABLE_SSL - bool useSsl{false}; - boost::asio::ssl::context sslContext{boost::asio::ssl::context::sslv23}; + bool useSsl{false}; + boost::asio::ssl::context sslContext{boost::asio::ssl::context::sslv23}; #endif - typename Adaptor::context* adaptorCtx; -}; // namespace crow -} // namespace crow + typename Adaptor::context* adaptorCtx; +}; // namespace crow +} // namespace crow diff --git a/crow/include/crow/logging.h b/crow/include/crow/logging.h index 4a68df38ce..353a448c95 100644 --- a/crow/include/crow/logging.h +++ b/crow/include/crow/logging.h @@ -7,117 +7,139 @@ #include <sstream> #include <string> -namespace crow { -enum class LogLevel { +namespace crow +{ +enum class LogLevel +{ #ifndef ERROR - DEBUG = 0, - INFO, - WARNING, - ERROR, - CRITICAL, + DEBUG = 0, + INFO, + WARNING, + ERROR, + CRITICAL, #endif - Debug = 0, - Info, - Warning, - Error, - Critical, + Debug = 0, + Info, + Warning, + Error, + Critical, }; -class ILogHandler { - public: - virtual void log(std::string message, LogLevel level) = 0; +class ILogHandler +{ + public: + virtual void log(std::string message, LogLevel level) = 0; }; -class CerrLogHandler : public ILogHandler { - public: - void log(std::string message, LogLevel /*level*/) override { - std::cerr << message; - } +class CerrLogHandler : public ILogHandler +{ + public: + void log(std::string message, LogLevel /*level*/) override + { + std::cerr << message; + } }; -class logger { - private: - // - static std::string timestamp() { - char date[32]; - time_t t = time(0); +class logger +{ + private: + // + static std::string timestamp() + { + char date[32]; + time_t t = time(0); - tm myTm{}; + tm myTm{}; #ifdef _MSC_VER - gmtime_s(&my_tm, &t); + gmtime_s(&my_tm, &t); #else - gmtime_r(&t, &myTm); + gmtime_r(&t, &myTm); #endif - size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &myTm); - return std::string(date, date + sz); - } + size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &myTm); + return std::string(date, date + sz); + } - public: - logger(const std::string& prefix, LogLevel level) : level(level) { + public: + logger(const std::string& prefix, LogLevel level) : level(level) + { #ifdef BMCWEB_ENABLE_LOGGING - stringstream << "(" << timestamp() << ") [" << prefix << "] "; + stringstream << "(" << timestamp() << ") [" << prefix << "] "; #endif - } - ~logger() { -#ifdef BMCWEB_ENABLE_LOGGING - if (level >= get_current_log_level()) { - stringstream << std::endl; - getHandlerRef()->log(stringstream.str(), level); } + ~logger() + { +#ifdef BMCWEB_ENABLE_LOGGING + if (level >= get_current_log_level()) + { + stringstream << std::endl; + getHandlerRef()->log(stringstream.str(), level); + } #endif - } + } - // - template <typename T> - logger& operator<<(T const& value) { + // + template <typename T> logger& operator<<(T const& value) + { #ifdef BMCWEB_ENABLE_LOGGING - if (level >= get_current_log_level()) { - stringstream << value; - } + if (level >= get_current_log_level()) + { + stringstream << value; + } #endif - return *this; - } - - // - static void setLogLevel(LogLevel level) { getLogLevelRef() = level; } - - static void setHandler(ILogHandler* handler) { getHandlerRef() = handler; } - - static LogLevel get_current_log_level() { return getLogLevelRef(); } - - private: - // - static LogLevel& getLogLevelRef() { - static auto currentLevel = static_cast<LogLevel>(1); - return currentLevel; - } - static ILogHandler*& getHandlerRef() { - static CerrLogHandler defaultHandler; - static ILogHandler* currentHandler = &defaultHandler; - return currentHandler; - } - - // - std::ostringstream stringstream; - LogLevel level; + return *this; + } + + // + static void setLogLevel(LogLevel level) + { + getLogLevelRef() = level; + } + + static void setHandler(ILogHandler* handler) + { + getHandlerRef() = handler; + } + + static LogLevel get_current_log_level() + { + return getLogLevelRef(); + } + + private: + // + static LogLevel& getLogLevelRef() + { + static auto currentLevel = static_cast<LogLevel>(1); + return currentLevel; + } + static ILogHandler*& getHandlerRef() + { + static CerrLogHandler defaultHandler; + static ILogHandler* currentHandler = &defaultHandler; + return currentHandler; + } + + // + std::ostringstream stringstream; + LogLevel level; }; -} // namespace crow - -#define BMCWEB_LOG_CRITICAL \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ - crow::logger("CRITICAL", crow::LogLevel::Critical) -#define BMCWEB_LOG_ERROR \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ - crow::logger("ERROR ", crow::LogLevel::Error) -#define BMCWEB_LOG_WARNING \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ - crow::logger("WARNING ", crow::LogLevel::Warning) -#define BMCWEB_LOG_INFO \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ - crow::logger("INFO ", crow::LogLevel::Info) -#define BMCWEB_LOG_DEBUG \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ - crow::logger("DEBUG ", crow::LogLevel::Debug) +} // namespace crow + +#define BMCWEB_LOG_CRITICAL \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ + crow::logger("CRITICAL", crow::LogLevel::Critical) +#define BMCWEB_LOG_ERROR \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ + crow::logger("ERROR ", crow::LogLevel::Error) +#define BMCWEB_LOG_WARNING \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ + crow::logger("WARNING ", crow::LogLevel::Warning) +#define BMCWEB_LOG_INFO \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ + crow::logger("INFO ", crow::LogLevel::Info) +#define BMCWEB_LOG_DEBUG \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ + crow::logger("DEBUG ", crow::LogLevel::Debug) diff --git a/crow/include/crow/middleware_context.h b/crow/include/crow/middleware_context.h index 03309f67c5..f109a44b1f 100644 --- a/crow/include/crow/middleware_context.h +++ b/crow/include/crow/middleware_context.h @@ -4,30 +4,32 @@ #include "crow/http_response.h" #include "crow/utility.h" -namespace crow { -namespace detail { +namespace crow +{ +namespace detail +{ template <typename... Middlewares> struct PartialContext : public black_magic::PopBack<Middlewares...>::template rebind< PartialContext>, - public black_magic::LastElementType<Middlewares...>::type::Context { - using parent_context = typename black_magic::PopBack< - Middlewares...>::template rebind<::crow::detail::PartialContext>; - template <int N> - using partial = typename std::conditional< - N == sizeof...(Middlewares) - 1, PartialContext, - typename parent_context::template partial<N>>::type; - - template <typename T> - typename T::Context& get() { - return static_cast<typename T::Context&>(*this); - } + public black_magic::LastElementType<Middlewares...>::type::Context +{ + using parent_context = typename black_magic::PopBack< + Middlewares...>::template rebind<::crow::detail::PartialContext>; + template <int N> + using partial = typename std::conditional< + N == sizeof...(Middlewares) - 1, PartialContext, + typename parent_context::template partial<N>>::type; + + template <typename T> typename T::Context& get() + { + return static_cast<typename T::Context&>(*this); + } }; -template <> -struct PartialContext<> { - template <int> - using partial = PartialContext; +template <> struct PartialContext<> +{ + template <int> using partial = PartialContext; }; template <int N, typename Context, typename Container, typename CurrentMW, @@ -39,25 +41,28 @@ template <typename... Middlewares> struct Context : private PartialContext<Middlewares...> // struct Context : private Middlewares::context... // simple but less type-safe { - template <int N, typename Context, typename Container> - friend typename std::enable_if<(N == 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res); - template <int N, typename Context, typename Container> - friend typename std::enable_if<(N > 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res); - - template <int N, typename Context, typename Container, typename CurrentMW, - typename... Middlewares2> - friend bool middlewareCallHelper(Container& middlewares, Request& req, - Response& res, Context& ctx); - - template <typename T> - typename T::Context& get() { - return static_cast<typename T::Context&>(*this); - } - - template <int N> - using partial = typename PartialContext<Middlewares...>::template partial<N>; + template <int N, typename Context, typename Container> + friend typename std::enable_if<(N == 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, + Request& req, Response& res); + template <int N, typename Context, typename Container> + friend typename std::enable_if<(N > 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, + Request& req, Response& res); + + template <int N, typename Context, typename Container, typename CurrentMW, + typename... Middlewares2> + friend bool middlewareCallHelper(Container& middlewares, Request& req, + Response& res, Context& ctx); + + template <typename T> typename T::Context& get() + { + return static_cast<typename T::Context&>(*this); + } + + template <int N> + using partial = + typename PartialContext<Middlewares...>::template partial<N>; }; -} // namespace detail -} // namespace crow +} // namespace detail +} // namespace crow diff --git a/crow/include/crow/query_string.h b/crow/include/crow/query_string.h index 060699477c..67e426acd7 100644 --- a/crow/include/crow/query_string.h +++ b/crow/include/crow/query_string.h @@ -6,7 +6,8 @@ #include <string> #include <vector> -namespace crow { +namespace crow +{ // ---------------------------------------------------------------------------- // qs_parse (modified) // https://github.com/bartgrantham/qs_parse @@ -37,306 +38,376 @@ char* qsScanvalue(const char* key, const char* qs, char* val, size_t val_len); #undef _qsSORTING // isxdigit _is_ available in <ctype.h>, but let's avoid another header instead -#define BMCWEB_QS_ISHEX(x) \ - ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \ - ((x) >= 'a' && (x) <= 'f')) \ - ? 1 \ - : 0) -#define BMCWEB_QS_HEX2DEC(x) \ - (((x) >= '0' && (x) <= '9') \ - ? (x)-48 \ - : ((x) >= 'A' && (x) <= 'F') ? (x)-55 \ - : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0) -#define BMCWEB_QS_ISQSCHR(x) \ - ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1) - -inline int qsStrncmp(const char* s, const char* qs, size_t n) { - int i = 0; - unsigned char u1, u2, unyb, lnyb; - - while (n-- > 0) { - u1 = static_cast<unsigned char>(*s++); - u2 = static_cast<unsigned char>(*qs++); - - if (!BMCWEB_QS_ISQSCHR(u1)) { - u1 = '\0'; - } - if (!BMCWEB_QS_ISQSCHR(u2)) { - u2 = '\0'; +#define BMCWEB_QS_ISHEX(x) \ + ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \ + ((x) >= 'a' && (x) <= 'f')) \ + ? 1 \ + : 0) +#define BMCWEB_QS_HEX2DEC(x) \ + (((x) >= '0' && (x) <= '9') \ + ? (x)-48 \ + : ((x) >= 'A' && (x) <= 'F') \ + ? (x)-55 \ + : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0) +#define BMCWEB_QS_ISQSCHR(x) \ + ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1) + +inline int qsStrncmp(const char* s, const char* qs, size_t n) +{ + int i = 0; + unsigned char u1, u2, unyb, lnyb; + + while (n-- > 0) + { + u1 = static_cast<unsigned char>(*s++); + u2 = static_cast<unsigned char>(*qs++); + + if (!BMCWEB_QS_ISQSCHR(u1)) + { + u1 = '\0'; + } + if (!BMCWEB_QS_ISQSCHR(u2)) + { + u2 = '\0'; + } + + if (u1 == '+') + { + u1 = ' '; + } + if (u1 == '%') // easier/safer than scanf + { + unyb = static_cast<unsigned char>(*s++); + lnyb = static_cast<unsigned char>(*s++); + if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) + { + u1 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); + } + else + { + u1 = '\0'; + } + } + + if (u2 == '+') + { + u2 = ' '; + } + if (u2 == '%') // easier/safer than scanf + { + unyb = static_cast<unsigned char>(*qs++); + lnyb = static_cast<unsigned char>(*qs++); + if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) + { + u2 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); + } + else + { + u2 = '\0'; + } + } + + if (u1 != u2) + { + return u1 - u2; + } + if (u1 == '\0') + { + return 0; + } + i++; } - - if (u1 == '+') { - u1 = ' '; + if (BMCWEB_QS_ISQSCHR(*qs)) + { + return -1; } - if (u1 == '%') // easier/safer than scanf + else { - unyb = static_cast<unsigned char>(*s++); - lnyb = static_cast<unsigned char>(*s++); - if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) { - u1 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); - } else { - u1 = '\0'; - } + return 0; } +} - if (u2 == '+') { - u2 = ' '; - } - if (u2 == '%') // easier/safer than scanf +inline int qsParse(char* qs, char* qs_kv[], int qs_kv_size) +{ + int i, j; + char* substrPtr; + + for (i = 0; i < qs_kv_size; i++) { - unyb = static_cast<unsigned char>(*qs++); - lnyb = static_cast<unsigned char>(*qs++); - if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) { - u2 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); - } else { - u2 = '\0'; - } + qs_kv[i] = NULL; } - if (u1 != u2) { - return u1 - u2; + // find the beginning of the k/v substrings or the fragment + substrPtr = qs + strcspn(qs, "?#"); + if (substrPtr[0] != '\0') + { + substrPtr++; } - if (u1 == '\0') { - return 0; + else + { + return 0; // no query or fragment } - i++; - } - if (BMCWEB_QS_ISQSCHR(*qs)) { - return -1; - } else { - return 0; - } -} -inline int qsParse(char* qs, char* qs_kv[], int qs_kv_size) { - int i, j; - char* substrPtr; - - for (i = 0; i < qs_kv_size; i++) { - qs_kv[i] = NULL; - } - - // find the beginning of the k/v substrings or the fragment - substrPtr = qs + strcspn(qs, "?#"); - if (substrPtr[0] != '\0') { - substrPtr++; - } else { - return 0; // no query or fragment - } - - i = 0; - while (i < qs_kv_size) { - qs_kv[i] = substrPtr; - j = strcspn(substrPtr, "&"); - if (substrPtr[j] == '\0') { - break; + i = 0; + while (i < qs_kv_size) + { + qs_kv[i] = substrPtr; + j = strcspn(substrPtr, "&"); + if (substrPtr[j] == '\0') + { + break; + } + substrPtr += j + 1; + i++; } - substrPtr += j + 1; - i++; - } - i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs - - // we only decode the values in place, the keys could have '='s in them - // which will hose our ability to distinguish keys from values later - for (j = 0; j < i; j++) { - substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#"); - if (substrPtr[0] == '&' || - substrPtr[0] == '\0') { // blank value: skip decoding - substrPtr[0] = '\0'; - } else { - qsDecode(++substrPtr); + i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs + + // we only decode the values in place, the keys could have '='s in them + // which will hose our ability to distinguish keys from values later + for (j = 0; j < i; j++) + { + substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#"); + if (substrPtr[0] == '&' || substrPtr[0] == '\0') + { // blank value: skip decoding + substrPtr[0] = '\0'; + } + else + { + qsDecode(++substrPtr); + } } - } #ifdef _qsSORTING // TODO: qsort qs_kv, using qs_strncmp() for the comparison #endif - return i; + return i; } -inline int qsDecode(char* qs) { - int i = 0, j = 0; +inline int qsDecode(char* qs) +{ + int i = 0, j = 0; - while (BMCWEB_QS_ISQSCHR(qs[j])) { - if (qs[j] == '+') { - qs[i] = ' '; - } else if (qs[j] == '%') // easier/safer than scanf + while (BMCWEB_QS_ISQSCHR(qs[j])) { - if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2])) { - qs[i] = '\0'; - return i; - } - qs[i] = - (BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) + BMCWEB_QS_HEX2DEC(qs[j + 2]); - j += 2; - } else { - qs[i] = qs[j]; + if (qs[j] == '+') + { + qs[i] = ' '; + } + else if (qs[j] == '%') // easier/safer than scanf + { + if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2])) + { + qs[i] = '\0'; + return i; + } + qs[i] = (BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) + + BMCWEB_QS_HEX2DEC(qs[j + 2]); + j += 2; + } + else + { + qs[i] = qs[j]; + } + i++; + j++; } - i++; - j++; - } - qs[i] = '\0'; + qs[i] = '\0'; - return i; + return i; } inline char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size, - int nth = 0) { - int i; - size_t keyLen, skip; + int nth = 0) +{ + int i; + size_t keyLen, skip; - keyLen = strlen(key); + keyLen = strlen(key); #ifdef _qsSORTING // TODO: binary search for key in the sorted qs_kv -#else // _qsSORTING - for (i = 0; i < qs_kv_size; i++) { - // we rely on the unambiguous '=' to find the value in our k/v pair - if (qsStrncmp(key, qs_kv[i], keyLen) == 0) { - skip = strcspn(qs_kv[i], "="); - if (qs_kv[i][skip] == '=') { - skip++; - } - // return (zero-char value) ? ptr to trailing '\0' : ptr to value - if (nth == 0) { - return qs_kv[i] + skip; - } else { - --nth; - } +#else // _qsSORTING + for (i = 0; i < qs_kv_size; i++) + { + // we rely on the unambiguous '=' to find the value in our k/v pair + if (qsStrncmp(key, qs_kv[i], keyLen) == 0) + { + skip = strcspn(qs_kv[i], "="); + if (qs_kv[i][skip] == '=') + { + skip++; + } + // return (zero-char value) ? ptr to trailing '\0' : ptr to value + if (nth == 0) + { + return qs_kv[i] + skip; + } + else + { + --nth; + } + } } - } -#endif // _qsSORTING +#endif // _qsSORTING - return NULL; + return NULL; } inline char* qsScanvalue(const char* key, const char* qs, char* val, - size_t val_len) { - size_t i, keyLen; - const char* tmp; - - // find the beginning of the k/v substrings - if ((tmp = strchr(qs, '?')) != NULL) { - qs = tmp + 1; - } - - keyLen = strlen(key); - while (qs[0] != '#' && qs[0] != '\0') { - if (qsStrncmp(key, qs, keyLen) == 0) { - break; + size_t val_len) +{ + size_t i, keyLen; + const char* tmp; + + // find the beginning of the k/v substrings + if ((tmp = strchr(qs, '?')) != NULL) + { + qs = tmp + 1; } - qs += strcspn(qs, "&") + 1; - } - if (qs[0] == '\0') { - return NULL; - } - - qs += strcspn(qs, "=&#"); - if (qs[0] == '=') { - qs++; - i = strcspn(qs, "&=#"); - strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1)); - qsDecode(val); - } else { - if (val_len > 0) { - val[0] = '\0'; + keyLen = strlen(key); + while (qs[0] != '#' && qs[0] != '\0') + { + if (qsStrncmp(key, qs, keyLen) == 0) + { + break; + } + qs += strcspn(qs, "&") + 1; + } + + if (qs[0] == '\0') + { + return NULL; } - } - return val; + qs += strcspn(qs, "=&#"); + if (qs[0] == '=') + { + qs++; + i = strcspn(qs, "&=#"); + strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1)); + qsDecode(val); + } + else + { + if (val_len > 0) + { + val[0] = '\0'; + } + } + + return val; } -} // namespace crow +} // namespace crow // ---------------------------------------------------------------------------- -namespace crow { -class QueryString { - public: - static const int maxKeyValuePairsCount = 256; +namespace crow +{ +class QueryString +{ + public: + static const int maxKeyValuePairsCount = 256; - QueryString() = default; + QueryString() = default; - QueryString(const QueryString& qs) : url(qs.url) { - for (auto p : qs.keyValuePairs) { - keyValuePairs.push_back( - const_cast<char*>(p - qs.url.c_str() + url.c_str())); - } - } - - QueryString& operator=(const QueryString& qs) { - url = qs.url; - keyValuePairs.clear(); - for (auto p : qs.keyValuePairs) { - keyValuePairs.push_back( - const_cast<char*>(p - qs.url.c_str() + url.c_str())); + QueryString(const QueryString& qs) : url(qs.url) + { + for (auto p : qs.keyValuePairs) + { + keyValuePairs.push_back( + const_cast<char*>(p - qs.url.c_str() + url.c_str())); + } } - return *this; - } - - QueryString& operator=(QueryString&& qs) { - keyValuePairs = std::move(qs.keyValuePairs); - auto* oldData = const_cast<char*>(qs.url.c_str()); - url = std::move(qs.url); - for (auto& p : keyValuePairs) { - p += const_cast<char*>(url.c_str()) - oldData; + + QueryString& operator=(const QueryString& qs) + { + url = qs.url; + keyValuePairs.clear(); + for (auto p : qs.keyValuePairs) + { + keyValuePairs.push_back( + const_cast<char*>(p - qs.url.c_str() + url.c_str())); + } + return *this; } - return *this; - } - explicit QueryString(std::string url) : url(std::move(url)) { - if (url.empty()) { - return; + QueryString& operator=(QueryString&& qs) + { + keyValuePairs = std::move(qs.keyValuePairs); + auto* oldData = const_cast<char*>(qs.url.c_str()); + url = std::move(qs.url); + for (auto& p : keyValuePairs) + { + p += const_cast<char*>(url.c_str()) - oldData; + } + return *this; } - keyValuePairs.resize(maxKeyValuePairsCount); + explicit QueryString(std::string url) : url(std::move(url)) + { + if (url.empty()) + { + return; + } - int count = qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount); - keyValuePairs.resize(count); - } + keyValuePairs.resize(maxKeyValuePairsCount); - void clear() { - keyValuePairs.clear(); - url.clear(); - } + int count = qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount); + keyValuePairs.resize(count); + } + + void clear() + { + keyValuePairs.clear(); + url.clear(); + } + + friend std::ostream& operator<<(std::ostream& os, const QueryString& qs) + { + os << "[ "; + for (size_t i = 0; i < qs.keyValuePairs.size(); ++i) + { + if (i != 0u) + { + os << ", "; + } + os << qs.keyValuePairs[i]; + } + os << " ]"; + return os; + } - friend std::ostream& operator<<(std::ostream& os, const QueryString& qs) { - os << "[ "; - for (size_t i = 0; i < qs.keyValuePairs.size(); ++i) { - if (i != 0u) { - os << ", "; - } - os << qs.keyValuePairs[i]; + char* get(const std::string& name) const + { + char* ret = + qsK2v(name.c_str(), keyValuePairs.data(), keyValuePairs.size()); + return ret; } - os << " ]"; - return os; - } - - char* get(const std::string& name) const { - char* ret = qsK2v(name.c_str(), keyValuePairs.data(), keyValuePairs.size()); - return ret; - } - - std::vector<char*> getList(const std::string& name) const { - std::vector<char*> ret; - std::string plus = name + "[]"; - char* element = nullptr; - - int count = 0; - while (1) { - element = qsK2v(plus.c_str(), keyValuePairs.data(), keyValuePairs.size(), - count++); - if (element == nullptr) { - break; - } - ret.push_back(element); + + std::vector<char*> getList(const std::string& name) const + { + std::vector<char*> ret; + std::string plus = name + "[]"; + char* element = nullptr; + + int count = 0; + while (1) + { + element = qsK2v(plus.c_str(), keyValuePairs.data(), + keyValuePairs.size(), count++); + if (element == nullptr) + { + break; + } + ret.push_back(element); + } + return ret; } - return ret; - } - private: - std::string url; - std::vector<char*> keyValuePairs; + private: + std::string url; + std::vector<char*> keyValuePairs; }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/routing.h b/crow/include/crow/routing.h index ddf307aa63..945f3611d8 100644 --- a/crow/include/crow/routing.h +++ b/crow/include/crow/routing.h @@ -1,5 +1,8 @@ #pragma once +#include "boost/container/flat_map.hpp" + +#include <boost/lexical_cast.hpp> #include <cerrno> #include <cstdint> #include <cstdlib> @@ -8,9 +11,6 @@ #include <tuple> #include <utility> #include <vector> -#include <boost/lexical_cast.hpp> - -#include "boost/container/flat_map.hpp" #include "crow/common.h" #include "crow/http_request.h" @@ -19,969 +19,1174 @@ #include "crow/utility.h" #include "crow/websocket.h" -namespace crow { -class BaseRule { - public: - BaseRule(std::string rule) : rule(std::move(rule)) {} +namespace crow +{ +class BaseRule +{ + public: + BaseRule(std::string rule) : rule(std::move(rule)) + { + } - virtual ~BaseRule() {} + virtual ~BaseRule() + { + } - virtual void validate() = 0; - std::unique_ptr<BaseRule> upgrade() { - if (ruleToUpgrade) return std::move(ruleToUpgrade); - return {}; - } + virtual void validate() = 0; + std::unique_ptr<BaseRule> upgrade() + { + if (ruleToUpgrade) + return std::move(ruleToUpgrade); + return {}; + } - virtual void handle(const Request&, Response&, const RoutingParams&) = 0; - virtual void handleUpgrade(const Request&, Response& res, SocketAdaptor&&) { - res = Response(boost::beast::http::status::not_found); - res.end(); - } + virtual void handle(const Request&, Response&, const RoutingParams&) = 0; + virtual void handleUpgrade(const Request&, Response& res, SocketAdaptor&&) + { + res = Response(boost::beast::http::status::not_found); + res.end(); + } #ifdef BMCWEB_ENABLE_SSL - virtual void handleUpgrade(const Request&, Response& res, SSLAdaptor&&) { - res = Response(boost::beast::http::status::not_found); - res.end(); - } + virtual void handleUpgrade(const Request&, Response& res, SSLAdaptor&&) + { + res = Response(boost::beast::http::status::not_found); + res.end(); + } #endif - uint32_t getMethods() { return methodsBitfield; } + uint32_t getMethods() + { + return methodsBitfield; + } - protected: - uint32_t methodsBitfield{1 << (int)boost::beast::http::verb::get}; + protected: + uint32_t methodsBitfield{1 << (int)boost::beast::http::verb::get}; - std::string rule; - std::string nameStr; + std::string rule; + std::string nameStr; - std::unique_ptr<BaseRule> ruleToUpgrade; + std::unique_ptr<BaseRule> ruleToUpgrade; - friend class Router; - template <typename T> - friend struct RuleParameterTraits; + friend class Router; + template <typename T> friend struct RuleParameterTraits; }; -namespace detail { -namespace routing_handler_call_helper { -template <typename T, int Pos> -struct CallPair { - using type = T; - static const int pos = Pos; +namespace detail +{ +namespace routing_handler_call_helper +{ +template <typename T, int Pos> struct CallPair +{ + using type = T; + static const int pos = Pos; }; -template <typename H1> -struct CallParams { - H1& handler; - const RoutingParams& params; - const Request& req; - Response& res; +template <typename H1> struct CallParams +{ + H1& handler; + const RoutingParams& params; + const Request& req; + Response& res; }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2> -struct Call {}; +struct Call +{ +}; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, - black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<int64_t, NInt>>; - Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<int64_t, NInt>>; + Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, - black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<uint64_t, NUint>>; - Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<uint64_t, NUint>>; + Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, - black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<double, NDouble>>; - Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<double, NDouble>>; + Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, - black_magic::S<std::string, Args1...>, black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<std::string, NString>>; - Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<std::string, Args1...>, black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<std::string, NString>>; + Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1> struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>, - black_magic::S<Args1...>> { - void operator()(F cparams) { - cparams.handler( - cparams.req, cparams.res, - cparams.params.template get<typename Args1::type>(Args1::pos)...); - } + black_magic::S<Args1...>> +{ + void operator()(F cparams) + { + cparams.handler( + cparams.req, cparams.res, + cparams.params.template get<typename Args1::type>(Args1::pos)...); + } }; -template <typename Func, typename... ArgsWrapped> -struct Wrapped { - template <typename... Args> - void set( - Func f, - typename std::enable_if< - !std::is_same< - typename std::tuple_element<0, std::tuple<Args..., void>>::type, - const Request&>::value, - int>::type = 0) { - handler = ( +template <typename Func, typename... ArgsWrapped> struct Wrapped +{ + template <typename... Args> + void set( + Func f, + typename std::enable_if< + !std::is_same< + typename std::tuple_element<0, std::tuple<Args..., void>>::type, + const Request&>::value, + int>::type = 0) + { + handler = ( #ifdef BMCWEB_CAN_USE_CPP14 - [f = std::move(f)] + [f = std::move(f)] #else - [f] + [f] #endif - (const Request&, Response& res, Args... args) { - res = Response(f(args...)); - res.end(); - }); - } - - template <typename Req, typename... Args> - struct ReqHandlerWrapper { - ReqHandlerWrapper(Func f) : f(std::move(f)) {} - - void operator()(const Request& req, Response& res, Args... args) { - res = Response(f(req, args...)); - res.end(); - } - - Func f; - }; - - template <typename... Args> - void set( - Func f, - typename std::enable_if< - std::is_same< - typename std::tuple_element<0, std::tuple<Args..., void>>::type, - const Request&>::value && - !std::is_same<typename std::tuple_element< - 1, std::tuple<Args..., void, void>>::type, - Response&>::value, - int>::type = 0) { - handler = ReqHandlerWrapper<Args...>(std::move(f)); - /*handler = ( - [f = std::move(f)] - (const Request& req, Response& res, Args... args){ - res = Response(f(req, args...)); - res.end(); - });*/ - } - - template <typename... Args> - void set( - Func f, - typename std::enable_if< - std::is_same< - typename std::tuple_element<0, std::tuple<Args..., void>>::type, - const Request&>::value && - std::is_same<typename std::tuple_element< - 1, std::tuple<Args..., void, void>>::type, - Response&>::value, - int>::type = 0) { - handler = std::move(f); - } - - template <typename... Args> - struct HandlerTypeHelper { - using type = - std::function<void(const crow::Request&, crow::Response&, Args...)>; - using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; - }; - - template <typename... Args> - struct HandlerTypeHelper<const Request&, Args...> { - using type = - std::function<void(const crow::Request&, crow::Response&, Args...)>; - using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; - }; - - template <typename... Args> - struct HandlerTypeHelper<const Request&, Response&, Args...> { - using type = - std::function<void(const crow::Request&, crow::Response&, Args...)>; - using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; - }; - - typename HandlerTypeHelper<ArgsWrapped...>::type handler; - - void operator()(const Request& req, Response& res, - const RoutingParams& params) { - detail::routing_handler_call_helper::Call< - detail::routing_handler_call_helper::CallParams<decltype(handler)>, 0, - 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type, - black_magic::S<>>()( - detail::routing_handler_call_helper::CallParams<decltype(handler)>{ - handler, params, req, res}); - } + (const Request&, Response& res, Args... args) { + res = Response(f(args...)); + res.end(); + }); + } + + template <typename Req, typename... Args> struct ReqHandlerWrapper + { + ReqHandlerWrapper(Func f) : f(std::move(f)) + { + } + + void operator()(const Request& req, Response& res, Args... args) + { + res = Response(f(req, args...)); + res.end(); + } + + Func f; + }; + + template <typename... Args> + void set( + Func f, + typename std::enable_if< + std::is_same< + typename std::tuple_element<0, std::tuple<Args..., void>>::type, + const Request&>::value && + !std::is_same<typename std::tuple_element< + 1, std::tuple<Args..., void, void>>::type, + Response&>::value, + int>::type = 0) + { + handler = ReqHandlerWrapper<Args...>(std::move(f)); + /*handler = ( + [f = std::move(f)] + (const Request& req, Response& res, Args... args){ + res = Response(f(req, args...)); + res.end(); + });*/ + } + + template <typename... Args> + void set( + Func f, + typename std::enable_if< + std::is_same< + typename std::tuple_element<0, std::tuple<Args..., void>>::type, + const Request&>::value && + std::is_same<typename std::tuple_element< + 1, std::tuple<Args..., void, void>>::type, + Response&>::value, + int>::type = 0) + { + handler = std::move(f); + } + + template <typename... Args> struct HandlerTypeHelper + { + using type = + std::function<void(const crow::Request&, crow::Response&, Args...)>; + using args_type = + black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + template <typename... Args> + struct HandlerTypeHelper<const Request&, Args...> + { + using type = + std::function<void(const crow::Request&, crow::Response&, Args...)>; + using args_type = + black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + template <typename... Args> + struct HandlerTypeHelper<const Request&, Response&, Args...> + { + using type = + std::function<void(const crow::Request&, crow::Response&, Args...)>; + using args_type = + black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + typename HandlerTypeHelper<ArgsWrapped...>::type handler; + + void operator()(const Request& req, Response& res, + const RoutingParams& params) + { + detail::routing_handler_call_helper::Call< + detail::routing_handler_call_helper::CallParams<decltype(handler)>, + 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type, + black_magic::S<>>()( + detail::routing_handler_call_helper::CallParams<decltype(handler)>{ + handler, params, req, res}); + } }; -} // namespace routing_handler_call_helper -} // namespace detail +} // namespace routing_handler_call_helper +} // namespace detail -class WebSocketRule : public BaseRule { - using self_t = WebSocketRule; +class WebSocketRule : public BaseRule +{ + using self_t = WebSocketRule; - public: - WebSocketRule(std::string rule) : BaseRule(std::move(rule)) {} + public: + WebSocketRule(std::string rule) : BaseRule(std::move(rule)) + { + } - void validate() override {} + void validate() override + { + } - void handle(const Request&, Response& res, const RoutingParams&) override { - res = Response(boost::beast::http::status::not_found); - res.end(); - } + void handle(const Request&, Response& res, const RoutingParams&) override + { + res = Response(boost::beast::http::status::not_found); + res.end(); + } - void handleUpgrade(const Request& req, Response&, - SocketAdaptor&& adaptor) override { - new crow::websocket::ConnectionImpl<SocketAdaptor>( - req, std::move(adaptor), openHandler, messageHandler, closeHandler, - errorHandler); - } -#ifdef BMCWEB_ENABLE_SSL - void handleUpgrade(const Request& req, Response&, - SSLAdaptor&& adaptor) override { - std::shared_ptr<crow::websocket::ConnectionImpl<SSLAdaptor>> myConnection = - std::make_shared<crow::websocket::ConnectionImpl<SSLAdaptor>>( + void handleUpgrade(const Request& req, Response&, + SocketAdaptor&& adaptor) override + { + new crow::websocket::ConnectionImpl<SocketAdaptor>( req, std::move(adaptor), openHandler, messageHandler, closeHandler, errorHandler); - myConnection->start(); - } + } +#ifdef BMCWEB_ENABLE_SSL + void handleUpgrade(const Request& req, Response&, + SSLAdaptor&& adaptor) override + { + std::shared_ptr<crow::websocket::ConnectionImpl<SSLAdaptor>> + myConnection = + std::make_shared<crow::websocket::ConnectionImpl<SSLAdaptor>>( + req, std::move(adaptor), openHandler, messageHandler, + closeHandler, errorHandler); + myConnection->start(); + } #endif - template <typename Func> - self_t& onopen(Func f) { - openHandler = f; - return *this; - } - - template <typename Func> - self_t& onmessage(Func f) { - messageHandler = f; - return *this; - } - - template <typename Func> - self_t& onclose(Func f) { - closeHandler = f; - return *this; - } - - template <typename Func> - self_t& onerror(Func f) { - errorHandler = f; - return *this; - } - - protected: - std::function<void(crow::websocket::Connection&)> openHandler; - std::function<void(crow::websocket::Connection&, const std::string&, bool)> - messageHandler; - std::function<void(crow::websocket::Connection&, const std::string&)> - closeHandler; - std::function<void(crow::websocket::Connection&)> errorHandler; + template <typename Func> self_t& onopen(Func f) + { + openHandler = f; + return *this; + } + + template <typename Func> self_t& onmessage(Func f) + { + messageHandler = f; + return *this; + } + + template <typename Func> self_t& onclose(Func f) + { + closeHandler = f; + return *this; + } + + template <typename Func> self_t& onerror(Func f) + { + errorHandler = f; + return *this; + } + + protected: + std::function<void(crow::websocket::Connection&)> openHandler; + std::function<void(crow::websocket::Connection&, const std::string&, bool)> + messageHandler; + std::function<void(crow::websocket::Connection&, const std::string&)> + closeHandler; + std::function<void(crow::websocket::Connection&)> errorHandler; }; -template <typename T> -struct RuleParameterTraits { - using self_t = T; - WebSocketRule& websocket() { - auto p = new WebSocketRule(((self_t*)this)->rule); - ((self_t*)this)->ruleToUpgrade.reset(p); - return *p; - } - - self_t& name(std::string name) noexcept { - ((self_t*)this)->nameStr = std::move(name); - return (self_t&)*this; - } - - self_t& methods(boost::beast::http::verb method) { - ((self_t*)this)->methodsBitfield = 1 << (int)method; - return (self_t&)*this; - } - - template <typename... MethodArgs> - self_t& methods(boost::beast::http::verb method, MethodArgs... args_method) { - methods(args_method...); - ((self_t*)this)->methodsBitfield |= 1 << (int)method; - return (self_t&)*this; - } +template <typename T> struct RuleParameterTraits +{ + using self_t = T; + WebSocketRule& websocket() + { + auto p = new WebSocketRule(((self_t*)this)->rule); + ((self_t*)this)->ruleToUpgrade.reset(p); + return *p; + } + + self_t& name(std::string name) noexcept + { + ((self_t*)this)->nameStr = std::move(name); + return (self_t&)*this; + } + + self_t& methods(boost::beast::http::verb method) + { + ((self_t*)this)->methodsBitfield = 1 << (int)method; + return (self_t&)*this; + } + + template <typename... MethodArgs> + self_t& methods(boost::beast::http::verb method, MethodArgs... args_method) + { + methods(args_method...); + ((self_t*)this)->methodsBitfield |= 1 << (int)method; + return (self_t&)*this; + } }; -class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> { - public: - DynamicRule(std::string rule) : BaseRule(std::move(rule)) {} - - void validate() override { - if (!erasedHandler) { - throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + - "no handler for url " + rule); - } - } - - void handle(const Request& req, Response& res, - const RoutingParams& params) override { - erasedHandler(req, res, params); - } - - template <typename Func> - void operator()(Func f) { - using function_t = utility::function_traits<Func>; - - erasedHandler = - wrap(std::move(f), black_magic::gen_seq<function_t::arity>()); - } - - // enable_if Arg1 == request && Arg2 == Response - // enable_if Arg1 == request && Arg2 != resposne - // enable_if Arg1 != request - - template <typename Func, unsigned... Indices> - - std::function<void(const Request&, Response&, const RoutingParams&)> wrap( - Func f, black_magic::Seq<Indices...>) { - using function_t = utility::function_traits<Func>; - - if (!black_magic::isParameterTagCompatible( - black_magic::getParameterTagRuntime(rule.c_str()), - black_magic::compute_parameter_tag_from_args_list< - typename function_t::template arg<Indices>...>::value)) { - throw std::runtime_error( - "routeDynamic: Handler type is mismatched with URL parameters: " + - rule); - } - auto ret = detail::routing_handler_call_helper::Wrapped< - Func, typename function_t::template arg<Indices>...>(); - ret.template set<typename function_t::template arg<Indices>...>( - std::move(f)); - return ret; - } - - template <typename Func> - void operator()(std::string name, Func&& f) { - nameStr = std::move(name); - (*this).template operator()<Func>(std::forward(f)); - } - - private: - std::function<void(const Request&, Response&, const RoutingParams&)> - erasedHandler; +class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> +{ + public: + DynamicRule(std::string rule) : BaseRule(std::move(rule)) + { + } + + void validate() override + { + if (!erasedHandler) + { + throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + + "no handler for url " + rule); + } + } + + void handle(const Request& req, Response& res, + const RoutingParams& params) override + { + erasedHandler(req, res, params); + } + + template <typename Func> void operator()(Func f) + { + using function_t = utility::function_traits<Func>; + + erasedHandler = + wrap(std::move(f), black_magic::gen_seq<function_t::arity>()); + } + + // enable_if Arg1 == request && Arg2 == Response + // enable_if Arg1 == request && Arg2 != resposne + // enable_if Arg1 != request + + template <typename Func, unsigned... Indices> + + std::function<void(const Request&, Response&, const RoutingParams&)> + wrap(Func f, black_magic::Seq<Indices...>) + { + using function_t = utility::function_traits<Func>; + + if (!black_magic::isParameterTagCompatible( + black_magic::getParameterTagRuntime(rule.c_str()), + black_magic::compute_parameter_tag_from_args_list< + typename function_t::template arg<Indices>...>::value)) + { + throw std::runtime_error("routeDynamic: Handler type is mismatched " + "with URL parameters: " + + rule); + } + auto ret = detail::routing_handler_call_helper::Wrapped< + Func, typename function_t::template arg<Indices>...>(); + ret.template set<typename function_t::template arg<Indices>...>( + std::move(f)); + return ret; + } + + template <typename Func> void operator()(std::string name, Func&& f) + { + nameStr = std::move(name); + (*this).template operator()<Func>(std::forward(f)); + } + + private: + std::function<void(const Request&, Response&, const RoutingParams&)> + erasedHandler; }; template <typename... Args> class TaggedRule : public BaseRule, - public RuleParameterTraits<TaggedRule<Args...>> { - public: - using self_t = TaggedRule<Args...>; + public RuleParameterTraits<TaggedRule<Args...>> +{ + public: + using self_t = TaggedRule<Args...>; + + TaggedRule(std::string rule) : BaseRule(std::move(rule)) + { + } - TaggedRule(std::string rule) : BaseRule(std::move(rule)) {} + void validate() override + { + if (!handler) + { + throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + + "no handler for url " + rule); + } + } - void validate() override { - if (!handler) { - throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + - "no handler for url " + rule); + template <typename Func> + typename std::enable_if< + black_magic::CallHelper<Func, black_magic::S<Args...>>::value, + void>::type + operator()(Func&& f) + { + static_assert( + black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + !std::is_same<void, decltype(f(std::declval<Args>()...))>::value, + "Handler function cannot have void return type; valid return " + "types: " + "string, int, crow::resposne, nlohmann::json"); + + handler = [f = std::move(f)](const Request&, Response& res, + Args... args) { + res = Response(f(args...)); + res.end(); + }; } - } - template <typename Func> - typename std::enable_if< - black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type - operator()(Func&& f) { - static_assert( - black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + template <typename Func> + typename std::enable_if< + !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && black_magic::CallHelper< Func, black_magic::S<crow::Request, Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - !std::is_same<void, decltype(f(std::declval<Args>()...))>::value, - "Handler function cannot have void return type; valid return types: " - "string, int, crow::resposne, nlohmann::json"); - - handler = [f = std::move(f)](const Request&, Response& res, Args... args) { - res = Response(f(args...)); - res.end(); - }; - } - - template <typename Func> - typename std::enable_if< - !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && - black_magic::CallHelper< - Func, black_magic::S<crow::Request, Args...>>::value, - void>::type - operator()(Func&& f) { - static_assert( - black_magic::CallHelper<Func, black_magic::S<Args...>>::value || - black_magic::CallHelper< + void>::type + operator()(Func&& f) + { + static_assert( + black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + !std::is_same<void, decltype(f(std::declval<crow::Request>(), + std::declval<Args>()...))>::value, + "Handler function cannot have void return type; valid return " + "types: " + "string, int, crow::resposne,nlohmann::json"); + + handler = [f = std::move(f)](const crow::Request& req, + crow::Response& res, Args... args) { + res = Response(f(req, args...)); + res.end(); + }; + } + + template <typename Func> + typename std::enable_if< + !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && + !black_magic::CallHelper< Func, black_magic::S<crow::Request, Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - !std::is_same<void, decltype(f(std::declval<crow::Request>(), - std::declval<Args>()...))>::value, - "Handler function cannot have void return type; valid return types: " - "string, int, crow::resposne,nlohmann::json"); - - handler = [f = std::move(f)](const crow::Request& req, crow::Response& res, - Args... args) { - res = Response(f(req, args...)); - res.end(); - }; - } - - template <typename Func> - typename std::enable_if< - !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && - !black_magic::CallHelper< - Func, black_magic::S<crow::Request, Args...>>::value, - void>::type - operator()(Func&& f) { - static_assert( - black_magic::CallHelper<Func, black_magic::S<Args...>>::value || - black_magic::CallHelper< - Func, black_magic::S<crow::Request, Args...>>::value || - black_magic::CallHelper< - Func, - black_magic::S<crow::Request, crow::Response&, Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - std::is_same<void, decltype(f(std::declval<crow::Request>(), - std::declval<crow::Response&>(), - std::declval<Args>()...))>::value, - "Handler function with response argument should have void return type"); - - handler = std::move(f); - } - - template <typename Func> - void operator()(std::string name, Func&& f) { - nameStr = std::move(name); - (*this).template operator()<Func>(std::forward(f)); - } - - void handle(const Request& req, Response& res, - const RoutingParams& params) override { - detail::routing_handler_call_helper::Call< - detail::routing_handler_call_helper::CallParams<decltype(handler)>, 0, - 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()( - detail::routing_handler_call_helper::CallParams<decltype(handler)>{ - handler, params, req, res}); - } - - private: - std::function<void(const crow::Request&, crow::Response&, Args...)> handler; + void>::type + operator()(Func&& f) + { + static_assert( + black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, crow::Response&, + Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + std::is_same<void, decltype(f(std::declval<crow::Request>(), + std::declval<crow::Response&>(), + std::declval<Args>()...))>::value, + "Handler function with response argument should have void return " + "type"); + + handler = std::move(f); + } + + template <typename Func> void operator()(std::string name, Func&& f) + { + nameStr = std::move(name); + (*this).template operator()<Func>(std::forward(f)); + } + + void handle(const Request& req, Response& res, + const RoutingParams& params) override + { + detail::routing_handler_call_helper::Call< + detail::routing_handler_call_helper::CallParams<decltype(handler)>, + 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()( + detail::routing_handler_call_helper::CallParams<decltype(handler)>{ + handler, params, req, res}); + } + + private: + std::function<void(const crow::Request&, crow::Response&, Args...)> handler; }; const int ruleSpecialRedirectSlash = 1; -class Trie { - public: - struct Node { - unsigned ruleIndex{}; - std::array<unsigned, (int)ParamType::MAX> paramChildrens{}; - boost::container::flat_map<std::string, unsigned> children; - - bool isSimpleNode() const { - return !ruleIndex && - std::all_of(std::begin(paramChildrens), std::end(paramChildrens), - [](unsigned x) { return !x; }); - } - }; - - Trie() : nodes(1) {} - - private: - void optimizeNode(Node* node) { - for (auto x : node->paramChildrens) { - if (!x) continue; - Node* child = &nodes[x]; - optimizeNode(child); - } - if (node->children.empty()) return; - bool mergeWithChild = true; - for (auto& kv : node->children) { - Node* child = &nodes[kv.second]; - if (!child->isSimpleNode()) { - mergeWithChild = false; - break; - } - } - if (mergeWithChild) { - decltype(node->children) merged; - for (auto& kv : node->children) { - Node* child = &nodes[kv.second]; - for (auto& childKv : child->children) { - merged[kv.first + childKv.first] = childKv.second; - } - } - node->children = std::move(merged); - optimizeNode(node); - } else { - for (auto& kv : node->children) { - Node* child = &nodes[kv.second]; - optimizeNode(child); - } - } - } - - void optimize() { optimizeNode(head()); } - - public: - void validate() { - if (!head()->isSimpleNode()) - throw std::runtime_error("Internal error: Trie header should be simple!"); - optimize(); - } - - void findRouteIndexes(const std::string& req_url, - std::vector<unsigned>& route_indexes, - const Node* node = nullptr, unsigned pos = 0) { - if (node == nullptr) { - node = head(); - } - for (auto& kv : node->children) { - const std::string& fragment = kv.first; - const Node* child = &nodes[kv.second]; - if (pos >= req_url.size()) { - if (child->ruleIndex != 0 && fragment != "/") { - route_indexes.push_back(child->ruleIndex); - } - findRouteIndexes(req_url, route_indexes, child, pos + fragment.size()); - } else { - if (req_url.compare(pos, fragment.size(), fragment) == 0) { - findRouteIndexes(req_url, route_indexes, child, - pos + fragment.size()); - } - } - } - } - - std::pair<unsigned, RoutingParams> find( - const boost::string_view req_url, const Node* node = nullptr, - unsigned pos = 0, RoutingParams* params = nullptr) const { - RoutingParams empty; - if (params == nullptr) params = ∅ - - unsigned found{}; - RoutingParams matchParams; - - if (node == nullptr) node = head(); - if (pos == req_url.size()) return {node->ruleIndex, *params}; - - auto updateFound = [&found, - &matchParams](std::pair<unsigned, RoutingParams>& ret) { - if (ret.first && (!found || found > ret.first)) { - found = ret.first; - matchParams = std::move(ret.second); - } +class Trie +{ + public: + struct Node + { + unsigned ruleIndex{}; + std::array<unsigned, (int)ParamType::MAX> paramChildrens{}; + boost::container::flat_map<std::string, unsigned> children; + + bool isSimpleNode() const + { + return !ruleIndex && std::all_of(std::begin(paramChildrens), + std::end(paramChildrens), + [](unsigned x) { return !x; }); + } }; - if (node->paramChildrens[(int)ParamType::INT]) { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+' || c == '-') { - char* eptr; - errno = 0; - long long int value = std::strtoll(req_url.data() + pos, &eptr, 10); - if (errno != ERANGE && eptr != req_url.data() + pos) { - params->intParams.push_back(value); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::INT]], - eptr - req_url.data(), params); - updateFound(ret); - params->intParams.pop_back(); - } - } - } - - if (node->paramChildrens[(int)ParamType::UINT]) { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+') { - char* eptr; - errno = 0; - unsigned long long int value = - std::strtoull(req_url.data() + pos, &eptr, 10); - if (errno != ERANGE && eptr != req_url.data() + pos) { - params->uintParams.push_back(value); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::UINT]], - eptr - req_url.data(), params); - updateFound(ret); - params->uintParams.pop_back(); - } - } - } - - if (node->paramChildrens[(int)ParamType::DOUBLE]) { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') { - char* eptr; - errno = 0; - double value = std::strtod(req_url.data() + pos, &eptr); - if (errno != ERANGE && eptr != req_url.data() + pos) { - params->doubleParams.push_back(value); - auto ret = find(req_url, - &nodes[node->paramChildrens[(int)ParamType::DOUBLE]], - eptr - req_url.data(), params); - updateFound(ret); - params->doubleParams.pop_back(); - } - } - } - - if (node->paramChildrens[(int)ParamType::STRING]) { - size_t epos = pos; - for (; epos < req_url.size(); epos++) { - if (req_url[epos] == '/') break; - } - - if (epos != pos) { - params->stringParams.emplace_back(req_url.substr(pos, epos - pos)); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::STRING]], - epos, params); - updateFound(ret); - params->stringParams.pop_back(); - } - } - - if (node->paramChildrens[(int)ParamType::PATH]) { - size_t epos = req_url.size(); - - if (epos != pos) { - params->stringParams.emplace_back(req_url.substr(pos, epos - pos)); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::PATH]], - epos, params); - updateFound(ret); - params->stringParams.pop_back(); - } - } - - for (auto& kv : node->children) { - const std::string& fragment = kv.first; - const Node* child = &nodes[kv.second]; - - if (req_url.compare(pos, fragment.size(), fragment) == 0) { - auto ret = find(req_url, child, pos + fragment.size(), params); - updateFound(ret); - } - } - - return {found, matchParams}; - } - - void add(const std::string& url, unsigned ruleIndex) { - unsigned idx{0}; - - for (unsigned i = 0; i < url.size(); i++) { - char c = url[i]; - if (c == '<') { - static struct ParamTraits { - ParamType type; - std::string name; - } paramTraits[] = { - {ParamType::INT, "<int>"}, {ParamType::UINT, "<uint>"}, - {ParamType::DOUBLE, "<float>"}, {ParamType::DOUBLE, "<double>"}, - {ParamType::STRING, "<str>"}, {ParamType::STRING, "<string>"}, - {ParamType::PATH, "<path>"}, - }; + Trie() : nodes(1) + { + } + + private: + void optimizeNode(Node* node) + { + for (auto x : node->paramChildrens) + { + if (!x) + continue; + Node* child = &nodes[x]; + optimizeNode(child); + } + if (node->children.empty()) + return; + bool mergeWithChild = true; + for (auto& kv : node->children) + { + Node* child = &nodes[kv.second]; + if (!child->isSimpleNode()) + { + mergeWithChild = false; + break; + } + } + if (mergeWithChild) + { + decltype(node->children) merged; + for (auto& kv : node->children) + { + Node* child = &nodes[kv.second]; + for (auto& childKv : child->children) + { + merged[kv.first + childKv.first] = childKv.second; + } + } + node->children = std::move(merged); + optimizeNode(node); + } + else + { + for (auto& kv : node->children) + { + Node* child = &nodes[kv.second]; + optimizeNode(child); + } + } + } - for (auto& x : paramTraits) { - if (url.compare(i, x.name.size(), x.name) == 0) { - if (!nodes[idx].paramChildrens[(int)x.type]) { - auto newNodeIdx = newNode(); - nodes[idx].paramChildrens[(int)x.type] = newNodeIdx; + void optimize() + { + optimizeNode(head()); + } + + public: + void validate() + { + if (!head()->isSimpleNode()) + throw std::runtime_error( + "Internal error: Trie header should be simple!"); + optimize(); + } + + void findRouteIndexes(const std::string& req_url, + std::vector<unsigned>& route_indexes, + const Node* node = nullptr, unsigned pos = 0) + { + if (node == nullptr) + { + node = head(); + } + for (auto& kv : node->children) + { + const std::string& fragment = kv.first; + const Node* child = &nodes[kv.second]; + if (pos >= req_url.size()) + { + if (child->ruleIndex != 0 && fragment != "/") + { + route_indexes.push_back(child->ruleIndex); + } + findRouteIndexes(req_url, route_indexes, child, + pos + fragment.size()); + } + else + { + if (req_url.compare(pos, fragment.size(), fragment) == 0) + { + findRouteIndexes(req_url, route_indexes, child, + pos + fragment.size()); + } + } + } + } + + std::pair<unsigned, RoutingParams> + find(const boost::string_view req_url, const Node* node = nullptr, + unsigned pos = 0, RoutingParams* params = nullptr) const + { + RoutingParams empty; + if (params == nullptr) + params = ∅ + + unsigned found{}; + RoutingParams matchParams; + + if (node == nullptr) + node = head(); + if (pos == req_url.size()) + return {node->ruleIndex, *params}; + + auto updateFound = + [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) { + if (ret.first && (!found || found > ret.first)) + { + found = ret.first; + matchParams = std::move(ret.second); + } + }; + + if (node->paramChildrens[(int)ParamType::INT]) + { + char c = req_url[pos]; + if ((c >= '0' && c <= '9') || c == '+' || c == '-') + { + char* eptr; + errno = 0; + long long int value = + std::strtoll(req_url.data() + pos, &eptr, 10); + if (errno != ERANGE && eptr != req_url.data() + pos) + { + params->intParams.push_back(value); + auto ret = + find(req_url, + &nodes[node->paramChildrens[(int)ParamType::INT]], + eptr - req_url.data(), params); + updateFound(ret); + params->intParams.pop_back(); + } } - idx = nodes[idx].paramChildrens[(int)x.type]; - i += x.name.size(); - break; - } - } - - i--; - } else { - std::string piece(&c, 1); - if (!nodes[idx].children.count(piece)) { - auto newNodeIdx = newNode(); - nodes[idx].children.emplace(piece, newNodeIdx); - } - idx = nodes[idx].children[piece]; - } - } - if (nodes[idx].ruleIndex) - throw std::runtime_error("handler already exists for " + url); - nodes[idx].ruleIndex = ruleIndex; - } - - private: - void debugNodePrint(Node* n, int level) { - for (int i = 0; i < (int)ParamType::MAX; i++) { - if (n->paramChildrens[i]) { - BMCWEB_LOG_DEBUG << std::string( - 2 * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/; - switch ((ParamType)i) { - case ParamType::INT: - BMCWEB_LOG_DEBUG << "<int>"; - break; - case ParamType::UINT: - BMCWEB_LOG_DEBUG << "<uint>"; - break; - case ParamType::DOUBLE: - BMCWEB_LOG_DEBUG << "<float>"; - break; - case ParamType::STRING: - BMCWEB_LOG_DEBUG << "<str>"; - break; - case ParamType::PATH: - BMCWEB_LOG_DEBUG << "<path>"; - break; - default: - BMCWEB_LOG_DEBUG << "<ERROR>"; - break; - } - - debugNodePrint(&nodes[n->paramChildrens[i]], level + 1); - } - } - for (auto& kv : n->children) { - BMCWEB_LOG_DEBUG << std::string(2 * level, - ' ') /*<< "(" << kv.second << ") "*/ - << kv.first; - debugNodePrint(&nodes[kv.second], level + 1); - } - } - - public: - void debugPrint() { debugNodePrint(head(), 0); } - - private: - const Node* head() const { return &nodes.front(); } - - Node* head() { return &nodes.front(); } - - unsigned newNode() { - nodes.resize(nodes.size() + 1); - return nodes.size() - 1; - } - - std::vector<Node> nodes; + } + + if (node->paramChildrens[(int)ParamType::UINT]) + { + char c = req_url[pos]; + if ((c >= '0' && c <= '9') || c == '+') + { + char* eptr; + errno = 0; + unsigned long long int value = + std::strtoull(req_url.data() + pos, &eptr, 10); + if (errno != ERANGE && eptr != req_url.data() + pos) + { + params->uintParams.push_back(value); + auto ret = + find(req_url, + &nodes[node->paramChildrens[(int)ParamType::UINT]], + eptr - req_url.data(), params); + updateFound(ret); + params->uintParams.pop_back(); + } + } + } + + if (node->paramChildrens[(int)ParamType::DOUBLE]) + { + char c = req_url[pos]; + if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') + { + char* eptr; + errno = 0; + double value = std::strtod(req_url.data() + pos, &eptr); + if (errno != ERANGE && eptr != req_url.data() + pos) + { + params->doubleParams.push_back(value); + auto ret = find( + req_url, + &nodes[node->paramChildrens[(int)ParamType::DOUBLE]], + eptr - req_url.data(), params); + updateFound(ret); + params->doubleParams.pop_back(); + } + } + } + + if (node->paramChildrens[(int)ParamType::STRING]) + { + size_t epos = pos; + for (; epos < req_url.size(); epos++) + { + if (req_url[epos] == '/') + break; + } + + if (epos != pos) + { + params->stringParams.emplace_back( + req_url.substr(pos, epos - pos)); + auto ret = + find(req_url, + &nodes[node->paramChildrens[(int)ParamType::STRING]], + epos, params); + updateFound(ret); + params->stringParams.pop_back(); + } + } + + if (node->paramChildrens[(int)ParamType::PATH]) + { + size_t epos = req_url.size(); + + if (epos != pos) + { + params->stringParams.emplace_back( + req_url.substr(pos, epos - pos)); + auto ret = find( + req_url, &nodes[node->paramChildrens[(int)ParamType::PATH]], + epos, params); + updateFound(ret); + params->stringParams.pop_back(); + } + } + + for (auto& kv : node->children) + { + const std::string& fragment = kv.first; + const Node* child = &nodes[kv.second]; + + if (req_url.compare(pos, fragment.size(), fragment) == 0) + { + auto ret = find(req_url, child, pos + fragment.size(), params); + updateFound(ret); + } + } + + return {found, matchParams}; + } + + void add(const std::string& url, unsigned ruleIndex) + { + unsigned idx{0}; + + for (unsigned i = 0; i < url.size(); i++) + { + char c = url[i]; + if (c == '<') + { + static struct ParamTraits + { + ParamType type; + std::string name; + } paramTraits[] = { + {ParamType::INT, "<int>"}, + {ParamType::UINT, "<uint>"}, + {ParamType::DOUBLE, "<float>"}, + {ParamType::DOUBLE, "<double>"}, + {ParamType::STRING, "<str>"}, + {ParamType::STRING, "<string>"}, + {ParamType::PATH, "<path>"}, + }; + + for (auto& x : paramTraits) + { + if (url.compare(i, x.name.size(), x.name) == 0) + { + if (!nodes[idx].paramChildrens[(int)x.type]) + { + auto newNodeIdx = newNode(); + nodes[idx].paramChildrens[(int)x.type] = newNodeIdx; + } + idx = nodes[idx].paramChildrens[(int)x.type]; + i += x.name.size(); + break; + } + } + + i--; + } + else + { + std::string piece(&c, 1); + if (!nodes[idx].children.count(piece)) + { + auto newNodeIdx = newNode(); + nodes[idx].children.emplace(piece, newNodeIdx); + } + idx = nodes[idx].children[piece]; + } + } + if (nodes[idx].ruleIndex) + throw std::runtime_error("handler already exists for " + url); + nodes[idx].ruleIndex = ruleIndex; + } + + private: + void debugNodePrint(Node* n, int level) + { + for (int i = 0; i < (int)ParamType::MAX; i++) + { + if (n->paramChildrens[i]) + { + BMCWEB_LOG_DEBUG << std::string( + 2 * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/; + switch ((ParamType)i) + { + case ParamType::INT: + BMCWEB_LOG_DEBUG << "<int>"; + break; + case ParamType::UINT: + BMCWEB_LOG_DEBUG << "<uint>"; + break; + case ParamType::DOUBLE: + BMCWEB_LOG_DEBUG << "<float>"; + break; + case ParamType::STRING: + BMCWEB_LOG_DEBUG << "<str>"; + break; + case ParamType::PATH: + BMCWEB_LOG_DEBUG << "<path>"; + break; + default: + BMCWEB_LOG_DEBUG << "<ERROR>"; + break; + } + + debugNodePrint(&nodes[n->paramChildrens[i]], level + 1); + } + } + for (auto& kv : n->children) + { + BMCWEB_LOG_DEBUG + << std::string(2 * level, ' ') /*<< "(" << kv.second << ") "*/ + << kv.first; + debugNodePrint(&nodes[kv.second], level + 1); + } + } + + public: + void debugPrint() + { + debugNodePrint(head(), 0); + } + + private: + const Node* head() const + { + return &nodes.front(); + } + + Node* head() + { + return &nodes.front(); + } + + unsigned newNode() + { + nodes.resize(nodes.size() + 1); + return nodes.size() - 1; + } + + std::vector<Node> nodes; }; -class Router { - public: - Router() : rules(2) {} - - DynamicRule& newRuleDynamic(const std::string& rule) { - std::unique_ptr<DynamicRule> ruleObject = - std::make_unique<DynamicRule>(rule); - DynamicRule* ptr = ruleObject.get(); - internalAddRuleObject(rule, std::move(ruleObject)); - - return *ptr; - } - - template <uint64_t N> - typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& - newRuleTagged(const std::string& rule) { - using RuleT = - typename black_magic::Arguments<N>::type::template rebind<TaggedRule>; - std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); - RuleT* ptr = ruleObject.get(); - - internalAddRuleObject(rule, std::move(ruleObject)); - - return *ptr; - } - - void internalAddRuleObject(const std::string& rule, - std::unique_ptr<BaseRule> ruleObject) { - rules.emplace_back(std::move(ruleObject)); - trie.add(rule, rules.size() - 1); - - // directory case: - // request to `/about' url matches `/about/' rule - if (rule.size() > 2 && rule.back() == '/') { - trie.add(rule.substr(0, rule.size() - 1), rules.size() - 1); - } - } - - void validate() { - trie.validate(); - for (auto& rule : rules) { - if (rule) { - auto upgraded = rule->upgrade(); - if (upgraded) rule = std::move(upgraded); - rule->validate(); - } - } - } - - template <typename Adaptor> - void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) { - auto found = trie.find(req.url); - unsigned ruleIndex = found.first; - if (!ruleIndex) { - BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; - res = Response(boost::beast::http::status::not_found); - res.end(); - return; - } - - if (ruleIndex >= rules.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - if (ruleIndex == ruleSpecialRedirectSlash) { - BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " - << req.url; - res = Response(boost::beast::http::status::moved_permanently); - - // TODO absolute url building - if (req.getHeaderValue("Host").empty()) { - res.addHeader("Location", std::string(req.url) + "/"); - } else { - res.addHeader( - "Location", - req.isSecure ? "https://" - : "http://" + std::string(req.getHeaderValue("Host")) + - std::string(req.url) + "/"); - } - res.end(); - return; - } - - if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == 0) { - BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url - << " with " << req.methodString() << "(" - << (uint32_t)req.method() << ") / " - << rules[ruleIndex]->getMethods(); - res = Response(boost::beast::http::status::not_found); - res.end(); - return; - } - - BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule - << "' " << (uint32_t)req.method() << " / " - << rules[ruleIndex]->getMethods(); - - // any uncaught exceptions become 500s - try { - rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor)); - } catch (std::exception& e) { - BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } catch (...) { - BMCWEB_LOG_ERROR - << "An uncaught exception occurred. The type was unknown " - "so no information was available."; - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - } - - void handle(const Request& req, Response& res) { - auto found = trie.find(req.url); - - unsigned ruleIndex = found.first; - - if (!ruleIndex) { - BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - - if (ruleIndex >= rules.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - if (ruleIndex == ruleSpecialRedirectSlash) { - BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " - << req.url; - res = Response(boost::beast::http::status::moved_permanently); - - // TODO absolute url building - if (req.getHeaderValue("Host").empty()) { - res.addHeader("Location", std::string(req.url) + "/"); - } else { - res.addHeader("Location", (req.isSecure ? "https://" : "http://") + - std::string(req.getHeaderValue("Host")) + - std::string(req.url) + "/"); - } - res.end(); - return; - } - - if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == 0) { - BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url - << " with " << req.methodString() << "(" - << (uint32_t)req.method() << ") / " - << rules[ruleIndex]->getMethods(); - res = Response(boost::beast::http::status::not_found); - res.end(); - return; - } - - BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' " - << (uint32_t)req.method() << " / " - << rules[ruleIndex]->getMethods(); - - // any uncaught exceptions become 500s - try { - rules[ruleIndex]->handle(req, res, found.second); - } catch (std::exception& e) { - BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } catch (...) { - BMCWEB_LOG_ERROR - << "An uncaught exception occurred. The type was unknown " - "so no information was available."; - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - } - - void debugPrint() { trie.debugPrint(); } - - std::vector<const std::string*> getRoutes(const std::string& parent) { - std::vector<unsigned> x; - std::vector<const std::string*> ret; - trie.findRouteIndexes(parent, x); - for (unsigned index : x) { - ret.push_back(&rules[index]->rule); - } - return ret; - } - - private: - std::vector<std::unique_ptr<BaseRule>> rules; - Trie trie; +class Router +{ + public: + Router() : rules(2) + { + } + + DynamicRule& newRuleDynamic(const std::string& rule) + { + std::unique_ptr<DynamicRule> ruleObject = + std::make_unique<DynamicRule>(rule); + DynamicRule* ptr = ruleObject.get(); + internalAddRuleObject(rule, std::move(ruleObject)); + + return *ptr; + } + + template <uint64_t N> + typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& + newRuleTagged(const std::string& rule) + { + using RuleT = typename black_magic::Arguments<N>::type::template rebind< + TaggedRule>; + std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); + RuleT* ptr = ruleObject.get(); + + internalAddRuleObject(rule, std::move(ruleObject)); + + return *ptr; + } + + void internalAddRuleObject(const std::string& rule, + std::unique_ptr<BaseRule> ruleObject) + { + rules.emplace_back(std::move(ruleObject)); + trie.add(rule, rules.size() - 1); + + // directory case: + // request to `/about' url matches `/about/' rule + if (rule.size() > 2 && rule.back() == '/') + { + trie.add(rule.substr(0, rule.size() - 1), rules.size() - 1); + } + } + + void validate() + { + trie.validate(); + for (auto& rule : rules) + { + if (rule) + { + auto upgraded = rule->upgrade(); + if (upgraded) + rule = std::move(upgraded); + rule->validate(); + } + } + } + + template <typename Adaptor> + void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) + { + auto found = trie.find(req.url); + unsigned ruleIndex = found.first; + if (!ruleIndex) + { + BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; + res = Response(boost::beast::http::status::not_found); + res.end(); + return; + } + + if (ruleIndex >= rules.size()) + throw std::runtime_error("Trie internal structure corrupted!"); + + if (ruleIndex == ruleSpecialRedirectSlash) + { + BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " + << req.url; + res = Response(boost::beast::http::status::moved_permanently); + + // TODO absolute url building + if (req.getHeaderValue("Host").empty()) + { + res.addHeader("Location", std::string(req.url) + "/"); + } + else + { + res.addHeader( + "Location", + req.isSecure + ? "https://" + : "http://" + std::string(req.getHeaderValue("Host")) + + std::string(req.url) + "/"); + } + res.end(); + return; + } + + if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == + 0) + { + BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url + << " with " << req.methodString() << "(" + << (uint32_t)req.method() << ") / " + << rules[ruleIndex]->getMethods(); + res = Response(boost::beast::http::status::not_found); + res.end(); + return; + } + + BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule + << "' " << (uint32_t)req.method() << " / " + << rules[ruleIndex]->getMethods(); + + // any uncaught exceptions become 500s + try + { + rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor)); + } + catch (std::exception& e) + { + BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + catch (...) + { + BMCWEB_LOG_ERROR + << "An uncaught exception occurred. The type was unknown " + "so no information was available."; + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + } + + void handle(const Request& req, Response& res) + { + auto found = trie.find(req.url); + + unsigned ruleIndex = found.first; + + if (!ruleIndex) + { + BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + + if (ruleIndex >= rules.size()) + throw std::runtime_error("Trie internal structure corrupted!"); + + if (ruleIndex == ruleSpecialRedirectSlash) + { + BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " + << req.url; + res = Response(boost::beast::http::status::moved_permanently); + + // TODO absolute url building + if (req.getHeaderValue("Host").empty()) + { + res.addHeader("Location", std::string(req.url) + "/"); + } + else + { + res.addHeader("Location", + (req.isSecure ? "https://" : "http://") + + std::string(req.getHeaderValue("Host")) + + std::string(req.url) + "/"); + } + res.end(); + return; + } + + if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == + 0) + { + BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url + << " with " << req.methodString() << "(" + << (uint32_t)req.method() << ") / " + << rules[ruleIndex]->getMethods(); + res = Response(boost::beast::http::status::not_found); + res.end(); + return; + } + + BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' " + << (uint32_t)req.method() << " / " + << rules[ruleIndex]->getMethods(); + + // any uncaught exceptions become 500s + try + { + rules[ruleIndex]->handle(req, res, found.second); + } + catch (std::exception& e) + { + BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + catch (...) + { + BMCWEB_LOG_ERROR + << "An uncaught exception occurred. The type was unknown " + "so no information was available."; + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + } + + void debugPrint() + { + trie.debugPrint(); + } + + std::vector<const std::string*> getRoutes(const std::string& parent) + { + std::vector<unsigned> x; + std::vector<const std::string*> ret; + trie.findRouteIndexes(parent, x); + for (unsigned index : x) + { + ret.push_back(&rules[index]->rule); + } + return ret; + } + + private: + std::vector<std::unique_ptr<BaseRule>> rules; + Trie trie; }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/socket_adaptors.h b/crow/include/crow/socket_adaptors.h index 1d43ca2faa..a47697f2a5 100644 --- a/crow/include/crow/socket_adaptors.h +++ b/crow/include/crow/socket_adaptors.h @@ -1,144 +1,200 @@ #pragma once -#include "crow/logging.h" - #include <boost/asio.hpp> #include <boost/lexical_cast.hpp> +#include "crow/logging.h" + #ifdef BMCWEB_ENABLE_SSL #include <boost/asio/ssl.hpp> #endif -namespace crow { +namespace crow +{ using namespace boost; using tcp = asio::ip::tcp; -struct SocketAdaptor { - using streamType = tcp::socket; - using secure = std::false_type; - using context = void; - SocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) - : socketCls(ioService) {} +struct SocketAdaptor +{ + using streamType = tcp::socket; + using secure = std::false_type; + using context = void; + SocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) : + socketCls(ioService) + { + } - boost::asio::io_service& getIoService() { return socketCls.get_io_service(); } + boost::asio::io_service& getIoService() + { + return socketCls.get_io_service(); + } - tcp::socket& rawSocket() { return socketCls; } + tcp::socket& rawSocket() + { + return socketCls; + } - tcp::socket& socket() { return socketCls; } + tcp::socket& socket() + { + return socketCls; + } - std::string remoteEndpoint() { - boost::system::error_code ec; - tcp::endpoint ep = socketCls.remote_endpoint(ec); - if (ec) { - return ""; + std::string remoteEndpoint() + { + boost::system::error_code ec; + tcp::endpoint ep = socketCls.remote_endpoint(ec); + if (ec) + { + return ""; + } + return boost::lexical_cast<std::string>(ep); } - return boost::lexical_cast<std::string>(ep); - } - bool isOpen() { return socketCls.is_open(); } + bool isOpen() + { + return socketCls.is_open(); + } - void close() { socketCls.close(); } + void close() + { + socketCls.close(); + } - template <typename F> - void start(F f) { - boost::system::error_code ec; - f(ec); - } + template <typename F> void start(F f) + { + boost::system::error_code ec; + f(ec); + } - tcp::socket socketCls; + tcp::socket socketCls; }; -struct TestSocketAdaptor { - using secure = std::false_type; - using context = void; - TestSocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) - : socketCls(ioService) {} +struct TestSocketAdaptor +{ + using secure = std::false_type; + using context = void; + TestSocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) : + socketCls(ioService) + { + } - boost::asio::io_service& getIoService() { return socketCls.get_io_service(); } + boost::asio::io_service& getIoService() + { + return socketCls.get_io_service(); + } - tcp::socket& rawSocket() { return socketCls; } + tcp::socket& rawSocket() + { + return socketCls; + } - tcp::socket& socket() { return socketCls; } + tcp::socket& socket() + { + return socketCls; + } - std::string remoteEndpoint() { return "Testhost"; } + std::string remoteEndpoint() + { + return "Testhost"; + } - bool isOpen() { return socketCls.is_open(); } + bool isOpen() + { + return socketCls.is_open(); + } - void close() { socketCls.close(); } + void close() + { + socketCls.close(); + } - template <typename F> - void start(F f) { - f(boost::system::error_code()); - } + template <typename F> void start(F f) + { + f(boost::system::error_code()); + } - tcp::socket socketCls; + tcp::socket socketCls; }; #ifdef BMCWEB_ENABLE_SSL -struct SSLAdaptor { - using streamType = boost::asio::ssl::stream<tcp::socket>; - using secure = std::true_type; - using context = boost::asio::ssl::context; - using ssl_socket_t = boost::asio::ssl::stream<tcp::socket>; - SSLAdaptor(boost::asio::io_service& ioService, context* ctx) - : sslSocket(new ssl_socket_t(ioService, *ctx)) {} - - boost::asio::ssl::stream<tcp::socket>& socket() { return *sslSocket; } - - tcp::socket::lowest_layer_type& rawSocket() { - return sslSocket->lowest_layer(); - } - - std::string remoteEndpoint() { - boost::system::error_code ec; - tcp::endpoint ep = rawSocket().remote_endpoint(ec); - if (ec) { - return ""; - } - return boost::lexical_cast<std::string>(ep); - } - - bool isOpen() { - /*TODO(ed) this is a bit of a cheat. - There are cases when running a websocket where sslSocket might have - std::move() called on it (to transfer ownership to websocket::Connection) - and be empty. This (and the check on close()) is a cheat to do something - sane in this scenario. the correct fix would likely involve changing the - http parser to return a specific code meaning "has been upgraded" so that - the doRead function knows not to try to close the Connection which would - fail, because the adapter is gone. As is, doRead believes the parse - failed, because isOpen now returns False (which could also mean the client - disconnected during parse) - UPdate: The parser does in fact have an "isUpgrade" method that is - intended for exactly this purpose. Todo is now to make doRead obey the - flag appropriately so this code can be changed back. - */ - if (sslSocket != nullptr) { - return sslSocket->lowest_layer().is_open(); - } - return false; - } - - void close() { - if (sslSocket == nullptr) { - return; - } - boost::system::error_code ec; - - // Shut it down - this->sslSocket->lowest_layer().close(); - } - - boost::asio::io_service& getIoService() { - return rawSocket().get_io_service(); - } - - template <typename F> - void start(F f) { - sslSocket->async_handshake( - boost::asio::ssl::stream_base::server, - [f](const boost::system::error_code& ec) { f(ec); }); - } - - std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> sslSocket; +struct SSLAdaptor +{ + using streamType = boost::asio::ssl::stream<tcp::socket>; + using secure = std::true_type; + using context = boost::asio::ssl::context; + using ssl_socket_t = boost::asio::ssl::stream<tcp::socket>; + SSLAdaptor(boost::asio::io_service& ioService, context* ctx) : + sslSocket(new ssl_socket_t(ioService, *ctx)) + { + } + + boost::asio::ssl::stream<tcp::socket>& socket() + { + return *sslSocket; + } + + tcp::socket::lowest_layer_type& rawSocket() + { + return sslSocket->lowest_layer(); + } + + std::string remoteEndpoint() + { + boost::system::error_code ec; + tcp::endpoint ep = rawSocket().remote_endpoint(ec); + if (ec) + { + return ""; + } + return boost::lexical_cast<std::string>(ep); + } + + bool isOpen() + { + /*TODO(ed) this is a bit of a cheat. + There are cases when running a websocket where sslSocket might have + std::move() called on it (to transfer ownership to + websocket::Connection) and be empty. This (and the check on close()) is + a cheat to do something sane in this scenario. the correct fix would + likely involve changing the http parser to return a specific code + meaning "has been upgraded" so that the doRead function knows not to try + to close the Connection which would fail, because the adapter is gone. + As is, doRead believes the parse failed, because isOpen now returns + False (which could also mean the client disconnected during parse) + UPdate: The parser does in fact have an "isUpgrade" method that is + intended for exactly this purpose. Todo is now to make doRead obey the + flag appropriately so this code can be changed back. + */ + if (sslSocket != nullptr) + { + return sslSocket->lowest_layer().is_open(); + } + return false; + } + + void close() + { + if (sslSocket == nullptr) + { + return; + } + boost::system::error_code ec; + + // Shut it down + this->sslSocket->lowest_layer().close(); + } + + boost::asio::io_service& getIoService() + { + return rawSocket().get_io_service(); + } + + template <typename F> void start(F f) + { + sslSocket->async_handshake( + boost::asio::ssl::stream_base::server, + [f](const boost::system::error_code& ec) { f(ec); }); + } + + std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> sslSocket; }; #endif -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/timer_queue.h b/crow/include/crow/timer_queue.h index f5bb467a1f..bf1e084a00 100644 --- a/crow/include/crow/timer_queue.h +++ b/crow/include/crow/timer_queue.h @@ -1,63 +1,78 @@ #pragma once +#include <boost/circular_buffer.hpp> +#include <boost/circular_buffer/space_optimized.hpp> #include <chrono> #include <functional> + #include "crow/logging.h" -#include <boost/circular_buffer.hpp> -#include <boost/circular_buffer/space_optimized.hpp> -namespace crow { -namespace detail { +namespace crow +{ +namespace detail +{ // fast timer queue for fixed tick value. -class TimerQueue { - public: - TimerQueue() { dq.set_capacity(100); } - - void cancel(int k) { - unsigned int index = static_cast<unsigned int>(k - step); - if (index < dq.size()) { - dq[index].second = nullptr; +class TimerQueue +{ + public: + TimerQueue() + { + dq.set_capacity(100); } - } - - int add(std::function<void()> f) { - dq.push_back( - std::make_pair(std::chrono::steady_clock::now(), std::move(f))); - int ret = step + dq.size() - 1; - - BMCWEB_LOG_DEBUG << "timer add inside: " << this << ' ' << ret; - return ret; - } - - void process() { - auto now = std::chrono::steady_clock::now(); - while (!dq.empty()) { - auto& x = dq.front(); - if (now - x.first < std::chrono::seconds(5)) { - break; - } - if (x.second) { - BMCWEB_LOG_DEBUG << "timer call: " << this << ' ' << step; - // we know that timer handlers are very simple currenty; call here - x.second(); - } - dq.pop_front(); - step++; + + void cancel(int k) + { + unsigned int index = static_cast<unsigned int>(k - step); + if (index < dq.size()) + { + dq[index].second = nullptr; + } + } + + int add(std::function<void()> f) + { + dq.push_back( + std::make_pair(std::chrono::steady_clock::now(), std::move(f))); + int ret = step + dq.size() - 1; + + BMCWEB_LOG_DEBUG << "timer add inside: " << this << ' ' << ret; + return ret; + } + + void process() + { + auto now = std::chrono::steady_clock::now(); + while (!dq.empty()) + { + auto& x = dq.front(); + if (now - x.first < std::chrono::seconds(5)) + { + break; + } + if (x.second) + { + BMCWEB_LOG_DEBUG << "timer call: " << this << ' ' << step; + // we know that timer handlers are very simple currenty; call + // here + x.second(); + } + dq.pop_front(); + step++; + } } - } - private: - using storage_type = - std::pair<std::chrono::time_point<std::chrono::steady_clock>, - std::function<void()>>; + private: + using storage_type = + std::pair<std::chrono::time_point<std::chrono::steady_clock>, + std::function<void()>>; - boost::circular_buffer_space_optimized<storage_type, - std::allocator<storage_type>> - dq{}; + boost::circular_buffer_space_optimized<storage_type, + std::allocator<storage_type>> + dq{}; - // boost::circular_buffer<storage_type> dq{20}; - // std::deque<storage_type> dq{}; - int step{}; + // boost::circular_buffer<storage_type> dq{20}; + // std::deque<storage_type> dq{}; + int step{}; }; -} // namespace detail -} // namespace crow +} // namespace detail +} // namespace crow diff --git a/crow/include/crow/utility.h b/crow/include/crow/utility.h index d2557b0af5..9d34e7113b 100644 --- a/crow/include/crow/utility.h +++ b/crow/include/crow/utility.h @@ -1,105 +1,136 @@ #pragma once +#include "nlohmann/json.hpp" + +#include <boost/utility/string_view.hpp> #include <cstdint> #include <cstring> #include <functional> #include <stdexcept> #include <string> #include <tuple> -#include "nlohmann/json.hpp" -#include <boost/utility/string_view.hpp> -namespace crow { -namespace black_magic { -struct OutOfRange { - OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} +namespace crow +{ +namespace black_magic +{ +struct OutOfRange +{ + OutOfRange(unsigned /*pos*/, unsigned /*length*/) + { + } }; -constexpr unsigned requiresInRange(unsigned i, unsigned len) { - return i >= len ? throw OutOfRange(i, len) : i; +constexpr unsigned requiresInRange(unsigned i, unsigned len) +{ + return i >= len ? throw OutOfRange(i, len) : i; } -class ConstStr { - const char* const beginPtr; - unsigned sizeUint; - - public: - template <unsigned N> - constexpr ConstStr(const char (&arr)[N]) : beginPtr(arr), sizeUint(N - 1) { - static_assert(N >= 1, "not a string literal"); - } - constexpr char operator[](unsigned i) const { - return requiresInRange(i, sizeUint), beginPtr[i]; - } +class ConstStr +{ + const char* const beginPtr; + unsigned sizeUint; + + public: + template <unsigned N> + constexpr ConstStr(const char (&arr)[N]) : beginPtr(arr), sizeUint(N - 1) + { + static_assert(N >= 1, "not a string literal"); + } + constexpr char operator[](unsigned i) const + { + return requiresInRange(i, sizeUint), beginPtr[i]; + } - constexpr operator const char*() const { return beginPtr; } + constexpr operator const char*() const + { + return beginPtr; + } - constexpr const char* begin() const { return beginPtr; } - constexpr const char* end() const { return beginPtr + sizeUint; } + constexpr const char* begin() const + { + return beginPtr; + } + constexpr const char* end() const + { + return beginPtr + sizeUint; + } - constexpr unsigned size() const { return sizeUint; } + constexpr unsigned size() const + { + return sizeUint; + } }; -constexpr unsigned findClosingTag(ConstStr s, unsigned p) { - return s[p] == '>' ? p : findClosingTag(s, p + 1); +constexpr unsigned findClosingTag(ConstStr s, unsigned p) +{ + return s[p] == '>' ? p : findClosingTag(s, p + 1); } -constexpr bool isValid(ConstStr s, unsigned i = 0, int f = 0) { - return i == s.size() - ? f == 0 - : f < 0 || f >= 2 - ? false - : s[i] == '<' ? isValid(s, i + 1, f + 1) - : s[i] == '>' ? isValid(s, i + 1, f - 1) - : isValid(s, i + 1, f); +constexpr bool isValid(ConstStr s, unsigned i = 0, int f = 0) +{ + return i == s.size() + ? f == 0 + : f < 0 || f >= 2 + ? false + : s[i] == '<' ? isValid(s, i + 1, f + 1) + : s[i] == '>' ? isValid(s, i + 1, f - 1) + : isValid(s, i + 1, f); } -constexpr bool isEquP(const char* a, const char* b, unsigned n) { - return *a == 0 && *b == 0 && n == 0 - ? true - : (*a == 0 || *b == 0) - ? false - : n == 0 ? true - : *a != *b ? false : isEquP(a + 1, b + 1, n - 1); +constexpr bool isEquP(const char* a, const char* b, unsigned n) +{ + return *a == 0 && *b == 0 && n == 0 + ? true + : (*a == 0 || *b == 0) + ? false + : n == 0 ? true + : *a != *b ? false : isEquP(a + 1, b + 1, n - 1); } constexpr bool isEquN(ConstStr a, unsigned ai, ConstStr b, unsigned bi, - unsigned n) { - return ai + n > a.size() || bi + n > b.size() - ? false - : n == 0 ? true - : a[ai] != b[bi] ? false - : isEquN(a, ai + 1, b, bi + 1, n - 1); + unsigned n) +{ + return ai + n > a.size() || bi + n > b.size() + ? false + : n == 0 ? true + : a[ai] != b[bi] ? false + : isEquN(a, ai + 1, b, bi + 1, n - 1); } -constexpr bool isInt(ConstStr s, unsigned i) { - return isEquN(s, i, "<int>", 0, 5); +constexpr bool isInt(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<int>", 0, 5); } -constexpr bool isUint(ConstStr s, unsigned i) { - return isEquN(s, i, "<uint>", 0, 6); +constexpr bool isUint(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<uint>", 0, 6); } -constexpr bool isFloat(ConstStr s, unsigned i) { - return isEquN(s, i, "<float>", 0, 7) || isEquN(s, i, "<double>", 0, 8); +constexpr bool isFloat(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<float>", 0, 7) || isEquN(s, i, "<double>", 0, 8); } -constexpr bool isStr(ConstStr s, unsigned i) { - return isEquN(s, i, "<str>", 0, 5) || isEquN(s, i, "<string>", 0, 8); +constexpr bool isStr(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<str>", 0, 5) || isEquN(s, i, "<string>", 0, 8); } -constexpr bool isPath(ConstStr s, unsigned i) { - return isEquN(s, i, "<path>", 0, 6); +constexpr bool isPath(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<path>", 0, 6); } -template <typename T> -struct parameter_tag { - static const int value = 0; -}; -#define BMCWEB_INTERNAL_PARAMETER_TAG(t, i) \ - template <> \ - struct parameter_tag<t> { \ - static const int value = i; \ - } +template <typename T> struct parameter_tag +{ + static const int value = 0; +}; +#define BMCWEB_INTERNAL_PARAMETER_TAG(t, i) \ + template <> struct parameter_tag<t> \ + { \ + static const int value = i; \ + } BMCWEB_INTERNAL_PARAMETER_TAG(int, 1); BMCWEB_INTERNAL_PARAMETER_TAG(char, 1); BMCWEB_INTERNAL_PARAMETER_TAG(short, 1); @@ -113,279 +144,290 @@ BMCWEB_INTERNAL_PARAMETER_TAG(unsigned long long, 2); BMCWEB_INTERNAL_PARAMETER_TAG(double, 3); BMCWEB_INTERNAL_PARAMETER_TAG(std::string, 4); #undef BMCWEB_INTERNAL_PARAMETER_TAG -template <typename... Args> -struct compute_parameter_tag_from_args_list; +template <typename... Args> struct compute_parameter_tag_from_args_list; -template <> -struct compute_parameter_tag_from_args_list<> { - static const int value = 0; +template <> struct compute_parameter_tag_from_args_list<> +{ + static const int value = 0; }; template <typename Arg, typename... Args> -struct compute_parameter_tag_from_args_list<Arg, Args...> { - static const int subValue = - compute_parameter_tag_from_args_list<Args...>::value; - static const int value = - parameter_tag<typename std::decay<Arg>::type>::value - ? subValue * 6 + parameter_tag<typename std::decay<Arg>::type>::value - : subValue; -}; - -static inline bool isParameterTagCompatible(uint64_t a, uint64_t b) { - if (a == 0) { - return b == 0; - } - if (b == 0) { - return a == 0; - } - int sa = a % 6; - int sb = a % 6; - if (sa == 5) { - sa = 4; - } - if (sb == 5) { - sb = 4; - } - if (sa != sb) { - return false; - } - return isParameterTagCompatible(a / 6, b / 6); +struct compute_parameter_tag_from_args_list<Arg, Args...> +{ + static const int subValue = + compute_parameter_tag_from_args_list<Args...>::value; + static const int value = + parameter_tag<typename std::decay<Arg>::type>::value + ? subValue * 6 + + parameter_tag<typename std::decay<Arg>::type>::value + : subValue; +}; + +static inline bool isParameterTagCompatible(uint64_t a, uint64_t b) +{ + if (a == 0) + { + return b == 0; + } + if (b == 0) + { + return a == 0; + } + int sa = a % 6; + int sb = a % 6; + if (sa == 5) + { + sa = 4; + } + if (sb == 5) + { + sb = 4; + } + if (sa != sb) + { + return false; + } + return isParameterTagCompatible(a / 6, b / 6); } -static inline unsigned findClosingTagRuntime(const char* s, unsigned p) { - return s[p] == 0 ? throw std::runtime_error("unmatched tag <") - : s[p] == '>' ? p : findClosingTagRuntime(s, p + 1); +static inline unsigned findClosingTagRuntime(const char* s, unsigned p) +{ + return s[p] == 0 ? throw std::runtime_error("unmatched tag <") + : s[p] == '>' ? p : findClosingTagRuntime(s, p + 1); } -static inline uint64_t getParameterTagRuntime(const char* s, unsigned p = 0) { - return s[p] == 0 - ? 0 - : s[p] == '<' - ? (std::strncmp(s + p, "<int>", 5) == 0 - ? getParameterTagRuntime( - s, findClosingTagRuntime(s, p)) * - 6 + - 1 - : std::strncmp(s + p, "<uint>", 6) == 0 - ? getParameterTagRuntime( - s, findClosingTagRuntime(s, p)) * - 6 + - 2 - : (std::strncmp(s + p, "<float>", 7) == 0 || - std::strncmp(s + p, "<double>", 8) == 0) - ? getParameterTagRuntime( - s, findClosingTagRuntime(s, p)) * - 6 + - 3 - : (std::strncmp(s + p, "<str>", 5) == 0 || - std::strncmp(s + p, "<string>", 8) == - 0) - ? getParameterTagRuntime( - s, - findClosingTagRuntime(s, p)) * - 6 + - 4 - : std::strncmp(s + p, "<path>", - 6) == 0 - ? getParameterTagRuntime( - s, - findClosingTagRuntime( - s, p)) * - 6 + - 5 - : throw std::runtime_error( - "invalid parameter " - "type")) - : getParameterTagRuntime(s, p + 1); +static inline uint64_t getParameterTagRuntime(const char* s, unsigned p = 0) +{ + return s[p] == 0 + ? 0 + : s[p] == '<' + ? (std::strncmp(s + p, "<int>", 5) == 0 + ? getParameterTagRuntime( + s, findClosingTagRuntime(s, p)) * + 6 + + 1 + : std::strncmp(s + p, "<uint>", 6) == 0 + ? getParameterTagRuntime( + s, findClosingTagRuntime(s, p)) * + 6 + + 2 + : (std::strncmp(s + p, "<float>", 7) == 0 || + std::strncmp(s + p, "<double>", 8) == 0) + ? getParameterTagRuntime( + s, findClosingTagRuntime(s, p)) * + 6 + + 3 + : (std::strncmp(s + p, "<str>", 5) == + 0 || + std::strncmp(s + p, "<string>", 8) == + 0) + ? getParameterTagRuntime( + s, findClosingTagRuntime( + s, p)) * + 6 + + 4 + : std::strncmp(s + p, "<path>", + 6) == 0 + ? getParameterTagRuntime( + s, + findClosingTagRuntime( + s, p)) * + 6 + + 5 + : throw std::runtime_error( + "invalid parameter " + "type")) + : getParameterTagRuntime(s, p + 1); } -constexpr uint64_t get_parameter_tag(ConstStr s, unsigned p = 0) { - return p == s.size() - ? 0 - : s[p] == '<' - ? (isInt(s, p) - ? get_parameter_tag(s, findClosingTag(s, p)) * 6 + 1 - : isUint(s, p) - ? get_parameter_tag(s, findClosingTag(s, p)) * - 6 + - 2 - : isFloat(s, p) - ? get_parameter_tag( - s, findClosingTag(s, p)) * - 6 + - 3 - : isStr(s, p) - ? get_parameter_tag( - s, findClosingTag(s, p)) * - 6 + - 4 - : isPath(s, p) - ? get_parameter_tag( - s, - findClosingTag(s, p)) * - 6 + - 5 - : throw std::runtime_error( - "invalid parameter " - "type")) - : get_parameter_tag(s, p + 1); +constexpr uint64_t get_parameter_tag(ConstStr s, unsigned p = 0) +{ + return p == s.size() + ? 0 + : s[p] == '<' + ? (isInt(s, p) + ? get_parameter_tag(s, findClosingTag(s, p)) * 6 + 1 + : isUint(s, p) + ? get_parameter_tag(s, findClosingTag(s, p)) * + 6 + + 2 + : isFloat(s, p) + ? get_parameter_tag( + s, findClosingTag(s, p)) * + 6 + + 3 + : isStr(s, p) + ? get_parameter_tag( + s, findClosingTag(s, p)) * + 6 + + 4 + : isPath(s, p) + ? get_parameter_tag( + s, findClosingTag( + s, p)) * + 6 + + 5 + : throw std::runtime_error( + "invalid parameter " + "type")) + : get_parameter_tag(s, p + 1); } -template <typename... T> -struct S { - template <typename U> - using push = S<U, T...>; - template <typename U> - using push_back = S<T..., U>; - template <template <typename... Args> class U> - using rebind = U<T...>; +template <typename... T> struct S +{ + template <typename U> using push = S<U, T...>; + template <typename U> using push_back = S<T..., U>; + template <template <typename... Args> class U> using rebind = U<T...>; }; -template <typename F, typename Set> -struct CallHelper; -template <typename F, typename... Args> -struct CallHelper<F, S<Args...>> { - template <typename F1, typename... Args1, - typename = decltype(std::declval<F1>()(std::declval<Args1>()...))> - static char __test(int); +template <typename F, typename Set> struct CallHelper; +template <typename F, typename... Args> struct CallHelper<F, S<Args...>> +{ + template <typename F1, typename... Args1, + typename = decltype(std::declval<F1>()(std::declval<Args1>()...))> + static char __test(int); - template <typename...> - static int __test(...); + template <typename...> static int __test(...); - static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char); + static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char); }; -template <int N> -struct SingleTagToType {}; +template <int N> struct SingleTagToType +{ +}; -template <> -struct SingleTagToType<1> { - using type = int64_t; +template <> struct SingleTagToType<1> +{ + using type = int64_t; }; -template <> -struct SingleTagToType<2> { - using type = uint64_t; +template <> struct SingleTagToType<2> +{ + using type = uint64_t; }; -template <> -struct SingleTagToType<3> { - using type = double; +template <> struct SingleTagToType<3> +{ + using type = double; }; -template <> -struct SingleTagToType<4> { - using type = std::string; +template <> struct SingleTagToType<4> +{ + using type = std::string; }; -template <> -struct SingleTagToType<5> { - using type = std::string; +template <> struct SingleTagToType<5> +{ + using type = std::string; }; -template <uint64_t Tag> -struct Arguments { - using subarguments = typename Arguments<Tag / 6>::type; - using type = typename subarguments::template push< - typename SingleTagToType<Tag % 6>::type>; +template <uint64_t Tag> struct Arguments +{ + using subarguments = typename Arguments<Tag / 6>::type; + using type = typename subarguments::template push< + typename SingleTagToType<Tag % 6>::type>; }; -template <> -struct Arguments<0> { - using type = S<>; +template <> struct Arguments<0> +{ + using type = S<>; }; -template <typename... T> -struct LastElementType { - using type = - typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type; +template <typename... T> struct LastElementType +{ + using type = + typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type; }; -template <> -struct LastElementType<> {}; +template <> struct LastElementType<> +{ +}; // from // http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth -template <class T> -using Invoke = typename T::type; +template <class T> using Invoke = typename T::type; -template <unsigned...> -struct Seq { - using type = Seq; +template <unsigned...> struct Seq +{ + using type = Seq; }; -template <class S1, class S2> -struct concat; +template <class S1, class S2> struct concat; template <unsigned... I1, unsigned... I2> -struct concat<Seq<I1...>, Seq<I2...>> : Seq<I1..., (sizeof...(I1) + I2)...> {}; +struct concat<Seq<I1...>, Seq<I2...>> : Seq<I1..., (sizeof...(I1) + I2)...> +{ +}; -template <class S1, class S2> -using Concat = Invoke<concat<S1, S2>>; +template <class S1, class S2> using Concat = Invoke<concat<S1, S2>>; -template <unsigned N> -struct gen_seq; -template <unsigned N> -using GenSeq = Invoke<gen_seq<N>>; +template <unsigned N> struct gen_seq; +template <unsigned N> using GenSeq = Invoke<gen_seq<N>>; -template <unsigned N> -struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> {}; +template <unsigned N> struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> +{ +}; -template <> -struct gen_seq<0> : Seq<> {}; -template <> -struct gen_seq<1> : Seq<0> {}; +template <> struct gen_seq<0> : Seq<> +{ +}; +template <> struct gen_seq<1> : Seq<0> +{ +}; -template <typename Seq, typename Tuple> -struct PopBackHelper; +template <typename Seq, typename Tuple> struct PopBackHelper; -template <unsigned... N, typename Tuple> -struct PopBackHelper<Seq<N...>, Tuple> { - template <template <typename... Args> class U> - using rebind = U<typename std::tuple_element<N, Tuple>::type...>; +template <unsigned... N, typename Tuple> struct PopBackHelper<Seq<N...>, Tuple> +{ + template <template <typename... Args> class U> + using rebind = U<typename std::tuple_element<N, Tuple>::type...>; }; template <typename... T> -struct PopBack //: public PopBackHelper<typename - // gen_seq<sizeof...(T)-1>::type, std::tuple<T...>> +struct PopBack //: public PopBackHelper<typename + // gen_seq<sizeof...(T)-1>::type, std::tuple<T...>> { - template <template <typename... Args> class U> - using rebind = - typename PopBackHelper<typename gen_seq<sizeof...(T) - 1>::type, - std::tuple<T...>>::template rebind<U>; + template <template <typename... Args> class U> + using rebind = + typename PopBackHelper<typename gen_seq<sizeof...(T) - 1>::type, + std::tuple<T...>>::template rebind<U>; }; -template <> -struct PopBack<> { - template <template <typename... Args> class U> - using rebind = U<>; +template <> struct PopBack<> +{ + template <template <typename... Args> class U> using rebind = U<>; }; // from // http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type -template <typename Tp, typename... List> -struct Contains : std::true_type {}; +template <typename Tp, typename... List> struct Contains : std::true_type +{ +}; template <typename Tp, typename Head, typename... Rest> struct Contains<Tp, Head, Rest...> : std::conditional<std::is_same<Tp, Head>::value, std::true_type, - Contains<Tp, Rest...>>::type {}; + Contains<Tp, Rest...>>::type +{ +}; -template <typename Tp> -struct Contains<Tp> : std::false_type {}; +template <typename Tp> struct Contains<Tp> : std::false_type +{ +}; -template <typename T> -struct EmptyContext {}; +template <typename T> struct EmptyContext +{ +}; -template <typename T> -struct promote { - using type = T; +template <typename T> struct promote +{ + using type = T; }; -#define BMCWEB_INTERNAL_PROMOTE_TYPE(t1, t2) \ - template <> \ - struct promote<t1> { \ - using type = t2; \ - } +#define BMCWEB_INTERNAL_PROMOTE_TYPE(t1, t2) \ + template <> struct promote<t1> \ + { \ + using type = t2; \ + } BMCWEB_INTERNAL_PROMOTE_TYPE(char, int64_t); BMCWEB_INTERNAL_PROMOTE_TYPE(short, int64_t); @@ -400,204 +442,229 @@ BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t); BMCWEB_INTERNAL_PROMOTE_TYPE(float, double); #undef BMCWEB_INTERNAL_PROMOTE_TYPE -template <typename T> -using promote_t = typename promote<T>::type; +template <typename T> using promote_t = typename promote<T>::type; -} // namespace black_magic +} // namespace black_magic -namespace detail { +namespace detail +{ template <class T, std::size_t N, class... Args> -struct GetIndexOfElementFromTupleByTypeImpl { - static constexpr auto value = N; +struct GetIndexOfElementFromTupleByTypeImpl +{ + static constexpr auto value = N; }; template <class T, std::size_t N, class... Args> -struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...> { - static constexpr auto value = N; +struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...> +{ + static constexpr auto value = N; }; template <class T, std::size_t N, class U, class... Args> -struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...> { - static constexpr auto value = - GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value; +struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...> +{ + static constexpr auto value = + GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value; }; -} // namespace detail +} // namespace detail -namespace utility { -template <class T, class... Args> -T& getElementByType(std::tuple<Args...>& t) { - return std::get< - detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t); +namespace utility +{ +template <class T, class... Args> T& getElementByType(std::tuple<Args...>& t) +{ + return std::get< + detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t); } -template <typename T> -struct function_traits; +template <typename T> struct function_traits; template <typename T> -struct function_traits : public function_traits<decltype(&T::operator())> { - using parent_t = function_traits<decltype(&T::operator())>; - static const size_t arity = parent_t::arity; - using result_type = typename parent_t::result_type; - template <size_t i> - using arg = typename parent_t::template arg<i>; +struct function_traits : public function_traits<decltype(&T::operator())> +{ + using parent_t = function_traits<decltype(&T::operator())>; + static const size_t arity = parent_t::arity; + using result_type = typename parent_t::result_type; + template <size_t i> using arg = typename parent_t::template arg<i>; }; template <typename ClassType, typename r, typename... Args> -struct function_traits<r (ClassType::*)(Args...) const> { - static const size_t arity = sizeof...(Args); +struct function_traits<r (ClassType::*)(Args...) const> +{ + static const size_t arity = sizeof...(Args); - using result_type = r; + using result_type = r; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; }; template <typename ClassType, typename r, typename... Args> -struct function_traits<r (ClassType::*)(Args...)> { - static const size_t arity = sizeof...(Args); +struct function_traits<r (ClassType::*)(Args...)> +{ + static const size_t arity = sizeof...(Args); - using result_type = r; + using result_type = r; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; }; template <typename r, typename... Args> -struct function_traits<std::function<r(Args...)>> { - static const size_t arity = sizeof...(Args); +struct function_traits<std::function<r(Args...)>> +{ + static const size_t arity = sizeof...(Args); - using result_type = r; + using result_type = r; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; }; inline static std::string base64encode( const char* data, size_t size, const char* key = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") { - std::string ret; - ret.resize((size + 2) / 3 * 4); - auto it = ret.begin(); - while (size >= 3) { - *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; - h = (((unsigned char)*data++) & 0x0F) << 2; - *it++ = key[h | ((((unsigned char)*data) & 0xC0) >> 6)]; - *it++ = key[((unsigned char)*data++) & 0x3F]; - - size -= 3; - } - if (size == 1) { - *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h]; - *it++ = '='; - *it++ = '='; - } else if (size == 2) { - *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; - h = (((unsigned char)*data++) & 0x0F) << 2; - *it++ = key[h]; - *it++ = '='; - } - return ret; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") +{ + std::string ret; + ret.resize((size + 2) / 3 * 4); + auto it = ret.begin(); + while (size >= 3) + { + *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; + h = (((unsigned char)*data++) & 0x0F) << 2; + *it++ = key[h | ((((unsigned char)*data) & 0xC0) >> 6)]; + *it++ = key[((unsigned char)*data++) & 0x3F]; + + size -= 3; + } + if (size == 1) + { + *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h]; + *it++ = '='; + *it++ = '='; + } + else if (size == 2) + { + *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; + h = (((unsigned char)*data++) & 0x0F) << 2; + *it++ = key[h]; + *it++ = '='; + } + return ret; } -inline static std::string base64encodeUrlsafe(const char* data, size_t size) { - return base64encode( - data, size, - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); +inline static std::string base64encodeUrlsafe(const char* data, size_t size) +{ + return base64encode( + data, size, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); } // TODO this is temporary and should be deleted once base64 is refactored out of // crow -inline bool base64Decode(const boost::string_view input, std::string& output) { - static const char nop = -1; - // See note on encoding_data[] in above function - static const char decodingData[] = { - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 62, nop, - nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, nop, nop, - nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, nop, nop, nop, nop, nop, nop, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop}; - - size_t inputLength = input.size(); - - // allocate space for output string - output.clear(); - output.reserve(((inputLength + 2) / 3) * 4); - - // for each 4-bytes sequence from the input, extract 4 6-bits sequences by - // droping first two bits - // and regenerate into 3 8-bits sequences - - for (size_t i = 0; i < inputLength; i++) { - char base64code0; - char base64code1; - char base64code2 = 0; // initialized to 0 to suppress warnings - char base64code3; - - base64code0 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code0 == nop) { // non base64 character - return false; - } - if (!(++i < inputLength)) { // we need at least two input bytes for first - // byte output - return false; - } - base64code1 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code1 == nop) { // non base64 character - return false; - } - output += - static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); - - if (++i < inputLength) { - char c = input[i]; - if (c == '=') { // padding , end of input - return (base64code1 & 0x0f) == 0; - } - base64code2 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code2 == nop) { // non base64 character - return false; - } - output += static_cast<char>(((base64code1 << 4) & 0xf0) | - ((base64code2 >> 2) & 0x0f)); - } - - if (++i < inputLength) { - char c = input[i]; - if (c == '=') { // padding , end of input - return (base64code2 & 0x03) == 0; - } - base64code3 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code3 == nop) { // non base64 character - return false; - } - output += static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); +inline bool base64Decode(const boost::string_view input, std::string& output) +{ + static const char nop = -1; + // See note on encoding_data[] in above function + static const char decodingData[] = { + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop}; + + size_t inputLength = input.size(); + + // allocate space for output string + output.clear(); + output.reserve(((inputLength + 2) / 3) * 4); + + // for each 4-bytes sequence from the input, extract 4 6-bits sequences by + // droping first two bits + // and regenerate into 3 8-bits sequences + + for (size_t i = 0; i < inputLength; i++) + { + char base64code0; + char base64code1; + char base64code2 = 0; // initialized to 0 to suppress warnings + char base64code3; + + base64code0 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code0 == nop) + { // non base64 character + return false; + } + if (!(++i < inputLength)) + { // we need at least two input bytes for first + // byte output + return false; + } + base64code1 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code1 == nop) + { // non base64 character + return false; + } + output += + static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); + + if (++i < inputLength) + { + char c = input[i]; + if (c == '=') + { // padding , end of input + return (base64code1 & 0x0f) == 0; + } + base64code2 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code2 == nop) + { // non base64 character + return false; + } + output += static_cast<char>(((base64code1 << 4) & 0xf0) | + ((base64code2 >> 2) & 0x0f)); + } + + if (++i < inputLength) + { + char c = input[i]; + if (c == '=') + { // padding , end of input + return (base64code2 & 0x03) == 0; + } + base64code3 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code3 == nop) + { // non base64 character + return false; + } + output += + static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); + } } - } - return true; + return true; } -} // namespace utility -} // namespace crow +} // namespace utility +} // namespace crow diff --git a/crow/include/crow/websocket.h b/crow/include/crow/websocket.h index 82c6db8d73..f345223ebc 100644 --- a/crow/include/crow/websocket.h +++ b/crow/include/crow/websocket.h @@ -1,206 +1,240 @@ #pragma once #include <array> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/beast/websocket.hpp> #include <functional> + #include "crow/http_request.h" #include "crow/socket_adaptors.h" -#include <boost/algorithm/string/predicate.hpp> -#include <boost/beast/websocket.hpp> #ifdef BMCWEB_ENABLE_SSL #include <boost/beast/websocket/ssl.hpp> #endif -namespace crow { -namespace websocket { -struct Connection : std::enable_shared_from_this<Connection> { - public: - explicit Connection(const crow::Request& req) - : req(req), userdataPtr(nullptr){}; - - virtual void sendBinary(const boost::beast::string_view msg) = 0; - virtual void sendBinary(std::string&& msg) = 0; - virtual void sendText(const boost::beast::string_view msg) = 0; - virtual void sendText(std::string&& msg) = 0; - virtual void close(const boost::beast::string_view msg = "quit") = 0; - virtual boost::asio::io_service& getIoService() = 0; - virtual ~Connection() = default; - - void userdata(void* u) { userdataPtr = u; } - void* userdata() { return userdataPtr; } +namespace crow +{ +namespace websocket +{ +struct Connection : std::enable_shared_from_this<Connection> +{ + public: + explicit Connection(const crow::Request& req) : + req(req), userdataPtr(nullptr){}; + + virtual void sendBinary(const boost::beast::string_view msg) = 0; + virtual void sendBinary(std::string&& msg) = 0; + virtual void sendText(const boost::beast::string_view msg) = 0; + virtual void sendText(std::string&& msg) = 0; + virtual void close(const boost::beast::string_view msg = "quit") = 0; + virtual boost::asio::io_service& getIoService() = 0; + virtual ~Connection() = default; + + void userdata(void* u) + { + userdataPtr = u; + } + void* userdata() + { + return userdataPtr; + } - crow::Request req; + crow::Request req; - private: - void* userdataPtr; + private: + void* userdataPtr; }; -template <typename Adaptor> -class ConnectionImpl : public Connection { - public: - ConnectionImpl( - const crow::Request& req, Adaptor&& adaptorIn, - std::function<void(Connection&)> open_handler, - std::function<void(Connection&, const std::string&, bool)> - message_handler, - std::function<void(Connection&, const std::string&)> close_handler, - std::function<void(Connection&)> error_handler) - : adaptor(std::move(adaptorIn)), - ws(adaptor.socket()), - Connection(req), +template <typename Adaptor> class ConnectionImpl : public Connection +{ + public: + ConnectionImpl( + const crow::Request& req, Adaptor&& adaptorIn, + std::function<void(Connection&)> open_handler, + std::function<void(Connection&, const std::string&, bool)> + message_handler, + std::function<void(Connection&, const std::string&)> close_handler, + std::function<void(Connection&)> error_handler) : + adaptor(std::move(adaptorIn)), + ws(adaptor.socket()), Connection(req), openHandler(std::move(open_handler)), messageHandler(std::move(message_handler)), closeHandler(std::move(close_handler)), - errorHandler(std::move(error_handler)) { - BMCWEB_LOG_DEBUG << "Creating new connection " << this; - } - - boost::asio::io_service& getIoService() override { - return adaptor.getIoService(); - } - - void start() { - BMCWEB_LOG_DEBUG << "starting connection " << this; - - boost::string_view protocol = - req.getHeaderValue(boost::beast::http::field::sec_websocket_protocol); - - // Perform the websocket upgrade - ws.async_accept_ex( - req.req, - [protocol{std::string(protocol)}]( - boost::beast::websocket::response_type & m) { - if (!protocol.empty()) { - m.insert(boost::beast::http::field::sec_websocket_protocol, - protocol); - } - }, - [ this, self(shared_from_this()) ](boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_ERROR << "Error in ws.async_accept " << ec; - return; - } - acceptDone(); - }); - } - - void sendBinary(const boost::beast::string_view msg) override { - ws.binary(true); - outBuffer.emplace_back(msg); - doWrite(); - } - - void sendBinary(std::string&& msg) override { - ws.binary(true); - outBuffer.emplace_back(std::move(msg)); - doWrite(); - } - - void sendText(const boost::beast::string_view msg) override { - ws.text(true); - outBuffer.emplace_back(msg); - doWrite(); - } - - void sendText(std::string&& msg) override { - ws.text(true); - outBuffer.emplace_back(std::move(msg)); - doWrite(); - } - - void close(const boost::beast::string_view msg) override { - ws.async_close( - boost::beast::websocket::close_code::normal, - [ this, self(shared_from_this()) ](boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_ERROR << "Error closing websocket " << ec; - return; - } - adaptor.close(); - }); - } + errorHandler(std::move(error_handler)) + { + BMCWEB_LOG_DEBUG << "Creating new connection " << this; + } - void acceptDone() { - BMCWEB_LOG_DEBUG << "Websocket accepted connection"; + boost::asio::io_service& getIoService() override + { + return adaptor.getIoService(); + } - if (openHandler) { - openHandler(*this); + void start() + { + BMCWEB_LOG_DEBUG << "starting connection " << this; + + boost::string_view protocol = req.getHeaderValue( + boost::beast::http::field::sec_websocket_protocol); + + // Perform the websocket upgrade + ws.async_accept_ex( + req.req, + [protocol{std::string(protocol)}]( + boost::beast::websocket::response_type& m) { + if (!protocol.empty()) + { + m.insert(boost::beast::http::field::sec_websocket_protocol, + protocol); + } + }, + [this, self(shared_from_this())](boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Error in ws.async_accept " << ec; + return; + } + acceptDone(); + }); } - doRead(); - } - - void doRead() { - ws.async_read( - inBuffer, [ this, self(shared_from_this()) ]( - boost::beast::error_code ec, std::size_t bytes_read) { - if (ec) { - if (ec != boost::beast::websocket::error::closed) { - BMCWEB_LOG_ERROR << "doRead error " << ec; - } - if (closeHandler) { - boost::beast::string_view reason = ws.reason().reason; - closeHandler(*this, std::string(reason)); - } - return; - } - if (messageHandler) { - // TODO(Ed) There must be a more direct way to do this conversion, - // but I can't find it at the moment. It should get optimized away - boost::asio::const_buffer cb = - boost::beast::buffers_front(inBuffer.data()); - boost::beast::string_view message( - reinterpret_cast<char const*>(cb.data()), cb.size()); - messageHandler(*this, std::string(message), ws.got_text()); - } - doRead(); - }); - } - - void doWrite() { - // If we're already doing a write, ignore the request, it will be picked up - // when the current write is complete - if (doingWrite) { - return; + + void sendBinary(const boost::beast::string_view msg) override + { + ws.binary(true); + outBuffer.emplace_back(msg); + doWrite(); + } + + void sendBinary(std::string&& msg) override + { + ws.binary(true); + outBuffer.emplace_back(std::move(msg)); + doWrite(); } - if (outBuffer.empty()) { - // Done for now - return; + void sendText(const boost::beast::string_view msg) override + { + ws.text(true); + outBuffer.emplace_back(msg); + doWrite(); } - doingWrite = true; - ws.async_write(boost::asio::buffer(outBuffer.front()), - [ this, self(shared_from_this()) ]( - boost::beast::error_code ec, std::size_t bytes_written) { - doingWrite = false; - outBuffer.erase(outBuffer.begin()); - if (ec == boost::beast::websocket::error::closed) { - // Do nothing here. doRead handler will call the - // closeHandler. - close("Write error"); - return; - } - if (ec) { - BMCWEB_LOG_ERROR << "Error in ws.async_write " << ec; - return; - } - doWrite(); - }); - } - - private: - Adaptor adaptor; - - boost::beast::websocket::stream< - std::add_lvalue_reference_t<typename Adaptor::streamType>> - ws; - - boost::beast::flat_static_buffer<4096> inBuffer; - std::vector<std::string> outBuffer; - bool doingWrite = false; - - std::function<void(Connection&)> openHandler; - std::function<void(Connection&, const std::string&, bool)> messageHandler; - std::function<void(Connection&, const std::string&)> closeHandler; - std::function<void(Connection&)> errorHandler; + + void sendText(std::string&& msg) override + { + ws.text(true); + outBuffer.emplace_back(std::move(msg)); + doWrite(); + } + + void close(const boost::beast::string_view msg) override + { + ws.async_close( + boost::beast::websocket::close_code::normal, + [this, self(shared_from_this())](boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Error closing websocket " << ec; + return; + } + adaptor.close(); + }); + } + + void acceptDone() + { + BMCWEB_LOG_DEBUG << "Websocket accepted connection"; + + if (openHandler) + { + openHandler(*this); + } + doRead(); + } + + void doRead() + { + ws.async_read( + inBuffer, [this, self(shared_from_this())]( + boost::beast::error_code ec, std::size_t bytes_read) { + if (ec) + { + if (ec != boost::beast::websocket::error::closed) + { + BMCWEB_LOG_ERROR << "doRead error " << ec; + } + if (closeHandler) + { + boost::beast::string_view reason = ws.reason().reason; + closeHandler(*this, std::string(reason)); + } + return; + } + if (messageHandler) + { + // TODO(Ed) There must be a more direct way to do this + // conversion, but I can't find it at the moment. It should + // get optimized away + boost::asio::const_buffer cb = + boost::beast::buffers_front(inBuffer.data()); + boost::beast::string_view message( + reinterpret_cast<char const*>(cb.data()), cb.size()); + messageHandler(*this, std::string(message), ws.got_text()); + } + doRead(); + }); + } + + void doWrite() + { + // If we're already doing a write, ignore the request, it will be picked + // up when the current write is complete + if (doingWrite) + { + return; + } + + if (outBuffer.empty()) + { + // Done for now + return; + } + doingWrite = true; + ws.async_write( + boost::asio::buffer(outBuffer.front()), + [this, self(shared_from_this())](boost::beast::error_code ec, + std::size_t bytes_written) { + doingWrite = false; + outBuffer.erase(outBuffer.begin()); + if (ec == boost::beast::websocket::error::closed) + { + // Do nothing here. doRead handler will call the + // closeHandler. + close("Write error"); + return; + } + if (ec) + { + BMCWEB_LOG_ERROR << "Error in ws.async_write " << ec; + return; + } + doWrite(); + }); + } + + private: + Adaptor adaptor; + + boost::beast::websocket::stream< + std::add_lvalue_reference_t<typename Adaptor::streamType>> + ws; + + boost::beast::flat_static_buffer<4096> inBuffer; + std::vector<std::string> outBuffer; + bool doingWrite = false; + + std::function<void(Connection&)> openHandler; + std::function<void(Connection&, const std::string&, bool)> messageHandler; + std::function<void(Connection&, const std::string&)> closeHandler; + std::function<void(Connection&)> errorHandler; }; -} // namespace websocket -} // namespace crow +} // namespace websocket +} // namespace crow diff --git a/include/ast_jpeg_decoder.hpp b/include/ast_jpeg_decoder.hpp index e6e0f08efb..e8bdddbf59 100644 --- a/include/ast_jpeg_decoder.hpp +++ b/include/ast_jpeg_decoder.hpp @@ -1,345 +1,390 @@ #pragma once #include <aspeed/JTABLES.H> -#include <ast_video_types.hpp> + #include <array> +#include <ast_video_types.hpp> #include <cassert> #include <cstdint> #include <cstring> #include <iostream> #include <vector> -namespace ast_video { +namespace ast_video +{ -struct ColorCache { - ColorCache() - : color{0x008080, 0xFF8080, 0x808080, 0xC08080}, index{0, 1, 2, 3} {} +struct ColorCache +{ + ColorCache() : + color{0x008080, 0xFF8080, 0x808080, 0xC08080}, index{0, 1, 2, 3} + { + } - unsigned long color[4]; - unsigned char index[4]; - unsigned char bitMapBits{}; + unsigned long color[4]; + unsigned char index[4]; + unsigned char bitMapBits{}; }; -struct RGB { - unsigned char b; - unsigned char g; - unsigned char r; - unsigned char reserved; +struct RGB +{ + unsigned char b; + unsigned char g; + unsigned char r; + unsigned char reserved; }; -enum class JpgBlock { - JPEG_NO_SKIP_CODE = 0x00, - JPEG_SKIP_CODE = 0x08, +enum class JpgBlock +{ + JPEG_NO_SKIP_CODE = 0x00, + JPEG_SKIP_CODE = 0x08, - JPEG_PASS2_CODE = 0x02, - JPEG_SKIP_PASS2_CODE = 0x0A, + JPEG_PASS2_CODE = 0x02, + JPEG_SKIP_PASS2_CODE = 0x0A, - LOW_JPEG_NO_SKIP_CODE = 0x04, - LOW_JPEG_SKIP_CODE = 0x0C, + LOW_JPEG_NO_SKIP_CODE = 0x04, + LOW_JPEG_SKIP_CODE = 0x0C, - VQ_NO_SKIP_1_COLOR_CODE = 0x05, - VQ_SKIP_1_COLOR_CODE = 0x0D, + VQ_NO_SKIP_1_COLOR_CODE = 0x05, + VQ_SKIP_1_COLOR_CODE = 0x0D, - VQ_NO_SKIP_2_COLOR_CODE = 0x06, - VQ_SKIP_2_COLOR_CODE = 0x0E, + VQ_NO_SKIP_2_COLOR_CODE = 0x06, + VQ_SKIP_2_COLOR_CODE = 0x0E, - VQ_NO_SKIP_4_COLOR_CODE = 0x07, - VQ_SKIP_4_COLOR_CODE = 0x0F, + VQ_NO_SKIP_4_COLOR_CODE = 0x07, + VQ_SKIP_4_COLOR_CODE = 0x0F, - FRAME_END_CODE = 0x09, + FRAME_END_CODE = 0x09, }; -class AstJpegDecoder { - public: - AstJpegDecoder() { - // TODO(ed) figure out how to init this in the constructor - yuvBuffer.resize(1920 * 1200); - outBuffer.resize(1920 * 1200); - for (auto &r : outBuffer) { - r.r = 0x00; - r.g = 0x00; - r.b = 0x00; - r.reserved = 0xAA; - } +class AstJpegDecoder +{ + public: + AstJpegDecoder() + { + // TODO(ed) figure out how to init this in the constructor + yuvBuffer.resize(1920 * 1200); + outBuffer.resize(1920 * 1200); + for (auto &r : outBuffer) + { + r.r = 0x00; + r.g = 0x00; + r.b = 0x00; + r.reserved = 0xAA; + } - int qfactor = 16; - - scalefactor = qfactor; - scalefactoruv = qfactor; - advancescalefactor = 16; - advancescalefactoruv = 16; - initJpgTable(); - } - - void loadQuantTable(std::array<long, 64> &quant_table) { - float scalefactorF[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder - // from zig-zag order - switch (ySelector) { - case 0: - stdLuminanceQt = tbl000Y; - break; - case 1: - stdLuminanceQt = tbl014Y; - break; - case 2: - stdLuminanceQt = tbl029Y; - break; - case 3: - stdLuminanceQt = tbl043Y; - break; - case 4: - stdLuminanceQt = tbl057Y; - break; - case 5: - stdLuminanceQt = tbl071Y; - break; - case 6: - stdLuminanceQt = tbl086Y; - break; - case 7: - stdLuminanceQt = tbl100Y; - break; - } - setQuantTable(stdLuminanceQt, static_cast<uint8_t>(scalefactor), tempQT); + int qfactor = 16; - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; - } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactorF[row] * scalefactorF[col]) * 65536); - j++; - } - } - bytePos += 64; - } - - void loadQuantTableCb(std::array<long, 64> &quant_table) { - float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder from zig-zag order - if (mapping == 0) { - switch (uvSelector) { - case 0: - stdChrominanceQt = tbl000Y; - break; - case 1: - stdChrominanceQt = tbl014Y; - break; - case 2: - stdChrominanceQt = tbl029Y; - break; - case 3: - stdChrominanceQt = tbl043Y; - break; - case 4: - stdChrominanceQt = tbl057Y; - break; - case 5: - stdChrominanceQt = tbl071Y; - break; - case 6: - stdChrominanceQt = tbl086Y; - break; - case 7: - stdChrominanceQt = tbl100Y; - break; - } - } else { - switch (uvSelector) { - case 0: - stdChrominanceQt = tbl000Uv; - break; - case 1: - stdChrominanceQt = tbl014Uv; - break; - case 2: - stdChrominanceQt = tbl029Uv; - break; - case 3: - stdChrominanceQt = tbl043Uv; - break; - case 4: - stdChrominanceQt = tbl057Uv; - break; - case 5: - stdChrominanceQt = tbl071Uv; - break; - case 6: - stdChrominanceQt = tbl086Uv; - break; - case 7: - stdChrominanceQt = tbl100Uv; - break; - } + scalefactor = qfactor; + scalefactoruv = qfactor; + advancescalefactor = 16; + advancescalefactoruv = 16; + initJpgTable(); } - setQuantTable(stdChrominanceQt, static_cast<uint8_t>(scalefactoruv), - tempQT); - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; - } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536); - j++; - } - } - bytePos += 64; - } - // Note: Added for Dual_JPEG - void loadAdvanceQuantTable(std::array<long, 64> &quant_table) { - float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder - // from zig-zag order - switch (advanceSelector) { - case 0: - stdLuminanceQt = tbl000Y; - break; - case 1: - stdLuminanceQt = tbl014Y; - break; - case 2: - stdLuminanceQt = tbl029Y; - break; - case 3: - stdLuminanceQt = tbl043Y; - break; - case 4: - stdLuminanceQt = tbl057Y; - break; - case 5: - stdLuminanceQt = tbl071Y; - break; - case 6: - stdLuminanceQt = tbl086Y; - break; - case 7: - stdLuminanceQt = tbl100Y; - break; - } - // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG - setQuantTable(stdLuminanceQt, static_cast<uint8_t>(advancescalefactor), - tempQT); + void loadQuantTable(std::array<long, 64> &quant_table) + { + float scalefactorF[8] = {1.0f, 1.387039845f, 1.306562965f, + 1.175875602f, 1.0f, 0.785694958f, + 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder + // from zig-zag order + switch (ySelector) + { + case 0: + stdLuminanceQt = tbl000Y; + break; + case 1: + stdLuminanceQt = tbl014Y; + break; + case 2: + stdLuminanceQt = tbl029Y; + break; + case 3: + stdLuminanceQt = tbl043Y; + break; + case 4: + stdLuminanceQt = tbl057Y; + break; + case 5: + stdLuminanceQt = tbl071Y; + break; + case 6: + stdLuminanceQt = tbl086Y; + break; + case 7: + stdLuminanceQt = tbl100Y; + break; + } + setQuantTable(stdLuminanceQt, static_cast<uint8_t>(scalefactor), + tempQT); - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; - } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536); - j++; - } + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactorF[row] * scalefactorF[col]) * + 65536); + j++; + } + } + bytePos += 64; } - bytePos += 64; - } - - // Note: Added for Dual-JPEG - void loadAdvanceQuantTableCb(std::array<long, 64> &quant_table) { - float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder - // from zig-zag order - if (mapping == 1) { - switch (advanceSelector) { - case 0: - stdChrominanceQt = tbl000Y; - break; - case 1: - stdChrominanceQt = tbl014Y; - break; - case 2: - stdChrominanceQt = tbl029Y; - break; - case 3: - stdChrominanceQt = tbl043Y; - break; - case 4: - stdChrominanceQt = tbl057Y; - break; - case 5: - stdChrominanceQt = tbl071Y; - break; - case 6: - stdChrominanceQt = tbl086Y; - break; - case 7: - stdChrominanceQt = tbl100Y; - break; - } - } else { - switch (advanceSelector) { - case 0: - stdChrominanceQt = tbl000Uv; - break; - case 1: - stdChrominanceQt = tbl014Uv; - break; - case 2: - stdChrominanceQt = tbl029Uv; - break; - case 3: - stdChrominanceQt = tbl043Uv; - break; - case 4: - stdChrominanceQt = tbl057Uv; - break; - case 5: - stdChrominanceQt = tbl071Uv; - break; - case 6: - stdChrominanceQt = tbl086Uv; - break; - case 7: - stdChrominanceQt = tbl100Uv; - break; - } + + void loadQuantTableCb(std::array<long, 64> &quant_table) + { + float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder from zig-zag order + if (mapping == 0) + { + switch (uvSelector) + { + case 0: + stdChrominanceQt = tbl000Y; + break; + case 1: + stdChrominanceQt = tbl014Y; + break; + case 2: + stdChrominanceQt = tbl029Y; + break; + case 3: + stdChrominanceQt = tbl043Y; + break; + case 4: + stdChrominanceQt = tbl057Y; + break; + case 5: + stdChrominanceQt = tbl071Y; + break; + case 6: + stdChrominanceQt = tbl086Y; + break; + case 7: + stdChrominanceQt = tbl100Y; + break; + } + } + else + { + switch (uvSelector) + { + case 0: + stdChrominanceQt = tbl000Uv; + break; + case 1: + stdChrominanceQt = tbl014Uv; + break; + case 2: + stdChrominanceQt = tbl029Uv; + break; + case 3: + stdChrominanceQt = tbl043Uv; + break; + case 4: + stdChrominanceQt = tbl057Uv; + break; + case 5: + stdChrominanceQt = tbl071Uv; + break; + case 6: + stdChrominanceQt = tbl086Uv; + break; + case 7: + stdChrominanceQt = tbl100Uv; + break; + } + } + setQuantTable(stdChrominanceQt, static_cast<uint8_t>(scalefactoruv), + tempQT); + + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactor[row] * scalefactor[col]) * + 65536); + j++; + } + } + bytePos += 64; } - // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG - setQuantTable(stdChrominanceQt, static_cast<uint8_t>(advancescalefactoruv), - tempQT); + // Note: Added for Dual_JPEG + void loadAdvanceQuantTable(std::array<long, 64> &quant_table) + { + float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder + // from zig-zag order + switch (advanceSelector) + { + case 0: + stdLuminanceQt = tbl000Y; + break; + case 1: + stdLuminanceQt = tbl014Y; + break; + case 2: + stdLuminanceQt = tbl029Y; + break; + case 3: + stdLuminanceQt = tbl043Y; + break; + case 4: + stdLuminanceQt = tbl057Y; + break; + case 5: + stdLuminanceQt = tbl071Y; + break; + case 6: + stdLuminanceQt = tbl086Y; + break; + case 7: + stdLuminanceQt = tbl100Y; + break; + } + // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG + setQuantTable(stdLuminanceQt, static_cast<uint8_t>(advancescalefactor), + tempQT); - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactor[row] * scalefactor[col]) * + 65536); + j++; + } + } + bytePos += 64; } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536); - j++; - } + + // Note: Added for Dual-JPEG + void loadAdvanceQuantTableCb(std::array<long, 64> &quant_table) + { + float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder + // from zig-zag order + if (mapping == 1) + { + switch (advanceSelector) + { + case 0: + stdChrominanceQt = tbl000Y; + break; + case 1: + stdChrominanceQt = tbl014Y; + break; + case 2: + stdChrominanceQt = tbl029Y; + break; + case 3: + stdChrominanceQt = tbl043Y; + break; + case 4: + stdChrominanceQt = tbl057Y; + break; + case 5: + stdChrominanceQt = tbl071Y; + break; + case 6: + stdChrominanceQt = tbl086Y; + break; + case 7: + stdChrominanceQt = tbl100Y; + break; + } + } + else + { + switch (advanceSelector) + { + case 0: + stdChrominanceQt = tbl000Uv; + break; + case 1: + stdChrominanceQt = tbl014Uv; + break; + case 2: + stdChrominanceQt = tbl029Uv; + break; + case 3: + stdChrominanceQt = tbl043Uv; + break; + case 4: + stdChrominanceQt = tbl057Uv; + break; + case 5: + stdChrominanceQt = tbl071Uv; + break; + case 6: + stdChrominanceQt = tbl086Uv; + break; + case 7: + stdChrominanceQt = tbl100Uv; + break; + } + } + // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG + setQuantTable(stdChrominanceQt, + static_cast<uint8_t>(advancescalefactoruv), tempQT); + + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactor[row] * scalefactor[col]) * + 65536); + j++; + } + } + bytePos += 64; } - bytePos += 64; - } - void idctTransform(short *coef, uint8_t *data, uint8_t nBlock) { + void idctTransform(short *coef, uint8_t *data, uint8_t nBlock) + { #define FIX_1_082392200 ((int)277) /* FIX(1.082392200) */ #define FIX_1_414213562 ((int)362) /* FIX(1.414213562) */ #define FIX_1_847759065 ((int)473) /* FIX(1.847759065) */ @@ -347,1008 +392,1156 @@ class AstJpegDecoder { #define MULTIPLY(var, cons) ((int)((var) * (cons)) >> 8) - int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - int tmp10, tmp11, tmp12, tmp13; - int z5, z10, z11, z12, z13; - int workspace[64]; /* buffers data between passes */ - - short *inptr = coef; - long *quantptr; - int *wsptr = workspace; - unsigned char *outptr; - unsigned char *rLimit = rlimitTable + 128; - int ctr, dcval, dctsize = 8; - - quantptr = &qt[nBlock][0]; - - // Pass 1: process columns from input (inptr), store into work array(wsptr) - - for (ctr = 8; ctr > 0; ctr--) { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if ((inptr[dctsize * 1] | inptr[dctsize * 2] | inptr[dctsize * 3] | - inptr[dctsize * 4] | inptr[dctsize * 5] | inptr[dctsize * 6] | - inptr[dctsize * 7]) == 0) { - /* AC terms all zero */ - dcval = static_cast<int>((inptr[dctsize * 0] * quantptr[dctsize * 0]) >> - 16); - - wsptr[dctsize * 0] = dcval; - wsptr[dctsize * 1] = dcval; - wsptr[dctsize * 2] = dcval; - wsptr[dctsize * 3] = dcval; - wsptr[dctsize * 4] = dcval; - wsptr[dctsize * 5] = dcval; - wsptr[dctsize * 6] = dcval; - wsptr[dctsize * 7] = dcval; - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - continue; - } - - /* Even part */ - - tmp0 = (inptr[dctsize * 0] * quantptr[dctsize * 0]) >> 16; - tmp1 = (inptr[dctsize * 2] * quantptr[dctsize * 2]) >> 16; - tmp2 = (inptr[dctsize * 4] * quantptr[dctsize * 4]) >> 16; - tmp3 = (inptr[dctsize * 6] * quantptr[dctsize * 6]) >> 16; - - tmp10 = tmp0 + tmp2; /* phase 3 */ - tmp11 = tmp0 - tmp2; - - tmp13 = tmp1 + tmp3; /* phases 5-3 */ - tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ - - tmp0 = tmp10 + tmp13; /* phase 2 */ - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - /* Odd part */ - - tmp4 = (inptr[dctsize * 1] * quantptr[dctsize * 1]) >> 16; - tmp5 = (inptr[dctsize * 3] * quantptr[dctsize * 3]) >> 16; - tmp6 = (inptr[dctsize * 5] * quantptr[dctsize * 5]) >> 16; - tmp7 = (inptr[dctsize * 7] * quantptr[dctsize * 7]) >> 16; - - z13 = tmp6 + tmp5; /* phase 6 */ - z10 = tmp6 - tmp5; - z11 = tmp4 + tmp7; - z12 = tmp4 - tmp7; - - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ - - z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ - tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ - tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ - - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 + tmp5; - - wsptr[dctsize * 0] = (tmp0 + tmp7); - wsptr[dctsize * 7] = (tmp0 - tmp7); - wsptr[dctsize * 1] = (tmp1 + tmp6); - wsptr[dctsize * 6] = (tmp1 - tmp6); - wsptr[dctsize * 2] = (tmp2 + tmp5); - wsptr[dctsize * 5] = (tmp2 - tmp5); - wsptr[dctsize * 4] = (tmp3 + tmp4); - wsptr[dctsize * 3] = (tmp3 - tmp4); - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - } + int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + int tmp10, tmp11, tmp12, tmp13; + int z5, z10, z11, z12, z13; + int workspace[64]; /* buffers data between passes */ + + short *inptr = coef; + long *quantptr; + int *wsptr = workspace; + unsigned char *outptr; + unsigned char *rLimit = rlimitTable + 128; + int ctr, dcval, dctsize = 8; + + quantptr = &qt[nBlock][0]; + + // Pass 1: process columns from input (inptr), store into work + // array(wsptr) + + for (ctr = 8; ctr > 0; ctr--) + { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit + * this by short-circuiting the IDCT calculation for any column in + * which all the AC terms are zero. In that case each output is + * equal to the DC coefficient (with scale factor as needed). With + * typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if ((inptr[dctsize * 1] | inptr[dctsize * 2] | inptr[dctsize * 3] | + inptr[dctsize * 4] | inptr[dctsize * 5] | inptr[dctsize * 6] | + inptr[dctsize * 7]) == 0) + { + /* AC terms all zero */ + dcval = static_cast<int>( + (inptr[dctsize * 0] * quantptr[dctsize * 0]) >> 16); + + wsptr[dctsize * 0] = dcval; + wsptr[dctsize * 1] = dcval; + wsptr[dctsize * 2] = dcval; + wsptr[dctsize * 3] = dcval; + wsptr[dctsize * 4] = dcval; + wsptr[dctsize * 5] = dcval; + wsptr[dctsize * 6] = dcval; + wsptr[dctsize * 7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } -/* Pass 2: process rows from work array, store into output array. */ -/* Note that we must descale the results by a factor of 8 == 2**3, */ -/* and also undo the PASS1_BITS scaling. */ + /* Even part */ -//#define RANGE_MASK 1023; //2 bits wider than legal samples -#define PASS1_BITS 0 -#define IDESCALE(x, n) ((int)((x) >> (n))) + tmp0 = (inptr[dctsize * 0] * quantptr[dctsize * 0]) >> 16; + tmp1 = (inptr[dctsize * 2] * quantptr[dctsize * 2]) >> 16; + tmp2 = (inptr[dctsize * 4] * quantptr[dctsize * 4]) >> 16; + tmp3 = (inptr[dctsize * 6] * quantptr[dctsize * 6]) >> 16; - wsptr = workspace; - for (ctr = 0; ctr < dctsize; ctr++) { - outptr = data + ctr * 8; + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the - * time). On machines with very fast multiplication, it's possible that - * the test takes more time than it's worth. In that case this section - * may be commented out. - */ - /* Even part */ + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ - tmp10 = (wsptr[0] + wsptr[4]); - tmp11 = (wsptr[0] - wsptr[4]); + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; - tmp13 = (wsptr[2] + wsptr[6]); - tmp12 = MULTIPLY((int)wsptr[2] - (int)wsptr[6], FIX_1_414213562) - tmp13; + /* Odd part */ - tmp0 = tmp10 + tmp13; - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; + tmp4 = (inptr[dctsize * 1] * quantptr[dctsize * 1]) >> 16; + tmp5 = (inptr[dctsize * 3] * quantptr[dctsize * 3]) >> 16; + tmp6 = (inptr[dctsize * 5] * quantptr[dctsize * 5]) >> 16; + tmp7 = (inptr[dctsize * 7] * quantptr[dctsize * 7]) >> 16; - /* Odd part */ + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; - z13 = wsptr[5] + wsptr[3]; - z10 = wsptr[5] - wsptr[3]; - z11 = wsptr[1] + wsptr[7]; - z12 = wsptr[1] - wsptr[7]; + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ - z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ - tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ - tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 + tmp5; + wsptr[dctsize * 0] = (tmp0 + tmp7); + wsptr[dctsize * 7] = (tmp0 - tmp7); + wsptr[dctsize * 1] = (tmp1 + tmp6); + wsptr[dctsize * 6] = (tmp1 - tmp6); + wsptr[dctsize * 2] = (tmp2 + tmp5); + wsptr[dctsize * 5] = (tmp2 - tmp5); + wsptr[dctsize * 4] = (tmp3 + tmp4); + wsptr[dctsize * 3] = (tmp3 - tmp4); - /* Final output stage: scale down by a factor of 8 and range-limit */ + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } - outptr[0] = rLimit[IDESCALE((tmp0 + tmp7), (PASS1_BITS + 3)) & 1023L]; - outptr[7] = rLimit[IDESCALE((tmp0 - tmp7), (PASS1_BITS + 3)) & 1023L]; - outptr[1] = rLimit[IDESCALE((tmp1 + tmp6), (PASS1_BITS + 3)) & 1023L]; - outptr[6] = rLimit[IDESCALE((tmp1 - tmp6), (PASS1_BITS + 3)) & 1023L]; - outptr[2] = rLimit[IDESCALE((tmp2 + tmp5), (PASS1_BITS + 3)) & 1023L]; - outptr[5] = rLimit[IDESCALE((tmp2 - tmp5), (PASS1_BITS + 3)) & 1023L]; - outptr[4] = rLimit[IDESCALE((tmp3 + tmp4), (PASS1_BITS + 3)) & 1023L]; - outptr[3] = rLimit[IDESCALE((tmp3 - tmp4), (PASS1_BITS + 3)) & 1023L]; +/* Pass 2: process rows from work array, store into output array. */ +/* Note that we must descale the results by a factor of 8 == 2**3, */ +/* and also undo the PASS1_BITS scaling. */ + +//#define RANGE_MASK 1023; //2 bits wider than legal samples +#define PASS1_BITS 0 +#define IDESCALE(x, n) ((int)((x) >> (n))) - wsptr += dctsize; /* advance pointer to next row */ + wsptr = workspace; + for (ctr = 0; ctr < dctsize; ctr++) + { + outptr = data + ctr * 8; + + /* Rows of zeroes can be exploited in the same way as we did with + * columns. However, the column calculation has created many nonzero + * AC terms, so the simplification applies less often (typically 5% + * to 10% of the time). On machines with very fast multiplication, + * it's possible that the test takes more time than it's worth. In + * that case this section may be commented out. + */ + /* Even part */ + + tmp10 = (wsptr[0] + wsptr[4]); + tmp11 = (wsptr[0] - wsptr[4]); + + tmp13 = (wsptr[2] + wsptr[6]); + tmp12 = MULTIPLY((int)wsptr[2] - (int)wsptr[6], FIX_1_414213562) - + tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit + */ + + outptr[0] = + rLimit[IDESCALE((tmp0 + tmp7), (PASS1_BITS + 3)) & 1023L]; + outptr[7] = + rLimit[IDESCALE((tmp0 - tmp7), (PASS1_BITS + 3)) & 1023L]; + outptr[1] = + rLimit[IDESCALE((tmp1 + tmp6), (PASS1_BITS + 3)) & 1023L]; + outptr[6] = + rLimit[IDESCALE((tmp1 - tmp6), (PASS1_BITS + 3)) & 1023L]; + outptr[2] = + rLimit[IDESCALE((tmp2 + tmp5), (PASS1_BITS + 3)) & 1023L]; + outptr[5] = + rLimit[IDESCALE((tmp2 - tmp5), (PASS1_BITS + 3)) & 1023L]; + outptr[4] = + rLimit[IDESCALE((tmp3 + tmp4), (PASS1_BITS + 3)) & 1023L]; + outptr[3] = + rLimit[IDESCALE((tmp3 - tmp4), (PASS1_BITS + 3)) & 1023L]; + + wsptr += dctsize; /* advance pointer to next row */ + } } - } - void yuvToRgb( - int txb, int tyb, - unsigned char - *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes - struct RGB *pYUV, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes - unsigned char - *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes - ) { - int i, j, pos, m, n; - unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; - int y; - struct RGB *pByte; - int nBlocksInMcu = 6; - unsigned int pixelX, pixelY; - - pByte = reinterpret_cast<struct RGB *>(pBgr); - if (yuvmode == YuvMode::YUV444) { - py = pYCbCr; - pcb = pYCbCr + 64; - pcr = pcb + 64; - - pixelX = txb * 8; - pixelY = tyb * 8; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 8; j++) { - for (i = 0; i < 8; i++) { - m = ((j << 3) + i); - y = py[m]; - cb = pcb[m]; - cr = pcr[m]; - n = pos + i; - // For 2Pass. Save the YUV value - pYUV[n].b = cb; - pYUV[n].g = y; - pYUV[n].r = cr; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + void yuvToRgb( + int txb, int tyb, + unsigned char + *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes + struct RGB *pYUV, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes + unsigned char + *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes + ) + { + int i, j, pos, m, n; + unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; + int y; + struct RGB *pByte; + int nBlocksInMcu = 6; + unsigned int pixelX, pixelY; + + pByte = reinterpret_cast<struct RGB *>(pBgr); + if (yuvmode == YuvMode::YUV444) + { + py = pYCbCr; + pcb = pYCbCr + 64; + pcr = pcb + 64; + + pixelX = txb * 8; + pixelY = tyb * 8; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + m = ((j << 3) + i); + y = py[m]; + cb = pcb[m]; + cr = pcr[m]; + n = pos + i; + // For 2Pass. Save the YUV value + pYUV[n].b = cb; + pYUV[n].g = y; + pYUV[n].r = cr; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } - } else { - for (i = 0; i < nBlocksInMcu - 2; i++) { - py420[i] = pYCbCr + i * 64; - } - pcb = pYCbCr + (nBlocksInMcu - 2) * 64; - pcr = pcb + 64; - - pixelX = txb * 16; - pixelY = tyb * 16; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 16; j++) { - for (i = 0; i < 16; i++) { - // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} - y = *(py420[(j >> 3) * 2 + (i >> 3)]++); - m = ((j >> 1) << 3) + (i >> 1); - cb = pcb[m]; - cr = pcr[m]; - n = pos + i; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + else + { + for (i = 0; i < nBlocksInMcu - 2; i++) + { + py420[i] = pYCbCr + i * 64; + } + pcb = pYCbCr + (nBlocksInMcu - 2) * 64; + pcr = pcb + 64; + + pixelX = txb * 16; + pixelY = tyb * 16; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 16; j++) + { + for (i = 0; i < 16; i++) + { + // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} + y = *(py420[(j >> 3) * 2 + (i >> 3)]++); + m = ((j >> 1) << 3) + (i >> 1); + cb = pcb[m]; + cr = pcr[m]; + n = pos + i; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } } - } - void yuvToBuffer( - int txb, int tyb, - unsigned char - *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes - struct RGB - *pYUV, // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes - unsigned char - *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes - ) { - int i, j, pos, m, n; - unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; - int y; - struct RGB *pByte; - int nBlocksInMcu = 6; - unsigned int pixelX, pixelY; - - pByte = reinterpret_cast<struct RGB *>(pBgr); - if (yuvmode == YuvMode::YUV444) { - py = pYCbCr; - pcb = pYCbCr + 64; - pcr = pcb + 64; - - pixelX = txb * 8; - pixelY = tyb * 8; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 8; j++) { - for (i = 0; i < 8; i++) { - m = ((j << 3) + i); - n = pos + i; - y = pYUV[n].g + (py[m] - 128); - cb = pYUV[n].b + (pcb[m] - 128); - cr = pYUV[n].r + (pcr[m] - 128); - pYUV[n].b = cb; - pYUV[n].g = y; - pYUV[n].r = cr; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + void yuvToBuffer( + int txb, int tyb, + unsigned char + *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes + struct RGB + *pYUV, // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes + unsigned char + *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes + ) + { + int i, j, pos, m, n; + unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; + int y; + struct RGB *pByte; + int nBlocksInMcu = 6; + unsigned int pixelX, pixelY; + + pByte = reinterpret_cast<struct RGB *>(pBgr); + if (yuvmode == YuvMode::YUV444) + { + py = pYCbCr; + pcb = pYCbCr + 64; + pcr = pcb + 64; + + pixelX = txb * 8; + pixelY = tyb * 8; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + m = ((j << 3) + i); + n = pos + i; + y = pYUV[n].g + (py[m] - 128); + cb = pYUV[n].b + (pcb[m] - 128); + cr = pYUV[n].r + (pcr[m] - 128); + pYUV[n].b = cb; + pYUV[n].g = y; + pYUV[n].r = cr; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } - } else { - for (i = 0; i < nBlocksInMcu - 2; i++) { - py420[i] = pYCbCr + i * 64; - } - pcb = pYCbCr + (nBlocksInMcu - 2) * 64; - pcr = pcb + 64; - - pixelX = txb * 16; - pixelY = tyb * 16; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 16; j++) { - for (i = 0; i < 16; i++) { - // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} - y = *(py420[(j >> 3) * 2 + (i >> 3)]++); - m = ((j >> 1) << 3) + (i >> 1); - cb = pcb[m]; - cr = pcr[m]; - n = pos + i; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + else + { + for (i = 0; i < nBlocksInMcu - 2; i++) + { + py420[i] = pYCbCr + i * 64; + } + pcb = pYCbCr + (nBlocksInMcu - 2) * 64; + pcr = pcb + 64; + + pixelX = txb * 16; + pixelY = tyb * 16; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 16; j++) + { + for (i = 0; i < 16; i++) + { + // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} + y = *(py420[(j >> 3) * 2 + (i >> 3)]++); + m = ((j >> 1) << 3) + (i >> 1); + cb = pcb[m]; + cr = pcr[m]; + n = pos + i; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } } - } - void decompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection) { - unsigned char *ptr; - unsigned char byTileYuv[768] = {}; - - memset(dctCoeff, 0, 384 * 2); - ptr = byTileYuv; - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); - idctTransform(dctCoeff, ptr, QT_TableSelection); - ptr += 64; - - if (yuvmode == YuvMode::YUV420) { - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 64); - idctTransform(dctCoeff + 64, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 128); - idctTransform(dctCoeff + 128, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 192); - idctTransform(dctCoeff + 192, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 256); - idctTransform(dctCoeff + 256, ptr, QT_TableSelection + 1); - ptr += 64; - - processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 320); - idctTransform(dctCoeff + 320, ptr, QT_TableSelection + 1); - } else { - processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); - idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); - ptr += 64; - - processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); - idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); - } - - // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); - // yuvBuffer for YUV record - yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), - reinterpret_cast<unsigned char *>(outBuf)); - } - - void decompress2Pass(int txb, int tyb, char *outBuf, - uint8_t QT_TableSelection) { - unsigned char *ptr; - unsigned char byTileYuv[768]; - memset(dctCoeff, 0, 384 * 2); - - ptr = byTileYuv; - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); - idctTransform(dctCoeff, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); - idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); - ptr += 64; - - processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); - idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); - - yuvToBuffer(txb, tyb, byTileYuv, yuvBuffer.data(), - reinterpret_cast<unsigned char *>(outBuf)); - // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); - } - - void vqDecompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection, - struct ColorCache *VQ) { - unsigned char *ptr, i; - unsigned char byTileYuv[192]; - int data; - - ptr = byTileYuv; - if (VQ->bitMapBits == 0) { - for (i = 0; i < 64; i++) { - ptr[0] = (VQ->color[VQ->index[0]] & 0xFF0000) >> 16; - ptr[64] = (VQ->color[VQ->index[0]] & 0x00FF00) >> 8; - ptr[128] = VQ->color[VQ->index[0]] & 0x0000FF; - ptr += 1; - } - } else { - for (i = 0; i < 64; i++) { - data = static_cast<int>(lookKbits(VQ->bitMapBits)); - ptr[0] = (VQ->color[VQ->index[data]] & 0xFF0000) >> 16; - ptr[64] = (VQ->color[VQ->index[data]] & 0x00FF00) >> 8; - ptr[128] = VQ->color[VQ->index[data]] & 0x0000FF; - ptr += 1; - skipKbits(VQ->bitMapBits); - } - } - // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); - yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), - reinterpret_cast<unsigned char *>(outBuf)); - } - - void moveBlockIndex() { - if (yuvmode == YuvMode::YUV444) { - txb++; - if (txb >= static_cast<int>(width / 8)) { - tyb++; - if (tyb >= static_cast<int>(height / 8)) { - tyb = 0; + void decompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection) + { + unsigned char *ptr; + unsigned char byTileYuv[768] = {}; + + memset(dctCoeff, 0, 384 * 2); + ptr = byTileYuv; + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); + idctTransform(dctCoeff, ptr, QT_TableSelection); + ptr += 64; + + if (yuvmode == YuvMode::YUV420) + { + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 64); + idctTransform(dctCoeff + 64, ptr, QT_TableSelection); + ptr += 64; + + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 128); + idctTransform(dctCoeff + 128, ptr, QT_TableSelection); + ptr += 64; + + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 192); + idctTransform(dctCoeff + 192, ptr, QT_TableSelection); + ptr += 64; + + processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 256); + idctTransform(dctCoeff + 256, ptr, QT_TableSelection + 1); + ptr += 64; + + processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 320); + idctTransform(dctCoeff + 320, ptr, QT_TableSelection + 1); } - txb = 0; - } - } else { - txb++; - if (txb >= static_cast<int>(width / 16)) { - tyb++; - if (tyb >= static_cast<int>(height / 16)) { - tyb = 0; + else + { + processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); + idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); + ptr += 64; + + processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); + idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); } - txb = 0; - } + + // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); + // yuvBuffer for YUV record + yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), + reinterpret_cast<unsigned char *>(outBuf)); } - } - void initColorTable() { - int i, x; - int nScale = 1L << 16; // equal to power(2,16) - int nHalf = nScale >> 1; + void decompress2Pass(int txb, int tyb, char *outBuf, + uint8_t QT_TableSelection) + { + unsigned char *ptr; + unsigned char byTileYuv[768]; + memset(dctCoeff, 0, 384 * 2); -#define FIX(x) ((int)((x)*nScale + 0.5)) + ptr = byTileYuv; + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); + idctTransform(dctCoeff, ptr, QT_TableSelection); + ptr += 64; - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>r value is nearest int to 1.597656 * x */ - /* Cb=>b value is nearest int to 2.015625 * x */ - /* Cr=>g value is scaled-up -0.8125 * x */ - /* Cb=>g value is scaled-up -0.390625 * x */ - for (i = 0, x = -128; i < 256; i++, x++) { - mCrToR[i] = (FIX(1.597656) * x + nHalf) >> 16; - mCbToB[i] = (FIX(2.015625) * x + nHalf) >> 16; - mCrToG[i] = (-FIX(0.8125) * x + nHalf) >> 16; - mCbToG[i] = (-FIX(0.390625) * x + nHalf) >> 16; - } - for (i = 0, x = -16; i < 256; i++, x++) { - mY[i] = (FIX(1.164) * x + nHalf) >> 16; + processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); + idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); + ptr += 64; + + processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); + idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); + + yuvToBuffer(txb, tyb, byTileYuv, yuvBuffer.data(), + reinterpret_cast<unsigned char *>(outBuf)); + // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); } - // For color Text Enchance Y Re-map. Recommend to disable in default - /* - for (i = 0; i < (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); - i++) { - temp = (double)i / - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate; - temp1 = 1.0 / VideoEngineInfo->INFData.Gamma1Parameter; - mY[i] = - (BYTE)(VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate * pow (temp, - temp1)); - if (mY[i] > 255) mY[i] = 255; + + void vqDecompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection, + struct ColorCache *VQ) + { + unsigned char *ptr, i; + unsigned char byTileYuv[192]; + int data; + + ptr = byTileYuv; + if (VQ->bitMapBits == 0) + { + for (i = 0; i < 64; i++) + { + ptr[0] = (VQ->color[VQ->index[0]] & 0xFF0000) >> 16; + ptr[64] = (VQ->color[VQ->index[0]] & 0x00FF00) >> 8; + ptr[128] = VQ->color[VQ->index[0]] & 0x0000FF; + ptr += 1; } - for (i = (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); i < 256; - i++) { - mY[i] = - (BYTE)((VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) + (256 - - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) * ( pow((double)((i - - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) / (256 - - (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate))), (1.0 / - VideoEngineInfo->INFData.Gamma2Parameter)) )); - if (mY[i] > 255) mY[i] = 255; + } + else + { + for (i = 0; i < 64; i++) + { + data = static_cast<int>(lookKbits(VQ->bitMapBits)); + ptr[0] = (VQ->color[VQ->index[data]] & 0xFF0000) >> 16; + ptr[64] = (VQ->color[VQ->index[data]] & 0x00FF00) >> 8; + ptr[128] = VQ->color[VQ->index[data]] & 0x0000FF; + ptr += 1; + skipKbits(VQ->bitMapBits); } - */ - } - void loadHuffmanTable(HuffmanTable *HT, const unsigned char *nrcode, - const unsigned char *value, - const unsigned short int *Huff_code) { - unsigned char k, j, i; - unsigned int code, codeIndex; - - for (j = 1; j <= 16; j++) { - HT->length[j] = nrcode[j]; - } - for (i = 0, k = 1; k <= 16; k++) { - for (j = 0; j < HT->length[k]; j++) { - HT->v[wordHiLo(k, j)] = value[i]; - i++; - } + } + // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); + yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), + reinterpret_cast<unsigned char *>(outBuf)); } - code = 0; - for (k = 1; k <= 16; k++) { - HT->minorCode[k] = static_cast<unsigned short int>(code); - for (j = 1; j <= HT->length[k]; j++) { - code++; - } - HT->majorCode[k] = static_cast<unsigned short int>(code - 1); - code *= 2; - if (HT->length[k] == 0) { - HT->minorCode[k] = 0xFFFF; - HT->majorCode[k] = 0; - } + void moveBlockIndex() + { + if (yuvmode == YuvMode::YUV444) + { + txb++; + if (txb >= static_cast<int>(width / 8)) + { + tyb++; + if (tyb >= static_cast<int>(height / 8)) + { + tyb = 0; + } + txb = 0; + } + } + else + { + txb++; + if (txb >= static_cast<int>(width / 16)) + { + tyb++; + if (tyb >= static_cast<int>(height / 16)) + { + tyb = 0; + } + txb = 0; + } + } } - HT->len[0] = 2; - i = 2; + void initColorTable() + { + int i, x; + int nScale = 1L << 16; // equal to power(2,16) + int nHalf = nScale >> 1; - for (codeIndex = 1; codeIndex < 65535; codeIndex++) { - if (codeIndex < Huff_code[i]) { - HT->len[codeIndex] = static_cast<unsigned char>(Huff_code[i + 1]); - } else { - i = i + 2; - HT->len[codeIndex] = static_cast<unsigned char>(Huff_code[i + 1]); - } - } - } - void initJpgTable() { - initColorTable(); - prepareRangeLimitTable(); - loadHuffmanTable(&htdc[0], stdDcLuminanceNrcodes, stdDcLuminanceValues, - dcLuminanceHuffmancode); - loadHuffmanTable(&htac[0], stdAcLuminanceNrcodes, stdAcLuminanceValues, - acLuminanceHuffmancode); - loadHuffmanTable(&htdc[1], stdDcChrominanceNrcodes, stdDcChrominanceValues, - dcChrominanceHuffmancode); - loadHuffmanTable(&htac[1], stdAcChrominanceNrcodes, stdAcChrominanceValues, - acChrominanceHuffmancode); - } - - void prepareRangeLimitTable() - /* Allocate and fill in the sample_range_limit table */ - { - int j; - rlimitTable = reinterpret_cast<unsigned char *>(malloc(5 * 256L + 128)); - /* First segment of "simple" table: limit[x] = 0 for x < 0 */ - memset((void *)rlimitTable, 0, 256); - rlimitTable += 256; /* allow negative subscripts of simple table */ - /* Main part of "simple" table: limit[x] = x */ - for (j = 0; j < 256; j++) { - rlimitTable[j] = j; - } - /* End of simple table, rest of first half of post-IDCT table */ - for (j = 256; j < 640; j++) { - rlimitTable[j] = 255; +#define FIX(x) ((int)((x)*nScale + 0.5)) + + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>r value is nearest int to 1.597656 * x */ + /* Cb=>b value is nearest int to 2.015625 * x */ + /* Cr=>g value is scaled-up -0.8125 * x */ + /* Cb=>g value is scaled-up -0.390625 * x */ + for (i = 0, x = -128; i < 256; i++, x++) + { + mCrToR[i] = (FIX(1.597656) * x + nHalf) >> 16; + mCbToB[i] = (FIX(2.015625) * x + nHalf) >> 16; + mCrToG[i] = (-FIX(0.8125) * x + nHalf) >> 16; + mCbToG[i] = (-FIX(0.390625) * x + nHalf) >> 16; + } + for (i = 0, x = -16; i < 256; i++, x++) + { + mY[i] = (FIX(1.164) * x + nHalf) >> 16; + } + // For color Text Enchance Y Re-map. Recommend to disable in default + /* + for (i = 0; i < + (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); i++) { temp = + (double)i / VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate; temp1 + = 1.0 / VideoEngineInfo->INFData.Gamma1Parameter; mY[i] = + (BYTE)(VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate * pow (temp, + temp1)); + if (mY[i] > 255) mY[i] = 255; + } + for (i = (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); i < + 256; i++) { mY[i] = + (BYTE)((VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) + (256 - + VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) * ( pow((double)((i + - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) / (256 - + (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate))), (1.0 / + VideoEngineInfo->INFData.Gamma2Parameter)) )); + if (mY[i] > 255) mY[i] = 255; + } + */ } + void loadHuffmanTable(HuffmanTable *HT, const unsigned char *nrcode, + const unsigned char *value, + const unsigned short int *Huff_code) + { + unsigned char k, j, i; + unsigned int code, codeIndex; + + for (j = 1; j <= 16; j++) + { + HT->length[j] = nrcode[j]; + } + for (i = 0, k = 1; k <= 16; k++) + { + for (j = 0; j < HT->length[k]; j++) + { + HT->v[wordHiLo(k, j)] = value[i]; + i++; + } + } + + code = 0; + for (k = 1; k <= 16; k++) + { + HT->minorCode[k] = static_cast<unsigned short int>(code); + for (j = 1; j <= HT->length[k]; j++) + { + code++; + } + HT->majorCode[k] = static_cast<unsigned short int>(code - 1); + code *= 2; + if (HT->length[k] == 0) + { + HT->minorCode[k] = 0xFFFF; + HT->majorCode[k] = 0; + } + } + + HT->len[0] = 2; + i = 2; - /* Second half of post-IDCT table */ - memset((void *)(rlimitTable + 640), 0, 384); - for (j = 0; j < 128; j++) { - rlimitTable[j + 1024] = j; + for (codeIndex = 1; codeIndex < 65535; codeIndex++) + { + if (codeIndex < Huff_code[i]) + { + HT->len[codeIndex] = + static_cast<unsigned char>(Huff_code[i + 1]); + } + else + { + i = i + 2; + HT->len[codeIndex] = + static_cast<unsigned char>(Huff_code[i + 1]); + } + } } - } - - inline unsigned short int wordHiLo(uint8_t byte_high, uint8_t byte_low) { - return (byte_high << 8) + byte_low; - } - - // river - void processHuffmanDataUnit(uint8_t DC_nr, uint8_t AC_nr, - signed short int *previous_DC, - unsigned short int position) { - uint8_t nr = 0; - uint8_t k; - unsigned short int tmpHcode; - uint8_t sizeVal, count0; - unsigned short int *minCode; - uint8_t *huffValues; - uint8_t byteTemp; - - minCode = htdc[DC_nr].minorCode; - // maj_code=htdc[DC_nr].majorCode; - huffValues = htdc[DC_nr].v; - - // DC - k = htdc[DC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; - // river - // tmp_Hcode=lookKbits(k); - tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); - skipKbits(k); - sizeVal = - huffValues[wordHiLo(k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; - if (sizeVal == 0) { - dctCoeff[position + 0] = *previous_DC; - } else { - dctCoeff[position + 0] = *previous_DC + getKbits(sizeVal); - *previous_DC = dctCoeff[position + 0]; + void initJpgTable() + { + initColorTable(); + prepareRangeLimitTable(); + loadHuffmanTable(&htdc[0], stdDcLuminanceNrcodes, stdDcLuminanceValues, + dcLuminanceHuffmancode); + loadHuffmanTable(&htac[0], stdAcLuminanceNrcodes, stdAcLuminanceValues, + acLuminanceHuffmancode); + loadHuffmanTable(&htdc[1], stdDcChrominanceNrcodes, + stdDcChrominanceValues, dcChrominanceHuffmancode); + loadHuffmanTable(&htac[1], stdAcChrominanceNrcodes, + stdAcChrominanceValues, acChrominanceHuffmancode); } - // Second, AC coefficient decoding - minCode = htac[AC_nr].minorCode; - // maj_code=htac[AC_nr].majorCode; - huffValues = htac[AC_nr].v; - - nr = 1; // AC coefficient - do { - k = htac[AC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; - tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); - skipKbits(k); - - byteTemp = - huffValues[wordHiLo(k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; - sizeVal = byteTemp & 0xF; - count0 = byteTemp >> 4; - if (sizeVal == 0) { - if (count0 != 0xF) { - break; + void prepareRangeLimitTable() + /* Allocate and fill in the sample_range_limit table */ + { + int j; + rlimitTable = reinterpret_cast<unsigned char *>(malloc(5 * 256L + 128)); + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + memset((void *)rlimitTable, 0, 256); + rlimitTable += 256; /* allow negative subscripts of simple table */ + /* Main part of "simple" table: limit[x] = x */ + for (j = 0; j < 256; j++) + { + rlimitTable[j] = j; + } + /* End of simple table, rest of first half of post-IDCT table */ + for (j = 256; j < 640; j++) + { + rlimitTable[j] = 255; + } + + /* Second half of post-IDCT table */ + memset((void *)(rlimitTable + 640), 0, 384); + for (j = 0; j < 128; j++) + { + rlimitTable[j + 1024] = j; } - nr += 16; - } else { - nr += count0; // skip count_0 zeroes - dctCoeff[position + dezigzag[nr++]] = getKbits(sizeVal); - } - } while (nr < 64); - } - - unsigned short int lookKbits(uint8_t k) { - unsigned short int revcode; - - revcode = static_cast<unsigned short int>(codebuf >> (32 - k)); - - return (revcode); - } - - void skipKbits(uint8_t k) { - unsigned long readbuf; - - if ((newbits - k) <= 0) { - readbuf = buffer[bufferIndex]; - bufferIndex++; - codebuf = - (codebuf << k) | ((newbuf | (readbuf >> (newbits))) >> (32 - k)); - newbuf = readbuf << (k - newbits); - newbits = 32 + newbits - k; - } else { - codebuf = (codebuf << k) | (newbuf >> (32 - k)); - newbuf = newbuf << k; - newbits -= k; } - } - signed short int getKbits(uint8_t k) { - signed short int signedWordvalue; + inline unsigned short int wordHiLo(uint8_t byte_high, uint8_t byte_low) + { + return (byte_high << 8) + byte_low; + } // river - // signed_wordvalue=lookKbits(k); - signedWordvalue = static_cast<unsigned short int>(codebuf >> (32 - k)); - if (((1L << (k - 1)) & signedWordvalue) == 0) { - // neg_pow2 was previously defined as the below. It seemed silly to keep - // a table of values around for something - // THat's relatively easy to compute, so it was replaced with the - // appropriate math - // signed_wordvalue = signed_wordvalue - (0xFFFF >> (16 - k)); - std::array<signed short int, 17> negPow2 = { - 0, -1, -3, -7, -15, -31, -63, -127, - -255, -511, -1023, -2047, -4095, -8191, -16383, -32767}; - - signedWordvalue = signedWordvalue + negPow2[k]; - } - skipKbits(k); - return signedWordvalue; - } - int initJpgDecoding() { - bytePos = 0; - loadQuantTable(qt[0]); - loadQuantTableCb(qt[1]); - // Note: Added for Dual-JPEG - loadAdvanceQuantTable(qt[2]); - loadAdvanceQuantTableCb(qt[3]); - return 1; - } - - void setQuantTable(const uint8_t *basic_table, uint8_t scale_factor, - std::array<uint8_t, 64> &newtable) - // Set quantization table and zigzag reorder it - { - uint8_t i; - long temp; - for (i = 0; i < 64; i++) { - temp = (static_cast<long>(basic_table[i] * 16) / scale_factor); - /* limit the values to the valid range */ - if (temp <= 0L) { - temp = 1L; - } - if (temp > 255L) { - temp = 255L; /* limit to baseline range if requested */ - } - newtable[zigzag[i]] = static_cast<uint8_t>(temp); - } - } - - void updatereadbuf(uint32_t *codebuf, uint32_t *newbuf, int walks, - int *newbits, std::vector<uint32_t> &buffer) { - unsigned long readbuf; - - if ((*newbits - walks) <= 0) { - readbuf = buffer[bufferIndex]; - bufferIndex++; - *codebuf = (*codebuf << walks) | - ((*newbuf | (readbuf >> (*newbits))) >> (32 - walks)); - *newbuf = readbuf << (walks - *newbits); - *newbits = 32 + *newbits - walks; - } else { - *codebuf = (*codebuf << walks) | (*newbuf >> (32 - walks)); - *newbuf = *newbuf << walks; - *newbits -= walks; - } - } - - uint32_t decode(std::vector<uint32_t> &bufferVector, unsigned long width, - unsigned long height, YuvMode yuvmode_in, int ySelector, - int uvSelector) { - ColorCache decodeColor; - if (width != userWidth || height != userHeight || yuvmode_in != yuvmode || - ySelector != ySelector || uvSelector != uvSelector) { - yuvmode = yuvmode_in; - ySelector = ySelector; // 0-7 - uvSelector = uvSelector; // 0-7 - userHeight = height; - userWidth = width; - width = width; - height = height; - - // TODO(ed) Magic number section. Document appropriately - advanceSelector = 0; // 0-7 - mapping = 0; // 0 or 1 - - if (yuvmode == YuvMode::YUV420) { - if ((width % 16) != 0u) { - width = width + 16 - (width % 16); + void processHuffmanDataUnit(uint8_t DC_nr, uint8_t AC_nr, + signed short int *previous_DC, + unsigned short int position) + { + uint8_t nr = 0; + uint8_t k; + unsigned short int tmpHcode; + uint8_t sizeVal, count0; + unsigned short int *minCode; + uint8_t *huffValues; + uint8_t byteTemp; + + minCode = htdc[DC_nr].minorCode; + // maj_code=htdc[DC_nr].majorCode; + huffValues = htdc[DC_nr].v; + + // DC + k = htdc[DC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; + // river + // tmp_Hcode=lookKbits(k); + tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); + skipKbits(k); + sizeVal = huffValues[wordHiLo( + k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; + if (sizeVal == 0) + { + dctCoeff[position + 0] = *previous_DC; } - if ((height % 16) != 0u) { - height = height + 16 - (height % 16); + else + { + dctCoeff[position + 0] = *previous_DC + getKbits(sizeVal); + *previous_DC = dctCoeff[position + 0]; } - } else { - if ((width % 8) != 0u) { - width = width + 8 - (width % 8); + + // Second, AC coefficient decoding + minCode = htac[AC_nr].minorCode; + // maj_code=htac[AC_nr].majorCode; + huffValues = htac[AC_nr].v; + + nr = 1; // AC coefficient + do + { + k = htac[AC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; + tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); + skipKbits(k); + + byteTemp = huffValues[wordHiLo( + k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; + sizeVal = byteTemp & 0xF; + count0 = byteTemp >> 4; + if (sizeVal == 0) + { + if (count0 != 0xF) + { + break; + } + nr += 16; + } + else + { + nr += count0; // skip count_0 zeroes + dctCoeff[position + dezigzag[nr++]] = getKbits(sizeVal); + } + } while (nr < 64); + } + + unsigned short int lookKbits(uint8_t k) + { + unsigned short int revcode; + + revcode = static_cast<unsigned short int>(codebuf >> (32 - k)); + + return (revcode); + } + + void skipKbits(uint8_t k) + { + unsigned long readbuf; + + if ((newbits - k) <= 0) + { + readbuf = buffer[bufferIndex]; + bufferIndex++; + codebuf = (codebuf << k) | + ((newbuf | (readbuf >> (newbits))) >> (32 - k)); + newbuf = readbuf << (k - newbits); + newbits = 32 + newbits - k; } - if ((height % 8) != 0u) { - height = height + 8 - (height % 8); + else + { + codebuf = (codebuf << k) | (newbuf >> (32 - k)); + newbuf = newbuf << k; + newbits -= k; } - } + } - initJpgDecoding(); + signed short int getKbits(uint8_t k) + { + signed short int signedWordvalue; + + // river + // signed_wordvalue=lookKbits(k); + signedWordvalue = static_cast<unsigned short int>(codebuf >> (32 - k)); + if (((1L << (k - 1)) & signedWordvalue) == 0) + { + // neg_pow2 was previously defined as the below. It seemed silly to + // keep a table of values around for something THat's relatively + // easy to compute, so it was replaced with the appropriate math + // signed_wordvalue = signed_wordvalue - (0xFFFF >> (16 - k)); + std::array<signed short int, 17> negPow2 = { + 0, -1, -3, -7, -15, -31, -63, -127, + -255, -511, -1023, -2047, -4095, -8191, -16383, -32767}; + + signedWordvalue = signedWordvalue + negPow2[k]; + } + skipKbits(k); + return signedWordvalue; } - // TODO(ed) cleanup cruft - buffer = bufferVector.data(); - - codebuf = bufferVector[0]; - newbuf = bufferVector[1]; - bufferIndex = 2; - - txb = tyb = 0; - newbits = 32; - dcy = dcCb = dcCr = 0; - - static const uint32_t vqHeaderMask = 0x01; - static const uint32_t vqNoUpdateHeader = 0x00; - static const uint32_t vqUpdateHeader = 0x01; - static const int vqNoUpdateLength = 0x03; - static const int vqUpdateLength = 0x1B; - static const uint32_t vqIndexMask = 0x03; - static const uint32_t vqColorMask = 0xFFFFFF; - - static const int blockAsT2100StartLength = 0x04; - static const int blockAsT2100SkipLength = 20; // S:1 H:3 X:8 Y:8 - - do { - auto blockHeader = static_cast<JpgBlock>((codebuf >> 28) & 0xFF); - switch (blockHeader) { - case JpgBlock::JPEG_NO_SKIP_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0); - break; - case JpgBlock::FRAME_END_CODE: - return 0; - break; - case JpgBlock::JPEG_SKIP_CODE: - - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0); - break; - case JpgBlock::VQ_NO_SKIP_1_COLOR_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 0; - - for (int i = 0; i < 1; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - break; - case JpgBlock::VQ_SKIP_1_COLOR_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 0; - - for (int i = 0; i < 1; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - break; - - case JpgBlock::VQ_NO_SKIP_2_COLOR_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 1; - - for (int i = 0; i < 2; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - break; - case JpgBlock::VQ_SKIP_2_COLOR_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 1; - - for (int i = 0; i < 2; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - - break; - case JpgBlock::VQ_NO_SKIP_4_COLOR_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 2; - - for (unsigned char &i : decodeColor.index) { - i = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[i] = ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); + int initJpgDecoding() + { + bytePos = 0; + loadQuantTable(qt[0]); + loadQuantTableCb(qt[1]); + // Note: Added for Dual-JPEG + loadAdvanceQuantTable(qt[2]); + loadAdvanceQuantTableCb(qt[3]); + return 1; + } + + void setQuantTable(const uint8_t *basic_table, uint8_t scale_factor, + std::array<uint8_t, 64> &newtable) + // Set quantization table and zigzag reorder it + { + uint8_t i; + long temp; + for (i = 0; i < 64; i++) + { + temp = (static_cast<long>(basic_table[i] * 16) / scale_factor); + /* limit the values to the valid range */ + if (temp <= 0L) + { + temp = 1L; } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - - break; - - case JpgBlock::VQ_SKIP_4_COLOR_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 2; - - for (unsigned char &i : decodeColor.index) { - i = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[i] = ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); + if (temp > 255L) + { + temp = 255L; /* limit to baseline range if requested */ } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); + newtable[zigzag[i]] = static_cast<uint8_t>(temp); + } + } - break; - case JpgBlock::JPEG_SKIP_PASS2_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; + void updatereadbuf(uint32_t *codebuf, uint32_t *newbuf, int walks, + int *newbits, std::vector<uint32_t> &buffer) + { + unsigned long readbuf; + + if ((*newbits - walks) <= 0) + { + readbuf = buffer[bufferIndex]; + bufferIndex++; + *codebuf = (*codebuf << walks) | + ((*newbuf | (readbuf >> (*newbits))) >> (32 - walks)); + *newbuf = readbuf << (walks - *newbits); + *newbits = 32 + *newbits - walks; + } + else + { + *codebuf = (*codebuf << walks) | (*newbuf >> (32 - walks)); + *newbuf = *newbuf << walks; + *newbits -= walks; + } + } - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decompress2Pass(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), - 2); + uint32_t decode(std::vector<uint32_t> &bufferVector, unsigned long width, + unsigned long height, YuvMode yuvmode_in, int ySelector, + int uvSelector) + { + ColorCache decodeColor; + if (width != userWidth || height != userHeight || + yuvmode_in != yuvmode || ySelector != ySelector || + uvSelector != uvSelector) + { + yuvmode = yuvmode_in; + ySelector = ySelector; // 0-7 + uvSelector = uvSelector; // 0-7 + userHeight = height; + userWidth = width; + width = width; + height = height; + + // TODO(ed) Magic number section. Document appropriately + advanceSelector = 0; // 0-7 + mapping = 0; // 0 or 1 + + if (yuvmode == YuvMode::YUV420) + { + if ((width % 16) != 0u) + { + width = width + 16 - (width % 16); + } + if ((height % 16) != 0u) + { + height = height + 16 - (height % 16); + } + } + else + { + if ((width % 8) != 0u) + { + width = width + 8 - (width % 8); + } + if ((height % 8) != 0u) + { + height = height + 8 - (height % 8); + } + } - break; - default: - // TODO(ed) propogate errors upstream - return -1; - break; - } - moveBlockIndex(); + initJpgDecoding(); + } + // TODO(ed) cleanup cruft + buffer = bufferVector.data(); + + codebuf = bufferVector[0]; + newbuf = bufferVector[1]; + bufferIndex = 2; + + txb = tyb = 0; + newbits = 32; + dcy = dcCb = dcCr = 0; + + static const uint32_t vqHeaderMask = 0x01; + static const uint32_t vqNoUpdateHeader = 0x00; + static const uint32_t vqUpdateHeader = 0x01; + static const int vqNoUpdateLength = 0x03; + static const int vqUpdateLength = 0x1B; + static const uint32_t vqIndexMask = 0x03; + static const uint32_t vqColorMask = 0xFFFFFF; + + static const int blockAsT2100StartLength = 0x04; + static const int blockAsT2100SkipLength = 20; // S:1 H:3 X:8 Y:8 + + do + { + auto blockHeader = static_cast<JpgBlock>((codebuf >> 28) & 0xFF); + switch (blockHeader) + { + case JpgBlock::JPEG_NO_SKIP_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0); + break; + case JpgBlock::FRAME_END_CODE: + return 0; + break; + case JpgBlock::JPEG_SKIP_CODE: + + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0); + break; + case JpgBlock::VQ_NO_SKIP_1_COLOR_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 0; + + for (int i = 0; i < 1; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + break; + case JpgBlock::VQ_SKIP_1_COLOR_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 0; + + for (int i = 0; i < 1; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + break; + + case JpgBlock::VQ_NO_SKIP_2_COLOR_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 1; + + for (int i = 0; i < 2; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + break; + case JpgBlock::VQ_SKIP_2_COLOR_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 1; + + for (int i = 0; i < 2; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + + break; + case JpgBlock::VQ_NO_SKIP_4_COLOR_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 2; + + for (unsigned char &i : decodeColor.index) + { + i = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[i] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + + break; + + case JpgBlock::VQ_SKIP_4_COLOR_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 2; + + for (unsigned char &i : decodeColor.index) + { + i = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[i] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + + break; + case JpgBlock::JPEG_SKIP_PASS2_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decompress2Pass(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), + 2); + + break; + default: + // TODO(ed) propogate errors upstream + return -1; + break; + } + moveBlockIndex(); - } while (bufferIndex <= bufferVector.size()); + } while (bufferIndex <= bufferVector.size()); - return -1; - } + return -1; + } #ifdef cimg_version - void dump_to_bitmap_file() { - cimg_library::CImg<unsigned char> image(width, height, 1, 3); - for (int y = 0; y < width; y++) { - for (int x = 0; x < height; x++) { - auto pixel = outBuffer[x + (y * width)]; - image(x, y, 0) = pixel.r; - image(x, y, 1) = pixel.g; - image(x, y, 2) = pixel.b; - } + void dump_to_bitmap_file() + { + cimg_library::CImg<unsigned char> image(width, height, 1, 3); + for (int y = 0; y < width; y++) + { + for (int x = 0; x < height; x++) + { + auto pixel = outBuffer[x + (y * width)]; + image(x, y, 0) = pixel.r; + image(x, y, 1) = pixel.g; + image(x, y, 2) = pixel.b; + } + } + image.save("/tmp/file2.bmp"); } - image.save("/tmp/file2.bmp"); - } #endif - private: - YuvMode yuvmode{}; - // width and height are the modes your display used - unsigned long width{}; - unsigned long height{}; - unsigned long userWidth{}; - unsigned long userHeight{}; - unsigned char ySelector{}; - int scalefactor; - int scalefactoruv; - int advancescalefactor; - int advancescalefactoruv; - int mapping{}; - unsigned char uvSelector{}; - unsigned char advanceSelector{}; - int bytePos{}; // current byte position - - // quantization tables, no more than 4 quantization tables - std::array<std::array<long, 64>, 4> qt{}; - - // DC huffman tables , no more than 4 (0..3) - std::array<HuffmanTable, 4> htdc{}; - // AC huffman tables (0..3) - std::array<HuffmanTable, 4> htac{}; - std::array<int, 256> mCrToR{}; - std::array<int, 256> mCbToB{}; - std::array<int, 256> mCrToG{}; - std::array<int, 256> mCbToG{}; - std::array<int, 256> mY{}; - unsigned long bufferIndex{}; - uint32_t codebuf{}, newbuf{}, readbuf{}; - const unsigned char *stdLuminanceQt{}; - const uint8_t *stdChrominanceQt{}; - - signed short int dcy{}, dcCb{}, dcCr{}; // Coeficientii DC pentru Y,Cb,Cr - signed short int dctCoeff[384]{}; - // std::vector<signed short int> dctCoeff; // Current DCT_coefficients - // quantization table number for Y, Cb, Cr - uint8_t yqNr = 0, cbQNr = 1, crQNr = 1; - // DC Huffman table number for Y,Cb, Cr - uint8_t ydcNr = 0, cbDcNr = 1, crDcNr = 1; - // AC Huffman table number for Y,Cb, Cr - uint8_t yacNr = 0, cbAcNr = 1, crAcNr = 1; - int txb = 0; - int tyb = 0; - int newbits{}; - uint8_t *rlimitTable{}; - std::vector<RGB> yuvBuffer; - // TODO(ed) this shouldn't exist. It is cruft that needs cleaning up - uint32_t *buffer{}; - - public: - std::vector<RGB> outBuffer; + private: + YuvMode yuvmode{}; + // width and height are the modes your display used + unsigned long width{}; + unsigned long height{}; + unsigned long userWidth{}; + unsigned long userHeight{}; + unsigned char ySelector{}; + int scalefactor; + int scalefactoruv; + int advancescalefactor; + int advancescalefactoruv; + int mapping{}; + unsigned char uvSelector{}; + unsigned char advanceSelector{}; + int bytePos{}; // current byte position + + // quantization tables, no more than 4 quantization tables + std::array<std::array<long, 64>, 4> qt{}; + + // DC huffman tables , no more than 4 (0..3) + std::array<HuffmanTable, 4> htdc{}; + // AC huffman tables (0..3) + std::array<HuffmanTable, 4> htac{}; + std::array<int, 256> mCrToR{}; + std::array<int, 256> mCbToB{}; + std::array<int, 256> mCrToG{}; + std::array<int, 256> mCbToG{}; + std::array<int, 256> mY{}; + unsigned long bufferIndex{}; + uint32_t codebuf{}, newbuf{}, readbuf{}; + const unsigned char *stdLuminanceQt{}; + const uint8_t *stdChrominanceQt{}; + + signed short int dcy{}, dcCb{}, dcCr{}; // Coeficientii DC pentru Y,Cb,Cr + signed short int dctCoeff[384]{}; + // std::vector<signed short int> dctCoeff; // Current DCT_coefficients + // quantization table number for Y, Cb, Cr + uint8_t yqNr = 0, cbQNr = 1, crQNr = 1; + // DC Huffman table number for Y,Cb, Cr + uint8_t ydcNr = 0, cbDcNr = 1, crDcNr = 1; + // AC Huffman table number for Y,Cb, Cr + uint8_t yacNr = 0, cbAcNr = 1, crAcNr = 1; + int txb = 0; + int tyb = 0; + int newbits{}; + uint8_t *rlimitTable{}; + std::vector<RGB> yuvBuffer; + // TODO(ed) this shouldn't exist. It is cruft that needs cleaning up + uint32_t *buffer{}; + + public: + std::vector<RGB> outBuffer; }; -} // namespace ast_video
\ No newline at end of file +} // namespace ast_video
\ No newline at end of file diff --git a/include/ast_video_puller.hpp b/include/ast_video_puller.hpp index c2ccea27c3..520fc68e18 100644 --- a/include/ast_video_puller.hpp +++ b/include/ast_video_puller.hpp @@ -1,186 +1,212 @@ #pragma once #include <ast_video_types.hpp> +#include <boost/asio.hpp> #include <cassert> #include <iostream> #include <mutex> #include <vector> -#include <boost/asio.hpp> -namespace ast_video { +namespace ast_video +{ // // Cursor struct is used in User Mode // -struct AstCurAttributionTag { - unsigned int posX; - unsigned int posY; - unsigned int curWidth; - unsigned int curHeight; - unsigned int curType; // 0:mono 1:color 2:disappear cursor - unsigned int curChangeFlag; +struct AstCurAttributionTag +{ + unsigned int posX; + unsigned int posY; + unsigned int curWidth; + unsigned int curHeight; + unsigned int curType; // 0:mono 1:color 2:disappear cursor + unsigned int curChangeFlag; }; // // For storing Cursor Information // -struct AstCursorTag { - AstCurAttributionTag attr; - // unsigned char icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2]; - unsigned char *icon; //[64*64*2]; +struct AstCursorTag +{ + AstCurAttributionTag attr; + // unsigned char icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2]; + unsigned char *icon; //[64*64*2]; }; // // For select image format, i.e. 422 JPG420, 444 JPG444, lumin/chrom table, 0 // ~ 11, low to high // -struct FeaturesTag { - short jpgFmt; // 422:JPG420, 444:JPG444 - short luminTbl; - short chromTbl; - short toleranceNoise; - int w; - int h; - unsigned char *buf; +struct FeaturesTag +{ + short jpgFmt; // 422:JPG420, 444:JPG444 + short luminTbl; + short chromTbl; + short toleranceNoise; + int w; + int h; + unsigned char *buf; }; // // For configure video engine control registers // -struct ImageInfo { - short doImageRefresh; // Action 0:motion 1:fullframe 2:quick cursor - char qcValid; // quick cursor enable/disable - unsigned int len; - int crypttype; - char cryptkey[16]; - union { - FeaturesTag features; - AstCursorTag cursorInfo; - } parameter; +struct ImageInfo +{ + short doImageRefresh; // Action 0:motion 1:fullframe 2:quick cursor + char qcValid; // quick cursor enable/disable + unsigned int len; + int crypttype; + char cryptkey[16]; + union + { + FeaturesTag features; + AstCursorTag cursorInfo; + } parameter; }; -class SimpleVideoPuller { - public: - SimpleVideoPuller() : imageInfo(){}; - - void initialize() { - std::cout << "Opening /dev/video\n"; - videoFd = open("/dev/video", O_RDWR); - if (videoFd == 0) { - std::cout << "Failed to open /dev/video\n"; - throw std::runtime_error("Failed to open /dev/video"); - } - std::cout << "Opened successfully\n"; - } - - RawVideoBuffer readVideo() { - assert(videoFd != 0); - RawVideoBuffer raw; - - imageInfo.doImageRefresh = 1; // full frame refresh - imageInfo.qcValid = 0; // quick cursor disabled - imageInfo.parameter.features.buf = - reinterpret_cast<unsigned char *>(raw.buffer.data()); - imageInfo.crypttype = -1; - std::cout << "Writing\n"; - - int status; - /* - status = write(videoFd, reinterpret_cast<char*>(&imageInfo), - sizeof(imageInfo)); - if (status != sizeof(imageInfo)) { - std::cout << "Write failed. Return: " << status << "\n"; - perror("perror output:"); - } - - std::cout << "Write done\n"; - */ - std::cout << "Reading\n"; - status = - read(videoFd, reinterpret_cast<char *>(&imageInfo), sizeof(imageInfo)); - std::cout << "Done reading\n"; - - if (status != 0) { - std::cerr << "Read failed with status " << status << "\n"; +class SimpleVideoPuller +{ + public: + SimpleVideoPuller() : imageInfo(){}; + + void initialize() + { + std::cout << "Opening /dev/video\n"; + videoFd = open("/dev/video", O_RDWR); + if (videoFd == 0) + { + std::cout << "Failed to open /dev/video\n"; + throw std::runtime_error("Failed to open /dev/video"); + } + std::cout << "Opened successfully\n"; } - raw.buffer.resize(imageInfo.len); - - raw.height = imageInfo.parameter.features.h; - raw.width = imageInfo.parameter.features.w; - if (imageInfo.parameter.features.jpgFmt == 422) { - raw.mode = YuvMode::YUV420; - } else { - raw.mode = YuvMode::YUV444; + RawVideoBuffer readVideo() + { + assert(videoFd != 0); + RawVideoBuffer raw; + + imageInfo.doImageRefresh = 1; // full frame refresh + imageInfo.qcValid = 0; // quick cursor disabled + imageInfo.parameter.features.buf = + reinterpret_cast<unsigned char *>(raw.buffer.data()); + imageInfo.crypttype = -1; + std::cout << "Writing\n"; + + int status; + /* + status = write(videoFd, reinterpret_cast<char*>(&imageInfo), + sizeof(imageInfo)); + if (status != sizeof(imageInfo)) { + std::cout << "Write failed. Return: " << status << "\n"; + perror("perror output:"); + } + + std::cout << "Write done\n"; + */ + std::cout << "Reading\n"; + status = read(videoFd, reinterpret_cast<char *>(&imageInfo), + sizeof(imageInfo)); + std::cout << "Done reading\n"; + + if (status != 0) + { + std::cerr << "Read failed with status " << status << "\n"; + } + + raw.buffer.resize(imageInfo.len); + + raw.height = imageInfo.parameter.features.h; + raw.width = imageInfo.parameter.features.w; + if (imageInfo.parameter.features.jpgFmt == 422) + { + raw.mode = YuvMode::YUV420; + } + else + { + raw.mode = YuvMode::YUV444; + } + return raw; } - return raw; - } - private: - int videoFd{}; - ImageInfo imageInfo; + private: + int videoFd{}; + ImageInfo imageInfo; }; #if defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) -class AsyncVideoPuller { - public: - using video_callback = std::function<void(RawVideoBuffer &)>; - - explicit AsyncVideoPuller(boost::asio::io_service &ioService) - : imageInfo(), devVideo(ioService, open("/dev/video", O_RDWR)) { - videobuf = std::make_shared<RawVideoBuffer>(); - - imageInfo.doImageRefresh = 1; // full frame refresh - imageInfo.qcValid = 0; // quick cursor disabled - imageInfo.parameter.features.buf = - reinterpret_cast<unsigned char *>(videobuf->buffer.data()); - imageInfo.crypttype = -1; - }; - - void registerCallback(video_callback &callback) { - std::lock_guard<std::mutex> lock(callbackMutex); - callbacks.push_back(callback); - startRead(); - } - - void startRead() { - auto mutableBuffer = boost::asio::buffer(&imageInfo, sizeof(imageInfo)); - boost::asio::async_read(devVideo, mutableBuffer, - [this](const boost::system::error_code &ec, - std::size_t bytes_transferred) { - if (ec) { - std::cerr << "Read failed with status " << ec - << "\n"; - } else { - this->readDone(); - } - }); - } - - void readDone() { - std::cout << "Done reading\n"; - videobuf->buffer.resize(imageInfo.len); - - videobuf->height = imageInfo.parameter.features.h; - videobuf->width = imageInfo.parameter.features.w; - if (imageInfo.parameter.features.jpgFmt == 422) { - videobuf->mode = YuvMode::YUV420; - } else { - videobuf->mode = YuvMode::YUV444; +class AsyncVideoPuller +{ + public: + using video_callback = std::function<void(RawVideoBuffer &)>; + + explicit AsyncVideoPuller(boost::asio::io_service &ioService) : + imageInfo(), devVideo(ioService, open("/dev/video", O_RDWR)) + { + videobuf = std::make_shared<RawVideoBuffer>(); + + imageInfo.doImageRefresh = 1; // full frame refresh + imageInfo.qcValid = 0; // quick cursor disabled + imageInfo.parameter.features.buf = + reinterpret_cast<unsigned char *>(videobuf->buffer.data()); + imageInfo.crypttype = -1; + }; + + void registerCallback(video_callback &callback) + { + std::lock_guard<std::mutex> lock(callbackMutex); + callbacks.push_back(callback); + startRead(); + } + + void startRead() + { + auto mutableBuffer = boost::asio::buffer(&imageInfo, sizeof(imageInfo)); + boost::asio::async_read(devVideo, mutableBuffer, + [this](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + if (ec) + { + std::cerr << "Read failed with status " + << ec << "\n"; + } + else + { + this->readDone(); + } + }); } - std::lock_guard<std::mutex> lock(callbackMutex); - for (auto &callback : callbacks) { - // TODO(ed) call callbacks async and double buffer frames - callback(*videobuf); + + void readDone() + { + std::cout << "Done reading\n"; + videobuf->buffer.resize(imageInfo.len); + + videobuf->height = imageInfo.parameter.features.h; + videobuf->width = imageInfo.parameter.features.w; + if (imageInfo.parameter.features.jpgFmt == 422) + { + videobuf->mode = YuvMode::YUV420; + } + else + { + videobuf->mode = YuvMode::YUV444; + } + std::lock_guard<std::mutex> lock(callbackMutex); + for (auto &callback : callbacks) + { + // TODO(ed) call callbacks async and double buffer frames + callback(*videobuf); + } } - } - - private: - std::shared_ptr<RawVideoBuffer> videobuf; - boost::asio::posix::stream_descriptor devVideo; - ImageInfo imageInfo; - std::mutex callbackMutex; - std::vector<video_callback> callbacks; + + private: + std::shared_ptr<RawVideoBuffer> videobuf; + boost::asio::posix::stream_descriptor devVideo; + ImageInfo imageInfo; + std::mutex callbackMutex; + std::vector<video_callback> callbacks; }; -#endif // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) -} // namespace ast_video +#endif // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) +} // namespace ast_video diff --git a/include/ast_video_types.hpp b/include/ast_video_types.hpp index f9801460e6..aeef1d8499 100644 --- a/include/ast_video_types.hpp +++ b/include/ast_video_types.hpp @@ -2,18 +2,24 @@ #include <cstdint> #include <vector> -namespace ast_video { -enum class YuvMode { YUV444 = 0, YUV420 = 1 }; +namespace ast_video +{ +enum class YuvMode +{ + YUV444 = 0, + YUV420 = 1 +}; -class RawVideoBuffer { - public: - RawVideoBuffer() : buffer(1024 * 1024 * 10, 0){}; - unsigned long height{}; - unsigned long width{}; - int ySelector{}; - int uvSelector{}; - YuvMode mode; - // TODO(ed) determine a more appropriate buffer size - std::vector<uint32_t> buffer; +class RawVideoBuffer +{ + public: + RawVideoBuffer() : buffer(1024 * 1024 * 10, 0){}; + unsigned long height{}; + unsigned long width{}; + int ySelector{}; + int uvSelector{}; + YuvMode mode; + // TODO(ed) determine a more appropriate buffer size + std::vector<uint32_t> buffer; }; -} // namespace ast_video
\ No newline at end of file +} // namespace ast_video
\ No newline at end of file diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp index e8b1a327b1..5dcd5ca362 100644 --- a/include/dbus_monitor.hpp +++ b/include/dbus_monitor.hpp @@ -1,26 +1,33 @@ #pragma once -#include <dbus_singleton.hpp> -#include <sdbusplus/bus/match.hpp> #include <crow/app.h> #include <crow/websocket.h> + #include <boost/container/flat_map.hpp> #include <boost/container/flat_set.hpp> +#include <dbus_singleton.hpp> +#include <sdbusplus/bus/match.hpp> -namespace nlohmann { +namespace nlohmann +{ template <typename... Args> -struct adl_serializer<sdbusplus::message::variant<Args...>> { - static void to_json(json& j, const sdbusplus::message::variant<Args...>& v) { - mapbox::util::apply_visitor([&](auto&& val) { j = val; }, v); - } +struct adl_serializer<sdbusplus::message::variant<Args...>> +{ + static void to_json(json& j, const sdbusplus::message::variant<Args...>& v) + { + mapbox::util::apply_visitor([&](auto&& val) { j = val; }, v); + } }; -} // namespace nlohmann +} // namespace nlohmann -namespace crow { -namespace dbus_monitor { +namespace crow +{ +namespace dbus_monitor +{ -struct DbusWebsocketSession { - std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; - boost::container::flat_set<std::string> interfaces; +struct DbusWebsocketSession +{ + std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; + boost::container::flat_set<std::string> interfaces; }; static boost::container::flat_map<crow::websocket::Connection*, @@ -28,165 +35,198 @@ static boost::container::flat_map<crow::websocket::Connection*, sessions; inline int onPropertyUpdate(sd_bus_message* m, void* userdata, - sd_bus_error* ret_error) { - if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) { - BMCWEB_LOG_ERROR << "Got sdbus error on match"; - return 0; - } - crow::websocket::Connection* connection = - static_cast<crow::websocket::Connection*>(userdata); - auto thisSession = sessions.find(connection); - if (thisSession == sessions.end()) { - BMCWEB_LOG_ERROR << "Couldn't find dbus connection " << connection; - return 0; - } - sdbusplus::message::message message(m); - using VariantType = - sdbusplus::message::variant<std::string, bool, int64_t, uint64_t, double>; - nlohmann::json j{{"event", message.get_member()}, - {"path", message.get_path()}}; - if (strcmp(message.get_member(), "PropertiesChanged") == 0) { - std::string interface_name; - boost::container::flat_map<std::string, VariantType> values; - message.read(interface_name, values); - j["properties"] = values; - j["interface"] = std::move(interface_name); - - } else if (strcmp(message.get_member(), "InterfacesAdded") == 0) { - std::string object_name; - boost::container::flat_map< - std::string, boost::container::flat_map<std::string, VariantType>> - values; - message.read(object_name, values); - for (const std::pair<std::string, - boost::container::flat_map<std::string, VariantType>>& - paths : values) { - auto it = thisSession->second.interfaces.find(paths.first); - if (it != thisSession->second.interfaces.end()) { - j["interfaces"][paths.first] = paths.second; - } + sd_bus_error* ret_error) +{ + if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) + { + BMCWEB_LOG_ERROR << "Got sdbus error on match"; + return 0; + } + crow::websocket::Connection* connection = + static_cast<crow::websocket::Connection*>(userdata); + auto thisSession = sessions.find(connection); + if (thisSession == sessions.end()) + { + BMCWEB_LOG_ERROR << "Couldn't find dbus connection " << connection; + return 0; + } + sdbusplus::message::message message(m); + using VariantType = sdbusplus::message::variant<std::string, bool, int64_t, + uint64_t, double>; + nlohmann::json j{{"event", message.get_member()}, + {"path", message.get_path()}}; + if (strcmp(message.get_member(), "PropertiesChanged") == 0) + { + std::string interface_name; + boost::container::flat_map<std::string, VariantType> values; + message.read(interface_name, values); + j["properties"] = values; + j["interface"] = std::move(interface_name); + } + else if (strcmp(message.get_member(), "InterfacesAdded") == 0) + { + std::string object_name; + boost::container::flat_map< + std::string, boost::container::flat_map<std::string, VariantType>> + values; + message.read(object_name, values); + for (const std::pair< + std::string, + boost::container::flat_map<std::string, VariantType>>& paths : + values) + { + auto it = thisSession->second.interfaces.find(paths.first); + if (it != thisSession->second.interfaces.end()) + { + j["interfaces"][paths.first] = paths.second; + } + } + } + else + { + BMCWEB_LOG_CRITICAL << "message " << message.get_member() + << " was unexpected"; + return 0; } - } else { - BMCWEB_LOG_CRITICAL << "message " << message.get_member() - << " was unexpected"; - return 0; - } - connection->sendText(j.dump()); - return 0; + connection->sendText(j.dump()); + return 0; }; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/subscribe") - .websocket() - .onopen([&](crow::websocket::Connection& conn) { - BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; - sessions[&conn] = DbusWebsocketSession(); - }) - .onclose([&](crow::websocket::Connection& conn, - const std::string& reason) { sessions.erase(&conn); }) - .onmessage([&](crow::websocket::Connection& conn, const std::string& data, - bool is_binary) { - DbusWebsocketSession& thisSession = sessions[&conn]; - BMCWEB_LOG_DEBUG << "Connection " << &conn << " recevied " << data; - nlohmann::json j = nlohmann::json::parse(data, nullptr, false); - if (j.is_discarded()) { - BMCWEB_LOG_ERROR << "Unable to parse json data for monitor"; - conn.close("Unable to parse json request"); - return; - } - nlohmann::json::iterator interfaces = j.find("interfaces"); - if (interfaces != j.end()) { - thisSession.interfaces.reserve(interfaces->size()); - for (auto& interface : *interfaces) { - const std::string* str = interface.get_ptr<const std::string*>(); - if (str != nullptr) { - thisSession.interfaces.insert(*str); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/subscribe") + .websocket() + .onopen([&](crow::websocket::Connection& conn) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; + sessions[&conn] = DbusWebsocketSession(); + }) + .onclose([&](crow::websocket::Connection& conn, + const std::string& reason) { sessions.erase(&conn); }) + .onmessage([&](crow::websocket::Connection& conn, + const std::string& data, bool is_binary) { + DbusWebsocketSession& thisSession = sessions[&conn]; + BMCWEB_LOG_DEBUG << "Connection " << &conn << " recevied " << data; + nlohmann::json j = nlohmann::json::parse(data, nullptr, false); + if (j.is_discarded()) + { + BMCWEB_LOG_ERROR << "Unable to parse json data for monitor"; + conn.close("Unable to parse json request"); + return; + } + nlohmann::json::iterator interfaces = j.find("interfaces"); + if (interfaces != j.end()) + { + thisSession.interfaces.reserve(interfaces->size()); + for (auto& interface : *interfaces) + { + const std::string* str = + interface.get_ptr<const std::string*>(); + if (str != nullptr) + { + thisSession.interfaces.insert(*str); + } + } } - } - } - nlohmann::json::iterator paths = j.find("paths"); - if (paths != j.end()) { - int interfaceCount = thisSession.interfaces.size(); - if (interfaceCount == 0) { - interfaceCount = 1; - } - // Reserve our matches upfront. For each path there is 1 for - // interfacesAdded, and InterfaceCount number for PropertiesChanged - thisSession.matches.reserve(thisSession.matches.size() + - paths->size() * (1 + interfaceCount)); - } - std::string object_manager_match_string; - std::string properties_match_string; - std::string object_manager_interfaces_match_string; - // These regexes derived on the rules here: - // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names - std::regex validPath("^/([A-Za-z0-9_]+/?)*$"); - std::regex validInterface( - "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$"); + nlohmann::json::iterator paths = j.find("paths"); + if (paths != j.end()) + { + int interfaceCount = thisSession.interfaces.size(); + if (interfaceCount == 0) + { + interfaceCount = 1; + } + // Reserve our matches upfront. For each path there is 1 for + // interfacesAdded, and InterfaceCount number for + // PropertiesChanged + thisSession.matches.reserve(thisSession.matches.size() + + paths->size() * + (1 + interfaceCount)); + } + std::string object_manager_match_string; + std::string properties_match_string; + std::string object_manager_interfaces_match_string; + // These regexes derived on the rules here: + // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names + std::regex validPath("^/([A-Za-z0-9_]+/?)*$"); + std::regex validInterface( + "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$"); - for (const auto& thisPath : *paths) { - const std::string* thisPathString = - thisPath.get_ptr<const std::string*>(); - if (thisPathString == nullptr) { - BMCWEB_LOG_ERROR << "subscribe path isn't a string?"; - conn.close(); - return; - } - if (!std::regex_match(*thisPathString, validPath)) { - BMCWEB_LOG_ERROR << "Invalid path name " << *thisPathString; - conn.close(); - return; - } - properties_match_string = - ("type='signal'," - "interface='org.freedesktop.DBus.Properties'," - "path_namespace='" + - *thisPathString + - "'," - "member='PropertiesChanged'"); - // If interfaces weren't specified, add a single match for all - // interfaces - if (thisSession.interfaces.size() == 0) { - BMCWEB_LOG_DEBUG << "Creating match " << properties_match_string; + for (const auto& thisPath : *paths) + { + const std::string* thisPathString = + thisPath.get_ptr<const std::string*>(); + if (thisPathString == nullptr) + { + BMCWEB_LOG_ERROR << "subscribe path isn't a string?"; + conn.close(); + return; + } + if (!std::regex_match(*thisPathString, validPath)) + { + BMCWEB_LOG_ERROR << "Invalid path name " << *thisPathString; + conn.close(); + return; + } + properties_match_string = + ("type='signal'," + "interface='org.freedesktop.DBus.Properties'," + "path_namespace='" + + *thisPathString + + "'," + "member='PropertiesChanged'"); + // If interfaces weren't specified, add a single match for all + // interfaces + if (thisSession.interfaces.size() == 0) + { + BMCWEB_LOG_DEBUG << "Creating match " + << properties_match_string; - thisSession.matches.emplace_back( - std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, properties_match_string, - onPropertyUpdate, &conn)); - } else { - // If interfaces were specified, add a match for each interface - for (const std::string& interface : thisSession.interfaces) { - if (!std::regex_match(interface, validInterface)) { - BMCWEB_LOG_ERROR << "Invalid interface name " << interface; - conn.close(); - return; - } - std::string ifaceMatchString = - properties_match_string + ",arg0='" + interface + "'"; - BMCWEB_LOG_DEBUG << "Creating match " << ifaceMatchString; - thisSession.matches.emplace_back( - std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, ifaceMatchString, - onPropertyUpdate, &conn)); + thisSession.matches.emplace_back( + std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + properties_match_string, onPropertyUpdate, &conn)); + } + else + { + // If interfaces were specified, add a match for each + // interface + for (const std::string& interface : thisSession.interfaces) + { + if (!std::regex_match(interface, validInterface)) + { + BMCWEB_LOG_ERROR << "Invalid interface name " + << interface; + conn.close(); + return; + } + std::string ifaceMatchString = properties_match_string + + ",arg0='" + interface + + "'"; + BMCWEB_LOG_DEBUG << "Creating match " + << ifaceMatchString; + thisSession.matches.emplace_back( + std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, ifaceMatchString, + onPropertyUpdate, &conn)); + } + } + object_manager_match_string = + ("type='signal'," + "interface='org.freedesktop.DBus.ObjectManager'," + "path_namespace='" + + *thisPathString + + "'," + "member='InterfacesAdded'"); + BMCWEB_LOG_DEBUG << "Creating match " + << object_manager_match_string; + thisSession.matches.emplace_back( + std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + object_manager_match_string, onPropertyUpdate, &conn)); } - } - object_manager_match_string = - ("type='signal'," - "interface='org.freedesktop.DBus.ObjectManager'," - "path_namespace='" + - *thisPathString + - "'," - "member='InterfacesAdded'"); - BMCWEB_LOG_DEBUG << "Creating match " << object_manager_match_string; - thisSession.matches.emplace_back( - std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, object_manager_match_string, - onPropertyUpdate, &conn)); - } - }); + }); } -} // namespace dbus_monitor -} // namespace crow +} // namespace dbus_monitor +} // namespace crow diff --git a/include/dbus_singleton.hpp b/include/dbus_singleton.hpp index a4a16bb2d1..2438152b3f 100644 --- a/include/dbus_singleton.hpp +++ b/include/dbus_singleton.hpp @@ -1,21 +1,28 @@ #pragma once -#include <sdbusplus/asio/connection.hpp> #include <iostream> +#include <sdbusplus/asio/connection.hpp> -namespace mapbox { +namespace mapbox +{ template <typename T, typename... Types> -const T* getPtr(const mapbox::util::variant<Types...>& v) { - if (v.template is<std::remove_const_t<T>>()) { - return &v.template get_unchecked<std::remove_const_t<T>>(); - } else { - return nullptr; - } +const T* getPtr(const mapbox::util::variant<Types...>& v) +{ + if (v.template is<std::remove_const_t<T>>()) + { + return &v.template get_unchecked<std::remove_const_t<T>>(); + } + else + { + return nullptr; + } } -} // namespace mapbox +} // namespace mapbox -namespace crow { -namespace connections { +namespace crow +{ +namespace connections +{ static std::shared_ptr<sdbusplus::asio::connection> systemBus; -} // namespace connections -} // namespace crow +} // namespace connections +} // namespace crow diff --git a/include/gzip_helper.hpp b/include/gzip_helper.hpp index e14fc1b50b..be13809236 100644 --- a/include/gzip_helper.hpp +++ b/include/gzip_helper.hpp @@ -1,56 +1,65 @@ #pragma once #include <zlib.h> + #include <cstring> #include <string> inline bool gzipInflate(const std::string& compressedBytes, - std::string& uncompressedBytes) { - if (compressedBytes.empty()) { - uncompressedBytes = compressedBytes; - return true; - } - - uncompressedBytes.clear(); + std::string& uncompressedBytes) +{ + if (compressedBytes.empty()) + { + uncompressedBytes = compressedBytes; + return true; + } - unsigned half_length = compressedBytes.size() / 2; + uncompressedBytes.clear(); - z_stream strm{}; + unsigned half_length = compressedBytes.size() / 2; - // The following line is nolint because we're declaring away constness. - // It's not clear why the input buffers on zlib aren't const, so this is a - // bit of a cheat for the moment - strm.next_in = (Bytef*)compressedBytes.data(); // NOLINT - strm.avail_in = compressedBytes.size(); - strm.total_out = 0; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; + z_stream strm{}; - bool done = false; + // The following line is nolint because we're declaring away constness. + // It's not clear why the input buffers on zlib aren't const, so this is a + // bit of a cheat for the moment + strm.next_in = (Bytef*)compressedBytes.data(); // NOLINT + strm.avail_in = compressedBytes.size(); + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; - if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { - return false; - } + bool done = false; - while (!done) { - // If our output buffer is too small - if (strm.total_out >= uncompressedBytes.size()) { - uncompressedBytes.resize(uncompressedBytes.size() + half_length); + if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) + { + return false; } - strm.next_out = - (Bytef*)(uncompressedBytes.data() + strm.total_out); // NOLINT - strm.avail_out = - ((uLong)uncompressedBytes.size() - strm.total_out); // NOLINT - - // Inflate another chunk. - int err = inflate(&strm, Z_SYNC_FLUSH); - if (err == Z_STREAM_END) { - done = true; - } else if (err != Z_OK) { - break; + while (!done) + { + // If our output buffer is too small + if (strm.total_out >= uncompressedBytes.size()) + { + uncompressedBytes.resize(uncompressedBytes.size() + half_length); + } + + strm.next_out = + (Bytef*)(uncompressedBytes.data() + strm.total_out); // NOLINT + strm.avail_out = + ((uLong)uncompressedBytes.size() - strm.total_out); // NOLINT + + // Inflate another chunk. + int err = inflate(&strm, Z_SYNC_FLUSH); + if (err == Z_STREAM_END) + { + done = true; + } + else if (err != Z_OK) + { + break; + } } - } - return inflateEnd(&strm) == Z_OK; + return inflateEnd(&strm) == Z_OK; }
\ No newline at end of file diff --git a/include/http_utility.hpp b/include/http_utility.hpp index f2d317206a..e13dfc0878 100644 --- a/include/http_utility.hpp +++ b/include/http_utility.hpp @@ -1,21 +1,27 @@ #pragma once #include <boost/algorithm/string.hpp> -namespace http_helpers { -inline bool requestPrefersHtml(const crow::Request& req) { - boost::string_view header = req.getHeaderValue("accept"); - std::vector<std::string> encodings; - // chrome currently sends 6 accepts headers, firefox sends 4. - encodings.reserve(6); - boost::split(encodings, header, boost::is_any_of(", "), - boost::token_compress_on); - for (const std::string& encoding : encodings) { - if (encoding == "text/html") { - return true; - } else if (encoding == "application/json") { - return false; +namespace http_helpers +{ +inline bool requestPrefersHtml(const crow::Request& req) +{ + boost::string_view header = req.getHeaderValue("accept"); + std::vector<std::string> encodings; + // chrome currently sends 6 accepts headers, firefox sends 4. + encodings.reserve(6); + boost::split(encodings, header, boost::is_any_of(", "), + boost::token_compress_on); + for (const std::string& encoding : encodings) + { + if (encoding == "text/html") + { + return true; + } + else if (encoding == "application/json") + { + return false; + } } - } - return false; + return false; } -} // namespace http_helpers
\ No newline at end of file +} // namespace http_helpers
\ No newline at end of file diff --git a/include/image_upload.hpp b/include/image_upload.hpp index df5c1ae491..2b84db8a33 100644 --- a/include/image_upload.hpp +++ b/include/image_upload.hpp @@ -1,103 +1,113 @@ #pragma once -#include <dbus_singleton.hpp> -#include <cstdio> -#include <fstream> -#include <memory> #include <crow/app.h> + #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_io.hpp> +#include <cstdio> +#include <dbus_singleton.hpp> +#include <fstream> +#include <memory> -namespace crow { -namespace image_upload { +namespace crow +{ +namespace image_upload +{ std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; inline void uploadImageHandler(const crow::Request& req, crow::Response& res, - const std::string& filename) { - // Only allow one FW update at a time - if (fwUpdateMatcher != nullptr) { - res.addHeader("Retry-After", "30"); - res.result(boost::beast::http::status::service_unavailable); - res.end(); - return; - } - // Make this const static so it survives outside this method - static boost::asio::deadline_timer timeout(*req.ioService, - boost::posix_time::seconds(5)); - - timeout.expires_from_now(boost::posix_time::seconds(5)); - - timeout.async_wait([&res](const boost::system::error_code& ec) { - fwUpdateMatcher = nullptr; - if (ec == asio::error::operation_aborted) { - // expected, we were canceled before the timer completed. - return; - } - BMCWEB_LOG_ERROR << "Timed out waiting for log event"; - - if (ec) { - BMCWEB_LOG_ERROR << "Async_wait failed " << ec; - return; + const std::string& filename) +{ + // Only allow one FW update at a time + if (fwUpdateMatcher != nullptr) + { + res.addHeader("Retry-After", "30"); + res.result(boost::beast::http::status::service_unavailable); + res.end(); + return; } + // Make this const static so it survives outside this method + static boost::asio::deadline_timer timeout(*req.ioService, + boost::posix_time::seconds(5)); - res.result(boost::beast::http::status::internal_server_error); - res.end(); - }); + timeout.expires_from_now(boost::posix_time::seconds(5)); - std::function<void(sdbusplus::message::message&)> callback = - [&res](sdbusplus::message::message& m) { - BMCWEB_LOG_DEBUG << "Match fired"; - boost::system::error_code ec; - timeout.cancel(ec); - if (ec) { - BMCWEB_LOG_ERROR << "error canceling timer " << ec; + timeout.async_wait([&res](const boost::system::error_code& ec) { + fwUpdateMatcher = nullptr; + if (ec == asio::error::operation_aborted) + { + // expected, we were canceled before the timer completed. + return; } - std::string versionInfo; - m.read(versionInfo); // Read in the object path that was just created + BMCWEB_LOG_ERROR << "Timed out waiting for log event"; - std::size_t index = versionInfo.rfind('/'); - if (index != std::string::npos) { - versionInfo.erase(0, index); + if (ec) + { + BMCWEB_LOG_ERROR << "Async_wait failed " << ec; + return; } - res.jsonValue = {{"data", std::move(versionInfo)}, - {"message", "200 OK"}, - {"status", "ok"}}; - BMCWEB_LOG_DEBUG << "ending response"; + + res.result(boost::beast::http::status::internal_server_error); res.end(); - fwUpdateMatcher = nullptr; - }; - fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, - "interface='org.freedesktop.DBus.ObjectManager',type='signal'," - "member='InterfacesAdded',path='/xyz/openbmc_project/logging'", - callback); + }); + + std::function<void(sdbusplus::message::message&)> callback = + [&res](sdbusplus::message::message& m) { + BMCWEB_LOG_DEBUG << "Match fired"; + boost::system::error_code ec; + timeout.cancel(ec); + if (ec) + { + BMCWEB_LOG_ERROR << "error canceling timer " << ec; + } + std::string versionInfo; + m.read( + versionInfo); // Read in the object path that was just created + + std::size_t index = versionInfo.rfind('/'); + if (index != std::string::npos) + { + versionInfo.erase(0, index); + } + res.jsonValue = {{"data", std::move(versionInfo)}, + {"message", "200 OK"}, + {"status", "ok"}}; + BMCWEB_LOG_DEBUG << "ending response"; + res.end(); + fwUpdateMatcher = nullptr; + }; + fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + "interface='org.freedesktop.DBus.ObjectManager',type='signal'," + "member='InterfacesAdded',path='/xyz/openbmc_project/logging'", + callback); - std::string filepath( - "/tmp/images/" + - boost::uuids::to_string(boost::uuids::random_generator()())); - BMCWEB_LOG_DEBUG << "Writing file to " << filepath; - std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | - std::ofstream::trunc); - out << req.body; - out.close(); + std::string filepath( + "/tmp/images/" + + boost::uuids::to_string(boost::uuids::random_generator()())); + BMCWEB_LOG_DEBUG << "Writing file to " << filepath; + std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | + std::ofstream::trunc); + out << req.body; + out.close(); } -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/upload/image/<str>") - .methods("POST"_method, - "PUT"_method)([](const crow::Request& req, crow::Response& res, - const std::string& filename) { - uploadImageHandler(req, res, filename); - }); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/upload/image/<str>") + .methods("POST"_method, + "PUT"_method)([](const crow::Request& req, crow::Response& res, + const std::string& filename) { + uploadImageHandler(req, res, filename); + }); - BMCWEB_ROUTE(app, "/upload/image") - .methods("POST"_method, - "PUT"_method)([](const crow::Request& req, crow::Response& res) { - uploadImageHandler(req, res, ""); - }); + BMCWEB_ROUTE(app, "/upload/image") + .methods("POST"_method, "PUT"_method)( + [](const crow::Request& req, crow::Response& res) { + uploadImageHandler(req, res, ""); + }); } -} // namespace image_upload -} // namespace crow +} // namespace image_upload +} // namespace crow diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp index 3e6443d29e..4f6c233ff7 100644 --- a/include/openbmc_dbus_rest.hpp +++ b/include/openbmc_dbus_rest.hpp @@ -1,64 +1,78 @@ #include <crow/app.h> - #include <tinyxml2.h> + +#include <boost/algorithm/string.hpp> +#include <boost/container/flat_set.hpp> #include <dbus_singleton.hpp> #include <experimental/filesystem> #include <fstream> -#include <boost/algorithm/string.hpp> -#include <boost/container/flat_set.hpp> -namespace crow { -namespace openbmc_mapper { +namespace crow +{ +namespace openbmc_mapper +{ void introspectObjects(crow::Response &res, std::string process_name, std::string path, - std::shared_ptr<nlohmann::json> transaction) { - crow::connections::systemBus->async_method_call( - [ - &res, transaction, processName{std::move(process_name)}, - objectPath{std::move(path)} - ](const boost::system::error_code ec, const std::string &introspect_xml) { - if (ec) { - BMCWEB_LOG_ERROR << "Introspect call failed with error: " - << ec.message() << " on process: " << processName - << " path: " << objectPath << "\n"; - - } else { - transaction->push_back({{"path", objectPath}}); - - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " << processName - << " " << objectPath << "\n"; - - } else { - tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); - while (node != nullptr) { - std::string childPath = node->Attribute("name"); - std::string newpath; - if (objectPath != "/") { - newpath += objectPath; - } - newpath += "/" + childPath; - // introspect the subobjects as well - introspectObjects(res, processName, newpath, transaction); - - node = node->NextSiblingElement("node"); + std::shared_ptr<nlohmann::json> transaction) +{ + crow::connections::systemBus->async_method_call( + [&res, transaction, processName{std::move(process_name)}, + objectPath{std::move(path)}](const boost::system::error_code ec, + const std::string &introspect_xml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " << ec.message() + << " on process: " << processName << " path: " << objectPath + << "\n"; } - } - } - // if we're the last outstanding caller, finish the request - if (transaction.use_count() == 1) { - res.jsonValue = {{"status", "ok"}, - {"bus_name", processName}, - {"objects", std::move(*transaction)}}; - res.end(); - } - }, - process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect"); + else + { + transaction->push_back({{"path", objectPath}}); + + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR << "XML document failed to parse " + << processName << " " << objectPath + << "\n"; + } + else + { + tinyxml2::XMLElement *node = + pRoot->FirstChildElement("node"); + while (node != nullptr) + { + std::string childPath = node->Attribute("name"); + std::string newpath; + if (objectPath != "/") + { + newpath += objectPath; + } + newpath += "/" + childPath; + // introspect the subobjects as well + introspectObjects(res, processName, newpath, + transaction); + + node = node->NextSiblingElement("node"); + } + } + } + // if we're the last outstanding caller, finish the request + if (transaction.use_count() == 1) + { + res.jsonValue = {{"status", "ok"}, + {"bus_name", processName}, + {"objects", std::move(*transaction)}}; + res.end(); + } + }, + process_name, path, "org.freedesktop.DBus.Introspectable", + "Introspect"); } // A smattering of common types to unpack. TODO(ed) this should really iterate @@ -74,46 +88,60 @@ using ManagedObjectType = std::vector<std::pair< std::string, boost::container::flat_map<std::string, DbusRestVariantType>>>>; -void getManagedObjectsForEnumerate( - const std::string &object_name, const std::string &connection_name, - crow::Response &res, std::shared_ptr<nlohmann::json> transaction) { - crow::connections::systemBus->async_method_call( - [&res, transaction](const boost::system::error_code ec, - const ManagedObjectType &objects) { - if (ec) { - BMCWEB_LOG_ERROR << ec; - } else { - nlohmann::json &dataJson = *transaction; - - for (auto &objectPath : objects) { - BMCWEB_LOG_DEBUG - << "Reading object " - << static_cast<const std::string &>(objectPath.first); - nlohmann::json &objectJson = - dataJson[static_cast<const std::string &>(objectPath.first)]; - if (objectJson.is_null()) { - objectJson = nlohmann::json::object(); +void getManagedObjectsForEnumerate(const std::string &object_name, + const std::string &connection_name, + crow::Response &res, + std::shared_ptr<nlohmann::json> transaction) +{ + crow::connections::systemBus->async_method_call( + [&res, transaction](const boost::system::error_code ec, + const ManagedObjectType &objects) { + if (ec) + { + BMCWEB_LOG_ERROR << ec; } - for (const auto &interface : objectPath.second) { - for (const auto &property : interface.second) { - nlohmann::json &propertyJson = objectJson[property.first]; - mapbox::util::apply_visitor( - [&propertyJson](auto &&val) { propertyJson = val; }, - property.second); - } + else + { + nlohmann::json &dataJson = *transaction; + + for (auto &objectPath : objects) + { + BMCWEB_LOG_DEBUG + << "Reading object " + << static_cast<const std::string &>(objectPath.first); + nlohmann::json &objectJson = + dataJson[static_cast<const std::string &>( + objectPath.first)]; + if (objectJson.is_null()) + { + objectJson = nlohmann::json::object(); + } + for (const auto &interface : objectPath.second) + { + for (const auto &property : interface.second) + { + nlohmann::json &propertyJson = + objectJson[property.first]; + mapbox::util::apply_visitor( + [&propertyJson](auto &&val) { + propertyJson = val; + }, + property.second); + } + } + } } - } - } - if (transaction.use_count() == 1) { - res.jsonValue = {{"message", "200 OK"}, - {"status", "ok"}, - {"data", std::move(*transaction)}}; - res.end(); - } - }, - connection_name, object_name, "org.freedesktop.DBus.ObjectManager", - "GetManagedObjects"); + if (transaction.use_count() == 1) + { + res.jsonValue = {{"message", "200 OK"}, + {"status", "ok"}, + {"data", std::move(*transaction)}}; + res.end(); + } + }, + connection_name, object_name, "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); } using GetSubTreeType = std::vector< @@ -121,1025 +149,1320 @@ using GetSubTreeType = std::vector< std::vector<std::pair<std::string, std::vector<std::string>>>>>; // Structure for storing data on an in progress action -struct InProgressActionData { - InProgressActionData(crow::Response &res) : res(res){}; - ~InProgressActionData() { - if (res.result() == boost::beast::http::status::internal_server_error) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = nlohmann::json::object(); +struct InProgressActionData +{ + InProgressActionData(crow::Response &res) : res(res){}; + ~InProgressActionData() + { + if (res.result() == boost::beast::http::status::internal_server_error) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = nlohmann::json::object(); + } + res.end(); + } + + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); } - res.end(); - } - - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } - crow::Response &res; - std::string path; - std::string methodName; - nlohmann::json arguments; + crow::Response &res; + std::string path; + std::string methodName; + nlohmann::json arguments; }; -std::vector<std::string> dbusArgSplit(const std::string &string) { - std::vector<std::string> ret; - if (string.empty()) { - return ret; - } - ret.push_back(""); - int containerDepth = 0; - - for (std::string::const_iterator character = string.begin(); - character != string.end(); character++) { - ret.back() += *character; - switch (*character) { - case ('a'): - break; - case ('('): - case ('{'): - containerDepth++; - break; - case ('}'): - case (')'): - containerDepth--; - if (containerDepth == 0) { - if (character + 1 != string.end()) { - ret.push_back(""); - } - } - break; - default: - if (containerDepth == 0) { - if (character + 1 != string.end()) { - ret.push_back(""); - } +std::vector<std::string> dbusArgSplit(const std::string &string) +{ + std::vector<std::string> ret; + if (string.empty()) + { + return ret; + } + ret.push_back(""); + int containerDepth = 0; + + for (std::string::const_iterator character = string.begin(); + character != string.end(); character++) + { + ret.back() += *character; + switch (*character) + { + case ('a'): + break; + case ('('): + case ('{'): + containerDepth++; + break; + case ('}'): + case (')'): + containerDepth--; + if (containerDepth == 0) + { + if (character + 1 != string.end()) + { + ret.push_back(""); + } + } + break; + default: + if (containerDepth == 0) + { + if (character + 1 != string.end()) + { + ret.push_back(""); + } + } + break; } - break; } - } } int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type, - const nlohmann::json &input_json) { - int r = 0; - BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() - << " to type: " << arg_type; - const std::vector<std::string> argTypes = dbusArgSplit(arg_type); - - // Assume a single object for now. - const nlohmann::json *j = &input_json; - nlohmann::json::const_iterator jIt = input_json.begin(); - - for (const std::string &arg_code : argTypes) { - // If we are decoding multiple objects, grab the pointer to the iterator, - // and increment it for the next loop - if (argTypes.size() > 1) { - if (jIt == input_json.end()) { - return -2; - } - j = &*jIt; - jIt++; - } - const int64_t *int_value = j->get_ptr<const int64_t *>(); - const uint64_t *uint_value = j->get_ptr<const uint64_t *>(); - const std::string *string_value = j->get_ptr<const std::string *>(); - const double *double_value = j->get_ptr<const double *>(); - const bool *b = j->get_ptr<const bool *>(); - int64_t v = 0; - double d = 0.0; - - // Do some basic type conversions that make sense. uint can be converted to - // int. int and uint can be converted to double - if (uint_value != nullptr && int_value == nullptr) { - v = static_cast<int64_t>(*uint_value); - int_value = &v; - } - if (uint_value != nullptr && double_value == nullptr) { - d = static_cast<double>(*uint_value); - double_value = &d; - } - if (int_value != nullptr && double_value == nullptr) { - d = static_cast<double>(*int_value); - double_value = &d; - } + const nlohmann::json &input_json) +{ + int r = 0; + BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() + << " to type: " << arg_type; + const std::vector<std::string> argTypes = dbusArgSplit(arg_type); + + // Assume a single object for now. + const nlohmann::json *j = &input_json; + nlohmann::json::const_iterator jIt = input_json.begin(); + + for (const std::string &arg_code : argTypes) + { + // If we are decoding multiple objects, grab the pointer to the + // iterator, and increment it for the next loop + if (argTypes.size() > 1) + { + if (jIt == input_json.end()) + { + return -2; + } + j = &*jIt; + jIt++; + } + const int64_t *int_value = j->get_ptr<const int64_t *>(); + const uint64_t *uint_value = j->get_ptr<const uint64_t *>(); + const std::string *string_value = j->get_ptr<const std::string *>(); + const double *double_value = j->get_ptr<const double *>(); + const bool *b = j->get_ptr<const bool *>(); + int64_t v = 0; + double d = 0.0; + + // Do some basic type conversions that make sense. uint can be + // converted to int. int and uint can be converted to double + if (uint_value != nullptr && int_value == nullptr) + { + v = static_cast<int64_t>(*uint_value); + int_value = &v; + } + if (uint_value != nullptr && double_value == nullptr) + { + d = static_cast<double>(*uint_value); + double_value = &d; + } + if (int_value != nullptr && double_value == nullptr) + { + d = static_cast<double>(*int_value); + double_value = &d; + } - if (arg_code == "s") { - if (string_value == nullptr) { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], - (void *)string_value->c_str()); - if (r < 0) { - return r; - } - } else if (arg_code == "i") { - if (int_value == nullptr) { - return -1; - } - int32_t i = static_cast<int32_t>(*int_value); - r = sd_bus_message_append_basic(m, arg_code[0], &i); - if (r < 0) { - return r; - } - } else if (arg_code == "b") { - // lots of ways bool could be represented here. Try them all - int bool_int = false; - if (int_value != nullptr) { - bool_int = *int_value > 0 ? 1 : 0; - } else if (b != nullptr) { - bool_int = b ? 1 : 0; - } else if (string_value != nullptr) { - bool_int = boost::istarts_with(*string_value, "t") ? 1 : 0; - } else { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], &bool_int); - if (r < 0) { - return r; - } - } else if (arg_code == "n") { - if (int_value == nullptr) { - return -1; - } - int16_t n = static_cast<int16_t>(*int_value); - r = sd_bus_message_append_basic(m, arg_code[0], &n); - if (r < 0) { - return r; - } - } else if (arg_code == "x") { - if (int_value == nullptr) { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], int_value); - if (r < 0) { - return r; - } - } else if (arg_code == "y") { - if (uint_value == nullptr) { - return -1; - } - uint8_t y = static_cast<uint8_t>(*uint_value); - r = sd_bus_message_append_basic(m, arg_code[0], &y); - } else if (arg_code == "q") { - if (uint_value == nullptr) { - return -1; - } - uint16_t q = static_cast<uint16_t>(*uint_value); - r = sd_bus_message_append_basic(m, arg_code[0], &q); - } else if (arg_code == "u") { - if (uint_value == nullptr) { - return -1; - } - uint32_t u = static_cast<uint32_t>(*uint_value); - r = sd_bus_message_append_basic(m, arg_code[0], &u); - } else if (arg_code == "t") { - if (uint_value == nullptr) { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], uint_value); - } else if (arg_code == "d") { - sd_bus_message_append_basic(m, arg_code[0], double_value); - } else if (boost::starts_with(arg_code, "a")) { - std::string contained_type = arg_code.substr(1); - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, - contained_type.c_str()); - if (r < 0) { - return r; - } - - for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); - ++it) { - r = convertJsonToDbus(m, contained_type, *it); - if (r < 0) { - return r; + if (arg_code == "s") + { + if (string_value == nullptr) + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], + (void *)string_value->c_str()); + if (r < 0) + { + return r; + } } + else if (arg_code == "i") + { + if (int_value == nullptr) + { + return -1; + } + int32_t i = static_cast<int32_t>(*int_value); + r = sd_bus_message_append_basic(m, arg_code[0], &i); + if (r < 0) + { + return r; + } + } + else if (arg_code == "b") + { + // lots of ways bool could be represented here. Try them all + int bool_int = false; + if (int_value != nullptr) + { + bool_int = *int_value > 0 ? 1 : 0; + } + else if (b != nullptr) + { + bool_int = b ? 1 : 0; + } + else if (string_value != nullptr) + { + bool_int = boost::istarts_with(*string_value, "t") ? 1 : 0; + } + else + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], &bool_int); + if (r < 0) + { + return r; + } + } + else if (arg_code == "n") + { + if (int_value == nullptr) + { + return -1; + } + int16_t n = static_cast<int16_t>(*int_value); + r = sd_bus_message_append_basic(m, arg_code[0], &n); + if (r < 0) + { + return r; + } + } + else if (arg_code == "x") + { + if (int_value == nullptr) + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], int_value); + if (r < 0) + { + return r; + } + } + else if (arg_code == "y") + { + if (uint_value == nullptr) + { + return -1; + } + uint8_t y = static_cast<uint8_t>(*uint_value); + r = sd_bus_message_append_basic(m, arg_code[0], &y); + } + else if (arg_code == "q") + { + if (uint_value == nullptr) + { + return -1; + } + uint16_t q = static_cast<uint16_t>(*uint_value); + r = sd_bus_message_append_basic(m, arg_code[0], &q); + } + else if (arg_code == "u") + { + if (uint_value == nullptr) + { + return -1; + } + uint32_t u = static_cast<uint32_t>(*uint_value); + r = sd_bus_message_append_basic(m, arg_code[0], &u); + } + else if (arg_code == "t") + { + if (uint_value == nullptr) + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], uint_value); + } + else if (arg_code == "d") + { + sd_bus_message_append_basic(m, arg_code[0], double_value); + } + else if (boost::starts_with(arg_code, "a")) + { + std::string contained_type = arg_code.substr(1); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, + contained_type.c_str()); + if (r < 0) + { + return r; + } + + for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); + ++it) + { + r = convertJsonToDbus(m, contained_type, *it); + if (r < 0) + { + return r; + } - it++; - } - sd_bus_message_close_container(m); - } else if (boost::starts_with(arg_code, "v")) { - std::string contained_type = arg_code.substr(1); - BMCWEB_LOG_DEBUG << "variant type: " << arg_code - << " appending variant of type: " << contained_type; - r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, - contained_type.c_str()); - if (r < 0) { - return r; - } - - r = convertJsonToDbus(m, contained_type, input_json); - if (r < 0) { - return r; - } - - r = sd_bus_message_close_container(m); - if (r < 0) { - return r; - } - } else if (boost::starts_with(arg_code, "(") && - boost::ends_with(arg_code, ")")) { - std::string contained_type = arg_code.substr(1, arg_code.size() - 1); - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, - contained_type.c_str()); - nlohmann::json::const_iterator it = j->begin(); - for (const std::string &arg_code : dbusArgSplit(arg_type)) { - if (it == j->end()) { - return -1; + it++; + } + sd_bus_message_close_container(m); } - r = convertJsonToDbus(m, arg_code, *it); - if (r < 0) { - return r; + else if (boost::starts_with(arg_code, "v")) + { + std::string contained_type = arg_code.substr(1); + BMCWEB_LOG_DEBUG + << "variant type: " << arg_code + << " appending variant of type: " << contained_type; + r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, + contained_type.c_str()); + if (r < 0) + { + return r; + } + + r = convertJsonToDbus(m, contained_type, input_json); + if (r < 0) + { + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + { + return r; + } } - it++; - } - r = sd_bus_message_close_container(m); - } else if (boost::starts_with(arg_code, "{") && - boost::ends_with(arg_code, "}")) { - std::string contained_type = arg_code.substr(1, arg_code.size() - 1); - r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, - contained_type.c_str()); - std::vector<std::string> codes = dbusArgSplit(contained_type); - if (codes.size() != 2) { - return -1; - } - const std::string &key_type = codes[0]; - const std::string &value_type = codes[1]; - for (auto it : j->items()) { - r = convertJsonToDbus(m, key_type, it.key()); - if (r < 0) { - return r; + else if (boost::starts_with(arg_code, "(") && + boost::ends_with(arg_code, ")")) + { + std::string contained_type = + arg_code.substr(1, arg_code.size() - 1); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, + contained_type.c_str()); + nlohmann::json::const_iterator it = j->begin(); + for (const std::string &arg_code : dbusArgSplit(arg_type)) + { + if (it == j->end()) + { + return -1; + } + r = convertJsonToDbus(m, arg_code, *it); + if (r < 0) + { + return r; + } + it++; + } + r = sd_bus_message_close_container(m); } + else if (boost::starts_with(arg_code, "{") && + boost::ends_with(arg_code, "}")) + { + std::string contained_type = + arg_code.substr(1, arg_code.size() - 1); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, + contained_type.c_str()); + std::vector<std::string> codes = dbusArgSplit(contained_type); + if (codes.size() != 2) + { + return -1; + } + const std::string &key_type = codes[0]; + const std::string &value_type = codes[1]; + for (auto it : j->items()) + { + r = convertJsonToDbus(m, key_type, it.key()); + if (r < 0) + { + return r; + } - r = convertJsonToDbus(m, value_type, it.value()); - if (r < 0) { - return r; + r = convertJsonToDbus(m, value_type, it.value()); + if (r < 0) + { + return r; + } + } + r = sd_bus_message_close_container(m); + } + else + { + return -2; + } + if (r < 0) + { + return r; } - } - r = sd_bus_message_close_container(m); - } else { - return -2; - } - if (r < 0) { - return r; - } - if (argTypes.size() > 1) { - jIt++; + if (argTypes.size() > 1) + { + jIt++; + } } - } } void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction, - const std::string &connectionName) { - BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " << connectionName; - crow::connections::systemBus->async_method_call( - [ - transaction, connectionName{std::string(connectionName)} - ](const boost::system::error_code ec, const std::string &introspect_xml) { - BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; - if (ec) { - BMCWEB_LOG_ERROR << "Introspect call failed with error: " - << ec.message() << " on process: " << connectionName - << "\n"; - } else { - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " - << connectionName << "\n"; - - } else { - tinyxml2::XMLElement *interface_node = - pRoot->FirstChildElement("interface"); - while (interface_node != nullptr) { - std::string this_interface_name = - interface_node->Attribute("name"); - tinyxml2::XMLElement *method_node = - interface_node->FirstChildElement("method"); - while (method_node != nullptr) { - std::string this_methodName = method_node->Attribute("name"); - BMCWEB_LOG_DEBUG << "Found method: " << this_methodName; - if (this_methodName == transaction->methodName) { - sdbusplus::message::message m = - crow::connections::systemBus->new_method_call( - connectionName.c_str(), transaction->path.c_str(), - this_interface_name.c_str(), - transaction->methodName.c_str()); - - tinyxml2::XMLElement *argument_node = - method_node->FirstChildElement("arg"); - - nlohmann::json::const_iterator arg_it = - transaction->arguments.begin(); - - while (argument_node != nullptr) { - std::string arg_direction = - argument_node->Attribute("direction"); - if (arg_direction == "in") { - std::string arg_type = argument_node->Attribute("type"); - if (arg_it == transaction->arguments.end()) { - transaction->setErrorStatus(); - return; - } - if (convertJsonToDbus(m.get(), arg_type, *arg_it) < 0) { - transaction->setErrorStatus(); - return; - } - - arg_it++; - } - argument_node = method_node->NextSiblingElement("arg"); - } - crow::connections::systemBus->async_send( - m, [transaction](boost::system::error_code ec, - sdbusplus::message::message &m) { - if (ec) { - transaction->setErrorStatus(); - return; + const std::string &connectionName) +{ + BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " + << connectionName; + crow::connections::systemBus->async_method_call( + [transaction, connectionName{std::string(connectionName)}]( + const boost::system::error_code ec, + const std::string &introspect_xml) { + BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " << ec.message() + << " on process: " << connectionName << "\n"; + } + else + { + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR << "XML document failed to parse " + << connectionName << "\n"; + } + else + { + tinyxml2::XMLElement *interface_node = + pRoot->FirstChildElement("interface"); + while (interface_node != nullptr) + { + std::string this_interface_name = + interface_node->Attribute("name"); + tinyxml2::XMLElement *method_node = + interface_node->FirstChildElement("method"); + while (method_node != nullptr) + { + std::string this_methodName = + method_node->Attribute("name"); + BMCWEB_LOG_DEBUG << "Found method: " + << this_methodName; + if (this_methodName == transaction->methodName) + { + sdbusplus::message::message m = + crow::connections::systemBus + ->new_method_call( + connectionName.c_str(), + transaction->path.c_str(), + this_interface_name.c_str(), + transaction->methodName.c_str()); + + tinyxml2::XMLElement *argument_node = + method_node->FirstChildElement("arg"); + + nlohmann::json::const_iterator arg_it = + transaction->arguments.begin(); + + while (argument_node != nullptr) + { + std::string arg_direction = + argument_node->Attribute("direction"); + if (arg_direction == "in") + { + std::string arg_type = + argument_node->Attribute("type"); + if (arg_it == + transaction->arguments.end()) + { + transaction->setErrorStatus(); + return; + } + if (convertJsonToDbus(m.get(), arg_type, + *arg_it) < 0) + { + transaction->setErrorStatus(); + return; + } + + arg_it++; + } + argument_node = + method_node->NextSiblingElement("arg"); + } + crow::connections::systemBus->async_send( + m, [transaction]( + boost::system::error_code ec, + sdbusplus::message::message &m) { + if (ec) + { + transaction->setErrorStatus(); + return; + } + transaction->res.jsonValue = { + {"status", "ok"}, + {"message", "200 OK"}, + {"data", nullptr}}; + }); + break; + } + method_node = + method_node->NextSiblingElement("method"); } - transaction->res.jsonValue = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", nullptr}}; - }); - break; + interface_node = + interface_node->NextSiblingElement("interface"); + } } - method_node = method_node->NextSiblingElement("method"); - } - interface_node = interface_node->NextSiblingElement("interface"); } - } - } - }, - connectionName, transaction->path, "org.freedesktop.DBus.Introspectable", - "Introspect"); + }, + connectionName, transaction->path, + "org.freedesktop.DBus.Introspectable", "Introspect"); } void handle_action(const crow::Request &req, crow::Response &res, - const std::string &objectPath, - const std::string &methodName) { - nlohmann::json requestDbusData = - nlohmann::json::parse(req.body, nullptr, false); - - if (requestDbusData.is_discarded()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - if (!requestDbusData.is_array()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - auto transaction = std::make_shared<InProgressActionData>(res); - - transaction->path = objectPath; - transaction->methodName = methodName; - transaction->arguments = std::move(requestDbusData); - crow::connections::systemBus->async_method_call( - [transaction]( - const boost::system::error_code ec, - const std::vector<std::pair<std::string, std::vector<std::string>>> - &interface_names) { - if (ec || interface_names.size() <= 0) { - transaction->setErrorStatus(); - return; - } + const std::string &objectPath, const std::string &methodName) +{ + nlohmann::json requestDbusData = + nlohmann::json::parse(req.body, nullptr, false); + + if (requestDbusData.is_discarded()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + if (!requestDbusData.is_array()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + auto transaction = std::make_shared<InProgressActionData>(res); + + transaction->path = objectPath; + transaction->methodName = methodName; + transaction->arguments = std::move(requestDbusData); + crow::connections::systemBus->async_method_call( + [transaction]( + const boost::system::error_code ec, + const std::vector<std::pair<std::string, std::vector<std::string>>> + &interface_names) { + if (ec || interface_names.size() <= 0) + { + transaction->setErrorStatus(); + return; + } - BMCWEB_LOG_DEBUG << "GetObject returned objects " - << interface_names.size(); + BMCWEB_LOG_DEBUG << "GetObject returned objects " + << interface_names.size(); - for (const std::pair<std::string, std::vector<std::string>> &object : - interface_names) { - findActionOnInterface(transaction, object.first); - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, - std::array<std::string, 0>()); + for (const std::pair<std::string, std::vector<std::string>> + &object : interface_names) + { + findActionOnInterface(transaction, object.first); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, + std::array<std::string, 0>()); } -void handle_list(crow::Response &res, const std::string &objectPath) { - crow::connections::systemBus->async_method_call( - [&res](const boost::system::error_code ec, - std::vector<std::string> &objectPaths) { - if (ec) { - res.result(boost::beast::http::status::internal_server_error); - } else { - res.jsonValue = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", std::move(objectPaths)}}; - } - res.end(); - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, - static_cast<int32_t>(99), std::array<std::string, 0>()); +void handle_list(crow::Response &res, const std::string &objectPath) +{ + crow::connections::systemBus->async_method_call( + [&res](const boost::system::error_code ec, + std::vector<std::string> &objectPaths) { + if (ec) + { + res.result(boost::beast::http::status::internal_server_error); + } + else + { + res.jsonValue = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", std::move(objectPaths)}}; + } + res.end(); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, + static_cast<int32_t>(99), std::array<std::string, 0>()); } -void handle_enumerate(crow::Response &res, const std::string &objectPath) { - crow::connections::systemBus->async_method_call( - [&res, objectPath{std::string(objectPath)} ]( - const boost::system::error_code ec, - const GetSubTreeType &object_names) { - if (ec) { - res.jsonValue = {{"message", "200 OK"}, - {"status", "ok"}, - {"data", nlohmann::json::object()}}; - - res.end(); - return; - } +void handle_enumerate(crow::Response &res, const std::string &objectPath) +{ + crow::connections::systemBus->async_method_call( + [&res, objectPath{std::string(objectPath)}]( + const boost::system::error_code ec, + const GetSubTreeType &object_names) { + if (ec) + { + res.jsonValue = {{"message", "200 OK"}, + {"status", "ok"}, + {"data", nlohmann::json::object()}}; - boost::container::flat_set<std::string> connections; + res.end(); + return; + } - for (const auto &object : object_names) { - for (const auto &Connection : object.second) { - connections.insert(Connection.first); - } - } + boost::container::flat_set<std::string> connections; - if (connections.size() <= 0) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - auto transaction = - std::make_shared<nlohmann::json>(nlohmann::json::object()); - for (const std::string &Connection : connections) { - getManagedObjectsForEnumerate(objectPath, Connection, res, - transaction); - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, (int32_t)0, - std::array<std::string, 0>()); + for (const auto &object : object_names) + { + for (const auto &Connection : object.second) + { + connections.insert(Connection.first); + } + } + + if (connections.size() <= 0) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + auto transaction = + std::make_shared<nlohmann::json>(nlohmann::json::object()); + for (const std::string &Connection : connections) + { + getManagedObjectsForEnumerate(objectPath, Connection, res, + transaction); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, + (int32_t)0, std::array<std::string, 0>()); } void handle_get(crow::Response &res, std::string &objectPath, - std::string &destProperty) { - BMCWEB_LOG_DEBUG << "handle_get: " << objectPath << " prop:" << destProperty; - std::shared_ptr<std::string> property_name = - std::make_shared<std::string>(std::move(destProperty)); - - std::shared_ptr<std::string> path = - std::make_shared<std::string>(std::move(objectPath)); - - using GetObjectType = - std::vector<std::pair<std::string, std::vector<std::string>>>; - crow::connections::systemBus->async_method_call( - [&res, path, property_name](const boost::system::error_code ec, - const GetObjectType &object_names) { - if (ec || object_names.size() <= 0) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - std::shared_ptr<nlohmann::json> response = - std::make_shared<nlohmann::json>(nlohmann::json::object()); - // The mapper should never give us an empty interface names list, but - // check anyway - for (const std::pair<std::string, std::vector<std::string>> connection : - object_names) { - const std::vector<std::string> &interfaceNames = connection.second; - - if (interfaceNames.size() <= 0) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - - for (const std::string &interface : interfaceNames) { - crow::connections::systemBus->async_method_call( - [&res, response, property_name]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, DbusRestVariantType>> &properties) { - if (ec) { - BMCWEB_LOG_ERROR << "Bad dbus request error: " << ec; - } else { - for (const std::pair<std::string, DbusRestVariantType> - &property : properties) { - // if property name is empty, or matches our search query, - // add it to the response json - - if (property_name->empty()) { - mapbox::util::apply_visitor( - [&response, &property](auto &&val) { - (*response)[property.first] = val; - }, - property.second); - } else if (property.first == *property_name) { - mapbox::util::apply_visitor( - [&response](auto &&val) { (*response) = val; }, - property.second); - } - } - } - if (response.use_count() == 1) { - res.jsonValue = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", *response}}; - + std::string &destProperty) +{ + BMCWEB_LOG_DEBUG << "handle_get: " << objectPath + << " prop:" << destProperty; + std::shared_ptr<std::string> property_name = + std::make_shared<std::string>(std::move(destProperty)); + + std::shared_ptr<std::string> path = + std::make_shared<std::string>(std::move(objectPath)); + + using GetObjectType = + std::vector<std::pair<std::string, std::vector<std::string>>>; + crow::connections::systemBus->async_method_call( + [&res, path, property_name](const boost::system::error_code ec, + const GetObjectType &object_names) { + if (ec || object_names.size() <= 0) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + std::shared_ptr<nlohmann::json> response = + std::make_shared<nlohmann::json>(nlohmann::json::object()); + // The mapper should never give us an empty interface names list, + // but check anyway + for (const std::pair<std::string, std::vector<std::string>> + connection : object_names) + { + const std::vector<std::string> &interfaceNames = + connection.second; + + if (interfaceNames.size() <= 0) + { + res.result(boost::beast::http::status::not_found); res.end(); - } - }, - connection.first, *path, "org.freedesktop.DBus.Properties", - "GetAll", interface); - } - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetObject", *path, - std::array<std::string, 0>()); + return; + } + + for (const std::string &interface : interfaceNames) + { + crow::connections::systemBus->async_method_call( + [&res, response, property_name]( + const boost::system::error_code ec, + const std::vector< + std::pair<std::string, DbusRestVariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad dbus request error: " + << ec; + } + else + { + for (const std::pair<std::string, + DbusRestVariantType> + &property : properties) + { + // if property name is empty, or matches our + // search query, add it to the response json + + if (property_name->empty()) + { + mapbox::util::apply_visitor( + [&response, &property](auto &&val) { + (*response)[property.first] = + val; + }, + property.second); + } + else if (property.first == *property_name) + { + mapbox::util::apply_visitor( + [&response](auto &&val) { + (*response) = val; + }, + property.second); + } + } + } + if (response.use_count() == 1) + { + res.jsonValue = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", *response}}; + + res.end(); + } + }, + connection.first, *path, + "org.freedesktop.DBus.Properties", "GetAll", interface); + } + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", *path, + std::array<std::string, 0>()); } -struct AsyncPutRequest { - AsyncPutRequest(crow::Response &res) : res(res) { - res.jsonValue = { - {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; - } - ~AsyncPutRequest() { - if (res.result() == boost::beast::http::status::internal_server_error) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = nlohmann::json::object(); +struct AsyncPutRequest +{ + AsyncPutRequest(crow::Response &res) : res(res) + { + res.jsonValue = { + {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; } + ~AsyncPutRequest() + { + if (res.result() == boost::beast::http::status::internal_server_error) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = nlohmann::json::object(); + } - if (res.jsonValue.empty()) { - res.result(boost::beast::http::status::forbidden); - res.jsonValue = { - {"status", "error"}, - {"message", "403 Forbidden"}, - {"data", - {{"message", - "The specified property cannot be created: " + propertyName}}}}; - } + if (res.jsonValue.empty()) + { + res.result(boost::beast::http::status::forbidden); + res.jsonValue = { + {"status", "error"}, + {"message", "403 Forbidden"}, + {"data", + {{"message", "The specified property cannot be created: " + + propertyName}}}}; + } - res.end(); - } + res.end(); + } - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); + } - crow::Response &res; - std::string objectPath; - std::string propertyName; - nlohmann::json propertyValue; + crow::Response &res; + std::string objectPath; + std::string propertyName; + nlohmann::json propertyValue; }; void handlePut(const crow::Request &req, crow::Response &res, - const std::string &objectPath, const std::string &destProperty) { - nlohmann::json requestDbusData = - nlohmann::json::parse(req.body, nullptr, false); - - if (requestDbusData.is_discarded()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - - nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); - if (propertyIt == requestDbusData.end()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - const nlohmann::json &propertySetValue = *propertyIt; - auto transaction = std::make_shared<AsyncPutRequest>(res); - transaction->objectPath = objectPath; - transaction->propertyName = destProperty; - transaction->propertyValue = propertySetValue; - - using GetObjectType = - std::vector<std::pair<std::string, std::vector<std::string>>>; - - crow::connections::systemBus->async_method_call( - [transaction](const boost::system::error_code ec, - const GetObjectType &object_names) { - if (!ec && object_names.size() <= 0) { - transaction->res.result(boost::beast::http::status::not_found); - return; - } + const std::string &objectPath, const std::string &destProperty) +{ + nlohmann::json requestDbusData = + nlohmann::json::parse(req.body, nullptr, false); + + if (requestDbusData.is_discarded()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } - for (const std::pair<std::string, std::vector<std::string>> connection : - object_names) { - const std::string &connectionName = connection.first; - - crow::connections::systemBus->async_method_call( - [ connectionName{std::string(connectionName)}, transaction ]( - const boost::system::error_code ec, - const std::string &introspectXml) { - if (ec) { - BMCWEB_LOG_ERROR - << "Introspect call failed with error: " << ec.message() - << " on process: " << connectionName; - transaction->setErrorStatus(); - return; - } - tinyxml2::XMLDocument doc; + nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); + if (propertyIt == requestDbusData.end()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + const nlohmann::json &propertySetValue = *propertyIt; + auto transaction = std::make_shared<AsyncPutRequest>(res); + transaction->objectPath = objectPath; + transaction->propertyName = destProperty; + transaction->propertyValue = propertySetValue; + + using GetObjectType = + std::vector<std::pair<std::string, std::vector<std::string>>>; + + crow::connections::systemBus->async_method_call( + [transaction](const boost::system::error_code ec, + const GetObjectType &object_names) { + if (!ec && object_names.size() <= 0) + { + transaction->res.result(boost::beast::http::status::not_found); + return; + } - doc.Parse(introspectXml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse: " - << introspectXml; - transaction->setErrorStatus(); - return; - } - tinyxml2::XMLElement *ifaceNode = - pRoot->FirstChildElement("interface"); - while (ifaceNode != nullptr) { - const char *interfaceName = ifaceNode->Attribute("name"); - BMCWEB_LOG_DEBUG << "found interface " << interfaceName; - tinyxml2::XMLElement *propNode = - ifaceNode->FirstChildElement("property"); - while (propNode != nullptr) { - const char *propertyName = propNode->Attribute("name"); - BMCWEB_LOG_DEBUG << "Found property " << propertyName; - if (propertyName == transaction->propertyName) { - const char *argType = propNode->Attribute("type"); - if (argType != nullptr) { - sdbusplus::message::message m = - crow::connections::systemBus->new_method_call( - connectionName.c_str(), - transaction->objectPath.c_str(), - "org.freedesktop.DBus.Properties", "Set"); - m.append(interfaceName, transaction->propertyName); - int r = sd_bus_message_open_container( - m.get(), SD_BUS_TYPE_VARIANT, argType); - if (r < 0) { - transaction->setErrorStatus(); - return; + for (const std::pair<std::string, std::vector<std::string>> + connection : object_names) + { + const std::string &connectionName = connection.first; + + crow::connections::systemBus->async_method_call( + [connectionName{std::string(connectionName)}, + transaction](const boost::system::error_code ec, + const std::string &introspectXml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " + << ec.message() + << " on process: " << connectionName; + transaction->setErrorStatus(); + return; } - r = convertJsonToDbus(m.get(), argType, - transaction->propertyValue); - if (r < 0) { - transaction->setErrorStatus(); - return; + tinyxml2::XMLDocument doc; + + doc.Parse(introspectXml.c_str()); + tinyxml2::XMLNode *pRoot = + doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR << "XML document failed to parse: " + << introspectXml; + transaction->setErrorStatus(); + return; } - r = sd_bus_message_close_container(m.get()); - if (r < 0) { - transaction->setErrorStatus(); - return; + tinyxml2::XMLElement *ifaceNode = + pRoot->FirstChildElement("interface"); + while (ifaceNode != nullptr) + { + const char *interfaceName = + ifaceNode->Attribute("name"); + BMCWEB_LOG_DEBUG << "found interface " + << interfaceName; + tinyxml2::XMLElement *propNode = + ifaceNode->FirstChildElement("property"); + while (propNode != nullptr) + { + const char *propertyName = + propNode->Attribute("name"); + BMCWEB_LOG_DEBUG << "Found property " + << propertyName; + if (propertyName == transaction->propertyName) + { + const char *argType = + propNode->Attribute("type"); + if (argType != nullptr) + { + sdbusplus::message::message m = + crow::connections::systemBus + ->new_method_call( + connectionName.c_str(), + transaction->objectPath + .c_str(), + "org.freedesktop.DBus." + "Properties", + "Set"); + m.append(interfaceName, + transaction->propertyName); + int r = sd_bus_message_open_container( + m.get(), SD_BUS_TYPE_VARIANT, + argType); + if (r < 0) + { + transaction->setErrorStatus(); + return; + } + r = convertJsonToDbus( + m.get(), argType, + transaction->propertyValue); + if (r < 0) + { + transaction->setErrorStatus(); + return; + } + r = sd_bus_message_close_container( + m.get()); + if (r < 0) + { + transaction->setErrorStatus(); + return; + } + + crow::connections::systemBus + ->async_send( + m, + [transaction]( + boost::system::error_code + ec, + sdbusplus::message::message + &m) { + BMCWEB_LOG_DEBUG << "sent"; + if (ec) + { + transaction->res + .jsonValue + ["status"] = + "error"; + transaction->res + .jsonValue + ["message"] = + ec.message(); + } + }); + } + } + propNode = + propNode->NextSiblingElement("property"); + } + ifaceNode = + ifaceNode->NextSiblingElement("interface"); } - - crow::connections::systemBus->async_send( - m, [transaction](boost::system::error_code ec, - sdbusplus::message::message &m) { - BMCWEB_LOG_DEBUG << "sent"; - if (ec) { - transaction->res.jsonValue["status"] = "error"; - transaction->res.jsonValue["message"] = - ec.message(); - } - }); - } - } - propNode = propNode->NextSiblingElement("property"); - } - ifaceNode = ifaceNode->NextSiblingElement("interface"); - } - }, - connectionName, transaction->objectPath, - "org.freedesktop.DBus.Introspectable", "Introspect"); - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetObject", transaction->objectPath, - std::array<std::string, 0>()); + }, + connectionName, transaction->objectPath, + "org.freedesktop.DBus.Introspectable", "Introspect"); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + transaction->objectPath, std::array<std::string, 0>()); } -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...> &app) { - BMCWEB_ROUTE(app, "/bus/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res) { - res.jsonValue = {{"busses", {{{"name", "system"}}}}, {"status", "ok"}}; - }); - - BMCWEB_ROUTE(app, "/bus/system/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res) { - - auto myCallback = [&res](const boost::system::error_code ec, - std::vector<std::string> &names) { - if (ec) { - BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; - res.result(boost::beast::http::status::internal_server_error); - } else { - std::sort(names.begin(), names.end()); - nlohmann::json j{{"status", "ok"}}; - auto &objectsSub = j["objects"]; - for (auto &name : names) { - objectsSub.push_back({{"name", name}}); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) +{ + BMCWEB_ROUTE(app, "/bus/") + .methods("GET"_method)( + [](const crow::Request &req, crow::Response &res) { + res.jsonValue = {{"busses", {{{"name", "system"}}}}, + {"status", "ok"}}; + }); + + BMCWEB_ROUTE(app, "/bus/system/") + .methods("GET"_method)( + [](const crow::Request &req, crow::Response &res) { + auto myCallback = [&res](const boost::system::error_code ec, + std::vector<std::string> &names) { + if (ec) + { + BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; + res.result( + boost::beast::http::status::internal_server_error); + } + else + { + std::sort(names.begin(), names.end()); + nlohmann::json j{{"status", "ok"}}; + auto &objectsSub = j["objects"]; + for (auto &name : names) + { + objectsSub.push_back({{"name", name}}); + } + res.jsonValue = std::move(j); + } + res.end(); + }; + crow::connections::systemBus->async_method_call( + std::move(myCallback), "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "ListNames"); + }); + + BMCWEB_ROUTE(app, "/list/") + .methods("GET"_method)( + [](const crow::Request &req, crow::Response &res) { + handle_list(res, "/"); + }); + + BMCWEB_ROUTE(app, "/xyz/<path>") + .methods("GET"_method, "PUT"_method, + "POST"_method)([](const crow::Request &req, + crow::Response &res, + const std::string &path) { + std::string objectPath = "/xyz/" + path; + + // Trim any trailing "/" at the end + if (boost::ends_with(objectPath, "/")) + { + objectPath.pop_back(); } - res.jsonValue = std::move(j); - } - res.end(); - }; - crow::connections::systemBus->async_method_call( - std::move(myCallback), "org.freedesktop.DBus", "/", - "org.freedesktop.DBus", "ListNames"); - }); - - BMCWEB_ROUTE(app, "/list/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res) { - handle_list(res, "/"); - }); - - BMCWEB_ROUTE(app, "/xyz/<path>") - .methods("GET"_method, "PUT"_method, - "POST"_method)([](const crow::Request &req, crow::Response &res, - const std::string &path) { - std::string objectPath = "/xyz/" + path; - - // Trim any trailing "/" at the end - if (boost::ends_with(objectPath, "/")) { - objectPath.pop_back(); - } - - // If accessing a single attribute, fill in and update objectPath, - // otherwise leave destProperty blank - std::string destProperty = ""; - const char *attrSeperator = "/attr/"; - size_t attrPosition = path.find(attrSeperator); - if (attrPosition != path.npos) { - objectPath = "/xyz/" + path.substr(0, attrPosition); - destProperty = - path.substr(attrPosition + strlen(attrSeperator), path.length()); - } - - if (req.method() == "POST"_method) { - constexpr const char *action_seperator = "/action/"; - size_t action_position = path.find(action_seperator); - if (action_position != path.npos) { - objectPath = "/xyz/" + path.substr(0, action_position); - std::string post_property = path.substr( - (action_position + strlen(action_seperator)), path.length()); - handle_action(req, res, objectPath, post_property); - return; - } - } else if (req.method() == "GET"_method) { - if (boost::ends_with(objectPath, "/enumerate")) { - objectPath.erase(objectPath.end() - 10, objectPath.end()); - handle_enumerate(res, objectPath); - } else if (boost::ends_with(objectPath, "/list")) { - objectPath.erase(objectPath.end() - 5, objectPath.end()); - handle_list(res, objectPath); - } else { - handle_get(res, objectPath, destProperty); - } - return; - } else if (req.method() == "PUT"_method) { - handlePut(req, res, objectPath, destProperty); - return; - } - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - }); - - BMCWEB_ROUTE(app, "/bus/system/<str>/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res, - const std::string &Connection) { - std::shared_ptr<nlohmann::json> transaction; - introspectObjects(res, Connection, "/", transaction); - }); - - BMCWEB_ROUTE(app, "/download/dump/<str>/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res, - const std::string &dumpId) { - std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); - if (!std::regex_match(dumpId, validFilename)) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - std::experimental::filesystem::path loc( - "/var/lib/phosphor-debug-collector/dumps"); + // If accessing a single attribute, fill in and update objectPath, + // otherwise leave destProperty blank + std::string destProperty = ""; + const char *attrSeperator = "/attr/"; + size_t attrPosition = path.find(attrSeperator); + if (attrPosition != path.npos) + { + objectPath = "/xyz/" + path.substr(0, attrPosition); + destProperty = path.substr(attrPosition + strlen(attrSeperator), + path.length()); + } - loc += dumpId; + if (req.method() == "POST"_method) + { + constexpr const char *action_seperator = "/action/"; + size_t action_position = path.find(action_seperator); + if (action_position != path.npos) + { + objectPath = "/xyz/" + path.substr(0, action_position); + std::string post_property = path.substr( + (action_position + strlen(action_seperator)), + path.length()); + handle_action(req, res, objectPath, post_property); + return; + } + } + else if (req.method() == "GET"_method) + { + if (boost::ends_with(objectPath, "/enumerate")) + { + objectPath.erase(objectPath.end() - 10, objectPath.end()); + handle_enumerate(res, objectPath); + } + else if (boost::ends_with(objectPath, "/list")) + { + objectPath.erase(objectPath.end() - 5, objectPath.end()); + handle_list(res, objectPath); + } + else + { + handle_get(res, objectPath, destProperty); + } + return; + } + else if (req.method() == "PUT"_method) + { + handlePut(req, res, objectPath, destProperty); + return; + } - if (!std::experimental::filesystem::exists(loc) || - !std::experimental::filesystem::is_directory(loc)) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - std::experimental::filesystem::directory_iterator files(loc); - for (auto &file : files) { - std::ifstream readFile(file.path()); - if (readFile.good()) { - continue; - } - res.addHeader("Content-Type", "application/octet-stream"); - res.body() = {std::istreambuf_iterator<char>(readFile), - std::istreambuf_iterator<char>()}; - res.end(); - } - res.result(boost::beast::http::status::not_found); - res.end(); - return; - }); - - BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res, - const std::string &processName, - const std::string &requestedPath) { - std::vector<std::string> strs; - boost::split(strs, requestedPath, boost::is_any_of("/")); - std::string objectPath; - std::string interfaceName; - std::string methodName; - auto it = strs.begin(); - if (it == strs.end()) { - objectPath = "/"; - } - while (it != strs.end()) { - // Check if segment contains ".". If it does, it must be an - // interface - if (it->find(".") != std::string::npos) { - break; - // THis check is neccesary as the trailing slash gets parsed as - // part of our <path> specifier above, which causes the normal - // trailing backslash redirector to fail. - } else if (!it->empty()) { - objectPath += "/" + *it; - } - it++; - } - if (it != strs.end()) { - interfaceName = *it; - it++; - - // after interface, we might have a method name - if (it != strs.end()) { - methodName = *it; - it++; - } - } - if (it != strs.end()) { - // if there is more levels past the method name, something went - // wrong, return not found - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - if (interfaceName.empty()) { - crow::connections::systemBus->async_method_call( - [&, processName, objectPath](const boost::system::error_code ec, - const std::string &introspect_xml) { - if (ec) { - BMCWEB_LOG_ERROR - << "Introspect call failed with error: " << ec.message() - << " on process: " << processName - << " path: " << objectPath << "\n"; - - } else { - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " - << processName << " " << objectPath - << "\n"; - res.jsonValue = {{"status", "XML parse error"}}; - res.result( - boost::beast::http::status::internal_server_error); - } else { - nlohmann::json interfacesArray = nlohmann::json::array(); - tinyxml2::XMLElement *interface = - pRoot->FirstChildElement("interface"); + res.result(boost::beast::http::status::method_not_allowed); + res.end(); + }); + + BMCWEB_ROUTE(app, "/bus/system/<str>/") + .methods("GET"_method)([](const crow::Request &req, crow::Response &res, + const std::string &Connection) { + std::shared_ptr<nlohmann::json> transaction; + introspectObjects(res, Connection, "/", transaction); + }); + + BMCWEB_ROUTE(app, "/download/dump/<str>/") + .methods("GET"_method)([](const crow::Request &req, crow::Response &res, + const std::string &dumpId) { + std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); + if (!std::regex_match(dumpId, validFilename)) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + std::experimental::filesystem::path loc( + "/var/lib/phosphor-debug-collector/dumps"); - while (interface != nullptr) { - std::string ifaceName = interface->Attribute("name"); - interfacesArray.push_back({{"name", ifaceName}}); + loc += dumpId; - interface = interface->NextSiblingElement("interface"); - } - res.jsonValue = {{"status", "ok"}, - {"bus_name", processName}, - {"interfaces", interfacesArray}, - {"objectPath", objectPath}}; - } + if (!std::experimental::filesystem::exists(loc) || + !std::experimental::filesystem::is_directory(loc)) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + std::experimental::filesystem::directory_iterator files(loc); + for (auto &file : files) + { + std::ifstream readFile(file.path()); + if (readFile.good()) + { + continue; } + res.addHeader("Content-Type", "application/octet-stream"); + res.body() = {std::istreambuf_iterator<char>(readFile), + std::istreambuf_iterator<char>()}; res.end(); - }, - processName, objectPath, "org.freedesktop.DBus.Introspectable", - "Introspect"); - } else { - crow::connections::systemBus->async_method_call( - [ - &, processName, objectPath, - interface_name{std::move(interfaceName)} - ](const boost::system::error_code ec, - const std::string &introspect_xml) { - if (ec) { - BMCWEB_LOG_ERROR - << "Introspect call failed with error: " << ec.message() - << " on process: " << processName - << " path: " << objectPath << "\n"; - - } else { - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " - << processName << " " << objectPath - << "\n"; - res.result( - boost::beast::http::status::internal_server_error); - - } else { - tinyxml2::XMLElement *node = - pRoot->FirstChildElement("node"); - - // if we know we're the only call, build the json directly - nlohmann::json methodsArray = nlohmann::json::array(); - nlohmann::json signalsArray = nlohmann::json::array(); - tinyxml2::XMLElement *interface = - pRoot->FirstChildElement("interface"); - - while (interface != nullptr) { - std::string ifaceName = interface->Attribute("name"); - - if (ifaceName == interfaceName) { - tinyxml2::XMLElement *methods = - interface->FirstChildElement("method"); - while (methods != nullptr) { - nlohmann::json argsArray = nlohmann::json::array(); - tinyxml2::XMLElement *arg = - methods->FirstChildElement("arg"); - while (arg != nullptr) { - argsArray.push_back( - {{"name", arg->Attribute("name")}, - {"type", arg->Attribute("type")}, - {"direction", arg->Attribute("direction")}}); - arg = arg->NextSiblingElement("arg"); - } - methodsArray.push_back( - {{"name", methods->Attribute("name")}, - {"uri", "/bus/system/" + processName + - objectPath + "/" + interfaceName + - "/" + methods->Attribute("name")}, - {"args", argsArray}}); - methods = methods->NextSiblingElement("method"); - } - tinyxml2::XMLElement *signals = - interface->FirstChildElement("signal"); - while (signals != nullptr) { - nlohmann::json argsArray = nlohmann::json::array(); - - tinyxml2::XMLElement *arg = - signals->FirstChildElement("arg"); - while (arg != nullptr) { - std::string name = arg->Attribute("name"); - std::string type = arg->Attribute("type"); - argsArray.push_back({ - {"name", name}, - {"type", type}, - }); - arg = arg->NextSiblingElement("arg"); - } - signalsArray.push_back( - {{"name", signals->Attribute("name")}, - {"args", argsArray}}); - signals = signals->NextSiblingElement("signal"); - } - - res.jsonValue = { - {"status", "ok"}, - {"bus_name", processName}, - {"interface", interfaceName}, - {"methods", methodsArray}, - {"objectPath", objectPath}, - {"properties", nlohmann::json::object()}, - {"signals", signalsArray}}; - - break; - } - - interface = interface->NextSiblingElement("interface"); - } - if (interface == nullptr) { - // if we got to the end of the list and never found a - // match, throw 404 - res.result(boost::beast::http::status::not_found); - } - } + } + res.result(boost::beast::http::status::not_found); + res.end(); + return; + }); + + BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") + .methods("GET"_method)([](const crow::Request &req, crow::Response &res, + const std::string &processName, + const std::string &requestedPath) { + std::vector<std::string> strs; + boost::split(strs, requestedPath, boost::is_any_of("/")); + std::string objectPath; + std::string interfaceName; + std::string methodName; + auto it = strs.begin(); + if (it == strs.end()) + { + objectPath = "/"; + } + while (it != strs.end()) + { + // Check if segment contains ".". If it does, it must be an + // interface + if (it->find(".") != std::string::npos) + { + break; + // THis check is neccesary as the trailing slash gets parsed + // as part of our <path> specifier above, which causes the + // normal trailing backslash redirector to fail. + } + else if (!it->empty()) + { + objectPath += "/" + *it; } + it++; + } + if (it != strs.end()) + { + interfaceName = *it; + it++; + + // after interface, we might have a method name + if (it != strs.end()) + { + methodName = *it; + it++; + } + } + if (it != strs.end()) + { + // if there is more levels past the method name, something went + // wrong, return not found + res.result(boost::beast::http::status::not_found); res.end(); - }, - processName, objectPath, "org.freedesktop.DBus.Introspectable", - "Introspect"); - } - }); + return; + } + if (interfaceName.empty()) + { + crow::connections::systemBus->async_method_call( + [&, processName, + objectPath](const boost::system::error_code ec, + const std::string &introspect_xml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " + << ec.message() + << " on process: " << processName + << " path: " << objectPath << "\n"; + } + else + { + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = + doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR + << "XML document failed to parse " + << processName << " " << objectPath << "\n"; + res.jsonValue = {{"status", "XML parse error"}}; + res.result(boost::beast::http::status:: + internal_server_error); + } + else + { + nlohmann::json interfacesArray = + nlohmann::json::array(); + tinyxml2::XMLElement *interface = + pRoot->FirstChildElement("interface"); + + while (interface != nullptr) + { + std::string ifaceName = + interface->Attribute("name"); + interfacesArray.push_back( + {{"name", ifaceName}}); + + interface = interface->NextSiblingElement( + "interface"); + } + res.jsonValue = { + {"status", "ok"}, + {"bus_name", processName}, + {"interfaces", interfacesArray}, + {"objectPath", objectPath}}; + } + } + res.end(); + }, + processName, objectPath, + "org.freedesktop.DBus.Introspectable", "Introspect"); + } + else + { + crow::connections::systemBus->async_method_call( + [&, processName, objectPath, + interface_name{std::move(interfaceName)}]( + const boost::system::error_code ec, + const std::string &introspect_xml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " + << ec.message() + << " on process: " << processName + << " path: " << objectPath << "\n"; + } + else + { + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = + doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR + << "XML document failed to parse " + << processName << " " << objectPath << "\n"; + res.result(boost::beast::http::status:: + internal_server_error); + } + else + { + tinyxml2::XMLElement *node = + pRoot->FirstChildElement("node"); + + // if we know we're the only call, build the + // json directly + nlohmann::json methodsArray = + nlohmann::json::array(); + nlohmann::json signalsArray = + nlohmann::json::array(); + tinyxml2::XMLElement *interface = + pRoot->FirstChildElement("interface"); + + while (interface != nullptr) + { + std::string ifaceName = + interface->Attribute("name"); + + if (ifaceName == interfaceName) + { + tinyxml2::XMLElement *methods = + interface->FirstChildElement( + "method"); + while (methods != nullptr) + { + nlohmann::json argsArray = + nlohmann::json::array(); + tinyxml2::XMLElement *arg = + methods->FirstChildElement( + "arg"); + while (arg != nullptr) + { + argsArray.push_back( + {{"name", + arg->Attribute("name")}, + {"type", + arg->Attribute("type")}, + {"direction", + arg->Attribute( + "direction")}}); + arg = arg->NextSiblingElement( + "arg"); + } + methodsArray.push_back( + {{"name", + methods->Attribute("name")}, + {"uri", + "/bus/system/" + processName + + objectPath + "/" + + interfaceName + "/" + + methods->Attribute( + "name")}, + {"args", argsArray}}); + methods = + methods->NextSiblingElement( + "method"); + } + tinyxml2::XMLElement *signals = + interface->FirstChildElement( + "signal"); + while (signals != nullptr) + { + nlohmann::json argsArray = + nlohmann::json::array(); + + tinyxml2::XMLElement *arg = + signals->FirstChildElement( + "arg"); + while (arg != nullptr) + { + std::string name = + arg->Attribute("name"); + std::string type = + arg->Attribute("type"); + argsArray.push_back({ + {"name", name}, + {"type", type}, + }); + arg = arg->NextSiblingElement( + "arg"); + } + signalsArray.push_back( + {{"name", + signals->Attribute("name")}, + {"args", argsArray}}); + signals = + signals->NextSiblingElement( + "signal"); + } + + res.jsonValue = { + {"status", "ok"}, + {"bus_name", processName}, + {"interface", interfaceName}, + {"methods", methodsArray}, + {"objectPath", objectPath}, + {"properties", + nlohmann::json::object()}, + {"signals", signalsArray}}; + + break; + } + + interface = interface->NextSiblingElement( + "interface"); + } + if (interface == nullptr) + { + // if we got to the end of the list and + // never found a match, throw 404 + res.result( + boost::beast::http::status::not_found); + } + } + } + res.end(); + }, + processName, objectPath, + "org.freedesktop.DBus.Introspectable", "Introspect"); + } + }); } -} // namespace openbmc_mapper -} // namespace crow +} // namespace openbmc_mapper +} // namespace crow diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp index 65e47402f7..f51f9aaddb 100644 --- a/include/pam_authenticate.hpp +++ b/include/pam_authenticate.hpp @@ -1,72 +1,84 @@ #pragma once #include <security/pam_appl.h> + +#include <boost/utility/string_view.hpp> #include <cstring> #include <memory> -#include <boost/utility/string_view.hpp> // function used to get user input inline int pamFunctionConversation(int numMsg, const struct pam_message** msg, - struct pam_response** resp, - void* appdataPtr) { - if (appdataPtr == nullptr) { - return PAM_AUTH_ERR; - } - auto* pass = reinterpret_cast<char*>( - malloc(std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1)); - std::strcpy(pass, reinterpret_cast<char*>(appdataPtr)); + struct pam_response** resp, void* appdataPtr) +{ + if (appdataPtr == nullptr) + { + return PAM_AUTH_ERR; + } + auto* pass = reinterpret_cast<char*>( + malloc(std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1)); + std::strcpy(pass, reinterpret_cast<char*>(appdataPtr)); - *resp = reinterpret_cast<pam_response*>( - calloc(numMsg, sizeof(struct pam_response))); + *resp = reinterpret_cast<pam_response*>( + calloc(numMsg, sizeof(struct pam_response))); - for (int i = 0; i < numMsg; ++i) { - /* Ignore all PAM messages except prompting for hidden input */ - if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) { - continue; - } + for (int i = 0; i < numMsg; ++i) + { + /* Ignore all PAM messages except prompting for hidden input */ + if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) + { + continue; + } - /* Assume PAM is only prompting for the password as hidden input */ - resp[i]->resp = pass; - } + /* Assume PAM is only prompting for the password as hidden input */ + resp[i]->resp = pass; + } - return PAM_SUCCESS; + return PAM_SUCCESS; } inline bool pamAuthenticateUser(const boost::string_view username, - const boost::string_view password) { - std::string userStr(username); - std::string passStr(password); - const struct pam_conv localConversation = { - pamFunctionConversation, const_cast<char*>(passStr.c_str())}; - pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start + const boost::string_view password) +{ + std::string userStr(username); + std::string passStr(password); + const struct pam_conv localConversation = { + pamFunctionConversation, const_cast<char*>(passStr.c_str())}; + pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start - if (pam_start("webserver", userStr.c_str(), &localConversation, - &localAuthHandle) != PAM_SUCCESS) { - return false; - } - int retval = - pam_authenticate(localAuthHandle, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); + if (pam_start("webserver", userStr.c_str(), &localConversation, + &localAuthHandle) != PAM_SUCCESS) + { + return false; + } + int retval = pam_authenticate(localAuthHandle, + PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); - if (retval != PAM_SUCCESS) { - if (retval == PAM_AUTH_ERR) { - // printf("Authentication failure.\n"); - } else { - // printf("pam_authenticate returned %d\n", retval); + if (retval != PAM_SUCCESS) + { + if (retval == PAM_AUTH_ERR) + { + // printf("Authentication failure.\n"); + } + else + { + // printf("pam_authenticate returned %d\n", retval); + } + pam_end(localAuthHandle, PAM_SUCCESS); + return false; } - pam_end(localAuthHandle, PAM_SUCCESS); - return false; - } - /* check that the account is healthy */ - if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) != - PAM_SUCCESS) { - pam_end(localAuthHandle, PAM_SUCCESS); - return false; - } + /* check that the account is healthy */ + if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) != + PAM_SUCCESS) + { + pam_end(localAuthHandle, PAM_SUCCESS); + return false; + } - if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS) { - return false; - } + if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS) + { + return false; + } - return true; + return true; } diff --git a/include/persistent_data_middleware.hpp b/include/persistent_data_middleware.hpp index 706f6f423a..b384f02304 100644 --- a/include/persistent_data_middleware.hpp +++ b/include/persistent_data_middleware.hpp @@ -1,121 +1,165 @@ #pragma once -#include <nlohmann/json.hpp> -#include <pam_authenticate.hpp> -#include <sessions.hpp> -#include <webassets.hpp> -#include <random> #include <crow/app.h> #include <crow/http_request.h> #include <crow/http_response.h> + #include <boost/container/flat_map.hpp> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_io.hpp> +#include <nlohmann/json.hpp> +#include <pam_authenticate.hpp> +#include <random> +#include <sessions.hpp> +#include <webassets.hpp> -namespace crow { - -namespace persistent_data { +namespace crow +{ -class Middleware { - // todo(ed) should read this from a fixed location somewhere, not CWD - static constexpr const char* filename = "bmcweb_persistent_data.json"; - int jsonRevision = 1; +namespace persistent_data +{ - public: - struct Context {}; +class Middleware +{ + // todo(ed) should read this from a fixed location somewhere, not CWD + static constexpr const char* filename = "bmcweb_persistent_data.json"; + int jsonRevision = 1; - Middleware() { readData(); } + public: + struct Context + { + }; - ~Middleware() { - if (persistent_data::SessionStore::getInstance().needsWrite()) { - writeData(); + Middleware() + { + readData(); } - } - - void beforeHandle(crow::Request& req, Response& res, Context& ctx) {} - - void afterHandle(Request& req, Response& res, Context& ctx) {} - - // TODO(ed) this should really use protobuf, or some other serialization - // library, but adding another dependency is somewhat outside the scope of - // this application for the moment - void readData() { - std::ifstream persistentFile(filename); - int fileRevision = 0; - if (persistentFile.is_open()) { - // call with exceptions disabled - auto data = nlohmann::json::parse(persistentFile, nullptr, false); - if (data.is_discarded()) { - BMCWEB_LOG_ERROR << "Error parsing persistent data in json file."; - } else { - for (const auto& item : data.items()) { - if (item.key() == "revision") { - fileRevision = 0; - - const uint64_t* uintPtr = item.value().get_ptr<const uint64_t*>(); - if (uintPtr == nullptr) { - BMCWEB_LOG_ERROR << "Failed to read revision flag"; - } else { - fileRevision = *uintPtr; - } - } else if (item.key() == "system_uuid") { - const std::string* jSystemUuid = - item.value().get_ptr<const std::string*>(); - if (jSystemUuid != nullptr) { - systemUuid = *jSystemUuid; - } - } else if (item.key() == "sessions") { - for (const auto& elem : item.value()) { - std::shared_ptr<UserSession> newSession = - UserSession::fromJson(elem); - if (newSession == nullptr) { - BMCWEB_LOG_ERROR - << "Problem reading session from persistent store"; - continue; - } - - BMCWEB_LOG_DEBUG << "Restored session: " << newSession->csrfToken - << " " << newSession->uniqueId << " " - << newSession->sessionToken; - SessionStore::getInstance().authTokens.emplace( - newSession->sessionToken, newSession); - } - } else { - // Do nothing in the case of extra fields. We may have cases where - // fields are added in the future, and we want to at least attempt - // to gracefully support downgrades in that case, even if we don't - // officially support it - } + ~Middleware() + { + if (persistent_data::SessionStore::getInstance().needsWrite()) + { + writeData(); } - } } - bool needWrite = false; - if (systemUuid.empty()) { - systemUuid = boost::uuids::to_string(boost::uuids::random_generator()()); - needWrite = true; + void beforeHandle(crow::Request& req, Response& res, Context& ctx) + { } - if (fileRevision < jsonRevision) { - needWrite = true; + + void afterHandle(Request& req, Response& res, Context& ctx) + { } - // write revision changes or system uuid changes immediately - if (needWrite) { - writeData(); + + // TODO(ed) this should really use protobuf, or some other serialization + // library, but adding another dependency is somewhat outside the scope of + // this application for the moment + void readData() + { + std::ifstream persistentFile(filename); + int fileRevision = 0; + if (persistentFile.is_open()) + { + // call with exceptions disabled + auto data = nlohmann::json::parse(persistentFile, nullptr, false); + if (data.is_discarded()) + { + BMCWEB_LOG_ERROR + << "Error parsing persistent data in json file."; + } + else + { + for (const auto& item : data.items()) + { + if (item.key() == "revision") + { + fileRevision = 0; + + const uint64_t* uintPtr = + item.value().get_ptr<const uint64_t*>(); + if (uintPtr == nullptr) + { + BMCWEB_LOG_ERROR << "Failed to read revision flag"; + } + else + { + fileRevision = *uintPtr; + } + } + else if (item.key() == "system_uuid") + { + const std::string* jSystemUuid = + item.value().get_ptr<const std::string*>(); + if (jSystemUuid != nullptr) + { + systemUuid = *jSystemUuid; + } + } + else if (item.key() == "sessions") + { + for (const auto& elem : item.value()) + { + std::shared_ptr<UserSession> newSession = + UserSession::fromJson(elem); + + if (newSession == nullptr) + { + BMCWEB_LOG_ERROR << "Problem reading session " + "from persistent store"; + continue; + } + + BMCWEB_LOG_DEBUG + << "Restored session: " << newSession->csrfToken + << " " << newSession->uniqueId << " " + << newSession->sessionToken; + SessionStore::getInstance().authTokens.emplace( + newSession->sessionToken, newSession); + } + } + else + { + // Do nothing in the case of extra fields. We may have + // cases where fields are added in the future, and we + // want to at least attempt to gracefully support + // downgrades in that case, even if we don't officially + // support it + } + } + } + } + bool needWrite = false; + + if (systemUuid.empty()) + { + systemUuid = + boost::uuids::to_string(boost::uuids::random_generator()()); + needWrite = true; + } + if (fileRevision < jsonRevision) + { + needWrite = true; + } + // write revision changes or system uuid changes immediately + if (needWrite) + { + writeData(); + } } - } - void writeData() { - std::ofstream persistentFile(filename); - nlohmann::json data{{"sessions", SessionStore::getInstance().authTokens}, - {"system_uuid", systemUuid}, - {"revision", jsonRevision}}; - persistentFile << data; - } + void writeData() + { + std::ofstream persistentFile(filename); + nlohmann::json data{ + {"sessions", SessionStore::getInstance().authTokens}, + {"system_uuid", systemUuid}, + {"revision", jsonRevision}}; + persistentFile << data; + } - std::string systemUuid{""}; + std::string systemUuid{""}; }; -} // namespace persistent_data -} // namespace crow +} // namespace persistent_data +} // namespace crow diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp index b81aa5474a..13e18380ad 100644 --- a/include/redfish_v1.hpp +++ b/include/redfish_v1.hpp @@ -1,15 +1,18 @@ #pragma once +#include <crow/app.h> + +#include <boost/algorithm/string.hpp> #include <dbus_singleton.hpp> -#include <persistent_data_middleware.hpp> -#include <token_authorization_middleware.hpp> #include <fstream> +#include <persistent_data_middleware.hpp> #include <streambuf> #include <string> -#include <crow/app.h> -#include <boost/algorithm/string.hpp> -namespace crow { -namespace redfish { +#include <token_authorization_middleware.hpp> +namespace crow +{ +namespace redfish +{ using ManagedObjectType = std::vector<std::pair< sdbusplus::message::object_path, @@ -17,108 +20,130 @@ using ManagedObjectType = std::vector<std::pair< std::string, boost::container::flat_map< std::string, sdbusplus::message::variant<bool>>>>>; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/redfish/") - .methods("GET"_method)([](const crow::Request& req, crow::Response& res) { - res.jsonValue = {{"v1", "/redfish/v1/"}}; - res.end(); - }); - - BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/") - .methods( - "GET"_method)([&](const crow::Request& req, crow::Response& res) { - crow::connections::systemBus->async_method_call( - [&](const boost::system::error_code ec, - const ManagedObjectType& users) { - if (ec) { - res.result(boost::beast::http::status::internal_server_error); - } else { - res.jsonValue = { - {"@odata.context", - "/redfish/v1/" - "$metadata#ManagerAccountCollection." - "ManagerAccountCollection"}, - {"@odata.id", "/redfish/v1/AccountService/Accounts"}, - {"@odata.type", - "#ManagerAccountCollection.ManagerAccountCollection"}, - {"Name", "Accounts Collection"}, - {"Description", "BMC User Accounts"}, - {"Members@odata.count", users.size()}}; - nlohmann::json memberArray = nlohmann::json::array(); - int userIndex = 0; - for (auto& user : users) { - const std::string& path = - static_cast<const std::string&>(user.first); - std::size_t lastIndex = path.rfind("/"); - if (lastIndex == std::string::npos) { - lastIndex = 0; - } else { - lastIndex += 1; - } - memberArray.push_back( - {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + - path.substr(lastIndex)}}); - } - res.jsonValue["Members"] = memberArray; - } - res.end(); - }, - "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/redfish/") + .methods("GET"_method)( + [](const crow::Request& req, crow::Response& res) { + res.jsonValue = {{"v1", "/redfish/v1/"}}; + res.end(); + }); - BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/") - .methods("GET"_method)([](const crow::Request& req, crow::Response& res, - const std::string& account_name) { + BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/") + .methods( + "GET"_method)([&](const crow::Request& req, crow::Response& res) { + crow::connections::systemBus->async_method_call( + [&](const boost::system::error_code ec, + const ManagedObjectType& users) { + if (ec) + { + res.result( + boost::beast::http::status::internal_server_error); + } + else + { + res.jsonValue = { + {"@odata.context", + "/redfish/v1/" + "$metadata#ManagerAccountCollection." + "ManagerAccountCollection"}, + {"@odata.id", + "/redfish/v1/AccountService/Accounts"}, + {"@odata.type", "#ManagerAccountCollection." + "ManagerAccountCollection"}, + {"Name", "Accounts Collection"}, + {"Description", "BMC User Accounts"}, + {"Members@odata.count", users.size()}}; + nlohmann::json memberArray = nlohmann::json::array(); + int userIndex = 0; + for (auto& user : users) + { + const std::string& path = + static_cast<const std::string&>(user.first); + std::size_t lastIndex = path.rfind("/"); + if (lastIndex == std::string::npos) + { + lastIndex = 0; + } + else + { + lastIndex += 1; + } + memberArray.push_back( + {{"@odata.id", + "/redfish/v1/AccountService/Accounts/" + + path.substr(lastIndex)}}); + } + res.jsonValue["Members"] = memberArray; + } + res.end(); + }, + "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }); - crow::connections::systemBus->async_method_call( - [&, accountName{std::move(account_name)} ]( - const boost::system::error_code ec, - const ManagedObjectType& users) { - if (ec) { - res.result(boost::beast::http::status::internal_server_error); - } else { - for (auto& user : users) { - const std::string& path = - static_cast<const std::string&>(user.first); - std::size_t lastIndex = path.rfind("/"); - if (lastIndex == std::string::npos) { - lastIndex = 0; - } else { - lastIndex += 1; - } - if (path.substr(lastIndex) == accountName) { - res.jsonValue = { - {"@odata.context", - "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, - {"@odata.id", "/redfish/v1/AccountService/Accounts/1"}, - {"@odata.type", - "#ManagerAccount.v1_0_3.ManagerAccount"}, - {"Id", "1"}, - {"Name", "User Account"}, - {"Description", "User Account"}, - {"Enabled", false}, - {"Password", nullptr}, - {"UserName", accountName}, - {"RoleId", "Administrator"}, - {"Links", - {{"Role", - {{"@odata.id", - "/redfish/v1/AccountService/Roles/" - "Administrator"}}}}}}; - break; - } - } - if (res.jsonValue.is_null()) { - res.result(boost::beast::http::status::not_found); - } - } - res.end(); - }, - "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }); + BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/") + .methods("GET"_method)([](const crow::Request& req, crow::Response& res, + const std::string& account_name) { + crow::connections::systemBus->async_method_call( + [&, accountName{std::move(account_name)}]( + const boost::system::error_code ec, + const ManagedObjectType& users) { + if (ec) + { + res.result( + boost::beast::http::status::internal_server_error); + } + else + { + for (auto& user : users) + { + const std::string& path = + static_cast<const std::string&>(user.first); + std::size_t lastIndex = path.rfind("/"); + if (lastIndex == std::string::npos) + { + lastIndex = 0; + } + else + { + lastIndex += 1; + } + if (path.substr(lastIndex) == accountName) + { + res.jsonValue = { + {"@odata.context", + "/redfish/v1/" + "$metadata#ManagerAccount.ManagerAccount"}, + {"@odata.id", + "/redfish/v1/AccountService/Accounts/1"}, + {"@odata.type", + "#ManagerAccount.v1_0_3.ManagerAccount"}, + {"Id", "1"}, + {"Name", "User Account"}, + {"Description", "User Account"}, + {"Enabled", false}, + {"Password", nullptr}, + {"UserName", accountName}, + {"RoleId", "Administrator"}, + {"Links", + {{"Role", + {{"@odata.id", + "/redfish/v1/AccountService/Roles/" + "Administrator"}}}}}}; + break; + } + } + if (res.jsonValue.is_null()) + { + res.result(boost::beast::http::status::not_found); + } + } + res.end(); + }, + "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }); } -} // namespace redfish -} // namespace crow +} // namespace redfish +} // namespace crow diff --git a/include/security_headers_middleware.hpp b/include/security_headers_middleware.hpp index 750f87b719..561fd81635 100644 --- a/include/security_headers_middleware.hpp +++ b/include/security_headers_middleware.hpp @@ -3,7 +3,8 @@ #include <crow/http_request.h> #include <crow/http_response.h> -namespace crow { +namespace crow +{ static const char* strictTransportSecurityKey = "Strict-Transport-Security"; static const char* strictTransportSecurityValue = "max-age=31536000; includeSubdomains; preload"; @@ -26,40 +27,46 @@ static const char* pragmaValue = "no-cache"; static const char* cacheControlKey = "Cache-Control"; static const char* cacheControlValue = "no-Store,no-Cache"; -struct SecurityHeadersMiddleware { - struct Context {}; +struct SecurityHeadersMiddleware +{ + struct Context + { + }; - void beforeHandle(crow::Request& req, Response& res, Context& ctx) { + void beforeHandle(crow::Request& req, Response& res, Context& ctx) + { #ifdef BMCWEB_INSECURE_DISABLE_XSS_PREVENTION - if ("OPTIONS"_method == req.method()) { - res.end(); - } + if ("OPTIONS"_method == req.method()) + { + res.end(); + } #endif - } + } - void afterHandle(Request& req, Response& res, Context& ctx) { - /* - TODO(ed) these should really check content types. for example, - X-UA-Compatible header doesn't make sense when retrieving a JSON or - javascript file. It doesn't hurt anything, it's just ugly. - */ - res.addHeader(strictTransportSecurityKey, strictTransportSecurityValue); - res.addHeader(uaCompatabilityKey, uaCompatabilityValue); - res.addHeader(xframeKey, xframeValue); - res.addHeader(xssKey, xssValue); - res.addHeader(contentSecurityKey, contentSecurityValue); - res.addHeader(pragmaKey, pragmaValue); - res.addHeader(cacheControlKey, cacheControlValue); + void afterHandle(Request& req, Response& res, Context& ctx) + { + /* + TODO(ed) these should really check content types. for example, + X-UA-Compatible header doesn't make sense when retrieving a JSON or + javascript file. It doesn't hurt anything, it's just ugly. + */ + res.addHeader(strictTransportSecurityKey, strictTransportSecurityValue); + res.addHeader(uaCompatabilityKey, uaCompatabilityValue); + res.addHeader(xframeKey, xframeValue); + res.addHeader(xssKey, xssValue); + res.addHeader(contentSecurityKey, contentSecurityValue); + res.addHeader(pragmaKey, pragmaValue); + res.addHeader(cacheControlKey, cacheControlValue); #ifdef BMCWEB_INSECURE_DISABLE_XSS_PREVENTION - res.addHeader("Access-Control-Allow-Origin", "http://localhost:8080"); - res.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH"); - res.addHeader("Access-Control-Allow-Credentials", "true"); - res.addHeader("Access-Control-Allow-Headers", - "Origin, Content-Type, Accept, Cookie, X-XSRF-TOKEN"); + res.addHeader("Access-Control-Allow-Origin", "http://localhost:8080"); + res.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH"); + res.addHeader("Access-Control-Allow-Credentials", "true"); + res.addHeader("Access-Control-Allow-Headers", + "Origin, Content-Type, Accept, Cookie, X-XSRF-TOKEN"); #endif - } + } }; -} // namespace crow +} // namespace crow diff --git a/include/sessions.hpp b/include/sessions.hpp index f549fde696..510f566476 100644 --- a/include/sessions.hpp +++ b/include/sessions.hpp @@ -1,232 +1,284 @@ #pragma once -#include <nlohmann/json.hpp> -#include <pam_authenticate.hpp> -#include <webassets.hpp> -#include <random> #include <crow/app.h> #include <crow/http_request.h> #include <crow/http_response.h> + #include <boost/container/flat_map.hpp> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_io.hpp> +#include <nlohmann/json.hpp> +#include <pam_authenticate.hpp> +#include <random> +#include <webassets.hpp> -namespace crow { +namespace crow +{ -namespace persistent_data { +namespace persistent_data +{ -enum class PersistenceType { - TIMEOUT, // User session times out after a predetermined amount of time - SINGLE_REQUEST // User times out once this request is completed. +enum class PersistenceType +{ + TIMEOUT, // User session times out after a predetermined amount of time + SINGLE_REQUEST // User times out once this request is completed. }; -struct UserSession { - std::string uniqueId; - std::string sessionToken; - std::string username; - std::string csrfToken; - std::chrono::time_point<std::chrono::steady_clock> lastUpdated; - PersistenceType persistence; - - /** - * @brief Fills object with data from UserSession's JSON representation - * - * This replaces nlohmann's from_json to ensure no-throw approach - * - * @param[in] j JSON object from which data should be loaded - * - * @return a shared pointer if data has been loaded properly, nullptr - * otherwise - */ - static std::shared_ptr<UserSession> fromJson(const nlohmann::json& j) { - std::shared_ptr<UserSession> userSession = std::make_shared<UserSession>(); - for (const auto& element : j.items()) { - const std::string* thisValue = - element.value().get_ptr<const std::string*>(); - if (thisValue == nullptr) { - BMCWEB_LOG_ERROR << "Error reading persistent store. Property " - << element.key() << " was not of type string"; - return nullptr; - } - if (element.key() == "unique_id") { - userSession->uniqueId = *thisValue; - } else if (element.key() == "session_token") { - userSession->sessionToken = *thisValue; - } else if (element.key() == "csrf_token") { - userSession->csrfToken = *thisValue; - } else if (element.key() == "username") { - userSession->username = *thisValue; - } else { - BMCWEB_LOG_ERROR << "Got unexpected property reading persistent file: " - << element.key(); - return nullptr; - } - } +struct UserSession +{ + std::string uniqueId; + std::string sessionToken; + std::string username; + std::string csrfToken; + std::chrono::time_point<std::chrono::steady_clock> lastUpdated; + PersistenceType persistence; + + /** + * @brief Fills object with data from UserSession's JSON representation + * + * This replaces nlohmann's from_json to ensure no-throw approach + * + * @param[in] j JSON object from which data should be loaded + * + * @return a shared pointer if data has been loaded properly, nullptr + * otherwise + */ + static std::shared_ptr<UserSession> fromJson(const nlohmann::json& j) + { + std::shared_ptr<UserSession> userSession = + std::make_shared<UserSession>(); + for (const auto& element : j.items()) + { + const std::string* thisValue = + element.value().get_ptr<const std::string*>(); + if (thisValue == nullptr) + { + BMCWEB_LOG_ERROR << "Error reading persistent store. Property " + << element.key() << " was not of type string"; + return nullptr; + } + if (element.key() == "unique_id") + { + userSession->uniqueId = *thisValue; + } + else if (element.key() == "session_token") + { + userSession->sessionToken = *thisValue; + } + else if (element.key() == "csrf_token") + { + userSession->csrfToken = *thisValue; + } + else if (element.key() == "username") + { + userSession->username = *thisValue; + } + else + { + BMCWEB_LOG_ERROR + << "Got unexpected property reading persistent file: " + << element.key(); + return nullptr; + } + } - // For now, sessions that were persisted through a reboot get their idle - // timer reset. This could probably be overcome with a better understanding - // of wall clock time and steady timer time, possibly persisting values with - // wall clock time instead of steady timer, but the tradeoffs of all the - // corner cases involved are non-trivial, so this is done temporarily - userSession->lastUpdated = std::chrono::steady_clock::now(); - userSession->persistence = PersistenceType::TIMEOUT; + // For now, sessions that were persisted through a reboot get their idle + // timer reset. This could probably be overcome with a better + // understanding of wall clock time and steady timer time, possibly + // persisting values with wall clock time instead of steady timer, but + // the tradeoffs of all the corner cases involved are non-trivial, so + // this is done temporarily + userSession->lastUpdated = std::chrono::steady_clock::now(); + userSession->persistence = PersistenceType::TIMEOUT; - return userSession; - } + return userSession; + } }; class Middleware; -class SessionStore { - public: - std::shared_ptr<UserSession> generateUserSession( - const boost::string_view username, - PersistenceType persistence = PersistenceType::TIMEOUT) { - // TODO(ed) find a secure way to not generate session identifiers if - // persistence is set to SINGLE_REQUEST - static constexpr std::array<char, 62> alphanum = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'b', 'C', - 'D', 'E', 'F', 'g', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'r', 'S', 'T', 'U', 'v', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', - 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - - // entropy: 30 characters, 62 possibilities. log2(62^30) = 178 bits of - // entropy. OWASP recommends at least 60 - // https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Entropy - std::string sessionToken; - sessionToken.resize(20, '0'); - std::uniform_int_distribution<int> dist(0, alphanum.size() - 1); - for (int i = 0; i < sessionToken.size(); ++i) { - sessionToken[i] = alphanum[dist(rd)]; +class SessionStore +{ + public: + std::shared_ptr<UserSession> generateUserSession( + const boost::string_view username, + PersistenceType persistence = PersistenceType::TIMEOUT) + { + // TODO(ed) find a secure way to not generate session identifiers if + // persistence is set to SINGLE_REQUEST + static constexpr std::array<char, 62> alphanum = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'b', 'C', + 'D', 'E', 'F', 'g', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'r', 'S', 'T', 'U', 'v', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', + 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + // entropy: 30 characters, 62 possibilities. log2(62^30) = 178 bits of + // entropy. OWASP recommends at least 60 + // https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Entropy + std::string sessionToken; + sessionToken.resize(20, '0'); + std::uniform_int_distribution<int> dist(0, alphanum.size() - 1); + for (int i = 0; i < sessionToken.size(); ++i) + { + sessionToken[i] = alphanum[dist(rd)]; + } + // Only need csrf tokens for cookie based auth, token doesn't matter + std::string csrfToken; + csrfToken.resize(20, '0'); + for (int i = 0; i < csrfToken.size(); ++i) + { + csrfToken[i] = alphanum[dist(rd)]; + } + + std::string uniqueId; + uniqueId.resize(10, '0'); + for (int i = 0; i < uniqueId.size(); ++i) + { + uniqueId[i] = alphanum[dist(rd)]; + } + auto session = std::make_shared<UserSession>(UserSession{ + uniqueId, sessionToken, std::string(username), csrfToken, + std::chrono::steady_clock::now(), persistence}); + auto it = authTokens.emplace(std::make_pair(sessionToken, session)); + // Only need to write to disk if session isn't about to be destroyed. + needWrite = persistence == PersistenceType::TIMEOUT; + return it.first->second; } - // Only need csrf tokens for cookie based auth, token doesn't matter - std::string csrfToken; - csrfToken.resize(20, '0'); - for (int i = 0; i < csrfToken.size(); ++i) { - csrfToken[i] = alphanum[dist(rd)]; + + std::shared_ptr<UserSession> + loginSessionByToken(const boost::string_view token) + { + applySessionTimeouts(); + auto sessionIt = authTokens.find(std::string(token)); + if (sessionIt == authTokens.end()) + { + return nullptr; + } + std::shared_ptr<UserSession> userSession = sessionIt->second; + userSession->lastUpdated = std::chrono::steady_clock::now(); + return userSession; } - std::string uniqueId; - uniqueId.resize(10, '0'); - for (int i = 0; i < uniqueId.size(); ++i) { - uniqueId[i] = alphanum[dist(rd)]; + std::shared_ptr<UserSession> getSessionByUid(const boost::string_view uid) + { + applySessionTimeouts(); + // TODO(Ed) this is inefficient + auto sessionIt = authTokens.begin(); + while (sessionIt != authTokens.end()) + { + if (sessionIt->second->uniqueId == uid) + { + return sessionIt->second; + } + sessionIt++; + } + return nullptr; } - auto session = std::make_shared<UserSession>( - UserSession{uniqueId, sessionToken, std::string(username), csrfToken, - std::chrono::steady_clock::now(), persistence}); - auto it = authTokens.emplace(std::make_pair(sessionToken, session)); - // Only need to write to disk if session isn't about to be destroyed. - needWrite = persistence == PersistenceType::TIMEOUT; - return it.first->second; - } - - std::shared_ptr<UserSession> loginSessionByToken( - const boost::string_view token) { - applySessionTimeouts(); - auto sessionIt = authTokens.find(std::string(token)); - if (sessionIt == authTokens.end()) { - return nullptr; + + void removeSession(std::shared_ptr<UserSession> session) + { + authTokens.erase(session->sessionToken); + needWrite = true; } - std::shared_ptr<UserSession> userSession = sessionIt->second; - userSession->lastUpdated = std::chrono::steady_clock::now(); - return userSession; - } - - std::shared_ptr<UserSession> getSessionByUid(const boost::string_view uid) { - applySessionTimeouts(); - // TODO(Ed) this is inefficient - auto sessionIt = authTokens.begin(); - while (sessionIt != authTokens.end()) { - if (sessionIt->second->uniqueId == uid) { - return sessionIt->second; - } - sessionIt++; + + std::vector<const std::string*> getUniqueIds( + bool getAll = true, + const PersistenceType& type = PersistenceType::SINGLE_REQUEST) + { + applySessionTimeouts(); + + std::vector<const std::string*> ret; + ret.reserve(authTokens.size()); + for (auto& session : authTokens) + { + if (getAll || type == session.second->persistence) + { + ret.push_back(&session.second->uniqueId); + } + } + return ret; + } + + bool needsWrite() + { + return needWrite; + } + int getTimeoutInSeconds() const + { + return std::chrono::seconds(timeoutInMinutes).count(); + }; + + // Persistent data middleware needs to be able to serialize our authTokens + // structure, which is private + friend Middleware; + + static SessionStore& getInstance() + { + static SessionStore sessionStore; + return sessionStore; } - return nullptr; - } - - void removeSession(std::shared_ptr<UserSession> session) { - authTokens.erase(session->sessionToken); - needWrite = true; - } - - std::vector<const std::string*> getUniqueIds( - bool getAll = true, - const PersistenceType& type = PersistenceType::SINGLE_REQUEST) { - applySessionTimeouts(); - - std::vector<const std::string*> ret; - ret.reserve(authTokens.size()); - for (auto& session : authTokens) { - if (getAll || type == session.second->persistence) { - ret.push_back(&session.second->uniqueId); - } + + SessionStore(const SessionStore&) = delete; + SessionStore& operator=(const SessionStore&) = delete; + + private: + SessionStore() : timeoutInMinutes(60) + { } - return ret; - } - - bool needsWrite() { return needWrite; } - int getTimeoutInSeconds() const { - return std::chrono::seconds(timeoutInMinutes).count(); - }; - - // Persistent data middleware needs to be able to serialize our authTokens - // structure, which is private - friend Middleware; - - static SessionStore& getInstance() { - static SessionStore sessionStore; - return sessionStore; - } - - SessionStore(const SessionStore&) = delete; - SessionStore& operator=(const SessionStore&) = delete; - - private: - SessionStore() : timeoutInMinutes(60) {} - - void applySessionTimeouts() { - auto timeNow = std::chrono::steady_clock::now(); - if (timeNow - lastTimeoutUpdate > std::chrono::minutes(1)) { - lastTimeoutUpdate = timeNow; - auto authTokensIt = authTokens.begin(); - while (authTokensIt != authTokens.end()) { - if (timeNow - authTokensIt->second->lastUpdated >= timeoutInMinutes) { - authTokensIt = authTokens.erase(authTokensIt); - needWrite = true; - } else { - authTokensIt++; + + void applySessionTimeouts() + { + auto timeNow = std::chrono::steady_clock::now(); + if (timeNow - lastTimeoutUpdate > std::chrono::minutes(1)) + { + lastTimeoutUpdate = timeNow; + auto authTokensIt = authTokens.begin(); + while (authTokensIt != authTokens.end()) + { + if (timeNow - authTokensIt->second->lastUpdated >= + timeoutInMinutes) + { + authTokensIt = authTokens.erase(authTokensIt); + needWrite = true; + } + else + { + authTokensIt++; + } + } } - } } - } - std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate; - boost::container::flat_map<std::string, std::shared_ptr<UserSession>> - authTokens; - std::random_device rd; - bool needWrite{false}; - std::chrono::minutes timeoutInMinutes; + std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate; + boost::container::flat_map<std::string, std::shared_ptr<UserSession>> + authTokens; + std::random_device rd; + bool needWrite{false}; + std::chrono::minutes timeoutInMinutes; }; -} // namespace persistent_data -} // namespace crow +} // namespace persistent_data +} // namespace crow // to_json(...) definition for objects of UserSession type -namespace nlohmann { +namespace nlohmann +{ template <> -struct adl_serializer<std::shared_ptr<crow::persistent_data::UserSession>> { - static void to_json( - nlohmann::json& j, - const std::shared_ptr<crow::persistent_data::UserSession>& p) { - if (p->persistence != - crow::persistent_data::PersistenceType::SINGLE_REQUEST) { - j = nlohmann::json{{"unique_id", p->uniqueId}, - {"session_token", p->sessionToken}, - {"username", p->username}, - {"csrf_token", p->csrfToken}}; +struct adl_serializer<std::shared_ptr<crow::persistent_data::UserSession>> +{ + static void + to_json(nlohmann::json& j, + const std::shared_ptr<crow::persistent_data::UserSession>& p) + { + if (p->persistence != + crow::persistent_data::PersistenceType::SINGLE_REQUEST) + { + j = nlohmann::json{{"unique_id", p->uniqueId}, + {"session_token", p->sessionToken}, + {"username", p->username}, + {"csrf_token", p->csrfToken}}; + } } - } }; -} // namespace nlohmann +} // namespace nlohmann diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp index 4eac8035f5..47893bfb5e 100644 --- a/include/ssl_key_handler.hpp +++ b/include/ssl_key_handler.hpp @@ -10,304 +10,355 @@ #include <openssl/rand.h> #include <openssl/rsa.h> #include <openssl/ssl.h> -#include <random> + #include <boost/asio.hpp> +#include <random> -namespace ensuressl { +namespace ensuressl +{ static void initOpenssl(); static void cleanupOpenssl(); static EVP_PKEY *createRsaKey(); static EVP_PKEY *createEcKey(); static void handleOpensslError(); -inline bool verifyOpensslKeyCert(const std::string &filepath) { - bool privateKeyValid = false; - bool certValid = false; - - std::cout << "Checking certs in file " << filepath << "\n"; - - FILE *file = fopen(filepath.c_str(), "r"); - if (file != NULL) { - EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); - int rc; - if (pkey != nullptr) { - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa != nullptr) { - std::cout << "Found an RSA key\n"; - if (RSA_check_key(rsa) == 1) { - // private_key_valid = true; - } else { - std::cerr << "Key not valid error number " << ERR_get_error() << "\n"; - } - RSA_free(rsa); - } else { - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - if (ec != nullptr) { - std::cout << "Found an EC key\n"; - if (EC_KEY_check_key(ec) == 1) { - privateKeyValid = true; - } else { - std::cerr << "Key not valid error number " << ERR_get_error() - << "\n"; - } - EC_KEY_free(ec); - } - } - - if (privateKeyValid) { - X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL); - if (x509 == nullptr) { - std::cout << "error getting x509 cert " << ERR_get_error() << "\n"; - } else { - rc = X509_verify(x509, pkey); - if (rc == 1) { - certValid = true; - } else { - std::cerr << "Error in verifying private key signature " - << ERR_get_error() << "\n"; - } +inline bool verifyOpensslKeyCert(const std::string &filepath) +{ + bool privateKeyValid = false; + bool certValid = false; + + std::cout << "Checking certs in file " << filepath << "\n"; + + FILE *file = fopen(filepath.c_str(), "r"); + if (file != NULL) + { + EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); + int rc; + if (pkey != nullptr) + { + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa != nullptr) + { + std::cout << "Found an RSA key\n"; + if (RSA_check_key(rsa) == 1) + { + // private_key_valid = true; + } + else + { + std::cerr << "Key not valid error number " + << ERR_get_error() << "\n"; + } + RSA_free(rsa); + } + else + { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); + if (ec != nullptr) + { + std::cout << "Found an EC key\n"; + if (EC_KEY_check_key(ec) == 1) + { + privateKeyValid = true; + } + else + { + std::cerr << "Key not valid error number " + << ERR_get_error() << "\n"; + } + EC_KEY_free(ec); + } + } + + if (privateKeyValid) + { + X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL); + if (x509 == nullptr) + { + std::cout << "error getting x509 cert " << ERR_get_error() + << "\n"; + } + else + { + rc = X509_verify(x509, pkey); + if (rc == 1) + { + certValid = true; + } + else + { + std::cerr << "Error in verifying private key signature " + << ERR_get_error() << "\n"; + } + } + } + + EVP_PKEY_free(pkey); } - } - - EVP_PKEY_free(pkey); + fclose(file); } - fclose(file); - } - return certValid; + return certValid; } -inline void generateSslCertificate(const std::string &filepath) { - FILE *pFile = NULL; - std::cout << "Generating new keys\n"; - initOpenssl(); - - // std::cerr << "Generating RSA key"; - // EVP_PKEY *pRsaPrivKey = create_rsa_key(); - - std::cerr << "Generating EC key\n"; - EVP_PKEY *pRsaPrivKey = createEcKey(); - if (pRsaPrivKey != nullptr) { - std::cerr << "Generating x509 Certificate\n"; - // Use this code to directly generate a certificate - X509 *x509; - x509 = X509_new(); - if (x509 != nullptr) { - // get a random number from the RNG for the certificate serial number - // If this is not random, regenerating certs throws broswer errors - std::random_device rd; - int serial = rd(); - - ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); - - // not before this moment - X509_gmtime_adj(X509_get_notBefore(x509), 0); - // Cert is valid for 10 years - X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * 365L * 10L); - - // set the public key to the key we just generated - X509_set_pubkey(x509, pRsaPrivKey); - - // get the subject name - X509_NAME *name; - name = X509_get_subject_name(x509); - - X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, - reinterpret_cast<const unsigned char *>("US"), - -1, -1, 0); - X509_NAME_add_entry_by_txt( - name, "O", MBSTRING_ASC, - reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, 0); - X509_NAME_add_entry_by_txt( - name, "CN", MBSTRING_ASC, - reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0); - // set the CSR options - X509_set_issuer_name(x509, name); - - // Sign the certificate with our private key - X509_sign(x509, pRsaPrivKey, EVP_sha256()); - - pFile = fopen(filepath.c_str(), "wt"); - - if (pFile != nullptr) { - PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL); - - PEM_write_X509(pFile, x509); - fclose(pFile); - pFile = NULL; - } - - X509_free(x509); - } +inline void generateSslCertificate(const std::string &filepath) +{ + FILE *pFile = NULL; + std::cout << "Generating new keys\n"; + initOpenssl(); + + // std::cerr << "Generating RSA key"; + // EVP_PKEY *pRsaPrivKey = create_rsa_key(); + + std::cerr << "Generating EC key\n"; + EVP_PKEY *pRsaPrivKey = createEcKey(); + if (pRsaPrivKey != nullptr) + { + std::cerr << "Generating x509 Certificate\n"; + // Use this code to directly generate a certificate + X509 *x509; + x509 = X509_new(); + if (x509 != nullptr) + { + // get a random number from the RNG for the certificate serial + // number If this is not random, regenerating certs throws broswer + // errors + std::random_device rd; + int serial = rd(); + + ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); + + // not before this moment + X509_gmtime_adj(X509_get_notBefore(x509), 0); + // Cert is valid for 10 years + X509_gmtime_adj(X509_get_notAfter(x509), + 60L * 60L * 24L * 365L * 10L); + + // set the public key to the key we just generated + X509_set_pubkey(x509, pRsaPrivKey); + + // get the subject name + X509_NAME *name; + name = X509_get_subject_name(x509); + + X509_NAME_add_entry_by_txt( + name, "C", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("US"), -1, -1, 0); + X509_NAME_add_entry_by_txt( + name, "O", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, + 0); + X509_NAME_add_entry_by_txt( + name, "CN", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0); + // set the CSR options + X509_set_issuer_name(x509, name); + + // Sign the certificate with our private key + X509_sign(x509, pRsaPrivKey, EVP_sha256()); + + pFile = fopen(filepath.c_str(), "wt"); + + if (pFile != nullptr) + { + PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, + NULL); + + PEM_write_X509(pFile, x509); + fclose(pFile); + pFile = NULL; + } + + X509_free(x509); + } - EVP_PKEY_free(pRsaPrivKey); - pRsaPrivKey = NULL; - } + EVP_PKEY_free(pRsaPrivKey); + pRsaPrivKey = NULL; + } - // cleanup_openssl(); + // cleanup_openssl(); } -EVP_PKEY *createRsaKey() { - RSA *pRSA = NULL; +EVP_PKEY *createRsaKey() +{ + RSA *pRSA = NULL; #if OPENSSL_VERSION_NUMBER < 0x00908000L - pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL); + pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL); #else - RSA_generate_key_ex(pRSA, 2048, NULL, NULL); + RSA_generate_key_ex(pRSA, 2048, NULL, NULL); #endif - EVP_PKEY *pKey = EVP_PKEY_new(); - if ((pRSA != nullptr) && (pKey != nullptr) && - EVP_PKEY_assign_RSA(pKey, pRSA)) { - /* pKey owns pRSA from now */ - if (RSA_check_key(pRSA) <= 0) { - fprintf(stderr, "RSA_check_key failed.\n"); - handleOpensslError(); - EVP_PKEY_free(pKey); - pKey = NULL; - } - } else { - handleOpensslError(); - if (pRSA != nullptr) { - RSA_free(pRSA); - pRSA = NULL; + EVP_PKEY *pKey = EVP_PKEY_new(); + if ((pRSA != nullptr) && (pKey != nullptr) && + EVP_PKEY_assign_RSA(pKey, pRSA)) + { + /* pKey owns pRSA from now */ + if (RSA_check_key(pRSA) <= 0) + { + fprintf(stderr, "RSA_check_key failed.\n"); + handleOpensslError(); + EVP_PKEY_free(pKey); + pKey = NULL; + } } - if (pKey != nullptr) { - EVP_PKEY_free(pKey); - pKey = NULL; + else + { + handleOpensslError(); + if (pRSA != nullptr) + { + RSA_free(pRSA); + pRSA = NULL; + } + if (pKey != nullptr) + { + EVP_PKEY_free(pKey); + pKey = NULL; + } } - } - return pKey; + return pKey; } -EVP_PKEY *createEcKey() { - EVP_PKEY *pKey = NULL; - int eccgrp = 0; - eccgrp = OBJ_txt2nid("prime256v1"); - - EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp); - if (myecc != nullptr) { - EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); - EC_KEY_generate_key(myecc); - pKey = EVP_PKEY_new(); - if (pKey != nullptr) { - if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) { - /* pKey owns pRSA from now */ - if (EC_KEY_check_key(myecc) <= 0) { - fprintf(stderr, "EC_check_key failed.\n"); +EVP_PKEY *createEcKey() +{ + EVP_PKEY *pKey = NULL; + int eccgrp = 0; + eccgrp = OBJ_txt2nid("prime256v1"); + + EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp); + if (myecc != nullptr) + { + EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); + EC_KEY_generate_key(myecc); + pKey = EVP_PKEY_new(); + if (pKey != nullptr) + { + if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) + { + /* pKey owns pRSA from now */ + if (EC_KEY_check_key(myecc) <= 0) + { + fprintf(stderr, "EC_check_key failed.\n"); + } + } } - } } - } - return pKey; + return pKey; } -void initOpenssl() { +void initOpenssl() +{ #if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - RAND_load_file("/dev/urandom", 1024); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + RAND_load_file("/dev/urandom", 1024); #endif } -void cleanupOpenssl() { - CRYPTO_cleanup_all_ex_data(); - ERR_free_strings(); +void cleanupOpenssl() +{ + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); #if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_remove_thread_state(0); + ERR_remove_thread_state(0); #endif - EVP_cleanup(); + EVP_cleanup(); } -void handleOpensslError() { ERR_print_errors_fp(stderr); } -inline void ensureOpensslKeyPresentAndValid(const std::string &filepath) { - bool pemFileValid = false; +void handleOpensslError() +{ + ERR_print_errors_fp(stderr); +} +inline void ensureOpensslKeyPresentAndValid(const std::string &filepath) +{ + bool pemFileValid = false; - pemFileValid = verifyOpensslKeyCert(filepath); + pemFileValid = verifyOpensslKeyCert(filepath); - if (!pemFileValid) { - std::cerr << "Error in verifying signature, regenerating\n"; - generateSslCertificate(filepath); - } + if (!pemFileValid) + { + std::cerr << "Error in verifying signature, regenerating\n"; + generateSslCertificate(filepath); + } } -inline boost::asio::ssl::context getSslContext( - const std::string &ssl_pem_file) { - boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23}; - mSslContext.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3 | - boost::asio::ssl::context::single_dh_use | - boost::asio::ssl::context::no_tlsv1 | - boost::asio::ssl::context::no_tlsv1_1); - - // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); - mSslContext.use_certificate_file(ssl_pem_file, - boost::asio::ssl::context::pem); - mSslContext.use_private_key_file(ssl_pem_file, - boost::asio::ssl::context::pem); - - // Set up EC curves to auto (boost asio doesn't have a method for this) - // There is a pull request to add this. Once this is included in an asio - // drop, use the right way - // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue - if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1) { - BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n"; - } - - // From mozilla "compatibility" - std::string mozillaCompatibilityCiphers = - "ECDHE-ECDSA-CHACHA20-POLY1305:" - "ECDHE-RSA-CHACHA20-POLY1305:" - "ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES128-GCM-SHA256:" - "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "DHE-RSA-AES128-GCM-SHA256:" - "DHE-RSA-AES256-GCM-SHA384:" - "ECDHE-ECDSA-AES128-SHA256:" - "ECDHE-RSA-AES128-SHA256:" - "ECDHE-ECDSA-AES128-SHA:" - "ECDHE-RSA-AES256-SHA384:" - "ECDHE-RSA-AES128-SHA:" - "ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-ECDSA-AES256-SHA:" - "ECDHE-RSA-AES256-SHA:" - "DHE-RSA-AES128-SHA256:" - "DHE-RSA-AES128-SHA:" - "DHE-RSA-AES256-SHA256:" - "DHE-RSA-AES256-SHA:" - "ECDHE-ECDSA-DES-CBC3-SHA:" - "ECDHE-RSA-DES-CBC3-SHA:" - "EDH-RSA-DES-CBC3-SHA:" - "AES128-GCM-SHA256:" - "AES256-GCM-SHA384:" - "AES128-SHA256:" - "AES256-SHA256:" - "AES128-SHA:" - "AES256-SHA:" - "DES-CBC3-SHA:" - "!DSS"; - - // From mozilla "modern" - std::string mozillaModernCiphers = - "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "ECDHE-ECDSA-CHACHA20-POLY1305:" - "ECDHE-RSA-CHACHA20-POLY1305:" - "ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES128-GCM-SHA256:" - "ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-RSA-AES256-SHA384:" - "ECDHE-ECDSA-AES128-SHA256:" - "ECDHE-RSA-AES128-SHA256"; - - std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL"; - - if (SSL_CTX_set_cipher_list(mSslContext.native_handle(), - mozillaCompatibilityCiphers.c_str()) != 1) { - BMCWEB_LOG_ERROR << "Error setting cipher list\n"; - } - return mSslContext; +inline boost::asio::ssl::context getSslContext(const std::string &ssl_pem_file) +{ + boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23}; + mSslContext.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::single_dh_use | + boost::asio::ssl::context::no_tlsv1 | + boost::asio::ssl::context::no_tlsv1_1); + + // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); + mSslContext.use_certificate_file(ssl_pem_file, + boost::asio::ssl::context::pem); + mSslContext.use_private_key_file(ssl_pem_file, + boost::asio::ssl::context::pem); + + // Set up EC curves to auto (boost asio doesn't have a method for this) + // There is a pull request to add this. Once this is included in an asio + // drop, use the right way + // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue + if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1) + { + BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n"; + } + + // From mozilla "compatibility" + std::string mozillaCompatibilityCiphers = "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-AES128-SHA256:" + "ECDHE-RSA-AES128-SHA256:" + "ECDHE-ECDSA-AES128-SHA:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-ECDSA-AES256-SHA:" + "ECDHE-RSA-AES256-SHA:" + "DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:" + "DHE-RSA-AES256-SHA256:" + "DHE-RSA-AES256-SHA:" + "ECDHE-ECDSA-DES-CBC3-SHA:" + "ECDHE-RSA-DES-CBC3-SHA:" + "EDH-RSA-DES-CBC3-SHA:" + "AES128-GCM-SHA256:" + "AES256-GCM-SHA384:" + "AES128-SHA256:" + "AES256-SHA256:" + "AES128-SHA:" + "AES256-SHA:" + "DES-CBC3-SHA:" + "!DSS"; + + // From mozilla "modern" + std::string mozillaModernCiphers = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-ECDSA-AES128-SHA256:" + "ECDHE-RSA-AES128-SHA256"; + + std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL"; + + if (SSL_CTX_set_cipher_list(mSslContext.native_handle(), + mozillaCompatibilityCiphers.c_str()) != 1) + { + BMCWEB_LOG_ERROR << "Error setting cipher list\n"; + } + return mSslContext; } -} // namespace ensuressl +} // namespace ensuressl #endif
\ No newline at end of file diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp index 2e286e15ca..c419c97fa7 100644 --- a/include/token_authorization_middleware.hpp +++ b/include/token_authorization_middleware.hpp @@ -1,359 +1,448 @@ #pragma once -#include <pam_authenticate.hpp> -#include <persistent_data_middleware.hpp> -#include <webassets.hpp> -#include <random> #include <crow/app.h> #include <crow/common.h> #include <crow/http_request.h> #include <crow/http_response.h> + #include <boost/container/flat_set.hpp> +#include <pam_authenticate.hpp> +#include <persistent_data_middleware.hpp> +#include <random> +#include <webassets.hpp> -namespace crow { +namespace crow +{ -namespace token_authorization { +namespace token_authorization +{ -class Middleware { - public: - struct Context { - std::shared_ptr<crow::persistent_data::UserSession> session; - }; +class Middleware +{ + public: + struct Context + { + std::shared_ptr<crow::persistent_data::UserSession> session; + }; - void beforeHandle(crow::Request& req, Response& res, Context& ctx) { - if (isOnWhitelist(req)) { - return; - } + void beforeHandle(crow::Request& req, Response& res, Context& ctx) + { + if (isOnWhitelist(req)) + { + return; + } - ctx.session = performXtokenAuth(req); - if (ctx.session == nullptr) { - ctx.session = performCookieAuth(req); - } - if (ctx.session == nullptr) { - boost::string_view authHeader = req.getHeaderValue("Authorization"); - if (!authHeader.empty()) { - // Reject any kind of auth other than basic or token - if (boost::starts_with(authHeader, "Token ")) { - ctx.session = performTokenAuth(authHeader); - } else if (boost::starts_with(authHeader, "Basic ")) { - ctx.session = performBasicAuth(authHeader); + ctx.session = performXtokenAuth(req); + if (ctx.session == nullptr) + { + ctx.session = performCookieAuth(req); } - } - } + if (ctx.session == nullptr) + { + boost::string_view authHeader = req.getHeaderValue("Authorization"); + if (!authHeader.empty()) + { + // Reject any kind of auth other than basic or token + if (boost::starts_with(authHeader, "Token ")) + { + ctx.session = performTokenAuth(authHeader); + } + else if (boost::starts_with(authHeader, "Basic ")) + { + ctx.session = performBasicAuth(authHeader); + } + } + } + + if (ctx.session == nullptr) + { + BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed"; + + // If it's a browser connecting, don't send the HTTP authenticate + // header, to avoid possible CSRF attacks with basic auth + if (http_helpers::requestPrefersHtml(req)) + { + res.result(boost::beast::http::status::temporary_redirect); + res.addHeader("Location", "/#/login"); + } + else + { + res.result(boost::beast::http::status::unauthorized); + // only send the WWW-authenticate header if this isn't a xhr + // from the browser. most scripts, + if (req.getHeaderValue("User-Agent").empty()) + { + res.addHeader("WWW-Authenticate", "Basic"); + } + } - if (ctx.session == nullptr) { - BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed"; - - // If it's a browser connecting, don't send the HTTP authenticate header, - // to avoid possible CSRF attacks with basic auth - if (http_helpers::requestPrefersHtml(req)) { - res.result(boost::beast::http::status::temporary_redirect); - res.addHeader("Location", "/#/login"); - } else { - res.result(boost::beast::http::status::unauthorized); - // only send the WWW-authenticate header if this isn't a xhr from the - // browser. most scripts, - if (req.getHeaderValue("User-Agent").empty()) { - res.addHeader("WWW-Authenticate", "Basic"); + res.end(); + return; } - } - res.end(); - return; + // TODO get user privileges here and propagate it via MW Context + // else let the request continue unharmed } - // TODO get user privileges here and propagate it via MW Context - // else let the request continue unharmed - } - - template <typename AllContext> - void afterHandle(Request& req, Response& res, Context& ctx, - AllContext& allctx) { - // TODO(ed) THis should really be handled by the persistent data - // middleware, but because it is upstream, it doesn't have access to the - // session information. Should the data middleware persist the current - // user session? - if (ctx.session != nullptr && - ctx.session->persistence == - crow::persistent_data::PersistenceType::SINGLE_REQUEST) { - persistent_data::SessionStore::getInstance().removeSession(ctx.session); + template <typename AllContext> + void afterHandle(Request& req, Response& res, Context& ctx, + AllContext& allctx) + { + // TODO(ed) THis should really be handled by the persistent data + // middleware, but because it is upstream, it doesn't have access to the + // session information. Should the data middleware persist the current + // user session? + if (ctx.session != nullptr && + ctx.session->persistence == + crow::persistent_data::PersistenceType::SINGLE_REQUEST) + { + persistent_data::SessionStore::getInstance().removeSession( + ctx.session); + } } - } - private: - const std::shared_ptr<crow::persistent_data::UserSession> performBasicAuth( - boost::string_view auth_header) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication"; + private: + const std::shared_ptr<crow::persistent_data::UserSession> + performBasicAuth(boost::string_view auth_header) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication"; + + std::string authData; + boost::string_view param = auth_header.substr(strlen("Basic ")); + if (!crow::utility::base64Decode(param, authData)) + { + return nullptr; + } + std::size_t separator = authData.find(':'); + if (separator == std::string::npos) + { + return nullptr; + } - std::string authData; - boost::string_view param = auth_header.substr(strlen("Basic ")); - if (!crow::utility::base64Decode(param, authData)) { - return nullptr; - } - std::size_t separator = authData.find(':'); - if (separator == std::string::npos) { - return nullptr; - } + std::string user = authData.substr(0, separator); + separator += 1; + if (separator > authData.size()) + { + return nullptr; + } + std::string pass = authData.substr(separator); - std::string user = authData.substr(0, separator); - separator += 1; - if (separator > authData.size()) { - return nullptr; - } - std::string pass = authData.substr(separator); + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user; - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user; + if (!pamAuthenticateUser(user, pass)) + { + return nullptr; + } - if (!pamAuthenticateUser(user, pass)) { - return nullptr; + // TODO(ed) generateUserSession is a little expensive for basic + // auth, as it generates some random identifiers that will never be + // used. This should have a "fast" path for when user tokens aren't + // needed. + // This whole flow needs to be revisited anyway, as we can't be + // calling directly into pam for every request + return persistent_data::SessionStore::getInstance().generateUserSession( + user, crow::persistent_data::PersistenceType::SINGLE_REQUEST); } - // TODO(ed) generateUserSession is a little expensive for basic - // auth, as it generates some random identifiers that will never be - // used. This should have a "fast" path for when user tokens aren't - // needed. - // This whole flow needs to be revisited anyway, as we can't be - // calling directly into pam for every request - return persistent_data::SessionStore::getInstance().generateUserSession( - user, crow::persistent_data::PersistenceType::SINGLE_REQUEST); - } - - const std::shared_ptr<crow::persistent_data::UserSession> performTokenAuth( - boost::string_view auth_header) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication"; - - boost::string_view token = auth_header.substr(strlen("Token ")); - auto session = - persistent_data::SessionStore::getInstance().loginSessionByToken(token); - return session; - } - - const std::shared_ptr<crow::persistent_data::UserSession> performXtokenAuth( - const crow::Request& req) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication"; - - boost::string_view token = req.getHeaderValue("X-Auth-Token"); - if (token.empty()) { - return nullptr; - } - auto session = - persistent_data::SessionStore::getInstance().loginSessionByToken(token); - return session; - } - - const std::shared_ptr<crow::persistent_data::UserSession> performCookieAuth( - const crow::Request& req) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication"; - - boost::string_view cookieValue = req.getHeaderValue("Cookie"); - if (cookieValue.empty()) { - return nullptr; - } + const std::shared_ptr<crow::persistent_data::UserSession> + performTokenAuth(boost::string_view auth_header) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication"; - auto startIndex = cookieValue.find("SESSION="); - if (startIndex == std::string::npos) { - return nullptr; + boost::string_view token = auth_header.substr(strlen("Token ")); + auto session = + persistent_data::SessionStore::getInstance().loginSessionByToken( + token); + return session; } - startIndex += sizeof("SESSION=") - 1; - auto endIndex = cookieValue.find(";", startIndex); - if (endIndex == std::string::npos) { - endIndex = cookieValue.size(); - } - boost::string_view authKey = - cookieValue.substr(startIndex, endIndex - startIndex); - - const std::shared_ptr<crow::persistent_data::UserSession> session = - persistent_data::SessionStore::getInstance().loginSessionByToken( - authKey); - if (session == nullptr) { - return nullptr; + + const std::shared_ptr<crow::persistent_data::UserSession> + performXtokenAuth(const crow::Request& req) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication"; + + boost::string_view token = req.getHeaderValue("X-Auth-Token"); + if (token.empty()) + { + return nullptr; + } + auto session = + persistent_data::SessionStore::getInstance().loginSessionByToken( + token); + return session; } + + const std::shared_ptr<crow::persistent_data::UserSession> + performCookieAuth(const crow::Request& req) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication"; + + boost::string_view cookieValue = req.getHeaderValue("Cookie"); + if (cookieValue.empty()) + { + return nullptr; + } + + auto startIndex = cookieValue.find("SESSION="); + if (startIndex == std::string::npos) + { + return nullptr; + } + startIndex += sizeof("SESSION=") - 1; + auto endIndex = cookieValue.find(";", startIndex); + if (endIndex == std::string::npos) + { + endIndex = cookieValue.size(); + } + boost::string_view authKey = + cookieValue.substr(startIndex, endIndex - startIndex); + + const std::shared_ptr<crow::persistent_data::UserSession> session = + persistent_data::SessionStore::getInstance().loginSessionByToken( + authKey); + if (session == nullptr) + { + return nullptr; + } #ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION - // RFC7231 defines methods that need csrf protection - if (req.method() != "GET"_method) { - boost::string_view csrf = req.getHeaderValue("X-XSRF-TOKEN"); - // Make sure both tokens are filled - if (csrf.empty() || session->csrfToken.empty()) { - return nullptr; - } - // Reject if csrf token not available - if (csrf != session->csrfToken) { - return nullptr; - } - } + // RFC7231 defines methods that need csrf protection + if (req.method() != "GET"_method) + { + boost::string_view csrf = req.getHeaderValue("X-XSRF-TOKEN"); + // Make sure both tokens are filled + if (csrf.empty() || session->csrfToken.empty()) + { + return nullptr; + } + // Reject if csrf token not available + if (csrf != session->csrfToken) + { + return nullptr; + } + } #endif - return session; - } - - // checks if request can be forwarded without authentication - bool isOnWhitelist(const crow::Request& req) const { - // it's allowed to GET root node without authentica tion - if ("GET"_method == req.method()) { - if (req.url == "/redfish/v1" || req.url == "/redfish/v1/" || - req.url == "/redfish" || req.url == "/redfish/" || - req.url == "/redfish/v1/odata" || req.url == "/redfish/v1/odata/") { - return true; - } else if (crow::webassets::routes.find(std::string(req.url)) != - crow::webassets::routes.end()) { - return true; - } + return session; } - // it's allowed to POST on session collection & login without - // authentication - if ("POST"_method == req.method()) { - if ((req.url == "/redfish/v1/SessionService/Sessions") || - (req.url == "/redfish/v1/SessionService/Sessions/") || - (req.url == "/login")) { - return true; - } - } + // checks if request can be forwarded without authentication + bool isOnWhitelist(const crow::Request& req) const + { + // it's allowed to GET root node without authentica tion + if ("GET"_method == req.method()) + { + if (req.url == "/redfish/v1" || req.url == "/redfish/v1/" || + req.url == "/redfish" || req.url == "/redfish/" || + req.url == "/redfish/v1/odata" || + req.url == "/redfish/v1/odata/") + { + return true; + } + else if (crow::webassets::routes.find(std::string(req.url)) != + crow::webassets::routes.end()) + { + return true; + } + } + + // it's allowed to POST on session collection & login without + // authentication + if ("POST"_method == req.method()) + { + if ((req.url == "/redfish/v1/SessionService/Sessions") || + (req.url == "/redfish/v1/SessionService/Sessions/") || + (req.url == "/login")) + { + return true; + } + } - return false; - } + return false; + } }; // TODO(ed) see if there is a better way to allow middlewares to request // routes. // Possibly an init function on first construction? -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - static_assert( - black_magic::Contains<persistent_data::Middleware, Middlewares...>::value, - "token_authorization middleware must be enabled in app to use " - "auth routes"); - BMCWEB_ROUTE(app, "/login") - .methods( - "POST"_method)([&](const crow::Request& req, crow::Response& res) { - boost::string_view contentType = req.getHeaderValue("content-type"); - boost::string_view username; - boost::string_view password; - - bool looksLikeIbm = false; - - // This object needs to be declared at this scope so the strings - // within it are not destroyed before we can use them - nlohmann::json loginCredentials; - // Check if auth was provided by a payload - if (contentType == "application/json") { - loginCredentials = nlohmann::json::parse(req.body, nullptr, false); - if (loginCredentials.is_discarded()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - - // check for username/password in the root object - // THis method is how intel APIs authenticate - nlohmann::json::iterator userIt = loginCredentials.find("username"); - nlohmann::json::iterator passIt = loginCredentials.find("password"); - if (userIt != loginCredentials.end() && - passIt != loginCredentials.end()) { - const std::string* userStr = userIt->get_ptr<const std::string*>(); - const std::string* passStr = passIt->get_ptr<const std::string*>(); - if (userStr != nullptr && passStr != nullptr) { - username = *userStr; - password = *passStr; - } - } else { - // Openbmc appears to push a data object that contains the same - // keys (username and password), attempt to use that - auto dataIt = loginCredentials.find("data"); - if (dataIt != loginCredentials.end()) { - // Some apis produce an array of value ["username", - // "password"] - if (dataIt->is_array()) { - if (dataIt->size() == 2) { - nlohmann::json::iterator userIt2 = dataIt->begin(); - nlohmann::json::iterator passIt2 = dataIt->begin() + 1; - looksLikeIbm = true; - if (userIt2 != dataIt->end() && passIt2 != dataIt->end()) { +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + static_assert( + black_magic::Contains<persistent_data::Middleware, + Middlewares...>::value, + "token_authorization middleware must be enabled in app to use " + "auth routes"); + BMCWEB_ROUTE(app, "/login") + .methods( + "POST"_method)([&](const crow::Request& req, crow::Response& res) { + boost::string_view contentType = req.getHeaderValue("content-type"); + boost::string_view username; + boost::string_view password; + + bool looksLikeIbm = false; + + // This object needs to be declared at this scope so the strings + // within it are not destroyed before we can use them + nlohmann::json loginCredentials; + // Check if auth was provided by a payload + if (contentType == "application/json") + { + loginCredentials = + nlohmann::json::parse(req.body, nullptr, false); + if (loginCredentials.is_discarded()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + + // check for username/password in the root object + // THis method is how intel APIs authenticate + nlohmann::json::iterator userIt = + loginCredentials.find("username"); + nlohmann::json::iterator passIt = + loginCredentials.find("password"); + if (userIt != loginCredentials.end() && + passIt != loginCredentials.end()) + { const std::string* userStr = - userIt2->get_ptr<const std::string*>(); + userIt->get_ptr<const std::string*>(); const std::string* passStr = - passIt2->get_ptr<const std::string*>(); - if (userStr != nullptr && passStr != nullptr) { - username = *userStr; - password = *passStr; + passIt->get_ptr<const std::string*>(); + if (userStr != nullptr && passStr != nullptr) + { + username = *userStr; + password = *passStr; } - } } - - } else if (dataIt->is_object()) { - nlohmann::json::iterator userIt2 = dataIt->find("username"); - nlohmann::json::iterator passIt2 = dataIt->find("password"); - if (userIt2 != dataIt->end() && passIt2 != dataIt->end()) { - const std::string* userStr = - userIt2->get_ptr<const std::string*>(); - const std::string* passStr = - passIt2->get_ptr<const std::string*>(); - if (userStr != nullptr && passStr != nullptr) { - username = *userStr; - password = *passStr; - } + else + { + // Openbmc appears to push a data object that contains the + // same keys (username and password), attempt to use that + auto dataIt = loginCredentials.find("data"); + if (dataIt != loginCredentials.end()) + { + // Some apis produce an array of value ["username", + // "password"] + if (dataIt->is_array()) + { + if (dataIt->size() == 2) + { + nlohmann::json::iterator userIt2 = + dataIt->begin(); + nlohmann::json::iterator passIt2 = + dataIt->begin() + 1; + looksLikeIbm = true; + if (userIt2 != dataIt->end() && + passIt2 != dataIt->end()) + { + const std::string* userStr = + userIt2->get_ptr<const std::string*>(); + const std::string* passStr = + passIt2->get_ptr<const std::string*>(); + if (userStr != nullptr && + passStr != nullptr) + { + username = *userStr; + password = *passStr; + } + } + } + } + else if (dataIt->is_object()) + { + nlohmann::json::iterator userIt2 = + dataIt->find("username"); + nlohmann::json::iterator passIt2 = + dataIt->find("password"); + if (userIt2 != dataIt->end() && + passIt2 != dataIt->end()) + { + const std::string* userStr = + userIt2->get_ptr<const std::string*>(); + const std::string* passStr = + passIt2->get_ptr<const std::string*>(); + if (userStr != nullptr && passStr != nullptr) + { + username = *userStr; + password = *passStr; + } + } + } + } } - } } - } - } else { - // check if auth was provided as a headers - username = req.getHeaderValue("username"); - password = req.getHeaderValue("password"); - } - - if (!username.empty() && !password.empty()) { - if (!pamAuthenticateUser(username, password)) { - res.result(boost::beast::http::status::unauthorized); - } else { - auto session = persistent_data::SessionStore::getInstance() - .generateUserSession(username); - - if (looksLikeIbm) { - // IBM requires a very specific login structure, and doesn't - // actually look at the status code. TODO(ed).... Fix that - // upstream - res.jsonValue = { - {"data", "User '" + std::string(username) + "' logged in"}, - {"message", "200 OK"}, - {"status", "ok"}}; - - // Hack alert. Boost beast by default doesn't let you declare - // multiple headers of the same name, and in most cases this is - // fine. Unfortunately here we need to set the Session cookie, - // which requires the httpOnly attribute, as well as the XSRF - // cookie, which requires it to not have an httpOnly attribute. - // To get the behavior we want, we simply inject the second - // "set-cookie" string into the value header, and get the result - // we want, even though we are technicaly declaring two headers - // here. - res.addHeader("Set-Cookie", - "XSRF-TOKEN=" + session->csrfToken + - "; Secure\r\nSet-Cookie: SESSION=" + - session->sessionToken + "; Secure; HttpOnly"); - } else { - // if content type is json, assume json token - res.jsonValue = {{"token", session->sessionToken}}; + else + { + // check if auth was provided as a headers + username = req.getHeaderValue("username"); + password = req.getHeaderValue("password"); } - } - } else { - res.result(boost::beast::http::status::bad_request); - } - res.end(); - }); - - BMCWEB_ROUTE(app, "/logout") - .methods( - "POST"_method)([&](const crow::Request& req, crow::Response& res) { - auto& session = - app.template getContext<token_authorization::Middleware>(req) - .session; - if (session != nullptr) { - persistent_data::SessionStore::getInstance().removeSession(session); - } - res.end(); - return; - }); + if (!username.empty() && !password.empty()) + { + if (!pamAuthenticateUser(username, password)) + { + res.result(boost::beast::http::status::unauthorized); + } + else + { + auto session = persistent_data::SessionStore::getInstance() + .generateUserSession(username); + + if (looksLikeIbm) + { + // IBM requires a very specific login structure, and + // doesn't actually look at the status code. + // TODO(ed).... Fix that upstream + res.jsonValue = { + {"data", + "User '" + std::string(username) + "' logged in"}, + {"message", "200 OK"}, + {"status", "ok"}}; + + // Hack alert. Boost beast by default doesn't let you + // declare multiple headers of the same name, and in + // most cases this is fine. Unfortunately here we need + // to set the Session cookie, which requires the + // httpOnly attribute, as well as the XSRF cookie, which + // requires it to not have an httpOnly attribute. To get + // the behavior we want, we simply inject the second + // "set-cookie" string into the value header, and get + // the result we want, even though we are technicaly + // declaring two headers here. + res.addHeader("Set-Cookie", + "XSRF-TOKEN=" + session->csrfToken + + "; Secure\r\nSet-Cookie: SESSION=" + + session->sessionToken + + "; Secure; HttpOnly"); + } + else + { + // if content type is json, assume json token + res.jsonValue = {{"token", session->sessionToken}}; + } + } + } + else + { + res.result(boost::beast::http::status::bad_request); + } + res.end(); + }); + + BMCWEB_ROUTE(app, "/logout") + .methods( + "POST"_method)([&](const crow::Request& req, crow::Response& res) { + auto& session = + app.template getContext<token_authorization::Middleware>(req) + .session; + if (session != nullptr) + { + persistent_data::SessionStore::getInstance().removeSession( + session); + } + res.end(); + return; + }); } -} // namespace token_authorization -} // namespace crow +} // namespace token_authorization +} // namespace crow diff --git a/include/web_kvm.hpp b/include/web_kvm.hpp index ad4b352eb4..747a137b87 100644 --- a/include/web_kvm.hpp +++ b/include/web_kvm.hpp @@ -1,181 +1,202 @@ -#include <string> #include <crow/app.h> -#include <boost/endian/arithmetic.hpp> #include <ast_jpeg_decoder.hpp> #include <ast_video_puller.hpp> +#include <boost/endian/arithmetic.hpp> +#include <string> -namespace crow { -namespace kvm { +namespace crow +{ +namespace kvm +{ static const std::string rfb33VersionString = "RFB 003.003\n"; static const std::string rfb37VersionString = "RFB 003.007\n"; static const std::string rfb38VersionString = "RFB 003.008\n"; -enum class RfbAuthScheme : uint8_t { - connection_failed = 0, - no_authentication = 1, - vnc_authentication = 2 +enum class RfbAuthScheme : uint8_t +{ + connection_failed = 0, + no_authentication = 1, + vnc_authentication = 2 }; -struct PixelFormatStruct { - boost::endian::big_uint8_t bitsPerPixel; - boost::endian::big_uint8_t depth; - boost::endian::big_uint8_t isBigEndian; - boost::endian::big_uint8_t isTrueColor; - boost::endian::big_uint16_t redMax; - boost::endian::big_uint16_t greenMax; - boost::endian::big_uint16_t blueMax; - boost::endian::big_uint8_t redShift; - boost::endian::big_uint8_t greenShift; - boost::endian::big_uint8_t blueShift; - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint8_t pad3; +struct PixelFormatStruct +{ + boost::endian::big_uint8_t bitsPerPixel; + boost::endian::big_uint8_t depth; + boost::endian::big_uint8_t isBigEndian; + boost::endian::big_uint8_t isTrueColor; + boost::endian::big_uint16_t redMax; + boost::endian::big_uint16_t greenMax; + boost::endian::big_uint16_t blueMax; + boost::endian::big_uint8_t redShift; + boost::endian::big_uint8_t greenShift; + boost::endian::big_uint8_t blueShift; + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint8_t pad3; }; -struct ServerInitializationMsg { - boost::endian::big_uint16_t framebufferWidth; - boost::endian::big_uint16_t framebufferHeight; - PixelFormatStruct pixelFormat; - boost::endian::big_uint32_t nameLength; +struct ServerInitializationMsg +{ + boost::endian::big_uint16_t framebufferWidth; + boost::endian::big_uint16_t framebufferHeight; + PixelFormatStruct pixelFormat; + boost::endian::big_uint32_t nameLength; }; -enum class client_to_server_msg_type : uint8_t { - set_pixel_format = 0, - fix_color_map_entries = 1, - set_encodings = 2, - framebuffer_update_request = 3, - key_event = 4, - pointer_event = 5, - client_cut_text = 6 +enum class client_to_server_msg_type : uint8_t +{ + set_pixel_format = 0, + fix_color_map_entries = 1, + set_encodings = 2, + framebuffer_update_request = 3, + key_event = 4, + pointer_event = 5, + client_cut_text = 6 }; -enum class server_to_client_message_type : uint8_t { - framebuffer_update = 0, - set_color_map_entries = 1, - bell_message = 2, - server_cut_text = 3 +enum class server_to_client_message_type : uint8_t +{ + framebuffer_update = 0, + set_color_map_entries = 1, + bell_message = 2, + server_cut_text = 3 }; -struct SetPixelFormatMsg { - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint8_t pad3; - PixelFormatStruct pixelFormat; +struct SetPixelFormatMsg +{ + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint8_t pad3; + PixelFormatStruct pixelFormat; }; -struct FrameBufferUpdateReq { - boost::endian::big_uint8_t incremental; - boost::endian::big_uint16_t xPosition; - boost::endian::big_uint16_t yPosition; - boost::endian::big_uint16_t width; - boost::endian::big_uint16_t height; +struct FrameBufferUpdateReq +{ + boost::endian::big_uint8_t incremental; + boost::endian::big_uint16_t xPosition; + boost::endian::big_uint16_t yPosition; + boost::endian::big_uint16_t width; + boost::endian::big_uint16_t height; }; -struct KeyEventMsg { - boost::endian::big_uint8_t downFlag; - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint32_t key; +struct KeyEventMsg +{ + boost::endian::big_uint8_t downFlag; + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint32_t key; }; -struct PointerEventMsg { - boost::endian::big_uint8_t buttonMask; - boost::endian::big_uint16_t xPosition; - boost::endian::big_uint16_t yPosition; +struct PointerEventMsg +{ + boost::endian::big_uint8_t buttonMask; + boost::endian::big_uint16_t xPosition; + boost::endian::big_uint16_t yPosition; }; -struct ClientCutTextMsg { - std::vector<uint8_t> data; +struct ClientCutTextMsg +{ + std::vector<uint8_t> data; }; -enum class encoding_type : uint32_t { - raw = 0x00, - copy_rectangle = 0x01, - rising_rectangle = 0x02, - corre = 0x04, - hextile = 0x05, - zlib = 0x06, - tight = 0x07, - zlibhex = 0x08, - ultra = 0x09, - zrle = 0x10, - zywrle = 0x011, - cache_enable = 0xFFFF0001, - xor_enable = 0xFFFF0006, - server_state_ultranvc = 0xFFFF8000, - enable_keepAlive = 0xFFFF8001, - enableftp_protocol_version = 0xFFFF8002, - tight_compress_level_0 = 0xFFFFFF00, - tight_compress_level_9 = 0xFFFFFF09, - x_cursor = 0xFFFFFF10, - rich_cursor = 0xFFFFFF11, - pointer_pos = 0xFFFFFF18, - last_rect = 0xFFFFFF20, - new_framebuffer_size = 0xFFFFFF21, - tight_quality_level_0 = 0xFFFFFFE0, - tight_quality_level_9 = 0xFFFFFFE9 +enum class encoding_type : uint32_t +{ + raw = 0x00, + copy_rectangle = 0x01, + rising_rectangle = 0x02, + corre = 0x04, + hextile = 0x05, + zlib = 0x06, + tight = 0x07, + zlibhex = 0x08, + ultra = 0x09, + zrle = 0x10, + zywrle = 0x011, + cache_enable = 0xFFFF0001, + xor_enable = 0xFFFF0006, + server_state_ultranvc = 0xFFFF8000, + enable_keepAlive = 0xFFFF8001, + enableftp_protocol_version = 0xFFFF8002, + tight_compress_level_0 = 0xFFFFFF00, + tight_compress_level_9 = 0xFFFFFF09, + x_cursor = 0xFFFFFF10, + rich_cursor = 0xFFFFFF11, + pointer_pos = 0xFFFFFF18, + last_rect = 0xFFFFFF20, + new_framebuffer_size = 0xFFFFFF21, + tight_quality_level_0 = 0xFFFFFFE0, + tight_quality_level_9 = 0xFFFFFFE9 }; -struct FramebufferRectangle { - boost::endian::big_uint16_t x{}; - boost::endian::big_uint16_t y{}; - boost::endian::big_uint16_t width{}; - boost::endian::big_uint16_t height{}; - boost::endian::big_uint32_t encoding{}; - std::vector<uint8_t> data; +struct FramebufferRectangle +{ + boost::endian::big_uint16_t x{}; + boost::endian::big_uint16_t y{}; + boost::endian::big_uint16_t width{}; + boost::endian::big_uint16_t height{}; + boost::endian::big_uint32_t encoding{}; + std::vector<uint8_t> data; }; -struct FramebufferUpdateMsg { - boost::endian::big_uint8_t messageType{}; - std::vector<FramebufferRectangle> rectangles; +struct FramebufferUpdateMsg +{ + boost::endian::big_uint8_t messageType{}; + std::vector<FramebufferRectangle> rectangles; }; -inline std::string serialize(const FramebufferUpdateMsg& msg) { - // calculate the size of the needed vector for serialization - size_t vectorSize = 4; - for (const auto& rect : msg.rectangles) { - vectorSize += 12 + rect.data.size(); - } - - std::string serialized(vectorSize, 0); - - size_t i = 0; - serialized[i++] = static_cast<char>( - server_to_client_message_type::framebuffer_update); // Type - serialized[i++] = 0; // Pad byte - boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size(); - std::memcpy(&serialized[i], &numberOfRectangles, sizeof(numberOfRectangles)); - i += sizeof(numberOfRectangles); - - for (const auto& rect : msg.rectangles) { - // copy the first part of the struct - size_t bufferSize = - sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>); - std::memcpy(&serialized[i], &rect, bufferSize); - i += bufferSize; - - std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); - i += rect.data.size(); - } - - return serialized; +inline std::string serialize(const FramebufferUpdateMsg& msg) +{ + // calculate the size of the needed vector for serialization + size_t vectorSize = 4; + for (const auto& rect : msg.rectangles) + { + vectorSize += 12 + rect.data.size(); + } + + std::string serialized(vectorSize, 0); + + size_t i = 0; + serialized[i++] = static_cast<char>( + server_to_client_message_type::framebuffer_update); // Type + serialized[i++] = 0; // Pad byte + boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size(); + std::memcpy(&serialized[i], &numberOfRectangles, + sizeof(numberOfRectangles)); + i += sizeof(numberOfRectangles); + + for (const auto& rect : msg.rectangles) + { + // copy the first part of the struct + size_t bufferSize = + sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>); + std::memcpy(&serialized[i], &rect, bufferSize); + i += bufferSize; + + std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); + i += rect.data.size(); + } + + return serialized; } -enum class VncState { - UNSTARTED, - AWAITING_CLIENT_VERSION, - AWAITING_CLIENT_AUTH_METHOD, - AWAITING_CLIENT_INIT_msg, - MAIN_LOOP +enum class VncState +{ + UNSTARTED, + AWAITING_CLIENT_VERSION, + AWAITING_CLIENT_AUTH_METHOD, + AWAITING_CLIENT_INIT_msg, + MAIN_LOOP }; -class ConnectionMetadata { - public: - ConnectionMetadata(){}; +class ConnectionMetadata +{ + public: + ConnectionMetadata(){}; - VncState vncState{VncState::UNSTARTED}; + VncState vncState{VncState::UNSTARTED}; }; using meta_list = std::vector<ConnectionMetadata>; @@ -183,171 +204,219 @@ meta_list connectionStates(10); ConnectionMetadata meta; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/kvmws") - .websocket() - .onopen([&](crow::websocket::Connection& conn) { - if (meta.vncState == VncState::UNSTARTED) { - meta.vncState = VncState::AWAITING_CLIENT_VERSION; - conn.sendBinary(rfb38VersionString); - } else { // SHould never happen - conn.close(); - } - - }) - .onclose( - [&](crow::websocket::Connection& conn, const std::string& reason) { - meta.vncState = VncState::UNSTARTED; - }) - .onmessage([&](crow::websocket::Connection& conn, const std::string& data, - bool is_binary) { - switch (meta.vncState) { - case VncState::AWAITING_CLIENT_VERSION: { - std::cout << "Client sent: " << data; - if (data == rfb38VersionString || data == rfb37VersionString) { - std::string authTypes{1, - (uint8_t)RfbAuthScheme::no_authentication}; - conn.sendBinary(authTypes); - meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD; - } else if (data == rfb33VersionString) { - // TODO(ed) Support older protocols - meta.vncState = VncState::UNSTARTED; - conn.close(); - } else { - // TODO(ed) Support older protocols - meta.vncState = VncState::UNSTARTED; - conn.close(); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/kvmws") + .websocket() + .onopen([&](crow::websocket::Connection& conn) { + if (meta.vncState == VncState::UNSTARTED) + { + meta.vncState = VncState::AWAITING_CLIENT_VERSION; + conn.sendBinary(rfb38VersionString); } - } break; - case VncState::AWAITING_CLIENT_AUTH_METHOD: { - std::string securityResult{{0, 0, 0, 0}}; - if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) { - meta.vncState = VncState::AWAITING_CLIENT_INIT_msg; - } else { - // Mark auth as failed - securityResult[3] = 1; - meta.vncState = VncState::UNSTARTED; + else + { // SHould never happen + conn.close(); } - conn.sendBinary(securityResult); - } break; - case VncState::AWAITING_CLIENT_INIT_msg: { - // Now send the server initialization - ServerInitializationMsg serverInitMsg{}; - serverInitMsg.framebufferWidth = 800; - serverInitMsg.framebufferHeight = 600; - serverInitMsg.pixelFormat.bitsPerPixel = 32; - serverInitMsg.pixelFormat.isBigEndian = 0; - serverInitMsg.pixelFormat.isTrueColor = 1; - serverInitMsg.pixelFormat.redMax = 255; - serverInitMsg.pixelFormat.greenMax = 255; - serverInitMsg.pixelFormat.blueMax = 255; - serverInitMsg.pixelFormat.redShift = 16; - serverInitMsg.pixelFormat.greenShift = 8; - serverInitMsg.pixelFormat.blueShift = 0; - serverInitMsg.nameLength = 0; - std::cout << "size: " << sizeof(serverInitMsg); - // TODO(ed) this is ugly. Crow should really have a span type - // interface - // to avoid the copy, but alas, today it does not. - std::string s(reinterpret_cast<char*>(&serverInitMsg), - sizeof(serverInitMsg)); - std::cout << "s.size() " << s.size(); - conn.sendBinary(s); - meta.vncState = VncState::MAIN_LOOP; - } break; - case VncState::MAIN_LOOP: { - if (data.size() >= sizeof(client_to_server_msg_type)) { - auto type = static_cast<client_to_server_msg_type>(data[0]); - std::cout << "Received client message type " - << static_cast<std::size_t>(type) << "\n"; - switch (type) { - case client_to_server_msg_type::set_pixel_format: { - } break; - - case client_to_server_msg_type::fix_color_map_entries: { - } break; - case client_to_server_msg_type::set_encodings: { - } break; - case client_to_server_msg_type::framebuffer_update_request: { - // Make sure the buffer is long enough to handle what we're - // about to do - if (data.size() >= sizeof(FrameBufferUpdateReq) + - sizeof(client_to_server_msg_type)) { - auto msg = reinterpret_cast<const FrameBufferUpdateReq*>( - data.data() + // NOLINT - sizeof(client_to_server_msg_type)); - // TODO(ed) find a better way to do this deserialization - - // Todo(ed) lifecycle of the video puller and decoder - // should be - // with the websocket, not recreated every time - ast_video::SimpleVideoPuller p; - p.initialize(); - auto out = p.readVideo(); - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, - out.ySelector, out.uvSelector); - - FramebufferUpdateMsg bufferUpdateMsg; - - // If the viewer is requesting a full update, force write - // of all pixels - - FramebufferRectangle thisRect; - thisRect.x = msg->xPosition; - thisRect.y = msg->yPosition; - thisRect.width = out.width; - thisRect.height = out.height; - thisRect.encoding = - static_cast<uint8_t>(encoding_type::raw); - std::cout << "Encoding is " << thisRect.encoding; - thisRect.data.reserve( - static_cast<std::size_t>(thisRect.width) * - static_cast<std::size_t>(thisRect.height) * 4); - std::cout << "Width " << out.width << " Height " - << out.height; - - for (int i = 0; i < out.width * out.height; i++) { - auto& pixel = d.outBuffer[i]; - thisRect.data.push_back(pixel.b); - thisRect.data.push_back(pixel.g); - thisRect.data.push_back(pixel.r); - thisRect.data.push_back(0); + }) + .onclose( + [&](crow::websocket::Connection& conn, const std::string& reason) { + meta.vncState = VncState::UNSTARTED; + }) + .onmessage([&](crow::websocket::Connection& conn, + const std::string& data, bool is_binary) { + switch (meta.vncState) + { + case VncState::AWAITING_CLIENT_VERSION: + { + std::cout << "Client sent: " << data; + if (data == rfb38VersionString || + data == rfb37VersionString) + { + std::string authTypes{ + 1, (uint8_t)RfbAuthScheme::no_authentication}; + conn.sendBinary(authTypes); + meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD; + } + else if (data == rfb33VersionString) + { + // TODO(ed) Support older protocols + meta.vncState = VncState::UNSTARTED; + conn.close(); + } + else + { + // TODO(ed) Support older protocols + meta.vncState = VncState::UNSTARTED; + conn.close(); } - - bufferUpdateMsg.rectangles.push_back(std::move(thisRect)); - auto serialized = serialize(bufferUpdateMsg); - - conn.sendBinary(serialized); - - } // TODO(Ed) handle error - } - break; - - case client_to_server_msg_type::key_event: { - } break; - - case client_to_server_msg_type::pointer_event: { - } break; - - case client_to_server_msg_type::client_cut_text: { - } break; - - default: - break; - } + case VncState::AWAITING_CLIENT_AUTH_METHOD: + { + std::string securityResult{{0, 0, 0, 0}}; + if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) + { + meta.vncState = VncState::AWAITING_CLIENT_INIT_msg; + } + else + { + // Mark auth as failed + securityResult[3] = 1; + meta.vncState = VncState::UNSTARTED; + } + conn.sendBinary(securityResult); + } + break; + case VncState::AWAITING_CLIENT_INIT_msg: + { + // Now send the server initialization + ServerInitializationMsg serverInitMsg{}; + serverInitMsg.framebufferWidth = 800; + serverInitMsg.framebufferHeight = 600; + serverInitMsg.pixelFormat.bitsPerPixel = 32; + serverInitMsg.pixelFormat.isBigEndian = 0; + serverInitMsg.pixelFormat.isTrueColor = 1; + serverInitMsg.pixelFormat.redMax = 255; + serverInitMsg.pixelFormat.greenMax = 255; + serverInitMsg.pixelFormat.blueMax = 255; + serverInitMsg.pixelFormat.redShift = 16; + serverInitMsg.pixelFormat.greenShift = 8; + serverInitMsg.pixelFormat.blueShift = 0; + serverInitMsg.nameLength = 0; + std::cout << "size: " << sizeof(serverInitMsg); + // TODO(ed) this is ugly. Crow should really have a span + // type interface to avoid the copy, but alas, today it does + // not. + std::string s(reinterpret_cast<char*>(&serverInitMsg), + sizeof(serverInitMsg)); + std::cout << "s.size() " << s.size(); + conn.sendBinary(s); + meta.vncState = VncState::MAIN_LOOP; + } + break; + case VncState::MAIN_LOOP: + { + if (data.size() >= sizeof(client_to_server_msg_type)) + { + auto type = + static_cast<client_to_server_msg_type>(data[0]); + std::cout << "Received client message type " + << static_cast<std::size_t>(type) << "\n"; + switch (type) + { + case client_to_server_msg_type::set_pixel_format: + { + } + break; + + case client_to_server_msg_type:: + fix_color_map_entries: + { + } + break; + case client_to_server_msg_type::set_encodings: + { + } + break; + case client_to_server_msg_type:: + framebuffer_update_request: + { + // Make sure the buffer is long enough to handle + // what we're about to do + if (data.size() >= + sizeof(FrameBufferUpdateReq) + + sizeof(client_to_server_msg_type)) + { + auto msg = reinterpret_cast< + const FrameBufferUpdateReq*>( + data.data() + // NOLINT + sizeof(client_to_server_msg_type)); + // TODO(ed) find a better way to do this + // deserialization + + // Todo(ed) lifecycle of the video puller + // and decoder should be with the websocket, + // not recreated every time + ast_video::SimpleVideoPuller p; + p.initialize(); + auto out = p.readVideo(); + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, + out.mode, out.ySelector, + out.uvSelector); + + FramebufferUpdateMsg bufferUpdateMsg; + + // If the viewer is requesting a full + // update, force write of all pixels + + FramebufferRectangle thisRect; + thisRect.x = msg->xPosition; + thisRect.y = msg->yPosition; + thisRect.width = out.width; + thisRect.height = out.height; + thisRect.encoding = static_cast<uint8_t>( + encoding_type::raw); + std::cout << "Encoding is " + << thisRect.encoding; + thisRect.data.reserve( + static_cast<std::size_t>( + thisRect.width) * + static_cast<std::size_t>( + thisRect.height) * + 4); + std::cout << "Width " << out.width + << " Height " << out.height; + + for (int i = 0; i < out.width * out.height; + i++) + { + auto& pixel = d.outBuffer[i]; + thisRect.data.push_back(pixel.b); + thisRect.data.push_back(pixel.g); + thisRect.data.push_back(pixel.r); + thisRect.data.push_back(0); + } + + bufferUpdateMsg.rectangles.push_back( + std::move(thisRect)); + auto serialized = + serialize(bufferUpdateMsg); + + conn.sendBinary(serialized); + + } // TODO(Ed) handle error + } + + break; + + case client_to_server_msg_type::key_event: + { + } + break; + + case client_to_server_msg_type::pointer_event: + { + } + break; + + case client_to_server_msg_type::client_cut_text: + { + } + break; + + default: + break; + } + } + } + break; + case VncState::UNSTARTED: + // Error? TODO + break; } - - } break; - case VncState::UNSTARTED: - // Error? TODO - break; - } - - }); + }); } -} // namespace kvm -} // namespace crow
\ No newline at end of file +} // namespace kvm +} // namespace crow
\ No newline at end of file diff --git a/include/webassets.hpp b/include/webassets.hpp index 5eabffedb9..7f1c1f5701 100644 --- a/include/webassets.hpp +++ b/include/webassets.hpp @@ -1,137 +1,161 @@ #pragma once -#include <experimental/filesystem> -#include <fstream> -#include <string> #include <crow/app.h> #include <crow/http_request.h> #include <crow/http_response.h> #include <crow/routing.h> + #include <boost/algorithm/string/replace.hpp> #include <boost/container/flat_set.hpp> +#include <experimental/filesystem> +#include <fstream> +#include <string> -namespace crow { -namespace webassets { +namespace crow +{ +namespace webassets +{ namespace filesystem = std::experimental::filesystem; -struct CmpStr { - bool operator()(const char* a, const char* b) const { - return std::strcmp(a, b) < 0; - } +struct CmpStr +{ + bool operator()(const char* a, const char* b) const + { + return std::strcmp(a, b) < 0; + } }; static boost::container::flat_set<std::string> routes; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - const static boost::container::flat_map<const char*, const char*, CmpStr> - contentTypes{ - {{".css", "text/css;charset=UTF-8"}, - {".html", "text/html;charset=UTF-8"}, - {".js", "text/html;charset=UTF-8"}, - {".png", "image/png;charset=UTF-8"}, - {".woff", "application/x-font-woff"}, - {".woff2", "application/x-font-woff2"}, - {".gif", "image/gif"}, - {".ico", "image/x-icon"}, - {".ttf", "application/x-font-ttf"}, - {".svg", "image/svg+xml"}, - {".eot", "application/vnd.ms-fontobject"}, - {".xml", "application/xml"}, - {".jpg", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".json", "application/json"}, - // dev tools don't care about map type, setting to json causes - // browser to show as text - // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files - {".map", "application/json"}}}; - filesystem::path rootpath{"/usr/share/www/"}; - filesystem::recursive_directory_iterator dirIter(rootpath); - // In certain cases, we might have both a gzipped version of the file AND a - // non-gzipped version. To avoid duplicated routes, we need to make sure we - // get the gzipped version first. Because the gzipped path should be longer - // than the non gzipped path, if we sort in Ascending order, we should be - // guaranteed to get the gzip version first. - std::vector<filesystem::directory_entry> paths(filesystem::begin(dirIter), - filesystem::end(dirIter)); - std::sort(paths.rbegin(), paths.rend()); - - for (const filesystem::directory_entry& dir : paths) { - filesystem::path absolutePath = dir.path(); - filesystem::path relativePath{ - absolutePath.string().substr(rootpath.string().size() - 1)}; - if (filesystem::is_directory(dir)) { - // don't recurse into hidden directories or symlinks - if (boost::starts_with(dir.path().filename().string(), ".") || - filesystem::is_symlink(dir)) { - dirIter.disable_recursion_pending(); - } - } else if (filesystem::is_regular_file(dir)) { - std::string extension = relativePath.extension(); - filesystem::path webpath = relativePath; - const char* contentEncoding = nullptr; - - if (extension == ".gz") { - webpath = webpath.replace_extension(""); - // Use the non-gzip version for determining content type - extension = webpath.extension().string(); - contentEncoding = "gzip"; - } - - if (boost::starts_with(webpath.filename().string(), "index.")) { - webpath = webpath.parent_path(); - if (webpath.string().size() == 0 || webpath.string().back() != '/') { - // insert the non-directory version of this path - routes.insert(webpath); - webpath += "/"; +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + const static boost::container::flat_map<const char*, const char*, CmpStr> + contentTypes{ + {{".css", "text/css;charset=UTF-8"}, + {".html", "text/html;charset=UTF-8"}, + {".js", "text/html;charset=UTF-8"}, + {".png", "image/png;charset=UTF-8"}, + {".woff", "application/x-font-woff"}, + {".woff2", "application/x-font-woff2"}, + {".gif", "image/gif"}, + {".ico", "image/x-icon"}, + {".ttf", "application/x-font-ttf"}, + {".svg", "image/svg+xml"}, + {".eot", "application/vnd.ms-fontobject"}, + {".xml", "application/xml"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".json", "application/json"}, + // dev tools don't care about map type, setting to json causes + // browser to show as text + // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files + {".map", "application/json"}}}; + filesystem::path rootpath{"/usr/share/www/"}; + filesystem::recursive_directory_iterator dirIter(rootpath); + // In certain cases, we might have both a gzipped version of the file AND a + // non-gzipped version. To avoid duplicated routes, we need to make sure we + // get the gzipped version first. Because the gzipped path should be longer + // than the non gzipped path, if we sort in Ascending order, we should be + // guaranteed to get the gzip version first. + std::vector<filesystem::directory_entry> paths(filesystem::begin(dirIter), + filesystem::end(dirIter)); + std::sort(paths.rbegin(), paths.rend()); + + for (const filesystem::directory_entry& dir : paths) + { + filesystem::path absolutePath = dir.path(); + filesystem::path relativePath{ + absolutePath.string().substr(rootpath.string().size() - 1)}; + if (filesystem::is_directory(dir)) + { + // don't recurse into hidden directories or symlinks + if (boost::starts_with(dir.path().filename().string(), ".") || + filesystem::is_symlink(dir)) + { + dirIter.disable_recursion_pending(); + } } - } - - std::pair<boost::container::flat_set<std::string>::iterator, bool> - inserted = routes.insert(webpath); - - if (!inserted.second) { - // Got a duplicated path. This is expected in certain situations - BMCWEB_LOG_DEBUG << "Got duplicated path " << webpath; - continue; - } - const char* contentType = nullptr; - - auto contentTypeIt = contentTypes.find(extension.c_str()); - if (contentTypeIt == contentTypes.end()) { - BMCWEB_LOG_ERROR << "Cannot determine content-type for " << absolutePath - << " with extension " << extension; - } else { - contentType = contentTypeIt->second; - } - - app.routeDynamic(webpath)( - [absolutePath, contentType, contentEncoding](const crow::Request& req, - crow::Response& res) { - if (contentType != nullptr) { - res.addHeader("Content-Type", contentType); + else if (filesystem::is_regular_file(dir)) + { + std::string extension = relativePath.extension(); + filesystem::path webpath = relativePath; + const char* contentEncoding = nullptr; + + if (extension == ".gz") + { + webpath = webpath.replace_extension(""); + // Use the non-gzip version for determining content type + extension = webpath.extension().string(); + contentEncoding = "gzip"; } - if (contentEncoding != nullptr) { - res.addHeader("Content-Encoding", contentEncoding); + if (boost::starts_with(webpath.filename().string(), "index.")) + { + webpath = webpath.parent_path(); + if (webpath.string().size() == 0 || + webpath.string().back() != '/') + { + // insert the non-directory version of this path + routes.insert(webpath); + webpath += "/"; + } } - // res.set_header("Cache-Control", "public, max-age=86400"); - std::ifstream inf(absolutePath); - if (!inf) { - BMCWEB_LOG_DEBUG << "failed to read file"; - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + std::pair<boost::container::flat_set<std::string>::iterator, bool> + inserted = routes.insert(webpath); + + if (!inserted.second) + { + // Got a duplicated path. This is expected in certain + // situations + BMCWEB_LOG_DEBUG << "Got duplicated path " << webpath; + continue; + } + const char* contentType = nullptr; + + auto contentTypeIt = contentTypes.find(extension.c_str()); + if (contentTypeIt == contentTypes.end()) + { + BMCWEB_LOG_ERROR << "Cannot determine content-type for " + << absolutePath << " with extension " + << extension; + } + else + { + contentType = contentTypeIt->second; } - res.body() = {std::istreambuf_iterator<char>(inf), - std::istreambuf_iterator<char>()}; - res.end(); - }); + app.routeDynamic(webpath)( + [absolutePath, contentType, contentEncoding]( + const crow::Request& req, crow::Response& res) { + if (contentType != nullptr) + { + res.addHeader("Content-Type", contentType); + } + + if (contentEncoding != nullptr) + { + res.addHeader("Content-Encoding", contentEncoding); + } + + // res.set_header("Cache-Control", "public, max-age=86400"); + std::ifstream inf(absolutePath); + if (!inf) + { + BMCWEB_LOG_DEBUG << "failed to read file"; + res.result( + boost::beast::http::status::internal_server_error); + res.end(); + return; + } + + res.body() = {std::istreambuf_iterator<char>(inf), + std::istreambuf_iterator<char>()}; + res.end(); + }); + } } - } -} // namespace webassets -} // namespace webassets -} // namespace crow +} // namespace webassets +} // namespace webassets +} // namespace crow diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp index 4865516b7a..085b76f6bd 100644 --- a/redfish-core/include/error_messages.hpp +++ b/redfish-core/include/error_messages.hpp @@ -24,9 +24,11 @@ #pragma once #include <nlohmann/json.hpp> -namespace redfish { +namespace redfish +{ -namespace messages { +namespace messages +{ constexpr const char* messageVersionPrefix = "Base.1.2.0."; constexpr const char* messageAnnotation = "@Message.ExtendedInfo"; @@ -636,6 +638,6 @@ nlohmann::json queryParameterOutOfRange(const std::string& arg1, * AUTOGENERATED FUNCTIONS END * *********************************/ -} // namespace messages +} // namespace messages -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/include/node.hpp b/redfish-core/include/node.hpp index 20d295197e..8dbb0c2d70 100644 --- a/redfish-core/include/node.hpp +++ b/redfish-core/include/node.hpp @@ -18,161 +18,190 @@ #include "privileges.hpp" #include "token_authorization_middleware.hpp" #include "webserver_common.hpp" + #include "crow.h" -namespace redfish { +namespace redfish +{ /** * AsyncResp * Gathers data needed for response processing after async calls are done */ -class AsyncResp { - public: - AsyncResp(crow::Response& response) : res(response) {} +class AsyncResp +{ + public: + AsyncResp(crow::Response& response) : res(response) + { + } - ~AsyncResp() { res.end(); } + ~AsyncResp() + { + res.end(); + } - crow::Response& res; + crow::Response& res; }; /** * @brief Abstract class used for implementing Redfish nodes. * */ -class Node { - public: - template <typename... Params> - Node(CrowApp& app, std::string&& entityUrl, Params... params) { - app.routeDynamic(entityUrl.c_str()) - .methods("GET"_method, "PATCH"_method, "POST"_method, - "DELETE"_method)([&](const crow::Request& req, - crow::Response& res, Params... params) { - std::vector<std::string> paramVec = {params...}; - dispatchRequest(app, req, res, paramVec); - }); - } - - virtual ~Node() = default; - - const std::string* getUrl() const { - auto odataId = json.find("@odata.id"); - if (odataId == json.end()) { - return nullptr; +class Node +{ + public: + template <typename... Params> + Node(CrowApp& app, std::string&& entityUrl, Params... params) + { + app.routeDynamic(entityUrl.c_str()) + .methods("GET"_method, "PATCH"_method, "POST"_method, + "DELETE"_method)([&](const crow::Request& req, + crow::Response& res, + Params... params) { + std::vector<std::string> paramVec = {params...}; + dispatchRequest(app, req, res, paramVec); + }); } - return odataId->get_ptr<const std::string*>(); - } - - /** - * @brief Inserts subroute fields into for the node's json in the form: - * "subroute_name" : { "odata.id": "node_url/subroute_name/" } - * Excludes metadata urls starting with "$" and child urls having - * more than one level. - * - * @return None - */ - void getSubRoutes(const std::vector<std::unique_ptr<Node>>& allNodes) { - const std::string* url = getUrl(); - if (url == nullptr) { - // BMCWEB_LOG_CRITICAL << "Unable to get url for route"; - return; - } + virtual ~Node() = default; - for (const auto& node : allNodes) { - const std::string* route = node->getUrl(); - if (route == nullptr) { - // BMCWEB_LOG_CRITICAL << "Unable to get url for route"; - continue; - } - if (boost::starts_with(*route, *url)) { - std::string subRoute = route->substr(url->size()); - if (subRoute.empty()) { - continue; + const std::string* getUrl() const + { + auto odataId = json.find("@odata.id"); + if (odataId == json.end()) + { + return nullptr; } - if (boost::starts_with(subRoute, "/")) { - subRoute.erase(0, 1); - } + return odataId->get_ptr<const std::string*>(); + } - if (boost::ends_with(subRoute, "/")) { - subRoute.pop_back(); + /** + * @brief Inserts subroute fields into for the node's json in the form: + * "subroute_name" : { "odata.id": "node_url/subroute_name/" } + * Excludes metadata urls starting with "$" and child urls having + * more than one level. + * + * @return None + */ + void getSubRoutes(const std::vector<std::unique_ptr<Node>>& allNodes) + { + const std::string* url = getUrl(); + if (url == nullptr) + { + // BMCWEB_LOG_CRITICAL << "Unable to get url for route"; + return; } - if (!boost::starts_with(subRoute, "$") && - subRoute.find('/') == std::string::npos) { - json[subRoute] = nlohmann::json{{"@odata.id", *route}}; + for (const auto& node : allNodes) + { + const std::string* route = node->getUrl(); + if (route == nullptr) + { + // BMCWEB_LOG_CRITICAL << "Unable to get url for route"; + continue; + } + if (boost::starts_with(*route, *url)) + { + std::string subRoute = route->substr(url->size()); + if (subRoute.empty()) + { + continue; + } + + if (boost::starts_with(subRoute, "/")) + { + subRoute.erase(0, 1); + } + + if (boost::ends_with(subRoute, "/")) + { + subRoute.pop_back(); + } + + if (!boost::starts_with(subRoute, "$") && + subRoute.find('/') == std::string::npos) + { + json[subRoute] = nlohmann::json{{"@odata.id", *route}}; + } + } } - } - } - } - - OperationMap entityPrivileges; - - protected: - // Node is designed to be an abstract class, so doGet is pure virtual - virtual void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) { - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - } - - virtual void doPatch(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) { - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - } - - virtual void doPost(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) { - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - } - - virtual void doDelete(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) { - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - } - - nlohmann::json json; - - private: - void dispatchRequest(CrowApp& app, const crow::Request& req, - crow::Response& res, - const std::vector<std::string>& params) { - auto ctx = - app.template getContext<crow::token_authorization::Middleware>(req); - - if (!isMethodAllowedForUser(req.method(), entityPrivileges, - ctx.session->username)) { - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - return; } - switch (req.method()) { - case "GET"_method: - doGet(res, req, params); - break; + OperationMap entityPrivileges; - case "PATCH"_method: - doPatch(res, req, params); - break; + protected: + // Node is designed to be an abstract class, so doGet is pure virtual + virtual void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) + { + res.result(boost::beast::http::status::method_not_allowed); + res.end(); + } - case "POST"_method: - doPost(res, req, params); - break; + virtual void doPatch(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) + { + res.result(boost::beast::http::status::method_not_allowed); + res.end(); + } - case "DELETE"_method: - doDelete(res, req, params); - break; + virtual void doPost(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) + { + res.result(boost::beast::http::status::method_not_allowed); + res.end(); + } - default: - res.result(boost::beast::http::status::not_found); + virtual void doDelete(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) + { + res.result(boost::beast::http::status::method_not_allowed); res.end(); } - return; - } + + nlohmann::json json; + + private: + void dispatchRequest(CrowApp& app, const crow::Request& req, + crow::Response& res, + const std::vector<std::string>& params) + { + auto ctx = + app.template getContext<crow::token_authorization::Middleware>(req); + + if (!isMethodAllowedForUser(req.method(), entityPrivileges, + ctx.session->username)) + { + res.result(boost::beast::http::status::method_not_allowed); + res.end(); + return; + } + + switch (req.method()) + { + case "GET"_method: + doGet(res, req, params); + break; + + case "PATCH"_method: + doPatch(res, req, params); + break; + + case "POST"_method: + doPost(res, req, params); + break; + + case "DELETE"_method: + doDelete(res, req, params); + break; + + default: + res.result(boost::beast::http::status::not_found); + res.end(); + } + return; + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/include/privileges.hpp b/redfish-core/include/privileges.hpp index 437bb15e04..0f6b903793 100644 --- a/redfish-core/include/privileges.hpp +++ b/redfish-core/include/privileges.hpp @@ -16,15 +16,21 @@ #pragma once #include <bitset> +#include <boost/container/flat_map.hpp> +#include <boost/optional.hpp> #include <cstdint> #include <vector> + #include "crow.h" -#include <boost/container/flat_map.hpp> -#include <boost/optional.hpp> -namespace redfish { +namespace redfish +{ -enum class PrivilegeType { BASE, OEM }; +enum class PrivilegeType +{ + BASE, + OEM +}; /** @brief A fixed array of compile time privileges */ constexpr std::array<const char*, 5> basePrivileges{ @@ -56,105 +62,118 @@ static const std::vector<std::string> privilegeNames{basePrivileges.begin(), * (user domain) and false otherwise. * */ -class Privileges { - public: - /** - * @brief Constructs object without any privileges active - * - */ - Privileges() = default; - - /** - * @brief Constructs object with given privileges active - * - * @param[in] privilegeList List of privileges to be activated - * - */ - Privileges(std::initializer_list<const char*> privilegeList) { - for (const char* privilege : privilegeList) { - if (!setSinglePrivilege(privilege)) { - BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege - << "in constructor"; - } +class Privileges +{ + public: + /** + * @brief Constructs object without any privileges active + * + */ + Privileges() = default; + + /** + * @brief Constructs object with given privileges active + * + * @param[in] privilegeList List of privileges to be activated + * + */ + Privileges(std::initializer_list<const char*> privilegeList) + { + for (const char* privilege : privilegeList) + { + if (!setSinglePrivilege(privilege)) + { + BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege + << "in constructor"; + } + } } - } - - /** - * @brief Sets given privilege in the bitset - * - * @param[in] privilege Privilege to be set - * - * @return None - * - */ - bool setSinglePrivilege(const char* privilege) { - for (int searchIndex = 0; searchIndex < privilegeNames.size(); - searchIndex++) { - if (privilege == privilegeNames[searchIndex]) { - privilegeBitset.set(searchIndex); - return true; - } + + /** + * @brief Sets given privilege in the bitset + * + * @param[in] privilege Privilege to be set + * + * @return None + * + */ + bool setSinglePrivilege(const char* privilege) + { + for (int searchIndex = 0; searchIndex < privilegeNames.size(); + searchIndex++) + { + if (privilege == privilegeNames[searchIndex]) + { + privilegeBitset.set(searchIndex); + return true; + } + } + + return false; } - return false; - } - - /** - * @brief Sets given privilege in the bitset - * - * @param[in] privilege Privilege to be set - * - * @return None - * - */ - bool setSinglePrivilege(const std::string& privilege) { - return setSinglePrivilege(privilege.c_str()); - } - - /** - * @brief Retrieves names of all active privileges for a given type - * - * @param[in] type Base or OEM - * - * @return Vector of active privileges. Pointers are valid until - * the setSinglePrivilege is called, or the Privilege structure is destroyed - * - */ - std::vector<const std::string*> getActivePrivilegeNames( - const PrivilegeType type) const { - std::vector<const std::string*> activePrivileges; - - int searchIndex = 0; - int endIndex = basePrivilegeCount; - if (type == PrivilegeType::OEM) { - searchIndex = basePrivilegeCount - 1; - endIndex = privilegeNames.size(); + /** + * @brief Sets given privilege in the bitset + * + * @param[in] privilege Privilege to be set + * + * @return None + * + */ + bool setSinglePrivilege(const std::string& privilege) + { + return setSinglePrivilege(privilege.c_str()); } - for (; searchIndex < endIndex; searchIndex++) { - if (privilegeBitset.test(searchIndex)) { - activePrivileges.emplace_back(&privilegeNames[searchIndex]); - } + /** + * @brief Retrieves names of all active privileges for a given type + * + * @param[in] type Base or OEM + * + * @return Vector of active privileges. Pointers are valid until + * the setSinglePrivilege is called, or the Privilege structure is destroyed + * + */ + std::vector<const std::string*> + getActivePrivilegeNames(const PrivilegeType type) const + { + std::vector<const std::string*> activePrivileges; + + int searchIndex = 0; + int endIndex = basePrivilegeCount; + if (type == PrivilegeType::OEM) + { + searchIndex = basePrivilegeCount - 1; + endIndex = privilegeNames.size(); + } + + for (; searchIndex < endIndex; searchIndex++) + { + if (privilegeBitset.test(searchIndex)) + { + activePrivileges.emplace_back(&privilegeNames[searchIndex]); + } + } + + return activePrivileges; } - return activePrivileges; - } - - /** - * @brief Determines if this Privilege set is a superset of the given - * privilege set - * - * @param[in] privilege Privilege to be checked - * - * @return None - * - */ - bool isSupersetOf(const Privileges& p) const { - return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; - } - - private: - std::bitset<maxPrivilegeCount> privilegeBitset = 0; + /** + * @brief Determines if this Privilege set is a superset of the given + * privilege set + * + * @param[in] privilege Privilege to be checked + * + * @return None + * + */ + bool isSupersetOf(const Privileges& p) const + { + return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; + } + + private: + std::bitset<maxPrivilegeCount> privilegeBitset = 0; }; using OperationMap = boost::container::flat_map<boost::beast::http::verb, @@ -171,23 +190,28 @@ using OperationMap = boost::container::flat_map<boost::beast::http::verb, */ inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, const OperationMap& operationMap, - const Privileges& userPrivileges) { - const auto& it = operationMap.find(method); - if (it == operationMap.end()) { - return false; - } + const Privileges& userPrivileges) +{ + const auto& it = operationMap.find(method); + if (it == operationMap.end()) + { + return false; + } - // If there are no privileges assigned, assume no privileges required - if (it->second.empty()) { - return true; - } + // If there are no privileges assigned, assume no privileges required + if (it->second.empty()) + { + return true; + } - for (auto& requiredPrivileges : it->second) { - if (userPrivileges.isSupersetOf(requiredPrivileges)) { - return true; + for (auto& requiredPrivileges : it->second) + { + if (userPrivileges.isSupersetOf(requiredPrivileges)) + { + return true; + } } - } - return false; + return false; } /** @@ -201,13 +225,14 @@ inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, */ inline bool isMethodAllowedForUser(const boost::beast::http::verb method, const OperationMap& operationMap, - const std::string& user) { - // TODO: load user privileges from configuration as soon as its available - // now we are granting all privileges to everyone. - Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", - "ConfigureUsers", "ConfigureComponents"}; - - return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); + const std::string& user) +{ + // TODO: load user privileges from configuration as soon as its available + // now we are granting all privileges to everyone. + Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", + "ConfigureUsers", "ConfigureComponents"}; + + return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); } -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index ab60abb90f..7f41b6339f 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -28,48 +28,53 @@ #include "../lib/update_service.hpp" #include "webserver_common.hpp" -namespace redfish { +namespace redfish +{ /* * @brief Top level class installing and providing Redfish services */ -class RedfishService { - public: - /* - * @brief Redfish service constructor - * - * Loads Redfish configuration and installs schema resources - * - * @param[in] app Crow app on which Redfish will initialize - */ - RedfishService(CrowApp& app) { - nodes.emplace_back(std::make_unique<AccountService>(app)); - nodes.emplace_back(std::make_unique<SessionCollection>(app)); - nodes.emplace_back(std::make_unique<Roles>(app)); - nodes.emplace_back(std::make_unique<RoleCollection>(app)); - nodes.emplace_back(std::make_unique<ServiceRoot>(app)); - nodes.emplace_back(std::make_unique<NetworkProtocol>(app)); - nodes.emplace_back(std::make_unique<SessionService>(app)); - nodes.emplace_back(std::make_unique<EthernetCollection>(app)); - nodes.emplace_back(std::make_unique<EthernetInterface>(app)); - nodes.emplace_back(std::make_unique<Thermal>(app)); - nodes.emplace_back(std::make_unique<ManagerCollection>(app)); - nodes.emplace_back(std::make_unique<Manager>(app)); - nodes.emplace_back(std::make_unique<ChassisCollection>(app)); - nodes.emplace_back(std::make_unique<Chassis>(app)); - nodes.emplace_back(std::make_unique<UpdateService>(app)); - nodes.emplace_back(std::make_unique<SoftwareInventoryCollection>(app)); - nodes.emplace_back(std::make_unique<SoftwareInventory>(app)); - nodes.emplace_back(std::make_unique<VlanNetworkInterfaceCollection>(app)); - nodes.emplace_back(std::make_unique<SystemsCollection>(app)); - nodes.emplace_back(std::make_unique<Systems>(app)); +class RedfishService +{ + public: + /* + * @brief Redfish service constructor + * + * Loads Redfish configuration and installs schema resources + * + * @param[in] app Crow app on which Redfish will initialize + */ + RedfishService(CrowApp& app) + { + nodes.emplace_back(std::make_unique<AccountService>(app)); + nodes.emplace_back(std::make_unique<SessionCollection>(app)); + nodes.emplace_back(std::make_unique<Roles>(app)); + nodes.emplace_back(std::make_unique<RoleCollection>(app)); + nodes.emplace_back(std::make_unique<ServiceRoot>(app)); + nodes.emplace_back(std::make_unique<NetworkProtocol>(app)); + nodes.emplace_back(std::make_unique<SessionService>(app)); + nodes.emplace_back(std::make_unique<EthernetCollection>(app)); + nodes.emplace_back(std::make_unique<EthernetInterface>(app)); + nodes.emplace_back(std::make_unique<Thermal>(app)); + nodes.emplace_back(std::make_unique<ManagerCollection>(app)); + nodes.emplace_back(std::make_unique<Manager>(app)); + nodes.emplace_back(std::make_unique<ChassisCollection>(app)); + nodes.emplace_back(std::make_unique<Chassis>(app)); + nodes.emplace_back(std::make_unique<UpdateService>(app)); + nodes.emplace_back(std::make_unique<SoftwareInventoryCollection>(app)); + nodes.emplace_back(std::make_unique<SoftwareInventory>(app)); + nodes.emplace_back( + std::make_unique<VlanNetworkInterfaceCollection>(app)); + nodes.emplace_back(std::make_unique<SystemsCollection>(app)); + nodes.emplace_back(std::make_unique<Systems>(app)); - for (auto& node : nodes) { - node->getSubRoutes(nodes); + for (auto& node : nodes) + { + node->getSubRoutes(nodes); + } } - } - private: - std::vector<std::unique_ptr<Node>> nodes; + private: + std::vector<std::unique_ptr<Node>> nodes; }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp index 25ac95417d..bde81ce72d 100644 --- a/redfish-core/include/utils/json_utils.hpp +++ b/redfish-core/include/utils/json_utils.hpp @@ -14,26 +14,36 @@ // limitations under the License. */ #pragma once -#include <nlohmann/json.hpp> #include <crow/http_request.h> #include <crow/http_response.h> -namespace redfish { +#include <nlohmann/json.hpp> + +namespace redfish +{ -namespace json_util { +namespace json_util +{ /** * @brief Defines JSON utils operation status */ -enum class Result { SUCCESS, NOT_EXIST, WRONG_TYPE, NULL_POINTER }; +enum class Result +{ + SUCCESS, + NOT_EXIST, + WRONG_TYPE, + NULL_POINTER +}; /** * @brief Describes JSON utils messages requirement */ -enum class MessageSetting { - NONE = 0x0, ///< No messages will be added - MISSING = 0x1, ///< PropertyMissing message will be added - TYPE_ERROR = 0x2 ///< PropertyValueTypeError message will be added +enum class MessageSetting +{ + NONE = 0x0, ///< No messages will be added + MISSING = 0x1, ///< PropertyMissing message will be added + TYPE_ERROR = 0x2 ///< PropertyValueTypeError message will be added }; /** @@ -287,6 +297,6 @@ Result getDouble(const char* fieldName, const nlohmann::json& json, bool processJsonFromRequest(crow::Response& res, const crow::Request& req, nlohmann::json& reqJson); -} // namespace json_util +} // namespace json_util -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp index 47b4c4c7b6..c58cafd633 100644 --- a/redfish-core/lib/account_service.hpp +++ b/redfish-core/lib/account_service.hpp @@ -17,40 +17,45 @@ #include "node.hpp" -namespace redfish { +namespace redfish +{ -class AccountService : public Node { - public: - AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") { - Node::json["@odata.id"] = "/redfish/v1/AccountService"; - Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#AccountService.AccountService"; - Node::json["Id"] = "AccountService"; - Node::json["Description"] = "BMC User Accounts"; - Node::json["Name"] = "Account Service"; - Node::json["ServiceEnabled"] = true; - Node::json["MinPasswordLength"] = 1; - Node::json["MaxPasswordLength"] = 20; - Node::json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts"; - Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles"; +class AccountService : public Node +{ + public: + AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") + { + Node::json["@odata.id"] = "/redfish/v1/AccountService"; + Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#AccountService.AccountService"; + Node::json["Id"] = "AccountService"; + Node::json["Description"] = "BMC User Accounts"; + Node::json["Name"] = "Account Service"; + Node::json["ServiceEnabled"] = true; + Node::json["MinPasswordLength"] = 1; + Node::json["MaxPasswordLength"] = 20; + Node::json["Accounts"]["@odata.id"] = + "/redfish/v1/AccountService/Accounts"; + Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles"; - entityPrivileges = { - {boost::beast::http::verb::get, - {{"ConfigureUsers"}, {"ConfigureManager"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, - {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, - {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, + {{"ConfigureUsers"}, {"ConfigureManager"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, + {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, + {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; + } - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp index b1c20a538a..7b68a270ad 100644 --- a/redfish-core/lib/chassis.hpp +++ b/redfish-core/lib/chassis.hpp @@ -16,9 +16,11 @@ #pragma once #include "node.hpp" + #include <boost/container/flat_map.hpp> -namespace redfish { +namespace redfish +{ /** * DBus types primitives for several generic DBus interfaces @@ -47,220 +49,251 @@ using PropertiesType = boost::container::flat_map<std::string, VariantType>; * This perhaps shall be different file, which has to be chosen on compile time * depending on OEM needs */ -class OnDemandChassisProvider { - public: - /** - * Function that retrieves all Chassis available through EntityManager. - * @param callback a function that shall be called to convert Dbus output into - * JSON. - */ - template <typename CallbackFunc> - void getChassisList(CallbackFunc &&callback) { - const std::array<const char *, 4> interfaces = { - "xyz.openbmc_project.Inventory.Item.Board", - "xyz.openbmc_project.Inventory.Item.Chassis", - "xyz.openbmc_project.Inventory.Item.PowerSupply", - "xyz.openbmc_project.Inventory.Item.System", +class OnDemandChassisProvider +{ + public: + /** + * Function that retrieves all Chassis available through EntityManager. + * @param callback a function that shall be called to convert Dbus output + * into JSON. + */ + template <typename CallbackFunc> + void getChassisList(CallbackFunc &&callback) + { + const std::array<const char *, 4> interfaces = { + "xyz.openbmc_project.Inventory.Item.Board", + "xyz.openbmc_project.Inventory.Item.Chassis", + "xyz.openbmc_project.Inventory.Item.PowerSupply", + "xyz.openbmc_project.Inventory.Item.System", + }; + crow::connections::systemBus->async_method_call( + [callback{std::move(callback)}]( + const boost::system::error_code error_code, + const std::vector<std::string> &resp) { + // Callback requires vector<string> to retrieve all available + // chassis list. + std::vector<std::string> chassisList; + if (error_code) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Entity Manager, and empty output could + // not be treated same way as error. + callback(false, chassisList); + return; + } + // Iterate over all retrieved ObjectPaths. + for (const std::string &objpath : resp) + { + std::size_t lastPos = objpath.rfind("/"); + if (lastPos != std::string::npos) + { + // and put it into output vector. + chassisList.emplace_back(objpath.substr(lastPos + 1)); + } + } + // Finally make a callback with useful data + callback(true, chassisList); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + "/xyz/openbmc_project/inventory", int32_t(3), interfaces); }; - crow::connections::systemBus->async_method_call( - [callback{std::move(callback)}]( - const boost::system::error_code error_code, - const std::vector<std::string> &resp) { - // Callback requires vector<string> to retrieve all available chassis - // list. - std::vector<std::string> chassisList; - if (error_code) { - // Something wrong on DBus, the error_code is not important at this - // moment, just return success=false, and empty output. Since size - // of vector may vary depending on information from Entity Manager, - // and empty output could not be treated same way as error. - callback(false, chassisList); - return; - } - // Iterate over all retrieved ObjectPaths. - for (const std::string &objpath : resp) { - std::size_t lastPos = objpath.rfind("/"); - if (lastPos != std::string::npos) { - // and put it into output vector. - chassisList.emplace_back(objpath.substr(lastPos + 1)); - } - } - // Finally make a callback with useful data - callback(true, chassisList); - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", - "/xyz/openbmc_project/inventory", int32_t(3), interfaces); - }; }; /** * ChassisCollection derived class for delivering Chassis Collection Schema */ -class ChassisCollection : public Node { - public: - ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") { - Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; - Node::json["@odata.id"] = "/redfish/v1/Chassis"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; - Node::json["Name"] = "Chassis Collection"; +class ChassisCollection : public Node +{ + public: + ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") + { + Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; + Node::json["@odata.id"] = "/redfish/v1/Chassis"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; + Node::json["Name"] = "Chassis Collection"; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // get chassis list, and call the below callback for JSON preparation - chassisProvider.getChassisList( - [&](const bool &success, const std::vector<std::string> &output) { - if (success) { - // ... prepare json array with appropriate @odata.id links - nlohmann::json chassisArray = nlohmann::json::array(); - for (const std::string &chassisItem : output) { - chassisArray.push_back( - {{"@odata.id", "/redfish/v1/Chassis/" + chassisItem}}); - } - // Then attach members, count size and return, - Node::json["Members"] = chassisArray; - Node::json["Members@odata.count"] = chassisArray.size(); - res.jsonValue = Node::json; - } else { - // ... otherwise, return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // get chassis list, and call the below callback for JSON preparation + chassisProvider.getChassisList( + [&](const bool &success, const std::vector<std::string> &output) { + if (success) + { + // ... prepare json array with appropriate @odata.id links + nlohmann::json chassisArray = nlohmann::json::array(); + for (const std::string &chassisItem : output) + { + chassisArray.push_back( + {{"@odata.id", + "/redfish/v1/Chassis/" + chassisItem}}); + } + // Then attach members, count size and return, + Node::json["Members"] = chassisArray; + Node::json["Members@odata.count"] = chassisArray.size(); + res.jsonValue = Node::json; + } + else + { + // ... otherwise, return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + } + res.end(); + }); + } - // Chassis Provider object - // TODO(Pawel) consider move it to singleton - OnDemandChassisProvider chassisProvider; + // Chassis Provider object + // TODO(Pawel) consider move it to singleton + OnDemandChassisProvider chassisProvider; }; /** * Chassis override class for delivering Chassis Schema */ -class Chassis : public Node { - public: - Chassis(CrowApp &app) - : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) { - Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; - Node::json["@odata.id"] = "/redfish/v1/Chassis"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; - Node::json["Name"] = "Chassis Collection"; - Node::json["ChassisType"] = "RackMount"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } +class Chassis : public Node +{ + public: + Chassis(CrowApp &app) : + Node(app, "/redfish/v1/Chassis/<str>/", std::string()) + { + Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; + Node::json["@odata.id"] = "/redfish/v1/Chassis"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; + Node::json["Name"] = "Chassis Collection"; + Node::json["ChassisType"] = "RackMount"; - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - res.jsonValue = Node::json; - const std::string &chassisId = params[0]; - crow::connections::systemBus->async_method_call( - [&res, chassisId(std::string(chassisId)) ]( - const boost::system::error_code error_code, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - if (error_code) { - res.jsonValue = {}; + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 1) + { res.result(boost::beast::http::status::internal_server_error); res.end(); return; - } - // Iterate over all retrieved ObjectPaths. - for (const std::pair<std::string, - std::vector<std::pair<std::string, - std::vector<std::string>>>> - &object : subtree) { - const std::string &path = object.first; - const std::vector<std::pair<std::string, std::vector<std::string>>> - &connectionNames = object.second; + } - if (!boost::ends_with(path, chassisId)) { - continue; - } - if (connectionNames.size() < 1) { - BMCWEB_LOG_ERROR << "Only got " << connectionNames.size() - << " Connection names"; - continue; - } + res.jsonValue = Node::json; + const std::string &chassisId = params[0]; + crow::connections::systemBus->async_method_call( + [&res, chassisId(std::string(chassisId))]( + const boost::system::error_code error_code, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + if (error_code) + { + res.jsonValue = {}; + res.result( + boost::beast::http::status::internal_server_error); + res.end(); + return; + } + // Iterate over all retrieved ObjectPaths. + for (const std::pair< + std::string, + std::vector< + std::pair<std::string, std::vector<std::string>>>> + &object : subtree) + { + const std::string &path = object.first; + const std::vector< + std::pair<std::string, std::vector<std::string>>> + &connectionNames = object.second; - const std::string connectionName = connectionNames[0].first; - crow::connections::systemBus->async_method_call( - [&res, chassisId(std::string(chassisId)) ]( - const boost::system::error_code error_code, - const std::vector<std::pair<std::string, VariantType>> - &propertiesList) { - for (const std::pair<std::string, VariantType> &property : - propertiesList) { - const std::string *value = - mapbox::getPtr<const std::string>(property.second); - if (value != nullptr) { - res.jsonValue[property.first] = *value; + if (!boost::ends_with(path, chassisId)) + { + continue; } - } - res.jsonValue["Name"] = chassisId; - res.jsonValue["Id"] = chassisId; - res.jsonValue["Thermal"] = { - {"@odata.id", - "/redfish/v1/Chassis/" + chassisId + "/Thermal"}}; - res.end(); - }, - connectionName, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); - // Found the Connection we were looking for, return - return; - } + if (connectionNames.size() < 1) + { + BMCWEB_LOG_ERROR << "Only got " + << connectionNames.size() + << " Connection names"; + continue; + } + + const std::string connectionName = connectionNames[0].first; + crow::connections::systemBus->async_method_call( + [&res, chassisId(std::string(chassisId))]( + const boost::system::error_code error_code, + const std::vector<std::pair< + std::string, VariantType>> &propertiesList) { + for (const std::pair<std::string, VariantType> + &property : propertiesList) + { + const std::string *value = + mapbox::getPtr<const std::string>( + property.second); + if (value != nullptr) + { + res.jsonValue[property.first] = *value; + } + } + res.jsonValue["Name"] = chassisId; + res.jsonValue["Id"] = chassisId; + res.jsonValue["Thermal"] = { + {"@odata.id", "/redfish/v1/Chassis/" + + chassisId + "/Thermal"}}; + res.end(); + }, + connectionName, path, "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Inventory.Decorator.Asset"); + // Found the Connection we were looking for, return + return; + } - // Couldn't find an object with that name. return an error - res.result(boost::beast::http::status::not_found); + // Couldn't find an object with that name. return an error + res.result(boost::beast::http::status::not_found); - res.end(); - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", - "/xyz/openbmc_project/inventory", int32_t(0), - std::array<const char *, 1>{ - "xyz.openbmc_project.Inventory.Decorator.Asset"}); - } + res.end(); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/inventory", int32_t(0), + std::array<const char *, 1>{ + "xyz.openbmc_project.Inventory.Decorator.Asset"}); + } - // Chassis Provider object - // TODO(Pawel) consider move it to singleton - OnDemandChassisProvider chassisProvider; -}; // namespace redfish + // Chassis Provider object + // TODO(Pawel) consider move it to singleton + OnDemandChassisProvider chassisProvider; +}; // namespace redfish -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp index 01111a4e5f..0c642798b5 100644 --- a/redfish-core/lib/ethernet.hpp +++ b/redfish-core/lib/ethernet.hpp @@ -15,13 +15,14 @@ */ #pragma once +#include <boost/container/flat_map.hpp> #include <dbus_singleton.hpp> #include <error_messages.hpp> #include <node.hpp> #include <utils/json_utils.hpp> -#include <boost/container/flat_map.hpp> -namespace redfish { +namespace redfish +{ /** * DBus types primitives for several generic DBus interfaces @@ -45,35 +46,40 @@ using GetManagedObjectsType = boost::container::flat_map< * Structure for keeping IPv4 data required by Redfish * TODO(Pawel) consider change everything to ptr, or to non-ptr values. */ -struct IPv4AddressData { - std::string id; - const std::string *address; - const std::string *domain; - const std::string *gateway; - std::string netmask; - std::string origin; - bool global; - /** - * @brief Operator< to enable sorting - * - * @param[in] obj Object to compare with - * - * @return This object id < supplied object id - */ - bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); } +struct IPv4AddressData +{ + std::string id; + const std::string *address; + const std::string *domain; + const std::string *gateway; + std::string netmask; + std::string origin; + bool global; + /** + * @brief Operator< to enable sorting + * + * @param[in] obj Object to compare with + * + * @return This object id < supplied object id + */ + bool operator<(const IPv4AddressData &obj) const + { + return (id < obj.id); + } }; /** * Structure for keeping basic single Ethernet Interface information * available from DBus */ -struct EthernetInterfaceData { - const unsigned int *speed; - const bool *autoNeg; - const std::string *hostname; - const std::string *defaultGateway; - const std::string *macAddress; - const unsigned int *vlanId; +struct EthernetInterfaceData +{ + const unsigned int *speed; + const bool *autoNeg; + const std::string *hostname; + const std::string *defaultGateway; + const std::string *macAddress; + const unsigned int *vlanId; }; /** @@ -85,1264 +91,1493 @@ struct EthernetInterfaceData { * This perhaps shall be different file, which has to be chosen on compile time * depending on OEM needs */ -class OnDemandEthernetProvider { - private: - // Consts that may have influence on EthernetProvider performance/memory usage - const size_t maxIpV4AddressesPerInterface = 10; - - // Helper function that allows to extract GetAllPropertiesType from - // GetManagedObjectsType, based on object path, and interface name - const PropertiesMapType *extractInterfaceProperties( - const sdbusplus::message::object_path &objpath, - const std::string &interface, const GetManagedObjectsType &dbus_data) { - const auto &dbusObj = dbus_data.find(objpath); - if (dbusObj != dbus_data.end()) { - const auto &iface = dbusObj->second.find(interface); - if (iface != dbusObj->second.end()) { - return &iface->second; - } +class OnDemandEthernetProvider +{ + private: + // Consts that may have influence on EthernetProvider performance/memory + // usage + const size_t maxIpV4AddressesPerInterface = 10; + + // Helper function that allows to extract GetAllPropertiesType from + // GetManagedObjectsType, based on object path, and interface name + const PropertiesMapType *extractInterfaceProperties( + const sdbusplus::message::object_path &objpath, + const std::string &interface, const GetManagedObjectsType &dbus_data) + { + const auto &dbusObj = dbus_data.find(objpath); + if (dbusObj != dbus_data.end()) + { + const auto &iface = dbusObj->second.find(interface); + if (iface != dbusObj->second.end()) + { + return &iface->second; + } + } + return nullptr; } - return nullptr; - } - - // Helper Wrapper that does inline object_path conversion from string - // into sdbusplus::message::object_path type - inline const PropertiesMapType *extractInterfaceProperties( - const std::string &objpath, const std::string &interface, - const GetManagedObjectsType &dbus_data) { - const auto &dbusObj = sdbusplus::message::object_path{objpath}; - return extractInterfaceProperties(dbusObj, interface, dbus_data); - } - - // Helper function that allows to get pointer to the property from - // GetAllPropertiesType native, or extracted by GetAllPropertiesType - template <typename T> - inline T const *const extractProperty(const PropertiesMapType &properties, - const std::string &name) { - const auto &property = properties.find(name); - if (property != properties.end()) { - return mapbox::getPtr<const T>(property->second); + + // Helper Wrapper that does inline object_path conversion from string + // into sdbusplus::message::object_path type + inline const PropertiesMapType * + extractInterfaceProperties(const std::string &objpath, + const std::string &interface, + const GetManagedObjectsType &dbus_data) + { + const auto &dbusObj = sdbusplus::message::object_path{objpath}; + return extractInterfaceProperties(dbusObj, interface, dbus_data); } - return nullptr; - } - // TODO(Pawel) Consider to move the above functions to dbus - // generic_interfaces.hpp - - // Helper function that extracts data from several dbus objects and several - // interfaces required by single ethernet interface instance - void extractEthernetInterfaceData(const std::string ðifaceId, - const GetManagedObjectsType &dbus_data, - EthernetInterfaceData ð_data) { - // Extract data that contains MAC Address - const PropertiesMapType *macProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/" + ethifaceId, - "xyz.openbmc_project.Network.MACAddress", dbus_data); - - if (macProperties != nullptr) { - eth_data.macAddress = - extractProperty<std::string>(*macProperties, "MACAddress"); + + // Helper function that allows to get pointer to the property from + // GetAllPropertiesType native, or extracted by GetAllPropertiesType + template <typename T> + inline T const *const extractProperty(const PropertiesMapType &properties, + const std::string &name) + { + const auto &property = properties.find(name); + if (property != properties.end()) + { + return mapbox::getPtr<const T>(property->second); + } + return nullptr; } + // TODO(Pawel) Consider to move the above functions to dbus + // generic_interfaces.hpp + + // Helper function that extracts data from several dbus objects and several + // interfaces required by single ethernet interface instance + void extractEthernetInterfaceData(const std::string ðifaceId, + const GetManagedObjectsType &dbus_data, + EthernetInterfaceData ð_data) + { + // Extract data that contains MAC Address + const PropertiesMapType *macProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/" + ethifaceId, + "xyz.openbmc_project.Network.MACAddress", dbus_data); + + if (macProperties != nullptr) + { + eth_data.macAddress = + extractProperty<std::string>(*macProperties, "MACAddress"); + } - const PropertiesMapType *vlanProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/" + ethifaceId, - "xyz.openbmc_project.Network.VLAN", dbus_data); + const PropertiesMapType *vlanProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/" + ethifaceId, + "xyz.openbmc_project.Network.VLAN", dbus_data); - if (vlanProperties != nullptr) { - eth_data.vlanId = extractProperty<unsigned int>(*vlanProperties, "Id"); - } + if (vlanProperties != nullptr) + { + eth_data.vlanId = + extractProperty<unsigned int>(*vlanProperties, "Id"); + } - // Extract data that contains link information (auto negotiation and speed) - const PropertiesMapType *ethProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/" + ethifaceId, - "xyz.openbmc_project.Network.EthernetInterface", dbus_data); + // Extract data that contains link information (auto negotiation and + // speed) + const PropertiesMapType *ethProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/" + ethifaceId, + "xyz.openbmc_project.Network.EthernetInterface", dbus_data); + + if (ethProperties != nullptr) + { + eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg"); + eth_data.speed = + extractProperty<unsigned int>(*ethProperties, "Speed"); + } - if (ethProperties != nullptr) { - eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg"); - eth_data.speed = extractProperty<unsigned int>(*ethProperties, "Speed"); + // Extract data that contains network config (HostName and DefaultGW) + const PropertiesMapType *configProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/config", + "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); + + if (configProperties != nullptr) + { + eth_data.hostname = + extractProperty<std::string>(*configProperties, "HostName"); + eth_data.defaultGateway = extractProperty<std::string>( + *configProperties, "DefaultGateway"); + } } - // Extract data that contains network config (HostName and DefaultGW) - const PropertiesMapType *configProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/config", - "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); - - if (configProperties != nullptr) { - eth_data.hostname = - extractProperty<std::string>(*configProperties, "HostName"); - eth_data.defaultGateway = - extractProperty<std::string>(*configProperties, "DefaultGateway"); + // Helper function that changes bits netmask notation (i.e. /24) + // into full dot notation + inline std::string getNetmask(unsigned int bits) + { + uint32_t value = 0xffffffff << (32 - bits); + std::string netmask = std::to_string((value >> 24) & 0xff) + "." + + std::to_string((value >> 16) & 0xff) + "." + + std::to_string((value >> 8) & 0xff) + "." + + std::to_string(value & 0xff); + return netmask; } - } - - // Helper function that changes bits netmask notation (i.e. /24) - // into full dot notation - inline std::string getNetmask(unsigned int bits) { - uint32_t value = 0xffffffff << (32 - bits); - std::string netmask = std::to_string((value >> 24) & 0xff) + "." + - std::to_string((value >> 16) & 0xff) + "." + - std::to_string((value >> 8) & 0xff) + "." + - std::to_string(value & 0xff); - return netmask; - } - - // Helper function that extracts data for single ethernet ipv4 address - void extractIPv4Data(const std::string ðifaceId, - const GetManagedObjectsType &dbus_data, - std::vector<IPv4AddressData> &ipv4_config) { - const std::string pathStart = - "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/"; - - // Since there might be several IPv4 configurations aligned with - // single ethernet interface, loop over all of them - for (auto &objpath : dbus_data) { - // Check if proper patter for object path appears - if (boost::starts_with(static_cast<const std::string &>(objpath.first), - pathStart)) { - // and get approrpiate interface - const auto &interface = - objpath.second.find("xyz.openbmc_project.Network.IP"); - if (interface != objpath.second.end()) { - // Make a properties 'shortcut', to make everything more readable - const PropertiesMapType &properties = interface->second; - // Instance IPv4AddressData structure, and set as appropriate - IPv4AddressData ipv4Address; - - ipv4Address.id = static_cast<const std::string &>(objpath.first) - .substr(pathStart.size()); - - // IPv4 address - ipv4Address.address = - extractProperty<std::string>(properties, "Address"); - // IPv4 gateway - ipv4Address.gateway = - extractProperty<std::string>(properties, "Gateway"); - - // Origin is kind of DBus object so fetch pointer... - const std::string *origin = - extractProperty<std::string>(properties, "Origin"); - if (origin != nullptr) { - ipv4Address.origin = - translateAddressOriginBetweenDBusAndRedfish(origin, true, true); - } - - // Netmask is presented as PrefixLength - const auto *mask = - extractProperty<uint8_t>(properties, "PrefixLength"); - if (mask != nullptr) { - // convert it to the string - ipv4Address.netmask = getNetmask(*mask); - } - - // Attach IPv4 only if address is present - if (ipv4Address.address != nullptr) { - // Check if given address is local, or global - if (boost::starts_with(*ipv4Address.address, "169.254")) { - ipv4Address.global = false; - } else { - ipv4Address.global = true; + + // Helper function that extracts data for single ethernet ipv4 address + void extractIPv4Data(const std::string ðifaceId, + const GetManagedObjectsType &dbus_data, + std::vector<IPv4AddressData> &ipv4_config) + { + const std::string pathStart = + "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/"; + + // Since there might be several IPv4 configurations aligned with + // single ethernet interface, loop over all of them + for (auto &objpath : dbus_data) + { + // Check if proper patter for object path appears + if (boost::starts_with( + static_cast<const std::string &>(objpath.first), pathStart)) + { + // and get approrpiate interface + const auto &interface = + objpath.second.find("xyz.openbmc_project.Network.IP"); + if (interface != objpath.second.end()) + { + // Make a properties 'shortcut', to make everything more + // readable + const PropertiesMapType &properties = interface->second; + // Instance IPv4AddressData structure, and set as + // appropriate + IPv4AddressData ipv4Address; + + ipv4Address.id = + static_cast<const std::string &>(objpath.first) + .substr(pathStart.size()); + + // IPv4 address + ipv4Address.address = + extractProperty<std::string>(properties, "Address"); + // IPv4 gateway + ipv4Address.gateway = + extractProperty<std::string>(properties, "Gateway"); + + // Origin is kind of DBus object so fetch pointer... + const std::string *origin = + extractProperty<std::string>(properties, "Origin"); + if (origin != nullptr) + { + ipv4Address.origin = + translateAddressOriginBetweenDBusAndRedfish( + origin, true, true); + } + + // Netmask is presented as PrefixLength + const auto *mask = + extractProperty<uint8_t>(properties, "PrefixLength"); + if (mask != nullptr) + { + // convert it to the string + ipv4Address.netmask = getNetmask(*mask); + } + + // Attach IPv4 only if address is present + if (ipv4Address.address != nullptr) + { + // Check if given address is local, or global + if (boost::starts_with(*ipv4Address.address, "169.254")) + { + ipv4Address.global = false; + } + else + { + ipv4Address.global = true; + } + ipv4_config.emplace_back(std::move(ipv4Address)); + } + } } - ipv4_config.emplace_back(std::move(ipv4Address)); - } } - } + + /** + * We have to sort this vector and ensure that order of IPv4 addresses + * is consistent between GETs to allow modification and deletion in + * PATCHes + */ + std::sort(ipv4_config.begin(), ipv4_config.end()); } + static const constexpr int ipV4AddressSectionsCount = 4; + + public: /** - * We have to sort this vector and ensure that order of IPv4 addresses - * is consistent between GETs to allow modification and deletion in PATCHes + * @brief Creates VLAN for given interface with given Id through D-Bus + * + * @param[in] ifaceId Id of interface for which VLAN will be created + * @param[in] inputVlanId ID of the new VLAN + * @param[in] callback Function that will be called after the operation + * + * @return None. */ - std::sort(ipv4_config.begin(), ipv4_config.end()); - } - - static const constexpr int ipV4AddressSectionsCount = 4; - - public: - /** - * @brief Creates VLAN for given interface with given Id through D-Bus - * - * @param[in] ifaceId Id of interface for which VLAN will be created - * @param[in] inputVlanId ID of the new VLAN - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, - CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, - static_cast<uint32_t>(inputVlanId)); - }; - - /** - * @brief Sets given Id on the given VLAN interface through D-Bus - * - * @param[in] ifaceId Id of VLAN interface that should be modified - * @param[in] inputVlanId New ID of the VLAN - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - static void changeVlanId(const std::string &ifaceId, - const uint32_t &inputVlanId, - CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", - std::string("/xyz/openbmc_project/network/") + ifaceId, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.VLAN", "Id", - sdbusplus::message::variant<uint32_t>(inputVlanId)); - }; - - /** - * @brief Helper function that verifies IP address to check if it is in - * proper format. If bits pointer is provided, also calculates active - * bit count for Subnet Mask. - * - * @param[in] ip IP that will be verified - * @param[out] bits Calculated mask in bits notation - * - * @return true in case of success, false otherwise - */ - bool ipv4VerifyIpAndGetBitcount(const std::string &ip, - uint8_t *bits = nullptr) { - std::vector<std::string> bytesInMask; - - boost::split(bytesInMask, ip, boost::is_any_of(".")); - - if (bytesInMask.size() != ipV4AddressSectionsCount) { - return false; - } + template <typename CallbackFunc> + void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, + CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network", + "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, + static_cast<uint32_t>(inputVlanId)); + }; - if (bits != nullptr) { - *bits = 0; - } + /** + * @brief Sets given Id on the given VLAN interface through D-Bus + * + * @param[in] ifaceId Id of VLAN interface that should be modified + * @param[in] inputVlanId New ID of the VLAN + * @param[in] callback Function that will be called after the operation + * + * @return None. + */ + template <typename CallbackFunc> + static void changeVlanId(const std::string &ifaceId, + const uint32_t &inputVlanId, + CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + std::string("/xyz/openbmc_project/network/") + ifaceId, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.VLAN", "Id", + sdbusplus::message::variant<uint32_t>(inputVlanId)); + }; + + /** + * @brief Helper function that verifies IP address to check if it is in + * proper format. If bits pointer is provided, also calculates active + * bit count for Subnet Mask. + * + * @param[in] ip IP that will be verified + * @param[out] bits Calculated mask in bits notation + * + * @return true in case of success, false otherwise + */ + bool ipv4VerifyIpAndGetBitcount(const std::string &ip, + uint8_t *bits = nullptr) + { + std::vector<std::string> bytesInMask; + + boost::split(bytesInMask, ip, boost::is_any_of(".")); - char *endPtr; - long previousValue = 255; - bool firstZeroInByteHit; - for (const std::string &byte : bytesInMask) { - if (byte.empty()) { - return false; - } - - // Use strtol instead of stroi to avoid exceptions - long value = std::strtol(byte.c_str(), &endPtr, 10); - - // endPtr should point to the end of the string, otherwise given string - // is not 100% number - if (*endPtr != '\0') { - return false; - } - - // Value should be contained in byte - if (value < 0 || value > 255) { - return false; - } - - if (bits != nullptr) { - // Mask has to be continuous between bytes - if (previousValue != 255 && value != 0) { - return false; + if (bytesInMask.size() != ipV4AddressSectionsCount) + { + return false; } - // Mask has to be continuous inside bytes - firstZeroInByteHit = false; - - // Count bits - for (int bitIdx = 7; bitIdx >= 0; bitIdx--) { - if (value & (1 << bitIdx)) { - if (firstZeroInByteHit) { - // Continuity not preserved - return false; - } else { - (*bits)++; + if (bits != nullptr) + { + *bits = 0; + } + + char *endPtr; + long previousValue = 255; + bool firstZeroInByteHit; + for (const std::string &byte : bytesInMask) + { + if (byte.empty()) + { + return false; + } + + // Use strtol instead of stroi to avoid exceptions + long value = std::strtol(byte.c_str(), &endPtr, 10); + + // endPtr should point to the end of the string, otherwise given + // string is not 100% number + if (*endPtr != '\0') + { + return false; + } + + // Value should be contained in byte + if (value < 0 || value > 255) + { + return false; } - } else { - firstZeroInByteHit = true; - } + + if (bits != nullptr) + { + // Mask has to be continuous between bytes + if (previousValue != 255 && value != 0) + { + return false; + } + + // Mask has to be continuous inside bytes + firstZeroInByteHit = false; + + // Count bits + for (int bitIdx = 7; bitIdx >= 0; bitIdx--) + { + if (value & (1 << bitIdx)) + { + if (firstZeroInByteHit) + { + // Continuity not preserved + return false; + } + else + { + (*bits)++; + } + } + else + { + firstZeroInByteHit = true; + } + } + } + + previousValue = value; } - } - previousValue = value; + return true; } - return true; - } - - /** - * @brief Changes IPv4 address type property (Address, Gateway) - * - * @param[in] ifaceId Id of interface whose IP should be modified - * @param[in] ipIdx index of IP in input array that should be modified - * @param[in] ipHash DBus Hash id of modified IP - * @param[in] name Name of field in JSON representation - * @param[in] newValue New value that should be written - * @param[io] asyncResp Response object that will be returned to client - * - * @return true if give IP is valid and has been sent do D-Bus, false - * otherwise - */ - void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, - const std::string &ipHash, - const std::string &name, - const std::string &newValue, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto callback = [ - asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)}, - newValue{std::move(newValue)} - ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue; - } + /** + * @brief Changes IPv4 address type property (Address, Gateway) + * + * @param[in] ifaceId Id of interface whose IP should be modified + * @param[in] ipIdx index of IP in input array that should be modified + * @param[in] ipHash DBus Hash id of modified IP + * @param[in] name Name of field in JSON representation + * @param[in] newValue New value that should be written + * @param[io] asyncResp Response object that will be returned to client + * + * @return true if give IP is valid and has been sent do D-Bus, false + * otherwise + */ + void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, + const std::string &ipHash, + const std::string &name, + const std::string &newValue, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, + name{std::move(name)}, newValue{std::move(newValue)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); + } + else + { + asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = + newValue; + } + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", name, + sdbusplus::message::variant<std::string>(newValue)); }; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.IP", name, - sdbusplus::message::variant<std::string>(newValue)); - }; - - /** - * @brief Changes IPv4 address origin property - * - * @param[in] ifaceId Id of interface whose IP should be modified - * @param[in] ipIdx index of IP in input array that should be modified - * @param[in] ipHash DBus Hash id of modified IP - * @param[in] newValue New value in Redfish format - * @param[in] newValueDbus New value in D-Bus format - * @param[io] asyncResp Response object that will be returned to client - * - * @return true if give IP is valid and has been sent do D-Bus, false - * otherwise - */ - void changeIPv4Origin(const std::string &ifaceId, int ipIdx, - const std::string &ipHash, const std::string &newValue, - const std::string &newValueDbus, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto callback = - [ asyncResp, ipIdx{std::move(ipIdx)}, - newValue{std::move(newValue)} ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin"); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = - newValue; - } + /** + * @brief Changes IPv4 address origin property + * + * @param[in] ifaceId Id of interface whose IP should be modified + * @param[in] ipIdx index of IP in input array that should be + * modified + * @param[in] ipHash DBus Hash id of modified IP + * @param[in] newValue New value in Redfish format + * @param[in] newValueDbus New value in D-Bus format + * @param[io] asyncResp Response object that will be returned to client + * + * @return true if give IP is valid and has been sent do D-Bus, false + * otherwise + */ + void changeIPv4Origin(const std::string &ifaceId, int ipIdx, + const std::string &ipHash, + const std::string &newValue, + const std::string &newValueDbus, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, + newValue{std::move(newValue)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + + "/AddressOrigin"); + } + else + { + asyncResp->res + .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = + newValue; + } + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", "Origin", + sdbusplus::message::variant<std::string>(newValueDbus)); }; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.IP", "Origin", - sdbusplus::message::variant<std::string>(newValueDbus)); - }; - - /** - * @brief Modifies SubnetMask for given IP - * - * @param[in] ifaceId Id of interface whose IP should be modified - * @param[in] ipIdx index of IP in input array that should be modified - * @param[in] ipHash DBus Hash id of modified IP - * @param[in] newValueStr Mask in dot notation as string - * @param[in] newValue Mask as PrefixLength in bitcount - * @param[io] asyncResp Response object that will be returned to client - * - * @return None - */ - void changeIPv4SubnetMaskProperty( - const std::string &ifaceId, int ipIdx, const std::string &ipHash, - const std::string &newValueStr, uint8_t &newValue, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto callback = [ - asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)} - ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = - newValueStr; - } + /** + * @brief Modifies SubnetMask for given IP + * + * @param[in] ifaceId Id of interface whose IP should be modified + * @param[in] ipIdx index of IP in input array that should be + * modified + * @param[in] ipHash DBus Hash id of modified IP + * @param[in] newValueStr Mask in dot notation as string + * @param[in] newValue Mask as PrefixLength in bitcount + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ + void changeIPv4SubnetMaskProperty( + const std::string &ifaceId, int ipIdx, const std::string &ipHash, + const std::string &newValueStr, uint8_t &newValue, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, + newValueStr{std::move(newValueStr)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); + } + else + { + asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = + newValueStr; + } + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", "PrefixLength", + sdbusplus::message::variant<uint8_t>(newValue)); }; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.IP", "PrefixLength", - sdbusplus::message::variant<uint8_t>(newValue)); - }; - - /** - * @brief Disables VLAN with given ifaceId - * - * @param[in] ifaceId Id of VLAN interface that should be disabled - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", - std::string("/xyz/openbmc_project/network/") + ifaceId, - "xyz.openbmc_project.Object.Delete", "Delete"); - }; - - /** - * @brief Sets given HostName of the machine through D-Bus - * - * @param[in] newHostname New name that HostName will be changed to - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - void setHostName(const std::string &newHostname, CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/config", - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.SystemConfiguration", "HostName", - sdbusplus::message::variant<std::string>(newHostname)); - }; - - /** - * @brief Deletes given IPv4 - * - * @param[in] ifaceId Id of interface whose IP should be deleted - * @param[in] ipIdx index of IP in input array that should be deleted - * @param[in] ipHash DBus Hash id of IP that should be deleted - * @param[io] asyncResp Response object that will be returned to client - * - * @return None - */ - void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, - unsigned int ipIdx, - const std::shared_ptr<AsyncResp> &asyncResp) { - crow::connections::systemBus->async_method_call( - [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ]( - const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; - } - }, - "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "xyz.openbmc_project.Object.Delete", "Delete"); - } - - /** - * @brief Creates IPv4 with given data - * - * @param[in] ifaceId Id of interface whose IP should be deleted - * @param[in] ipIdx index of IP in input array that should be deleted - * @param[in] ipHash DBus Hash id of IP that should be deleted - * @param[io] asyncResp Response object that will be returned to client - * - * @return None - */ - void createIPv4(const std::string &ifaceId, unsigned int ipIdx, - uint8_t subnetMask, const std::string &gateway, - const std::string &address, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto createIpHandler = [ - ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} - ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); - } + /** + * @brief Disables VLAN with given ifaceId + * + * @param[in] ifaceId Id of VLAN interface that should be disabled + * @param[in] callback Function that will be called after the operation + * + * @return None. + */ + template <typename CallbackFunc> + static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + std::string("/xyz/openbmc_project/network/") + ifaceId, + "xyz.openbmc_project.Object.Delete", "Delete"); }; - crow::connections::systemBus->async_method_call( - std::move(createIpHandler), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId, - "xyz.openbmc_project.Network.IP.Create", "IP", - "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, - gateway); - } - - /** - * @brief Translates Address Origin value from D-Bus to Redfish format and - * vice-versa - * - * @param[in] inputOrigin Input value that should be translated - * @param[in] isIPv4 True for IPv4 origins, False for IPv6 - * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse - * - * @return Empty string in case of failure, translated value otherwise - */ - std::string translateAddressOriginBetweenDBusAndRedfish( - const std::string *inputOrigin, bool isIPv4, bool isFromDBus) { - // Invalid pointer - if (inputOrigin == nullptr) { - return ""; + /** + * @brief Sets given HostName of the machine through D-Bus + * + * @param[in] newHostname New name that HostName will be changed to + * @param[in] callback Function that will be called after the operation + * + * @return None. + */ + template <typename CallbackFunc> + void setHostName(const std::string &newHostname, CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/config", + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.SystemConfiguration", "HostName", + sdbusplus::message::variant<std::string>(newHostname)); + }; + + /** + * @brief Deletes given IPv4 + * + * @param[in] ifaceId Id of interface whose IP should be deleted + * @param[in] ipIdx index of IP in input array that should be deleted + * @param[in] ipHash DBus Hash id of IP that should be deleted + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ + void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, + unsigned int ipIdx, + const std::shared_ptr<AsyncResp> &asyncResp) + { + crow::connections::systemBus->async_method_call( + [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); + } + else + { + asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; + } + }, + "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "xyz.openbmc_project.Object.Delete", "Delete"); } - static const constexpr unsigned int firstIPv4OnlyIdx = 1; - static const constexpr unsigned int firstIPv6OnlyIdx = 3; - - std::array<std::pair<const char *, const char *>, 6> translationTable{ - {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", - "IPv4LinkLocal"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", - "LinkLocal"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; - - for (unsigned int i = 0; i < translationTable.size(); i++) { - // Skip unrelated - if (isIPv4 && i >= firstIPv6OnlyIdx) break; - if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue; - - // When translating D-Bus to Redfish compare input to first element - if (isFromDBus && translationTable[i].first == *inputOrigin) - return translationTable[i].second; - - // When translating Redfish to D-Bus compare input to second element - if (!isFromDBus && translationTable[i].second == *inputOrigin) - return translationTable[i].first; + /** + * @brief Creates IPv4 with given data + * + * @param[in] ifaceId Id of interface whose IP should be deleted + * @param[in] ipIdx index of IP in input array that should be deleted + * @param[in] ipHash DBus Hash id of IP that should be deleted + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ + void createIPv4(const std::string &ifaceId, unsigned int ipIdx, + uint8_t subnetMask, const std::string &gateway, + const std::string &address, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto createIpHandler = [ipIdx{std::move(ipIdx)}, + asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); + } + }; + + crow::connections::systemBus->async_method_call( + std::move(createIpHandler), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId, + "xyz.openbmc_project.Network.IP.Create", "IP", + "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, + gateway); } - // If we are still here, that means that value has not been found - return ""; - } - - /** - * Function that retrieves all properties for given Ethernet Interface - * Object - * from EntityManager Network Manager - * @param ethifaceId a eth interface id to query on DBus - * @param callback a function that shall be called to convert Dbus output - * into JSON - */ - template <typename CallbackFunc> - void getEthernetIfaceData(const std::string ðifaceId, - CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - [ - this, ethifaceId{std::move(ethifaceId)}, callback{std::move(callback)} - ](const boost::system::error_code error_code, - const GetManagedObjectsType &resp) { - EthernetInterfaceData ethData{}; - std::vector<IPv4AddressData> ipv4Data; - ipv4Data.reserve(maxIpV4AddressesPerInterface); - - if (error_code) { - // Something wrong on DBus, the error_code is not important at - // this moment, just return success=false, and empty output. Since - // size of vector may vary depending on information from Network - // Manager, and empty output could not be treated same way as - // error. - callback(false, ethData, ipv4Data); - return; - } + /** + * @brief Translates Address Origin value from D-Bus to Redfish format and + * vice-versa + * + * @param[in] inputOrigin Input value that should be translated + * @param[in] isIPv4 True for IPv4 origins, False for IPv6 + * @param[in] isFromDBus True for DBus->Redfish conversion, false for + * reverse + * + * @return Empty string in case of failure, translated value otherwise + */ + std::string translateAddressOriginBetweenDBusAndRedfish( + const std::string *inputOrigin, bool isIPv4, bool isFromDBus) + { + // Invalid pointer + if (inputOrigin == nullptr) + { + return ""; + } - // Find interface - if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) == - resp.end()) { - // Interface has not been found - callback(false, ethData, ipv4Data); - return; - } + static const constexpr unsigned int firstIPv4OnlyIdx = 1; + static const constexpr unsigned int firstIPv6OnlyIdx = 3; + + std::array<std::pair<const char *, const char *>, 6> translationTable{ + {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", + "IPv4LinkLocal"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", + "LinkLocal"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; + + for (unsigned int i = 0; i < translationTable.size(); i++) + { + // Skip unrelated + if (isIPv4 && i >= firstIPv6OnlyIdx) + break; + if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) + continue; + + // When translating D-Bus to Redfish compare input to first element + if (isFromDBus && translationTable[i].first == *inputOrigin) + return translationTable[i].second; + + // When translating Redfish to D-Bus compare input to second element + if (!isFromDBus && translationTable[i].second == *inputOrigin) + return translationTable[i].first; + } - extractEthernetInterfaceData(ethifaceId, resp, ethData); - extractIPv4Data(ethifaceId, resp, ipv4Data); + // If we are still here, that means that value has not been found + return ""; + } - // Fix global GW - for (IPv4AddressData &ipv4 : ipv4Data) { - if ((ipv4.global) && - ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { - ipv4.gateway = ethData.defaultGateway; - } - } - - // Finally make a callback with useful data - callback(true, ethData, ipv4Data); - }, - "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }; - - /** - * Function that retrieves all Ethernet Interfaces available through Network - * Manager - * @param callback a function that shall be called to convert Dbus output into - * JSON. - */ - template <typename CallbackFunc> - void getEthernetIfaceList(CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - [ this, callback{std::move(callback)} ]( - const boost::system::error_code error_code, - GetManagedObjectsType &resp) { - // Callback requires vector<string> to retrieve all available ethernet - // interfaces - std::vector<std::string> ifaceList; - ifaceList.reserve(resp.size()); - if (error_code) { - // Something wrong on DBus, the error_code is not important at this - // moment, just return success=false, and empty output. Since size - // of vector may vary depending on information from Network Manager, - // and empty output could not be treated same way as error. - callback(false, ifaceList); - return; - } - - // Iterate over all retrieved ObjectPaths. - for (auto &objpath : resp) { - // And all interfaces available for certain ObjectPath. - for (auto &interface : objpath.second) { - // If interface is xyz.openbmc_project.Network.EthernetInterface, - // this is what we're looking for. - if (interface.first == - "xyz.openbmc_project.Network.EthernetInterface") { - // Cut out everything until last "/", ... - const std::string &ifaceId = - static_cast<const std::string &>(objpath.first); - std::size_t lastPos = ifaceId.rfind("/"); - if (lastPos != std::string::npos) { - // and put it into output vector. - ifaceList.emplace_back(ifaceId.substr(lastPos + 1)); + /** + * Function that retrieves all properties for given Ethernet Interface + * Object + * from EntityManager Network Manager + * @param ethifaceId a eth interface id to query on DBus + * @param callback a function that shall be called to convert Dbus output + * into JSON + */ + template <typename CallbackFunc> + void getEthernetIfaceData(const std::string ðifaceId, + CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + [this, ethifaceId{std::move(ethifaceId)}, + callback{std::move(callback)}]( + const boost::system::error_code error_code, + const GetManagedObjectsType &resp) { + EthernetInterfaceData ethData{}; + std::vector<IPv4AddressData> ipv4Data; + ipv4Data.reserve(maxIpV4AddressesPerInterface); + + if (error_code) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Network Manager, and empty output could + // not be treated same way as error. + callback(false, ethData, ipv4Data); + return; } - } - } - } - // Finally make a callback with useful data - callback(true, ifaceList); - }, - "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }; + + // Find interface + if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) == + resp.end()) + { + // Interface has not been found + callback(false, ethData, ipv4Data); + return; + } + + extractEthernetInterfaceData(ethifaceId, resp, ethData); + extractIPv4Data(ethifaceId, resp, ipv4Data); + + // Fix global GW + for (IPv4AddressData &ipv4 : ipv4Data) + { + if ((ipv4.global) && ((ipv4.gateway == nullptr) || + (*ipv4.gateway == "0.0.0.0"))) + { + ipv4.gateway = ethData.defaultGateway; + } + } + + // Finally make a callback with useful data + callback(true, ethData, ipv4Data); + }, + "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }; + + /** + * Function that retrieves all Ethernet Interfaces available through Network + * Manager + * @param callback a function that shall be called to convert Dbus output + * into JSON. + */ + template <typename CallbackFunc> + void getEthernetIfaceList(CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + [this, callback{std::move(callback)}]( + const boost::system::error_code error_code, + GetManagedObjectsType &resp) { + // Callback requires vector<string> to retrieve all available + // ethernet interfaces + std::vector<std::string> ifaceList; + ifaceList.reserve(resp.size()); + if (error_code) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Network Manager, and empty output could + // not be treated same way as error. + callback(false, ifaceList); + return; + } + + // Iterate over all retrieved ObjectPaths. + for (auto &objpath : resp) + { + // And all interfaces available for certain ObjectPath. + for (auto &interface : objpath.second) + { + // If interface is + // xyz.openbmc_project.Network.EthernetInterface, this + // is what we're looking for. + if (interface.first == + "xyz.openbmc_project.Network.EthernetInterface") + { + // Cut out everything until last "/", ... + const std::string &ifaceId = + static_cast<const std::string &>(objpath.first); + std::size_t lastPos = ifaceId.rfind("/"); + if (lastPos != std::string::npos) + { + // and put it into output vector. + ifaceList.emplace_back( + ifaceId.substr(lastPos + 1)); + } + } + } + } + // Finally make a callback with useful data + callback(true, ifaceList); + }, + "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }; }; /** * EthernetCollection derived class for delivering Ethernet Collection Schema */ -class EthernetCollection : public Node { - public: - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - EthernetCollection(CrowApp &app) - : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { - Node::json["@odata.type"] = - "#EthernetInterfaceCollection.EthernetInterfaceCollection"; - Node::json["@odata.context"] = - "/redfish/v1/" - "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; - Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; - Node::json["Name"] = "Ethernet Network Interface Collection"; - Node::json["Description"] = - "Collection of EthernetInterfaces for this Manager"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for - // any Manager, not only hardcoded 'openbmc'. - std::string managerId = "openbmc"; - - // get eth interface list, and call the below callback for JSON preparation - ethernetProvider.getEthernetIfaceList([&, managerId{std::move(managerId)} ]( - const bool &success, const std::vector<std::string> &iface_list) { - if (success) { - nlohmann::json ifaceArray = nlohmann::json::array(); - for (const std::string &ifaceItem : iface_list) { - ifaceArray.push_back( - {{"@odata.id", "/redfish/v1/Managers/" + managerId + - "/EthernetInterfaces/" + ifaceItem}}); - } - Node::json["Members"] = ifaceArray; - Node::json["Members@odata.count"] = ifaceArray.size(); +class EthernetCollection : public Node +{ + public: + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + EthernetCollection(CrowApp &app) : + Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") + { + Node::json["@odata.type"] = + "#EthernetInterfaceCollection.EthernetInterfaceCollection"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; Node::json["@odata.id"] = - "/redfish/v1/Managers/" + managerId + "/EthernetInterfaces"; - res.jsonValue = Node::json; - } else { - // No success, best what we can do is return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } - - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; + "/redfish/v1/Managers/openbmc/EthernetInterfaces"; + Node::json["Name"] = "Ethernet Network Interface Collection"; + Node::json["Description"] = + "Collection of EthernetInterfaces for this Manager"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } + + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces + // for any Manager, not only hardcoded 'openbmc'. + std::string managerId = "openbmc"; + + // get eth interface list, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceList( + [&, managerId{std::move(managerId)}]( + const bool &success, + const std::vector<std::string> &iface_list) { + if (success) + { + nlohmann::json ifaceArray = nlohmann::json::array(); + for (const std::string &ifaceItem : iface_list) + { + ifaceArray.push_back( + {{"@odata.id", "/redfish/v1/Managers/" + managerId + + "/EthernetInterfaces/" + + ifaceItem}}); + } + Node::json["Members"] = ifaceArray; + Node::json["Members@odata.count"] = ifaceArray.size(); + Node::json["@odata.id"] = "/redfish/v1/Managers/" + + managerId + "/EthernetInterfaces"; + res.jsonValue = Node::json; + } + else + { + // No success, best what we can do is return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + } + res.end(); + }); + } + + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; }; /** * EthernetInterface derived class for delivering Ethernet Schema */ -class EthernetInterface : public Node { - public: - /* - * Default Constructor - */ - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - EthernetInterface(CrowApp &app) - : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", - std::string()) { - Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; - Node::json["Name"] = "Manager Ethernet Interface"; - Node::json["Description"] = "Management Network Interface"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - // TODO(kkowalsk) Find a suitable class/namespace for this - static void handleVlanPatch(const std::string &ifaceId, - const nlohmann::json &input, - const EthernetInterfaceData ð_data, - const std::string &pathPrefix, - const std::shared_ptr<AsyncResp> &asyncResp) { - if (!input.is_object()) { - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix); - return; +class EthernetInterface : public Node +{ + public: + /* + * Default Constructor + */ + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + EthernetInterface(CrowApp &app) : + Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", + std::string()) + { + Node::json["@odata.type"] = + "#EthernetInterface.v1_2_0.EthernetInterface"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; + Node::json["Name"] = "Manager Ethernet Interface"; + Node::json["Description"] = "Management Network Interface"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix; - nlohmann::json ¶msJson = - (pathPrefix == "/") - ? asyncResp->res.jsonValue - : asyncResp->res.jsonValue[nlohmann::json_pointer<nlohmann::json>( - pathPrefix)]; - bool inputVlanEnabled; - uint64_t inputVlanId; - - json_util::Result inputVlanEnabledState = json_util::getBool( - "VLANEnable", input, inputVlanEnabled, - static_cast<int>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable")); - json_util::Result inputVlanIdState = json_util::getUnsigned( - "VLANId", input, inputVlanId, - static_cast<int>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, std::string(pathStart + "/VLANId")); - bool inputInvalid = false; - - // Do not proceed if fields in VLAN object were of wrong type - if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || - inputVlanIdState == json_util::Result::WRONG_TYPE) { - return; - } + // TODO(kkowalsk) Find a suitable class/namespace for this + static void handleVlanPatch(const std::string &ifaceId, + const nlohmann::json &input, + const EthernetInterfaceData ð_data, + const std::string &pathPrefix, + const std::shared_ptr<AsyncResp> &asyncResp) + { + if (!input.is_object()) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input.dump(), "VLAN"), + pathPrefix); + return; + } - // Verify input - if (eth_data.vlanId == nullptr) { - // This interface is not a VLAN. Cannot do anything with it - // TODO(kkowalsk) Change this message - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::propertyMissing("VLANEnable"), - pathPrefix); - - inputInvalid = true; - } else { - // Load actual data into field values if they were not provided - if (inputVlanEnabledState == json_util::Result::NOT_EXIST) { - inputVlanEnabled = true; - } - - if (inputVlanIdState == json_util::Result::NOT_EXIST) { - inputVlanId = *eth_data.vlanId; - } - } + const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix; + nlohmann::json ¶msJson = + (pathPrefix == "/") + ? asyncResp->res.jsonValue + : asyncResp->res + .jsonValue[nlohmann::json_pointer<nlohmann::json>( + pathPrefix)]; + bool inputVlanEnabled; + uint64_t inputVlanId; + + json_util::Result inputVlanEnabledState = json_util::getBool( + "VLANEnable", input, inputVlanEnabled, + static_cast<int>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable")); + json_util::Result inputVlanIdState = json_util::getUnsigned( + "VLANId", input, inputVlanId, + static_cast<int>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, std::string(pathStart + "/VLANId")); + bool inputInvalid = false; + + // Do not proceed if fields in VLAN object were of wrong type + if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || + inputVlanIdState == json_util::Result::WRONG_TYPE) + { + return; + } - // Do not proceed if input has not been valid - if (inputInvalid) { - return; - } + // Verify input + if (eth_data.vlanId == nullptr) + { + // This interface is not a VLAN. Cannot do anything with it + // TODO(kkowalsk) Change this message + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::propertyMissing("VLANEnable"), + pathPrefix); - // VLAN is configured on the interface - if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) { - // Change VLAN Id - paramsJson["VLANId"] = inputVlanId; - OnDemandEthernetProvider::changeVlanId( - ifaceId, static_cast<uint32_t>(inputVlanId), - [&, asyncResp, pathPrefx{std::move(pathPrefix)} ]( - const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), pathPrefix); - } else { - paramsJson["VLANEnable"] = true; + inputInvalid = true; + } + else + { + // Load actual data into field values if they were not provided + if (inputVlanEnabledState == json_util::Result::NOT_EXIST) + { + inputVlanEnabled = true; } - }); - } else if (inputVlanEnabled == false) { - // Disable VLAN - OnDemandEthernetProvider::disableVlan( - ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ]( - const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), pathPrefix); - } else { - paramsJson["VLANEnable"] = false; + + if (inputVlanIdState == json_util::Result::NOT_EXIST) + { + inputVlanId = *eth_data.vlanId; } - }); - } - } + } - private: - void handleHostnamePatch(const nlohmann::json &input, - const EthernetInterfaceData ð_data, - const std::shared_ptr<AsyncResp> &asyncResp) { - if (input.is_string()) { - std::string newHostname = input.get<std::string>(); - - if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) { - // Change hostname - ethernetProvider.setHostName( - newHostname, - [asyncResp, newHostname](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), - "/HostName"); - } else { - asyncResp->res.jsonValue["HostName"] = newHostname; - } - }); - } - } else { - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input.dump(), "HostName"), - "/HostName"); - } - } - - void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input, - const std::vector<IPv4AddressData> &ipv4_data, - const std::shared_ptr<AsyncResp> &asyncResp) { - if (!input.is_array()) { - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), - "/IPv4Addresses"); - return; - } + // Do not proceed if input has not been valid + if (inputInvalid) + { + return; + } - // According to Redfish PATCH definition, size must be at least equal - if (input.size() < ipv4_data.size()) { - // TODO(kkowalsk) This should be a message indicating that not enough - // data has been provided - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), "/IPv4Addresses"); - return; + // VLAN is configured on the interface + if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) + { + // Change VLAN Id + paramsJson["VLANId"] = inputVlanId; + OnDemandEthernetProvider::changeVlanId( + ifaceId, static_cast<uint32_t>(inputVlanId), + [&, asyncResp, pathPrefx{std::move(pathPrefix)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::internalError(), + pathPrefix); + } + else + { + paramsJson["VLANEnable"] = true; + } + }); + } + else if (inputVlanEnabled == false) + { + // Disable VLAN + OnDemandEthernetProvider::disableVlan( + ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::internalError(), + pathPrefix); + } + else + { + paramsJson["VLANEnable"] = false; + } + }); + } } - json_util::Result addressFieldState; - json_util::Result subnetMaskFieldState; - json_util::Result addressOriginFieldState; - json_util::Result gatewayFieldState; - const std::string *addressFieldValue; - const std::string *subnetMaskFieldValue; - const std::string *addressOriginFieldValue = nullptr; - const std::string *gatewayFieldValue; - uint8_t subnetMaskAsPrefixLength; - std::string addressOriginInDBusFormat; - - bool errorDetected = false; - for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) { - // Check that entry is not of some unexpected type - if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) { - // Invalid object type - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input[entryIdx].dump(), - "IPv4Address"), - "/IPv4Addresses/" + std::to_string(entryIdx)); - - continue; - } - - // Try to load fields - addressFieldState = json_util::getString( - "Address", input[entryIdx], addressFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); - subnetMaskFieldState = json_util::getString( - "SubnetMask", input[entryIdx], subnetMaskFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); - addressOriginFieldState = json_util::getString( - "AddressOrigin", input[entryIdx], addressOriginFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); - gatewayFieldState = json_util::getString( - "Gateway", input[entryIdx], gatewayFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); - - if (addressFieldState == json_util::Result::WRONG_TYPE || - subnetMaskFieldState == json_util::Result::WRONG_TYPE || - addressOriginFieldState == json_util::Result::WRONG_TYPE || - gatewayFieldState == json_util::Result::WRONG_TYPE) { - return; - } - - if (addressFieldState == json_util::Result::SUCCESS && - !ethernetProvider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueFormatError(*addressFieldValue, "Address"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); - } - - if (subnetMaskFieldState == json_util::Result::SUCCESS && - !ethernetProvider.ipv4VerifyIpAndGetBitcount( - *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueFormatError(*subnetMaskFieldValue, - "SubnetMask"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); - } - - // get Address origin in proper format - addressOriginInDBusFormat = - ethernetProvider.translateAddressOriginBetweenDBusAndRedfish( - addressOriginFieldValue, true, false); - - if (addressOriginFieldState == json_util::Result::SUCCESS && - addressOriginInDBusFormat.empty()) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueNotInList(*addressOriginFieldValue, - "AddressOrigin"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); - } - - if (gatewayFieldState == json_util::Result::SUCCESS && - !ethernetProvider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); - } - - // If any error occured do not proceed with current entry, but do not - // end loop - if (errorDetected) { - errorDetected = false; - continue; - } - - if (entryIdx >= ipv4_data.size()) { - asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = input[entryIdx]; - - // Verify that all field were provided - if (addressFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::propertyMissing("Address"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); + private: + void handleHostnamePatch(const nlohmann::json &input, + const EthernetInterfaceData ð_data, + const std::shared_ptr<AsyncResp> &asyncResp) + { + if (input.is_string()) + { + std::string newHostname = input.get<std::string>(); + + if (eth_data.hostname == nullptr || + newHostname != *eth_data.hostname) + { + // Change hostname + ethernetProvider.setHostName( + newHostname, [asyncResp, newHostname]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::internalError(), "/HostName"); + } + else + { + asyncResp->res.jsonValue["HostName"] = newHostname; + } + }); + } } - - if (subnetMaskFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::propertyMissing("SubnetMask"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); + else + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input.dump(), "HostName"), + "/HostName"); } + } - if (addressOriginFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyMissing("AddressOrigin"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); + void handleIPv4Patch(const std::string &ifaceId, + const nlohmann::json &input, + const std::vector<IPv4AddressData> &ipv4_data, + const std::shared_ptr<AsyncResp> &asyncResp) + { + if (!input.is_array()) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), + "/IPv4Addresses"); + return; } - if (gatewayFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::propertyMissing("Gateway"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); + // According to Redfish PATCH definition, size must be at least equal + if (input.size() < ipv4_data.size()) + { + // TODO(kkowalsk) This should be a message indicating that not + // enough data has been provided + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::internalError(), + "/IPv4Addresses"); + return; } - // If any error occured do not proceed with current entry, but do not - // end loop - if (errorDetected) { - errorDetected = false; - continue; - } + json_util::Result addressFieldState; + json_util::Result subnetMaskFieldState; + json_util::Result addressOriginFieldState; + json_util::Result gatewayFieldState; + const std::string *addressFieldValue; + const std::string *subnetMaskFieldValue; + const std::string *addressOriginFieldValue = nullptr; + const std::string *gatewayFieldValue; + uint8_t subnetMaskAsPrefixLength; + std::string addressOriginInDBusFormat; + + bool errorDetected = false; + for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) + { + // Check that entry is not of some unexpected type + if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) + { + // Invalid object type + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input[entryIdx].dump(), + "IPv4Address"), + "/IPv4Addresses/" + std::to_string(entryIdx)); + + continue; + } - // Create IPv4 with provided data - ethernetProvider.createIPv4(ifaceId, entryIdx, subnetMaskAsPrefixLength, - *gatewayFieldValue, *addressFieldValue, - asyncResp); - } else { - // Existing object that should be modified/deleted/remain unchanged - if (input[entryIdx].is_null()) { - // Object should be deleted - ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, entryIdx, - asyncResp); - } else if (input[entryIdx].is_object()) { - if (input[entryIdx].size() == 0) { - // Object shall remain unchanged - continue; - } - - // Apply changes - if (addressFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].address != nullptr && - *ipv4_data[entryIdx].address != *addressFieldValue) { - ethernetProvider.changeIPv4AddressProperty( - ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address", - *addressFieldValue, asyncResp); - } - - if (subnetMaskFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) { - ethernetProvider.changeIPv4SubnetMaskProperty( - ifaceId, entryIdx, ipv4_data[entryIdx].id, - *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp); - } - - if (addressOriginFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].origin != *addressFieldValue) { - ethernetProvider.changeIPv4Origin( - ifaceId, entryIdx, ipv4_data[entryIdx].id, - *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp); - } - - if (gatewayFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].gateway != nullptr && - *ipv4_data[entryIdx].gateway != *gatewayFieldValue) { - ethernetProvider.changeIPv4AddressProperty( - ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway", - *gatewayFieldValue, asyncResp); - } + // Try to load fields + addressFieldState = json_util::getString( + "Address", input[entryIdx], addressFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); + subnetMaskFieldState = json_util::getString( + "SubnetMask", input[entryIdx], subnetMaskFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); + addressOriginFieldState = json_util::getString( + "AddressOrigin", input[entryIdx], addressOriginFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/AddressOrigin"); + gatewayFieldState = json_util::getString( + "Gateway", input[entryIdx], gatewayFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); + + if (addressFieldState == json_util::Result::WRONG_TYPE || + subnetMaskFieldState == json_util::Result::WRONG_TYPE || + addressOriginFieldState == json_util::Result::WRONG_TYPE || + gatewayFieldState == json_util::Result::WRONG_TYPE) + { + return; + } + + if (addressFieldState == json_util::Result::SUCCESS && + !ethernetProvider.ipv4VerifyIpAndGetBitcount( + *addressFieldValue)) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueFormatError(*addressFieldValue, + "Address"), + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); + } + + if (subnetMaskFieldState == json_util::Result::SUCCESS && + !ethernetProvider.ipv4VerifyIpAndGetBitcount( + *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueFormatError(*subnetMaskFieldValue, + "SubnetMask"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/SubnetMask"); + } + + // get Address origin in proper format + addressOriginInDBusFormat = + ethernetProvider.translateAddressOriginBetweenDBusAndRedfish( + addressOriginFieldValue, true, false); + + if (addressOriginFieldState == json_util::Result::SUCCESS && + addressOriginInDBusFormat.empty()) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueNotInList(*addressOriginFieldValue, + "AddressOrigin"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/AddressOrigin"); + } + + if (gatewayFieldState == json_util::Result::SUCCESS && + !ethernetProvider.ipv4VerifyIpAndGetBitcount( + *gatewayFieldValue)) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueFormatError(*gatewayFieldValue, + "Gateway"), + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); + } + + // If any error occured do not proceed with current entry, but do + // not end loop + if (errorDetected) + { + errorDetected = false; + continue; + } + + if (entryIdx >= ipv4_data.size()) + { + asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = + input[entryIdx]; + + // Verify that all field were provided + if (addressFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("Address"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/Address"); + } + + if (subnetMaskFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("SubnetMask"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/SubnetMask"); + } + + if (addressOriginFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("AddressOrigin"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/AddressOrigin"); + } + + if (gatewayFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("Gateway"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/Gateway"); + } + + // If any error occured do not proceed with current entry, but + // do not end loop + if (errorDetected) + { + errorDetected = false; + continue; + } + + // Create IPv4 with provided data + ethernetProvider.createIPv4( + ifaceId, entryIdx, subnetMaskAsPrefixLength, + *gatewayFieldValue, *addressFieldValue, asyncResp); + } + else + { + // Existing object that should be modified/deleted/remain + // unchanged + if (input[entryIdx].is_null()) + { + // Object should be deleted + ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, + entryIdx, asyncResp); + } + else if (input[entryIdx].is_object()) + { + if (input[entryIdx].size() == 0) + { + // Object shall remain unchanged + continue; + } + + // Apply changes + if (addressFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].address != nullptr && + *ipv4_data[entryIdx].address != *addressFieldValue) + { + ethernetProvider.changeIPv4AddressProperty( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + "Address", *addressFieldValue, asyncResp); + } + + if (subnetMaskFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) + { + ethernetProvider.changeIPv4SubnetMaskProperty( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + *subnetMaskFieldValue, subnetMaskAsPrefixLength, + asyncResp); + } + + if (addressOriginFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].origin != *addressFieldValue) + { + ethernetProvider.changeIPv4Origin( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + *addressOriginFieldValue, addressOriginInDBusFormat, + asyncResp); + } + + if (gatewayFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].gateway != nullptr && + *ipv4_data[entryIdx].gateway != *gatewayFieldValue) + { + ethernetProvider.changeIPv4AddressProperty( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + "Gateway", *gatewayFieldValue, asyncResp); + } + } + } } - } - } - } - - nlohmann::json parseInterfaceData( - const std::string &ifaceId, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - // Copy JSON object to avoid race condition - nlohmann::json jsonResponse(Node::json); - - // Fill out obvious data... - jsonResponse["Id"] = ifaceId; - jsonResponse["@odata.id"] = - "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId; - - // ... then the one from DBus, regarding eth iface... - if (eth_data.speed != nullptr) jsonResponse["SpeedMbps"] = *eth_data.speed; - - if (eth_data.macAddress != nullptr) - jsonResponse["MACAddress"] = *eth_data.macAddress; - - if (eth_data.hostname != nullptr) - jsonResponse["HostName"] = *eth_data.hostname; - - if (eth_data.vlanId != nullptr) { - nlohmann::json &vlanObj = jsonResponse["VLAN"]; - vlanObj["VLANEnable"] = true; - vlanObj["VLANId"] = *eth_data.vlanId; - } else { - nlohmann::json &vlanObj = jsonResponse["VLANs"]; - vlanObj["@odata.id"] = - "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId + - "/VLANs"; } - // ... at last, check if there are IPv4 data and prepare appropriate - // collection - if (ipv4_data.size() > 0) { - nlohmann::json ipv4Array = nlohmann::json::array(); - for (auto &ipv4Config : ipv4_data) { - nlohmann::json jsonIpv4; - if (ipv4Config.address != nullptr) { - jsonIpv4["Address"] = *ipv4Config.address; - if (ipv4Config.gateway != nullptr) - jsonIpv4["Gateway"] = *ipv4Config.gateway; - - jsonIpv4["AddressOrigin"] = ipv4Config.origin; - jsonIpv4["SubnetMask"] = ipv4Config.netmask; - - ipv4Array.push_back(std::move(jsonIpv4)); + nlohmann::json + parseInterfaceData(const std::string &ifaceId, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) + { + // Copy JSON object to avoid race condition + nlohmann::json jsonResponse(Node::json); + + // Fill out obvious data... + jsonResponse["Id"] = ifaceId; + jsonResponse["@odata.id"] = + "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId; + + // ... then the one from DBus, regarding eth iface... + if (eth_data.speed != nullptr) + jsonResponse["SpeedMbps"] = *eth_data.speed; + + if (eth_data.macAddress != nullptr) + jsonResponse["MACAddress"] = *eth_data.macAddress; + + if (eth_data.hostname != nullptr) + jsonResponse["HostName"] = *eth_data.hostname; + + if (eth_data.vlanId != nullptr) + { + nlohmann::json &vlanObj = jsonResponse["VLAN"]; + vlanObj["VLANEnable"] = true; + vlanObj["VLANId"] = *eth_data.vlanId; + } + else + { + nlohmann::json &vlanObj = jsonResponse["VLANs"]; + vlanObj["@odata.id"] = + "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId + + "/VLANs"; } - } - jsonResponse["IPv4Addresses"] = std::move(ipv4Array); - } - return jsonResponse; - } - - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call (two params) to get - // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; - } + // ... at last, check if there are IPv4 data and prepare appropriate + // collection + if (ipv4_data.size() > 0) + { + nlohmann::json ipv4Array = nlohmann::json::array(); + for (auto &ipv4Config : ipv4_data) + { + nlohmann::json jsonIpv4; + if (ipv4Config.address != nullptr) + { + jsonIpv4["Address"] = *ipv4Config.address; + if (ipv4Config.gateway != nullptr) + jsonIpv4["Gateway"] = *ipv4Config.gateway; + + jsonIpv4["AddressOrigin"] = ipv4Config.origin; + jsonIpv4["SubnetMask"] = ipv4Config.netmask; + + ipv4Array.push_back(std::move(jsonIpv4)); + } + } + jsonResponse["IPv4Addresses"] = std::move(ipv4Array); + } - const std::string &ifaceId = params[0]; - - // get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, - [&, ifaceId](const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (success) { - res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data); - } else { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("EthernetInterface", ifaceId)); - res.result(boost::beast::http::status::not_found); - } - res.end(); - }); - } - - void doPatch(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call (two params) to get - // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + return jsonResponse; } - const std::string &ifaceId = params[0]; - - nlohmann::json patchReq; + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call (two params) to get + // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - if (!json_util::processJsonFromRequest(res, req, patchReq)) { - return; + const std::string &ifaceId = params[0]; + + // get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, + [&, ifaceId](const bool &success, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (success) + { + res.jsonValue = + parseInterfaceData(ifaceId, eth_data, ipv4_data); + } + else + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "EthernetInterface", ifaceId)); + res.result(boost::beast::http::status::not_found); + } + res.end(); + }); } - // get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, ifaceId, patchReq = std::move(patchReq) ]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (!success) { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); - res.result(boost::beast::http::status::not_found); + void doPatch(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call (two params) to get + // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); res.end(); + return; + } + + const std::string &ifaceId = params[0]; + nlohmann::json patchReq; + + if (!json_util::processJsonFromRequest(res, req, patchReq)) + { return; - } - - res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data); - - std::shared_ptr<AsyncResp> asyncResp = - std::make_shared<AsyncResp>(res); - - for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); - ++propertyIt) { - if (propertyIt.key() == "VLAN") { - handleVlanPatch(ifaceId, propertyIt.value(), eth_data, "/VLAN", - asyncResp); - } else if (propertyIt.key() == "HostName") { - handleHostnamePatch(propertyIt.value(), eth_data, asyncResp); - } else if (propertyIt.key() == "IPv4Addresses") { - handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data, - asyncResp); - } else if (propertyIt.key() == "IPv6Addresses") { - // TODO(kkowalsk) IPv6 Not supported on D-Bus yet - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyNotWritable(propertyIt.key())); - } else { - auto fieldInJsonIt = res.jsonValue.find(propertyIt.key()); - - if (fieldInJsonIt == res.jsonValue.end()) { - // Field not in scope of defined fields - messages::addMessageToJsonRoot( - res.jsonValue, messages::propertyUnknown(propertyIt.key())); - } else if (*fieldInJsonIt != *propertyIt) { - // User attempted to modify non-writable field - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyNotWritable(propertyIt.key())); - } - } - } - }); - } + } + + // get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, + [&, ifaceId, patchReq = std::move(patchReq)]( + const bool &success, const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (!success) + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + res.end(); + + return; + } + + res.jsonValue = + parseInterfaceData(ifaceId, eth_data, ipv4_data); + + std::shared_ptr<AsyncResp> asyncResp = + std::make_shared<AsyncResp>(res); + + for (auto propertyIt = patchReq.begin(); + propertyIt != patchReq.end(); ++propertyIt) + { + if (propertyIt.key() == "VLAN") + { + handleVlanPatch(ifaceId, propertyIt.value(), eth_data, + "/VLAN", asyncResp); + } + else if (propertyIt.key() == "HostName") + { + handleHostnamePatch(propertyIt.value(), eth_data, + asyncResp); + } + else if (propertyIt.key() == "IPv4Addresses") + { + handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data, + asyncResp); + } + else if (propertyIt.key() == "IPv6Addresses") + { + // TODO(kkowalsk) IPv6 Not supported on D-Bus yet + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyNotWritable(propertyIt.key())); + } + else + { + auto fieldInJsonIt = + res.jsonValue.find(propertyIt.key()); + + if (fieldInJsonIt == res.jsonValue.end()) + { + // Field not in scope of defined fields + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyUnknown(propertyIt.key())); + } + else if (*fieldInJsonIt != *propertyIt) + { + // User attempted to modify non-writable field + messages::addMessageToJsonRoot( + res.jsonValue, messages::propertyNotWritable( + propertyIt.key())); + } + } + } + }); + } - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; }; class VlanNetworkInterfaceCollection; @@ -1350,422 +1585,499 @@ class VlanNetworkInterfaceCollection; /** * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema */ -class VlanNetworkInterface : public Node { - public: - /* - * Default Constructor - */ - template <typename CrowApp> - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - VlanNetworkInterface(CrowApp &app) - : Node( +class VlanNetworkInterface : public Node +{ + public: + /* + * Default Constructor + */ + template <typename CrowApp> + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + VlanNetworkInterface(CrowApp &app) : + Node( app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>", - std::string(), std::string()) { - Node::json["@odata.type"] = - "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; - Node::json["Name"] = "VLAN Network Interface"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - nlohmann::json parseInterfaceData( - const std::string &parent_ifaceId, const std::string &ifaceId, - const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - // Copy JSON object to avoid race condition - nlohmann::json jsonResponse(Node::json); - - // Fill out obvious data... - jsonResponse["Id"] = ifaceId; - jsonResponse["@odata.id"] = - "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_ifaceId + - "/VLANs/" + ifaceId; - - jsonResponse["VLANEnable"] = true; - jsonResponse["VLANId"] = *eth_data.vlanId; - - return jsonResponse; - } - - bool verifyNames(crow::Response &res, const std::string &parent, - const std::string &iface) { - if (!boost::starts_with(iface, parent + "_")) { - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", iface)); - res.result(boost::beast::http::status::not_found); - res.end(); - - return false; - } else { - return true; - } - } - - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call (two params) to get - // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 2) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + std::string(), std::string()) + { + Node::json["@odata.type"] = + "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; + Node::json["Name"] = "VLAN Network Interface"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - const std::string &parentIfaceId = params[0]; - const std::string &ifaceId = params[1]; + private: + nlohmann::json + parseInterfaceData(const std::string &parent_ifaceId, + const std::string &ifaceId, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) + { + // Copy JSON object to avoid race condition + nlohmann::json jsonResponse(Node::json); + + // Fill out obvious data... + jsonResponse["Id"] = ifaceId; + jsonResponse["@odata.id"] = + "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + + parent_ifaceId + "/VLANs/" + ifaceId; - if (!verifyNames(res, parentIfaceId, ifaceId)) { - return; + jsonResponse["VLANEnable"] = true; + jsonResponse["VLANId"] = *eth_data.vlanId; + + return jsonResponse; } - // Get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, parentIfaceId, ifaceId]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (success && eth_data.vlanId != nullptr) { - res.jsonValue = - parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data); - } else { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, and - // other errors + bool verifyNames(crow::Response &res, const std::string &parent, + const std::string &iface) + { + if (!boost::starts_with(iface, parent + "_")) + { messages::addMessageToErrorJson( res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); + messages::resourceNotFound("VLAN Network Interface", iface)); res.result(boost::beast::http::status::not_found); - } - res.end(); - }); - } - - void doPatch(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 2) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + res.end(); + + return false; + } + else + { + return true; + } } - const std::string &parentIfaceId = params[0]; - const std::string &ifaceId = params[1]; + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call (two params) to get + // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 2) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - if (!verifyNames(res, parentIfaceId, ifaceId)) { - return; - } + const std::string &parentIfaceId = params[0]; + const std::string &ifaceId = params[1]; - nlohmann::json patchReq; + if (!verifyNames(res, parentIfaceId, ifaceId)) + { + return; + } - if (!json_util::processJsonFromRequest(res, req, patchReq)) { - return; + // Get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, [&, parentIfaceId, + ifaceId](const bool &success, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (success && eth_data.vlanId != nullptr) + { + res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, + eth_data, ipv4_data); + } + else + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + } + res.end(); + }); } - // Get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq) ]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (!success) { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, - // and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); - res.result(boost::beast::http::status::not_found); + void doPatch(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 2) + { + res.result(boost::beast::http::status::internal_server_error); res.end(); + return; + } + const std::string &parentIfaceId = params[0]; + const std::string &ifaceId = params[1]; + + if (!verifyNames(res, parentIfaceId, ifaceId)) + { return; - } - - res.jsonValue = - parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data); - - std::shared_ptr<AsyncResp> asyncResp = - std::make_shared<AsyncResp>(res); - - for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); - ++propertyIt) { - if (propertyIt.key() != "VLANEnable" && - propertyIt.key() != "VLANId") { - auto fieldInJsonIt = res.jsonValue.find(propertyIt.key()); - - if (fieldInJsonIt == res.jsonValue.end()) { - // Field not in scope of defined fields - messages::addMessageToJsonRoot( - res.jsonValue, messages::propertyUnknown(propertyIt.key())); - } else if (*fieldInJsonIt != *propertyIt) { - // User attempted to modify non-writable field - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyNotWritable(propertyIt.key())); - } - } - } - - EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, "/", - asyncResp); - }); - } - - void doDelete(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 2) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + } + + nlohmann::json patchReq; + + if (!json_util::processJsonFromRequest(res, req, patchReq)) + { + return; + } + + // Get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, + [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)]( + const bool &success, const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (!success) + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + res.end(); + + return; + } + + res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, + eth_data, ipv4_data); + + std::shared_ptr<AsyncResp> asyncResp = + std::make_shared<AsyncResp>(res); + + for (auto propertyIt = patchReq.begin(); + propertyIt != patchReq.end(); ++propertyIt) + { + if (propertyIt.key() != "VLANEnable" && + propertyIt.key() != "VLANId") + { + auto fieldInJsonIt = + res.jsonValue.find(propertyIt.key()); + + if (fieldInJsonIt == res.jsonValue.end()) + { + // Field not in scope of defined fields + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyUnknown(propertyIt.key())); + } + else if (*fieldInJsonIt != *propertyIt) + { + // User attempted to modify non-writable field + messages::addMessageToJsonRoot( + res.jsonValue, messages::propertyNotWritable( + propertyIt.key())); + } + } + } + + EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, + "/", asyncResp); + }); } - const std::string &parentIfaceId = params[0]; - const std::string &ifaceId = params[1]; + void doDelete(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 2) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + + const std::string &parentIfaceId = params[0]; + const std::string &ifaceId = params[1]; + + if (!verifyNames(res, parentIfaceId, ifaceId)) + { + return; + } - if (!verifyNames(res, parentIfaceId, ifaceId)) { - return; + // Get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, [&, parentIfaceId, + ifaceId](const bool &success, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (success && eth_data.vlanId != nullptr) + { + res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, + eth_data, ipv4_data); + + // Disable VLAN + OnDemandEthernetProvider::disableVlan( + ifaceId, [&](const boost::system::error_code ec) { + if (ec) + { + res.jsonValue = nlohmann::json::object(); + messages::addMessageToErrorJson( + res.jsonValue, messages::internalError()); + res.result(boost::beast::http::status:: + internal_server_error); + } + res.end(); + }); + } + else + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + res.end(); + } + }); } - // Get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, parentIfaceId, ifaceId]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (success && eth_data.vlanId != nullptr) { - res.jsonValue = - parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data); + /** + * This allows VlanNetworkInterfaceCollection to reuse this class' doGet + * method, to maintain consistency of returned data, as Collection's doPost + * should return data for created member which should match member's doGet + * result in 100%. + */ + friend VlanNetworkInterfaceCollection; - // Disable VLAN - OnDemandEthernetProvider::disableVlan( - ifaceId, [&](const boost::system::error_code ec) { - if (ec) { - res.jsonValue = nlohmann::json::object(); - messages::addMessageToErrorJson(res.jsonValue, - messages::internalError()); - res.result( - boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } else { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, - // and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); - res.result(boost::beast::http::status::not_found); - res.end(); - } - }); - } - - /** - * This allows VlanNetworkInterfaceCollection to reuse this class' doGet - * method, to maintain consistency of returned data, as Collection's doPost - * should return data for created member which should match member's doGet - * result in 100%. - */ - friend VlanNetworkInterfaceCollection; - - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; }; /** * VlanNetworkInterfaceCollection derived class for delivering * VLANNetworkInterface Collection Schema */ -class VlanNetworkInterfaceCollection : public Node { - public: - template <typename CrowApp> - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - VlanNetworkInterfaceCollection(CrowApp &app) - : Node(app, +class VlanNetworkInterfaceCollection : public Node +{ + public: + template <typename CrowApp> + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + VlanNetworkInterfaceCollection(CrowApp &app) : + Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/", std::string()), - memberVlan(app) { - Node::json["@odata.type"] = - "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata" - "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; - Node::json["Name"] = "VLAN Network Interface Collection"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 1) { - // This means there is a problem with the router - res.result(boost::beast::http::status::internal_server_error); - res.end(); - - return; + memberVlan(app) + { + Node::json["@odata.type"] = + "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata" + "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; + Node::json["Name"] = "VLAN Network Interface Collection"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for - // any Manager, not only hardcoded 'openbmc'. - std::string managerId = "openbmc"; - std::string rootInterfaceName = params[0]; - - // get eth interface list, and call the below callback for JSON preparation - ethernetProvider.getEthernetIfaceList([ - &, managerId{std::move(managerId)}, - rootInterfaceName{std::move(rootInterfaceName)} - ](const bool &success, const std::vector<std::string> &iface_list) { - if (success) { - bool rootInterfaceFound = false; - nlohmann::json ifaceArray = nlohmann::json::array(); - - for (const std::string &ifaceItem : iface_list) { - if (ifaceItem == rootInterfaceName) { - rootInterfaceFound = true; - } else if (boost::starts_with(ifaceItem, rootInterfaceName + "_")) { - ifaceArray.push_back( - {{"@odata.id", "/redfish/v1/Managers/" + managerId + - "/EthernetInterfaces/" + rootInterfaceName + - "/VLANs/" + ifaceItem}}); - } - } + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 1) + { + // This means there is a problem with the router + res.result(boost::beast::http::status::internal_server_error); + res.end(); - if (rootInterfaceFound) { - Node::json["Members"] = ifaceArray; - Node::json["Members@odata.count"] = ifaceArray.size(); - Node::json["@odata.id"] = "/redfish/v1/Managers/" + managerId + - "/EthernetInterfaces/" + rootInterfaceName + - "/VLANs"; - res.jsonValue = Node::json; - } else { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("EthernetInterface", - rootInterfaceName)); - res.result(boost::beast::http::status::not_found); - res.end(); + return; } - } else { - // No success, best what we can do is return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } - - void doPost(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 1) { - // This means there is a problem with the router - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - - nlohmann::json postReq; - if (!json_util::processJsonFromRequest(res, req, postReq)) { - return; + // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces + // for any Manager, not only hardcoded 'openbmc'. + std::string managerId = "openbmc"; + std::string rootInterfaceName = params[0]; + + // get eth interface list, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceList( + [&, managerId{std::move(managerId)}, + rootInterfaceName{std::move(rootInterfaceName)}]( + const bool &success, + const std::vector<std::string> &iface_list) { + if (success) + { + bool rootInterfaceFound = false; + nlohmann::json ifaceArray = nlohmann::json::array(); + + for (const std::string &ifaceItem : iface_list) + { + if (ifaceItem == rootInterfaceName) + { + rootInterfaceFound = true; + } + else if (boost::starts_with(ifaceItem, + rootInterfaceName + "_")) + { + ifaceArray.push_back( + {{"@odata.id", "/redfish/v1/Managers/" + + managerId + + "/EthernetInterfaces/" + + rootInterfaceName + + "/VLANs/" + ifaceItem}}); + } + } + + if (rootInterfaceFound) + { + Node::json["Members"] = ifaceArray; + Node::json["Members@odata.count"] = ifaceArray.size(); + Node::json["@odata.id"] = "/redfish/v1/Managers/" + + managerId + + "/EthernetInterfaces/" + + rootInterfaceName + "/VLANs"; + res.jsonValue = Node::json; + } + else + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("EthernetInterface", + rootInterfaceName)); + res.result(boost::beast::http::status::not_found); + res.end(); + } + } + else + { + // No success, best what we can do is return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + } + res.end(); + }); } - // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for - // any Manager, not only hardcoded 'openbmc'. - std::string managerId = "openbmc"; - std::string rootInterfaceName = params[0]; - uint64_t vlanId; - bool errorDetected; + void doPost(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 1) + { + // This means there is a problem with the router + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - if (json_util::getUnsigned( - "VLANId", postReq, vlanId, - static_cast<uint8_t>(json_util::MessageSetting::MISSING) | - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) { - res.end(); - return; - } + nlohmann::json postReq; - // get eth interface list, and call the below callback for JSON preparation - ethernetProvider.getEthernetIfaceList([ - &, managerId{std::move(managerId)}, - rootInterfaceName{std::move(rootInterfaceName)} - ](const bool &success, const std::vector<std::string> &iface_list) { - if (success) { - bool rootInterfaceFound = false; - - for (const std::string &ifaceItem : iface_list) { - if (ifaceItem == rootInterfaceName) { - rootInterfaceFound = true; - break; - } + if (!json_util::processJsonFromRequest(res, req, postReq)) + { + return; } - if (rootInterfaceFound) { - ethernetProvider.createVlan( - rootInterfaceName, vlanId, - [&, vlanId, rootInterfaceName, - req{std::move(req)} ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToErrorJson(res.jsonValue, - messages::internalError()); - res.end(); - } else { - memberVlan.doGet( - res, req, - {rootInterfaceName, - rootInterfaceName + "_" + std::to_string(vlanId)}); - } - }); - } else { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("EthernetInterface", - rootInterfaceName)); - res.result(boost::beast::http::status::not_found); - res.end(); + // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces + // for any Manager, not only hardcoded 'openbmc'. + std::string managerId = "openbmc"; + std::string rootInterfaceName = params[0]; + uint64_t vlanId; + bool errorDetected; + + if (json_util::getUnsigned( + "VLANId", postReq, vlanId, + static_cast<uint8_t>(json_util::MessageSetting::MISSING) | + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) + { + res.end(); + return; } - } else { - // No success, best what we can do is return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - res.end(); - } - }); - } - - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; - VlanNetworkInterface memberVlan; + + // get eth interface list, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceList( + [&, managerId{std::move(managerId)}, + rootInterfaceName{std::move(rootInterfaceName)}]( + const bool &success, + const std::vector<std::string> &iface_list) { + if (success) + { + bool rootInterfaceFound = false; + + for (const std::string &ifaceItem : iface_list) + { + if (ifaceItem == rootInterfaceName) + { + rootInterfaceFound = true; + break; + } + } + + if (rootInterfaceFound) + { + ethernetProvider.createVlan( + rootInterfaceName, vlanId, + [&, vlanId, rootInterfaceName, req{std::move(req)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::internalError()); + res.end(); + } + else + { + memberVlan.doGet( + res, req, + {rootInterfaceName, + rootInterfaceName + "_" + + std::to_string(vlanId)}); + } + }); + } + else + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("EthernetInterface", + rootInterfaceName)); + res.result(boost::beast::http::status::not_found); + res.end(); + } + } + else + { + // No success, best what we can do is return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + res.end(); + } + }); + } + + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; + VlanNetworkInterface memberVlan; }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 7a861b4a3d..86fe567f3a 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -17,101 +17,111 @@ #include "node.hpp" -namespace redfish { +namespace redfish +{ -class Manager : public Node { - public: - Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/openbmc/") { - Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc"; - Node::json["@odata.type"] = "#Manager.v1_3_0.Manager"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager"; - Node::json["Id"] = "openbmc"; - Node::json["Name"] = "OpenBmc Manager"; - Node::json["Description"] = "Baseboard Management Controller"; - Node::json["PowerState"] = "On"; - Node::json["UUID"] = - app.template getMiddleware<crow::persistent_data::Middleware>() - .systemUuid; - Node::json["Model"] = "OpenBmc"; // TODO(ed), get model - Node::json["FirmwareVersion"] = "1234456789"; // TODO(ed), get fwversion - Node::json["EthernetInterfaces"] = nlohmann::json( - {{"@odata.id", - "/redfish/v1/Managers/openbmc/EthernetInterfaces"}}); // TODO(Pawel), - // remove this - // when - // subroutes - // will work - // correctly +class Manager : public Node +{ + public: + Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/openbmc/") + { + Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc"; + Node::json["@odata.type"] = "#Manager.v1_3_0.Manager"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager"; + Node::json["Id"] = "openbmc"; + Node::json["Name"] = "OpenBmc Manager"; + Node::json["Description"] = "Baseboard Management Controller"; + Node::json["PowerState"] = "On"; + Node::json["UUID"] = + app.template getMiddleware<crow::persistent_data::Middleware>() + .systemUuid; + Node::json["Model"] = "OpenBmc"; // TODO(ed), get model + Node::json["FirmwareVersion"] = "1234456789"; // TODO(ed), get fwversion + Node::json["EthernetInterfaces"] = nlohmann::json( + {{"@odata.id", "/redfish/v1/Managers/openbmc/" + "EthernetInterfaces"}}); // TODO(Pawel), + // remove this + // when + // subroutes + // will work + // correctly - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - Node::json["DateTime"] = getDateTime(); - // Copy over the static data to include the entries added by SubRoute - res.jsonValue = Node::json; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + Node::json["DateTime"] = getDateTime(); + // Copy over the static data to include the entries added by SubRoute + res.jsonValue = Node::json; + res.end(); + } - std::string getDateTime() const { - std::array<char, 128> dateTime; - std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); - std::time_t time = std::time(nullptr); + std::string getDateTime() const + { + std::array<char, 128> dateTime; + std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); + std::time_t time = std::time(nullptr); - if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", - std::localtime(&time))) { - // insert the colon required by the ISO 8601 standard - redfishDateTime = std::string(dateTime.data()); - redfishDateTime.insert(redfishDateTime.end() - 2, ':'); - } + if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", + std::localtime(&time))) + { + // insert the colon required by the ISO 8601 standard + redfishDateTime = std::string(dateTime.data()); + redfishDateTime.insert(redfishDateTime.end() - 2, ':'); + } - return redfishDateTime; - } + return redfishDateTime; + } }; -class ManagerCollection : public Node { - public: - ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/") { - Node::json["@odata.id"] = "/redfish/v1/Managers"; - Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; - Node::json["Name"] = "Manager Collection"; - Node::json["Members@odata.count"] = 1; - Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; +class ManagerCollection : public Node +{ + public: + ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/") + { + Node::json["@odata.id"] = "/redfish/v1/Managers"; + Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; + Node::json["Name"] = "Manager Collection"; + Node::json["Members@odata.count"] = 1; + Node::json["Members"] = { + {{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - // Collections don't include the static data added by SubRoute because it - // has a duplicate entry for members - res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; - res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection"; - res.jsonValue["@odata.context"] = - "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; - res.jsonValue["Name"] = "Manager Collection"; - res.jsonValue["Members@odata.count"] = 1; - res.jsonValue["Members"] = { - {{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + // Collections don't include the static data added by SubRoute because + // it has a duplicate entry for members + res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; + res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection"; + res.jsonValue["@odata.context"] = + "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; + res.jsonValue["Name"] = "Manager Collection"; + res.jsonValue["Members@odata.count"] = 1; + res.jsonValue["Members"] = { + {{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; + res.end(); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp index ae29df05f6..bb021f5297 100644 --- a/redfish-core/lib/network_protocol.hpp +++ b/redfish-core/lib/network_protocol.hpp @@ -18,24 +18,27 @@ #include "error_messages.hpp" #include "node.hpp" -namespace redfish { - -enum NetworkProtocolUnitStructFields { - NET_PROTO_UNIT_NAME, - NET_PROTO_UNIT_DESC, - NET_PROTO_UNIT_LOAD_STATE, - NET_PROTO_UNIT_ACTIVE_STATE, - NET_PROTO_UNIT_SUB_STATE, - NET_PROTO_UNIT_DEVICE, - NET_PROTO_UNIT_OBJ_PATH, - NET_PROTO_UNIT_ALWAYS_0, - NET_PROTO_UNIT_ALWAYS_EMPTY, - NET_PROTO_UNIT_ALWAYS_ROOT_PATH +namespace redfish +{ + +enum NetworkProtocolUnitStructFields +{ + NET_PROTO_UNIT_NAME, + NET_PROTO_UNIT_DESC, + NET_PROTO_UNIT_LOAD_STATE, + NET_PROTO_UNIT_ACTIVE_STATE, + NET_PROTO_UNIT_SUB_STATE, + NET_PROTO_UNIT_DEVICE, + NET_PROTO_UNIT_OBJ_PATH, + NET_PROTO_UNIT_ALWAYS_0, + NET_PROTO_UNIT_ALWAYS_EMPTY, + NET_PROTO_UNIT_ALWAYS_ROOT_PATH }; -enum NetworkProtocolListenResponseElements { - NET_PROTO_LISTEN_TYPE, - NET_PROTO_LISTEN_STREAM +enum NetworkProtocolListenResponseElements +{ + NET_PROTO_LISTEN_TYPE, + NET_PROTO_LISTEN_STREAM }; /** @@ -46,9 +49,10 @@ using UnitStruct = std::string, sdbusplus::message::object_path, uint32_t, std::string, sdbusplus::message::object_path>; -struct ServiceConfiguration { - const char* serviceName; - const char* socketPath; +struct ServiceConfiguration +{ + const char* serviceName; + const char* socketPath; }; const static boost::container::flat_map<const char*, ServiceConfiguration> @@ -63,128 +67,161 @@ const static boost::container::flat_map<const char*, ServiceConfiguration> {"phosphor-ipmi-net.service", "/org/freedesktop/systemd1/unit/phosphor_2dipmi_2dnet_2esocket"}}}; -class NetworkProtocol : public Node { - public: - NetworkProtocol(CrowApp& app) - : Node(app, "/redfish/v1/Managers/openbmc/NetworkProtocol") { - Node::json["@odata.type"] = - "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"; - Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/NetworkProtocol"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; - Node::json["Id"] = "NetworkProtocol"; - Node::json["Name"] = "Manager Network Protocol"; - Node::json["Description"] = "Manager Network Service"; - Node::json["Status"]["Health"] = "OK"; - Node::json["Status"]["HealthRollup"] = "OK"; - Node::json["Status"]["State"] = "Enabled"; - - for (auto& protocol : protocolToDBus) { - Node::json[protocol.first]["ProtocolEnabled"] = false; +class NetworkProtocol : public Node +{ + public: + NetworkProtocol(CrowApp& app) : + Node(app, "/redfish/v1/Managers/openbmc/NetworkProtocol") + { + Node::json["@odata.type"] = + "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"; + Node::json["@odata.id"] = + "/redfish/v1/Managers/openbmc/NetworkProtocol"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; + Node::json["Id"] = "NetworkProtocol"; + Node::json["Name"] = "Manager Network Protocol"; + Node::json["Description"] = "Manager Network Service"; + Node::json["Status"]["Health"] = "OK"; + Node::json["Status"]["HealthRollup"] = "OK"; + Node::json["Status"]["State"] = "Enabled"; + + for (auto& protocol : protocolToDBus) + { + Node::json[protocol.first]["ProtocolEnabled"] = false; + } + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; } - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); - - getData(asyncResp); - } - - std::string getHostName() const { - std::string hostName; - - std::array<char, HOST_NAME_MAX> hostNameCStr; - if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) { - hostName = hostNameCStr.data(); + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + getData(asyncResp); } - return hostName; - } - - void getData(const std::shared_ptr<AsyncResp>& asyncResp) { - Node::json["HostName"] = getHostName(); - asyncResp->res.jsonValue = Node::json; - - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code ec, - const std::vector<UnitStruct>& resp) { - if (ec) { - asyncResp->res.jsonValue = nlohmann::json::object(); - messages::addMessageToErrorJson(asyncResp->res.jsonValue, - messages::internalError()); - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - for (auto& unit : resp) { - for (auto& kv : protocolToDBus) { - if (kv.second.serviceName == - std::get<NET_PROTO_UNIT_NAME>(unit)) { - continue; - } - const char* service = kv.first; - const char* socketPath = kv.second.socketPath; - - asyncResp->res.jsonValue[service]["ProtocolEnabled"] = - std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == "running"; - - crow::connections::systemBus->async_method_call( - [ asyncResp, service{std::string(service)}, socketPath ]( - const boost::system::error_code ec, - const sdbusplus::message::variant<std::vector< - std::tuple<std::string, std::string>>>& resp) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), - "/" + service); - return; - } - const std::vector<std::tuple<std::string, std::string>>* - responsePtr = mapbox::getPtr<const std::vector< - std::tuple<std::string, std::string>>>(resp); - if (responsePtr == nullptr || responsePtr->size() < 1) { - return; - } - const std::string& listenStream = - std::get<NET_PROTO_LISTEN_STREAM>((*responsePtr)[0]); - std::size_t lastColonPos = listenStream.rfind(":"); - if (lastColonPos == std::string::npos) { - // Not a port - return; - } - std::string portStr = listenStream.substr(lastColonPos + 1); - char* endPtr = nullptr; - // Use strtol instead of stroi to avoid exceptions - long port = std::strtol(portStr.c_str(), &endPtr, 10); - - if (*endPtr != '\0' || portStr.empty()) { - // Invalid value - asyncResp->res.jsonValue[service]["Port"] = nullptr; - } else { - // Everything OK - asyncResp->res.jsonValue[service]["Port"] = port; + std::string getHostName() const + { + std::string hostName; + + std::array<char, HOST_NAME_MAX> hostNameCStr; + if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) + { + hostName = hostNameCStr.data(); + } + return hostName; + } + + void getData(const std::shared_ptr<AsyncResp>& asyncResp) + { + Node::json["HostName"] = getHostName(); + asyncResp->res.jsonValue = Node::json; + + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, + const std::vector<UnitStruct>& resp) { + if (ec) + { + asyncResp->res.jsonValue = nlohmann::json::object(); + messages::addMessageToErrorJson(asyncResp->res.jsonValue, + messages::internalError()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + for (auto& unit : resp) + { + for (auto& kv : protocolToDBus) + { + if (kv.second.serviceName == + std::get<NET_PROTO_UNIT_NAME>(unit)) + { + continue; + } + const char* service = kv.first; + const char* socketPath = kv.second.socketPath; + + asyncResp->res.jsonValue[service]["ProtocolEnabled"] = + std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == + "running"; + + crow::connections::systemBus->async_method_call( + [asyncResp, service{std::string(service)}, + socketPath]( + const boost::system::error_code ec, + const sdbusplus::message::variant<std::vector< + std::tuple<std::string, std::string>>>& + resp) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::internalError(), + "/" + service); + return; + } + const std::vector<std::tuple< + std::string, std::string>>* responsePtr = + mapbox::getPtr<const std::vector< + std::tuple<std::string, std::string>>>( + resp); + if (responsePtr == nullptr || + responsePtr->size() < 1) + { + return; + } + + const std::string& listenStream = + std::get<NET_PROTO_LISTEN_STREAM>( + (*responsePtr)[0]); + std::size_t lastColonPos = + listenStream.rfind(":"); + if (lastColonPos == std::string::npos) + { + // Not a port + return; + } + std::string portStr = + listenStream.substr(lastColonPos + 1); + char* endPtr = nullptr; + // Use strtol instead of stroi to avoid + // exceptions + long port = + std::strtol(portStr.c_str(), &endPtr, 10); + + if (*endPtr != '\0' || portStr.empty()) + { + // Invalid value + asyncResp->res.jsonValue[service]["Port"] = + nullptr; + } + else + { + // Everything OK + asyncResp->res.jsonValue[service]["Port"] = + port; + } + }, + "org.freedesktop.systemd1", socketPath, + "org.freedesktop.DBus.Properties", "Get", + "org.freedesktop.systemd1.Socket", "Listen"); } - }, - "org.freedesktop.systemd1", socketPath, - "org.freedesktop.DBus.Properties", "Get", - "org.freedesktop.systemd1.Socket", "Listen"); - } - } - }, - "org.freedesktop.systemd1", "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", "ListUnits"); - } + } + }, + "org.freedesktop.systemd1", "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", "ListUnits"); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp index ca1c3757da..2406250fa3 100644 --- a/redfish-core/lib/redfish_sessions.hpp +++ b/redfish-core/lib/redfish_sessions.hpp @@ -19,312 +19,348 @@ #include "node.hpp" #include "persistent_data_middleware.hpp" -namespace redfish { +namespace redfish +{ class SessionCollection; -class Sessions : public Node { - public: - Sessions(CrowApp& app) - : Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) { - Node::json["@odata.type"] = "#Session.v1_0_2.Session"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; - Node::json["Name"] = "User Session"; - Node::json["Description"] = "Manager User Session"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - auto session = - crow::persistent_data::SessionStore::getInstance().getSessionByUid( - params[0]); - - if (session == nullptr) { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("Session", params[0])); - - res.result(boost::beast::http::status::not_found); - res.end(); - return; +class Sessions : public Node +{ + public: + Sessions(CrowApp& app) : + Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) + { + Node::json["@odata.type"] = "#Session.v1_0_2.Session"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; + Node::json["Name"] = "User Session"; + Node::json["Description"] = "Manager User Session"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; } - Node::json["Id"] = session->uniqueId; - Node::json["UserName"] = session->username; - Node::json["@odata.id"] = - "/redfish/v1/SessionService/Sessions/" + session->uniqueId; - - res.jsonValue = Node::json; - res.end(); - } - - void doDelete(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - // Need only 1 param which should be id of session to be deleted - if (params.size() != 1) { - // This should be handled by crow and never happen - BMCWEB_LOG_ERROR - << "Session DELETE has been called with invalid number of params"; - - res.result(boost::beast::http::status::bad_request); - messages::addMessageToErrorJson(res.jsonValue, messages::generalError()); - - res.end(); - return; + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + auto session = + crow::persistent_data::SessionStore::getInstance().getSessionByUid( + params[0]); + + if (session == nullptr) + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("Session", params[0])); + + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + + Node::json["Id"] = session->uniqueId; + Node::json["UserName"] = session->username; + Node::json["@odata.id"] = + "/redfish/v1/SessionService/Sessions/" + session->uniqueId; + + res.jsonValue = Node::json; + res.end(); } - auto session = - crow::persistent_data::SessionStore::getInstance().getSessionByUid( - params[0]); - - if (session == nullptr) { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("Session", params[0])); - - res.result(boost::beast::http::status::not_found); - res.end(); - return; + void doDelete(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + // Need only 1 param which should be id of session to be deleted + if (params.size() != 1) + { + // This should be handled by crow and never happen + BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " + "number of params"; + + res.result(boost::beast::http::status::bad_request); + messages::addMessageToErrorJson(res.jsonValue, + messages::generalError()); + + res.end(); + return; + } + + auto session = + crow::persistent_data::SessionStore::getInstance().getSessionByUid( + params[0]); + + if (session == nullptr) + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("Session", params[0])); + + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + + // DELETE should return representation of object that will be removed + doGet(res, req, params); + + crow::persistent_data::SessionStore::getInstance().removeSession( + session); } - // DELETE should return representation of object that will be removed - doGet(res, req, params); - - crow::persistent_data::SessionStore::getInstance().removeSession(session); - } - - /** - * This allows SessionCollection to reuse this class' doGet method, to - * maintain consistency of returned data, as Collection's doPost should return - * data for created member which should match member's doGet result in 100% - */ - friend SessionCollection; + /** + * This allows SessionCollection to reuse this class' doGet method, to + * maintain consistency of returned data, as Collection's doPost should + * return data for created member which should match member's doGet result + * in 100% + */ + friend SessionCollection; }; -class SessionCollection : public Node { - public: - SessionCollection(CrowApp& app) - : Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) { - Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; - Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#SessionCollection.SessionCollection"; - Node::json["Name"] = "Session Collection"; - Node::json["Description"] = "Session Collection"; - Node::json["Members@odata.count"] = 0; - Node::json["Members"] = nlohmann::json::array(); - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - std::vector<const std::string*> sessionIds = - crow::persistent_data::SessionStore::getInstance().getUniqueIds( - false, crow::persistent_data::PersistenceType::TIMEOUT); - - Node::json["Members@odata.count"] = sessionIds.size(); - Node::json["Members"] = nlohmann::json::array(); - for (const std::string* uid : sessionIds) { - Node::json["Members"].push_back( - {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); +class SessionCollection : public Node +{ + public: + SessionCollection(CrowApp& app) : + Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) + { + Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; + Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#SessionCollection.SessionCollection"; + Node::json["Name"] = "Session Collection"; + Node::json["Description"] = "Session Collection"; + Node::json["Members@odata.count"] = 0; + Node::json["Members"] = nlohmann::json::array(); + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {}}}; } - res.jsonValue = Node::json; - res.end(); - } - - void doPost(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - boost::beast::http::status status; - std::string username; - bool userAuthSuccessful = - authenticateUser(req, status, username, res.jsonValue); - res.result(status); - - if (!userAuthSuccessful) { - res.end(); - return; + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + std::vector<const std::string*> sessionIds = + crow::persistent_data::SessionStore::getInstance().getUniqueIds( + false, crow::persistent_data::PersistenceType::TIMEOUT); + + Node::json["Members@odata.count"] = sessionIds.size(); + Node::json["Members"] = nlohmann::json::array(); + for (const std::string* uid : sessionIds) + { + Node::json["Members"].push_back( + {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); + } + + res.jsonValue = Node::json; + res.end(); } - // User is authenticated - create session for him - auto session = - crow::persistent_data::SessionStore::getInstance().generateUserSession( - username); - res.addHeader("X-Auth-Token", session->sessionToken); - - res.addHeader("Location", - "/redfish/v1/SessionService/Sessions/" + session->uniqueId); - - // Return data for created session - memberSession.doGet(res, req, {session->uniqueId}); - - // No need for res.end(), as it is called by doGet() - } - - /** - * @brief Verifies data provided in request and tries to authenticate user - * - * @param[in] req Crow request containing authentication data - * @param[out] httpRespCode HTTP Code that should be returned in response - * @param[out] user Retrieved username - not filled on failure - * @param[out] errJson JSON to which error messages will be written - * - * @return true if authentication was successful, false otherwise - */ - bool authenticateUser(const crow::Request& req, - boost::beast::http::status& httpRespCode, - std::string& user, nlohmann::json& errJson) { - // We need only UserName and Password - nothing more, nothing less - static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; - - // call with exceptions disabled - auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false); - if (loginCredentials.is_discarded()) { - httpRespCode = boost::beast::http::status::bad_request; - - messages::addMessageToErrorJson(errJson, messages::malformedJSON()); - - return false; + void doPost(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + boost::beast::http::status status; + std::string username; + bool userAuthSuccessful = + authenticateUser(req, status, username, res.jsonValue); + res.result(status); + + if (!userAuthSuccessful) + { + res.end(); + return; + } + + // User is authenticated - create session for him + auto session = crow::persistent_data::SessionStore::getInstance() + .generateUserSession(username); + res.addHeader("X-Auth-Token", session->sessionToken); + + res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + + session->uniqueId); + + // Return data for created session + memberSession.doGet(res, req, {session->uniqueId}); + + // No need for res.end(), as it is called by doGet() } - // Check that there are only as many fields as there should be - if (loginCredentials.size() != numberOfRequiredFieldsInReq) { - httpRespCode = boost::beast::http::status::bad_request; - - messages::addMessageToErrorJson(errJson, messages::malformedJSON()); - - return false; - } - - // Find fields that we need - UserName and Password - auto userIt = loginCredentials.find("UserName"); - auto passIt = loginCredentials.find("Password"); - if (userIt == loginCredentials.end() || passIt == loginCredentials.end()) { - httpRespCode = boost::beast::http::status::bad_request; - - if (userIt == loginCredentials.end()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("UserName")); - } - - if (passIt == loginCredentials.end()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("Password")); - } - - return false; + /** + * @brief Verifies data provided in request and tries to authenticate user + * + * @param[in] req Crow request containing authentication data + * @param[out] httpRespCode HTTP Code that should be returned in response + * @param[out] user Retrieved username - not filled on failure + * @param[out] errJson JSON to which error messages will be written + * + * @return true if authentication was successful, false otherwise + */ + bool authenticateUser(const crow::Request& req, + boost::beast::http::status& httpRespCode, + std::string& user, nlohmann::json& errJson) + { + // We need only UserName and Password - nothing more, nothing less + static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; + + // call with exceptions disabled + auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false); + if (loginCredentials.is_discarded()) + { + httpRespCode = boost::beast::http::status::bad_request; + + messages::addMessageToErrorJson(errJson, messages::malformedJSON()); + + return false; + } + + // Check that there are only as many fields as there should be + if (loginCredentials.size() != numberOfRequiredFieldsInReq) + { + httpRespCode = boost::beast::http::status::bad_request; + + messages::addMessageToErrorJson(errJson, messages::malformedJSON()); + + return false; + } + + // Find fields that we need - UserName and Password + auto userIt = loginCredentials.find("UserName"); + auto passIt = loginCredentials.find("Password"); + if (userIt == loginCredentials.end() || + passIt == loginCredentials.end()) + { + httpRespCode = boost::beast::http::status::bad_request; + + if (userIt == loginCredentials.end()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("UserName")); + } + + if (passIt == loginCredentials.end()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("Password")); + } + + return false; + } + + // Check that given data is of valid type (string) + if (!userIt->is_string() || !passIt->is_string()) + { + httpRespCode = boost::beast::http::status::bad_request; + + if (!userIt->is_string()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyValueTypeError(userIt->dump(), + "UserName")); + } + + if (!passIt->is_string()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyValueTypeError(userIt->dump(), + "Password")); + } + + return false; + } + + // Extract username and password + std::string username = userIt->get<const std::string>(); + std::string password = passIt->get<const std::string>(); + + // Verify that required fields are not empty + if (username.empty() || password.empty()) + { + httpRespCode = boost::beast::http::status::bad_request; + + if (username.empty()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("UserName")); + } + + if (password.empty()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("Password")); + } + + return false; + } + + // Finally - try to authenticate user + if (!pamAuthenticateUser(username, password)) + { + httpRespCode = boost::beast::http::status::unauthorized; + + messages::addMessageToErrorJson( + errJson, + messages::resourceAtUriUnauthorized( + std::string(req.url), "Invalid username or password")); + + return false; + } + + // User authenticated successfully + httpRespCode = boost::beast::http::status::ok; + user = username; + + return true; } - // Check that given data is of valid type (string) - if (!userIt->is_string() || !passIt->is_string()) { - httpRespCode = boost::beast::http::status::bad_request; - - if (!userIt->is_string()) { - messages::addMessageToErrorJson( - errJson, - messages::propertyValueTypeError(userIt->dump(), "UserName")); - } - - if (!passIt->is_string()) { - messages::addMessageToErrorJson( - errJson, - messages::propertyValueTypeError(userIt->dump(), "Password")); - } + /** + * Member session to ensure consistency between collection's doPost and + * member's doGet, as they should return 100% matching data + */ + Sessions memberSession; +}; - return false; +class SessionService : public Node +{ + public: + SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") + { + Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; + Node::json["@odata.id"] = "/redfish/v1/SessionService/"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#SessionService.SessionService"; + Node::json["Name"] = "Session Service"; + Node::json["Id"] = "SessionService"; + Node::json["Description"] = "Session Service"; + Node::json["SessionTimeout"] = + crow::persistent_data::SessionStore::getInstance() + .getTimeoutInSeconds(); + Node::json["ServiceEnabled"] = true; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; } - // Extract username and password - std::string username = userIt->get<const std::string>(); - std::string password = passIt->get<const std::string>(); - - // Verify that required fields are not empty - if (username.empty() || password.empty()) { - httpRespCode = boost::beast::http::status::bad_request; - - if (username.empty()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("UserName")); - } - - if (password.empty()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("Password")); - } - - return false; + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); } - - // Finally - try to authenticate user - if (!pamAuthenticateUser(username, password)) { - httpRespCode = boost::beast::http::status::unauthorized; - - messages::addMessageToErrorJson( - errJson, messages::resourceAtUriUnauthorized( - std::string(req.url), "Invalid username or password")); - - return false; - } - - // User authenticated successfully - httpRespCode = boost::beast::http::status::ok; - user = username; - - return true; - } - - /** - * Member session to ensure consistency between collection's doPost and - * member's doGet, as they should return 100% matching data - */ - Sessions memberSession; -}; - -class SessionService : public Node { - public: - SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") { - Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; - Node::json["@odata.id"] = "/redfish/v1/SessionService/"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#SessionService.SessionService"; - Node::json["Name"] = "Session Service"; - Node::json["Id"] = "SessionService"; - Node::json["Description"] = "Session Service"; - Node::json["SessionTimeout"] = - crow::persistent_data::SessionStore::getInstance() - .getTimeoutInSeconds(); - Node::json["ServiceEnabled"] = true; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/roles.hpp b/redfish-core/lib/roles.hpp index 72d79486bb..258eed31b2 100644 --- a/redfish-core/lib/roles.hpp +++ b/redfish-core/lib/roles.hpp @@ -17,74 +17,82 @@ #include "node.hpp" -namespace redfish { +namespace redfish +{ -class Roles : public Node { - public: - Roles(CrowApp& app) - : Node(app, "/redfish/v1/AccountService/Roles/Administrator/") { - Node::json["@odata.id"] = "/redfish/v1/AccountService/Roles/Administrator"; - Node::json["@odata.type"] = "#Role.v1_0_2.Role"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Role.Role"; - Node::json["Id"] = "Administrator"; - Node::json["Name"] = "User Role"; - Node::json["Description"] = "Administrator User Role"; - Node::json["IsPredefined"] = true; - Node::json["AssignedPrivileges"] = {"Login", "ConfigureManager", - "ConfigureUsers", "ConfigureSelf", - "ConfigureComponents"}; - Node::json["OemPrivileges"] = nlohmann::json::array(); - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } +class Roles : public Node +{ + public: + Roles(CrowApp& app) : + Node(app, "/redfish/v1/AccountService/Roles/Administrator/") + { + Node::json["@odata.id"] = + "/redfish/v1/AccountService/Roles/Administrator"; + Node::json["@odata.type"] = "#Role.v1_0_2.Role"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Role.Role"; + Node::json["Id"] = "Administrator"; + Node::json["Name"] = "User Role"; + Node::json["Description"] = "Administrator User Role"; + Node::json["IsPredefined"] = true; + Node::json["AssignedPrivileges"] = {"Login", "ConfigureManager", + "ConfigureUsers", "ConfigureSelf", + "ConfigureComponents"}; + Node::json["OemPrivileges"] = nlohmann::json::array(); + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); + } }; -class RoleCollection : public Node { - public: - RoleCollection(CrowApp& app) - : Node(app, "/redfish/v1/AccountService/Roles/") { - Node::json["@odata.id"] = "/redfish/v1/AccountService/Roles"; - Node::json["@odata.type"] = "#RoleCollection.RoleCollection"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#RoleCollection.RoleCollection"; - Node::json["Name"] = "Roles Collection"; - Node::json["Description"] = "BMC User Roles"; - Node::json["Members@odata.count"] = 1; - Node::json["Members"] = { - {{"@odata.id", "/redfish/v1/AccountService/Roles/Administrator"}}}; +class RoleCollection : public Node +{ + public: + RoleCollection(CrowApp& app) : + Node(app, "/redfish/v1/AccountService/Roles/") + { + Node::json["@odata.id"] = "/redfish/v1/AccountService/Roles"; + Node::json["@odata.type"] = "#RoleCollection.RoleCollection"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#RoleCollection.RoleCollection"; + Node::json["Name"] = "Roles Collection"; + Node::json["Description"] = "BMC User Roles"; + Node::json["Members@odata.count"] = 1; + Node::json["Members"] = { + {{"@odata.id", "/redfish/v1/AccountService/Roles/Administrator"}}}; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - // This is a short term solution to work around a bug. GetSubroutes - // accidentally recognizes the Roles/Administrator route as a subroute - // (because it's hardcoded to a single entity). Remove this line when that - // is resolved - res.jsonValue.erase("Administrator"); - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + // This is a short term solution to work around a bug. GetSubroutes + // accidentally recognizes the Roles/Administrator route as a subroute + // (because it's hardcoded to a single entity). Remove this line when + // that is resolved + res.jsonValue.erase("Administrator"); + res.end(); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp index 7f9fa684bc..c390cd7908 100644 --- a/redfish-core/lib/sensors.hpp +++ b/redfish-core/lib/sensors.hpp @@ -16,13 +16,15 @@ #pragma once #include <math.h> -#include <dbus_singleton.hpp> + #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/container/flat_map.hpp> #include <boost/range/algorithm/replace_copy_if.hpp> +#include <dbus_singleton.hpp> -namespace redfish { +namespace redfish +{ constexpr const char* dbusSensorPrefix = "/xyz/openbmc_project/sensors/"; @@ -41,32 +43,38 @@ using ManagedObjectsVectorType = std::vector<std::pair< * SensorsAsyncResp * Gathers data needed for response processing after async calls are done */ -class SensorsAsyncResp { - public: - SensorsAsyncResp(crow::Response& response, const std::string& chassisId, - const std::initializer_list<const char*> types) - : res(response), chassisId(chassisId), types(types) { - res.jsonValue["@odata.id"] = - "/redfish/v1/Chassis/" + chassisId + "/Thermal"; - } - - ~SensorsAsyncResp() { - if (res.result() == boost::beast::http::status::internal_server_error) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = nlohmann::json::object(); +class SensorsAsyncResp +{ + public: + SensorsAsyncResp(crow::Response& response, const std::string& chassisId, + const std::initializer_list<const char*> types) : + res(response), + chassisId(chassisId), types(types) + { + res.jsonValue["@odata.id"] = + "/redfish/v1/Chassis/" + chassisId + "/Thermal"; } - res.end(); - } - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } + ~SensorsAsyncResp() + { + if (res.result() == boost::beast::http::status::internal_server_error) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = nlohmann::json::object(); + } + res.end(); + } + + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); + } - crow::Response& res; - std::string chassisId{}; - const std::vector<const char*> types; + crow::Response& res; + std::string chassisId{}; + const std::vector<const char*> types; }; /** @@ -78,70 +86,84 @@ class SensorsAsyncResp { template <typename Callback> void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, const boost::container::flat_set<std::string>& sensorNames, - Callback&& callback) { - BMCWEB_LOG_DEBUG << "getConnections enter"; - const std::string path = "/xyz/openbmc_project/sensors"; - const std::array<std::string, 1> interfaces = { - "xyz.openbmc_project.Sensor.Value"}; - - // Response handler for parsing objects subtree - auto respHandler = - [ callback{std::move(callback)}, SensorsAsyncResp, sensorNames ]( - const boost::system::error_code ec, const GetSubTreeType& subtree) { - BMCWEB_LOG_DEBUG << "getConnections resp_handler enter"; - if (ec) { - SensorsAsyncResp->setErrorStatus(); - BMCWEB_LOG_ERROR << "getConnections resp_handler: Dbus error " << ec; - return; - } + Callback&& callback) +{ + BMCWEB_LOG_DEBUG << "getConnections enter"; + const std::string path = "/xyz/openbmc_project/sensors"; + const std::array<std::string, 1> interfaces = { + "xyz.openbmc_project.Sensor.Value"}; + + // Response handler for parsing objects subtree + auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp, + sensorNames](const boost::system::error_code ec, + const GetSubTreeType& subtree) { + BMCWEB_LOG_DEBUG << "getConnections resp_handler enter"; + if (ec) + { + SensorsAsyncResp->setErrorStatus(); + BMCWEB_LOG_ERROR << "getConnections resp_handler: Dbus error " + << ec; + return; + } - BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees"; + BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees"; - // Make unique list of connections only for requested sensor types and - // found in the chassis - boost::container::flat_set<std::string> connections; - // Intrinsic to avoid malloc. Most systems will have < 8 sensor producers - connections.reserve(8); + // Make unique list of connections only for requested sensor types and + // found in the chassis + boost::container::flat_set<std::string> connections; + // Intrinsic to avoid malloc. Most systems will have < 8 sensor + // producers + connections.reserve(8); - BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size(); - for (const std::string& tsensor : sensorNames) { - BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor; - } + BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size(); + for (const std::string& tsensor : sensorNames) + { + BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor; + } - for (const std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>& - object : subtree) { - for (const char* type : SensorsAsyncResp->types) { - if (boost::starts_with(object.first, type)) { - auto lastPos = object.first.rfind('/'); - if (lastPos != std::string::npos) { - std::string sensorName = object.first.substr(lastPos + 1); - - if (sensorNames.find(sensorName) != sensorNames.end()) { - // For each Connection name - for (const std::pair<std::string, std::vector<std::string>>& - objData : object.second) { - BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first; - connections.insert(objData.first); - } + for (const std::pair< + std::string, + std::vector<std::pair<std::string, std::vector<std::string>>>>& + object : subtree) + { + for (const char* type : SensorsAsyncResp->types) + { + if (boost::starts_with(object.first, type)) + { + auto lastPos = object.first.rfind('/'); + if (lastPos != std::string::npos) + { + std::string sensorName = + object.first.substr(lastPos + 1); + + if (sensorNames.find(sensorName) != sensorNames.end()) + { + // For each Connection name + for (const std::pair<std::string, + std::vector<std::string>>& + objData : object.second) + { + BMCWEB_LOG_DEBUG << "Adding connection: " + << objData.first; + connections.insert(objData.first); + } + } + } + break; + } } - } - break; } - } - } - BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections"; - callback(std::move(connections)); - BMCWEB_LOG_DEBUG << "getConnections resp_handler exit"; - }; - - // Make call to ObjectMapper to find all sensors objects - crow::connections::systemBus->async_method_call( - std::move(respHandler), "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", - "GetSubTree", path, 2, interfaces); - BMCWEB_LOG_DEBUG << "getConnections exit"; + BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections"; + callback(std::move(connections)); + BMCWEB_LOG_DEBUG << "getConnections resp_handler exit"; + }; + + // Make call to ObjectMapper to find all sensors objects + crow::connections::systemBus->async_method_call( + std::move(respHandler), "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces); + BMCWEB_LOG_DEBUG << "getConnections exit"; } /** @@ -151,64 +173,74 @@ void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, */ template <typename Callback> void getChassis(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, - Callback&& callback) { - BMCWEB_LOG_DEBUG << "getChassis enter"; - // Process response from EntityManager and extract chassis data - auto respHandler = [ callback{std::move(callback)}, SensorsAsyncResp ]( - const boost::system::error_code ec, ManagedObjectsVectorType& resp) { - BMCWEB_LOG_DEBUG << "getChassis respHandler enter"; - if (ec) { - BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec; - SensorsAsyncResp->setErrorStatus(); - return; - } - boost::container::flat_set<std::string> sensorNames; - - // SensorsAsyncResp->chassisId - bool foundChassis = false; - std::vector<std::string> split; - // Reserve space for - // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames - split.reserve(8); - - for (const auto& objDictEntry : resp) { - const std::string& objectPath = - static_cast<const std::string&>(objDictEntry.first); - boost::algorithm::split(split, objectPath, boost::is_any_of("/")); - if (split.size() < 2) { - BMCWEB_LOG_ERROR << "Got path that isn't long enough " << objectPath; - split.clear(); - continue; - } - const std::string& sensorName = split.end()[-1]; - const std::string& chassisName = split.end()[-2]; - - if (chassisName != SensorsAsyncResp->chassisId) { - split.clear(); - continue; - } - BMCWEB_LOG_DEBUG << "New sensor: " << sensorName; - foundChassis = true; - sensorNames.emplace(sensorName); - split.clear(); + Callback&& callback) +{ + BMCWEB_LOG_DEBUG << "getChassis enter"; + // Process response from EntityManager and extract chassis data + auto respHandler = [callback{std::move(callback)}, + SensorsAsyncResp](const boost::system::error_code ec, + ManagedObjectsVectorType& resp) { + BMCWEB_LOG_DEBUG << "getChassis respHandler enter"; + if (ec) + { + BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec; + SensorsAsyncResp->setErrorStatus(); + return; + } + boost::container::flat_set<std::string> sensorNames; + + // SensorsAsyncResp->chassisId + bool foundChassis = false; + std::vector<std::string> split; + // Reserve space for + // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames + split.reserve(8); + + for (const auto& objDictEntry : resp) + { + const std::string& objectPath = + static_cast<const std::string&>(objDictEntry.first); + boost::algorithm::split(split, objectPath, boost::is_any_of("/")); + if (split.size() < 2) + { + BMCWEB_LOG_ERROR << "Got path that isn't long enough " + << objectPath; + split.clear(); + continue; + } + const std::string& sensorName = split.end()[-1]; + const std::string& chassisName = split.end()[-2]; + + if (chassisName != SensorsAsyncResp->chassisId) + { + split.clear(); + continue; + } + BMCWEB_LOG_DEBUG << "New sensor: " << sensorName; + foundChassis = true; + sensorNames.emplace(sensorName); + split.clear(); + }; + BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names"; + + if (!foundChassis) + { + BMCWEB_LOG_INFO << "Unable to find chassis named " + << SensorsAsyncResp->chassisId; + SensorsAsyncResp->res.result(boost::beast::http::status::not_found); + } + else + { + callback(sensorNames); + } + BMCWEB_LOG_DEBUG << "getChassis respHandler exit"; }; - BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names"; - - if (!foundChassis) { - BMCWEB_LOG_INFO << "Unable to find chassis named " - << SensorsAsyncResp->chassisId; - SensorsAsyncResp->res.result(boost::beast::http::status::not_found); - } else { - callback(sensorNames); - } - BMCWEB_LOG_DEBUG << "getChassis respHandler exit"; - }; - - // Make call to EntityManager to find all chassis objects - crow::connections::systemBus->async_method_call( - respHandler, "xyz.openbmc_project.EntityManager", "/", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - BMCWEB_LOG_DEBUG << "getChassis exit"; + + // Make call to EntityManager to find all chassis objects + crow::connections::systemBus->async_method_call( + respHandler, "xyz.openbmc_project.EntityManager", "/", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + BMCWEB_LOG_DEBUG << "getChassis exit"; } /** @@ -225,116 +257,146 @@ void objectInterfacesToJson( const boost::container::flat_map< std::string, boost::container::flat_map<std::string, SensorVariant>>& interfacesDict, - nlohmann::json& sensor_json) { - // We need a value interface before we can do anything with it - auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value"); - if (valueIt == interfacesDict.end()) { - BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface"; - return; - } - - // Assume values exist as is (10^0 == 1) if no scale exists - int64_t scaleMultiplier = 0; - - auto scaleIt = valueIt->second.find("Scale"); - // If a scale exists, pull value as int64, and use the scaling. - if (scaleIt != valueIt->second.end()) { - const int64_t* int64Value = mapbox::getPtr<const int64_t>(scaleIt->second); - if (int64Value != nullptr) { - scaleMultiplier = *int64Value; + nlohmann::json& sensor_json) +{ + // We need a value interface before we can do anything with it + auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value"); + if (valueIt == interfacesDict.end()) + { + BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface"; + return; } - } - - sensor_json["MemberId"] = sensorName; - sensor_json["Name"] = sensorName; - sensor_json["Status"]["State"] = "Enabled"; - sensor_json["Status"]["Health"] = "OK"; - - // Parameter to set to override the type we get from dbus, and force it to - // int, regardless of what is available. This is used for schemas like fan, - // that require integers, not floats. - bool forceToInt = false; - - const char* unit = "Reading"; - if (sensorType == "temperature") { - unit = "ReadingCelsius"; - sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature"; - // TODO(ed) Documentation says that path should be type fan_tach, - // implementation seems to implement fan - } else if (sensorType == "fan" || sensorType == "fan_tach") { - unit = "Reading"; - sensor_json["ReadingUnits"] = "RPM"; - sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan"; - forceToInt = true; - } else if (sensorType == "voltage") { - unit = "ReadingVolts"; - sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage"; - } else { - BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName; - return; - } - // Map of dbus interface name, dbus property name and redfish property_name - std::vector<std::tuple<const char*, const char*, const char*>> properties; - properties.reserve(7); - - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", - "WarningHigh", "UpperThresholdNonCritical"); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", - "WarningLow", "LowerThresholdNonCritical"); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", - "CriticalHigh", "UpperThresholdCritical"); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", - "CriticalLow", "LowerThresholdCritical"); - - if (sensorType == "temperature") { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "MinReadingRangeTemp"); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "MaxReadingRangeTemp"); - } else { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "MinReadingRange"); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "MaxReadingRange"); - } - - for (const std::tuple<const char*, const char*, const char*>& p : - properties) { - auto interfaceProperties = interfacesDict.find(std::get<0>(p)); - if (interfaceProperties != interfacesDict.end()) { - auto valueIt = interfaceProperties->second.find(std::get<1>(p)); - if (valueIt != interfaceProperties->second.end()) { - const SensorVariant& valueVariant = valueIt->second; - nlohmann::json& valueIt = sensor_json[std::get<2>(p)]; - - // Attempt to pull the int64 directly - const int64_t* int64Value = mapbox::getPtr<const int64_t>(valueVariant); - - if (int64Value != nullptr) { - if (forceToInt || scaleMultiplier >= 0) { - valueIt = *int64Value * std::pow(10, scaleMultiplier); - } else { - valueIt = *int64Value * - std::pow(10, static_cast<double>(scaleMultiplier)); - } + + // Assume values exist as is (10^0 == 1) if no scale exists + int64_t scaleMultiplier = 0; + + auto scaleIt = valueIt->second.find("Scale"); + // If a scale exists, pull value as int64, and use the scaling. + if (scaleIt != valueIt->second.end()) + { + const int64_t* int64Value = + mapbox::getPtr<const int64_t>(scaleIt->second); + if (int64Value != nullptr) + { + scaleMultiplier = *int64Value; } - // Attempt to pull the float directly - const double* doubleValue = mapbox::getPtr<const double>(valueVariant); - - if (doubleValue != nullptr) { - if (!forceToInt) { - valueIt = *doubleValue * - std::pow(10, static_cast<double>(scaleMultiplier)); - } else { - valueIt = static_cast<int64_t>(*doubleValue * - std::pow(10, scaleMultiplier)); - } + } + + sensor_json["MemberId"] = sensorName; + sensor_json["Name"] = sensorName; + sensor_json["Status"]["State"] = "Enabled"; + sensor_json["Status"]["Health"] = "OK"; + + // Parameter to set to override the type we get from dbus, and force it to + // int, regardless of what is available. This is used for schemas like fan, + // that require integers, not floats. + bool forceToInt = false; + + const char* unit = "Reading"; + if (sensorType == "temperature") + { + unit = "ReadingCelsius"; + sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature"; + // TODO(ed) Documentation says that path should be type fan_tach, + // implementation seems to implement fan + } + else if (sensorType == "fan" || sensorType == "fan_tach") + { + unit = "Reading"; + sensor_json["ReadingUnits"] = "RPM"; + sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan"; + forceToInt = true; + } + else if (sensorType == "voltage") + { + unit = "ReadingVolts"; + sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage"; + } + else + { + BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName; + return; + } + // Map of dbus interface name, dbus property name and redfish property_name + std::vector<std::tuple<const char*, const char*, const char*>> properties; + properties.reserve(7); + + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningHigh", "UpperThresholdNonCritical"); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningLow", "LowerThresholdNonCritical"); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalHigh", "UpperThresholdCritical"); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalLow", "LowerThresholdCritical"); + + if (sensorType == "temperature") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "MinReadingRangeTemp"); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "MaxReadingRangeTemp"); + } + else + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "MinReadingRange"); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "MaxReadingRange"); + } + + for (const std::tuple<const char*, const char*, const char*>& p : + properties) + { + auto interfaceProperties = interfacesDict.find(std::get<0>(p)); + if (interfaceProperties != interfacesDict.end()) + { + auto valueIt = interfaceProperties->second.find(std::get<1>(p)); + if (valueIt != interfaceProperties->second.end()) + { + const SensorVariant& valueVariant = valueIt->second; + nlohmann::json& valueIt = sensor_json[std::get<2>(p)]; + + // Attempt to pull the int64 directly + const int64_t* int64Value = + mapbox::getPtr<const int64_t>(valueVariant); + + if (int64Value != nullptr) + { + if (forceToInt || scaleMultiplier >= 0) + { + valueIt = *int64Value * std::pow(10, scaleMultiplier); + } + else + { + valueIt = + *int64Value * + std::pow(10, static_cast<double>(scaleMultiplier)); + } + } + // Attempt to pull the float directly + const double* doubleValue = + mapbox::getPtr<const double>(valueVariant); + + if (doubleValue != nullptr) + { + if (!forceToInt) + { + valueIt = + *doubleValue * + std::pow(10, static_cast<double>(scaleMultiplier)); + } + else + { + valueIt = static_cast<int64_t>( + *doubleValue * std::pow(10, scaleMultiplier)); + } + } + } } - } } - } - BMCWEB_LOG_DEBUG << "Added sensor " << sensorName; + BMCWEB_LOG_DEBUG << "Added sensor " << sensorName; } /** @@ -342,106 +404,140 @@ void objectInterfacesToJson( * chassis. * @param SensorsAsyncResp Pointer to object holding response data */ -void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp) { - BMCWEB_LOG_DEBUG << "getChassisData enter"; - auto getChassisCb = [&, SensorsAsyncResp]( - boost::container::flat_set<std::string>& - sensorNames) { - BMCWEB_LOG_DEBUG << "getChassisCb enter"; - auto getConnectionCb = - [&, SensorsAsyncResp, sensorNames]( - const boost::container::flat_set<std::string>& connections) { - BMCWEB_LOG_DEBUG << "getConnectionCb enter"; - // Get managed objects from all services exposing sensors - for (const std::string& connection : connections) { - // Response handler to process managed objects - auto getManagedObjectsCb = [&, SensorsAsyncResp, sensorNames]( - const boost::system::error_code ec, - ManagedObjectsVectorType& resp) { - BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter"; - if (ec) { - BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec; - SensorsAsyncResp->setErrorStatus(); - return; - } - // Go through all objects and update response with - // sensor data - for (const auto& objDictEntry : resp) { - const std::string& objPath = - static_cast<const std::string&>(objDictEntry.first); - BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object " - << objPath; - - std::vector<std::string> split; - // Reserve space for - // /xyz/openbmc_project/sensors/<name>/<subname> - split.reserve(6); - boost::algorithm::split(split, objPath, boost::is_any_of("/")); - if (split.size() < 6) { - BMCWEB_LOG_ERROR << "Got path that isn't long enough " - << objPath; - continue; - } - // These indexes aren't intuitive, as boost::split puts an empty - // string at the beggining - const std::string& sensorType = split[4]; - const std::string& sensorName = split[5]; - BMCWEB_LOG_DEBUG << "sensorName " << sensorName - << " sensorType " << sensorType; - if (sensorNames.find(sensorName) == sensorNames.end()) { - BMCWEB_LOG_ERROR << sensorName << " not in sensor list "; - continue; - } - - const char* fieldName = nullptr; - if (sensorType == "temperature") { - fieldName = "Temperatures"; - } else if (sensorType == "fan" || sensorType == "fan_tach") { - fieldName = "Fans"; - } else if (sensorType == "voltage") { - fieldName = "Voltages"; - } else if (sensorType == "current") { - fieldName = "PowerSupply"; - } else if (sensorType == "power") { - fieldName = "PowerSupply"; - } else { - BMCWEB_LOG_ERROR << "Unsure how to handle sensorType " - << sensorType; - continue; - } - - nlohmann::json& tempArray = - SensorsAsyncResp->res.jsonValue[fieldName]; - - // Create the array if it doesn't yet exist - if (tempArray.is_array() == false) { - tempArray = nlohmann::json::array(); - } - - tempArray.push_back( - {{"@odata.id", "/redfish/v1/Chassis/" + - SensorsAsyncResp->chassisId + - "/Thermal#/" + sensorName}}); - nlohmann::json& sensorJson = tempArray.back(); - objectInterfacesToJson(sensorName, sensorType, - objDictEntry.second, sensorJson); - } - BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit"; +void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp) +{ + BMCWEB_LOG_DEBUG << "getChassisData enter"; + auto getChassisCb = [&, SensorsAsyncResp]( + boost::container::flat_set<std::string>& + sensorNames) { + BMCWEB_LOG_DEBUG << "getChassisCb enter"; + auto getConnectionCb = + [&, SensorsAsyncResp, sensorNames]( + const boost::container::flat_set<std::string>& connections) { + BMCWEB_LOG_DEBUG << "getConnectionCb enter"; + // Get managed objects from all services exposing sensors + for (const std::string& connection : connections) + { + // Response handler to process managed objects + auto getManagedObjectsCb = + [&, SensorsAsyncResp, + sensorNames](const boost::system::error_code ec, + ManagedObjectsVectorType& resp) { + BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter"; + if (ec) + { + BMCWEB_LOG_ERROR + << "getManagedObjectsCb DBUS error: " << ec; + SensorsAsyncResp->setErrorStatus(); + return; + } + // Go through all objects and update response with + // sensor data + for (const auto& objDictEntry : resp) + { + const std::string& objPath = + static_cast<const std::string&>( + objDictEntry.first); + BMCWEB_LOG_DEBUG + << "getManagedObjectsCb parsing object " + << objPath; + + std::vector<std::string> split; + // Reserve space for + // /xyz/openbmc_project/sensors/<name>/<subname> + split.reserve(6); + boost::algorithm::split(split, objPath, + boost::is_any_of("/")); + if (split.size() < 6) + { + BMCWEB_LOG_ERROR + << "Got path that isn't long enough " + << objPath; + continue; + } + // These indexes aren't intuitive, as + // boost::split puts an empty string at the + // beggining + const std::string& sensorType = split[4]; + const std::string& sensorName = split[5]; + BMCWEB_LOG_DEBUG << "sensorName " << sensorName + << " sensorType " + << sensorType; + if (sensorNames.find(sensorName) == + sensorNames.end()) + { + BMCWEB_LOG_ERROR << sensorName + << " not in sensor list "; + continue; + } + + const char* fieldName = nullptr; + if (sensorType == "temperature") + { + fieldName = "Temperatures"; + } + else if (sensorType == "fan" || + sensorType == "fan_tach") + { + fieldName = "Fans"; + } + else if (sensorType == "voltage") + { + fieldName = "Voltages"; + } + else if (sensorType == "current") + { + fieldName = "PowerSupply"; + } + else if (sensorType == "power") + { + fieldName = "PowerSupply"; + } + else + { + BMCWEB_LOG_ERROR + << "Unsure how to handle sensorType " + << sensorType; + continue; + } + + nlohmann::json& tempArray = + SensorsAsyncResp->res.jsonValue[fieldName]; + + // Create the array if it doesn't yet exist + if (tempArray.is_array() == false) + { + tempArray = nlohmann::json::array(); + } + + tempArray.push_back( + {{"@odata.id", + "/redfish/v1/Chassis/" + + SensorsAsyncResp->chassisId + + "/Thermal#/" + sensorName}}); + nlohmann::json& sensorJson = tempArray.back(); + objectInterfacesToJson(sensorName, sensorType, + objDictEntry.second, + sensorJson); + } + BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit"; + }; + crow::connections::systemBus->async_method_call( + getManagedObjectsCb, connection, "/", + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); + }; + BMCWEB_LOG_DEBUG << "getConnectionCb exit"; }; - crow::connections::systemBus->async_method_call( - getManagedObjectsCb, connection, "/", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }; - BMCWEB_LOG_DEBUG << "getConnectionCb exit"; - }; - // get connections and then pass it to get sensors - getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb)); - BMCWEB_LOG_DEBUG << "getChassisCb exit"; - }; - - // get chassis information related to sensors - getChassis(SensorsAsyncResp, std::move(getChassisCb)); - BMCWEB_LOG_DEBUG << "getChassisData exit"; + // get connections and then pass it to get sensors + getConnections(SensorsAsyncResp, sensorNames, + std::move(getConnectionCb)); + BMCWEB_LOG_DEBUG << "getChassisCb exit"; + }; + + // get chassis information related to sensors + getChassis(SensorsAsyncResp, std::move(getChassisCb)); + BMCWEB_LOG_DEBUG << "getChassisData exit"; }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp index 6fa9632c9b..d2c5163a8a 100644 --- a/redfish-core/lib/service_root.hpp +++ b/redfish-core/lib/service_root.hpp @@ -15,62 +15,69 @@ */ #pragma once -#include <systemd/sd-id128.h> #include "node.hpp" -namespace redfish { +#include <systemd/sd-id128.h> -class ServiceRoot : public Node { - public: - ServiceRoot(CrowApp& app) : Node(app, "/redfish/v1/") { - Node::json["@odata.type"] = "#ServiceRoot.v1_1_1.ServiceRoot"; - Node::json["@odata.id"] = "/redfish/v1/"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"; - Node::json["Id"] = "RootService"; - Node::json["Name"] = "Root Service"; - Node::json["RedfishVersion"] = "1.1.0"; - Node::json["Links"]["Sessions"] = { - {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; +namespace redfish +{ - Node::json["UUID"] = getUuid(); +class ServiceRoot : public Node +{ + public: + ServiceRoot(CrowApp& app) : Node(app, "/redfish/v1/") + { + Node::json["@odata.type"] = "#ServiceRoot.v1_1_1.ServiceRoot"; + Node::json["@odata.id"] = "/redfish/v1/"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"; + Node::json["Id"] = "RootService"; + Node::json["Name"] = "Root Service"; + Node::json["RedfishVersion"] = "1.1.0"; + Node::json["Links"]["Sessions"] = { + {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; - entityPrivileges = { - {boost::beast::http::verb::get, {}}, - {boost::beast::http::verb::head, {}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } + Node::json["UUID"] = getUuid(); - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } + entityPrivileges = { + {boost::beast::http::verb::get, {}}, + {boost::beast::http::verb::head, {}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - const std::string getUuid() { - // If we are using a version of systemd that can get the app specific uuid, - // use that + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); + } + + const std::string getUuid() + { + // If we are using a version of systemd that can get the app specific + // uuid, use that #ifdef sd_id128_get_machine_app_specific - std::array<char, SD_ID128_STRING_MAX> string; - sd_id128_t id = SD_ID128_NULL; + std::array<char, SD_ID128_STRING_MAX> string; + sd_id128_t id = SD_ID128_NULL; - // This ID needs to match the one in ipmid - int r = sd_id128_get_machine_app_specific( - SD_ID128_MAKE(e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, - 45, 78), - &id); - if (r < 0) { - return "00000000-0000-0000-0000-000000000000"; - } - return string.data(); + // This ID needs to match the one in ipmid + int r = sd_id128_get_machine_app_specific( + SD_ID128_MAKE(e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, + 12, 45, 78), + &id); + if (r < 0) + { + return "00000000-0000-0000-0000-000000000000"; + } + return string.data(); #else - return "00000000-0000-0000-0000-000000000000"; + return "00000000-0000-0000-0000-000000000000"; #endif - } + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp index b7f91b9916..cd49883859 100644 --- a/redfish-core/lib/systems.hpp +++ b/redfish-core/lib/systems.hpp @@ -15,36 +15,44 @@ */ #pragma once +#include "boost/container/flat_map.hpp" +#include "node.hpp" + #include <error_messages.hpp> #include <utils/json_utils.hpp> -#include "node.hpp" -#include "boost/container/flat_map.hpp" -namespace redfish { +namespace redfish +{ /** * SystemAsyncResp * Gathers data needed for response processing after async calls are done */ -class SystemAsyncResp { - public: - SystemAsyncResp(crow::Response &response) : res(response) {} +class SystemAsyncResp +{ + public: + SystemAsyncResp(crow::Response &response) : res(response) + { + } - ~SystemAsyncResp() { - if (res.result() != (boost::beast::http::status::ok)) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = messages::internalError(); + ~SystemAsyncResp() + { + if (res.result() != (boost::beast::http::status::ok)) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = messages::internalError(); + } + res.end(); } - res.end(); - } - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); + } - crow::Response &res; + crow::Response &res; }; /** @@ -59,628 +67,853 @@ class SystemAsyncResp { * This perhaps shall be different file, which has to be chosen on compile time * depending on OEM needs */ -class OnDemandSystemsProvider { - public: - template <typename CallbackFunc> - void getBaseboardList(CallbackFunc &&callback) { - BMCWEB_LOG_DEBUG << "Get list of available boards."; - crow::connections::systemBus->async_method_call( - [callback{std::move(callback)}](const boost::system::error_code ec, - const std::vector<std::string> &resp) { - // Callback requires vector<string> to retrieve all available board - // list. - std::vector<std::string> boardList; - if (ec) { - // Something wrong on DBus, the error_code is not important at this - // moment, just return success=false, and empty output. Since size - // of vector may vary depending on information from Entity Manager, - // and empty output could not be treated same way as error. - callback(false, boardList); - return; - } - BMCWEB_LOG_DEBUG << "Got " << resp.size() << " boards."; - // Iterate over all retrieved ObjectPaths. - for (const std::string &objpath : resp) { - std::size_t lastPos = objpath.rfind("/"); - if (lastPos != std::string::npos) { - boardList.emplace_back(objpath.substr(lastPos + 1)); - } - } - // Finally make a callback with useful data - callback(true, boardList); - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", - "/xyz/openbmc_project/inventory", int32_t(0), - std::array<const char *, 1>{ - "xyz.openbmc_project.Inventory.Item.Board"}); - }; - - /** - * @brief Retrieves computer system properties over dbus - * - * @param[in] aResp Shared pointer for completing asynchronous calls - * @param[in] name Computer system name from request - * - * @return None. - */ - void getComputerSystem(std::shared_ptr<SystemAsyncResp> aResp, - const std::string &name) { - const std::array<const char *, 5> interfaces = { - "xyz.openbmc_project.Inventory.Decorator.Asset", - "xyz.openbmc_project.Inventory.Item.Cpu", - "xyz.openbmc_project.Inventory.Item.Dimm", - "xyz.openbmc_project.Inventory.Item.System", - "xyz.openbmc_project.Common.UUID", - }; - BMCWEB_LOG_DEBUG << "Get available system components."; - crow::connections::systemBus->async_method_call( - [ name, aResp{std::move(aResp)} ]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error"; - aResp->setErrorStatus(); - return; - } - bool foundName = false; - // Iterate over all retrieved ObjectPaths. - for (const std::pair<std::string, - std::vector<std::pair<std::string, - std::vector<std::string>>>> - &object : subtree) { - const std::string &path = object.first; - BMCWEB_LOG_DEBUG << "Got path: " << path; - const std::vector<std::pair<std::string, std::vector<std::string>>> - &connectionNames = object.second; - if (connectionNames.size() < 1) { - continue; - } - // Check if computer system exist - if (boost::ends_with(path, name)) { - foundName = true; - BMCWEB_LOG_DEBUG << "Found name: " << name; - const std::string connectionName = connectionNames[0].first; - crow::connections::systemBus->async_method_call( - [ aResp, name(std::string(name)) ]( - const boost::system::error_code ec, - const std::vector<std::pair<std::string, VariantType>> - &propertiesList) { - if (ec) { - BMCWEB_LOG_ERROR << "DBUS response error: " << ec; - aResp->setErrorStatus(); - return; +class OnDemandSystemsProvider +{ + public: + template <typename CallbackFunc> + void getBaseboardList(CallbackFunc &&callback) + { + BMCWEB_LOG_DEBUG << "Get list of available boards."; + crow::connections::systemBus->async_method_call( + [callback{std::move(callback)}]( + const boost::system::error_code ec, + const std::vector<std::string> &resp) { + // Callback requires vector<string> to retrieve all available + // board list. + std::vector<std::string> boardList; + if (ec) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Entity Manager, and empty output could + // not be treated same way as error. + callback(false, boardList); + return; + } + BMCWEB_LOG_DEBUG << "Got " << resp.size() << " boards."; + // Iterate over all retrieved ObjectPaths. + for (const std::string &objpath : resp) + { + std::size_t lastPos = objpath.rfind("/"); + if (lastPos != std::string::npos) + { + boardList.emplace_back(objpath.substr(lastPos + 1)); } - BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() - << "properties for system"; - for (const std::pair<std::string, VariantType> &property : - propertiesList) { - const std::string *value = - mapbox::getPtr<const std::string>(property.second); - if (value != nullptr) { - aResp->res.jsonValue[property.first] = *value; - } + } + // Finally make a callback with useful data + callback(true, boardList); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + "/xyz/openbmc_project/inventory", int32_t(0), + std::array<const char *, 1>{ + "xyz.openbmc_project.Inventory.Item.Board"}); + }; + + /** + * @brief Retrieves computer system properties over dbus + * + * @param[in] aResp Shared pointer for completing asynchronous calls + * @param[in] name Computer system name from request + * + * @return None. + */ + void getComputerSystem(std::shared_ptr<SystemAsyncResp> aResp, + const std::string &name) + { + const std::array<const char *, 5> interfaces = { + "xyz.openbmc_project.Inventory.Decorator.Asset", + "xyz.openbmc_project.Inventory.Item.Cpu", + "xyz.openbmc_project.Inventory.Item.Dimm", + "xyz.openbmc_project.Inventory.Item.System", + "xyz.openbmc_project.Common.UUID", + }; + BMCWEB_LOG_DEBUG << "Get available system components."; + crow::connections::systemBus->async_method_call( + [name, aResp{std::move(aResp)}]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error"; + aResp->setErrorStatus(); + return; + } + bool foundName = false; + // Iterate over all retrieved ObjectPaths. + for (const std::pair< + std::string, + std::vector< + std::pair<std::string, std::vector<std::string>>>> + &object : subtree) + { + const std::string &path = object.first; + BMCWEB_LOG_DEBUG << "Got path: " << path; + const std::vector< + std::pair<std::string, std::vector<std::string>>> + &connectionNames = object.second; + if (connectionNames.size() < 1) + { + continue; } - aResp->res.jsonValue["Name"] = name; - aResp->res.jsonValue["Id"] = - aResp->res.jsonValue["SerialNumber"]; - }, - connectionName, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); - } else { - // This is not system, so check if it's cpu, dimm, UUID or BiosVer - for (auto const &s : connectionNames) { - for (auto const &i : s.second) { - if (boost::ends_with(i, "Dimm")) { - BMCWEB_LOG_DEBUG << "Found Dimm, now get it properties."; - crow::connections::systemBus->async_method_call( - [&, aResp](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, VariantType>> &properties) { - if (ec) { - BMCWEB_LOG_ERROR << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() - << "Dimm properties."; - for (const auto &p : properties) { - if (p.first == "MemorySize") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - if ((value != nullptr) && (*value != "NULL")) { - // Remove units char - int32_t unitCoeff; - if (boost::ends_with(*value, "MB")) { - unitCoeff = 1000; - } else if (boost::ends_with(*value, "KB")) { - unitCoeff = 1000000; - } else { - BMCWEB_LOG_ERROR - << "Unsupported memory units"; - aResp->setErrorStatus(); - return; + // Check if computer system exist + if (boost::ends_with(path, name)) + { + foundName = true; + BMCWEB_LOG_DEBUG << "Found name: " << name; + const std::string connectionName = + connectionNames[0].first; + crow::connections::systemBus->async_method_call( + [aResp, name(std::string(name))]( + const boost::system::error_code ec, + const std::vector< + std::pair<std::string, VariantType>> + &propertiesList) { + if (ec) + { + BMCWEB_LOG_ERROR << "DBUS response error: " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Got " + << propertiesList.size() + << "properties for system"; + for (const std::pair<std::string, VariantType> + &property : propertiesList) + { + const std::string *value = + mapbox::getPtr<const std::string>( + property.second); + if (value != nullptr) + { + aResp->res.jsonValue[property.first] = + *value; + } } + aResp->res.jsonValue["Name"] = name; + aResp->res.jsonValue["Id"] = + aResp->res.jsonValue["SerialNumber"]; + }, + connectionName, path, + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Inventory.Decorator.Asset"); + } + else + { + // This is not system, so check if it's cpu, dimm, UUID + // or BiosVer + for (auto const &s : connectionNames) + { + for (auto const &i : s.second) + { + if (boost::ends_with(i, "Dimm")) + { + BMCWEB_LOG_DEBUG + << "Found Dimm, now get it properties."; + crow::connections::systemBus->async_method_call( + [&, aResp]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, VariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_ERROR + << "DBUS response error " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG + << "Got " << properties.size() + << "Dimm properties."; + for (const auto &p : properties) + { + if (p.first == "MemorySize") + { + const std::string *value = + mapbox::getPtr< + const std::string>( + p.second); + if ((value != nullptr) && + (*value != "NULL")) + { + // Remove units char + int32_t unitCoeff; + if (boost::ends_with( + *value, "MB")) + { + unitCoeff = 1000; + } + else if (boost:: + ends_with( + *value, + "KB")) + { + unitCoeff = 1000000; + } + else + { + BMCWEB_LOG_ERROR + << "Unsupported" + " memory " + "units"; + aResp + ->setErrorStatus(); + return; + } - auto memSize = boost::lexical_cast<int>( - value->substr(0, value->length() - 2)); - aResp->res.jsonValue["TotalSystemMemoryGiB"] += - memSize * unitCoeff; - aResp->res.jsonValue["MemorySummary"]["Status"] - ["State"] = "Enabled"; - } - } - } - }, - s.first, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Item.Dimm"); - } else if (boost::ends_with(i, "Cpu")) { - BMCWEB_LOG_DEBUG << "Found Cpu, now get it properties."; - crow::connections::systemBus->async_method_call( - [&, aResp](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, VariantType>> &properties) { - if (ec) { - BMCWEB_LOG_ERROR << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() - << "Cpu properties."; - for (const auto &p : properties) { - if (p.first == "ProcessorFamily") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - if (value != nullptr) { - aResp->res - .jsonValue["ProcessorSummary"]["Count"] = - aResp->res - .jsonValue["ProcessorSummary"]["Count"] - .get<int>() + - 1; - aResp->res.jsonValue["ProcessorSummary"] - ["Status"]["State"] = - "Enabled"; - aResp->res - .jsonValue["ProcessorSummary"]["Model"] = - *value; - } - } - } - }, - s.first, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Item.Cpu"); - } else if (boost::ends_with(i, "UUID")) { - BMCWEB_LOG_DEBUG << "Found UUID, now get it properties."; - crow::connections::systemBus->async_method_call( - [aResp](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, VariantType>> &properties) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() - << "UUID properties."; - for (const std::pair<std::string, VariantType> &p : - properties) { - if (p.first == "BIOSVer") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - if (value != nullptr) { - aResp->res.jsonValue["BiosVersion"] = *value; - } - } - if (p.first == "UUID") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - BMCWEB_LOG_DEBUG << "UUID = " << *value - << " length " << value->length(); - if (value != nullptr) { - // Workaround for to short return str in smbios - // demo app, 32 bytes are described by spec - if (value->length() > 0 && - value->length() < 32) { - std::string correctedValue = *value; - correctedValue.append(32 - value->length(), - '0'); - value = &correctedValue; - } else if (value->length() == 32) { - aResp->res.jsonValue["UUID"] = - value->substr(0, 8) + "-" + - value->substr(8, 4) + "-" + - value->substr(12, 4) + "-" + - value->substr(16, 4) + "-" + - value->substr(20, 12); + auto memSize = + boost::lexical_cast< + int>(value->substr( + 0, + value->length() - + 2)); + aResp->res.jsonValue + ["TotalSystemMemory" + "GiB"] += + memSize * unitCoeff; + aResp->res.jsonValue + ["MemorySummary"] + ["Status"] + ["State"] = + "Enabled"; + } + } + } + }, + s.first, path, + "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Inventory.Item." + "Dimm"); + } + else if (boost::ends_with(i, "Cpu")) + { + BMCWEB_LOG_DEBUG + << "Found Cpu, now get it properties."; + crow::connections::systemBus + ->async_method_call( + [&, aResp]( + const boost::system::error_code + ec, + const std::vector<std::pair< + std::string, VariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_ERROR + << "DBUS response " + "error " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG + << "Got " + << properties.size() + << "Cpu properties."; + for (const auto &p : properties) + { + if (p.first == + "ProcessorFamily") + { + const std::string + *value = + mapbox::getPtr< + const std:: + string>( + p.second); + if (value != nullptr) + { + aResp->res.jsonValue + ["ProcessorSumm" + "ary"] + ["Count"] = + aResp->res + .jsonValue + ["Proce" + "ssorS" + "ummar" + "y"] + ["Coun" + "t"] + .get< + int>() + + 1; + aResp->res.jsonValue + ["ProcessorSumm" + "ary"] + ["Status"] + ["State"] = + "Enabled"; + aResp->res.jsonValue + ["ProcessorSumm" + "ary"] + ["Model"] = + *value; + } + } + } + }, + s.first, path, + "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Inventory." + "Item.Cpu"); + } + else if (boost::ends_with(i, "UUID")) + { + BMCWEB_LOG_DEBUG + << "Found UUID, now get it properties."; + crow::connections::systemBus->async_method_call( + [aResp]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, VariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_DEBUG + << "DBUS response error " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG + << "Got " << properties.size() + << "UUID properties."; + for (const std::pair<std::string, + VariantType> + &p : properties) + { + if (p.first == "BIOSVer") + { + const std::string *value = + mapbox::getPtr< + const std::string>( + p.second); + if (value != nullptr) + { + aResp->res.jsonValue + ["BiosVersion"] = + *value; + } + } + if (p.first == "UUID") + { + const std::string *value = + mapbox::getPtr< + const std::string>( + p.second); + BMCWEB_LOG_DEBUG + << "UUID = " << *value + << " length " + << value->length(); + if (value != nullptr) + { + // Workaround for to + // short return str in + // smbios demo app, 32 + // bytes are described + // by spec + if (value->length() > + 0 && + value->length() < + 32) + { + std::string + correctedValue = + *value; + correctedValue.append( + 32 - + value + ->length(), + '0'); + value = + &correctedValue; + } + else if ( + value->length() == + 32) + { + aResp->res.jsonValue + ["UUID"] = + value->substr( + 0, 8) + + "-" + + value->substr( + 8, 4) + + "-" + + value->substr( + 12, 4) + + "-" + + value->substr( + 16, 4) + + "-" + + value->substr( + 20, 12); + } + } + } + } + }, + s.first, path, + "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Common.UUID"); } - } } - } - }, - s.first, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Common.UUID"); - } + } + } } - } - } - } - if (foundName == false) { - aResp->setErrorStatus(); - } - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", - "/xyz/openbmc_project/inventory", int32_t(0), interfaces); - } + if (foundName == false) + { + aResp->setErrorStatus(); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/inventory", int32_t(0), interfaces); + } - /** - * @brief Retrieves identify led group properties over dbus - * - * @param[in] aResp Shared pointer for completing asynchronous calls. - * @param[in] callback Callback for process retrieved data. - * - * @return None. - */ - template <typename CallbackFunc> - void getLedGroupIdentify(std::shared_ptr<SystemAsyncResp> aResp, - CallbackFunc &&callback) { - BMCWEB_LOG_DEBUG << "Get led groups"; - crow::connections::systemBus->async_method_call( - [ - aResp{std::move(aResp)}, &callback - ](const boost::system::error_code &ec, const ManagedObjectsType &resp) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << resp.size() << "led group objects."; - for (const auto &objPath : resp) { - const std::string &path = objPath.first; - if (path.rfind("enclosure_identify") != std::string::npos) { - for (const auto &interface : objPath.second) { - if (interface.first == "xyz.openbmc_project.Led.Group") { - for (const auto &property : interface.second) { - if (property.first == "Asserted") { - const bool *asserted = - mapbox::getPtr<const bool>(property.second); - if (nullptr != asserted) { - callback(*asserted, aResp); - } else { - callback(false, aResp); - } + /** + * @brief Retrieves identify led group properties over dbus + * + * @param[in] aResp Shared pointer for completing asynchronous calls. + * @param[in] callback Callback for process retrieved data. + * + * @return None. + */ + template <typename CallbackFunc> + void getLedGroupIdentify(std::shared_ptr<SystemAsyncResp> aResp, + CallbackFunc &&callback) + { + BMCWEB_LOG_DEBUG << "Get led groups"; + crow::connections::systemBus->async_method_call( + [aResp{std::move(aResp)}, + &callback](const boost::system::error_code &ec, + const ManagedObjectsType &resp) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Got " << resp.size() + << "led group objects."; + for (const auto &objPath : resp) + { + const std::string &path = objPath.first; + if (path.rfind("enclosure_identify") != std::string::npos) + { + for (const auto &interface : objPath.second) + { + if (interface.first == + "xyz.openbmc_project.Led.Group") + { + for (const auto &property : interface.second) + { + if (property.first == "Asserted") + { + const bool *asserted = + mapbox::getPtr<const bool>( + property.second); + if (nullptr != asserted) + { + callback(*asserted, aResp); + } + else + { + callback(false, aResp); + } + } + } + } + } } - } } - } - } - } - }, - "xyz.openbmc_project.LED.GroupManager", - "/xyz/openbmc_project/led/groups", "org.freedesktop.DBus.ObjectManager", - "GetManagedObjects"); - } + }, + "xyz.openbmc_project.LED.GroupManager", + "/xyz/openbmc_project/led/groups", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + } - template <typename CallbackFunc> - void getLedIdentify(std::shared_ptr<SystemAsyncResp> aResp, - CallbackFunc &&callback) { - BMCWEB_LOG_DEBUG << "Get identify led properties"; - crow::connections::systemBus->async_method_call( - [ aResp{std::move(aResp)}, &callback ]( - const boost::system::error_code ec, - const PropertiesType &properties) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() << "led properties."; - std::string output; - for (const auto &property : properties) { - if (property.first == "State") { - const std::string *s = - mapbox::getPtr<std::string>(property.second); - if (nullptr != s) { - BMCWEB_LOG_DEBUG << "Identify Led State: " << *s; - const auto pos = s->rfind('.'); - if (pos != std::string::npos) { - auto led = s->substr(pos + 1); - for (const std::pair<const char *, const char *> &p : - std::array<std::pair<const char *, const char *>, 3>{ - {{"On", "Lit"}, - {"Blink", "Blinking"}, - {"Off", "Off"}}}) { - if (led == p.first) { - output = p.second; + template <typename CallbackFunc> + void getLedIdentify(std::shared_ptr<SystemAsyncResp> aResp, + CallbackFunc &&callback) + { + BMCWEB_LOG_DEBUG << "Get identify led properties"; + crow::connections::systemBus->async_method_call( + [aResp{std::move(aResp)}, + &callback](const boost::system::error_code ec, + const PropertiesType &properties) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Got " << properties.size() + << "led properties."; + std::string output; + for (const auto &property : properties) + { + if (property.first == "State") + { + const std::string *s = + mapbox::getPtr<std::string>(property.second); + if (nullptr != s) + { + BMCWEB_LOG_DEBUG << "Identify Led State: " << *s; + const auto pos = s->rfind('.'); + if (pos != std::string::npos) + { + auto led = s->substr(pos + 1); + for (const std::pair<const char *, const char *> + &p : + std::array< + std::pair<const char *, const char *>, + 3>{{{"On", "Lit"}, + {"Blink", "Blinking"}, + {"Off", "Off"}}}) + { + if (led == p.first) + { + output = p.second; + } + } + } + } } - } } - } - } - } - callback(output, aResp); - }, - "xyz.openbmc_project.LED.Controller.identify", - "/xyz/openbmc_project/led/physical/identify", - "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.Led.Physical"); - } + callback(output, aResp); + }, + "xyz.openbmc_project.LED.Controller.identify", + "/xyz/openbmc_project/led/physical/identify", + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Led.Physical"); + } - /** - * @brief Retrieves host state properties over dbus - * - * @param[in] aResp Shared pointer for completing asynchronous calls. - * - * @return None. - */ - void getHostState(std::shared_ptr<SystemAsyncResp> aResp) { - BMCWEB_LOG_DEBUG << "Get host information."; - crow::connections::systemBus->async_method_call( - [aResp{std::move(aResp)}](const boost::system::error_code ec, - const PropertiesType &properties) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() << "host properties."; - for (const auto &property : properties) { - if (property.first == "CurrentHostState") { - const std::string *s = - mapbox::getPtr<const std::string>(property.second); - BMCWEB_LOG_DEBUG << "Host state: " << *s; - if (nullptr != s) { - const auto pos = s->rfind('.'); - if (pos != std::string::npos) { - // Verify Host State - if (s->substr(pos + 1) == "Running") { - aResp->res.jsonValue["PowerState"] = "On"; - aResp->res.jsonValue["Status"]["State"] = "Enabled"; - } else { - aResp->res.jsonValue["PowerState"] = "Off"; - aResp->res.jsonValue["Status"]["State"] = "Disabled"; - } + /** + * @brief Retrieves host state properties over dbus + * + * @param[in] aResp Shared pointer for completing asynchronous calls. + * + * @return None. + */ + void getHostState(std::shared_ptr<SystemAsyncResp> aResp) + { + BMCWEB_LOG_DEBUG << "Get host information."; + crow::connections::systemBus->async_method_call( + [aResp{std::move(aResp)}](const boost::system::error_code ec, + const PropertiesType &properties) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + aResp->setErrorStatus(); + return; } - } - } - } - }, - "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", - "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.State.Host"); - } + BMCWEB_LOG_DEBUG << "Got " << properties.size() + << "host properties."; + for (const auto &property : properties) + { + if (property.first == "CurrentHostState") + { + const std::string *s = + mapbox::getPtr<const std::string>(property.second); + BMCWEB_LOG_DEBUG << "Host state: " << *s; + if (nullptr != s) + { + const auto pos = s->rfind('.'); + if (pos != std::string::npos) + { + // Verify Host State + if (s->substr(pos + 1) == "Running") + { + aResp->res.jsonValue["PowerState"] = "On"; + aResp->res.jsonValue["Status"]["State"] = + "Enabled"; + } + else + { + aResp->res.jsonValue["PowerState"] = "Off"; + aResp->res.jsonValue["Status"]["State"] = + "Disabled"; + } + } + } + } + } + }, + "xyz.openbmc_project.State.Host", + "/xyz/openbmc_project/state/host0", + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.State.Host"); + } }; /** * SystemsCollection derived class for delivering ComputerSystems Collection * Schema */ -class SystemsCollection : public Node { - public: - SystemsCollection(CrowApp &app) : Node(app, "/redfish/v1/Systems/") { - Node::json["@odata.type"] = - "#ComputerSystemCollection.ComputerSystemCollection"; - Node::json["@odata.id"] = "/redfish/v1/Systems"; - Node::json["@odata.context"] = - "/redfish/v1/" - "$metadata#ComputerSystemCollection.ComputerSystemCollection"; - Node::json["Name"] = "Computer System Collection"; +class SystemsCollection : public Node +{ + public: + SystemsCollection(CrowApp &app) : Node(app, "/redfish/v1/Systems/") + { + Node::json["@odata.type"] = + "#ComputerSystemCollection.ComputerSystemCollection"; + Node::json["@odata.id"] = "/redfish/v1/Systems"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#ComputerSystemCollection.ComputerSystemCollection"; + Node::json["Name"] = "Computer System Collection"; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // Get board list, and call the below callback for JSON preparation - provider.getBaseboardList( - [&](const bool &success, const std::vector<std::string> &output) { - if (success) { - // ... prepare json array with appropriate @odata.id links - nlohmann::json boardArray = nlohmann::json::array(); - for (const std::string &boardItem : output) { - boardArray.push_back( - {{"@odata.id", "/redfish/v1/Systems/" + boardItem}}); + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // Get board list, and call the below callback for JSON preparation + provider.getBaseboardList([&](const bool &success, + const std::vector<std::string> &output) { + if (success) + { + // ... prepare json array with appropriate @odata.id links + nlohmann::json boardArray = nlohmann::json::array(); + for (const std::string &boardItem : output) + { + boardArray.push_back( + {{"@odata.id", "/redfish/v1/Systems/" + boardItem}}); + } + // Then attach members, count size and return, + Node::json["Members"] = boardArray; + Node::json["Members@odata.count"] = boardArray.size(); + res.jsonValue = Node::json; } - // Then attach members, count size and return, - Node::json["Members"] = boardArray; - Node::json["Members@odata.count"] = boardArray.size(); - res.jsonValue = Node::json; - } else { - // ... otherwise, return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); + else + { + // ... otherwise, return INTERNALL ERROR + res.result(boost::beast::http::status::internal_server_error); + } + res.end(); }); - } + } - OnDemandSystemsProvider provider; + OnDemandSystemsProvider provider; }; /** * Systems override class for delivering ComputerSystems Schema */ -class Systems : public Node { - public: - /* - * Default Constructor - */ - Systems(CrowApp &app) - : Node(app, "/redfish/v1/Systems/<str>/", std::string()) { - Node::json["@odata.type"] = "#ComputerSystem.v1_3_0.ComputerSystem"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ComputerSystem.ComputerSystem"; - Node::json["SystemType"] = "Physical"; - Node::json["Description"] = "Computer System"; - Node::json["Boot"]["BootSourceOverrideEnabled"] = - "Disabled"; // TODO(Dawid), get real boot data - Node::json["Boot"]["BootSourceOverrideTarget"] = - "None"; // TODO(Dawid), get real boot data - Node::json["Boot"]["BootSourceOverrideMode"] = - "Legacy"; // TODO(Dawid), get real boot data - Node::json["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"] = { - "None", "Pxe", "Hdd", "Cd", - "BiosSetup", "UefiShell", "Usb"}; // TODO(Dawid), get real boot data - Node::json["ProcessorSummary"]["Count"] = int(0); - Node::json["ProcessorSummary"]["Status"]["State"] = "Disabled"; - Node::json["MemorySummary"]["TotalSystemMemoryGiB"] = int(0); - Node::json["MemorySummary"]["Status"]["State"] = "Disabled"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } +class Systems : public Node +{ + public: + /* + * Default Constructor + */ + Systems(CrowApp &app) : + Node(app, "/redfish/v1/Systems/<str>/", std::string()) + { + Node::json["@odata.type"] = "#ComputerSystem.v1_3_0.ComputerSystem"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ComputerSystem.ComputerSystem"; + Node::json["SystemType"] = "Physical"; + Node::json["Description"] = "Computer System"; + Node::json["Boot"]["BootSourceOverrideEnabled"] = + "Disabled"; // TODO(Dawid), get real boot data + Node::json["Boot"]["BootSourceOverrideTarget"] = + "None"; // TODO(Dawid), get real boot data + Node::json["Boot"]["BootSourceOverrideMode"] = + "Legacy"; // TODO(Dawid), get real boot data + Node::json["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"] = + {"None", "Pxe", "Hdd", "Cd", + "BiosSetup", "UefiShell", "Usb"}; // TODO(Dawid), get real boot + // data + Node::json["ProcessorSummary"]["Count"] = int(0); + Node::json["ProcessorSummary"]["Status"]["State"] = "Disabled"; + Node::json["MemorySummary"]["TotalSystemMemoryGiB"] = int(0); + Node::json["MemorySummary"]["Status"]["State"] = "Disabled"; - private: - OnDemandSystemsProvider provider; - - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // Check if there is required param, truly entering this shall be - // impossible - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - const std::string &name = params[0]; + private: + OnDemandSystemsProvider provider; + + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // Check if there is required param, truly entering this shall be + // impossible + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - res.jsonValue = Node::json; - res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; + const std::string &name = params[0]; - auto asyncResp = std::make_shared<SystemAsyncResp>(res); + res.jsonValue = Node::json; + res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; - provider.getLedGroupIdentify( - asyncResp, [&](const bool &asserted, - const std::shared_ptr<SystemAsyncResp> &aResp) { - if (asserted) { - // If led group is asserted, then another call is needed to - // get led status - provider.getLedIdentify( - aResp, [](const std::string &ledStatus, - const std::shared_ptr<SystemAsyncResp> &aResp) { - if (!ledStatus.empty()) { - aResp->res.jsonValue["IndicatorLED"] = ledStatus; - } - }); - } else { - aResp->res.jsonValue["IndicatorLED"] = "Off"; - } - }); - provider.getComputerSystem(asyncResp, name); - provider.getHostState(asyncResp); - } + auto asyncResp = std::make_shared<SystemAsyncResp>(res); - void doPatch(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // Check if there is required param, truly entering this shall be - // impossible - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - // Parse JSON request body - nlohmann::json patch; - if (!json_util::processJsonFromRequest(res, req, patch)) { - return; - } - // Find key with new led value - const std::string &name = params[0]; - const std::string *reqLedState = nullptr; - json_util::Result r = json_util::getString( - "IndicatorLED", patch, reqLedState, - static_cast<int>(json_util::MessageSetting::TYPE_ERROR) | - static_cast<int>(json_util::MessageSetting::MISSING), - res.jsonValue, std::string("/" + name + "/IndicatorLED")); - if ((r != json_util::Result::SUCCESS) || (reqLedState == nullptr)) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - // Verify key value - std::string dbusLedState; - for (const auto &p : boost::container::flat_map<const char *, const char *>{ - {"On", "Lit"}, {"Blink", "Blinking"}, {"Off", "Off"}}) { - if (*reqLedState == p.second) { - dbusLedState = p.first; - } + provider.getLedGroupIdentify( + asyncResp, [&](const bool &asserted, + const std::shared_ptr<SystemAsyncResp> &aResp) { + if (asserted) + { + // If led group is asserted, then another call is needed to + // get led status + provider.getLedIdentify( + aResp, + [](const std::string &ledStatus, + const std::shared_ptr<SystemAsyncResp> &aResp) { + if (!ledStatus.empty()) + { + aResp->res.jsonValue["IndicatorLED"] = + ledStatus; + } + }); + } + else + { + aResp->res.jsonValue["IndicatorLED"] = "Off"; + } + }); + provider.getComputerSystem(asyncResp, name); + provider.getHostState(asyncResp); } - // Update led status - auto asyncResp = std::make_shared<SystemAsyncResp>(res); - res.jsonValue = Node::json; - res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; + void doPatch(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // Check if there is required param, truly entering this shall be + // impossible + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + // Parse JSON request body + nlohmann::json patch; + if (!json_util::processJsonFromRequest(res, req, patch)) + { + return; + } + // Find key with new led value + const std::string &name = params[0]; + const std::string *reqLedState = nullptr; + json_util::Result r = json_util::getString( + "IndicatorLED", patch, reqLedState, + static_cast<int>(json_util::MessageSetting::TYPE_ERROR) | + static_cast<int>(json_util::MessageSetting::MISSING), + res.jsonValue, std::string("/" + name + "/IndicatorLED")); + if ((r != json_util::Result::SUCCESS) || (reqLedState == nullptr)) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + // Verify key value + std::string dbusLedState; + for (const auto &p : + boost::container::flat_map<const char *, const char *>{ + {"On", "Lit"}, {"Blink", "Blinking"}, {"Off", "Off"}}) + { + if (*reqLedState == p.second) + { + dbusLedState = p.first; + } + } - provider.getHostState(asyncResp); - provider.getComputerSystem(asyncResp, name); + // Update led status + auto asyncResp = std::make_shared<SystemAsyncResp>(res); + res.jsonValue = Node::json; + res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; - if (dbusLedState.empty()) { - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyValueNotInList(*reqLedState, "IndicatorLED")); - } else { - // Update led group - BMCWEB_LOG_DEBUG << "Update led group."; - crow::connections::systemBus->async_method_call( - [&, asyncResp{std::move(asyncResp)} ]( - const boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - asyncResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Led group update done."; - }, - "xyz.openbmc_project.LED.GroupManager", - "/xyz/openbmc_project/led/groups/enclosure_identify", - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Led.Group", "Asserted", - sdbusplus::message::variant<bool>( - (dbusLedState == "Off" ? false : true))); - // Update identify led status - BMCWEB_LOG_DEBUG << "Update led SoftwareInventoryCollection."; - crow::connections::systemBus->async_method_call( - [&, asyncResp{std::move(asyncResp)} ]( - const boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - asyncResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Led state update done."; - res.jsonValue["IndicatorLED"] = *reqLedState; - }, - "xyz.openbmc_project.LED.Controller.identify", - "/xyz/openbmc_project/led/physical/identify", - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Led.Physical", "State", - sdbusplus::message::variant<std::string>( - "xyz.openbmc_project.Led.Physical.Action." + dbusLedState)); + provider.getHostState(asyncResp); + provider.getComputerSystem(asyncResp, name); + + if (dbusLedState.empty()) + { + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyValueNotInList(*reqLedState, "IndicatorLED")); + } + else + { + // Update led group + BMCWEB_LOG_DEBUG << "Update led group."; + crow::connections::systemBus->async_method_call( + [&, asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + asyncResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Led group update done."; + }, + "xyz.openbmc_project.LED.GroupManager", + "/xyz/openbmc_project/led/groups/enclosure_identify", + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Led.Group", "Asserted", + sdbusplus::message::variant<bool>( + (dbusLedState == "Off" ? false : true))); + // Update identify led status + BMCWEB_LOG_DEBUG << "Update led SoftwareInventoryCollection."; + crow::connections::systemBus->async_method_call( + [&, asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + asyncResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Led state update done."; + res.jsonValue["IndicatorLED"] = *reqLedState; + }, + "xyz.openbmc_project.LED.Controller.identify", + "/xyz/openbmc_project/led/physical/identify", + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Led.Physical", "State", + sdbusplus::message::variant<std::string>( + "xyz.openbmc_project.Led.Physical.Action." + dbusLedState)); + } } - } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp index 8fb291b9bb..37b0f43f3a 100644 --- a/redfish-core/lib/thermal.hpp +++ b/redfish-core/lib/thermal.hpp @@ -18,44 +18,49 @@ #include "node.hpp" #include "sensors.hpp" -namespace redfish { +namespace redfish +{ -class Thermal : public Node { - public: - Thermal(CrowApp& app) - : Node((app), "/redfish/v1/Chassis/<str>/Thermal/", std::string()) { - Node::json["@odata.type"] = "#Thermal.v1_4_0.Thermal"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Thermal.Thermal"; - Node::json["Id"] = "Thermal"; - Node::json["Name"] = "Thermal"; +class Thermal : public Node +{ + public: + Thermal(CrowApp& app) : + Node((app), "/redfish/v1/Chassis/<str>/Thermal/", std::string()) + { + Node::json["@odata.type"] = "#Thermal.v1_4_0.Thermal"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Thermal.Thermal"; + Node::json["Id"] = "Thermal"; + Node::json["Name"] = "Thermal"; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; } - const std::string& chassisName = params[0]; - res.jsonValue = Node::json; - auto asyncResp = std::make_shared<SensorsAsyncResp>( - res, chassisName, - std::initializer_list<const char*>{ - "/xyz/openbmc_project/sensors/fan", - "/xyz/openbmc_project/sensors/temperature"}); - getChassisData(asyncResp); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + const std::string& chassisName = params[0]; + + res.jsonValue = Node::json; + auto asyncResp = std::make_shared<SensorsAsyncResp>( + res, chassisName, + std::initializer_list<const char*>{ + "/xyz/openbmc_project/sensors/fan", + "/xyz/openbmc_project/sensors/temperature"}); + getChassisData(asyncResp); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp index 677b6d1741..658937d9a5 100644 --- a/redfish-core/lib/update_service.hpp +++ b/redfish-core/lib/update_service.hpp @@ -16,381 +16,450 @@ #pragma once #include "node.hpp" + #include <boost/container/flat_map.hpp> -namespace redfish { +namespace redfish +{ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; -class UpdateService : public Node { - public: - UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") { - Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService"; - Node::json["@odata.id"] = "/redfish/v1/UpdateService"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#UpdateService.UpdateService"; - Node::json["Id"] = "UpdateService"; - Node::json["Description"] = "Service for Software Update"; - Node::json["Name"] = "Update Service"; - Node::json["HttpPushUri"] = "/redfish/v1/UpdateService"; - // UpdateService cannot be disabled - Node::json["ServiceEnabled"] = true; - Node::json["FirmwareInventory"] = { - {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - res.jsonValue = Node::json; - res.end(); - } - static void activateImage(const std::string &objPath) { - crow::connections::systemBus->async_method_call( - [objPath](const boost::system::error_code error_code) { - if (error_code) { - BMCWEB_LOG_DEBUG << "error_code = " << error_code; - BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); - } - }, - "xyz.openbmc_project.Software.BMC.Updater", objPath, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Software.Activation", "RequestedActivation", - sdbusplus::message::variant<std::string>( - "xyz.openbmc_project.Software.Activation.RequestedActivations." - "Active")); - } - void doPost(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - BMCWEB_LOG_DEBUG << "doPost..."; - - // Only allow one FW update at a time - if (fwUpdateMatcher != nullptr) { - res.addHeader("Retry-After", "30"); - res.result(boost::beast::http::status::service_unavailable); - res.jsonValue = messages::serviceTemporarilyUnavailable("3"); - res.end(); - return; +class UpdateService : public Node +{ + public: + UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") + { + Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService"; + Node::json["@odata.id"] = "/redfish/v1/UpdateService"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#UpdateService.UpdateService"; + Node::json["Id"] = "UpdateService"; + Node::json["Description"] = "Service for Software Update"; + Node::json["Name"] = "Update Service"; + Node::json["HttpPushUri"] = "/redfish/v1/UpdateService"; + // UpdateService cannot be disabled + Node::json["ServiceEnabled"] = true; + Node::json["FirmwareInventory"] = { + {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - // Make this const static so it survives outside this method - static boost::asio::deadline_timer timeout(*req.ioService, - boost::posix_time::seconds(5)); - - timeout.expires_from_now(boost::posix_time::seconds(5)); - - timeout.async_wait([&res](const boost::system::error_code &ec) { - fwUpdateMatcher = nullptr; - if (ec == boost::asio::error::operation_aborted) { - // expected, we were canceled before the timer completed. - return; - } - BMCWEB_LOG_ERROR << "Timed out waiting for firmware object being created"; - BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server"; - if (ec) { - BMCWEB_LOG_ERROR << "Async_wait failed" << ec; - return; - } - - res.result(boost::beast::http::status::internal_server_error); - res.jsonValue = redfish::messages::internalError(); - res.end(); - }); - - auto callback = [&res](sdbusplus::message::message &m) { - BMCWEB_LOG_DEBUG << "Match fired"; - bool flag = false; - - if (m.is_method_error()) { - BMCWEB_LOG_DEBUG << "Dbus method error!!!"; + + private: + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + res.jsonValue = Node::json; res.end(); - return; - } - std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, - sdbusplus::message::variant<std::string>>>>> - interfaces_properties; - - sdbusplus::message::object_path objPath; - - m.read(objPath, interfaces_properties); // Read in the object path - // that was just created - // std::string str_objpath = objPath.str; // keep a copy for - // constructing response message - BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath; - for (auto &interface : interfaces_properties) { - BMCWEB_LOG_DEBUG << "interface = " << interface.first; - - if (interface.first == "xyz.openbmc_project.Software.Activation") { - // cancel timer only when xyz.openbmc_project.Software.Activation - // interface is added - boost::system::error_code ec; - timeout.cancel(ec); - if (ec) { - BMCWEB_LOG_ERROR << "error canceling timer " << ec; - } - UpdateService::activateImage(objPath.str); // str_objpath); - res.jsonValue = redfish::messages::success(); - BMCWEB_LOG_DEBUG << "ending response"; - res.end(); - fwUpdateMatcher = nullptr; + } + static void activateImage(const std::string &objPath) + { + crow::connections::systemBus->async_method_call( + [objPath](const boost::system::error_code error_code) { + if (error_code) + { + BMCWEB_LOG_DEBUG << "error_code = " << error_code; + BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); + } + }, + "xyz.openbmc_project.Software.BMC.Updater", objPath, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Software.Activation", "RequestedActivation", + sdbusplus::message::variant<std::string>( + "xyz.openbmc_project.Software.Activation.RequestedActivations." + "Active")); + } + void doPost(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + BMCWEB_LOG_DEBUG << "doPost..."; + + // Only allow one FW update at a time + if (fwUpdateMatcher != nullptr) + { + res.addHeader("Retry-After", "30"); + res.result(boost::beast::http::status::service_unavailable); + res.jsonValue = messages::serviceTemporarilyUnavailable("3"); + res.end(); + return; } - } - }; - - fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, - "interface='org.freedesktop.DBus.ObjectManager',type='signal'," - "member='InterfacesAdded',path='/xyz/openbmc_project/software'", - callback); - - std::string filepath( - "/tmp/images/" + - boost::uuids::to_string(boost::uuids::random_generator()())); - BMCWEB_LOG_DEBUG << "Writing file to " << filepath; - std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | - std::ofstream::trunc); - out << req.body; - out.close(); - BMCWEB_LOG_DEBUG << "file upload complete!!"; - } -}; + // Make this const static so it survives outside this method + static boost::asio::deadline_timer timeout( + *req.ioService, boost::posix_time::seconds(5)); + + timeout.expires_from_now(boost::posix_time::seconds(5)); + + timeout.async_wait([&res](const boost::system::error_code &ec) { + fwUpdateMatcher = nullptr; + if (ec == boost::asio::error::operation_aborted) + { + // expected, we were canceled before the timer completed. + return; + } + BMCWEB_LOG_ERROR + << "Timed out waiting for firmware object being created"; + BMCWEB_LOG_ERROR + << "FW image may has already been uploaded to server"; + if (ec) + { + BMCWEB_LOG_ERROR << "Async_wait failed" << ec; + return; + } -class SoftwareInventoryCollection : public Node { - public: - template <typename CrowApp> - SoftwareInventoryCollection(CrowApp &app) - : Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") { - Node::json["@odata.type"] = - "#SoftwareInventoryCollection.SoftwareInventoryCollection"; - Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory"; - Node::json["@odata.context"] = - "/redfish/v1/" - "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection"; - Node::json["Name"] = "Software Inventory Collection"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); - res.jsonValue = Node::json; - - crow::connections::systemBus->async_method_call( - [asyncResp]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - if (ec) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = 0; - - for (auto &obj : subtree) { - const std::vector<std::pair<std::string, std::vector<std::string>>> - &connections = obj.second; - - for (auto &conn : connections) { - const std::string &connectionName = conn.first; - BMCWEB_LOG_DEBUG << "connectionName = " << connectionName; - BMCWEB_LOG_DEBUG << "obj.first = " << obj.first; - - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code error_code, - const VariantType &activation) { - BMCWEB_LOG_DEBUG << "safe returned in lambda function"; - if (error_code) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } + res.result(boost::beast::http::status::internal_server_error); + res.jsonValue = redfish::messages::internalError(); + res.end(); + }); - const std::string *sw_inv_purpose = - mapbox::getPtr<const std::string>(activation); - if (sw_inv_purpose == nullptr) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - std::size_t last_pos = sw_inv_purpose->rfind("."); - if (last_pos == std::string::npos) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; + auto callback = [&res](sdbusplus::message::message &m) { + BMCWEB_LOG_DEBUG << "Match fired"; + bool flag = false; + + if (m.is_method_error()) + { + BMCWEB_LOG_DEBUG << "Dbus method error!!!"; + res.end(); + return; + } + std::vector<std::pair< + std::string, + std::vector<std::pair< + std::string, sdbusplus::message::variant<std::string>>>>> + interfaces_properties; + + sdbusplus::message::object_path objPath; + + m.read(objPath, interfaces_properties); // Read in the object path + // that was just created + // std::string str_objpath = objPath.str; // keep a copy for + // constructing response message + BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath; + for (auto &interface : interfaces_properties) + { + BMCWEB_LOG_DEBUG << "interface = " << interface.first; + + if (interface.first == + "xyz.openbmc_project.Software.Activation") + { + // cancel timer only when + // xyz.openbmc_project.Software.Activation interface is + // added + boost::system::error_code ec; + timeout.cancel(ec); + if (ec) + { + BMCWEB_LOG_ERROR << "error canceling timer " << ec; } - nlohmann::json &members = - asyncResp->res.jsonValue["Members"]; - members.push_back( - {{"@odata.id", - "/redfish/v1/UpdateService/FirmwareInventory/" + - sw_inv_purpose->substr(last_pos + 1)}}); - asyncResp->res.jsonValue["Members@odata.count"] = - members.size(); - }, - connectionName, obj.first, "org.freedesktop.DBus.Properties", - "Get", "xyz.openbmc_project.Software.Activation", - "Activation"); + UpdateService::activateImage(objPath.str); // str_objpath); + res.jsonValue = redfish::messages::success(); + BMCWEB_LOG_DEBUG << "ending response"; + res.end(); + fwUpdateMatcher = nullptr; + } } - } - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", - "/xyz/openbmc_project/software", int32_t(1), - std::array<const char *, 1>{"xyz.openbmc_project.Software.Version"}); - } + }; + + fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + "interface='org.freedesktop.DBus.ObjectManager',type='signal'," + "member='InterfacesAdded',path='/xyz/openbmc_project/software'", + callback); + + std::string filepath( + "/tmp/images/" + + boost::uuids::to_string(boost::uuids::random_generator()())); + BMCWEB_LOG_DEBUG << "Writing file to " << filepath; + std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | + std::ofstream::trunc); + out << req.body; + out.close(); + BMCWEB_LOG_DEBUG << "file upload complete!!"; + } }; -class SoftwareInventory : public Node { - public: - template <typename CrowApp> - SoftwareInventory(CrowApp &app) - : Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/", - std::string()) { - Node::json["@odata.type"] = "#SoftwareInventory.v1_1_0.SoftwareInventory"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory"; - Node::json["Name"] = "Software Inventory"; - Node::json["Updateable"] = false; - Node::json["Status"]["Health"] = "OK"; - Node::json["Status"]["HealthRollup"] = "OK"; - Node::json["Status"]["State"] = "Enabled"; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); - res.jsonValue = Node::json; - - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.jsonValue = messages::internalError(); - res.end(); - return; +class SoftwareInventoryCollection : public Node +{ + public: + template <typename CrowApp> + SoftwareInventoryCollection(CrowApp &app) : + Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") + { + Node::json["@odata.type"] = + "#SoftwareInventoryCollection.SoftwareInventoryCollection"; + Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection"; + Node::json["Name"] = "Software Inventory Collection"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - std::shared_ptr<std::string> sw_id = - std::make_shared<std::string>(params[0]); + private: + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + res.jsonValue = Node::json; + + crow::connections::systemBus->async_method_call( + [asyncResp]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + if (ec) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = 0; + + for (auto &obj : subtree) + { + const std::vector< + std::pair<std::string, std::vector<std::string>>> + &connections = obj.second; + + for (auto &conn : connections) + { + const std::string &connectionName = conn.first; + BMCWEB_LOG_DEBUG << "connectionName = " + << connectionName; + BMCWEB_LOG_DEBUG << "obj.first = " << obj.first; + + crow::connections::systemBus->async_method_call( + [asyncResp]( + const boost::system::error_code error_code, + const VariantType &activation) { + BMCWEB_LOG_DEBUG + << "safe returned in lambda function"; + if (error_code) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + + const std::string *sw_inv_purpose = + mapbox::getPtr<const std::string>( + activation); + if (sw_inv_purpose == nullptr) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + std::size_t last_pos = + sw_inv_purpose->rfind("."); + if (last_pos == std::string::npos) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + nlohmann::json &members = + asyncResp->res.jsonValue["Members"]; + members.push_back( + {{"@odata.id", "/redfish/v1/UpdateService/" + "FirmwareInventory/" + + sw_inv_purpose->substr( + last_pos + 1)}}); + asyncResp->res + .jsonValue["Members@odata.count"] = + members.size(); + }, + connectionName, obj.first, + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.Software.Activation", + "Activation"); + } + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/software", int32_t(1), + std::array<const char *, 1>{ + "xyz.openbmc_project.Software.Version"}); + } +}; - res.jsonValue["@odata.id"] = - "/redfish/v1/UpdateService/FirmwareInventory/" + *sw_id; +class SoftwareInventory : public Node +{ + public: + template <typename CrowApp> + SoftwareInventory(CrowApp &app) : + Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/", + std::string()) + { + Node::json["@odata.type"] = + "#SoftwareInventory.v1_1_0.SoftwareInventory"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory"; + Node::json["Name"] = "Software Inventory"; + Node::json["Updateable"] = false; + Node::json["Status"]["Health"] = "OK"; + Node::json["Status"]["HealthRollup"] = "OK"; + Node::json["Status"]["State"] = "Enabled"; + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - crow::connections::systemBus->async_method_call( - [asyncResp, sw_id]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - BMCWEB_LOG_DEBUG << "doGet callback..."; - if (ec) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); + private: + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + res.jsonValue = Node::json; + + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.jsonValue = messages::internalError(); + res.end(); return; - } - - for (const std::pair<std::string, - std::vector<std::pair<std::string, - std::vector<std::string>>>> - &obj : subtree) { - if (boost::ends_with(obj.first, *sw_id) != true) { - continue; - } - - if (obj.second.size() <= 1) { - continue; - } + } - crow::connections::systemBus->async_method_call( - [asyncResp, sw_id]( - const boost::system::error_code error_code, - const boost::container::flat_map<std::string, VariantType> - &propertiesList) { - if (error_code) { + std::shared_ptr<std::string> sw_id = + std::make_shared<std::string>(params[0]); + + res.jsonValue["@odata.id"] = + "/redfish/v1/UpdateService/FirmwareInventory/" + *sw_id; + + crow::connections::systemBus->async_method_call( + [asyncResp, sw_id]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + BMCWEB_LOG_DEBUG << "doGet callback..."; + if (ec) + { asyncResp->res.result( boost::beast::http::status::internal_server_error); return; - } - boost::container::flat_map<std::string, - VariantType>::const_iterator it = - propertiesList.find("Purpose"); - if (it == propertiesList.end()) { - BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - const std::string *sw_inv_purpose = - mapbox::getPtr<const std::string>(it->second); - if (sw_inv_purpose == nullptr) { - BMCWEB_LOG_DEBUG << "wrong types for property\"Purpose\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - BMCWEB_LOG_DEBUG << "sw_inv_purpose = " << *sw_inv_purpose; - if (boost::ends_with(*sw_inv_purpose, "." + *sw_id)) { - it = propertiesList.find("Version"); - if (it == propertiesList.end()) { - BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; + } + + for (const std::pair< + std::string, + std::vector< + std::pair<std::string, std::vector<std::string>>>> + &obj : subtree) + { + if (boost::ends_with(obj.first, *sw_id) != true) + { + continue; } - const std::string *version = - mapbox::getPtr<const std::string>(it->second); - - if (version != nullptr) { - BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; + if (obj.second.size() <= 1) + { + continue; } - asyncResp->res.jsonValue["Version"] = *version; - asyncResp->res.jsonValue["Id"] = *sw_id; - } - }, - obj.second[0].first, obj.first, - "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.Software.Version"); - } - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", - "/xyz/openbmc_project/software", int32_t(1), - std::array<const char *, 1>{"xyz.openbmc_project.Software.Version"}); - } + + crow::connections::systemBus->async_method_call( + [asyncResp, + sw_id](const boost::system::error_code error_code, + const boost::container::flat_map< + std::string, VariantType> &propertiesList) { + if (error_code) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + boost::container::flat_map< + std::string, VariantType>::const_iterator it = + propertiesList.find("Purpose"); + if (it == propertiesList.end()) + { + BMCWEB_LOG_DEBUG + << "Can't find property \"Purpose\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + const std::string *sw_inv_purpose = + mapbox::getPtr<const std::string>(it->second); + if (sw_inv_purpose == nullptr) + { + BMCWEB_LOG_DEBUG + << "wrong types for property\"Purpose\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + + BMCWEB_LOG_DEBUG << "sw_inv_purpose = " + << *sw_inv_purpose; + if (boost::ends_with(*sw_inv_purpose, "." + *sw_id)) + { + it = propertiesList.find("Version"); + if (it == propertiesList.end()) + { + BMCWEB_LOG_DEBUG + << "Can't find property \"Version\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + + const std::string *version = + mapbox::getPtr<const std::string>( + it->second); + + if (version != nullptr) + { + BMCWEB_LOG_DEBUG + << "Can't find property \"Version\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + asyncResp->res.jsonValue["Version"] = *version; + asyncResp->res.jsonValue["Id"] = *sw_id; + } + }, + obj.second[0].first, obj.first, + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Software.Version"); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/software", int32_t(1), + std::array<const char *, 1>{ + "xyz.openbmc_project.Software.Version"}); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp index 4773e3d8a7..a8077629ac 100644 --- a/redfish-core/src/error_messages.cpp +++ b/redfish-core/src/error_messages.cpp @@ -13,78 +13,93 @@ // See the License for the specific language governing permissions and // limitations under the License. */ -#include <error_messages.hpp> #include <crow/logging.h> -namespace redfish { +#include <error_messages.hpp> -namespace messages { +namespace redfish +{ -void addMessageToErrorJson(nlohmann::json& target, - const nlohmann::json& message) { - auto& error = target["error"]; - - // If this is the first error message, fill in the information from the first - // error message to the top level struct - if (!error.is_object()) { - auto message_id_iterator = message.find("MessageId"); - if (message_id_iterator == message.end()) { - BMCWEB_LOG_CRITICAL << "Attempt to add error message without MessageId"; - return; - } +namespace messages +{ - auto message_field_iterator = message.find("Message"); - if (message_field_iterator == message.end()) { - BMCWEB_LOG_CRITICAL << "Attempt to add error message without Message"; - return; - } - // clang-format off +void addMessageToErrorJson(nlohmann::json& target, + const nlohmann::json& message) +{ + auto& error = target["error"]; + + // If this is the first error message, fill in the information from the + // first error message to the top level struct + if (!error.is_object()) + { + auto message_id_iterator = message.find("MessageId"); + if (message_id_iterator == message.end()) + { + BMCWEB_LOG_CRITICAL + << "Attempt to add error message without MessageId"; + return; + } + + auto message_field_iterator = message.find("Message"); + if (message_field_iterator == message.end()) + { + BMCWEB_LOG_CRITICAL + << "Attempt to add error message without Message"; + return; + } + // clang-format off error = { {"code", *message_id_iterator}, {"message", *message_field_iterator} }; - // clang-format on - } else { - // More than 1 error occurred, so the message has to be generic - error["code"] = std::string(messageVersionPrefix) + "GeneralError"; - error["message"] = - "A general error has occurred. See ExtendedInfo for more" - "information."; - } - - // This check could technically be done in in the default construction - // branch above, but because we need the pointer to the extended info field - // anyway, it's more efficient to do it here. - auto& extended_info = error[messages::messageAnnotation]; - if (!extended_info.is_array()) { - extended_info = nlohmann::json::array(); - } - - extended_info.push_back(message); + // clang-format on + } + else + { + // More than 1 error occurred, so the message has to be generic + error["code"] = std::string(messageVersionPrefix) + "GeneralError"; + error["message"] = + "A general error has occurred. See ExtendedInfo for more" + "information."; + } + + // This check could technically be done in in the default construction + // branch above, but because we need the pointer to the extended info field + // anyway, it's more efficient to do it here. + auto& extended_info = error[messages::messageAnnotation]; + if (!extended_info.is_array()) + { + extended_info = nlohmann::json::array(); + } + + extended_info.push_back(message); } -void addMessageToJsonRoot(nlohmann::json& target, - const nlohmann::json& message) { - if (!target[messages::messageAnnotation].is_array()) { - // Force object to be an array - target[messages::messageAnnotation] = nlohmann::json::array(); - } +void addMessageToJsonRoot(nlohmann::json& target, const nlohmann::json& message) +{ + if (!target[messages::messageAnnotation].is_array()) + { + // Force object to be an array + target[messages::messageAnnotation] = nlohmann::json::array(); + } - target[messages::messageAnnotation].push_back(message); + target[messages::messageAnnotation].push_back(message); } void addMessageToJson(nlohmann::json& target, const nlohmann::json& message, - const std::string& fieldPath) { - nlohmann::json_pointer<nlohmann::json> extendedInfo( - fieldPath + messages::messageAnnotation); - - if (!target[extendedInfo].is_array()) { - // Force object to be an array - target[extendedInfo] = nlohmann::json::array(); - } + const std::string& fieldPath) +{ + nlohmann::json_pointer<nlohmann::json> extendedInfo( + fieldPath + messages::messageAnnotation); + + if (!target[extendedInfo].is_array()) + { + // Force object to be an array + target[extendedInfo] = nlohmann::json::array(); + } - // Object exists and it is an array so we can just push in the message - target[extendedInfo].push_back(message); + // Object exists and it is an array so we can just push in the message + target[extendedInfo].push_back(message); } /********************************* @@ -98,17 +113,18 @@ void addMessageToJson(nlohmann::json& target, const nlohmann::json& message, * See header file for more information * @endinternal */ -nlohmann::json resourceInUse() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceInUse"}, - {"Message", - "The change to the requested resource failed because the resource is in " - "use or in transition."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the condition and resubmit the request if the operation " - "failed."}}; +nlohmann::json resourceInUse() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceInUse"}, + {"Message", "The change to the requested resource failed because the " + "resource is in " + "use or in transition."}, + {"Severity", "Warning"}, + {"Resolution", + "Remove the condition and resubmit the request if the operation " + "failed."}}; } /** @@ -118,16 +134,17 @@ nlohmann::json resourceInUse() { * See header file for more information * @endinternal */ -nlohmann::json malformedJSON() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.MalformedJSON"}, - {"Message", - "The request body submitted was malformed JSON and could not be parsed " - "by the receiving service."}, - {"Severity", "Critical"}, - {"Resolution", - "Ensure that the request body is valid JSON and resubmit the request."}}; +nlohmann::json malformedJSON() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.MalformedJSON"}, + {"Message", "The request body submitted was malformed JSON and could " + "not be parsed " + "by the receiving service."}, + {"Severity", "Critical"}, + {"Resolution", "Ensure that the request body is valid JSON and " + "resubmit the request."}}; } /** @@ -137,15 +154,16 @@ nlohmann::json malformedJSON() { * See header file for more information * @endinternal */ -nlohmann::json resourceMissingAtURI(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceMissingAtURI"}, - {"Message", "The resource at the URI " + arg1 + " was not found."}, - {"Severity", "Critical"}, - {"Resolution", - "Place a valid resource at the URI or correct the URI and resubmit the " - "request."}}; +nlohmann::json resourceMissingAtURI(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceMissingAtURI"}, + {"Message", "The resource at the URI " + arg1 + " was not found."}, + {"Severity", "Critical"}, + {"Resolution", "Place a valid resource at the URI or correct the URI " + "and resubmit the " + "request."}}; } /** @@ -157,17 +175,19 @@ nlohmann::json resourceMissingAtURI(const std::string& arg1) { */ nlohmann::json actionParameterValueFormatError(const std::string& arg1, const std::string& arg2, - const std::string& arg3) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionParameterValueFormatError"}, - {"Message", - "The value " + arg1 + " for the parameter " + arg2 + " in the action " + - arg3 + " is of a different format than the parameter can accept."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the value for the parameter in the request body and resubmit " - "the request if the operation failed."}}; + const std::string& arg3) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionParameterValueFormatError"}, + {"Message", + "The value " + arg1 + " for the parameter " + arg2 + + " in the action " + arg3 + + " is of a different format than the parameter can accept."}, + {"Severity", "Warning"}, + {"Resolution", + "Correct the value for the parameter in the request body and resubmit " + "the request if the operation failed."}}; } /** @@ -177,17 +197,18 @@ nlohmann::json actionParameterValueFormatError(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json internalError() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.InternalError"}, - {"Message", - "The request failed due to an internal service error. The service is " - "still operational."}, - {"Severity", "Critical"}, - {"Resolution", - "Resubmit the request. If the problem persists, consider resetting the " - "service."}}; +nlohmann::json internalError() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.InternalError"}, + {"Message", + "The request failed due to an internal service error. The service is " + "still operational."}, + {"Severity", "Critical"}, + {"Resolution", "Resubmit the request. If the problem persists, " + "consider resetting the " + "service."}}; } /** @@ -197,16 +218,17 @@ nlohmann::json internalError() { * See header file for more information * @endinternal */ -nlohmann::json unrecognizedRequestBody() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.UnrecognizedRequestBody"}, - {"Message", - "The service detected a malformed request body that it was unable to " - "interpret."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the request body and resubmit the request if it failed."}}; +nlohmann::json unrecognizedRequestBody() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.UnrecognizedRequestBody"}, + {"Message", + "The service detected a malformed request body that it was unable to " + "interpret."}, + {"Severity", "Warning"}, + {"Resolution", + "Correct the request body and resubmit the request if it failed."}}; } /** @@ -217,17 +239,18 @@ nlohmann::json unrecognizedRequestBody() { * @endinternal */ nlohmann::json resourceAtUriUnauthorized(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceAtUriUnauthorized"}, - {"Message", "While accessing the resource at " + arg1 + - ", the service received an authorization error " + arg2 + - "."}, - {"Severity", "Critical"}, - {"Resolution", - "Ensure that the appropriate access is provided for the service in " - "order for it to access the URI."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceAtUriUnauthorized"}, + {"Message", "While accessing the resource at " + arg1 + + ", the service received an authorization error " + + arg2 + "."}, + {"Severity", "Critical"}, + {"Resolution", + "Ensure that the appropriate access is provided for the service in " + "order for it to access the URI."}}; } /** @@ -238,17 +261,18 @@ nlohmann::json resourceAtUriUnauthorized(const std::string& arg1, * @endinternal */ nlohmann::json actionParameterUnknown(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionParameterUnknown"}, - {"Message", "The action " + arg1 + - " was submitted with the invalid parameter " + arg2 + - "."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the invalid parameter and resubmit the request if the " - "operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionParameterUnknown"}, + {"Message", "The action " + arg1 + + " was submitted with the invalid parameter " + arg2 + + "."}, + {"Severity", "Warning"}, + {"Resolution", + "Correct the invalid parameter and resubmit the request if the " + "operation failed."}}; } /** @@ -258,15 +282,16 @@ nlohmann::json actionParameterUnknown(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json resourceCannotBeDeleted() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceCannotBeDeleted"}, - {"Message", - "The delete request failed because the resource requested cannot be " - "deleted."}, - {"Severity", "Critical"}, - {"Resolution", "Do not attempt to delete a non-deletable resource."}}; +nlohmann::json resourceCannotBeDeleted() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceCannotBeDeleted"}, + {"Message", + "The delete request failed because the resource requested cannot be " + "deleted."}, + {"Severity", "Critical"}, + {"Resolution", "Do not attempt to delete a non-deletable resource."}}; } /** @@ -276,15 +301,16 @@ nlohmann::json resourceCannotBeDeleted() { * See header file for more information * @endinternal */ -nlohmann::json propertyDuplicate(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyDuplicate"}, - {"Message", "The property " + arg1 + " was duplicated in the request."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the duplicate property from the request body and resubmit the " - "request if the operation failed."}}; +nlohmann::json propertyDuplicate(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyDuplicate"}, + {"Message", "The property " + arg1 + " was duplicated in the request."}, + {"Severity", "Warning"}, + {"Resolution", + "Remove the duplicate property from the request body and resubmit the " + "request if the operation failed."}}; } /** @@ -294,15 +320,16 @@ nlohmann::json propertyDuplicate(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json serviceTemporarilyUnavailable(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ServiceTemporarilyUnavailable"}, - {"Message", "The service is temporarily unavailable. Retry in " + arg1 + - " seconds."}, - {"Severity", "Critical"}, - {"Resolution", - "Wait for the indicated retry duration and retry the operation."}}; +nlohmann::json serviceTemporarilyUnavailable(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ServiceTemporarilyUnavailable"}, + {"Message", "The service is temporarily unavailable. Retry in " + + arg1 + " seconds."}, + {"Severity", "Critical"}, + {"Resolution", + "Wait for the indicated retry duration and retry the operation."}}; } /** @@ -314,17 +341,18 @@ nlohmann::json serviceTemporarilyUnavailable(const std::string& arg1) { */ nlohmann::json resourceAlreadyExists(const std::string& arg1, const std::string& arg2, - const std::string& arg3) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceAlreadyExists"}, - {"Message", "The requested resource of type " + arg1 + - " with the property " + arg2 + " with the value " + arg3 + - " already exists."}, - {"Severity", "Critical"}, - {"Resolution", - "Do not repeat the create operation as the resource has already been " - "created."}}; + const std::string& arg3) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceAlreadyExists"}, + {"Message", "The requested resource of type " + arg1 + + " with the property " + arg2 + " with the value " + + arg3 + " already exists."}, + {"Severity", "Critical"}, + {"Resolution", + "Do not repeat the create operation as the resource has already been " + "created."}}; } /** @@ -334,15 +362,16 @@ nlohmann::json resourceAlreadyExists(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json accountForSessionNoLongerExists() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.AccountForSessionNoLongerExists"}, - {"Message", - "The account for the current session has been removed, thus the current " - "session has been removed as well."}, - {"Severity", "OK"}, - {"Resolution", "Attempt to connect with a valid account."}}; +nlohmann::json accountForSessionNoLongerExists() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.AccountForSessionNoLongerExists"}, + {"Message", "The account for the current session has been removed, " + "thus the current " + "session has been removed as well."}, + {"Severity", "OK"}, + {"Resolution", "Attempt to connect with a valid account."}}; } /** @@ -352,16 +381,18 @@ nlohmann::json accountForSessionNoLongerExists() { * See header file for more information * @endinternal */ -nlohmann::json createFailedMissingReqProperties(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.CreateFailedMissingReqProperties"}, - {"Message", "The create operation failed because the required property " + - arg1 + " was missing from the request."}, - {"Severity", "Critical"}, - {"Resolution", - "Correct the body to include the required property with a valid value " - "and resubmit the request if the operation failed."}}; +nlohmann::json createFailedMissingReqProperties(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.CreateFailedMissingReqProperties"}, + {"Message", + "The create operation failed because the required property " + arg1 + + " was missing from the request."}, + {"Severity", "Critical"}, + {"Resolution", + "Correct the body to include the required property with a valid value " + "and resubmit the request if the operation failed."}}; } /** @@ -372,17 +403,18 @@ nlohmann::json createFailedMissingReqProperties(const std::string& arg1) { * @endinternal */ nlohmann::json propertyValueFormatError(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyValueFormatError"}, - {"Message", - "The value " + arg1 + " for the property " + arg2 + - " is of a different format than the property can accept."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the value for the property in the request body and resubmit " - "the request if the operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyValueFormatError"}, + {"Message", + "The value " + arg1 + " for the property " + arg2 + + " is of a different format than the property can accept."}, + {"Severity", "Warning"}, + {"Resolution", + "Correct the value for the property in the request body and resubmit " + "the request if the operation failed."}}; } /** @@ -393,16 +425,17 @@ nlohmann::json propertyValueFormatError(const std::string& arg1, * @endinternal */ nlohmann::json propertyValueNotInList(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyValueNotInList"}, - {"Message", "The value " + arg1 + " for the property " + arg2 + - " is not in the list of acceptable values."}, - {"Severity", "Warning"}, - {"Resolution", - "Choose a value from the enumeration list that the implementation can " - "support and resubmit the request if the operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyValueNotInList"}, + {"Message", "The value " + arg1 + " for the property " + arg2 + + " is not in the list of acceptable values."}, + {"Severity", "Warning"}, + {"Resolution", + "Choose a value from the enumeration list that the implementation can " + "support and resubmit the request if the operation failed."}}; } /** @@ -412,16 +445,17 @@ nlohmann::json propertyValueNotInList(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json resourceAtUriInUnknownFormat(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceAtUriInUnknownFormat"}, - {"Message", "The resource at " + arg1 + - " is in a format not recognized by the service."}, - {"Severity", "Critical"}, - {"Resolution", - "Place an image or resource or file that is recognized by the service " - "at the URI."}}; +nlohmann::json resourceAtUriInUnknownFormat(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceAtUriInUnknownFormat"}, + {"Message", "The resource at " + arg1 + + " is in a format not recognized by the service."}, + {"Severity", "Critical"}, + {"Resolution", + "Place an image or resource or file that is recognized by the service " + "at the URI."}}; } /** @@ -431,17 +465,18 @@ nlohmann::json resourceAtUriInUnknownFormat(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json serviceInUnknownState() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ServiceInUnknownState"}, - {"Message", - "The operation failed because the service is in an unknown state and " - "can no longer take incoming requests."}, - {"Severity", "Critical"}, - {"Resolution", - "Restart the service and resubmit the request if the operation " - "failed."}}; +nlohmann::json serviceInUnknownState() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ServiceInUnknownState"}, + {"Message", + "The operation failed because the service is in an unknown state and " + "can no longer take incoming requests."}, + {"Severity", "Critical"}, + {"Resolution", + "Restart the service and resubmit the request if the operation " + "failed."}}; } /** @@ -451,18 +486,19 @@ nlohmann::json serviceInUnknownState() { * See header file for more information * @endinternal */ -nlohmann::json eventSubscriptionLimitExceeded() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.EventSubscriptionLimitExceeded"}, - {"Message", - "The event subscription failed due to the number of simultaneous " - "subscriptions exceeding the limit of the implementation."}, - {"Severity", "Critical"}, - {"Resolution", - "Reduce the number of other subscriptions before trying to establish " - "the event subscription or increase the limit of simultaneous " - "subscriptions (if supported)."}}; +nlohmann::json eventSubscriptionLimitExceeded() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.EventSubscriptionLimitExceeded"}, + {"Message", + "The event subscription failed due to the number of simultaneous " + "subscriptions exceeding the limit of the implementation."}, + {"Severity", "Critical"}, + {"Resolution", + "Reduce the number of other subscriptions before trying to establish " + "the event subscription or increase the limit of simultaneous " + "subscriptions (if supported)."}}; } /** @@ -473,16 +509,17 @@ nlohmann::json eventSubscriptionLimitExceeded() { * @endinternal */ nlohmann::json actionParameterMissing(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionParameterMissing"}, - {"Message", "The action " + arg1 + " requires the parameter " + arg2 + - " to be present in the request body."}, - {"Severity", "Critical"}, - {"Resolution", - "Supply the action with the required parameter in the request body when " - "the request is resubmitted."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionParameterMissing"}, + {"Message", "The action " + arg1 + " requires the parameter " + arg2 + + " to be present in the request body."}, + {"Severity", "Critical"}, + {"Resolution", "Supply the action with the required parameter in the " + "request body when " + "the request is resubmitted."}}; } /** @@ -492,15 +529,16 @@ nlohmann::json actionParameterMissing(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json stringValueTooLong(const std::string& arg1, const int& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.StringValueTooLong"}, - {"Message", "The string " + arg1 + " exceeds the length limit " + - std::to_string(arg2) + "."}, - {"Severity", "Warning"}, - {"Resolution", - "Resubmit the request with an appropriate string length."}}; +nlohmann::json stringValueTooLong(const std::string& arg1, const int& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.StringValueTooLong"}, + {"Message", "The string " + arg1 + " exceeds the length limit " + + std::to_string(arg2) + "."}, + {"Severity", "Warning"}, + {"Resolution", + "Resubmit the request with an appropriate string length."}}; } /** @@ -511,16 +549,18 @@ nlohmann::json stringValueTooLong(const std::string& arg1, const int& arg2) { * @endinternal */ nlohmann::json propertyValueTypeError(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyValueTypeError"}, - {"Message", "The value " + arg1 + " for the property " + arg2 + - " is of a different type than the property can accept."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the value for the property in the request body and resubmit " - "the request if the operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyValueTypeError"}, + {"Message", + "The value " + arg1 + " for the property " + arg2 + + " is of a different type than the property can accept."}, + {"Severity", "Warning"}, + {"Resolution", + "Correct the value for the property in the request body and resubmit " + "the request if the operation failed."}}; } /** @@ -531,15 +571,16 @@ nlohmann::json propertyValueTypeError(const std::string& arg1, * @endinternal */ nlohmann::json resourceNotFound(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceNotFound"}, - {"Message", "The requested resource of type " + arg1 + " named " + arg2 + - " was not found."}, - {"Severity", "Critical"}, - {"Resolution", - "Provide a valid resource identifier and resubmit the request."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceNotFound"}, + {"Message", "The requested resource of type " + arg1 + " named " + + arg2 + " was not found."}, + {"Severity", "Critical"}, + {"Resolution", + "Provide a valid resource identifier and resubmit the request."}}; } /** @@ -549,16 +590,18 @@ nlohmann::json resourceNotFound(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json couldNotEstablishConnection(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.CouldNotEstablishConnection"}, - {"Message", "The service failed to establish a Connection with the URI " + - arg1 + "."}, - {"Severity", "Critical"}, - {"Resolution", - "Ensure that the URI contains a valid and reachable node name, protocol " - "information and other URI components."}}; +nlohmann::json couldNotEstablishConnection(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.CouldNotEstablishConnection"}, + {"Message", + "The service failed to establish a Connection with the URI " + arg1 + + "."}, + {"Severity", "Critical"}, + {"Resolution", "Ensure that the URI contains a valid and reachable " + "node name, protocol " + "information and other URI components."}}; } /** @@ -568,17 +611,18 @@ nlohmann::json couldNotEstablishConnection(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json propertyNotWritable(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyNotWritable"}, - {"Message", - "The property " + arg1 + - " is a read only property and cannot be assigned a value."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the property from the request body and resubmit the request if " - "the operation failed."}}; +nlohmann::json propertyNotWritable(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyNotWritable"}, + {"Message", + "The property " + arg1 + + " is a read only property and cannot be assigned a value."}, + {"Severity", "Warning"}, + {"Resolution", "Remove the property from the request body and resubmit " + "the request if " + "the operation failed."}}; } /** @@ -589,16 +633,18 @@ nlohmann::json propertyNotWritable(const std::string& arg1) { * @endinternal */ nlohmann::json queryParameterValueTypeError(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.QueryParameterValueTypeError"}, - {"Message", "The value " + arg1 + " for the query parameter " + arg2 + - " is of a different type than the parameter can accept."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the value for the query parameter in the request and resubmit " - "the request if the operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.QueryParameterValueTypeError"}, + {"Message", + "The value " + arg1 + " for the query parameter " + arg2 + + " is of a different type than the parameter can accept."}, + {"Severity", "Warning"}, + {"Resolution", "Correct the value for the query parameter in the " + "request and resubmit " + "the request if the operation failed."}}; } /** @@ -608,17 +654,18 @@ nlohmann::json queryParameterValueTypeError(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json serviceShuttingDown() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ServiceShuttingDown"}, - {"Message", - "The operation failed because the service is shutting down and can no " - "longer take incoming requests."}, - {"Severity", "Critical"}, - {"Resolution", - "When the service becomes available, resubmit the request if the " - "operation failed."}}; +nlohmann::json serviceShuttingDown() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ServiceShuttingDown"}, + {"Message", + "The operation failed because the service is shutting down and can no " + "longer take incoming requests."}, + {"Severity", "Critical"}, + {"Resolution", + "When the service becomes available, resubmit the request if the " + "operation failed."}}; } /** @@ -629,18 +676,19 @@ nlohmann::json serviceShuttingDown() { * @endinternal */ nlohmann::json actionParameterDuplicate(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionParameterDuplicate"}, - {"Message", - "The action " + arg1 + - " was submitted with more than one value for the parameter " + arg2 + - "."}, - {"Severity", "Warning"}, - {"Resolution", - "Resubmit the action with only one instance of the parameter in the " - "request body if the operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionParameterDuplicate"}, + {"Message", + "The action " + arg1 + + " was submitted with more than one value for the parameter " + + arg2 + "."}, + {"Severity", "Warning"}, + {"Resolution", + "Resubmit the action with only one instance of the parameter in the " + "request body if the operation failed."}}; } /** @@ -651,16 +699,17 @@ nlohmann::json actionParameterDuplicate(const std::string& arg1, * @endinternal */ nlohmann::json actionParameterNotSupported(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionParameterNotSupported"}, - {"Message", "The parameter " + arg1 + " for the action " + arg2 + - " is not supported on the target resource."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the parameter supplied and resubmit the request if the " - "operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionParameterNotSupported"}, + {"Message", "The parameter " + arg1 + " for the action " + arg2 + + " is not supported on the target resource."}, + {"Severity", "Warning"}, + {"Resolution", + "Remove the parameter supplied and resubmit the request if the " + "operation failed."}}; } /** @@ -671,14 +720,16 @@ nlohmann::json actionParameterNotSupported(const std::string& arg1, * @endinternal */ nlohmann::json sourceDoesNotSupportProtocol(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.SourceDoesNotSupportProtocol"}, - {"Message", "The other end of the Connection at " + arg1 + - " does not support the specified protocol " + arg2 + "."}, - {"Severity", "Critical"}, - {"Resolution", "Change protocols or URIs. "}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.SourceDoesNotSupportProtocol"}, + {"Message", "The other end of the Connection at " + arg1 + + " does not support the specified protocol " + arg2 + + "."}, + {"Severity", "Critical"}, + {"Resolution", "Change protocols or URIs. "}}; } /** @@ -688,13 +739,14 @@ nlohmann::json sourceDoesNotSupportProtocol(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json accountRemoved() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.AccountRemoved"}, - {"Message", "The account was successfully removed."}, - {"Severity", "OK"}, - {"Resolution", "No resolution is required."}}; +nlohmann::json accountRemoved() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.AccountRemoved"}, + {"Message", "The account was successfully removed."}, + {"Severity", "OK"}, + {"Resolution", "No resolution is required."}}; } /** @@ -704,16 +756,17 @@ nlohmann::json accountRemoved() { * See header file for more information * @endinternal */ -nlohmann::json accessDenied(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.AccessDenied"}, - {"Message", "While attempting to establish a Connection to " + arg1 + - ", the service denied access."}, - {"Severity", "Critical"}, - {"Resolution", - "Attempt to ensure that the URI is correct and that the service has the " - "appropriate credentials."}}; +nlohmann::json accessDenied(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.AccessDenied"}, + {"Message", "While attempting to establish a Connection to " + arg1 + + ", the service denied access."}, + {"Severity", "Critical"}, + {"Resolution", "Attempt to ensure that the URI is correct and that the " + "service has the " + "appropriate credentials."}}; } /** @@ -723,15 +776,16 @@ nlohmann::json accessDenied(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json queryNotSupported() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.QueryNotSupported"}, - {"Message", "Querying is not supported by the implementation."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the query parameters and resubmit the request if the operation " - "failed."}}; +nlohmann::json queryNotSupported() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.QueryNotSupported"}, + {"Message", "Querying is not supported by the implementation."}, + {"Severity", "Warning"}, + {"Resolution", "Remove the query parameters and resubmit the request " + "if the operation " + "failed."}}; } /** @@ -741,17 +795,18 @@ nlohmann::json queryNotSupported() { * See header file for more information * @endinternal */ -nlohmann::json createLimitReachedForResource() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.CreateLimitReachedForResource"}, - {"Message", - "The create operation failed because the resource has reached the limit " - "of possible resources."}, - {"Severity", "Critical"}, - {"Resolution", - "Either delete resources and resubmit the request if the operation " - "failed or do not resubmit the request."}}; +nlohmann::json createLimitReachedForResource() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.CreateLimitReachedForResource"}, + {"Message", "The create operation failed because the resource has " + "reached the limit " + "of possible resources."}, + {"Severity", "Critical"}, + {"Resolution", + "Either delete resources and resubmit the request if the operation " + "failed or do not resubmit the request."}}; } /** @@ -761,14 +816,15 @@ nlohmann::json createLimitReachedForResource() { * See header file for more information * @endinternal */ -nlohmann::json generalError() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.GeneralError"}, - {"Message", - "A general error has occurred. See ExtendedInfo for more information."}, - {"Severity", "Critical"}, - {"Resolution", "See ExtendedInfo for more information."}}; +nlohmann::json generalError() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.GeneralError"}, + {"Message", "A general error has occurred. See ExtendedInfo for more " + "information."}, + {"Severity", "Critical"}, + {"Resolution", "See ExtendedInfo for more information."}}; } /** @@ -778,13 +834,14 @@ nlohmann::json generalError() { * See header file for more information * @endinternal */ -nlohmann::json success() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.Success"}, - {"Message", "Successfully Completed Request"}, - {"Severity", "OK"}, - {"Resolution", "None"}}; +nlohmann::json success() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.Success"}, + {"Message", "Successfully Completed Request"}, + {"Severity", "OK"}, + {"Resolution", "None"}}; } /** @@ -794,13 +851,14 @@ nlohmann::json success() { * See header file for more information * @endinternal */ -nlohmann::json created() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.Created"}, - {"Message", "The resource has been created successfully"}, - {"Severity", "OK"}, - {"Resolution", "None"}}; +nlohmann::json created() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.Created"}, + {"Message", "The resource has been created successfully"}, + {"Severity", "OK"}, + {"Resolution", "None"}}; } /** @@ -810,17 +868,18 @@ nlohmann::json created() { * See header file for more information * @endinternal */ -nlohmann::json propertyUnknown(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyUnknown"}, - {"Message", - "The property " + arg1 + - " is not in the list of valid properties for the resource."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the unknown property from the request body and resubmit the " - "request if the operation failed."}}; +nlohmann::json propertyUnknown(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyUnknown"}, + {"Message", + "The property " + arg1 + + " is not in the list of valid properties for the resource."}, + {"Severity", "Warning"}, + {"Resolution", + "Remove the unknown property from the request body and resubmit the " + "request if the operation failed."}}; } /** @@ -830,14 +889,16 @@ nlohmann::json propertyUnknown(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json noValidSession() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.NoValidSession"}, - {"Message", - "There is no valid session established with the implementation."}, - {"Severity", "Critical"}, - {"Resolution", "Establish as session before attempting any operations."}}; +nlohmann::json noValidSession() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.NoValidSession"}, + {"Message", + "There is no valid session established with the implementation."}, + {"Severity", "Critical"}, + {"Resolution", + "Establish as session before attempting any operations."}}; } /** @@ -847,15 +908,16 @@ nlohmann::json noValidSession() { * See header file for more information * @endinternal */ -nlohmann::json invalidObject(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.InvalidObject"}, - {"Message", "The object at " + arg1 + " is invalid."}, - {"Severity", "Critical"}, - {"Resolution", - "Either the object is malformed or the URI is not correct. Correct the " - "condition and resubmit the request if it failed."}}; +nlohmann::json invalidObject(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.InvalidObject"}, + {"Message", "The object at " + arg1 + " is invalid."}, + {"Severity", "Critical"}, + {"Resolution", "Either the object is malformed or the URI is not " + "correct. Correct the " + "condition and resubmit the request if it failed."}}; } /** @@ -865,17 +927,18 @@ nlohmann::json invalidObject(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json resourceInStandby() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceInStandby"}, - {"Message", - "The request could not be performed because the resource is in " - "standby."}, - {"Severity", "Critical"}, - {"Resolution", - "Ensure that the resource is in the correct power state and resubmit " - "the request."}}; +nlohmann::json resourceInStandby() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceInStandby"}, + {"Message", + "The request could not be performed because the resource is in " + "standby."}, + {"Severity", "Critical"}, + {"Resolution", + "Ensure that the resource is in the correct power state and resubmit " + "the request."}}; } /** @@ -887,17 +950,19 @@ nlohmann::json resourceInStandby() { */ nlohmann::json actionParameterValueTypeError(const std::string& arg1, const std::string& arg2, - const std::string& arg3) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionParameterValueTypeError"}, - {"Message", "The value " + arg1 + " for the parameter " + arg2 + - " in the action " + arg3 + - " is of a different type than the parameter can accept."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the value for the parameter in the request body and resubmit " - "the request if the operation failed."}}; + const std::string& arg3) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionParameterValueTypeError"}, + {"Message", + "The value " + arg1 + " for the parameter " + arg2 + + " in the action " + arg3 + + " is of a different type than the parameter can accept."}, + {"Severity", "Warning"}, + {"Resolution", + "Correct the value for the parameter in the request body and resubmit " + "the request if the operation failed."}}; } /** @@ -907,18 +972,19 @@ nlohmann::json actionParameterValueTypeError(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json sessionLimitExceeded() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.SessionLimitExceeded"}, - {"Message", - "The session establishment failed due to the number of simultaneous " - "sessions exceeding the limit of the implementation."}, - {"Severity", "Critical"}, - {"Resolution", - "Reduce the number of other sessions before trying to establish the " - "session or increase the limit of simultaneous sessions (if " - "supported)."}}; +nlohmann::json sessionLimitExceeded() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.SessionLimitExceeded"}, + {"Message", + "The session establishment failed due to the number of simultaneous " + "sessions exceeding the limit of the implementation."}, + {"Severity", "Critical"}, + {"Resolution", + "Reduce the number of other sessions before trying to establish the " + "session or increase the limit of simultaneous sessions (if " + "supported)."}}; } /** @@ -928,16 +994,18 @@ nlohmann::json sessionLimitExceeded() { * See header file for more information * @endinternal */ -nlohmann::json actionNotSupported(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ActionNotSupported"}, - {"Message", "The action " + arg1 + " is not supported by the resource."}, - {"Severity", "Critical"}, - {"Resolution", - "The action supplied cannot be resubmitted to the implementation. " - "Perhaps the action was invalid, the wrong resource was the target or " - "the implementation documentation may be of assistance."}}; +nlohmann::json actionNotSupported(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ActionNotSupported"}, + {"Message", + "The action " + arg1 + " is not supported by the resource."}, + {"Severity", "Critical"}, + {"Resolution", + "The action supplied cannot be resubmitted to the implementation. " + "Perhaps the action was invalid, the wrong resource was the target or " + "the implementation documentation may be of assistance."}}; } /** @@ -947,15 +1015,16 @@ nlohmann::json actionNotSupported(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json invalidIndex(const int& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.InvalidIndex"}, - {"Message", "The index " + std::to_string(arg1) + - " is not a valid offset into the array."}, - {"Severity", "Warning"}, - {"Resolution", - "Verify the index value provided is within the bounds of the array."}}; +nlohmann::json invalidIndex(const int& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.InvalidIndex"}, + {"Message", "The index " + std::to_string(arg1) + + " is not a valid offset into the array."}, + {"Severity", "Warning"}, + {"Resolution", + "Verify the index value provided is within the bounds of the array."}}; } /** @@ -965,16 +1034,17 @@ nlohmann::json invalidIndex(const int& arg1) { * See header file for more information * @endinternal */ -nlohmann::json emptyJSON() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.EmptyJSON"}, - {"Message", - "The request body submitted contained an empty JSON object and the " - "service is unable to process it."}, - {"Severity", "Warning"}, - {"Resolution", - "Add properties in the JSON object and resubmit the request."}}; +nlohmann::json emptyJSON() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.EmptyJSON"}, + {"Message", + "The request body submitted contained an empty JSON object and the " + "service is unable to process it."}, + {"Severity", "Warning"}, + {"Resolution", + "Add properties in the JSON object and resubmit the request."}}; } /** @@ -984,15 +1054,16 @@ nlohmann::json emptyJSON() { * See header file for more information * @endinternal */ -nlohmann::json queryNotSupportedOnResource() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.QueryNotSupportedOnResource"}, - {"Message", "Querying is not supported on the requested resource."}, - {"Severity", "Warning"}, - {"Resolution", - "Remove the query parameters and resubmit the request if the operation " - "failed."}}; +nlohmann::json queryNotSupportedOnResource() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.QueryNotSupportedOnResource"}, + {"Message", "Querying is not supported on the requested resource."}, + {"Severity", "Warning"}, + {"Resolution", "Remove the query parameters and resubmit the request " + "if the operation " + "failed."}}; } /** @@ -1002,18 +1073,19 @@ nlohmann::json queryNotSupportedOnResource() { * See header file for more information * @endinternal */ -nlohmann::json insufficientPrivilege() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.InsufficientPrivilege"}, - {"Message", - "There are insufficient privileges for the account or credentials " - "associated with the current session to perform the requested " - "operation."}, - {"Severity", "Critical"}, - {"Resolution", - "Either abandon the operation or change the associated access rights " - "and resubmit the request if the operation failed."}}; +nlohmann::json insufficientPrivilege() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.InsufficientPrivilege"}, + {"Message", + "There are insufficient privileges for the account or credentials " + "associated with the current session to perform the requested " + "operation."}, + {"Severity", "Critical"}, + {"Resolution", + "Either abandon the operation or change the associated access rights " + "and resubmit the request if the operation failed."}}; } /** @@ -1024,14 +1096,15 @@ nlohmann::json insufficientPrivilege() { * @endinternal */ nlohmann::json propertyValueModified(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyValueModified"}, - {"Message", "The property " + arg1 + " was assigned the value " + arg2 + - " due to modification by the service."}, - {"Severity", "Warning"}, - {"Resolution", "No resolution is required."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyValueModified"}, + {"Message", "The property " + arg1 + " was assigned the value " + arg2 + + " due to modification by the service."}, + {"Severity", "Warning"}, + {"Resolution", "No resolution is required."}}; } /** @@ -1041,15 +1114,16 @@ nlohmann::json propertyValueModified(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json accountNotModified() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.AccountNotModified"}, - {"Message", "The account modification request failed."}, - {"Severity", "Warning"}, - {"Resolution", - "The modification may have failed due to permission issues or issues " - "with the request body."}}; +nlohmann::json accountNotModified() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.AccountNotModified"}, + {"Message", "The account modification request failed."}, + {"Severity", "Warning"}, + {"Resolution", + "The modification may have failed due to permission issues or issues " + "with the request body."}}; } /** @@ -1060,17 +1134,18 @@ nlohmann::json accountNotModified() { * @endinternal */ nlohmann::json queryParameterValueFormatError(const std::string& arg1, - const std::string& arg2) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.QueryParameterValueFormatError"}, - {"Message", - "The value " + arg1 + " for the parameter " + arg2 + - " is of a different format than the parameter can accept."}, - {"Severity", "Warning"}, - {"Resolution", - "Correct the value for the query parameter in the request and resubmit " - "the request if the operation failed."}}; + const std::string& arg2) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.QueryParameterValueFormatError"}, + {"Message", + "The value " + arg1 + " for the parameter " + arg2 + + " is of a different format than the parameter can accept."}, + {"Severity", "Warning"}, + {"Resolution", "Correct the value for the query parameter in the " + "request and resubmit " + "the request if the operation failed."}}; } /** @@ -1080,17 +1155,18 @@ nlohmann::json queryParameterValueFormatError(const std::string& arg1, * See header file for more information * @endinternal */ -nlohmann::json propertyMissing(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.PropertyMissing"}, - {"Message", - "The property " + arg1 + - " is a required property and must be included in the request."}, - {"Severity", "Warning"}, - {"Resolution", - "Ensure that the property is in the request body and has a valid value " - "and resubmit the request if the operation failed."}}; +nlohmann::json propertyMissing(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.PropertyMissing"}, + {"Message", + "The property " + arg1 + + " is a required property and must be included in the request."}, + {"Severity", "Warning"}, + {"Resolution", "Ensure that the property is in the request body and " + "has a valid value " + "and resubmit the request if the operation failed."}}; } /** @@ -1100,17 +1176,18 @@ nlohmann::json propertyMissing(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json resourceExhaustion(const std::string& arg1) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.ResourceExhaustion"}, - {"Message", "The resource " + arg1 + - " was unable to satisfy the request " - "due to unavailability of " - "resources."}, - {"Severity", "Critical"}, - {"Resolution", - "Ensure that the resources are available and resubmit the request."}}; +nlohmann::json resourceExhaustion(const std::string& arg1) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.ResourceExhaustion"}, + {"Message", "The resource " + arg1 + + " was unable to satisfy the request " + "due to unavailability of " + "resources."}, + {"Severity", "Critical"}, + {"Resolution", + "Ensure that the resources are available and resubmit the request."}}; } /** @@ -1120,13 +1197,14 @@ nlohmann::json resourceExhaustion(const std::string& arg1) { * See header file for more information * @endinternal */ -nlohmann::json accountModified() { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.AccountModified"}, - {"Message", "The account was successfully modified."}, - {"Severity", "OK"}, - {"Resolution", "No resolution is required."}}; +nlohmann::json accountModified() +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.AccountModified"}, + {"Message", "The account was successfully modified."}, + {"Severity", "OK"}, + {"Resolution", "No resolution is required."}}; } /** @@ -1138,24 +1216,26 @@ nlohmann::json accountModified() { */ nlohmann::json queryParameterOutOfRange(const std::string& arg1, const std::string& arg2, - const std::string& arg3) { - return nlohmann::json{ - {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, - {"MessageId", "Base.1.2.0.QueryParameterOutOfRange"}, - {"Message", "The value " + arg1 + " for the query parameter " + arg2 + - " is out of range " + arg3 + "."}, - {"Severity", "Warning"}, - {"Resolution", - "Reduce the value for the query parameter to a value that is within " - "range, such as a start or count value that is within bounds of the " - "number of resources in a collection or a page that is within the range " - "of valid pages."}}; + const std::string& arg3) +{ + return nlohmann::json{ + {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.2.0.QueryParameterOutOfRange"}, + {"Message", "The value " + arg1 + " for the query parameter " + arg2 + + " is out of range " + arg3 + "."}, + {"Severity", "Warning"}, + {"Resolution", + "Reduce the value for the query parameter to a value that is within " + "range, such as a start or count value that is within bounds of the " + "number of resources in a collection or a page that is within the " + "range " + "of valid pages."}}; } /********************************* * AUTOGENERATED FUNCTIONS END * *********************************/ -} // namespace messages +} // namespace messages -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/src/utils/json_utils.cpp b/redfish-core/src/utils/json_utils.cpp index 5d81c21fa9..868601f72d 100644 --- a/redfish-core/src/utils/json_utils.cpp +++ b/redfish-core/src/utils/json_utils.cpp @@ -14,440 +14,512 @@ // limitations under the License. */ #include "utils/json_utils.hpp" + #include <error_messages.hpp> -namespace redfish { +namespace redfish +{ -namespace json_util { +namespace json_util +{ Result getString(const char* fieldName, const nlohmann::json& json, - const std::string*& output) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + const std::string*& output) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - output = fieldIt->get_ptr<const std::string*>(); + output = fieldIt->get_ptr<const std::string*>(); - // Verify type - we know that it exists, so nullptr means wrong type - if (output == nullptr) { - return Result::WRONG_TYPE; - } + // Verify type - we know that it exists, so nullptr means wrong type + if (output == nullptr) + { + return Result::WRONG_TYPE; + } - return Result::SUCCESS; + return Result::SUCCESS; } Result getObject(const char* fieldName, const nlohmann::json& json, - nlohmann::json* output) { - // Verify input pointer - if (output == nullptr) { - return Result::NULL_POINTER; - } + nlohmann::json* output) +{ + // Verify input pointer + if (output == nullptr) + { + return Result::NULL_POINTER; + } - // Find field - auto fieldIt = json.find(fieldName); + // Find field + auto fieldIt = json.find(fieldName); - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - // Verify type - if (!fieldIt->is_object()) { - return Result::WRONG_TYPE; - } + // Verify type + if (!fieldIt->is_object()) + { + return Result::WRONG_TYPE; + } - // Extract value - *output = *fieldIt; + // Extract value + *output = *fieldIt; - return Result::SUCCESS; + return Result::SUCCESS; } Result getArray(const char* fieldName, const nlohmann::json& json, - nlohmann::json* output) { - // Verify input pointer - if (output == nullptr) { - return Result::NULL_POINTER; - } + nlohmann::json* output) +{ + // Verify input pointer + if (output == nullptr) + { + return Result::NULL_POINTER; + } - // Find field - auto fieldIt = json.find(fieldName); + // Find field + auto fieldIt = json.find(fieldName); - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - // Verify type - if (!fieldIt->is_array()) { - return Result::WRONG_TYPE; - } + // Verify type + if (!fieldIt->is_array()) + { + return Result::WRONG_TYPE; + } - // Extract value - *output = *fieldIt; + // Extract value + *output = *fieldIt; - return Result::SUCCESS; + return Result::SUCCESS; } Result getInt(const char* fieldName, const nlohmann::json& json, - int64_t& output) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + int64_t& output) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - const int64_t* retVal = fieldIt->get_ptr<const int64_t*>(); + const int64_t* retVal = fieldIt->get_ptr<const int64_t*>(); - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - return Result::WRONG_TYPE; - } + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + return Result::WRONG_TYPE; + } - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } Result getUnsigned(const char* fieldName, const nlohmann::json& json, - uint64_t& output) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + uint64_t& output) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - const uint64_t* retVal = fieldIt->get_ptr<const uint64_t*>(); + const uint64_t* retVal = fieldIt->get_ptr<const uint64_t*>(); - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - return Result::WRONG_TYPE; - } + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + return Result::WRONG_TYPE; + } - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } -Result getBool(const char* fieldName, const nlohmann::json& json, - bool& output) { - // Find field - auto fieldIt = json.find(fieldName); +Result getBool(const char* fieldName, const nlohmann::json& json, bool& output) +{ + // Find field + auto fieldIt = json.find(fieldName); - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - const bool* retVal = fieldIt->get_ptr<const bool*>(); + const bool* retVal = fieldIt->get_ptr<const bool*>(); - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - return Result::WRONG_TYPE; - } + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + return Result::WRONG_TYPE; + } - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } Result getDouble(const char* fieldName, const nlohmann::json& json, - double& output) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - return Result::NOT_EXIST; - } + double& output) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + return Result::NOT_EXIST; + } - const double* retVal = fieldIt->get_ptr<const double*>(); + const double* retVal = fieldIt->get_ptr<const double*>(); - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - return Result::WRONG_TYPE; - } + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + return Result::WRONG_TYPE; + } - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } Result getString(const char* fieldName, const nlohmann::json& json, const std::string*& output, uint8_t msgCfgMap, - nlohmann::json& msgJson, const std::string&& fieldPath) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + nlohmann::json& msgJson, const std::string&& fieldPath) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } + + return Result::NOT_EXIST; } - return Result::NOT_EXIST; - } + output = fieldIt->get_ptr<const std::string*>(); - output = fieldIt->get_ptr<const std::string*>(); + // Verify type - we know that it exists, so nullptr means wrong type + if (output == nullptr) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } - // Verify type - we know that it exists, so nullptr means wrong type - if (output == nullptr) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + return Result::WRONG_TYPE; } - return Result::WRONG_TYPE; - } - - return Result::SUCCESS; + return Result::SUCCESS; } Result getObject(const char* fieldName, const nlohmann::json& json, nlohmann::json* output, uint8_t msgCfgMap, - nlohmann::json& msgJson, const std::string&& fieldPath) { - // Verify input pointer - if (output == nullptr) { - return Result::NULL_POINTER; - } - - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + nlohmann::json& msgJson, const std::string&& fieldPath) +{ + // Verify input pointer + if (output == nullptr) + { + return Result::NULL_POINTER; } - return Result::NOT_EXIST; - } + // Find field + auto fieldIt = json.find(fieldName); - // Verify type - if (!fieldIt->is_object()) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } + + return Result::NOT_EXIST; } - return Result::WRONG_TYPE; - } + // Verify type + if (!fieldIt->is_object()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } + + return Result::WRONG_TYPE; + } - // Extract value - *output = *fieldIt; + // Extract value + *output = *fieldIt; - return Result::SUCCESS; + return Result::SUCCESS; } Result getArray(const char* fieldName, const nlohmann::json& json, nlohmann::json* output, uint8_t msgCfgMap, - nlohmann::json& msgJson, const std::string&& fieldPath) { - // Verify input pointer - if (output == nullptr) { - return Result::NULL_POINTER; - } - - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + nlohmann::json& msgJson, const std::string&& fieldPath) +{ + // Verify input pointer + if (output == nullptr) + { + return Result::NULL_POINTER; } - return Result::NOT_EXIST; - } + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } - // Verify type - if (!fieldIt->is_array()) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + return Result::NOT_EXIST; } - return Result::WRONG_TYPE; - } + // Verify type + if (!fieldIt->is_array()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } + + return Result::WRONG_TYPE; + } - // Extract value - *output = *fieldIt; + // Extract value + *output = *fieldIt; - return Result::SUCCESS; + return Result::SUCCESS; } Result getInt(const char* fieldName, const nlohmann::json& json, int64_t& output, uint8_t msgCfgMap, nlohmann::json& msgJson, - const std::string&& fieldPath) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + const std::string&& fieldPath) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } + + return Result::NOT_EXIST; } - return Result::NOT_EXIST; - } + const int64_t* retVal = fieldIt->get_ptr<const int64_t*>(); - const int64_t* retVal = fieldIt->get_ptr<const int64_t*>(); + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + return Result::WRONG_TYPE; } - return Result::WRONG_TYPE; - } - - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } Result getUnsigned(const char* fieldName, const nlohmann::json& json, uint64_t& output, uint8_t msgCfgMap, nlohmann::json& msgJson, - const std::string&& fieldPath) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + const std::string&& fieldPath) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } + + return Result::NOT_EXIST; } - return Result::NOT_EXIST; - } + const uint64_t* retVal = fieldIt->get_ptr<const uint64_t*>(); - const uint64_t* retVal = fieldIt->get_ptr<const uint64_t*>(); + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + return Result::WRONG_TYPE; } - return Result::WRONG_TYPE; - } - - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } Result getBool(const char* fieldName, const nlohmann::json& json, bool& output, uint8_t msgCfgMap, nlohmann::json& msgJson, - const std::string&& fieldPath) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + const std::string&& fieldPath) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } + + return Result::NOT_EXIST; } - return Result::NOT_EXIST; - } + const bool* retVal = fieldIt->get_ptr<const bool*>(); - const bool* retVal = fieldIt->get_ptr<const bool*>(); + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + return Result::WRONG_TYPE; } - return Result::WRONG_TYPE; - } - - // Extract value - output = *retVal; + // Extract value + output = *retVal; - return Result::SUCCESS; + return Result::SUCCESS; } Result getDouble(const char* fieldName, const nlohmann::json& json, double& output, uint8_t msgCfgMap, nlohmann::json& msgJson, - const std::string&& fieldPath) { - // Find field - auto fieldIt = json.find(fieldName); - - // Verify existence - if (fieldIt == json.end()) { - if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) { - messages::addMessageToJson(msgJson, messages::propertyMissing(fieldName), - fieldPath); + const std::string&& fieldPath) +{ + // Find field + auto fieldIt = json.find(fieldName); + + // Verify existence + if (fieldIt == json.end()) + { + if (msgCfgMap & static_cast<int>(MessageSetting::MISSING)) + { + messages::addMessageToJson( + msgJson, messages::propertyMissing(fieldName), fieldPath); + } + + return Result::NOT_EXIST; } - return Result::NOT_EXIST; - } + const double* retVal = fieldIt->get_ptr<const double*>(); - const double* retVal = fieldIt->get_ptr<const double*>(); + // Verify type - we know that it exists, so nullptr means wrong type + if (retVal == nullptr) + { + if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) + { + messages::addMessageToJson( + msgJson, + messages::propertyValueTypeError(fieldIt->dump(), fieldName), + fieldPath); + } - // Verify type - we know that it exists, so nullptr means wrong type - if (retVal == nullptr) { - if (msgCfgMap & static_cast<int>(MessageSetting::TYPE_ERROR)) { - messages::addMessageToJson( - msgJson, messages::propertyValueTypeError(fieldIt->dump(), fieldName), - fieldPath); + return Result::WRONG_TYPE; } - return Result::WRONG_TYPE; - } + // Extract value + output = *retVal; - // Extract value - output = *retVal; - - return Result::SUCCESS; + return Result::SUCCESS; } bool processJsonFromRequest(crow::Response& res, const crow::Request& req, - nlohmann::json& reqJson) { - reqJson = nlohmann::json::parse(req.body, nullptr, false); + nlohmann::json& reqJson) +{ + reqJson = nlohmann::json::parse(req.body, nullptr, false); - if (reqJson.is_discarded()) { - messages::addMessageToErrorJson(res.jsonValue, messages::malformedJSON()); + if (reqJson.is_discarded()) + { + messages::addMessageToErrorJson(res.jsonValue, + messages::malformedJSON()); - res.result(boost::beast::http::status::bad_request); - res.end(); + res.result(boost::beast::http::status::bad_request); + res.end(); - return false; - } + return false; + } - return true; + return true; } -} // namespace json_util +} // namespace json_util -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/ut/privileges_test.cpp b/redfish-core/ut/privileges_test.cpp index 92cd6c42a4..d857290250 100644 --- a/redfish-core/ut/privileges_test.cpp +++ b/redfish-core/ut/privileges_test.cpp @@ -1,114 +1,127 @@ +#include "nlohmann/json.hpp" #include "privileges.hpp" + #include <fstream> #include <string> -#include "nlohmann/json.hpp" + #include "gmock/gmock.h" using namespace redfish; -TEST(PrivilegeTest, PrivilegeConstructor) { - Privileges privileges{"Login", "ConfigureManager"}; +TEST(PrivilegeTest, PrivilegeConstructor) +{ + Privileges privileges{"Login", "ConfigureManager"}; - EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), - ::testing::UnorderedElementsAre( - ::testing::Pointee(&"Login"[0]), - ::testing::Pointee(&"ConfigureManager"[0]))); + EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), + ::testing::UnorderedElementsAre( + ::testing::Pointee(&"Login"[0]), + ::testing::Pointee(&"ConfigureManager"[0]))); } -TEST(PrivilegeTest, PrivilegeCheckForNoPrivilegesRequired) { - Privileges userPrivileges{"Login"}; +TEST(PrivilegeTest, PrivilegeCheckForNoPrivilegesRequired) +{ + Privileges userPrivileges{"Login"}; - OperationMap entityPrivileges{{boost::beast::http::verb::get, {{"Login"}}}}; + OperationMap entityPrivileges{{boost::beast::http::verb::get, {{"Login"}}}}; - EXPECT_TRUE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_TRUE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, PrivilegeCheckForSingleCaseSuccess) { - auto userPrivileges = Privileges{"Login"}; - OperationMap entityPrivileges{{boost::beast::http::verb::get, {}}}; +TEST(PrivilegeTest, PrivilegeCheckForSingleCaseSuccess) +{ + auto userPrivileges = Privileges{"Login"}; + OperationMap entityPrivileges{{boost::beast::http::verb::get, {}}}; - EXPECT_TRUE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_TRUE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, PrivilegeCheckForSingleCaseFailure) { - auto userPrivileges = Privileges{"Login"}; - OperationMap entityPrivileges{ - {boost::beast::http::verb::get, {{"ConfigureManager"}}}}; +TEST(PrivilegeTest, PrivilegeCheckForSingleCaseFailure) +{ + auto userPrivileges = Privileges{"Login"}; + OperationMap entityPrivileges{ + {boost::beast::http::verb::get, {{"ConfigureManager"}}}}; - EXPECT_FALSE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_FALSE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, PrivilegeCheckForANDCaseSuccess) { - auto userPrivileges = - Privileges{"Login", "ConfigureManager", "ConfigureSelf"}; - OperationMap entityPrivileges{ - {boost::beast::http::verb::get, - {{"Login", "ConfigureManager", "ConfigureSelf"}}}}; +TEST(PrivilegeTest, PrivilegeCheckForANDCaseSuccess) +{ + auto userPrivileges = + Privileges{"Login", "ConfigureManager", "ConfigureSelf"}; + OperationMap entityPrivileges{ + {boost::beast::http::verb::get, + {{"Login", "ConfigureManager", "ConfigureSelf"}}}}; - EXPECT_TRUE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_TRUE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, PrivilegeCheckForANDCaseFailure) { - auto userPrivileges = Privileges{"Login", "ConfigureManager"}; - OperationMap entityPrivileges{ - {boost::beast::http::verb::get, - {{"Login", "ConfigureManager", "ConfigureSelf"}}}}; +TEST(PrivilegeTest, PrivilegeCheckForANDCaseFailure) +{ + auto userPrivileges = Privileges{"Login", "ConfigureManager"}; + OperationMap entityPrivileges{ + {boost::beast::http::verb::get, + {{"Login", "ConfigureManager", "ConfigureSelf"}}}}; - EXPECT_FALSE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_FALSE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, PrivilegeCheckForORCaseSuccess) { - auto userPrivileges = Privileges{"ConfigureManager"}; - OperationMap entityPrivileges{ - {boost::beast::http::verb::get, {{"Login"}, {"ConfigureManager"}}}}; +TEST(PrivilegeTest, PrivilegeCheckForORCaseSuccess) +{ + auto userPrivileges = Privileges{"ConfigureManager"}; + OperationMap entityPrivileges{ + {boost::beast::http::verb::get, {{"Login"}, {"ConfigureManager"}}}}; - EXPECT_TRUE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_TRUE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, PrivilegeCheckForORCaseFailure) { - auto userPrivileges = Privileges{"ConfigureComponents"}; - OperationMap entityPrivileges = OperationMap( - {{boost::beast::http::verb::get, {{"Login"}, {"ConfigureManager"}}}}); +TEST(PrivilegeTest, PrivilegeCheckForORCaseFailure) +{ + auto userPrivileges = Privileges{"ConfigureComponents"}; + OperationMap entityPrivileges = OperationMap( + {{boost::beast::http::verb::get, {{"Login"}, {"ConfigureManager"}}}}); - EXPECT_FALSE(isMethodAllowedWithPrivileges(boost::beast::http::verb::get, - entityPrivileges, userPrivileges)); + EXPECT_FALSE(isMethodAllowedWithPrivileges( + boost::beast::http::verb::get, entityPrivileges, userPrivileges)); } -TEST(PrivilegeTest, DefaultPrivilegeBitsetsAreEmpty) { - Privileges privileges; +TEST(PrivilegeTest, DefaultPrivilegeBitsetsAreEmpty) +{ + Privileges privileges; - EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), - ::testing::IsEmpty()); + EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), + ::testing::IsEmpty()); - EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::OEM), - ::testing::IsEmpty()); + EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::OEM), + ::testing::IsEmpty()); } -TEST(PrivilegeTest, GetActivePrivilegeNames) { - Privileges privileges; - - EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), - ::testing::IsEmpty()); - - std::array<const char*, 5> expectedPrivileges{ - "Login", "ConfigureManager", "ConfigureUsers", "ConfigureComponents", - "ConfigureSelf"}; - - for (const auto& privilege : expectedPrivileges) { - EXPECT_TRUE(privileges.setSinglePrivilege(privilege)); - } - - EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), - ::testing::UnorderedElementsAre( - ::testing::Pointee(expectedPrivileges[0]), - ::testing::Pointee(expectedPrivileges[1]), - ::testing::Pointee(expectedPrivileges[2]), - ::testing::Pointee(expectedPrivileges[3]), - ::testing::Pointee(expectedPrivileges[4]))); +TEST(PrivilegeTest, GetActivePrivilegeNames) +{ + Privileges privileges; + + EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), + ::testing::IsEmpty()); + + std::array<const char*, 5> expectedPrivileges{ + "Login", "ConfigureManager", "ConfigureUsers", "ConfigureComponents", + "ConfigureSelf"}; + + for (const auto& privilege : expectedPrivileges) + { + EXPECT_TRUE(privileges.setSinglePrivilege(privilege)); + } + + EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::BASE), + ::testing::UnorderedElementsAre( + ::testing::Pointee(expectedPrivileges[0]), + ::testing::Pointee(expectedPrivileges[1]), + ::testing::Pointee(expectedPrivileges[2]), + ::testing::Pointee(expectedPrivileges[3]), + ::testing::Pointee(expectedPrivileges[4]))); } diff --git a/src/ast_jpeg_decoder_test.cpp b/src/ast_jpeg_decoder_test.cpp index 277ba2cc8b..1552f46f76 100644 --- a/src/ast_jpeg_decoder_test.cpp +++ b/src/ast_jpeg_decoder_test.cpp @@ -1,4 +1,5 @@ #include "ast_jpeg_decoder.hpp" + #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -10,150 +11,160 @@ using namespace testing; MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + - PrintToString(a) + " and " + PrintToString(b)) { - return a <= arg && arg <= b; + PrintToString(a) + " and " + PrintToString(b)) +{ + return a <= arg && arg <= b; }; -TEST(AstJpegDecoder, AllBlue) { - ast_video::RawVideoBuffer out; - - // This binary blog was created on the aspeed hardware using a blue screen - // consisting of the color 0x8EFFFA in a web browser window - FILE *fp = fopen("test_resources/aspeedbluescreen.bin", "rb"); - EXPECT_NE(fp, nullptr); - size_t bufferlen = - fread(out.buffer.data(), sizeof(decltype(out.buffer)::value_type), - out.buffer.size(), fp); - fclose(fp); - - ASSERT_GT(bufferlen, 0); - - out.ySelector = 0; - out.uvSelector = 0; - out.mode = ast_video::YuvMode::YUV444; - out.width = 800; - out.height = 600; - - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, - out.uvSelector); - - int tolerance = 16; - - // All pixels should be blue (0x8EFFFA) to within a tolerance (due to jpeg - // compression artifacts and quanitization) - for (int i = 0; i < out.width * out.height; i++) { - ast_video::RGB &pixel = d.outBuffer[i]; - EXPECT_GT(pixel.r, 0x8E - tolerance); - EXPECT_LT(pixel.r, 0x8E + tolerance); - EXPECT_GT(pixel.g, 0xFF - tolerance); - EXPECT_LT(pixel.g, 0xFF + tolerance); - EXPECT_GT(pixel.b, 0xF1 - tolerance); - EXPECT_LT(pixel.b, 0xF1 + tolerance); - } +TEST(AstJpegDecoder, AllBlue) +{ + ast_video::RawVideoBuffer out; + + // This binary blog was created on the aspeed hardware using a blue screen + // consisting of the color 0x8EFFFA in a web browser window + FILE *fp = fopen("test_resources/aspeedbluescreen.bin", "rb"); + EXPECT_NE(fp, nullptr); + size_t bufferlen = + fread(out.buffer.data(), sizeof(decltype(out.buffer)::value_type), + out.buffer.size(), fp); + fclose(fp); + + ASSERT_GT(bufferlen, 0); + + out.ySelector = 0; + out.uvSelector = 0; + out.mode = ast_video::YuvMode::YUV444; + out.width = 800; + out.height = 600; + + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, + out.uvSelector); + + int tolerance = 16; + + // All pixels should be blue (0x8EFFFA) to within a tolerance (due to jpeg + // compression artifacts and quanitization) + for (int i = 0; i < out.width * out.height; i++) + { + ast_video::RGB &pixel = d.outBuffer[i]; + EXPECT_GT(pixel.r, 0x8E - tolerance); + EXPECT_LT(pixel.r, 0x8E + tolerance); + EXPECT_GT(pixel.g, 0xFF - tolerance); + EXPECT_LT(pixel.g, 0xFF + tolerance); + EXPECT_GT(pixel.b, 0xF1 - tolerance); + EXPECT_LT(pixel.b, 0xF1 + tolerance); + } } -TEST(AstJpegDecoder, AllBlack) { - ast_video::RawVideoBuffer out; - - // This binary blog was created on the aspeed hardware using a black screen - FILE *fp = fopen("test_resources/aspeedblackscreen.bin", "rb"); - EXPECT_NE(fp, nullptr); - size_t bufferlen = fread(out.buffer.data(), sizeof(char), - out.buffer.size() * sizeof(long), fp); - fclose(fp); - - ASSERT_GT(bufferlen, 0); - - out.ySelector = 0; - out.uvSelector = 0; - out.mode = ast_video::YuvMode::YUV444; - out.width = 800; - out.height = 600; - - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, - out.uvSelector); - - // All pixels should be blue (0x8EFFFA) to within a tolerance (due to jpeg - // compression artifacts and quanitization) - for (int x = 0; x < out.width; x++) { - for (int y = 0; y < out.height; y++) { - ast_video::RGB pixel = d.outBuffer[x + (y * out.width)]; - ASSERT_EQ(pixel.r, 0x00) << "X:" << x << " Y: " << y; - ASSERT_EQ(pixel.g, 0x00) << "X:" << x << " Y: " << y; - ASSERT_EQ(pixel.b, 0x00) << "X:" << x << " Y: " << y; +TEST(AstJpegDecoder, AllBlack) +{ + ast_video::RawVideoBuffer out; + + // This binary blog was created on the aspeed hardware using a black screen + FILE *fp = fopen("test_resources/aspeedblackscreen.bin", "rb"); + EXPECT_NE(fp, nullptr); + size_t bufferlen = fread(out.buffer.data(), sizeof(char), + out.buffer.size() * sizeof(long), fp); + fclose(fp); + + ASSERT_GT(bufferlen, 0); + + out.ySelector = 0; + out.uvSelector = 0; + out.mode = ast_video::YuvMode::YUV444; + out.width = 800; + out.height = 600; + + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, + out.uvSelector); + + // All pixels should be blue (0x8EFFFA) to within a tolerance (due to jpeg + // compression artifacts and quanitization) + for (int x = 0; x < out.width; x++) + { + for (int y = 0; y < out.height; y++) + { + ast_video::RGB pixel = d.outBuffer[x + (y * out.width)]; + ASSERT_EQ(pixel.r, 0x00) << "X:" << x << " Y: " << y; + ASSERT_EQ(pixel.g, 0x00) << "X:" << x << " Y: " << y; + ASSERT_EQ(pixel.b, 0x00) << "X:" << x << " Y: " << y; + } } - } } -TEST(AstJpegDecoder, TestColors) { - ast_video::RawVideoBuffer out; - - // This binary blog was created on the aspeed hardware using a blue screen - // consisting of the color 0x8EFFFA in a web browser window - FILE *fp = fopen("test_resources/ubuntu_444_800x600_0chrom_0lum.bin", "rb"); - EXPECT_NE(fp, nullptr); - size_t bufferlen = fread(out.buffer.data(), sizeof(char), - out.buffer.size() * sizeof(long), fp); - fclose(fp); - - ASSERT_GT(bufferlen, 0); - - out.ySelector = 0; - out.uvSelector = 0; - out.mode = ast_video::YuvMode::YUV444; - out.width = 800; - out.height = 600; - - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, - out.uvSelector); - - int tolerance = 16; - /* - for (int i = 0; i < out.width * out.height; i++) { - ast_video::RGB &pixel = d.outBuffer[i]; - EXPECT_GT(pixel.r, 0x8E - tolerance); - EXPECT_LT(pixel.r, 0x8E + tolerance); - EXPECT_GT(pixel.g, 0xFF - tolerance); - EXPECT_LT(pixel.g, 0xFF + tolerance); - EXPECT_GT(pixel.b, 0xF1 - tolerance); - EXPECT_LT(pixel.b, 0xF1 + tolerance); - } - */ +TEST(AstJpegDecoder, TestColors) +{ + ast_video::RawVideoBuffer out; + + // This binary blog was created on the aspeed hardware using a blue screen + // consisting of the color 0x8EFFFA in a web browser window + FILE *fp = fopen("test_resources/ubuntu_444_800x600_0chrom_0lum.bin", "rb"); + EXPECT_NE(fp, nullptr); + size_t bufferlen = fread(out.buffer.data(), sizeof(char), + out.buffer.size() * sizeof(long), fp); + fclose(fp); + + ASSERT_GT(bufferlen, 0); + + out.ySelector = 0; + out.uvSelector = 0; + out.mode = ast_video::YuvMode::YUV444; + out.width = 800; + out.height = 600; + + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, + out.uvSelector); + + int tolerance = 16; + /* + for (int i = 0; i < out.width * out.height; i++) { + ast_video::RGB &pixel = d.outBuffer[i]; + EXPECT_GT(pixel.r, 0x8E - tolerance); + EXPECT_LT(pixel.r, 0x8E + tolerance); + EXPECT_GT(pixel.g, 0xFF - tolerance); + EXPECT_LT(pixel.g, 0xFF + tolerance); + EXPECT_GT(pixel.b, 0xF1 - tolerance); + EXPECT_LT(pixel.b, 0xF1 + tolerance); + } + */ } // Tests the buffers around the screen aren't written to -TEST(AstJpegDecoder, BufferLimits) { - ast_video::RawVideoBuffer out; - - // This binary blog was created on the aspeed hardware using a black screen - FILE *fp = fopen("test_resources/aspeedblackscreen.bin", "rb"); - EXPECT_NE(fp, nullptr); - size_t bufferlen = fread(out.buffer.data(), sizeof(char), - out.buffer.size() * sizeof(long), fp); - fclose(fp); - - ASSERT_GT(bufferlen, 0); - - out.ySelector = 0; - out.uvSelector = 0; - out.mode = ast_video::YuvMode::YUV444; - out.width = 800; - out.height = 600; - - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, - out.uvSelector); - // reserved pixel should be default value - for (auto &pixel : d.outBuffer) { - EXPECT_EQ(pixel.reserved, 0xAA); - } - // All pixels beyond the buffer should be zero - for (int i = out.width * out.height; i < d.outBuffer.size(); i++) { - EXPECT_EQ(d.outBuffer[i].r, 0x00) << "index:" << i; - EXPECT_EQ(d.outBuffer[i].b, 0x00) << "index:" << i; - EXPECT_EQ(d.outBuffer[i].g, 0x00) << "index:" << i; - } +TEST(AstJpegDecoder, BufferLimits) +{ + ast_video::RawVideoBuffer out; + + // This binary blog was created on the aspeed hardware using a black screen + FILE *fp = fopen("test_resources/aspeedblackscreen.bin", "rb"); + EXPECT_NE(fp, nullptr); + size_t bufferlen = fread(out.buffer.data(), sizeof(char), + out.buffer.size() * sizeof(long), fp); + fclose(fp); + + ASSERT_GT(bufferlen, 0); + + out.ySelector = 0; + out.uvSelector = 0; + out.mode = ast_video::YuvMode::YUV444; + out.width = 800; + out.height = 600; + + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, + out.uvSelector); + // reserved pixel should be default value + for (auto &pixel : d.outBuffer) + { + EXPECT_EQ(pixel.reserved, 0xAA); + } + // All pixels beyond the buffer should be zero + for (int i = out.width * out.height; i < d.outBuffer.size(); i++) + { + EXPECT_EQ(d.outBuffer[i].r, 0x00) << "index:" << i; + EXPECT_EQ(d.outBuffer[i].b, 0x00) << "index:" << i; + EXPECT_EQ(d.outBuffer[i].g, 0x00) << "index:" << i; + } }
\ No newline at end of file diff --git a/src/ast_video_puller_test.cpp b/src/ast_video_puller_test.cpp index 58adda9697..4fa3dccc68 100644 --- a/src/ast_video_puller_test.cpp +++ b/src/ast_video_puller_test.cpp @@ -2,6 +2,7 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> + #include <ast_jpeg_decoder.hpp> #include <ast_video_puller.hpp> #include <chrono> @@ -10,38 +11,46 @@ #include <iostream> #include <thread> #include <vector> + #include <gmock/gmock.h> #include <gtest/gtest.h> -TEST(AstvideoPuller, BasicRead) { - ast_video::RawVideoBuffer out; - bool have_hardware = false; - if (access("/dev/video", F_OK) != -1) { - ast_video::SimpleVideoPuller p; - p.initialize(); - out = p.readVideo(); - } else { - FILE *fp = fopen("test_resources/ubuntu_444_800x600_0chrom_0lum.bin", "rb"); - if (fp) { - size_t newLen = fread(out.buffer.data(), sizeof(char), - out.buffer.size() * sizeof(long), fp); - if (ferror(fp) != 0) { - fputs("Error reading file", stderr); - } - fclose(fp); - out.buffer.resize(newLen); - out.mode = ast_video::YuvMode::YUV444; - out.width = 800; - out.height = 600; - out.ySelector = 0; - out.uvSelector = 0; +TEST(AstvideoPuller, BasicRead) +{ + ast_video::RawVideoBuffer out; + bool have_hardware = false; + if (access("/dev/video", F_OK) != -1) + { + ast_video::SimpleVideoPuller p; + p.initialize(); + out = p.readVideo(); + } + else + { + FILE *fp = + fopen("test_resources/ubuntu_444_800x600_0chrom_0lum.bin", "rb"); + if (fp) + { + size_t newLen = fread(out.buffer.data(), sizeof(char), + out.buffer.size() * sizeof(long), fp); + if (ferror(fp) != 0) + { + fputs("Error reading file", stderr); + } + fclose(fp); + out.buffer.resize(newLen); + out.mode = ast_video::YuvMode::YUV444; + out.width = 800; + out.height = 600; + out.ySelector = 0; + out.uvSelector = 0; + } } - } - FILE *fp = fopen("/tmp/screendata.bin", "wb"); - fwrite(out.buffer.data(), sizeof(char), out.buffer.size(), fp); + FILE *fp = fopen("/tmp/screendata.bin", "wb"); + fwrite(out.buffer.data(), sizeof(char), out.buffer.size(), fp); - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, - out.uvSelector); + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, + out.uvSelector); } diff --git a/src/base64.cpp b/src/base64.cpp index 0013c28ac3..851ef72a42 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -1,143 +1,165 @@ #include <base64.hpp> -namespace base64 { -bool base64_encode(const std::string &input, std::string &output) { - // This is left as a raw array (and not a range checked std::array) under the - // suspicion that the optimizer is not smart enough to remove the range checks - // that would be done below if at were called. As is, this array is 64 bytes - // long, which should be greater than the max of 0b00111111 when indexed - // NOLINT calls below are to silence clang-tidy about this - // TODO(ed) this requires further investigation if a more safe method could be - // used without performance impact. - static const char encoding_data[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - size_t input_length = input.size(); - - // allocate space for output string - output.clear(); - output.reserve(((input_length + 2) / 3) * 4); - - // for each 3-bytes sequence from the input, extract 4 6-bits sequences and - // encode using - // encoding_data lookup table. - // if input do not contains enough chars to complete 3-byte sequence,use pad - // char '=' - for (size_t i = 0; i < input_length; i++) { - int base64code0 = 0; - int base64code1 = 0; - int base64code2 = 0; - int base64code3 = 0; - - base64code0 = (input[i] >> 2) & 0x3f; // 1-byte 6 bits - - output += encoding_data[base64code0]; // NOLINT - base64code1 = (input[i] << 4) & 0x3f; // 1-byte 2 bits + - - if (++i < input_length) { - base64code1 |= (input[i] >> 4) & 0x0f; // 2-byte 4 bits - output += encoding_data[base64code1]; // NOLINT - base64code2 = (input[i] << 2) & 0x3f; // 2-byte 4 bits + - - if (++i < input_length) { - base64code2 |= (input[i] >> 6) & 0x03; // 3-byte 2 bits - base64code3 = input[i] & 0x3f; // 3-byte 6 bits - output += encoding_data[base64code2]; // NOLINT - output += encoding_data[base64code3]; // NOLINT - } else { - output += encoding_data[base64code2]; // NOLINT - output += '='; - } - } else { - output += encoding_data[base64code1]; // NOLINT - output += '='; - output += '='; +namespace base64 +{ +bool base64_encode(const std::string &input, std::string &output) +{ + // This is left as a raw array (and not a range checked std::array) under + // the suspicion that the optimizer is not smart enough to remove the range + // checks that would be done below if at were called. As is, this array is + // 64 bytes long, which should be greater than the max of 0b00111111 when + // indexed NOLINT calls below are to silence clang-tidy about this + // TODO(ed) this requires further investigation if a more safe method could + // be used without performance impact. + static const char encoding_data[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + size_t input_length = input.size(); + + // allocate space for output string + output.clear(); + output.reserve(((input_length + 2) / 3) * 4); + + // for each 3-bytes sequence from the input, extract 4 6-bits sequences and + // encode using + // encoding_data lookup table. + // if input do not contains enough chars to complete 3-byte sequence,use pad + // char '=' + for (size_t i = 0; i < input_length; i++) + { + int base64code0 = 0; + int base64code1 = 0; + int base64code2 = 0; + int base64code3 = 0; + + base64code0 = (input[i] >> 2) & 0x3f; // 1-byte 6 bits + + output += encoding_data[base64code0]; // NOLINT + base64code1 = (input[i] << 4) & 0x3f; // 1-byte 2 bits + + + if (++i < input_length) + { + base64code1 |= (input[i] >> 4) & 0x0f; // 2-byte 4 bits + output += encoding_data[base64code1]; // NOLINT + base64code2 = (input[i] << 2) & 0x3f; // 2-byte 4 bits + + + if (++i < input_length) + { + base64code2 |= (input[i] >> 6) & 0x03; // 3-byte 2 bits + base64code3 = input[i] & 0x3f; // 3-byte 6 bits + output += encoding_data[base64code2]; // NOLINT + output += encoding_data[base64code3]; // NOLINT + } + else + { + output += encoding_data[base64code2]; // NOLINT + output += '='; + } + } + else + { + output += encoding_data[base64code1]; // NOLINT + output += '='; + output += '='; + } } - } - return true; + return true; } -bool base64_decode(const std::string &input, std::string &output) { - static const char nop = -1; - // See note on encoding_data[] in above function - static const char decoding_data[] = { - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 62, nop, - nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, nop, nop, - nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, nop, nop, nop, nop, nop, nop, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop}; - - size_t input_length = input.size(); - - // allocate space for output string - output.clear(); - output.reserve(((input_length + 2) / 3) * 4); - - // for each 4-bytes sequence from the input, extract 4 6-bits sequences by - // droping first two bits - // and regenerate into 3 8-bits sequences - - for (size_t i = 0; i < input_length; i++) { - char base64code0; - char base64code1; - char base64code2 = 0; // initialized to 0 to suppress warnings - char base64code3; - - base64code0 = decoding_data[static_cast<int>(input[i])]; // NOLINT - if (base64code0 == nop) { // non base64 character - return false; +bool base64_decode(const std::string &input, std::string &output) +{ + static const char nop = -1; + // See note on encoding_data[] in above function + static const char decoding_data[] = { + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop}; + + size_t input_length = input.size(); + + // allocate space for output string + output.clear(); + output.reserve(((input_length + 2) / 3) * 4); + + // for each 4-bytes sequence from the input, extract 4 6-bits sequences by + // droping first two bits + // and regenerate into 3 8-bits sequences + + for (size_t i = 0; i < input_length; i++) + { + char base64code0; + char base64code1; + char base64code2 = 0; // initialized to 0 to suppress warnings + char base64code3; + + base64code0 = decoding_data[static_cast<int>(input[i])]; // NOLINT + if (base64code0 == nop) + { // non base64 character + return false; + } + if (!(++i < input_length)) + { // we need at least two input bytes for first + // byte output + return false; + } + base64code1 = decoding_data[static_cast<int>(input[i])]; // NOLINT + if (base64code1 == nop) + { // non base64 character + return false; + } + output += + static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); + + if (++i < input_length) + { + char c = input[i]; + if (c == '=') + { // padding , end of input + return (base64code1 & 0x0f) == 0; + } + base64code2 = decoding_data[static_cast<int>(input[i])]; // NOLINT + if (base64code2 == nop) + { // non base64 character + return false; + } + output += static_cast<char>(((base64code1 << 4) & 0xf0) | + ((base64code2 >> 2) & 0x0f)); + } + + if (++i < input_length) + { + char c = input[i]; + if (c == '=') + { // padding , end of input + return (base64code2 & 0x03) == 0; + } + base64code3 = decoding_data[static_cast<int>(input[i])]; // NOLINT + if (base64code3 == nop) + { // non base64 character + return false; + } + output += + static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); + } } - if (!(++i < input_length)) { // we need at least two input bytes for first - // byte output - return false; - } - base64code1 = decoding_data[static_cast<int>(input[i])]; // NOLINT - if (base64code1 == nop) { // non base64 character - return false; - } - output += - static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); - - if (++i < input_length) { - char c = input[i]; - if (c == '=') { // padding , end of input - return (base64code1 & 0x0f) == 0; - } - base64code2 = decoding_data[static_cast<int>(input[i])]; // NOLINT - if (base64code2 == nop) { // non base64 character - return false; - } - output += static_cast<char>(((base64code1 << 4) & 0xf0) | - ((base64code2 >> 2) & 0x0f)); - } - - if (++i < input_length) { - char c = input[i]; - if (c == '=') { // padding , end of input - return (base64code2 & 0x03) == 0; - } - base64code3 = decoding_data[static_cast<int>(input[i])]; // NOLINT - if (base64code3 == nop) { // non base64 character - return false; - } - output += static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); - } - } - return true; + return true; } -} // namespace base64 +} // namespace base64 diff --git a/src/crow_getroutes_test.cpp b/src/crow_getroutes_test.cpp index 29052a9a21..e76d221479 100644 --- a/src/crow_getroutes_test.cpp +++ b/src/crow_getroutes_test.cpp @@ -1,43 +1,47 @@ #include <crow/app.h> + #include "gmock/gmock.h" #include "gtest/gtest.h" using namespace crow; using namespace std; -TEST(GetRoutes, TestEmptyRoutes) { - SimpleApp app; - decltype(app)::server_t server(&app, "127.0.0.1", 45451); +TEST(GetRoutes, TestEmptyRoutes) +{ + SimpleApp app; + decltype(app)::server_t server(&app, "127.0.0.1", 45451); - EXPECT_THAT(app.getRoutes(), testing::IsEmpty()); + EXPECT_THAT(app.getRoutes(), testing::IsEmpty()); } // Tests that static urls are correctly passed -TEST(GetRoutes, TestOneRoute) { - SimpleApp app; - decltype(app)::server_t server(&app, "127.0.0.1", 45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); +TEST(GetRoutes, TestOneRoute) +{ + SimpleApp app; + decltype(app)::server_t server(&app, "127.0.0.1", 45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - EXPECT_THAT(app.getRoutes(), - testing::ElementsAre(testing::Pointee(std::string("/")))); + EXPECT_THAT(app.getRoutes(), + testing::ElementsAre(testing::Pointee(std::string("/")))); } // Tests that static urls are correctly passed -TEST(GetRoutes, TestlotsOfRoutes) { - SimpleApp app; - decltype(app)::server_t server(&app, "127.0.0.1", 45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - BMCWEB_ROUTE(app, "/foo")([]() { return boost::beast::http::status::ok; }); - BMCWEB_ROUTE(app, "/bar")([]() { return boost::beast::http::status::ok; }); - BMCWEB_ROUTE(app, "/baz")([]() { return boost::beast::http::status::ok; }); - BMCWEB_ROUTE(app, "/boo")([]() { return boost::beast::http::status::ok; }); - BMCWEB_ROUTE(app, "/moo")([]() { return boost::beast::http::status::ok; }); - - EXPECT_THAT(app.getRoutes(), testing::UnorderedElementsAre( - testing::Pointee(std::string("/")), - testing::Pointee(std::string("/foo")), - testing::Pointee(std::string("/bar")), - testing::Pointee(std::string("/baz")), - testing::Pointee(std::string("/boo")), - testing::Pointee(std::string("/moo")))); +TEST(GetRoutes, TestlotsOfRoutes) +{ + SimpleApp app; + decltype(app)::server_t server(&app, "127.0.0.1", 45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/foo")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/bar")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/baz")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/boo")([]() { return boost::beast::http::status::ok; }); + BMCWEB_ROUTE(app, "/moo")([]() { return boost::beast::http::status::ok; }); + + EXPECT_THAT(app.getRoutes(), testing::UnorderedElementsAre( + testing::Pointee(std::string("/")), + testing::Pointee(std::string("/foo")), + testing::Pointee(std::string("/bar")), + testing::Pointee(std::string("/baz")), + testing::Pointee(std::string("/boo")), + testing::Pointee(std::string("/moo")))); }
\ No newline at end of file diff --git a/src/crow_test.cpp b/src/crow_test.cpp index cd668171db..96b90b0ed2 100644 --- a/src/crow_test.cpp +++ b/src/crow_test.cpp @@ -1,7 +1,9 @@ -#include "crow.h" #include <iostream> #include <sstream> #include <vector> + +#include "crow.h" + #include "gtest/gtest.h" #undef BMCWEB_LOG_LEVEL #define BMCWEB_LOG_LEVEL 0 @@ -10,864 +12,934 @@ using namespace std; using namespace crow; bool failed__ = false; -void error_print() { cerr << endl; } +void error_print() +{ + cerr << endl; +} template <typename A, typename... Args> -void error_print(const A& a, Args... args) { - cerr << a; - error_print(args...); +void error_print(const A& a, Args... args) +{ + cerr << a; + error_print(args...); } -template <typename... Args> -void fail(Args... args) { - error_print(args...); - failed__ = true; +template <typename... Args> void fail(Args... args) +{ + error_print(args...); + failed__ = true; } -#define ASSERT_EQUAL(a, b) \ - if ((a) != (b)) \ - fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", \ - (b), ", " #a " == " #b ", at " __FILE__ ":", __LINE__) -#define ASSERT_NOTEQUAL(a, b) \ - if ((a) == (b)) \ - fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), \ - ", " #a " != " #b ", at " __FILE__ ":", __LINE__) - -#define DISABLE_TEST(x) \ - struct test##x { \ - void test(); \ - } x##_; \ - void test##x::test() +#define ASSERT_EQUAL(a, b) \ + if ((a) != (b)) \ + fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", \ + (b), ", " #a " == " #b ", at " __FILE__ ":", __LINE__) +#define ASSERT_NOTEQUAL(a, b) \ + if ((a) == (b)) \ + fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), \ + ", " #a " != " #b ", at " __FILE__ ":", __LINE__) + +#define DISABLE_TEST(x) \ + struct test##x \ + { \ + void test(); \ + } x##_; \ + void test##x::test() #define LOCALHOST_ADDRESS "127.0.0.1" -TEST(Crow, Rule) { - TaggedRule<> r("/http/"); - r.name("abc"); +TEST(Crow, Rule) +{ + TaggedRule<> r("/http/"); + r.name("abc"); - // empty handler - fail to validate - try { - r.validate(); - fail("empty handler should fail to validate"); - } catch (runtime_error& e) { - } + // empty handler - fail to validate + try + { + r.validate(); + fail("empty handler should fail to validate"); + } + catch (runtime_error& e) + { + } - int x = 0; + int x = 0; - // registering handler - r([&x] { - x = 1; - return ""; - }); + // registering handler + r([&x] { + x = 1; + return ""; + }); - r.validate(); + r.validate(); - Response res; + Response res; - // executing handler - ASSERT_EQUAL(0, x); - boost::beast::http::request<boost::beast::http::string_body> req{}; - r.handle(Request(req), res, RoutingParams()); - ASSERT_EQUAL(1, x); + // executing handler + ASSERT_EQUAL(0, x); + boost::beast::http::request<boost::beast::http::string_body> req{}; + r.handle(Request(req), res, RoutingParams()); + ASSERT_EQUAL(1, x); - // registering handler with Request argument - r([&x](const crow::Request&) { - x = 2; - return ""; - }); + // registering handler with Request argument + r([&x](const crow::Request&) { + x = 2; + return ""; + }); - r.validate(); + r.validate(); - // executing handler - ASSERT_EQUAL(1, x); - r.handle(Request(req), res, RoutingParams()); - ASSERT_EQUAL(2, x); + // executing handler + ASSERT_EQUAL(1, x); + r.handle(Request(req), res, RoutingParams()); + ASSERT_EQUAL(2, x); } -TEST(Crow, ParameterTagging) { - static_assert(black_magic::isValid("<int><int><int>"), "valid url"); - static_assert(!black_magic::isValid("<int><int<<int>"), "invalid url"); - static_assert(!black_magic::isValid("nt>"), "invalid url"); - ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>")); - ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>")); - ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>")); - ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>")); - ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>")); - ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>")); - ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>")); - ASSERT_EQUAL(6 * 6 + 6 + 1, - black_magic::get_parameter_tag("<int><int><int>")); - ASSERT_EQUAL(6 * 6 + 6 + 2, - black_magic::get_parameter_tag("<uint><int><int>")); - ASSERT_EQUAL(6 * 6 + 6 * 3 + 2, - black_magic::get_parameter_tag("<uint><double><int>")); - - // url definition parsed in compile time, build into *one number*, and given - // to template argument - static_assert( - std::is_same<black_magic::S<uint64_t, double, int64_t>, - black_magic::Arguments<6 * 6 + 6 * 3 + 2>::type>::value, - "tag to type container"); +TEST(Crow, ParameterTagging) +{ + static_assert(black_magic::isValid("<int><int><int>"), "valid url"); + static_assert(!black_magic::isValid("<int><int<<int>"), "invalid url"); + static_assert(!black_magic::isValid("nt>"), "invalid url"); + ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>")); + ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>")); + ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>")); + ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>")); + ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>")); + ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>")); + ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>")); + ASSERT_EQUAL(6 * 6 + 6 + 1, + black_magic::get_parameter_tag("<int><int><int>")); + ASSERT_EQUAL(6 * 6 + 6 + 2, + black_magic::get_parameter_tag("<uint><int><int>")); + ASSERT_EQUAL(6 * 6 + 6 * 3 + 2, + black_magic::get_parameter_tag("<uint><double><int>")); + + // url definition parsed in compile time, build into *one number*, and given + // to template argument + static_assert( + std::is_same<black_magic::S<uint64_t, double, int64_t>, + black_magic::Arguments<6 * 6 + 6 * 3 + 2>::type>::value, + "tag to type container"); } -TEST(Crow, PathRouting) { - SimpleApp app; +TEST(Crow, PathRouting) +{ + SimpleApp app; - BMCWEB_ROUTE(app, "/file") - ([] { return "file"; }); + BMCWEB_ROUTE(app, "/file") + ([] { return "file"; }); - BMCWEB_ROUTE(app, "/path/") - ([] { return "path"; }); + BMCWEB_ROUTE(app, "/path/") + ([] { return "path"; }); - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - req.url = "/file"; + req.url = "/file"; - app.handle(req, res); + app.handle(req, res); - ASSERT_EQUAL(200, res.resultInt()); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + ASSERT_EQUAL(200, res.resultInt()); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - req.url = "/file/"; + req.url = "/file/"; - app.handle(req, res); - ASSERT_EQUAL(404, res.resultInt()); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + app.handle(req, res); + ASSERT_EQUAL(404, res.resultInt()); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - req.url = "/path"; + req.url = "/path"; - app.handle(req, res); - ASSERT_NOTEQUAL(404, res.resultInt()); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + app.handle(req, res); + ASSERT_NOTEQUAL(404, res.resultInt()); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - req.url = "/path/"; + req.url = "/path/"; - app.handle(req, res); - ASSERT_EQUAL(200, res.resultInt()); - } + app.handle(req, res); + ASSERT_EQUAL(200, res.resultInt()); + } } -TEST(Crow, RoutingTest) { - SimpleApp app; - int A{}; - uint32_t b{}; - double C{}; - string D{}; - string E{}; - - BMCWEB_ROUTE(app, "/0/<uint>") - ([&](uint32_t b) { - b = b; - return "OK"; - }); - - BMCWEB_ROUTE(app, "/1/<int>/<uint>") - ([&](int a, uint32_t b) { - A = a; - b = b; - return "OK"; - }); - - BMCWEB_ROUTE(app, "/4/<int>/<uint>/<double>/<string>") - ([&](int a, uint32_t b, double c, string d) { - A = a; - b = b; - C = c; - D = d; - return "OK"; - }); - - BMCWEB_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>") - ([&](int a, uint32_t b, double c, string d, string e) { - A = a; - b = b; - C = c; - D = d; - E = e; - return "OK"; - }); - - app.validate(); - // app.debugPrint(); - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; +TEST(Crow, RoutingTest) +{ + SimpleApp app; + int A{}; + uint32_t b{}; + double C{}; + string D{}; + string E{}; + + BMCWEB_ROUTE(app, "/0/<uint>") + ([&](uint32_t b) { + b = b; + return "OK"; + }); + + BMCWEB_ROUTE(app, "/1/<int>/<uint>") + ([&](int a, uint32_t b) { + A = a; + b = b; + return "OK"; + }); + + BMCWEB_ROUTE(app, "/4/<int>/<uint>/<double>/<string>") + ([&](int a, uint32_t b, double c, string d) { + A = a; + b = b; + C = c; + D = d; + return "OK"; + }); + + BMCWEB_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>") + ([&](int a, uint32_t b, double c, string d, string e) { + A = a; + b = b; + C = c; + D = d; + E = e; + return "OK"; + }); - req.url = "/-1"; + app.validate(); + // app.debugPrint(); + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - app.handle(req, res); + req.url = "/-1"; - ASSERT_EQUAL(404, res.resultInt()); - } + app.handle(req, res); - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + ASSERT_EQUAL(404, res.resultInt()); + } - req.url = "/0/1001999"; + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - app.handle(req, res); + req.url = "/0/1001999"; - ASSERT_EQUAL(200, res.resultInt()); + app.handle(req, res); - ASSERT_EQUAL(1001999, b); - } + ASSERT_EQUAL(200, res.resultInt()); - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + ASSERT_EQUAL(1001999, b); + } - req.url = "/1/-100/1999"; + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - app.handle(req, res); + req.url = "/1/-100/1999"; - ASSERT_EQUAL(200, res.resultInt()); + app.handle(req, res); - ASSERT_EQUAL(-100, A); - ASSERT_EQUAL(1999, b); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + ASSERT_EQUAL(200, res.resultInt()); - req.url = "/4/5000/3/-2.71828/hellhere"; + ASSERT_EQUAL(-100, A); + ASSERT_EQUAL(1999, b); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - app.handle(req, res); + req.url = "/4/5000/3/-2.71828/hellhere"; - ASSERT_EQUAL(200, res.resultInt()); + app.handle(req, res); - ASSERT_EQUAL(5000, A); - ASSERT_EQUAL(3, b); - ASSERT_EQUAL(-2.71828, C); - ASSERT_EQUAL("hellhere", D); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + ASSERT_EQUAL(200, res.resultInt()); - req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d"; + ASSERT_EQUAL(5000, A); + ASSERT_EQUAL(3, b); + ASSERT_EQUAL(-2.71828, C); + ASSERT_EQUAL("hellhere", D); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - app.handle(req, res); + req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d"; - ASSERT_EQUAL(200, res.resultInt()); + app.handle(req, res); - ASSERT_EQUAL(-5, A); - ASSERT_EQUAL(999, b); - ASSERT_EQUAL(3.141592, C); - ASSERT_EQUAL("hello_there", D); - ASSERT_EQUAL("a/b/c/d", E); - } + ASSERT_EQUAL(200, res.resultInt()); + + ASSERT_EQUAL(-5, A); + ASSERT_EQUAL(999, b); + ASSERT_EQUAL(3.141592, C); + ASSERT_EQUAL("hello_there", D); + ASSERT_EQUAL("a/b/c/d", E); + } } -TEST(Crow, simple_response_RoutingParams) { - ASSERT_EQUAL(100, - Response(boost::beast::http::status::continue_).resultInt()); - ASSERT_EQUAL(200, Response("Hello there").resultInt()); - ASSERT_EQUAL(500, Response(boost::beast::http::status::internal_server_error, - "Internal Error?") - .resultInt()); - - RoutingParams rp; - rp.intParams.push_back(1); - rp.intParams.push_back(5); - rp.uintParams.push_back(2); - rp.doubleParams.push_back(3); - rp.stringParams.push_back("hello"); - ASSERT_EQUAL(1, rp.get<int64_t>(0)); - ASSERT_EQUAL(5, rp.get<int64_t>(1)); - ASSERT_EQUAL(2, rp.get<uint64_t>(0)); - ASSERT_EQUAL(3, rp.get<double>(0)); - ASSERT_EQUAL("hello", rp.get<string>(0)); +TEST(Crow, simple_response_RoutingParams) +{ + ASSERT_EQUAL(100, + Response(boost::beast::http::status::continue_).resultInt()); + ASSERT_EQUAL(200, Response("Hello there").resultInt()); + ASSERT_EQUAL(500, + Response(boost::beast::http::status::internal_server_error, + "Internal Error?") + .resultInt()); + + RoutingParams rp; + rp.intParams.push_back(1); + rp.intParams.push_back(5); + rp.uintParams.push_back(2); + rp.doubleParams.push_back(3); + rp.stringParams.push_back("hello"); + ASSERT_EQUAL(1, rp.get<int64_t>(0)); + ASSERT_EQUAL(5, rp.get<int64_t>(1)); + ASSERT_EQUAL(2, rp.get<uint64_t>(0)); + ASSERT_EQUAL(3, rp.get<double>(0)); + ASSERT_EQUAL("hello", rp.get<string>(0)); } -TEST(Crow, handler_with_response) { - SimpleApp app; - BMCWEB_ROUTE(app, "/")([](const crow::Request&, crow::Response&) {}); +TEST(Crow, handler_with_response) +{ + SimpleApp app; + BMCWEB_ROUTE(app, "/")([](const crow::Request&, crow::Response&) {}); } -TEST(Crow, http_method) { - SimpleApp app; - - BMCWEB_ROUTE(app, "/").methods("POST"_method, - "GET"_method)([](const Request& req) { - if (req.method() == "GET"_method) - return "2"; - else - return "1"; - }); - - BMCWEB_ROUTE(app, "/get_only") - .methods("GET"_method)([](const Request& /*req*/) { return "get"; }); - BMCWEB_ROUTE(app, "/post_only") - .methods("POST"_method)([](const Request& /*req*/) { return "post"; }); - - // cannot have multiple handlers for the same url - // BMCWEB_ROUTE(app, "/") - //.methods("GET"_method) - //([]{ return "2"; }); - - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; +TEST(Crow, http_method) +{ + SimpleApp app; - req.url = "/"; - app.handle(req, res); + BMCWEB_ROUTE(app, "/").methods("POST"_method, + "GET"_method)([](const Request& req) { + if (req.method() == "GET"_method) + return "2"; + else + return "1"; + }); - ASSERT_EQUAL("2", res.body()); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + BMCWEB_ROUTE(app, "/get_only") + .methods("GET"_method)([](const Request& /*req*/) { return "get"; }); + BMCWEB_ROUTE(app, "/post_only") + .methods("POST"_method)([](const Request& /*req*/) { return "post"; }); - req.url = "/"; - r.method("POST"_method); - app.handle(req, res); + // cannot have multiple handlers for the same url + // BMCWEB_ROUTE(app, "/") + //.methods("GET"_method) + //([]{ return "2"; }); - ASSERT_EQUAL("1", res.body()); - } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + req.url = "/"; + app.handle(req, res); + + ASSERT_EQUAL("2", res.body()); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - req.url = "/get_only"; - app.handle(req, res); + req.url = "/"; + r.method("POST"_method); + app.handle(req, res); - ASSERT_EQUAL("get", res.body()); - } + ASSERT_EQUAL("1", res.body()); + } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; - req.url = "/get_only"; - r.method("POST"_method); - app.handle(req, res); + req.url = "/get_only"; + app.handle(req, res); - ASSERT_NOTEQUAL("get", res.body()); - } -} + ASSERT_EQUAL("get", res.body()); + } -TEST(Crow, server_handling_error_request) { - static char buf[2048]; - SimpleApp app; - BMCWEB_ROUTE(app, "/")([] { return "A"; }); - Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&] { server.run(); }); - std::string sendmsg = "POX"; - asio::io_service is; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - - c.send(asio::buffer(sendmsg)); - - try { - c.receive(asio::buffer(buf, 2048)); - fail(); - } catch (std::exception& e) { - // std::cerr << e.what() << std::endl; - } - } - server.stop(); + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; + + req.url = "/get_only"; + r.method("POST"_method); + app.handle(req, res); + + ASSERT_NOTEQUAL("get", res.body()); + } } -TEST(Crow, multi_server) { - static char buf[2048]; - SimpleApp app1, app2; - BMCWEB_ROUTE(app1, "/").methods("GET"_method, - "POST"_method)([] { return "A"; }); - BMCWEB_ROUTE(app2, "/").methods("GET"_method, - "POST"_method)([] { return "B"; }); +TEST(Crow, server_handling_error_request) +{ + static char buf[2048]; + SimpleApp app; + BMCWEB_ROUTE(app, "/")([] { return "A"; }); + Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451); + auto _ = async(launch::async, [&] { server.run(); }); + std::string sendmsg = "POX"; + asio::io_service is; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451); - Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452); + c.send(asio::buffer(sendmsg)); - auto _ = async(launch::async, [&] { server1.run(); }); - auto _2 = async(launch::async, [&] { server2.run(); }); + try + { + c.receive(asio::buffer(buf, 2048)); + fail(); + } + catch (std::exception& e) + { + // std::cerr << e.what() << std::endl; + } + } + server.stop(); +} - std::string sendmsg = - "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=b\r\n"; - asio::io_service is; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); +TEST(Crow, multi_server) +{ + static char buf[2048]; + SimpleApp app1, app2; + BMCWEB_ROUTE(app1, "/").methods("GET"_method, + "POST"_method)([] { return "A"; }); + BMCWEB_ROUTE(app2, "/").methods("GET"_method, + "POST"_method)([] { return "B"; }); - c.send(asio::buffer(sendmsg)); + Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451); + Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452); - size_t recved = c.receive(asio::buffer(buf, 2048)); - ASSERT_EQUAL('A', buf[recved - 1]); - } + auto _ = async(launch::async, [&] { server1.run(); }); + auto _2 = async(launch::async, [&] { server2.run(); }); - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452)); + std::string sendmsg = + "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=b\r\n"; + asio::io_service is; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + + c.send(asio::buffer(sendmsg)); - for (auto ch : sendmsg) { - char buf[1] = {ch}; - c.send(asio::buffer(buf)); + size_t recved = c.receive(asio::buffer(buf, 2048)); + ASSERT_EQUAL('A', buf[recved - 1]); } - size_t recved = c.receive(asio::buffer(buf, 2048)); - ASSERT_EQUAL('b', buf[recved - 1]); - } + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452)); + + for (auto ch : sendmsg) + { + char buf[1] = {ch}; + c.send(asio::buffer(buf)); + } + + size_t recved = c.receive(asio::buffer(buf, 2048)); + ASSERT_EQUAL('b', buf[recved - 1]); + } - server1.stop(); - server2.stop(); + server1.stop(); + server2.stop(); } -TEST(Crow, black_magic) { - using namespace black_magic; - static_assert( - std::is_same<void, LastElementType<int, char, void>::type>::value, - "LastElementType"); - static_assert( - std::is_same< - char, PopBack<int, char, void>::rebind<LastElementType>::type>::value, - "pop_back"); - static_assert( - std::is_same<int, PopBack<int, char, void>::rebind<PopBack>::rebind< - LastElementType>::type>::value, - "pop_back"); +TEST(Crow, black_magic) +{ + using namespace black_magic; + static_assert( + std::is_same<void, LastElementType<int, char, void>::type>::value, + "LastElementType"); + static_assert( + std::is_same<char, PopBack<int, char, + void>::rebind<LastElementType>::type>::value, + "pop_back"); + static_assert( + std::is_same<int, PopBack<int, char, void>::rebind<PopBack>::rebind< + LastElementType>::type>::value, + "pop_back"); } -struct NullMiddleware { - struct Context {}; +struct NullMiddleware +{ + struct Context + { + }; - template <typename AllContext> - void beforeHandle(Request&, Response&, Context&, AllContext&) {} + template <typename AllContext> + void beforeHandle(Request&, Response&, Context&, AllContext&) + { + } - template <typename AllContext> - void afterHandle(Request&, Response&, Context&, AllContext&) {} + template <typename AllContext> + void afterHandle(Request&, Response&, Context&, AllContext&) + { + } }; -struct NullSimpleMiddleware { - struct Context {}; +struct NullSimpleMiddleware +{ + struct Context + { + }; - void beforeHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/) {} + void beforeHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/) + { + } - void afterHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/) {} + void afterHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/) + { + } }; -TEST(Crow, middleware_simple) { - App<NullMiddleware, NullSimpleMiddleware> app; - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - BMCWEB_ROUTE(app, "/") - ([&](const crow::Request& req) { - app.getContext<NullMiddleware>(req); - app.getContext<NullSimpleMiddleware>(req); - return ""; - }); +TEST(Crow, middleware_simple) +{ + App<NullMiddleware, NullSimpleMiddleware> app; + decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + BMCWEB_ROUTE(app, "/") + ([&](const crow::Request& req) { + app.getContext<NullMiddleware>(req); + app.getContext<NullSimpleMiddleware>(req); + return ""; + }); } -struct IntSettingMiddleware { - struct Context { - int val; - }; +struct IntSettingMiddleware +{ + struct Context + { + int val; + }; - template <typename AllContext> - void beforeHandle(Request&, Response&, Context& ctx, AllContext&) { - ctx.val = 1; - } + template <typename AllContext> + void beforeHandle(Request&, Response&, Context& ctx, AllContext&) + { + ctx.val = 1; + } - template <typename AllContext> - void afterHandle(Request&, Response&, Context& ctx, AllContext&) { - ctx.val = 2; - } + template <typename AllContext> + void afterHandle(Request&, Response&, Context& ctx, AllContext&) + { + ctx.val = 2; + } }; std::vector<std::string> test_middleware_context_vector; -struct FirstMW { - struct Context { - std::vector<string> v; - }; +struct FirstMW +{ + struct Context + { + std::vector<string> v; + }; - void beforeHandle(Request& /*req*/, Response& /*res*/, Context& ctx) { - ctx.v.push_back("1 before"); - } + void beforeHandle(Request& /*req*/, Response& /*res*/, Context& ctx) + { + ctx.v.push_back("1 before"); + } - void afterHandle(Request& /*req*/, Response& /*res*/, Context& ctx) { - ctx.v.push_back("1 after"); - test_middleware_context_vector = ctx.v; - } + void afterHandle(Request& /*req*/, Response& /*res*/, Context& ctx) + { + ctx.v.push_back("1 after"); + test_middleware_context_vector = ctx.v; + } }; -struct SecondMW { - struct Context {}; - template <typename AllContext> - void beforeHandle(Request& req, Response& res, Context&, - AllContext& all_ctx) { - all_ctx.template get<FirstMW>().v.push_back("2 before"); - if (req.url == "/break") res.end(); - } - - template <typename AllContext> - void afterHandle(Request&, Response&, Context&, AllContext& all_ctx) { - all_ctx.template get<FirstMW>().v.push_back("2 after"); - } +struct SecondMW +{ + struct Context + { + }; + template <typename AllContext> + void beforeHandle(Request& req, Response& res, Context&, + AllContext& all_ctx) + { + all_ctx.template get<FirstMW>().v.push_back("2 before"); + if (req.url == "/break") + res.end(); + } + + template <typename AllContext> + void afterHandle(Request&, Response&, Context&, AllContext& all_ctx) + { + all_ctx.template get<FirstMW>().v.push_back("2 after"); + } }; -struct ThirdMW { - struct Context {}; - template <typename AllContext> - void beforeHandle(Request&, Response&, Context&, AllContext& all_ctx) { - all_ctx.template get<FirstMW>().v.push_back("3 before"); - } - - template <typename AllContext> - void afterHandle(Request&, Response&, Context&, AllContext& all_ctx) { - all_ctx.template get<FirstMW>().v.push_back("3 after"); - } +struct ThirdMW +{ + struct Context + { + }; + template <typename AllContext> + void beforeHandle(Request&, Response&, Context&, AllContext& all_ctx) + { + all_ctx.template get<FirstMW>().v.push_back("3 before"); + } + + template <typename AllContext> + void afterHandle(Request&, Response&, Context&, AllContext& all_ctx) + { + all_ctx.template get<FirstMW>().v.push_back("3 after"); + } }; -TEST(Crow, middlewareContext) { - static char buf[2048]; - // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>) - // so it leads to compile error if we remove FirstMW from definition - // App<IntSettingMiddleware, SecondMW> app; - // or change the order of FirstMW and SecondMW - // App<IntSettingMiddleware, SecondMW, FirstMW> app; +TEST(Crow, middlewareContext) +{ + static char buf[2048]; + // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>) + // so it leads to compile error if we remove FirstMW from definition + // App<IntSettingMiddleware, SecondMW> app; + // or change the order of FirstMW and SecondMW + // App<IntSettingMiddleware, SecondMW, FirstMW> app; + + App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app; + + int x{}; + BMCWEB_ROUTE(app, "/") + ([&](const Request& req) { + { + auto& ctx = app.getContext<IntSettingMiddleware>(req); + x = ctx.val; + } + { + auto& ctx = app.getContext<FirstMW>(req); + ctx.v.push_back("handle"); + } + + return ""; + }); + BMCWEB_ROUTE(app, "/break") + ([&](const Request& req) { + { + auto& ctx = app.getContext<FirstMW>(req); + ctx.v.push_back("handle"); + } - App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app; + return ""; + }); - int x{}; - BMCWEB_ROUTE(app, "/") - ([&](const Request& req) { + decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + auto _ = async(launch::async, [&] { server.run(); }); + std::string sendmsg = "GET /\r\n\r\n"; + asio::io_service is; { - auto& ctx = app.getContext<IntSettingMiddleware>(req); - x = ctx.val; + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + + c.send(asio::buffer(sendmsg)); + + c.receive(asio::buffer(buf, 2048)); + c.close(); } { - auto& ctx = app.getContext<FirstMW>(req); - ctx.v.push_back("handle"); + auto& out = test_middleware_context_vector; + ASSERT_EQUAL(1, x); + ASSERT_EQUAL(7, out.size()); + ASSERT_EQUAL("1 before", out[0]); + ASSERT_EQUAL("2 before", out[1]); + ASSERT_EQUAL("3 before", out[2]); + ASSERT_EQUAL("handle", out[3]); + ASSERT_EQUAL("3 after", out[4]); + ASSERT_EQUAL("2 after", out[5]); + ASSERT_EQUAL("1 after", out[6]); } + std::string sendmsg2 = "GET /break\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + + c.send(asio::buffer(sendmsg2)); - return ""; - }); - BMCWEB_ROUTE(app, "/break") - ([&](const Request& req) { + c.receive(asio::buffer(buf, 2048)); + c.close(); + } { - auto& ctx = app.getContext<FirstMW>(req); - ctx.v.push_back("handle"); + auto& out = test_middleware_context_vector; + ASSERT_EQUAL(4, out.size()); + ASSERT_EQUAL("1 before", out[0]); + ASSERT_EQUAL("2 before", out[1]); + ASSERT_EQUAL("2 after", out[2]); + ASSERT_EQUAL("1 after", out[3]); } + server.stop(); +} - return ""; - }); +TEST(Crow, bug_quick_repeated_request) +{ + static char buf[2048]; - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&] { server.run(); }); - std::string sendmsg = "GET /\r\n\r\n"; - asio::io_service is; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + SimpleApp app; - c.send(asio::buffer(sendmsg)); + BMCWEB_ROUTE(app, "/")([&] { return "hello"; }); - c.receive(asio::buffer(buf, 2048)); - c.close(); - } - { - auto& out = test_middleware_context_vector; - ASSERT_EQUAL(1, x); - ASSERT_EQUAL(7, out.size()); - ASSERT_EQUAL("1 before", out[0]); - ASSERT_EQUAL("2 before", out[1]); - ASSERT_EQUAL("3 before", out[2]); - ASSERT_EQUAL("handle", out[3]); - ASSERT_EQUAL("3 after", out[4]); - ASSERT_EQUAL("2 after", out[5]); - ASSERT_EQUAL("1 after", out[6]); - } - std::string sendmsg2 = "GET /break\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - - c.send(asio::buffer(sendmsg2)); - - c.receive(asio::buffer(buf, 2048)); - c.close(); - } - { - auto& out = test_middleware_context_vector; - ASSERT_EQUAL(4, out.size()); - ASSERT_EQUAL("1 before", out[0]); - ASSERT_EQUAL("2 before", out[1]); - ASSERT_EQUAL("2 after", out[2]); - ASSERT_EQUAL("1 after", out[3]); - } - server.stop(); + decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + auto _ = async(launch::async, [&] { server.run(); }); + std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; + asio::io_service is; + { + std::vector<std::future<void>> v; + for (int i = 0; i < 5; i++) + { + v.push_back(async(launch::async, [&] { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + + for (int j = 0; j < 5; j++) + { + c.send(asio::buffer(sendmsg)); + + size_t received = c.receive(asio::buffer(buf, 2048)); + ASSERT_EQUAL("hello", std::string(buf + received - 5, + buf + received)); + } + c.close(); + })); + } + } + server.stop(); } -TEST(Crow, bug_quick_repeated_request) { - static char buf[2048]; +TEST(Crow, simple_url_params) +{ + static char buf[2048]; + + SimpleApp app; + + QueryString lastUrlParams; + + BMCWEB_ROUTE(app, "/params") + ([&lastUrlParams](const crow::Request& req) { + lastUrlParams = std::move(req.urlParams); + return "OK"; + }); - SimpleApp app; + /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2 - BMCWEB_ROUTE(app, "/")([&] { return "hello"; }); + decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + auto _ = async(launch::async, [&] { server.run(); }); + asio::io_service is; + std::string sendmsg; - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&] { server.run(); }); - std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; - asio::io_service is; - { - std::vector<std::future<void>> v; - for (int i = 0; i < 5; i++) { - v.push_back(async(launch::async, [&] { + // check empty params + sendmsg = "GET /params\r\n\r\n"; + { asio::ip::tcp::socket c(is); c.connect(asio::ip::tcp::endpoint( asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); - for (int j = 0; j < 5; j++) { - c.send(asio::buffer(sendmsg)); + stringstream ss; + ss << lastUrlParams; - size_t received = c.receive(asio::buffer(buf, 2048)); - ASSERT_EQUAL("hello", - std::string(buf + received - 5, buf + received)); - } + ASSERT_EQUAL("[ ]", ss.str()); + } + // check single presence + sendmsg = "GET /params?foobar\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + + ASSERT_TRUE(lastUrlParams.get("missing") == nullptr); + ASSERT_TRUE(lastUrlParams.get("foobar") != nullptr); + ASSERT_TRUE(lastUrlParams.getList("missing").empty()); + } + // check multiple presence + sendmsg = "GET /params?foo&bar&baz\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); c.close(); - })); + + ASSERT_TRUE(lastUrlParams.get("missing") == nullptr); + ASSERT_TRUE(lastUrlParams.get("foo") != nullptr); + ASSERT_TRUE(lastUrlParams.get("bar") != nullptr); + ASSERT_TRUE(lastUrlParams.get("baz") != nullptr); } - } - server.stop(); -} + // check single value + sendmsg = "GET /params?hello=world\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); -TEST(Crow, simple_url_params) { - static char buf[2048]; - - SimpleApp app; - - QueryString lastUrlParams; - - BMCWEB_ROUTE(app, "/params") - ([&lastUrlParams](const crow::Request& req) { - lastUrlParams = std::move(req.urlParams); - return "OK"; - }); - - /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2 - - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&] { server.run(); }); - asio::io_service is; - std::string sendmsg; - - // check empty params - sendmsg = "GET /params\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - stringstream ss; - ss << lastUrlParams; - - ASSERT_EQUAL("[ ]", ss.str()); - } - // check single presence - sendmsg = "GET /params?foobar\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_TRUE(lastUrlParams.get("missing") == nullptr); - ASSERT_TRUE(lastUrlParams.get("foobar") != nullptr); - ASSERT_TRUE(lastUrlParams.getList("missing").empty()); - } - // check multiple presence - sendmsg = "GET /params?foo&bar&baz\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_TRUE(lastUrlParams.get("missing") == nullptr); - ASSERT_TRUE(lastUrlParams.get("foo") != nullptr); - ASSERT_TRUE(lastUrlParams.get("bar") != nullptr); - ASSERT_TRUE(lastUrlParams.get("baz") != nullptr); - } - // check single value - sendmsg = "GET /params?hello=world\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world"); - } - // check multiple value - sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world"); - ASSERT_EQUAL(string(lastUrlParams.get("left")), "right"); - ASSERT_EQUAL(string(lastUrlParams.get("up")), "down"); - } - // check multiple value, multiple types - sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_EQUAL(boost::lexical_cast<int>(lastUrlParams.get("int")), 100); - ASSERT_EQUAL(boost::lexical_cast<double>(lastUrlParams.get("double")), - 123.45); - ASSERT_EQUAL(boost::lexical_cast<bool>(lastUrlParams.get("boolean")), true); - } - // check single array value - sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_TRUE(lastUrlParams.get("tmnt") == nullptr); - ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 1); - ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo"); - } - // check multiple array value - sendmsg = - "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - - ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 3); - ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo"); - ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[1]), "donatello"); - ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[2]), "raphael"); - } - server.stop(); + ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world"); + } + // check multiple value + sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + + ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world"); + ASSERT_EQUAL(string(lastUrlParams.get("left")), "right"); + ASSERT_EQUAL(string(lastUrlParams.get("up")), "down"); + } + // check multiple value, multiple types + sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + + ASSERT_EQUAL(boost::lexical_cast<int>(lastUrlParams.get("int")), 100); + ASSERT_EQUAL(boost::lexical_cast<double>(lastUrlParams.get("double")), + 123.45); + ASSERT_EQUAL(boost::lexical_cast<bool>(lastUrlParams.get("boolean")), + true); + } + // check single array value + sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + + ASSERT_TRUE(lastUrlParams.get("tmnt") == nullptr); + ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 1); + ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo"); + } + // check multiple array value + sendmsg = + "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + + ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 3); + ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo"); + ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[1]), "donatello"); + ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[2]), "raphael"); + } + server.stop(); } -TEST(Crow, routeDynamic) { - SimpleApp app; - int x = 1; - app.routeDynamic("/")([&] { - x = 2; - return ""; - }); - - app.routeDynamic("/set4")([&](const Request&) { - x = 4; - return ""; - }); - app.routeDynamic("/set5")([&](const Request&, Response& res) { - x = 5; - res.end(); - }); - - app.routeDynamic("/set_int/<int>")([&](int y) { - x = y; - return ""; - }); - - try { - app.routeDynamic("/invalid_test/<double>/<path>")([]() { return ""; }); - fail(); - } catch (std::exception&) { - } - - // app is in an invalid state when routeDynamic throws an exception. - try { - app.validate(); - fail(); - } catch (std::exception&) { - } +TEST(Crow, routeDynamic) +{ + SimpleApp app; + int x = 1; + app.routeDynamic("/")([&] { + x = 2; + return ""; + }); + + app.routeDynamic("/set4")([&](const Request&) { + x = 4; + return ""; + }); + app.routeDynamic("/set5")([&](const Request&, Response& res) { + x = 5; + res.end(); + }); + + app.routeDynamic("/set_int/<int>")([&](int y) { + x = y; + return ""; + }); + + try + { + app.routeDynamic("/invalid_test/<double>/<path>")([]() { return ""; }); + fail(); + } + catch (std::exception&) + { + } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; - req.url = "/"; - app.handle(req, res); - ASSERT_EQUAL(x, 2); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; - req.url = "/set_int/42"; - app.handle(req, res); - ASSERT_EQUAL(x, 42); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; - req.url = "/set5"; - app.handle(req, res); - ASSERT_EQUAL(x, 5); - } - { - boost::beast::http::request<boost::beast::http::string_body> r{}; - Request req{r}; - Response res; - req.url = "/set4"; - app.handle(req, res); - ASSERT_EQUAL(x, 4); - } + // app is in an invalid state when routeDynamic throws an exception. + try + { + app.validate(); + fail(); + } + catch (std::exception&) + { + } + + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; + req.url = "/"; + app.handle(req, res); + ASSERT_EQUAL(x, 2); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; + req.url = "/set_int/42"; + app.handle(req, res); + ASSERT_EQUAL(x, 42); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; + req.url = "/set5"; + app.handle(req, res); + ASSERT_EQUAL(x, 5); + } + { + boost::beast::http::request<boost::beast::http::string_body> r{}; + Request req{r}; + Response res; + req.url = "/set4"; + app.handle(req, res); + ASSERT_EQUAL(x, 4); + } } diff --git a/src/getvideo_main.cpp b/src/getvideo_main.cpp index 7055d35dfc..8d5f7afd3b 100644 --- a/src/getvideo_main.cpp +++ b/src/getvideo_main.cpp @@ -1,15 +1,15 @@ #include <fcntl.h> #include <unistd.h> + #include <chrono> +#include <cstdio> +#include <cstdlib> #include <fstream> #include <iomanip> #include <iostream> #include <thread> #include <vector> -#include <cstdio> -#include <cstdlib> - //#define BUILD_CIMG #ifdef BUILD_CIMG #define cimg_display 0 @@ -19,52 +19,60 @@ #include <ast_jpeg_decoder.hpp> #include <ast_video_puller.hpp> -int main() { - ast_video::RawVideoBuffer out; - bool have_hardware = false; - if (access("/dev/video", F_OK) != -1) { - ast_video::SimpleVideoPuller p; - p.initialize(); - out = p.readVideo(); - } else { - FILE *fp = fopen("/home/ed/screendata.bin", "rb"); - if (fp != nullptr) { - size_t newLen = fread(out.buffer.data(), sizeof(char), - out.buffer.size() * sizeof(long), fp); - if (ferror(fp) != 0) { - fputs("Error reading file", stderr); - } - fclose(fp); - out.buffer.resize(newLen); - out.mode = ast_video::YuvMode::YUV444; - out.width = 800; - out.height = 600; - out.ySelector = 0; - out.uvSelector = 0; +int main() +{ + ast_video::RawVideoBuffer out; + bool have_hardware = false; + if (access("/dev/video", F_OK) != -1) + { + ast_video::SimpleVideoPuller p; + p.initialize(); + out = p.readVideo(); + } + else + { + FILE *fp = fopen("/home/ed/screendata.bin", "rb"); + if (fp != nullptr) + { + size_t newLen = fread(out.buffer.data(), sizeof(char), + out.buffer.size() * sizeof(long), fp); + if (ferror(fp) != 0) + { + fputs("Error reading file", stderr); + } + fclose(fp); + out.buffer.resize(newLen); + out.mode = ast_video::YuvMode::YUV444; + out.width = 800; + out.height = 600; + out.ySelector = 0; + out.uvSelector = 0; + } } - } - FILE *fp = fopen("/tmp/screendata.bin", "wb"); - fwrite(out.buffer.data(), sizeof(char), out.buffer.size(), fp); + FILE *fp = fopen("/tmp/screendata.bin", "wb"); + fwrite(out.buffer.data(), sizeof(char), out.buffer.size(), fp); - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, - out.uvSelector); + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, out.mode, out.ySelector, + out.uvSelector); #ifdef BUILD_CIMG - cimg_library::CImg<unsigned char> image(out.width, out.height, 1, - 3 /*numchannels*/); - for (int y = 0; y < out.height; y++) { - for (int x = 0; x < out.width; x++) { - auto pixel = d.outBuffer[x + (y * out.width)]; - image(x, y, 0) = pixel.r; - image(x, y, 1) = pixel.g; - image(x, y, 2) = pixel.b; + cimg_library::CImg<unsigned char> image(out.width, out.height, 1, + 3 /*numchannels*/); + for (int y = 0; y < out.height; y++) + { + for (int x = 0; x < out.width; x++) + { + auto pixel = d.outBuffer[x + (y * out.width)]; + image(x, y, 0) = pixel.r; + image(x, y, 1) = pixel.g; + image(x, y, 2) = pixel.b; + } } - } - image.save("/tmp/file2.bmp"); + image.save("/tmp/file2.bmp"); #endif - std::cout << "Done!\n"; + std::cout << "Done!\n"; - return 1; + return 1; } diff --git a/src/gtest_main.cpp b/src/gtest_main.cpp index eec00675a3..77e70aac1d 100644 --- a/src/gtest_main.cpp +++ b/src/gtest_main.cpp @@ -1,7 +1,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); }
\ No newline at end of file diff --git a/src/kvm_websocket_test.cpp b/src/kvm_websocket_test.cpp index b5e1bdcffd..1ddaad9b35 100644 --- a/src/kvm_websocket_test.cpp +++ b/src/kvm_websocket_test.cpp @@ -1,9 +1,12 @@ +#include "gzip_helper.hpp" +#include "web_kvm.hpp" + #include <iostream> #include <sstream> #include <vector> -#include "gzip_helper.hpp" -#include "web_kvm.hpp" + #include "crow.h" + #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -11,93 +14,101 @@ using namespace crow; using namespace testing; // Tests static files are loaded correctly -TEST(Kvm, BasicRfb) { - return; // TODO(ed) Make hte code below work again - SimpleApp app; - - crow::kvm::requestRoutes(app); - app.bindaddr("127.0.0.1").port(45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - auto _ = async(std::launch::async, [&] { app.run(); }); - auto routes = app.getRoutes(); - asio::io_service is; - - { - // Retry a couple of times waiting for the server to come up - // TODO(ed) This is really unfortunate, and should use some form of mock - asio::ip::tcp::socket c(is); - for (int i = 0; i < 200; i++) { - try { - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - c.close(); - break; - } catch (std::exception e) { - // do nothing. We expect this to fail while the server is starting up - } +TEST(Kvm, BasicRfb) +{ + return; // TODO(ed) Make hte code below work again + SimpleApp app; + + crow::kvm::requestRoutes(app); + app.bindaddr("127.0.0.1").port(45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + auto _ = async(std::launch::async, [&] { app.run(); }); + auto routes = app.getRoutes(); + asio::io_service is; + + { + // Retry a couple of times waiting for the server to come up + // TODO(ed) This is really unfortunate, and should use some form of + // mock + asio::ip::tcp::socket c(is); + for (int i = 0; i < 200; i++) + { + try + { + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.close(); + break; + } + catch (std::exception e) + { + // do nothing. We expect this to fail while the server is + // starting up + } + } + } + + // Get the websocket + std::string sendmsg = ("GET /kvmws HTTP/1.1\r\n" + "Host: localhost:45451\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: aLeGkmLPZmdv5tTyEpJ3jQ==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; " + "client_max_window_bits\r\n" + "Sec-WebSocket-Protocol: binary\r\n" + "\r\n"); + + asio::ip::tcp::socket socket(is); + socket.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + socket.send(asio::buffer(sendmsg)); + + // Read the Response status line. The Response streambuf will automatically + // grow to accommodate the entire line. The growth may be limited by passing + // a maximum size to the streambuf constructor. + boost::asio::streambuf response; + boost::asio::read_until(socket, response, "\r\n"); + + // Check that Response is OK. + std::istream response_stream(&response); + std::string http_response; + std::getline(response_stream, http_response); + + EXPECT_EQ(http_response, "HTTP/1.1 101 Switching Protocols\r"); + + // Read the Response headers, which are terminated by a blank line. + boost::asio::read_until(socket, response, "\r\n\r\n"); + + // Process the Response headers. + std::string header; + std::vector<std::string> headers; + while (std::getline(response_stream, header) && header != "\r") + { + headers.push_back(header); } - } - - // Get the websocket - std::string sendmsg = - ("GET /kvmws HTTP/1.1\r\n" - "Host: localhost:45451\r\n" - "Connection: Upgrade\r\n" - "Upgrade: websocket\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Key: aLeGkmLPZmdv5tTyEpJ3jQ==\r\n" - "Sec-WebSocket-Extensions: permessage-deflate; " - "client_max_window_bits\r\n" - "Sec-WebSocket-Protocol: binary\r\n" - "\r\n"); - - asio::ip::tcp::socket socket(is); - socket.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - socket.send(asio::buffer(sendmsg)); - - // Read the Response status line. The Response streambuf will automatically - // grow to accommodate the entire line. The growth may be limited by passing - // a maximum size to the streambuf constructor. - boost::asio::streambuf response; - boost::asio::read_until(socket, response, "\r\n"); - - // Check that Response is OK. - std::istream response_stream(&response); - std::string http_response; - std::getline(response_stream, http_response); - - EXPECT_EQ(http_response, "HTTP/1.1 101 Switching Protocols\r"); - - // Read the Response headers, which are terminated by a blank line. - boost::asio::read_until(socket, response, "\r\n\r\n"); - - // Process the Response headers. - std::string header; - std::vector<std::string> headers; - while (std::getline(response_stream, header) && header != "\r") { - headers.push_back(header); - } - - EXPECT_THAT(headers, Contains("Upgrade: websocket\r")); - EXPECT_THAT(headers, Contains("Connection: Upgrade\r")); - EXPECT_THAT(headers, Contains("Sec-WebSocket-Protocol: binary\r")); - // TODO(ed) This is the result that it gives today. Need to check websocket - // docs and make - // sure that this calclution is actually being done to spec - EXPECT_THAT(headers, - Contains("Sec-WebSocket-Accept: /CnDM3l79rIxniLNyxMryXbtLEU=\r")); - std::array<char, 13> rfb_open_string; - - // - // socket.receive(rfb_open_string.data(), rfb_open_string.size()); - boost::asio::read(socket, boost::asio::buffer(rfb_open_string)); - auto open_string = - std::string(std::begin(rfb_open_string), std::end(rfb_open_string)); - // Todo(ed) find out what the two characters at the end of the websocket - // stream are - open_string = open_string.substr(2); - EXPECT_EQ(open_string, "RFB 003.008"); - - app.stop(); + + EXPECT_THAT(headers, Contains("Upgrade: websocket\r")); + EXPECT_THAT(headers, Contains("Connection: Upgrade\r")); + EXPECT_THAT(headers, Contains("Sec-WebSocket-Protocol: binary\r")); + // TODO(ed) This is the result that it gives today. Need to check websocket + // docs and make + // sure that this calclution is actually being done to spec + EXPECT_THAT( + headers, + Contains("Sec-WebSocket-Accept: /CnDM3l79rIxniLNyxMryXbtLEU=\r")); + std::array<char, 13> rfb_open_string; + + // + // socket.receive(rfb_open_string.data(), rfb_open_string.size()); + boost::asio::read(socket, boost::asio::buffer(rfb_open_string)); + auto open_string = + std::string(std::begin(rfb_open_string), std::end(rfb_open_string)); + // Todo(ed) find out what the two characters at the end of the websocket + // stream are + open_string = open_string.substr(2); + EXPECT_EQ(open_string, "RFB 003.008"); + + app.stop(); }
\ No newline at end of file diff --git a/src/msan_test.cpp b/src/msan_test.cpp index 9fcb9d530c..7e42e48659 100644 --- a/src/msan_test.cpp +++ b/src/msan_test.cpp @@ -1,7 +1,9 @@ #include <string> + #include "gtest/gtest.h" -TEST(MemorySanitizer, TestIsWorking) { - std::string foo("foo"); - EXPECT_STREQ("foo", foo.c_str()); +TEST(MemorySanitizer, TestIsWorking) +{ + std::string foo("foo"); + EXPECT_STREQ("foo", foo.c_str()); } diff --git a/src/openbmc_jtag_rest_test.cpp b/src/openbmc_jtag_rest_test.cpp index 7e40d6ddee..42504d3895 100644 --- a/src/openbmc_jtag_rest_test.cpp +++ b/src/openbmc_jtag_rest_test.cpp @@ -1,51 +1,53 @@ #include "openbmc_dbus_rest.hpp" + #include "gmock/gmock.h" #include "gtest/gtest.h" -TEST(OpenBmcDbusTest, TestArgSplit) { - // test the basic types - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("x"), - ::testing::ElementsAre("x")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("y"), - ::testing::ElementsAre("y")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("b"), - ::testing::ElementsAre("b")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("n"), - ::testing::ElementsAre("n")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("q"), - ::testing::ElementsAre("q")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("i"), - ::testing::ElementsAre("i")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("u"), - ::testing::ElementsAre("u")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("x"), - ::testing::ElementsAre("x")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("t"), - ::testing::ElementsAre("t")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("d"), - ::testing::ElementsAre("d")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("h"), - ::testing::ElementsAre("h")); - // test arrays - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("ai"), - ::testing::ElementsAre("ai")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("ax"), - ::testing::ElementsAre("ax")); - // test tuples - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("(sss)"), - ::testing::ElementsAre("(sss)")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("(sss)b"), - ::testing::ElementsAre("(sss)", "b")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("b(sss)"), - ::testing::ElementsAre("b", "(sss)")); +TEST(OpenBmcDbusTest, TestArgSplit) +{ + // test the basic types + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("x"), + ::testing::ElementsAre("x")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("y"), + ::testing::ElementsAre("y")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("b"), + ::testing::ElementsAre("b")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("n"), + ::testing::ElementsAre("n")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("q"), + ::testing::ElementsAre("q")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("i"), + ::testing::ElementsAre("i")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("u"), + ::testing::ElementsAre("u")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("x"), + ::testing::ElementsAre("x")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("t"), + ::testing::ElementsAre("t")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("d"), + ::testing::ElementsAre("d")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("h"), + ::testing::ElementsAre("h")); + // test arrays + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("ai"), + ::testing::ElementsAre("ai")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("ax"), + ::testing::ElementsAre("ax")); + // test tuples + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("(sss)"), + ::testing::ElementsAre("(sss)")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("(sss)b"), + ::testing::ElementsAre("(sss)", "b")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("b(sss)"), + ::testing::ElementsAre("b", "(sss)")); - // Test nested types - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("a{si}b"), - ::testing::ElementsAre("a{si}", "b")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("a(sss)b"), - ::testing::ElementsAre("a(sss)", "b")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("aa{si}b"), - ::testing::ElementsAre("aa{si}", "b")); - EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("i{si}b"), - ::testing::ElementsAre("b", "aa{si}")); + // Test nested types + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("a{si}b"), + ::testing::ElementsAre("a{si}", "b")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("a(sss)b"), + ::testing::ElementsAre("a(sss)", "b")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("aa{si}b"), + ::testing::ElementsAre("aa{si}", "b")); + EXPECT_THAT(crow::openbmc_mapper::dbus_arg_split("i{si}b"), + ::testing::ElementsAre("b", "aa{si}")); } diff --git a/src/security_headers_middleware_test.cpp b/src/security_headers_middleware_test.cpp index e7008cb165..2af15c6d07 100644 --- a/src/security_headers_middleware_test.cpp +++ b/src/security_headers_middleware_test.cpp @@ -1,5 +1,7 @@ -#include <security_headers_middleware.hpp> #include <crow/app.h> + +#include <security_headers_middleware.hpp> + #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -7,68 +9,78 @@ using namespace crow; using namespace std; // Tests that the security headers are added correctly -TEST(SecurityHeaders, TestHeadersExist) { - App<SecurityHeadersMiddleware> app; - app.bindaddr("127.0.0.1").port(45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - auto _ = async(launch::async, [&] { app.run(); }); +TEST(SecurityHeaders, TestHeadersExist) +{ + App<SecurityHeadersMiddleware> app; + app.bindaddr("127.0.0.1").port(45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + auto _ = async(launch::async, [&] { app.run(); }); - asio::io_service is; - std::array<char, 2048> buf; - std::string sendmsg; + asio::io_service is; + std::array<char, 2048> buf; + std::string sendmsg; - { - // Retry a couple of times waiting for the server to come up - // TODO(ed) This is really unfortunate, and should use some form of mock - asio::ip::tcp::socket c(is); - for (int i = 0; i < 200; i++) { - try { - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - c.close(); - break; - } catch (std::exception e) { - // do nothing. We expect this to fail while the server is starting up - } + { + // Retry a couple of times waiting for the server to come up + // TODO(ed) This is really unfortunate, and should use some form of + // mock + asio::ip::tcp::socket c(is); + for (int i = 0; i < 200; i++) + { + try + { + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.close(); + break; + } + catch (std::exception e) + { + // do nothing. We expect this to fail while the server is + // starting up + } + } } - } - // Test correct login credentials - sendmsg = "GET /\r\n\r\n"; + // Test correct login credentials + sendmsg = "GET /\r\n\r\n"; - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), - 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf)); - c.close(); - auto return_code = std::string(&buf[9], &buf[12]); - EXPECT_EQ("200", return_code); - std::string response(std::begin(buf), std::end(buf)); + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf)); + c.close(); + auto return_code = std::string(&buf[9], &buf[12]); + EXPECT_EQ("200", return_code); + std::string response(std::begin(buf), std::end(buf)); - // This is a routine to split strings until a blank is hit - // TODO(ed) this should really use the HTTP parser - std::vector<std::string> headers; - std::string::size_type pos = 0; - std::string::size_type prev = 0; - while ((pos = response.find("\r\n", prev)) != std::string::npos) { - auto this_string = response.substr(prev, pos - prev); - if (this_string == "") { - break; + // This is a routine to split strings until a blank is hit + // TODO(ed) this should really use the HTTP parser + std::vector<std::string> headers; + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = response.find("\r\n", prev)) != std::string::npos) + { + auto this_string = response.substr(prev, pos - prev); + if (this_string == "") + { + break; + } + headers.push_back(this_string); + prev = pos + 2; } - headers.push_back(this_string); - prev = pos + 2; - } - headers.push_back(response.substr(prev)); + headers.push_back(response.substr(prev)); - EXPECT_EQ(headers[0], "HTTP/1.1 200 OK"); - EXPECT_THAT(headers, ::testing::Contains("Strict-Transport-Security: " - "max-age=31536000; " - "includeSubdomains; preload")); - EXPECT_THAT(headers, ::testing::Contains("X-UA-Compatible: IE=11")); - EXPECT_THAT(headers, ::testing::Contains("X-Frame-Options: DENY")); - EXPECT_THAT(headers, ::testing::Contains("X-XSS-Protection: 1; mode=block")); - EXPECT_THAT(headers, ::testing::Contains( - "X-Content-Security-Policy: default-src 'self'")); - app.stop(); + EXPECT_EQ(headers[0], "HTTP/1.1 200 OK"); + EXPECT_THAT(headers, ::testing::Contains("Strict-Transport-Security: " + "max-age=31536000; " + "includeSubdomains; preload")); + EXPECT_THAT(headers, ::testing::Contains("X-UA-Compatible: IE=11")); + EXPECT_THAT(headers, ::testing::Contains("X-Frame-Options: DENY")); + EXPECT_THAT(headers, + ::testing::Contains("X-XSS-Protection: 1; mode=block")); + EXPECT_THAT(headers, ::testing::Contains( + "X-Content-Security-Policy: default-src 'self'")); + app.stop(); } diff --git a/src/token_authorization_middleware_test.cpp b/src/token_authorization_middleware_test.cpp index ab735b3a52..812a6d193e 100644 --- a/src/token_authorization_middleware_test.cpp +++ b/src/token_authorization_middleware_test.cpp @@ -1,287 +1,322 @@ #include "token_authorization_middleware.hpp" +#include "webserver_common.hpp" + #include <condition_variable> #include <future> #include <mutex> -#include "webserver_common.hpp" + #include "gmock/gmock.h" #include "gtest/gtest.h" using namespace crow; -class TokenAuth : public ::testing::Test { - public: - TokenAuth() - : lk(std::unique_lock<std::mutex>(m)), - io(std::make_shared<boost::asio::io_service>()) {} - - std::mutex m; - std::condition_variable cv; - std::unique_lock<std::mutex> lk; - std::shared_ptr<boost::asio::io_service> io; - int testPort = 45451; +class TokenAuth : public ::testing::Test +{ + public: + TokenAuth() : + lk(std::unique_lock<std::mutex>(m)), + io(std::make_shared<boost::asio::io_service>()) + { + } + + std::mutex m; + std::condition_variable cv; + std::unique_lock<std::mutex> lk; + std::shared_ptr<boost::asio::io_service> io; + int testPort = 45451; }; -TEST_F(TokenAuth, SpecialResourcesAreAcceptedWithoutAuth) { - CrowApp app(io); - crow::token_authorization::requestRoutes(app); - BMCWEB_ROUTE(app, "/redfish/v1") - ([]() { return boost::beast::http::status::ok; }); - auto _ = std::async(std::launch::async, [&] { - app.port(testPort).run(); - cv.notify_one(); - io->run(); - }); - - asio::io_service is; - std::string sendmsg; - - static char buf[2048]; - - // Homepage should be passed with no credentials - sendmsg = "GET /\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - EXPECT_EQ("200", std::string(buf + 9, buf + 12)); - } +TEST_F(TokenAuth, SpecialResourcesAreAcceptedWithoutAuth) +{ + CrowApp app(io); + crow::token_authorization::requestRoutes(app); + BMCWEB_ROUTE(app, "/redfish/v1") + ([]() { return boost::beast::http::status::ok; }); + auto _ = std::async(std::launch::async, [&] { + app.port(testPort).run(); + cv.notify_one(); + io->run(); + }); + + asio::io_service is; + std::string sendmsg; + + static char buf[2048]; + + // Homepage should be passed with no credentials + sendmsg = "GET /\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + EXPECT_EQ("200", std::string(buf + 9, buf + 12)); + } - // static should be passed with no credentials - sendmsg = "GET /static/index.html\r\n\r\n"; - { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - EXPECT_EQ("404", std::string(buf + 9, buf + 12)); - } + // static should be passed with no credentials + sendmsg = "GET /static/index.html\r\n\r\n"; + { + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + EXPECT_EQ("404", std::string(buf + 9, buf + 12)); + } - app.stop(); + app.stop(); } // Tests that Base64 basic strings work -TEST(TokenAuthentication, TestRejectedResource) { - App<crow::persistent_data::Middleware, crow::token_authorization::Middleware> - app; - app.bindaddr("127.0.0.1").port(45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - auto _ = async(std::launch::async, [&] { app.run(); }); - - asio::io_service is; - static char buf[2048]; - - // Other resources should not be passed - std::string sendmsg = "GET /foo\r\n\r\n"; - asio::ip::tcp::socket c(is); - for (int i = 0; i < 200; i++) { - try { - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - } catch (std::exception e) { - // do nothing +TEST(TokenAuthentication, TestRejectedResource) +{ + App<crow::persistent_data::Middleware, + crow::token_authorization::Middleware> + app; + app.bindaddr("127.0.0.1").port(45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + auto _ = async(std::launch::async, [&] { app.run(); }); + + asio::io_service is; + static char buf[2048]; + + // Other resources should not be passed + std::string sendmsg = "GET /foo\r\n\r\n"; + asio::ip::tcp::socket c(is); + for (int i = 0; i < 200; i++) + { + try + { + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + } + catch (std::exception e) + { + // do nothing + } } - } - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - EXPECT_EQ("401", std::string(buf + 9, buf + 12)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + EXPECT_EQ("401", std::string(buf + 9, buf + 12)); - app.stop(); + app.stop(); } // Tests that Base64 basic strings work -TEST(TokenAuthentication, TestGetLoginUrl) { - App<crow::persistent_data::Middleware, crow::token_authorization::Middleware> - app; - app.bindaddr("127.0.0.1").port(45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - auto _ = async(std::launch::async, [&] { app.run(); }); - - asio::io_service is; - static char buf[2048]; - - // Other resources should not be passed - std::string sendmsg = "GET /login\r\n\r\n"; - asio::ip::tcp::socket c(is); - for (int i = 0; i < 200; i++) { - try { - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - } catch (std::exception e) { - // do nothing +TEST(TokenAuthentication, TestGetLoginUrl) +{ + App<crow::persistent_data::Middleware, + crow::token_authorization::Middleware> + app; + app.bindaddr("127.0.0.1").port(45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + auto _ = async(std::launch::async, [&] { app.run(); }); + + asio::io_service is; + static char buf[2048]; + + // Other resources should not be passed + std::string sendmsg = "GET /login\r\n\r\n"; + asio::ip::tcp::socket c(is); + for (int i = 0; i < 200; i++) + { + try + { + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + } + catch (std::exception e) + { + // do nothing + } } - } - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf, 2048)); - c.close(); - EXPECT_EQ("401", std::string(buf + 9, buf + 12)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf, 2048)); + c.close(); + EXPECT_EQ("401", std::string(buf + 9, buf + 12)); - app.stop(); + app.stop(); } // Tests boundary conditions on login -TEST(TokenAuthentication, TestPostBadLoginUrl) { - App<crow::persistent_data::Middleware, crow::token_authorization::Middleware> - app; - app.bindaddr("127.0.0.1").port(45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - auto _ = async(std::launch::async, [&] { app.run(); }); - - asio::io_service is; - std::array<char, 2048> buf; - std::string sendmsg; - - auto send_to_localhost = [&is, &buf](std::string sendmsg) { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf)); - c.close(); - }; - - { - // Retry a couple of times waiting for the server to come up - asio::ip::tcp::socket c(is); - for (int i = 0; i < 200; i++) { - try { +TEST(TokenAuthentication, TestPostBadLoginUrl) +{ + App<crow::persistent_data::Middleware, + crow::token_authorization::Middleware> + app; + app.bindaddr("127.0.0.1").port(45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + auto _ = async(std::launch::async, [&] { app.run(); }); + + asio::io_service is; + std::array<char, 2048> buf; + std::string sendmsg; + + auto send_to_localhost = [&is, &buf](std::string sendmsg) { + asio::ip::tcp::socket c(is); c.connect(asio::ip::tcp::endpoint( asio::ip::address::from_string("127.0.0.1"), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf)); c.close(); - break; - } catch (std::exception e) { - // do nothing. We expect this to fail while the server is starting up - } + }; + + { + // Retry a couple of times waiting for the server to come up + asio::ip::tcp::socket c(is); + for (int i = 0; i < 200; i++) + { + try + { + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.close(); + break; + } + catch (std::exception e) + { + // do nothing. We expect this to fail while the server is + // starting up + } + } } - } - - // Test blank login credentials - sendmsg = "POST /login\r\nContent-Length:0\r\n\r\n\r\n"; - { - send_to_localhost(sendmsg); - auto return_code = std::string(&buf[9], &buf[12]); - EXPECT_EQ("400", return_code); - } - - // Test wrong login credentials - sendmsg = - "POST /login\r\nContent-Length:38\r\n\r\n{\"username\": \"foo\", " - "\"password\": \"bar\"}\r\n"; - { - send_to_localhost(sendmsg); - auto return_code = std::string(&buf[9], &buf[12]); - EXPECT_EQ("401", return_code); - // TODO(ed) need to test more here. Response string? - } - - // Test only sending a username - sendmsg = - "POST /login\r\nContent-Length:19\r\n\r\n{\"username\": \"foo\"}\r\n"; - { - send_to_localhost(sendmsg); - auto return_code = std::string(&buf[9], &buf[12]); - EXPECT_EQ("400", return_code); - } - - // Test only sending a password - sendmsg = - "POST /login\r\nContent-Length:19\r\n\r\n{\"password\": \"foo\"}\r\n"; - { - send_to_localhost(sendmsg); - auto return_code = std::string(&buf[9], &buf[12]); - EXPECT_EQ("400", return_code); - } - - app.stop(); -} -// Test class that allows login for a fixed password. -class KnownLoginAuthenticator { - public: - inline bool authenticate(const std::string& username, - const std::string& password) { - return (username == "dude") && (password == "foo"); - } -}; + // Test blank login credentials + sendmsg = "POST /login\r\nContent-Length:0\r\n\r\n\r\n"; + { + send_to_localhost(sendmsg); + auto return_code = std::string(&buf[9], &buf[12]); + EXPECT_EQ("400", return_code); + } -TEST(TokenAuthentication, TestSuccessfulLogin) { - App<crow::persistent_data::Middleware, crow::token_authorization::Middleware> - app; - app.bindaddr("127.0.0.1").port(45451); - BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); - auto _ = async(std::launch::async, [&] { app.run(); }); + // Test wrong login credentials + sendmsg = "POST /login\r\nContent-Length:38\r\n\r\n{\"username\": \"foo\", " + "\"password\": \"bar\"}\r\n"; + { + send_to_localhost(sendmsg); + auto return_code = std::string(&buf[9], &buf[12]); + EXPECT_EQ("401", return_code); + // TODO(ed) need to test more here. Response string? + } - asio::io_service is; - std::array<char, 2048> buf; - std::string sendmsg; + // Test only sending a username + sendmsg = + "POST /login\r\nContent-Length:19\r\n\r\n{\"username\": \"foo\"}\r\n"; + { + send_to_localhost(sendmsg); + auto return_code = std::string(&buf[9], &buf[12]); + EXPECT_EQ("400", return_code); + } - auto send_to_localhost = [&is, &buf](std::string sendmsg) { - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 45451)); - c.send(asio::buffer(sendmsg)); - c.receive(asio::buffer(buf)); - c.close(); - }; + // Test only sending a password + sendmsg = + "POST /login\r\nContent-Length:19\r\n\r\n{\"password\": \"foo\"}\r\n"; + { + send_to_localhost(sendmsg); + auto return_code = std::string(&buf[9], &buf[12]); + EXPECT_EQ("400", return_code); + } - { - // Retry a couple of times waiting for the server to come up - asio::ip::tcp::socket c(is); - for (int i = 0; i < 200; i++) { - try { + app.stop(); +} + +// Test class that allows login for a fixed password. +class KnownLoginAuthenticator +{ + public: + inline bool authenticate(const std::string& username, + const std::string& password) + { + return (username == "dude") && (password == "foo"); + } +}; + +TEST(TokenAuthentication, TestSuccessfulLogin) +{ + App<crow::persistent_data::Middleware, + crow::token_authorization::Middleware> + app; + app.bindaddr("127.0.0.1").port(45451); + BMCWEB_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; }); + auto _ = async(std::launch::async, [&] { app.run(); }); + + asio::io_service is; + std::array<char, 2048> buf; + std::string sendmsg; + + auto send_to_localhost = [&is, &buf](std::string sendmsg) { + asio::ip::tcp::socket c(is); c.connect(asio::ip::tcp::endpoint( asio::ip::address::from_string("127.0.0.1"), 45451)); + c.send(asio::buffer(sendmsg)); + c.receive(asio::buffer(buf)); c.close(); - break; - } catch (std::exception e) { - // do nothing. We expect this to fail while the server is starting up - } + }; + + { + // Retry a couple of times waiting for the server to come up + asio::ip::tcp::socket c(is); + for (int i = 0; i < 200; i++) + { + try + { + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + c.close(); + break; + } + catch (std::exception e) + { + // do nothing. We expect this to fail while the server is + // starting up + } + } } - } - - // Test correct login credentials - sendmsg = - "POST /login\r\nContent-Length:40\r\n\r\n{\"username\": \"dude\", " - "\"password\": \"foo\"}\r\n"; - { - send_to_localhost(sendmsg); - std::string response(std::begin(buf), std::end(buf)); - // This is a routine to split strings until a newline is hit - // TODO(ed) this should really use the HTTP parser - std::vector<std::string> headers; - std::string::size_type pos = 0; - std::string::size_type prev = 0; - int content_length = 0; - std::string content_encoding(""); - while ((pos = response.find("\r\n", prev)) != std::string::npos) { - auto this_string = response.substr(prev, pos - prev); - if (this_string == "") { - prev = pos + 2; - break; - } - - headers.push_back(this_string); - prev = pos + 2; + + // Test correct login credentials + sendmsg = + "POST /login\r\nContent-Length:40\r\n\r\n{\"username\": \"dude\", " + "\"password\": \"foo\"}\r\n"; + { + send_to_localhost(sendmsg); + std::string response(std::begin(buf), std::end(buf)); + // This is a routine to split strings until a newline is hit + // TODO(ed) this should really use the HTTP parser + std::vector<std::string> headers; + std::string::size_type pos = 0; + std::string::size_type prev = 0; + int content_length = 0; + std::string content_encoding(""); + while ((pos = response.find("\r\n", prev)) != std::string::npos) + { + auto this_string = response.substr(prev, pos - prev); + if (this_string == "") + { + prev = pos + 2; + break; + } + + headers.push_back(this_string); + prev = pos + 2; + } + EXPECT_EQ(headers[0], "HTTP/1.1 200 OK"); + EXPECT_THAT(headers, + testing::Contains("Content-Type: application/json")); + auto http_content = response.substr(prev); } - EXPECT_EQ(headers[0], "HTTP/1.1 200 OK"); - EXPECT_THAT(headers, testing::Contains("Content-Type: application/json")); - auto http_content = response.substr(prev); - } - - // Try to use those login credentials to access a resource - sendmsg = - "GET /\r\nAuthorization: token\r\n\r\n{\"username\": \"dude\", " - "\"password\": \"dude\"}\r\n"; - { - send_to_localhost(sendmsg); - auto return_code = std::string(&buf[9], &buf[12]); - EXPECT_EQ("200", return_code); - } - - app.stop(); + + // Try to use those login credentials to access a resource + sendmsg = "GET /\r\nAuthorization: token\r\n\r\n{\"username\": \"dude\", " + "\"password\": \"dude\"}\r\n"; + { + send_to_localhost(sendmsg); + auto return_code = std::string(&buf[9], &buf[12]); + EXPECT_EQ("200", return_code); + } + + app.stop(); }
\ No newline at end of file diff --git a/src/webassets_test.cpp b/src/webassets_test.cpp index 2633c9b65b..a3106f2796 100644 --- a/src/webassets_test.cpp +++ b/src/webassets_test.cpp @@ -1,135 +1,145 @@ #include <crow/app.h> -#include <gmock/gmock.h> -#include <gzip_helper.hpp> -#include <webassets.hpp> -#include <sstream> #include <boost/algorithm/string/predicate.hpp> #include <boost/lexical_cast.hpp> +#include <gzip_helper.hpp> +#include <sstream> +#include <webassets.hpp> + #include "gtest/gtest.h" +#include <gmock/gmock.h> using namespace crow; using namespace std; using namespace testing; // Tests static files are loaded correctly -TEST(Webassets, StaticFilesFixedRoutes) { - std::array<char, 2048> buf; - SimpleApp app; - webassets::requestRoutes(app); - Server<SimpleApp> server(&app, "127.0.0.1", 45451); - auto _ = async(launch::async, [&] { server.run(); }); - - // get the homepage - std::string sendmsg = "GET /\r\n\r\n"; - - asio::io_service is; - - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), - 45451)); - - c.send(asio::buffer(sendmsg)); - - c.receive(asio::buffer(buf, 2048)); - c.close(); - - std::string response(std::begin(buf), std::end(buf)); - // This is a routine to split strings until a newline is hit - // TODO(ed) this should really use the HTTP parser - std::vector<std::string> headers; - std::string::size_type pos = 0; - std::string::size_type prev = 0; - int content_length = 0; - std::string content_encoding(""); - while ((pos = response.find("\r\n", prev)) != std::string::npos) { - auto this_string = response.substr(prev, pos - prev); - if (this_string == "") { - prev = pos + 2; - break; +TEST(Webassets, StaticFilesFixedRoutes) +{ + std::array<char, 2048> buf; + SimpleApp app; + webassets::requestRoutes(app); + Server<SimpleApp> server(&app, "127.0.0.1", 45451); + auto _ = async(launch::async, [&] { server.run(); }); + + // get the homepage + std::string sendmsg = "GET /\r\n\r\n"; + + asio::io_service is; + + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + + c.send(asio::buffer(sendmsg)); + + c.receive(asio::buffer(buf, 2048)); + c.close(); + + std::string response(std::begin(buf), std::end(buf)); + // This is a routine to split strings until a newline is hit + // TODO(ed) this should really use the HTTP parser + std::vector<std::string> headers; + std::string::size_type pos = 0; + std::string::size_type prev = 0; + int content_length = 0; + std::string content_encoding(""); + while ((pos = response.find("\r\n", prev)) != std::string::npos) + { + auto this_string = response.substr(prev, pos - prev); + if (this_string == "") + { + prev = pos + 2; + break; + } + + if (boost::starts_with(this_string, "Content-Length: ")) + { + content_length = boost::lexical_cast<int>(this_string.substr(16)); + // TODO(ed) This is an unfortunate test, but it's all we have at + // this point Realistically, the index.html will be more than 500 + // bytes. This test will need to be improved at some point + EXPECT_GT(content_length, 500); + } + if (boost::starts_with(this_string, "Content-Encoding: ")) + { + content_encoding = this_string.substr(18); + } + + headers.push_back(this_string); + prev = pos + 2; } - if (boost::starts_with(this_string, "Content-Length: ")) { - content_length = boost::lexical_cast<int>(this_string.substr(16)); - // TODO(ed) This is an unfortunate test, but it's all we have at this - // point - // Realistically, the index.html will be more than 500 bytes. This - // test will need to be improved at some point - EXPECT_GT(content_length, 500); - } - if (boost::starts_with(this_string, "Content-Encoding: ")) { - content_encoding = this_string.substr(18); + auto http_content = response.substr(prev); + // TODO(ed) ideally the server should support non-compressed gzip assets. + // Once this occurs, this line will be obsolete + std::string ungziped_content = http_content; + if (content_encoding == "gzip") + { + EXPECT_TRUE(gzipInflate(http_content, ungziped_content)); } - headers.push_back(this_string); - prev = pos + 2; - } - - auto http_content = response.substr(prev); - // TODO(ed) ideally the server should support non-compressed gzip assets. - // Once this occurs, this line will be obsolete - std::string ungziped_content = http_content; - if (content_encoding == "gzip") { - EXPECT_TRUE(gzipInflate(http_content, ungziped_content)); - } - - EXPECT_EQ(headers[0], "HTTP/1.1 200 OK"); - EXPECT_THAT(headers, - ::testing::Contains("Content-Type: text/html;charset=UTF-8")); + EXPECT_EQ(headers[0], "HTTP/1.1 200 OK"); + EXPECT_THAT(headers, + ::testing::Contains("Content-Type: text/html;charset=UTF-8")); - EXPECT_EQ(ungziped_content.substr(0, 21), "<!DOCTYPE html>\n<html"); + EXPECT_EQ(ungziped_content.substr(0, 21), "<!DOCTYPE html>\n<html"); - server.stop(); + server.stop(); } // Tests static files are loaded correctly -TEST(Webassets, EtagIsSane) { - std::array<char, 2048> buf; - SimpleApp app; - webassets::requestRoutes(app); - Server<SimpleApp> server(&app, "127.0.0.1", 45451); - auto _ = async(launch::async, [&] { server.run(); }); - - // get the homepage - std::string sendmsg = "GET /\r\n\r\n"; - - asio::io_service is; - - asio::ip::tcp::socket c(is); - c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), - 45451)); - - c.send(asio::buffer(sendmsg)); - - c.receive(asio::buffer(buf, 2048)); - c.close(); - - std::string response(std::begin(buf), std::end(buf)); - // This is a routine to split strings until a newline is hit - // TODO(ed) this should really use the HTTP parser - std::vector<std::string> headers; - std::string::size_type pos = 0; - std::string::size_type prev = 0; - int content_length = 0; - std::string content_encoding(""); - while ((pos = response.find("\r\n", prev)) != std::string::npos) { - auto this_string = response.substr(prev, pos - prev); - if (this_string == "") { - break; +TEST(Webassets, EtagIsSane) +{ + std::array<char, 2048> buf; + SimpleApp app; + webassets::requestRoutes(app); + Server<SimpleApp> server(&app, "127.0.0.1", 45451); + auto _ = async(launch::async, [&] { server.run(); }); + + // get the homepage + std::string sendmsg = "GET /\r\n\r\n"; + + asio::io_service is; + + asio::ip::tcp::socket c(is); + c.connect(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 45451)); + + c.send(asio::buffer(sendmsg)); + + c.receive(asio::buffer(buf, 2048)); + c.close(); + + std::string response(std::begin(buf), std::end(buf)); + // This is a routine to split strings until a newline is hit + // TODO(ed) this should really use the HTTP parser + std::vector<std::string> headers; + std::string::size_type pos = 0; + std::string::size_type prev = 0; + int content_length = 0; + std::string content_encoding(""); + while ((pos = response.find("\r\n", prev)) != std::string::npos) + { + auto this_string = response.substr(prev, pos - prev); + if (this_string == "") + { + break; + } + + if (boost::starts_with(this_string, "ETag: ")) + { + auto etag = this_string.substr(6); + // ETAG should not be blank + EXPECT_NE(etag, ""); + // SHa1 is 20 characters long + EXPECT_EQ(etag.size(), 40); + EXPECT_THAT(etag, MatchesRegex("^[a-f0-9]+$")); + } + + headers.push_back(this_string); + prev = pos + 2; } - if (boost::starts_with(this_string, "ETag: ")) { - auto etag = this_string.substr(6); - // ETAG should not be blank - EXPECT_NE(etag, ""); - // SHa1 is 20 characters long - EXPECT_EQ(etag.size(), 40); - EXPECT_THAT(etag, MatchesRegex("^[a-f0-9]+$")); - } - - headers.push_back(this_string); - prev = pos + 2; - } - - server.stop(); + server.stop(); } diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp index 2f89ad4269..051b1fed61 100644 --- a/src/webserver_main.cpp +++ b/src/webserver_main.cpp @@ -1,8 +1,12 @@ +#include <crow/app.h> #include <systemd/sd-daemon.h> + #include <bmcweb/settings.hpp> +#include <boost/asio.hpp> #include <dbus_monitor.hpp> #include <dbus_singleton.hpp> #include <image_upload.hpp> +#include <memory> #include <openbmc_dbus_rest.hpp> #include <persistent_data_middleware.hpp> #include <redfish.hpp> @@ -12,84 +16,91 @@ #include <sdbusplus/server.hpp> #include <security_headers_middleware.hpp> #include <ssl_key_handler.hpp> +#include <string> #include <token_authorization_middleware.hpp> #include <web_kvm.hpp> #include <webassets.hpp> #include <webserver_common.hpp> -#include <memory> -#include <string> -#include <crow/app.h> -#include <boost/asio.hpp> constexpr int defaultPort = 18080; template <typename... Middlewares> -void setupSocket(crow::Crow<Middlewares...>& app) { - int listenFd = sd_listen_fds(0); - if (1 == listenFd) { - BMCWEB_LOG_INFO << "attempting systemd socket activation"; - if (sd_is_socket_inet(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_STREAM, 1, 0)) { - BMCWEB_LOG_INFO << "Starting webserver on socket handle " - << SD_LISTEN_FDS_START; - app.socket(SD_LISTEN_FDS_START); - } else { - BMCWEB_LOG_INFO << "bad incoming socket, starting webserver on port " - << defaultPort; - app.port(defaultPort); +void setupSocket(crow::Crow<Middlewares...>& app) +{ + int listenFd = sd_listen_fds(0); + if (1 == listenFd) + { + BMCWEB_LOG_INFO << "attempting systemd socket activation"; + if (sd_is_socket_inet(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_STREAM, 1, + 0)) + { + BMCWEB_LOG_INFO << "Starting webserver on socket handle " + << SD_LISTEN_FDS_START; + app.socket(SD_LISTEN_FDS_START); + } + else + { + BMCWEB_LOG_INFO + << "bad incoming socket, starting webserver on port " + << defaultPort; + app.port(defaultPort); + } + } + else + { + BMCWEB_LOG_INFO << "Starting webserver on port " << defaultPort; + app.port(defaultPort); } - } else { - BMCWEB_LOG_INFO << "Starting webserver on port " << defaultPort; - app.port(defaultPort); - } } -int main(int argc, char** argv) { - crow::logger::setLogLevel(crow::LogLevel::DEBUG); +int main(int argc, char** argv) +{ + crow::logger::setLogLevel(crow::LogLevel::DEBUG); - auto io = std::make_shared<boost::asio::io_service>(); - CrowApp app(io); + auto io = std::make_shared<boost::asio::io_service>(); + CrowApp app(io); #ifdef BMCWEB_ENABLE_SSL - std::string sslPemFile("server.pem"); - std::cout << "Building SSL Context\n"; + std::string sslPemFile("server.pem"); + std::cout << "Building SSL Context\n"; - ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile); - std::cout << "SSL Enabled\n"; - auto sslContext = ensuressl::getSslContext(sslPemFile); - app.ssl(std::move(sslContext)); + ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile); + std::cout << "SSL Enabled\n"; + auto sslContext = ensuressl::getSslContext(sslPemFile); + app.ssl(std::move(sslContext)); #endif - // Static assets need to be initialized before Authorization, because auth - // needs to build the whitelist from the static routes + // Static assets need to be initialized before Authorization, because auth + // needs to build the whitelist from the static routes #ifdef BMCWEB_ENABLE_STATIC_HOSTING - crow::webassets::requestRoutes(app); + crow::webassets::requestRoutes(app); #endif #ifdef BMCWEB_ENABLE_KVM - crow::kvm::requestRoutes(app); + crow::kvm::requestRoutes(app); #endif #ifdef BMCWEB_ENABLE_REDFISH - crow::redfish::requestRoutes(app); + crow::redfish::requestRoutes(app); #endif #ifdef BMCWEB_ENABLE_DBUS_REST - crow::dbus_monitor::requestRoutes(app); - crow::image_upload::requestRoutes(app); - crow::openbmc_mapper::requestRoutes(app); + crow::dbus_monitor::requestRoutes(app); + crow::image_upload::requestRoutes(app); + crow::openbmc_mapper::requestRoutes(app); #endif - crow::token_authorization::requestRoutes(app); + crow::token_authorization::requestRoutes(app); - BMCWEB_LOG_INFO << "bmcweb (" << __DATE__ << ": " << __TIME__ << ')'; - setupSocket(app); + BMCWEB_LOG_INFO << "bmcweb (" << __DATE__ << ": " << __TIME__ << ')'; + setupSocket(app); - crow::connections::systemBus = - std::make_shared<sdbusplus::asio::connection>(*io); - redfish::RedfishService redfish(app); + crow::connections::systemBus = + std::make_shared<sdbusplus::asio::connection>(*io); + redfish::RedfishService redfish(app); - app.run(); - io->run(); + app.run(); + io->run(); - crow::connections::systemBus.reset(); + crow::connections::systemBus.reset(); } |