From c6d3ea2d8e92716b7b5f7a4d0fb8906e2d7119de Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 4 Sep 2015 14:37:01 +0200 Subject: [PATCH] restructured a lot of classes --- alpm/alpmdatabase.cpp | 90 ++++ alpm/alpmdatabase.h | 97 +++++ alpm/alpmpackage.cpp | 92 +++++ alpm/alpmpackage.h | 108 +++++ alpm/aurpackage.cpp | 41 ++ alpm/aurpackage.h | 18 + alpm/config.cpp | 28 +- alpm/config.h | 8 +- alpm/database.cpp | 149 ------- alpm/database.h | 256 ------------ alpm/group.cpp | 13 +- alpm/group.h | 25 +- alpm/list.h | 1 + alpm/manager.cpp | 314 ++++++++------ alpm/manager.h | 90 ++-- alpm/mingwbundle.cpp | 295 +++++++++++-- alpm/mingwbundle.h | 10 +- alpm/package.cpp | 404 ++++++++---------- alpm/package.h | 795 +++++++++++++++++++----------------- alpm/repository.cpp | 298 ++++++++++++++ alpm/repository.h | 288 +++++++++++++ alpm/resolvebuildorder.cpp | 6 +- alpm/resolvebuildorder.h | 2 +- alpm/updatelookup.cpp | 185 --------- alpm/updatelookup.h | 145 ------- alpm/upgradelookup.cpp | 155 +++++++ alpm/upgradelookup.h | 137 +++++++ alpm/utilities.cpp | 99 ++++- alpm/utilities.h | 23 +- general.pri | 3 - main.cpp | 8 +- network/aurquery.cpp | 137 ------- network/aurquery.h | 71 ---- network/connection.cpp | 21 +- network/connection.h | 4 +- network/userrepository.cpp | 145 +++++++ network/userrepository.h | 79 ++++ repoindex.pro | 36 +- web/index.html | 6 +- web/js/alpm.js | 6 +- web/js/entrymanagement.js | 18 + web/js/packagemanagement.js | 44 +- web/js/repomanagement.js | 36 +- web/test.html | 17 +- 44 files changed, 2951 insertions(+), 1852 deletions(-) create mode 100644 alpm/alpmdatabase.cpp create mode 100644 alpm/alpmdatabase.h create mode 100644 alpm/alpmpackage.cpp create mode 100644 alpm/alpmpackage.h create mode 100644 alpm/aurpackage.cpp create mode 100644 alpm/aurpackage.h delete mode 100644 alpm/database.cpp delete mode 100644 alpm/database.h create mode 100644 alpm/repository.cpp create mode 100644 alpm/repository.h delete mode 100644 alpm/updatelookup.cpp delete mode 100644 alpm/updatelookup.h create mode 100644 alpm/upgradelookup.cpp create mode 100644 alpm/upgradelookup.h delete mode 100644 network/aurquery.cpp delete mode 100644 network/aurquery.h create mode 100644 network/userrepository.cpp create mode 100644 network/userrepository.h diff --git a/alpm/alpmdatabase.cpp b/alpm/alpmdatabase.cpp new file mode 100644 index 0000000..5172eae --- /dev/null +++ b/alpm/alpmdatabase.cpp @@ -0,0 +1,90 @@ +#include "alpmdatabase.h" +#include "group.h" +#include "upgradelookup.h" +#include "alpmpackage.h" +#include "utilities.h" + +#include + +#include +#include + +using namespace std; + +namespace PackageManagement { + +using namespace Utilities; + +/*! + * \brief The AlpmDataBase class wraps an ALPM data base struct and holds additional meta information. + * + * All packages returned by the AlpmDataBase class are AlpmPackage instances. + */ + +/*! + * \brief Creates a new instance wrapping the specified database struct. + */ +PackageManagement::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)) +{ + if(alpm_db_get_usage(dataBase, &m_usage)) { + m_usage = static_cast(ALPM_DB_USAGE_ALL); + } + if(m_name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { + m_description = QStringLiteral("The local database"); + } else { + if((m_usage & ALPM_DB_USAGE_SYNC) || (m_usage & ALPM_DB_USAGE_INSTALL) || (m_usage & ALPM_DB_USAGE_UPGRADE)) { + m_description = QStringLiteral("Sync database »%1«").arg(m_name); + } else { + 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(const char *str : servers()) { + m_serverUrls << qstr(str); + } + m_sigLevel = alpm_db_get_siglevel(m_ptr); +} + +//AlpmDataBase::AlpmDataBase(const QString &dataBaseFile, QObject *parent) : +// PackageSource(parent), +// m_ptr(nullptr) +//{} + +RepositoryType AlpmDataBase::type() const +{ + return RepositoryType::AlpmDataBase; +} + +bool AlpmDataBase::isSourceOnly() const +{ + return false; +} + +/*! + * \brief Adds the specified server URLs. + */ +bool AlpmDataBase::addServerUrls(const QStringList &urls) +{ + bool res = true; + for(const auto &url : urls) { + if(alpm_db_add_server(m_ptr, url.toLocal8Bit().data()) != 0) { + res = false; + } else { + m_serverUrls << url; + } + } + return res; +} + +} // namespace Alpm + diff --git a/alpm/alpmdatabase.h b/alpm/alpmdatabase.h new file mode 100644 index 0000000..5684d2e --- /dev/null +++ b/alpm/alpmdatabase.h @@ -0,0 +1,97 @@ +#ifndef ALPM_DATABASE_H +#define ALPM_DATABASE_H + +#include "repository.h" +#include "list.h" + +#include + +#include + +namespace PackageManagement { + +class AlpmDataBase : public Repository +{ +public: + explicit AlpmDataBase(alpm_db_t *dataBase, const QString &dbPath, QObject *parent = nullptr); +// explicit AlpmDataBase(const QString &dataBaseFile, QObject *parent = nullptr); + + RepositoryType type() const; + bool isSourceOnly() const; + + // operators + bool operator ==(const AlpmDataBase &other) const; + bool operator !=(const AlpmDataBase &other) const; + + // database meta data + alpm_db_t *ptr(); + const QString &dataBaseFile() const; + StringList servers() const; + bool setServers(StringList servers); + bool addServerUrls(const QStringList &urls); + PackageList search(StringList terms) const; + +private: + alpm_db_t *m_ptr; + QString m_dbFile; +}; + +/*! + * \brief Checks whether the specified ALPM database is equal the current instance. + */ +inline bool AlpmDataBase::operator ==(const AlpmDataBase &other) const +{ + return m_ptr == other.m_ptr; +} + +/*! + * \brief Checks whether the specified ALPM database is not equal to the current instance. + */ +inline bool AlpmDataBase::operator !=(const AlpmDataBase &other) const +{ + return m_ptr != other.m_ptr; +} + +/*! + * \brief Returns the pointer to the underlying database struct. + */ +inline alpm_db_t *AlpmDataBase::ptr() +{ + return m_ptr; +} + +/*! + * \brief Returns the path of the data base file. + */ +inline const QString &AlpmDataBase::dataBaseFile() const +{ + return m_dbFile; +} + +/*! + * \brief Returns the servers of the database. + */ +inline StringList AlpmDataBase::servers() const +{ + return alpm_db_get_servers(m_ptr); +} + +/*! + * \brief Sets the servers of the database. + */ +inline bool AlpmDataBase::setServers(StringList servers) +{ + return alpm_db_set_servers(m_ptr, servers.begin().ptr()) == 0; +} + +/*! + * \brief Performs a search using the build in ALPM function. + */ +inline PackageList AlpmDataBase::search(StringList terms) const +{ + return alpm_db_search(m_ptr, terms.begin().ptr()); +} + +} // namespace Alpm + +#endif // ALPM_DATABASE_H diff --git a/alpm/alpmpackage.cpp b/alpm/alpmpackage.cpp new file mode 100644 index 0000000..e24a481 --- /dev/null +++ b/alpm/alpmpackage.cpp @@ -0,0 +1,92 @@ +#include "alpmpackage.h" +#include "alpmdatabase.h" +#include "utilities.h" + +#include + +using namespace ChronoUtilities; + +namespace PackageManagement { + +/*! + * \cond + */ + +namespace Utilities { + +inline QList depinfos(DependencyList list) +{ + QList infos; + for(const auto *dep : list) { + infos << Dependency(qstr(dep->name), qstr(dep->version), dep->mod); + } + return infos; +} + +} + +/*! + * \endcond + */ + +using namespace Utilities; + +/*! + * \brief The AlpmPackage class wraps an ALPM package struct and holds additional meta information. + */ + +/*! + * \brief Constructs a new package instance for the specified ALPM \a package. + */ +AlpmPackage::AlpmPackage(alpm_pkg_t *package, AlpmDataBase *source) : + Package(QString::fromLocal8Bit(alpm_pkg_get_name(package)), source), + m_ptr(package) +{ + m_origin = static_cast(alpm_pkg_get_origin(package)); + m_hasGeneralInfo = m_hasBuildRelatedMetaData = m_hasInstallRelatedMetaData = true; + m_version = qstr(alpm_pkg_get_version(package)); + m_description = qstr(alpm_pkg_get_desc(package)); + m_upstreamUrl = qstr(alpm_pkg_get_url(package)); + m_licenses = qstrlist(alpm_pkg_get_licenses(package)); + m_groups = qstrlist(alpm_pkg_get_groups(package)); + m_dependencies = depinfos(alpm_pkg_get_depends(package)); + m_optionalDependencies = depinfos(alpm_pkg_get_optdepends(package)); + m_conflicts = depinfos(alpm_pkg_get_conflicts(package)); + m_provides = depinfos(alpm_pkg_get_provides(package)); + m_replaces = depinfos(alpm_pkg_get_replaces(package)); + alpm_list_t *tmp; + m_requiredBy = qstrlist(tmp = alpm_pkg_compute_requiredby(package)); + FREELIST(tmp); + m_optionalFor = qstrlist(tmp = alpm_pkg_compute_optionalfor(package)); + FREELIST(tmp); + m_hasInstallScript = alpm_pkg_has_scriptlet(package); + m_fileName = qstr(alpm_pkg_get_filename(package)); + m_buildDate = DateTime::fromTimeStamp(alpm_pkg_get_builddate(package)); + m_packer = qstr(alpm_pkg_get_packager(package)); + m_md5 = qstr(alpm_pkg_get_md5sum(package)); + m_sha256 = qstr(alpm_pkg_get_sha256sum(package)); + m_buildArchitecture = qstr(alpm_pkg_get_arch(package)); + m_packageSize = alpm_pkg_get_size(package); + m_installDate = DateTime::fromTimeStamp(alpm_pkg_get_installdate(package)); + m_installedSize = alpm_pkg_get_isize(package); + for(const auto *backupEntry : BackupList(alpm_pkg_get_backup(package))) { + m_backupFiles << qstr(backupEntry->name); + } + m_validationMethods = alpm_pkg_get_validation(package); + m_installReason = alpm_pkg_get_reason(package); + alpm_filelist_t *fileList = alpm_pkg_get_files(package); + for(alpm_file_t *file = fileList->files, *end = fileList->files + fileList->count; file != end; ++file) { + QJsonObject fileInfo; + fileInfo.insert(QStringLiteral("name"), qstr(file->name)); + if(file->mode) { + fileInfo.insert(QStringLiteral("mode"), static_cast(file->mode)); + } + if(file->size) { + fileInfo.insert(QStringLiteral("size"), static_cast(file->size)); + } + m_files << fileInfo; + } +} + +} // namespace PackageManagement + diff --git a/alpm/alpmpackage.h b/alpm/alpmpackage.h new file mode 100644 index 0000000..a47e8ee --- /dev/null +++ b/alpm/alpmpackage.h @@ -0,0 +1,108 @@ +#ifndef PACKAGEMANAGEMENT_ALPMPACKAGE_H +#define PACKAGEMANAGEMENT_ALPMPACKAGE_H + +#include "package.h" + +namespace PackageManagement { + +class AlpmDataBase; + +class AlpmPackage : public Package +{ +public: + AlpmPackage(alpm_pkg_t *package, AlpmDataBase *source = nullptr); + + // ALPM specific meta data + const alpm_pkg_t *ptr() const; + alpm_pkg_t *ptr(); + const char *base64Signature() const; + void *openChangelog() const; + size_t readChangelog(void *changelog, void *buffer, size_t bufferSize); + bool closeChangelog(void *changelog); + +protected: + alpm_pkg_t *m_ptr; +}; + +inline uint qHash(const AlpmPackage &key) +{ + return qHash(key.ptr()); +} + +/*! + * \brief Returns a pointer to the underlying alpm_pkg_t. + */ +inline const alpm_pkg_t *AlpmPackage::ptr() const +{ + return m_ptr; +} + +/*! + * \brief Returns a pointer to the underlying alpm_pkg_t. + */ +inline alpm_pkg_t *AlpmPackage::ptr() +{ + return m_ptr; +} + +inline const char *AlpmPackage::base64Signature() const +{ + return alpm_pkg_get_base64_sig(m_ptr); +} + +inline void *AlpmPackage::openChangelog() const +{ + return alpm_pkg_changelog_open(m_ptr); +} + +inline size_t AlpmPackage::readChangelog(void *changelog, void *buffer, size_t bufferSize) +{ + return alpm_pkg_changelog_read(buffer, bufferSize, m_ptr, changelog); +} + +inline bool AlpmPackage::closeChangelog(void *changelog) +{ + return alpm_pkg_changelog_close(m_ptr, changelog); +} + +class AlpmOwnershipPackage : public AlpmPackage +{ +public: + // constructor, destructor + AlpmOwnershipPackage(alpm_pkg_t *package); + AlpmOwnershipPackage(const AlpmOwnershipPackage &other) = delete; + AlpmOwnershipPackage(AlpmOwnershipPackage &&other); + ~AlpmOwnershipPackage(); + + // assignment operator + AlpmOwnershipPackage &operator =(const AlpmOwnershipPackage &other) = delete; + AlpmOwnershipPackage &operator =(AlpmOwnershipPackage &&other); +}; + +inline AlpmOwnershipPackage::AlpmOwnershipPackage(alpm_pkg_t *package) : + AlpmPackage(package) +{} + +inline AlpmOwnershipPackage::AlpmOwnershipPackage(AlpmOwnershipPackage &&other) : + AlpmPackage(other.m_ptr) +{ + other.m_ptr = nullptr; +} + +inline AlpmOwnershipPackage &AlpmOwnershipPackage::operator =(AlpmOwnershipPackage &&other) +{ + if(this != &other) { + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + } + return *this; +} + +inline AlpmOwnershipPackage::~AlpmOwnershipPackage() +{ + alpm_pkg_free(ptr()); +} + +} // namespace PackageManagement + +#endif // PACKAGEMANAGEMENT_ALPMPACKAGE_H diff --git a/alpm/aurpackage.cpp b/alpm/aurpackage.cpp new file mode 100644 index 0000000..4b178ee --- /dev/null +++ b/alpm/aurpackage.cpp @@ -0,0 +1,41 @@ +#include "aurpackage.h" + +#include "network/userrepository.h" + +#include + +using namespace ChronoUtilities; + +namespace PackageManagement { + +/*! + * \brief The AurPackage class holds information about AUR packages. It allows to convert the information + * to JSON objects used by the network classes and the web interface. + */ + +/*! + * \brief Creates a new instance from the specified "AurJson value". + */ +AurPackage::AurPackage(const QJsonValue &aurJsonValue, UserRepository *source) : + Package(QString(), source) +{ + m_origin = PackageOrigin::Aur; + QJsonObject obj = aurJsonValue.toObject(); + m_name = obj.value(QStringLiteral("Name")).toString(); + m_hasGeneralInfo = true; + m_id = obj.value(QStringLiteral("ID")).toInt(-1); + m_categoryId = obj.value(QStringLiteral("CategoryID")).toInt(-1); + m_version = obj.value(QStringLiteral("Version")).toString(); + m_description = obj.value(QStringLiteral("Description")).toString(); + m_upstreamUrl = obj.value(QStringLiteral("URL")).toString(); + m_votes = obj.value(QStringLiteral("NumVotes")).toInt(0); + m_outOfDate = DateTime::fromTimeStamp(obj.value(QStringLiteral("OutOfDate")).toInt()); + m_maintainer = obj.value(QStringLiteral("Maintainer")).toString(); + m_firstSubmitted = DateTime::fromTimeStamp(obj.value(QStringLiteral("FirstSubmitted")).toInt()); + m_lastModified = DateTime::fromTimeStamp(obj.value(QStringLiteral("LastModified")).toInt()); + m_licenses << obj.value(QStringLiteral("License")).toString(); + m_tarUrl = obj.value(QStringLiteral("URLPath")).toString(); +} + +} // namespace PackageManagement + diff --git a/alpm/aurpackage.h b/alpm/aurpackage.h new file mode 100644 index 0000000..a61ffb3 --- /dev/null +++ b/alpm/aurpackage.h @@ -0,0 +1,18 @@ +#ifndef PACKAGEMANAGEMENT_AURPACKAGE_H +#define PACKAGEMANAGEMENT_AURPACKAGE_H + +#include "package.h" + +namespace PackageManagement { + +class UserRepository; + +class AurPackage : public Package +{ +public: + AurPackage(const QJsonValue &aurJsonValue, UserRepository *source); +}; + +} // namespace PackageManagement + +#endif // PACKAGEMANAGEMENT_AURPACKAGE_H diff --git a/alpm/config.cpp b/alpm/config.cpp index aa0967a..cd53a7e 100644 --- a/alpm/config.cpp +++ b/alpm/config.cpp @@ -40,7 +40,11 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) : insecureArg("insecure", string(), "forces the server to run in insecure mode"), aurArg("aur", "u", "enables/disables AUR queries"), verboseArg("verbose", "v", "be verbose"), - outputFileArg("output-file", "f", "specifies the output file") + 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") { const initializer_list pathValueName = {"path"}; const initializer_list pkgValueNames = {"package 1", "package 2", "package 3"}; @@ -83,10 +87,23 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) : outputFileArg.setCombinable(true); outputFileArg.setRequired(true); outputFileArg.setRequiredValueCount(1); - outputFileArg.setValueNames({"path"}); + 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); serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg, &aurArg}); buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg}); - mingwBundleArg.setSecondaryArguments({&outputFileArg}); + mingwBundleArg.setSecondaryArguments({&targetDirArg, &targetNameArg, &targetFormatArg, &iconThemesArg}); parser.setMainArguments({&buildOrderArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &helpArg}); } @@ -271,7 +288,10 @@ void RepoEntry::load(const QJsonValue &jsonValue) m_upgradeSources << str; } } - m_sigLevel = Manager::parseSigLevel(obj.value(QStringLiteral("sigLevel")).toString().toLocal8Bit().data()); + const auto sigLevelValue = obj.value(QStringLiteral("sigLevel")); + if(sigLevelValue.isString()) { + m_sigLevel = Manager::parseSigLevel(sigLevelValue.toString().toLocal8Bit().data()); + } } } // namespace Alpm diff --git a/alpm/config.h b/alpm/config.h index 333b6c9..6223ac2 100644 --- a/alpm/config.h +++ b/alpm/config.h @@ -7,9 +7,7 @@ #include #include -QT_BEGIN_NAMESPACE -class QJsonValue; -QT_END_NAMESPACE +QT_FORWARD_DECLARE_CLASS(QJsonValue) namespace PackageManagement { @@ -33,6 +31,10 @@ public: ApplicationUtilities::Argument aurArg; ApplicationUtilities::Argument verboseArg; ApplicationUtilities::Argument outputFileArg; + ApplicationUtilities::Argument targetDirArg; + ApplicationUtilities::Argument targetNameArg; + ApplicationUtilities::Argument targetFormatArg; + ApplicationUtilities::Argument iconThemesArg; }; class Config; diff --git a/alpm/database.cpp b/alpm/database.cpp deleted file mode 100644 index 788ee16..0000000 --- a/alpm/database.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "database.h" -#include "group.h" -#include "updatelookup.h" - -#include -#include - -using namespace std; - -namespace PackageManagement { - -/*! - * \brief The AlpmDataBase class wraps an ALPM database. - */ - -/*! - * \brief Returns the usage of the database. - */ -alpm_db_usage_t AlpmDataBase::usage() const -{ - alpm_db_usage_t usage; - if(!alpm_db_get_usage(m_ptr, &usage)) { - return usage; - } else { - return static_cast(ALPM_DB_USAGE_ALL); // return default usage - } -} - -/*! - * \brief Returns the server URLs. - */ -const QJsonArray &AlpmDataBase::serverUrls() const -{ - if(m_serverUrls.empty()) { - for(const char *str : servers()) { - m_serverUrls << str; - } - } - return m_serverUrls; -} - -/*! - * \brief Adds the specified server URLs. - */ -bool AlpmDataBase::addServerUrls(const QStringList &urls) -{ - m_serverUrls = QJsonArray(); - for(const auto &url : urls) { - if(alpm_db_add_server(m_ptr, url.toLocal8Bit().data()) != 0) { - return false; - } - } - return true; -} - -/*! - * \brief Returns the packages of the database. - */ -const std::map &AlpmDataBase::packages() const -{ - if(m_packages.empty()) { - for(const AlpmPackage &pkg : PackageList(alpm_db_get_pkgcache(m_ptr))) { - m_packages.emplace(QString::fromLocal8Bit(pkg.name()), pkg); - } - } - return m_packages; -} - -/*! - * \brief Returns the package names of the data base as QStringList. - */ -QStringList AlpmDataBase::packageNames() const -{ - QStringList packageNames; - packageNames.reserve(packages().size()); - for(const auto &pkg : packages()) { - packageNames << pkg.first; - } - return packageNames; -} - -/*! - * \brief Returns the package names of the data base as a QJsonArray. - */ -const QJsonArray &AlpmDataBase::packageNameJsonArray() const -{ - if(m_packageNamesJsonArray.empty()) { - for(const auto &pkg : packages()) { - m_packageNamesJsonArray << pkg.first; - } - } - return m_packageNamesJsonArray; -} - -/*! - * \brief Returns group info (group name + package names). - */ -QJsonObject AlpmDataBase::groupInfo() const -{ - QJsonObject groupInfo; - groupInfo.insert(QStringLiteral("repo"), QString::fromLocal8Bit(name())); - QJsonArray groupsArray; - for(const AlpmGroup group : groups()) { - QJsonObject info; - info.insert(QStringLiteral("name"), QString::fromLocal8Bit(group.name())); - info.insert(QStringLiteral("pkgs"), group.packageNames()); - groupsArray << info; - } - groupInfo.insert(QStringLiteral("groups"), groupsArray); - return groupInfo; -} - -void AlpmDataBase::checkForUpgrades(const QList &syncDbs, UpdateLookupResults &results) const -{ - if(syncDbs.isEmpty()) { - results.noSources = true; - } else { - for(const auto &dbPkg : packages()) { - bool orphaned = true; - for(const auto *syncDb : syncDbs) { - const auto &syncDbPkgs = syncDb->packages(); - try { - const auto &syncPkg = syncDbPkgs.at(dbPkg.first); - switch(dbPkg.second.compareVersion(syncPkg)) { - case PackageVersionComparsion::Equal: - break; // ignore equal packages - case PackageVersionComparsion::SoftwareUpgrade: - results.newVersions << makeUpdateResult(syncPkg, dbPkg.second.version()); - break; - case PackageVersionComparsion::PackageUpgradeOnly: - results.newReleases << makeUpdateResult(syncPkg, dbPkg.second.version()); - break; - case PackageVersionComparsion::NewerThenSyncVersion: - results.downgrades << makeUpdateResult(syncPkg, dbPkg.second.version()); - } - orphaned = false; - } catch(out_of_range &) { - } - } - if(orphaned) { - results.orphaned << dbPkg.second; - } - } - } -} - - -} // namespace Alpm - diff --git a/alpm/database.h b/alpm/database.h deleted file mode 100644 index dbb7ff3..0000000 --- a/alpm/database.h +++ /dev/null @@ -1,256 +0,0 @@ -#ifndef ALPM_DATABASE_H -#define ALPM_DATABASE_H - -#include "list.h" -#include "updatelookup.h" - -#include - -#include -#include - -#include - -namespace PackageManagement { - -class AlpmDataBase -{ -public: - AlpmDataBase(alpm_db_t *dataBase = nullptr, const QString &dbFile = QString(), const QString &srcDir = QString(), const QString &pkgDir = QString()); - - // operators - operator bool() const; - bool operator ==(const AlpmDataBase &other) const; - bool operator !=(const AlpmDataBase &other) const; - - // database properties - alpm_db_t *ptr() const; - const char *name() const; - const QString &dataBaseFile() const; - const QString &sourcesDirectory() const; - void setSourcesDirectory(const QString &dir); - const QString &packagesDirectory() const; - void setPackagesDirectory(const QString &dir); - alpm_siglevel_t sigLevel() const; - alpm_db_usage_t usage() const; - StringList servers() const; - bool setServers(StringList servers); - const QJsonArray &serverUrls() const; - bool addServerUrls(const QStringList &urls); - alpm_pkg_t *package(const char *name) const; - const std::map &packages() const; - QStringList packageNames() const; - const QJsonArray &packageNameJsonArray() const; - alpm_group_t *group(const char *name) const; - GroupList groups() const; - QJsonObject groupInfo() const; - PackageList search(StringList terms) const; - - // upgrade lookup - const QStringList &upgradeSources() const; - QStringList &upgradeSources(); - template - void checkForUpgrades(const std::map &syncDbPkgs, UpdateLookupResults &results) const; - void checkForUpgrades(const QList &syncDbs, UpdateLookupResults &results) const; - -private: - alpm_db_t *m_ptr; - QString m_dbFile; - QString m_srcDir; - QString m_pkgDir; - QStringList m_upgradeSources; - mutable QJsonArray m_serverUrls; - mutable std::map m_packages; - mutable QJsonArray m_packageNamesJsonArray; - mutable QJsonArray m_groupInfo; -}; - -/*! - * \brief Creates a new instance wrapping the specified database struct. - */ -inline AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString &dbFile, const QString &srcDir, const QString &pkgDir) : - m_ptr(dataBase), - m_dbFile(dbFile), - m_srcDir(srcDir), - m_pkgDir(pkgDir) -{} - -/*! - * \brief Checks whether the specified ALPM database is equal to the current instance. - */ -inline bool AlpmDataBase::operator ==(const AlpmDataBase &other) const -{ - return m_ptr == other.m_ptr; -} - -/*! - * \brief Checks whether the specified ALPM database is not equal to the current instance. - */ -inline bool AlpmDataBase::operator !=(const AlpmDataBase &other) const -{ - return m_ptr != other.m_ptr; -} - -/*! - * \brief Returns the pointer to the underlying database struct. - */ -inline alpm_db_t *AlpmDataBase::ptr() const -{ - return m_ptr; -} - -/*! - * \brief Returns the name of the database. - */ -inline const char *AlpmDataBase::name() const -{ - return alpm_db_get_name(m_ptr); -} - -/*! - * \brief Returns the path of the data base file. - */ -inline const QString &AlpmDataBase::dataBaseFile() const -{ - return m_dbFile; -} - -/*! - * \brief Returns the path of the local sources directory. - */ -inline const QString &AlpmDataBase::sourcesDirectory() const -{ - return m_srcDir; -} - -/*! - * \brief Sets the path of the local sources directory. - */ -inline void AlpmDataBase::setSourcesDirectory(const QString &dir) -{ - m_srcDir = dir; -} - -/*! - * \brief Returns the path of the local packages directory. - */ -inline const QString &AlpmDataBase::packagesDirectory() const -{ - return m_pkgDir; -} - -/*! - * \brief Sets the path of the local packages directory. - */ -inline void AlpmDataBase::setPackagesDirectory(const QString &dir) -{ - m_pkgDir = dir; -} - -/*! - * \brief Returns the signature level of the database. - */ -inline alpm_siglevel_t AlpmDataBase::sigLevel() const -{ - return alpm_db_get_siglevel(m_ptr); -} - -/*! - * \brief Returns the servers of the database. - */ -inline StringList AlpmDataBase::servers() const -{ - return alpm_db_get_servers(m_ptr); -} - -/*! - * \brief Sets the servers of the database. - */ -inline bool AlpmDataBase::setServers(StringList servers) -{ - return alpm_db_set_servers(m_ptr, servers.begin().ptr()) == 0; -} - -/*! - * \brief Returns the package with the specified \a name. - */ -inline alpm_pkg_t *AlpmDataBase::package(const char *name) const -{ - return alpm_db_get_pkg(m_ptr, name); -} - -/*! - * \brief Returns the groups. - */ -inline alpm_group_t *AlpmDataBase::group(const char *name) const -{ - return alpm_db_get_group(m_ptr, name); -} - -/*! - * \brief Returns the groups. - */ -inline GroupList AlpmDataBase::groups() const -{ - return alpm_db_get_groupcache(m_ptr); -} - -/*! - * \brief Performs a search using the build in ALPM function. - */ -inline PackageList AlpmDataBase::search(StringList terms) const -{ - return alpm_db_search(m_ptr, terms.begin().ptr()); -} - -/*! - * \brief Returns a list of databases which are used (by default) to upgrade this database. - */ -inline const QStringList &AlpmDataBase::upgradeSources() const -{ - return m_upgradeSources; -} - -/*! - * \brief Returns a list of databases which are used (by default) to upgrade this database. - */ -inline QStringList &AlpmDataBase::upgradeSources() -{ - return m_upgradeSources; -} - -/*! - * \brief Returns whether the database instance is valid or null. - */ -inline PackageManagement::AlpmDataBase::operator bool() const -{ - return m_ptr != nullptr; -} - -template -void AlpmDataBase::checkForUpgrades(const std::map &syncDbPkgs, UpdateLookupResults &results) const -{ - for(const auto &dbPkg : packages()) { - try { - const auto &syncPkg = syncDbPkgs.at(dbPkg.first); - switch(dbPkg.second.compareVersion(syncPkg)) { - case PackageVersionComparsion::Equal: - break; // ignore equal packages - case PackageVersionComparsion::SoftwareUpgrade: - results.newVersions << makeUpdateResult(syncPkg, dbPkg.second.version()); - break; - case PackageVersionComparsion::PackageUpgradeOnly: - results.newReleases << makeUpdateResult(syncPkg, dbPkg.second.version()); - break; - case PackageVersionComparsion::NewerThenSyncVersion: - results.downgrades << makeUpdateResult(syncPkg, dbPkg.second.version()); - } - } catch(std::out_of_range &) { - results.orphaned << dbPkg.second; - } - } -} - -} // namespace Alpm - -#endif // ALPM_DATABASE_H diff --git a/alpm/group.cpp b/alpm/group.cpp index d7f51e5..dbcb6ef 100644 --- a/alpm/group.cpp +++ b/alpm/group.cpp @@ -6,15 +6,16 @@ namespace PackageManagement { /*! - * \brief Returns the package names of the group as QJsonArray. + * \class AlpmGroup + * \brief The AlpmGroup class wraps an ALPM group struct. */ -QJsonArray AlpmGroup::packageNames() const + +GroupInfo::GroupInfo(const AlpmGroup &group) { - QJsonArray pkgNames; - for(AlpmPackage pkg : packages()) { - pkgNames << QString::fromLocal8Bit(pkg.name()); + name = QString::fromLocal8Bit(group.name()); + for(auto *pkg : PackageList(group.packages())) { + packages << QString::fromLocal8Bit(alpm_pkg_get_name(pkg)); } - return pkgNames; } } // namespace Alpm diff --git a/alpm/group.h b/alpm/group.h index 2a93d93..d1d5d38 100644 --- a/alpm/group.h +++ b/alpm/group.h @@ -2,14 +2,13 @@ #define ALPM_GROUP_H #include "list.h" +#include "package.h" #include -#include +#include -QT_BEGIN_NAMESPACE -class QJsonArray; -QT_END_NAMESPACE +QT_FORWARD_DECLARE_CLASS(QJsonArray) namespace PackageManagement { @@ -21,11 +20,9 @@ public: // group properties const char *name() const; PackageList packages() const; - QJsonArray packageNames() const; private: const alpm_group_t *m_ptr; - }; /*! @@ -51,6 +48,20 @@ inline PackageList AlpmGroup::packages() const return m_ptr->packages; } -} // namespace Alpm +class GroupInfo +{ +public: + GroupInfo(const QString &name, const QStringList &packages = QStringList()); + GroupInfo(const AlpmGroup &group); + QString name; + QStringList packages; +}; + +inline GroupInfo::GroupInfo(const QString &name, const QStringList &packages) : + name(name), + packages(packages) +{} + +} #endif // ALPM_GROUP_H diff --git a/alpm/list.h b/alpm/list.h index 9fa93b7..17ac9e8 100644 --- a/alpm/list.h +++ b/alpm/list.h @@ -111,6 +111,7 @@ typedef AlpmList StringList; typedef AlpmList DependencyList; typedef AlpmList PackageList; typedef AlpmList GroupList; +typedef AlpmList BackupList; } // namespace Alpm diff --git a/alpm/manager.cpp b/alpm/manager.cpp index 3590d94..f302ff5 100644 --- a/alpm/manager.cpp +++ b/alpm/manager.cpp @@ -1,11 +1,14 @@ #include "manager.h" -#include "database.h" +#include "alpmdatabase.h" #include "utilities.h" #include "list.h" #include "config.h" +#include "network/userrepository.h" + #include #include +#include #include #include @@ -22,9 +25,23 @@ using namespace ConversionUtilities; namespace PackageManagement { +/*! + * \cond + */ + constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL | ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; +inline ostream &operator <<(ostream &stream, const QString &str) +{ + stream << str.toLocal8Bit().data(); + return stream; +} + +/*! + * \endcond + */ + /*! * \brief The Manager class helps accessing ALPM. * @@ -40,12 +57,15 @@ constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL | Manager::Manager(const Config &config) : m_config(config), m_sigLevel(defaultSigLevel), - m_localFileSigLevel(ALPM_SIG_USE_DEFAULT), - m_aur(m_networkAccessManager) + m_localFileSigLevel(ALPM_SIG_USE_DEFAULT) { alpm_errno_t err; if(!(m_handle = alpm_initialize(config.alpmRootDir().toLocal8Bit().data(), config.alpmDbPath().toLocal8Bit().data(), &err))) { - throw runtime_error(string("Cannot initialize alpm: ") + alpm_strerror(err)); + throw runtime_error(string("Cannot initialize ALPM: ") + alpm_strerror(err)); + } + m_localDb = make_unique(alpm_get_localdb(m_handle), config.alpmDbPath()); + if(config.isAurEnabled()) { + m_userRepo = make_unique(m_networkAccessManager); } } @@ -60,41 +80,75 @@ Manager::~Manager() /*! * \brief Returns the package with the specified name from the specified database. */ -AlpmPackage Manager::packageFromSyncDataBase(const QString &dbName, const QString &pkgName) const +AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &pkgName) { - try { - if(dbName == QLatin1String("local")) { - return localDataBase().packages().at(pkgName); - } else { - return syncDataBases().at(dbName).packages().at(pkgName); - } - } catch(out_of_range &) { - return AlpmPackage(); + if(auto *db = dataBaseByName(dbName)) { + return static_cast(db->packageByName(pkgName)); + } else { + return nullptr; } } +/*! + * \brief Returns the package with the specified name from the specified database. + */ +const AlpmPackage *Manager::packageFromDataBase(const QString &dbName, const QString &pkgName) const +{ + if(const auto *db = dataBaseByName(dbName)) { + return static_cast(db->packageByName(pkgName)); + } else { + return nullptr; + } +} + +/*! + * \brief Gets the package with the specified \a name from one of the sync databases. + */ +AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName) +{ + for(const auto &dbEntry : syncDataBases()) { + if(auto *pkg = dbEntry.second->packageByName(pkgName)) { + return static_cast(pkg); + } + } + return nullptr; +} + +/*! + * \brief Gets the package with the specified \a name from one of the sync databases. + */ +const AlpmPackage *Manager::packageFromSyncDataBases(const QString &pkgName) const +{ + for(const auto &dbEntry : syncDataBases()) { + if(const auto *pkg = dbEntry.second->packageByName(pkgName)) { + return static_cast(pkg); + } + } + return nullptr; +} + /*! * \brief Creates a new package instance for the specified package file. * * Verifies the integrity of the file if the option is set. */ -AlpmOwnershipPackage Manager::packageFromFile(const char *fileName, bool verifyIntegrity) +unique_ptr Manager::packageFromFile(const char *fileName, bool verifyIntegrity) { alpm_pkg_t *pkg; if(alpm_pkg_load(m_handle, fileName, verifyIntegrity, static_cast(m_localFileSigLevel), &pkg) != 0) { throw runtime_error(string("Unable to load package file: ") + alpm_strerror(alpm_errno(m_handle))); } else { - return AlpmOwnershipPackage(pkg); + return make_unique(pkg); } } /*! * \brief Sets the install reason for the specified \a package. */ -void Manager::setInstallReason(AlpmPackage package, alpm_pkgreason_t reason) +void Manager::setInstallReason(AlpmPackage *package, alpm_pkgreason_t reason) { - if(alpm_pkg_set_reason(package.ptr(), reason)) { - throw runtime_error(string("Unable to set install reason of the package ") + package.name() + ": " + alpm_strerror(alpm_errno(m_handle))); + if(alpm_pkg_set_reason(package->ptr(), reason)) { + throw runtime_error(string("Unable to set install reason of the package ") + package->name().toLocal8Bit().data() + ": " + alpm_strerror(alpm_errno(m_handle))); } } @@ -302,12 +356,12 @@ void Manager::applyPacmanConfig() cerr << "Warning: Included file \"" << path << "\" has no values." << endl; } } + 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() << dbName; + localDataBase()->upgradeSources() << emplaced.first->second.get(); } - m_syncDbs.emplace(dbName, AlpmDataBase(db, QStringLiteral("%1/sync/%2").arg(m_config.alpmDbPath(), dbName))); } else { cerr << "Error: Unable to add sync database [" << scope.first << "]" << endl; } @@ -324,18 +378,19 @@ void Manager::applyPacmanConfig() */ void Manager::applyRepoIndexConfig() { + // check whether an entry already exists, if not create a new one for(const RepoEntry &repoEntry : m_config.repoEntries()) { - AlpmDataBase *syncDb; + AlpmDataBase *syncDb = nullptr; try { - syncDb = &m_syncDbs.at(repoEntry.name()); + syncDb = m_syncDbs.at(repoEntry.name()).get(); cerr << "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 \"" - << repoEntry.name().toLocal8Bit().data() << "\" has already been added from the Pacman config." << endl; + << repoEntry.name() << "\" has already been added from the Pacman config." << endl; } if(repoEntry.sigLevel()) { - cerr << "Warning: Can't use sig level path specified in repo index config because the repo \"" - << repoEntry.name().toLocal8Bit().data() << "\" has already been added from the Pacman config." << endl; + cerr << "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()); @@ -345,21 +400,39 @@ void Manager::applyRepoIndexConfig() } 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; } 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(), AlpmDataBase(db, repoEntry.dataBasePath(), repoEntry.sourceDir(), repoEntry.packageDir())); + auto emplaced = m_syncDbs.emplace(repoEntry.name(), make_unique(db, m_config.alpmDbPath())); if(emplaced.second) { - syncDb = &emplaced.first->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().toLocal8Bit().data() << "]" << endl; + cerr << "Added database [" << repoEntry.name() << "]" << endl; } } } } if(syncDb) { syncDb->addServerUrls(repoEntry.servers()); - syncDb->upgradeSources() << repoEntry.upgradeSources(); } } + // add upgrade sources + for(const RepoEntry &repoEntry : m_config.repoEntries()) { + try { + auto &upgradeSources = m_syncDbs.at(repoEntry.name())->upgradeSources(); + for(const auto &upgradeSourceName : repoEntry.upgradeSources()) { + if(auto *source = repositoryByName(upgradeSourceName)) { + upgradeSources << source; + } else { + cerr << "Warning: The specified upgrade source \"" << upgradeSourceName << "\" can not be found and will be ignored." << endl; + } + } + } catch(const out_of_range &) { + // entry should have been added before + } + } + } /*! @@ -372,56 +445,15 @@ void Manager::unregisterSyncDataBases() } } -/*! - * \brief Returns the local data base. - */ -const AlpmDataBase &Manager::localDataBase() const -{ - if(!m_localDb) { - QMutexLocker locker(&m_localDbMutex); - if(!m_localDb) { - m_localDb = alpm_get_localdb(m_handle); - } - } - return m_localDb; -} - -/*! - * \brief Returns the local data base. - */ -AlpmDataBase &Manager::localDataBase() -{ - if(!m_localDb) { - m_localDb = alpm_get_localdb(m_handle); - } - return m_localDb; -} - /*! * \brief Returns a list of all sync databases. * \remarks Sync databases must be registered with parsePacmanConfig() before. */ -const std::map &Manager::syncDataBases() const +const map > &Manager::syncDataBases() const { return m_syncDbs; // m_syncDbs has been filled when the databases were registered } -/*! - * \brief Returns basic information about the specified repository. - */ -QJsonObject Manager::basicRepoInfo(AlpmDataBase db, const QString &name, const QString &desc) const -{ - QJsonObject repoInfo; - repoInfo.insert(QStringLiteral("name"), name); - repoInfo.insert(QStringLiteral("desc"), desc); - repoInfo.insert(QStringLiteral("servers"), db.serverUrls()); - repoInfo.insert(QStringLiteral("usage"), Utilities::usageStrings(db.usage())); - repoInfo.insert(QStringLiteral("sigLevel"), Utilities::sigLevelStrings(db.sigLevel())); - repoInfo.insert(QStringLiteral("upgradeSources"), QJsonArray::fromStringList(db.upgradeSources())); - repoInfo.insert(QStringLiteral("packages"), db.packageNameJsonArray()); - return repoInfo; -} - /*! * \brief Returns basic information about all repositories known to the manager. * @@ -433,17 +465,22 @@ const QJsonArray &Manager::basicRepoInfo() const if(m_basicRepoInfo.isEmpty()) { QMutexLocker locker(&m_basicRepoInfoMutex); if(m_basicRepoInfo.isEmpty()) { - m_basicRepoInfo << basicRepoInfo(localDataBase(), QStringLiteral("local"), QStringLiteral("The local database.")); - auto const &syncDbs = syncDataBases(); - for(const auto &syncDb : syncDbs) { + // add local data base + m_basicRepoInfo << localDataBase()->basicInfo(); + // add sync data bases + for(const auto &syncDb : syncDataBases()) { // check if the "sync" database is actually used for syncing - auto usage = syncDb.second.usage(); + auto usage = syncDb.second->usage(); if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) { - m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The sync database »%1«.").arg(syncDb.first)); + m_basicRepoInfo << syncDb.second->basicInfo(); } else { - m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The database »%1«.").arg(syncDb.first)); + m_basicRepoInfo << syncDb.second->basicInfo(); } } + // add AUR + if(config().isAurEnabled()) { + m_basicRepoInfo << userRepository()->basicInfo(); + } } } return m_basicRepoInfo; @@ -452,26 +489,38 @@ const QJsonArray &Manager::basicRepoInfo() const /*! * \brief Returns package information for the specified selection of packages. */ -const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full) const +const QJsonArray Manager::packageInfo(const QJsonObject &pkgSelection, bool full) const { QJsonArray pkgInfos; - for(const auto &pkgSelJsonVal : pkgSelection) { - QJsonObject pkgSel = pkgSelJsonVal.toObject(); - if(!pkgSel.isEmpty()) { - QString repoName = pkgSel.value(QStringLiteral("repo")).toString(); - QString pkgName = pkgSel.value(QStringLiteral("name")).toString(); - AlpmPackage pkg; - QJsonObject pkgInfo; - if(!repoName.isEmpty() && !pkgName.isEmpty() && (pkg = packageFromSyncDataBase(repoName, pkgName))) { - pkgInfo = full ? pkg.fullInfo() : pkg.basicInfo(); - } else { - pkgInfo.insert(QStringLiteral("error"), QStringLiteral("na")); + for(auto i = pkgSelection.constBegin(), end = pkgSelection.constEnd(); i != end; ++i) { + if(auto *repo = repositoryByName(i.key())) { + for(const auto &entry : i.value().toArray()) { + const auto entryObj = entry.toObject(); + const auto pkgName = entryObj.value(QStringLiteral("name")).toString(); + if(!pkgName.isEmpty()) { + QJsonObject pkgInfo; + if(auto *pkg = repo->packageByName(pkgName)) { + pkgInfo = full ? pkg->fullInfo() : pkg->basicInfo(); + } else { + pkgInfo.insert(QStringLiteral("error"), QStringLiteral("na")); + } + pkgInfo.insert(QStringLiteral("name"), pkgName); + pkgInfo.insert(QStringLiteral("repo"), repo->name()); + const auto index = entryObj.value(QStringLiteral("index")); + if(!index.isNull() && !index.isUndefined()) { + pkgInfo.insert(QStringLiteral("index"), index); + } + pkgInfos << pkgInfo; + } } - pkgInfo.insert(QStringLiteral("name"), pkgName); - pkgInfo.insert(QStringLiteral("repo"), repoName); - pkgInfo.insert(QStringLiteral("index"), pkgSel.value(QStringLiteral("index"))); - pkgInfos << pkgInfo; + } else { + // specified repository can not be found + QJsonObject errorObj; + errorObj.insert(QStringLiteral("repo"), i.key()); + errorObj.insert(QStringLiteral("error"), QStringLiteral("na")); + pkgInfos << errorObj; } + } return pkgInfos; } @@ -484,9 +533,9 @@ 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(); + m_groupInfo << db.second->groupInfo(); } } } @@ -495,41 +544,70 @@ const QJsonArray &Manager::groupInfo() const /*! * \brief Returns the ALPM database with the specified name. - * \throws Throws std::out_of_range if the specified database is unknown to the manager. */ -const AlpmDataBase &Manager::dataBaseByName(const QString &dbName) const +const AlpmDataBase *Manager::dataBaseByName(const QString &dbName) const { - if(dbName == QLatin1String("local")) { + if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { return localDataBase(); } else { - return m_syncDbs.at(dbName); + try { + return m_syncDbs.at(dbName).get(); + } catch(const out_of_range &) { + return nullptr; + } } } /*! * \brief Returns the ALPM database with the specified name. - * \throws Throws std::out_of_range if the specified database is unknown to the manager. */ -AlpmDataBase &Manager::dataBaseByName(const QString &dbName) +AlpmDataBase *Manager::dataBaseByName(const QString &dbName) { - if(dbName == QLatin1String("local")) { + if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { return localDataBase(); } else { - return m_syncDbs.at(dbName); + try { + return m_syncDbs.at(dbName).get(); + } catch(const out_of_range &) { + return nullptr; + } } } /*! - * \brief Checks for upgrades availabel to the specified database. - * - * The \a request must have the following values: - * - db: Specifies the name of the database to check for upgrades. - * - syncdbs: Array with the names of the databases used as upgrade sources. - * If not present, appropriate upgrade sources will be determined automatically. + * \brief Returns the package source with the specified name. */ -void Manager::invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallback callback) const +const Repository *Manager::repositoryByName(const QString &name) const { - new UpdateLookup(*this, request, callback); // this object will delete itself + if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { + return localDataBase(); + } else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) { + return userRepository(); + } else { + try { + return m_syncDbs.at(name).get(); + } catch(const out_of_range &) { + return nullptr; + } + } +} + +/*! + * \brief Returns the package source with the specified name. + */ +Repository *Manager::repositoryByName(const QString &name) +{ + if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { + return localDataBase(); + } else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) { + return userRepository(); + } else { + try { + return m_syncDbs.at(name).get(); + } catch(const out_of_range &) { + return nullptr; + } + } } /*! @@ -537,18 +615,10 @@ void Manager::invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallba * * Appropriate upgrade sources will be determined automatically; does not check the AUR. */ -const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db) const +const UpgradeLookupResults Manager::checkForUpgrades(AlpmDataBase *db) const { - UpdateLookupResults results; - QList syncDbSel; - for(const auto &syncDbName : db.upgradeSources()) { - try { - syncDbSel << &(syncDataBases().at(syncDbName)); - } catch(out_of_range &) { - ; - } - } - db.checkForUpgrades(syncDbSel, results); + UpgradeLookupResults results; + db->checkForUpgrades(results); return results; } diff --git a/alpm/manager.h b/alpm/manager.h index 7941732..9f265da 100644 --- a/alpm/manager.h +++ b/alpm/manager.h @@ -1,10 +1,8 @@ #ifndef ALPM_MANAGER_H #define ALPM_MANAGER_H -#include "database.h" -#include "updatelookup.h" - -#include "network/aurquery.h" +#include "upgradelookup.h" +#include "alpmpackage.h" #include @@ -14,11 +12,13 @@ #include #include +#include namespace PackageManagement { class Config; -template class UpdateLookupResults; +class UserRepository; +class AlpmDataBase; class Manager { @@ -42,30 +42,33 @@ public: void applyRepoIndexConfig(); void unregisterSyncDataBases(); - const AurQuery &aurQuery() const; - AurQuery &aurQuery(); + const UserRepository *userRepository() const; + UserRepository *userRepository(); // package lookup - AlpmPackage packageFromSyncDataBase(const QString &dbName, const QString &pkgName) const; - AlpmPackage packageFromSyncDataBases(const char *name) const; - AlpmOwnershipPackage packageFromFile(const char *fileName, bool verifyIntegrity = false); - bool isPackageIgnored(AlpmPackage package) const; - void setInstallReason(AlpmPackage package, alpm_pkgreason_t reason); + 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); + bool isPackageIgnored(const AlpmPackage *package) const; + void setInstallReason(AlpmPackage *package, alpm_pkgreason_t reason); - // database 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 UpdateLookupResults checkForUpgrades(const AlpmDataBase &db) const; + // 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); + const Repository *repositoryByName(const QString &name) const; + Repository *repositoryByName(const QString &name); + const UpgradeLookupResults checkForUpgrades(AlpmDataBase *db) const; // JSON serialization, handling JSON requests - QJsonObject basicRepoInfo(AlpmDataBase db, const QString &name, const QString &desc = QString()) const; + const QJsonObject basicRepoInfo(const Repository *packageSource) const; const QJsonArray &basicRepoInfo() const; - const QJsonArray packageInfo(const QJsonArray &pkgSelection, bool full = true) const; + const QJsonArray packageInfo(const QJsonObject &pkgSelection, bool full = true) const; const QJsonArray &groupInfo() const; - void invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallback callback) const; private: void cleanup(); @@ -76,10 +79,9 @@ private: int m_localFileSigLevel; QString m_pacmanCacheDir; QNetworkAccessManager m_networkAccessManager; - AurQuery m_aur; - mutable AlpmDataBase m_localDb; - mutable QMutex m_localDbMutex; - mutable std::map m_syncDbs; + std::unique_ptr m_userRepo; + std::unique_ptr m_localDb; + std::map > m_syncDbs; mutable QJsonArray m_basicRepoInfo; mutable QMutex m_basicRepoInfoMutex; mutable QJsonArray m_groupInfo; @@ -135,20 +137,12 @@ inline void Manager::setLocalFileSigLevel(int sigLevel) m_localFileSigLevel = sigLevel; } -/*! - * \brief Gets the package with the specified \a name from one of the sync databases. - */ -inline AlpmPackage Manager::packageFromSyncDataBases(const char *name) const -{ - return alpm_find_dbs_satisfier(m_handle, alpm_get_syncdbs(m_handle), name); -} - /*! * \brief Returns whether the specified \a package is ignored. */ -inline bool Manager::isPackageIgnored(AlpmPackage package) const +inline bool Manager::isPackageIgnored(const AlpmPackage *package) const { - return alpm_pkg_should_ignore(m_handle, package.ptr()); + return alpm_pkg_should_ignore(m_handle, const_cast(package->ptr())); } /*! @@ -160,14 +154,30 @@ inline const QString &Manager::pacmanCacheDir() const return m_pacmanCacheDir; } -inline const AurQuery &Manager::aurQuery() const +inline const UserRepository *Manager::userRepository() const { - return m_aur; + return m_userRepo.get(); } -inline AurQuery &Manager::aurQuery() +inline UserRepository *Manager::userRepository() { - return m_aur; + return m_userRepo.get(); +} + +/*! + * \brief Returns the local data base. + */ +inline const AlpmDataBase *Manager::localDataBase() const +{ + return m_localDb.get(); +} + +/*! + * \brief Returns the local data base. + */ +inline AlpmDataBase *Manager::localDataBase() +{ + return m_localDb.get(); } } diff --git a/alpm/mingwbundle.cpp b/alpm/mingwbundle.cpp index 133cd01..13cd40c 100644 --- a/alpm/mingwbundle.cpp +++ b/alpm/mingwbundle.cpp @@ -1,13 +1,19 @@ #include "mingwbundle.h" +#include "utilities.h" #include "manager.h" #include #include #include +#include +#include -#include #include +#include +#include +#include +#include #include #include @@ -16,18 +22,25 @@ using namespace std; namespace PackageManagement { +using namespace Utilities; + const string prefix("mingw-w64-"); -MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages) : +MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages, const ApplicationUtilities::StringVector &iconPackages) : m_manager(manager) { + cerr << "Resolving dependencies ..." << endl; string missing; + // add mingw-w64 packages for(const auto &pkgName : packages) { bool found = false; for(const auto &syncDb : manager.syncDataBases()) { - if(auto pkg = syncDb.second.package(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data())) { + if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data()))) { if(missing.empty()) { - m_packages.emplace_back(syncDb.second, pkg); + decltype(m_packages)::value_type entry(syncDb.second.get(), pkg); + if(find(m_packages.cbegin(), m_packages.cend(), entry) == m_packages.cend()) { + m_packages.emplace_back(entry); + } } addDependencies(pkg); found = true; @@ -39,50 +52,125 @@ MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::Str missing.append(pkgName); } } + // add additional icon packages + for(const auto &pkgName : iconPackages) { + bool found = false; + 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); + if(find(m_packages.cbegin(), m_packages.cend(), entry) == m_packages.cend()) { + m_packages.emplace_back(entry); + } + } + found = true; + break; + } + } + if(!found) { + missing.push_back(' '); + missing.append(pkgName); + } + } if(!missing.empty()) { throw runtime_error("The following packages can not be found:" + missing); } else { cerr << "Adding the following packages:"; for(const auto &pkg : m_packages) { - cerr << ' ' << pkg.second.name(); + cerr << ' ' << pkg.second->name().toLocal8Bit().data(); } cerr << endl; } } +void MingwBundle::addDependencies(const Package *pkg) +{ + string missing; + 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()) { + if(const auto *pkg = syncDbEntry.second->packageProviding(dep)) { + if(missing.empty()) { + decltype(m_packages)::value_type entry(syncDbEntry.second.get(), pkg); + if(find(m_packages.cbegin(), m_packages.cend(), entry) == m_packages.cend()) { + m_packages.emplace_back(entry); + } + } + addDependencies(pkg); + found = true; + break; + } + } + if(!found) { + missing.push_back(' '); + missing.append(dep.name.toLocal8Bit().data()); + } + } + } + if(!missing.empty()) { + throw runtime_error("The following dependencies of the " + string(pkg->name().toLocal8Bit().data()) + " package can not be resolved:" + missing); + } +} + enum class RelevantFileType { Binary, - Translation + Translation, + QtTranslation, + QtPlugin, + Icon }; enum class RelevantFileArch { x86_64, - i686 + i686, + Any }; struct RelevantFile { - RelevantFile(const KArchiveFile *file, const RelevantFileType type, const RelevantFileArch arch) : + RelevantFile(const KArchiveFile *file, const RelevantFileType type, const RelevantFileArch arch, const QString &subDir = QString()) : file(file), fileType(type), - arch(arch) + arch(arch), + subDir(subDir) {} const KArchiveFile *file; RelevantFileType fileType; RelevantFileArch arch; + QString subDir; }; struct PkgFileInfo { - PkgFileInfo(const QString &path) : path(path), failure(false) {} + PkgFileInfo(const QString &name, const QString &path) : + name(name), + path(path), + failure(false) + {} + QString name; QString path; unique_ptr archive; list relevantFiles; bool failure; }; +void addEntries(PkgFileInfo &pkgFileInfo, RelevantFileType fileType, const KArchiveDirectory *dir, const QString &relPath = QString()) +{ + QString newPath = relPath.isEmpty() ? dir->name() : relPath % QChar('/') % dir->name(); + for(const auto &entryName : dir->entries()) { + if(auto *entry = dir->entry(entryName)) { + if(entry->isDirectory()) { + addEntries(pkgFileInfo, fileType, static_cast(entry), newPath); + } else if(entry->isFile()) { + pkgFileInfo.relevantFiles.emplace_back(static_cast(entry), fileType, RelevantFileArch::Any, newPath); + } + } + } +} + void getFiles(PkgFileInfo &pkgFileInfo) { pkgFileInfo.archive = make_unique(pkgFileInfo.path); @@ -104,65 +192,190 @@ void getFiles(PkgFileInfo &pkgFileInfo) if(entry->isFile()) { const auto *binFile = static_cast(entry); pkgFileInfo.relevantFiles.emplace_back(binFile, RelevantFileType::Binary, root.first); - cerr << entryName.toLocal8Bit().data() << endl; + } + } + } + } + } + const auto *libEntry = rootDir->entry(QStringLiteral("lib")); + if(libEntry && libEntry->isDirectory()) { + const auto *libDir = static_cast(libEntry); + const auto *qtEntry = libDir->entry("qt"); + if(qtEntry && qtEntry->isDirectory()) { + const auto *qtDir = static_cast(qtEntry); + const auto *pluginsEntry = qtDir->entry(QStringLiteral("plugins")); + if(pluginsEntry && pluginsEntry->isDirectory()) { + const auto *pluginsDir = static_cast(pluginsEntry); + for(const auto &pluginCategory : pluginsDir->entries()) { + const auto *categoryEntry = pluginsDir->entry(pluginCategory); + if(categoryEntry && categoryEntry->isDirectory()) { + const auto *categoryDir = static_cast(categoryEntry); + for(const auto &entryName : categoryDir->entries()) { + if(const auto *pluginEntry = categoryDir->entry(entryName)) { + if(pluginEntry->isFile()) { + const auto *pluginFile = static_cast(pluginEntry); + pkgFileInfo.relevantFiles.emplace_back(pluginFile, RelevantFileType::QtPlugin, root.first, categoryDir->name()); + } + } + } + } + } + } + } + } + const auto *shareEntry = rootDir->entry(QStringLiteral("share")); + if(shareEntry && shareEntry->isDirectory()) { + const auto *shareDir = static_cast(shareEntry); + const auto *qtEntry = shareDir->entry(QStringLiteral("qt")); + if(qtEntry && qtEntry->isDirectory()) { + const auto *qtDir = static_cast(qtEntry); + const auto *trEntry = qtDir->entry(QStringLiteral("translations")); + if(trEntry && trEntry->isDirectory()) { + const auto trDir = static_cast(trEntry); + for(const auto &entryName : trDir->entries()) { + if(entryName.endsWith(QLatin1String(".qm"))) { + if(const auto *qmEntry = trDir->entry(entryName)) { + if(qmEntry->isFile()) { + const auto *qmFile = static_cast(qmEntry); + pkgFileInfo.relevantFiles.emplace_back(qmFile, RelevantFileType::QtTranslation, root.first); + } + } + } + } + } + } + if(pkgFileInfo.name.compare(QLatin1String("qt"))) { + const auto *appEntry = shareDir->entry(pkgFileInfo.name); + if(appEntry && appEntry->isDirectory()) { + const auto *appDir = static_cast(appEntry); + const auto *trEntry = appDir->entry(QStringLiteral("translations")); + if(trEntry && trEntry->isDirectory()) { + const auto trDir = static_cast(trEntry); + for(const auto &entryName : trDir->entries()) { + if(entryName.endsWith(QLatin1String(".qm"))) { + if(const auto *qmEntry = trDir->entry(entryName)) { + if(qmEntry->isFile()) { + const auto *qmFile = static_cast(qmEntry); + pkgFileInfo.relevantFiles.emplace_back(qmFile, RelevantFileType::Translation, root.first); + } + } + } } } } } } } - } - + const auto *iconsEntry = pkgFileInfo.archive->directory()->entry(QStringLiteral("usr/share/icons")); + if(iconsEntry && iconsEntry->isDirectory()) { + const auto *iconsDir = static_cast(iconsEntry); + for(const auto &themeName : iconsDir->entries()) { + const auto *themeEntry = iconsDir->entry(themeName); + if(themeEntry && themeEntry->isDirectory()) { + addEntries(pkgFileInfo, RelevantFileType::Icon, static_cast(themeEntry)); + } + } + } } else { pkgFileInfo.failure = true; } } -void MingwBundle::createBundle(const string &path) const +void MingwBundle::createBundle(const string &targetDir, const string &targetName, const string &targetFormat) const { + cerr << "Gathering relevant files ..." << endl; + // get package files list pkgFiles; for(const auto &entry : m_packages) { QString pkgFile; - if(!entry.first.packagesDirectory().isEmpty()) { - pkgFile = QStringLiteral("%1/%2").arg(entry.first.packagesDirectory(), QString::fromLocal8Bit(entry.second.fileName())); + if(!entry.first->packagesDirectory().isEmpty()) { + pkgFile = entry.first->packagesDirectory() % QChar('/') % entry.second->fileName(); } if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) { if(!m_manager.pacmanCacheDir().isEmpty()) { - pkgFile = QStringLiteral("%1/%2").arg(m_manager.pacmanCacheDir(), QString::fromLocal8Bit(entry.second.fileName())); + pkgFile = m_manager.pacmanCacheDir() % QChar('/') % entry.second->fileName(); } if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) { - throw runtime_error("The package file " + string(entry.second.fileName()) + " can't be found."); + throw runtime_error("The package file " + string(entry.second->fileName().toLocal8Bit().data()) + " can't be found."); // TODO: download package from mirror } } - pkgFiles.emplace_back(pkgFile); + pkgFiles.emplace_back(entry.second->name().startsWith(QLatin1String("mingw-w64-")) ? entry.second->name().mid(10) : entry.second->name(), pkgFile); } + // get relevant files from packages QtConcurrent::blockingMap(pkgFiles, getFiles); -} - -void MingwBundle::addDependencies(const AlpmPackage &pkg) -{ - string missing; - for(const auto &depInfo : pkg.dependencies()) { - bool found = false; - for(const auto &syncDb : m_manager.syncDataBases()) { - if(auto pkg = syncDb.second.package(depInfo->name)) { - if(missing.empty()) { - m_packages.emplace_back(syncDb.second, pkg); - } - addDependencies(pkg); - found = true; - break; - } - } - if(!found) { - missing.push_back(' '); - missing.append(depInfo->name); + // check whether all packages could be opened + string failed; + for(const auto &pkgFile : pkgFiles) { + if(pkgFile.failure) { + failed.push_back(' '); + failed.append(pkgFile.path.toLocal8Bit().data()); } } - if(!missing.empty()) { - throw runtime_error("The following dependencies of the " + string(pkg.name()) + " package can not be resolved:" + missing); + if(!failed.empty()) { + throw runtime_error("Unable to open the following package files:" + failed); + } + // make a list with package info to be included in the target archive + QJsonArray pkgArray; + for(const auto &entry : m_packages) { + pkgArray << entry.second->basicInfo(true); + } + QJsonDocument pkgList; + pkgList.setArray(pkgArray); + QByteArray pkgListBytes = pkgList.toJson(); + // make target archive + static const QString user(QStringLiteral("root")); + static const QString &group = user; + static const pair roots[] = { + make_pair(RelevantFileArch::x86_64, QStringLiteral("x86_64-w64-mingw32")), + make_pair(RelevantFileArch::i686, QStringLiteral("i686-w64-mingw32")) + }; + 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; + unique_ptr targetArchive; + if(targetFormat == "7z") { + targetArchive = make_unique(targetPath); + } else if(targetFormat == "zip") { + targetArchive = make_unique(targetPath); + } else if(ConversionUtilities::startsWith(targetFormat, "tar")) { + targetArchive = make_unique(targetPath); + } else { + throw runtime_error("Specified archive format \"" + targetFormat + "\" is unknown."); + } + if(targetArchive->open(QIODevice::WriteOnly)) { + // add package list + targetArchive->writeFile(root.second % QStringLiteral("/var/lib/repoindex/packages.list"), pkgListBytes, 0100644, user, group); + // add relevant files from packages + for(const auto &pkgFile : pkgFiles) { + for(const RelevantFile &relevantFile : pkgFile.relevantFiles) { + if(relevantFile.arch == RelevantFileArch::Any || relevantFile.arch == root.first) { + switch(relevantFile.fileType) { + case RelevantFileType::Binary: + targetArchive->writeFile(root.second % QStringLiteral("/bin/") % relevantFile.file->name(), relevantFile.file->data(), 0100755, user, group); + break; + case RelevantFileType::Translation: + targetArchive->writeFile(root.second % QStringLiteral("/share/") % pkgFile.name % QStringLiteral("/translations/") % relevantFile.file->name(), relevantFile.file->data(), 0100644, user, group); + break; + case RelevantFileType::QtTranslation: + targetArchive->writeFile(root.second % QStringLiteral("/share/qt/translations/") % relevantFile.file->name(), relevantFile.file->data(), 0100644, user, group); + break; + case RelevantFileType::QtPlugin: + targetArchive->writeFile(root.second % QStringLiteral("/bin/") % relevantFile.subDir % QChar('/') % relevantFile.file->name(), relevantFile.file->data(), 0100755, user, group); + break; + case RelevantFileType::Icon: + targetArchive->writeFile(root.second % QStringLiteral("/share/icons/") % relevantFile.subDir % QChar('/') % relevantFile.file->name(), relevantFile.file->data(), 0100644, user, group); + break; + } + } + } + } + } else if(targetArchive->device()) { + throw runtime_error("Unable to open target archive: " + string(targetArchive->device()->errorString().toLocal8Bit().data())); + } else { + throw runtime_error("Unable to open target archive."); + } } } diff --git a/alpm/mingwbundle.h b/alpm/mingwbundle.h index be618b3..41eba9d 100644 --- a/alpm/mingwbundle.h +++ b/alpm/mingwbundle.h @@ -2,7 +2,7 @@ #define PACKAGEMANAGEMENT_MINGWBUNDLE_H #include "package.h" -#include "database.h" +#include "alpmdatabase.h" #include @@ -15,15 +15,15 @@ class Manager; class MingwBundle { public: - MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages); + MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages, const ApplicationUtilities::StringVector &iconPackages); - void createBundle(const std::string &path) const; + void createBundle(const std::string &targetDir, const std::string &targetName, const std::string &targetFormat) const; private: - void addDependencies(const AlpmPackage &pkg); + 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 30a7007..b47d855 100644 --- a/alpm/package.cpp +++ b/alpm/package.cpp @@ -1,5 +1,7 @@ #include "package.h" -#include "database.h" +#include "alpmdatabase.h" +#include "utilities.h" +#include "repository.h" #include #include @@ -11,82 +13,191 @@ using namespace ChronoUtilities; namespace PackageManagement { +/*! + * \class The Package class holds meta information about an + * Arch Linux package. + */ + +/*! + * \brief Constructs a new package instance. + * + * Since it is intenced to use the Package class as base class only, + * this constructor is protected. + */ +Package::Package(const QString &name, Repository *source) : + m_origin(PackageOrigin::Unknown), + m_source(source), + m_hasGeneralInfo(false), + m_name(name), + m_hasInstallScript(false), + m_hasBuildRelatedMetaData(false), + m_hasInstallRelatedMetaData(false), + m_validationMethods(ALPM_PKG_VALIDATION_UNKNOWN), + m_installReason(ALPM_PKG_REASON_EXPLICIT), + m_id(-1), + m_categoryId(-1), + m_votes(-1) +{ + // initialization must be done in derived class +} + +Package::~Package() +{} + +bool Package::matches(const QString &name, const QString &version, const Dependency &dependency) +{ + 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; + case ALPM_DEP_MOD_GE: + return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThenSyncVersion; + case ALPM_DEP_MOD_LE: + return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade; + case ALPM_DEP_MOD_GT: + return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::NewerThenSyncVersion; + case ALPM_DEP_MOD_LT: + return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade; + default: + ; + } + } + return false; +} + /*! * \cond */ -inline QString qstr(const char *str) -{ - return QString::fromLocal8Bit(str); -} +namespace Utilities { -inline QJsonArray qjarry(StringList list) +inline void put(QJsonObject &obj, const QString &key, const QJsonValue &value) { - QJsonArray jsonArray; - for(const char *str : list) { - jsonArray << qstr(str); + if(!value.isNull()) { + obj.insert(key, value); } - return jsonArray; } -inline QJsonArray qjarry(DependencyList list) +inline void put(QJsonObject &obj, const QString &key, const DateTime dateTime) { - QJsonArray jsonArray; - for(alpm_depend_t *dep : list) { - QJsonObject depObj; - depObj.insert(QStringLiteral("name"), dep->name); - depObj.insert(QStringLiteral("desc"), dep->desc); - depObj.insert(QStringLiteral("ver"), dep->version); - switch(dep->mod) { - case ALPM_DEP_MOD_ANY: - depObj.insert(QStringLiteral("mod"), QStringLiteral("any")); - break; - case ALPM_DEP_MOD_EQ: - depObj.insert(QStringLiteral("mod"), QStringLiteral("eq")); - break; - case ALPM_DEP_MOD_GE: - depObj.insert(QStringLiteral("mod"), QStringLiteral("ge")); - break; - case ALPM_DEP_MOD_LE: - depObj.insert(QStringLiteral("mod"), QStringLiteral("le")); - break; - case ALPM_DEP_MOD_GT: - depObj.insert(QStringLiteral("mod"), QStringLiteral("gt")); - break; - case ALPM_DEP_MOD_LT: - depObj.insert(QStringLiteral("mod"), QStringLiteral("lt")); - break; + if(!dateTime.isNull()) { + put(obj, key, QString::fromLocal8Bit(dateTime.toString().data())); + } +} + +inline void put(QJsonObject &obj, const QString &key, const QStringList &values) +{ + if(!values.isEmpty()) { + put(obj, key, QJsonArray::fromStringList(values)); + } +} + +void put(QJsonObject &obj, const QString &key, const QList &dependencies) +{ + if(!dependencies.isEmpty()) { + QJsonArray jsonArray; + for(const auto &dep : dependencies) { + QJsonObject depObj; + depObj.insert(QStringLiteral("name"), dep.name); + depObj.insert(QStringLiteral("ver"), dep.version); + switch(dep.mode) { + case ALPM_DEP_MOD_ANY: + depObj.insert(QStringLiteral("mod"), QStringLiteral("any")); + break; + case ALPM_DEP_MOD_EQ: + depObj.insert(QStringLiteral("mod"), QStringLiteral("eq")); + break; + case ALPM_DEP_MOD_GE: + depObj.insert(QStringLiteral("mod"), QStringLiteral("ge")); + break; + case ALPM_DEP_MOD_LE: + depObj.insert(QStringLiteral("mod"), QStringLiteral("le")); + break; + case ALPM_DEP_MOD_GT: + depObj.insert(QStringLiteral("mod"), QStringLiteral("gt")); + break; + case ALPM_DEP_MOD_LT: + depObj.insert(QStringLiteral("mod"), QStringLiteral("lt")); + break; + } + jsonArray << depObj; } - jsonArray << depObj; + put(obj, key, jsonArray); } - return jsonArray; } -inline QJsonArray qjarry(_alpm_pkgvalidation_t validation) -{ - QJsonArray jsonArray; - if(validation & 0x1) { - jsonArray << QStringLiteral("none"); - } - if(validation & 0x2) { - jsonArray << QStringLiteral("MD5"); - } - if(validation & 0x4) { - jsonArray << QStringLiteral("SHA256"); - } - if(validation & 0x8) { - jsonArray << QStringLiteral("signature"); - } - if(jsonArray.empty()) { - jsonArray << QStringLiteral("unknown"); - } - return jsonArray; } /*! * \endcond */ +using namespace Utilities; + +/*! + * \brief Returns basic information about the packages as JSON object. + */ +QJsonObject Package::basicInfo(bool includeRepoAndName) const +{ + QJsonObject info; + if(includeRepoAndName) { + if(source()) { + put(info, QStringLiteral("repo"), source()->name()); + } + put(info, QStringLiteral("name"), name()); + } + put(info, QStringLiteral("archs"), architectures()); + put(info, QStringLiteral("arch"), buildArchitecture()); + put(info, QStringLiteral("ver"), version()); + put(info, QStringLiteral("desc"), description()); + put(info, QStringLiteral("bdate"), buildDate()); + put(info, QStringLiteral("flagdate"), outOfDate()); + return info; +} + +/*! + * \brief Returns full information about the package as JSON object. + */ +QJsonObject Package::fullInfo(bool includeRepoAndName) const +{ + QJsonObject info; + if(includeRepoAndName) { + if(source()) { + put(info, QStringLiteral("repo"), source()->name()); + } + put(info, QStringLiteral("name"), name()); + } + put(info, QStringLiteral("archs"), architectures()); + put(info, QStringLiteral("arch"), buildArchitecture()); + put(info, QStringLiteral("ver"), version()); + put(info, QStringLiteral("desc"), description()); + put(info, QStringLiteral("bdate"), buildDate()); + put(info, QStringLiteral("bdate"), buildDate()); + put(info, QStringLiteral("flagdate"), outOfDate()); + put(info, QStringLiteral("idate"), installDate()); + put(info, QStringLiteral("isize"), QJsonValue(static_cast(installedSize()))); + put(info, QStringLiteral("url"), upstreamUrl()); + put(info, QStringLiteral("lic"), licenses()); + put(info, QStringLiteral("grp"), groups()); + put(info, QStringLiteral("prov"), provides()); + put(info, QStringLiteral("optd"), optionalDependencies()); + put(info, QStringLiteral("deps"), dependencies()); + put(info, QStringLiteral("requ"), requiredBy()); + put(info, QStringLiteral("optf"), optionalFor()); + put(info, QStringLiteral("conf"), conflicts()); + put(info, QStringLiteral("repl"), replaces()); + put(info, QStringLiteral("pack"), packer()); + put(info, QStringLiteral("expl"), QJsonValue(installReason() == ALPM_PKG_REASON_EXPLICIT)); + put(info, QStringLiteral("scri"), QJsonValue(hasInstallScript())); + put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods())); + put(info, QStringLiteral("file"), fileName()); + put(info, QStringLiteral("files"), files()); + return info; +} + /*! * \brief The PackageVersion class helps parsing package versions. */ @@ -94,18 +205,11 @@ inline QJsonArray qjarry(_alpm_pkgvalidation_t validation) /*! * \brief Constructs a new PackageVersion instance from the specified \a versionStr. */ -PackageVersion::PackageVersion(const QString &versionStr) : - PackageVersion(versionStr.toLocal8Bit().data()) -{} - -/*! - * \brief Constructs a new PackageVersion instance from the specified \a versionStr. - */ -PackageVersion::PackageVersion(const char *versionStr) +PackageVersion::PackageVersion(const QString &versionStr) { // determine start offsets of version and release - const char *versionBeg = nullptr, *releaseBeg = nullptr; - for(const char *i = versionStr; ; ++i) { + const ushort *str = versionStr.utf16(), *versionBeg = nullptr, *releaseBeg = nullptr; + for(const auto *i = str; ; ++i) { switch(*i) { case 0: goto terminationFound; @@ -123,22 +227,22 @@ PackageVersion::PackageVersion(const char *versionStr) terminationFound: if(versionBeg) { // epoch present - epoch = QString::fromLocal8Bit(versionStr, versionBeg - versionStr - 1); + epoch = QString::fromUtf16(str, versionBeg - str - 1); if(releaseBeg) { // release present - version = QString::fromLocal8Bit(versionBeg, releaseBeg - versionBeg - 1); - release = QString::fromLocal8Bit(releaseBeg); + version = QString::fromUtf16(versionBeg, releaseBeg - versionBeg - 1); + release = QString::fromUtf16(releaseBeg); } else { - version = QString::fromLocal8Bit(versionBeg); + version = QString::fromUtf16(versionBeg); } } else { // epoch not present if(releaseBeg) { // release present - version = QString::fromLocal8Bit(versionStr, releaseBeg - versionStr - 1); - release = QString::fromLocal8Bit(releaseBeg); + version = QString::fromUtf16(str, releaseBeg - str - 1); + release = QString::fromUtf16(releaseBeg); } else { - version = QString::fromLocal8Bit(versionStr); + version = QString::fromUtf16(str); } } } @@ -227,157 +331,5 @@ PackageVersionComparsion PackageVersion::compare(const PackageVersion &other) co return PackageVersionComparsion::Equal; } -/*! - * \brief The AurPackage class holds information about AUR packages. It allows to convert the information - * to JSON objects used by the network classes and the web interface. - */ - -/*! - * \brief Creates a new instance from the specified "AurJson value". - */ -AurPackage::AurPackage(const QJsonValue &aurJsonValue) : - AurPackage() -{ - QJsonObject obj = aurJsonValue.toObject(); - m_id = obj.value(QStringLiteral("ID")).toInt(-1); - m_categoryId = obj.value(QStringLiteral("CategoryID")).toInt(-1); - m_name = obj.value(QStringLiteral("Name")).toString(); - m_version = obj.value(QStringLiteral("Version")).toString(); - m_description = obj.value(QStringLiteral("Description")).toString(); - m_upstreamUrl = obj.value(QStringLiteral("URL")).toString(); - m_votes = obj.value(QStringLiteral("NumVotes")).toInt(0); - m_outOfDate = DateTime::fromTimeStamp(obj.value(QStringLiteral("OutOfDate")).toInt()); - m_maintainer = obj.value(QStringLiteral("Maintainer")).toString(); - m_firstSubmitted = DateTime::fromTimeStamp(obj.value(QStringLiteral("FirstSubmitted")).toInt()); - m_lastModified = DateTime::fromTimeStamp(obj.value(QStringLiteral("LastModified")).toInt()); - m_license = obj.value(QStringLiteral("License")).toString(); - m_tarUrl = obj.value(QStringLiteral("URLPath")).toString(); -} - - - -/*! - * \brief Returns basic information about the packages as JSON object. - */ -QJsonObject AurPackage::basicInfo(bool includeRepoAndName) const -{ - QJsonObject packageInfo; - if(includeRepoAndName) { - packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur")); - packageInfo.insert(QStringLiteral("name"), name()); - } - packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("ver"), version()); - packageInfo.insert(QStringLiteral("desc"), description()); - packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("flagdate"), outOfDate().isNull() ? QStringLiteral("none") : qstr(outOfDate().toString().data())); - return packageInfo; -} - -/*! - * \brief Returns full information about the package as JSON object. - */ -QJsonObject AurPackage::fullInfo(bool includeRepoAndName) const -{ - QJsonObject packageInfo; - if(includeRepoAndName) { - packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur")); - packageInfo.insert(QStringLiteral("name"), name()); - } - packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("ver"), version()); - packageInfo.insert(QStringLiteral("desc"), description()); - packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("idate"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("isize"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("url"), upstreamUrl()); - packageInfo.insert(QStringLiteral("lic"), license()); - packageInfo.insert(QStringLiteral("grp"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("prov"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("optd"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("deps"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("requ"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("optf"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("conf"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("repl"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("pack"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("expl"), QStringLiteral("n.a.")); - packageInfo.insert(QStringLiteral("scri"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("sig"), QStringLiteral("TODO")); - packageInfo.insert(QStringLiteral("file"), QStringLiteral("n.a.")); - return packageInfo; -} - -/*! - * \brief The AlpmPackage class helps getting information about ALPM packages. It allows to convert the - * information to JSON objects used by the network classes and the web interface. - */ - -/*! - * \brief Returns basic information about the packages as JSON object. - */ -QJsonObject AlpmPackage::basicInfo(bool includeRepoAndName) const -{ - QJsonObject packageInfo; - if(includeRepoAndName) { - packageInfo.insert(QStringLiteral("repo"), qstr(AlpmDataBase(associatedDatabase()).name())); - packageInfo.insert(QStringLiteral("name"), qstr(name())); - } - packageInfo.insert(QStringLiteral("arch"), qstr(architecture())); - packageInfo.insert(QStringLiteral("ver"), qstr(version())); - packageInfo.insert(QStringLiteral("desc"), qstr(description())); - packageInfo.insert(QStringLiteral("bdate"), qstr(buildDate().toString().c_str())); - packageInfo.insert(QStringLiteral("flagdate"), QStringLiteral("n.a.")); - return packageInfo; -} - -/*! - * \brief Returns full information about the package as JSON object. - */ -QJsonObject AlpmPackage::fullInfo(bool includeRepoAndName) const -{ - QJsonObject packageInfo; - if(includeRepoAndName) { - packageInfo.insert(QStringLiteral("repo"), qstr(AlpmDataBase(associatedDatabase()).name())); - packageInfo.insert(QStringLiteral("name"), qstr(name())); - } - packageInfo.insert(QStringLiteral("arch"), qstr(architecture())); - packageInfo.insert(QStringLiteral("ver"), qstr(version())); - packageInfo.insert(QStringLiteral("desc"), qstr(description())); - packageInfo.insert(QStringLiteral("bdate"), qstr(buildDate().toString().c_str())); - packageInfo.insert(QStringLiteral("idate"), qstr(installDate().toString().c_str())); - packageInfo.insert(QStringLiteral("isize"), static_cast(installedSize())); - packageInfo.insert(QStringLiteral("url"), qstr(upstreamUrl())); - packageInfo.insert(QStringLiteral("lic"), qjarry(licenses())); - packageInfo.insert(QStringLiteral("grp"), qjarry(groups())); - packageInfo.insert(QStringLiteral("prov"), qjarry(provides())); - packageInfo.insert(QStringLiteral("optd"), qjarry(optionalDependencies())); - packageInfo.insert(QStringLiteral("deps"), qjarry(dependencies())); - packageInfo.insert(QStringLiteral("requ"), qjarry(requiredBy())); - packageInfo.insert(QStringLiteral("optf"), qjarry(optionalFor())); - packageInfo.insert(QStringLiteral("conf"), qjarry(conflicts())); - packageInfo.insert(QStringLiteral("repl"), qjarry(replaces())); - packageInfo.insert(QStringLiteral("pack"), qstr(packager())); - packageInfo.insert(QStringLiteral("expl"), installReason() == ALPM_PKG_REASON_EXPLICIT); - packageInfo.insert(QStringLiteral("scri"), hasInstallScript()); - packageInfo.insert(QStringLiteral("sig"), qjarry(validation())); - packageInfo.insert(QStringLiteral("file"), qstr(fileName())); - QJsonArray fileInfos; - alpm_filelist_t *fileList = files(); - for(alpm_file_t *file = fileList->files, *end = fileList->files + fileList->count; file != end; ++file) { - QJsonObject fileInfo; - fileInfo.insert(QStringLiteral("name"), qstr(file->name)); - if(file->mode) { - fileInfo.insert(QStringLiteral("mode"), static_cast(file->mode)); - } - if(file->size) { - fileInfo.insert(QStringLiteral("size"), static_cast(file->size)); - } - fileInfos << fileInfo; - } - packageInfo.insert(QStringLiteral("files"), fileInfos); - return packageInfo; -} - } diff --git a/alpm/package.h b/alpm/package.h index 5aa59fa..4c0444c 100644 --- a/alpm/package.h +++ b/alpm/package.h @@ -12,14 +12,15 @@ #include #include +#include -QT_BEGIN_NAMESPACE -class QJsonObject; -class QJsonValue; -QT_END_NAMESPACE +QT_FORWARD_DECLARE_CLASS(QJsonObject) +QT_FORWARD_DECLARE_CLASS(QJsonValue) namespace PackageManagement { +class Repository; + /*! * \brief The PackageVersionComparsion enum defines possible results of packages version comparison. */ @@ -41,11 +42,29 @@ enum class PackageVersionPartComparsion Older /*!< Part 2 is newer then part 1. */ }; +/*! + * \brief The PackageOrigin enum describes where a Package instance comes from. + */ +enum class PackageOrigin +{ + Unknown = 20, /*! The origin is unknown. */ + File = ALPM_PKG_FROM_FILE, /*!< The instance has been created from a package file; source() returns nullptr in this case. */ + LocalDb = ALPM_PKG_FROM_LOCALDB, /*! The instance is from the local data base; source() is an AlpmDataBase instance. */ + SyncDb = ALPM_PKG_FROM_SYNCDB, /*! The instance is from a sync data base; source() is an AlpmDataBase instance. */ + Aur = 21 /*! The instance is from the AUR; source() is a UserRepository instance. */ +}; + +enum class InstallStatus +{ + Explicit = ALPM_PKG_REASON_EXPLICIT, + AsDependency = ALPM_PKG_REASON_DEPEND, + None = 20 +}; + class PackageVersion { public: - PackageVersion(const QString &versionStr); - PackageVersion(const char *versionStr); + explicit PackageVersion(const QString &versionStr); static PackageVersionPartComparsion compareParts(const QString &part1, const QString &part2); PackageVersionComparsion compare(const PackageVersion &other) const; @@ -55,487 +74,525 @@ public: QString release; }; -class AurPackage +class Dependency { public: - AurPackage(); - AurPackage(const QJsonValue &aurJsonValue); + explicit Dependency(const QString &name, const QString &version, _alpm_depmod_t mode = ALPM_DEP_MOD_ANY); + QString name; + QString version; + _alpm_depmod_t mode; +}; - bool isValid() const; - bool hasFullInfo() const; - int id() const; - int categoryId() const; +inline Dependency::Dependency(const QString &name, const QString &version, _alpm_depmod_t mode) : + name(name), + version(version), + mode(mode) +{} + +class Package +{ +public: + virtual ~Package(); + + // general package meta data + PackageOrigin origin() const; + Repository *source() const; + bool hasGeneralInfo() const; const QString &name() const; const QString &version() const; - template - PackageVersionComparsion compareVersion(const Package &syncPackage) const; const QString &description() const; const QString &upstreamUrl() const; + const QStringList &licenses() const; + const QStringList &groups() const; + const QList &dependencies() const; + const QList &optionalDependencies() const; + const QList &conflicts() const; + const QList &provides() const; + const QList &replaces() const; + const QStringList &requiredBy() const; + const QStringList &optionalFor() const; + bool hasInstallScript() const; + + // build related meta data + bool hasBuildRelatedMetaData() const; + const QString &fileName() const; + const QJsonArray &files() const; + ChronoUtilities::DateTime buildDate() const; + const QString &packer() const; + const QString &md5() const; + const QString &sha256() const; + const QString &buildArchitecture() const; + uint32 packageSize() const; + const QList &makeDependencies() const; + + // installation related meta data + bool hasInstallRelatedMetaData() const; + ChronoUtilities::DateTime installDate() const; + uint32 installedSize() const; + const QStringList &backupFiles() const; + alpm_pkgvalidation_t validationMethods() const; + alpm_pkgreason_t installReason() const; + + // source related meta data + bool hasSourceRelatedMetaData() const; + const QString &baseName() const; + const QStringList &architectures() const; + int id() const; + int categoryId() const; int votes() const; ChronoUtilities::DateTime outOfDate() const; const QString &maintainer() const; ChronoUtilities::DateTime firstSubmitted() const; ChronoUtilities::DateTime lastModified() const; - const QString &license() const; const QString &tarUrl() const; - // JSON serialization - QJsonObject basicInfo(bool includeRepoAndName) const; - QJsonObject fullInfo(bool includeRepoAndName = false) const; - -private: - bool m_fullInfo; - int m_id; - int m_categoryId; - QString m_name; - QString m_version; - QString m_description; - QString m_upstreamUrl; - int m_votes; - ChronoUtilities::DateTime m_outOfDate; - QString m_maintainer; - ChronoUtilities::DateTime m_firstSubmitted; - ChronoUtilities::DateTime m_lastModified; - QString m_license; - QString m_tarUrl; -}; - -/*! - * \brief Constructs a empty, invalid AUR package. - */ -inline AurPackage::AurPackage() : - m_fullInfo(false), - m_id(-1), - m_categoryId(-1), - m_votes(-1), - m_outOfDate(false) -{} - -/*! - * \brief Returns an indication whether the package is valid. - */ -inline bool AurPackage::isValid() const -{ - return m_id >= 0; -} - -/*! - * \brief Returns an indication whether full package info is available. - */ -inline bool AurPackage::hasFullInfo() const -{ - return m_fullInfo; -} - -/*! - * \brief Returns the ID of the package in the AUR. - */ -inline int AurPackage::id() const -{ - return m_id; -} - -/*! - * \brief Returns the category ID of the package in the AUR. - */ -inline int AurPackage::categoryId() const -{ - return m_categoryId; -} - -/*! - * \brief Returns the name of the package. - */ -inline const QString &AurPackage::name() const -{ - return m_name; -} - -/*! - * \brief Returns the version of the package. - */ -inline const QString &AurPackage::version() const -{ - return m_version; -} - -template -inline PackageVersionComparsion AurPackage::compareVersion(const Package &syncPackage) const -{ - return PackageVersion(version()).compare(PackageVersion(syncPackage.version())); -} - -/*! - * \brief Returns the description of the package. - */ -inline const QString &AurPackage::description() const -{ - return m_description; -} - -/*! - * \brief Returns the upstream URL of the package. - */ -inline const QString &AurPackage::upstreamUrl() const -{ - return m_upstreamUrl; -} - -/*! - * \brief Returns the votes of the package in the AUR. - */ -inline int AurPackage::votes() const -{ - return m_votes; -} - -/*! - * \brief Returns wheter the package is flagged as out-of-date. - */ -inline ChronoUtilities::DateTime AurPackage::outOfDate() const -{ - return m_outOfDate; -} - -/*! - * \brief Returns the maintainer of the package. - */ -inline const QString &AurPackage::maintainer() const -{ - return m_maintainer; -} - -/*! - * \brief Returns the time the package was first submitted. - */ -inline ChronoUtilities::DateTime AurPackage::firstSubmitted() const -{ - return m_firstSubmitted; -} - -/*! - * \brief Returns the time the package was last modified. - */ -inline ChronoUtilities::DateTime AurPackage::lastModified() const -{ - return m_lastModified; -} - -/*! - * \brief Returns the license. - */ -inline const QString &AurPackage::license() const -{ - return m_license; -} - -/*! - * \brief Returns a URL to the tar file from AUR. - */ -inline const QString &AurPackage::tarUrl() const -{ - return m_tarUrl; -} - -class AlpmPackage -{ -public: - AlpmPackage(alpm_pkg_t *package = nullptr); - - // package properties - const alpm_pkg_t *ptr() const; - alpm_pkg_t *ptr(); - bool hasInstallScript() const; - const char *fileName() const; - const char *name() const; - const char *version() const; - template - PackageVersionComparsion compareVersion(const Package &syncPackage) const; - alpm_pkgfrom_t origin() const; - const char *description() const; - const char *upstreamUrl() const; - ChronoUtilities::DateTime buildDate() const; - ChronoUtilities::DateTime installDate() const; - const char *packager() const; - const char *md5() const; - const char *sha256() const; - const char *architecture() const; - off_t packageSize() const; - off_t installedSize() const; - alpm_pkgreason_t installReason() const; - void setInstallReason(alpm_pkgreason_t reason); - StringList licenses() const; - StringList groups() const; - DependencyList dependencies() const; - DependencyList optionalDependencies() const; - DependencyList conflicts() const; - DependencyList provides() const; - StringList deltas() const; - DependencyList replaces() const; - alpm_filelist_t *files() const; - AlpmList backupFiles() const; - alpm_db_t *associatedDatabase() const; - const char *base64Signature() const; - alpm_pkgvalidation_t validation() const; - StringList requiredBy() const; - StringList optionalFor() const; - void *openChangelog() const; - size_t readChangelog(void *changelog, void *buffer, size_t bufferSize); - bool closeChangelog(void *changelog); - operator bool() const; + // version comparsion + PackageVersionComparsion compareVersion(const Package *syncPackage) const; + PackageVersionComparsion compareVersion(const Dependency &dependency) const; + static bool matches(const QString &name, const QString &version, const Dependency &dependency); + bool matches(const Dependency &dependency); // JSON serialization QJsonObject basicInfo(bool includeRepoAndName = false) const; QJsonObject fullInfo(bool includeRepoAndName = false) const; protected: - alpm_pkg_t *m_ptr; + explicit Package(const QString &name, Repository *source); + + PackageOrigin m_origin; + Repository *m_source; + + // general package meta data + bool m_hasGeneralInfo; + QString m_name; + QString m_version; + QString m_description; + QString m_upstreamUrl; + QStringList m_licenses; + QStringList m_groups; + QList m_dependencies; + QList m_optionalDependencies; + QList m_conflicts; + QList m_provides; + QList m_replaces; + QStringList m_requiredBy; + QStringList m_optionalFor; + bool m_hasInstallScript; + + // build related meta data + bool m_hasBuildRelatedMetaData; + QString m_fileName; + QJsonArray m_files; + ChronoUtilities::DateTime m_buildDate; + QString m_packer; + QString m_md5; + QString m_sha256; + QString m_buildArchitecture; + uint32 m_packageSize; + QList m_makeDependencies; + + // installation related meta data + bool m_hasInstallRelatedMetaData; + ChronoUtilities::DateTime m_installDate; + uint32 m_installedSize; + QStringList m_backupFiles; + alpm_pkgvalidation_t m_validationMethods; + alpm_pkgreason_t m_installReason; + + // source related meta data + bool m_hasSourceRelatedMetaData; + QString m_baseName; + QStringList m_architectures; + int m_id; + int m_categoryId; + int m_votes; + ChronoUtilities::DateTime m_outOfDate; + QString m_maintainer; + ChronoUtilities::DateTime m_firstSubmitted; + ChronoUtilities::DateTime m_lastModified; + QString m_tarUrl; }; -inline uint qHash(const AlpmPackage &key) -{ - return qHash(key.ptr()); -} - /*! - * \brief Constructs a new package instance for the specified ALPM \a package. + * \brief Returns where the package instance comes from (local db, sync db, pkg file, AUR). */ -inline AlpmPackage::AlpmPackage(alpm_pkg_t *package) : - m_ptr(package) -{} - -inline const alpm_pkg_t *AlpmPackage::ptr() const +inline PackageOrigin Package::origin() const { - return m_ptr; -} - -inline alpm_pkg_t *AlpmPackage::ptr() -{ - return m_ptr; -} - -inline bool AlpmPackage::hasInstallScript() const -{ - return alpm_pkg_has_scriptlet(m_ptr); -} - -inline const char *AlpmPackage::fileName() const -{ - return alpm_pkg_get_filename(m_ptr); -} - -inline const char *AlpmPackage::name() const -{ - return alpm_pkg_get_name(m_ptr); -} - -inline const char *AlpmPackage::version() const -{ - return alpm_pkg_get_version(m_ptr); + return m_origin; } /*! - * \brief Compares the version of this package with the version of the specified package. + * \brief Returns the package source. + * \remarks Might be nullptr if no source is associated. + */ +inline Repository *Package::source() const +{ + return m_source; +} + +/*! + * \brief Returns whether general information is available for the package. + */ +inline bool Package::hasGeneralInfo() const +{ + return m_hasGeneralInfo; +} + +/*! + * \brief Returns the name. + */ +inline const QString &Package::name() const +{ + return m_name; +} + +/*! + * \brief Returns the version. + */ +inline const QString &Package::version() const +{ + return m_version; +} + +/*! + * \brief Returns the description. + */ +inline const QString &Package::description() const +{ + return m_description; +} + +/*! + * \brief Returns the upstream URL. + */ +inline const QString &Package::upstreamUrl() const +{ + return m_upstreamUrl; +} + +/*! + * \brief Returns the licenses. + */ +inline const QStringList &Package::licenses() const +{ + return m_licenses; +} + +/*! + * \brief Returns the groups. + */ +inline const QStringList &Package::groups() const +{ + return m_groups; +} + +/*! + * \brief Returns the dependencies. + */ +inline const QList &Package::dependencies() const +{ + return m_dependencies; +} + +/*! + * \brief Returns the optional dependencies. + */ +inline const QList &Package::optionalDependencies() const +{ + return m_optionalDependencies; +} + +/*! + * \brief Returns conflicting packages. + */ +inline const QList &Package::conflicts() const +{ + return m_conflicts; +} + +/*! + * \brief Returns provides. + */ +inline const QList &Package::provides() const +{ + return m_provides; +} + +/*! + * \brief Returns packages which are replaced by this package. + */ +inline const QList &Package::replaces() const +{ + return m_replaces; +} + +/*! + * \brief Returns packages requiring this packages. + */ +inline const QStringList &Package::requiredBy() const +{ + return m_requiredBy; +} + +/*! + * \brief Returns packages having this package as optional dependency. + */ +inline const QStringList &Package::optionalFor() const +{ + return m_optionalFor; +} + +/*! + * \brief Returns whether the package has an install script. + */ +inline bool Package::hasInstallScript() const +{ + return m_hasInstallScript; +} + +/*! + * \brief Returns whether the package has build-related meta data. * - * This method distinguishes between software upgrades and package releases. See Alpm::PackageVersionComparsion enum. + * Build-related meta data is information about a particular package file such + * as architecture, file name, build date, .... */ -template -inline PackageVersionComparsion AlpmPackage::compareVersion(const Package &syncPackage) const +inline bool Package::hasBuildRelatedMetaData() const { - return PackageVersion(version()).compare(PackageVersion(syncPackage.version())); + return m_hasBuildRelatedMetaData; } -inline alpm_pkgfrom_t AlpmPackage::origin() const +/*! + * \brief Returns the file name of the package file. + */ +inline const QString &Package::fileName() const { - return alpm_pkg_get_origin(m_ptr); + return m_fileName; } -inline const char *AlpmPackage::description() const +/*! + * \brief Returns the file of the package as JSON array. + */ +inline const QJsonArray &Package::files() const { - return alpm_pkg_get_desc(m_ptr); + return m_files; } -inline const char *AlpmPackage::upstreamUrl() const +/*! + * \brief Returns the build date of the package file. + */ +inline ChronoUtilities::DateTime Package::buildDate() const { - return alpm_pkg_get_url(m_ptr); + return m_buildDate; } -inline ChronoUtilities::DateTime AlpmPackage::buildDate() const +/*! + * \brief Returns the packer of the package file. + */ +inline const QString &Package::packer() const { - return ChronoUtilities::DateTime::fromTimeStamp(static_cast(alpm_pkg_get_builddate(m_ptr))); + return m_packer; } -inline ChronoUtilities::DateTime AlpmPackage::installDate() const +/*! + * \brief Returns the MD5 hash of the package file. + */ +inline const QString &Package::md5() const { - return ChronoUtilities::DateTime::fromTimeStamp(static_cast(alpm_pkg_get_installdate(m_ptr))); + return m_md5; } -inline const char *AlpmPackage::packager() const +/*! + * \brief Returns the SHA-256 hash of the package file. + */ +inline const QString &Package::sha256() const { - return alpm_pkg_get_packager(m_ptr); + return m_sha256; } -inline const char *AlpmPackage::md5() const +/*! + * \brief Returns the architecture of the package file. + */ +inline const QString &Package::buildArchitecture() const { - return alpm_pkg_get_md5sum(m_ptr); + return m_buildArchitecture; } -inline const char *AlpmPackage::sha256() const +/*! + * \brief Returns the size of the package file. + */ +inline uint32 Package::packageSize() const { - return alpm_pkg_get_sha256sum(m_ptr); + return m_packageSize; } -inline const char *AlpmPackage::architecture() const +/*! + * \brief Returns make dependencies. + */ +inline const QList &Package::makeDependencies() const { - return alpm_pkg_get_arch(m_ptr); + return m_makeDependencies; } -inline off_t AlpmPackage::packageSize() const +/*! + * \brief Returns whether install-related meta data is available. + * + * Install-related meta data is information such as the install date, + * the installed size, files backuped during installation, ... + * + * Most of the install-related meta data is only available for packages + * from the local data base (see origin()). + */ +inline bool Package::hasInstallRelatedMetaData() const { - return alpm_pkg_get_size(m_ptr); + return m_hasInstallRelatedMetaData; } -inline off_t AlpmPackage::installedSize() const +/*! + * \brief Returns the install date. + */ +inline ChronoUtilities::DateTime Package::installDate() const { - return alpm_pkg_get_isize(m_ptr); + return m_installDate; } -inline alpm_pkgreason_t AlpmPackage::installReason() const +/*! + * \brief Returns the installed size. + */ +inline uint32 Package::installedSize() const { - return alpm_pkg_get_reason(m_ptr); + return m_installedSize; } -inline StringList AlpmPackage::licenses() const +/*! + * \brief Returns the files which have been backued up during installation. + */ +inline const QStringList &Package::backupFiles() const { - return alpm_pkg_get_licenses(m_ptr); + return m_backupFiles; } -inline StringList AlpmPackage::groups() const +/*! + * \brief Returns the validation methods used during installation. + */ +inline alpm_pkgvalidation_t Package::validationMethods() const { - return alpm_pkg_get_groups(m_ptr); + return m_validationMethods; } -inline DependencyList AlpmPackage::dependencies() const +/*! + * \brief Returns whether the package has been installed explicitely or as a dependency. + */ +inline alpm_pkgreason_t Package::installReason() const { - return alpm_pkg_get_depends(m_ptr); + return m_installReason; } -inline DependencyList AlpmPackage::optionalDependencies() const +/*! + * \brief Returns whether source-related meta data is available. + * + * Source-related meta data is information about the PKGBUILD file such has + * its AUR IDs, AUR votes, maintainer, flag date, ... + */ +inline bool Package::hasSourceRelatedMetaData() const { - return alpm_pkg_get_optdepends(m_ptr); + return m_hasSourceRelatedMetaData; } -inline DependencyList AlpmPackage::conflicts() const +/*! + * \brief Returns the base name. + */ +inline const QString &Package::baseName() const { - return alpm_pkg_get_conflicts(m_ptr); + return m_baseName; } -inline DependencyList AlpmPackage::provides() const +/*! + * \brief Returns the architecutes (from the PKGBUILD file). + * \remarks For the architecture of the particular binary package + * see buildArchitecture(). + */ +inline const QStringList &Package::architectures() const { - return alpm_pkg_get_provides(m_ptr); + return m_architectures; } -inline StringList AlpmPackage::deltas() const +/*! + * \brief Returns the ID. + */ +inline int Package::id() const { - return alpm_pkg_get_deltas(m_ptr); + return m_id; } -inline DependencyList AlpmPackage::replaces() const +/*! + * \brief Returns the category ID. + */ +inline int Package::categoryId() const { - return alpm_pkg_get_replaces(m_ptr); + return m_categoryId; } -inline alpm_filelist_t *AlpmPackage::files() const +/*! + * \brief Returns the votes of the package. + */ +inline int Package::votes() const { - return alpm_pkg_get_files(m_ptr); + return m_votes; } -inline AlpmList AlpmPackage::backupFiles() const +/*! + * \brief Returns the flag date. + */ +inline ChronoUtilities::DateTime Package::outOfDate() const { - return alpm_pkg_get_backup(m_ptr); + return m_outOfDate; } -inline alpm_db_t *AlpmPackage::associatedDatabase() const +/*! + * \brief Returns the maintainer. + */ +inline const QString &Package::maintainer() const { - return alpm_pkg_get_db(m_ptr); + return m_maintainer; } -inline const char *AlpmPackage::base64Signature() const +/*! + * \brief Returns when the package was first submitted. + */ +inline ChronoUtilities::DateTime Package::firstSubmitted() const { - return alpm_pkg_get_base64_sig(m_ptr); + return m_firstSubmitted; } -inline alpm_pkgvalidation_t AlpmPackage::validation() const +/*! + * \brief Returns the last time when the package was modified. + */ +inline ChronoUtilities::DateTime Package::lastModified() const { - return alpm_pkg_get_validation(m_ptr); + return m_lastModified; } -inline StringList AlpmPackage::requiredBy() const +/*! + * \brief Returns a URL to a tar file with the sources. + */ +inline const QString &Package::tarUrl() const { - return alpm_pkg_compute_requiredby(m_ptr); + return m_tarUrl; } -inline StringList AlpmPackage::optionalFor() const +inline PackageVersionComparsion Package::compareVersion(const Package *syncPackage) const { - return alpm_pkg_compute_optionalfor(m_ptr); + return PackageVersion(version()).compare(PackageVersion(syncPackage->version())); } -inline void *AlpmPackage::openChangelog() const +inline PackageVersionComparsion Package::compareVersion(const Dependency &dependency) const { - return alpm_pkg_changelog_open(m_ptr); + return PackageVersion(version()).compare(PackageVersion(dependency.version)); } -inline size_t AlpmPackage::readChangelog(void *changelog, void *buffer, size_t bufferSize) +inline bool Package::matches(const Dependency &dependency) { - return alpm_pkg_changelog_read(buffer, bufferSize, m_ptr, changelog); -} - -inline bool AlpmPackage::closeChangelog(void *changelog) -{ - return alpm_pkg_changelog_close(m_ptr, changelog); -} - -inline PackageManagement::AlpmPackage::operator bool() const -{ - return m_ptr != nullptr; -} - -class AlpmOwnershipPackage : public AlpmPackage -{ -public: - // constructor, destructor - AlpmOwnershipPackage(alpm_pkg_t *package); - AlpmOwnershipPackage(const AlpmOwnershipPackage &other) = delete; - AlpmOwnershipPackage(AlpmOwnershipPackage &&other); - ~AlpmOwnershipPackage(); - - // assignment operator - AlpmOwnershipPackage &operator =(const AlpmOwnershipPackage &other) = delete; - AlpmOwnershipPackage &operator =(AlpmOwnershipPackage &&other); -}; - -inline AlpmOwnershipPackage::AlpmOwnershipPackage(alpm_pkg_t *package) : - AlpmPackage(package) -{} - -inline AlpmOwnershipPackage::AlpmOwnershipPackage(AlpmOwnershipPackage &&other) : - AlpmPackage(other.m_ptr) -{ - other.m_ptr = nullptr; -} - -inline AlpmOwnershipPackage &AlpmOwnershipPackage::operator =(AlpmOwnershipPackage &&other) -{ - if(this != &other) { - m_ptr = other.m_ptr; - other.m_ptr = nullptr; - } - return *this; -} - -inline AlpmOwnershipPackage::~AlpmOwnershipPackage() -{ - alpm_pkg_free(ptr()); + return matches(name(), version(), dependency); } } diff --git a/alpm/repository.cpp b/alpm/repository.cpp new file mode 100644 index 0000000..96442b0 --- /dev/null +++ b/alpm/repository.cpp @@ -0,0 +1,298 @@ +#include "repository.h" +#include "upgradelookup.h" +#include "utilities.h" + +#include +#include + +using namespace std; + +namespace PackageManagement { + +/*! + * \brief Constructs a new reply. + */ +Reply::Reply(QNetworkReply *networkReply) : + m_networkReply(networkReply) +{ + networkReply->setParent(this); + connect(networkReply, &QNetworkReply::finished, this, &Reply::processData); +} + +/*! + * \fn PackageSource::type() + * \brief Returns the type of the package source. + */ + +/*! + * \brief Returns a list of all package names. + */ +const QStringList PackageManagement::Repository::packageNames() const +{ + QStringList names; + names.reserve(m_packages.size()); + for(const auto &entry : m_packages) { + names << entry.first; + } + return names; +} + +/*! + * \brief Requests suggestions for the specified search phrase. + * \returns Returns a reply object used for the request. The reply must be destroyed by the caller + * using destroyLater() after resultsAvailable() has been emitted. + */ +SuggestionsReply *Repository::requestSuggestions(const QString &) const +{ + return nullptr; +} + +/*! + * \class Repository + * \brief The Repository class represents a repository (binary repositories as well as source-only repos). + */ + +/*! + * \brief Constructs a new repository (protected since this is a pure virtual class). + */ +Repository::Repository(const QString &name, QObject *parent) : + QObject(parent), + m_name(name), + m_usage(static_cast(0)), + m_sigLevel(static_cast(ALPM_SIGSTATUS_INVALID)) +{} + +/*! + * \brief Destroys the package source. + */ +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. + * + * AlpmDataBase instances load all available packages in the cache + * at the beginning and hence do not require explicit requests. + * + * 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 +{ + return false; +} + +/*! + * \brief Requests package information for the specified package. + * \returns Returns a reply object used for the request. The reply must be destroyed by the caller + * using destroyLater() after resultsAvailable() has been emitted. + * \remarks + * If \a forceUpdate is true, package information which has already been retrieved + * and is still cached is requested again. Otherwise these packages will not be + * requested again. If it turns out, that all packages are already cached, nullptr + * is returned in this case. + */ +PackageReply *Repository::requestPackageInfo(const QStringList &, bool ) const +{ + return nullptr; +} + +/*! + * \brief Requests full package information for the specified package. + * \returns Returns a reply object used for the request. The reply must be destroyed by the caller + * using destroyLater() after resultsAvailable() has been emitted. + * \remarks + * If \a forceUpdate is true, package information which has already been retrieved + * and is still cached is requested again. Otherwise these packages will not be + * 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 +{ + return nullptr; +} + +/*! + * \brief Returns the first package providing the specified \a dependency. + * \remarks Returns nullptr if no packages provides the \a dependency. + */ +const Package *Repository::packageProviding(const Dependency &dependency) const +{ + for(const 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(const 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. + */ +QList Repository::packagesProviding(const Dependency &dependency) const +{ + QList res; + for(const auto &entry : m_packages) { + if(entry.second->matches(dependency)) { + // check whether package matches "directly" + res << entry.second.get(); + } else { + // check whether at least one of the provides matches + for(const auto &provide : entry.second->provides()) { + if(Package::matches(provide.name, provide.version, dependency)) { + res << entry.second.get(); + break; + } + } + } + } + return res; +} + +/*! + * \brief Returns all packages matching the specified predicate. + */ +QList Repository::packageByFilter(std::function pred) +{ + QList packages; + for(const auto &entry : m_packages) { + if(pred(entry.second.get())) { + packages << entry.second.get(); + } + } + return packages; +} + +QJsonArray Repository::upgradeSourcesJsonArray() const +{ + QJsonArray sources; + for(const auto *source : upgradeSources()) { + sources << source->name(); + } + return sources; +} + +void Repository::checkForUpgrades(UpgradeLookupResults &results, const QList &upgradeSources) const +{ + if(upgradeSources.isEmpty()) { + results.noSources = true; + } else { + for(const auto &pkgEntry : packages()) { + bool orphaned = true; + for(const auto *src : upgradeSources) { + if(const auto *syncPkg = src->packageByName(pkgEntry.first)) { + switch(pkgEntry.second->compareVersion(syncPkg)) { + case PackageVersionComparsion::Equal: + break; // ignore equal packages + case PackageVersionComparsion::SoftwareUpgrade: + results.newVersions << UpgradeResult(syncPkg, pkgEntry.second->version()); + break; + case PackageVersionComparsion::PackageUpgradeOnly: + results.newReleases << UpgradeResult(syncPkg, pkgEntry.second->version()); + break; + case PackageVersionComparsion::NewerThenSyncVersion: + results.downgrades << UpgradeResult(syncPkg, pkgEntry.second->version()); + } + orphaned = false; + } + } + if(orphaned) { + results.orphaned << pkgEntry.second.get(); + } + } + } +} + +/*! + * \brief Returns all package names as JSON array. + */ +QJsonArray Repository::packageNamesJsonArray() const +{ + QJsonArray names; + for(const auto &entry : m_packages) { + names << entry.first; + } + return names; +} + +/*! + * \cond + */ + +inline void put(QJsonObject &obj, const QString &key, const QJsonValue &value) +{ + if(!value.isNull()) { + obj.insert(key, value); + } +} + +inline void put(QJsonObject &obj, const QString &key, const QStringList &values) +{ + if(!values.isEmpty()) { + put(obj, key, QJsonArray::fromStringList(values)); + } +} + +/*! + * \endcond + */ + +/*! + * \brief Returns basic information about the repository. + */ +QJsonObject Repository::basicInfo() const +{ + QJsonObject info; + put(info, QStringLiteral("name"), name()); + put(info, QStringLiteral("desc"), description()); + put(info, QStringLiteral("servers"), serverUrls()); + put(info, QStringLiteral("usage"), Utilities::usageStrings(usage())); + 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("srcOnly"), isSourceOnly()); + return info; +} + +/*! + * \brief Returns group information as JSON object. + */ +QJsonObject Repository::groupInfo() const +{ + QJsonObject info; + put(info, QStringLiteral("repo"), name()); + QJsonArray groupsArray; + for(const auto &groupEntry : groups()) { + QJsonObject info; + put(info, QStringLiteral("name"), groupEntry.first); + QJsonArray pkgNames; + for(const auto *pkg : groupEntry.second) { + pkgNames << pkg->name(); + } + put(info, QStringLiteral("pkgs"), pkgNames); + groupsArray << info; + } + info.insert(QStringLiteral("groups"), groupsArray); + return info; +} + +} // namespace PackageManagement + diff --git a/alpm/repository.h b/alpm/repository.h new file mode 100644 index 0000000..20a851d --- /dev/null +++ b/alpm/repository.h @@ -0,0 +1,288 @@ +#ifndef PACKAGEMANAGEMENT_PACKAGESOURCE_H +#define PACKAGEMANAGEMENT_PACKAGESOURCE_H + +#include "package.h" +#include "group.h" + +#include + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QNetworkReply) + +namespace PackageManagement { + +class UpgradeLookupResults; + +class Reply : public QObject +{ + Q_OBJECT +public: + Reply(QNetworkReply *networkReply); + const QString &error() const; + +signals: + void resultsAvailable(); + +private slots: + virtual void processData() = 0; + +protected: + QNetworkReply *m_networkReply; + QString m_error; +}; + +inline const QString &Reply::error() const +{ + return m_error; +} + +class PackageReply : public Reply +{ + Q_OBJECT +public: + PackageReply(QNetworkReply *networkReply, std::map > &packages); + const std::map > &packages() const; + +protected: + std::map > &m_packages; +}; + +inline PackageReply::PackageReply(QNetworkReply *networkReply, std::map > &packages) : + Reply(networkReply), + m_packages(packages) +{} + +inline const std::map > &PackageReply::packages() const +{ + return m_packages; +} + +class SuggestionsReply : public Reply +{ + Q_OBJECT +public: + SuggestionsReply(QNetworkReply *networkReply); + const QJsonArray &suggestions() const; + +protected: + QJsonArray m_suggestions; +}; + +inline SuggestionsReply::SuggestionsReply(QNetworkReply *networkReply) : + Reply(networkReply) +{} + +inline const QJsonArray &SuggestionsReply::suggestions() const +{ + return m_suggestions; +} + +enum class RepositoryType +{ + AlpmDataBase, /*! The repository is an AlpmDataBase instance. */ + UserRepository, /*! The repository is a UserRepository instance. */ + Other /*! The repository type is unknown. */ +}; + +class Repository : public QObject +{ + Q_OBJECT +public: + ~Repository(); + + virtual RepositoryType type() const = 0; + + // general meta data + const QString &name() const; + const QString &description() const; + const std::map > &packages() const; + std::map > &packages(); + const QStringList packageNames() const; + alpm_db_usage_t usage() const; + virtual bool isSourceOnly() const; + const std::map > &groups() const; + const QStringList &serverUrls() const; + alpm_siglevel_t sigLevel() const; + + // gathering data + virtual bool requestsRequired() 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; + + // package search + const Package *packageByName(const QString &name) const; + Package *packageByName(const QString &name); + const Package *packageProviding(const Dependency &dependency) const; + QList packagesProviding(const Dependency &dependency) const; + QList packageByFilter(std::function pred); + + // upgrade lookup + const QList &upgradeSources() const; + QList &upgradeSources(); + QJsonArray upgradeSourcesJsonArray() const; + void checkForUpgrades(UpgradeLookupResults &results) const; + void checkForUpgrades(UpgradeLookupResults &results, const QList &upgradeSources) const; + + // build system + const QString &sourcesDirectory() const; + void setSourcesDirectory(const QString &dir); + const QString &packagesDirectory() const; + void setPackagesDirectory(const QString &dir); + + // JSON serialization + QJsonArray packageNamesJsonArray() const; + QJsonObject basicInfo() const; + QJsonObject groupInfo() const; + +protected: + explicit Repository(const QString &name, QObject *parent = nullptr); + + QString m_name; + QString m_description; + std::map > m_packages; + alpm_db_usage_t m_usage; + std::map > m_groups; + QStringList m_serverUrls; + alpm_siglevel_t m_sigLevel; + QList m_upgradeSources; + QString m_srcDir; + QString m_pkgDir; +}; + +/*! + * \brief Returns the name. + */ +inline const QString &Repository::name() const +{ + return m_name; +} + +/*! + * \brief Returns the description. + */ +inline const QString &Repository::description() const +{ + return m_description; +} + +/*! + * \brief Returns the packages. + */ +inline const std::map > &Repository::packages() const +{ + return m_packages; +} + +/*! + * \brief Returns the packages. + */ +inline std::map > &Repository::packages() +{ + return m_packages; +} + +/*! + * \brief Returns the package with the specified \a name or nullptr if the package can not be found. + * \remarks Ownership remains by this instance. + */ +inline const Package *Repository::packageByName(const QString &name) const +{ + try { + return m_packages.at(name).get(); + } catch(const std::out_of_range &) { + return nullptr; + } +} + +/*! + * \brief Returns the package with the specified \a name or nullptr if the package can not be found. + * \remarks Ownership remains by this instance. + */ +inline Package *Repository::packageByName(const QString &name) +{ + try { + return m_packages.at(name).get(); + } catch(const std::out_of_range &) { + return nullptr; + } +} + +inline alpm_db_usage_t Repository::usage() const +{ + return m_usage; +} + +inline const std::map > &Repository::groups() const +{ + return m_groups; +} + +/*! + * \brief Returns the server URLs. + */ +inline const QStringList &Repository::serverUrls() const +{ + return m_serverUrls; +} + +/*! + * \brief Returns the signature level of the database. + */ +inline alpm_siglevel_t Repository::sigLevel() const +{ + return m_sigLevel; +} + +inline const QList &Repository::upgradeSources() const +{ + return m_upgradeSources; +} + +inline QList &Repository::upgradeSources() +{ + return m_upgradeSources; +} + +inline void Repository::checkForUpgrades(UpgradeLookupResults &results) const +{ + checkForUpgrades(results, upgradeSources()); +} + +/*! + * \brief Returns the path of the local sources directory. + */ +inline const QString &Repository::sourcesDirectory() const +{ + return m_srcDir; +} + +/*! + * \brief Sets the path of the local sources directory. + */ +inline void Repository::setSourcesDirectory(const QString &dir) +{ + m_srcDir = dir; +} + +/*! + * \brief Returns the path of the local packages directory. + */ +inline const QString &Repository::packagesDirectory() const +{ + return m_pkgDir; +} + +/*! + * \brief Sets the path of the local packages directory. + */ +inline void Repository::setPackagesDirectory(const QString &dir) +{ + m_pkgDir = dir; +} + +} // namespace PackageManagement + +#endif // PACKAGEMANAGEMENT_PACKAGESOURCE_H diff --git a/alpm/resolvebuildorder.cpp b/alpm/resolvebuildorder.cpp index 158fa73..1baa096 100644 --- a/alpm/resolvebuildorder.cpp +++ b/alpm/resolvebuildorder.cpp @@ -172,8 +172,8 @@ 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)) { + for(auto dep : pkg->dependencies()) { + if(auto *depTask = addDep(tasks, dep.name)) { task->addDep(depTask); } } @@ -184,7 +184,7 @@ void BuildOrderResolver::addDeps(QList &tasks, TaskInfo *task) const } } -TaskInfo *BuildOrderResolver::addDep(QList &tasks, const char *depName) const +TaskInfo *BuildOrderResolver::addDep(QList &tasks, const QString &depName) const { if(auto *task = TaskInfo::find(tasks, depName)) { // we've already added a task for this dependency diff --git a/alpm/resolvebuildorder.h b/alpm/resolvebuildorder.h index 3135552..6f48ba8 100644 --- a/alpm/resolvebuildorder.h +++ b/alpm/resolvebuildorder.h @@ -20,7 +20,7 @@ public: private: void addDeps(QList &tasks, TaskInfo *task) const; - TaskInfo *addDep(QList &pkgInfos, const char *depName) const; + TaskInfo *addDep(QList &pkgInfos, const QString &depName) const; const Manager &m_manager; }; diff --git a/alpm/updatelookup.cpp b/alpm/updatelookup.cpp deleted file mode 100644 index 354ca55..0000000 --- a/alpm/updatelookup.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "updatelookup.h" -#include "manager.h" -#include "database.h" -#include "config.h" - -#include -#include - -#include - -using namespace std; - -namespace PackageManagement { - -UpdateLookup::UpdateLookup(const Manager &manager, const QJsonObject &request, const UpdateLookupCallback callback, QObject *parent) : - QObject(parent), - m_manager(manager), - m_request(request), - m_callback(callback), - m_db(nullptr), - m_syncDbsWatcher(new QFutureWatcher >(this)), - m_aurWatcher(new QFutureWatcher >(this)), - m_aurReply(nullptr), - m_sourcesAvailable(false) -{ - QJsonArray errors; - const auto dbName = request.value(QStringLiteral("db")).toString(); - try { - m_db = &manager.dataBaseByName(dbName); - } catch (const out_of_range &) { - errors << QStringLiteral("Database \"%1\" can not be found.").arg(dbName); - } - if(errors.isEmpty()) { - // invoke syncdb lookup - connect(m_syncDbsWatcher, &QFutureWatcher >::finished, this, &UpdateLookup::lookupDone); - m_syncDbsWatcher->setFuture(QtConcurrent::run(this, &UpdateLookup::checkSyncDbs, request.value(QStringLiteral("syncdbs")))); - // invoke AUR lookup - if(m_db->upgradeSources().contains(QStringLiteral("aur"), Qt::CaseInsensitive) || request.value(QStringLiteral("aur")).toBool(false)) { - if(manager.config().isAurEnabled()) { - connect(&m_manager.aurQuery(), &AurQuery::packageInfoAvailable, this, &UpdateLookup::aurPackageInfoAvailable); - connect(m_aurWatcher, &QFutureWatcher >::finished, this, &UpdateLookup::lookupDone); - if(!(m_aurReply = m_manager.aurQuery().requestPackageInfo(m_db->packageNames()))) { - aurPackageInfoAvailable(nullptr); - } - } else { - errors << QStringLiteral("The AUR is configured as upgrade source for the database \"%1\" but AUR queries are not enabled.").arg(dbName); - } - } - } else { - QJsonObject results; - results.insert(QStringLiteral("errors"), errors); - callback(move(results)); - deleteLater(); - } -} - -UpdateLookupResults UpdateLookup::checkSyncDbs(const QJsonValue &syncDbsValue) -{ - UpdateLookupResults results; - const auto &syncDbs = m_manager.syncDataBases(); - QList syncDbSel; - if(syncDbsValue.type() == QJsonValue::Array) { - for(const auto &syncDbVal : syncDbsValue.toArray()) { - const auto syncDbName = syncDbVal.toString(); - if(syncDbName == QLatin1String("local")) { - syncDbSel << &(m_manager.localDataBase()); - } else if(syncDbName == QLatin1String("aur")) { - continue; // the AUR is checked separately - } else { - try { - syncDbSel << &(syncDbs.at(syncDbName)); - } catch(out_of_range &) { - results.warnings << QStringLiteral("The specified sync database \"%1\" can not be found.").arg(syncDbName); - } - } - } - } else { - for(const auto &syncDbName : m_db->upgradeSources()) { - if(syncDbName == QLatin1String("local")) { - syncDbSel << &(m_manager.localDataBase()); - } else if(syncDbName == QLatin1String("aur")) { - continue; // the AUR is checked separately - } else { - try { - syncDbSel << &(syncDbs.at(syncDbName)); - } catch(out_of_range &) { - results.warnings << QStringLiteral("The associated upgrade database \"%1\" can not be found.").arg(syncDbName); - } - } - } - } - m_db->checkForUpgrades(syncDbSel, results); - return results; -} - -void UpdateLookup::aurPackageInfoAvailable(const QNetworkReply *reply) -{ - // check whether the package info requested by THIS INSTANCE is available - if(m_aurReply == reply) { - m_aurWatcher->setFuture(QtConcurrent::run(this, &UpdateLookup::checkAur)); - } -} - -UpdateLookupResults UpdateLookup::checkAur() -{ - UpdateLookupResults results; - m_db->checkForUpgrades(m_manager.aurQuery().packages(), results); - return results; -} - -void UpdateLookup::lookupDone() -{ - const auto *sender = this->sender(); - if(sender == static_cast(m_syncDbsWatcher)) { - // add results from syncdb lookup - m_syncDbsResults = m_syncDbsWatcher->result(); - if(!m_syncDbsResults.noSources) { - m_sourcesAvailable = true; - for(const auto pkg : m_syncDbsResults.newVersions) { - m_softwareUpdates << pkg.json(); - } - for(const auto pkg : m_syncDbsResults.newReleases) { - m_packageOnlyUpdates << pkg.json(); - } - for(const auto pkg : m_syncDbsResults.downgrades) { - m_downgrades << pkg.json(); - } - for(const auto &warning : m_syncDbsResults.warnings) { - m_warnings << warning; - } - for(const auto &error : m_syncDbsResults.errors) { - m_errors << error; - } - } - } else if(sender == static_cast(m_aurWatcher)) { - // add results from AUR lookup - m_aurResults = m_aurWatcher->result(); - if(!m_aurResults.noSources) { - m_sourcesAvailable = true; - for(const auto pkg : m_aurResults.newVersions) { - m_softwareUpdates << pkg.json(); - } - for(const auto pkg : m_aurResults.newReleases) { - m_packageOnlyUpdates << pkg.json(); - } - for(const auto pkg : m_aurResults.downgrades) { - m_downgrades << pkg.json(); - } - for(const auto &warning : m_aurResults.warnings) { - m_warnings << warning; - } - for(const auto &error : m_aurResults.errors) { - m_errors << error; - } - } - } - // check whether everything is done - if(m_syncDbsWatcher->isFinished() && (!m_aurReply || m_aurWatcher->isFinished())) { - QJsonObject results; - // determine orphaned packages - for(const auto pkg : m_aurReply ? m_aurResults.orphaned.intersect(m_syncDbsResults.orphaned) : m_syncDbsResults.orphaned) { - m_orphanedPackages << pkg.basicInfo(true); - } - // add results to results QJsonObject - results.insert(QStringLiteral("softwareUpdates"), m_softwareUpdates); - results.insert(QStringLiteral("packageOnlyUpdates"), m_packageOnlyUpdates); - results.insert(QStringLiteral("downgrades"), m_downgrades); - results.insert(QStringLiteral("orphanedPackages"), m_orphanedPackages); - if(!m_sourcesAvailable) { - m_errors << QStringLiteral("No update sources associated for database \"%1\".").arg(QString::fromLocal8Bit(m_db->name())); - } - if(!m_warnings.isEmpty()) { - results.insert(QStringLiteral("warnings"), m_warnings); - } - if(!m_errors.isEmpty()) { - results.insert(QStringLiteral("errors"), m_errors); - } - m_callback(move(results)); - // lookup done, delete this helper object - deleteLater(); - } -} - -} // namespace PackageManagement - diff --git a/alpm/updatelookup.h b/alpm/updatelookup.h deleted file mode 100644 index 54eb8fd..0000000 --- a/alpm/updatelookup.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef PACKAGEMANAGEMENT_UPDATELOOKUP_H -#define PACKAGEMANAGEMENT_UPDATELOOKUP_H - -#include "package.h" - -#include -#include -#include -#include - -#include - -QT_FORWARD_DECLARE_CLASS(QNetworkReply) - -namespace PackageManagement { - -class Manager; -class AlpmDataBase; - -typedef std::function UpdateLookupCallback; - -template -class UpdateResult -{ -public: - UpdateResult(const Package &package, const VersionType &previousVersion); - Package package; - VersionType previousVersion; - QJsonObject json() const; -}; - -template -inline UpdateResult::UpdateResult(const Package &package, const VersionType &previousVersion) : - package(package), - previousVersion(previousVersion) -{} - -template -QJsonObject UpdateResult::json() const -{ - QJsonObject obj; - obj.insert(QStringLiteral("pkg"), package.basicInfo(true)); - obj.insert(QStringLiteral("prevVersion"), previousVersion); - return obj; -} - -template -inline UpdateResult makeUpdateResult(const Package &package, const QString &previousVersion) -{ - return UpdateResult(package, previousVersion); -} - -template -inline UpdateResult makeUpdateResult(const Package &package, const char *previousVersion) -{ - return UpdateResult(package, QString::fromLocal8Bit(previousVersion)); -} - - - -template -class UpdateLookupResults -{ -public: - UpdateLookupResults(); - - /*! - * \brief Indicates that there are no upgrade sources available. - */ - bool noSources; - - /*! - * \brief Packages providing a software upgrade (new version). - */ - QList > newVersions; - - /*! - * \brief Package upgrades only (new release). - */ - QList > newReleases; - - /*! - * \brief Downgrades (older version in sync db). - */ - QList > downgrades; - - /*! - * \brief Orphaned packages (could not be found in any of the sync dbs). - */ - QSet orphaned; - - /*! - * \brief Warnings occured when checking for updates. - */ - QStringList warnings; - - /*! - * \brief Errors occured when checking for updates. - */ - QStringList errors; -}; - -/*! - * \brief Constructs new update lookup results. - */ -template -inline UpdateLookupResults::UpdateLookupResults() : - noSources(false) -{} - -class UpdateLookup : public QObject -{ - Q_OBJECT -public: - explicit UpdateLookup(const Manager &manager, const QJsonObject &request, const UpdateLookupCallback callback, QObject *parent = nullptr); - -private slots: - UpdateLookupResults checkSyncDbs(const QJsonValue &syncDbsValue); - void aurPackageInfoAvailable(const QNetworkReply *reply); - UpdateLookupResults checkAur(); - - void lookupDone(); - -private: - const Manager &m_manager; - const QJsonObject m_request; - const UpdateLookupCallback m_callback; - const AlpmDataBase *m_db; - QFutureWatcher > *m_syncDbsWatcher; - QFutureWatcher > *m_aurWatcher; - UpdateLookupResults m_syncDbsResults; - UpdateLookupResults m_aurResults; - QNetworkReply *m_aurReply; - bool m_sourcesAvailable; - QJsonArray m_warnings; - QJsonArray m_errors; - QJsonArray m_softwareUpdates; - QJsonArray m_packageOnlyUpdates; - QJsonArray m_downgrades; - QJsonArray m_orphanedPackages; -}; - -} // namespace PackageManagement - -#endif // PACKAGEMANAGEMENT_UPDATELOOKUP_H diff --git a/alpm/upgradelookup.cpp b/alpm/upgradelookup.cpp new file mode 100644 index 0000000..abd948a --- /dev/null +++ b/alpm/upgradelookup.cpp @@ -0,0 +1,155 @@ +#include "upgradelookup.h" +#include "manager.h" +#include "alpmdatabase.h" +#include "config.h" + +#include + +#include + +using namespace std; + +namespace PackageManagement { + +QJsonObject UpgradeResult::json() const +{ + QJsonObject obj; + obj.insert(QStringLiteral("pkg"), package->basicInfo(true)); + obj.insert(QStringLiteral("prevVersion"), previousVersion); + return obj; +} + +UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, const Repository *upgradeSource) : + QObject(upgradeLookup), + m_toCheck(upgradeLookup->toCheck()), + m_upgradeSource(upgradeSource), + m_reply(nullptr), + 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; + } + } + sourceReady(); +} + +const UpgradeLookupResults &UpgradeLookupProcess::results() const +{ + return m_results; +} + +void UpgradeLookupProcess::sourceReady() +{ + // if a request was required, check whether there occured an error + if(m_reply && !m_reply->error().isEmpty()) { + m_results.errors << m_reply->error(); + emit finished(); + } else { + connect(m_watcher, &QFutureWatcher::finished, this, &UpgradeLookupProcess::finished); + m_watcher->setFuture(QtConcurrent::run(this, &UpgradeLookupProcess::checkUpgrades)); + } +} + +void UpgradeLookupProcess::checkUpgrades() +{ + m_toCheck->checkForUpgrades(m_results, QList() << m_upgradeSource); +} + +UpgradeLookup::UpgradeLookup(const Manager &manager, const QJsonObject &request, QObject *parent) : + QObject(parent), + m_request(request), + m_toCheck(nullptr), + m_remainingProcesses(0), + m_firstFinished(false) +{ + const auto toCheckName = request.value(QStringLiteral("db")).toString(); + if((m_toCheck = manager.repositoryByName(toCheckName))) { + // construct upgrade lookup processes + const auto syncDbsArray = request.value(QStringLiteral("syncdbs")).toArray(); + if(syncDbsArray.isEmpty()) { + for(const auto *src : m_toCheck->upgradeSources()) { + new UpgradeLookupProcess(this, src); + ++m_remainingProcesses; + } + } else { + for(const auto &syncDbValue : syncDbsArray) { + const auto syncDbName = syncDbValue.toString(); + if(const auto *src = manager.repositoryByName(syncDbName)) { + new UpgradeLookupProcess(this, src); + ++m_remainingProcesses; + } else { + m_warningsArray << QStringLiteral("The specified upgrade source \"%1\" can not be found.").arg(syncDbName); + } + } + } + // check whether any processes could be constructed + if(!m_remainingProcesses) { + m_errorsArray << QStringLiteral("No upgrade sources associated for repository \"%1\".").arg(m_toCheck->name()); + } else { + return; // no errors so far + } + } else { + m_errorsArray << QStringLiteral("Repository \"%1\" can not be found.").arg(toCheckName); + } + // there are errors + QJsonObject results; + results.insert(QStringLiteral("errors"), m_errorsArray); + emit resultsAvailable(request.value(QStringLiteral("what")), request.value(QStringLiteral("id")), results); + deleteLater(); +} + +void UpgradeLookup::processFinished() +{ + assert(m_remainingProcesses); + // add results + const auto &results = static_cast(sender())->results(); + for(const auto pkg : results.newVersions) { + m_softwareUpdatesArray << pkg.json(); + } + for(const auto pkg : results.newReleases) { + m_packageOnlyUpdatesArray << pkg.json(); + } + for(const auto pkg : results.downgrades) { + m_downgradesArray << pkg.json(); + } + for(const auto &warning : results.warnings) { + m_warningsArray << warning; + } + for(const auto &error : results.errors) { + m_errorsArray << error; + } + if(m_firstFinished) { + m_orphanedPackages = m_orphanedPackages.intersect(results.orphaned); + } else { + m_firstFinished = true; + m_orphanedPackages = results.orphaned; + } + // check whether all processes are finished + if(--m_remainingProcesses == 0) { + // finally make info for orphanded packages + for(const auto *pkg : m_orphanedPackages) { + m_orphanedPackagesArray << pkg->basicInfo(true); + } + // add results to results QJsonObject + QJsonObject results; + results.insert(QStringLiteral("softwareUpdates"), m_softwareUpdatesArray); + results.insert(QStringLiteral("packageOnlyUpdates"), m_packageOnlyUpdatesArray); + results.insert(QStringLiteral("downgrades"), m_downgradesArray); + results.insert(QStringLiteral("orphanedPackages"), m_orphanedPackagesArray); + if(!m_warningsArray.isEmpty()) { + results.insert(QStringLiteral("warnings"), m_warningsArray); + } + if(!m_errorsArray.isEmpty()) { + results.insert(QStringLiteral("errors"), m_errorsArray); + } + emit resultsAvailable(m_request.value(QStringLiteral("what")), m_request.value(QStringLiteral("id")), results); + // lookup done, delete this helper object + deleteLater(); + } +} + +} // namespace PackageManagement diff --git a/alpm/upgradelookup.h b/alpm/upgradelookup.h new file mode 100644 index 0000000..6a276a0 --- /dev/null +++ b/alpm/upgradelookup.h @@ -0,0 +1,137 @@ +#ifndef PACKAGEMANAGEMENT_UPDATELOOKUP_H +#define PACKAGEMANAGEMENT_UPDATELOOKUP_H + +#include "package.h" + +#include +#include +#include +#include + +namespace PackageManagement { + +class Manager; +class UpgradeLookup; +class UpgradeLookupProcess; +class PackageReply; + +class UpgradeResult +{ +public: + UpgradeResult(const Package *package, const QString &previousVersion); + const Package *package; + QString previousVersion; + QJsonObject json() const; +}; + +inline UpgradeResult::UpgradeResult(const Package *package, const QString &previousVersion) : + package(package), + previousVersion(previousVersion) +{} + +class UpgradeLookupResults +{ +public: + UpgradeLookupResults(); + + /*! + * \brief Indicates that there are no upgrade sources available. + */ + bool noSources; + + /*! + * \brief Packages providing a software upgrade (new version). + */ + QList newVersions; + + /*! + * \brief Package upgrades only (new release). + */ + QList newReleases; + + /*! + * \brief Downgrades (older version in sync db). + */ + QList downgrades; + + /*! + * \brief Orphaned packages (could not be found in any of the sync dbs). + */ + QSet orphaned; + + /*! + * \brief Warnings occured when checking for updates. + */ + QStringList warnings; + + /*! + * \brief Errors occured when checking for updates. + */ + QStringList errors; +}; + +/*! + * \brief Constructs new update lookup results. + */ +inline UpgradeLookupResults::UpgradeLookupResults() : + noSources(false) +{} + +class UpgradeLookupProcess : public QObject +{ + Q_OBJECT +public: + explicit UpgradeLookupProcess(UpgradeLookup *upgradeLookup, const Repository *upgradeSource); + const UpgradeLookupResults &results() const; + +signals: + void finished(); + +private slots: + void sourceReady(); + void checkUpgrades(); + +private: + const Repository *m_toCheck; + const Repository *m_upgradeSource; + PackageReply *m_reply; + QFutureWatcher *m_watcher; + UpgradeLookupResults m_results; +}; + +class UpgradeLookup : public QObject +{ + Q_OBJECT + friend class UpgradeLookupProcess; +public: + explicit UpgradeLookup(const Manager &manager, const QJsonObject &request, QObject *parent = nullptr); + const Repository *toCheck() const; + +signals: + void resultsAvailable(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value); + +private slots: + void processFinished(); + +private: + const QJsonObject m_request; + const Repository *m_toCheck; + unsigned int m_remainingProcesses; + bool m_firstFinished; + QSet m_orphanedPackages; + QJsonArray m_warningsArray; + QJsonArray m_errorsArray; + QJsonArray m_softwareUpdatesArray; + QJsonArray m_packageOnlyUpdatesArray; + QJsonArray m_downgradesArray; + QJsonArray m_orphanedPackagesArray; +}; + +inline const Repository *UpgradeLookup::toCheck() const +{ + return m_toCheck; +} + +} // namespace PackageManagement + +#endif // PACKAGEMANAGEMENT_UPDATELOOKUP_H diff --git a/alpm/utilities.cpp b/alpm/utilities.cpp index 8e47391..ad5bb46 100644 --- a/alpm/utilities.cpp +++ b/alpm/utilities.cpp @@ -1,7 +1,8 @@ #include "utilities.h" -#include +#include #include +#include using namespace std; @@ -105,6 +106,102 @@ QString sigStatusString(alpm_sigstatus_t sigStatus) } } +/*! + * \brief Returns the string representation for the specified \a validationMethods. + */ +QJsonArray validationMethodsStrings(alpm_pkgvalidation_t validationMethods) +{ + QJsonArray jsonArray; + if(validationMethods & 0x1) { + jsonArray << QStringLiteral("none"); + } + if(validationMethods & 0x2) { + jsonArray << QStringLiteral("MD5"); + } + if(validationMethods & 0x4) { + jsonArray << QStringLiteral("SHA256"); + } + if(validationMethods & 0x8) { + jsonArray << QStringLiteral("signature"); + } + if(jsonArray.empty()) { + jsonArray << QStringLiteral("unknown"); + } + return jsonArray; +} + +QJsonArray qjarry(StringList list) +{ + QJsonArray jsonArray; + for(const char *str : list) { + jsonArray << qstr(str); + } + return jsonArray; +} + +QJsonArray qjarry(DependencyList list) +{ + QJsonArray jsonArray; + for(alpm_depend_t *dep : list) { + QJsonObject depObj; + depObj.insert(QStringLiteral("name"), dep->name); + depObj.insert(QStringLiteral("desc"), dep->desc); + depObj.insert(QStringLiteral("ver"), dep->version); + switch(dep->mod) { + case ALPM_DEP_MOD_ANY: + depObj.insert(QStringLiteral("mod"), QStringLiteral("any")); + break; + case ALPM_DEP_MOD_EQ: + depObj.insert(QStringLiteral("mod"), QStringLiteral("eq")); + break; + case ALPM_DEP_MOD_GE: + depObj.insert(QStringLiteral("mod"), QStringLiteral("ge")); + break; + case ALPM_DEP_MOD_LE: + depObj.insert(QStringLiteral("mod"), QStringLiteral("le")); + break; + case ALPM_DEP_MOD_GT: + depObj.insert(QStringLiteral("mod"), QStringLiteral("gt")); + break; + case ALPM_DEP_MOD_LT: + depObj.insert(QStringLiteral("mod"), QStringLiteral("lt")); + break; + } + jsonArray << depObj; + } + return jsonArray; +} + +QJsonArray qjarry(_alpm_pkgvalidation_t validation) +{ + QJsonArray jsonArray; + if(validation & 0x1) { + jsonArray << QStringLiteral("none"); + } + if(validation & 0x2) { + jsonArray << QStringLiteral("MD5"); + } + if(validation & 0x4) { + jsonArray << QStringLiteral("SHA256"); + } + if(validation & 0x8) { + jsonArray << QStringLiteral("signature"); + } + if(jsonArray.empty()) { + jsonArray << QStringLiteral("unknown"); + } + return jsonArray; +} + +QStringList qstrlist(StringList list) +{ + QStringList strings; + for(const auto *str : list) { + strings << qstr(str); + } + return strings; +} + } } // namespace Alpm diff --git a/alpm/utilities.h b/alpm/utilities.h index 2ef5aaa..55064f7 100644 --- a/alpm/utilities.h +++ b/alpm/utilities.h @@ -3,15 +3,12 @@ #include "list.h" -#include +#include #include #include -QT_BEGIN_NAMESPACE -class QString; -class QJsonArray; -QT_END_NAMESPACE +QT_FORWARD_DECLARE_CLASS(QJsonArray) namespace PackageManagement { @@ -22,6 +19,22 @@ std::list getNames(DependencyList dependencyList); QJsonArray sigLevelStrings(alpm_siglevel_t sigLevel); QJsonArray usageStrings(alpm_db_usage_t usage); QString sigStatusString(alpm_sigstatus_t sigStatus); +QJsonArray validationMethodsStrings(alpm_pkgvalidation_t validationMethods); + +QJsonArray qjarry(StringList list); +QJsonArray qjarry(DependencyList list); +QJsonArray qjarry(_alpm_pkgvalidation_t validation); +QStringList qstrlist(StringList list); + +inline QString qstr(const char *str) +{ + return QString::fromLocal8Bit(str); +} + +inline QString qstr(const std::string &str) +{ + return QString::fromLocal8Bit(str.data()); +} } diff --git a/general.pri b/general.pri index 89889a6..9b062bc 100644 --- a/general.pri +++ b/general.pri @@ -8,9 +8,6 @@ RCC_DIR = ./res # compiler flags QMAKE_CXXFLAGS += -std=c++11 QMAKE_LFLAGS += -std=c++11 -unix { - QMAKE_LFLAGS += "-Wl,--rpath=./" -} # prefix targetprefix = $$(TARGET_PREFIX) message("Using target prefix \"$${targetprefix}\".") diff --git a/main.cpp b/main.cpp index 1059890..afd1404 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "alpm/config.h" #include "alpm/resolvebuildorder.h" #include "alpm/mingwbundle.h" + #include "network/server.h" #include @@ -39,6 +40,7 @@ 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 @@ -61,8 +63,10 @@ int main(int argc, char *argv[]) } cout << endl; } else if(configArgs.mingwBundleArg.isPresent()) { - MingwBundle bundle(manager, configArgs.mingwBundleArg.values()); - bundle.createBundle(configArgs.outputFileArg.values().front()); + 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 { cout << "No command line arguments specified. See --help for available commands." << endl; diff --git a/network/aurquery.cpp b/network/aurquery.cpp deleted file mode 100644 index c8f5fff..0000000 --- a/network/aurquery.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "aurquery.h" - -#include "alpm/package.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; - -namespace PackageManagement { - -QUrl AurQuery::m_aurRpcUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php")); - -QUrl AurQuery::m_aurPkgbuildUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD")); - -const char *requestTypeProp = "type"; -const QString rpcRequestTypeKey(QStringLiteral("type")); -const QString rpcRequestTypeSuggest(QStringLiteral("suggest")); -const QString rpcRequestTypeMultiInfo(QStringLiteral("multiinfo")); -const QString rpcArgKey(QStringLiteral("arg")); -const QString rpcArgArray(QStringLiteral("arg[]")); -const QString pkgbuildRequestType(QString("pkgbuild")); - -AurQuery::AurQuery(QNetworkAccessManager &networkAccessManager, QObject *parent) : - QObject(parent), - m_networkAccessManager(networkAccessManager) -{} - -QNetworkReply *AurQuery::requestSuggestions(const QString &phrase) const -{ - auto url = m_aurRpcUrl; - QUrlQuery query; - query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeSuggest); - query.addQueryItem(rpcArgKey, phrase); - url.setQuery(query); - auto *reply = m_networkAccessManager.get(QNetworkRequest(url)); - reply->setProperty(requestTypeProp, rpcRequestTypeSuggest); - connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived); - return reply; -} - -/*! - * \brief Requests package information for the specified packages from the AUR. - * \returns Returns the QNetworkReply used for the request. - * \remarks - * If \a forceUpdate is true, package information which has already been retrieved - * and is still cached is requested again. Otherwise these packages will not be - * requested again. If it turns out, that all packages are already cached, nullptr - * is returned in this case. - */ -QNetworkReply *AurQuery::requestPackageInfo(const QStringList &packageNames, bool forceUpdate) const -{ - QUrlQuery query; - for(const auto &packageName : packageNames) { - if(forceUpdate || !m_packages.count(packageName)) { - query.addQueryItem(rpcArgArray, packageName); - } - } - if(query.isEmpty()) { - return nullptr; - } else { - auto url = m_aurRpcUrl; - query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeMultiInfo); - url.setQuery(query); - auto *reply = m_networkAccessManager.get(QNetworkRequest(url)); - reply->setProperty(requestTypeProp, rpcRequestTypeMultiInfo); - connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived); - return reply; - } -} - -QNetworkReply *AurQuery::requestFullPackageInfo(const QString &package, bool forceUpdate) const -{ - try { - const auto &pkg = m_packages.at(package); - if(pkg.hasFullInfo() && !forceUpdate) { - return nullptr; - } - } catch(const out_of_range &) { - } - auto url = m_aurPkgbuildUrl; - QUrlQuery query; - query.addQueryItem(QStringLiteral("h"), package); - url.setQuery(query); - auto *reply = m_networkAccessManager.get(QNetworkRequest(url)); - reply->setProperty(requestTypeProp, pkgbuildRequestType); - connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived); - return reply; -} - -void AurQuery::dataReceived() -{ - if(auto *reply = qobject_cast(sender())) { - if(reply->error() == QNetworkReply::NoError) { - QJsonParseError error; - QByteArray data = reply->readAll(); - cerr << "AUR reply: " << data.data() << endl; - const QJsonDocument doc = QJsonDocument::fromJson(data, &error); - //const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error); - if(error.error == QJsonParseError::NoError) { - const QString requestType = reply->property(requestTypeProp).toString(); - if(requestType == rpcRequestTypeSuggest) { - emit suggestionsAvailable(reply, doc.array()); - } else if(requestType == rpcRequestTypeMultiInfo) { - processPackageInfo(reply, doc.object().value(QStringLiteral("results")).toArray()); - } else { - cerr << "Error: Reply has invalid type. (should never happen)" << endl; - } - } else { - cerr << "Error: Unable to parse JSON received from AUR: " << error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl; - } - } else { - cerr << "Error: Unable to request data from AUR: " << reply->errorString().toLocal8Bit().data() << endl; - } - } -} - -void AurQuery::processPackageInfo(const QNetworkReply *reply, const QJsonArray &results) -{ - for(const auto &result : results) { - AurPackage package(result); - if(!package.name().isEmpty()) { - m_packages[package.name()] = move(package); - } - } - emit packageInfoAvailable(reply); -} - -} // namespace Alpm - diff --git a/network/aurquery.h b/network/aurquery.h deleted file mode 100644 index a9c7473..0000000 --- a/network/aurquery.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef ALPM_AURQUERY_H -#define ALPM_AURQUERY_H - -#include "alpm/package.h" - -#include -#include - -#include - -QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager) -QT_FORWARD_DECLARE_CLASS(QNetworkReply) - -namespace PackageManagement { - -class AurQuery : public QObject -{ - Q_OBJECT -public: - AurQuery(QNetworkAccessManager &networkAccessManager, QObject *parent = nullptr); - - static const QUrl aurRpcUrl(); - static void setAurRpcUrl(const QUrl &aurRpcUrl); - - QNetworkReply *requestSuggestions(const QString &phrase) const; - QNetworkReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const; - QNetworkReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const; - const std::map &packages() const; - std::map &packages(); - -signals: - void suggestionsAvailable(const QNetworkReply *reply, const QJsonArray &suggestions); - void packageInfoAvailable(const QNetworkReply *reply); - -private slots: - void dataReceived(); - -private: - void processPackageInfo(const QNetworkReply *reply, const QJsonArray &results); - - QNetworkAccessManager &m_networkAccessManager; - std::map m_packages; - - static QUrl m_aurRpcUrl; - static QUrl m_aurPkgbuildUrl; -}; - -inline const QUrl AurQuery::aurRpcUrl() -{ - return m_aurRpcUrl; -} - -inline void AurQuery::setAurRpcUrl(const QUrl &aur4Url) -{ - m_aurRpcUrl = aur4Url; -} - -inline const std::map &AurQuery::packages() const -{ - return m_packages; -} - - -inline std::map &AurQuery::packages() -{ - return m_packages; -} - -} // namespace Alpm - -#endif // ALPM_AURQUERY_H diff --git a/network/connection.cpp b/network/connection.cpp index d9847b1..a4c8d2d 100644 --- a/network/connection.cpp +++ b/network/connection.cpp @@ -1,19 +1,18 @@ #include "connection.h" #include "alpm/manager.h" +#include "alpm/upgradelookup.h" #include #include #include #include -#include - -using namespace std; +using namespace PackageManagement; namespace Network { -Connection::Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent) : +Connection::Connection(const Manager &alpmManager, QWebSocket *socket, QObject *parent) : QObject(parent), m_socket(socket), m_alpmManager(alpmManager), @@ -39,7 +38,7 @@ void Connection::sendError(const QString &msg) sendJson(response); } -void Connection::sendResult(const QString &what, const QJsonValue &id, const QJsonValue &value) +void Connection::sendResult(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value) { QJsonObject response; response.insert(QStringLiteral("class"), QStringLiteral("results")); @@ -51,7 +50,7 @@ void Connection::sendResult(const QString &what, const QJsonValue &id, const QJs sendJson(response); } -void Connection::sendResults(const QString &what, const QJsonValue &id, const QJsonArray &values) +void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonArray &values) { QJsonObject response; response.insert(QStringLiteral("class"), QStringLiteral("results")); @@ -71,18 +70,14 @@ void Connection::handleQuery(const QJsonObject &obj) m_repoInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_repoInfoUpdatesRequested); sendResults(what, id, m_alpmManager.basicRepoInfo()); } else if(what == QLatin1String("basicpkginfo")) { - sendResults(what, id, m_alpmManager.packageInfo(obj.value("sel").toArray(), false)); + sendResults(what, id, m_alpmManager.packageInfo(obj.value("sel").toObject(), false)); } else if(what == QLatin1String("fullpkginfo")) { - sendResults(what, id, m_alpmManager.packageInfo(obj.value("sel").toArray(), true)); + sendResults(what, id, m_alpmManager.packageInfo(obj.value("sel").toObject(), true)); } else if(what == QLatin1String("groupinfo")) { m_groupInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_groupInfoUpdatesRequested); sendResults(what, id, m_alpmManager.groupInfo()); } else if(what == QLatin1String("checkforupdates")) { - //sendResult(what, id, m_alpmManager.invokeUpgradeLookup(obj)); - m_alpmManager.invokeUpgradeLookup(obj, bind(&Connection::sendResult, this, what, id, placeholders::_1)); - //m_alpmManager.checkForUpgrades(obj, [this] (const QJsonObject &results) { - // sendResult(what, id, results); - //}; + connect(new UpgradeLookup(m_alpmManager, obj), &UpgradeLookup::resultsAvailable, this, &Connection::sendResult); } else if(what == QLatin1String("ping")) { sendResult(what, id, QStringLiteral("pong")); } else { diff --git a/network/connection.h b/network/connection.h index c8428ef..97fc505 100644 --- a/network/connection.h +++ b/network/connection.h @@ -24,12 +24,12 @@ private slots: void processTextMessage(const QString &message); void processBinaryMessage(const QByteArray &message); void socketDisconnected(); + void sendResult(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value); + void sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonArray &values); private: void sendJson(const QJsonObject &obj); void sendError(const QString &msg); - void sendResult(const QString &what, const QJsonValue &id, const QJsonValue &value); - void sendResults(const QString &what, const QJsonValue &id, const QJsonArray &values); void handleQuery(const QJsonObject &obj); QWebSocket *m_socket; diff --git a/network/userrepository.cpp b/network/userrepository.cpp new file mode 100644 index 0000000..fc2a4b4 --- /dev/null +++ b/network/userrepository.cpp @@ -0,0 +1,145 @@ +#include "userrepository.h" + +#include "alpm/aurpackage.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace PackageManagement { + +const char *requestTypeProp = "type"; +const QString rpcRequestTypeKey(QStringLiteral("type")); +const QString rpcRequestTypeSuggest(QStringLiteral("suggest")); +const QString rpcRequestTypeMultiInfo(QStringLiteral("multiinfo")); +const QString rpcArgKey(QStringLiteral("arg")); +const QString rpcArgArray(QStringLiteral("arg[]")); +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")); + +AurPackageReply::AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo) : + PackageReply(networkReply, userRepo->packages()), + m_userRepo(userRepo) +{} + +void AurPackageReply::processData() +{ + if(m_networkReply->error() == QNetworkReply::NoError) { + QJsonParseError error; + //QByteArray data = m_networkReply->readAll(); + //cerr << "AUR reply: " << data.data() << endl; + //const QJsonDocument doc = QJsonDocument::fromJson(data, &error); + const QJsonDocument doc = QJsonDocument::fromJson(m_networkReply->readAll(), &error); + if(error.error == QJsonParseError::NoError) { + for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) { + auto package = make_unique(result, m_userRepo); + if(!package->name().isEmpty()) { + m_packages[package->name()] = move(package); + } + } + } 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(); + } + emit resultsAvailable(); +} + +AurSuggestionsReply::AurSuggestionsReply(QNetworkReply *networkReply) : + SuggestionsReply(networkReply) +{} + +void AurSuggestionsReply::processData() +{ + if(m_networkReply->error() == QNetworkReply::NoError) { + QJsonParseError error; + //QByteArray data = m_networkReply->readAll(); + //cerr << "AUR reply: " << data.data() << endl; + //const QJsonDocument doc = QJsonDocument::fromJson(data, &error); + const QJsonDocument doc = QJsonDocument::fromJson(m_networkReply->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(); + } + emit resultsAvailable(); +} + +UserRepository::UserRepository(QNetworkAccessManager &networkAccessManager, QObject *parent) : + Repository(QStringLiteral("AUR"), parent), + m_networkAccessManager(networkAccessManager) +{ + m_description = QStringLiteral("Arch User Repository"); +} + +RepositoryType UserRepository::type() const +{ + return RepositoryType::UserRepository; +} + +bool UserRepository::requestsRequired() const +{ + return true; +} + +AurSuggestionsReply *UserRepository::requestSuggestions(const QString &phrase) const +{ + auto url = m_aurRpcUrl; + QUrlQuery query; + query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeSuggest); + query.addQueryItem(rpcArgKey, phrase); + url.setQuery(query); + return new AurSuggestionsReply(m_networkAccessManager.get(QNetworkRequest(url))); +} + +AurPackageReply *UserRepository::requestPackageInfo(const QStringList &packageNames, bool forceUpdate) const +{ + QUrlQuery query; + for(const auto &packageName : packageNames) { + if(forceUpdate || !m_packages.count(packageName)) { + query.addQueryItem(rpcArgArray, packageName); + } + } + if(query.isEmpty()) { + return nullptr; + } else { + auto url = m_aurRpcUrl; + query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeMultiInfo); + url.setQuery(query); + return new AurPackageReply(m_networkAccessManager.get(QNetworkRequest(url)), const_cast(this)); + } +} + +AurPackageReply *UserRepository::requestFullPackageInfo(const QString &package, bool forceUpdate) const +{ + try { + const auto &pkg = m_packages.at(package); + if(pkg->hasGeneralInfo() && !forceUpdate) { + return nullptr; + } + } 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)); +} + +} // namespace Alpm + diff --git a/network/userrepository.h b/network/userrepository.h new file mode 100644 index 0000000..059da6d --- /dev/null +++ b/network/userrepository.h @@ -0,0 +1,79 @@ +#ifndef ALPM_USER_REPOSITORY_H +#define ALPM_USER_REPOSITORY_H + +#include "alpm/package.h" +#include "alpm/repository.h" + +#include +#include + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager) +QT_FORWARD_DECLARE_CLASS(QNetworkReply) + +namespace PackageManagement { + +class UserRepository; + +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 AurSuggestionsReply : public SuggestionsReply +{ + Q_OBJECT +public: + AurSuggestionsReply(QNetworkReply *networkReply); + +private slots: + void processData(); +}; + +class UserRepository : public Repository +{ + Q_OBJECT +public: + UserRepository(QNetworkAccessManager &networkAccessManager, QObject *parent = nullptr); + + RepositoryType type() const; + + static const QUrl aurRpcUrl(); + static void setAurRpcUrl(const QUrl &aurRpcUrl); + + bool requestsRequired() 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; + +private: + QNetworkAccessManager &m_networkAccessManager; + static QUrl m_aurRpcUrl; + static QUrl m_aurPkgbuildUrl; + static QUrl m_aurSrcInfoUrl; +}; + +inline const QUrl UserRepository::aurRpcUrl() +{ + return m_aurRpcUrl; +} + +inline void UserRepository::setAurRpcUrl(const QUrl &aur4Url) +{ + m_aurRpcUrl = aur4Url; +} + +} // namespace Alpm + +#endif // ALPM_USER_REPOSITORY_H diff --git a/repoindex.pro b/repoindex.pro index 9339261..86ef93f 100644 --- a/repoindex.pro +++ b/repoindex.pro @@ -1,4 +1,7 @@ projectname = repoindex +appname = "Repository Index" +appauthor = Martchus +appurl = "https://github.com/$${appauthor}/$${projectname}" VERSION = 1.0.0 # include ../../common.pri when building as part of a subdirs project; otherwise include general.pri @@ -18,30 +21,36 @@ SOURCES += main.cpp \ alpm/manager.cpp \ alpm/package.cpp \ alpm/utilities.cpp \ - alpm/database.cpp \ network/server.cpp \ network/connection.cpp \ alpm/group.cpp \ alpm/config.cpp \ - network/aurquery.cpp \ - alpm/updatelookup.cpp \ alpm/resolvebuildorder.cpp \ - alpm/mingwbundle.cpp + alpm/mingwbundle.cpp \ + network/userrepository.cpp \ + alpm/alpmpackage.cpp \ + alpm/aurpackage.cpp \ + alpm/alpmdatabase.cpp \ + alpm/repository.cpp \ + alpm/upgradelookup.cpp HEADERS += \ alpm/manager.h \ alpm/package.h \ alpm/list.h \ alpm/utilities.h \ - alpm/database.h \ network/server.h \ network/connection.h \ alpm/group.h \ alpm/config.h \ - network/aurquery.h \ - alpm/updatelookup.h \ alpm/resolvebuildorder.h \ - alpm/mingwbundle.h + alpm/mingwbundle.h \ + network/userrepository.h \ + alpm/alpmpackage.h \ + alpm/aurpackage.h \ + alpm/alpmdatabase.h \ + alpm/repository.h \ + alpm/upgradelookup.h DISTFILES += \ README.md \ @@ -60,13 +69,18 @@ DISTFILES += \ web/css/dashboard.css \ repoindex.conf.js +# defines +CONFIG(release, debug|release) { + DEFINES += NDEBUG +} + # libs and includepath CONFIG(debug, debug|release) { - LIBS += -L../../ -lc++utilitiesd -lalpm -lKF5Archive + LIBS += -lc++utilitiesd } else { - LIBS += -L../../ -lc++utilities -lalpm -lKF5Archive + LIBS += -lc++utilities } -INCLUDEPATH += ../ +LIBS += -lalpm -lKF5Archive # installs target.path = $$(INSTALL_ROOT)/bin diff --git a/web/index.html b/web/index.html index e439ee8..7435d06 100644 --- a/web/index.html +++ b/web/index.html @@ -232,11 +232,9 @@ Package count Usage Signature level + Source-only Upgrade sources - - Upgrades - - + Upgrades diff --git a/web/js/alpm.js b/web/js/alpm.js index c9345e6..2201bf1 100644 --- a/web/js/alpm.js +++ b/web/js/alpm.js @@ -248,10 +248,10 @@ repoMgr.removeEntries(); pkgMgr.removeEntries(); for(var i1 = 0; i1 < values.length; ++i1) { - repoMgr.addEntry(values[i1]); - var packages = values[i1].packages; + var repoEntry = repoMgr.addEntry(values[i1]); + var packages = repoEntry.info.packages; for(var i2 = 0; i2 < packages.length; ++i2) { - pkgMgr.addEntry(values[i1].name, packages[i2]); + pkgMgr.addEntry(repoEntry, packages[i2]); } } this.hasBasicRepoInfo = true; diff --git a/web/js/entrymanagement.js b/web/js/entrymanagement.js index 429bb2f..0598fd6 100644 --- a/web/js/entrymanagement.js +++ b/web/js/entrymanagement.js @@ -180,6 +180,24 @@ } }; + this.entryByName = function(entryName) { + if(this.customSelection) { + for(var i = 0; i < this.customSelection.length; ++i) { + var entry = this.customSelection[i]; + if(entry.info && entry.info.name === entryName) { + return entry; + } + } + } else { + for(var i = 0; i < this.entries.length; ++i) { + var entry = this.entries[i]; + if(entry.info && entry.info.name === entryName) { + return entry; + } + } + } + }; + // function called by the ALPM client when requested data is available this.useRequestedData = function() { this.invalidate(); diff --git a/web/js/packagemanagement.js b/web/js/packagemanagement.js index ed66127..599cb39 100644 --- a/web/js/packagemanagement.js +++ b/web/js/packagemanagement.js @@ -8,7 +8,8 @@ var PackageEntry = {}; PackageEntry.prototype = new repoindex.Entry(); PackageEntry.prototype.constructor = PackageEntry; - PackageEntry = function(packageInfo, color) { + PackageEntry = function(repoEntry, packageInfo, color) { + this.repoEntry = repoEntry; // might be undefined repoindex.Entry.prototype.constructor.call(this, packageInfo); // init row element @@ -20,7 +21,8 @@ }; this.initTableRow = function() { - var values = [this.info.arch, this.info.repo, this.info.name, this.info.ver, this.info.desc, this.info.bdate, ""]; + var srcOnly = this.repoEntry && this.repoEntry.info.srcOnly; + var values = [srcOnly ? "n/a" : this.info.arch, this.info.repo, this.info.name, this.info.ver, this.info.desc, srcOnly ? "n/a" : this.info.bdate, this.info.flagdate]; for(var i = 0; i < 7; ++i) { this.rowElement.addCell(repoindex.makeStr(values[i])); } @@ -35,6 +37,7 @@ if(info.ver) this.info.ver = info.ver; if(info.desc) this.info.desc = info.desc; if(info.bdate) this.info.bdate = info.bdate; + if(info.flagdate) this.info.flagdate = info.flagdate; this.info.basic = true; if(!noUpdate) { this.updateTableRow(); @@ -80,22 +83,19 @@ this.getContainerQuantity = repoindex.entryManagerGetRepoQuantity; this.createCustomEntry = function(color) { - return new PackageEntry({}, color); + return new PackageEntry(undefined, {}, color); }; - this.addEntry = function(repoName, packageName) { + this.addEntry = function(repoEntry, packageName) { var packageInfo = { index: this.entries.length, - arch: undefined, - repo: repoName, + repo: repoEntry.info.name, name: packageName, - version: undefined, - desc: undefined, - builddate: undefined, - flagdate: undefined, received: false }; - this.entries.push(new PackageEntry(packageInfo)); + var entry = new PackageEntry(repoEntry, packageInfo); + this.entries.push(entry); + return entry; }; // handle a page selection @@ -106,15 +106,22 @@ // if there is no page because there are no package entries, pageElement is null if(pageElement) { // show elements of selected page - var packageSelection = []; // package selection for requesting package infos + var packageSelection = {}; // package selection for requesting package infos + var entriesRequired = false; pageElement.forRange(function(i) { var entry = mgr.filteredEntries[i]; entry.add(mgr.entryContainer); if(!entry.info.basic) { - packageSelection.push({index: entry.info.index, repo: entry.info.repo, name: entry.info.name}); + var repoArray = packageSelection[entry.info.repo]; + if(!Array.isArray(repoArray)) { + packageSelection[entry.info.repo] = [{index: entry.info.index, name: entry.info.name}]; + } else { + repoArray.push({index: entry.info.index, name: entry.info.name}); + } + entriesRequired = true; } }, mgr.filteredEntries.length); - if(packageSelection.length > 0) { + if(entriesRequired) { var pkgEntries = repoindex.pageManager.packageManager.relevantEntries(); var useBasicPackageInfo = function(values) { for(var i = 0; i < values.length; ++i) { @@ -228,7 +235,9 @@ }; if(!i.full) { // don't have the full package info yet -> request full package info - repoindex.alpmClient.requestFullPackagesInfo([{index: entryIndex, repo: i.repo, name: i.name}], useFullPackageInfo); + var packageSelection = {}; + packageSelection[i.repo] = [{index: entryIndex, name: i.name}]; + repoindex.alpmClient.requestFullPackagesInfo(packageSelection, useFullPackageInfo); } // set currentInfo (the pageManager needs this value to update the hash) this.currentInfo = i; @@ -254,7 +263,10 @@ showEntry(); } else { // no -> request full info, use callback to show entry when info becomes available - repoindex.alpmClient.requestFullPackagesInfo([{index: i.index, repo: i.repo, name: i.name}], showEntry()); + //repoindex.alpmClient.requestFullPackagesInfo([{index: i.index, repo: i.repo, name: i.name}], showEntry()); + var packageSelection = {}; + packageSelection[i.repo] = [{index: entryIndex, name: i.name}]; + repoindex.alpmClient.requestFullPackagesInfo(packageSelection, showEntry); } } else { // no -> show error diff --git a/web/js/repomanagement.js b/web/js/repomanagement.js index 2d253ef..7526da8 100644 --- a/web/js/repomanagement.js +++ b/web/js/repomanagement.js @@ -4,6 +4,9 @@ RepoEntry.prototype = new repoindex.Entry(); RepoEntry.prototype.constructor = RepoEntry; RepoEntry = function(repoInfo, enabled) { + if(enabled === undefined) { + enabled = !repoInfo.requestRequired; + } repoindex.Entry.prototype.constructor.call(this, repoInfo, enabled); this.info.currentServer = 0; @@ -40,12 +43,14 @@ this.link.setAttribute("data-placement", "bottom"); $(this.link).tooltip(); - // create badge with package count - var span = document.createElement("span"); - span.className = "badge"; - span.appendChild(document.createTextNode(repoInfo.packages.length)); - this.link.appendChild(document.createTextNode(" ")); - this.link.appendChild(span); + if(!repoInfo.requestRequired) { + // create badge with package count + var span = document.createElement("span"); + span.className = "badge"; + span.appendChild(document.createTextNode(repoInfo.packages.length)); + this.link.appendChild(document.createTextNode(" ")); + this.link.appendChild(span); + } this.link.add = function() { repoindex.pageManager.repoManager.buttonContainer.appendChild(this); @@ -103,12 +108,13 @@ // provide a function to add repo entries this.addEntry = function(repoInfo) { - var entry = new RepoEntry(repoInfo, true); + var entry = new RepoEntry(repoInfo); entry.statusChanged = this.applyRepoStatusChange; entry.pageManager = this; entry.repoEntryManager = this; entry.info.index = this.entries.length; this.entries.push(entry); + return entry; }; this.baseRemoveEntries = this.removeEntries; @@ -172,9 +178,10 @@ var i = entry.info; repoindex.setText("repo_name", i.name); repoindex.setText("repo_desc", i.desc); - repoindex.setText("repo_pkgcount", i.packages.length); + repoindex.setText("repo_pkgcount", i.requestRequired ? "unknown" : i.packages.length); repoindex.setText("repo_usage", repoindex.makeArray(i.usage, ", ")); repoindex.setText("repo_siglevel", repoindex.makeArray(i.sigLevel, ", ")); + repoindex.setText("repo_source_only", repoindex.makeBool(i.srcOnly)); repoindex.setText("repo_upgrade_sources", repoindex.makeArray(i.upgradeSources, ", ")); var invokeUpdateLookupParams = {repo: i.name, invoke: "updatelookup"}; repoindex.setDownloadButton("repo_checkforupdates", "check for updates", repoindex.makeHash(repoindex.Pages.Repositories, invokeUpdateLookupParams, true), function() { @@ -238,6 +245,7 @@ } ]; var pkgMgr = repoindex.pageManager.packageManager; + var repoMgr = repoindex.pageManager.repoManager; for(var i1 = 0; i1 < updates.length; ++i1) { for(var i2 = 0; i2 < updates[i1].entries.length; ++i2) { var newEntry = pkgMgr.createCustomEntry(updates[i1].color); @@ -247,6 +255,10 @@ if(updates[i1].entries[i2].prevVersion) { newEntry.info.ver = updates[i1].entries[i2].prevVersion + " → " + (newEntry.info.ver ? newEntry.info.ver : "?"); } + // find associated repo entry + if((newEntry.repoEntry = repoMgr.entryByName(newEntry.info.repo))) { + newEntry.repoEntry.updateEnabled(true); // ensure repo is enabled + } newEntry.updateTableRow(); } else { newEntry.applyBasicInfo(updates[i1].entries[i2]); @@ -276,14 +288,6 @@ repoindex.pageManager.setPage(repoindex.Pages.Packages); } }; - // determine sync databases (this is done by the server now) - //var syncDbNames = []; - //var repoEntries = repoindex.pageManager.repoManager.entries; - //for(var index = 0; index < repoEntries.length; ++index) { - // if(repoEntries[index].info.name !== repo) { - // syncDbNames.push(repoEntries[index].info.name); - // } - //} repoindex.alpmClient.checkForUpdates(repo, null, showUpdates); }; // basic repo info available? diff --git a/web/test.html b/web/test.html index 143eef9..d469a80 100644 --- a/web/test.html +++ b/web/test.html @@ -18,13 +18,18 @@