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