1 /*
2 Copyright (c) 2018 Intel Corporation
3 
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8       http://www.apache.org/licenses/LICENSE-2.0
9 
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 #pragma once
17 
18 #include "account_service.hpp"
19 #include "app.hpp"
20 #include "async_resp.hpp"
21 #include "credential_pipe.hpp"
22 #include "dbus_utility.hpp"
23 #include "generated/enums/virtual_media.hpp"
24 #include "query.hpp"
25 #include "registries/privilege_registry.hpp"
26 #include "utils/json_utils.hpp"
27 
28 #include <boost/url/format.hpp>
29 #include <boost/url/url_view.hpp>
30 #include <boost/url/url_view_base.hpp>
31 
32 #include <array>
33 #include <ranges>
34 #include <string_view>
35 
36 namespace redfish
37 {
38 
39 enum class VmMode
40 {
41     Invalid,
42     Legacy,
43     Proxy
44 };
45 
parseObjectPathAndGetMode(const sdbusplus::message::object_path & itemPath,const std::string & resName)46 inline VmMode parseObjectPathAndGetMode(
47     const sdbusplus::message::object_path& itemPath, const std::string& resName)
48 {
49     std::string thisPath = itemPath.filename();
50     BMCWEB_LOG_DEBUG("Filename: {}, ThisPath: {}", itemPath.str, thisPath);
51 
52     if (thisPath.empty())
53     {
54         return VmMode::Invalid;
55     }
56 
57     if (thisPath != resName)
58     {
59         return VmMode::Invalid;
60     }
61 
62     auto mode = itemPath.parent_path();
63     auto type = mode.parent_path();
64 
65     if (mode.filename().empty() || type.filename().empty())
66     {
67         return VmMode::Invalid;
68     }
69 
70     if (type.filename() != "VirtualMedia")
71     {
72         return VmMode::Invalid;
73     }
74     std::string modeStr = mode.filename();
75     if (modeStr == "Legacy")
76     {
77         return VmMode::Legacy;
78     }
79     if (modeStr == "Proxy")
80     {
81         return VmMode::Proxy;
82     }
83     return VmMode::Invalid;
84 }
85 
86 using CheckItemHandler =
87     std::function<void(const std::string& service, const std::string& resName,
88                        const std::shared_ptr<bmcweb::AsyncResp>&,
89                        const std::pair<sdbusplus::message::object_path,
90                                        dbus::utility::DBusInterfacesMap>&)>;
91 
92 inline void
findAndParseObject(const std::string & service,const std::string & resName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,CheckItemHandler && handler)93     findAndParseObject(const std::string& service, const std::string& resName,
94                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
95                        CheckItemHandler&& handler)
96 {
97     sdbusplus::message::object_path path("/xyz/openbmc_project/VirtualMedia");
98     dbus::utility::getManagedObjects(
99         service, path,
100         [service, resName, asyncResp, handler = std::move(handler)](
101             const boost::system::error_code& ec,
102             const dbus::utility::ManagedObjectType& subtree) {
103             if (ec)
104             {
105                 BMCWEB_LOG_DEBUG("DBUS response error");
106 
107                 return;
108             }
109 
110             for (const auto& item : subtree)
111             {
112                 VmMode mode = parseObjectPathAndGetMode(item.first, resName);
113                 if (mode != VmMode::Invalid)
114                 {
115                     handler(service, resName, asyncResp, item);
116                     return;
117                 }
118             }
119 
120             BMCWEB_LOG_DEBUG("Parent item not found");
121             asyncResp->res.result(boost::beast::http::status::not_found);
122         });
123 }
124 
125 /**
126  * @brief Function extracts transfer protocol name from URI.
127  */
getTransferProtocolTypeFromUri(const std::string & imageUri)128 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri)
129 {
130     boost::system::result<boost::urls::url_view> url =
131         boost::urls::parse_uri(imageUri);
132     if (!url)
133     {
134         return "None";
135     }
136     std::string_view scheme = url->scheme();
137     if (scheme == "smb")
138     {
139         return "CIFS";
140     }
141     if (scheme == "https")
142     {
143         return "HTTPS";
144     }
145 
146     return "None";
147 }
148 
149 /**
150  * @brief Read all known properties from VM object interfaces
151  */
152 inline void
vmParseInterfaceObject(const dbus::utility::DBusInterfacesMap & interfaces,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)153     vmParseInterfaceObject(const dbus::utility::DBusInterfacesMap& interfaces,
154                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
155 {
156     for (const auto& [interface, values] : interfaces)
157     {
158         if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint")
159         {
160             for (const auto& [property, value] : values)
161             {
162                 if (property == "EndpointId")
163                 {
164                     const std::string* endpointIdValue =
165                         std::get_if<std::string>(&value);
166                     if (endpointIdValue == nullptr)
167                     {
168                         continue;
169                     }
170                     if (!endpointIdValue->empty())
171                     {
172                         // Proxy mode
173                         asyncResp->res
174                             .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
175                             *endpointIdValue;
176                         asyncResp->res.jsonValue["TransferProtocolType"] =
177                             "OEM";
178                     }
179                 }
180                 if (property == "ImageURL")
181                 {
182                     const std::string* imageUrlValue =
183                         std::get_if<std::string>(&value);
184                     if (imageUrlValue != nullptr && !imageUrlValue->empty())
185                     {
186                         std::filesystem::path filePath = *imageUrlValue;
187                         if (!filePath.has_filename())
188                         {
189                             // this will handle https share, which not
190                             // necessarily has to have filename given.
191                             asyncResp->res.jsonValue["ImageName"] = "";
192                         }
193                         else
194                         {
195                             asyncResp->res.jsonValue["ImageName"] =
196                                 filePath.filename();
197                         }
198 
199                         asyncResp->res.jsonValue["Image"] = *imageUrlValue;
200                         asyncResp->res.jsonValue["TransferProtocolType"] =
201                             getTransferProtocolTypeFromUri(*imageUrlValue);
202 
203                         asyncResp->res.jsonValue["ConnectedVia"] =
204                             virtual_media::ConnectedVia::URI;
205                     }
206                 }
207                 if (property == "WriteProtected")
208                 {
209                     const bool* writeProtectedValue = std::get_if<bool>(&value);
210                     if (writeProtectedValue != nullptr)
211                     {
212                         asyncResp->res.jsonValue["WriteProtected"] =
213                             *writeProtectedValue;
214                     }
215                 }
216             }
217         }
218         if (interface == "xyz.openbmc_project.VirtualMedia.Process")
219         {
220             for (const auto& [property, value] : values)
221             {
222                 if (property == "Active")
223                 {
224                     const bool* activeValue = std::get_if<bool>(&value);
225                     if (activeValue == nullptr)
226                     {
227                         BMCWEB_LOG_DEBUG("Value Active not found");
228                         return;
229                     }
230                     asyncResp->res.jsonValue["Inserted"] = *activeValue;
231 
232                     if (*activeValue)
233                     {
234                         asyncResp->res.jsonValue["ConnectedVia"] =
235                             virtual_media::ConnectedVia::Applet;
236                     }
237                 }
238             }
239         }
240     }
241 }
242 
243 /**
244  * @brief Fill template for Virtual Media Item.
245  */
vmItemTemplate(const std::string & name,const std::string & resName)246 inline nlohmann::json vmItemTemplate(const std::string& name,
247                                      const std::string& resName)
248 {
249     nlohmann::json item;
250     item["@odata.id"] = boost::urls::format(
251         "/redfish/v1/Managers/{}/VirtualMedia/{}", name, resName);
252 
253     item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
254     item["Name"] = "Virtual Removable Media";
255     item["Id"] = resName;
256     item["WriteProtected"] = true;
257     item["ConnectedVia"] = virtual_media::ConnectedVia::NotConnected;
258     item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"});
259     item["TransferMethod"] = virtual_media::TransferMethod::Stream;
260     item["Oem"]["OpenBMC"]["@odata.type"] =
261         "#OpenBMCVirtualMedia.v1_0_0.VirtualMedia";
262     item["Oem"]["OpenBMC"]["@odata.id"] = boost::urls::format(
263         "/redfish/v1/Managers/{}/VirtualMedia/{}#/Oem/OpenBMC", name, resName);
264 
265     return item;
266 }
267 
268 /**
269  *  @brief Fills collection data
270  */
getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & name)271 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
272                               const std::string& service,
273                               const std::string& name)
274 {
275     BMCWEB_LOG_DEBUG("Get available Virtual Media resources.");
276     sdbusplus::message::object_path objPath(
277         "/xyz/openbmc_project/VirtualMedia");
278     dbus::utility::getManagedObjects(
279         service, objPath,
280         [name, asyncResp{std::move(asyncResp)}](
281             const boost::system::error_code& ec,
282             const dbus::utility::ManagedObjectType& subtree) {
283             if (ec)
284             {
285                 BMCWEB_LOG_DEBUG("DBUS response error");
286                 return;
287             }
288             nlohmann::json& members = asyncResp->res.jsonValue["Members"];
289             members = nlohmann::json::array();
290 
291             for (const auto& object : subtree)
292             {
293                 nlohmann::json item;
294                 std::string path = object.first.filename();
295                 if (path.empty())
296                 {
297                     continue;
298                 }
299 
300                 item["@odata.id"] = boost::urls::format(
301                     "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path);
302                 members.emplace_back(std::move(item));
303             }
304             asyncResp->res.jsonValue["Members@odata.count"] = members.size();
305         });
306 }
307 
308 inline void
afterGetVmData(const std::string & name,const std::string &,const std::string & resName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::pair<sdbusplus::message::object_path,dbus::utility::DBusInterfacesMap> & item)309     afterGetVmData(const std::string& name, const std::string& /*service*/,
310                    const std::string& resName,
311                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
312                    const std::pair<sdbusplus::message::object_path,
313                                    dbus::utility::DBusInterfacesMap>& item)
314 {
315     VmMode mode = parseObjectPathAndGetMode(item.first, resName);
316     if (mode == VmMode::Invalid)
317     {
318         return;
319     }
320 
321     asyncResp->res.jsonValue = vmItemTemplate(name, resName);
322 
323     // Check if dbus path is Legacy type
324     if (mode == VmMode::Legacy)
325     {
326         asyncResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
327                                 ["target"] = boost::urls::format(
328             "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.InsertMedia",
329             name, resName);
330     }
331 
332     vmParseInterfaceObject(item.second, asyncResp);
333 
334     asyncResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
335                             ["target"] = boost::urls::format(
336         "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.EjectMedia",
337         name, resName);
338 }
339 
340 /**
341  *  @brief Fills data for specific resource
342  */
getVmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,const std::string & resName)343 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
344                       const std::string& service, const std::string& name,
345                       const std::string& resName)
346 {
347     BMCWEB_LOG_DEBUG("Get Virtual Media resource data.");
348 
349     findAndParseObject(service, resName, asyncResp,
350                        std::bind_front(afterGetVmData, name));
351 }
352 
353 /**
354  * @brief Transfer protocols supported for InsertMedia action.
355  *
356  */
357 enum class TransferProtocol
358 {
359     https,
360     smb,
361     invalid
362 };
363 
364 /**
365  * @brief Function extracts transfer protocol type from URI.
366  *
367  */
368 inline std::optional<TransferProtocol>
getTransferProtocolFromUri(const boost::urls::url_view_base & imageUri)369     getTransferProtocolFromUri(const boost::urls::url_view_base& imageUri)
370 {
371     std::string_view scheme = imageUri.scheme();
372     if (scheme == "smb")
373     {
374         return TransferProtocol::smb;
375     }
376     if (scheme == "https")
377     {
378         return TransferProtocol::https;
379     }
380     if (!scheme.empty())
381     {
382         return TransferProtocol::invalid;
383     }
384 
385     return {};
386 }
387 
388 /**
389  * @brief Function convert transfer protocol from string param.
390  *
391  */
getTransferProtocolFromParam(const std::optional<std::string> & transferProtocolType)392 inline std::optional<TransferProtocol> getTransferProtocolFromParam(
393     const std::optional<std::string>& transferProtocolType)
394 {
395     if (!transferProtocolType)
396     {
397         return {};
398     }
399 
400     if (*transferProtocolType == "CIFS")
401     {
402         return TransferProtocol::smb;
403     }
404 
405     if (*transferProtocolType == "HTTPS")
406     {
407         return TransferProtocol::https;
408     }
409 
410     return TransferProtocol::invalid;
411 }
412 
413 /**
414  * @brief Function extends URI with transfer protocol type.
415  *
416  */
getUriWithTransferProtocol(const std::string & imageUri,const TransferProtocol & transferProtocol)417 inline std::string getUriWithTransferProtocol(
418     const std::string& imageUri, const TransferProtocol& transferProtocol)
419 {
420     if (transferProtocol == TransferProtocol::smb)
421     {
422         return "smb://" + imageUri;
423     }
424 
425     if (transferProtocol == TransferProtocol::https)
426     {
427         return "https://" + imageUri;
428     }
429 
430     return imageUri;
431 }
432 
433 struct InsertMediaActionParams
434 {
435     std::optional<std::string> imageUrl;
436     std::optional<std::string> userName;
437     std::optional<std::string> password;
438     std::optional<std::string> transferMethod;
439     std::optional<std::string> transferProtocolType;
440     std::optional<bool> writeProtected = true;
441     std::optional<bool> inserted;
442 };
443 
444 /**
445  * @brief Function transceives data with dbus directly.
446  *
447  * All BMC state properties will be retrieved before sending reset request.
448  */
doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,const std::string & imageUrl,bool rw,std::string && userName,std::string && password)449 inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
450                             const std::string& service, const std::string& name,
451                             const std::string& imageUrl, bool rw,
452                             std::string&& userName, std::string&& password)
453 {
454     int fd = -1;
455     std::shared_ptr<CredentialsPipe> secretPipe;
456     if (!userName.empty() || !password.empty())
457     {
458         // Payload must contain data + NULL delimiters
459         constexpr const size_t secretLimit = 1024;
460         if (userName.size() + password.size() + 2 > secretLimit)
461         {
462             BMCWEB_LOG_ERROR("Credentials too long to handle");
463             messages::unrecognizedRequestBody(asyncResp->res);
464             return;
465         }
466 
467         // Open pipe
468         secretPipe = std::make_shared<CredentialsPipe>(
469             crow::connections::systemBus->get_io_context());
470         fd = secretPipe->releaseFd();
471 
472         // Pass secret over pipe
473         secretPipe->asyncWrite(
474             std::move(userName), std::move(password),
475             [asyncResp,
476              secretPipe](const boost::system::error_code& ec, std::size_t) {
477                 if (ec)
478                 {
479                     BMCWEB_LOG_ERROR("Failed to pass secret: {}", ec);
480                     messages::internalError(asyncResp->res);
481                 }
482             });
483     }
484 
485     dbus::utility::DbusVariantType unixFd(
486         std::in_place_type<sdbusplus::message::unix_fd>, fd);
487 
488     sdbusplus::message::object_path path(
489         "/xyz/openbmc_project/VirtualMedia/Legacy");
490     path /= name;
491     crow::connections::systemBus->async_method_call(
492         [asyncResp,
493          secretPipe](const boost::system::error_code& ec, bool success) {
494             if (ec)
495             {
496                 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
497                 messages::internalError(asyncResp->res);
498                 return;
499             }
500             if (!success)
501             {
502                 BMCWEB_LOG_ERROR("Service responded with error");
503                 messages::internalError(asyncResp->res);
504             }
505         },
506         service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount",
507         imageUrl, rw, unixFd);
508 }
509 
510 /**
511  * @brief Function validate parameters of insert media request.
512  *
513  */
validateParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & resName,InsertMediaActionParams & actionParams)514 inline void validateParams(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
515                            const std::string& service,
516                            const std::string& resName,
517                            InsertMediaActionParams& actionParams)
518 {
519     BMCWEB_LOG_DEBUG("Validation started");
520     // required param imageUrl must not be empty
521     if (!actionParams.imageUrl)
522     {
523         BMCWEB_LOG_ERROR("Request action parameter Image is empty.");
524 
525         messages::propertyValueFormatError(asyncResp->res, "<empty>", "Image");
526 
527         return;
528     }
529 
530     // optional param inserted must be true
531     if (actionParams.inserted && !*actionParams.inserted)
532     {
533         BMCWEB_LOG_ERROR(
534             "Request action optional parameter Inserted must be true.");
535 
536         messages::actionParameterNotSupported(asyncResp->res, "Inserted",
537                                               "InsertMedia");
538 
539         return;
540     }
541 
542     // optional param transferMethod must be stream
543     if (actionParams.transferMethod &&
544         (*actionParams.transferMethod != "Stream"))
545     {
546         BMCWEB_LOG_ERROR("Request action optional parameter "
547                          "TransferMethod must be Stream.");
548 
549         messages::actionParameterNotSupported(asyncResp->res, "TransferMethod",
550                                               "InsertMedia");
551 
552         return;
553     }
554     boost::system::result<boost::urls::url_view> url =
555         boost::urls::parse_uri(*actionParams.imageUrl);
556     if (!url)
557     {
558         messages::actionParameterValueFormatError(
559             asyncResp->res, *actionParams.imageUrl, "Image", "InsertMedia");
560         return;
561     }
562     std::optional<TransferProtocol> uriTransferProtocolType =
563         getTransferProtocolFromUri(*url);
564 
565     std::optional<TransferProtocol> paramTransferProtocolType =
566         getTransferProtocolFromParam(actionParams.transferProtocolType);
567 
568     // ImageUrl does not contain valid protocol type
569     if (uriTransferProtocolType &&
570         *uriTransferProtocolType == TransferProtocol::invalid)
571     {
572         BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
573                          "contain specified protocol type from list: "
574                          "(smb, https).");
575 
576         messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
577 
578         return;
579     }
580 
581     // transferProtocolType should contain value from list
582     if (paramTransferProtocolType &&
583         *paramTransferProtocolType == TransferProtocol::invalid)
584     {
585         BMCWEB_LOG_ERROR("Request action parameter TransferProtocolType "
586                          "must be provided with value from list: "
587                          "(CIFS, HTTPS).");
588 
589         messages::propertyValueNotInList(
590             asyncResp->res, actionParams.transferProtocolType.value_or(""),
591             "TransferProtocolType");
592         return;
593     }
594 
595     // valid transfer protocol not provided either with URI nor param
596     if (!uriTransferProtocolType && !paramTransferProtocolType)
597     {
598         BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
599                          "contain specified protocol type or param "
600                          "TransferProtocolType must be provided.");
601 
602         messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
603 
604         return;
605     }
606 
607     // valid transfer protocol provided both with URI and param
608     if (paramTransferProtocolType && uriTransferProtocolType)
609     {
610         // check if protocol is the same for URI and param
611         if (*paramTransferProtocolType != *uriTransferProtocolType)
612         {
613             BMCWEB_LOG_ERROR("Request action parameter "
614                              "TransferProtocolType must  contain the "
615                              "same protocol type as protocol type "
616                              "provided with param imageUrl.");
617 
618             messages::actionParameterValueTypeError(
619                 asyncResp->res, actionParams.transferProtocolType.value_or(""),
620                 "TransferProtocolType", "InsertMedia");
621 
622             return;
623         }
624     }
625 
626     // validation passed, add protocol to URI if needed
627     if (!uriTransferProtocolType && paramTransferProtocolType)
628     {
629         actionParams.imageUrl = getUriWithTransferProtocol(
630             *actionParams.imageUrl, *paramTransferProtocolType);
631     }
632 
633     if (!actionParams.userName)
634     {
635         actionParams.userName = "";
636     }
637 
638     if (!actionParams.password)
639     {
640         actionParams.password = "";
641     }
642 
643     doMountVmLegacy(asyncResp, service, resName, *actionParams.imageUrl,
644                     !(actionParams.writeProtected.value_or(false)),
645                     std::move(*actionParams.userName),
646                     std::move(*actionParams.password));
647 }
648 
649 /**
650  * @brief Function transceives data with dbus directly.
651  *
652  * All BMC state properties will be retrieved before sending reset request.
653  */
doEjectAction(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,bool legacy)654 inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
655                           const std::string& service, const std::string& name,
656                           bool legacy)
657 {
658     // Legacy mount requires parameter with image
659     if (legacy)
660     {
661         crow::connections::systemBus->async_method_call(
662             [asyncResp](const boost::system::error_code& ec) {
663                 if (ec)
664                 {
665                     BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
666 
667                     messages::internalError(asyncResp->res);
668                     return;
669                 }
670             },
671             service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
672             "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
673     }
674     else // proxy
675     {
676         crow::connections::systemBus->async_method_call(
677             [asyncResp](const boost::system::error_code& ec) {
678                 if (ec)
679                 {
680                     BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
681 
682                     messages::internalError(asyncResp->res);
683                     return;
684                 }
685             },
686             service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
687             "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
688     }
689 }
690 
handleManagersVirtualMediaActionInsertPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)691 inline void handleManagersVirtualMediaActionInsertPost(
692     crow::App& app, const crow::Request& req,
693     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
694     const std::string& name, const std::string& resName)
695 {
696     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
697     {
698         return;
699     }
700 
701     constexpr std::string_view action = "VirtualMedia.InsertMedia";
702     if (name != "bmc")
703     {
704         messages::resourceNotFound(asyncResp->res, action, resName);
705 
706         return;
707     }
708     InsertMediaActionParams actionParams;
709 
710     // Read obligatory parameters (url of image)
711     if (!json_util::readJsonAction(
712             req, asyncResp->res, "Image", actionParams.imageUrl,
713             "WriteProtected", actionParams.writeProtected, "UserName",
714             actionParams.userName, "Password", actionParams.password,
715             "Inserted", actionParams.inserted, "TransferMethod",
716             actionParams.transferMethod, "TransferProtocolType",
717             actionParams.transferProtocolType))
718     {
719         return;
720     }
721 
722     dbus::utility::getDbusObject(
723         "/xyz/openbmc_project/VirtualMedia", {},
724         [asyncResp, action, actionParams,
725          resName](const boost::system::error_code& ec,
726                   const dbus::utility::MapperGetObject& getObjectType) mutable {
727             if (ec)
728             {
729                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
730                 messages::resourceNotFound(asyncResp->res, action, resName);
731 
732                 return;
733             }
734 
735             std::string service = getObjectType.begin()->first;
736             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
737 
738             sdbusplus::message::object_path path(
739                 "/xyz/openbmc_project/VirtualMedia");
740             dbus::utility::getManagedObjects(
741                 service, path,
742                 [service, resName, action, actionParams, asyncResp](
743                     const boost::system::error_code& ec2,
744                     const dbus::utility::ManagedObjectType& subtree) mutable {
745                     if (ec2)
746                     {
747                         // Not possible in proxy mode
748                         BMCWEB_LOG_DEBUG("InsertMedia not "
749                                          "allowed in proxy mode");
750                         messages::resourceNotFound(asyncResp->res, action,
751                                                    resName);
752 
753                         return;
754                     }
755                     for (const auto& object : subtree)
756                     {
757                         VmMode mode =
758                             parseObjectPathAndGetMode(object.first, resName);
759                         if (mode == VmMode::Legacy)
760                         {
761                             validateParams(asyncResp, service, resName,
762                                            actionParams);
763 
764                             return;
765                         }
766                     }
767                     BMCWEB_LOG_DEBUG("Parent item not found");
768                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
769                                                resName);
770                 });
771         });
772 }
773 
handleManagersVirtualMediaActionEject(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerName,const std::string & resName)774 inline void handleManagersVirtualMediaActionEject(
775     crow::App& app, const crow::Request& req,
776     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
777     const std::string& managerName, const std::string& resName)
778 {
779     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
780     {
781         return;
782     }
783 
784     constexpr std::string_view action = "VirtualMedia.EjectMedia";
785     if (managerName != "bmc")
786     {
787         messages::resourceNotFound(asyncResp->res, action, resName);
788 
789         return;
790     }
791 
792     dbus::utility::getDbusObject(
793         "/xyz/openbmc_project/VirtualMedia", {},
794         [asyncResp, action,
795          resName](const boost::system::error_code& ec2,
796                   const dbus::utility::MapperGetObject& getObjectType) {
797             if (ec2)
798             {
799                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}",
800                                  ec2);
801                 messages::internalError(asyncResp->res);
802 
803                 return;
804             }
805             std::string service = getObjectType.begin()->first;
806             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
807 
808             sdbusplus::message::object_path path(
809                 "/xyz/openbmc_project/VirtualMedia");
810             dbus::utility::getManagedObjects(
811                 service, path,
812                 [resName, service, action,
813                  asyncResp](const boost::system::error_code& ec,
814                             const dbus::utility::ManagedObjectType& subtree) {
815                     if (ec)
816                     {
817                         BMCWEB_LOG_ERROR("ObjectMapper : No Service found");
818                         messages::resourceNotFound(asyncResp->res, action,
819                                                    resName);
820                         return;
821                     }
822 
823                     for (const auto& object : subtree)
824                     {
825                         VmMode mode =
826                             parseObjectPathAndGetMode(object.first, resName);
827                         if (mode != VmMode::Invalid)
828                         {
829                             doEjectAction(asyncResp, service, resName,
830                                           mode == VmMode::Legacy);
831                             return;
832                         }
833                     }
834                     BMCWEB_LOG_DEBUG("Parent item not found");
835                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
836                                                resName);
837                 });
838         });
839 }
840 
handleManagersVirtualMediaCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name)841 inline void handleManagersVirtualMediaCollectionGet(
842     crow::App& app, const crow::Request& req,
843     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
844     const std::string& name)
845 {
846     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
847     {
848         return;
849     }
850     if (name != "bmc")
851     {
852         messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
853 
854         return;
855     }
856 
857     asyncResp->res.jsonValue["@odata.type"] =
858         "#VirtualMediaCollection.VirtualMediaCollection";
859     asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
860     asyncResp->res.jsonValue["@odata.id"] =
861         boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name);
862 
863     dbus::utility::getDbusObject(
864         "/xyz/openbmc_project/VirtualMedia", {},
865         [asyncResp, name](const boost::system::error_code& ec,
866                           const dbus::utility::MapperGetObject& getObjectType) {
867             if (ec)
868             {
869                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
870                 messages::internalError(asyncResp->res);
871 
872                 return;
873             }
874             std::string service = getObjectType.begin()->first;
875             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
876 
877             getVmResourceList(asyncResp, service, name);
878         });
879 }
880 
881 inline void
handleVirtualMediaGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)882     handleVirtualMediaGet(crow::App& app, const crow::Request& req,
883                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
884                           const std::string& name, const std::string& resName)
885 {
886     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
887     {
888         return;
889     }
890     if (name != "bmc")
891     {
892         messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
893 
894         return;
895     }
896 
897     dbus::utility::getDbusObject(
898         "/xyz/openbmc_project/VirtualMedia", {},
899         [asyncResp, name,
900          resName](const boost::system::error_code& ec,
901                   const dbus::utility::MapperGetObject& getObjectType) {
902             if (ec)
903             {
904                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
905                 messages::internalError(asyncResp->res);
906 
907                 return;
908             }
909             std::string service = getObjectType.begin()->first;
910             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
911 
912             getVmData(asyncResp, service, name, resName);
913         });
914 }
915 
requestNBDVirtualMediaRoutes(App & app)916 inline void requestNBDVirtualMediaRoutes(App& app)
917 {
918     BMCWEB_ROUTE(
919         app,
920         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
921         .privileges(redfish::privileges::postVirtualMedia)
922         .methods(boost::beast::http::verb::post)(std::bind_front(
923             handleManagersVirtualMediaActionInsertPost, std::ref(app)));
924 
925     BMCWEB_ROUTE(
926         app,
927         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
928         .privileges(redfish::privileges::postVirtualMedia)
929         .methods(boost::beast::http::verb::post)(std::bind_front(
930             handleManagersVirtualMediaActionEject, std::ref(app)));
931 
932     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
933         .privileges(redfish::privileges::getVirtualMediaCollection)
934         .methods(boost::beast::http::verb::get)(std::bind_front(
935             handleManagersVirtualMediaCollectionGet, std::ref(app)));
936 
937     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
938         .privileges(redfish::privileges::getVirtualMedia)
939         .methods(boost::beast::http::verb::get)(
940             std::bind_front(handleVirtualMediaGet, std::ref(app)));
941 }
942 
943 } // namespace redfish
944