diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c0a267..9e8c39a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,9 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) set(HEADER_FILES alpm/manager.h alpm/package.h - alpm/list.h alpm/utilities.h network/server.h network/connection.h - alpm/group.h alpm/config.h alpm/resolvebuildorder.h alpm/mingwbundle.h @@ -28,7 +26,6 @@ set(SRC_FILES alpm/manager.cpp alpm/package.cpp alpm/utilities.cpp - alpm/group.cpp alpm/config.cpp alpm/resolvebuildorder.cpp alpm/mingwbundle.cpp @@ -85,8 +82,8 @@ set(META_APP_AUTHOR "Martchus") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_DESCRIPTION "Arch Linux repository browser") set(META_VERSION_MAJOR 0) -set(META_VERSION_MINOR 0) -set(META_VERSION_PATCH 2) +set(META_VERSION_MINOR 1) +set(META_VERSION_PATCH 0) # stringification of meta data set(META_PROJECT_NAME_STR "\"${META_PROJECT_NAME}\"") @@ -141,7 +138,7 @@ add_definitions( # executable and linking add_executable(${META_PROJECT_NAME} ${HEADER_FILES} ${SRC_FILES} ${WEB_FILES} ${RES_FILES}) -target_link_libraries(${META_PROJECT_NAME} c++utilities alpm Qt5::Core Qt5::Concurrent Qt5::Network Qt5::WebSockets KF5::Archive) +target_link_libraries(${META_PROJECT_NAME} c++utilities Qt5::Core Qt5::Concurrent Qt5::Network Qt5::WebSockets KF5::Archive) set_target_properties(${META_PROJECT_NAME} PROPERTIES CXX_STANDARD 11 ) diff --git a/alpm/alpmdatabase.cpp b/alpm/alpmdatabase.cpp index d5da152..1672230 100644 --- a/alpm/alpmdatabase.cpp +++ b/alpm/alpmdatabase.cpp @@ -1,17 +1,19 @@ #include "./alpmdatabase.h" -#include "./group.h" #include "./upgradelookup.h" #include "./alpmpackage.h" #include "./utilities.h" #include -#include +#include +#include #include #include #include +#include + using namespace std; namespace RepoIndex { @@ -28,68 +30,134 @@ using namespace Utilities; class LoadPackage { public: - LoadPackage(AlpmDatabase *db, QMutex *mutex) : - m_db(db), - m_mutex(mutex) + LoadPackage(AlpmDatabase *database, PackageOrigin origin) : + m_db(database), + m_origin(origin) {} - void operator()(alpm_pkg_t *pkg) + void operator()(const QPair > &description) { - auto res = make_unique(pkg, m_db); - QMutexLocker locker(m_mutex); - for(const auto &grpName : res->groups()) { - m_db->groups()[grpName] << res.get(); - } - m_db->packages().emplace(res->name(), move(res)); + m_db->addPackageFromDescription(description.first, description.second, m_origin); } private: - AlpmDatabase *m_db; - QMutex *m_mutex; + AlpmDatabase *const m_db; + const PackageOrigin m_origin; }; -AlpmPackageLoader::AlpmPackageLoader(AlpmDatabase *repository) +void AlpmDatabase::loadDescriptions(QList > > &descriptions) { - for(auto *pkg : PackageList(alpm_db_get_pkgcache(repository->ptr()))) { - m_packages << pkg; + QFileInfo pathInfo(databasePath()); + if(pathInfo.isDir()) { + static const QStringList relevantFiles = QStringList() << QStringLiteral("desc") << QStringLiteral("files"); + QDir dbDir(databasePath()); + QStringList pkgDirNames = dbDir.entryList(QDir::Dirs | QDir::Readable | QDir::Executable | QDir::NoDotAndDotDot); + descriptions.reserve(pkgDirNames.size()); + for(QString &pkgDirName : pkgDirNames) { + if(dbDir.cd(pkgDirName)) { + Utilities::stripVersion(pkgDirName); + const QStringList descFileNames = dbDir.entryList(relevantFiles, QDir::Files | QDir::Readable | QDir::NoDotAndDotDot); + QList descData; + descData.reserve(descFileNames.size()); + for(const QString &descFileName : descFileNames) { + QFile descFile(dbDir.absoluteFilePath(descFileName)); + if(descFile.open(QFile::ReadOnly)) { + descData << descFile.readAll(); + } else { + // TODO: error handling (can't open pkg file) + } + } + if(!descData.isEmpty()) { + descriptions << qMakePair(pkgDirName, descData); + } + dbDir.cdUp(); + } else { + // TODO: error handling (can't enter pkg dir) + } + } + } else if(pathInfo.isFile()) { + KTar tar(databasePath()); + const KArchiveDirectory *dbDir; + if(tar.open(QIODevice::ReadOnly) && (dbDir = tar.directory())) { + QStringList pkgDirNames = dbDir->entries(); + descriptions.reserve(pkgDirNames.size()); + for(QString &pkgDirName : pkgDirNames) { + if(const auto *pkgEntry = dbDir->entry(pkgDirName)) { + if(pkgEntry->isDirectory()) { + Utilities::stripVersion(pkgDirName); + const auto *pkgDir = static_cast(pkgEntry); + const QStringList descFileNames = pkgDir->entries(); + QList descData; + descData.reserve(descFileNames.size()); + for(const QString &descFileName : descFileNames) { + if(const auto *descEntry = pkgDir->entry(descFileName)) { + if(descEntry->isFile()) { + descData << static_cast(descEntry)->data(); + } else { + // there shouldn't be any subdirs + } + } + } + if(!descData.isEmpty()) { + descriptions << qMakePair(pkgDirName, descData); + } + } else { + // there shouldn't be any files + } + } + } + } else { + // TODO: error handling (can't open sync db file) + } + } else { + // TODO: error handling } - m_future = QtConcurrent::map(m_packages, LoadPackage(repository, &m_mutex)); +} + +AlpmPackageLoader::AlpmPackageLoader(AlpmDatabase *repository, PackageOrigin origin) +{ + repository->loadDescriptions(m_descriptions); + m_future = QtConcurrent::map(m_descriptions, LoadPackage(repository, origin)); } /*! * \brief Creates a new instance wrapping the specified database struct. */ -AlpmDatabase::AlpmDatabase(alpm_db_t *dataBase, const QString &dbPath, uint32 index, QObject *parent) : - Repository(QString::fromLocal8Bit(alpm_db_get_name(dataBase)), index, parent), - m_ptr(dataBase), - m_dbFile(QStringLiteral("%1/sync/%2").arg(dbPath, m_name)) -{} +AlpmDatabase::AlpmDatabase(const QString &name, const QString &dbPath, RepositoryUsage usage, SignatureLevel sigLevel, uint32 index, QObject *parent) : + Repository(name, index, parent), + m_dbPath(dbPath) +{ + m_usage = usage; + m_sigLevel = sigLevel; +} AlpmPackageLoader *AlpmDatabase::init() { - if(alpm_db_get_usage(m_ptr, &m_usage)) { - m_usage = static_cast(ALPM_DB_USAGE_ALL); - } + // set description, determine origin + PackageOrigin origin; if(m_name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) { m_description = QStringLiteral("The local database"); + origin = PackageOrigin::LocalDb; } else { - if((m_usage & ALPM_DB_USAGE_SYNC) || (m_usage & ALPM_DB_USAGE_INSTALL) || (m_usage & ALPM_DB_USAGE_UPGRADE)) { + if((m_usage & RepositoryUsage::Sync) || (m_usage & RepositoryUsage::Install) || (m_usage & RepositoryUsage::Upgrade)) { m_description = QStringLiteral("Sync database »%1«").arg(m_name); } else { m_description = QStringLiteral("Database »%1«").arg(m_name); } + origin = PackageOrigin::SyncDb; } - for(const char *str : servers()) { - m_serverUrls << qstr(str); - } - m_sigLevel = alpm_db_get_siglevel(m_ptr); - return new AlpmPackageLoader(this); -} -//AlpmDatabase::AlpmDatabase(const QString &dataBaseFile, QObject *parent) : -// PackageSource(parent), -// m_ptr(nullptr) -//{} + // initialization of packages is done concurrently via AlpmPackageLoader: ~ 4 sec + return new AlpmPackageLoader(this, origin); + + // without concurrency: ~ 12 sec + //QList > > descriptions; + //loadDescriptions(descriptions); + //for(const auto &description : descriptions) { + // addPackageFromDescription(description.first, description.second, origin); + //} + //return nullptr; +} RepositoryType AlpmDatabase::type() const { @@ -109,22 +177,6 @@ PackageDetailAvailability AlpmDatabase::requestsRequired(PackageDetail packageDe } } -/*! - * \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; -} - std::unique_ptr AlpmDatabase::emptyPackage() { return make_unique(this); diff --git a/alpm/alpmdatabase.h b/alpm/alpmdatabase.h index d63a60f..b1bd24a 100644 --- a/alpm/alpmdatabase.h +++ b/alpm/alpmdatabase.h @@ -2,7 +2,6 @@ #define ALPM_DATABASE_H #include "./repository.h" -#include "./list.h" #include #include @@ -14,37 +13,30 @@ namespace RepoIndex { class AlpmPackage; class AlpmDatabase; +class LoadPackage; class AlpmPackageLoader : public PackageLoader { public: - AlpmPackageLoader(AlpmDatabase *db); + AlpmPackageLoader(AlpmDatabase *db, PackageOrigin origin); private: - QList m_packages; + QList > > m_descriptions; }; class AlpmDatabase : public Repository { + friend class AlpmPackageLoader; public: - explicit AlpmDatabase(alpm_db_t *dataBase, const QString &dbPath, uint32 index = invalidIndex, QObject *parent = nullptr); + explicit AlpmDatabase(const QString &name, const QString &dbPath, RepositoryUsage usage, SignatureLevel sigLevel, uint32 index = invalidIndex, QObject *parent = nullptr); AlpmPackageLoader *init(); -// explicit AlpmDatabase(const QString &dataBaseFile, QObject *parent = nullptr); RepositoryType type() const; PackageDetailAvailability requestsRequired(PackageDetail packageDetail) 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; + const QString &databasePath() const; + void setDatabasePath(const QString &dbPath); signals: void initiated(); @@ -53,64 +45,26 @@ protected: std::unique_ptr emptyPackage(); private: - alpm_db_t *m_ptr; - QString m_dbFile; + void loadDescriptions(QList > > &descriptions); + + QString m_dbPath; }; /*! - * \brief Checks whether the specified ALPM database is equal the current instance. + * \brief Returns the path of the database directory/file. */ -inline bool AlpmDatabase::operator ==(const AlpmDatabase &other) const +inline const QString &AlpmDatabase::databasePath() const { - return m_ptr == other.m_ptr; + return m_dbPath; } /*! - * \brief Checks whether the specified ALPM database is not equal to the current instance. + * \brief Sets the path of the database directory/file. + * \remarks Does not invoke reinitialization using the new path. */ -inline bool AlpmDatabase::operator !=(const AlpmDatabase &other) const +inline void AlpmDatabase::setDatabasePath(const QString &dbPath) { - 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()); + m_dbPath = dbPath; } } // namespace Alpm diff --git a/alpm/alpmpackage.cpp b/alpm/alpmpackage.cpp index 49754c7..254b0ff 100644 --- a/alpm/alpmpackage.cpp +++ b/alpm/alpmpackage.cpp @@ -2,99 +2,50 @@ #include "./alpmdatabase.h" #include "./utilities.h" -#include +#include +#include +#include -#include +#include using namespace ChronoUtilities; namespace RepoIndex { -/*! - * \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 an empty ALPM package. + * \brief Constructs an empty ALPM package for the specified \a repository. + * \remarks This constructor is called by the AlpmDatabase class which puts the package name and + * available meta data via the Package::putDescription() method. */ AlpmPackage::AlpmPackage(AlpmDatabase *repository) : - Package(QString(), repository), - m_ptr(nullptr) + Package(QString(), repository) {} -/*! - * \brief Constructs a new instance for the specified ALPM \a package. - */ -AlpmPackage::AlpmPackage(alpm_pkg_t *package, AlpmDatabase *alpmDatabase) : - Package(QString::fromLocal8Bit(alpm_pkg_get_name(package)), alpmDatabase), - m_ptr(package) +AlpmPackage::AlpmPackage(const QString &packageFilePath) : + Package(QString(), nullptr) { - m_origin = static_cast(alpm_pkg_get_origin(package)); - m_hasGeneralInfo = m_hasAllGeneralInfo = 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_packager = 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)); + m_origin = PackageOrigin::File; + m_name = m_fileName = QFileInfo(packageFilePath).fileName(); + Utilities::stripVersion(m_name); + KTar pkgTar(packageFilePath); + const KArchiveDirectory *mainDir; + if(pkgTar.open(QIODevice::ReadOnly) && (mainDir = pkgTar.directory())) { + if(const KArchiveEntry *pkgInfoEntry = mainDir->entry(QStringLiteral(".PKGINFO"))) { + if(pkgInfoEntry->isFile()) { + // parse fields + QList > packageInfo; + AlpmDatabase::parsePkgInfo(static_cast(pkgInfoEntry)->data(), m_name, packageInfo); + putInfo(QList >(), packageInfo, true); + } else { + // TODO: handle error (.PKGINFO is not a file) + } } - if(file->size) { - fileInfo.insert(QStringLiteral("size"), static_cast(file->size)); - } - m_files << fileInfo; + } else { + // TODO: handle error (can't open package file) } } diff --git a/alpm/alpmpackage.h b/alpm/alpmpackage.h index 19b002e..4ad65d2 100644 --- a/alpm/alpmpackage.h +++ b/alpm/alpmpackage.h @@ -11,109 +11,10 @@ class AlpmPackage : public Package { public: AlpmPackage(AlpmDatabase *repository); - AlpmPackage(alpm_pkg_t *package, AlpmDatabase *alpmDatabase); - AlpmPackage(alpm_pkg_t *package); + AlpmPackage(const QString &packageFile); - // 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; }; -/*! - * \brief Constructs a new instance for the specified ALPM \a package. - * \remarks This method is only meant to be used for packages which do - * not belong to a database. - */ -inline AlpmPackage::AlpmPackage(alpm_pkg_t *package) : - AlpmPackage(package, nullptr) -{} - -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/config.cpp b/alpm/config.cpp index d309b53..7ce70b4 100644 --- a/alpm/config.cpp +++ b/alpm/config.cpp @@ -316,6 +316,10 @@ void Config::loadFromArgs(const ConfigArgs &args) } } +RepoEntry::RepoEntry() : + m_sigLevel(SignatureLevel::UseDefault) +{} + /*! * \brief Loads the values from the specified JSON value. */ diff --git a/alpm/config.h b/alpm/config.h index 9873baa..5dcea44 100644 --- a/alpm/config.h +++ b/alpm/config.h @@ -11,6 +11,8 @@ QT_FORWARD_DECLARE_CLASS(QJsonValue) namespace RepoIndex { +enum class SignatureLevel; + // these are needed from the beginning and are initialized in the main() extern bool useShSyntax; extern const char *shchar; @@ -57,12 +59,12 @@ class RepoEntry public: RepoEntry(); const QString &name() const; - const QString &dataBasePath() const; + const QString &databasePath() const; const QString &sourceDir() const; const QString &packageDir() const; const QStringList &servers() const; const QStringList &upgradeSources() const; - int sigLevel() const; + SignatureLevel sigLevel() const; void load(const QJsonValue &jsonValue); private: @@ -72,19 +74,15 @@ private: QString m_packageDir; QStringList m_servers; QStringList m_upgradeSources; - int m_sigLevel; + SignatureLevel m_sigLevel; }; -inline RepoEntry::RepoEntry() : - m_sigLevel(0) -{} - inline const QString &RepoEntry::name() const { return m_name; } -inline const QString &RepoEntry::dataBasePath() const +inline const QString &RepoEntry::databasePath() const { return m_dataBaseFile; } @@ -109,7 +107,7 @@ inline const QStringList &RepoEntry::upgradeSources() const return m_upgradeSources; } -inline int RepoEntry::sigLevel() const +inline SignatureLevel RepoEntry::sigLevel() const { return m_sigLevel; } diff --git a/alpm/manager.cpp b/alpm/manager.cpp index 2add908..bd9b9e4 100644 --- a/alpm/manager.cpp +++ b/alpm/manager.cpp @@ -1,6 +1,5 @@ #include "./manager.h" #include "./utilities.h" -#include "./list.h" #include "./config.h" #include "./alpmdatabase.h" @@ -17,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -33,8 +34,8 @@ namespace RepoIndex { * \cond */ -constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL | - ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; +constexpr auto defaultSigLevel = SignatureLevel::Package | SignatureLevel::PackageOptional + | SignatureLevel::Database | SignatureLevel::DatabaseOptional; inline ostream &operator <<(ostream &stream, const QString &str) { @@ -62,9 +63,9 @@ Manager::Manager(const Config &config) : m_config(config), m_writeCacheBeforeGone(true), m_sigLevel(defaultSigLevel), - m_localFileSigLevel(ALPM_SIG_USE_DEFAULT) + m_localFileSigLevel(SignatureLevel::UseDefault) { - initAlpmHandle(); + addLocalDatabase(); if(config.isAurEnabled()) { m_userRepo = make_unique(m_networkAccessManager); } @@ -78,7 +79,7 @@ Manager::~Manager() if(m_writeCacheBeforeGone) { writeCache(); } - cleanupAlpm(); + removeAllDatabases(); } /*! @@ -131,21 +132,6 @@ const AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName) con return nullptr; } -/*! - * \brief Creates a new package instance for the specified package file. - * - * Verifies the integrity of the file if the option is set. - */ -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 make_unique(pkg); - } -} - /*! * \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database * and sources requirering requests such as the AUR). @@ -176,16 +162,6 @@ const Package *Manager::packageProviding(const Dependency &dependency) const return nullptr; } -/*! - * \brief Sets the install reason for the specified \a package. - */ -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().toLocal8Bit().data() + ": " + alpm_strerror(alpm_errno(m_handle))); - } -} - /*! * \brief Returns the last value with the specified \a key in the specified multimap. */ @@ -201,9 +177,9 @@ const string &lastValue(const multimap &mm, const string &key) /*! * \brief Parses a "SigLevel" denotation from pacman config file. */ -int Manager::parseSigLevel(const string &sigLevelStr) +SignatureLevel Manager::parseSigLevel(const string &sigLevelStr) { - int sigLevel = defaultSigLevel; + SignatureLevel sigLevel = defaultSigLevel; // split sig level denotation into parts const auto parts = splitString >(sigLevelStr, " "); for(const auto &part : parts) { @@ -220,40 +196,40 @@ int Manager::parseSigLevel(const string &sigLevelStr) // set sig level according part if(!strcmp(partStart, "Never")) { if(package) { - sigLevel &= ~ALPM_SIG_PACKAGE; + sigLevel &= ~SignatureLevel::Package; } if(db) { - sigLevel &= ~ALPM_SIG_DATABASE; + sigLevel &= ~SignatureLevel::Database; } } else if(!strcmp(partStart, "Optional")) { if(package) { - sigLevel |= ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL; + sigLevel |= SignatureLevel::Package | SignatureLevel::PackageOptional; } if(db) { - sigLevel |= ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; + sigLevel |= SignatureLevel::Database | SignatureLevel::DatabaseOptional; } } else if(!strcmp(partStart, "Required")) { if(package) { - sigLevel |= ALPM_SIG_PACKAGE; - sigLevel &= ~ALPM_SIG_PACKAGE_OPTIONAL; + sigLevel |= SignatureLevel::Package; + sigLevel &= ~SignatureLevel::PackageOptional; } if(db) { - sigLevel |= ALPM_SIG_DATABASE; - sigLevel &= ~ALPM_SIG_DATABASE_OPTIONAL; + sigLevel |= SignatureLevel::Database; + sigLevel &= ~SignatureLevel::DatabaseOptional; } } else if(!strcmp(partStart, "TrustedOnly")) { if(package) { - sigLevel &= ~(ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK); + sigLevel &= ~(SignatureLevel::PackageMarginalOk | SignatureLevel::PackageUnknownOk); } if(db) { - sigLevel &= ~(ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK); + sigLevel &= ~(SignatureLevel::DatabaseMarginalOk | SignatureLevel::DatabaseUnknownOk); } } else if(!strcmp(partStart, "TrustAll")) { if(package) { - sigLevel |= ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK; + sigLevel |= SignatureLevel::PackageMarginalOk | SignatureLevel::PackageUnknownOk; } if(db) { - sigLevel |= ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK; + sigLevel |= SignatureLevel::DatabaseMarginalOk | SignatureLevel::DatabaseUnknownOk; } } else { cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl; @@ -265,30 +241,30 @@ int Manager::parseSigLevel(const string &sigLevelStr) /*! * \brief Parses a "Usage" denotation from pacman config file. */ -int Manager::parseUsage(const string &usageStr) +RepositoryUsage Manager::parseUsage(const string &usageStr) { - int usage = 0; - const auto parts = splitString >(usageStr, " "); + RepositoryUsage usage = RepositoryUsage::None; + const auto parts = splitString >(usageStr, " ", EmptyPartsTreat::Omit); for(const auto &part : parts) { if(part == "Sync") { - usage |= ALPM_DB_USAGE_SYNC; + usage |= RepositoryUsage::Sync; } else if(part == "Search") { - usage |= ALPM_DB_USAGE_SEARCH; + usage |= RepositoryUsage::Search; } else if(part == "Install") { - usage |= ALPM_DB_USAGE_INSTALL; + usage |= RepositoryUsage::Install; } else if(part == "Upgrade") { - usage |= ALPM_DB_USAGE_UPGRADE; + usage |= RepositoryUsage::Upgrade; } else { cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl; } } - return usage ? usage : ALPM_DB_USAGE_ALL; + return usage != RepositoryUsage::None ? usage : RepositoryUsage::All; } /*! - * \brief Registers sync databases listed in the Pacman config file. Also reads the cache dir. + * \brief Adds sync databases listed in the Pacman config file. Also reads the cache dir. */ -void Manager::registerDataBasesFromPacmanConfig() +void Manager::addDataBasesFromPacmanConfig() { // open config file and parse as ini try { @@ -301,12 +277,12 @@ void Manager::registerDataBasesFromPacmanConfig() } // determine current cpu archtitecture (required for server URLs) static const string sysArch(QSysInfo::currentCpuArchitecture().toStdString()); - string arch = sysArch; + string arch(sysArch); const auto &config = configIni.data(); // read relevant options static const string sigLevelKey("SigLevel"); static const string usageKey("Usage"); - int globalSigLevel = defaultSigLevel; + SignatureLevel globalSigLevel = defaultSigLevel; for(auto &scope : config) { if(scope.first == "options") { // iterate through all "config" scopes (just to cover the case that there are multiple "options" scopes) @@ -337,71 +313,73 @@ void Manager::registerDataBasesFromPacmanConfig() } else { // read sig level and usage const auto &sigLevelStr = lastValue(scope.second, sigLevelKey); - int sigLevel = sigLevelStr.empty() ? globalSigLevel : parseSigLevel(sigLevelStr); - int usage = parseUsage(lastValue(scope.second, usageKey)); - // try to register database in the ALPM system - if(alpm_db_t *db = alpm_register_syncdb(m_handle, scope.first.c_str(), static_cast(sigLevel))) { - // set usage - if(alpm_db_set_usage(db, static_cast(usage)) == 0) { - if(m_config.isVerbose() || m_config.runServer()) { - cerr << shchar << "Added database [" << scope.first << ']' << endl; - } - } else { - if(m_config.isVerbose() || m_config.runServer()) { - cerr << shchar << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl; - } + SignatureLevel sigLevel = sigLevelStr.empty() ? globalSigLevel : parseSigLevel(sigLevelStr); + RepositoryUsage usage = parseUsage(lastValue(scope.second, usageKey)); + + // determine path of db file + // -> currently just use the file from pacman dir, TODO: download syncdata base + QFileInfo dbPathRegular(m_config.alpmDbPath() % QStringLiteral("/sync/") % dbName % QStringLiteral(".db")); + QFileInfo dbPathWithFiles(m_config.alpmDbPath() % QStringLiteral("/sync/") % dbName % QStringLiteral(".files")); + QString dbPath; + if(dbPathWithFiles.isFile() && (!dbPathRegular.isFile() || dbPathWithFiles.lastModified() > dbPathRegular.lastModified())) { + dbPath = dbPathWithFiles.absoluteFilePath(); + } else if(dbPathRegular.isFile()) { + dbPath = dbPathRegular.absoluteFilePath(); + } else { + cerr << shchar << "Error: Unable to locate database file for [" << scope.first << "]" << endl; + } + + // add sync db to internal map (use as index size + 1 because the local database has index 0) + m_syncDbs.emplace_back(make_unique(dbName, dbPath, usage, sigLevel, m_syncDbs.size() + 1)); + AlpmDatabase *emplacedDb = m_syncDbs.back().get(); + m_syncDbMap.emplace(dbName, emplacedDb); + if(usage & RepositoryUsage::Upgrade) { + // -> db is used to upgrade local database + localDataBase()->upgradeSources() << emplacedDb; + } + + // add servers + for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) { + string url = range.first->second; + findAndReplace(url, "$repo", scope.first); + findAndReplace(url, "$arch", arch); + emplacedDb->serverUrls() << Utilities::qstr(url); + if(m_config.isVerbose() || m_config.runServer()) { + cerr << shchar << "Added server: " << url << endl; } - // add servers - for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) { - string url = range.first->second; - findAndReplace(url, "$repo", scope.first); - findAndReplace(url, "$arch", arch); - alpm_db_add_server(db, url.c_str()); - if(m_config.isVerbose() || m_config.runServer()) { - cerr << shchar << "Added server: " << url << endl; - } - } - // add included servers - for(auto range = scope.second.equal_range("Include"); range.first != range.second; ++range.first) { - const auto &path = range.first->second; - auto &includedIni = includedInis[path]; - if(includedIni.data().empty()) { - try { - fstream includedFile; - includedFile.exceptions(ios_base::failbit | ios_base::badbit); - includedFile.open(path, ios_base::in); - includedIni.parse(includedFile); - } catch (const ios_base::failure &) { - cerr << shchar << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl; - } - } + } + + // add included servers / parse mirror list + for(auto range = scope.second.equal_range("Include"); range.first != range.second; ++range.first) { + const auto &path = range.first->second; + auto &includedIni = includedInis[path]; + if(includedIni.data().empty()) { try { - for(auto &scope : includedIni.data()) { - if(scope.first.empty()) { - for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) { - string url = range.first->second; - findAndReplace(url, "$repo", scope.first); - findAndReplace(url, "$arch", arch); - alpm_db_add_server(db, url.c_str()); - if(m_config.isVerbose() || m_config.runServer()) { - cerr << shchar << "Added server: " << url << endl; - } + fstream includedFile; + includedFile.exceptions(ios_base::failbit | ios_base::badbit); + includedFile.open(path, ios_base::in); + includedIni.parse(includedFile); + } catch (const ios_base::failure &) { + cerr << shchar << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl; + } + } + try { + for(auto &scope : includedIni.data()) { + if(scope.first.empty()) { + for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) { + string url = range.first->second; + findAndReplace(url, "$repo", scope.first); + findAndReplace(url, "$arch", arch); + emplacedDb->serverUrls() << Utilities::qstr(url); + if(m_config.isVerbose() || m_config.runServer()) { + cerr << shchar << "Added server: " << url << endl; } } } - } catch (const out_of_range &) { - cerr << shchar << "Warning: Included file \"" << path << "\" has no values." << endl; } + } catch (const out_of_range &) { + cerr << shchar << "Warning: Included file \"" << path << "\" has no values." << endl; } - // add sync db to internal map (use as index size + 1 because the local database has index 0) - m_syncDbs.emplace_back(make_unique(db, m_config.alpmDbPath(), m_syncDbs.size() + 1)); - auto emplaced = m_syncDbMap.emplace(dbName, m_syncDbs.back().get()); - if(usage & ALPM_DB_USAGE_UPGRADE) { - // -> db is used to upgrade local database - localDataBase()->upgradeSources() << emplaced.first->second; - } - } else { - cerr << shchar << "Error: Unable to add sync database [" << scope.first << ']' << endl; } } } @@ -412,9 +390,9 @@ void Manager::registerDataBasesFromPacmanConfig() } /*! - * \brief Registers sync databases listed in the repository index configuration. + * \brief Adds sync databases listed in the repository index configuration. */ -void Manager::registerDatabasesFromRepoIndexConfig() +void Manager::addDatabasesFromRepoIndexConfig() { // check whether an entry already exists, if not create a new one for(const RepoEntry &repoEntry : m_config.repoEntries()) { @@ -422,13 +400,11 @@ void Manager::registerDatabasesFromRepoIndexConfig() try { syncDb = m_syncDbMap.at(repoEntry.name()); cerr << shchar << "Applying config for database [" << syncDb->name() << ']' << endl; - if(!repoEntry.dataBasePath().isEmpty()) { - cerr << shchar << "Warning: Can't use data base path specified in repo index config because the repo \"" - << repoEntry.name() << "\" has already been added from the Pacman config." << endl; + if(!repoEntry.databasePath().isEmpty()) { + syncDb->setDatabasePath(repoEntry.databasePath()); } - if(repoEntry.sigLevel()) { - cerr << shchar << "Warning: Can't use sig level specified in repo index config because the repo \"" - << repoEntry.name() << "\" has already been added from the Pacman config." << endl; + if(repoEntry.sigLevel() != SignatureLevel::UseDefault) { + syncDb->setSigLevel(repoEntry.sigLevel()); } syncDb->setPackagesDirectory(repoEntry.packageDir()); syncDb->setSourcesDirectory(repoEntry.sourceDir()); @@ -438,24 +414,40 @@ void Manager::registerDatabasesFromRepoIndexConfig() } else if(repoEntry.name().startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) { cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl; } else { - // TODO: database path - auto *db = alpm_register_syncdb(m_handle, repoEntry.name().toLocal8Bit().data(), static_cast(repoEntry.sigLevel())); - m_syncDbs.emplace_back(make_unique(db, m_config.alpmDbPath(), m_syncDbs.size() + 1)); - auto emplaced = m_syncDbMap.emplace(repoEntry.name(), m_syncDbs.back().get()); - if(emplaced.second) { - syncDb = emplaced.first->second; - syncDb->setSourcesDirectory(repoEntry.sourceDir()); - syncDb->setPackagesDirectory(repoEntry.packageDir()); - if(m_config.isVerbose() || m_config.runServer()) { - cerr << shchar << "Added database [" << repoEntry.name() << ']' << endl; + // determine path of db file + // -> currently just use the file from pacman dir, TODO: download syncdata base + QString dbPath; + if(repoEntry.databasePath().isEmpty()) { + QFileInfo dbPathRegular(m_config.alpmDbPath() % QStringLiteral("/sync/") % repoEntry.name() % QStringLiteral(".db")); + QFileInfo dbPathWithFiles(m_config.alpmDbPath() % QStringLiteral("/sync/") % repoEntry.name() % QStringLiteral(".files")); + if(dbPathWithFiles.isFile() && (!dbPathRegular.isFile() || dbPathWithFiles.lastModified() > dbPathRegular.lastModified())) { + dbPath = dbPathWithFiles.absolutePath(); + } else if(dbPathRegular.isFile()) { + dbPath = dbPathRegular.absolutePath(); + } else { + cerr << shchar << "Error: Unable to locate database file for [" << repoEntry.name().toLocal8Bit().data() << "]" << endl; } + } else { + dbPath = repoEntry.databasePath(); + } + + // add sync db to internal map (use as index size + 1 because the local database has index 0) + m_syncDbs.emplace_back(make_unique(repoEntry.name(), dbPath, RepositoryUsage::None, repoEntry.sigLevel(), m_syncDbs.size() + 1)); + syncDb = m_syncDbs.back().get(); + m_syncDbMap.emplace(repoEntry.name(), syncDb); + + syncDb->setSourcesDirectory(repoEntry.sourceDir()); + syncDb->setPackagesDirectory(repoEntry.packageDir()); + if(m_config.isVerbose() || m_config.runServer()) { + cerr << shchar << "Added database [" << repoEntry.name() << ']' << endl; } } } if(syncDb) { - syncDb->addServerUrls(repoEntry.servers()); + syncDb->serverUrls() << repoEntry.servers(); } } + // add upgrade sources for(const RepoEntry &repoEntry : m_config.repoEntries()) { try { @@ -471,17 +463,20 @@ void Manager::registerDatabasesFromRepoIndexConfig() // entry should have been added before } } - } /*! * \brief Initiates all ALPM data bases. - * \remarks Must be called, after all relevant sync data bases have been registered (eg. via applyPacmanConfig()). + * \remarks Must be called, after all relevant sync databases have been added (eg. via applyPacmanConfig()). */ void Manager::initAlpmDataBases(bool computeRequiredBy) -{ - // call the init method +{ { + // call the init method + if(m_config.isVerbose() || m_config.runServer()) { + cerr << "Initializing ALPM databases ... "; + cerr.flush(); + } QList loaders; loaders.reserve(m_syncDbMap.size() + 1); loaders << localDataBase()->init(); @@ -489,12 +484,25 @@ void Manager::initAlpmDataBases(bool computeRequiredBy) loaders << syncDbEntry.second->init(); } for(auto *loader : loaders) { - loader->future().waitForFinished(); - delete loader; + if(loader) { + loader->future().waitForFinished(); + delete loader; + } + } + for(auto &syncDbEntry : m_syncDbMap) { + syncDbEntry.second->updateGroups(); } } + if(m_config.isVerbose() || m_config.runServer()) { + cerr << "DONE" << endl; + } + // compute required-by and optional-for if(computeRequiredBy) { + if(m_config.isVerbose() || m_config.runServer()) { + cerr << "Calculating required-by/optional-for ... "; + cerr.flush(); + } QList > futures; futures.reserve(m_syncDbMap.size() + 1); futures << localDataBase()->computeRequiredBy(*this); @@ -504,7 +512,12 @@ void Manager::initAlpmDataBases(bool computeRequiredBy) for(auto &future : futures) { future.waitForFinished(); } + if(m_config.isVerbose() || m_config.runServer()) { + cerr << "DONE" << endl; + } } + + } /*! @@ -598,12 +611,11 @@ void Manager::maintainCache() /*! * \brief Unregisters all registred sync databases. + * \remarks TODO */ void Manager::unregisterSyncDataBases() { - if(alpm_unregister_all_syncdbs(m_handle)) { - throw runtime_error(string("Cannot unregister sync databases: ") + alpm_strerror(alpm_errno(m_handle))); - } + } /*! @@ -636,7 +648,7 @@ const QJsonObject &Manager::basicRepoInfo() const // check if the "sync" database is actually used for syncing QReadLocker locker(syncDb.second->lock()); auto usage = syncDb.second->usage(); - if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) { + if((usage & RepositoryUsage::Sync) || (usage & RepositoryUsage::Install) || (usage & RepositoryUsage::Upgrade)) { m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo()); } else { m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo()); @@ -716,31 +728,21 @@ const QJsonArray &Manager::groupInfo() const } /*! - * \brief Initiates the ALPM library handle and databases. - * \remarks Do not call this function, if ALPM is already initiated. + * \brief Add local database. */ -void Manager::initAlpmHandle() +void Manager::addLocalDatabase() { - 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)); - } - m_localDb = make_unique(alpm_get_localdb(m_handle), config().alpmDbPath(), 0); + m_localDb = make_unique(QStringLiteral("local"), config().alpmDbPath() % QStringLiteral("/local"), RepositoryUsage::None, SignatureLevel::UseDefault, 0); } /*! - * \brief Frees the ALPM library handle and databases. - * \remarks Do not call any ALPM related methods except initAlpmHandle() to reinit ALPM. + * \brief Removes all ALPM databases. */ -void Manager::cleanupAlpm() +void Manager::removeAllDatabases() { - if(m_handle) { - alpm_release(m_handle); - m_handle = nullptr; - m_localDb.reset(); - m_syncDbs.clear(); - m_syncDbMap.clear(); - } + m_localDb.reset(); + m_syncDbs.clear(); + m_syncDbMap.clear(); } /*! diff --git a/alpm/manager.h b/alpm/manager.h index fe960a4..4ca512b 100644 --- a/alpm/manager.h +++ b/alpm/manager.h @@ -4,8 +4,6 @@ #include "./upgradelookup.h" #include "./alpmpackage.h" -#include - #include #include #include @@ -21,6 +19,7 @@ namespace RepoIndex { class Config; class UserRepository; class AlpmDatabase; +enum class RepositoryUsage; class Manager { @@ -40,17 +39,17 @@ public: // configuration, signature level, etc const Config &config() const; - int sigLevel() const; - void setSigLevel(int sigLevel); - int localFileSigLevel() const; - void setLocalFileSigLevel(int sigLevel); + SignatureLevel sigLevel() const; + void setSigLevel(SignatureLevel sigLevel); + SignatureLevel localFileSigLevel() const; + void setLocalFileSigLevel(SignatureLevel sigLevel); const QString &pacmanCacheDir() const; - static int parseSigLevel(const std::string &sigLevelStr = std::string()); - static int parseUsage(const std::string &usageStr); - void initAlpmHandle(); - void cleanupAlpm(); - void registerDataBasesFromPacmanConfig(); - void registerDatabasesFromRepoIndexConfig(); + static SignatureLevel parseSigLevel(const std::string &sigLevelStr = std::string()); + static RepositoryUsage parseUsage(const std::string &usageStr); + void addLocalDatabase(); + void removeAllDatabases(); + void addDataBasesFromPacmanConfig(); + void addDatabasesFromRepoIndexConfig(); void initAlpmDataBases(bool computeRequiredBy); // caching @@ -71,11 +70,8 @@ public: const AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName) const; AlpmPackage *packageFromSyncDatabases(const QString &pkgName); const AlpmPackage *packageFromSyncDatabases(const QString &pkgName) const; - std::unique_ptr packageFromFile(const char *fileName, bool verifyIntegrity = false); Package *packageProviding(const Dependency &dependency); const Package *packageProviding(const Dependency &dependency) const; - bool isPackageIgnored(const AlpmPackage *package) const; - void setInstallReason(AlpmPackage *package, alpm_pkgreason_t reason); // repository lookup const AlpmDatabase *localDataBase() const; @@ -99,9 +95,8 @@ private: const Config &m_config; bool m_writeCacheBeforeGone; std::unique_ptr m_cacheTimer; - alpm_handle_t *m_handle; - int m_sigLevel; - int m_localFileSigLevel; + SignatureLevel m_sigLevel; + SignatureLevel m_localFileSigLevel; QString m_pacmanCacheDir; QNetworkAccessManager m_networkAccessManager; std::unique_ptr m_userRepo; @@ -152,7 +147,7 @@ inline void Manager::setWriteCacheBeforeGone(bool writeCacheBeforeGone) * when parsePacmanConfig() is called or can be set * manually using setSigLevel(). */ -inline int Manager::sigLevel() const +inline SignatureLevel Manager::sigLevel() const { return m_sigLevel; } @@ -160,7 +155,7 @@ inline int Manager::sigLevel() const /*! * \brief Sets the signature level. */ -inline void Manager::setSigLevel(int sigLevel) +inline void Manager::setSigLevel(SignatureLevel sigLevel) { m_sigLevel = sigLevel; } @@ -172,7 +167,7 @@ inline void Manager::setSigLevel(int sigLevel) * when parsePacmanConfig() is called or can be set * manually using setSigLevel(). */ -inline int Manager::localFileSigLevel() const +inline SignatureLevel Manager::localFileSigLevel() const { return m_localFileSigLevel; } @@ -180,19 +175,11 @@ inline int Manager::localFileSigLevel() const /*! * \brief Sets the signature level used when opening local files. */ -inline void Manager::setLocalFileSigLevel(int sigLevel) +inline void Manager::setLocalFileSigLevel(SignatureLevel sigLevel) { m_localFileSigLevel = sigLevel; } -/*! - * \brief Returns whether the specified \a package is ignored. - */ -inline bool Manager::isPackageIgnored(const AlpmPackage *package) const -{ - return alpm_pkg_should_ignore(m_handle, const_cast(package->ptr())); -} - /*! * \brief Returns the Pacman cache directory. * \remarks This is read from the Pacman configuration. diff --git a/alpm/mingwbundle.cpp b/alpm/mingwbundle.cpp index 8619e89..087a10a 100644 --- a/alpm/mingwbundle.cpp +++ b/alpm/mingwbundle.cpp @@ -516,7 +516,7 @@ void MingwBundle::createBundle(const string &targetDir, const string &targetName for(const auto &pkgFileStdStr : m_extraPackages) { QString pkgFile = QString::fromLocal8Bit(pkgFileStdStr.data()); if(QFile::exists(pkgFile)) { - const auto pkg = m_manager.packageFromFile(pkgFileStdStr.data()); // do not catch the exception here + const auto pkg = make_unique(pkgFile); // do not catch the exception here pkgFiles.emplace_back(pkg->name().startsWith(QLatin1String("mingw-w64-")) ? pkg->name().mid(10) : pkg->name(), pkgFile); } else { throw runtime_error("The specified extra package \"" + pkgFileStdStr + "\" can't be found."); diff --git a/alpm/package.cpp b/alpm/package.cpp index 5ce97ed..889c3ad 100644 --- a/alpm/package.cpp +++ b/alpm/package.cpp @@ -41,8 +41,8 @@ Package::Package(const QString &name, Repository *repository) : m_hasInstallScript(false), m_hasBuildRelatedMetaData(false), m_hasInstallRelatedMetaData(false), - m_validationMethods(ALPM_PKG_VALIDATION_UNKNOWN), - m_installReason(ALPM_PKG_REASON_EXPLICIT), + m_validationMethods(PackageValidation::Unknown), + m_installReason(InstallStatus::None), m_id(-1), m_categoryId(-1), m_votes(-1) @@ -131,17 +131,17 @@ bool Package::matches(const QString &name, const QString &version, const Depende if(name == dependency.name) { PackageVersionComparsion cmp; switch(dependency.mode) { - case ALPM_DEP_MOD_ANY: + case DependencyMode::Any: return true; - case ALPM_DEP_MOD_EQ: + case DependencyMode::Equal: return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::Equal; - case ALPM_DEP_MOD_GE: + case DependencyMode::GreatherEqual: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThenSyncVersion; - case ALPM_DEP_MOD_LE: + case DependencyMode::LessEqual: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade; - case ALPM_DEP_MOD_GT: + case DependencyMode::GreatherThen: return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::NewerThenSyncVersion; - case ALPM_DEP_MOD_LT: + case DependencyMode::LessThen: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade; default: ; @@ -221,7 +221,7 @@ QDataStream &operator >>(QDataStream &in, QList &dependencies) in >> version; quint32 mode; in >> mode; - dependencies << Dependency(name, version, static_cast<_alpm_depmod_t>(mode)); + dependencies << Dependency(name, version, static_cast(mode)); } return in; } @@ -329,7 +329,7 @@ QJsonObject Package::detailedInfo() const put(info, QStringLiteral("conf"), conflicts()); put(info, QStringLiteral("repl"), replaces()); put(info, QStringLiteral("pack"), packager()); - put(info, QStringLiteral("expl"), QJsonValue(installReason() == ALPM_PKG_REASON_EXPLICIT)); + put(info, QStringLiteral("expl"), QJsonValue(installReason() == InstallStatus::Explicit)); put(info, QStringLiteral("scri"), QJsonValue(hasInstallScript())); put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods())); put(info, QStringLiteral("file"), fileName()); @@ -401,9 +401,9 @@ void Package::restoreFromCacheStream(QDataStream &in) // installation related meta data in >> m_hasInstallRelatedMetaData >> m_installDate >> m_installedSize >> m_backupFiles; in >> tmp; - m_validationMethods = static_cast(m_validationMethods); // TODO: validate value + m_validationMethods = static_cast(tmp); // TODO: validate value in >> tmp; - m_installReason = static_cast(m_installReason); // TODO: validate value + m_installReason = static_cast(tmp); // TODO: validate value // source related meta data in >> m_hasSourceRelatedMetaData >> m_baseName >> m_architectures >> m_id >> m_categoryId >> m_votes >> m_outOfDate @@ -581,6 +581,10 @@ void Package::putInfo(const QList > &baseInfo, const QLi m_hasBuildRelatedMetaData = includesBuildRelatedMetaData; } +/*! + * \cond + */ + inline void put(QString &lhs, const QStringList &rhs) { if(!rhs.isEmpty()) { @@ -603,6 +607,19 @@ inline void put(QJsonArray &lhs, const QStringList &rhs) } } +inline void putFiles(QJsonArray &lhs, const QStringList &rhs) +{ + for(const QString &value : rhs) { + QJsonObject fileObj; + fileObj.insert(QStringLiteral("name"), value); + lhs << fileObj; + } +} + +/*! + * \endcond + */ + /*! * \brief Puts the specified desc/depends/files key value(s) pairs; clears current values before inserting new values. * \remarks @@ -611,10 +628,11 @@ inline void put(QJsonArray &lhs, const QStringList &rhs) * - Actual parsing of desc/depends/files is done in Repository::addPackagesFromSrcInfo() because one info file * applies to multiple packages in case of split packages. */ -void Package::putDescription(QString name, const QList > &description) +void Package::putDescription(QString name, const QList > &description, PackageOrigin origin) { // clear current meta data - m_hasInstallRelatedMetaData = false; + m_origin = origin; + m_hasInstallRelatedMetaData = origin == PackageOrigin::LocalDb; m_fileName.clear(); m_description.clear(); m_upstreamUrl.clear(); @@ -632,8 +650,8 @@ void Package::putDescription(QString name, const QList(m_validationMethods | ALPM_PKG_VALIDATION_MD5SUM); + m_validationMethods = m_validationMethods | PackageValidation::Md5Sum; } else if(value == QLatin1String("sha256")) { - m_validationMethods = static_cast(m_validationMethods | ALPM_PKG_VALIDATION_SHA256SUM); + m_validationMethods = m_validationMethods | PackageValidation::Sha256Sum; } else if(value == QLatin1String("pgp")) { - m_validationMethods = static_cast(m_validationMethods | ALPM_PKG_VALIDATION_SIGNATURE); + m_validationMethods = m_validationMethods | PackageValidation::Signature; } else { // TODO: error handling (imporant?) } } } m_hasInstallRelatedMetaData = true; + } else if(field == QLatin1String("GROUPS")) { + m_groups = values; } } @@ -805,7 +823,7 @@ PackageVersion::PackageVersion() */ QString RepoIndex::PackageVersion::toString() const { - if(epoch.isEmpty()) { + if(epoch.isEmpty() || epoch == QLatin1String("0")) { return version % QChar('-') % (release.isEmpty() ? QStringLiteral("1") : release); } else { return epoch % QChar(':') % version % QChar('-') % (release.isEmpty() ? QStringLiteral("1") : release); @@ -912,30 +930,30 @@ Dependency::Dependency(const QString &dependency) } int suffixBeg; if((suffixBeg = actualDependency.lastIndexOf(QLatin1String(">="))) > 0) { - mode = ALPM_DEP_MOD_GE; + mode = DependencyMode::GreatherEqual; } else if((suffixBeg = actualDependency.lastIndexOf(QLatin1String("<="))) > 0) { - mode = ALPM_DEP_MOD_LE; + mode = DependencyMode::LessEqual; } else if((suffixBeg = actualDependency.lastIndexOf(QChar('='))) > 0) { - mode = ALPM_DEP_MOD_EQ; + mode = DependencyMode::Equal; } else if((suffixBeg = actualDependency.lastIndexOf(QChar('<'))) > 0) { - mode = ALPM_DEP_MOD_LT; + mode = DependencyMode::LessThen; } else if((suffixBeg = actualDependency.lastIndexOf(QChar('>'))) > 0) { - mode = ALPM_DEP_MOD_GT; + mode = DependencyMode::GreatherThen; } else { - mode = ALPM_DEP_MOD_ANY; + mode = DependencyMode::Any; } switch(mode) { - case ALPM_DEP_MOD_ANY: + case DependencyMode::Any: name = actualDependency.toString(); break; - case ALPM_DEP_MOD_GE: - case ALPM_DEP_MOD_LE: + case DependencyMode::GreatherEqual: + case DependencyMode::LessEqual: name = actualDependency.mid(0, suffixBeg).toString(); version = actualDependency.mid(suffixBeg + 2).toString(); break; - case ALPM_DEP_MOD_EQ: - case ALPM_DEP_MOD_LT: - case ALPM_DEP_MOD_GT: + case DependencyMode::Equal: + case DependencyMode::LessThen: + case DependencyMode::GreatherThen: name = actualDependency.mid(0, suffixBeg).toString(); version = actualDependency.mid(suffixBeg + 1).toString(); break; @@ -947,15 +965,15 @@ Dependency::Dependency(const QString &dependency) QString Dependency::toString() const { switch(mode) { - case ALPM_DEP_MOD_EQ: + case DependencyMode::Equal: return name % QChar('=') % version; - case ALPM_DEP_MOD_GE: + case DependencyMode::GreatherEqual: return name % QChar('>') % QChar('=') % version; - case ALPM_DEP_MOD_LE: + case DependencyMode::LessEqual: return name % QChar('<') % QChar('=') % version; - case ALPM_DEP_MOD_GT: + case DependencyMode::GreatherThen: return name % QChar('>') % version; - case ALPM_DEP_MOD_LT: + case DependencyMode::LessThen: return name % QChar('<') % version; default: return name; @@ -971,22 +989,22 @@ QJsonObject Dependency::toJson() const obj.insert(QStringLiteral("name"), name); obj.insert(QStringLiteral("ver"), version); switch(mode) { - case ALPM_DEP_MOD_ANY: + case DependencyMode::Any: obj.insert(QStringLiteral("mod"), QStringLiteral("any")); break; - case ALPM_DEP_MOD_EQ: + case DependencyMode::Equal: obj.insert(QStringLiteral("mod"), QStringLiteral("eq")); break; - case ALPM_DEP_MOD_GE: + case DependencyMode::GreatherEqual: obj.insert(QStringLiteral("mod"), QStringLiteral("ge")); break; - case ALPM_DEP_MOD_LE: + case DependencyMode::LessEqual: obj.insert(QStringLiteral("mod"), QStringLiteral("le")); break; - case ALPM_DEP_MOD_GT: + case DependencyMode::GreatherThen: obj.insert(QStringLiteral("mod"), QStringLiteral("gt")); break; - case ALPM_DEP_MOD_LT: + case DependencyMode::LessThen: obj.insert(QStringLiteral("mod"), QStringLiteral("lt")); break; default: diff --git a/alpm/package.h b/alpm/package.h index e9e6ca3..6a339f6 100644 --- a/alpm/package.h +++ b/alpm/package.h @@ -1,12 +1,8 @@ #ifndef ALPM_PACKAGE_H #define ALPM_PACKAGE_H -#include "./list.h" - #include -#include - #include #include #include @@ -43,23 +39,106 @@ enum class PackageVersionPartComparsion /*! * \brief The PackageOrigin enum describes where a Package instance comes from. + * \remarks ALPM type: alpm_pkgfrom_t */ 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. */ + File = 1, /*!< The instance has been created from a package file; source() returns nullptr in this case. */ + LocalDb = 2, /*! The instance is from the local data base; source() is an AlpmDataBase instance. */ + SyncDb = 3, /*! 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. */ }; +/*! + * \brief The InstallStatus enum specifies whether a package has been installed explicitely + * or as dependency. + */ enum class InstallStatus { - Explicit = ALPM_PKG_REASON_EXPLICIT, - AsDependency = ALPM_PKG_REASON_DEPEND, + Explicit = 0, + AsDependency = 1, None = 20 }; +/*! + * \brief The PackageValidation enum specifies methods used to validate a package. + * \remarks ALMP type: alpm_pkgvalidation_t + */ +enum class PackageValidation { + Unknown = 0, + None = (1 << 0), + Md5Sum = (1 << 1), + Sha256Sum = (1 << 2), + Signature = (1 << 3) +}; + +constexpr PackageValidation operator |(PackageValidation lhs, PackageValidation rhs) +{ + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +constexpr bool operator &(PackageValidation lhs, PackageValidation rhs) +{ + return (static_cast(lhs) & static_cast(rhs)) != 0; +} + +/*! + * \brief The SignatureLevel enum specifies PGP signature verification options. + */ +enum class SignatureLevel { + Package = (1 << 0), + PackageOptional = (1 << 1), + PackageMarginalOk = (1 << 2), + PackageUnknownOk = (1 << 3), + + Database = (1 << 10), + DatabaseOptional = (1 << 11), + DatabaseMarginalOk = (1 << 12), + DatabaseUnknownOk = (1 << 13), + + UseDefault = (1 << 31) +}; + +constexpr SignatureLevel operator |(SignatureLevel lhs, SignatureLevel rhs) +{ + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +constexpr bool operator &(SignatureLevel lhs, SignatureLevel rhs) +{ + return (static_cast(lhs) & static_cast(rhs)) != 0; +} + +constexpr int operator ~(SignatureLevel lhs) +{ + return ~static_cast(lhs); +} + +inline SignatureLevel &operator &=(SignatureLevel &lhs, int rhs) +{ + lhs = static_cast(static_cast(lhs) & rhs); + return lhs; +} + +inline SignatureLevel &operator |=(SignatureLevel &lhs, SignatureLevel rhs) +{ + lhs = static_cast(static_cast(lhs) | static_cast(rhs)); + return lhs; +} + +/*! + * \brief The SigStatus enum specifies PGP signature verification status return codes. + */ +enum class SignatureStatus { + Valid, + KeyExpired, + SigExpired, + KeyUnknown, + KeyDisabled, + InvalidId +}; + class PackageVersion { public: @@ -75,10 +154,23 @@ public: QString release; }; +/*! + * \brief The DependencyMode enum specifies the version constraints in dependency specs. + * \remarks ALPM: alpm_depmod_t + */ +enum class DependencyMode { + Any = 1, /*! No version constraint */ + Equal, /*! Test version equality (package=x.y.z) */ + GreatherEqual, /*! Test for at least a version (package>=x.y.z) */ + LessEqual, /*! Test for at most a version (package<=x.y.z) */ + GreatherThen, /*! Test for greater than some version (package>x.y.z) */ + LessThen /*! Test for less than some version (package(dependency.mode) ^ seed ^ qHash(dependency.description, seed); } class Manager; @@ -136,8 +228,11 @@ public: bool isRequiredByComputed() const; void computeRequiredBy(Manager &manager); const QStringList &requiredBy() const; + QStringList &requiredBy(); const QStringList &optionalFor() const; + QStringList &optionalFor(); bool hasInstallScript() const; + void setHasInstallScript(bool hasInstallScript); // build related meta data bool hasBuildRelatedMetaData() const; @@ -157,8 +252,8 @@ public: ChronoUtilities::DateTime installDate() const; uint32 installedSize() const; const QStringList &backupFiles() const; - alpm_pkgvalidation_t validationMethods() const; - alpm_pkgreason_t installReason() const; + PackageValidation validationMethods() const; + InstallStatus installReason() const; // source related meta data bool hasSourceRelatedMetaData() const; @@ -193,7 +288,7 @@ public: // parsing .SRCINFO/.PKGINFO/desc/depends/files info void putInfo(const QList > &baseInfo, const QList > &pkgInfo, bool includesSourceRelatedMetaData = false, bool includesBuildRelatedMetaData = false); - void putDescription(QString name, const QList > &description); + void putDescription(QString name, const QList > &description, PackageOrigin origin); void putSourceFile(const QString &path, const QByteArray &data); protected: @@ -242,8 +337,8 @@ protected: ChronoUtilities::DateTime m_installDate; uint32 m_installedSize; QStringList m_backupFiles; - alpm_pkgvalidation_t m_validationMethods; - alpm_pkgreason_t m_installReason; + PackageValidation m_validationMethods; + InstallStatus m_installReason; // source related meta data bool m_hasSourceRelatedMetaData; @@ -405,6 +500,14 @@ inline const QStringList &Package::requiredBy() const return m_requiredBy; } +/*! + * \brief Returns packages requiring this packages. + */ +inline QStringList &Package::requiredBy() +{ + return m_requiredBy; +} + /*! * \brief Returns packages having this package as optional dependency. */ @@ -413,6 +516,14 @@ inline const QStringList &Package::optionalFor() const return m_optionalFor; } +/*! + * \brief Returns packages having this package as optional dependency. + */ +inline QStringList &Package::optionalFor() +{ + return m_optionalFor; +} + /*! * \brief Returns whether the package has an install script. */ @@ -421,6 +532,14 @@ inline bool Package::hasInstallScript() const return m_hasInstallScript; } +/*! + * \brief Sets whether the package has an install script. + */ +inline void Package::setHasInstallScript(bool hasInstallScript) +{ + m_hasInstallScript = hasInstallScript; +} + /*! * \brief Returns whether the package has build-related meta data. * @@ -554,7 +673,7 @@ inline const QStringList &Package::backupFiles() const /*! * \brief Returns the validation methods used during installation. */ -inline alpm_pkgvalidation_t Package::validationMethods() const +inline PackageValidation Package::validationMethods() const { return m_validationMethods; } @@ -562,7 +681,7 @@ inline alpm_pkgvalidation_t Package::validationMethods() const /*! * \brief Returns whether the package has been installed explicitely or as a dependency. */ -inline alpm_pkgreason_t Package::installReason() const +inline InstallStatus Package::installReason() const { return m_installReason; } @@ -694,6 +813,11 @@ inline PackageVersionComparsion Package::compareVersion(const Dependency &depend return PackageVersion(version()).compare(PackageVersion(dependency.version)); } +inline uint qHash(const Package &package, uint seed) +{ + return static_cast(((reinterpret_cast(package.repository()) >> (8 * sizeof(uint) - 1)) ^ reinterpret_cast(package.repository())) & (~0U)) ^ seed ^ qHash(package.name(), seed); +} + } #endif // ALPM_PACKAGE_H diff --git a/alpm/repository.cpp b/alpm/repository.cpp index 8faf34e..d39f506 100644 --- a/alpm/repository.cpp +++ b/alpm/repository.cpp @@ -61,7 +61,7 @@ void Reply::replyFinished() /*! * \brief Returns a list of all package names. */ -const QStringList RepoIndex::Repository::packageNames() const +const QStringList Repository::packageNames() const { QStringList names; names.reserve(m_packages.size()); @@ -71,6 +71,16 @@ const QStringList RepoIndex::Repository::packageNames() const return names; } +void Repository::updateGroups() +{ + m_groups.clear(); + for(auto &entry : m_packages) { + for(const QString &group : entry.second->groups()) { + m_groups[group] << entry.second.get(); + } + } +} + /*! * \brief Initializes the repository. * \remarks @@ -107,8 +117,8 @@ Repository::Repository(const QString &name, uint32 index, QObject *parent) : m_index(index), m_name(name), m_maxPackageAge(TimeSpan::infinity()), - m_usage(static_cast(0)), - m_sigLevel(static_cast(ALPM_SIGSTATUS_INVALID)), + m_usage(RepositoryUsage::None), + m_sigLevel(SignatureLevel::UseDefault), m_lock(QReadWriteLock::Recursive) {} @@ -266,14 +276,14 @@ QFuture Repository::computeRequiredBy(Manager &manager, bool forceUpdate) QJsonObject Repository::suggestions(const QString &term) const { QJsonArray suggestions; -// size_t remainingSuggestions = 20; -// for(auto i = packages().lower_bound(term), end = packages().cend(); i != end && remainingSuggestions; ++i, --remainingSuggestions) { -// if(i->first.startsWith(term, Qt::CaseInsensitive)) { -// suggestions << i->first; -// } else { -// break; -// } -// } + // size_t remainingSuggestions = 20; + // for(auto i = packages().lower_bound(term), end = packages().cend(); i != end && remainingSuggestions; ++i, --remainingSuggestions) { + // if(i->first.startsWith(term, Qt::CaseInsensitive)) { + // suggestions << i->first; + // } else { + // break; + // } + // } for(const auto &pkgEntry : packages()) { if(pkgEntry.first.contains(term, Qt::CaseInsensitive)) { suggestions << pkgEntry.first; @@ -543,15 +553,207 @@ void Repository::cleanOutdatedPackages() for(auto i = m_packages.begin(); i != m_packages.end(); ) { const Package &pkg = *i->second; if((now - pkg.timeStamp()) > maxPackageAge()) { - i = m_packages.erase(i); + i = m_packages.erase(i); } else { - ++i; + ++i; + } + } +} + +void Repository::parsePkgInfo(const QByteArray &pkgInfo, QString &name, QList > packageInfo) +{ + // define states + enum { + FieldName, // reading field name (initial state) + EquationSign, // expecting equation sign + Pad, // expecting padding + FieldValue, // reading field value + Comment // reading comment + } state = FieldName; + + // define variables to store parsing results + QByteArray currentFieldName; + currentFieldName.reserve(16); + QByteArray currentFieldValue; + currentFieldValue.reserve(32); + packageInfo.reserve(16); + + // state machine: consumes each char of .SRCINFO + for(const char c : pkgInfo) { + switch(state) { + case FieldName: + switch(c) { + case '#': + // discard truncated line + currentFieldName.clear(); + state = Comment; + case ' ': + // field name complete, expect equation sign + if(!currentFieldName.isEmpty()) { + state = EquationSign; + } + break; + case '\n': case '\r': case '\t': + // discard truncated line + currentFieldName.clear(); + break; + default: + currentFieldName.append(c); + } + break; + case EquationSign: + switch(c) { + case '=': + state = Pad; + break; + case '\n': case '\r': case '\t': + // unexpected new line -> discard truncated line + currentFieldName.clear(); + break; + default: + ; // ignore unexpected characters + } + break; + case Pad: + switch(c) { + case ' ': + state = FieldValue; + break; + case '\n': case '\r': case '\t': + // unexpected new line -> discard truncated line + currentFieldName.clear(); + break; + default: + ; // ignore unexpected characters + } + break; + case FieldValue: + switch(c) { + case '\n': case '\r': + state = FieldName; + if(!currentFieldValue.isEmpty() && "pkgname" == currentFieldName) { + // put current info to current package + name = currentFieldValue; + } + packageInfo << QPair(currentFieldName, currentFieldValue); + currentFieldName.clear(); + currentFieldValue.clear(); + break; + default: + currentFieldValue.append(c); + } + break; + case Comment: + switch(c) { + case '\n': case '\r': case '\t': + state = FieldName; + break; + default: + ; // ignore outcommented characters + } + break; } } } /*! - * \brief Adds packages parsed from the specified .SRCINFO/.PKGINFO file. + * \brief Parses the specified package \a descriptions (desc/depends/files file). + * + * Stores the results in \a fields. The package name is also stored in \a name. + */ +void Repository::parseDescriptions(const QList &descriptions, QString &name, QList > &fields) +{ + // define variables to store parsing results + QByteArray currentFieldName; + currentFieldName.reserve(16); + QByteArray currentFieldValue; + currentFieldValue.reserve(16); + QStringList currentFieldValues; + + for(const QByteArray &description : descriptions) { + // define states + enum { + FieldName, // reading field name + NewLine, // expecting new line (after field name) + Next, // start reading next field value / next field name (initial state) + FieldValue, // reading field value + } state = Next; + + // state machine: consumes each char of desc + for(const char c : description) { + switch(state) { + case FieldName: + switch(c) { + case '%': + state = NewLine; + break; + default: + currentFieldName.append(c); + } + break; + case NewLine: + switch(c) { + case '\n': case '\r': + state = Next; + break; + default: + ; // ignore unexpected characters + } + break; + case Next: + switch(c) { + case '\n': case '\r': case '\t': case ' ': + break; + case '%': + state = FieldName; + // next field -> put current field + if(!currentFieldName.isEmpty()) { + fields << QPair(currentFieldName, currentFieldValues); + currentFieldName.clear(); + currentFieldValues.clear(); + } + break; + default: + state = FieldValue; + currentFieldValue.append(c); + } + break; + case FieldValue: + switch(c) { + case '\n': case '\r': + state = Next; + currentFieldValues << currentFieldValue; + if(!currentFieldValue.isEmpty() && "NAME" == currentFieldName) { + name = currentFieldValues.back(); + } + currentFieldValue.clear(); + break; + default: + currentFieldValue.append(c); + } + } + } + + // all characters read + switch(state) { + case FieldValue: + currentFieldValues << currentFieldValue; + if(!currentFieldValue.isEmpty() && "NAME" == currentFieldName) { + name = currentFieldValues.back(); + } + default: + ; + } + + // put last field + if(!currentFieldName.isEmpty()) { + fields << QPair(currentFieldName, currentFieldValues); + } + } +} + +/*! + * \brief Adds packages parsed from the specified .SRCINFO file. * \remarks Updates existing packages. * \returns Returns the added/updated packages. In the case of a split package more then * one package is returned. @@ -568,9 +770,9 @@ QList Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo) } state = FieldName; // define variables to store parsing results - QString currentFieldName; + QByteArray currentFieldName; currentFieldName.reserve(16); - QString currentFieldValue; + QByteArray currentFieldValue; currentFieldValue.reserve(32); QString packageBase; packageBase.reserve(32); @@ -634,10 +836,10 @@ QList Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo) switch(c) { case '\n': case '\r': state = FieldName; - if(currentFieldName == QLatin1String("pkgbase")) { + if("pkgbase" == currentFieldName) { // pkgbase packageBase = currentFieldValue; - } else if(currentFieldName == QLatin1String("pkgname")) { + } else if("pkgname" == currentFieldName) { // next package if(packageBase.isEmpty()) { // no pkgbase specified -> use the first pkgname as pkgbase @@ -646,6 +848,7 @@ QList Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo) // put current info to current package if(currentPackage) { currentPackage->putInfo(baseInfo, packageInfo, true); + // TODO: add groups packages << currentPackage; } // find next package @@ -691,95 +894,37 @@ QList Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo) /*! * \brief Adds packages parsed from the specified desc/depends/files file. - * \remarks Updates the package if it already exists. - * \returns Returns the added/updated package. + * \remarks + * - Updates the package if it already exists. + * - If \a name is empty and the description doesn't provide a name either, the package can not be added. + * \returns Returns the added/updated package or nullptr if no package could be added. */ -Package *Repository::addPackageFromDescription(QString name, const QList &descriptions) +Package *Repository::addPackageFromDescription(QString name, const QList &descriptions, PackageOrigin origin) { - // define variables to store parsing results - QString currentFieldName; - currentFieldName.reserve(16); - QStringList currentFieldValues; + // parse fields QList > fields; + parseDescriptions(descriptions, name, fields); - for(const QByteArray &description : descriptions) { - // define states - enum { - FieldName, // reading field name - NewLine, // expecting new line (after field name) - Next, // start reading next field value / next field name (initial state) - FieldValue, // reading field value - } state = Next; - - // state machine: consumes each char of desc - for(const char c : description) { - switch(state) { - case FieldName: - switch(c) { - case '%': - state = NewLine; - break; - default: - currentFieldName.append(c); - } - break; - case NewLine: - switch(c) { - case '\n': case '\r': - state = Next; - break; - default: - ; // ignore unexpected characters - } - break; - case Next: - switch(c) { - case '\n': case '\r': case '\t': case ' ': - break; - case '%': - state = FieldName; - // next field -> put current field - if(!currentFieldName.isEmpty()) { - if(!currentFieldValues.isEmpty() && currentFieldName == QLatin1String("NAME")) { - name = currentFieldValues.back(); - } - fields << QPair(currentFieldName, currentFieldValues); - currentFieldName.clear(); - currentFieldValues.clear(); - } - break; - default: - state = FieldValue; - currentFieldValues << QString(QChar(c)); - } - break; - case FieldValue: - switch(c) { - case '\n': case '\r': - state = Next; - break; - default: - currentFieldValues.back().append(c); - } - } - } - - // put last field - if(!currentFieldName.isEmpty()) { - if(!currentFieldValues.isEmpty() && currentFieldName == QLatin1String("NAME")) { - name = currentFieldValues.back(); - } - fields << QPair(currentFieldName, currentFieldValues); - } + // check whether name is empty + if(name.isEmpty()) { + return nullptr; } // find/create package for description - auto &pkg = m_packages[name]; - if(!pkg) { - pkg = emptyPackage(); + Package *pkgRawPtr; + { + QWriteLocker locker(&m_lock); + auto &pkgPtrRef = m_packages[name]; + if(!pkgPtrRef) { + pkgPtrRef = emptyPackage(); + } + pkgRawPtr = pkgPtrRef.get(); } - pkg->putDescription(name, fields); - return pkg.get(); + + // add groups + pkgRawPtr->putDescription(name, fields, origin); + + return pkgRawPtr; } } // namespace PackageManagement diff --git a/alpm/repository.h b/alpm/repository.h index 399a6d0..1e0ff40 100644 --- a/alpm/repository.h +++ b/alpm/repository.h @@ -2,7 +2,6 @@ #define PACKAGEMANAGEMENT_PACKAGESOURCE_H #include "./package.h" -#include "./group.h" #include #include @@ -169,6 +168,29 @@ enum class PackageDetailAvailability FullRequest /*! The information is available after the requestFullPackageInfo() method has been called for the package. */ }; +/*! + * \brief The RepositoryUsage enum specifies the usage of a repository. + */ +enum class RepositoryUsage { + None = 0, + Sync = 1, /*! The repository is used when synchronizing. */ + Search = (1 << 1), /*! The repository is used when searching. */ + Install = (1 << 2), /*! The repository is used to install packages. */ + Upgrade = (1 << 3), /*! The repository is used to upgrade packages. */ + All = (1 << 4) - 1, /*! The reposiotry is used for everything. */ +}; + +constexpr bool operator &(RepositoryUsage lhs, RepositoryUsage rhs) +{ + return (static_cast(lhs) & static_cast(rhs)) != 0; +} + +inline RepositoryUsage &operator |=(RepositoryUsage &lhs, RepositoryUsage rhs) +{ + lhs = static_cast(static_cast(lhs) | static_cast(rhs)); + return lhs; +} + class Repository : public QObject { Q_OBJECT @@ -184,13 +206,16 @@ public: const std::map > &packages() const; std::map > &packages(); const QStringList packageNames() const; - alpm_db_usage_t usage() const; + RepositoryUsage usage() const; bool isSourceOnly() const; bool isPackageOnly() const; std::map > &groups(); const std::map > &groups() const; + void updateGroups(); const QStringList &serverUrls() const; - alpm_siglevel_t sigLevel() const; + QStringList &serverUrls(); + SignatureLevel sigLevel() const; + void setSigLevel(SignatureLevel sigLevel); // gathering data virtual PackageLoader *init(); @@ -241,8 +266,10 @@ public: void wipePackages(); // parsing src/pkg info + static void parsePkgInfo(const QByteArray &pkgInfo, QString &name, QList > packageInfo); + static void parseDescriptions(const QList &descriptions, QString &name, QList > &fields); QList addPackagesFromSrcInfo(const QByteArray &srcInfo); - Package *addPackageFromDescription(QString name, const QList &descriptions); + Package *addPackageFromDescription(QString name, const QList &descriptions, PackageOrigin origin); // thread synchronization QReadWriteLock *lock() const; @@ -258,10 +285,10 @@ protected: QString m_description; std::map > m_packages; ChronoUtilities::TimeSpan m_maxPackageAge; - alpm_db_usage_t m_usage; + RepositoryUsage m_usage; std::map > m_groups; QStringList m_serverUrls; - alpm_siglevel_t m_sigLevel; + SignatureLevel m_sigLevel; QList m_upgradeSources; QString m_srcDir; QString m_pkgDir; @@ -362,16 +389,26 @@ inline Package *Repository::packageByName(const QString &name) } } -inline alpm_db_usage_t Repository::usage() const +/*! + * \brief Returns the operations the repository is used for. + */ +inline RepositoryUsage Repository::usage() const { return m_usage; } +/*! + * \brief Returns the groups the packages of the repository belong to. + */ inline std::map > &Repository::groups() { return m_groups; } + +/*! + * \brief Returns the groups the packages of the repository belong to. + */ inline const std::map > &Repository::groups() const { return m_groups; @@ -385,14 +422,31 @@ inline const QStringList &Repository::serverUrls() const return m_serverUrls; } +/*! + * \brief Returns the server URLs. + * \remarks This non-const version is used by the manager to add the servers. + */ +inline QStringList &Repository::serverUrls() +{ + return m_serverUrls; +} + /*! * \brief Returns the signature level of the database. */ -inline alpm_siglevel_t Repository::sigLevel() const +inline SignatureLevel Repository::sigLevel() const { return m_sigLevel; } +/*! + * \brief Sets the signature level. + */ +inline void Repository::setSigLevel(SignatureLevel sigLevel) +{ + m_sigLevel = sigLevel; +} + inline const QList &Repository::upgradeSources() const { return m_upgradeSources; diff --git a/alpm/utilities.cpp b/alpm/utilities.cpp index c036e58..0006fc4 100644 --- a/alpm/utilities.cpp +++ b/alpm/utilities.cpp @@ -16,50 +16,38 @@ namespace RepoIndex { namespace Utilities { -/*! - * \brief Returns the package names of the specified \a dependencyList. - */ -list getNames(DependencyList dependencyList) -{ - list res; - for(auto dependency : dependencyList) { - res.push_back(dependency->name); - } - return res; -} - /*! * \brief Returns string representations for the specified \a sigLevel. */ -QJsonArray sigLevelStrings(alpm_siglevel_t sigLevel) +QJsonArray sigLevelStrings(SignatureLevel sigLevel) { QJsonArray options; - if(sigLevel & ALPM_SIG_PACKAGE) { + if(sigLevel & SignatureLevel::Package) { options << QStringLiteral("package"); } - if(sigLevel & ALPM_SIG_PACKAGE_OPTIONAL) { + if(sigLevel & SignatureLevel::PackageOptional) { options << QStringLiteral("package optional"); } - if(sigLevel & ALPM_SIG_PACKAGE_MARGINAL_OK) { + if(sigLevel & SignatureLevel::PackageMarginalOk) { options << QStringLiteral("package marginal ok"); } - if(sigLevel & ALPM_SIG_PACKAGE_UNKNOWN_OK) { + if(sigLevel & SignatureLevel::PackageUnknownOk) { options << QStringLiteral("package unknown ok"); } - if(sigLevel & ALPM_SIG_DATABASE) { + if(sigLevel & SignatureLevel::Database) { options << QStringLiteral("database"); } - if(sigLevel & ALPM_SIG_DATABASE_OPTIONAL) { + if(sigLevel & SignatureLevel::DatabaseOptional) { options << QStringLiteral("database optional"); } - if(sigLevel & ALPM_SIG_DATABASE_MARGINAL_OK) { + if(sigLevel & SignatureLevel::DatabaseMarginalOk) { options << QStringLiteral("database marginal ok"); } - if(sigLevel & ALPM_SIG_DATABASE_UNKNOWN_OK) { + if(sigLevel & SignatureLevel::DatabaseUnknownOk) { options << QStringLiteral("database unknown ok"); } - if(sigLevel & ALPM_SIG_USE_DEFAULT) { - options << QStringLiteral("use default"); + if(sigLevel & SignatureLevel::UseDefault) { + options << QStringLiteral("default"); } return options; } @@ -67,39 +55,42 @@ QJsonArray sigLevelStrings(alpm_siglevel_t sigLevel) /*! * \brief Returns string representations for the specified database \a usage. */ -QJsonArray usageStrings(alpm_db_usage_t usage) +QJsonArray usageStrings(RepositoryUsage usage) { QJsonArray strings; - if(usage & ALPM_DB_USAGE_SYNC) { + if(usage & RepositoryUsage::Sync) { strings << QStringLiteral("refreshing"); } - if(usage & ALPM_DB_USAGE_SEARCH) { + if(usage & RepositoryUsage::Search) { strings << QStringLiteral("searching"); } - if(usage & ALPM_DB_USAGE_INSTALL) { + if(usage & RepositoryUsage::Install) { strings << QStringLiteral("installing"); } - if(usage & ALPM_DB_USAGE_UPGRADE) { + if(usage & RepositoryUsage::Upgrade) { strings << QStringLiteral("upgrading"); } + if(strings.empty()) { + strings << QStringLiteral("none"); + } return strings; } /*! * \brief Returns the string representation for the specified \a sigStatus. */ -QString sigStatusString(alpm_sigstatus_t sigStatus) +QString sigStatusString(SignatureStatus sigStatus) { switch(sigStatus) { - case ALPM_SIGSTATUS_VALID: + case SignatureStatus::Valid: return QStringLiteral("valid"); - case ALPM_SIGSTATUS_KEY_EXPIRED: + case SignatureStatus::KeyExpired: return QStringLiteral("key expired"); - case ALPM_SIGSTATUS_SIG_EXPIRED: + case SignatureStatus::SigExpired: return QStringLiteral("sig expired"); - case ALPM_SIGSTATUS_KEY_UNKNOWN: + case SignatureStatus::KeyUnknown: return QStringLiteral("key unknown"); - case ALPM_SIGSTATUS_KEY_DISABLED: + case SignatureStatus::KeyDisabled: return QStringLiteral("key disabled"); default: return QStringLiteral("invalid"); @@ -109,19 +100,19 @@ QString sigStatusString(alpm_sigstatus_t sigStatus) /*! * \brief Returns the string representation for the specified \a validationMethods. */ -QJsonArray validationMethodsStrings(alpm_pkgvalidation_t validationMethods) +QJsonArray validationMethodsStrings(PackageValidation validationMethods) { QJsonArray jsonArray; - if(validationMethods & 0x1) { + if(validationMethods & PackageValidation::None) { jsonArray << QStringLiteral("none"); } - if(validationMethods & 0x2) { + if(validationMethods & PackageValidation::Md5Sum) { jsonArray << QStringLiteral("MD5"); } - if(validationMethods & 0x4) { + if(validationMethods & PackageValidation::Sha256Sum) { jsonArray << QStringLiteral("SHA256"); } - if(validationMethods & 0x8) { + if(validationMethods & PackageValidation::Signature) { jsonArray << QStringLiteral("signature"); } if(jsonArray.empty()) { @@ -130,78 +121,6 @@ QJsonArray validationMethodsStrings(alpm_pkgvalidation_t validationMethods) 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; -} - void printValues(ostream &output, const char *label, const QStringList &values) { output << label << ':'; @@ -242,6 +161,18 @@ void printError(string &message) } } +void stripVersion(QString &packageName) +{ + int lastHyphen = packageName.lastIndexOf(QChar('-')); + if(lastHyphen > 0) { + packageName.remove(lastHyphen, packageName.size() - lastHyphen); + int lastHyphen = packageName.lastIndexOf(QChar('-')); + if(lastHyphen > 0) { + packageName.remove(lastHyphen, packageName.size() - lastHyphen); + } + } +} + } } // namespace Alpm diff --git a/alpm/utilities.h b/alpm/utilities.h index 9dfddf9..e782882 100644 --- a/alpm/utilities.h +++ b/alpm/utilities.h @@ -1,12 +1,12 @@ #ifndef ALPM_UTILITIES_H #define ALPM_UTILITIES_H -#include "./list.h" +#include "repository.h" +#include "package.h" #include #include -#include QT_FORWARD_DECLARE_CLASS(QJsonArray) @@ -14,16 +14,11 @@ namespace RepoIndex { namespace Utilities { -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); +QJsonArray sigLevelStrings(SignatureLevel sigLevel); +QJsonArray usageStrings(RepositoryUsage usage); +QString sigStatusString(SignatureStatus sigStatus); +QJsonArray validationMethodsStrings(PackageValidation validationMethods); +void stripVersion(QString &packageName); inline QString qstr(const char *str) { diff --git a/main.cpp b/main.cpp index 1b67841..2e9e02e 100644 --- a/main.cpp +++ b/main.cpp @@ -53,8 +53,8 @@ int main(int argc, char *argv[]) // setup manager Manager manager(config); cerr << shchar << "Loading databases ..." << endl; - manager.registerDataBasesFromPacmanConfig(); - manager.registerDatabasesFromRepoIndexConfig(); + manager.addDataBasesFromPacmanConfig(); + manager.addDatabasesFromRepoIndexConfig(); manager.initAlpmDataBases(configArgs.serverArg.isPresent()); cerr << shchar << "Restoring cache ..." << endl; manager.restoreCache(); diff --git a/network/connection.cpp b/network/connection.cpp index 7b55453..70167d8 100644 --- a/network/connection.cpp +++ b/network/connection.cpp @@ -130,10 +130,10 @@ void Connection::handleCmd(const QJsonObject &obj) } else if(what == QLatin1String("reinitalpm")) { if(m_socket->peerAddress().isLoopback()) { cerr << shchar << "Info: Reinit of ALPM databases triggered via web interface." << endl; - m_manager.cleanupAlpm(); - m_manager.initAlpmHandle(); - m_manager.registerDataBasesFromPacmanConfig(); - m_manager.registerDatabasesFromRepoIndexConfig(); + m_manager.removeAllDatabases(); + m_manager.addLocalDatabase(); + m_manager.addDataBasesFromPacmanConfig(); + m_manager.addDatabasesFromRepoIndexConfig(); m_manager.initAlpmDataBases(true); } else { sendError(QStringLiteral("rejected"), id); diff --git a/repoindex.pro b/repoindex.pro index 4ee7eb9..3a98860 100644 --- a/repoindex.pro +++ b/repoindex.pro @@ -27,11 +27,9 @@ CONFIG(release, debug|release) { HEADERS += \ alpm/manager.h \ alpm/package.h \ - alpm/list.h \ alpm/utilities.h \ network/server.h \ network/connection.h \ - alpm/group.h \ alpm/config.h \ alpm/resolvebuildorder.h \ alpm/mingwbundle.h \ @@ -53,7 +51,6 @@ SOURCES += \ alpm/utilities.cpp \ network/server.cpp \ network/connection.cpp \ - alpm/group.cpp \ alpm/config.cpp \ alpm/resolvebuildorder.cpp \ alpm/mingwbundle.cpp \ @@ -102,7 +99,6 @@ CONFIG(debug, debug|release) { } else { LIBS += -lc++utilities } -LIBS += -lalpm # installs target.path = $$(INSTALL_ROOT)/bin