repoindex/alpm/config.cpp

364 lines
16 KiB
C++

#include "./config.h"
#include "./manager.h"
#include <c++utilities/conversion/stringconversion.h>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <iostream>
using namespace std;
using namespace ApplicationUtilities;
using namespace ConversionUtilities;
using namespace ChronoUtilities;
namespace RepoIndex {
bool useShSyntax = false;
const char *shchar = "";
/*!
* \class Alpm::Config
* \brief The ConfigArgs class bundles the command line arguments for the application.
*/
/*!
* \brief Initializes the command line arguments for the configuration of the application.
*/
ConfigArgs::ConfigArgs(ArgumentParser &parser) :
helpArg(parser),
buildOrderArg("build-order", "b", "calculates the build order to build the specified packages"),
addSourceOnlyDepsArg("add-src-only-deps", "a", "adds source-only dependencies to build chain"),
requireSourcesArg("require-sources", "q", "requires specified packages to be from a source repository"),
serverArg("server", "s", "runs a websocket server providing the web interface with information"),
upgradeLookupArg("upgrade-lookup", "u", "checks available upgrades for the specified repository"),
mingwBundleArg("mingw-w64-bundle", "m", "creates an archive with the runtime-relevant files from the specified mingw-w64-packages and their dependencies"),
repoindexConfArg("repoindex-conf", "c", "specifies the path of the repo index config file (default is /etc/repoindex.conf"),
rootdirArg("root-dir", "r", "specifies the root directory (default is /)"),
dbpathArg("db-path", "d", "specifies the pacman database path (default is /var/lib/pacman)"),
pacmanConfArg("pacman-conf", "p", "specifies the path of the pacman config file (default is /etc/pacman.conf"),
websocketAddrArg("addr", string(), "specifies the listening address for the websocket server, default is 127.0.0.1"),
websocketPortArg("port", string(), "specifies the listening port for the websocket server, default is 1234"),
certFileArg("cert-file", string(), "specifies the SSL certificate"),
keyFileArg("key-file", string(), "specifies the private SSL key"),
insecureArg("insecure", string(), "forces the server to run in insecure mode"),
aurArg("aur", string(), "enables/disables AUR queries"),
verboseArg("verbose", "v", "be verbose"),
outputFileArg("output-file", "f", "specifies the output file"),
targetDirArg("target-dir", "t", "the directory to store the target archive"),
targetNameArg("target-name", "n", "specifies the name of the target archive"),
targetFormatArg("target-format", "e", "specifies the format of the target archive"),
iconThemesArg("icon-packages", "i", "specifies the names of the icon packages to include"),
defaultIconThemeArg("default-icon-theme", string(), "specifies the name of the default icon theme (should be included in --icon-packages)"),
extraPackagesArg("extra-packages", string(), "specifies extra packages to be included"),
cacheDirArg("cache-dir", string(), "specifies the cache directory (default is /var/cache/repoindex)"),
storageDirArg("storage-dir", string(), "specifies the storage directory (default is /var/lib/repoindex)"),
shSyntaxArg("sh-syntax", string(), "prints the output using shell syntax: export REPOINDEX_RESULTS=('res1' 'res2' 'res3') or export REPOINDEX_ERROR='some error message'"),
repoArg("repo", string(), "specifies the repository")
{
const initializer_list<string> pathValueName = {"path"};
const initializer_list<string> pkgValueNames = {"package 1", "package 2", "package 3"};
buildOrderArg.setDenotesOperation(true);
buildOrderArg.setRequiredValueCount(-1);
buildOrderArg.setValueNames(pkgValueNames);
requireSourcesArg.setCombinable(true);
serverArg.setDenotesOperation(true);
upgradeLookupArg.setDenotesOperation(true);
upgradeLookupArg.setRequiredValueCount(1);
upgradeLookupArg.setValueNames({"repo"});
mingwBundleArg.setDenotesOperation(true);
mingwBundleArg.setRequiredValueCount(-1);
mingwBundleArg.setValueNames(pkgValueNames);
repoindexConfArg.setCombinable(true);
repoindexConfArg.setValueNames(pathValueName);
repoindexConfArg.setRequiredValueCount(1);
rootdirArg.setCombinable(true);
rootdirArg.setValueNames({"directory"});
rootdirArg.setRequiredValueCount(1);
dbpathArg.setCombinable(true);
dbpathArg.setValueNames(pathValueName);
dbpathArg.setRequiredValueCount(1);
pacmanConfArg.setCombinable(true);
pacmanConfArg.setValueNames(pathValueName);
pacmanConfArg.setRequiredValueCount(1);
websocketAddrArg.setCombinable(true);
websocketAddrArg.setValueNames({"IP address"});
websocketAddrArg.setRequiredValueCount(1);
websocketPortArg.setCombinable(true);
websocketPortArg.setValueNames({"port number"});
websocketPortArg.setRequiredValueCount(1);
certFileArg.setCombinable(true);
certFileArg.setValueNames(pathValueName);
certFileArg.setRequiredValueCount(1);
keyFileArg.setCombinable(true);
keyFileArg.setValueNames(pathValueName);
keyFileArg.setRequiredValueCount(1);
insecureArg.setCombinable(true);
aurArg.setCombinable(true);
aurArg.setRequiredValueCount(1);
aurArg.setValueNames({"enabled/disabled"});
verboseArg.setCombinable(true);
outputFileArg.setCombinable(true);
outputFileArg.setRequired(true);
outputFileArg.setRequiredValueCount(1);
outputFileArg.setValueNames(pathValueName);
targetDirArg.setCombinable(true);
targetDirArg.setRequiredValueCount(1);
targetDirArg.setValueNames(pathValueName);
targetNameArg.setCombinable(true);
targetNameArg.setRequired(true);
targetNameArg.setRequiredValueCount(1);
targetNameArg.setValueNames({"name"});
targetFormatArg.setCombinable(true);
targetFormatArg.setRequiredValueCount(1);
targetFormatArg.setValueNames({"zip/7z/tar.gz/tar.bz/tar.xz"});
iconThemesArg.setCombinable(true);
iconThemesArg.setRequiredValueCount(-1);
iconThemesArg.setValueNames(pkgValueNames);
defaultIconThemeArg.setCombinable(true);
defaultIconThemeArg.setRequiredValueCount(1);
defaultIconThemeArg.setValueNames({"theme name"});
extraPackagesArg.setCombinable(true);
extraPackagesArg.setRequiredValueCount(-1);
extraPackagesArg.setValueNames(pkgValueNames);
shSyntaxArg.setCombinable(true);
repoArg.setRequiredValueCount(1);
repoArg.setValueNames({"repo name"});
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &websocketAddrArg, &websocketPortArg, &insecureArg, &aurArg, &shSyntaxArg});
upgradeLookupArg.setSecondaryArguments({&shSyntaxArg});
buildOrderArg.setSecondaryArguments({&aurArg, &addSourceOnlyDepsArg, &requireSourcesArg, &verboseArg, &shSyntaxArg});
mingwBundleArg.setSecondaryArguments({&targetDirArg, &targetNameArg, &targetFormatArg, &iconThemesArg, &defaultIconThemeArg, &extraPackagesArg});
cacheDirArg.setCombinable(true);
cacheDirArg.setRequiredValueCount(1);
cacheDirArg.setValueNames(pathValueName);
storageDirArg.setCombinable(true);
storageDirArg.setRequiredValueCount(1);
storageDirArg.setValueNames(pathValueName);
parser.setMainArguments({&buildOrderArg, &upgradeLookupArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &cacheDirArg, &helpArg});
}
/*!
* \class Alpm::Config
* \brief The Config class represents the configuration for the application.
*/
/*!
* \brief Creates the default configuration for the application.
*/
Config::Config() :
m_alpmRootDir(QStringLiteral("/")),
m_alpmDbPath(QStringLiteral("/var/lib/pacman")),
m_pacmanConfFile(QStringLiteral("/etc/pacman.conf")),
m_cacheDir(QStringLiteral("/var/cache/repoindex")),
m_storageDir(QStringLiteral("/var/lib/repoindex")),
m_websocketServerListeningAddr(QHostAddress::LocalHost),
m_websocketServerListeningPort(1234),
m_serverInsecure(false),
m_localEnabled(true),
m_reposFromPacmanConfEnabled(false),
m_aurEnabled(true),
m_verbose(false),
m_runServer(false)
{}
/*!
* \cond
*/
inline void assign(quint16 &num, const QJsonValue &val)
{
if(!val.isUndefined()) {
auto n = val.toInt(-1);
if(n > 0 && n <= static_cast<quint16>(-1)) {
num = static_cast<quint16>(n);
} else {
cerr << shchar << "Error: The specified value \"" << n << "\" is not a number." << endl;
}
}
}
inline void assign(QHostAddress &addr, const QString &val)
{
if(!val.isEmpty() && !addr.setAddress(val)) {
// checking some special values might be useful
if(val == QLatin1String("localhost")) {
addr = QHostAddress::LocalHost;
} else if(val == QLatin1String("localhost-IPv6")) {
addr = QHostAddress::LocalHostIPv6;
} else if(val == QLatin1String("any-IPv4")) {
addr = QHostAddress::AnyIPv4;
} else if(val == QLatin1String("any-IPv6")) {
addr = QHostAddress::AnyIPv6;
} else if(val == QLatin1String("any")) {
addr = QHostAddress::Any;
} else {
cerr << shchar << "Error: Unable to parse the specified host address \"" << val.toStdString() << "\"." << endl;
}
}
}
inline void assign(QString &str, const Argument &arg)
{
if(arg.isPresent()) {
str = QString::fromLocal8Bit(arg.values().front().data(), arg.values().front().size());
}
}
inline void assign(quint16 &num, const Argument &arg)
{
if(arg.isPresent()) {
try {
num = stringToNumber<quint16>(arg.values().front());
} catch(const ConversionException &) {
cerr << shchar << "Error: The specified argument value \"" << arg.values().front() << "\" is not a number." << endl;
}
}
}
inline void assign(bool &num, const Argument &arg)
{
num |= arg.isPresent();
}
/*!
* \endcond
*/
/*!
* \brief Loads the configuration from the specified configuration file.
*/
void Config::loadFromConfigFile(const QString &configFilePath)
{
QFile file(configFilePath);
if(file.open(QFile::ReadOnly)) {
QJsonParseError error;
auto doc = QJsonDocument::fromJson(file.readAll(), &error);
if(error.error == QJsonParseError::NoError) {
auto mainObj = doc.object();
auto alpmObj = mainObj.value(QStringLiteral("alpm")).toObject();
m_alpmRootDir = alpmObj.value(QStringLiteral("rootDir")).toString(m_alpmRootDir);
m_alpmDbPath = alpmObj.value(QStringLiteral("dbPath")).toString(m_alpmDbPath);
m_pacmanConfFile = alpmObj.value(QStringLiteral("pacmanConfigFile")).toString(m_pacmanConfFile);
m_cacheDir = mainObj.value(QStringLiteral("cacheDir")).toString(m_cacheDir);
m_storageDir = mainObj.value(QStringLiteral("storageDir")).toString(m_storageDir);
auto aurObj = mainObj.value(QStringLiteral("aur")).toObject();
m_aurEnabled = aurObj.value(QStringLiteral("enabled")).toBool(m_aurEnabled);
auto serverObj = mainObj.value(QStringLiteral("server")).toObject();
assign(m_websocketServerListeningAddr, serverObj.value(QStringLiteral("websocketListeningAddr")).toString());
assign(m_websocketServerListeningPort, serverObj.value(QStringLiteral("websocketListeningPort")));
m_serverCertFile = serverObj.value(QStringLiteral("certFile")).toString(m_serverCertFile);
m_serverKeyFile = serverObj.value(QStringLiteral("keyFile")).toString(m_serverKeyFile);
m_serverInsecure = serverObj.value(QStringLiteral("insecure")).toBool(m_serverInsecure);
auto reposObj = mainObj.value(QStringLiteral("repos")).toObject();
m_localEnabled = reposObj.value(QStringLiteral("localEnabled")).toBool(m_localEnabled);
m_reposFromPacmanConfEnabled = reposObj.value(QStringLiteral("fromPacmanConfig")).toBool(m_reposFromPacmanConfEnabled);
for(const auto &repo : reposObj.value(QStringLiteral("add")).toArray()) {
m_repoEntries << RepoEntry();
m_repoEntries.back().load(repo);
}
} else {
cerr << shchar << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": "
<< error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl;
}
} else {
cerr << shchar << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl;
}
}
/*!
* \brief Loads the configuration from the configuration file.
*
* The config file is obtained from the specified config args. If no such argument is present,
* some default locations are checked.
*/
void Config::loadFromConfigFile(const ConfigArgs &args)
{
if(args.repoindexConfArg.isPresent()) {
loadFromConfigFile(QString::fromLocal8Bit(args.repoindexConfArg.values().front().data()));
return;
} else {
for(const auto &defaultPath : {QStringLiteral("./repoindex.conf"), QStringLiteral("/etc/repoindex.conf")}) {
if(QFile::exists(defaultPath)) {
loadFromConfigFile(defaultPath);
return;
}
}
}
}
/*!
* \brief Loads the config from the specified configuration arguments.
*/
void Config::loadFromArgs(const ConfigArgs &args)
{
assign(m_alpmRootDir, args.rootdirArg);
assign(m_alpmDbPath, args.dbpathArg);
assign(m_pacmanConfFile, args.pacmanConfArg);
if(args.aurArg.isPresent()) {
auto val = args.aurArg.values().front();
if(val == "enabled") {
m_aurEnabled = true;
} else if(val == "disabled") {
m_aurEnabled = false;
} else {
cerr << shchar << "Warning: The specified value for the argument --aur-enabled is invalid and will be ignored." << endl;
}
}
assign(m_websocketServerListeningPort, args.websocketPortArg);
assign(m_serverCertFile, args.certFileArg);
assign(m_serverKeyFile, args.keyFileArg);
assign(m_serverInsecure, args.insecureArg);
if(args.websocketAddrArg.isPresent()) {
assign(m_websocketServerListeningAddr, QString::fromLocal8Bit(args.websocketAddrArg.values().front().data(), args.websocketAddrArg.values().front().size()));
}
m_verbose = args.verboseArg.isPresent();
m_runServer = args.serverArg.isPresent();
if(args.cacheDirArg.isPresent()) {
m_cacheDir = QString::fromLocal8Bit(args.cacheDirArg.values().front().data());
}
if(args.storageDirArg.isPresent()) {
m_storageDir = QString::fromLocal8Bit(args.storageDirArg.values().front().data());
}
}
RepoEntry::RepoEntry() :
m_sigLevel(SignatureLevel::UseDefault),
m_ignored(false)
{}
/*!
* \brief Loads the values from the specified JSON value.
*/
void RepoEntry::load(const QJsonValue &jsonValue)
{
auto obj = jsonValue.toObject();
m_name = obj.value(QStringLiteral("name")).toString(m_name);
m_dataBaseFile = obj.value(QStringLiteral("dataBaseFile")).toString(m_dataBaseFile);
m_sourceDir = obj.value(QStringLiteral("sourcesDir")).toString(m_sourceDir);
m_packageDir = obj.value(QStringLiteral("packagesDir")).toString(m_packageDir);
for(const auto &server : obj.value(QStringLiteral("servers")).toArray()) {
auto str = server.toString();
if(!str.isEmpty()) {
m_servers << str;
}
}
for(const auto &upgradeSources : obj.value(QStringLiteral("upgradeSources")).toArray()) {
auto str = upgradeSources.toString();
if(!str.isEmpty()) {
m_upgradeSources << str;
}
}
const auto sigLevelValue = obj.value(QStringLiteral("sigLevel"));
if(sigLevelValue.isString()) {
m_sigLevel = Manager::parseSigLevel(sigLevelValue.toString().toLocal8Bit().data());
}
m_maxDatabaseAge = TimeSpan::fromSeconds(obj.value(QStringLiteral("maxAge")).toDouble());
m_ignored = obj.value(QStringLiteral("ignored")).toBool(m_ignored);
}
} // namespace Alpm