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