added previous version to update results; started to work on build order resolver and mingw-w64 installer

This commit is contained in:
Martchus 2015-08-19 02:13:28 +02:00
parent b42ef62cd6
commit 8f32dd0da0
32 changed files with 1604 additions and 504 deletions

View File

@ -1,4 +1,5 @@
#include "config.h"
#include "manager.h"
#include <c++utilities/conversion/stringconversion.h>
@ -25,7 +26,9 @@ namespace PackageManagement {
*/
ConfigArgs::ConfigArgs(ArgumentParser &parser) :
helpArg(parser),
buildOrderArg("build-order", "b", "calculates the build order to build the specified packages"),
serverArg("server", "s", "runs a websocket server providing the web interface with information"),
mingwBundleArg("mingw-w64-bundle", "m", "creates an archive with the runtime-relevant files from the specified mingw-w64-packages and their dependencies"),
repoindexConfArg("repoindex-conf", "c", "specifies the path of the repo index config file (default is /etc/repoindex.conf"),
rootdirArg("root-dir", "r", "specifies the root directory (default is /)"),
dbpathArg("db-path", "d", "specifies the pacman database path (default is /var/lib/pacman)"),
@ -34,10 +37,20 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
websocketPortArg("port", "p", "specifies the listening port for the websocket server, default is 1234"),
certFileArg("cert-file", string(), "specifies the SSL certificate"),
keyFileArg("key-file", string(), "specifies the private SSL key"),
insecureArg("insecure", string(), "forces the server to run in insecure mode")
insecureArg("insecure", string(), "forces the server to run in insecure mode"),
aurArg("aur", "u", "enables/disables AUR queries"),
verboseArg("verbose", "v", "be verbose"),
outputFileArg("output-file", "f", "specifies the output file")
{
initializer_list<string> pathValueName = {"path"};
const initializer_list<string> pathValueName = {"path"};
const initializer_list<string> pkgValueNames = {"package 1", "package 2", "package 3"};
buildOrderArg.setDenotesOperation(true);
buildOrderArg.setRequiredValueCount(-1);
buildOrderArg.setValueNames(pkgValueNames);
serverArg.setDenotesOperation(true);
mingwBundleArg.setDenotesOperation(true);
mingwBundleArg.setRequiredValueCount(-1);
mingwBundleArg.setValueNames(pkgValueNames);
repoindexConfArg.setCombinable(true);
repoindexConfArg.setValueNames(pathValueName);
repoindexConfArg.setRequiredValueCount(1);
@ -63,8 +76,18 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
keyFileArg.setValueNames(pathValueName);
keyFileArg.setRequiredValueCount(1);
insecureArg.setCombinable(true);
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg});
parser.setMainArguments({&serverArg, &repoindexConfArg, &helpArg});
aurArg.setCombinable(true);
aurArg.setRequiredValueCount(1);
aurArg.setValueNames({"enabled/disabled"});
verboseArg.setCombinable(true);
outputFileArg.setCombinable(true);
outputFileArg.setRequired(true);
outputFileArg.setRequiredValueCount(1);
outputFileArg.setValueNames({"path"});
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &insecureArg, &aurArg});
buildOrderArg.setSecondaryArguments({&aurArg, &verboseArg});
mingwBundleArg.setSecondaryArguments({&outputFileArg});
parser.setMainArguments({&buildOrderArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &helpArg});
}
/*!
@ -77,12 +100,15 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
*/
Config::Config() :
m_alpmRootDir(QStringLiteral("/")),
m_alpmDbPath(QStringLiteral("/var/lib/pacman/")),
m_alpmDbPath(QStringLiteral("/var/lib/pacman")),
m_pacmanConfFile(QStringLiteral("/etc/pacman.conf")),
m_websocketServerListeningAddr(QHostAddress::LocalHost),
m_websocketServerListeningPort(1234),
m_serverInsecure(false),
m_reposFromPacmanConf(false)
m_reposFromPacmanConf(false),
m_aurEnabled(true),
m_verbose(false),
m_runServer(false)
{}
/*!
@ -150,6 +176,8 @@ void Config::loadFromConfigFile(const QString &configFilePath)
m_alpmRootDir = alpmObj.value(QStringLiteral("rootDir")).toString(m_alpmRootDir);
m_alpmDbPath = alpmObj.value(QStringLiteral("dbPath")).toString(m_alpmDbPath);
m_pacmanConfFile = alpmObj.value(QStringLiteral("pacmanConfigFile")).toString(m_pacmanConfFile);
auto aurObj = mainObj.value(QStringLiteral("aur")).toObject();
m_aurEnabled = aurObj.value(QStringLiteral("enabled")).toBool(m_aurEnabled);
auto serverObj = mainObj.value(QStringLiteral("server")).toObject();
assign(m_websocketServerListeningAddr, serverObj.value(QStringLiteral("websocketListeningAddr")).toString());
assign(m_websocketServerListeningPort, serverObj.value(QStringLiteral("websocketListeningPort")));
@ -164,7 +192,7 @@ void Config::loadFromConfigFile(const QString &configFilePath)
}
} else {
cerr << "Error: Unable to parse config file \"" << configFilePath.toLocal8Bit().data() << "\": "
<< error.errorString().toLocal8Bit().data() << endl;
<< error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl;
}
} else {
cerr << "Error: Unable to open config file \"" << configFilePath.toLocal8Bit().data() << "\"." << endl;
@ -200,6 +228,16 @@ void Config::loadFromArgs(const ConfigArgs &args)
assign(m_alpmRootDir, args.rootdirArg);
assign(m_alpmDbPath, args.dbpathArg);
assign(m_pacmanConfFile, args.pacmanConfArg);
if(args.aurArg.isPresent()) {
auto val = args.aurArg.values().front();
if(val == "enabled") {
m_aurEnabled = true;
} else if(val == "disabled") {
m_aurEnabled = false;
} else {
cerr << "Warning: The specified value for the argument --aur-enabled is invalid and will be ignored." << endl;
}
}
assign(m_websocketServerListeningPort, args.websocketPortArg);
assign(m_serverCertFile, args.certFileArg);
assign(m_serverKeyFile, args.keyFileArg);
@ -207,6 +245,8 @@ void Config::loadFromArgs(const ConfigArgs &args)
if(args.websocketAddrArg.isPresent()) {
assign(m_websocketServerListeningAddr, QString::fromLocal8Bit(args.websocketAddrArg.values().front().data(), args.websocketAddrArg.values().front().size()));
}
m_verbose = args.verboseArg.isPresent();
m_runServer = args.serverArg.isPresent();
}
/*!
@ -216,15 +256,22 @@ void RepoEntry::load(const QJsonValue &jsonValue)
{
auto obj = jsonValue.toObject();
m_name = obj.value(QStringLiteral("name")).toString(m_name);
m_dbPath = obj.value(QStringLiteral("dbpath")).toString(m_dbPath);
m_srcPath = obj.value(QStringLiteral("srcpath")).toString(m_srcPath);
m_pkgPath = obj.value(QStringLiteral("pkgpath")).toString(m_pkgPath);
m_dataBaseFile = obj.value(QStringLiteral("dataBaseFile")).toString(m_dataBaseFile);
m_sourceDir = obj.value(QStringLiteral("sourcesDir")).toString(m_sourceDir);
m_packageDir = obj.value(QStringLiteral("packagesDir")).toString(m_packageDir);
for(const auto &server : obj.value(QStringLiteral("servers")).toArray()) {
auto str = server.toString();
if(!str.isEmpty()) {
m_servers << str;
}
}
for(const auto &upgradeSources : obj.value(QStringLiteral("upgradeSources")).toArray()) {
auto str = upgradeSources.toString();
if(!str.isEmpty()) {
m_upgradeSources << str;
}
}
m_sigLevel = Manager::parseSigLevel(obj.value(QStringLiteral("sigLevel")).toString().toLocal8Bit().data());
}
} // namespace Alpm

View File

@ -18,7 +18,9 @@ class ConfigArgs
public:
ConfigArgs(ApplicationUtilities::ArgumentParser &parser);
ApplicationUtilities::HelpArgument helpArg;
ApplicationUtilities::Argument buildOrderArg;
ApplicationUtilities::Argument serverArg;
ApplicationUtilities::Argument mingwBundleArg;
ApplicationUtilities::Argument repoindexConfArg;
ApplicationUtilities::Argument rootdirArg;
ApplicationUtilities::Argument dbpathArg;
@ -28,6 +30,9 @@ public:
ApplicationUtilities::Argument certFileArg;
ApplicationUtilities::Argument keyFileArg;
ApplicationUtilities::Argument insecureArg;
ApplicationUtilities::Argument aurArg;
ApplicationUtilities::Argument verboseArg;
ApplicationUtilities::Argument outputFileArg;
};
class Config;
@ -36,39 +41,48 @@ class RepoEntry
{
friend class Config;
public:
RepoEntry();
const QString &name() const;
const QString &dbPath() const;
const QString &srcPath() const;
const QString &pkgPath() const;
const QString &dataBasePath() const;
const QString &sourceDir() const;
const QString &packageDir() const;
const QStringList &servers() const;
const QStringList &upgradeSources() const;
int sigLevel() const;
void load(const QJsonValue &jsonValue);
private:
QString m_name;
QString m_dbPath;
QString m_srcPath;
QString m_pkgPath;
QString m_dataBaseFile;
QString m_sourceDir;
QString m_packageDir;
QStringList m_servers;
QStringList m_upgradeSources;
int m_sigLevel;
};
inline RepoEntry::RepoEntry() :
m_sigLevel(0)
{}
inline const QString &RepoEntry::name() const
{
return m_name;
}
inline const QString &RepoEntry::dbPath() const
inline const QString &RepoEntry::dataBasePath() const
{
return m_dbPath;
return m_dataBaseFile;
}
inline const QString &RepoEntry::srcPath() const
inline const QString &RepoEntry::sourceDir() const
{
return m_srcPath;
return m_sourceDir;
}
inline const QString &RepoEntry::pkgPath() const
inline const QString &RepoEntry::packageDir() const
{
return m_pkgPath;
return m_packageDir;
}
inline const QStringList &RepoEntry::servers() const
@ -76,6 +90,16 @@ inline const QStringList &RepoEntry::servers() const
return m_servers;
}
inline const QStringList &RepoEntry::upgradeSources() const
{
return m_upgradeSources;
}
inline int RepoEntry::sigLevel() const
{
return m_sigLevel;
}
class Config
{
public:
@ -91,6 +115,9 @@ public:
bool serverInsecure() const;
bool reposFromPacmanConf() const;
const QList<RepoEntry> &repoEntries() const;
bool isAurEnabled() const;
bool isVerbose() const;
bool runServer() const;
void loadFromConfigFile(const QString &args);
void loadFromConfigFile(const ConfigArgs &args);
@ -107,9 +134,11 @@ private:
QString m_serverKeyFile;
bool m_serverInsecure;
bool m_reposFromPacmanConf;
QList<RepoEntry> m_repoEntries;
bool m_reposFromPacmanConf;
bool m_aurEnabled;
bool m_verbose;
bool m_runServer;
};
inline const QString &Config::alpmRootDir() const
@ -162,6 +191,21 @@ inline const QList<RepoEntry> &Config::repoEntries() const
return m_repoEntries;
}
inline bool Config::isAurEnabled() const
{
return m_aurEnabled;
}
inline bool Config::isVerbose() const
{
return m_verbose;
}
inline bool Config::runServer() const
{
return m_runServer;
}
} // namespace Alpm
#endif // ALPM_CONFIG_H

View File

@ -1,5 +1,6 @@
#include "database.h"
#include "group.h"
#include "updatelookup.h"
#include <QList>
#include <QJsonObject>
@ -38,6 +39,20 @@ const QJsonArray &AlpmDataBase::serverUrls() const
return m_serverUrls;
}
/*!
* \brief Adds the specified server URLs.
*/
bool AlpmDataBase::addServerUrls(const QStringList &urls)
{
m_serverUrls = QJsonArray();
for(const auto &url : urls) {
if(alpm_db_add_server(m_ptr, url.toLocal8Bit().data()) != 0) {
return false;
}
}
return true;
}
/*!
* \brief Returns the packages of the database.
*/
@ -95,7 +110,7 @@ QJsonObject AlpmDataBase::groupInfo() const
return groupInfo;
}
void AlpmDataBase::checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults &results) const
void AlpmDataBase::checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults<AlpmPackage> &results) const
{
if(syncDbs.isEmpty()) {
results.noSources = true;
@ -110,13 +125,13 @@ void AlpmDataBase::checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs,
case PackageVersionComparsion::Equal:
break; // ignore equal packages
case PackageVersionComparsion::SoftwareUpgrade:
results.newVersions << syncPkg;
results.newVersions << makeUpdateResult(syncPkg, dbPkg.second.version());
break;
case PackageVersionComparsion::PackageUpgradeOnly:
results.newReleases << syncPkg;
results.newReleases << makeUpdateResult(syncPkg, dbPkg.second.version());
break;
case PackageVersionComparsion::NewerThenSyncVersion:
results.downgrades << syncPkg;
results.downgrades << makeUpdateResult(syncPkg, dbPkg.second.version());
}
orphaned = false;
} catch(out_of_range &) {

View File

@ -2,7 +2,7 @@
#define ALPM_DATABASE_H
#include "list.h"
#include "package.h"
#include "updatelookup.h"
#include <alpm.h>
@ -13,51 +13,10 @@
namespace PackageManagement {
class UpdateLookupResults
{
public:
UpdateLookupResults();
/*!
* \brief Indicates that there are no upgrade sources available.
*/
bool noSources;
/*!
* \brief Packages providing a software upgrade (new version).
*/
QList<AlpmPackage> newVersions;
/*!
* \brief Package upgrades only (new release).
*/
QList<AlpmPackage> newReleases;
/*!
* \brief Downgrades (older version in sync db).
*/
QList<AlpmPackage> downgrades;
/*!
* \brief Orphaned packages (could not be found in any of the sync dbs).
*/
QList<AlpmPackage> orphaned;
QStringList warnings;
QStringList errors;
};
/*!
* \brief Constructs new update lookup results.
*/
inline UpdateLookupResults::UpdateLookupResults() :
noSources(false)
{}
class AlpmDataBase
{
public:
AlpmDataBase(alpm_db_t *dataBase = nullptr);
AlpmDataBase(alpm_db_t *dataBase = nullptr, const QString &dbFile = QString(), const QString &srcDir = QString(), const QString &pkgDir = QString());
// operators
operator bool() const;
@ -67,11 +26,17 @@ public:
// database properties
alpm_db_t *ptr() const;
const char *name() const;
const QString &dataBaseFile() const;
const QString &sourcesDirectory() const;
void setSourcesDirectory(const QString &dir);
const QString &packagesDirectory() const;
void setPackagesDirectory(const QString &dir);
alpm_siglevel_t sigLevel() const;
alpm_db_usage_t usage() const;
StringList servers() const;
const QJsonArray &serverUrls() const;
bool setServers(StringList servers);
const QJsonArray &serverUrls() const;
bool addServerUrls(const QStringList &urls);
alpm_pkg_t *package(const char *name) const;
const std::map<QString, AlpmPackage> &packages() const;
QStringList packageNames() const;
@ -84,11 +49,16 @@ public:
// upgrade lookup
const QStringList &upgradeSources() const;
QStringList &upgradeSources();
void checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults &results) const;
template<class SyncPackageType>
void checkForUpgrades(const std::map<QString, SyncPackageType> &syncDbPkgs, UpdateLookupResults<SyncPackageType> &results) const;
void checkForUpgrades(const QList<const AlpmDataBase *> &syncDbs, UpdateLookupResults<AlpmPackage> &results) const;
private:
alpm_db_t *m_ptr;
QStringList m_updateSources;
QString m_dbFile;
QString m_srcDir;
QString m_pkgDir;
QStringList m_upgradeSources;
mutable QJsonArray m_serverUrls;
mutable std::map<QString, AlpmPackage> m_packages;
mutable QJsonArray m_packageNamesJsonArray;
@ -98,8 +68,11 @@ private:
/*!
* \brief Creates a new instance wrapping the specified database struct.
*/
inline AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase) :
m_ptr(dataBase)
inline AlpmDataBase::AlpmDataBase(alpm_db_t *dataBase, const QString &dbFile, const QString &srcDir, const QString &pkgDir) :
m_ptr(dataBase),
m_dbFile(dbFile),
m_srcDir(srcDir),
m_pkgDir(pkgDir)
{}
/*!
@ -134,6 +107,46 @@ inline const char *AlpmDataBase::name() const
return alpm_db_get_name(m_ptr);
}
/*!
* \brief Returns the path of the data base file.
*/
inline const QString &AlpmDataBase::dataBaseFile() const
{
return m_dbFile;
}
/*!
* \brief Returns the path of the local sources directory.
*/
inline const QString &AlpmDataBase::sourcesDirectory() const
{
return m_srcDir;
}
/*!
* \brief Sets the path of the local sources directory.
*/
inline void AlpmDataBase::setSourcesDirectory(const QString &dir)
{
m_srcDir = dir;
}
/*!
* \brief Returns the path of the local packages directory.
*/
inline const QString &AlpmDataBase::packagesDirectory() const
{
return m_pkgDir;
}
/*!
* \brief Sets the path of the local packages directory.
*/
inline void AlpmDataBase::setPackagesDirectory(const QString &dir)
{
m_pkgDir = dir;
}
/*!
* \brief Returns the signature level of the database.
*/
@ -155,7 +168,7 @@ inline StringList AlpmDataBase::servers() const
*/
inline bool AlpmDataBase::setServers(StringList servers)
{
return alpm_db_set_servers(m_ptr, servers.begin().ptr()) != 0;
return alpm_db_set_servers(m_ptr, servers.begin().ptr()) == 0;
}
/*!
@ -195,7 +208,7 @@ inline PackageList AlpmDataBase::search(StringList terms) const
*/
inline const QStringList &AlpmDataBase::upgradeSources() const
{
return m_updateSources;
return m_upgradeSources;
}
/*!
@ -203,7 +216,7 @@ inline const QStringList &AlpmDataBase::upgradeSources() const
*/
inline QStringList &AlpmDataBase::upgradeSources()
{
return m_updateSources;
return m_upgradeSources;
}
/*!
@ -214,6 +227,30 @@ inline PackageManagement::AlpmDataBase::operator bool() const
return m_ptr != nullptr;
}
template<class SyncPackageType>
void AlpmDataBase::checkForUpgrades(const std::map<QString, SyncPackageType> &syncDbPkgs, UpdateLookupResults<SyncPackageType> &results) const
{
for(const auto &dbPkg : packages()) {
try {
const auto &syncPkg = syncDbPkgs.at(dbPkg.first);
switch(dbPkg.second.compareVersion(syncPkg)) {
case PackageVersionComparsion::Equal:
break; // ignore equal packages
case PackageVersionComparsion::SoftwareUpgrade:
results.newVersions << makeUpdateResult(syncPkg, dbPkg.second.version());
break;
case PackageVersionComparsion::PackageUpgradeOnly:
results.newReleases << makeUpdateResult(syncPkg, dbPkg.second.version());
break;
case PackageVersionComparsion::NewerThenSyncVersion:
results.downgrades << makeUpdateResult(syncPkg, dbPkg.second.version());
}
} catch(std::out_of_range &) {
results.orphaned << dbPkg.second;
}
}
}
} // namespace Alpm
#endif // ALPM_DATABASE_H

View File

@ -8,10 +8,9 @@
#include <c++utilities/conversion/stringconversion.h>
#include <QList>
#include <QJsonObject>
#include <QSysInfo>
#include <QtConcurrent/QtConcurrent>
#include <QMutexLocker>
#include <fstream>
#include <iostream>
#include <unordered_map>
@ -38,8 +37,7 @@ constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
* \param rootdir Specifies the root directory.
* \param dbpath Specifies the database directory.
*/
Manager::Manager(const Config &config, QObject *parent) :
QObject(parent),
Manager::Manager(const Config &config) :
m_config(config),
m_sigLevel(defaultSigLevel),
m_localFileSigLevel(ALPM_SIG_USE_DEFAULT),
@ -62,7 +60,7 @@ Manager::~Manager()
/*!
* \brief Returns the package with the specified name from the specified database.
*/
AlpmPackage Manager::packageFromSyncDataBase(const QString &dbName, const QString &pkgName)
AlpmPackage Manager::packageFromSyncDataBase(const QString &dbName, const QString &pkgName) const
{
try {
if(dbName == QLatin1String("local")) {
@ -115,7 +113,7 @@ const string &lastValue(const multimap<string, string> &mm, const string &key)
/*!
* \brief Parses a "SigLevel" denotation from pacman config file.
*/
int parseSigLevel(const string &sigLevelStr = string())
int Manager::parseSigLevel(const string &sigLevelStr)
{
int sigLevel = defaultSigLevel;
// split sig level denotation into parts
@ -179,7 +177,7 @@ int parseSigLevel(const string &sigLevelStr = string())
/*!
* \brief Parses a "Usage" denotation from pacman config file.
*/
int parseUsage(const string &usageStr)
int Manager::parseUsage(const string &usageStr)
{
int usage = 0;
const auto parts = splitString<list<string> >(usageStr, " ");
@ -200,9 +198,9 @@ int parseUsage(const string &usageStr)
}
/*!
* \brief Parses the Pacman configuration. Registers the listed sync databases.
* \brief Parses and applies the Pacman configuration. Registers the listed sync databases.
*/
void Manager::parsePacmanConfig()
void Manager::applyPacmanConfig()
{
// open config file and parse as ini
try {
@ -229,7 +227,7 @@ void Manager::parsePacmanConfig()
}
const auto &specifiedDir = lastValue(options, "CacheDir");
if(!specifiedDir.empty()) {
m_cacheDir = QString::fromStdString(specifiedDir);
m_pacmanCacheDir = QString::fromStdString(specifiedDir);
}
globalSigLevel = parseSigLevel(lastValue(options, sigLevelKey));
} catch(const out_of_range &) {
@ -242,9 +240,9 @@ void Manager::parsePacmanConfig()
if(scope.first != "options") {
// read and validate database name
QString dbName = QString::fromLocal8Bit(scope.first.c_str());
if(dbName == QLatin1String("local")) {
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
cerr << "Error: Unable to add database from pacman config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
} else if(dbName.startsWith(QLatin1String("aur"))) {
} else if(dbName.startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
cerr << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
} 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;
@ -257,9 +255,13 @@ void Manager::parsePacmanConfig()
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;
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added database [" << scope.first << "]" << endl;
}
} else {
cerr << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
if(m_config.isVerbose() || m_config.runServer()) {
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) {
@ -267,7 +269,9 @@ void Manager::parsePacmanConfig()
findAndReplace<string>(url, "$repo", scope.first);
findAndReplace<string>(url, "$arch", arch);
alpm_db_add_server(db, url.c_str());
cerr << "Added server: " << url << endl;
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added server: " << url << endl;
}
}
// add included servers
for(auto range = scope.second.equal_range("Include"); range.first != range.second; ++range.first) {
@ -290,7 +294,9 @@ void Manager::parsePacmanConfig()
findAndReplace<string>(url, "$repo", scope.first);
findAndReplace<string>(url, "$arch", arch);
alpm_db_add_server(db, url.c_str());
cerr << "Added server: " << url << endl;
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added server: " << url << endl;
}
}
} catch (const out_of_range &) {
cerr << "Warning: Included file \"" << path << "\" has no values." << endl;
@ -301,15 +307,58 @@ void Manager::parsePacmanConfig()
// -> db is used to upgrade local database
localDataBase().upgradeSources() << dbName;
}
m_syncDbs.emplace(dbName, db);
m_syncDbs.emplace(dbName, AlpmDataBase(db, QStringLiteral("%1/sync/%2").arg(m_config.alpmDbPath(), dbName)));
} else {
cerr << "Unable to add sync database [" << scope.first << "]" << endl;
cerr << "Error: 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.");
throw ios_base::failure("Error: An IO exception occured when parsing the config file.");
}
}
/*!
* \brief Applies the repository index configuration.
*/
void Manager::applyRepoIndexConfig()
{
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
AlpmDataBase *syncDb;
try {
syncDb = &m_syncDbs.at(repoEntry.name());
cerr << "Applying config for database [" << syncDb->name() << "]" << endl;
if(!repoEntry.dataBasePath().isEmpty()) {
cerr << "Warning: Can't use data base path specified in repo index config because the repo \""
<< repoEntry.name().toLocal8Bit().data() << "\" has already been added from the Pacman config." << endl;
}
if(repoEntry.sigLevel()) {
cerr << "Warning: Can't use sig level path specified in repo index config because the repo \""
<< repoEntry.name().toLocal8Bit().data() << "\" has already been added from the Pacman config." << endl;
}
syncDb->setPackagesDirectory(repoEntry.packageDir());
syncDb->setSourcesDirectory(repoEntry.sourceDir());
} catch(const out_of_range &) {
if(repoEntry.name().compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
cerr << "Error: Unable to add database from repo index config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
} else if(repoEntry.name().startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
cerr << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
} else {
auto *db = alpm_register_syncdb(m_handle, repoEntry.name().toLocal8Bit().data(), static_cast<alpm_siglevel_t>(repoEntry.sigLevel()));
auto emplaced = m_syncDbs.emplace(repoEntry.name(), AlpmDataBase(db, repoEntry.dataBasePath(), repoEntry.sourceDir(), repoEntry.packageDir()));
if(emplaced.second) {
syncDb = &emplaced.first->second;
if(m_config.isVerbose() || m_config.runServer()) {
cerr << "Added database [" << repoEntry.name().toLocal8Bit().data() << "]" << endl;
}
}
}
}
if(syncDb) {
syncDb->addServerUrls(repoEntry.servers());
syncDb->upgradeSources() << repoEntry.upgradeSources();
}
}
}
@ -329,7 +378,10 @@ void Manager::unregisterSyncDataBases()
const AlpmDataBase &Manager::localDataBase() const
{
if(!m_localDb) {
m_localDb = alpm_get_localdb(m_handle);
QMutexLocker locker(&m_localDbMutex);
if(!m_localDb) {
m_localDb = alpm_get_localdb(m_handle);
}
}
return m_localDb;
}
@ -365,6 +417,7 @@ QJsonObject Manager::basicRepoInfo(AlpmDataBase db, const QString &name, const Q
repoInfo.insert(QStringLiteral("servers"), db.serverUrls());
repoInfo.insert(QStringLiteral("usage"), Utilities::usageStrings(db.usage()));
repoInfo.insert(QStringLiteral("sigLevel"), Utilities::sigLevelStrings(db.sigLevel()));
repoInfo.insert(QStringLiteral("upgradeSources"), QJsonArray::fromStringList(db.upgradeSources()));
repoInfo.insert(QStringLiteral("packages"), db.packageNameJsonArray());
return repoInfo;
}
@ -378,15 +431,18 @@ QJsonObject Manager::basicRepoInfo(AlpmDataBase db, const QString &name, const Q
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));
QMutexLocker locker(&m_basicRepoInfoMutex);
if(m_basicRepoInfo.isEmpty()) {
m_basicRepoInfo << basicRepoInfo(localDataBase(), QStringLiteral("local"), QStringLiteral("The local database."));
auto const &syncDbs = syncDataBases();
for(const auto &syncDb : syncDbs) {
// 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));
}
}
}
}
@ -396,7 +452,7 @@ const QJsonArray &Manager::basicRepoInfo() const
/*!
* \brief Returns package information for the specified selection of packages.
*/
const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full)
const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full) const
{
QJsonArray pkgInfos;
for(const auto &pkgSelJsonVal : pkgSelection) {
@ -426,9 +482,12 @@ const QJsonArray Manager::packageInfo(const QJsonArray &pkgSelection, bool full)
const QJsonArray &Manager::groupInfo() const
{
if(m_groupInfo.empty()) {
m_groupInfo << localDataBase().groupInfo();
for(const auto &db : m_syncDbs) {
m_groupInfo << db.second.groupInfo();
QMutexLocker locker(&m_groupInfoMutex);
if(m_groupInfo.empty()) {
m_groupInfo << localDataBase().groupInfo();
for(const auto &db : m_syncDbs) {
m_groupInfo << db.second.groupInfo();
}
}
}
return m_groupInfo;
@ -468,103 +527,19 @@ AlpmDataBase &Manager::dataBaseByName(const QString &dbName)
* - 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
void Manager::invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallback callback) 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;
new UpdateLookup(*this, request, callback); // this object will delete itself
}
/*!
* \brief Checks the specified database for upgrades.
*
* Appropriate upgrade sources will be determined automatically.
* Appropriate upgrade sources will be determined automatically; does not check the AUR.
*/
const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db) const
const UpdateLookupResults<AlpmPackage> Manager::checkForUpgrades(const AlpmDataBase &db) const
{
UpdateLookupResults results;
UpdateLookupResults<AlpmPackage> results;
QList<const AlpmDataBase *> syncDbSel;
for(const auto &syncDbName : db.upgradeSources()) {
try {
@ -577,11 +552,4 @@ const UpdateLookupResults Manager::checkForUpgrades(const AlpmDataBase &db) cons
return results;
}
//void Manager::addTask()
//{
// QUuid uuid;
// while(m_tasks.find(uuid = QUuid::createUuid()) != m_tasks.cend());
// m_tasks.emplace(uuid);
//}
}

View File

@ -1,45 +1,52 @@
#ifndef ALPM_MANAGER_H
#define ALPM_MANAGER_H
#include "package.h"
#include "database.h"
#include "updatelookup.h"
#include "network/aurquery.h"
#include <alpm.h>
#include <QNetworkAccessManager>
#include <QJsonObject>
#include <QJsonArray>
#include <QFuture>
#include <QUuid>
#include <QMutex>
#include <map>
#include <unordered_map>
namespace PackageManagement {
class Config;
template<class Package> class UpdateLookupResults;
class Manager : QObject
class Manager
{
public:
Manager(const Config &config, QObject *parent = nullptr);
explicit Manager(const Config &config);
Manager(const Manager &other) = delete;
Manager(Manager &&other) = delete;
Manager &operator =(const Manager &other) = delete;
~Manager();
// configuration, signature level, etc
const Config &config() const;
int sigLevel() const;
void setSigLevel(int sigLevel);
int localFileSigLevel() const;
void setLocalFileSigLevel(int sigLevel);
const QString &cacheDir() const;
void parsePacmanConfig();
const QString &pacmanCacheDir() const;
static int parseSigLevel(const std::string &sigLevelStr = std::string());
static int parseUsage(const std::string &usageStr);
void applyPacmanConfig();
void applyRepoIndexConfig();
void unregisterSyncDataBases();
const AurQuery &aurQuery() const;
AurQuery &aurQuery();
// package lookup
AlpmPackage packageFromSyncDataBase(const QString &dbName, const QString &pkgName);
AlpmPackage packageFromSyncDataBase(const QString &dbName, const QString &pkgName) const;
AlpmPackage packageFromSyncDataBases(const char *name) const;
AlpmOwnershipPackage packageFromFile(const char *fileName, bool verifyIntegrity = false);
bool isPackageIgnored(AlpmPackage package) const;
@ -51,18 +58,14 @@ public:
const std::map<QString, AlpmDataBase> &syncDataBases() const;
const AlpmDataBase &dataBaseByName(const QString &dbName) const;
AlpmDataBase &dataBaseByName(const QString &dbName);
const UpdateLookupResults checkForUpgrades(const AlpmDataBase &db) const;
// asynchronous tasks
// void addTask();
const UpdateLookupResults<AlpmPackage> checkForUpgrades(const AlpmDataBase &db) const;
// JSON serialization, handling JSON requests
QJsonObject basicRepoInfo(AlpmDataBase db, const QString &name, const QString &desc = QString()) const;
const QJsonArray &basicRepoInfo() const;
const QJsonArray packageInfo(const QJsonArray &pkgSelection, bool full = true);
const QJsonArray packageInfo(const QJsonArray &pkgSelection, bool full = true) const;
const QJsonArray &groupInfo() const;
QJsonObject invokeUpgradeLookup(const QJsonObject &request) const;
const UpdateLookupResults checkForUpgrades(const AlpmDataBase &db, const QJsonValue &syncDbsValue) const;
void invokeUpgradeLookup(const QJsonObject &request, UpdateLookupCallback callback) const;
private:
void cleanup();
@ -71,16 +74,27 @@ private:
alpm_handle_t *m_handle;
int m_sigLevel;
int m_localFileSigLevel;
QString m_cacheDir;
QString m_pacmanCacheDir;
QNetworkAccessManager m_networkAccessManager;
AurQuery m_aur;
mutable AlpmDataBase m_localDb;
mutable QMutex m_localDbMutex;
mutable std::map<QString, AlpmDataBase> m_syncDbs;
mutable QJsonArray m_basicRepoInfo;
mutable QJsonArray m_packageInfo;
mutable QMutex m_basicRepoInfoMutex;
mutable QJsonArray m_groupInfo;
mutable QMutex m_groupInfoMutex;
};
/*!
* \brief Returns the configuration of the manager.
* \remarks The configuration has been specified when constructing the manager.
*/
inline const Config &Manager::config() const
{
return m_config;
}
/*!
* \brief Returns the signature level.
*
@ -138,12 +152,22 @@ inline bool Manager::isPackageIgnored(AlpmPackage package) const
}
/*!
* \brief Returns the cache directory.
* \brief Returns the Pacman cache directory.
* \remarks This is read from the Pacman configuration.
*/
inline const QString &Manager::cacheDir() const
inline const QString &Manager::pacmanCacheDir() const
{
return m_cacheDir;
return m_pacmanCacheDir;
}
inline const AurQuery &Manager::aurQuery() const
{
return m_aur;
}
inline AurQuery &Manager::aurQuery()
{
return m_aur;
}
}

170
alpm/mingwbundle.cpp Normal file
View File

@ -0,0 +1,170 @@
#include "mingwbundle.h"
#include "manager.h"
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/misc/memory.h>
#include <KTar>
#include <QFile>
#include <QtConcurrent/QtConcurrent>
#include <string>
#include <iostream>
using namespace std;
namespace PackageManagement {
const string prefix("mingw-w64-");
MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages) :
m_manager(manager)
{
string missing;
for(const auto &pkgName : packages) {
bool found = false;
for(const auto &syncDb : manager.syncDataBases()) {
if(auto pkg = syncDb.second.package(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data())) {
if(missing.empty()) {
m_packages.emplace_back(syncDb.second, pkg);
}
addDependencies(pkg);
found = true;
break;
}
}
if(!found) {
missing.push_back(' ');
missing.append(pkgName);
}
}
if(!missing.empty()) {
throw runtime_error("The following packages can not be found:" + missing);
} else {
cerr << "Adding the following packages:";
for(const auto &pkg : m_packages) {
cerr << ' ' << pkg.second.name();
}
cerr << endl;
}
}
enum class RelevantFileType
{
Binary,
Translation
};
enum class RelevantFileArch
{
x86_64,
i686
};
struct RelevantFile
{
RelevantFile(const KArchiveFile *file, const RelevantFileType type, const RelevantFileArch arch) :
file(file),
fileType(type),
arch(arch)
{}
const KArchiveFile *file;
RelevantFileType fileType;
RelevantFileArch arch;
};
struct PkgFileInfo
{
PkgFileInfo(const QString &path) : path(path), failure(false) {}
QString path;
unique_ptr<KTar> archive;
list<RelevantFile> relevantFiles;
bool failure;
};
void getFiles(PkgFileInfo &pkgFileInfo)
{
pkgFileInfo.archive = make_unique<KTar>(pkgFileInfo.path);
if(pkgFileInfo.archive->open(QIODevice::ReadOnly)) {
static const pair<RelevantFileArch, QString> roots[] = {
make_pair(RelevantFileArch::x86_64, QStringLiteral("/usr/x86_64-w64-mingw32")),
make_pair(RelevantFileArch::i686, QStringLiteral("/usr/i686-w64-mingw32"))
};
for(const auto &root : roots) {
const auto *rootEntry = pkgFileInfo.archive->directory()->entry(root.second);
if(rootEntry && rootEntry->isDirectory()) {
const auto *rootDir = static_cast<const KArchiveDirectory *>(rootEntry);
const auto *binEntry = rootDir->entry(QStringLiteral("bin"));
if(binEntry && binEntry->isDirectory()) {
const auto *binDir = static_cast<const KArchiveDirectory *>(binEntry);
for(const auto &entryName : binDir->entries()) {
if(entryName.endsWith(QLatin1String(".exe")) || entryName.endsWith(QLatin1String(".dll"))) {
if(const auto *entry = binDir->entry(entryName)) {
if(entry->isFile()) {
const auto *binFile = static_cast<const KArchiveFile *>(entry);
pkgFileInfo.relevantFiles.emplace_back(binFile, RelevantFileType::Binary, root.first);
cerr << entryName.toLocal8Bit().data() << endl;
}
}
}
}
}
}
}
} else {
pkgFileInfo.failure = true;
}
}
void MingwBundle::createBundle(const string &path) const
{
list<PkgFileInfo> pkgFiles;
for(const auto &entry : m_packages) {
QString pkgFile;
if(!entry.first.packagesDirectory().isEmpty()) {
pkgFile = QStringLiteral("%1/%2").arg(entry.first.packagesDirectory(), QString::fromLocal8Bit(entry.second.fileName()));
}
if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) {
if(!m_manager.pacmanCacheDir().isEmpty()) {
pkgFile = QStringLiteral("%1/%2").arg(m_manager.pacmanCacheDir(), QString::fromLocal8Bit(entry.second.fileName()));
}
if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) {
throw runtime_error("The package file " + string(entry.second.fileName()) + " can't be found.");
// TODO: download package from mirror
}
}
pkgFiles.emplace_back(pkgFile);
}
QtConcurrent::blockingMap(pkgFiles, getFiles);
}
void MingwBundle::addDependencies(const AlpmPackage &pkg)
{
string missing;
for(const auto &depInfo : pkg.dependencies()) {
bool found = false;
for(const auto &syncDb : m_manager.syncDataBases()) {
if(auto pkg = syncDb.second.package(depInfo->name)) {
if(missing.empty()) {
m_packages.emplace_back(syncDb.second, pkg);
}
addDependencies(pkg);
found = true;
break;
}
}
if(!found) {
missing.push_back(' ');
missing.append(depInfo->name);
}
}
if(!missing.empty()) {
throw runtime_error("The following dependencies of the " + string(pkg.name()) + " package can not be resolved:" + missing);
}
}
} // namespace PackageManagement

31
alpm/mingwbundle.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef PACKAGEMANAGEMENT_MINGWBUNDLE_H
#define PACKAGEMANAGEMENT_MINGWBUNDLE_H
#include "package.h"
#include "database.h"
#include <c++utilities/application/argumentparser.h>
#include <QList>
namespace PackageManagement {
class Manager;
class MingwBundle
{
public:
MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages);
void createBundle(const std::string &path) const;
private:
void addDependencies(const AlpmPackage &pkg);
const Manager &m_manager;
std::list<std::pair<const AlpmDataBase &, AlpmPackage> > m_packages;
};
} // namespace PackageManagement
#endif // PACKAGEMANAGEMENT_MINGWBUNDLE_H

View File

@ -11,10 +11,93 @@ using namespace ChronoUtilities;
namespace PackageManagement {
/*!
* \cond
*/
inline QString qstr(const char *str)
{
return QString::fromLocal8Bit(str);
}
inline QJsonArray qjarry(StringList list)
{
QJsonArray jsonArray;
for(const char *str : list) {
jsonArray << qstr(str);
}
return jsonArray;
}
inline 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;
}
inline QJsonArray qjarry(_alpm_pkgvalidation_t validation)
{
QJsonArray jsonArray;
if(validation & 0x1) {
jsonArray << QStringLiteral("none");
}
if(validation & 0x2) {
jsonArray << QStringLiteral("MD5");
}
if(validation & 0x4) {
jsonArray << QStringLiteral("SHA256");
}
if(validation & 0x8) {
jsonArray << QStringLiteral("signature");
}
if(jsonArray.empty()) {
jsonArray << QStringLiteral("unknown");
}
return jsonArray;
}
/*!
* \endcond
*/
/*!
* \brief The PackageVersion class helps parsing package versions.
*/
/*!
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
*/
PackageVersion::PackageVersion(const QString &versionStr) :
PackageVersion(versionStr.toLocal8Bit().data())
{}
/*!
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
*/
@ -158,10 +241,12 @@ AurPackage::AurPackage(const QJsonValue &aurJsonValue) :
QJsonObject obj = aurJsonValue.toObject();
m_id = obj.value(QStringLiteral("ID")).toInt(-1);
m_categoryId = obj.value(QStringLiteral("CategoryID")).toInt(-1);
m_name = obj.value(QStringLiteral("Name")).toString();
m_version = obj.value(QStringLiteral("Version")).toString();
m_description = obj.value(QStringLiteral("Description")).toString();
m_upstreamUrl = obj.value(QStringLiteral("URL")).toString();
m_votes = obj.value(QStringLiteral("NumVotes")).toInt(0);
m_outOfDate = obj.value(QStringLiteral("OutOfDate")).toInt() != 0;
m_outOfDate = DateTime::fromTimeStamp(obj.value(QStringLiteral("OutOfDate")).toInt());
m_maintainer = obj.value(QStringLiteral("Maintainer")).toString();
m_firstSubmitted = DateTime::fromTimeStamp(obj.value(QStringLiteral("FirstSubmitted")).toInt());
m_lastModified = DateTime::fromTimeStamp(obj.value(QStringLiteral("LastModified")).toInt());
@ -169,87 +254,65 @@ AurPackage::AurPackage(const QJsonValue &aurJsonValue) :
m_tarUrl = obj.value(QStringLiteral("URLPath")).toString();
}
/*!
* \brief Returns basic information about the packages as JSON object.
*/
QJsonObject AurPackage::basicInfo(bool includeRepoAndName) const
{
QJsonObject packageInfo;
if(includeRepoAndName) {
packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur"));
packageInfo.insert(QStringLiteral("name"), name());
}
packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("ver"), version());
packageInfo.insert(QStringLiteral("desc"), description());
packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("flagdate"), outOfDate().isNull() ? QStringLiteral("none") : qstr(outOfDate().toString().data()));
return packageInfo;
}
/*!
* \brief Returns full information about the package as JSON object.
*/
QJsonObject AurPackage::fullInfo(bool includeRepoAndName) const
{
QJsonObject packageInfo;
if(includeRepoAndName) {
packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur"));
packageInfo.insert(QStringLiteral("name"), name());
}
packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("ver"), version());
packageInfo.insert(QStringLiteral("desc"), description());
packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("idate"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("isize"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("url"), upstreamUrl());
packageInfo.insert(QStringLiteral("lic"), license());
packageInfo.insert(QStringLiteral("grp"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("prov"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("optd"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("deps"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("requ"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("optf"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("conf"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("repl"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("pack"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("expl"), QStringLiteral("n.a."));
packageInfo.insert(QStringLiteral("scri"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("sig"), QStringLiteral("TODO"));
packageInfo.insert(QStringLiteral("file"), QStringLiteral("n.a."));
return packageInfo;
}
/*!
* \brief The AlpmPackage class helps getting information about ALPM packages. It allows to convert the
* information to JSON objects used by the network classes and the web interface.
*/
/*!
* \cond
*/
inline QString qstr(const char *str)
{
return QString::fromLocal8Bit(str);
}
inline QJsonArray qjarry(StringList list)
{
QJsonArray jsonArray;
for(const char *str : list) {
jsonArray << qstr(str);
}
return jsonArray;
}
inline 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;
}
inline QJsonArray qjarry(_alpm_pkgvalidation_t validation)
{
QJsonArray jsonArray;
if(validation & 0x1) {
jsonArray << QStringLiteral("none");
}
if(validation & 0x2) {
jsonArray << QStringLiteral("MD5");
}
if(validation & 0x4) {
jsonArray << QStringLiteral("SHA256");
}
if(validation & 0x8) {
jsonArray << QStringLiteral("signature");
}
if(jsonArray.empty()) {
jsonArray << QStringLiteral("unknown");
}
return jsonArray;
}
/*!
* \endcond
*/
/*!
* \brief Returns basic information about the packages as JSON object.
*/
@ -264,6 +327,7 @@ QJsonObject AlpmPackage::basicInfo(bool includeRepoAndName) const
packageInfo.insert(QStringLiteral("ver"), qstr(version()));
packageInfo.insert(QStringLiteral("desc"), qstr(description()));
packageInfo.insert(QStringLiteral("bdate"), qstr(buildDate().toString().c_str()));
packageInfo.insert(QStringLiteral("flagdate"), QStringLiteral("n.a."));
return packageInfo;
}

View File

@ -11,6 +11,7 @@
#include <string>
#include <QString>
#include <QHash>
QT_BEGIN_NAMESPACE
class QJsonObject;
@ -43,6 +44,7 @@ enum class PackageVersionPartComparsion
class PackageVersion
{
public:
PackageVersion(const QString &versionStr);
PackageVersion(const char *versionStr);
static PackageVersionPartComparsion compareParts(const QString &part1, const QString &part2);
@ -60,39 +62,49 @@ public:
AurPackage(const QJsonValue &aurJsonValue);
bool isValid() const;
bool hasFullInfo() const;
int id() const;
int categoryId() const;
const QString &name() const;
const QString &version() const;
template<class Package>
PackageVersionComparsion compareVersion(const Package &syncPackage) const;
const QString &description() const;
const QString &upstreamUrl() const;
int votes() const;
bool isOutOfDate() const;
ChronoUtilities::DateTime outOfDate() const;
const QString &maintainer() const;
ChronoUtilities::DateTime firstSubmitted() const;
ChronoUtilities::DateTime lastModified() const;
const QString &license() const;
const QString &tarUrl() const;
// JSON serialization
QJsonObject basicInfo(bool includeRepoAndName) const;
QJsonObject fullInfo(bool includeRepoAndName = false) const;
private:
bool m_fullInfo;
int m_id;
int m_categoryId;
QString m_name;
QString m_version;
QString m_description;
QString m_upstreamUrl;
int m_votes;
bool m_outOfDate;
ChronoUtilities::DateTime m_outOfDate;
QString m_maintainer;
ChronoUtilities::DateTime m_firstSubmitted;
ChronoUtilities::DateTime m_lastModified;
QString m_license;
QString m_tarUrl;
};
/*!
* \brief Constructs a empty, invalid AUR package.
*/
inline AurPackage::AurPackage() :
m_fullInfo(false),
m_id(-1),
m_categoryId(-1),
m_votes(-1),
@ -107,6 +119,14 @@ inline bool AurPackage::isValid() const
return m_id >= 0;
}
/*!
* \brief Returns an indication whether full package info is available.
*/
inline bool AurPackage::hasFullInfo() const
{
return m_fullInfo;
}
/*!
* \brief Returns the ID of the package in the AUR.
*/
@ -131,6 +151,20 @@ inline const QString &AurPackage::name() const
return m_name;
}
/*!
* \brief Returns the version of the package.
*/
inline const QString &AurPackage::version() const
{
return m_version;
}
template<class Package>
inline PackageVersionComparsion AurPackage::compareVersion(const Package &syncPackage) const
{
return PackageVersion(version()).compare(PackageVersion(syncPackage.version()));
}
/*!
* \brief Returns the description of the package.
*/
@ -158,7 +192,7 @@ inline int AurPackage::votes() const
/*!
* \brief Returns wheter the package is flagged as out-of-date.
*/
inline bool AurPackage::isOutOfDate() const
inline ChronoUtilities::DateTime AurPackage::outOfDate() const
{
return m_outOfDate;
}
@ -209,12 +243,14 @@ public:
AlpmPackage(alpm_pkg_t *package = nullptr);
// package properties
const alpm_pkg_t *ptr() const;
alpm_pkg_t *ptr();
bool hasInstallScript() const;
const char *fileName() const;
const char *name() const;
const char *version() const;
PackageVersionComparsion compareVersion(const AlpmPackage &syncPackage) const;
template<class Package>
PackageVersionComparsion compareVersion(const Package &syncPackage) const;
alpm_pkgfrom_t origin() const;
const char *description() const;
const char *upstreamUrl() const;
@ -256,6 +292,11 @@ protected:
alpm_pkg_t *m_ptr;
};
inline uint qHash(const AlpmPackage &key)
{
return qHash(key.ptr());
}
/*!
* \brief Constructs a new package instance for the specified ALPM \a package.
*/
@ -263,6 +304,11 @@ inline AlpmPackage::AlpmPackage(alpm_pkg_t *package) :
m_ptr(package)
{}
inline const alpm_pkg_t *AlpmPackage::ptr() const
{
return m_ptr;
}
inline alpm_pkg_t *AlpmPackage::ptr()
{
return m_ptr;
@ -293,7 +339,8 @@ inline const char *AlpmPackage::version() const
*
* This method distinguishes between software upgrades and package releases. See Alpm::PackageVersionComparsion enum.
*/
inline PackageVersionComparsion AlpmPackage::compareVersion(const AlpmPackage &syncPackage) const
template<class Package>
inline PackageVersionComparsion AlpmPackage::compareVersion(const Package &syncPackage) const
{
return PackageVersion(version()).compare(PackageVersion(syncPackage.version()));
}

202
alpm/resolvebuildorder.cpp Normal file
View File

@ -0,0 +1,202 @@
#include "resolvebuildorder.h"
#include "manager.h"
#include "config.h"
#include <c++utilities/misc/memory.h>
#include <iostream>
#include <sstream>
using namespace std;
using namespace ApplicationUtilities;
namespace PackageManagement {
class TaskInfo
{
public:
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
const QString &name() const;
const QList<TaskInfo *> deps() const;
void addDep(TaskInfo *dep);
bool isDone() const;
bool isVisited() const;
bool isOnlyDependency() const;
void add(QList<TaskInfo *> &results);
static void addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results);
static TaskInfo *find(const QList<TaskInfo *> &tasks, const QString &name);
private:
QString m_name;
QList<TaskInfo *> m_deps;
bool m_done;
bool m_visited;
bool m_onlyDep;
};
inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInfo *> &deps) :
m_name(name),
m_deps(deps),
m_done(false),
m_visited(false),
m_onlyDep(onlyDependency)
{}
inline const QString &TaskInfo::name() const
{
return m_name;
}
inline const QList<TaskInfo *> TaskInfo::deps() const
{
return m_deps;
}
inline void TaskInfo::addDep(TaskInfo *dep)
{
m_deps << dep;
}
inline bool TaskInfo::isDone() const
{
return m_done;
}
inline bool TaskInfo::isVisited() const
{
return m_visited;
}
inline bool TaskInfo::isOnlyDependency() const
{
return m_onlyDep;
}
void TaskInfo::add(QList<TaskInfo *> &results)
{
if(!m_done) {
if(m_visited) {
throw *this; // cyclic dependency
} else {
m_visited = true;
}
for(auto *dep : m_deps) {
dep->add(results);
}
m_done = true;
results << this;
}
}
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
{
for(auto *task : tasks) {
if(!task->m_done) {
task->add(results);
}
}
}
TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
{
for(auto *task : tasks) {
if(task->name() == name) {
return task;
}
}
return nullptr;
}
template<class ListType>
class DestroyList
{
public:
DestroyList(ListType &list) :
m_list(list)
{}
~DestroyList()
{
qDeleteAll(m_list);
}
private:
ListType &m_list;
};
BuildOrderResolver::BuildOrderResolver(const Manager &manager) :
m_manager(manager)
{}
QStringList BuildOrderResolver::resolve(const StringVector &packages) const
{
cerr << "Getting package information ..." << endl;
QList<TaskInfo *> tasks;
tasks.reserve(packages.size());
try {
// add a task for each specified package
for(const auto &pkgName : packages) {
tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
}
// find specified packages and their dependencies
for(auto *task : tasks) {
addDeps(tasks, task);
}
cerr << "Relevant packages: ";
for(const auto *task : tasks) {
cerr << task->name().toLocal8Bit().data() << ' ';
}
cerr << endl;
// topo sort
QList<TaskInfo *> results;
results.reserve(tasks.size());
try {
TaskInfo::addAll(tasks, results);
QStringList names;
names.reserve(results.size());
for(const auto *res : results) {
names << res->name();
}
return names;
} catch (const TaskInfo &cyclic) {
throw runtime_error("Can't resolve build order; the package " + cyclic.name().toStdString() + " is a cyclic dependency.");
}
} catch(...) {
qDeleteAll(tasks);
throw;
}
}
void BuildOrderResolver::addDeps(QList<TaskInfo *> &tasks, TaskInfo *task) const
{
if(const auto pkg = m_manager.packageFromSyncDataBases(task->name().toLocal8Bit().data())) {
for(auto dep : pkg.dependencies()) {
if(auto *depTask = addDep(tasks, dep->name)) {
task->addDep(depTask);
}
}
} else {
stringstream ss;
ss << "The package \"" << task->name().toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build";
throw runtime_error(ss.str());
}
}
TaskInfo *BuildOrderResolver::addDep(QList<TaskInfo *> &tasks, const char *depName) const
{
if(auto *task = TaskInfo::find(tasks, depName)) {
// we've already added a task for this dependency
return task;
} else {
// create new task
//task = new TaskInfo(QString::fromLocal8Bit(depName));
//tasks << task;
//return task;
return nullptr;
}
}
} // namespace PackageManagement

29
alpm/resolvebuildorder.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef PACKAGEMANAGEMENT_RESOLVEBUILDORDER_H
#define PACKAGEMANAGEMENT_RESOLVEBUILDORDER_H
#include <c++utilities/application/argumentparser.h>
#include <QString>
#include <QList>
namespace PackageManagement {
class Manager;
class TaskInfo;
class BuildOrderResolver
{
public:
BuildOrderResolver(const Manager &manager);
QStringList resolve(const ApplicationUtilities::StringVector &packages) const;
private:
void addDeps(QList<TaskInfo *> &tasks, TaskInfo *task) const;
TaskInfo *addDep(QList<TaskInfo *> &pkgInfos, const char *depName) const;
const Manager &m_manager;
};
} // namespace PackageManagement
#endif // PACKAGEMANAGEMENT_RESOLVEBUILDORDER_H

185
alpm/updatelookup.cpp Normal file
View File

@ -0,0 +1,185 @@
#include "updatelookup.h"
#include "manager.h"
#include "database.h"
#include "config.h"
#include <QNetworkReply>
#include <QtConcurrent/QtConcurrent>
#include <initializer_list>
using namespace std;
namespace PackageManagement {
UpdateLookup::UpdateLookup(const Manager &manager, const QJsonObject &request, const UpdateLookupCallback callback, QObject *parent) :
QObject(parent),
m_manager(manager),
m_request(request),
m_callback(callback),
m_db(nullptr),
m_syncDbsWatcher(new QFutureWatcher<UpdateLookupResults<AlpmPackage> >(this)),
m_aurWatcher(new QFutureWatcher<UpdateLookupResults<AurPackage> >(this)),
m_aurReply(nullptr),
m_sourcesAvailable(false)
{
QJsonArray errors;
const auto dbName = request.value(QStringLiteral("db")).toString();
try {
m_db = &manager.dataBaseByName(dbName);
} catch (const out_of_range &) {
errors << QStringLiteral("Database \"%1\" can not be found.").arg(dbName);
}
if(errors.isEmpty()) {
// invoke syncdb lookup
connect(m_syncDbsWatcher, &QFutureWatcher<UpdateLookupResults<AlpmPackage> >::finished, this, &UpdateLookup::lookupDone);
m_syncDbsWatcher->setFuture(QtConcurrent::run(this, &UpdateLookup::checkSyncDbs, request.value(QStringLiteral("syncdbs"))));
// invoke AUR lookup
if(m_db->upgradeSources().contains(QStringLiteral("aur"), Qt::CaseInsensitive) || request.value(QStringLiteral("aur")).toBool(false)) {
if(manager.config().isAurEnabled()) {
connect(&m_manager.aurQuery(), &AurQuery::packageInfoAvailable, this, &UpdateLookup::aurPackageInfoAvailable);
connect(m_aurWatcher, &QFutureWatcher<UpdateLookupResults<AurPackage> >::finished, this, &UpdateLookup::lookupDone);
if(!(m_aurReply = m_manager.aurQuery().requestPackageInfo(m_db->packageNames()))) {
aurPackageInfoAvailable(nullptr);
}
} else {
errors << QStringLiteral("The AUR is configured as upgrade source for the database \"%1\" but AUR queries are not enabled.").arg(dbName);
}
}
} else {
QJsonObject results;
results.insert(QStringLiteral("errors"), errors);
callback(move(results));
deleteLater();
}
}
UpdateLookupResults<AlpmPackage> UpdateLookup::checkSyncDbs(const QJsonValue &syncDbsValue)
{
UpdateLookupResults<AlpmPackage> results;
const auto &syncDbs = m_manager.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 << &(m_manager.localDataBase());
} else if(syncDbName == QLatin1String("aur")) {
continue; // the AUR is checked separately
} else {
try {
syncDbSel << &(syncDbs.at(syncDbName));
} catch(out_of_range &) {
results.warnings << QStringLiteral("The specified sync database \"%1\" can not be found.").arg(syncDbName);
}
}
}
} else {
for(const auto &syncDbName : m_db->upgradeSources()) {
if(syncDbName == QLatin1String("local")) {
syncDbSel << &(m_manager.localDataBase());
} else if(syncDbName == QLatin1String("aur")) {
continue; // the AUR is checked separately
} else {
try {
syncDbSel << &(syncDbs.at(syncDbName));
} catch(out_of_range &) {
results.warnings << QStringLiteral("The associated upgrade database \"%1\" can not be found.").arg(syncDbName);
}
}
}
}
m_db->checkForUpgrades(syncDbSel, results);
return results;
}
void UpdateLookup::aurPackageInfoAvailable(const QNetworkReply *reply)
{
// check whether the package info requested by THIS INSTANCE is available
if(m_aurReply == reply) {
m_aurWatcher->setFuture(QtConcurrent::run(this, &UpdateLookup::checkAur));
}
}
UpdateLookupResults<AurPackage> UpdateLookup::checkAur()
{
UpdateLookupResults<AurPackage> results;
m_db->checkForUpgrades(m_manager.aurQuery().packages(), results);
return results;
}
void UpdateLookup::lookupDone()
{
const auto *sender = this->sender();
if(sender == static_cast<QObject *>(m_syncDbsWatcher)) {
// add results from syncdb lookup
m_syncDbsResults = m_syncDbsWatcher->result();
if(!m_syncDbsResults.noSources) {
m_sourcesAvailable = true;
for(const auto pkg : m_syncDbsResults.newVersions) {
m_softwareUpdates << pkg.json();
}
for(const auto pkg : m_syncDbsResults.newReleases) {
m_packageOnlyUpdates << pkg.json();
}
for(const auto pkg : m_syncDbsResults.downgrades) {
m_downgrades << pkg.json();
}
for(const auto &warning : m_syncDbsResults.warnings) {
m_warnings << warning;
}
for(const auto &error : m_syncDbsResults.errors) {
m_errors << error;
}
}
} else if(sender == static_cast<QObject *>(m_aurWatcher)) {
// add results from AUR lookup
m_aurResults = m_aurWatcher->result();
if(!m_aurResults.noSources) {
m_sourcesAvailable = true;
for(const auto pkg : m_aurResults.newVersions) {
m_softwareUpdates << pkg.json();
}
for(const auto pkg : m_aurResults.newReleases) {
m_packageOnlyUpdates << pkg.json();
}
for(const auto pkg : m_aurResults.downgrades) {
m_downgrades << pkg.json();
}
for(const auto &warning : m_aurResults.warnings) {
m_warnings << warning;
}
for(const auto &error : m_aurResults.errors) {
m_errors << error;
}
}
}
// check whether everything is done
if(m_syncDbsWatcher->isFinished() && (!m_aurReply || m_aurWatcher->isFinished())) {
QJsonObject results;
// determine orphaned packages
for(const auto pkg : m_aurReply ? m_aurResults.orphaned.intersect(m_syncDbsResults.orphaned) : m_syncDbsResults.orphaned) {
m_orphanedPackages << pkg.basicInfo(true);
}
// add results to results QJsonObject
results.insert(QStringLiteral("softwareUpdates"), m_softwareUpdates);
results.insert(QStringLiteral("packageOnlyUpdates"), m_packageOnlyUpdates);
results.insert(QStringLiteral("downgrades"), m_downgrades);
results.insert(QStringLiteral("orphanedPackages"), m_orphanedPackages);
if(!m_sourcesAvailable) {
m_errors << QStringLiteral("No update sources associated for database \"%1\".").arg(QString::fromLocal8Bit(m_db->name()));
}
if(!m_warnings.isEmpty()) {
results.insert(QStringLiteral("warnings"), m_warnings);
}
if(!m_errors.isEmpty()) {
results.insert(QStringLiteral("errors"), m_errors);
}
m_callback(move(results));
// lookup done, delete this helper object
deleteLater();
}
}
} // namespace PackageManagement

145
alpm/updatelookup.h Normal file
View File

@ -0,0 +1,145 @@
#ifndef PACKAGEMANAGEMENT_UPDATELOOKUP_H
#define PACKAGEMANAGEMENT_UPDATELOOKUP_H
#include "package.h"
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QFutureWatcher>
#include <functional>
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
namespace PackageManagement {
class Manager;
class AlpmDataBase;
typedef std::function<void (const QJsonObject &&results)> UpdateLookupCallback;
template<class Package, class VersionType = QString>
class UpdateResult
{
public:
UpdateResult(const Package &package, const VersionType &previousVersion);
Package package;
VersionType previousVersion;
QJsonObject json() const;
};
template<class Package, class VersionType>
inline UpdateResult<Package, VersionType>::UpdateResult(const Package &package, const VersionType &previousVersion) :
package(package),
previousVersion(previousVersion)
{}
template<class Package, class VersionType>
QJsonObject UpdateResult<Package, VersionType>::json() const
{
QJsonObject obj;
obj.insert(QStringLiteral("pkg"), package.basicInfo(true));
obj.insert(QStringLiteral("prevVersion"), previousVersion);
return obj;
}
template<class Package>
inline UpdateResult<Package> makeUpdateResult(const Package &package, const QString &previousVersion)
{
return UpdateResult<Package>(package, previousVersion);
}
template<class Package>
inline UpdateResult<Package> makeUpdateResult(const Package &package, const char *previousVersion)
{
return UpdateResult<Package>(package, QString::fromLocal8Bit(previousVersion));
}
template<class Package>
class UpdateLookupResults
{
public:
UpdateLookupResults();
/*!
* \brief Indicates that there are no upgrade sources available.
*/
bool noSources;
/*!
* \brief Packages providing a software upgrade (new version).
*/
QList<UpdateResult<Package> > newVersions;
/*!
* \brief Package upgrades only (new release).
*/
QList<UpdateResult<Package> > newReleases;
/*!
* \brief Downgrades (older version in sync db).
*/
QList<UpdateResult<Package> > downgrades;
/*!
* \brief Orphaned packages (could not be found in any of the sync dbs).
*/
QSet<AlpmPackage> orphaned;
/*!
* \brief Warnings occured when checking for updates.
*/
QStringList warnings;
/*!
* \brief Errors occured when checking for updates.
*/
QStringList errors;
};
/*!
* \brief Constructs new update lookup results.
*/
template<class Package>
inline UpdateLookupResults<Package>::UpdateLookupResults() :
noSources(false)
{}
class UpdateLookup : public QObject
{
Q_OBJECT
public:
explicit UpdateLookup(const Manager &manager, const QJsonObject &request, const UpdateLookupCallback callback, QObject *parent = nullptr);
private slots:
UpdateLookupResults<AlpmPackage> checkSyncDbs(const QJsonValue &syncDbsValue);
void aurPackageInfoAvailable(const QNetworkReply *reply);
UpdateLookupResults<AurPackage> checkAur();
void lookupDone();
private:
const Manager &m_manager;
const QJsonObject m_request;
const UpdateLookupCallback m_callback;
const AlpmDataBase *m_db;
QFutureWatcher<UpdateLookupResults<AlpmPackage> > *m_syncDbsWatcher;
QFutureWatcher<UpdateLookupResults<AurPackage> > *m_aurWatcher;
UpdateLookupResults<AlpmPackage> m_syncDbsResults;
UpdateLookupResults<AurPackage> m_aurResults;
QNetworkReply *m_aurReply;
bool m_sourcesAvailable;
QJsonArray m_warnings;
QJsonArray m_errors;
QJsonArray m_softwareUpdates;
QJsonArray m_packageOnlyUpdates;
QJsonArray m_downgrades;
QJsonArray m_orphanedPackages;
};
} // namespace PackageManagement
#endif // PACKAGEMANAGEMENT_UPDATELOOKUP_H

View File

@ -1,6 +1,8 @@
#include "alpm/manager.h"
#include "alpm/utilities.h"
#include "alpm/config.h"
#include "alpm/resolvebuildorder.h"
#include "alpm/mingwbundle.h"
#include "network/server.h"
#include <c++utilities/application/argumentparser.h>
@ -9,6 +11,7 @@
#include <QCoreApplication>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace ApplicationUtilities;
@ -33,17 +36,34 @@ int main(int argc, char *argv[])
Config config;
config.loadFromConfigFile(configArgs);
config.loadFromArgs(configArgs);
// run websocket server
if(configArgs.serverArg.isPresent()) {
// setup ALPM
Manager alpmManager(config);
alpmManager.parsePacmanConfig();
// setup the server
if(find_if(parser.mainArguments().cbegin(), parser.mainArguments().cend(), [&configArgs] (const Argument *arg) {
return arg != &configArgs.helpArg && arg->isPresent();
}) != parser.mainArguments().cend()) {
// create app
QCoreApplication application(argc, argv);
Server server(alpmManager, config);
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
// run Qt loop
return application.exec();
// setup ALPM
Manager manager(config);
manager.applyPacmanConfig();
manager.applyRepoIndexConfig();
if(configArgs.serverArg.isPresent()) {
// setup the server
Server server(manager, manager.config());
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
// run Qt loop
return application.exec();
} else if(configArgs.buildOrderArg.isPresent()) {
BuildOrderResolver resolver(manager);
const QStringList results = resolver.resolve(configArgs.buildOrderArg.values());
// print results
cout << "Results: ";
for(const auto &pkgName : results) {
cout << pkgName.toLocal8Bit().data() << ' ';
}
cout << endl;
} else if(configArgs.mingwBundleArg.isPresent()) {
MingwBundle bundle(manager, configArgs.mingwBundleArg.values());
bundle.createBundle(configArgs.outputFileArg.values().front());
}
} else {
cout << "No command line arguments specified. See --help for available commands." << endl;
}

View File

@ -16,53 +16,105 @@ using namespace std;
namespace PackageManagement {
QUrl AurQuery::m_aur4RequestUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php"));
QUrl AurQuery::m_aurRpcUrl = QUrl(QStringLiteral("https://aur.archlinux.org/rpc.php"));
QUrl AurQuery::m_aurPkgbuildUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD"));
const char *requestTypeProp = "type";
const QString rpcRequestTypeKey(QStringLiteral("type"));
const QString rpcRequestTypeSuggest(QStringLiteral("suggest"));
const QString rpcRequestTypeMultiInfo(QStringLiteral("multiinfo"));
const QString rpcArgKey(QStringLiteral("arg"));
const QString rpcArgArray(QStringLiteral("arg[]"));
const QString pkgbuildRequestType(QString("pkgbuild"));
AurQuery::AurQuery(QNetworkAccessManager &networkAccessManager, QObject *parent) :
QObject(parent),
m_networkAccessManager(networkAccessManager)
{}
void AurQuery::requestSuggestions(const QString &phrase) const
QNetworkReply *AurQuery::requestSuggestions(const QString &phrase) const
{
auto url = m_aur4RequestUrl;
auto url = m_aurRpcUrl;
QUrlQuery query;
query.addQueryItem(QStringLiteral("type"), QStringLiteral("suggest"));
query.addQueryItem(QStringLiteral("arg"), phrase);
//auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
//connect(reply, &QNetworkReply::finished, this, &AurEnquiry::suggestionsReceived);
}
void AurQuery::requestPackageInfo(const QStringList &packageNames) const
{
auto url = m_aur4RequestUrl;
QUrlQuery query;
query.addQueryItem(QStringLiteral("type"), QStringLiteral("multiinfo"));
for(const auto &packageName : packageNames) {
query.addQueryItem(QStringLiteral("arg[]"), packageName);
}
query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeSuggest);
query.addQueryItem(rpcArgKey, phrase);
url.setQuery(query);
auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
reply->setProperty("type", QStringLiteral("packageinfo"));
connect(reply, &QNetworkReply::finished, this, &AurQuery::packageInfoReceived);
reply->setProperty(requestTypeProp, rpcRequestTypeSuggest);
connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived);
return reply;
}
void AurQuery::packageInfoReceived()
/*!
* \brief Requests package information for the specified packages from the AUR.
* \returns Returns the QNetworkReply used for the request.
* \remarks
* If \a forceUpdate is true, package information which has already been retrieved
* and is still cached is requested again. Otherwise these packages will not be
* requested again. If it turns out, that all packages are already cached, nullptr
* is returned in this case.
*/
QNetworkReply *AurQuery::requestPackageInfo(const QStringList &packageNames, bool forceUpdate) const
{
QUrlQuery query;
for(const auto &packageName : packageNames) {
if(forceUpdate || !m_packages.count(packageName)) {
query.addQueryItem(rpcArgArray, packageName);
}
}
if(query.isEmpty()) {
return nullptr;
} else {
auto url = m_aurRpcUrl;
query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeMultiInfo);
url.setQuery(query);
auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
reply->setProperty(requestTypeProp, rpcRequestTypeMultiInfo);
connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived);
return reply;
}
}
QNetworkReply *AurQuery::requestFullPackageInfo(const QString &package, bool forceUpdate) const
{
try {
const auto &pkg = m_packages.at(package);
if(pkg.hasFullInfo() && !forceUpdate) {
return nullptr;
}
} catch(const out_of_range &) {
}
auto url = m_aurPkgbuildUrl;
QUrlQuery query;
query.addQueryItem(QStringLiteral("h"), package);
url.setQuery(query);
auto *reply = m_networkAccessManager.get(QNetworkRequest(url));
reply->setProperty(requestTypeProp, pkgbuildRequestType);
connect(reply, &QNetworkReply::finished, this, &AurQuery::dataReceived);
return reply;
}
void AurQuery::dataReceived()
{
if(auto *reply = qobject_cast<QNetworkReply *>(sender())) {
if(reply->error() == QNetworkReply::NoError) {
QJsonParseError error;
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error);
QByteArray data = reply->readAll();
cerr << "AUR reply: " << data.data() << endl;
const QJsonDocument doc = QJsonDocument::fromJson(data, &error);
//const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error);
if(error.error == QJsonParseError::NoError) {
const auto results = doc.object().value(QStringLiteral("results")).toArray();
for(const auto &result : results) {
AurPackage package(result);
if(!package.name().isEmpty()) {
m_packages.emplace(package.name(), move(package));
}
const QString requestType = reply->property(requestTypeProp).toString();
if(requestType == rpcRequestTypeSuggest) {
emit suggestionsAvailable(reply, doc.array());
} else if(requestType == rpcRequestTypeMultiInfo) {
processPackageInfo(reply, doc.object().value(QStringLiteral("results")).toArray());
} else {
cerr << "Error: Reply has invalid type. (should never happen)" << endl;
}
emit packageInfoAvailable();
} else {
cerr << "Error: Unable to parse JSON received from AUR: " << error.errorString().toLocal8Bit().data() << endl;
cerr << "Error: Unable to parse JSON received from AUR: " << error.errorString().toLocal8Bit().data() << " at character " << error.offset << endl;
}
} else {
cerr << "Error: Unable to request data from AUR: " << reply->errorString().toLocal8Bit().data() << endl;
@ -70,5 +122,16 @@ void AurQuery::packageInfoReceived()
}
}
void AurQuery::processPackageInfo(const QNetworkReply *reply, const QJsonArray &results)
{
for(const auto &result : results) {
AurPackage package(result);
if(!package.name().isEmpty()) {
m_packages[package.name()] = move(package);
}
}
emit packageInfoAvailable(reply);
}
} // namespace Alpm

View File

@ -9,49 +9,58 @@
#include <vector>
QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager)
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
namespace PackageManagement {
class AurQuery : public QObject
{
Q_OBJECT
public:
AurQuery(QNetworkAccessManager &networkAccessManager, QObject *parent = nullptr);
static const QUrl aur4RequestUrl();
static void setAur4RequestUrl(const QUrl &aur4RequestUrl);
void requestSuggestions(const QString &phrase) const;
void requestPackageInfo(const QStringList &packageNames) const;
static const QUrl aurRpcUrl();
static void setAurRpcUrl(const QUrl &aurRpcUrl);
QNetworkReply *requestSuggestions(const QString &phrase) const;
QNetworkReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false) const;
QNetworkReply *requestFullPackageInfo(const QString &package, bool forceUpdate = false) const;
const std::map<QString, AurPackage> &packages() const;
std::map<QString, AurPackage> &packages();
signals:
void suggestionsAvailable(const QJsonArray &suggestions);
void packageInfoAvailable();
void suggestionsAvailable(const QNetworkReply *reply, const QJsonArray &suggestions);
void packageInfoAvailable(const QNetworkReply *reply);
private slots:
void packageInfoReceived();
void dataReceived();
private:
void processPackageInfo(const QNetworkReply *reply, const QJsonArray &results);
QNetworkAccessManager &m_networkAccessManager;
std::map<QString, AurPackage> m_packages;
static QUrl m_aurRequstUrl;
static QUrl m_aur4RequestUrl;
static QUrl m_aurRpcUrl;
static QUrl m_aurPkgbuildUrl;
};
inline const QUrl AurQuery::aur4RequestUrl()
inline const QUrl AurQuery::aurRpcUrl()
{
return m_aur4RequestUrl;
return m_aurRpcUrl;
}
inline void AurQuery::setAur4RequestUrl(const QUrl &aur4Url)
inline void AurQuery::setAurRpcUrl(const QUrl &aur4Url)
{
m_aur4RequestUrl = aur4Url;
m_aurRpcUrl = aur4Url;
}
inline const std::map<QString, AurPackage> &AurQuery::packages() const
{
return m_packages;
}
inline std::map<QString, AurPackage> &AurQuery::packages()
{
return m_packages;

View File

@ -13,7 +13,7 @@ using namespace std;
namespace Network {
Connection::Connection(PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent) :
Connection::Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent) :
QObject(parent),
m_socket(socket),
m_alpmManager(alpmManager),
@ -78,8 +78,8 @@ void Connection::handleQuery(const QJsonObject &obj)
m_groupInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_groupInfoUpdatesRequested);
sendResults(what, id, m_alpmManager.groupInfo());
} else if(what == QLatin1String("checkforupdates")) {
m_alpmManager.invokeUpgradeLookup(obj);
//m_alpmManager.invokeUpgradeLookup(obj, bind(&Connection::sendResult, this, what, id, placeholders::_1));
//sendResult(what, id, m_alpmManager.invokeUpgradeLookup(obj));
m_alpmManager.invokeUpgradeLookup(obj, bind(&Connection::sendResult, this, what, id, placeholders::_1));
//m_alpmManager.checkForUpgrades(obj, [this] (const QJsonObject &results) {
// sendResult(what, id, results);
//};

View File

@ -18,7 +18,7 @@ class Connection : public QObject
Q_OBJECT
public:
Connection(PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
Connection(const PackageManagement::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
private slots:
void processTextMessage(const QString &message);
@ -33,7 +33,7 @@ private:
void handleQuery(const QJsonObject &obj);
QWebSocket *m_socket;
PackageManagement::Manager &m_alpmManager;
const PackageManagement::Manager &m_alpmManager;
bool m_repoInfoUpdatesRequested;
bool m_groupInfoUpdatesRequested;

View File

@ -1,12 +0,0 @@
#include "query.h"
namespace Network {
Query::Query(QObject *parent) :
QObject(parent)
{
}
} // namespace Network

View File

@ -1,21 +0,0 @@
#ifndef NETWORK_QUERY_H
#define NETWORK_QUERY_H
#include <QObject>
namespace Network {
class Query : public QObject
{
public:
Query(QObject *parent = nullptr);
private:
QString m_what;
QString m_clientId;
};
} // namespace Network
#endif // NETWORK_QUERY_H

View File

@ -14,7 +14,7 @@ using namespace std;
namespace Network {
Server::Server(PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent) :
Server::Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent) :
QObject(parent),
m_server(new QWebSocketServer(QStringLiteral("Repository index server"),
config.serverInsecure() ? QWebSocketServer::NonSecureMode : QWebSocketServer::SecureMode,

View File

@ -22,7 +22,7 @@ class Server : public QObject
Q_OBJECT
public:
Server(PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent = nullptr);
Server(const PackageManagement::Manager &alpmManager, const PackageManagement::Config &config, QObject *parent = nullptr);
~Server();
signals:
@ -34,7 +34,7 @@ private slots:
private:
QWebSocketServer *m_server;
PackageManagement::Manager &m_alpmManager;
const PackageManagement::Manager &m_alpmManager;
};
}

View File

@ -1,50 +1,36 @@
{// ALPM related config
alpm: {
// the root directory for ALPM
//rootDir: "/",
{
"alpm": {
"rootDir": "/",
"dbPath": "/var/lib/pacman",
"pacmanConfigFile": "/etc/pacman.conf"
},
// the database directory for ALPM
//dbPath: "/var/lib/pacman/",
"aur": {
"enabled": true
},
// the pacman config file
//pacmanConfigFile: "/etc/pacman.conf"
},
"server": {
"listeningPort": 1234,
"certFile": "some.cert",
"keyFile": "some.key",
"insecure": false
},
// server related config
server: {
// the listening port
//listeningPort: 1234,
"repos": {
"fromPacmanConfig": true,
// the SSL certificate
//certFile: "some.cert",
// the private SSL key
//keyFile: "some.key",
// forces the server to run in insecure mode
//insecure: false
}
// repositories related config
repos: {
// indicates whether the repositories listed in
// the pacman config file should be loaded
//fromPacmanConfig: true,
// lists additional repos to be loaded
add: [
/* example repo entry
{name: "reponame",
dbpath: "path/to/database/file",
srcpath: "path/to/local/source/files",
pkgpath: "path/to/local/pkg/files",
servers: [
"https://some/mirror",
"https://another/mirror
]
}
*/
]
}
"add": [
{"name": "examplerepo",
"dataBaseFile": "path/to/database/file",
"sourcesDir": "path/to/local/source/dir",
"packagesDir": "path/to/local/pkg/dir",
"upgradeSources": ["aur"],
"server": [
"https://some/mirror",
"https://another/mirror"
]
}
]
}
}

View File

@ -12,7 +12,7 @@ TEMPLATE = app
CONFIG += console # enables qDebug()
QT += core network websockets
QT += core network websockets concurrent KArchive
SOURCES += main.cpp \
alpm/manager.cpp \
@ -23,8 +23,10 @@ SOURCES += main.cpp \
network/connection.cpp \
alpm/group.cpp \
alpm/config.cpp \
network/query.cpp \
network/aurquery.cpp
network/aurquery.cpp \
alpm/updatelookup.cpp \
alpm/resolvebuildorder.cpp \
alpm/mingwbundle.cpp
HEADERS += \
alpm/manager.h \
@ -36,8 +38,10 @@ HEADERS += \
network/connection.h \
alpm/group.h \
alpm/config.h \
network/query.h \
network/aurquery.h
network/aurquery.h \
alpm/updatelookup.h \
alpm/resolvebuildorder.h \
alpm/mingwbundle.h
DISTFILES += \
README.md \
@ -58,9 +62,9 @@ DISTFILES += \
# libs and includepath
CONFIG(debug, debug|release) {
LIBS += -L../../ -lc++utilitiesd -lalpm
LIBS += -L../../ -lc++utilitiesd -lalpm -lKF5Archive
} else {
LIBS += -L../../ -lc++utilities -lalpm
LIBS += -L../../ -lc++utilities -lalpm -lKF5Archive
}
INCLUDEPATH += ../

View File

@ -91,6 +91,10 @@ span.glyphicon {
.navbar-fixed-top {
border-bottom: 5px solid #337ab7;
}
.navbar-form {
padding-left: 0px;
padding-right: 8px;
}
/*
* Package info

View File

@ -36,7 +36,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Repository index</a>
<!--<a class="navbar-brand collapsed" href="#">Repository index</a>-->
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
@ -80,7 +80,7 @@
<button type="button" class="close" onclick="repoindex.pageManager.hideErrors()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong>Errors</strong>
<strong>Notifications</strong>
<ul id="error_list"></ul>
</div>
<!-- repositories -->
@ -232,8 +232,9 @@
<tr><th>Package count</th><td id="repo_pkgcount"></td></tr>
<tr><th>Usage</th><td id="repo_usage"></td></tr>
<tr><th>Signature level</th><td id="repo_siglevel"></td></tr>
<tr><th>Upgrade sources</th><td id="repo_upgrade_sources"></td></tr>
<tr>
<th>Updates</th>
<th>Upgrades</th>
<td><span id="repo_checkforupdates"></span></td>
</tr>
</tbody>

View File

@ -1,5 +1,7 @@
var repoindex = (function(repoindex) {
repoindex.removeFilterButtonHtml = '<button type="button" class="close" onclick="repoindex.pageManager.applySearchTerm(null, false, true);" aria-label="Close"><span aria-hidden="true">&times;</span></button>';
repoindex.Entry = function(info, enabled) {
// basic initialization
this.info = info;
@ -82,15 +84,15 @@
// provide functions to apply filter and update UI
this.applyFilter = function() {
if(this.customSelection) {
this.filteredEntries = this.customSelection;
} else {
//if(this.customSelection) {
// this.filteredEntries = this.customSelection;
//} else {
if((this.filterName || this.filterRepos) && this.filterPred) {
this.filteredEntries = this.entries.filter(this.filterPred, this);
this.filteredEntries = this.relevantEntries().filter(this.filterPred, this);
} else {
this.filteredEntries = this.entries;
this.filteredEntries = this.relevantEntries();
}
}
//}
// update pagination (the pageSelected method defined above will be invoked here automatically)
this.pagination.entryCount = this.filteredEntries.length;
this.pagination.update();
@ -140,17 +142,16 @@
} else {
var entryText = this.customSelection ? (this.filteredEntries.length === 1 ? this.customSelectionName : this.customSelectionNamePlural) : (this.filteredEntries.length === 1 ? this.entryName : this.entryNamePlural);
if(this.filterDescr) {
var removeFilterButton = '<button type="button" class="close" onclick="repoindex.pageManager.applySearchTerm()" aria-label="Close"><span aria-hidden="true">&times;</span></button>';
if(this.filterName) {
return "<b>" + this.filteredEntries.length + "</b> " + entryText
+ containerText + " "
+ repoindex.escapeHtml(this.filterDescr) + " <i>"+ repoindex.escapeHtml(this.filterName) + "</i>."
+ removeFilterButton;
+ repoindex.removeFilterButtonHtml;
} else {
return "<b>" + this.filteredEntries.length + "</b> " + entryText
+ containerText + " "
+ repoindex.escapeHtml(this.filterDescr) + "."
+ removeFilterButton;
+ repoindex.removeFilterButtonHtml;
}
} else {
return "Showing <b>"

View File

@ -22,13 +22,13 @@
this.initTableRow = function() {
var values = [this.info.arch, this.info.repo, this.info.name, this.info.ver, this.info.desc, this.info.bdate, ""];
for(var i = 0; i < 7; ++i) {
this.rowElement.addCell(values[i]);
this.rowElement.addCell(repoindex.makeStr(values[i]));
}
};
this.initTableRow();
this.applyBasicInfo = function(info) {
this.applyBasicInfo = function(info, noUpdate) {
if(info.repo) this.info.repo = info.repo;
if(info.name) this.info.name = info.name;
if(info.arch) this.info.arch = info.arch;
@ -36,10 +36,12 @@
if(info.desc) this.info.desc = info.desc;
if(info.bdate) this.info.bdate = info.bdate;
this.info.basic = true;
this.updateTableRow();
if(!noUpdate) {
this.updateTableRow();
}
};
this.applyFullInfo = function(info) {
this.applyFullInfo = function(info, noUpdate) {
this.applyBasicInfo(info);
if(info.idate) this.info.idate = info.idate;
if(info.isize) this.info.isize = info.isize;
@ -60,7 +62,9 @@
if(info.file) this.info.file = info.file;
if(info.files) this.info.files = info.files;
this.info.full = true;
this.updateTableRow();
if(!noUpdate) {
this.updateTableRow();
}
};
};
@ -82,13 +86,13 @@
this.addEntry = function(repoName, packageName) {
var packageInfo = {
index: this.entries.length,
arch: "",
arch: undefined,
repo: repoName,
name: packageName,
version: "",
desc: "",
builddate: "",
flagdate: "",
version: undefined,
desc: undefined,
builddate: undefined,
flagdate: undefined,
received: false
};
this.entries.push(new PackageEntry(packageInfo));
@ -169,7 +173,7 @@
if(i.url) {
repoindex.setLink("pkg_url", i.url, i.url, window.open);
} else {
repoindex.setText("pkg_url", "none");
repoindex.setText("pkg_url", "unknown");
}
repoindex.setText("pkg_lic", repoindex.makeArray(i.lic));
repoindex.setPackageNames("pkg_grp", repoindex.pack(i.grp), repoindex.Pages.Groups);
@ -208,7 +212,15 @@
entry.applyFullInfo(info);
}
} else {
repoindex.pageManager.addError("Error: " + info.error);
var errorMsg;
switch(info.error) {
case "na":
errorMsg = "The server can't find the requested (full) package info."
break;
default:
errorMsg = "The server can't deliver the requested (full) package info."
};
repoindex.pageManager.addError("Error: " + errorMsg);
}
}
// set properties (again) to actually show applied info
@ -260,8 +272,9 @@
};
this.showMirrorsForIndex = function(entryIndex) {
if(entryIndex >= 0 && entryIndex < this.entries.length) {
var info = this.entries[entryIndex].info;
var entry = this.entryByIndex(entryIndex);
if(entry) {
var info = entry.info;
repoindex.setText("title_mirror_selection", "Mirrors for package <i>" + repoindex.escapeHtml(info.name) + "</i>", true);
var listMirrorSelection = document.getElementById("list_mirror_selection");
listMirrorSelection.wipeChildren();
@ -286,19 +299,29 @@
listMirrorSelection.appendChild(liElement);
}
mirrorsAvailable = 1;
} else if(info.repo === "local") {
mirrorsAvailable = -1;
}
}
if(mirrorsAvailable === 1) {
repoindex.setText("status_mirror_selection", "Select a mirror:");
} else if(mirrorsAvailable === -1) {
repoindex.setText("status_mirror_selection", "The package entry belongs to the <i>local</i> database. Hence there are no mirrors available.", true);
} else {
repoindex.setText("status_mirror_selection", "No mirrors available.");
}
$("#dlg_mirror_selection").modal("show");
}
if(mirrorsAvailable !== 1) {
// no mirrors available?
if(info.repo === "local" || info.repo === "LOCAL") {
// no surprise because its an package from the local database
mirrorsAvailable = -1;
} else if(info.repo === "aur" || info.repo === "AUR") {
// no surprise because its an AUR package
mirrorsAvailable = -2;
}
}
if(mirrorsAvailable === 1) {
repoindex.setText("status_mirror_selection", "Select a mirror:");
} else if(mirrorsAvailable === -1) {
repoindex.setText("status_mirror_selection", "The package belongs to the <i>local</i> database. Hence there are no mirrors available.", true);
} else if(mirrorsAvailable === -2) {
repoindex.setText("status_mirror_selection", "The package is from the <i>Arch Linux User Repository</i>. Hence there are no mirrors available.", true);
} else {
repoindex.setText("status_mirror_selection", "No mirrors available.");
}
$("#dlg_mirror_selection").modal("show");
};
this.showPackageNotFound = function(repo, name) {

View File

@ -234,7 +234,7 @@
};
// provide a function to apply an entered search term
this.applySearchTerm = function(searchTerm, exact) {
this.applySearchTerm = function(searchTerm, exact, reset) {
var mgr = undefined;
switch(this.currentPage) {
case repoindex.Pages.Packages:
@ -250,7 +250,9 @@
;
}
if(mgr) {
mgr.customSelection = undefined;
if(reset) {
mgr.customSelection = undefined;
}
mgr.filterName = searchTerm;
mgr.filterNameExact = exact;
mgr.filterDescr = searchTerm ? (exact ? "with the name" : "for the search term") : undefined;

View File

@ -175,6 +175,7 @@
repoindex.setText("repo_pkgcount", i.packages.length);
repoindex.setText("repo_usage", repoindex.makeArray(i.usage, ", "));
repoindex.setText("repo_siglevel", repoindex.makeArray(i.sigLevel, ", "));
repoindex.setText("repo_upgrade_sources", repoindex.makeArray(i.upgradeSources, ", "));
var invokeUpdateLookupParams = {repo: i.name, invoke: "updatelookup"};
repoindex.setDownloadButton("repo_checkforupdates", "check for updates", repoindex.makeHash(repoindex.Pages.Repositories, invokeUpdateLookupParams, true), function() {
repoindex.pageManager.repoManager.showAvailableUpdates(i.name);
@ -241,7 +242,15 @@
for(var i2 = 0; i2 < updates[i1].entries.length; ++i2) {
var newEntry = pkgMgr.createCustomEntry(updates[i1].color);
newEntry.info.index = updateEntries.length;
newEntry.applyBasicInfo(updates[i1].entries[i2]);
if(updates[i1].entries[i2].pkg) {
newEntry.applyBasicInfo(updates[i1].entries[i2].pkg, true);
if(updates[i1].entries[i2].prevVersion) {
newEntry.info.ver = updates[i1].entries[i2].prevVersion + " → " + (newEntry.info.ver ? newEntry.info.ver : "?");
}
newEntry.updateTableRow();
} else {
newEntry.applyBasicInfo(updates[i1].entries[i2]);
}
updateEntries.push(newEntry);
}
}
@ -251,14 +260,17 @@
}
}
// show updates via package manager
pkgMgr.filterName = undefined;
pkgMgr.customSelection = updateEntries;
pkgMgr.customSelectionName = "upgrade";
pkgMgr.customSelectionNamePlural = "upgrades";
var quandityInfo = repoindex.makeQuandityInfo(updates);
if(quandityInfo) {
pkgMgr.customSelectionInfoText = "Updates for the repository <i>" + repoindex.escapeHtml(repo) + "</i>: " + quandityInfo;
pkgMgr.customSelectionInfoText = "Updates for the repository <i>"
+ repoindex.escapeHtml(repo)
+ "</i>: " + quandityInfo
+ repoindex.removeFilterButtonHtml;
}
pkgMgr.filterName = repo;
pkgMgr.filterDescr = "for the repository"
pkgMgr.invalidate();
repoindex.pageManager.setPage(repoindex.Pages.Packages);

View File

@ -212,8 +212,8 @@
}
};
repoindex.makeStr = function(str) {
return str ? str : "";
repoindex.makeStr = function(str, na) {
return str ? str : (na ? na : "unknown");
};
repoindex.makeArray = function(array, separator) {
@ -234,7 +234,7 @@
return (sizeInByte / 1099511627776.0).toFixed(2) + " TiB";
}
} else {
return "";
return "unknown";
}
};