588 lines
22 KiB
C++
588 lines
22 KiB
C++
|
#include "manager.h"
|
||
|
#include "database.h"
|
||
|
#include "utilities.h"
|
||
|
#include "list.h"
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <c++utilities/io/inifile.h>
|
||
|
#include <c++utilities/conversion/stringconversion.h>
|
||
|
|
||
|
#include <QList>
|
||
|
#include <QJsonObject>
|
||
|
#include <QSysInfo>
|
||
|
#include <QtConcurrent/QtConcurrent>
|
||
|
|
||
|
#include <fstream>
|
||
|
#include <iostream>
|
||
|
#include <unordered_map>
|
||
|
#include <algorithm>
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace IoUtilities;
|
||
|
using namespace ConversionUtilities;
|
||
|
|
||
|
namespace PackageManagement {
|
||
|
|
||
|
constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
|
||
|
ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
|
||
|
|
||
|
/*!
|
||
|
* \brief The Manager class helps accessing ALPM.
|
||
|
*
|
||
|
* - It queries the ALPM for database and package information.
|
||
|
* - It serializes the information as JSON objects used by the network classes and the web interface.
|
||
|
*/
|
||
|
|
||
|
/*!
|
||
|
* \brief Creates a new manager class; initializes a new ALPM handle.
|
||
|
* \param rootdir Specifies the root directory.
|
||
|
* \param dbpath Specifies the database directory.
|
||
|
*/
|
||
|
Manager::Manager(const Config &config, QObject *parent) :
|
||
|
QObject(parent),
|
||
|
m_config(config),
|
||
|
m_sigLevel(defaultSigLevel),
|
||
|
m_localFileSigLevel(ALPM_SIG_USE_DEFAULT),
|
||
|
m_aur(m_networkAccessManager)
|
||
|
{
|
||
|
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));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Releases the associated ALPM handle.
|
||
|
*/
|
||
|
Manager::~Manager()
|
||
|
{
|
||
|
alpm_release(m_handle);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Returns the package with the specified name from the specified database.
|
||
|
*/
|
||
|
AlpmPackage Manager::packageFromSyncDataBase(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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \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)
|
||
|
{
|
||
|
alpm_pkg_t *pkg;
|
||
|
if(alpm_pkg_load(m_handle, fileName, verifyIntegrity, static_cast<alpm_siglevel_t>(m_localFileSigLevel), &pkg) != 0) {
|
||
|
throw runtime_error(string("Unable to load package file: ") + alpm_strerror(alpm_errno(m_handle)));
|
||
|
} else {
|
||
|
return AlpmOwnershipPackage(pkg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \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() + ": " + alpm_strerror(alpm_errno(m_handle)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Returns the last value with the specified \a key in the specified multimap.
|
||
|
*/
|
||
|
const string &lastValue(const multimap<string, string> &mm, const string &key)
|
||
|
{
|
||
|
static const string defaultValue;
|
||
|
const auto i = find_if(mm.crbegin(), mm.crend(), [&key] (const pair<string, string> &i) {
|
||
|
return i.first == key;
|
||
|
});
|
||
|
return i != mm.crend() ? i->second : defaultValue;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Parses a "SigLevel" denotation from pacman config file.
|
||
|
*/
|
||
|
int parseSigLevel(const string &sigLevelStr = string())
|
||
|
{
|
||
|
int sigLevel = defaultSigLevel;
|
||
|
// split sig level denotation into parts
|
||
|
const auto parts = splitString<list<string> >(sigLevelStr, " ");
|
||
|
for(const auto &part : parts) {
|
||
|
// determine whether part affect packages, databases or both
|
||
|
bool package = true, db = true;
|
||
|
const char *partStart = part.data();
|
||
|
if(!strncmp(partStart, "Package", 7)) {
|
||
|
db = false; // package only part
|
||
|
partStart += 7;
|
||
|
} else if(!strncmp(partStart, "Database", 8)) {
|
||
|
package = false; // db only part
|
||
|
partStart += 8;
|
||
|
}
|
||
|
// set sig level according part
|
||
|
if(!strcmp(partStart, "Never")) {
|
||
|
if(package) {
|
||
|
sigLevel &= ~ALPM_SIG_PACKAGE;
|
||
|
}
|
||
|
if(db) {
|
||
|
sigLevel &= ~ALPM_SIG_DATABASE;
|
||
|
}
|
||
|
} else if(!strcmp(partStart, "Optional")) {
|
||
|
if(package) {
|
||
|
sigLevel |= ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL;
|
||
|
}
|
||
|
if(db) {
|
||
|
sigLevel |= ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
|
||
|
}
|
||
|
} else if(!strcmp(partStart, "Required")) {
|
||
|
if(package) {
|
||
|
sigLevel |= ALPM_SIG_PACKAGE;
|
||
|
sigLevel &= ~ALPM_SIG_PACKAGE_OPTIONAL;
|
||
|
}
|
||
|
if(db) {
|
||
|
sigLevel |= ALPM_SIG_DATABASE;
|
||
|
sigLevel &= ~ALPM_SIG_DATABASE_OPTIONAL;
|
||
|
}
|
||
|
} else if(!strcmp(partStart, "TrustedOnly")) {
|
||
|
if(package) {
|
||
|
sigLevel &= ~(ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK);
|
||
|
}
|
||
|
if(db) {
|
||
|
sigLevel &= ~(ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK);
|
||
|
}
|
||
|
} else if(!strcmp(partStart, "TrustAll")) {
|
||
|
if(package) {
|
||
|
sigLevel |= ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK;
|
||
|
}
|
||
|
if(db) {
|
||
|
sigLevel |= ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK;
|
||
|
}
|
||
|
} else {
|
||
|
cerr << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl;
|
||
|
}
|
||
|
}
|
||
|
return sigLevel;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Parses a "Usage" denotation from pacman config file.
|
||
|
*/
|
||
|
int parseUsage(const string &usageStr)
|
||
|
{
|
||
|
int usage = 0;
|
||
|
const auto parts = splitString<list<string> >(usageStr, " ");
|
||
|
for(const auto &part : parts) {
|
||
|
if(part == "Sync") {
|
||
|
usage |= ALPM_DB_USAGE_SYNC;
|
||
|
} else if(part == "Search") {
|
||
|
usage |= ALPM_DB_USAGE_SEARCH;
|
||
|
} else if(part == "Install") {
|
||
|
usage |= ALPM_DB_USAGE_INSTALL;
|
||
|
} else if(part == "Upgrade") {
|
||
|
usage |= ALPM_DB_USAGE_UPGRADE;
|
||
|
} else {
|
||
|
cerr << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl;
|
||
|
}
|
||
|
}
|
||
|
return usage ? usage : ALPM_DB_USAGE_ALL;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Parses the Pacman configuration. Registers the listed sync databases.
|
||
|
*/
|
||
|
void Manager::parsePacmanConfig()
|
||
|
{
|
||
|
// open config file and parse as ini
|
||
|
try {
|
||
|
IniFile configIni;
|
||
|
{
|
||
|
fstream configFile;
|
||
|
configFile.exceptions(ios_base::failbit | ios_base::badbit);
|
||
|
configFile.open(m_config.pacmanConfFile().toLocal8Bit().data(), ios_base::in);
|
||
|
configIni.parse(configFile);
|
||
|
}
|
||
|
// determine current cpu archtitecture (required for server URLs)
|
||
|
static const string sysArch(QSysInfo::currentCpuArchitecture().toStdString());
|
||
|
string arch = sysArch;
|
||
|
const auto &config = configIni.data();
|
||
|
// read relevant options
|
||
|
static const string sigLevelKey("SigLevel");
|
||
|
static const string usageKey("Usage");
|
||
|
int globalSigLevel;
|
||
|
try {
|
||
|
const auto &options = config.at("options");
|
||
|
const auto &specifiedArch = lastValue(options, "Architecture");
|
||
|
if(!specifiedArch.empty() && specifiedArch != "auto") {
|
||
|
arch = specifiedArch;
|
||
|
}
|
||
|
const auto &specifiedDir = lastValue(options, "CacheDir");
|
||
|
if(!specifiedDir.empty()) {
|
||
|
m_cacheDir = QString::fromStdString(specifiedDir);
|
||
|
}
|
||
|
globalSigLevel = parseSigLevel(lastValue(options, sigLevelKey));
|
||
|
} catch(const out_of_range &) {
|
||
|
// no options specified
|
||
|
globalSigLevel = defaultSigLevel;
|
||
|
}
|
||
|
// register sync databases
|
||
|
unordered_map<string, IniFile> includedInis;
|
||
|
for(const auto &scope : config) {
|
||
|
if(scope.first != "options") {
|
||
|
// read and validate database name
|
||
|
QString dbName = QString::fromLocal8Bit(scope.first.c_str());
|
||
|
if(dbName == QLatin1String("local")) {
|
||
|
cerr << "Error: Unable to add database from pacman config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
|
||
|
} else if(dbName.startsWith(QLatin1String("aur"))) {
|
||
|
cerr << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
|
||
|
} else if(m_syncDbs.count(dbName)) {
|
||
|
cerr << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl;
|
||
|
} 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<alpm_siglevel_t>(sigLevel))) {
|
||
|
// set usage
|
||
|
if(alpm_db_set_usage(db, static_cast<alpm_db_usage_t>(usage)) == 0) {
|
||
|
cerr << "Added database [" << scope.first << "]" << endl;
|
||
|
} else {
|
||
|
cerr << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
|
||
|
}
|
||
|
// add servers
|
||
|
for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
|
||
|
string url = range.first->second;
|
||
|
findAndReplace<string>(url, "$repo", scope.first);
|
||
|
findAndReplace<string>(url, "$arch", arch);
|
||
|
alpm_db_add_server(db, url.c_str());
|
||
|
cerr << "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 << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl;
|
||
|
}
|
||
|
}
|
||
|
try {
|
||
|
const auto &includedScope = includedIni.data().at(string());
|
||
|
for(auto range = includedScope.equal_range("Server"); range.first != range.second; ++range.first) {
|
||
|
string url = range.first->second;
|
||
|
findAndReplace<string>(url, "$repo", scope.first);
|
||
|
findAndReplace<string>(url, "$arch", arch);
|
||
|
alpm_db_add_server(db, url.c_str());
|
||
|
cerr << "Added server: " << url << endl;
|
||
|
}
|
||
|
} catch (const out_of_range &) {
|
||
|
cerr << "Warning: Included file \"" << path << "\" has no values." << endl;
|
||
|
}
|
||
|
}
|
||
|
// add sync db to internal map
|
||
|
if(usage & ALPM_DB_USAGE_UPGRADE) {
|
||
|
// -> db is used to upgrade local database
|
||
|
localDataBase().upgradeSources() << dbName;
|
||
|
}
|
||
|
m_syncDbs.emplace(dbName, db);
|
||
|
} else {
|
||
|
cerr << "Unable to add sync database [" << scope.first << "]" << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} catch (const ios_base::failure &) {
|
||
|
throw ios_base::failure("An IO exception occured when parsing the config file.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Unregisters all registred sync databases.
|
||
|
*/
|
||
|
void Manager::unregisterSyncDataBases()
|
||
|
{
|
||
|
if(alpm_unregister_all_syncdbs(m_handle)) {
|
||
|
throw runtime_error(string("Cannot unregister sync databases: ") + alpm_strerror(alpm_errno(m_handle)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Returns the local data base.
|
||
|
*/
|
||
|
const AlpmDataBase &Manager::localDataBase() const
|
||
|
{
|
||
|
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<QString, AlpmDataBase> &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("packages"), db.packageNameJsonArray());
|
||
|
return repoInfo;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Returns basic information about all repositories known to the manager.
|
||
|
*
|
||
|
* The results include the local database ("local") and the names of
|
||
|
* the registered sync databases.
|
||
|
*/
|
||
|
const QJsonArray &Manager::basicRepoInfo() const
|
||
|
{
|
||
|
if(m_basicRepoInfo.isEmpty()) {
|
||
|
m_basicRepoInfo << basicRepoInfo(localDataBase(), QStringLiteral("local"), QStringLiteral("The local database."));
|
||
|
auto const &syncDbs = syncDataBases();
|
||
|
for(const auto &syncDb : syncDbs) {
|
||
|
// check if the "sync" database is actually used for syncing
|
||
|
auto usage = syncDb.second.usage();
|
||
|
if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) {
|
||
|
m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The sync database »%1«.").arg(syncDb.first));
|
||
|
} else {
|
||
|
m_basicRepoInfo << basicRepoInfo(syncDb.second, syncDb.first, QStringLiteral("The database »%1«.").arg(syncDb.first));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return m_basicRepoInfo;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Returns package information for the specified selection of packages.
|
||
|
*/
|
||
|
const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full)
|
||
|
{
|
||
|
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"));
|
||
|
}
|
||
|
pkgInfo.insert(QStringLiteral("name"), pkgName);
|
||
|
pkgInfo.insert(QStringLiteral("repo"), repoName);
|
||
|
pkgInfo.insert(QStringLiteral("index"), pkgSel.value(QStringLiteral("index")));
|
||
|
pkgInfos << pkgInfo;
|
||
|
}
|
||
|
}
|
||
|
return pkgInfos;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Returns group information for the local database and all registred sync databases.
|
||
|
*/
|
||
|
const QJsonArray &Manager::groupInfo() const
|
||
|
{
|
||
|
if(m_groupInfo.empty()) {
|
||
|
m_groupInfo << localDataBase().groupInfo();
|
||
|
for(const auto &db : m_syncDbs) {
|
||
|
m_groupInfo << db.second.groupInfo();
|
||
|
}
|
||
|
}
|
||
|
return m_groupInfo;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \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
|
||
|
{
|
||
|
if(dbName == QLatin1String("local")) {
|
||
|
return localDataBase();
|
||
|
} else {
|
||
|
return m_syncDbs.at(dbName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \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)
|
||
|
{
|
||
|
if(dbName == QLatin1String("local")) {
|
||
|
return localDataBase();
|
||
|
} else {
|
||
|
return m_syncDbs.at(dbName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \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.
|
||
|
*/
|
||
|
QJsonObject Manager::invokeUpgradeLookup(const QJsonObject &request) const
|
||
|
{
|
||
|
QJsonObject result;
|
||
|
QJsonArray errors;
|
||
|
const AlpmDataBase &db = dataBaseByName(request.value(QStringLiteral("db")).toString());
|
||
|
QJsonValue syncDbsValue = request.value(QStringLiteral("syncdbs"));
|
||
|
if(!db) {
|
||
|
errors << QStringLiteral("Database to be checked not found.");
|
||
|
} else {
|
||
|
QJsonArray warnings;
|
||
|
bool searchAur = request.value(QStringLiteral("aur")).toBool(false);
|
||
|
if(searchAur) {
|
||
|
m_aur.requestPackageInfo(db.packageNames());
|
||
|
}
|
||
|
const auto results = checkForUpgrades(db, syncDbsValue);
|
||
|
if(searchAur || !results.noSources) {
|
||
|
QJsonArray softwareUpdates;
|
||
|
QJsonArray packageOnlyUpdates;
|
||
|
QJsonArray downgrades;
|
||
|
QJsonArray orphanedPackages;
|
||
|
if(!results.noSources) {
|
||
|
for(const auto pkg : results.newVersions) {
|
||
|
softwareUpdates << pkg.basicInfo(true);
|
||
|
}
|
||
|
for(const auto pkg : results.newReleases) {
|
||
|
packageOnlyUpdates << pkg.basicInfo(true);
|
||
|
}
|
||
|
for(const auto pkg : results.downgrades) {
|
||
|
downgrades << pkg.basicInfo(true);
|
||
|
}
|
||
|
for(const auto pkg : results.orphaned) {
|
||
|
orphanedPackages << pkg.basicInfo(true);
|
||
|
}
|
||
|
}
|
||
|
if(!warnings.isEmpty()) {
|
||
|
result.insert(QStringLiteral("warnings"), warnings);
|
||
|
}
|
||
|
result.insert(QStringLiteral("softwareUpdates"), softwareUpdates);
|
||
|
result.insert(QStringLiteral("packageOnlyUpdates"), packageOnlyUpdates);
|
||
|
result.insert(QStringLiteral("downgrades"), downgrades);
|
||
|
result.insert(QStringLiteral("orphanedPackages"), orphanedPackages);
|
||
|
} else {
|
||
|
errors << QStringLiteral("No update sources associated for database \"%1\".").arg(QString::fromLocal8Bit(db.name()));
|
||
|
}
|
||
|
}
|
||
|
if(!errors.isEmpty()) {
|
||
|
result.insert(QStringLiteral("errors"), errors);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Checks the specified database for upgrades.
|
||
|
*/
|
||
|
const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db, const QJsonValue &syncDbsValue) const
|
||
|
{
|
||
|
UpdateLookupResults results;
|
||
|
const auto &syncDbs = syncDataBases();
|
||
|
QList<const AlpmDataBase *> syncDbSel;
|
||
|
if(syncDbsValue.type() == QJsonValue::Array) {
|
||
|
for(const auto &syncDbVal : syncDbsValue.toArray()) {
|
||
|
const auto syncDbName = syncDbVal.toString();
|
||
|
if(syncDbName == QLatin1String("local")) {
|
||
|
syncDbSel << &(localDataBase());
|
||
|
} 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 : db.upgradeSources()) {
|
||
|
if(syncDbName == QLatin1String("local")) {
|
||
|
syncDbSel << &(localDataBase());
|
||
|
} else {
|
||
|
try {
|
||
|
syncDbSel << &(syncDbs.at(syncDbName));
|
||
|
} catch(out_of_range &) {
|
||
|
results.warnings << QStringLiteral("The associated upgrade database \"%1\" can not be found.").arg(syncDbName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
db.checkForUpgrades(syncDbSel, results);
|
||
|
return results;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \brief Checks the specified database for upgrades.
|
||
|
*
|
||
|
* Appropriate upgrade sources will be determined automatically.
|
||
|
*/
|
||
|
const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db) const
|
||
|
{
|
||
|
UpdateLookupResults results;
|
||
|
QList<const AlpmDataBase *> syncDbSel;
|
||
|
for(const auto &syncDbName : db.upgradeSources()) {
|
||
|
try {
|
||
|
syncDbSel << &(syncDataBases().at(syncDbName));
|
||
|
} catch(out_of_range &) {
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
db.checkForUpgrades(syncDbSel, results);
|
||
|
return results;
|
||
|
}
|
||
|
|
||
|
//void Manager::addTask()
|
||
|
//{
|
||
|
// QUuid uuid;
|
||
|
// while(m_tasks.find(uuid = QUuid::createUuid()) != m_tasks.cend());
|
||
|
// m_tasks.emplace(uuid);
|
||
|
//}
|
||
|
|
||
|
}
|