1 #pragma once
2 
3 #include "bmcweb_config.h"
4 
5 #include "app.hpp"
6 #include "async_resp.hpp"
7 #include "http_request.hpp"
8 #include "persistent_data.hpp"
9 #include "query.hpp"
10 #include "registries/privilege_registry.hpp"
11 #include "utils/systemd_utils.hpp"
12 
13 #include <tinyxml2.h>
14 
15 #include <nlohmann/json.hpp>
16 
17 namespace redfish
18 {
19 
20 inline std::string
getMetadataPieceForFile(const std::filesystem::path & filename)21     getMetadataPieceForFile(const std::filesystem::path& filename)
22 {
23     std::string xml;
24     tinyxml2::XMLDocument doc;
25     std::string pathStr = filename.string();
26     if (doc.LoadFile(pathStr.c_str()) != tinyxml2::XML_SUCCESS)
27     {
28         BMCWEB_LOG_ERROR("Failed to open XML file {}", pathStr);
29         return "";
30     }
31     xml += std::format("    <edmx:Reference Uri=\"/redfish/v1/schema/{}\">\n",
32                        filename.filename().string());
33     // std::string edmx = "{http://docs.oasis-open.org/odata/ns/edmx}";
34     // std::string edm = "{http://docs.oasis-open.org/odata/ns/edm}";
35     const char* edmx = "edmx:Edmx";
36     for (tinyxml2::XMLElement* edmxNode = doc.FirstChildElement(edmx);
37          edmxNode != nullptr; edmxNode = edmxNode->NextSiblingElement(edmx))
38     {
39         const char* dataServices = "edmx:DataServices";
40         for (tinyxml2::XMLElement* node =
41                  edmxNode->FirstChildElement(dataServices);
42              node != nullptr; node = node->NextSiblingElement(dataServices))
43         {
44             BMCWEB_LOG_DEBUG("Got data service for {}", pathStr);
45             const char* schemaTag = "Schema";
46             for (tinyxml2::XMLElement* schemaNode =
47                      node->FirstChildElement(schemaTag);
48                  schemaNode != nullptr;
49                  schemaNode = schemaNode->NextSiblingElement(schemaTag))
50             {
51                 std::string ns = schemaNode->Attribute("Namespace");
52                 // BMCWEB_LOG_DEBUG("Found namespace {}", ns);
53                 std::string alias;
54                 if (std::string_view(ns).starts_with("RedfishExtensions"))
55                 {
56                     alias = " Alias=\"Redfish\"";
57                 }
58                 xml += std::format(
59                     "        <edmx:Include Namespace=\"{}\"{}/>\n", ns, alias);
60             }
61         }
62     }
63     xml += "    </edmx:Reference>\n";
64     return xml;
65 }
66 
67 inline void
handleMetadataGet(App &,const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)68     handleMetadataGet(App& /*app*/, const crow::Request& /*req*/,
69                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
70 {
71     std::filesystem::path schema("/usr/share/www/redfish/v1/schema");
72     std::error_code ec;
73     auto iter = std::filesystem::directory_iterator(schema, ec);
74     if (ec)
75     {
76         BMCWEB_LOG_ERROR("Failed to open XML folder {}", schema.string());
77         asyncResp->res.result(
78             boost::beast::http::status::internal_server_error);
79         return;
80     }
81     std::string xml;
82 
83     xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
84     xml +=
85         "<edmx:Edmx xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\" Version=\"4.0\">\n";
86     for (const auto& dirEntry : iter)
87     {
88         std::string path = dirEntry.path().filename();
89         if (!std::string_view(path).ends_with("_v1.xml"))
90         {
91             continue;
92         }
93         std::string metadataPiece = getMetadataPieceForFile(dirEntry.path());
94         if (metadataPiece.empty())
95         {
96             asyncResp->res.result(
97                 boost::beast::http::status::internal_server_error);
98             return;
99         }
100         xml += metadataPiece;
101     }
102     xml += "    <edmx:DataServices>\n";
103     xml +=
104         "        <Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"Service\">\n";
105     xml +=
106         "            <EntityContainer Name=\"Service\" Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n";
107     xml += "        </Schema>\n";
108     xml += "    </edmx:DataServices>\n";
109     xml += "</edmx:Edmx>\n";
110 
111     asyncResp->res.addHeader(boost::beast::http::field::content_type,
112                              "application/xml");
113     asyncResp->res.write(std::move(xml));
114 }
115 
requestRoutesMetadata(App & app)116 inline void requestRoutesMetadata(App& app)
117 {
118     BMCWEB_ROUTE(app, "/redfish/v1/$metadata/")
119         .methods(boost::beast::http::verb::get)(
120             std::bind_front(handleMetadataGet, std::ref(app)));
121 }
122 
123 } // namespace redfish
124