// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#include "app.hpp"
#include "async_resp.hpp"
#include "dbus_singleton.hpp"
#include "dbus_utility.hpp"
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
#include "parsing.hpp"
#include "routing.hpp"
#include "str_utility.hpp"

#include <systemd/sd-bus-protocol.h>
#include <systemd/sd-bus.h>
#include <tinyxml2.h>

#include <boost/beast/http/status.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/vector.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/system/error_code.hpp>
#include <nlohmann/json.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/exception.hpp>
#include <sdbusplus/message.hpp>
#include <sdbusplus/message/native_types.hpp>

#include <algorithm>
#include <array>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <ranges>
#include <regex>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

// IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp>
// IWYU pragma: no_include <boost/system/detail/error_code.hpp>
// IWYU pragma: no_include <boost/system/detail/error_category.hpp>
// IWYU pragma: no_include <errno.h>
// IWYU pragma: no_include <string.h>
// IWYU pragma: no_include <ext/alloc_traits.h>
// IWYU pragma: no_include <exception>
// IWYU pragma: no_include <boost/type_index/type_index_facade.hpp>

namespace crow
{
namespace openbmc_mapper
{
const constexpr char* notFoundMsg = "404 Not Found";
const constexpr char* badReqMsg = "400 Bad Request";
const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
const constexpr char* forbiddenMsg = "403 Forbidden";
const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
const constexpr char* methodFailedMsg = "500 Method Call Failed";
const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
const constexpr char* notFoundDesc =
    "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
const constexpr char* propNotFoundDesc =
    "The specified property cannot be found";
const constexpr char* noJsonDesc = "No JSON object could be decoded";
const constexpr char* invalidContentType =
    "Content-type header is missing or invalid";
const constexpr char* methodNotFoundDesc =
    "The specified method cannot be found";
const constexpr char* methodNotAllowedDesc = "Method not allowed";
const constexpr char* forbiddenPropDesc =
    "The specified property cannot be created";
const constexpr char* forbiddenResDesc =
    "The specified resource cannot be created";

inline bool validateFilename(const std::string& filename)
{
    static std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");

    return std::regex_match(filename, validFilename);
}

inline void setErrorResponse(crow::Response& res,
                             boost::beast::http::status result,
                             const std::string& desc, std::string_view msg)
{
    res.result(result);
    res.jsonValue["data"]["description"] = desc;
    res.jsonValue["message"] = msg;
    res.jsonValue["status"] = "error";
}

inline void
    introspectObjects(const std::string& processName,
                      const std::string& objectPath,
                      const std::shared_ptr<bmcweb::AsyncResp>& transaction)
{
    if (transaction->res.jsonValue.is_null())
    {
        transaction->res.jsonValue["status"] = "ok";
        transaction->res.jsonValue["bus_name"] = processName;
        transaction->res.jsonValue["objects"] = nlohmann::json::array();
    }

    crow::connections::systemBus->async_method_call(
        [transaction, processName{std::string(processName)},
         objectPath{std::string(objectPath)}](
            const boost::system::error_code& ec,
            const std::string& introspectXml) {
        if (ec)
        {
            BMCWEB_LOG_ERROR(
                "Introspect call failed with error: {} on process: {} path: {}",
                ec.message(), processName, objectPath);
            return;
        }
        nlohmann::json::object_t object;
        object["path"] = objectPath;

        transaction->res.jsonValue["objects"].emplace_back(std::move(object));

        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 {} {}", processName,
                             objectPath);
        }
        else
        {
            tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
            while (node != nullptr)
            {
                const char* childPath = node->Attribute("name");
                if (childPath != nullptr)
                {
                    std::string newpath;
                    if (objectPath != "/")
                    {
                        newpath += objectPath;
                    }
                    newpath += std::string("/") + childPath;
                    // introspect the subobjects as well
                    introspectObjects(processName, newpath, transaction);
                }

                node = node->NextSiblingElement("node");
            }
        }
    },
        processName, objectPath, "org.freedesktop.DBus.Introspectable",
        "Introspect");
}

inline void getPropertiesForEnumerate(
    const std::string& objectPath, const std::string& service,
    const std::string& interface,
    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
    BMCWEB_LOG_DEBUG("getPropertiesForEnumerate {} {} {}", objectPath, service,
                     interface);

    sdbusplus::asio::getAllProperties(
        *crow::connections::systemBus, service, objectPath, interface,
        [asyncResp, objectPath, service,
         interface](const boost::system::error_code& ec,
                    const dbus::utility::DBusPropertiesMap& propertiesList) {
        if (ec)
        {
            BMCWEB_LOG_ERROR(
                "GetAll on path {} iface {} service {} failed with code {}",
                objectPath, interface, service, ec);
            return;
        }

        nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
        nlohmann::json& objectJson = dataJson[objectPath];
        if (objectJson.is_null())
        {
            objectJson = nlohmann::json::object();
        }

        for (const auto& [name, value] : propertiesList)
        {
            nlohmann::json& propertyJson = objectJson[name];
            std::visit(
                [&propertyJson](auto&& val) {
                if constexpr (std::is_same_v<std::decay_t<decltype(val)>,
                                             sdbusplus::message::unix_fd>)
                {
                    propertyJson = val.fd;
                }
                else
                {
                    propertyJson = val;
                }
            },
                value);
        }
    });
}

// Find any results that weren't picked up by ObjectManagers, to be
// called after all ObjectManagers are searched for and called.
inline void findRemainingObjectsForEnumerate(
    const std::string& objectPath,
    const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
    BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate");
    const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];

    for (const auto& [path, interface_map] : *subtree)
    {
        if (path == objectPath)
        {
            // An enumerate does not return the target path's properties
            continue;
        }
        if (dataJson.find(path) == dataJson.end())
        {
            for (const auto& [service, interfaces] : interface_map)
            {
                for (const auto& interface : interfaces)
                {
                    if (!interface.starts_with("org.freedesktop.DBus"))
                    {
                        getPropertiesForEnumerate(path, service, interface,
                                                  asyncResp);
                    }
                }
            }
        }
    }
}

struct InProgressEnumerateData
{
    InProgressEnumerateData(
        const std::string& objectPathIn,
        const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
        objectPath(objectPathIn),
        asyncResp(asyncRespIn)
    {}

    ~InProgressEnumerateData()
    {
        try
        {
            findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
        }
        catch (...)
        {
            BMCWEB_LOG_CRITICAL(
                "findRemainingObjectsForEnumerate threw exception");
        }
    }

    InProgressEnumerateData(const InProgressEnumerateData&) = delete;
    InProgressEnumerateData(InProgressEnumerateData&&) = delete;
    InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
    InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
    const std::string objectPath;
    std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
    std::shared_ptr<bmcweb::AsyncResp> asyncResp;
};

inline void getManagedObjectsForEnumerate(
    const std::string& objectName, const std::string& objectManagerPath,
    const std::string& connectionName,
    const std::shared_ptr<InProgressEnumerateData>& transaction)
{
    BMCWEB_LOG_DEBUG(
        "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}",
        objectName, objectManagerPath, connectionName);
    sdbusplus::message::object_path path(objectManagerPath);
    dbus::utility::getManagedObjects(
        connectionName, path,
        [transaction, objectName,
         connectionName](const boost::system::error_code& ec,
                         const dbus::utility::ManagedObjectType& objects) {
        if (ec)
        {
            BMCWEB_LOG_ERROR(
                "GetManagedObjects on path {} on connection {} failed with code {}",
                objectName, connectionName, ec);
            return;
        }

        nlohmann::json& dataJson =
            transaction->asyncResp->res.jsonValue["data"];

        for (const auto& objectPath : objects)
        {
            if (objectPath.first.str.starts_with(objectName))
            {
                BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str);
                nlohmann::json& objectJson = dataJson[objectPath.first.str];
                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];
                        std::visit(
                            [&propertyJson](auto&& val) {
                            if constexpr (std::is_same_v<
                                              std::decay_t<decltype(val)>,
                                              sdbusplus::message::unix_fd>)
                            {
                                propertyJson = val.fd;
                            }
                            else
                            {
                                propertyJson = val;
                            }
                        },
                            property.second);
                    }
                }
            }
            for (const auto& interface : objectPath.second)
            {
                if (interface.first == "org.freedesktop.DBus.ObjectManager")
                {
                    getManagedObjectsForEnumerate(objectPath.first.str,
                                                  objectPath.first.str,
                                                  connectionName, transaction);
                }
            }
        }
    });
}

inline void findObjectManagerPathForEnumerate(
    const std::string& objectName, const std::string& connectionName,
    const std::shared_ptr<InProgressEnumerateData>& transaction)
{
    BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}",
                     objectName, connectionName);
    crow::connections::systemBus->async_method_call(
        [transaction, objectName, connectionName](
            const boost::system::error_code& ec,
            const dbus::utility::MapperGetAncestorsResponse& objects) {
        if (ec)
        {
            BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}",
                             objectName, ec);
            return;
        }

        for (const auto& pathGroup : objects)
        {
            for (const auto& connectionGroup : pathGroup.second)
            {
                if (connectionGroup.first == connectionName)
                {
                    // Found the object manager path for this resource.
                    getManagedObjectsForEnumerate(objectName, pathGroup.first,
                                                  connectionName, transaction);
                    return;
                }
            }
        }
    },
        "xyz.openbmc_project.ObjectMapper",
        "/xyz/openbmc_project/object_mapper",
        "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
        std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
}

// Uses GetObject to add the object info about the target /enumerate path to
// the results of GetSubTree, as GetSubTree will not return info for the
// target path, and then continues on enumerating the rest of the tree.
inline void getObjectAndEnumerate(
    const std::shared_ptr<InProgressEnumerateData>& transaction)
{
    dbus::utility::getDbusObject(
        transaction->objectPath, {},
        [transaction](const boost::system::error_code& ec,
                      const dbus::utility::MapperGetObject& objects) {
        if (ec)
        {
            BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}",
                             transaction->objectPath, ec);
            return;
        }

        BMCWEB_LOG_DEBUG("GetObject for {} has {} entries",
                         transaction->objectPath, objects.size());
        if (!objects.empty())
        {
            transaction->subtree->emplace_back(transaction->objectPath,
                                               objects);
        }

        // Map indicating connection name, and the path where the object
        // manager exists
        boost::container::flat_map<
            std::string, std::string, std::less<>,
            std::vector<std::pair<std::string, std::string>>>
            connections;

        for (const auto& object : *(transaction->subtree))
        {
            for (const auto& connection : object.second)
            {
                for (const auto& interface : connection.second)
                {
                    BMCWEB_LOG_DEBUG("{} has interface {}", connection.first,
                                     interface);
                    if (interface == "org.freedesktop.DBus.ObjectManager")
                    {
                        BMCWEB_LOG_DEBUG("found object manager path {}",
                                         object.first);
                        connections[connection.first] = object.first;
                    }
                }
            }
        }
        BMCWEB_LOG_DEBUG("Got {} connections", connections.size());

        for (const auto& connection : connections)
        {
            // If we already know where the object manager is, we don't
            // need to search for it, we can call directly in to
            // getManagedObjects
            if (!connection.second.empty())
            {
                getManagedObjectsForEnumerate(transaction->objectPath,
                                              connection.second,
                                              connection.first, transaction);
            }
            else
            {
                // otherwise we need to find the object manager path
                // before we can continue
                findObjectManagerPathForEnumerate(
                    transaction->objectPath, connection.first, transaction);
            }
        }
    });
}

// Structure for storing data on an in progress action
struct InProgressActionData
{
    explicit InProgressActionData(
        const std::shared_ptr<bmcweb::AsyncResp>& res) :
        asyncResp(res)
    {}
    ~InProgressActionData()
    {
        // Methods could have been called across different owners
        // and interfaces, where some calls failed and some passed.
        //
        // The rules for this are:
        // * if no method was called - error
        // * if a method failed and none passed - error
        //   (converse: if at least one method passed - OK)
        // * for the method output:
        //   * if output processing didn't fail, return the data

        // Only deal with method returns if nothing failed earlier
        if (asyncResp->res.result() == boost::beast::http::status::ok)
        {
            if (!methodPassed)
            {
                if (!methodFailed)
                {
                    setErrorResponse(asyncResp->res,
                                     boost::beast::http::status::not_found,
                                     methodNotFoundDesc, notFoundMsg);
                }
            }
            else
            {
                if (outputFailed)
                {
                    setErrorResponse(
                        asyncResp->res,
                        boost::beast::http::status::internal_server_error,
                        "Method output failure", methodOutputFailedMsg);
                }
                else
                {
                    asyncResp->res.jsonValue["status"] = "ok";
                    asyncResp->res.jsonValue["message"] = "200 OK";
                    asyncResp->res.jsonValue["data"] = methodResponse;
                }
            }
        }
    }
    InProgressActionData(const InProgressActionData&) = delete;
    InProgressActionData(InProgressActionData&&) = delete;
    InProgressActionData& operator=(const InProgressActionData&) = delete;
    InProgressActionData& operator=(InProgressActionData&&) = delete;

    void setErrorStatus(const std::string& desc)
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::bad_request, desc,
                         badReqMsg);
    }
    std::shared_ptr<bmcweb::AsyncResp> asyncResp;
    std::string path;
    std::string methodName;
    std::string interfaceName;
    bool methodPassed = false;
    bool methodFailed = false;
    bool outputFailed = false;
    bool convertedToArray = false;
    nlohmann::json methodResponse;
    nlohmann::json arguments;
};

inline std::vector<std::string> dbusArgSplit(const std::string& string)
{
    std::vector<std::string> ret;
    if (string.empty())
    {
        return ret;
    }
    ret.emplace_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.emplace_back("");
                    }
                }
                break;
            default:
                if (containerDepth == 0)
                {
                    if (character + 1 != string.end())
                    {
                        ret.emplace_back("");
                    }
                }
                break;
        }
    }

    return ret;
}

inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
                             const nlohmann::json& inputJson)
{
    int r = 0;
    BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType);
    const std::vector<std::string> argTypes = dbusArgSplit(argType);

    // Assume a single object for now.
    const nlohmann::json* j = &inputJson;
    nlohmann::json::const_iterator jIt = inputJson.begin();

    for (const std::string& argCode : 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 == inputJson.end())
            {
                return -2;
            }
            j = &*jIt;
            jIt++;
        }
        const int64_t* intValue = j->get_ptr<const int64_t*>();
        const std::string* stringValue = j->get_ptr<const std::string*>();
        const double* doubleValue = 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 (intValue == nullptr)
        {
            const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
            if (uintValue != nullptr)
            {
                v = static_cast<int64_t>(*uintValue);
                intValue = &v;
            }
        }
        if (doubleValue == nullptr)
        {
            const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
            if (uintValue != nullptr)
            {
                d = static_cast<double>(*uintValue);
                doubleValue = &d;
            }
        }
        if (doubleValue == nullptr)
        {
            if (intValue != nullptr)
            {
                d = static_cast<double>(*intValue);
                doubleValue = &d;
            }
        }

        if (argCode == "s")
        {
            if (stringValue == nullptr)
            {
                return -1;
            }
            r = sd_bus_message_append_basic(
                m, argCode[0], static_cast<const void*>(stringValue->data()));
            if (r < 0)
            {
                return r;
            }
        }
        else if (argCode == "i")
        {
            if (intValue == nullptr)
            {
                return -1;
            }
            if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
                (*intValue > std::numeric_limits<int32_t>::max()))
            {
                return -ERANGE;
            }
            int32_t i = static_cast<int32_t>(*intValue);
            r = sd_bus_message_append_basic(m, argCode[0], &i);
            if (r < 0)
            {
                return r;
            }
        }
        else if (argCode == "b")
        {
            // lots of ways bool could be represented here.  Try them all
            int boolInt = 0;
            if (intValue != nullptr)
            {
                if (*intValue == 1)
                {
                    boolInt = 1;
                }
                else if (*intValue == 0)
                {
                    boolInt = 0;
                }
                else
                {
                    return -ERANGE;
                }
            }
            else if (b != nullptr)
            {
                boolInt = *b ? 1 : 0;
            }
            else if (stringValue != nullptr)
            {
                if (!stringValue->empty())
                {
                    if (stringValue->front() == 't' ||
                        stringValue->front() == 'T')
                    {
                        boolInt = 1;
                    }
                }
            }
            else
            {
                return -1;
            }
            r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
            if (r < 0)
            {
                return r;
            }
        }
        else if (argCode == "n")
        {
            if (intValue == nullptr)
            {
                return -1;
            }
            if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
                (*intValue > std::numeric_limits<int16_t>::max()))
            {
                return -ERANGE;
            }
            int16_t n = static_cast<int16_t>(*intValue);
            r = sd_bus_message_append_basic(m, argCode[0], &n);
            if (r < 0)
            {
                return r;
            }
        }
        else if (argCode == "x")
        {
            if (intValue == nullptr)
            {
                return -1;
            }
            r = sd_bus_message_append_basic(m, argCode[0], intValue);
            if (r < 0)
            {
                return r;
            }
        }
        else if (argCode == "y")
        {
            const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
            if (uintValue == nullptr)
            {
                return -1;
            }
            if (*uintValue > std::numeric_limits<uint8_t>::max())
            {
                return -ERANGE;
            }
            uint8_t y = static_cast<uint8_t>(*uintValue);
            r = sd_bus_message_append_basic(m, argCode[0], &y);
        }
        else if (argCode == "q")
        {
            const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
            if (uintValue == nullptr)
            {
                return -1;
            }
            if (*uintValue > std::numeric_limits<uint16_t>::max())
            {
                return -ERANGE;
            }
            uint16_t q = static_cast<uint16_t>(*uintValue);
            r = sd_bus_message_append_basic(m, argCode[0], &q);
        }
        else if (argCode == "u")
        {
            const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
            if (uintValue == nullptr)
            {
                return -1;
            }
            if (*uintValue > std::numeric_limits<uint32_t>::max())
            {
                return -ERANGE;
            }
            uint32_t u = static_cast<uint32_t>(*uintValue);
            r = sd_bus_message_append_basic(m, argCode[0], &u);
        }
        else if (argCode == "t")
        {
            const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
            if (uintValue == nullptr)
            {
                return -1;
            }
            r = sd_bus_message_append_basic(m, argCode[0], uintValue);
        }
        else if (argCode == "d")
        {
            if (doubleValue == nullptr)
            {
                return -1;
            }
            if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
                (*doubleValue > std::numeric_limits<double>::max()))
            {
                return -ERANGE;
            }
            sd_bus_message_append_basic(m, argCode[0], doubleValue);
        }
        else if (argCode.starts_with("a"))
        {
            std::string containedType = argCode.substr(1);
            r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
                                              containedType.c_str());
            if (r < 0)
            {
                return r;
            }

            for (const auto& it : *j)
            {
                r = convertJsonToDbus(m, containedType, it);
                if (r < 0)
                {
                    return r;
                }
            }
            sd_bus_message_close_container(m);
        }
        else if (argCode.starts_with("v"))
        {
            std::string containedType = argCode.substr(1);
            BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}",
                             argCode, containedType);
            r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
                                              containedType.c_str());
            if (r < 0)
            {
                return r;
            }

            r = convertJsonToDbus(m, containedType, inputJson);
            if (r < 0)
            {
                return r;
            }

            r = sd_bus_message_close_container(m);
            if (r < 0)
            {
                return r;
            }
        }
        else if (argCode.starts_with("(") && argCode.ends_with(")"))
        {
            std::string containedType = argCode.substr(1, argCode.size() - 2);
            r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
                                              containedType.c_str());
            if (r < 0)
            {
                return r;
            }

            nlohmann::json::const_iterator it = j->begin();
            for (const std::string& argCode2 : dbusArgSplit(containedType))
            {
                if (it == j->end())
                {
                    return -1;
                }
                r = convertJsonToDbus(m, argCode2, *it);
                if (r < 0)
                {
                    return r;
                }
                it++;
            }
            r = sd_bus_message_close_container(m);
        }
        else if (argCode.starts_with("{") && argCode.ends_with("}"))
        {
            std::string containedType = argCode.substr(1, argCode.size() - 2);
            r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
                                              containedType.c_str());
            if (r < 0)
            {
                return r;
            }

            std::vector<std::string> codes = dbusArgSplit(containedType);
            if (codes.size() != 2)
            {
                return -1;
            }
            const std::string& keyType = codes[0];
            const std::string& valueType = codes[1];
            for (const auto& it : j->items())
            {
                r = convertJsonToDbus(m, keyType, it.key());
                if (r < 0)
                {
                    return r;
                }

                r = convertJsonToDbus(m, valueType, it.value());
                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++;
        }
    }

    return r;
}

template <typename T>
int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
                    nlohmann::json& data)
{
    T value;
    // When T == char*, this warning fires.  Unclear how to resolve
    // Given that sd-bus takes a void pointer to a char*, and that's
    // Not something we can fix.
    // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
    int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!",
                         typeCode);
        return r;
    }

    data = value;
    return 0;
}

int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
                      nlohmann::json& response);

inline int readDictEntryFromMessage(const std::string& typeCode,
                                    sdbusplus::message_t& m,
                                    nlohmann::json& object)
{
    std::vector<std::string> types = dbusArgSplit(typeCode);
    if (types.size() != 2)
    {
        BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}",
                         types.size());
        return -1;
    }

    int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
                                           typeCode.c_str());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r);
        return r;
    }

    nlohmann::json key;
    r = convertDBusToJSON(types[0], m, key);
    if (r < 0)
    {
        return r;
    }

    const std::string* keyPtr = key.get_ptr<const std::string*>();
    if (keyPtr == nullptr)
    {
        // json doesn't support non-string keys.  If we hit this condition,
        // convert the result to a string so we can proceed
        key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
        keyPtr = key.get_ptr<const std::string*>();
        // in theory this can't fail now, but lets be paranoid about it
        // anyway
        if (keyPtr == nullptr)
        {
            return -1;
        }
    }
    nlohmann::json& value = object[*keyPtr];

    r = convertDBusToJSON(types[1], m, value);
    if (r < 0)
    {
        return r;
    }

    r = sd_bus_message_exit_container(m.get());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
        return r;
    }

    return 0;
}

inline int readArrayFromMessage(const std::string& typeCode,
                                sdbusplus::message_t& m, nlohmann::json& data)
{
    if (typeCode.size() < 2)
    {
        BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode);
        return -1;
    }

    std::string containedType = typeCode.substr(1);

    int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
                                           containedType.c_str());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
        return r;
    }

    bool dict = containedType.starts_with("{") && containedType.ends_with("}");

    if (dict)
    {
        // Remove the { }
        containedType = containedType.substr(1, containedType.size() - 2);
        data = nlohmann::json::object();
    }
    else
    {
        data = nlohmann::json::array();
    }

    while (true)
    {
        r = sd_bus_message_at_end(m.get(), 0);
        if (r < 0)
        {
            BMCWEB_LOG_ERROR("sd_bus_message_at_end failed");
            return r;
        }

        if (r > 0)
        {
            break;
        }

        // Dictionaries are only ever seen in an array
        if (dict)
        {
            r = readDictEntryFromMessage(containedType, m, data);
            if (r < 0)
            {
                return r;
            }
        }
        else
        {
            data.push_back(nlohmann::json());

            r = convertDBusToJSON(containedType, m, data.back());
            if (r < 0)
            {
                return r;
            }
        }
    }

    r = sd_bus_message_exit_container(m.get());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
        return r;
    }

    return 0;
}

inline int readStructFromMessage(const std::string& typeCode,
                                 sdbusplus::message_t& m, nlohmann::json& data)
{
    if (typeCode.size() < 3)
    {
        BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode);
        return -1;
    }

    std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
    std::vector<std::string> types = dbusArgSplit(containedTypes);

    int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
                                           containedTypes.c_str());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
        return r;
    }

    for (const std::string& type : types)
    {
        data.push_back(nlohmann::json());
        r = convertDBusToJSON(type, m, data.back());
        if (r < 0)
        {
            return r;
        }
    }

    r = sd_bus_message_exit_container(m.get());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
        return r;
    }
    return 0;
}

inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
{
    const char* containerType = nullptr;
    int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed");
        return r;
    }

    r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
                                       containerType);
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
        return r;
    }

    r = convertDBusToJSON(containerType, m, data);
    if (r < 0)
    {
        return r;
    }

    r = sd_bus_message_exit_container(m.get());
    if (r < 0)
    {
        BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed");
        return r;
    }

    return 0;
}

inline int convertDBusToJSON(const std::string& returnType,
                             sdbusplus::message_t& m, nlohmann::json& response)
{
    int r = 0;
    const std::vector<std::string> returnTypes = dbusArgSplit(returnType);

    for (const std::string& typeCode : returnTypes)
    {
        nlohmann::json* thisElement = &response;
        if (returnTypes.size() > 1)
        {
            response.push_back(nlohmann::json{});
            thisElement = &response.back();
        }

        if (typeCode == "s" || typeCode == "g" || typeCode == "o")
        {
            r = readMessageItem<char*>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "b")
        {
            r = readMessageItem<int>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }

            *thisElement = static_cast<bool>(thisElement->get<int>());
        }
        else if (typeCode == "u")
        {
            r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "i")
        {
            r = readMessageItem<int32_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "x")
        {
            r = readMessageItem<int64_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "t")
        {
            r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "n")
        {
            r = readMessageItem<int16_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "q")
        {
            r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "y")
        {
            r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "d")
        {
            r = readMessageItem<double>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode == "h")
        {
            r = readMessageItem<int>(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode.starts_with("a"))
        {
            r = readArrayFromMessage(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
        {
            r = readStructFromMessage(typeCode, m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else if (typeCode.starts_with("v"))
        {
            r = readVariantFromMessage(m, *thisElement);
            if (r < 0)
            {
                return r;
            }
        }
        else
        {
            BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode);
            return -2;
        }
    }

    return 0;
}

inline void handleMethodResponse(
    const std::shared_ptr<InProgressActionData>& transaction,
    sdbusplus::message_t& m, const std::string& returnType)
{
    nlohmann::json data;

    int r = convertDBusToJSON(returnType, m, data);
    if (r < 0)
    {
        transaction->outputFailed = true;
        return;
    }

    if (data.is_null())
    {
        return;
    }

    if (transaction->methodResponse.is_null())
    {
        transaction->methodResponse = std::move(data);
        return;
    }

    // If they're both dictionaries or arrays, merge into one.
    // Otherwise, make the results an array with every result
    // an entry.  Could also just fail in that case, but it
    // seems better to get the data back somehow.

    if (transaction->methodResponse.is_object() && data.is_object())
    {
        for (const auto& obj : data.items())
        {
            // Note: Will overwrite the data for a duplicate key
            transaction->methodResponse.emplace(obj.key(),
                                                std::move(obj.value()));
        }
        return;
    }

    if (transaction->methodResponse.is_array() && data.is_array())
    {
        for (auto& obj : data)
        {
            transaction->methodResponse.emplace_back(std::move(obj));
        }
        return;
    }

    if (!transaction->convertedToArray)
    {
        // They are different types. May as well turn them into an array
        nlohmann::json j = std::move(transaction->methodResponse);
        transaction->methodResponse = nlohmann::json::array();
        transaction->methodResponse.emplace_back(std::move(j));
        transaction->methodResponse.emplace_back(std::move(data));
        transaction->convertedToArray = true;
    }
    else
    {
        transaction->methodResponse.emplace_back(std::move(data));
    }
}

inline void findActionOnInterface(
    const 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& introspectXml) {
        BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml);
        if (ec)
        {
            BMCWEB_LOG_ERROR(
                "Introspect call failed with error: {} on process: {}",
                ec.message(), connectionName);
            return;
        }
        tinyxml2::XMLDocument doc;

        doc.Parse(introspectXml.data(), introspectXml.size());
        tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
        if (pRoot == nullptr)
        {
            BMCWEB_LOG_ERROR("XML document failed to parse {}", connectionName);
            return;
        }
        tinyxml2::XMLElement* interfaceNode =
            pRoot->FirstChildElement("interface");
        while (interfaceNode != nullptr)
        {
            const char* thisInterfaceName = interfaceNode->Attribute("name");
            if (thisInterfaceName != nullptr)
            {
                if (!transaction->interfaceName.empty() &&
                    (transaction->interfaceName != thisInterfaceName))
                {
                    interfaceNode =
                        interfaceNode->NextSiblingElement("interface");
                    continue;
                }

                tinyxml2::XMLElement* methodNode =
                    interfaceNode->FirstChildElement("method");
                while (methodNode != nullptr)
                {
                    const char* thisMethodName = methodNode->Attribute("name");
                    BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName);
                    if (thisMethodName != nullptr &&
                        thisMethodName == transaction->methodName)
                    {
                        BMCWEB_LOG_DEBUG(
                            "Found method named {} on interface {}",
                            thisMethodName, thisInterfaceName);
                        sdbusplus::message_t m =
                            crow::connections::systemBus->new_method_call(
                                connectionName.c_str(),
                                transaction->path.c_str(), thisInterfaceName,
                                transaction->methodName.c_str());

                        tinyxml2::XMLElement* argumentNode =
                            methodNode->FirstChildElement("arg");

                        std::string returnType;

                        // Find the output type
                        while (argumentNode != nullptr)
                        {
                            const char* argDirection =
                                argumentNode->Attribute("direction");
                            const char* argType =
                                argumentNode->Attribute("type");
                            if (argDirection != nullptr && argType != nullptr &&
                                std::string(argDirection) == "out")
                            {
                                returnType = argType;
                                break;
                            }
                            argumentNode =
                                argumentNode->NextSiblingElement("arg");
                        }

                        auto argIt = transaction->arguments.begin();

                        argumentNode = methodNode->FirstChildElement("arg");

                        while (argumentNode != nullptr)
                        {
                            const char* argDirection =
                                argumentNode->Attribute("direction");
                            const char* argType =
                                argumentNode->Attribute("type");
                            if (argDirection != nullptr && argType != nullptr &&
                                std::string(argDirection) == "in")
                            {
                                if (argIt == transaction->arguments.end())
                                {
                                    transaction->setErrorStatus(
                                        "Invalid method args");
                                    return;
                                }
                                if (convertJsonToDbus(m.get(),
                                                      std::string(argType),
                                                      *argIt) < 0)
                                {
                                    transaction->setErrorStatus(
                                        "Invalid method arg type");
                                    return;
                                }

                                argIt++;
                            }
                            argumentNode =
                                argumentNode->NextSiblingElement("arg");
                        }

                        crow::connections::systemBus->async_send(
                            m, [transaction, returnType](
                                   const boost::system::error_code& ec2,
                                   sdbusplus::message_t& m2) {
                            if (ec2)
                            {
                                transaction->methodFailed = true;
                                const sd_bus_error* e = m2.get_error();

                                if (e != nullptr)
                                {
                                    setErrorResponse(
                                        transaction->asyncResp->res,
                                        boost::beast::http::status::bad_request,
                                        e->name, e->message);
                                }
                                else
                                {
                                    setErrorResponse(
                                        transaction->asyncResp->res,
                                        boost::beast::http::status::bad_request,
                                        "Method call failed", methodFailedMsg);
                                }
                                return;
                            }
                            transaction->methodPassed = true;

                            handleMethodResponse(transaction, m2, returnType);
                        });
                        break;
                    }
                    methodNode = methodNode->NextSiblingElement("method");
                }
            }
            interfaceNode = interfaceNode->NextSiblingElement("interface");
        }
    },
        connectionName, transaction->path,
        "org.freedesktop.DBus.Introspectable", "Introspect");
}

inline void handleAction(const crow::Request& req,
                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                         const std::string& objectPath,
                         const std::string& methodName)
{
    BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath,
                     methodName);
    nlohmann::json requestDbusData;

    JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
    if (ret == JsonParseResult::BadContentType)
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::unsupported_media_type,
                         invalidContentType, unsupportedMediaMsg);
        return;
    }
    if (ret != JsonParseResult::Success)
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::bad_request, noJsonDesc,
                         badReqMsg);
        return;
    }
    nlohmann::json::iterator data = requestDbusData.find("data");
    if (data == requestDbusData.end())
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::bad_request, noJsonDesc,
                         badReqMsg);
        return;
    }

    if (!data->is_array())
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::bad_request, noJsonDesc,
                         badReqMsg);
        return;
    }
    auto transaction = std::make_shared<InProgressActionData>(asyncResp);

    transaction->path = objectPath;
    transaction->methodName = methodName;
    transaction->arguments = std::move(*data);
    dbus::utility::getDbusObject(
        objectPath, {},
        [transaction](
            const boost::system::error_code& ec,
            const std::vector<std::pair<std::string, std::vector<std::string>>>&
                interfaceNames) {
        if (ec || interfaceNames.empty())
        {
            BMCWEB_LOG_ERROR("Can't find object");
            setErrorResponse(transaction->asyncResp->res,
                             boost::beast::http::status::not_found,
                             notFoundDesc, notFoundMsg);
            return;
        }

        BMCWEB_LOG_DEBUG("GetObject returned {} object(s)",
                         interfaceNames.size());

        for (const std::pair<std::string, std::vector<std::string>>& object :
             interfaceNames)
        {
            findActionOnInterface(transaction, object.first);
        }
    });
}

inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                         const std::string& objectPath)
{
    BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath);

    dbus::utility::getDbusObject(
        objectPath, {},
        [asyncResp, objectPath](
            const boost::system::error_code& ec,
            const std::vector<std::pair<std::string, std::vector<std::string>>>&
                interfaceNames) {
        if (ec || interfaceNames.empty())
        {
            BMCWEB_LOG_ERROR("Can't find object");
            setErrorResponse(asyncResp->res,
                             boost::beast::http::status::method_not_allowed,
                             methodNotAllowedDesc, methodNotAllowedMsg);
            return;
        }

        auto transaction = std::make_shared<InProgressActionData>(asyncResp);
        transaction->path = objectPath;
        transaction->methodName = "Delete";
        transaction->interfaceName = "xyz.openbmc_project.Object.Delete";

        for (const std::pair<std::string, std::vector<std::string>>& object :
             interfaceNames)
        {
            findActionOnInterface(transaction, object.first);
        }
    });
}

inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                       const std::string& objectPath, int32_t depth = 0)
{
    dbus::utility::getSubTreePaths(
        objectPath, depth, {},
        [asyncResp](
            const boost::system::error_code& ec,
            const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
        if (ec)
        {
            setErrorResponse(asyncResp->res,
                             boost::beast::http::status::not_found,
                             notFoundDesc, notFoundMsg);
        }
        else
        {
            asyncResp->res.jsonValue["status"] = "ok";
            asyncResp->res.jsonValue["message"] = "200 OK";
            asyncResp->res.jsonValue["data"] = objectPaths;
        }
    });
}

inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                            const std::string& objectPath)
{
    BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath);

    asyncResp->res.jsonValue["message"] = "200 OK";
    asyncResp->res.jsonValue["status"] = "ok";
    asyncResp->res.jsonValue["data"] = nlohmann::json::object();

    dbus::utility::getSubTree(
        objectPath, 0, {},
        [objectPath, asyncResp](
            const boost::system::error_code& ec,
            const dbus::utility::MapperGetSubTreeResponse& objectNames) {
        auto transaction = std::make_shared<InProgressEnumerateData>(objectPath,
                                                                     asyncResp);

        transaction->subtree =
            std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
                objectNames);

        if (ec)
        {
            BMCWEB_LOG_ERROR("GetSubTree failed on {}",
                             transaction->objectPath);
            setErrorResponse(transaction->asyncResp->res,
                             boost::beast::http::status::not_found,
                             notFoundDesc, notFoundMsg);
            return;
        }

        // Add the data for the path passed in to the results
        // as if GetSubTree returned it, and continue on enumerating
        getObjectAndEnumerate(transaction);
    });
}

inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                      std::string& objectPath, std::string& destProperty)
{
    BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty);
    std::shared_ptr<std::string> propertyName =
        std::make_shared<std::string>(std::move(destProperty));

    std::shared_ptr<std::string> path =
        std::make_shared<std::string>(std::move(objectPath));

    dbus::utility::getDbusObject(
        *path, {},
        [asyncResp, path,
         propertyName](const boost::system::error_code& ec,
                       const dbus::utility::MapperGetObject& objectNames) {
        if (ec || objectNames.empty())
        {
            setErrorResponse(asyncResp->res,
                             boost::beast::http::status::not_found,
                             notFoundDesc, notFoundMsg);
            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 : objectNames)
        {
            const std::vector<std::string>& interfaceNames = connection.second;

            if (interfaceNames.empty())
            {
                setErrorResponse(asyncResp->res,
                                 boost::beast::http::status::not_found,
                                 notFoundDesc, notFoundMsg);
                return;
            }

            for (const std::string& interface : interfaceNames)
            {
                sdbusplus::message_t m =
                    crow::connections::systemBus->new_method_call(
                        connection.first.c_str(), path->c_str(),
                        "org.freedesktop.DBus.Properties", "GetAll");
                m.append(interface);
                crow::connections::systemBus->async_send(
                    m, [asyncResp, response,
                        propertyName](const boost::system::error_code& ec2,
                                      sdbusplus::message_t& msg) {
                    if (ec2)
                    {
                        BMCWEB_LOG_ERROR("Bad dbus request error: {}", ec2);
                    }
                    else
                    {
                        nlohmann::json properties;
                        int r = convertDBusToJSON("a{sv}", msg, properties);
                        if (r < 0)
                        {
                            BMCWEB_LOG_ERROR("convertDBusToJSON failed");
                        }
                        else
                        {
                            for (const auto& prop : properties.items())
                            {
                                // if property name is empty, or
                                // matches our search query, add it
                                // to the response json

                                if (propertyName->empty())
                                {
                                    (*response)[prop.key()] =
                                        std::move(prop.value());
                                }
                                else if (prop.key() == *propertyName)
                                {
                                    *response = std::move(prop.value());
                                }
                            }
                        }
                    }
                    if (response.use_count() == 1)
                    {
                        if (!propertyName->empty() && response->empty())
                        {
                            setErrorResponse(
                                asyncResp->res,
                                boost::beast::http::status::not_found,
                                propNotFoundDesc, notFoundMsg);
                        }
                        else
                        {
                            asyncResp->res.jsonValue["status"] = "ok";
                            asyncResp->res.jsonValue["message"] = "200 OK";
                            asyncResp->res.jsonValue["data"] = *response;
                        }
                    }
                });
            }
        }
    });
}

struct AsyncPutRequest
{
    explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
        asyncResp(resIn)
    {}
    ~AsyncPutRequest()
    {
        if (asyncResp->res.jsonValue.empty())
        {
            setErrorResponse(asyncResp->res,
                             boost::beast::http::status::forbidden,
                             forbiddenMsg, forbiddenPropDesc);
        }
    }

    AsyncPutRequest(const AsyncPutRequest&) = delete;
    AsyncPutRequest(AsyncPutRequest&&) = delete;
    AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
    AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;

    void setErrorStatus(const std::string& desc)
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::internal_server_error,
                         desc, badReqMsg);
    }

    const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
    std::string objectPath;
    std::string propertyName;
    nlohmann::json propertyValue;
};

inline void handlePut(const crow::Request& req,
                      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                      const std::string& objectPath,
                      const std::string& destProperty)
{
    if (destProperty.empty())
    {
        setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
                         forbiddenResDesc, forbiddenMsg);
        return;
    }
    nlohmann::json requestDbusData;

    JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
    if (ret == JsonParseResult::BadContentType)
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::unsupported_media_type,
                         invalidContentType, unsupportedMediaMsg);
        return;
    }

    if (ret != JsonParseResult::Success)
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::bad_request, noJsonDesc,
                         badReqMsg);
        return;
    }

    auto propertyIt = requestDbusData.find("data");
    if (propertyIt == requestDbusData.end())
    {
        setErrorResponse(asyncResp->res,
                         boost::beast::http::status::bad_request, noJsonDesc,
                         badReqMsg);
        return;
    }
    const nlohmann::json& propertySetValue = *propertyIt;
    auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
    transaction->objectPath = objectPath;
    transaction->propertyName = destProperty;
    transaction->propertyValue = propertySetValue;

    dbus::utility::getDbusObject(
        transaction->objectPath, {},
        [transaction](const boost::system::error_code& ec2,
                      const dbus::utility::MapperGetObject& objectNames) {
        if (!ec2 && objectNames.empty())
        {
            setErrorResponse(transaction->asyncResp->res,
                             boost::beast::http::status::not_found,
                             propNotFoundDesc, notFoundMsg);
            return;
        }

        for (const std::pair<std::string, std::vector<std::string>>&
                 connection : objectNames)
        {
            const std::string& connectionName = connection.first;

            crow::connections::systemBus->async_method_call(
                [connectionName{std::string(connectionName)},
                 transaction](const boost::system::error_code& ec3,
                              const std::string& introspectXml) {
                if (ec3)
                {
                    BMCWEB_LOG_ERROR(
                        "Introspect call failed with error: {} on process: {}",
                        ec3.message(), connectionName);
                    transaction->setErrorStatus("Unexpected Error");
                    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("Unexpected Error");
                    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_t 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(
                                        "Unexpected Error");
                                    return;
                                }
                                r = convertJsonToDbus(
                                    m.get(), argType,
                                    transaction->propertyValue);
                                if (r < 0)
                                {
                                    if (r == -ERANGE)
                                    {
                                        transaction->setErrorStatus(
                                            "Provided property value "
                                            "is out of range for the "
                                            "property type");
                                    }
                                    else
                                    {
                                        transaction->setErrorStatus(
                                            "Invalid arg type");
                                    }
                                    return;
                                }
                                r = sd_bus_message_close_container(m.get());
                                if (r < 0)
                                {
                                    transaction->setErrorStatus(
                                        "Unexpected Error");
                                    return;
                                }
                                crow::connections::systemBus->async_send(
                                    m, [transaction](
                                           const boost::system::error_code& ec,
                                           sdbusplus::message_t& m2) {
                                    BMCWEB_LOG_DEBUG("sent");
                                    if (ec)
                                    {
                                        const sd_bus_error* e = m2.get_error();
                                        setErrorResponse(
                                            transaction->asyncResp->res,
                                            boost::beast::http::status::
                                                forbidden,
                                            (e) != nullptr
                                                ? e->name
                                                : ec.category().name(),
                                            (e) != nullptr ? e->message
                                                           : ec.message());
                                    }
                                    else
                                    {
                                        transaction->asyncResp->res
                                            .jsonValue["status"] = "ok";
                                        transaction->asyncResp->res
                                            .jsonValue["message"] = "200 OK";
                                        transaction->asyncResp->res
                                            .jsonValue["data"] = nullptr;
                                    }
                                });
                            }
                        }
                        propNode = propNode->NextSiblingElement("property");
                    }
                    ifaceNode = ifaceNode->NextSiblingElement("interface");
                }
            },
                connectionName, transaction->objectPath,
                "org.freedesktop.DBus.Introspectable", "Introspect");
        }
    });
}

inline void handleDBusUrl(const crow::Request& req,
                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                          std::string& objectPath)
{
    // If accessing a single attribute, fill in and update objectPath,
    // otherwise leave destProperty blank
    std::string destProperty;
    const char* attrSeperator = "/attr/";
    size_t attrPosition = objectPath.find(attrSeperator);
    if (attrPosition != std::string::npos)
    {
        destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
                                         objectPath.length());
        objectPath.resize(attrPosition);
    }

    if (req.method() == boost::beast::http::verb::post)
    {
        constexpr const char* actionSeperator = "/action/";
        size_t actionPosition = objectPath.find(actionSeperator);
        if (actionPosition != std::string::npos)
        {
            std::string postProperty =
                objectPath.substr((actionPosition + strlen(actionSeperator)),
                                  objectPath.length());
            objectPath.resize(actionPosition);
            handleAction(req, asyncResp, objectPath, postProperty);
            return;
        }
    }
    else if (req.method() == boost::beast::http::verb::get)
    {
        if (objectPath.ends_with("/enumerate"))
        {
            objectPath.erase(objectPath.end() - sizeof("enumerate"),
                             objectPath.end());
            handleEnumerate(asyncResp, objectPath);
        }
        else if (objectPath.ends_with("/list"))
        {
            objectPath.erase(objectPath.end() - sizeof("list"),
                             objectPath.end());
            handleList(asyncResp, objectPath);
        }
        else
        {
            // Trim any trailing "/" at the end
            if (objectPath.ends_with("/"))
            {
                objectPath.pop_back();
                handleList(asyncResp, objectPath, 1);
            }
            else
            {
                handleGet(asyncResp, objectPath, destProperty);
            }
        }
        return;
    }
    else if (req.method() == boost::beast::http::verb::put)
    {
        handlePut(req, asyncResp, objectPath, destProperty);
        return;
    }
    else if (req.method() == boost::beast::http::verb::delete_)
    {
        handleDelete(asyncResp, objectPath);
        return;
    }

    setErrorResponse(asyncResp->res,
                     boost::beast::http::status::method_not_allowed,
                     methodNotAllowedDesc, methodNotAllowedMsg);
}

inline void
    handleBusSystemPost(const crow::Request& req,
                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                        const std::string& processName,
                        const std::string& requestedPath)
{
    std::vector<std::string> strs;

    bmcweb::split(strs, requestedPath, '/');
    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 necessary as the trailing slash gets
            // parsed as part of our <path> specifier above, which
            // causes the normal trailing backslash redirector to
            // fail.
        }
        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
        asyncResp->res.result(boost::beast::http::status::not_found);
        return;
    }
    if (interfaceName.empty())
    {
        crow::connections::systemBus->async_method_call(
            [asyncResp, processName,
             objectPath](const boost::system::error_code& ec,
                         const std::string& introspectXml) {
            if (ec)
            {
                BMCWEB_LOG_ERROR(
                    "Introspect call failed with error: {} on process: {} path: {}",
                    ec.message(), processName, objectPath);
                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 {} {}",
                                 processName, objectPath);
                asyncResp->res.jsonValue["status"] = "XML parse error";
                asyncResp->res.result(
                    boost::beast::http::status::internal_server_error);
                return;
            }

            BMCWEB_LOG_DEBUG("{}", introspectXml);
            asyncResp->res.jsonValue["status"] = "ok";
            asyncResp->res.jsonValue["bus_name"] = processName;
            asyncResp->res.jsonValue["object_path"] = objectPath;

            nlohmann::json& interfacesArray =
                asyncResp->res.jsonValue["interfaces"];
            interfacesArray = nlohmann::json::array();
            tinyxml2::XMLElement* interface =
                pRoot->FirstChildElement("interface");

            while (interface != nullptr)
            {
                const char* ifaceName = interface->Attribute("name");
                if (ifaceName != nullptr)
                {
                    nlohmann::json::object_t interfaceObj;
                    interfaceObj["name"] = ifaceName;
                    interfacesArray.emplace_back(std::move(interfaceObj));
                }

                interface = interface->NextSiblingElement("interface");
            }
        },
            processName, objectPath, "org.freedesktop.DBus.Introspectable",
            "Introspect");
    }
    else if (methodName.empty())
    {
        crow::connections::systemBus->async_method_call(
            [asyncResp, processName, objectPath,
             interfaceName](const boost::system::error_code& ec,
                            const std::string& introspectXml) {
            if (ec)
            {
                BMCWEB_LOG_ERROR(
                    "Introspect call failed with error: {} on process: {} path: {}",
                    ec.message(), processName, objectPath);
                return;
            }
            tinyxml2::XMLDocument doc;

            doc.Parse(introspectXml.data(), introspectXml.size());
            tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
            if (pRoot == nullptr)
            {
                BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
                                 processName, objectPath);
                asyncResp->res.result(
                    boost::beast::http::status::internal_server_error);
                return;
            }

            asyncResp->res.jsonValue["status"] = "ok";
            asyncResp->res.jsonValue["bus_name"] = processName;
            asyncResp->res.jsonValue["interface"] = interfaceName;
            asyncResp->res.jsonValue["object_path"] = objectPath;

            nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"];
            methodsArray = nlohmann::json::array();

            nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"];
            signalsArray = nlohmann::json::array();

            nlohmann::json& propertiesObj =
                asyncResp->res.jsonValue["properties"];
            propertiesObj = nlohmann::json::object();

            // if we know we're the only call, build the
            // json directly
            tinyxml2::XMLElement* interface =
                pRoot->FirstChildElement("interface");
            while (interface != nullptr)
            {
                const char* ifaceName = interface->Attribute("name");

                if (ifaceName != nullptr && ifaceName == interfaceName)
                {
                    break;
                }

                interface = interface->NextSiblingElement("interface");
            }
            if (interface == nullptr)
            {
                // if we got to the end of the list and
                // never found a match, throw 404
                asyncResp->res.result(boost::beast::http::status::not_found);
                return;
            }

            tinyxml2::XMLElement* methods =
                interface->FirstChildElement("method");
            while (methods != nullptr)
            {
                nlohmann::json argsArray = nlohmann::json::array();
                tinyxml2::XMLElement* arg = methods->FirstChildElement("arg");
                while (arg != nullptr)
                {
                    nlohmann::json thisArg;
                    for (const char* fieldName : std::array<const char*, 3>{
                             "name", "direction", "type"})
                    {
                        const char* fieldValue = arg->Attribute(fieldName);
                        if (fieldValue != nullptr)
                        {
                            thisArg[fieldName] = fieldValue;
                        }
                    }
                    argsArray.emplace_back(std::move(thisArg));
                    arg = arg->NextSiblingElement("arg");
                }

                const char* name = methods->Attribute("name");
                if (name != nullptr)
                {
                    std::string uri;
                    uri.reserve(14 + processName.size() + objectPath.size() +
                                interfaceName.size() + strlen(name));
                    uri += "/bus/system/";
                    uri += processName;
                    uri += objectPath;
                    uri += "/";
                    uri += interfaceName;
                    uri += "/";
                    uri += name;

                    nlohmann::json::object_t object;
                    object["name"] = name;
                    object["uri"] = std::move(uri);
                    object["args"] = argsArray;

                    methodsArray.emplace_back(std::move(object));
                }
                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)
                {
                    const char* name = arg->Attribute("name");
                    const char* type = arg->Attribute("type");
                    if (name != nullptr && type != nullptr)
                    {
                        argsArray.push_back({
                            {"name", name},
                            {"type", type},
                        });
                    }
                    arg = arg->NextSiblingElement("arg");
                }
                const char* name = signals->Attribute("name");
                if (name != nullptr)
                {
                    nlohmann::json::object_t object;
                    object["name"] = name;
                    object["args"] = argsArray;
                    signalsArray.emplace_back(std::move(object));
                }

                signals = signals->NextSiblingElement("signal");
            }

            tinyxml2::XMLElement* property =
                interface->FirstChildElement("property");
            while (property != nullptr)
            {
                const char* name = property->Attribute("name");
                const char* type = property->Attribute("type");
                if (type != nullptr && name != nullptr)
                {
                    sdbusplus::message_t m =
                        crow::connections::systemBus->new_method_call(
                            processName.c_str(), objectPath.c_str(),
                            "org.freedesktop."
                            "DBus."
                            "Properties",
                            "Get");
                    m.append(interfaceName, name);
                    nlohmann::json& propertyItem = propertiesObj[name];
                    crow::connections::systemBus->async_send(
                        m, [&propertyItem,
                            asyncResp](const boost::system::error_code& ec2,
                                       sdbusplus::message_t& msg) {
                        if (ec2)
                        {
                            return;
                        }

                        convertDBusToJSON("v", msg, propertyItem);
                    });
                }
                property = property->NextSiblingElement("property");
            }
        },
            processName, objectPath, "org.freedesktop.DBus.Introspectable",
            "Introspect");
    }
    else
    {
        if (req.method() != boost::beast::http::verb::post)
        {
            asyncResp->res.result(boost::beast::http::status::not_found);
            return;
        }

        nlohmann::json requestDbusData;
        JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
        if (ret == JsonParseResult::BadContentType)
        {
            setErrorResponse(asyncResp->res,
                             boost::beast::http::status::unsupported_media_type,
                             invalidContentType, unsupportedMediaMsg);
            return;
        }
        if (ret != JsonParseResult::Success)
        {
            setErrorResponse(asyncResp->res,
                             boost::beast::http::status::bad_request,
                             noJsonDesc, badReqMsg);
            return;
        }

        if (!requestDbusData.is_array())
        {
            asyncResp->res.result(boost::beast::http::status::bad_request);
            return;
        }
        auto transaction = std::make_shared<InProgressActionData>(asyncResp);

        transaction->path = objectPath;
        transaction->methodName = methodName;
        transaction->arguments = std::move(requestDbusData);

        findActionOnInterface(transaction, processName);
    }
}

inline void requestRoutes(App& app)
{
    BMCWEB_ROUTE(app, "/bus/")
        .privileges({{"Login"}})
        .methods(boost::beast::http::verb::get)(
            [](const crow::Request&,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
        nlohmann::json::array_t buses;
        nlohmann::json& bus = buses.emplace_back();
        bus["name"] = "system";
        asyncResp->res.jsonValue["busses"] = std::move(buses);
        asyncResp->res.jsonValue["status"] = "ok";
    });

    BMCWEB_ROUTE(app, "/bus/system/")
        .privileges({{"Login"}})
        .methods(boost::beast::http::verb::get)(
            [](const crow::Request&,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
        auto myCallback = [asyncResp](const boost::system::error_code& ec,
                                      std::vector<std::string>& names) {
            if (ec)
            {
                BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
                asyncResp->res.result(
                    boost::beast::http::status::internal_server_error);
            }
            else
            {
                std::ranges::sort(names);
                asyncResp->res.jsonValue["status"] = "ok";
                auto& objectsSub = asyncResp->res.jsonValue["objects"];
                for (const auto& name : names)
                {
                    nlohmann::json::object_t object;
                    object["name"] = name;
                    objectsSub.emplace_back(std::move(object));
                }
            }
        };
        crow::connections::systemBus->async_method_call(
            std::move(myCallback), "org.freedesktop.DBus", "/",
            "org.freedesktop.DBus", "ListNames");
    });

    BMCWEB_ROUTE(app, "/list/")
        .privileges({{"Login"}})
        .methods(boost::beast::http::verb::get)(
            [](const crow::Request&,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
        handleList(asyncResp, "/");
    });

    BMCWEB_ROUTE(app, "/xyz/<path>")
        .privileges({{"Login"}})
        .methods(boost::beast::http::verb::get)(
            [](const crow::Request& req,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
               const std::string& path) {
        std::string objectPath = "/xyz/" + path;
        handleDBusUrl(req, asyncResp, objectPath);
    });

    BMCWEB_ROUTE(app, "/xyz/<path>")
        .privileges({{"ConfigureComponents", "ConfigureManager"}})
        .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
                 boost::beast::http::verb::delete_)(
            [](const crow::Request& req,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
               const std::string& path) {
        std::string objectPath = "/xyz/" + path;
        handleDBusUrl(req, asyncResp, objectPath);
    });

    BMCWEB_ROUTE(app, "/org/<path>")
        .privileges({{"Login"}})
        .methods(boost::beast::http::verb::get)(
            [](const crow::Request& req,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
               const std::string& path) {
        std::string objectPath = "/org/" + path;
        handleDBusUrl(req, asyncResp, objectPath);
    });

    BMCWEB_ROUTE(app, "/org/<path>")
        .privileges({{"ConfigureComponents", "ConfigureManager"}})
        .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
                 boost::beast::http::verb::delete_)(
            [](const crow::Request& req,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
               const std::string& path) {
        std::string objectPath = "/org/" + path;
        handleDBusUrl(req, asyncResp, objectPath);
    });

    BMCWEB_ROUTE(app, "/download/dump/<str>/")
        .privileges({{"ConfigureManager"}})
        .methods(boost::beast::http::verb::get)(
            [](const crow::Request&,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
               const std::string& dumpId) {
        if (!validateFilename(dumpId))
        {
            asyncResp->res.result(boost::beast::http::status::bad_request);
            return;
        }
        std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps");

        loc /= dumpId;

        if (!std::filesystem::exists(loc) ||
            !std::filesystem::is_directory(loc))
        {
            BMCWEB_LOG_ERROR("{}Not found", loc.string());
            asyncResp->res.result(boost::beast::http::status::not_found);
            return;
        }
        std::filesystem::directory_iterator files(loc);

        for (const auto& file : files)
        {
            if (!asyncResp->res.openFile(file))
            {
                continue;
            }

            asyncResp->res.addHeader(boost::beast::http::field::content_type,
                                     "application/octet-stream");

            // Assuming only one dump file will be present in the dump
            // id directory
            std::string dumpFileName = file.path().filename().string();

            // Filename should be in alphanumeric, dot and underscore
            // Its based on phosphor-debug-collector application
            // dumpfile format
            static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
            if (!std::regex_match(dumpFileName, dumpFileRegex))
            {
                BMCWEB_LOG_ERROR("Invalid dump filename {}", dumpFileName);
                asyncResp->res.result(boost::beast::http::status::not_found);
                return;
            }
            std::string contentDispositionParam = "attachment; filename=\"" +
                                                  dumpFileName + "\"";

            asyncResp->res.addHeader(
                boost::beast::http::field::content_disposition,
                contentDispositionParam);

            return;
        }
        asyncResp->res.result(boost::beast::http::status::not_found);
        return;
    });

    BMCWEB_ROUTE(app, "/bus/system/<str>/")
        .privileges({{"Login"}})

        .methods(boost::beast::http::verb::get)(
            [](const crow::Request&,
               const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
               const std::string& connection) {
        introspectObjects(connection, "/", asyncResp);
    });

    BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
        .privileges({{"ConfigureComponents", "ConfigureManager"}})
        .methods(boost::beast::http::verb::get,
                 boost::beast::http::verb::post)(handleBusSystemPost);
}
} // namespace openbmc_mapper
} // namespace crow