From fde2f592ed1d19a45ac94c4315a8aa0fdfa2bc41 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sat, 5 Sep 2015 17:25:05 +0200 Subject: [PATCH] restructured some classes, overall improvements --- alpm/alpmdatabase.cpp | 79 +++++++++++++---- alpm/alpmdatabase.h | 59 +++++++++---- alpm/alpmpackage.cpp | 4 +- alpm/alpmpackage.h | 6 +- alpm/aurpackage.cpp | 2 +- alpm/aurpackage.h | 2 +- alpm/config.cpp | 37 +++++--- alpm/config.h | 10 ++- alpm/group.cpp | 2 +- alpm/group.h | 2 +- alpm/list.h | 6 +- alpm/manager.cpp | 174 ++++++++++++++++++++++++++----------- alpm/manager.h | 43 +++++---- alpm/mingwbundle.cpp | 21 ++--- alpm/mingwbundle.h | 4 +- alpm/package.cpp | 10 ++- alpm/package.h | 30 +++++-- alpm/repository.cpp | 71 ++++++++++----- alpm/repository.h | 60 +++++++++++-- alpm/resolvebuildorder.cpp | 90 +++++++++++-------- alpm/resolvebuildorder.h | 5 +- alpm/upgradelookup.cpp | 28 ++++-- alpm/upgradelookup.h | 2 +- alpm/utilities.cpp | 2 +- alpm/utilities.h | 2 +- main.cpp | 51 ++++++++--- network/connection.cpp | 4 +- network/connection.h | 10 +-- network/server.cpp | 8 +- network/server.h | 8 +- network/userrepository.cpp | 70 ++++++++++----- network/userrepository.h | 23 +++-- 32 files changed, 650 insertions(+), 275 deletions(-) diff --git a/alpm/alpmdatabase.cpp b/alpm/alpmdatabase.cpp index 5172eae..5f808a9 100644 --- a/alpm/alpmdatabase.cpp +++ b/alpm/alpmdatabase.cpp @@ -6,12 +6,15 @@ #include +#include "alpm.h" + #include #include +#include using namespace std; -namespace PackageManagement { +namespace RepoIndex { using namespace Utilities; @@ -21,15 +24,48 @@ using namespace Utilities; * All packages returned by the AlpmDataBase class are AlpmPackage instances. */ +class LoadPackage +{ +public: + LoadPackage(AlpmDatabase *db, QMutex *mutex) : + m_db(db), + m_mutex(mutex) + {} + + void operator()(alpm_pkg_t *pkg) + { + auto res = make_unique(pkg, m_db); + QMutexLocker locker(m_mutex); + for(const auto &grpName : res->groups()) { + m_db->groups()[grpName] << res.get(); + } + m_db->packages().emplace(res->name(), move(res)); + } +private: + AlpmDatabase *m_db; + QMutex *m_mutex; +}; + +PackageLoader::PackageLoader(AlpmDatabase *db) +{ + for(auto *pkg : PackageList(alpm_db_get_pkgcache(db->ptr()))) { + m_packages << pkg; + } + m_future = QtConcurrent::map(m_packages, LoadPackage(db, &m_mutex)); +} + /*! * \brief Creates a new instance wrapping the specified database struct. */ -PackageManagement::AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent) : +RepoIndex::AlpmDatabase::AlpmDatabase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent) : Repository(QString::fromLocal8Bit(alpm_db_get_name(dataBase)), parent), m_ptr(dataBase), m_dbFile(QStringLiteral("%1/sync/%2").arg(dbPath, m_name)) +{} + +PackageLoader *AlpmDatabase::init() { - if(alpm_db_get_usage(dataBase, &m_usage)) { + if(alpm_db_get_usage(m_ptr, &m_usage)) { m_usage = static_cast(ALPM_DB_USAGE_ALL); } if(m_name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { @@ -41,39 +77,48 @@ PackageManagement::AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString m_description = QStringLiteral("Database »%1«").arg(m_name); } } - for(auto *nativePkg : PackageList(alpm_db_get_pkgcache(m_ptr))) { - auto pkg = make_unique(nativePkg, this); - QString pkgName = pkg->name(); - for(const auto &grpName : pkg->groups()) { - m_groups[grpName] << pkg.get(); - } - m_packages.emplace(pkgName, move(pkg)); - } +// for(auto *nativePkg : PackageList(alpm_db_get_pkgcache(m_ptr))) { +// auto pkg = make_unique(nativePkg, this); +// QString pkgName = pkg->name(); +// for(const auto &grpName : pkg->groups()) { +// m_groups[grpName] << pkg.get(); +// } +// m_packages.emplace(pkgName, move(pkg)); +// } for(const char *str : servers()) { m_serverUrls << qstr(str); } m_sigLevel = alpm_db_get_siglevel(m_ptr); + + return new PackageLoader(this); } -//AlpmDataBase::AlpmDataBase(const QString &dataBaseFile, QObject *parent) : +//AlpmDatabase::AlpmDatabase(const QString &dataBaseFile, QObject *parent) : // PackageSource(parent), // m_ptr(nullptr) //{} -RepositoryType AlpmDataBase::type() const +RepositoryType AlpmDatabase::type() const { - return RepositoryType::AlpmDataBase; + return RepositoryType::AlpmDatabase; } -bool AlpmDataBase::isSourceOnly() const +PackageDetailAvailability AlpmDatabase::requestsRequired(PackageDetail packageDetail) const { - return false; + switch(packageDetail) { + case PackageDetail::Basics: + case PackageDetail::Dependencies: + case PackageDetail::PackageInfo: + return PackageDetailAvailability::Immediately; + default: + return PackageDetailAvailability::Never; + } } /*! * \brief Adds the specified server URLs. */ -bool AlpmDataBase::addServerUrls(const QStringList &urls) +bool AlpmDatabase::addServerUrls(const QStringList &urls) { bool res = true; for(const auto &url : urls) { diff --git a/alpm/alpmdatabase.h b/alpm/alpmdatabase.h index 5684d2e..41d0204 100644 --- a/alpm/alpmdatabase.h +++ b/alpm/alpmdatabase.h @@ -4,24 +4,48 @@ #include "repository.h" #include "list.h" -#include - #include +#include +#include -namespace PackageManagement { +#include -class AlpmDataBase : public Repository +namespace RepoIndex { + +class AlpmPackage; +class AlpmDatabase; + +class PackageLoader { public: - explicit AlpmDataBase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent = nullptr); -// explicit AlpmDataBase(const QString &dataBaseFile, QObject *parent = nullptr); + PackageLoader(AlpmDatabase *db); + QFuture &future(); + +private: + QMutex m_mutex; + QList m_packages; + QFuture m_future; +}; + +inline QFuture &PackageLoader::future() +{ + return m_future; +} + +class AlpmDatabase : public Repository +{ + friend class EmplacePackage; +public: + explicit AlpmDatabase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent = nullptr); + PackageLoader *init(); +// explicit AlpmDatabase(const QString &dataBaseFile, QObject *parent = nullptr); RepositoryType type() const; - bool isSourceOnly() const; + PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const; // operators - bool operator ==(const AlpmDataBase &other) const; - bool operator !=(const AlpmDataBase &other) const; + bool operator ==(const AlpmDatabase &other) const; + bool operator !=(const AlpmDatabase &other) const; // database meta data alpm_db_t *ptr(); @@ -31,6 +55,9 @@ public: bool addServerUrls(const QStringList &urls); PackageList search(StringList terms) const; +signals: + void initiated(); + private: alpm_db_t *m_ptr; QString m_dbFile; @@ -39,7 +66,7 @@ private: /*! * \brief Checks whether the specified ALPM database is equal the current instance. */ -inline bool AlpmDataBase::operator ==(const AlpmDataBase &other) const +inline bool AlpmDatabase::operator ==(const AlpmDatabase &other) const { return m_ptr == other.m_ptr; } @@ -47,7 +74,7 @@ inline bool AlpmDataBase::operator ==(const AlpmDataBase &other) const /*! * \brief Checks whether the specified ALPM database is not equal to the current instance. */ -inline bool AlpmDataBase::operator !=(const AlpmDataBase &other) const +inline bool AlpmDatabase::operator !=(const AlpmDatabase &other) const { return m_ptr != other.m_ptr; } @@ -55,7 +82,7 @@ inline bool AlpmDataBase::operator !=(const AlpmDataBase &other) const /*! * \brief Returns the pointer to the underlying database struct. */ -inline alpm_db_t *AlpmDataBase::ptr() +inline alpm_db_t *AlpmDatabase::ptr() { return m_ptr; } @@ -63,7 +90,7 @@ inline alpm_db_t *AlpmDataBase::ptr() /*! * \brief Returns the path of the data base file. */ -inline const QString &AlpmDataBase::dataBaseFile() const +inline const QString &AlpmDatabase::dataBaseFile() const { return m_dbFile; } @@ -71,7 +98,7 @@ inline const QString &AlpmDataBase::dataBaseFile() const /*! * \brief Returns the servers of the database. */ -inline StringList AlpmDataBase::servers() const +inline StringList AlpmDatabase::servers() const { return alpm_db_get_servers(m_ptr); } @@ -79,7 +106,7 @@ inline StringList AlpmDataBase::servers() const /*! * \brief Sets the servers of the database. */ -inline bool AlpmDataBase::setServers(StringList servers) +inline bool AlpmDatabase::setServers(StringList servers) { return alpm_db_set_servers(m_ptr, servers.begin().ptr()) == 0; } @@ -87,7 +114,7 @@ inline bool AlpmDataBase::setServers(StringList servers) /*! * \brief Performs a search using the build in ALPM function. */ -inline PackageList AlpmDataBase::search(StringList terms) const +inline PackageList AlpmDatabase::search(StringList terms) const { return alpm_db_search(m_ptr, terms.begin().ptr()); } diff --git a/alpm/alpmpackage.cpp b/alpm/alpmpackage.cpp index e24a481..34ab633 100644 --- a/alpm/alpmpackage.cpp +++ b/alpm/alpmpackage.cpp @@ -6,7 +6,7 @@ using namespace ChronoUtilities; -namespace PackageManagement { +namespace RepoIndex { /*! * \cond @@ -38,7 +38,7 @@ using namespace Utilities; /*! * \brief Constructs a new package instance for the specified ALPM \a package. */ -AlpmPackage::AlpmPackage(alpm_pkg_t *package, AlpmDataBase *source) : +AlpmPackage::AlpmPackage(alpm_pkg_t *package, AlpmDatabase *source) : Package(QString::fromLocal8Bit(alpm_pkg_get_name(package)), source), m_ptr(package) { diff --git a/alpm/alpmpackage.h b/alpm/alpmpackage.h index a47e8ee..23daea9 100644 --- a/alpm/alpmpackage.h +++ b/alpm/alpmpackage.h @@ -3,14 +3,14 @@ #include "package.h" -namespace PackageManagement { +namespace RepoIndex { -class AlpmDataBase; +class AlpmDatabase; class AlpmPackage : public Package { public: - AlpmPackage(alpm_pkg_t *package, AlpmDataBase *source = nullptr); + AlpmPackage(alpm_pkg_t *package, AlpmDatabase *source = nullptr); // ALPM specific meta data const alpm_pkg_t *ptr() const; diff --git a/alpm/aurpackage.cpp b/alpm/aurpackage.cpp index 4b178ee..30a3ab0 100644 --- a/alpm/aurpackage.cpp +++ b/alpm/aurpackage.cpp @@ -6,7 +6,7 @@ using namespace ChronoUtilities; -namespace PackageManagement { +namespace RepoIndex { /*! * \brief The AurPackage class holds information about AUR packages. It allows to convert the information diff --git a/alpm/aurpackage.h b/alpm/aurpackage.h index a61ffb3..eeab977 100644 --- a/alpm/aurpackage.h +++ b/alpm/aurpackage.h @@ -3,7 +3,7 @@ #include "package.h" -namespace PackageManagement { +namespace RepoIndex { class UserRepository; diff --git a/alpm/config.cpp b/alpm/config.cpp index cd53a7e..1442426 100644 --- a/alpm/config.cpp +++ b/alpm/config.cpp @@ -14,7 +14,10 @@ using namespace std; using namespace ApplicationUtilities; using namespace ConversionUtilities; -namespace PackageManagement { +namespace RepoIndex { + +bool useShSyntax = false; +const char *shchar = ""; /*! * \class Alpm::Config @@ -28,23 +31,26 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) : helpArg(parser), buildOrderArg("build-order", "b", "calculates the build order to build the specified packages"), 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", "a", "specifies the listening address for the websocket server, default is 127.0.0.1"), - websocketPortArg("port", "p", "specifies the listening port for the websocket server, default is 1234"), + 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", "u", "enables/disables AUR queries"), + 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") + iconThemesArg("icon-packages", "i", "specifies the names of the icon packages to include"), + 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"}; @@ -52,6 +58,7 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) : buildOrderArg.setRequiredValueCount(-1); buildOrderArg.setValueNames(pkgValueNames); serverArg.setDenotesOperation(true); + upgradeLookupArg.setDenotesOperation(true); mingwBundleArg.setDenotesOperation(true); mingwBundleArg.setRequiredValueCount(-1); mingwBundleArg.setValueNames(pkgValueNames); @@ -101,8 +108,12 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) : iconThemesArg.setCombinable(true); iconThemesArg.setRequiredValueCount(-1); iconThemesArg.setValueNames(pkgValueNames); - serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg, &aurArg}); - buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg}); + shSyntaxArg.setCombinable(true); + repoArg.setRequiredValueCount(1); + repoArg.setValueNames({"repo name"}); + serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &websocketAddrArg, &websocketPortArg, &insecureArg, &aurArg}); + upgradeLookupArg.setSecondaryArguments({&repoArg, &shSyntaxArg}); + buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg, &shSyntaxArg}); mingwBundleArg.setSecondaryArguments({&targetDirArg, &targetNameArg, &targetFormatArg, &iconThemesArg}); parser.setMainArguments({&buildOrderArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &helpArg}); } @@ -139,7 +150,7 @@ inline void assign(quint16 &num, const QJsonValue &val) if(n > 0 && n <= static_cast(-1)) { num = static_cast(n); } else { - cerr << "Error: The specified value \"" << n << "\" is not a number." << endl; + cerr << shchar << "Error: The specified value \"" << n << "\" is not a number." << endl; } } } @@ -147,7 +158,7 @@ inline void assign(quint16 &num, const QJsonValue &val) inline void assign(QHostAddress &addr, const QString &val) { if(!val.isEmpty() && !addr.setAddress(val)) { - cerr << "Error: Unable to parse the specified host address \"" << val.toStdString() << "\"." << endl; + cerr << shchar << "Error: Unable to parse the specified host address \"" << val.toStdString() << "\"." << endl; } } @@ -164,7 +175,7 @@ inline void assign(quint16 &num, const Argument &arg) try { num = stringToNumber(arg.values().front()); } catch(const ConversionException &) { - cerr << "Error: The specified argument value \"" << arg.values().front() << "\" is not a number." << endl; + cerr << shchar << "Error: The specified argument value \"" << arg.values().front() << "\" is not a number." << endl; } } } @@ -208,11 +219,11 @@ void Config::loadFromConfigFile(const QString &configFilePath) m_repoEntries.back().load(repo); } } else { - cerr << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": " + cerr << shchar << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": " << error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl; } } else { - cerr << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl; + cerr << shchar << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl; } } @@ -252,7 +263,7 @@ void Config::loadFromArgs(const ConfigArgs &args) } else if(val == "disabled") { m_aurEnabled = false; } else { - cerr << "Warning: The specified value for the argument --aur-enabled is invalid and will be ignored." << endl; + cerr << shchar << "Warning: The specified value for the argument --aur-enabled is invalid and will be ignored." << endl; } } assign(m_websocketServerListeningPort, args.websocketPortArg); diff --git a/alpm/config.h b/alpm/config.h index 6223ac2..370f5e4 100644 --- a/alpm/config.h +++ b/alpm/config.h @@ -9,7 +9,11 @@ QT_FORWARD_DECLARE_CLASS(QJsonValue) -namespace PackageManagement { +namespace RepoIndex { + +// these are needed from the beginning and are initialized in the main() +extern bool useShSyntax; +extern const char *shchar; class ConfigArgs { @@ -18,6 +22,7 @@ public: ApplicationUtilities::HelpArgument helpArg; ApplicationUtilities::Argument buildOrderArg; ApplicationUtilities::Argument serverArg; + ApplicationUtilities::Argument upgradeLookupArg; ApplicationUtilities::Argument mingwBundleArg; ApplicationUtilities::Argument repoindexConfArg; ApplicationUtilities::Argument rootdirArg; @@ -35,6 +40,8 @@ public: ApplicationUtilities::Argument targetNameArg; ApplicationUtilities::Argument targetFormatArg; ApplicationUtilities::Argument iconThemesArg; + ApplicationUtilities::Argument shSyntaxArg; + ApplicationUtilities::Argument repoArg; }; class Config; @@ -120,6 +127,7 @@ public: bool isAurEnabled() const; bool isVerbose() const; bool runServer() const; + const char *shSyntax() const; void loadFromConfigFile(const QString &args); void loadFromConfigFile(const ConfigArgs &args); diff --git a/alpm/group.cpp b/alpm/group.cpp index dbcb6ef..1c0d317 100644 --- a/alpm/group.cpp +++ b/alpm/group.cpp @@ -3,7 +3,7 @@ #include -namespace PackageManagement { +namespace RepoIndex { /*! * \class AlpmGroup diff --git a/alpm/group.h b/alpm/group.h index d1d5d38..4422f7a 100644 --- a/alpm/group.h +++ b/alpm/group.h @@ -10,7 +10,7 @@ QT_FORWARD_DECLARE_CLASS(QJsonArray) -namespace PackageManagement { +namespace RepoIndex { class AlpmGroup { diff --git a/alpm/list.h b/alpm/list.h index 17ac9e8..df464f1 100644 --- a/alpm/list.h +++ b/alpm/list.h @@ -3,13 +3,15 @@ #include -namespace PackageManagement { +#include + +namespace RepoIndex { /*! * \brief The ListIterator class is used to iterate Alpm::List instances. */ template -class AlpmListIterator +class AlpmListIterator : public std::iterator { public: /*! diff --git a/alpm/manager.cpp b/alpm/manager.cpp index f302ff5..9d37b85 100644 --- a/alpm/manager.cpp +++ b/alpm/manager.cpp @@ -12,8 +12,8 @@ #include #include -#include #include + #include #include #include @@ -23,7 +23,7 @@ using namespace std; using namespace IoUtilities; using namespace ConversionUtilities; -namespace PackageManagement { +namespace RepoIndex { /*! * \cond @@ -63,7 +63,7 @@ Manager::Manager(const Config &config) : if(!(m_handle = alpm_initialize(config.alpmRootDir().toLocal8Bit().data(), config.alpmDbPath().toLocal8Bit().data(), &err))) { throw runtime_error(string("Cannot initialize ALPM: ") + alpm_strerror(err)); } - m_localDb = make_unique(alpm_get_localdb(m_handle), config.alpmDbPath()); + m_localDb = make_unique(alpm_get_localdb(m_handle), config.alpmDbPath()); if(config.isAurEnabled()) { m_userRepo = make_unique(m_networkAccessManager); } @@ -78,11 +78,11 @@ Manager::~Manager() } /*! - * \brief Returns the package with the specified name from the specified database. + * \brief Returns the first package with the specified name from the specified database. */ -AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &pkgName) +AlpmPackage *Manager::packageFromDatabase(const QString &dbName, const QString &pkgName) { - if(auto *db = dataBaseByName(dbName)) { + if(auto *db = databaseByName(dbName)) { return static_cast(db->packageByName(pkgName)); } else { return nullptr; @@ -90,11 +90,11 @@ AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString & } /*! - * \brief Returns the package with the specified name from the specified database. + * \brief Returns the first package with the specified name from the specified database. */ -const AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &pkgName) const +const AlpmPackage *Manager::packageFromDatabase(const QString &dbName, const QString &pkgName) const { - if(const auto *db = dataBaseByName(dbName)) { + if(const auto *db = databaseByName(dbName)) { return static_cast(db->packageByName(pkgName)); } else { return nullptr; @@ -102,11 +102,11 @@ const AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QSt } /*! - * \brief Gets the package with the specified \a name from one of the sync databases. + * \brief Returns the first package with the specified \a name from one of the sync databases. */ -AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName) +AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName) { - for(const auto &dbEntry : syncDataBases()) { + for(const auto &dbEntry : syncDatabases()) { if(auto *pkg = dbEntry.second->packageByName(pkgName)) { return static_cast(pkg); } @@ -115,11 +115,11 @@ AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName) } /*! - * \brief Gets the package with the specified \a name from one of the sync databases. + * \brief Returns the first package with the specified \a name from one of the sync databases. */ -const AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName) const +const AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName) const { - for(const auto &dbEntry : syncDataBases()) { + for(const auto &dbEntry : syncDatabases()) { if(const auto *pkg = dbEntry.second->packageByName(pkgName)) { return static_cast(pkg); } @@ -142,6 +142,36 @@ unique_ptr Manager::packageFromFile(const char *fileName, } } +/*! + * \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database). + */ +Package *Manager::packageProviding(const Dependency &dependency) +{ + for(auto &dbEntry : syncDatabases()) { + if(auto *pkg = dbEntry.second->packageProviding(dependency)) { + return pkg; + } + } + if(config().isAurEnabled()) { + // TODO: check AUR + } + return nullptr; +} + +/*! + * \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database + * and sources requirering requests such as the AUR). + */ +const Package *Manager::packageProviding(const Dependency &dependency) const +{ + for(const auto &dbEntry : syncDatabases()) { + if(const auto *pkg = dbEntry.second->packageProviding(dependency)) { + return pkg; + } + } + return nullptr; +} + /*! * \brief Sets the install reason for the specified \a package. */ @@ -222,7 +252,7 @@ int Manager::parseSigLevel(const string &sigLevelStr) sigLevel |= ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK; } } else { - cerr << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl; + cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl; } } return sigLevel; @@ -245,7 +275,7 @@ int Manager::parseUsage(const string &usageStr) } else if(part == "Upgrade") { usage |= ALPM_DB_USAGE_UPGRADE; } else { - cerr << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl; + cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl; } } return usage ? usage : ALPM_DB_USAGE_ALL; @@ -295,11 +325,11 @@ void Manager::applyPacmanConfig() // read and validate database name QString dbName = QString::fromLocal8Bit(scope.first.c_str()); if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { - cerr << "Error: Unable to add database from pacman config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl; + cerr << shchar << "Error: Unable to add database from pacman config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl; } else if(dbName.startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) { - cerr << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl; + cerr << shchar << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl; } else if(m_syncDbs.count(dbName)) { - cerr << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl; + cerr << shchar << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl; } else { // read sig level and usage const auto &sigLevelStr = lastValue(scope.second, sigLevelKey); @@ -310,11 +340,11 @@ void Manager::applyPacmanConfig() // set usage if(alpm_db_set_usage(db, static_cast(usage)) == 0) { if(m_config.isVerbose() || m_config.runServer()) { - cerr << "Added database [" << scope.first << "]" << endl; + cerr << shchar << "Added database [" << scope.first << "]" << endl; } } else { if(m_config.isVerbose() || m_config.runServer()) { - cerr << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl; + cerr << shchar << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl; } } // add servers @@ -324,7 +354,7 @@ void Manager::applyPacmanConfig() findAndReplace(url, "$arch", arch); alpm_db_add_server(db, url.c_str()); if(m_config.isVerbose() || m_config.runServer()) { - cerr << "Added server: " << url << endl; + cerr << shchar << "Added server: " << url << endl; } } // add included servers @@ -338,7 +368,7 @@ void Manager::applyPacmanConfig() includedFile.open(path, ios_base::in); includedIni.parse(includedFile); } catch (const ios_base::failure &) { - cerr << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl; + cerr << shchar << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl; } } try { @@ -349,21 +379,21 @@ void Manager::applyPacmanConfig() findAndReplace(url, "$arch", arch); alpm_db_add_server(db, url.c_str()); if(m_config.isVerbose() || m_config.runServer()) { - cerr << "Added server: " << url << endl; + cerr << shchar << "Added server: " << url << endl; } } } catch (const out_of_range &) { - cerr << "Warning: Included file \"" << path << "\" has no values." << endl; + cerr << shchar << "Warning: Included file \"" << path << "\" has no values." << endl; } } - auto emplaced = m_syncDbs.emplace(dbName, make_unique(db, m_config.alpmDbPath())); + auto emplaced = m_syncDbs.emplace(dbName, make_unique(db, m_config.alpmDbPath())); // add sync db to internal map if(usage & ALPM_DB_USAGE_UPGRADE) { // -> db is used to upgrade local database - localDataBase()->upgradeSources() << emplaced.first->second.get(); + localDatabase()->upgradeSources() << emplaced.first->second.get(); } } else { - cerr << "Error: Unable to add sync database [" << scope.first << "]" << endl; + cerr << shchar << "Error: Unable to add sync database [" << scope.first << "]" << endl; } } } @@ -380,35 +410,35 @@ void Manager::applyRepoIndexConfig() { // check whether an entry already exists, if not create a new one for(const RepoEntry &repoEntry : m_config.repoEntries()) { - AlpmDataBase *syncDb = nullptr; + AlpmDatabase *syncDb = nullptr; try { syncDb = m_syncDbs.at(repoEntry.name()).get(); - cerr << "Applying config for database [" << syncDb->name() << "]" << endl; + cerr << shchar << "Applying config for database [" << syncDb->name() << "]" << endl; if(!repoEntry.dataBasePath().isEmpty()) { - cerr << "Warning: Can't use data base path specified in repo index config because the repo \"" + cerr << shchar << "Warning: Can't use data base path specified in repo index config because the repo \"" << repoEntry.name() << "\" has already been added from the Pacman config." << endl; } if(repoEntry.sigLevel()) { - cerr << "Warning: Can't use sig level specified in repo index config because the repo \"" + cerr << shchar << "Warning: Can't use sig level specified in repo index config because the repo \"" << repoEntry.name() << "\" has already been added from the Pacman config." << endl; } syncDb->setPackagesDirectory(repoEntry.packageDir()); syncDb->setSourcesDirectory(repoEntry.sourceDir()); } catch(const out_of_range &) { if(repoEntry.name().compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { - cerr << "Error: Unable to add database from repo index config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl; + cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl; } else if(repoEntry.name().startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) { - cerr << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl; + cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl; } else { // TODO: database path auto *db = alpm_register_syncdb(m_handle, repoEntry.name().toLocal8Bit().data(), static_cast(repoEntry.sigLevel())); - auto emplaced = m_syncDbs.emplace(repoEntry.name(), make_unique(db, m_config.alpmDbPath())); + auto emplaced = m_syncDbs.emplace(repoEntry.name(), make_unique(db, m_config.alpmDbPath())); if(emplaced.second) { syncDb = emplaced.first->second.get(); syncDb->setSourcesDirectory(repoEntry.sourceDir()); syncDb->setPackagesDirectory(repoEntry.packageDir()); if(m_config.isVerbose() || m_config.runServer()) { - cerr << "Added database [" << repoEntry.name() << "]" << endl; + cerr << shchar << "Added database [" << repoEntry.name() << "]" << endl; } } } @@ -425,7 +455,7 @@ void Manager::applyRepoIndexConfig() if(auto *source = repositoryByName(upgradeSourceName)) { upgradeSources << source; } else { - cerr << "Warning: The specified upgrade source \"" << upgradeSourceName << "\" can not be found and will be ignored." << endl; + cerr << shchar << "Warning: The specified upgrade source \"" << upgradeSourceName << "\" can not be found and will be ignored." << endl; } } } catch(const out_of_range &) { @@ -435,6 +465,24 @@ void Manager::applyRepoIndexConfig() } +/*! + * \brief Initiates all ALPM data bases. + * \remarks Must be called, after all relevant sync data bases have been registered (eg. via applyPacmanConfig()). + */ +void Manager::initAlpmDataBases() +{ + QList loaders; + loaders.reserve(m_syncDbs.size() + 1); + loaders << localDatabase()->init(); + for(auto &syncDbEntry : m_syncDbs) { + loaders << syncDbEntry.second->init(); + } + for(auto *loader : loaders) { + loader->future().waitForFinished(); + delete loader; + } +} + /*! * \brief Unregisters all registred sync databases. */ @@ -449,7 +497,7 @@ void Manager::unregisterSyncDataBases() * \brief Returns a list of all sync databases. * \remarks Sync databases must be registered with parsePacmanConfig() before. */ -const map > &Manager::syncDataBases() const +const map > &Manager::syncDatabases() const { return m_syncDbs; // m_syncDbs has been filled when the databases were registered } @@ -466,9 +514,9 @@ const QJsonArray &Manager::basicRepoInfo() const QMutexLocker locker(&m_basicRepoInfoMutex); if(m_basicRepoInfo.isEmpty()) { // add local data base - m_basicRepoInfo << localDataBase()->basicInfo(); + m_basicRepoInfo << localDatabase()->basicInfo(); // add sync data bases - for(const auto &syncDb : syncDataBases()) { + for(const auto &syncDb : syncDatabases()) { // check if the "sync" database is actually used for syncing auto usage = syncDb.second->usage(); if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) { @@ -533,7 +581,7 @@ const QJsonArray &Manager::groupInfo() const if(m_groupInfo.empty()) { QMutexLocker locker(&m_groupInfoMutex); if(m_groupInfo.empty()) { - m_groupInfo << localDataBase()->groupInfo(); + m_groupInfo << localDatabase()->groupInfo(); for(const auto &db : m_syncDbs) { m_groupInfo << db.second->groupInfo(); } @@ -545,10 +593,10 @@ const QJsonArray &Manager::groupInfo() const /*! * \brief Returns the ALPM database with the specified name. */ -const AlpmDataBase *Manager::dataBaseByName(const QString &dbName) const +const AlpmDatabase *Manager::databaseByName(const QString &dbName) const { if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { - return localDataBase(); + return localDatabase(); } else { try { return m_syncDbs.at(dbName).get(); @@ -561,10 +609,10 @@ const AlpmDataBase *Manager::dataBaseByName(const QString &dbName) const /*! * \brief Returns the ALPM database with the specified name. */ -AlpmDataBase *Manager::dataBaseByName(const QString &dbName) +AlpmDatabase *Manager::databaseByName(const QString &dbName) { if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { - return localDataBase(); + return localDatabase(); } else { try { return m_syncDbs.at(dbName).get(); @@ -580,7 +628,7 @@ AlpmDataBase *Manager::dataBaseByName(const QString &dbName) const Repository *Manager::repositoryByName(const QString &name) const { if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { - return localDataBase(); + return localDatabase(); } else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) { return userRepository(); } else { @@ -598,7 +646,7 @@ const Repository *Manager::repositoryByName(const QString &name) const Repository *Manager::repositoryByName(const QString &name) { if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { - return localDataBase(); + return localDatabase(); } else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) { return userRepository(); } else { @@ -610,12 +658,40 @@ Repository *Manager::repositoryByName(const QString &name) } } +/*! + * \brief Returns a list of all repositories excluding the local database. + */ +QList Manager::repositories() const +{ + QList repos; + repos.reserve(m_syncDbs.size() + 1); + for(const auto &dbEntry : m_syncDbs) { + repos << dbEntry.second.get(); + } + repos << m_userRepo.get(); + return repos; +} + +/*! + * \brief Returns a list of all repositories excluding the local database. + */ +QList Manager::repositories() +{ + QList repos; + repos.reserve(m_syncDbs.size() + 1); + for(auto &dbEntry : m_syncDbs) { + repos << dbEntry.second.get(); + } + repos << m_userRepo.get(); + return repos; +} + /*! * \brief Checks the specified database for upgrades. * * Appropriate upgrade sources will be determined automatically; does not check the AUR. */ -const UpgradeLookupResults Manager::checkForUpgrades(AlpmDataBase *db) const +const UpgradeLookupResults Manager::checkForUpgrades(AlpmDatabase *db) const { UpgradeLookupResults results; db->checkForUpgrades(results); diff --git a/alpm/manager.h b/alpm/manager.h index 9f265da..b69e9fc 100644 --- a/alpm/manager.h +++ b/alpm/manager.h @@ -14,11 +14,11 @@ #include #include -namespace PackageManagement { +namespace RepoIndex { class Config; class UserRepository; -class AlpmDataBase; +class AlpmDatabase; class Manager { @@ -40,29 +40,34 @@ public: static int parseUsage(const std::string &usageStr); void applyPacmanConfig(); void applyRepoIndexConfig(); + void initAlpmDataBases(); void unregisterSyncDataBases(); - const UserRepository *userRepository() const; - UserRepository *userRepository(); // package lookup - AlpmPackage *packageFromDataBase(const QString &dbName, const QString &pkgName); - const AlpmPackage *packageFromDataBase(const QString &dbName, const QString &pkgName) const; - AlpmPackage *packageFromSyncDataBases(const QString &pkgName); - const AlpmPackage *packageFromSyncDataBases(const QString &pkgName) const; + AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName); + const AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName) const; + AlpmPackage *packageFromSyncDatabases(const QString &pkgName); + const AlpmPackage *packageFromSyncDatabases(const QString &pkgName) const; std::unique_ptr packageFromFile(const char *fileName, bool verifyIntegrity = false); + Package *packageProviding(const Dependency &dependency); + const Package *packageProviding(const Dependency &dependency) const; bool isPackageIgnored(const AlpmPackage *package) const; void setInstallReason(AlpmPackage *package, alpm_pkgreason_t reason); - // data base lookup - const AlpmDataBase *localDataBase() const; - AlpmDataBase *localDataBase(); - const std::map > &syncDataBases() const; - const AlpmDataBase *dataBaseByName(const QString &dbName) const; - AlpmDataBase *dataBaseByName(const QString &dbName); + // repository lookup + const AlpmDatabase *localDatabase() const; + AlpmDatabase *localDatabase(); + const std::map > &syncDatabases() const; + const AlpmDatabase *databaseByName(const QString &dbName) const; + AlpmDatabase *databaseByName(const QString &dbName); const Repository *repositoryByName(const QString &name) const; Repository *repositoryByName(const QString &name); - const UpgradeLookupResults checkForUpgrades(AlpmDataBase *db) const; + const UserRepository *userRepository() const; + UserRepository *userRepository(); + QList repositories() const; + QList repositories(); + const UpgradeLookupResults checkForUpgrades(AlpmDatabase *db) const; // JSON serialization, handling JSON requests const QJsonObject basicRepoInfo(const Repository *packageSource) const; @@ -80,8 +85,8 @@ private: QString m_pacmanCacheDir; QNetworkAccessManager m_networkAccessManager; std::unique_ptr m_userRepo; - std::unique_ptr m_localDb; - std::map > m_syncDbs; + std::unique_ptr m_localDb; + std::map > m_syncDbs; mutable QJsonArray m_basicRepoInfo; mutable QMutex m_basicRepoInfoMutex; mutable QJsonArray m_groupInfo; @@ -167,7 +172,7 @@ inline UserRepository *Manager::userRepository() /*! * \brief Returns the local data base. */ -inline const AlpmDataBase *Manager::localDataBase() const +inline const AlpmDatabase *Manager::localDatabase() const { return m_localDb.get(); } @@ -175,7 +180,7 @@ inline const AlpmDataBase *Manager::localDataBase() const /*! * \brief Returns the local data base. */ -inline AlpmDataBase *Manager::localDataBase() +inline AlpmDatabase *Manager::localDatabase() { return m_localDb.get(); } diff --git a/alpm/mingwbundle.cpp b/alpm/mingwbundle.cpp index 13cd40c..4129e22 100644 --- a/alpm/mingwbundle.cpp +++ b/alpm/mingwbundle.cpp @@ -1,6 +1,7 @@ #include "mingwbundle.h" #include "utilities.h" #include "manager.h" +#include "config.h" #include #include @@ -20,7 +21,7 @@ using namespace std; -namespace PackageManagement { +namespace RepoIndex { using namespace Utilities; @@ -29,12 +30,12 @@ const string prefix("mingw-w64-"); MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages, const ApplicationUtilities::StringVector &iconPackages) : m_manager(manager) { - cerr << "Resolving dependencies ..." << endl; + cerr << shchar << "Resolving dependencies ..." << endl; string missing; // add mingw-w64 packages for(const auto &pkgName : packages) { bool found = false; - for(const auto &syncDb : manager.syncDataBases()) { + for(const auto &syncDb : manager.syncDatabases()) { if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data()))) { if(missing.empty()) { decltype(m_packages)::value_type entry(syncDb.second.get(), pkg); @@ -55,7 +56,7 @@ MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::Str // add additional icon packages for(const auto &pkgName : iconPackages) { bool found = false; - for(const auto &syncDb : manager.syncDataBases()) { + for(const auto &syncDb : manager.syncDatabases()) { if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(pkgName.data()))) { if(missing.empty()) { decltype(m_packages)::value_type entry(syncDb.second.get(), pkg); @@ -75,11 +76,11 @@ MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::Str if(!missing.empty()) { throw runtime_error("The following packages can not be found:" + missing); } else { - cerr << "Adding the following packages:"; + cerr << shchar << "Adding the following packages:"; for(const auto &pkg : m_packages) { - cerr << ' ' << pkg.second->name().toLocal8Bit().data(); + cerr << shchar << ' ' << pkg.second->name().toLocal8Bit().data(); } - cerr << endl; + cerr << shchar << endl; } } @@ -89,7 +90,7 @@ void MingwBundle::addDependencies(const Package *pkg) for(const auto &dep : pkg->dependencies()) { if(dep.name.startsWith(QLatin1String("mingw-w64-"), Qt::CaseInsensitive)) { bool found = false; - for(const auto &syncDbEntry : m_manager.syncDataBases()) { + for(const auto &syncDbEntry : m_manager.syncDatabases()) { if(const auto *pkg = syncDbEntry.second->packageProviding(dep)) { if(missing.empty()) { decltype(m_packages)::value_type entry(syncDbEntry.second.get(), pkg); @@ -284,7 +285,7 @@ void getFiles(PkgFileInfo &pkgFileInfo) void MingwBundle::createBundle(const string &targetDir, const string &targetName, const string &targetFormat) const { - cerr << "Gathering relevant files ..." << endl; + cerr << shchar << "Gathering relevant files ..." << endl; // get package files list pkgFiles; for(const auto &entry : m_packages) { @@ -333,7 +334,7 @@ void MingwBundle::createBundle(const string &targetDir, const string &targetName }; for(const auto &root : roots) { QString targetPath = qstr(targetDir) % QChar('/') % root.second % QChar('-') % qstr(targetName) % QChar('.') % qstr(targetFormat); - cerr << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl; + cerr << shchar << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl; unique_ptr targetArchive; if(targetFormat == "7z") { targetArchive = make_unique(targetPath); diff --git a/alpm/mingwbundle.h b/alpm/mingwbundle.h index 41eba9d..661f32a 100644 --- a/alpm/mingwbundle.h +++ b/alpm/mingwbundle.h @@ -8,7 +8,7 @@ #include -namespace PackageManagement { +namespace RepoIndex { class Manager; @@ -23,7 +23,7 @@ private: void addDependencies(const Package *pkg); const Manager &m_manager; - std::list > m_packages; + std::list > m_packages; }; } // namespace PackageManagement diff --git a/alpm/package.cpp b/alpm/package.cpp index b47d855..661fbf3 100644 --- a/alpm/package.cpp +++ b/alpm/package.cpp @@ -11,7 +11,7 @@ using namespace std; using namespace ChronoUtilities; -namespace PackageManagement { +namespace RepoIndex { /*! * \class The Package class holds meta information about an @@ -46,13 +46,16 @@ Package::~Package() bool Package::matches(const QString &name, const QString &version, const Dependency &dependency) { + if(name == QStringLiteral("gst-plugins-base-libs") && dependency.name == QStringLiteral("gst-plugins-base-libs")) { + dependency.name.begin(); + } if(name == dependency.name) { PackageVersionComparsion cmp; switch(dependency.mode) { case ALPM_DEP_MOD_ANY: return true; case ALPM_DEP_MOD_EQ: - return version == dependency.version; + return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::Equal; case ALPM_DEP_MOD_GE: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThenSyncVersion; case ALPM_DEP_MOD_LE: @@ -234,15 +237,18 @@ PackageVersion::PackageVersion(const QString &versionStr) release = QString::fromUtf16(releaseBeg); } else { version = QString::fromUtf16(versionBeg); + release = QStringLiteral("1"); } } else { // epoch not present + epoch = QStringLiteral("0"); if(releaseBeg) { // release present version = QString::fromUtf16(str, releaseBeg - str - 1); release = QString::fromUtf16(releaseBeg); } else { version = QString::fromUtf16(str); + release = QStringLiteral("1"); } } } diff --git a/alpm/package.h b/alpm/package.h index 4c0444c..ef896b9 100644 --- a/alpm/package.h +++ b/alpm/package.h @@ -7,17 +7,16 @@ #include -#include -#include - #include #include #include +#include + QT_FORWARD_DECLARE_CLASS(QJsonObject) QT_FORWARD_DECLARE_CLASS(QJsonValue) -namespace PackageManagement { +namespace RepoIndex { class Repository; @@ -77,7 +76,7 @@ public: class Dependency { public: - explicit Dependency(const QString &name, const QString &version, _alpm_depmod_t mode = ALPM_DEP_MOD_ANY); + explicit Dependency(const QString &name, const QString &version = QString(), _alpm_depmod_t mode = ALPM_DEP_MOD_ANY); QString name; QString version; _alpm_depmod_t mode; @@ -145,6 +144,7 @@ public: ChronoUtilities::DateTime firstSubmitted() const; ChronoUtilities::DateTime lastModified() const; const QString &tarUrl() const; + const std::map &sourceFiles() const; // version comparsion PackageVersionComparsion compareVersion(const Package *syncPackage) const; @@ -211,6 +211,7 @@ protected: ChronoUtilities::DateTime m_firstSubmitted; ChronoUtilities::DateTime m_lastModified; QString m_tarUrl; + std::map m_sourceFiles; }; /*! @@ -508,7 +509,7 @@ inline const QString &Package::baseName() const /*! * \brief Returns the architecutes (from the PKGBUILD file). - * \remarks For the architecture of the particular binary package + * \remarks For the architecture of the particular package file * see buildArchitecture(). */ inline const QStringList &Package::architectures() const @@ -580,16 +581,33 @@ inline const QString &Package::tarUrl() const return m_tarUrl; } +/*! + * \brief Returns the source files of the package. + */ +inline const std::map &Package::sourceFiles() const +{ + return m_sourceFiles; +} + +/*! + * \brief Compares the version of the package with the specified sync package. + */ inline PackageVersionComparsion Package::compareVersion(const Package *syncPackage) const { return PackageVersion(version()).compare(PackageVersion(syncPackage->version())); } +/*! + * \brief Compares the version of the package with the version of the specified \a dependency. + */ inline PackageVersionComparsion Package::compareVersion(const Dependency &dependency) const { return PackageVersion(version()).compare(PackageVersion(dependency.version)); } +/*! + * \brief Checks whether the package matches the specified \a dependency. + */ inline bool Package::matches(const Dependency &dependency) { return matches(name(), version(), dependency); diff --git a/alpm/repository.cpp b/alpm/repository.cpp index 96442b0..483900e 100644 --- a/alpm/repository.cpp +++ b/alpm/repository.cpp @@ -7,27 +7,40 @@ using namespace std; -namespace PackageManagement { +namespace RepoIndex { /*! - * \brief Constructs a new reply. + * \brief Constructs a new reply for a single network reply. */ -Reply::Reply(QNetworkReply *networkReply) : - m_networkReply(networkReply) +Reply::Reply(QNetworkReply *networkReply) { networkReply->setParent(this); connect(networkReply, &QNetworkReply::finished, this, &Reply::processData); + m_networkReplies.reserve(1); + m_networkReplies << networkReply; } /*! - * \fn PackageSource::type() + * \brief Constructs a new reply for multiple network replies. + */ +Reply::Reply(const QList networkReplies) : + m_networkReplies(networkReplies) +{ + for(auto *networkReply : networkReplies) { + networkReply->setParent(this); + connect(networkReply, &QNetworkReply::finished, this, &Reply::processData); + } +} + +/*! + * \fn Repository::type() * \brief Returns the type of the package source. */ /*! * \brief Returns a list of all package names. */ -const QStringList PackageManagement::Repository::packageNames() const +const QStringList RepoIndex::Repository::packageNames() const { QStringList names; names.reserve(m_packages.size()); @@ -63,32 +76,26 @@ Repository::Repository(const QString &name, QObject *parent) : {} /*! - * \brief Destroys the package source. + * \brief Destroys the repository. */ Repository::~Repository() {} /*! - * \brief Returns whether the repository only has sources but no binary packages. - */ -bool Repository::isSourceOnly() const -{ - return true; -} - -/*! - * \brief Returns whether requests are required. + * \brief Returns whether explicit requests are required to get the specified information + * about the package of this repository. * * AlpmDataBase instances load all available packages in the cache - * at the beginning and hence do not require explicit requests. + * at the beginning and hence do not require explicit requests for package names, version, + * description, dependencies and most other information. However make dependencies are not available at all. * * UserRepository instances on the other hand have an empty package * cache at the beginning so packages must be requested explicitely * using the requestPackageInfo() method. */ -bool Repository::requestsRequired() const +PackageDetailAvailability Repository::requestsRequired(PackageDetail ) const { - return false; + return PackageDetailAvailability::Never; } /*! @@ -116,7 +123,7 @@ PackageReply *Repository::requestPackageInfo(const QStringList &, bool ) const * requested again. If it turns out, that all packages are already cached, nullptr * is returned in this case. */ -PackageReply *Repository::requestFullPackageInfo(const QString &, bool ) const +PackageReply *Repository::requestFullPackageInfo(const QStringList &, bool ) const { return nullptr; } @@ -143,6 +150,28 @@ const Package *Repository::packageProviding(const Dependency &dependency) const return nullptr; } +/*! + * \brief Returns the first package providing the specified \a dependency. + * \remarks Returns nullptr if no packages provides the \a dependency. + */ +Package *Repository::packageProviding(const Dependency &dependency) +{ + for(auto &entry : m_packages) { + if(entry.second->matches(dependency)) { + // check whether package matches "directly" + return entry.second.get(); + } else { + // check whether at least one of the provides matches + for(auto &provide : entry.second->provides()) { + if(Package::matches(provide.name, provide.version, dependency)) { + return entry.second.get(); + } + } + } + } + return nullptr; +} + /*! * \brief Returns all packages providing the specified \a dependency. */ @@ -267,7 +296,7 @@ QJsonObject Repository::basicInfo() const put(info, QStringLiteral("sigLevel"), Utilities::sigLevelStrings(sigLevel())); put(info, QStringLiteral("upgradeSources"), upgradeSourcesJsonArray()); put(info, QStringLiteral("packages"), packageNamesJsonArray()); - put(info, QStringLiteral("requestRequired"), requestsRequired()); + put(info, QStringLiteral("requestRequired"), requestsRequired(PackageDetail::Basics) != PackageDetailAvailability::Immediately); put(info, QStringLiteral("srcOnly"), isSourceOnly()); return info; } diff --git a/alpm/repository.h b/alpm/repository.h index 20a851d..dd5f9a4 100644 --- a/alpm/repository.h +++ b/alpm/repository.h @@ -11,7 +11,7 @@ QT_FORWARD_DECLARE_CLASS(QNetworkReply) -namespace PackageManagement { +namespace RepoIndex { class UpgradeLookupResults; @@ -20,6 +20,7 @@ class Reply : public QObject Q_OBJECT public: Reply(QNetworkReply *networkReply); + Reply(const QList networkReplies); const QString &error() const; signals: @@ -29,7 +30,7 @@ private slots: virtual void processData() = 0; protected: - QNetworkReply *m_networkReply; + QList m_networkReplies; QString m_error; }; @@ -43,6 +44,7 @@ class PackageReply : public Reply Q_OBJECT public: PackageReply(QNetworkReply *networkReply, std::map > &packages); + PackageReply(const QList &networkReplies, std::map > &packages); const std::map > &packages() const; protected: @@ -54,6 +56,11 @@ inline PackageReply::PackageReply(QNetworkReply *networkReply, std::map &networkReplies, std::map > &packages) : + Reply(networkReplies), + m_packages(packages) +{} + inline const std::map > &PackageReply::packages() const { return m_packages; @@ -79,13 +86,39 @@ inline const QJsonArray &SuggestionsReply::suggestions() const return m_suggestions; } +/*! + * \brief The RepositoryType enum specifies the type of a repository object. + */ enum class RepositoryType { - AlpmDataBase, /*! The repository is an AlpmDataBase instance. */ + AlpmDatabase, /*! The repository is an AlpmDataBase instance. */ UserRepository, /*! The repository is a UserRepository instance. */ Other /*! The repository type is unknown. */ }; +/*! + * \brief The PackageDetail enum is used to refer to some kind of information about a package. + */ +enum class PackageDetail +{ + Basics, /*! Basic information about the package such as knowing it exists, its name, version and description. */ + Dependencies, /*! The runtime dependencies of the package. */ + SourceInfo, /*! The source info such as make dependencies of the package. */ + PackageInfo /*! Information related to a specific package (pkg-file). */ +}; + +/*! + * \brief The PackageDetailAvailability enum describes when some kind of information about a package + * becomes available. + */ +enum class PackageDetailAvailability +{ + Never, /*! The information is not available and can't be requested. */ + Immediately, /*! The information is available immediately after the repository object has been constructed. */ + Request, /*! The information is available after the requestPackageInfo() method has been called for the package. */ + FullRequest /*! The information is available after the requestFullPackageInfo() method has been called for the package. */ +}; + class Repository : public QObject { Q_OBJECT @@ -101,21 +134,23 @@ public: std::map > &packages(); const QStringList packageNames() const; alpm_db_usage_t usage() const; - virtual bool isSourceOnly() const; + bool isSourceOnly() const; + std::map > &groups(); const std::map > &groups() const; const QStringList &serverUrls() const; alpm_siglevel_t sigLevel() const; // gathering data - virtual bool requestsRequired() const; + virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const; virtual SuggestionsReply *requestSuggestions(const QString &phrase) const; virtual PackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const; - virtual PackageReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const; + virtual PackageReply *requestFullPackageInfo(const QStringList &package, bool forceUpdate = false) const; // package search const Package *packageByName(const QString &name) const; Package *packageByName(const QString &name); const Package *packageProviding(const Dependency &dependency) const; + Package *packageProviding(const Dependency &dependency); QList packagesProviding(const Dependency &dependency) const; QList packageByFilter(std::function pred); @@ -168,6 +203,14 @@ inline const QString &Repository::description() const return m_description; } +/*! + * \brief Returns whether the repository only has sources but no built packages. + */ +inline bool Repository::isSourceOnly() const +{ + return requestsRequired(PackageDetail::PackageInfo) == PackageDetailAvailability::Never; +} + /*! * \brief Returns the packages. */ @@ -215,6 +258,11 @@ inline alpm_db_usage_t Repository::usage() const return m_usage; } +inline std::map > &Repository::groups() +{ + return m_groups; +} + inline const std::map > &Repository::groups() const { return m_groups; diff --git a/alpm/resolvebuildorder.cpp b/alpm/resolvebuildorder.cpp index 1baa096..02e2f2b 100644 --- a/alpm/resolvebuildorder.cpp +++ b/alpm/resolvebuildorder.cpp @@ -11,7 +11,7 @@ using namespace std; using namespace ApplicationUtilities; -namespace PackageManagement { +namespace RepoIndex { class TaskInfo { @@ -19,6 +19,7 @@ public: TaskInfo(QString name, bool onlyDependency = false, const QList &deps = QList()); const QString &name() const; + void setName(const QString &name); const QList deps() const; void addDep(TaskInfo *dep); bool isDone() const; @@ -49,6 +50,11 @@ inline const QString &TaskInfo::name() const return m_name; } +inline void TaskInfo::setName(const QString &name) +{ + m_name = name; +} + inline const QList TaskInfo::deps() const { return m_deps; @@ -76,26 +82,32 @@ inline bool TaskInfo::isOnlyDependency() const void TaskInfo::add(QList &results) { - if(!m_done) { - if(m_visited) { - throw *this; // cyclic dependency + if(!isDone()) { + if(isVisited()) { + // cyclic dependency + if(isOnlyDependency()) { + // if this is only a dependency (which we don't want to build) don't care about it + return; + } else { + throw *this; + } } else { m_visited = true; } - for(auto *dep : m_deps) { + for(auto *dep : deps()) { dep->add(results); } m_done = true; - results << this; + if(!isOnlyDependency()) { + results << this; + } } } void TaskInfo::addAll(const QList &tasks, QList &results) { for(auto *task : tasks) { - if(!task->m_done) { - task->add(results); - } + task->add(results); } } @@ -132,7 +144,7 @@ BuildOrderResolver::BuildOrderResolver(const Manager &manager) : QStringList BuildOrderResolver::resolve(const StringVector &packages) const { - cerr << "Getting package information ..." << endl; + cerr << shchar << "Getting package information ..." << endl; QList tasks; tasks.reserve(packages.size()); try { @@ -141,18 +153,20 @@ QStringList BuildOrderResolver::resolve(const StringVector &packages) const tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data())); } // find specified packages and their dependencies - for(auto *task : tasks) { - addDeps(tasks, task); + for(int i = 0, size = tasks.size(); i != size; ++i) { + addDeps(tasks, tasks.at(i)); } - cerr << "Relevant packages: "; - for(const auto *task : tasks) { - cerr << task->name().toLocal8Bit().data() << ' '; + if(m_manager.config().isVerbose()) { + cerr << shchar << "Relevant packages: "; + for(const auto *task : tasks) { + cerr << task->name().toLocal8Bit().data() << ' '; + } + cerr << endl; } - cerr << endl; // topo sort QList results; - results.reserve(tasks.size()); try { + results.reserve(packages.size()); TaskInfo::addAll(tasks, results); QStringList names; names.reserve(results.size()); @@ -171,30 +185,38 @@ QStringList BuildOrderResolver::resolve(const StringVector &packages) const void BuildOrderResolver::addDeps(QList &tasks, TaskInfo *task) const { - if(const auto pkg = m_manager.packageFromSyncDataBases(task->name().toLocal8Bit().data())) { - for(auto dep : pkg->dependencies()) { - if(auto *depTask = addDep(tasks, dep.name)) { - task->addDep(depTask); - } - } + if(const auto pkg = m_manager.packageProviding(Dependency(task->name()))) { + task->setName(pkg->name()); // update the name to ensure we have the acutal package name and not just a "provides" name + addDeps(tasks, task, pkg->dependencies()); } else { stringstream ss; - ss << "The package \"" << task->name().toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build"; + ss << "The specified package \"" << task->name().toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build"; throw runtime_error(ss.str()); } } -TaskInfo *BuildOrderResolver::addDep(QList &tasks, const QString &depName) const +void BuildOrderResolver::addDeps(QList &tasks, TaskInfo *task, const QList &dependencies) const { - if(auto *task = TaskInfo::find(tasks, depName)) { - // we've already added a task for this dependency - return task; - } else { - // create new task - //task = new TaskInfo(QString::fromLocal8Bit(depName)); - //tasks << task; - //return task; - return nullptr; + for(auto &dep : dependencies) { + if(const auto depPkg = m_manager.packageProviding(dep)) { + auto *depTask = TaskInfo::find(tasks, depPkg->name()); + if(depTask) { + // we've already added a task for this dependency + // adds dependency task to the dependencies of "parent" task + task->addDep(depTask); + } else { + // create new task + tasks << (depTask = new TaskInfo(depPkg->name(), true)); + // adds dependency task to the dependencies of "parent" task + task->addDep(depTask); + // add dependencies of the dependency + addDeps(tasks, depTask, depPkg->dependencies()); + } + } else { + stringstream ss; + ss << "The dependency \"" << dep.name.toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build"; + throw runtime_error(ss.str()); + } } } diff --git a/alpm/resolvebuildorder.h b/alpm/resolvebuildorder.h index 6f48ba8..ed85b05 100644 --- a/alpm/resolvebuildorder.h +++ b/alpm/resolvebuildorder.h @@ -6,10 +6,11 @@ #include #include -namespace PackageManagement { +namespace RepoIndex { class Manager; class TaskInfo; +class Dependency; class BuildOrderResolver { @@ -20,7 +21,7 @@ public: private: void addDeps(QList &tasks, TaskInfo *task) const; - TaskInfo *addDep(QList &pkgInfos, const QString &depName) const; + void addDeps(QList &tasks, TaskInfo *task, const QList &dependencies) const; const Manager &m_manager; }; diff --git a/alpm/upgradelookup.cpp b/alpm/upgradelookup.cpp index abd948a..b9042c8 100644 --- a/alpm/upgradelookup.cpp +++ b/alpm/upgradelookup.cpp @@ -9,7 +9,7 @@ using namespace std; -namespace PackageManagement { +namespace RepoIndex { QJsonObject UpgradeResult::json() const { @@ -27,14 +27,26 @@ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, const R m_watcher(new QFutureWatcher(this)) { connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished); - if(m_upgradeSource->requestsRequired()) { - if((m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames()))) { - m_reply->setParent(this); - connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady); - return; - } + switch(m_upgradeSource->requestsRequired()) { + case PackageDetailAvailability::Request: + m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames()); + break; + case PackageDetailAvailability::FullRequest: + m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames()); + break; + case PackageDetailAvailability::Never: + m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name()); + emit finished(); + return; + case PackageDetailAvailability::Immediately: + break; + } + if(m_reply) { + m_reply->setParent(this); + connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady); + } else { + sourceReady(); } - sourceReady(); } const UpgradeLookupResults &UpgradeLookupProcess::results() const diff --git a/alpm/upgradelookup.h b/alpm/upgradelookup.h index 6a276a0..65e51ca 100644 --- a/alpm/upgradelookup.h +++ b/alpm/upgradelookup.h @@ -8,7 +8,7 @@ #include #include -namespace PackageManagement { +namespace RepoIndex { class Manager; class UpgradeLookup; diff --git a/alpm/utilities.cpp b/alpm/utilities.cpp index ad5bb46..bd2920f 100644 --- a/alpm/utilities.cpp +++ b/alpm/utilities.cpp @@ -6,7 +6,7 @@ using namespace std; -namespace PackageManagement { +namespace RepoIndex { namespace Utilities { diff --git a/alpm/utilities.h b/alpm/utilities.h index 55064f7..f69cb2b 100644 --- a/alpm/utilities.h +++ b/alpm/utilities.h @@ -10,7 +10,7 @@ QT_FORWARD_DECLARE_CLASS(QJsonArray) -namespace PackageManagement { +namespace RepoIndex { namespace Utilities { diff --git a/main.cpp b/main.cpp index afd1404..752145f 100644 --- a/main.cpp +++ b/main.cpp @@ -7,6 +7,7 @@ #include "network/server.h" #include +#include #include #include @@ -16,11 +17,18 @@ using namespace std; using namespace ApplicationUtilities; -using namespace PackageManagement; -using namespace Network; +using namespace RepoIndex; int main(int argc, char *argv[]) { + // check whether shell syntax is enabled + for(char **arg = argv, **end = argv + argc; arg != end; ++arg) { + if(strcmp(*arg, "--sh-syntax") == 0) { + useShSyntax = true; + shchar = "# "; + break; + } + } // setup the argument parser ArgumentParser parser; ConfigArgs configArgs(parser); @@ -29,7 +37,7 @@ int main(int argc, char *argv[]) try { parser.parseArgs(argc, argv); } catch (Failure &e) { - cerr << "Unable to parse arguments: " << e.what() << endl; + cerr << shchar << "Unable to parse arguments: " << e.what() << endl; return 2; } try { @@ -40,13 +48,14 @@ int main(int argc, char *argv[]) if(find_if(parser.mainArguments().cbegin(), parser.mainArguments().cend(), [&configArgs] (const Argument *arg) { return arg != &configArgs.helpArg && arg->isPresent(); }) != parser.mainArguments().cend()) { - cerr << "Loading databases ..." << endl; // create app QCoreApplication application(argc, argv); // setup ALPM Manager manager(config); manager.applyPacmanConfig(); manager.applyRepoIndexConfig(); + cerr << shchar << "Loading databases ..." << endl; + manager.initAlpmDataBases(); if(configArgs.serverArg.isPresent()) { // setup the server Server server(manager, manager.config()); @@ -57,22 +66,42 @@ int main(int argc, char *argv[]) BuildOrderResolver resolver(manager); const QStringList results = resolver.resolve(configArgs.buildOrderArg.values()); // print results - cout << "Results: "; - for(const auto &pkgName : results) { - cout << pkgName.toLocal8Bit().data() << ' '; + if(useShSyntax) { + cout << "export REPOINDEX_RESULTS=("; + for(const auto &pkgName : results) { + cout << ' ' << '\'' << pkgName.toLocal8Bit().data() << '\''; + } + cout << ' ' << ')' << endl; + } else { + cout << "Results: "; + for(const auto &pkgName : results) { + cout << pkgName.toLocal8Bit().data() << ' '; + } + cout << endl; } - cout << endl; } else if(configArgs.mingwBundleArg.isPresent()) { MingwBundle bundle(manager, configArgs.mingwBundleArg.values(), configArgs.iconThemesArg.values()); bundle.createBundle(configArgs.targetDirArg.isPresent() ? configArgs.targetDirArg.values().front() : string("."), configArgs.targetNameArg.values().front(), configArgs.targetFormatArg.isPresent() ? configArgs.targetFormatArg.values().front() : string("zip")); + } else if(configArgs.upgradeLookupArg.isPresent()) { + cerr << shchar << "TODO" << endl; + } + } else if(!configArgs.helpArg.isPresent()) { + if(useShSyntax) { + cerr << "export REPOINDEX_ERROR='No command line arguments specified. See --help for available commands.'" << endl; + } else { + cerr << "No command line arguments specified. See --help for available commands." << endl; } - } else { - cout << "No command line arguments specified. See --help for available commands." << endl; } } catch (std::exception &e) { - cerr << "Error: " << e.what() << endl; + if(useShSyntax) { + string error = e.what(); + ConversionUtilities::findAndReplace(error, "'", "\'"); + cerr << "export REPOINDEX_ERROR='" << error << '\'' << endl; + } else { + cerr << shchar << "Error: " << e.what() << endl; + } return 1; } } diff --git a/network/connection.cpp b/network/connection.cpp index a4c8d2d..b643afa 100644 --- a/network/connection.cpp +++ b/network/connection.cpp @@ -8,9 +8,7 @@ #include #include -using namespace PackageManagement; - -namespace Network { +namespace RepoIndex { Connection::Connection(const Manager &alpmManager, QWebSocket *socket, QObject *parent) : QObject(parent), diff --git a/network/connection.h b/network/connection.h index 97fc505..17063d7 100644 --- a/network/connection.h +++ b/network/connection.h @@ -7,18 +7,16 @@ QT_FORWARD_DECLARE_CLASS(QWebSocket) -namespace PackageManagement { -class Manager; -} +namespace RepoIndex { -namespace Network { +class Manager; class Connection : public QObject { Q_OBJECT public: - Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr); + Connection(const RepoIndex::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr); private slots: void processTextMessage(const QString &message); @@ -33,7 +31,7 @@ private: void handleQuery(const QJsonObject &obj); QWebSocket *m_socket; - const PackageManagement::Manager &m_alpmManager; + const RepoIndex::Manager &m_alpmManager; bool m_repoInfoUpdatesRequested; bool m_groupInfoUpdatesRequested; diff --git a/network/server.cpp b/network/server.cpp index eb31a91..c911cb1 100644 --- a/network/server.cpp +++ b/network/server.cpp @@ -12,9 +12,9 @@ using namespace std; -namespace Network { +namespace RepoIndex { -Server::Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent) : +Server::Server(const RepoIndex::Manager &alpmManager, const RepoIndex::Config &config, QObject *parent) : QObject(parent), m_server(new QWebSocketServer(QStringLiteral("Repository index server"), config.serverInsecure() ? QWebSocketServer::NonSecureMode : QWebSocketServer::SecureMode, @@ -48,11 +48,11 @@ Server::Server(const PackageManagement::Manager &alpmManager, const PackageManag sslConfiguration.setProtocol(QSsl::TlsV1SslV3); m_server->setSslConfiguration(sslConfiguration); } else { - cerr << "Warning: The server is running in insecure mode." << endl; + cerr << shchar << "Warning: The server is running in insecure mode." << endl; } // start listening if(m_server->listen(config.websocketServerListeningAddr(), config.websocketServerListeningPort())) { - cerr << m_server->serverName().toLocal8Bit().data() << " is listening on port " << m_server->serverPort() << endl; + cerr << shchar << m_server->serverName().toLocal8Bit().data() << " is listening on port " << m_server->serverPort() << endl; connect(m_server, &QWebSocketServer::newConnection, this, &Server::incomingConnection); connect(m_server, &QWebSocketServer::closed, this, &Server::closed); } else { diff --git a/network/server.h b/network/server.h index 9fe41c7..3ca5fd3 100644 --- a/network/server.h +++ b/network/server.h @@ -6,14 +6,14 @@ #include #include -namespace PackageManagement { +namespace RepoIndex { class Config; class Manager; } -namespace Network { +namespace RepoIndex { class Connection; @@ -22,7 +22,7 @@ class Server : public QObject Q_OBJECT public: - Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent = nullptr); + Server(const RepoIndex::Manager &alpmManager, const RepoIndex::Config &config, QObject *parent = nullptr); ~Server(); signals: @@ -34,7 +34,7 @@ private slots: private: QWebSocketServer *m_server; - const PackageManagement::Manager &m_alpmManager; + const RepoIndex::Manager &m_alpmManager; }; } diff --git a/network/userrepository.cpp b/network/userrepository.cpp index fc2a4b4..599b4fc 100644 --- a/network/userrepository.cpp +++ b/network/userrepository.cpp @@ -15,7 +15,7 @@ using namespace std; -namespace PackageManagement { +namespace RepoIndex { const char *requestTypeProp = "type"; const QString rpcRequestTypeKey(QStringLiteral("type")); @@ -27,6 +27,7 @@ const QString pkgbuildRequestType(QString("pkgbuild")); QUrl UserRepository::m_aurRpcUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php")); QUrl UserRepository::m_aurPkgbuildUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD")); QUrl UserRepository::m_aurSrcInfoUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO")); +QString UserRepository::m_aurSnapshotPath = QStringLiteral("https://aur.archlinux.org/cgit/aur.git/snapshot/%1.tar.gz"); AurPackageReply::AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo) : PackageReply(networkReply, userRepo->packages()), @@ -35,12 +36,13 @@ AurPackageReply::AurPackageReply(QNetworkReply *networkReply, UserRepository *us void AurPackageReply::processData() { - if(m_networkReply->error() == QNetworkReply::NoError) { + auto *reply = m_networkReplies.front(); + if(reply->error() == QNetworkReply::NoError) { QJsonParseError error; //QByteArray data = m_networkReply->readAll(); - //cerr << "AUR reply: " << data.data() << endl; + //cerr << shchar << "AUR reply: " << data.data() << endl; //const QJsonDocument doc = QJsonDocument::fromJson(data, &error); - const QJsonDocument doc = QJsonDocument::fromJson(m_networkReply->readAll(), &error); + const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error); if(error.error == QJsonParseError::NoError) { for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) { auto package = make_unique(result, m_userRepo); @@ -52,30 +54,42 @@ void AurPackageReply::processData() m_error = QStringLiteral("Error: Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset); } } else { - m_error = QStringLiteral("Error: Unable to request data from AUR: ") + m_networkReply->errorString(); + m_error = QStringLiteral("Error: Unable to request data from AUR: ") + reply->errorString(); } emit resultsAvailable(); } +AurFullPackageReply::AurFullPackageReply(const QList &networkReplies, UserRepository *userRepo) : + PackageReply(networkReplies, userRepo->packages()), + m_userRepo(userRepo) +{} + +void AurFullPackageReply::processData() +{ + //auto *reply = static_cast(sender()); + // TODO +} + AurSuggestionsReply::AurSuggestionsReply(QNetworkReply *networkReply) : SuggestionsReply(networkReply) {} void AurSuggestionsReply::processData() { - if(m_networkReply->error() == QNetworkReply::NoError) { + auto *reply = m_networkReplies.front(); + if(reply->error() == QNetworkReply::NoError) { QJsonParseError error; //QByteArray data = m_networkReply->readAll(); - //cerr << "AUR reply: " << data.data() << endl; + //cerr << shchar << "AUR reply: " << data.data() << endl; //const QJsonDocument doc = QJsonDocument::fromJson(data, &error); - const QJsonDocument doc = QJsonDocument::fromJson(m_networkReply->readAll(), &error); + const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error); if(error.error == QJsonParseError::NoError) { m_suggestions = doc.array(); } else { m_error = QStringLiteral("Error: Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset); } } else { - m_error = QStringLiteral("Error: Unable to request data from AUR: ") + m_networkReply->errorString(); + m_error = QStringLiteral("Error: Unable to request data from AUR: ") + reply->errorString(); } emit resultsAvailable(); } @@ -92,9 +106,17 @@ RepositoryType UserRepository::type() const return RepositoryType::UserRepository; } -bool UserRepository::requestsRequired() const +PackageDetailAvailability UserRepository::requestsRequired(PackageDetail packageDetail) const { - return true; + switch(packageDetail) { + case PackageDetail::Basics: + return PackageDetailAvailability::Request; + case PackageDetail::Dependencies: + case PackageDetail::SourceInfo: + return PackageDetailAvailability::FullRequest; + case PackageDetail::PackageInfo: + return PackageDetailAvailability::Never; + } } AurSuggestionsReply *UserRepository::requestSuggestions(const QString &phrase) const @@ -125,20 +147,24 @@ AurPackageReply *UserRepository::requestPackageInfo(const QStringList &packageNa } } -AurPackageReply *UserRepository::requestFullPackageInfo(const QString &package, bool forceUpdate) const +AurFullPackageReply *UserRepository::requestFullPackageInfo(const QStringList &packageNames, bool forceUpdate) const { - try { - const auto &pkg = m_packages.at(package); - if(pkg->hasGeneralInfo() && !forceUpdate) { - return nullptr; + QList replies; + for(const auto &packageName : packageNames) { + try { + const auto &pkg = m_packages.at(packageName); + if(!pkg->hasGeneralInfo() || !pkg->hasSourceRelatedMetaData() || forceUpdate) { + if(pkg->tarUrl().isEmpty()) { + replies << m_networkAccessManager.get(QNetworkRequest(m_aurSnapshotPath.arg(pkg->name()))); + } else { + replies << m_networkAccessManager.get(QNetworkRequest(pkg->tarUrl())); + } + } + } catch(const out_of_range &) { + replies << m_networkAccessManager.get(QNetworkRequest(m_aurSnapshotPath.arg(packageName))); } - } catch(const out_of_range &) { } - auto url = m_aurPkgbuildUrl; - QUrlQuery query; - query.addQueryItem(QStringLiteral("h"), package); - url.setQuery(query); - return new AurPackageReply(m_networkAccessManager.get(QNetworkRequest(url)), const_cast(this)); + return new AurFullPackageReply(replies, const_cast(this)); } } // namespace Alpm diff --git a/network/userrepository.h b/network/userrepository.h index 059da6d..b9295d5 100644 --- a/network/userrepository.h +++ b/network/userrepository.h @@ -13,7 +13,7 @@ QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager) QT_FORWARD_DECLARE_CLASS(QNetworkReply) -namespace PackageManagement { +namespace RepoIndex { class UserRepository; @@ -22,7 +22,19 @@ class AurPackageReply : public PackageReply Q_OBJECT public: AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo); - const std::map > &packages() const; + +private slots: + void processData(); + +private: + UserRepository *m_userRepo; +}; + +class AurFullPackageReply : public PackageReply +{ + Q_OBJECT +public: + AurFullPackageReply(const QList &networkReplies, UserRepository *userRepo); private slots: void processData(); @@ -52,16 +64,17 @@ public: static const QUrl aurRpcUrl(); static void setAurRpcUrl(const QUrl &aurRpcUrl); - bool requestsRequired() const; + PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const; AurSuggestionsReply *requestSuggestions(const QString &phrase) const; AurPackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const; - AurPackageReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const; + AurFullPackageReply *requestFullPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const; private: - QNetworkAccessManager &m_networkAccessManager; + QNetworkAccessManager &m_networkAccessManager; static QUrl m_aurRpcUrl; static QUrl m_aurPkgbuildUrl; static QUrl m_aurSrcInfoUrl; + static QString m_aurSnapshotPath; }; inline const QUrl UserRepository::aurRpcUrl()