#include "./config.h" #include "./manager.h" #include #include #include #include #include #include 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 pathValueName = {"path"}; const initializer_list 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(-1)) { num = static_cast(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(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