mumble-voip_mumble/src/mumble/PluginManifest.cpp

125 lines
4.2 KiB
C++

// Copyright 2021-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "PluginManifest.h"
#include <regex>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/SAX/InputSource.h>
#include <Poco/SAX/SAXException.h>
void PluginManifest::parse(std::istream &input) {
Poco::XML::InputSource source(input);
Poco::XML::DOMParser parser;
Poco::AutoPtr< Poco::XML::Document > doc;
try {
doc = parser.parse(&source);
} catch (const Poco::XML::SAXParseException &) {
throw PluginManifestException("Plugin manifest uses malformed XML");
}
Poco::XML::Element *rootElement = doc->documentElement();
if (rootElement->nodeName() != "bundle") {
throw PluginManifestException("Plugin manifest is missing the root \"bundle\" element");
}
if (!rootElement->hasAttribute("version")) {
throw PluginManifestException("Plugin manifest specifies \"bundle\" element without the \"version\" attribute");
}
std::string formatVersion = rootElement->getAttribute("version");
if (formatVersion == "1.0.0") {
parse_v1_0_0(doc);
} else {
throw PluginManifestException(std::string("Plugin manifest uses unknown format version \"") + formatVersion
+ "\"");
}
}
bool PluginManifest::specifiesPluginPath(const std::string &os, const std::string &arch) const {
return m_pluginMap.find({ os, arch }) != m_pluginMap.end();
}
std::string PluginManifest::getPluginPath(const std::string &os, const std::string &arch) const {
auto it = m_pluginMap.find({ os, arch });
if (it == m_pluginMap.end()) {
throw PluginManifestException("PluginManifest: Invalid access to getPluginPath");
}
return it->second;
}
const std::unordered_map< PluginRuntimeSpec, std::string > &PluginManifest::getSpecifiedPluginPaths() const {
return m_pluginMap;
}
void PluginManifest::parse_v1_0_0(Poco::AutoPtr< Poco::XML::Document > document) {
Poco::XML::Element *assets = document->documentElement()->getChildElement("assets");
if (!assets) {
throw PluginManifestException("Plugin manifest does not contain the \"assets\" element");
}
Poco::AutoPtr< Poco::XML::NodeList > pluginNodes = assets->getElementsByTagName("plugin");
for (std::size_t i = 0; i < pluginNodes->length(); ++i) {
Poco::XML::Element *current =
dynamic_cast< Poco::XML::Element * >(pluginNodes->item(static_cast< unsigned long >(i)));
if (!current) {
throw PluginManifestException("Plugin manifest uses \"plugin\" node of unexpected type");
}
std::string binaryPath = current->innerText();
std::string os = current->getAttribute("os");
std::string arch = current->getAttribute("arch");
if (binaryPath.empty()) {
throw PluginManifestException("Plugin manifest declares \"plugin\" element with empty path");
}
if (os.empty()) {
throw PluginManifestException("Plugin manifest declares \"plugin\" element without \"os\" attribute");
}
if (arch.empty()) {
throw PluginManifestException("Plugin manifest declares \"plugin\" element without \"arch\" attribute");
}
PluginRuntimeSpec spec = { std::move(os), std::move(arch) };
m_pluginMap.insert({ std::move(spec), std::move(binaryPath) });
}
Poco::XML::Element *nameElement = document->documentElement()->getChildElement("name");
if (!nameElement) {
throw PluginManifestException("Plugin manifest does not specify the \"name\" element");
}
Poco::XML::Element *versionElement = document->documentElement()->getChildElement("version");
if (!versionElement) {
throw PluginManifestException("Plugin manifest does not specify the \"version\" element");
}
m_name = nameElement->innerText();
m_version = versionElement->innerText();
if (m_name.empty()) {
throw PluginManifestException("Plugin manifest specifies empty name");
}
if (m_version.empty()) {
throw PluginManifestException("Plugin manifest specifies empty version");
}
std::regex versionRegex("\\d+\\.\\d+\\.\\d+");
if (!std::regex_match(m_version, versionRegex)) {
throw PluginManifestException(
"Plugin manifest specifies version that does not follow the format major.minor.path");
}
}