fixed bugs
This commit is contained in:
parent
2dd0294bb7
commit
920eddbeed
|
@ -39,3 +39,7 @@ Makefile*
|
|||
|
||||
# documentation
|
||||
/doc
|
||||
|
||||
# testing
|
||||
/testing
|
||||
/cert
|
||||
|
|
|
@ -52,7 +52,7 @@ private:
|
|||
const PackageOrigin m_origin;
|
||||
};
|
||||
|
||||
void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions)
|
||||
DatabaseError AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions)
|
||||
{
|
||||
QFileInfo pathInfo(databasePath());
|
||||
if(pathInfo.isDir()) {
|
||||
|
@ -71,7 +71,7 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
|||
if(descFile.open(QFile::ReadOnly)) {
|
||||
descData << descFile.readAll();
|
||||
} else {
|
||||
// TODO: error handling (can't open pkg file)
|
||||
return DatabaseError::UnableToOpenDescFile;
|
||||
}
|
||||
}
|
||||
if(!descData.isEmpty()) {
|
||||
|
@ -79,7 +79,7 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
|||
}
|
||||
dbDir.cdUp();
|
||||
} else {
|
||||
// TODO: error handling (can't enter pkg dir)
|
||||
return DatabaseError::UnableToEnterDirectory;
|
||||
}
|
||||
}
|
||||
} else if(pathInfo.isFile()) {
|
||||
|
@ -101,7 +101,7 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
|||
if(descEntry->isFile()) {
|
||||
descData << static_cast<const KArchiveFile *>(descEntry)->data();
|
||||
} else {
|
||||
// there shouldn't be any subdirs
|
||||
// there shouldn't be any subdirs anyways
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,22 +109,25 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
|||
descriptions << qMakePair(pkgDirName, descData);
|
||||
}
|
||||
} else {
|
||||
// there shouldn't be any files
|
||||
// there shouldn't be any files anyways
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: error handling (can't open sync db file)
|
||||
return DatabaseError::UnableToOpenArchive;
|
||||
}
|
||||
} else {
|
||||
// TODO: error handling
|
||||
return DatabaseError::NotFound;
|
||||
}
|
||||
return DatabaseError::NoError;
|
||||
}
|
||||
|
||||
AlpmPackageLoader::AlpmPackageLoader(AlpmDatabase *repository, PackageOrigin origin)
|
||||
AlpmPackageLoader::AlpmPackageLoader(AlpmDatabase *repository, PackageOrigin origin) :
|
||||
m_db(repository)
|
||||
{
|
||||
repository->loadDescriptions(m_descriptions);
|
||||
m_future = QtConcurrent::map(m_descriptions, LoadPackage(repository, origin));
|
||||
if((m_error = repository->loadDescriptions(m_descriptions)) == DatabaseError::NoError) {
|
||||
m_future = QtConcurrent::map(m_descriptions, LoadPackage(repository, origin));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -138,7 +141,7 @@ AlpmDatabase::AlpmDatabase(const QString &name, const QString &dbPath, Repositor
|
|||
m_sigLevel = sigLevel;
|
||||
}
|
||||
|
||||
AlpmPackageLoader *AlpmDatabase::init()
|
||||
AlpmPackageLoader *AlpmDatabase::internalInit()
|
||||
{
|
||||
// set description, determine origin
|
||||
PackageOrigin origin;
|
||||
|
@ -157,15 +160,16 @@ AlpmPackageLoader *AlpmDatabase::init()
|
|||
// wipe current packages
|
||||
wipePackages();
|
||||
|
||||
// initialization of packages is done concurrently via AlpmPackageLoader: ~ 4 sec
|
||||
// initialization of packages is done concurrently via AlpmPackageLoader
|
||||
return new AlpmPackageLoader(this, origin);
|
||||
|
||||
// without concurrency: ~ 12 sec
|
||||
// without concurrency
|
||||
//QList<QPair<QString, QList<QByteArray> > > descriptions;
|
||||
//loadDescriptions(descriptions);
|
||||
//for(const auto &description : descriptions) {
|
||||
// addPackageFromDescription(description.first, description.second, origin);
|
||||
//}
|
||||
//emit initialized();
|
||||
//return nullptr;
|
||||
}
|
||||
|
||||
|
@ -209,8 +213,9 @@ QNetworkRequest AlpmDatabase::filesDatabaseRequest()
|
|||
*/
|
||||
void AlpmDatabase::downloadDatabase(const QString &targetDir, bool filesDatabase)
|
||||
{
|
||||
QWriteLocker locker(lock());
|
||||
if(serverUrls().isEmpty()) {
|
||||
return;
|
||||
return; // no server URLs available
|
||||
}
|
||||
cerr << shchar << "Downloading " << (filesDatabase ? "files" : "regular") << " database for [" << name().toLocal8Bit().data() << "] from mirror " << serverUrls().front().toLocal8Bit().data() << " ..." << endl;
|
||||
QNetworkReply *reply = networkAccessManager().get(filesDatabase ? filesDatabaseRequest() : regularDatabaseRequest());
|
||||
|
@ -248,28 +253,42 @@ std::unique_ptr<Package> AlpmDatabase::emptyPackage()
|
|||
void AlpmDatabase::databaseDownloadFinished()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
bool filesDatabase = reply->property("filesDatabase").toBool();
|
||||
QReadLocker locker(lock());
|
||||
if(reply->error() == QNetworkReply::NoError) {
|
||||
QString newDatabasePath;
|
||||
cerr << "Downloaded database file for [" << name().toLocal8Bit().data() << "] successfully." << endl;
|
||||
QString newDatabasePath = m_downloadTargetDir % QChar('/') % name() % (filesDatabase ? QStringLiteral(".files") : QStringLiteral(".db"));
|
||||
newDatabasePath = m_downloadTargetDir % QChar('/') % name() % (filesDatabase ? QStringLiteral(".files") : QStringLiteral(".db"));
|
||||
if(QFile::exists(newDatabasePath)) {
|
||||
QString backupFile(newDatabasePath + QStringLiteral(".bak"));
|
||||
QFile::remove(backupFile);
|
||||
if(!QFile::rename(newDatabasePath, backupFile)) {
|
||||
cerr << "An IO error occured when storing database file for [" << name().toLocal8Bit().data() << "]: Unable to rename present database file." << endl;
|
||||
reply = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
locker.unlock();
|
||||
QFile outputFile(newDatabasePath);
|
||||
if(outputFile.open(QFile::WriteOnly) && outputFile.write(reply->readAll())) {
|
||||
outputFile.close();
|
||||
m_dbPath = newDatabasePath;
|
||||
init();
|
||||
{
|
||||
QWriteLocker locker(lock());
|
||||
m_dbPath = newDatabasePath;
|
||||
}
|
||||
initAsSoonAsPossible();
|
||||
} else {
|
||||
locker.relock();
|
||||
cerr << "An IO error occured when storing database file for [" << name().toLocal8Bit().data() << "]: Unable to create/write output file." << endl;
|
||||
}
|
||||
} else {
|
||||
cerr << "An error occured when dwonloading database file for [" << name().toLocal8Bit().data() << "]: " << reply->errorString().toLocal8Bit().data() << endl;
|
||||
if(filesDatabase && reply->error() == QNetworkReply::ContentNotFoundError) {
|
||||
cerr << "-> Attempting to download regular database file instead of files database file." << endl;
|
||||
locker.unlock();
|
||||
downloadDatabase(m_downloadTargetDir, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,21 +17,53 @@ class AlpmPackage;
|
|||
class AlpmDatabase;
|
||||
class LoadPackage;
|
||||
|
||||
enum class DatabaseError
|
||||
{
|
||||
NoError,
|
||||
NotFound,
|
||||
NoAccess,
|
||||
UnableToOpenArchive,
|
||||
UnableToOpenDescFile,
|
||||
UnableToEnterDirectory
|
||||
};
|
||||
|
||||
class AlpmPackageLoader : public PackageLoader
|
||||
{
|
||||
public:
|
||||
AlpmPackageLoader(AlpmDatabase *db, PackageOrigin origin);
|
||||
AlpmDatabase *database() const;
|
||||
DatabaseError error() const;
|
||||
|
||||
private:
|
||||
AlpmDatabase *const m_db;
|
||||
DatabaseError m_error;
|
||||
QList<QPair<QString, QList<QByteArray> > > m_descriptions;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the associated database.
|
||||
*/
|
||||
inline AlpmDatabase *AlpmPackageLoader::database() const
|
||||
{
|
||||
return m_db;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the error status.
|
||||
*/
|
||||
inline DatabaseError AlpmPackageLoader::error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
class AlpmDatabase : public Repository
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class AlpmPackageLoader;
|
||||
public:
|
||||
explicit AlpmDatabase(const QString &name, const QString &dbPath, RepositoryUsage usage, SignatureLevel sigLevel, uint32 index = invalidIndex, QObject *parent = nullptr);
|
||||
AlpmPackageLoader *init();
|
||||
AlpmPackageLoader *internalInit();
|
||||
|
||||
RepositoryType type() const;
|
||||
PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const;
|
||||
|
@ -44,9 +76,6 @@ public:
|
|||
void downloadDatabase(const QString &targetDir, bool filesDatabase = true);
|
||||
void refresh(const QString &targetDir);
|
||||
|
||||
signals:
|
||||
void initiated();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Package> emptyPackage();
|
||||
|
||||
|
@ -54,7 +83,7 @@ private slots:
|
|||
void databaseDownloadFinished();
|
||||
|
||||
private:
|
||||
void loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions);
|
||||
DatabaseError loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions);
|
||||
QNetworkRequest regularDatabaseRequest();
|
||||
QNetworkRequest filesDatabaseRequest();
|
||||
|
||||
|
|
|
@ -157,7 +157,8 @@ Config::Config() :
|
|||
m_websocketServerListeningAddr(QHostAddress::LocalHost),
|
||||
m_websocketServerListeningPort(1234),
|
||||
m_serverInsecure(false),
|
||||
m_reposFromPacmanConf(false),
|
||||
m_localEnabled(true),
|
||||
m_reposFromPacmanConfEnabled(false),
|
||||
m_aurEnabled(true),
|
||||
m_verbose(false),
|
||||
m_runServer(false)
|
||||
|
@ -252,7 +253,8 @@ void Config::loadFromConfigFile(const QString &configFilePath)
|
|||
m_serverKeyFile = serverObj.value(QStringLiteral("keyFile")).toString(m_serverKeyFile);
|
||||
m_serverInsecure = serverObj.value(QStringLiteral("insecure")).toBool(m_serverInsecure);
|
||||
auto reposObj = mainObj.value(QStringLiteral("repos")).toObject();
|
||||
m_reposFromPacmanConf = serverObj.value(QStringLiteral("fromPacmanConfig")).toBool(m_reposFromPacmanConf);
|
||||
m_localEnabled = reposObj.value(QStringLiteral("localEnabled")).toBool(m_localEnabled);
|
||||
m_reposFromPacmanConfEnabled = reposObj.value(QStringLiteral("fromPacmanConfig")).toBool(m_reposFromPacmanConfEnabled);
|
||||
for(const auto &repo : reposObj.value(QStringLiteral("add")).toArray()) {
|
||||
m_repoEntries << RepoEntry();
|
||||
m_repoEntries.back().load(repo);
|
||||
|
@ -323,7 +325,8 @@ void Config::loadFromArgs(const ConfigArgs &args)
|
|||
}
|
||||
|
||||
RepoEntry::RepoEntry() :
|
||||
m_sigLevel(SignatureLevel::UseDefault)
|
||||
m_sigLevel(SignatureLevel::UseDefault),
|
||||
m_ignored(false)
|
||||
{}
|
||||
|
||||
/*!
|
||||
|
@ -353,6 +356,7 @@ void RepoEntry::load(const QJsonValue &jsonValue)
|
|||
m_sigLevel = Manager::parseSigLevel(sigLevelValue.toString().toLocal8Bit().data());
|
||||
}
|
||||
m_maxDatabaseAge = TimeSpan::fromSeconds(obj.value(QStringLiteral("maxAge")).toDouble());
|
||||
m_ignored = obj.value(QStringLiteral("ignored")).toBool(m_ignored);
|
||||
}
|
||||
|
||||
} // namespace Alpm
|
||||
|
|
|
@ -68,6 +68,7 @@ public:
|
|||
const QStringList &upgradeSources() const;
|
||||
SignatureLevel sigLevel() const;
|
||||
ChronoUtilities::TimeSpan maxDatabaseAge() const;
|
||||
bool isIgnored() const;
|
||||
void load(const QJsonValue &jsonValue);
|
||||
|
||||
private:
|
||||
|
@ -79,6 +80,7 @@ private:
|
|||
QStringList m_upgradeSources;
|
||||
SignatureLevel m_sigLevel;
|
||||
ChronoUtilities::TimeSpan m_maxDatabaseAge;
|
||||
bool m_ignored;
|
||||
};
|
||||
|
||||
inline const QString &RepoEntry::name() const
|
||||
|
@ -121,6 +123,11 @@ inline ChronoUtilities::TimeSpan RepoEntry::maxDatabaseAge() const
|
|||
return m_maxDatabaseAge;
|
||||
}
|
||||
|
||||
inline bool RepoEntry::isIgnored() const
|
||||
{
|
||||
return m_ignored;
|
||||
}
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
|
@ -136,7 +143,8 @@ public:
|
|||
const QString &serverCertFile() const;
|
||||
const QString &serverKeyFile() const;
|
||||
bool serverInsecure() const;
|
||||
bool reposFromPacmanConf() const;
|
||||
bool isLocalDatabaseEnabled() const;
|
||||
bool areReposFromPacmanConfEnabled() const;
|
||||
const QList<RepoEntry> &repoEntries() const;
|
||||
bool isAurEnabled() const;
|
||||
bool isVerbose() const;
|
||||
|
@ -161,7 +169,8 @@ private:
|
|||
bool m_serverInsecure;
|
||||
|
||||
QList<RepoEntry> m_repoEntries;
|
||||
bool m_reposFromPacmanConf;
|
||||
bool m_localEnabled;
|
||||
bool m_reposFromPacmanConfEnabled;
|
||||
bool m_aurEnabled;
|
||||
bool m_verbose;
|
||||
bool m_runServer;
|
||||
|
@ -217,9 +226,14 @@ inline bool Config::serverInsecure() const
|
|||
return m_serverInsecure;
|
||||
}
|
||||
|
||||
inline bool Config::reposFromPacmanConf() const
|
||||
inline bool Config::isLocalDatabaseEnabled() const
|
||||
{
|
||||
return m_reposFromPacmanConf;
|
||||
return m_localEnabled;
|
||||
}
|
||||
|
||||
inline bool Config::areReposFromPacmanConfEnabled() const
|
||||
{
|
||||
return m_reposFromPacmanConfEnabled;
|
||||
}
|
||||
|
||||
inline const QList<RepoEntry> &Config::repoEntries() const
|
||||
|
|
393
alpm/manager.cpp
393
alpm/manager.cpp
|
@ -23,6 +23,7 @@
|
|||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace IoUtilities;
|
||||
|
@ -59,13 +60,16 @@ inline ostream &operator <<(ostream &stream, const QString &str)
|
|||
* \param rootdir Specifies the root directory.
|
||||
* \param dbpath Specifies the database directory.
|
||||
*/
|
||||
Manager::Manager(const Config &config) :
|
||||
Manager::Manager(const Config &config, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_config(config),
|
||||
m_writeCacheBeforeGone(true),
|
||||
m_sigLevel(defaultSigLevel),
|
||||
m_localFileSigLevel(SignatureLevel::UseDefault)
|
||||
{
|
||||
addLocalDatabase();
|
||||
if(config.isLocalDatabaseEnabled()) {
|
||||
addLocalDatabase();
|
||||
}
|
||||
if(config.isAurEnabled()) {
|
||||
m_userRepo = make_unique<UserRepository>();
|
||||
}
|
||||
|
@ -309,7 +313,7 @@ void Manager::addDataBasesFromPacmanConfig()
|
|||
} else if(dbName.startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
|
||||
cerr << shchar << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
|
||||
} else if(m_syncDbMap.count(dbName)) {
|
||||
cerr << shchar << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl;
|
||||
cerr << shchar << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database [" << scope.first << "]." << endl;
|
||||
} else {
|
||||
// read sig level and usage
|
||||
const auto &sigLevelStr = lastValue(scope.second, sigLevelKey);
|
||||
|
@ -320,10 +324,11 @@ void Manager::addDataBasesFromPacmanConfig()
|
|||
m_syncDbs.emplace_back(make_unique<AlpmDatabase>(dbName, findDatabasePath(dbName, false, false), usage, sigLevel, m_syncDbs.size() + 1));
|
||||
AlpmDatabase *emplacedDb = m_syncDbs.back().get();
|
||||
m_syncDbMap.emplace(dbName, emplacedDb);
|
||||
connectRepository(emplacedDb);
|
||||
cerr << shchar << "Added [" << dbName << "]" << endl;
|
||||
if(usage & RepositoryUsage::Upgrade) {
|
||||
if(localDatabase() && usage & RepositoryUsage::Upgrade) {
|
||||
// -> db is used to upgrade local database
|
||||
localDataBase()->upgradeSources() << emplacedDb;
|
||||
localDatabase()->upgradeSources() << emplacedDb;
|
||||
}
|
||||
|
||||
// add servers
|
||||
|
@ -352,9 +357,9 @@ void Manager::addDataBasesFromPacmanConfig()
|
|||
}
|
||||
}
|
||||
try {
|
||||
for(auto &scope : includedIni.data()) {
|
||||
if(scope.first.empty()) {
|
||||
for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
|
||||
for(auto &nestedScope : includedIni.data()) {
|
||||
if(nestedScope.first.empty()) {
|
||||
for(auto range = nestedScope.second.equal_range("Server"); range.first != range.second; ++range.first) {
|
||||
string url = range.first->second;
|
||||
findAndReplace<string>(url, "$repo", scope.first);
|
||||
findAndReplace<string>(url, "$arch", arch);
|
||||
|
@ -384,9 +389,18 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
|||
{
|
||||
// check whether an entry already exists, if not create a new one
|
||||
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
|
||||
AlpmDatabase *syncDb = nullptr;
|
||||
try {
|
||||
syncDb = m_syncDbMap.at(repoEntry.name());
|
||||
AlpmDatabase *syncDb;
|
||||
if(m_localDb && repoEntry.name() == QLatin1String("local")) {
|
||||
syncDb = m_localDb.get();
|
||||
} else {
|
||||
try {
|
||||
syncDb = m_syncDbMap.at(repoEntry.name());
|
||||
|
||||
} catch(const out_of_range &) {
|
||||
syncDb = nullptr;
|
||||
}
|
||||
}
|
||||
if(syncDb) {
|
||||
cerr << shchar << "Applying config for database [" << syncDb->name() << ']' << endl;
|
||||
if(!repoEntry.databasePath().isEmpty()) {
|
||||
syncDb->setDatabasePath(repoEntry.databasePath());
|
||||
|
@ -396,12 +410,21 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
|||
}
|
||||
syncDb->setPackagesDirectory(repoEntry.packageDir());
|
||||
syncDb->setSourcesDirectory(repoEntry.sourceDir());
|
||||
} catch(const out_of_range &) {
|
||||
if(!repoEntry.maxDatabaseAge().isNull()) {
|
||||
syncDb->setMaxPackageAge(repoEntry.maxDatabaseAge());
|
||||
}
|
||||
} else {
|
||||
if(repoEntry.name().compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
|
||||
} else if(repoEntry.name().startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
|
||||
cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
|
||||
} else {
|
||||
// repo name mustn't be empty
|
||||
if(repoEntry.name().isEmpty()) {
|
||||
cerr << shchar << "Warning: Ignoring empty repository entry in configuration file." << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine path of db file
|
||||
// -> currently just use the file from pacman dir, TODO: download syncdata base
|
||||
QString dbPath;
|
||||
|
@ -414,7 +437,7 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
|||
|
||||
// add sync db to internal map (use as index size + 1 because the local database has index 0)
|
||||
m_syncDbs.emplace_back(make_unique<AlpmDatabase>(repoEntry.name(), dbPath, RepositoryUsage::None, repoEntry.sigLevel(), m_syncDbs.size() + 1));
|
||||
syncDb = m_syncDbs.back().get();
|
||||
connectRepository(syncDb = m_syncDbs.back().get());
|
||||
m_syncDbMap.emplace(repoEntry.name(), syncDb);
|
||||
|
||||
syncDb->setSourcesDirectory(repoEntry.sourceDir());
|
||||
|
@ -453,55 +476,81 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
|||
* \brief Initiates all ALPM data bases.
|
||||
* \remarks Must be called, after all relevant sync databases have been added (eg. via applyPacmanConfig()).
|
||||
*/
|
||||
void Manager::initAlpmDataBases(bool computeRequiredBy)
|
||||
void Manager::initAlpmDataBases()
|
||||
{
|
||||
{
|
||||
// call the init method
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Initializing ALPM databases ... ";
|
||||
cerr.flush();
|
||||
// connect computeRequiredBy()
|
||||
if(m_config.runServer()) {
|
||||
if(localDatabase()) {
|
||||
QObject::connect(localDatabase(), &AlpmDatabase::initialized, bind(&Manager::computeRequiredBy, this, localDatabase()));
|
||||
}
|
||||
QList<PackageLoader *> loaders;
|
||||
loaders.reserve(m_syncDbMap.size() + 1);
|
||||
loaders << localDataBase()->init();
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
loaders << syncDbEntry.second->init();
|
||||
QObject::connect(syncDbEntry.second, &AlpmDatabase::initialized, bind(&Manager::computeRequiredBy, this, syncDbEntry.second));
|
||||
}
|
||||
for(auto *loader : loaders) {
|
||||
if(loader) {
|
||||
loader->future().waitForFinished();
|
||||
delete loader;
|
||||
}
|
||||
|
||||
// call the init method
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Initializing ALPM databases ... ";
|
||||
cerr.flush();
|
||||
}
|
||||
QList<AlpmPackageLoader *> loaders;
|
||||
loaders.reserve(m_syncDbMap.size() + (localDatabase() ? 1 : 0));
|
||||
if(localDatabase()) {
|
||||
loaders << static_cast<AlpmPackageLoader *>(localDatabase()->init());
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
loaders << static_cast<AlpmPackageLoader *>(syncDbEntry.second->init());
|
||||
}
|
||||
for(auto *loader : loaders) {
|
||||
if(loader) {
|
||||
loader->future().waitForFinished();
|
||||
switch(loader->error()) {
|
||||
case DatabaseError::NoError:
|
||||
break;
|
||||
case DatabaseError::NotFound:
|
||||
if(loader->database()->serverUrls().isEmpty()) {
|
||||
// there are no server URLs associated -> print error
|
||||
cerr << endl << shchar << "Unable to locate database file for ALPM database [" << loader->database()->name().toLocal8Bit().data() << "]" << endl;
|
||||
} else {
|
||||
// try to download the database from server
|
||||
loader->database()->downloadDatabase(m_config.storageDir() + QStringLiteral("/sync"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cerr << endl << shchar << "Unable to initialize ALPM database [" << loader->database()->name().toLocal8Bit().data() << "]" << endl;
|
||||
// TODO: print the cause of the problem
|
||||
}
|
||||
delete loader;
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
syncDbEntry.second->updateGroups();
|
||||
}
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
syncDbEntry.second->updateGroups();
|
||||
}
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "DONE" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// compute required-by and optional-for
|
||||
if(computeRequiredBy) {
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "Calculating required-by/optional-for ... ";
|
||||
cerr.flush();
|
||||
}
|
||||
QList<QFuture<void> > futures;
|
||||
futures.reserve(m_syncDbMap.size() + 1);
|
||||
futures << localDataBase()->computeRequiredBy(*this);
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
futures << syncDbEntry.second->computeRequiredBy(*this);
|
||||
}
|
||||
for(auto &future : futures) {
|
||||
future.waitForFinished();
|
||||
}
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "DONE" << endl;
|
||||
/*!
|
||||
* \brief Computes required-by and optional-for fields for the packages of the specified \a repo.
|
||||
* \remarks
|
||||
* - The computing is performed asynchronously so this method will return immidiately.
|
||||
* - Do not call this method if the specified \a repo is busy.
|
||||
*/
|
||||
void Manager::computeRequiredBy(Repository *repo)
|
||||
{
|
||||
// find relevant databases
|
||||
QList<Repository *> relevantDbs;
|
||||
if(repo == localDatabase()) {
|
||||
relevantDbs.reserve(1);
|
||||
relevantDbs << repo;
|
||||
} else {
|
||||
relevantDbs.reserve(m_syncDbs.size());
|
||||
for(auto &syncDb : m_syncDbs) {
|
||||
relevantDbs << syncDb.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
repo->computeRequiredBy(relevantDbs);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -552,6 +601,9 @@ void Manager::writeCache()
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Restores the cache for all repositories where caching makes sense.
|
||||
*/
|
||||
void Manager::restoreCache()
|
||||
{
|
||||
// could iterate through all repos and check isCachingUseful() but
|
||||
|
@ -570,22 +622,32 @@ void Manager::restoreCache()
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Removes outdated packages from the cache.
|
||||
* \remarks Currently only the AUR cache is affected.
|
||||
*/
|
||||
void Manager::cleanCache()
|
||||
{
|
||||
// currently clear only AUR cache
|
||||
if(userRepository()) {
|
||||
userRepository()->cleanOutdatedPackages();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Removes all cached packages.
|
||||
* \remarks Currently only the AUR cache is affected.
|
||||
*/
|
||||
void Manager::wipeCache()
|
||||
{
|
||||
// currently wipe only AUR cache
|
||||
if(userRepository()) {
|
||||
userRepository()->wipePackages();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Cleans and writes the cache.
|
||||
* \remarks Automatically called if automatic cache maintenance is enabled.
|
||||
*/
|
||||
void Manager::maintainCache()
|
||||
{
|
||||
cleanCache();
|
||||
|
@ -593,6 +655,65 @@ void Manager::maintainCache()
|
|||
writeCache();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether automatic updates are enabled.
|
||||
* \remarks If enabled, the ALPM databases will be updated frequently.
|
||||
*/
|
||||
bool Manager::isAutoUpdateEnabled() const
|
||||
{
|
||||
return m_updateTimer && m_updateTimer->isActive();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether automatic updates are enabled.
|
||||
* \sa isAutoUpdateEnabled()
|
||||
*/
|
||||
void Manager::setAutoUpdateEnabled(bool enabled)
|
||||
{
|
||||
if(isAutoCacheMaintenanceEnabled() != enabled) {
|
||||
if(enabled) {
|
||||
if(!m_updateTimer) {
|
||||
m_updateTimer = make_unique<QTimer>();
|
||||
m_updateTimer->setInterval(5 * 60 * 1000);
|
||||
QObject::connect(m_updateTimer.get(), &QTimer::timeout, bind(&Manager::updateAlpmDatabases, this));
|
||||
}
|
||||
m_updateTimer->start();
|
||||
} else {
|
||||
m_updateTimer->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Triggers updating ALPM databases with outdated packages.
|
||||
*/
|
||||
void Manager::updateAlpmDatabases()
|
||||
{
|
||||
if(localDatabase()) {
|
||||
if(localDatabase()->hasOutdatedPackages()) {
|
||||
localDatabase()->init();
|
||||
}
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
if(syncDbEntry.second->hasOutdatedPackages()) {
|
||||
syncDbEntry.second->refresh(m_config.storageDir() + QStringLiteral("/sync"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Triggers updating all ALPM databases (regardless whether packages are outdated or not).
|
||||
*/
|
||||
void Manager::forceUpdateAlpmDatabases()
|
||||
{
|
||||
if(localDatabase()) {
|
||||
localDatabase()->init();
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
syncDbEntry.second->refresh(m_config.storageDir() + QStringLiteral("/sync"));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns a list of all sync databases.
|
||||
* \remarks Sync databases must be registered with parsePacmanConfig() before.
|
||||
|
@ -602,37 +723,48 @@ const std::map<QString, AlpmDatabase *> &Manager::syncDatabases() const
|
|||
return m_syncDbMap; // m_syncDbs has been filled when the databases were registered
|
||||
}
|
||||
|
||||
QJsonObject emptyJsonObject;
|
||||
|
||||
/*!
|
||||
* \brief Returns basic information about all repositories known to the manager.
|
||||
*
|
||||
* The results include the local database ("local") and the names of
|
||||
* the registered sync databases.
|
||||
*/
|
||||
const QJsonObject &Manager::basicRepoInfo() const
|
||||
{
|
||||
QMutexLocker locker(&m_basicRepoInfoMutex);
|
||||
if(m_basicRepoInfo.isEmpty()) {
|
||||
QMutexLocker locker(&m_basicRepoInfoMutex);
|
||||
if(m_basicRepoInfo.isEmpty()) {
|
||||
// add local data base
|
||||
{
|
||||
QReadLocker locker(localDataBase()->lock());
|
||||
m_basicRepoInfo.insert(localDataBase()->name(), localDataBase()->basicInfo());
|
||||
if(localDatabase()) {
|
||||
if(localDatabase()->isBusy()) {
|
||||
m_basicRepoInfo.insert(QStringLiteral("local"), QStringLiteral("incomplete"));
|
||||
} else {
|
||||
QReadLocker locker(localDatabase()->lock());
|
||||
m_basicRepoInfo.insert(localDatabase()->name(), localDatabase()->basicInfo());
|
||||
}
|
||||
}
|
||||
// add sync data bases
|
||||
for(const auto &syncDb : syncDatabases()) {
|
||||
// check if the "sync" database is actually used for syncing
|
||||
QReadLocker locker(syncDb.second->lock());
|
||||
auto usage = syncDb.second->usage();
|
||||
if((usage & RepositoryUsage::Sync) || (usage & RepositoryUsage::Install) || (usage & RepositoryUsage::Upgrade)) {
|
||||
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
|
||||
if(syncDb.second->isBusy()) {
|
||||
m_basicRepoInfo.insert(syncDb.first, QStringLiteral("incomplete"));
|
||||
} else {
|
||||
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
|
||||
// check if the "sync" database is actually used for syncing
|
||||
QReadLocker locker(syncDb.second->lock());
|
||||
auto usage = syncDb.second->usage();
|
||||
if((usage & RepositoryUsage::Sync) || (usage & RepositoryUsage::Install) || (usage & RepositoryUsage::Upgrade)) {
|
||||
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
|
||||
} else {
|
||||
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
// add AUR
|
||||
if(userRepository()) {
|
||||
QReadLocker locker(userRepository()->lock());
|
||||
m_basicRepoInfo.insert(userRepository()->name(), userRepository()->basicInfo());
|
||||
if(userRepository()->isBusy()) {
|
||||
m_basicRepoInfo.insert(QStringLiteral("aur"), QStringLiteral("incomplete"));
|
||||
} else {
|
||||
QReadLocker locker(userRepository()->lock());
|
||||
m_basicRepoInfo.insert(userRepository()->name(), userRepository()->basicInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -644,70 +776,139 @@ const QJsonObject &Manager::basicRepoInfo() const
|
|||
* \remarks Does not request any information and hence will only return information
|
||||
* which does not need to be requested or has been requested yet.
|
||||
*/
|
||||
const QJsonArray Manager::packageInfo(const QJsonObject &pkgSelection, PackageInfoPart part) const
|
||||
QJsonArray Manager::packageInfo(const QJsonObject &pkgSelection, PackageInfoPart part) const
|
||||
{
|
||||
QJsonArray results;
|
||||
for(auto i = pkgSelection.constBegin(), end = pkgSelection.constEnd(); i != end; ++i) {
|
||||
QJsonObject res;
|
||||
res.insert(QStringLiteral("repo"), i.key());
|
||||
if(auto *repo = repositoryByName(i.key())) {
|
||||
for(const auto &entry : i.value().toArray()) {
|
||||
const auto entryObj = entry.toObject();
|
||||
const auto pkgName = entryObj.value(QStringLiteral("name")).toString();
|
||||
if(!pkgName.isEmpty()) {
|
||||
QJsonObject res;
|
||||
res.insert(QStringLiteral("name"), pkgName);
|
||||
res.insert(QStringLiteral("repo"), repo->name());
|
||||
if(repo->isBusy()) {
|
||||
// specified repository is busy
|
||||
res.insert(QStringLiteral("error"), QStringLiteral("busy"));
|
||||
results << res;
|
||||
} else {
|
||||
QReadLocker locker(repo->lock());
|
||||
for(const auto &entry : i.value().toArray()) {
|
||||
const auto entryObj = entry.toObject();
|
||||
const auto pkgName = entryObj.value(QStringLiteral("name")).toString();
|
||||
const auto index = entryObj.value(QStringLiteral("index"));
|
||||
if(!index.isNull() && !index.isUndefined()) {
|
||||
res.insert(QStringLiteral("index"), index);
|
||||
}
|
||||
if(auto *pkg = repo->packageByName(pkgName)) {
|
||||
if(part & Basics) {
|
||||
res.insert(QStringLiteral("basics"), pkg->basicInfo());
|
||||
if(!pkgName.isEmpty()) {
|
||||
res.insert(QStringLiteral("name"), pkgName);
|
||||
if(auto *pkg = repo->packageByName(pkgName)) {
|
||||
if(part & Basics) {
|
||||
res.insert(QStringLiteral("basics"), pkg->basicInfo());
|
||||
}
|
||||
if(part & Details) {
|
||||
res.insert(QStringLiteral("details"), pkg->detailedInfo());
|
||||
}
|
||||
} else {
|
||||
res.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||
}
|
||||
if(part & Details) {
|
||||
res.insert(QStringLiteral("details"), pkg->detailedInfo());
|
||||
}
|
||||
} else {
|
||||
res.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||
results << res;
|
||||
}
|
||||
results << res;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// specified repository can not be found
|
||||
QJsonObject errorObj;
|
||||
errorObj.insert(QStringLiteral("repo"), i.key());
|
||||
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||
results << errorObj;
|
||||
res.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||
results << res;
|
||||
}
|
||||
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
QJsonObject incompleteGroupInfo(const QString &repo)
|
||||
{
|
||||
QJsonObject busyObject;
|
||||
busyObject.insert(QStringLiteral("repo"), QStringLiteral("local"));
|
||||
busyObject.insert(QStringLiteral("incomplete"), true);
|
||||
return busyObject;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns group information for the local database and all registred sync databases.
|
||||
*/
|
||||
const QJsonArray &Manager::groupInfo() const
|
||||
{
|
||||
QMutexLocker locker(&m_groupInfoMutex);
|
||||
if(m_groupInfo.empty()) {
|
||||
QMutexLocker locker(&m_groupInfoMutex);
|
||||
if(m_groupInfo.empty()) {
|
||||
m_groupInfo << localDataBase()->groupInfo();
|
||||
if(localDatabase()) {
|
||||
if(localDatabase()->isBusy()) {
|
||||
m_groupInfo << incompleteGroupInfo(QStringLiteral("local"));
|
||||
} else {
|
||||
QReadLocker locker(localDatabase()->lock());
|
||||
m_groupInfo << localDatabase()->groupInfo();
|
||||
}
|
||||
}
|
||||
for(const auto &db : m_syncDbMap) {
|
||||
m_groupInfo << db.second->groupInfo();
|
||||
if(db.second->isBusy()) {
|
||||
m_groupInfo << incompleteGroupInfo(db.first);
|
||||
} else {
|
||||
QReadLocker locker(db.second->lock());
|
||||
m_groupInfo << db.second->groupInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_groupInfo;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to invalidate cached JSON serialization.
|
||||
*
|
||||
* This method is called after a repository has been initialized or the
|
||||
* required-by computition has finished.
|
||||
*/
|
||||
void Manager::invalidateCachedJsonSerialization()
|
||||
{
|
||||
m_basicRepoInfo = QJsonObject();
|
||||
m_groupInfo = QJsonArray();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to emit the updatesAvailable() signal.
|
||||
*
|
||||
* Emits the signal only if no repository is busy.
|
||||
*/
|
||||
void Manager::emitUpdatesAvailable()
|
||||
{
|
||||
if(localDatabase() && localDatabase()->isBusy()) {
|
||||
return;
|
||||
}
|
||||
if(userRepository() && userRepository()->isBusy()) {
|
||||
return;
|
||||
}
|
||||
for(const auto &syncDb : m_syncDbs) {
|
||||
if(syncDb->isBusy()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit updatesAvailable();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called after a repository has been added to connect
|
||||
* the available() signal.
|
||||
*/
|
||||
void Manager::connectRepository(Repository *repo)
|
||||
{
|
||||
connect(repo, &Repository::available, this, &Manager::invalidateCachedJsonSerialization);
|
||||
connect(repo, &Repository::requiredByComputed, this, &Manager::emitUpdatesAvailable);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Add the local database.
|
||||
*/
|
||||
void Manager::addLocalDatabase()
|
||||
{
|
||||
m_localDb = make_unique<AlpmDatabase>(QStringLiteral("local"), config().alpmDbPath() % QStringLiteral("/local"), RepositoryUsage::None, SignatureLevel::UseDefault, 0);
|
||||
connectRepository(m_localDb.get());
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -726,7 +927,7 @@ void Manager::removeAllDatabases()
|
|||
const AlpmDatabase *Manager::databaseByName(const QString &dbName) const
|
||||
{
|
||||
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
return localDataBase();
|
||||
return localDatabase();
|
||||
} else {
|
||||
try {
|
||||
return m_syncDbMap.at(dbName);
|
||||
|
@ -742,7 +943,7 @@ const AlpmDatabase *Manager::databaseByName(const QString &dbName) const
|
|||
AlpmDatabase *Manager::databaseByName(const QString &dbName)
|
||||
{
|
||||
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
return localDataBase();
|
||||
return localDatabase();
|
||||
} else {
|
||||
try {
|
||||
return m_syncDbMap.at(dbName);
|
||||
|
@ -758,8 +959,8 @@ AlpmDatabase *Manager::databaseByName(const QString &dbName)
|
|||
const Repository *Manager::repositoryByName(const QString &name) const
|
||||
{
|
||||
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
return localDataBase();
|
||||
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
|
||||
return localDatabase();
|
||||
} else if(name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0) {
|
||||
return userRepository();
|
||||
} else {
|
||||
try {
|
||||
|
@ -776,8 +977,8 @@ const Repository *Manager::repositoryByName(const QString &name) const
|
|||
Repository *Manager::repositoryByName(const QString &name)
|
||||
{
|
||||
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||
return localDataBase();
|
||||
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
|
||||
return localDatabase();
|
||||
} else if(name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0) {
|
||||
return userRepository();
|
||||
} else {
|
||||
try {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QMutex>
|
||||
#include <QAtomicInteger>
|
||||
#include <QObject>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
@ -20,8 +22,10 @@ class UserRepository;
|
|||
class AlpmDatabase;
|
||||
enum class RepositoryUsage;
|
||||
|
||||
class Manager
|
||||
class Manager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum PackageInfoPart {
|
||||
None = 0x0,
|
||||
|
@ -30,10 +34,7 @@ public:
|
|||
};
|
||||
Q_DECLARE_FLAGS(PackageInfoParts, PackageInfoPart)
|
||||
|
||||
explicit Manager(const Config &config);
|
||||
Manager(const Manager &other) = delete;
|
||||
Manager(Manager &&other) = delete;
|
||||
Manager &operator =(const Manager &other) = delete;
|
||||
explicit Manager(const Config &config, QObject *parent = nullptr);
|
||||
~Manager();
|
||||
|
||||
// configuration, signature level, etc
|
||||
|
@ -49,7 +50,8 @@ public:
|
|||
void removeAllDatabases();
|
||||
void addDataBasesFromPacmanConfig();
|
||||
void addDatabasesFromRepoIndexConfig();
|
||||
void initAlpmDataBases(bool computeRequiredBy);
|
||||
void initAlpmDataBases();
|
||||
void computeRequiredBy(Repository *repo);
|
||||
|
||||
// caching
|
||||
bool writeCacheBeforeGone() const;
|
||||
|
@ -62,7 +64,11 @@ public:
|
|||
void wipeCache();
|
||||
void maintainCache();
|
||||
|
||||
// refreshing
|
||||
// updating
|
||||
bool isAutoUpdateEnabled() const;
|
||||
void setAutoUpdateEnabled(bool enabled);
|
||||
void updateAlpmDatabases();
|
||||
void forceUpdateAlpmDatabases();
|
||||
|
||||
// package lookup
|
||||
AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName);
|
||||
|
@ -73,8 +79,8 @@ public:
|
|||
const Package *packageProviding(const Dependency &dependency) const;
|
||||
|
||||
// repository lookup
|
||||
const AlpmDatabase *localDataBase() const;
|
||||
AlpmDatabase *localDataBase();
|
||||
const AlpmDatabase *localDatabase() const;
|
||||
AlpmDatabase *localDatabase();
|
||||
const std::map<QString, AlpmDatabase *> &syncDatabases() const;
|
||||
const AlpmDatabase *databaseByName(const QString &dbName) const;
|
||||
AlpmDatabase *databaseByName(const QString &dbName);
|
||||
|
@ -86,15 +92,25 @@ public:
|
|||
QString proposedDatabasePath(const QString &name, bool files) const;
|
||||
|
||||
// JSON serialization, handling JSON requests
|
||||
const QJsonObject basicRepoInfo(const Repository *packageSource) const;
|
||||
const QJsonObject &basicRepoInfo() const;
|
||||
const QJsonArray packageInfo(const QJsonObject &pkgSelection, PackageInfoPart part) const;
|
||||
QJsonArray packageInfo(const QJsonObject &pkgSelection, PackageInfoPart part) const;
|
||||
const QJsonArray &groupInfo() const;
|
||||
|
||||
signals:
|
||||
void updatesAvailable();
|
||||
|
||||
private slots:
|
||||
void invalidateCachedJsonSerialization();
|
||||
void emitUpdatesAvailable();
|
||||
|
||||
private:
|
||||
void connectRepository(Repository *repo);
|
||||
|
||||
private:
|
||||
const Config &m_config;
|
||||
bool m_writeCacheBeforeGone;
|
||||
std::unique_ptr<QTimer> m_cacheTimer;
|
||||
std::unique_ptr<QTimer> m_updateTimer;
|
||||
SignatureLevel m_sigLevel;
|
||||
SignatureLevel m_localFileSigLevel;
|
||||
QString m_pacmanCacheDir;
|
||||
|
@ -201,7 +217,7 @@ inline UserRepository *Manager::userRepository()
|
|||
/*!
|
||||
* \brief Returns the local data base.
|
||||
*/
|
||||
inline const AlpmDatabase *Manager::localDataBase() const
|
||||
inline const AlpmDatabase *Manager::localDatabase() const
|
||||
{
|
||||
return m_localDb.get();
|
||||
}
|
||||
|
@ -209,7 +225,7 @@ inline const AlpmDatabase *Manager::localDataBase() const
|
|||
/*!
|
||||
* \brief Returns the local data base.
|
||||
*/
|
||||
inline AlpmDatabase *Manager::localDataBase()
|
||||
inline AlpmDatabase *Manager::localDatabase()
|
||||
{
|
||||
return m_localDb.get();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "./alpmdatabase.h"
|
||||
#include "./utilities.h"
|
||||
#include "./repository.h"
|
||||
#include "./manager.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
@ -75,18 +74,18 @@ Package::~Package()
|
|||
{}
|
||||
|
||||
/*!
|
||||
* \brief Computes required-by and optional-for.
|
||||
* \brief Computes required-by and optional-for fields.
|
||||
*
|
||||
* Sources the specified \a relevantRepositores for packages depending on this package.
|
||||
*/
|
||||
void Package::computeRequiredBy(Manager &manager)
|
||||
void Package::computeRequiredBy(const QList<Repository *> &relevantRepositories)
|
||||
{
|
||||
if(m_requiredByComputed) {
|
||||
m_requiredBy.clear();
|
||||
m_optionalFor.clear();
|
||||
}
|
||||
switch(origin()) {
|
||||
case PackageOrigin::File:
|
||||
case PackageOrigin::LocalDb:
|
||||
for(const auto &pkgEntry : manager.localDataBase()->packages()) {
|
||||
for(const Repository *repo : relevantRepositories) {
|
||||
for(const auto &pkgEntry : repo->packages()) {
|
||||
for(const auto &dep : pkgEntry.second->dependencies()) {
|
||||
if(dep.name == m_name) {
|
||||
m_requiredBy << pkgEntry.first;
|
||||
|
@ -100,27 +99,6 @@ void Package::computeRequiredBy(Manager &manager)
|
|||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PackageOrigin::SyncDb:
|
||||
for(const auto &dbEntry : manager.syncDatabases()) {
|
||||
for(const auto &pkgEntry : dbEntry.second->packages()) {
|
||||
for(const auto &dep : pkgEntry.second->dependencies()) {
|
||||
if(dep.name == m_name) {
|
||||
m_requiredBy << pkgEntry.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(const auto &dep : pkgEntry.second->optionalDependencies()) {
|
||||
if(dep.name == m_name) {
|
||||
m_optionalFor << pkgEntry.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
; // can not compute this for packages from other sources
|
||||
}
|
||||
m_requiredByComputed = true;
|
||||
}
|
||||
|
@ -320,9 +298,9 @@ QJsonObject Package::basicInfo(bool includeRepoAndName) const
|
|||
QJsonObject Package::detailedInfo() const
|
||||
{
|
||||
QJsonObject info;
|
||||
put(info, QStringLiteral("installAvail"), hasInstallRelatedMetaData());
|
||||
put(info, QStringLiteral("buildAvail"), hasBuildRelatedMetaData());
|
||||
put(info, QStringLiteral("srcAvail"), hasSourceRelatedMetaData());
|
||||
put(info, QStringLiteral("iav"), hasInstallRelatedMetaData());
|
||||
put(info, QStringLiteral("bav"), hasBuildRelatedMetaData());
|
||||
put(info, QStringLiteral("sav"), hasSourceRelatedMetaData());
|
||||
put(info, QStringLiteral("idate"), installDate());
|
||||
put(info, QStringLiteral("isize"), QJsonValue(static_cast<long long int>(installedSize())));
|
||||
put(info, QStringLiteral("csize"), QJsonValue(static_cast<long long int>(packageSize())));
|
||||
|
@ -334,8 +312,10 @@ QJsonObject Package::detailedInfo() const
|
|||
put(info, QStringLiteral("optd"), optionalDependencies());
|
||||
put(info, QStringLiteral("mkd"), makeDependencies());
|
||||
put(info, QStringLiteral("chkd"), checkDependencies());
|
||||
put(info, QStringLiteral("requ"), requiredBy());
|
||||
put(info, QStringLiteral("optf"), optionalFor());
|
||||
if(isRequiredByComputed()) {
|
||||
put(info, QStringLiteral("requ"), requiredBy());
|
||||
put(info, QStringLiteral("optf"), optionalFor());
|
||||
}
|
||||
put(info, QStringLiteral("conf"), conflicts());
|
||||
put(info, QStringLiteral("repl"), replaces());
|
||||
put(info, QStringLiteral("pack"), packager());
|
||||
|
@ -344,6 +324,17 @@ QJsonObject Package::detailedInfo() const
|
|||
put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods()));
|
||||
put(info, QStringLiteral("file"), fileName());
|
||||
put(info, QStringLiteral("files"), files());
|
||||
put(info, QStringLiteral("fsub"), firstSubmitted());
|
||||
put(info, QStringLiteral("lmod"), lastModified());
|
||||
if(!maintainer().isEmpty()) {
|
||||
put(info, QStringLiteral("main"), maintainer());
|
||||
}
|
||||
if(!tarUrl().isEmpty()) {
|
||||
put(info, QStringLiteral("srctar"), tarUrl());
|
||||
}
|
||||
if(votes() >= 0) {
|
||||
put(info, QStringLiteral("votes"), votes());
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -623,6 +614,7 @@ const map<QString, void(Package::*)(const QStringList &)> Package::m_descMap {
|
|||
{QStringLiteral("PACKAGER"), &Package::setPackager},
|
||||
{QStringLiteral("MD5SUM"), &Package::setMd5},
|
||||
{QStringLiteral("SHA256SUM"), &Package::setSha256},
|
||||
{QStringLiteral("PGPSIG"), &Package::setPgpSignature},
|
||||
{QStringLiteral("FILES"), &Package::setFiles},
|
||||
{QStringLiteral("REASON"), &Package::setInstallReason},
|
||||
{QStringLiteral("VALIDATION"), &Package::setValidation},
|
||||
|
@ -727,13 +719,31 @@ void Package::setPackager(const QStringList &values)
|
|||
void Package::setMd5(const QStringList &values)
|
||||
{
|
||||
if(!values.isEmpty()) {
|
||||
m_md5 = values.back();
|
||||
if(!(m_md5 = values.back()).isEmpty()) {
|
||||
m_validationMethods |= PackageValidation::Md5Sum;
|
||||
} else {
|
||||
m_validationMethods &= ~PackageValidation::Md5Sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Package::setSha256(const QStringList &values)
|
||||
{
|
||||
if(!values.isEmpty()) {
|
||||
m_sha256 = values.back();
|
||||
if(!(m_sha256 = values.back()).isEmpty()) {
|
||||
m_validationMethods |= PackageValidation::Sha256Sum;
|
||||
} else {
|
||||
m_validationMethods &= ~PackageValidation::Sha256Sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Package::setPgpSignature(const QStringList &values)
|
||||
{
|
||||
if(!values.isEmpty()) {
|
||||
if(!(m_pgpSignature = values.back()).isEmpty()) {
|
||||
m_validationMethods |= PackageValidation::PgpSignature;
|
||||
} else {
|
||||
m_validationMethods &= ~PackageValidation::PgpSignature;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Package::setFiles(const QStringList &values)
|
||||
|
@ -754,7 +764,7 @@ void Package::setValidation(const QStringList &values)
|
|||
} else if(value == QLatin1String("sha256")) {
|
||||
m_validationMethods = m_validationMethods | PackageValidation::Sha256Sum;
|
||||
} else if(value == QLatin1String("pgp")) {
|
||||
m_validationMethods = m_validationMethods | PackageValidation::Signature;
|
||||
m_validationMethods = m_validationMethods | PackageValidation::PgpSignature;
|
||||
} else {
|
||||
// TODO: error handling (imporant?)
|
||||
}
|
||||
|
|
|
@ -52,8 +52,7 @@ enum class PackageOrigin
|
|||
};
|
||||
|
||||
/*!
|
||||
* \brief The InstallStatus enum specifies whether a package has been installed explicitely
|
||||
* or as dependency.
|
||||
* \brief The InstallStatus enum specifies whether a package has been installed explicitely or as dependency.
|
||||
*/
|
||||
enum class InstallStatus
|
||||
{
|
||||
|
@ -71,12 +70,12 @@ enum class PackageValidation {
|
|||
None = (1 << 0),
|
||||
Md5Sum = (1 << 1),
|
||||
Sha256Sum = (1 << 2),
|
||||
Signature = (1 << 3)
|
||||
PgpSignature = (1 << 3)
|
||||
};
|
||||
|
||||
constexpr PackageValidation operator |(PackageValidation lhs, PackageValidation rhs)
|
||||
{
|
||||
return static_cast<PackageValidation>(static_cast<int>(lhs) & static_cast<int>(rhs));
|
||||
return static_cast<PackageValidation>(static_cast<int>(lhs) | static_cast<int>(rhs));
|
||||
}
|
||||
|
||||
constexpr bool operator &(PackageValidation lhs, PackageValidation rhs)
|
||||
|
@ -84,6 +83,23 @@ constexpr bool operator &(PackageValidation lhs, PackageValidation rhs)
|
|||
return (static_cast<int>(lhs) & static_cast<int>(rhs)) != 0;
|
||||
}
|
||||
|
||||
constexpr int operator ~(PackageValidation lhs)
|
||||
{
|
||||
return ~static_cast<int>(lhs);
|
||||
}
|
||||
|
||||
inline PackageValidation &operator |=(PackageValidation &lhs, PackageValidation rhs)
|
||||
{
|
||||
lhs = static_cast<PackageValidation>(static_cast<int>(lhs) | static_cast<int>(rhs));
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline PackageValidation &operator &=(PackageValidation &lhs, int rhs)
|
||||
{
|
||||
lhs = static_cast<PackageValidation>(static_cast<int>(lhs) & rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief The SignatureLevel enum specifies PGP signature verification options.
|
||||
*/
|
||||
|
@ -227,7 +243,7 @@ public:
|
|||
const QList<Dependency> &provides() const;
|
||||
const QList<Dependency> &replaces() const;
|
||||
bool isRequiredByComputed() const;
|
||||
void computeRequiredBy(Manager &manager);
|
||||
void computeRequiredBy(const QList<Repository *> &relevantRepositories);
|
||||
const QStringList &requiredBy() const;
|
||||
QStringList &requiredBy();
|
||||
const QStringList &optionalFor() const;
|
||||
|
@ -328,6 +344,7 @@ protected:
|
|||
QString m_packager;
|
||||
QString m_md5;
|
||||
QString m_sha256;
|
||||
QString m_pgpSignature;
|
||||
QString m_buildArchitecture;
|
||||
uint32 m_packageSize;
|
||||
QList<Dependency> m_makeDependencies;
|
||||
|
@ -377,6 +394,7 @@ protected:
|
|||
void setPackager(const QStringList &values);
|
||||
void setMd5(const QStringList &values);
|
||||
void setSha256(const QStringList &values);
|
||||
void setPgpSignature(const QStringList &values);
|
||||
void setFiles(const QStringList &values);
|
||||
void setValidation(const QStringList &values);
|
||||
void setGroups(const QStringList &values);
|
||||
|
|
|
@ -65,7 +65,9 @@ Package *PackageFinder::packageProviding(const Dependency &dependency)
|
|||
|
||||
void PackageFinder::addResults()
|
||||
{
|
||||
#ifdef DEBUG_BUILD
|
||||
assert(m_remainingReplies);
|
||||
#endif
|
||||
// add results
|
||||
auto *reply = static_cast<PackageReply *>(sender());
|
||||
auto *repo = reply->repository();
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace RepoIndex {
|
|||
|
||||
PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent) :
|
||||
PackageLookup(parent),
|
||||
m_manager(manager),
|
||||
m_what(request.value(QStringLiteral("what")).toString()),
|
||||
m_part(Manager::None)
|
||||
{
|
||||
|
@ -26,6 +27,7 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques
|
|||
return;
|
||||
}
|
||||
m_packageSelection = request.value(QStringLiteral("sel")).toObject();
|
||||
m_repos.reserve(m_packageSelection.size());
|
||||
for(auto i = m_packageSelection.constBegin(), end = m_packageSelection.constEnd(); i != end; ++i) {
|
||||
if(auto *repo = manager.repositoryByName(i.key())) {
|
||||
QStringList packagesToBeRequested;
|
||||
|
@ -37,6 +39,33 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques
|
|||
}
|
||||
}
|
||||
if(!packagesToBeRequested.isEmpty()) {
|
||||
m_repos << qMakePair(repo, packagesToBeRequested);
|
||||
}
|
||||
} else {
|
||||
// specified repository can not be found
|
||||
QJsonObject errorObj;
|
||||
errorObj.insert(QStringLiteral("repo"), i.key());
|
||||
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||
m_results << errorObj;
|
||||
}
|
||||
}
|
||||
performLookup();
|
||||
}
|
||||
|
||||
void PackageInfoLookup::performLookup()
|
||||
{
|
||||
for(auto &entry : m_repos) {
|
||||
if(Repository *repo = entry.first) {
|
||||
const QStringList &packagesToBeRequested = entry.second;
|
||||
if(repo->isBusy()) {
|
||||
// repo is busy -> try again when available
|
||||
connect(repo, &Repository::available, this, &PackageInfoLookup::performLookup);
|
||||
} else {
|
||||
// disconnect to ensure the lookup isn't done twice
|
||||
disconnect(repo, nullptr, this, nullptr);
|
||||
// this repo can be skipped when this method is called again because other repos where busy
|
||||
entry.first = nullptr;
|
||||
// request package info
|
||||
QReadLocker locker(repo->lock());
|
||||
if(const auto *reply = (m_part & Manager::Details ? repo->requestFullPackageInfo(packagesToBeRequested) : repo->requestPackageInfo(packagesToBeRequested))) {
|
||||
connect(reply, &PackageReply::resultsAvailable, this, &PackageInfoLookup::addResultsFromReply);
|
||||
|
@ -46,13 +75,7 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques
|
|||
addResultsDirectly(packagesToBeRequested, repo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// specified repository can not be found
|
||||
QJsonObject errorObj;
|
||||
errorObj.insert(QStringLiteral("repo"), i.key());
|
||||
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||
m_results << errorObj;
|
||||
}
|
||||
} // else: repo already processed
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +113,9 @@ void PackageInfoLookup::addResultsDirectly(const QStringList &packageNames, cons
|
|||
|
||||
void PackageInfoLookup::addResultsFromReply()
|
||||
{
|
||||
#ifdef DEBUG_BUILD
|
||||
assert(m_remainingReplies);
|
||||
#endif
|
||||
auto *reply = static_cast<PackageReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if(reply->error().isEmpty()) {
|
||||
|
|
|
@ -13,13 +13,16 @@ public:
|
|||
explicit PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void performLookup();
|
||||
void addResultsDirectly(const QStringList &packageNames, const Repository *repo);
|
||||
void addResultsFromReply();
|
||||
|
||||
private:
|
||||
Manager &m_manager;
|
||||
const QString m_what;
|
||||
Manager::PackageInfoParts m_part;
|
||||
QJsonObject m_packageSelection;
|
||||
QList<QPair<Repository *, QStringList> > m_repos;
|
||||
QList<Package *> m_packages;
|
||||
|
||||
};
|
||||
|
|
|
@ -46,7 +46,9 @@ Reply::Reply(const QList<QNetworkReply *> networkReplies) :
|
|||
*/
|
||||
void Reply::replyFinished()
|
||||
{
|
||||
#ifdef DEBUG_BUILD
|
||||
assert(m_remainingReplies);
|
||||
#endif
|
||||
processData(static_cast<QNetworkReply *>(sender()));
|
||||
if(!--m_remainingReplies) {
|
||||
emit resultsAvailable();
|
||||
|
@ -84,12 +86,56 @@ void Repository::updateGroups()
|
|||
/*!
|
||||
* \brief Initializes the repository.
|
||||
* \remarks
|
||||
* - The repository mustn't be busy if this method is called.
|
||||
* - Does not restore cache. For restoring cache see restoreFromCacheStream().
|
||||
* - Performs asynchronously and hence returns immidiately. Returns a PackageLoader
|
||||
* object which QFuture can be used to wait for initializing to finish.
|
||||
* - Might return nullptr if initialization is tivial.
|
||||
* object which QFuture can be used to wait until the initialization is finished.
|
||||
* - Alternatively the available() and initialized() signals can be used.
|
||||
* - Might return nullptr if initialization is tivial. In this case the available
|
||||
* and initialized() signals are not emitted.
|
||||
* - The returned future might be not running indicating the process
|
||||
* has already finished. In this case the available and initialized() signals are not emitted.
|
||||
* - Locks the repository for write access. Flags the repository as busy.
|
||||
*/
|
||||
PackageLoader *Repository::init()
|
||||
{
|
||||
addBusyFlag();
|
||||
QWriteLocker locker(lock());
|
||||
if(PackageLoader *loader = internalInit()) {
|
||||
if(loader->future().isRunning()) {
|
||||
auto watcher = new QFutureWatcher<void>;
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::removeBusyFlag);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::initialized);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);
|
||||
watcher->setFuture(loader->future());
|
||||
}
|
||||
return loader;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Repository::initAsSoonAsPossible()
|
||||
{
|
||||
if(isBusy()) {
|
||||
auto connection = make_shared<QMetaObject::Connection>();
|
||||
*connection = connect(this, &Repository::available, [connection, this] {
|
||||
disconnect(*connection);
|
||||
init();
|
||||
});
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief This method can must overriden when subclassing to initialize the repository.
|
||||
* \remarks
|
||||
* - Mustn't emit any signals.
|
||||
* - The repository is already locked when this method is called. Hence mustn't lock the repository.
|
||||
* \sa init()
|
||||
*/
|
||||
PackageLoader *Repository::internalInit()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -237,23 +283,47 @@ QList<Package *> Repository::packageByFilter(std::function<bool (const Package *
|
|||
* \cond
|
||||
*/
|
||||
|
||||
class Blocker
|
||||
{
|
||||
public:
|
||||
Blocker(const QList<Repository *> &relevantRepos)
|
||||
{
|
||||
m_blockedRepos.reserve(relevantRepos.size());
|
||||
for(Repository *repo : relevantRepos) {
|
||||
if(repo->lock()->tryLockForWrite()) {
|
||||
m_blockedRepos << repo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Blocker()
|
||||
{
|
||||
for(Repository *repo : m_blockedRepos) {
|
||||
repo->lock()->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QList<Repository *> m_blockedRepos;
|
||||
};
|
||||
|
||||
class ComputeRequired
|
||||
{
|
||||
public:
|
||||
ComputeRequired(Manager &manager, bool forceUpdate) :
|
||||
m_manager(manager),
|
||||
ComputeRequired(const QList<Repository *> &relevantRepos, bool forceUpdate) :
|
||||
m_relevantRepos(relevantRepos),
|
||||
m_forceUpdate(forceUpdate)
|
||||
{}
|
||||
|
||||
void operator () (const pair<const QString, unique_ptr<Package> > &packageEntry)
|
||||
{
|
||||
if(m_forceUpdate || !packageEntry.second->isRequiredByComputed()) {
|
||||
packageEntry.second->computeRequiredBy(m_manager);
|
||||
packageEntry.second->computeRequiredBy(m_relevantRepos);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Manager &m_manager;
|
||||
const QList<Repository *> m_relevantRepos;
|
||||
bool m_forceUpdate;
|
||||
};
|
||||
|
||||
|
@ -263,11 +333,24 @@ private:
|
|||
|
||||
/*!
|
||||
* \brief Computes required-by and optional-for for all packages.
|
||||
* \remarks Computition is done async.
|
||||
*
|
||||
* Sources the packages of all \a relevantRepositories for packages depending on the packages of this repository.
|
||||
*
|
||||
* \remarks
|
||||
* - Computation is done async.
|
||||
* - The repository mustn't be busy. Flags the repository as busy.
|
||||
* - \a relevantRepositories might contain the current instance.
|
||||
* - The available() and requiredByComputed() signals are emitted after computition has finished.
|
||||
*/
|
||||
QFuture<void> Repository::computeRequiredBy(Manager &manager, bool forceUpdate)
|
||||
QFuture<void> Repository::computeRequiredBy(const QList<Repository *> &relevantRepositories, bool forceUpdate)
|
||||
{
|
||||
return QtConcurrent::map(m_packages, ComputeRequired(manager, forceUpdate));
|
||||
addBusyFlag(); // flag repository as busy
|
||||
auto *watcher = new QFutureWatcher<void>;
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::removeBusyFlag);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::requiredByComputed);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);
|
||||
watcher->setFuture(QtConcurrent::map(m_packages, ComputeRequired(relevantRepositories, forceUpdate)));
|
||||
return watcher->future();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -771,6 +854,8 @@ void Repository::parseDescriptions(const QList<QByteArray> &descriptions, QStrin
|
|||
// put last field
|
||||
if(!currentFieldName.isEmpty()) {
|
||||
fields << QPair<QString, QStringList>(currentFieldName, currentFieldValues);
|
||||
currentFieldName.clear();
|
||||
currentFieldValues.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -944,4 +1029,21 @@ Package *Repository::addPackageFromDescription(QString name, const QList<QByteAr
|
|||
return pkgRawPtr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to add the busy flag.
|
||||
*/
|
||||
void Repository::addBusyFlag()
|
||||
{
|
||||
m_isBusy.store(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to remove the busy flag.
|
||||
*/
|
||||
void Repository::removeBusyFlag()
|
||||
{
|
||||
m_isBusy.store(0);
|
||||
emit available();
|
||||
}
|
||||
|
||||
} // namespace PackageManagement
|
||||
|
|
|
@ -200,6 +200,7 @@ public:
|
|||
virtual RepositoryType type() const = 0;
|
||||
|
||||
// general meta data
|
||||
bool isBusy() const;
|
||||
uint32 index() const;
|
||||
const QString &name() const;
|
||||
const QString &description() const;
|
||||
|
@ -218,7 +219,10 @@ public:
|
|||
void setSigLevel(SignatureLevel sigLevel);
|
||||
|
||||
// gathering data
|
||||
virtual PackageLoader *init();
|
||||
public:
|
||||
PackageLoader *init();
|
||||
void initAsSoonAsPossible();
|
||||
virtual PackageLoader *internalInit();
|
||||
virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const;
|
||||
virtual SuggestionsReply *requestSuggestions(const QString &phrase);
|
||||
virtual PackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false);
|
||||
|
@ -231,7 +235,7 @@ public:
|
|||
Package *packageProviding(const Dependency &dependency);
|
||||
QList<const Package *> packagesProviding(const Dependency &dependency) const;
|
||||
QList<Package *> packageByFilter(std::function<bool (const Package *)> pred);
|
||||
QFuture<void> computeRequiredBy(Manager &manager, bool forceUpdate = false);
|
||||
QFuture<void> computeRequiredBy(const QList<Repository *> &relevantRepositories, bool forceUpdate = false);
|
||||
QJsonObject suggestions(const QString &term) const;
|
||||
|
||||
// upgrade lookup
|
||||
|
@ -277,10 +281,29 @@ public:
|
|||
|
||||
static const uint32 invalidIndex = static_cast<uint32>(-1);
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Emitted after initialization has finished.
|
||||
*/
|
||||
void initialized();
|
||||
|
||||
/*!
|
||||
* \brief Emitted after required-by computation has finished.
|
||||
*/
|
||||
void requiredByComputed();
|
||||
|
||||
/*!
|
||||
* \brief Indicates the repository is not busy anymore; emitted after either initialization or required-by computation has finished.
|
||||
*/
|
||||
void available();
|
||||
|
||||
protected slots:
|
||||
void addBusyFlag();
|
||||
void removeBusyFlag();
|
||||
|
||||
protected:
|
||||
explicit Repository(const QString &name, uint32 index = invalidIndex, QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
uint32 m_index;
|
||||
QString m_name;
|
||||
QString m_description;
|
||||
|
@ -293,6 +316,7 @@ protected:
|
|||
QList<Repository *> m_upgradeSources;
|
||||
QString m_srcDir;
|
||||
QString m_pkgDir;
|
||||
QAtomicInteger<byte> m_isBusy;
|
||||
|
||||
private:
|
||||
QReadWriteLock m_lock;
|
||||
|
@ -306,6 +330,18 @@ inline QJsonObject SuggestionsReply::suggestions() const
|
|||
return m_repo->suggestions(m_term);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an indication whether the repository is busy (either initializing or computing required-by).
|
||||
*
|
||||
* In this case the repository shouldn't be touched until the available() signal is emitted.
|
||||
*
|
||||
* \remarks This method is thread-safe.
|
||||
*/
|
||||
inline bool Repository::isBusy() const
|
||||
{
|
||||
return m_isBusy.load() != 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the index of the repository.
|
||||
*
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
namespace RepoIndex {
|
||||
|
||||
SuggestionsLookup::SuggestionsLookup(Manager &manager, const QJsonObject &request, QObject *parent) :
|
||||
PackageLookup(parent)
|
||||
PackageLookup(parent),
|
||||
m_searchTerm(request.value(QStringLiteral("term")).toString())
|
||||
{
|
||||
m_id = request.value(QStringLiteral("id"));
|
||||
const auto searchTerm = request.value(QStringLiteral("term")).toString();
|
||||
if(searchTerm.isEmpty()) {
|
||||
if(m_searchTerm.isEmpty()) {
|
||||
m_errors << QStringLiteral("No search term specified.");
|
||||
}
|
||||
const auto repos = request.value(QStringLiteral("repos")).toArray();
|
||||
|
@ -22,15 +22,10 @@ SuggestionsLookup::SuggestionsLookup(Manager &manager, const QJsonObject &reques
|
|||
m_errors << QStringLiteral("No repositories specified.");
|
||||
}
|
||||
if(m_errors.isEmpty()) {
|
||||
m_repos.reserve(repos.size());
|
||||
for(const auto &repoName : repos) {
|
||||
if(auto *repo = manager.repositoryByName(repoName.toString())) {
|
||||
QReadLocker locker(repo->lock());
|
||||
if(const auto *reply = repo->requestSuggestions(searchTerm)) {
|
||||
connect(reply, &SuggestionsReply::resultsAvailable, this, &SuggestionsLookup::addResults);
|
||||
++m_remainingReplies;
|
||||
} else {
|
||||
m_results << repo->suggestions(searchTerm);
|
||||
}
|
||||
m_repos << repo;
|
||||
} else {
|
||||
m_errors << QStringLiteral("The specified repository \"%1\" does not exist.").arg(repoName.toString());
|
||||
}
|
||||
|
@ -40,9 +35,36 @@ SuggestionsLookup::SuggestionsLookup(Manager &manager, const QJsonObject &reques
|
|||
deleteLater();
|
||||
}
|
||||
|
||||
void SuggestionsLookup::performLookup()
|
||||
{
|
||||
for(Repository *&repo : m_repos) {
|
||||
if(repo) {
|
||||
if(repo->isBusy()) {
|
||||
// repo is busy -> try again when available
|
||||
connect(repo, &Repository::available, this, &SuggestionsLookup::performLookup);
|
||||
} else {
|
||||
// disconnect to ensure the lookup isn't done twice
|
||||
disconnect(repo, nullptr, this, nullptr);
|
||||
// request suggestions
|
||||
QReadLocker locker(repo->lock());
|
||||
if(const auto *reply = repo->requestSuggestions(m_searchTerm)) {
|
||||
connect(reply, &SuggestionsReply::resultsAvailable, this, &SuggestionsLookup::addResults);
|
||||
++m_remainingReplies;
|
||||
} else {
|
||||
m_results << repo->suggestions(m_searchTerm);
|
||||
}
|
||||
// this repo can be skipped when this method is called again because other repos where busy
|
||||
repo = nullptr;
|
||||
}
|
||||
} // else: repo already processed
|
||||
}
|
||||
}
|
||||
|
||||
void SuggestionsLookup::addResults()
|
||||
{
|
||||
#ifdef DEBUG_BUILD
|
||||
assert(m_remainingReplies);
|
||||
#endif
|
||||
auto *reply = static_cast<SuggestionsReply *>(sender());
|
||||
{
|
||||
QReadLocker locker(reply->repository()->lock());
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
namespace RepoIndex {
|
||||
|
||||
class Manager;
|
||||
class Repository;
|
||||
|
||||
class SuggestionsLookup : public PackageLookup
|
||||
{
|
||||
|
@ -14,7 +15,12 @@ public:
|
|||
SuggestionsLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void performLookup();
|
||||
void addResults();
|
||||
|
||||
private:
|
||||
QList<Repository *> m_repos;
|
||||
const QString m_searchTerm;
|
||||
};
|
||||
|
||||
} // namespace RepoIndex
|
||||
|
|
|
@ -52,28 +52,7 @@ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, Reposit
|
|||
m_watcher(new QFutureWatcher<void>(this))
|
||||
{
|
||||
connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished);
|
||||
{
|
||||
switch(m_upgradeSource->requestsRequired()) {
|
||||
case PackageDetailAvailability::Request:
|
||||
m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames());
|
||||
break;
|
||||
case PackageDetailAvailability::FullRequest:
|
||||
m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames());
|
||||
break;
|
||||
case PackageDetailAvailability::Never:
|
||||
m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name());
|
||||
emit finished();
|
||||
return;
|
||||
case PackageDetailAvailability::Immediately:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_reply) {
|
||||
m_reply->setParent(this);
|
||||
connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady);
|
||||
} else {
|
||||
sourceReady();
|
||||
}
|
||||
requestSources();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -84,16 +63,58 @@ inline const UpgradeLookupResults &UpgradeLookupProcess::results() const
|
|||
return m_results;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to request the sources.
|
||||
*/
|
||||
void UpgradeLookupProcess::requestSources()
|
||||
{
|
||||
// ensure the repository to check and the upgrade source are both not busy
|
||||
if(m_toCheck->isBusy()) {
|
||||
connect(m_toCheck, &Repository::available, this, &UpgradeLookupProcess::requestSources);
|
||||
} else {
|
||||
disconnect(m_toCheck, nullptr, this, nullptr);
|
||||
}
|
||||
if(m_upgradeSource->isBusy()) {
|
||||
connect(m_upgradeSource, &Repository::available, this, &UpgradeLookupProcess::requestSources);
|
||||
} else {
|
||||
disconnect(m_upgradeSource, nullptr, this, nullptr);
|
||||
}
|
||||
// request sources if required
|
||||
switch(m_upgradeSource->requestsRequired()) {
|
||||
case PackageDetailAvailability::Request:
|
||||
m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames());
|
||||
break;
|
||||
case PackageDetailAvailability::FullRequest:
|
||||
m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames());
|
||||
break;
|
||||
case PackageDetailAvailability::Never:
|
||||
m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name());
|
||||
emit finished();
|
||||
return;
|
||||
case PackageDetailAvailability::Immediately:
|
||||
break;
|
||||
}
|
||||
if(m_reply) {
|
||||
// a request is required -> wait until results are available
|
||||
m_reply->setParent(this);
|
||||
connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady);
|
||||
} else {
|
||||
// no request required -> call sourceReady immidiately
|
||||
sourceReady();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called when the upgrade source is ready.
|
||||
*/
|
||||
void UpgradeLookupProcess::sourceReady()
|
||||
{
|
||||
// if a request was required, check whether there occured an error
|
||||
// if a request was required, check whether an error occured
|
||||
if(m_reply && !m_reply->error().isEmpty()) {
|
||||
m_results.errors << m_reply->error();
|
||||
emit finished();
|
||||
} else {
|
||||
// invoke the actual upgrade lookup
|
||||
connect(m_watcher, &QFutureWatcher<void>::finished, this, &UpgradeLookupProcess::finished);
|
||||
m_watcher->setFuture(QtConcurrent::run(this, &UpgradeLookupProcess::checkUpgrades));
|
||||
}
|
||||
|
@ -210,7 +231,9 @@ UpgradeLookupJson::UpgradeLookupJson(Manager &manager, const QJsonObject &reques
|
|||
|
||||
void UpgradeLookupJson::processFinished()
|
||||
{
|
||||
#ifdef DEBUG_BUILD
|
||||
assert(m_remainingProcesses);
|
||||
#endif
|
||||
// add results
|
||||
const auto &results = static_cast<UpgradeLookupProcess *>(sender())->results();
|
||||
for(const auto &res : results.newVersions) {
|
||||
|
@ -301,7 +324,9 @@ UpgradeLookupCli::UpgradeLookupCli(Manager &manager, const string &repo, QObject
|
|||
|
||||
void UpgradeLookupCli::processFinished()
|
||||
{
|
||||
#ifdef DEBUG_BUILD
|
||||
assert(m_remainingProcesses);
|
||||
#endif
|
||||
// add results
|
||||
const auto &results = static_cast<UpgradeLookupProcess *>(sender())->results();
|
||||
m_softwareUpgradesArray.reserve(m_softwareUpgradesArray.size() + results.newVersions.size());
|
||||
|
|
|
@ -91,6 +91,7 @@ signals:
|
|||
void finished();
|
||||
|
||||
private slots:
|
||||
void requestSources();
|
||||
void sourceReady();
|
||||
void checkUpgrades();
|
||||
|
||||
|
|
|
@ -112,8 +112,8 @@ QJsonArray validationMethodsStrings(PackageValidation validationMethods)
|
|||
if(validationMethods & PackageValidation::Sha256Sum) {
|
||||
jsonArray << QStringLiteral("SHA256");
|
||||
}
|
||||
if(validationMethods & PackageValidation::Signature) {
|
||||
jsonArray << QStringLiteral("signature");
|
||||
if(validationMethods & PackageValidation::PgpSignature) {
|
||||
jsonArray << QStringLiteral("PGP signature");
|
||||
}
|
||||
//if(jsonArray.empty()) {
|
||||
// jsonArray << QStringLiteral("unknown");
|
||||
|
|
103
general.pri
103
general.pri
|
@ -1,103 +0,0 @@
|
|||
# specify build directories for moc, object and rcc files
|
||||
MOC_DIR = ./moc
|
||||
OBJECTS_DIR = ./obj
|
||||
RCC_DIR = ./res
|
||||
|
||||
# compiler flags: enable C++11
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
QMAKE_LFLAGS += -std=c++11
|
||||
|
||||
# disable new ABI (can't catch ios_base::failure with new ABI)
|
||||
DEFINES += _GLIBCXX_USE_CXX11_ABI=0
|
||||
|
||||
# variables to check target architecture
|
||||
win32-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
|
||||
win32-g++-32:QMAKE_TARGET.arch = x86
|
||||
win32-g++-64:QMAKE_TARGET.arch = x86_64
|
||||
linux-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
|
||||
linux-g++-32:QMAKE_TARGET.arch = x86
|
||||
linux-g++-64:QMAKE_TARGET.arch = x86_64
|
||||
|
||||
# determine and print target prefix
|
||||
targetprefix = $$(TARGET_PREFIX)
|
||||
message("Using target prefix \"$${targetprefix}\".")
|
||||
|
||||
# print install root
|
||||
message("Using install root \"$$(INSTALL_ROOT)\".")
|
||||
|
||||
# set target
|
||||
CONFIG(debug, debug|release) {
|
||||
TARGET = $${targetprefix}$${projectname}d
|
||||
} else {
|
||||
TARGET = $${targetprefix}$${projectname}
|
||||
}
|
||||
|
||||
# add defines for meta data
|
||||
DEFINES += "APP_METADATA_AVAIL"
|
||||
DEFINES += "'PROJECT_NAME=\"$${projectname}\"'"
|
||||
DEFINES += "'APP_NAME=\"$${appname}\"'"
|
||||
DEFINES += "'APP_AUTHOR=\"$${appauthor}\"'"
|
||||
DEFINES += "'APP_URL=\"$${appurl}\"'"
|
||||
DEFINES += "'APP_VERSION=\"$${VERSION}\"'"
|
||||
|
||||
# configure Qt modules and defines
|
||||
mobile {
|
||||
DEFINES += CONFIG_MOBILE
|
||||
} else:desktop {
|
||||
DEFINES += CONFIG_DESKTOP
|
||||
} else:android {
|
||||
CONFIG += mobile
|
||||
DEFINES += CONFIG_MOBILE
|
||||
} else {
|
||||
CONFIG += desktop
|
||||
DEFINES += CONFIG_DESKTOP
|
||||
}
|
||||
no-gui {
|
||||
QT -= gui
|
||||
DEFINES += GUI_NONE
|
||||
guiqtquick || guiqtwidgets {
|
||||
error("Can not use no-gui with guiqtquick or guiqtwidgets.")
|
||||
} else {
|
||||
message("Configured for no GUI support.")
|
||||
}
|
||||
} else {
|
||||
QT += gui
|
||||
mobile {
|
||||
CONFIG += guiqtquick
|
||||
}
|
||||
desktop {
|
||||
CONFIG += guiqtwidgets
|
||||
}
|
||||
}
|
||||
guiqtquick {
|
||||
message("Configured for Qt Quick GUI support.")
|
||||
QT += quick
|
||||
CONFIG(debug, debug|release) {
|
||||
CONFIG += qml_debug
|
||||
}
|
||||
DEFINES += GUI_QTQUICK
|
||||
}
|
||||
guiqtwidgets {
|
||||
message("Configured for Qt widgets GUI support.")
|
||||
QT += widgets
|
||||
DEFINES += GUI_QTWIDGETS
|
||||
DEFINES += MODEL_UNDO_SUPPORT
|
||||
}
|
||||
|
||||
# configuration for cross compliation with mingw-w64
|
||||
win32 {
|
||||
QMAKE_TARGET_PRODUCT = "$${appname}"
|
||||
QMAKE_TARGET_COPYRIGHT = "by $${appauthor}"
|
||||
}
|
||||
mingw-w64-manualstrip-dll {
|
||||
QMAKE_POST_LINK=$${CROSS_COMPILE}strip --strip-unneeded ./release/$(TARGET); \
|
||||
$${CROSS_COMPILE}strip --strip-unneeded ./release/lib$(TARGET).a
|
||||
}
|
||||
mingw-w64-manualstrip-exe {
|
||||
QMAKE_POST_LINK=$${CROSS_COMPILE}strip --strip-unneeded ./release/$(TARGET)
|
||||
}
|
||||
mingw-w64-noversion {
|
||||
TARGET_EXT = ".dll"
|
||||
TARGET_VERSION_EXT = ""
|
||||
CONFIG += skip_target_version_ext
|
||||
}
|
9
main.cpp
9
main.cpp
|
@ -15,6 +15,8 @@
|
|||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QReadWriteLock>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
using namespace RepoIndex;
|
||||
|
@ -53,9 +55,11 @@ int main(int argc, char *argv[])
|
|||
// setup manager
|
||||
Manager manager(config);
|
||||
cerr << shchar << "Loading databases ..." << endl;
|
||||
manager.addDataBasesFromPacmanConfig();
|
||||
if(config.areReposFromPacmanConfEnabled()) {
|
||||
manager.addDataBasesFromPacmanConfig();
|
||||
}
|
||||
manager.addDatabasesFromRepoIndexConfig();
|
||||
manager.initAlpmDataBases(configArgs.serverArg.isPresent());
|
||||
manager.initAlpmDataBases();
|
||||
cerr << shchar << "Restoring cache ... ";
|
||||
manager.restoreCache();
|
||||
cerr << shchar << "DONE" << endl;
|
||||
|
@ -64,6 +68,7 @@ int main(int argc, char *argv[])
|
|||
// setup the server
|
||||
Server server(manager, manager.config());
|
||||
manager.setAutoCacheMaintenanceEnabled(true);
|
||||
manager.setAutoUpdateEnabled(true);
|
||||
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
|
||||
return application.exec();
|
||||
} else if(configArgs.buildOrderArg.isPresent()) {
|
||||
|
|
|
@ -18,10 +18,10 @@ using namespace std;
|
|||
|
||||
namespace RepoIndex {
|
||||
|
||||
Connection::Connection(Manager &alpmManager, QWebSocket *socket, QObject *parent) :
|
||||
Connection::Connection(Manager &manager, QWebSocket *socket, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_socket(socket),
|
||||
m_manager(alpmManager),
|
||||
m_manager(manager),
|
||||
m_repoInfoUpdatesRequested(false),
|
||||
m_groupInfoUpdatesRequested(false)
|
||||
{
|
||||
|
@ -29,6 +29,7 @@ Connection::Connection(Manager &alpmManager, QWebSocket *socket, QObject *parent
|
|||
connect(socket, &QWebSocket::textMessageReceived, this, &Connection::processTextMessage);
|
||||
connect(socket, &QWebSocket::binaryMessageReceived, this, &Connection::processBinaryMessage);
|
||||
connect(socket, &QWebSocket::disconnected, this, &Connection::socketDisconnected);
|
||||
connect(&manager, &Manager::updatesAvailable, this, &Connection::updatedAvailable);
|
||||
}
|
||||
|
||||
void Connection::sendJson(const QJsonObject &obj)
|
||||
|
@ -59,7 +60,7 @@ void Connection::sendResult(const QJsonValue &what, const QJsonValue &id, const
|
|||
sendJson(response);
|
||||
}
|
||||
|
||||
void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonArray &values)
|
||||
void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonValue &values)
|
||||
{
|
||||
QJsonObject response;
|
||||
response.insert(QStringLiteral("class"), QStringLiteral("results"));
|
||||
|
@ -71,6 +72,16 @@ void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const
|
|||
sendJson(response);
|
||||
}
|
||||
|
||||
void Connection::updatedAvailable()
|
||||
{
|
||||
if(m_repoInfoUpdatesRequested) {
|
||||
sendResult(QStringLiteral("basicrepoinfo"), QJsonValue(), m_manager.basicRepoInfo());
|
||||
}
|
||||
if(m_groupInfoUpdatesRequested) {
|
||||
sendResults(QStringLiteral("groupinfo"), QJsonValue(), m_manager.groupInfo());
|
||||
}
|
||||
}
|
||||
|
||||
template<class Lookup, typename... Args>
|
||||
void Connection::performLookup(const QJsonObject &request, Args &&...args)
|
||||
{
|
||||
|
@ -129,12 +140,23 @@ void Connection::handleCmd(const QJsonObject &obj)
|
|||
}
|
||||
} else if(what == QLatin1String("reinitalpm")) {
|
||||
if(m_socket->peerAddress().isLoopback()) {
|
||||
cerr << shchar << "Info: Reinit of ALPM databases triggered via web interface." << endl;
|
||||
cerr << shchar << "Info: Re-initializing ALPM databases (triggered via web interface) ..." << endl;
|
||||
m_manager.removeAllDatabases();
|
||||
m_manager.addLocalDatabase();
|
||||
m_manager.addDataBasesFromPacmanConfig();
|
||||
if(m_manager.config().isLocalDatabaseEnabled()) {
|
||||
m_manager.addLocalDatabase();
|
||||
}
|
||||
if(m_manager.config().areReposFromPacmanConfEnabled()) {
|
||||
m_manager.addDataBasesFromPacmanConfig();
|
||||
}
|
||||
m_manager.addDatabasesFromRepoIndexConfig();
|
||||
m_manager.initAlpmDataBases(true);
|
||||
m_manager.initAlpmDataBases();
|
||||
} else {
|
||||
sendError(QStringLiteral("rejected"), id);
|
||||
}
|
||||
} else if(what == QLatin1String("updatealpm")) {
|
||||
if(m_socket->peerAddress().isLoopback()) {
|
||||
cerr << shchar << "Info: Forcing update of all ALPM databases (triggered via web interface) ..." << endl;
|
||||
m_manager.forceUpdateAlpmDatabases();
|
||||
} else {
|
||||
sendError(QStringLiteral("rejected"), id);
|
||||
}
|
||||
|
|
|
@ -17,14 +17,15 @@ class Connection : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Connection(RepoIndex::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
|
||||
Connection(Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void processTextMessage(const QString &message);
|
||||
void processBinaryMessage(const QByteArray &message);
|
||||
void socketDisconnected();
|
||||
void sendResult(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value);
|
||||
void sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonArray &values);
|
||||
void sendResults(const QJsonValue &what, const QJsonValue &id, const QJsonValue &values);
|
||||
void updatedAvailable();
|
||||
|
||||
private:
|
||||
void sendJson(const QJsonObject &obj);
|
||||
|
@ -39,6 +40,8 @@ private:
|
|||
bool m_groupInfoUpdatesRequested;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // CONNECTION_H
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
},
|
||||
|
||||
"cacheDir": ".",
|
||||
"storageDir": ".",
|
||||
|
||||
"aur": {
|
||||
"enabled": true
|
||||
|
@ -19,6 +20,7 @@
|
|||
},
|
||||
|
||||
"repos": {
|
||||
"localEnabled": true,
|
||||
"fromPacmanConfig": true,
|
||||
|
||||
"add": [
|
||||
|
@ -30,13 +32,22 @@
|
|||
"https://localhost/repo/arch/$repo/os/$arch"
|
||||
]
|
||||
},
|
||||
|
||||
{"name": "local", "maxAge": 3600},
|
||||
{"name": "core", "maxAge": 28800},
|
||||
{"name": "extra", "maxAge": 28800},
|
||||
{"name": "community", "maxAge": 28800},
|
||||
{"name": "multilib", "maxAge": 28800},
|
||||
|
||||
|
||||
{"name": "ownstuff-testing",
|
||||
"sourcesDir": "path/to/local/source/dir",
|
||||
"packagesDir": "/run/media/devel/repo/arch/ownstuff-testing/os/x86_64",
|
||||
"upgradeSources": ["aur"],
|
||||
"server": [
|
||||
"ignore": true,
|
||||
"sourcesDir": "path/to/local/source/dir",
|
||||
"packagesDir": "/run/media/devel/repo/arch/ownstuff-testing/os/x86_64",
|
||||
"upgradeSources": ["aur"],
|
||||
"server": [
|
||||
"https://localhost/repo/arch/$repo/os/$arch"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<li id="nav_packages" class="active"><a href="#packages">Packages <span class="sr-only">(current)</span></a></li>
|
||||
<li id="nav_groups"><a href="#groups">Groups</a></li>
|
||||
<li id="nav_repositories"><a href="#repositories">Repositories</a></li>
|
||||
<li id="nav_settings"><a id="link_settings" href="#" onclick="return false;" aria-label="Settings" data-toggle="popover" data-placement="bottom" title="Settings" data-content="TODO"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a></li>
|
||||
<!--<li id="nav_settings"><a id="link_settings" href="#" onclick="return false;" aria-label="Settings" data-toggle="popover" data-placement="bottom" title="Settings" data-content="TODO"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a></li>-->
|
||||
<li id="nav_about"><a id="link_settings" href="#" onclick="$('#dlg_about').modal('show'); return false;"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-left" target="#" onsubmit="repoindex.pageManager.applySearchTerm(this.searchtermInput.value, this.searchtermExact.checked, true); return false;">
|
||||
|
@ -75,7 +75,8 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" onclick="repoindex.client.stopServer(); return false;">Stop</a></li>
|
||||
<li><a href="#" onclick="repoindex.client.reinitAlpm(); return false;">Reinit ALPM</a></li>
|
||||
<li><a href="#" onclick="repoindex.client.reinitAlpm(); return false;">Reinit ALPM databases</a></li>
|
||||
<li><a href="#" onclick="repoindex.client.updateAlpm(); return false;">Update ALPM databases</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button id="nav_connect" class="btn btn-danger" onclick="repoindex.client.init();"><span class="glyphicon glyphicon glyphicon-refresh" aria-hidden="true" id="connection_glyphicon"></span> <span id="connection_status">Disconnected</span></button>
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
FullPackageInfo: "fullpkginfo",
|
||||
GroupInfo: "groupinfo",
|
||||
UpgradeLookup: "upgradelookup",
|
||||
Suggestions: "suggestions"
|
||||
Suggestions: "suggestions",
|
||||
Postponed: "postponed"
|
||||
};
|
||||
|
||||
repoindex.isLoopback = function(domain) {
|
||||
|
@ -183,7 +184,7 @@
|
|||
repoindex.pageManager.addError("Server replied \"" + what + "\" with insufficiant data.");
|
||||
} else {
|
||||
var request = this.requestById(id);
|
||||
if(request && request.sent) {
|
||||
if(!id || (request && request.sent)) {
|
||||
// use the received information
|
||||
switch(what) {
|
||||
case repoindex.RequestType.BasicRepoInfo:
|
||||
|
@ -204,13 +205,22 @@
|
|||
case repoindex.RequestType.Suggestions:
|
||||
this.useSuggestions(values);
|
||||
break;
|
||||
case repoindex.RequestType.Postponed:
|
||||
// server is busy, but will send results later
|
||||
pageManager.addError("The server is busy.");
|
||||
// -> do not call the callbacks already
|
||||
request = undefined;
|
||||
break;
|
||||
default:
|
||||
pageManager.addError("Server replied unknown results: " + what);
|
||||
return; // don't invoke callbacks when results are of unknown type
|
||||
}
|
||||
// also invoke callbacks registred for this request
|
||||
for(var i = 0; i < request.callbacks.length; ++i) {
|
||||
request.callbacks[i](values);
|
||||
if(request) {
|
||||
// also invoke callbacks registred for this request
|
||||
for(var i = 0; i < request.callbacks.length; ++i) {
|
||||
request.callbacks[i](values);
|
||||
}
|
||||
// TODO: remove request object from list of sent requests
|
||||
}
|
||||
} else {
|
||||
repoindex.pageManager.addError("Server replied \"" + what + "\" with unknown ID.");
|
||||
|
@ -220,11 +230,11 @@
|
|||
|
||||
// define functions to perform several requests
|
||||
this.requestBasicRepoInfo = function(callback) {
|
||||
this.scheduleRequest(repoindex.RequestType.BasicRepoInfo, {upgrades: "true"}, callback);
|
||||
this.scheduleRequest(repoindex.RequestType.BasicRepoInfo, {updates: true}, callback);
|
||||
};
|
||||
|
||||
this.requestBasicPackagesInfo = function(packageSelection, callback) {
|
||||
this.scheduleRequest(repoindex.RequestType.BasicPackageInfo, {sel: packageSelection}, callback);
|
||||
this.scheduleRequest(repoindex.RequestType.BasicPackageInfo, {sel: packageSelection, updates: true}, callback);
|
||||
};
|
||||
|
||||
this.requestFullPackagesInfo = function(packageSelection, callback) {
|
||||
|
@ -232,7 +242,7 @@
|
|||
};
|
||||
|
||||
this.requestGroupInfo = function(callback) {
|
||||
this.scheduleRequest(repoindex.RequestType.GroupInfo, {upgrades: "true"}, callback);
|
||||
this.scheduleRequest(repoindex.RequestType.GroupInfo, {updates: true}, callback);
|
||||
};
|
||||
|
||||
this.requestSuggestions = function(repoNames, searchTerm, callback) {
|
||||
|
@ -266,6 +276,10 @@
|
|||
this.sendCmd("reinitalpm");
|
||||
};
|
||||
|
||||
this.updateAlpm = function() {
|
||||
this.sendCmd("updatealpm");
|
||||
};
|
||||
|
||||
this.checkForUpgrades = function(dbName, syncdbNames, callback) {
|
||||
var params = {
|
||||
db: dbName
|
||||
|
@ -303,11 +317,16 @@
|
|||
var pkgMgr = repoindex.pageManager.packageManager;
|
||||
repoMgr.removeEntries();
|
||||
pkgMgr.removeEntries();
|
||||
var incomplete = false;
|
||||
var reposInOrder = [];
|
||||
for(var repoName in value) {
|
||||
if(value.hasOwnProperty(repoName)) {
|
||||
reposInOrder.push({name: repoName, info: value[repoName]});
|
||||
//var repoInfo = value[repoName];
|
||||
var info = value[repoName];
|
||||
if(info.incomplete) {
|
||||
incomplete = true;
|
||||
} else {
|
||||
reposInOrder.push({name: repoName, info: info});
|
||||
}
|
||||
}
|
||||
}
|
||||
reposInOrder.sort(function(lhs, rhs) {
|
||||
|
@ -337,7 +356,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(incomplete) {
|
||||
repoindex.pageManager.addError("Server replied incomplete repository info: Server is busy.");
|
||||
}
|
||||
this.hasBasicRepoInfo = true;
|
||||
pkgMgr.invalidate();
|
||||
repoMgr.invalidate();
|
||||
|
@ -369,7 +390,24 @@
|
|||
}
|
||||
}
|
||||
if(value.error) {
|
||||
repoindex.pageManager.addError("Server replied error in package info: " + value.error);
|
||||
switch(value.error) {
|
||||
case "na":
|
||||
if(value.repo) {
|
||||
if(value.name) {
|
||||
repoindex.pageManager.addError("Server replied error: The package " + value.name + " doesn't exist in the repository " + value.repo + ".");
|
||||
} else {
|
||||
repoindex.pageManager.addError("Server replied error: The repository " + value.repo + " doesn't exist.");
|
||||
}
|
||||
} else {
|
||||
repoindex.pageManager.addError("Server replied error: The requested info is not available.");
|
||||
}
|
||||
break;
|
||||
case "busy":
|
||||
repoindex.pageManager.addError("Server replied error in package info: The server is busy.");
|
||||
break;
|
||||
default:
|
||||
repoindex.pageManager.addError("Server replied unknown error code in package info: " + value.error);
|
||||
}
|
||||
}
|
||||
// updating table rows or any other GUI elements is done via callbacks
|
||||
};
|
||||
|
@ -377,9 +415,13 @@
|
|||
this.useGroupInfo = function(values) {
|
||||
var groupMgr = repoindex.pageManager.groupManager;
|
||||
groupMgr.removeEntries();
|
||||
var incomplete = false;
|
||||
var groupEntries = groupMgr.entries;
|
||||
for(var i1 = 0; i1 < values.length; ++i1) {
|
||||
var info = values[i1];
|
||||
if(info.incomplete) {
|
||||
incomplete = true;
|
||||
}
|
||||
if(info.repo && info.groups) {
|
||||
for(var i2 = 0; i2 < info.groups.length; ++i2) {
|
||||
var group = info.groups[i2];
|
||||
|
@ -387,6 +429,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
if(incomplete) {
|
||||
repoindex.pageManager.addError("Server replied incomplete group info: Server is busy.");
|
||||
}
|
||||
this.hasGroupInfo = true;
|
||||
groupMgr.useRequestedData();
|
||||
};
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
};
|
||||
|
||||
this.remove = function() {
|
||||
this.rowElement.parent.removeChild(this.rowElement);
|
||||
this.rowElement.parentNode.removeChild(this.rowElement);
|
||||
};
|
||||
|
||||
this.hide = function() {
|
||||
|
|
|
@ -171,11 +171,13 @@
|
|||
if(details.chkd && details.chkd.length) {
|
||||
repoindex.addPackageNames(tb, "Check deps", repoindex.pkgNamesFromDeps(details.chkd));
|
||||
}
|
||||
repoindex.addPackageNames(tb, "Required by", details.requ);
|
||||
repoindex.addPackageNames(tb, "Optional for", details.optf);
|
||||
if(details.requ || details.optf) {
|
||||
repoindex.addPackageNames(tb, "Required by", details.requ);
|
||||
repoindex.addPackageNames(tb, "Optional for", details.optf);
|
||||
}
|
||||
repoindex.addPackageNames(tb, "Conflicts with", repoindex.pkgNamesFromDeps(details.conf));
|
||||
repoindex.addPackageNames(tb, "Replaces", repoindex.pkgNamesFromDeps(details.repl));
|
||||
if(details.buildAvail) {
|
||||
if(details.bav) {
|
||||
if(entry.info.repo !== "local") { // local repo does no provide package size
|
||||
repoindex.addField(tb, "Package size", repoindex.makeDataSize(details.csize));
|
||||
}
|
||||
|
@ -187,14 +189,31 @@
|
|||
repoindex.addField(tb, "Install reason", details.expl ? "explicitly installed" : "installed as dependency");
|
||||
}
|
||||
repoindex.addField(tb, "Install script", repoindex.makeBool(details.scri));
|
||||
repoindex.addField(tb, "Validated by", repoindex.makeArray(details.sig));
|
||||
repoindex.addField(tb, "Validation methods", repoindex.makeArray(details.sig, ", "));
|
||||
repoindex.setTree(repoindex.addField(tb, "Package files"), repoindex.makeTree(details.files));
|
||||
}
|
||||
if(details.sav) {
|
||||
if(details.main) {
|
||||
repoindex.addField(tb, "Maintainer", repoindex.makeStr(details.main));
|
||||
}
|
||||
if(basics.flagdate) {
|
||||
repoindex.addField(tb, "Out-of-date", repoindex.makeStr(basics.flagdate));
|
||||
}
|
||||
if(details.fsub) {
|
||||
repoindex.addField(tb, "First submitted", repoindex.makeStr(details.fsub));
|
||||
}
|
||||
if(details.lmod) {
|
||||
repoindex.addField(tb, "Last modified", repoindex.makeStr(details.lmod));
|
||||
}
|
||||
if(details.votes) {
|
||||
repoindex.addField(tb, "Votes", repoindex.makeStr(details.votes));
|
||||
}
|
||||
}
|
||||
// -> update download buttons
|
||||
if(details.buildAvail || details.srcAvail) {
|
||||
if(details.bav || details.sav) {
|
||||
var downloadElement = repoindex.addField(tb, "Download");
|
||||
var spanElement;
|
||||
if(details.buildAvail) {
|
||||
if(details.bav) {
|
||||
spanElement = document.createElement("span");
|
||||
var downloadPkgParams = {repo: entry.info.repo, pkg: entry.name, down: "pkg"};
|
||||
repoindex.setDownloadButton(spanElement, "package", repoindex.makeHash(repoindex.Pages.Packages, downloadPkgParams, true), function() {
|
||||
|
@ -203,10 +222,13 @@
|
|||
});
|
||||
downloadElement.appendChild(spanElement);
|
||||
}
|
||||
if(details.srcAvail) {
|
||||
if(details.srctar && typeof details.srctar === "string") {
|
||||
spanElement = document.createElement("span");
|
||||
var downloadSrcParams = {repo: entry.info.repo, pkg: entry.name, down: "src"};
|
||||
repoindex.setDownloadButton(spanElement, "source", repoindex.makeHash(repoindex.Pages.Packages, downloadSrcParams, true));
|
||||
repoindex.setDownloadButton(spanElement, "source", repoindex.makeHash(repoindex.Pages.Packages, downloadSrcParams, true), function() {
|
||||
repoindex.pageManager.denoteHash(repoindex.Pages.Packages, downloadSrcParams);
|
||||
window.open("https://aur.archlinux.org" + details.srctar);
|
||||
});
|
||||
downloadElement.appendChild(spanElement);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
RepoEntry.prototype.constructor = RepoEntry;
|
||||
RepoEntry = function(repoName, repoInfo, enabled) {
|
||||
if(enabled === undefined) {
|
||||
// per default enable all repos with a fix number of packages
|
||||
enabled = repoInfo.packageCount;
|
||||
// per default enable all repos with a fix number of packages except the local database
|
||||
enabled = repoInfo.packageCount && repoName !== "local";
|
||||
}
|
||||
repoindex.Entry.prototype.constructor.call(this, repoName, repoInfo, enabled);
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
|||
};
|
||||
|
||||
this.link.remove = function() {
|
||||
this.parent.removeChild(this);
|
||||
this.parentNode.removeChild(this);
|
||||
};
|
||||
};
|
||||
this.createLink();
|
||||
|
|
Loading…
Reference in New Issue