fixed bugs
This commit is contained in:
parent
2dd0294bb7
commit
920eddbeed
|
@ -39,3 +39,7 @@ Makefile*
|
||||||
|
|
||||||
# documentation
|
# documentation
|
||||||
/doc
|
/doc
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/testing
|
||||||
|
/cert
|
||||||
|
|
|
@ -52,7 +52,7 @@ private:
|
||||||
const PackageOrigin m_origin;
|
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());
|
QFileInfo pathInfo(databasePath());
|
||||||
if(pathInfo.isDir()) {
|
if(pathInfo.isDir()) {
|
||||||
|
@ -71,7 +71,7 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
||||||
if(descFile.open(QFile::ReadOnly)) {
|
if(descFile.open(QFile::ReadOnly)) {
|
||||||
descData << descFile.readAll();
|
descData << descFile.readAll();
|
||||||
} else {
|
} else {
|
||||||
// TODO: error handling (can't open pkg file)
|
return DatabaseError::UnableToOpenDescFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!descData.isEmpty()) {
|
if(!descData.isEmpty()) {
|
||||||
|
@ -79,7 +79,7 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
||||||
}
|
}
|
||||||
dbDir.cdUp();
|
dbDir.cdUp();
|
||||||
} else {
|
} else {
|
||||||
// TODO: error handling (can't enter pkg dir)
|
return DatabaseError::UnableToEnterDirectory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(pathInfo.isFile()) {
|
} else if(pathInfo.isFile()) {
|
||||||
|
@ -101,7 +101,7 @@ void AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &
|
||||||
if(descEntry->isFile()) {
|
if(descEntry->isFile()) {
|
||||||
descData << static_cast<const KArchiveFile *>(descEntry)->data();
|
descData << static_cast<const KArchiveFile *>(descEntry)->data();
|
||||||
} else {
|
} 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);
|
descriptions << qMakePair(pkgDirName, descData);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// there shouldn't be any files
|
// there shouldn't be any files anyways
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: error handling (can't open sync db file)
|
return DatabaseError::UnableToOpenArchive;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
if((m_error = repository->loadDescriptions(m_descriptions)) == DatabaseError::NoError) {
|
||||||
m_future = QtConcurrent::map(m_descriptions, LoadPackage(repository, origin));
|
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;
|
m_sigLevel = sigLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
AlpmPackageLoader *AlpmDatabase::init()
|
AlpmPackageLoader *AlpmDatabase::internalInit()
|
||||||
{
|
{
|
||||||
// set description, determine origin
|
// set description, determine origin
|
||||||
PackageOrigin origin;
|
PackageOrigin origin;
|
||||||
|
@ -157,15 +160,16 @@ AlpmPackageLoader *AlpmDatabase::init()
|
||||||
// wipe current packages
|
// wipe current packages
|
||||||
wipePackages();
|
wipePackages();
|
||||||
|
|
||||||
// initialization of packages is done concurrently via AlpmPackageLoader: ~ 4 sec
|
// initialization of packages is done concurrently via AlpmPackageLoader
|
||||||
return new AlpmPackageLoader(this, origin);
|
return new AlpmPackageLoader(this, origin);
|
||||||
|
|
||||||
// without concurrency: ~ 12 sec
|
// without concurrency
|
||||||
//QList<QPair<QString, QList<QByteArray> > > descriptions;
|
//QList<QPair<QString, QList<QByteArray> > > descriptions;
|
||||||
//loadDescriptions(descriptions);
|
//loadDescriptions(descriptions);
|
||||||
//for(const auto &description : descriptions) {
|
//for(const auto &description : descriptions) {
|
||||||
// addPackageFromDescription(description.first, description.second, origin);
|
// addPackageFromDescription(description.first, description.second, origin);
|
||||||
//}
|
//}
|
||||||
|
//emit initialized();
|
||||||
//return nullptr;
|
//return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,8 +213,9 @@ QNetworkRequest AlpmDatabase::filesDatabaseRequest()
|
||||||
*/
|
*/
|
||||||
void AlpmDatabase::downloadDatabase(const QString &targetDir, bool filesDatabase)
|
void AlpmDatabase::downloadDatabase(const QString &targetDir, bool filesDatabase)
|
||||||
{
|
{
|
||||||
|
QWriteLocker locker(lock());
|
||||||
if(serverUrls().isEmpty()) {
|
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;
|
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());
|
QNetworkReply *reply = networkAccessManager().get(filesDatabase ? filesDatabaseRequest() : regularDatabaseRequest());
|
||||||
|
@ -248,28 +253,42 @@ std::unique_ptr<Package> AlpmDatabase::emptyPackage()
|
||||||
void AlpmDatabase::databaseDownloadFinished()
|
void AlpmDatabase::databaseDownloadFinished()
|
||||||
{
|
{
|
||||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||||
|
reply->deleteLater();
|
||||||
bool filesDatabase = reply->property("filesDatabase").toBool();
|
bool filesDatabase = reply->property("filesDatabase").toBool();
|
||||||
|
QReadLocker locker(lock());
|
||||||
if(reply->error() == QNetworkReply::NoError) {
|
if(reply->error() == QNetworkReply::NoError) {
|
||||||
|
QString newDatabasePath;
|
||||||
cerr << "Downloaded database file for [" << name().toLocal8Bit().data() << "] successfully." << endl;
|
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)) {
|
if(QFile::exists(newDatabasePath)) {
|
||||||
QString backupFile(newDatabasePath + QStringLiteral(".bak"));
|
QString backupFile(newDatabasePath + QStringLiteral(".bak"));
|
||||||
QFile::remove(backupFile);
|
QFile::remove(backupFile);
|
||||||
if(!QFile::rename(newDatabasePath, 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;
|
cerr << "An IO error occured when storing database file for [" << name().toLocal8Bit().data() << "]: Unable to rename present database file." << endl;
|
||||||
|
reply = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
locker.unlock();
|
||||||
QFile outputFile(newDatabasePath);
|
QFile outputFile(newDatabasePath);
|
||||||
if(outputFile.open(QFile::WriteOnly) && outputFile.write(reply->readAll())) {
|
if(outputFile.open(QFile::WriteOnly) && outputFile.write(reply->readAll())) {
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
m_dbPath = newDatabasePath;
|
{
|
||||||
init();
|
QWriteLocker locker(lock());
|
||||||
|
m_dbPath = newDatabasePath;
|
||||||
|
}
|
||||||
|
initAsSoonAsPossible();
|
||||||
} else {
|
} else {
|
||||||
|
locker.relock();
|
||||||
cerr << "An IO error occured when storing database file for [" << name().toLocal8Bit().data() << "]: Unable to create/write output file." << endl;
|
cerr << "An IO error occured when storing database file for [" << name().toLocal8Bit().data() << "]: Unable to create/write output file." << endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cerr << "An error occured when dwonloading database file for [" << name().toLocal8Bit().data() << "]: " << reply->errorString().toLocal8Bit().data() << endl;
|
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 AlpmDatabase;
|
||||||
class LoadPackage;
|
class LoadPackage;
|
||||||
|
|
||||||
|
enum class DatabaseError
|
||||||
|
{
|
||||||
|
NoError,
|
||||||
|
NotFound,
|
||||||
|
NoAccess,
|
||||||
|
UnableToOpenArchive,
|
||||||
|
UnableToOpenDescFile,
|
||||||
|
UnableToEnterDirectory
|
||||||
|
};
|
||||||
|
|
||||||
class AlpmPackageLoader : public PackageLoader
|
class AlpmPackageLoader : public PackageLoader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AlpmPackageLoader(AlpmDatabase *db, PackageOrigin origin);
|
AlpmPackageLoader(AlpmDatabase *db, PackageOrigin origin);
|
||||||
|
AlpmDatabase *database() const;
|
||||||
|
DatabaseError error() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
AlpmDatabase *const m_db;
|
||||||
|
DatabaseError m_error;
|
||||||
QList<QPair<QString, QList<QByteArray> > > m_descriptions;
|
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
|
class AlpmDatabase : public Repository
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
friend class AlpmPackageLoader;
|
friend class AlpmPackageLoader;
|
||||||
public:
|
public:
|
||||||
explicit AlpmDatabase(const QString &name, const QString &dbPath, RepositoryUsage usage, SignatureLevel sigLevel, uint32 index = invalidIndex, QObject *parent = nullptr);
|
explicit AlpmDatabase(const QString &name, const QString &dbPath, RepositoryUsage usage, SignatureLevel sigLevel, uint32 index = invalidIndex, QObject *parent = nullptr);
|
||||||
AlpmPackageLoader *init();
|
AlpmPackageLoader *internalInit();
|
||||||
|
|
||||||
RepositoryType type() const;
|
RepositoryType type() const;
|
||||||
PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const;
|
PackageDetailAvailability requestsRequired(PackageDetail packageDetail) const;
|
||||||
|
@ -44,9 +76,6 @@ public:
|
||||||
void downloadDatabase(const QString &targetDir, bool filesDatabase = true);
|
void downloadDatabase(const QString &targetDir, bool filesDatabase = true);
|
||||||
void refresh(const QString &targetDir);
|
void refresh(const QString &targetDir);
|
||||||
|
|
||||||
signals:
|
|
||||||
void initiated();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<Package> emptyPackage();
|
std::unique_ptr<Package> emptyPackage();
|
||||||
|
|
||||||
|
@ -54,7 +83,7 @@ private slots:
|
||||||
void databaseDownloadFinished();
|
void databaseDownloadFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions);
|
DatabaseError loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions);
|
||||||
QNetworkRequest regularDatabaseRequest();
|
QNetworkRequest regularDatabaseRequest();
|
||||||
QNetworkRequest filesDatabaseRequest();
|
QNetworkRequest filesDatabaseRequest();
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,8 @@ Config::Config() :
|
||||||
m_websocketServerListeningAddr(QHostAddress::LocalHost),
|
m_websocketServerListeningAddr(QHostAddress::LocalHost),
|
||||||
m_websocketServerListeningPort(1234),
|
m_websocketServerListeningPort(1234),
|
||||||
m_serverInsecure(false),
|
m_serverInsecure(false),
|
||||||
m_reposFromPacmanConf(false),
|
m_localEnabled(true),
|
||||||
|
m_reposFromPacmanConfEnabled(false),
|
||||||
m_aurEnabled(true),
|
m_aurEnabled(true),
|
||||||
m_verbose(false),
|
m_verbose(false),
|
||||||
m_runServer(false)
|
m_runServer(false)
|
||||||
|
@ -252,7 +253,8 @@ void Config::loadFromConfigFile(const QString &configFilePath)
|
||||||
m_serverKeyFile = serverObj.value(QStringLiteral("keyFile")).toString(m_serverKeyFile);
|
m_serverKeyFile = serverObj.value(QStringLiteral("keyFile")).toString(m_serverKeyFile);
|
||||||
m_serverInsecure = serverObj.value(QStringLiteral("insecure")).toBool(m_serverInsecure);
|
m_serverInsecure = serverObj.value(QStringLiteral("insecure")).toBool(m_serverInsecure);
|
||||||
auto reposObj = mainObj.value(QStringLiteral("repos")).toObject();
|
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()) {
|
for(const auto &repo : reposObj.value(QStringLiteral("add")).toArray()) {
|
||||||
m_repoEntries << RepoEntry();
|
m_repoEntries << RepoEntry();
|
||||||
m_repoEntries.back().load(repo);
|
m_repoEntries.back().load(repo);
|
||||||
|
@ -323,7 +325,8 @@ void Config::loadFromArgs(const ConfigArgs &args)
|
||||||
}
|
}
|
||||||
|
|
||||||
RepoEntry::RepoEntry() :
|
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_sigLevel = Manager::parseSigLevel(sigLevelValue.toString().toLocal8Bit().data());
|
||||||
}
|
}
|
||||||
m_maxDatabaseAge = TimeSpan::fromSeconds(obj.value(QStringLiteral("maxAge")).toDouble());
|
m_maxDatabaseAge = TimeSpan::fromSeconds(obj.value(QStringLiteral("maxAge")).toDouble());
|
||||||
|
m_ignored = obj.value(QStringLiteral("ignored")).toBool(m_ignored);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Alpm
|
} // namespace Alpm
|
||||||
|
|
|
@ -68,6 +68,7 @@ public:
|
||||||
const QStringList &upgradeSources() const;
|
const QStringList &upgradeSources() const;
|
||||||
SignatureLevel sigLevel() const;
|
SignatureLevel sigLevel() const;
|
||||||
ChronoUtilities::TimeSpan maxDatabaseAge() const;
|
ChronoUtilities::TimeSpan maxDatabaseAge() const;
|
||||||
|
bool isIgnored() const;
|
||||||
void load(const QJsonValue &jsonValue);
|
void load(const QJsonValue &jsonValue);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -79,6 +80,7 @@ private:
|
||||||
QStringList m_upgradeSources;
|
QStringList m_upgradeSources;
|
||||||
SignatureLevel m_sigLevel;
|
SignatureLevel m_sigLevel;
|
||||||
ChronoUtilities::TimeSpan m_maxDatabaseAge;
|
ChronoUtilities::TimeSpan m_maxDatabaseAge;
|
||||||
|
bool m_ignored;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const QString &RepoEntry::name() const
|
inline const QString &RepoEntry::name() const
|
||||||
|
@ -121,6 +123,11 @@ inline ChronoUtilities::TimeSpan RepoEntry::maxDatabaseAge() const
|
||||||
return m_maxDatabaseAge;
|
return m_maxDatabaseAge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool RepoEntry::isIgnored() const
|
||||||
|
{
|
||||||
|
return m_ignored;
|
||||||
|
}
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -136,7 +143,8 @@ public:
|
||||||
const QString &serverCertFile() const;
|
const QString &serverCertFile() const;
|
||||||
const QString &serverKeyFile() const;
|
const QString &serverKeyFile() const;
|
||||||
bool serverInsecure() const;
|
bool serverInsecure() const;
|
||||||
bool reposFromPacmanConf() const;
|
bool isLocalDatabaseEnabled() const;
|
||||||
|
bool areReposFromPacmanConfEnabled() const;
|
||||||
const QList<RepoEntry> &repoEntries() const;
|
const QList<RepoEntry> &repoEntries() const;
|
||||||
bool isAurEnabled() const;
|
bool isAurEnabled() const;
|
||||||
bool isVerbose() const;
|
bool isVerbose() const;
|
||||||
|
@ -161,7 +169,8 @@ private:
|
||||||
bool m_serverInsecure;
|
bool m_serverInsecure;
|
||||||
|
|
||||||
QList<RepoEntry> m_repoEntries;
|
QList<RepoEntry> m_repoEntries;
|
||||||
bool m_reposFromPacmanConf;
|
bool m_localEnabled;
|
||||||
|
bool m_reposFromPacmanConfEnabled;
|
||||||
bool m_aurEnabled;
|
bool m_aurEnabled;
|
||||||
bool m_verbose;
|
bool m_verbose;
|
||||||
bool m_runServer;
|
bool m_runServer;
|
||||||
|
@ -217,9 +226,14 @@ inline bool Config::serverInsecure() const
|
||||||
return m_serverInsecure;
|
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
|
inline const QList<RepoEntry> &Config::repoEntries() const
|
||||||
|
|
393
alpm/manager.cpp
393
alpm/manager.cpp
|
@ -23,6 +23,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace IoUtilities;
|
using namespace IoUtilities;
|
||||||
|
@ -59,13 +60,16 @@ inline ostream &operator <<(ostream &stream, const QString &str)
|
||||||
* \param rootdir Specifies the root directory.
|
* \param rootdir Specifies the root directory.
|
||||||
* \param dbpath Specifies the database 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_config(config),
|
||||||
m_writeCacheBeforeGone(true),
|
m_writeCacheBeforeGone(true),
|
||||||
m_sigLevel(defaultSigLevel),
|
m_sigLevel(defaultSigLevel),
|
||||||
m_localFileSigLevel(SignatureLevel::UseDefault)
|
m_localFileSigLevel(SignatureLevel::UseDefault)
|
||||||
{
|
{
|
||||||
addLocalDatabase();
|
if(config.isLocalDatabaseEnabled()) {
|
||||||
|
addLocalDatabase();
|
||||||
|
}
|
||||||
if(config.isAurEnabled()) {
|
if(config.isAurEnabled()) {
|
||||||
m_userRepo = make_unique<UserRepository>();
|
m_userRepo = make_unique<UserRepository>();
|
||||||
}
|
}
|
||||||
|
@ -309,7 +313,7 @@ void Manager::addDataBasesFromPacmanConfig()
|
||||||
} else if(dbName.startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
|
} 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;
|
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)) {
|
} 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 {
|
} else {
|
||||||
// read sig level and usage
|
// read sig level and usage
|
||||||
const auto &sigLevelStr = lastValue(scope.second, sigLevelKey);
|
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));
|
m_syncDbs.emplace_back(make_unique<AlpmDatabase>(dbName, findDatabasePath(dbName, false, false), usage, sigLevel, m_syncDbs.size() + 1));
|
||||||
AlpmDatabase *emplacedDb = m_syncDbs.back().get();
|
AlpmDatabase *emplacedDb = m_syncDbs.back().get();
|
||||||
m_syncDbMap.emplace(dbName, emplacedDb);
|
m_syncDbMap.emplace(dbName, emplacedDb);
|
||||||
|
connectRepository(emplacedDb);
|
||||||
cerr << shchar << "Added [" << dbName << "]" << endl;
|
cerr << shchar << "Added [" << dbName << "]" << endl;
|
||||||
if(usage & RepositoryUsage::Upgrade) {
|
if(localDatabase() && usage & RepositoryUsage::Upgrade) {
|
||||||
// -> db is used to upgrade local database
|
// -> db is used to upgrade local database
|
||||||
localDataBase()->upgradeSources() << emplacedDb;
|
localDatabase()->upgradeSources() << emplacedDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add servers
|
// add servers
|
||||||
|
@ -352,9 +357,9 @@ void Manager::addDataBasesFromPacmanConfig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
for(auto &scope : includedIni.data()) {
|
for(auto &nestedScope : includedIni.data()) {
|
||||||
if(scope.first.empty()) {
|
if(nestedScope.first.empty()) {
|
||||||
for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
|
for(auto range = nestedScope.second.equal_range("Server"); range.first != range.second; ++range.first) {
|
||||||
string url = range.first->second;
|
string url = range.first->second;
|
||||||
findAndReplace<string>(url, "$repo", scope.first);
|
findAndReplace<string>(url, "$repo", scope.first);
|
||||||
findAndReplace<string>(url, "$arch", arch);
|
findAndReplace<string>(url, "$arch", arch);
|
||||||
|
@ -384,9 +389,18 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
||||||
{
|
{
|
||||||
// check whether an entry already exists, if not create a new one
|
// check whether an entry already exists, if not create a new one
|
||||||
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
|
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
|
||||||
AlpmDatabase *syncDb = nullptr;
|
AlpmDatabase *syncDb;
|
||||||
try {
|
if(m_localDb && repoEntry.name() == QLatin1String("local")) {
|
||||||
syncDb = m_syncDbMap.at(repoEntry.name());
|
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;
|
cerr << shchar << "Applying config for database [" << syncDb->name() << ']' << endl;
|
||||||
if(!repoEntry.databasePath().isEmpty()) {
|
if(!repoEntry.databasePath().isEmpty()) {
|
||||||
syncDb->setDatabasePath(repoEntry.databasePath());
|
syncDb->setDatabasePath(repoEntry.databasePath());
|
||||||
|
@ -396,12 +410,21 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
||||||
}
|
}
|
||||||
syncDb->setPackagesDirectory(repoEntry.packageDir());
|
syncDb->setPackagesDirectory(repoEntry.packageDir());
|
||||||
syncDb->setSourcesDirectory(repoEntry.sourceDir());
|
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) {
|
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;
|
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)) {
|
} 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;
|
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 {
|
} 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
|
// determine path of db file
|
||||||
// -> currently just use the file from pacman dir, TODO: download syncdata base
|
// -> currently just use the file from pacman dir, TODO: download syncdata base
|
||||||
QString dbPath;
|
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)
|
// 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));
|
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);
|
m_syncDbMap.emplace(repoEntry.name(), syncDb);
|
||||||
|
|
||||||
syncDb->setSourcesDirectory(repoEntry.sourceDir());
|
syncDb->setSourcesDirectory(repoEntry.sourceDir());
|
||||||
|
@ -453,55 +476,81 @@ void Manager::addDatabasesFromRepoIndexConfig()
|
||||||
* \brief Initiates all ALPM data bases.
|
* \brief Initiates all ALPM data bases.
|
||||||
* \remarks Must be called, after all relevant sync databases have been added (eg. via applyPacmanConfig()).
|
* \remarks Must be called, after all relevant sync databases have been added (eg. via applyPacmanConfig()).
|
||||||
*/
|
*/
|
||||||
void Manager::initAlpmDataBases(bool computeRequiredBy)
|
void Manager::initAlpmDataBases()
|
||||||
{
|
{
|
||||||
{
|
// connect computeRequiredBy()
|
||||||
// call the init method
|
if(m_config.runServer()) {
|
||||||
if(m_config.isVerbose() || m_config.runServer()) {
|
if(localDatabase()) {
|
||||||
cerr << "Initializing ALPM databases ... ";
|
QObject::connect(localDatabase(), &AlpmDatabase::initialized, bind(&Manager::computeRequiredBy, this, localDatabase()));
|
||||||
cerr.flush();
|
|
||||||
}
|
}
|
||||||
QList<PackageLoader *> loaders;
|
|
||||||
loaders.reserve(m_syncDbMap.size() + 1);
|
|
||||||
loaders << localDataBase()->init();
|
|
||||||
for(auto &syncDbEntry : m_syncDbMap) {
|
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();
|
// call the init method
|
||||||
delete loader;
|
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()) {
|
if(m_config.isVerbose() || m_config.runServer()) {
|
||||||
cerr << "DONE" << endl;
|
cerr << "DONE" << endl;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// compute required-by and optional-for
|
/*!
|
||||||
if(computeRequiredBy) {
|
* \brief Computes required-by and optional-for fields for the packages of the specified \a repo.
|
||||||
if(m_config.isVerbose() || m_config.runServer()) {
|
* \remarks
|
||||||
cerr << "Calculating required-by/optional-for ... ";
|
* - The computing is performed asynchronously so this method will return immidiately.
|
||||||
cerr.flush();
|
* - Do not call this method if the specified \a repo is busy.
|
||||||
}
|
*/
|
||||||
QList<QFuture<void> > futures;
|
void Manager::computeRequiredBy(Repository *repo)
|
||||||
futures.reserve(m_syncDbMap.size() + 1);
|
{
|
||||||
futures << localDataBase()->computeRequiredBy(*this);
|
// find relevant databases
|
||||||
for(auto &syncDbEntry : m_syncDbMap) {
|
QList<Repository *> relevantDbs;
|
||||||
futures << syncDbEntry.second->computeRequiredBy(*this);
|
if(repo == localDatabase()) {
|
||||||
}
|
relevantDbs.reserve(1);
|
||||||
for(auto &future : futures) {
|
relevantDbs << repo;
|
||||||
future.waitForFinished();
|
} else {
|
||||||
}
|
relevantDbs.reserve(m_syncDbs.size());
|
||||||
if(m_config.isVerbose() || m_config.runServer()) {
|
for(auto &syncDb : m_syncDbs) {
|
||||||
cerr << "DONE" << endl;
|
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()
|
void Manager::restoreCache()
|
||||||
{
|
{
|
||||||
// could iterate through all repos and check isCachingUseful() but
|
// 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()
|
void Manager::cleanCache()
|
||||||
{
|
{
|
||||||
// currently clear only AUR cache
|
|
||||||
if(userRepository()) {
|
if(userRepository()) {
|
||||||
userRepository()->cleanOutdatedPackages();
|
userRepository()->cleanOutdatedPackages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Removes all cached packages.
|
||||||
|
* \remarks Currently only the AUR cache is affected.
|
||||||
|
*/
|
||||||
void Manager::wipeCache()
|
void Manager::wipeCache()
|
||||||
{
|
{
|
||||||
// currently wipe only AUR cache
|
|
||||||
if(userRepository()) {
|
if(userRepository()) {
|
||||||
userRepository()->wipePackages();
|
userRepository()->wipePackages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Cleans and writes the cache.
|
||||||
|
* \remarks Automatically called if automatic cache maintenance is enabled.
|
||||||
|
*/
|
||||||
void Manager::maintainCache()
|
void Manager::maintainCache()
|
||||||
{
|
{
|
||||||
cleanCache();
|
cleanCache();
|
||||||
|
@ -593,6 +655,65 @@ void Manager::maintainCache()
|
||||||
writeCache();
|
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.
|
* \brief Returns a list of all sync databases.
|
||||||
* \remarks Sync databases must be registered with parsePacmanConfig() before.
|
* \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
|
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.
|
* \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
|
const QJsonObject &Manager::basicRepoInfo() const
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&m_basicRepoInfoMutex);
|
||||||
if(m_basicRepoInfo.isEmpty()) {
|
if(m_basicRepoInfo.isEmpty()) {
|
||||||
QMutexLocker locker(&m_basicRepoInfoMutex);
|
|
||||||
if(m_basicRepoInfo.isEmpty()) {
|
if(m_basicRepoInfo.isEmpty()) {
|
||||||
// add local data base
|
// add local data base
|
||||||
{
|
if(localDatabase()) {
|
||||||
QReadLocker locker(localDataBase()->lock());
|
if(localDatabase()->isBusy()) {
|
||||||
m_basicRepoInfo.insert(localDataBase()->name(), localDataBase()->basicInfo());
|
m_basicRepoInfo.insert(QStringLiteral("local"), QStringLiteral("incomplete"));
|
||||||
|
} else {
|
||||||
|
QReadLocker locker(localDatabase()->lock());
|
||||||
|
m_basicRepoInfo.insert(localDatabase()->name(), localDatabase()->basicInfo());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// add sync data bases
|
// add sync data bases
|
||||||
for(const auto &syncDb : syncDatabases()) {
|
for(const auto &syncDb : syncDatabases()) {
|
||||||
// check if the "sync" database is actually used for syncing
|
if(syncDb.second->isBusy()) {
|
||||||
QReadLocker locker(syncDb.second->lock());
|
m_basicRepoInfo.insert(syncDb.first, QStringLiteral("incomplete"));
|
||||||
auto usage = syncDb.second->usage();
|
|
||||||
if((usage & RepositoryUsage::Sync) || (usage & RepositoryUsage::Install) || (usage & RepositoryUsage::Upgrade)) {
|
|
||||||
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
|
|
||||||
} else {
|
} 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
|
// add AUR
|
||||||
if(userRepository()) {
|
if(userRepository()) {
|
||||||
QReadLocker locker(userRepository()->lock());
|
if(userRepository()->isBusy()) {
|
||||||
m_basicRepoInfo.insert(userRepository()->name(), userRepository()->basicInfo());
|
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
|
* \remarks Does not request any information and hence will only return information
|
||||||
* which does not need to be requested or has been requested yet.
|
* 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;
|
QJsonArray results;
|
||||||
for(auto i = pkgSelection.constBegin(), end = pkgSelection.constEnd(); i != end; ++i) {
|
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())) {
|
if(auto *repo = repositoryByName(i.key())) {
|
||||||
for(const auto &entry : i.value().toArray()) {
|
if(repo->isBusy()) {
|
||||||
const auto entryObj = entry.toObject();
|
// specified repository is busy
|
||||||
const auto pkgName = entryObj.value(QStringLiteral("name")).toString();
|
res.insert(QStringLiteral("error"), QStringLiteral("busy"));
|
||||||
if(!pkgName.isEmpty()) {
|
results << res;
|
||||||
QJsonObject res;
|
} else {
|
||||||
res.insert(QStringLiteral("name"), pkgName);
|
QReadLocker locker(repo->lock());
|
||||||
res.insert(QStringLiteral("repo"), repo->name());
|
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"));
|
const auto index = entryObj.value(QStringLiteral("index"));
|
||||||
if(!index.isNull() && !index.isUndefined()) {
|
if(!index.isNull() && !index.isUndefined()) {
|
||||||
res.insert(QStringLiteral("index"), index);
|
res.insert(QStringLiteral("index"), index);
|
||||||
}
|
}
|
||||||
if(auto *pkg = repo->packageByName(pkgName)) {
|
if(!pkgName.isEmpty()) {
|
||||||
if(part & Basics) {
|
res.insert(QStringLiteral("name"), pkgName);
|
||||||
res.insert(QStringLiteral("basics"), pkg->basicInfo());
|
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) {
|
results << res;
|
||||||
res.insert(QStringLiteral("details"), pkg->detailedInfo());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.insert(QStringLiteral("error"), QStringLiteral("na"));
|
|
||||||
}
|
}
|
||||||
results << res;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// specified repository can not be found
|
// specified repository can not be found
|
||||||
QJsonObject errorObj;
|
res.insert(QStringLiteral("error"), QStringLiteral("na"));
|
||||||
errorObj.insert(QStringLiteral("repo"), i.key());
|
results << res;
|
||||||
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
|
|
||||||
results << errorObj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return results;
|
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.
|
* \brief Returns group information for the local database and all registred sync databases.
|
||||||
*/
|
*/
|
||||||
const QJsonArray &Manager::groupInfo() const
|
const QJsonArray &Manager::groupInfo() const
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&m_groupInfoMutex);
|
||||||
if(m_groupInfo.empty()) {
|
if(m_groupInfo.empty()) {
|
||||||
QMutexLocker locker(&m_groupInfoMutex);
|
|
||||||
if(m_groupInfo.empty()) {
|
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) {
|
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;
|
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.
|
* \brief Add the local database.
|
||||||
*/
|
*/
|
||||||
void Manager::addLocalDatabase()
|
void Manager::addLocalDatabase()
|
||||||
{
|
{
|
||||||
m_localDb = make_unique<AlpmDatabase>(QStringLiteral("local"), config().alpmDbPath() % QStringLiteral("/local"), RepositoryUsage::None, SignatureLevel::UseDefault, 0);
|
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
|
const AlpmDatabase *Manager::databaseByName(const QString &dbName) const
|
||||||
{
|
{
|
||||||
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||||
return localDataBase();
|
return localDatabase();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return m_syncDbMap.at(dbName);
|
return m_syncDbMap.at(dbName);
|
||||||
|
@ -742,7 +943,7 @@ const AlpmDatabase *Manager::databaseByName(const QString &dbName) const
|
||||||
AlpmDatabase *Manager::databaseByName(const QString &dbName)
|
AlpmDatabase *Manager::databaseByName(const QString &dbName)
|
||||||
{
|
{
|
||||||
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||||
return localDataBase();
|
return localDatabase();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return m_syncDbMap.at(dbName);
|
return m_syncDbMap.at(dbName);
|
||||||
|
@ -758,8 +959,8 @@ AlpmDatabase *Manager::databaseByName(const QString &dbName)
|
||||||
const Repository *Manager::repositoryByName(const QString &name) const
|
const Repository *Manager::repositoryByName(const QString &name) const
|
||||||
{
|
{
|
||||||
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||||
return localDataBase();
|
return localDatabase();
|
||||||
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
|
} else if(name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0) {
|
||||||
return userRepository();
|
return userRepository();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
@ -776,8 +977,8 @@ const Repository *Manager::repositoryByName(const QString &name) const
|
||||||
Repository *Manager::repositoryByName(const QString &name)
|
Repository *Manager::repositoryByName(const QString &name)
|
||||||
{
|
{
|
||||||
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
|
||||||
return localDataBase();
|
return localDatabase();
|
||||||
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
|
} else if(name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0) {
|
||||||
return userRepository();
|
return userRepository();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QAtomicInteger>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -20,8 +22,10 @@ class UserRepository;
|
||||||
class AlpmDatabase;
|
class AlpmDatabase;
|
||||||
enum class RepositoryUsage;
|
enum class RepositoryUsage;
|
||||||
|
|
||||||
class Manager
|
class Manager : public QObject
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum PackageInfoPart {
|
enum PackageInfoPart {
|
||||||
None = 0x0,
|
None = 0x0,
|
||||||
|
@ -30,10 +34,7 @@ public:
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(PackageInfoParts, PackageInfoPart)
|
Q_DECLARE_FLAGS(PackageInfoParts, PackageInfoPart)
|
||||||
|
|
||||||
explicit Manager(const Config &config);
|
explicit Manager(const Config &config, QObject *parent = nullptr);
|
||||||
Manager(const Manager &other) = delete;
|
|
||||||
Manager(Manager &&other) = delete;
|
|
||||||
Manager &operator =(const Manager &other) = delete;
|
|
||||||
~Manager();
|
~Manager();
|
||||||
|
|
||||||
// configuration, signature level, etc
|
// configuration, signature level, etc
|
||||||
|
@ -49,7 +50,8 @@ public:
|
||||||
void removeAllDatabases();
|
void removeAllDatabases();
|
||||||
void addDataBasesFromPacmanConfig();
|
void addDataBasesFromPacmanConfig();
|
||||||
void addDatabasesFromRepoIndexConfig();
|
void addDatabasesFromRepoIndexConfig();
|
||||||
void initAlpmDataBases(bool computeRequiredBy);
|
void initAlpmDataBases();
|
||||||
|
void computeRequiredBy(Repository *repo);
|
||||||
|
|
||||||
// caching
|
// caching
|
||||||
bool writeCacheBeforeGone() const;
|
bool writeCacheBeforeGone() const;
|
||||||
|
@ -62,7 +64,11 @@ public:
|
||||||
void wipeCache();
|
void wipeCache();
|
||||||
void maintainCache();
|
void maintainCache();
|
||||||
|
|
||||||
// refreshing
|
// updating
|
||||||
|
bool isAutoUpdateEnabled() const;
|
||||||
|
void setAutoUpdateEnabled(bool enabled);
|
||||||
|
void updateAlpmDatabases();
|
||||||
|
void forceUpdateAlpmDatabases();
|
||||||
|
|
||||||
// package lookup
|
// package lookup
|
||||||
AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName);
|
AlpmPackage *packageFromDatabase(const QString &dbName, const QString &pkgName);
|
||||||
|
@ -73,8 +79,8 @@ public:
|
||||||
const Package *packageProviding(const Dependency &dependency) const;
|
const Package *packageProviding(const Dependency &dependency) const;
|
||||||
|
|
||||||
// repository lookup
|
// repository lookup
|
||||||
const AlpmDatabase *localDataBase() const;
|
const AlpmDatabase *localDatabase() const;
|
||||||
AlpmDatabase *localDataBase();
|
AlpmDatabase *localDatabase();
|
||||||
const std::map<QString, AlpmDatabase *> &syncDatabases() const;
|
const std::map<QString, AlpmDatabase *> &syncDatabases() const;
|
||||||
const AlpmDatabase *databaseByName(const QString &dbName) const;
|
const AlpmDatabase *databaseByName(const QString &dbName) const;
|
||||||
AlpmDatabase *databaseByName(const QString &dbName);
|
AlpmDatabase *databaseByName(const QString &dbName);
|
||||||
|
@ -86,15 +92,25 @@ public:
|
||||||
QString proposedDatabasePath(const QString &name, bool files) const;
|
QString proposedDatabasePath(const QString &name, bool files) const;
|
||||||
|
|
||||||
// JSON serialization, handling JSON requests
|
// JSON serialization, handling JSON requests
|
||||||
const QJsonObject basicRepoInfo(const Repository *packageSource) const;
|
|
||||||
const QJsonObject &basicRepoInfo() 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;
|
const QJsonArray &groupInfo() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void updatesAvailable();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void invalidateCachedJsonSerialization();
|
||||||
|
void emitUpdatesAvailable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void connectRepository(Repository *repo);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Config &m_config;
|
const Config &m_config;
|
||||||
bool m_writeCacheBeforeGone;
|
bool m_writeCacheBeforeGone;
|
||||||
std::unique_ptr<QTimer> m_cacheTimer;
|
std::unique_ptr<QTimer> m_cacheTimer;
|
||||||
|
std::unique_ptr<QTimer> m_updateTimer;
|
||||||
SignatureLevel m_sigLevel;
|
SignatureLevel m_sigLevel;
|
||||||
SignatureLevel m_localFileSigLevel;
|
SignatureLevel m_localFileSigLevel;
|
||||||
QString m_pacmanCacheDir;
|
QString m_pacmanCacheDir;
|
||||||
|
@ -201,7 +217,7 @@ inline UserRepository *Manager::userRepository()
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the local data base.
|
* \brief Returns the local data base.
|
||||||
*/
|
*/
|
||||||
inline const AlpmDatabase *Manager::localDataBase() const
|
inline const AlpmDatabase *Manager::localDatabase() const
|
||||||
{
|
{
|
||||||
return m_localDb.get();
|
return m_localDb.get();
|
||||||
}
|
}
|
||||||
|
@ -209,7 +225,7 @@ inline const AlpmDatabase *Manager::localDataBase() const
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the local data base.
|
* \brief Returns the local data base.
|
||||||
*/
|
*/
|
||||||
inline AlpmDatabase *Manager::localDataBase()
|
inline AlpmDatabase *Manager::localDatabase()
|
||||||
{
|
{
|
||||||
return m_localDb.get();
|
return m_localDb.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "./alpmdatabase.h"
|
#include "./alpmdatabase.h"
|
||||||
#include "./utilities.h"
|
#include "./utilities.h"
|
||||||
#include "./repository.h"
|
#include "./repository.h"
|
||||||
#include "./manager.h"
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonValue>
|
#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) {
|
if(m_requiredByComputed) {
|
||||||
m_requiredBy.clear();
|
m_requiredBy.clear();
|
||||||
m_optionalFor.clear();
|
m_optionalFor.clear();
|
||||||
}
|
}
|
||||||
switch(origin()) {
|
for(const Repository *repo : relevantRepositories) {
|
||||||
case PackageOrigin::File:
|
for(const auto &pkgEntry : repo->packages()) {
|
||||||
case PackageOrigin::LocalDb:
|
|
||||||
for(const auto &pkgEntry : manager.localDataBase()->packages()) {
|
|
||||||
for(const auto &dep : pkgEntry.second->dependencies()) {
|
for(const auto &dep : pkgEntry.second->dependencies()) {
|
||||||
if(dep.name == m_name) {
|
if(dep.name == m_name) {
|
||||||
m_requiredBy << pkgEntry.first;
|
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;
|
m_requiredByComputed = true;
|
||||||
}
|
}
|
||||||
|
@ -320,9 +298,9 @@ QJsonObject Package::basicInfo(bool includeRepoAndName) const
|
||||||
QJsonObject Package::detailedInfo() const
|
QJsonObject Package::detailedInfo() const
|
||||||
{
|
{
|
||||||
QJsonObject info;
|
QJsonObject info;
|
||||||
put(info, QStringLiteral("installAvail"), hasInstallRelatedMetaData());
|
put(info, QStringLiteral("iav"), hasInstallRelatedMetaData());
|
||||||
put(info, QStringLiteral("buildAvail"), hasBuildRelatedMetaData());
|
put(info, QStringLiteral("bav"), hasBuildRelatedMetaData());
|
||||||
put(info, QStringLiteral("srcAvail"), hasSourceRelatedMetaData());
|
put(info, QStringLiteral("sav"), hasSourceRelatedMetaData());
|
||||||
put(info, QStringLiteral("idate"), installDate());
|
put(info, QStringLiteral("idate"), installDate());
|
||||||
put(info, QStringLiteral("isize"), QJsonValue(static_cast<long long int>(installedSize())));
|
put(info, QStringLiteral("isize"), QJsonValue(static_cast<long long int>(installedSize())));
|
||||||
put(info, QStringLiteral("csize"), QJsonValue(static_cast<long long int>(packageSize())));
|
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("optd"), optionalDependencies());
|
||||||
put(info, QStringLiteral("mkd"), makeDependencies());
|
put(info, QStringLiteral("mkd"), makeDependencies());
|
||||||
put(info, QStringLiteral("chkd"), checkDependencies());
|
put(info, QStringLiteral("chkd"), checkDependencies());
|
||||||
put(info, QStringLiteral("requ"), requiredBy());
|
if(isRequiredByComputed()) {
|
||||||
put(info, QStringLiteral("optf"), optionalFor());
|
put(info, QStringLiteral("requ"), requiredBy());
|
||||||
|
put(info, QStringLiteral("optf"), optionalFor());
|
||||||
|
}
|
||||||
put(info, QStringLiteral("conf"), conflicts());
|
put(info, QStringLiteral("conf"), conflicts());
|
||||||
put(info, QStringLiteral("repl"), replaces());
|
put(info, QStringLiteral("repl"), replaces());
|
||||||
put(info, QStringLiteral("pack"), packager());
|
put(info, QStringLiteral("pack"), packager());
|
||||||
|
@ -344,6 +324,17 @@ QJsonObject Package::detailedInfo() const
|
||||||
put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods()));
|
put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods()));
|
||||||
put(info, QStringLiteral("file"), fileName());
|
put(info, QStringLiteral("file"), fileName());
|
||||||
put(info, QStringLiteral("files"), files());
|
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;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,6 +614,7 @@ const map<QString, void(Package::*)(const QStringList &)> Package::m_descMap {
|
||||||
{QStringLiteral("PACKAGER"), &Package::setPackager},
|
{QStringLiteral("PACKAGER"), &Package::setPackager},
|
||||||
{QStringLiteral("MD5SUM"), &Package::setMd5},
|
{QStringLiteral("MD5SUM"), &Package::setMd5},
|
||||||
{QStringLiteral("SHA256SUM"), &Package::setSha256},
|
{QStringLiteral("SHA256SUM"), &Package::setSha256},
|
||||||
|
{QStringLiteral("PGPSIG"), &Package::setPgpSignature},
|
||||||
{QStringLiteral("FILES"), &Package::setFiles},
|
{QStringLiteral("FILES"), &Package::setFiles},
|
||||||
{QStringLiteral("REASON"), &Package::setInstallReason},
|
{QStringLiteral("REASON"), &Package::setInstallReason},
|
||||||
{QStringLiteral("VALIDATION"), &Package::setValidation},
|
{QStringLiteral("VALIDATION"), &Package::setValidation},
|
||||||
|
@ -727,13 +719,31 @@ void Package::setPackager(const QStringList &values)
|
||||||
void Package::setMd5(const QStringList &values)
|
void Package::setMd5(const QStringList &values)
|
||||||
{
|
{
|
||||||
if(!values.isEmpty()) {
|
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)
|
void Package::setSha256(const QStringList &values)
|
||||||
{
|
{
|
||||||
if(!values.isEmpty()) {
|
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)
|
void Package::setFiles(const QStringList &values)
|
||||||
|
@ -754,7 +764,7 @@ void Package::setValidation(const QStringList &values)
|
||||||
} else if(value == QLatin1String("sha256")) {
|
} else if(value == QLatin1String("sha256")) {
|
||||||
m_validationMethods = m_validationMethods | PackageValidation::Sha256Sum;
|
m_validationMethods = m_validationMethods | PackageValidation::Sha256Sum;
|
||||||
} else if(value == QLatin1String("pgp")) {
|
} else if(value == QLatin1String("pgp")) {
|
||||||
m_validationMethods = m_validationMethods | PackageValidation::Signature;
|
m_validationMethods = m_validationMethods | PackageValidation::PgpSignature;
|
||||||
} else {
|
} else {
|
||||||
// TODO: error handling (imporant?)
|
// TODO: error handling (imporant?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,7 @@ enum class PackageOrigin
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The InstallStatus enum specifies whether a package has been installed explicitely
|
* \brief The InstallStatus enum specifies whether a package has been installed explicitely or as dependency.
|
||||||
* or as dependency.
|
|
||||||
*/
|
*/
|
||||||
enum class InstallStatus
|
enum class InstallStatus
|
||||||
{
|
{
|
||||||
|
@ -71,12 +70,12 @@ enum class PackageValidation {
|
||||||
None = (1 << 0),
|
None = (1 << 0),
|
||||||
Md5Sum = (1 << 1),
|
Md5Sum = (1 << 1),
|
||||||
Sha256Sum = (1 << 2),
|
Sha256Sum = (1 << 2),
|
||||||
Signature = (1 << 3)
|
PgpSignature = (1 << 3)
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr PackageValidation operator |(PackageValidation lhs, PackageValidation rhs)
|
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)
|
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;
|
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.
|
* \brief The SignatureLevel enum specifies PGP signature verification options.
|
||||||
*/
|
*/
|
||||||
|
@ -227,7 +243,7 @@ public:
|
||||||
const QList<Dependency> &provides() const;
|
const QList<Dependency> &provides() const;
|
||||||
const QList<Dependency> &replaces() const;
|
const QList<Dependency> &replaces() const;
|
||||||
bool isRequiredByComputed() const;
|
bool isRequiredByComputed() const;
|
||||||
void computeRequiredBy(Manager &manager);
|
void computeRequiredBy(const QList<Repository *> &relevantRepositories);
|
||||||
const QStringList &requiredBy() const;
|
const QStringList &requiredBy() const;
|
||||||
QStringList &requiredBy();
|
QStringList &requiredBy();
|
||||||
const QStringList &optionalFor() const;
|
const QStringList &optionalFor() const;
|
||||||
|
@ -328,6 +344,7 @@ protected:
|
||||||
QString m_packager;
|
QString m_packager;
|
||||||
QString m_md5;
|
QString m_md5;
|
||||||
QString m_sha256;
|
QString m_sha256;
|
||||||
|
QString m_pgpSignature;
|
||||||
QString m_buildArchitecture;
|
QString m_buildArchitecture;
|
||||||
uint32 m_packageSize;
|
uint32 m_packageSize;
|
||||||
QList<Dependency> m_makeDependencies;
|
QList<Dependency> m_makeDependencies;
|
||||||
|
@ -377,6 +394,7 @@ protected:
|
||||||
void setPackager(const QStringList &values);
|
void setPackager(const QStringList &values);
|
||||||
void setMd5(const QStringList &values);
|
void setMd5(const QStringList &values);
|
||||||
void setSha256(const QStringList &values);
|
void setSha256(const QStringList &values);
|
||||||
|
void setPgpSignature(const QStringList &values);
|
||||||
void setFiles(const QStringList &values);
|
void setFiles(const QStringList &values);
|
||||||
void setValidation(const QStringList &values);
|
void setValidation(const QStringList &values);
|
||||||
void setGroups(const QStringList &values);
|
void setGroups(const QStringList &values);
|
||||||
|
|
|
@ -65,7 +65,9 @@ Package *PackageFinder::packageProviding(const Dependency &dependency)
|
||||||
|
|
||||||
void PackageFinder::addResults()
|
void PackageFinder::addResults()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
assert(m_remainingReplies);
|
assert(m_remainingReplies);
|
||||||
|
#endif
|
||||||
// add results
|
// add results
|
||||||
auto *reply = static_cast<PackageReply *>(sender());
|
auto *reply = static_cast<PackageReply *>(sender());
|
||||||
auto *repo = reply->repository();
|
auto *repo = reply->repository();
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace RepoIndex {
|
||||||
|
|
||||||
PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent) :
|
PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent) :
|
||||||
PackageLookup(parent),
|
PackageLookup(parent),
|
||||||
|
m_manager(manager),
|
||||||
m_what(request.value(QStringLiteral("what")).toString()),
|
m_what(request.value(QStringLiteral("what")).toString()),
|
||||||
m_part(Manager::None)
|
m_part(Manager::None)
|
||||||
{
|
{
|
||||||
|
@ -26,6 +27,7 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_packageSelection = request.value(QStringLiteral("sel")).toObject();
|
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) {
|
for(auto i = m_packageSelection.constBegin(), end = m_packageSelection.constEnd(); i != end; ++i) {
|
||||||
if(auto *repo = manager.repositoryByName(i.key())) {
|
if(auto *repo = manager.repositoryByName(i.key())) {
|
||||||
QStringList packagesToBeRequested;
|
QStringList packagesToBeRequested;
|
||||||
|
@ -37,6 +39,33 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!packagesToBeRequested.isEmpty()) {
|
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());
|
QReadLocker locker(repo->lock());
|
||||||
if(const auto *reply = (m_part & Manager::Details ? repo->requestFullPackageInfo(packagesToBeRequested) : repo->requestPackageInfo(packagesToBeRequested))) {
|
if(const auto *reply = (m_part & Manager::Details ? repo->requestFullPackageInfo(packagesToBeRequested) : repo->requestPackageInfo(packagesToBeRequested))) {
|
||||||
connect(reply, &PackageReply::resultsAvailable, this, &PackageInfoLookup::addResultsFromReply);
|
connect(reply, &PackageReply::resultsAvailable, this, &PackageInfoLookup::addResultsFromReply);
|
||||||
|
@ -46,13 +75,7 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques
|
||||||
addResultsDirectly(packagesToBeRequested, repo);
|
addResultsDirectly(packagesToBeRequested, repo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} // else: repo already processed
|
||||||
// specified repository can not be found
|
|
||||||
QJsonObject errorObj;
|
|
||||||
errorObj.insert(QStringLiteral("repo"), i.key());
|
|
||||||
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
|
|
||||||
m_results << errorObj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +113,9 @@ void PackageInfoLookup::addResultsDirectly(const QStringList &packageNames, cons
|
||||||
|
|
||||||
void PackageInfoLookup::addResultsFromReply()
|
void PackageInfoLookup::addResultsFromReply()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
assert(m_remainingReplies);
|
assert(m_remainingReplies);
|
||||||
|
#endif
|
||||||
auto *reply = static_cast<PackageReply *>(sender());
|
auto *reply = static_cast<PackageReply *>(sender());
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
if(reply->error().isEmpty()) {
|
if(reply->error().isEmpty()) {
|
||||||
|
|
|
@ -13,13 +13,16 @@ public:
|
||||||
explicit PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
|
explicit PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void performLookup();
|
||||||
void addResultsDirectly(const QStringList &packageNames, const Repository *repo);
|
void addResultsDirectly(const QStringList &packageNames, const Repository *repo);
|
||||||
void addResultsFromReply();
|
void addResultsFromReply();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Manager &m_manager;
|
||||||
const QString m_what;
|
const QString m_what;
|
||||||
Manager::PackageInfoParts m_part;
|
Manager::PackageInfoParts m_part;
|
||||||
QJsonObject m_packageSelection;
|
QJsonObject m_packageSelection;
|
||||||
|
QList<QPair<Repository *, QStringList> > m_repos;
|
||||||
QList<Package *> m_packages;
|
QList<Package *> m_packages;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,7 +46,9 @@ Reply::Reply(const QList<QNetworkReply *> networkReplies) :
|
||||||
*/
|
*/
|
||||||
void Reply::replyFinished()
|
void Reply::replyFinished()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
assert(m_remainingReplies);
|
assert(m_remainingReplies);
|
||||||
|
#endif
|
||||||
processData(static_cast<QNetworkReply *>(sender()));
|
processData(static_cast<QNetworkReply *>(sender()));
|
||||||
if(!--m_remainingReplies) {
|
if(!--m_remainingReplies) {
|
||||||
emit resultsAvailable();
|
emit resultsAvailable();
|
||||||
|
@ -84,12 +86,56 @@ void Repository::updateGroups()
|
||||||
/*!
|
/*!
|
||||||
* \brief Initializes the repository.
|
* \brief Initializes the repository.
|
||||||
* \remarks
|
* \remarks
|
||||||
|
* - The repository mustn't be busy if this method is called.
|
||||||
* - Does not restore cache. For restoring cache see restoreFromCacheStream().
|
* - Does not restore cache. For restoring cache see restoreFromCacheStream().
|
||||||
* - Performs asynchronously and hence returns immidiately. Returns a PackageLoader
|
* - Performs asynchronously and hence returns immidiately. Returns a PackageLoader
|
||||||
* object which QFuture can be used to wait for initializing to finish.
|
* object which QFuture can be used to wait until the initialization is finished.
|
||||||
* - Might return nullptr if initialization is tivial.
|
* - 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()
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -237,23 +283,47 @@ QList<Package *> Repository::packageByFilter(std::function<bool (const Package *
|
||||||
* \cond
|
* \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
|
class ComputeRequired
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ComputeRequired(Manager &manager, bool forceUpdate) :
|
ComputeRequired(const QList<Repository *> &relevantRepos, bool forceUpdate) :
|
||||||
m_manager(manager),
|
m_relevantRepos(relevantRepos),
|
||||||
m_forceUpdate(forceUpdate)
|
m_forceUpdate(forceUpdate)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void operator () (const pair<const QString, unique_ptr<Package> > &packageEntry)
|
void operator () (const pair<const QString, unique_ptr<Package> > &packageEntry)
|
||||||
{
|
{
|
||||||
if(m_forceUpdate || !packageEntry.second->isRequiredByComputed()) {
|
if(m_forceUpdate || !packageEntry.second->isRequiredByComputed()) {
|
||||||
packageEntry.second->computeRequiredBy(m_manager);
|
packageEntry.second->computeRequiredBy(m_relevantRepos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Manager &m_manager;
|
const QList<Repository *> m_relevantRepos;
|
||||||
bool m_forceUpdate;
|
bool m_forceUpdate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -263,11 +333,24 @@ private:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Computes required-by and optional-for for all packages.
|
* \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
|
// put last field
|
||||||
if(!currentFieldName.isEmpty()) {
|
if(!currentFieldName.isEmpty()) {
|
||||||
fields << QPair<QString, QStringList>(currentFieldName, currentFieldValues);
|
fields << QPair<QString, QStringList>(currentFieldName, currentFieldValues);
|
||||||
|
currentFieldName.clear();
|
||||||
|
currentFieldValues.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -944,4 +1029,21 @@ Package *Repository::addPackageFromDescription(QString name, const QList<QByteAr
|
||||||
return pkgRawPtr;
|
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
|
} // namespace PackageManagement
|
||||||
|
|
|
@ -200,6 +200,7 @@ public:
|
||||||
virtual RepositoryType type() const = 0;
|
virtual RepositoryType type() const = 0;
|
||||||
|
|
||||||
// general meta data
|
// general meta data
|
||||||
|
bool isBusy() const;
|
||||||
uint32 index() const;
|
uint32 index() const;
|
||||||
const QString &name() const;
|
const QString &name() const;
|
||||||
const QString &description() const;
|
const QString &description() const;
|
||||||
|
@ -218,7 +219,10 @@ public:
|
||||||
void setSigLevel(SignatureLevel sigLevel);
|
void setSigLevel(SignatureLevel sigLevel);
|
||||||
|
|
||||||
// gathering data
|
// gathering data
|
||||||
virtual PackageLoader *init();
|
public:
|
||||||
|
PackageLoader *init();
|
||||||
|
void initAsSoonAsPossible();
|
||||||
|
virtual PackageLoader *internalInit();
|
||||||
virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const;
|
virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const;
|
||||||
virtual SuggestionsReply *requestSuggestions(const QString &phrase);
|
virtual SuggestionsReply *requestSuggestions(const QString &phrase);
|
||||||
virtual PackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false);
|
virtual PackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false);
|
||||||
|
@ -231,7 +235,7 @@ public:
|
||||||
Package *packageProviding(const Dependency &dependency);
|
Package *packageProviding(const Dependency &dependency);
|
||||||
QList<const Package *> packagesProviding(const Dependency &dependency) const;
|
QList<const Package *> packagesProviding(const Dependency &dependency) const;
|
||||||
QList<Package *> packageByFilter(std::function<bool (const Package *)> pred);
|
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;
|
QJsonObject suggestions(const QString &term) const;
|
||||||
|
|
||||||
// upgrade lookup
|
// upgrade lookup
|
||||||
|
@ -277,10 +281,29 @@ public:
|
||||||
|
|
||||||
static const uint32 invalidIndex = static_cast<uint32>(-1);
|
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:
|
protected:
|
||||||
explicit Repository(const QString &name, uint32 index = invalidIndex, QObject *parent = nullptr);
|
explicit Repository(const QString &name, uint32 index = invalidIndex, QObject *parent = nullptr);
|
||||||
|
|
||||||
protected:
|
|
||||||
uint32 m_index;
|
uint32 m_index;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_description;
|
QString m_description;
|
||||||
|
@ -293,6 +316,7 @@ protected:
|
||||||
QList<Repository *> m_upgradeSources;
|
QList<Repository *> m_upgradeSources;
|
||||||
QString m_srcDir;
|
QString m_srcDir;
|
||||||
QString m_pkgDir;
|
QString m_pkgDir;
|
||||||
|
QAtomicInteger<byte> m_isBusy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QReadWriteLock m_lock;
|
QReadWriteLock m_lock;
|
||||||
|
@ -306,6 +330,18 @@ inline QJsonObject SuggestionsReply::suggestions() const
|
||||||
return m_repo->suggestions(m_term);
|
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.
|
* \brief Returns the index of the repository.
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
namespace RepoIndex {
|
namespace RepoIndex {
|
||||||
|
|
||||||
SuggestionsLookup::SuggestionsLookup(Manager &manager, const QJsonObject &request, QObject *parent) :
|
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"));
|
m_id = request.value(QStringLiteral("id"));
|
||||||
const auto searchTerm = request.value(QStringLiteral("term")).toString();
|
if(m_searchTerm.isEmpty()) {
|
||||||
if(searchTerm.isEmpty()) {
|
|
||||||
m_errors << QStringLiteral("No search term specified.");
|
m_errors << QStringLiteral("No search term specified.");
|
||||||
}
|
}
|
||||||
const auto repos = request.value(QStringLiteral("repos")).toArray();
|
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.");
|
m_errors << QStringLiteral("No repositories specified.");
|
||||||
}
|
}
|
||||||
if(m_errors.isEmpty()) {
|
if(m_errors.isEmpty()) {
|
||||||
|
m_repos.reserve(repos.size());
|
||||||
for(const auto &repoName : repos) {
|
for(const auto &repoName : repos) {
|
||||||
if(auto *repo = manager.repositoryByName(repoName.toString())) {
|
if(auto *repo = manager.repositoryByName(repoName.toString())) {
|
||||||
QReadLocker locker(repo->lock());
|
m_repos << repo;
|
||||||
if(const auto *reply = repo->requestSuggestions(searchTerm)) {
|
|
||||||
connect(reply, &SuggestionsReply::resultsAvailable, this, &SuggestionsLookup::addResults);
|
|
||||||
++m_remainingReplies;
|
|
||||||
} else {
|
|
||||||
m_results << repo->suggestions(searchTerm);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
m_errors << QStringLiteral("The specified repository \"%1\" does not exist.").arg(repoName.toString());
|
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();
|
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()
|
void SuggestionsLookup::addResults()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
assert(m_remainingReplies);
|
assert(m_remainingReplies);
|
||||||
|
#endif
|
||||||
auto *reply = static_cast<SuggestionsReply *>(sender());
|
auto *reply = static_cast<SuggestionsReply *>(sender());
|
||||||
{
|
{
|
||||||
QReadLocker locker(reply->repository()->lock());
|
QReadLocker locker(reply->repository()->lock());
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
namespace RepoIndex {
|
namespace RepoIndex {
|
||||||
|
|
||||||
class Manager;
|
class Manager;
|
||||||
|
class Repository;
|
||||||
|
|
||||||
class SuggestionsLookup : public PackageLookup
|
class SuggestionsLookup : public PackageLookup
|
||||||
{
|
{
|
||||||
|
@ -14,7 +15,12 @@ public:
|
||||||
SuggestionsLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
|
SuggestionsLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void performLookup();
|
||||||
void addResults();
|
void addResults();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Repository *> m_repos;
|
||||||
|
const QString m_searchTerm;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace RepoIndex
|
} // namespace RepoIndex
|
||||||
|
|
|
@ -52,28 +52,7 @@ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, Reposit
|
||||||
m_watcher(new QFutureWatcher<void>(this))
|
m_watcher(new QFutureWatcher<void>(this))
|
||||||
{
|
{
|
||||||
connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished);
|
connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished);
|
||||||
{
|
requestSources();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -84,16 +63,58 @@ inline const UpgradeLookupResults &UpgradeLookupProcess::results() const
|
||||||
return m_results;
|
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.
|
* \brief Internally called when the upgrade source is ready.
|
||||||
*/
|
*/
|
||||||
void UpgradeLookupProcess::sourceReady()
|
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()) {
|
if(m_reply && !m_reply->error().isEmpty()) {
|
||||||
m_results.errors << m_reply->error();
|
m_results.errors << m_reply->error();
|
||||||
emit finished();
|
emit finished();
|
||||||
} else {
|
} else {
|
||||||
|
// invoke the actual upgrade lookup
|
||||||
connect(m_watcher, &QFutureWatcher<void>::finished, this, &UpgradeLookupProcess::finished);
|
connect(m_watcher, &QFutureWatcher<void>::finished, this, &UpgradeLookupProcess::finished);
|
||||||
m_watcher->setFuture(QtConcurrent::run(this, &UpgradeLookupProcess::checkUpgrades));
|
m_watcher->setFuture(QtConcurrent::run(this, &UpgradeLookupProcess::checkUpgrades));
|
||||||
}
|
}
|
||||||
|
@ -210,7 +231,9 @@ UpgradeLookupJson::UpgradeLookupJson(Manager &manager, const QJsonObject &reques
|
||||||
|
|
||||||
void UpgradeLookupJson::processFinished()
|
void UpgradeLookupJson::processFinished()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
assert(m_remainingProcesses);
|
assert(m_remainingProcesses);
|
||||||
|
#endif
|
||||||
// add results
|
// add results
|
||||||
const auto &results = static_cast<UpgradeLookupProcess *>(sender())->results();
|
const auto &results = static_cast<UpgradeLookupProcess *>(sender())->results();
|
||||||
for(const auto &res : results.newVersions) {
|
for(const auto &res : results.newVersions) {
|
||||||
|
@ -301,7 +324,9 @@ UpgradeLookupCli::UpgradeLookupCli(Manager &manager, const string &repo, QObject
|
||||||
|
|
||||||
void UpgradeLookupCli::processFinished()
|
void UpgradeLookupCli::processFinished()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
assert(m_remainingProcesses);
|
assert(m_remainingProcesses);
|
||||||
|
#endif
|
||||||
// add results
|
// add results
|
||||||
const auto &results = static_cast<UpgradeLookupProcess *>(sender())->results();
|
const auto &results = static_cast<UpgradeLookupProcess *>(sender())->results();
|
||||||
m_softwareUpgradesArray.reserve(m_softwareUpgradesArray.size() + results.newVersions.size());
|
m_softwareUpgradesArray.reserve(m_softwareUpgradesArray.size() + results.newVersions.size());
|
||||||
|
|
|
@ -91,6 +91,7 @@ signals:
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void requestSources();
|
||||||
void sourceReady();
|
void sourceReady();
|
||||||
void checkUpgrades();
|
void checkUpgrades();
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,8 @@ QJsonArray validationMethodsStrings(PackageValidation validationMethods)
|
||||||
if(validationMethods & PackageValidation::Sha256Sum) {
|
if(validationMethods & PackageValidation::Sha256Sum) {
|
||||||
jsonArray << QStringLiteral("SHA256");
|
jsonArray << QStringLiteral("SHA256");
|
||||||
}
|
}
|
||||||
if(validationMethods & PackageValidation::Signature) {
|
if(validationMethods & PackageValidation::PgpSignature) {
|
||||||
jsonArray << QStringLiteral("signature");
|
jsonArray << QStringLiteral("PGP signature");
|
||||||
}
|
}
|
||||||
//if(jsonArray.empty()) {
|
//if(jsonArray.empty()) {
|
||||||
// jsonArray << QStringLiteral("unknown");
|
// 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 <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ApplicationUtilities;
|
using namespace ApplicationUtilities;
|
||||||
using namespace RepoIndex;
|
using namespace RepoIndex;
|
||||||
|
@ -53,9 +55,11 @@ int main(int argc, char *argv[])
|
||||||
// setup manager
|
// setup manager
|
||||||
Manager manager(config);
|
Manager manager(config);
|
||||||
cerr << shchar << "Loading databases ..." << endl;
|
cerr << shchar << "Loading databases ..." << endl;
|
||||||
manager.addDataBasesFromPacmanConfig();
|
if(config.areReposFromPacmanConfEnabled()) {
|
||||||
|
manager.addDataBasesFromPacmanConfig();
|
||||||
|
}
|
||||||
manager.addDatabasesFromRepoIndexConfig();
|
manager.addDatabasesFromRepoIndexConfig();
|
||||||
manager.initAlpmDataBases(configArgs.serverArg.isPresent());
|
manager.initAlpmDataBases();
|
||||||
cerr << shchar << "Restoring cache ... ";
|
cerr << shchar << "Restoring cache ... ";
|
||||||
manager.restoreCache();
|
manager.restoreCache();
|
||||||
cerr << shchar << "DONE" << endl;
|
cerr << shchar << "DONE" << endl;
|
||||||
|
@ -64,6 +68,7 @@ int main(int argc, char *argv[])
|
||||||
// setup the server
|
// setup the server
|
||||||
Server server(manager, manager.config());
|
Server server(manager, manager.config());
|
||||||
manager.setAutoCacheMaintenanceEnabled(true);
|
manager.setAutoCacheMaintenanceEnabled(true);
|
||||||
|
manager.setAutoUpdateEnabled(true);
|
||||||
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
|
QObject::connect(&server, &Server::closed, &application, &QCoreApplication::quit);
|
||||||
return application.exec();
|
return application.exec();
|
||||||
} else if(configArgs.buildOrderArg.isPresent()) {
|
} else if(configArgs.buildOrderArg.isPresent()) {
|
||||||
|
|
|
@ -18,10 +18,10 @@ using namespace std;
|
||||||
|
|
||||||
namespace RepoIndex {
|
namespace RepoIndex {
|
||||||
|
|
||||||
Connection::Connection(Manager &alpmManager, QWebSocket *socket, QObject *parent) :
|
Connection::Connection(Manager &manager, QWebSocket *socket, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_socket(socket),
|
m_socket(socket),
|
||||||
m_manager(alpmManager),
|
m_manager(manager),
|
||||||
m_repoInfoUpdatesRequested(false),
|
m_repoInfoUpdatesRequested(false),
|
||||||
m_groupInfoUpdatesRequested(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::textMessageReceived, this, &Connection::processTextMessage);
|
||||||
connect(socket, &QWebSocket::binaryMessageReceived, this, &Connection::processBinaryMessage);
|
connect(socket, &QWebSocket::binaryMessageReceived, this, &Connection::processBinaryMessage);
|
||||||
connect(socket, &QWebSocket::disconnected, this, &Connection::socketDisconnected);
|
connect(socket, &QWebSocket::disconnected, this, &Connection::socketDisconnected);
|
||||||
|
connect(&manager, &Manager::updatesAvailable, this, &Connection::updatedAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::sendJson(const QJsonObject &obj)
|
void Connection::sendJson(const QJsonObject &obj)
|
||||||
|
@ -59,7 +60,7 @@ void Connection::sendResult(const QJsonValue &what, const QJsonValue &id, const
|
||||||
sendJson(response);
|
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;
|
QJsonObject response;
|
||||||
response.insert(QStringLiteral("class"), QStringLiteral("results"));
|
response.insert(QStringLiteral("class"), QStringLiteral("results"));
|
||||||
|
@ -71,6 +72,16 @@ void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const
|
||||||
sendJson(response);
|
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>
|
template<class Lookup, typename... Args>
|
||||||
void Connection::performLookup(const QJsonObject &request, Args &&...args)
|
void Connection::performLookup(const QJsonObject &request, Args &&...args)
|
||||||
{
|
{
|
||||||
|
@ -129,12 +140,23 @@ void Connection::handleCmd(const QJsonObject &obj)
|
||||||
}
|
}
|
||||||
} else if(what == QLatin1String("reinitalpm")) {
|
} else if(what == QLatin1String("reinitalpm")) {
|
||||||
if(m_socket->peerAddress().isLoopback()) {
|
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.removeAllDatabases();
|
||||||
m_manager.addLocalDatabase();
|
if(m_manager.config().isLocalDatabaseEnabled()) {
|
||||||
m_manager.addDataBasesFromPacmanConfig();
|
m_manager.addLocalDatabase();
|
||||||
|
}
|
||||||
|
if(m_manager.config().areReposFromPacmanConfEnabled()) {
|
||||||
|
m_manager.addDataBasesFromPacmanConfig();
|
||||||
|
}
|
||||||
m_manager.addDatabasesFromRepoIndexConfig();
|
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 {
|
} else {
|
||||||
sendError(QStringLiteral("rejected"), id);
|
sendError(QStringLiteral("rejected"), id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,15 @@ class Connection : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Connection(RepoIndex::Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
|
Connection(Manager &alpmManager, QWebSocket *socket, QObject *parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void processTextMessage(const QString &message);
|
void processTextMessage(const QString &message);
|
||||||
void processBinaryMessage(const QByteArray &message);
|
void processBinaryMessage(const QByteArray &message);
|
||||||
void socketDisconnected();
|
void socketDisconnected();
|
||||||
void sendResult(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value);
|
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:
|
private:
|
||||||
void sendJson(const QJsonObject &obj);
|
void sendJson(const QJsonObject &obj);
|
||||||
|
@ -39,6 +40,8 @@ private:
|
||||||
bool m_groupInfoUpdatesRequested;
|
bool m_groupInfoUpdatesRequested;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONNECTION_H
|
#endif // CONNECTION_H
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"cacheDir": ".",
|
"cacheDir": ".",
|
||||||
|
"storageDir": ".",
|
||||||
|
|
||||||
"aur": {
|
"aur": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"repos": {
|
"repos": {
|
||||||
|
"localEnabled": true,
|
||||||
"fromPacmanConfig": true,
|
"fromPacmanConfig": true,
|
||||||
|
|
||||||
"add": [
|
"add": [
|
||||||
|
@ -30,13 +32,22 @@
|
||||||
"https://localhost/repo/arch/$repo/os/$arch"
|
"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",
|
{"name": "ownstuff-testing",
|
||||||
"sourcesDir": "path/to/local/source/dir",
|
"ignore": true,
|
||||||
"packagesDir": "/run/media/devel/repo/arch/ownstuff-testing/os/x86_64",
|
"sourcesDir": "path/to/local/source/dir",
|
||||||
"upgradeSources": ["aur"],
|
"packagesDir": "/run/media/devel/repo/arch/ownstuff-testing/os/x86_64",
|
||||||
"server": [
|
"upgradeSources": ["aur"],
|
||||||
|
"server": [
|
||||||
"https://localhost/repo/arch/$repo/os/$arch"
|
"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_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_groups"><a href="#groups">Groups</a></li>
|
||||||
<li id="nav_repositories"><a href="#repositories">Repositories</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>
|
<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>
|
</ul>
|
||||||
<form class="navbar-form navbar-left" target="#" onsubmit="repoindex.pageManager.applySearchTerm(this.searchtermInput.value, this.searchtermExact.checked, true); return false;">
|
<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>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#" onclick="repoindex.client.stopServer(); return false;">Stop</a></li>
|
<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>
|
</ul>
|
||||||
</div>
|
</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>
|
<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",
|
FullPackageInfo: "fullpkginfo",
|
||||||
GroupInfo: "groupinfo",
|
GroupInfo: "groupinfo",
|
||||||
UpgradeLookup: "upgradelookup",
|
UpgradeLookup: "upgradelookup",
|
||||||
Suggestions: "suggestions"
|
Suggestions: "suggestions",
|
||||||
|
Postponed: "postponed"
|
||||||
};
|
};
|
||||||
|
|
||||||
repoindex.isLoopback = function(domain) {
|
repoindex.isLoopback = function(domain) {
|
||||||
|
@ -183,7 +184,7 @@
|
||||||
repoindex.pageManager.addError("Server replied \"" + what + "\" with insufficiant data.");
|
repoindex.pageManager.addError("Server replied \"" + what + "\" with insufficiant data.");
|
||||||
} else {
|
} else {
|
||||||
var request = this.requestById(id);
|
var request = this.requestById(id);
|
||||||
if(request && request.sent) {
|
if(!id || (request && request.sent)) {
|
||||||
// use the received information
|
// use the received information
|
||||||
switch(what) {
|
switch(what) {
|
||||||
case repoindex.RequestType.BasicRepoInfo:
|
case repoindex.RequestType.BasicRepoInfo:
|
||||||
|
@ -204,13 +205,22 @@
|
||||||
case repoindex.RequestType.Suggestions:
|
case repoindex.RequestType.Suggestions:
|
||||||
this.useSuggestions(values);
|
this.useSuggestions(values);
|
||||||
break;
|
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:
|
default:
|
||||||
pageManager.addError("Server replied unknown results: " + what);
|
pageManager.addError("Server replied unknown results: " + what);
|
||||||
return; // don't invoke callbacks when results are of unknown type
|
return; // don't invoke callbacks when results are of unknown type
|
||||||
}
|
}
|
||||||
// also invoke callbacks registred for this request
|
if(request) {
|
||||||
for(var i = 0; i < request.callbacks.length; ++i) {
|
// also invoke callbacks registred for this request
|
||||||
request.callbacks[i](values);
|
for(var i = 0; i < request.callbacks.length; ++i) {
|
||||||
|
request.callbacks[i](values);
|
||||||
|
}
|
||||||
|
// TODO: remove request object from list of sent requests
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
repoindex.pageManager.addError("Server replied \"" + what + "\" with unknown ID.");
|
repoindex.pageManager.addError("Server replied \"" + what + "\" with unknown ID.");
|
||||||
|
@ -220,11 +230,11 @@
|
||||||
|
|
||||||
// define functions to perform several requests
|
// define functions to perform several requests
|
||||||
this.requestBasicRepoInfo = function(callback) {
|
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.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) {
|
this.requestFullPackagesInfo = function(packageSelection, callback) {
|
||||||
|
@ -232,7 +242,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
this.requestGroupInfo = function(callback) {
|
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) {
|
this.requestSuggestions = function(repoNames, searchTerm, callback) {
|
||||||
|
@ -266,6 +276,10 @@
|
||||||
this.sendCmd("reinitalpm");
|
this.sendCmd("reinitalpm");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.updateAlpm = function() {
|
||||||
|
this.sendCmd("updatealpm");
|
||||||
|
};
|
||||||
|
|
||||||
this.checkForUpgrades = function(dbName, syncdbNames, callback) {
|
this.checkForUpgrades = function(dbName, syncdbNames, callback) {
|
||||||
var params = {
|
var params = {
|
||||||
db: dbName
|
db: dbName
|
||||||
|
@ -303,11 +317,16 @@
|
||||||
var pkgMgr = repoindex.pageManager.packageManager;
|
var pkgMgr = repoindex.pageManager.packageManager;
|
||||||
repoMgr.removeEntries();
|
repoMgr.removeEntries();
|
||||||
pkgMgr.removeEntries();
|
pkgMgr.removeEntries();
|
||||||
|
var incomplete = false;
|
||||||
var reposInOrder = [];
|
var reposInOrder = [];
|
||||||
for(var repoName in value) {
|
for(var repoName in value) {
|
||||||
if(value.hasOwnProperty(repoName)) {
|
if(value.hasOwnProperty(repoName)) {
|
||||||
reposInOrder.push({name: repoName, info: value[repoName]});
|
var info = value[repoName];
|
||||||
//var repoInfo = value[repoName];
|
if(info.incomplete) {
|
||||||
|
incomplete = true;
|
||||||
|
} else {
|
||||||
|
reposInOrder.push({name: repoName, info: info});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reposInOrder.sort(function(lhs, rhs) {
|
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;
|
this.hasBasicRepoInfo = true;
|
||||||
pkgMgr.invalidate();
|
pkgMgr.invalidate();
|
||||||
repoMgr.invalidate();
|
repoMgr.invalidate();
|
||||||
|
@ -369,7 +390,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(value.error) {
|
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
|
// updating table rows or any other GUI elements is done via callbacks
|
||||||
};
|
};
|
||||||
|
@ -377,9 +415,13 @@
|
||||||
this.useGroupInfo = function(values) {
|
this.useGroupInfo = function(values) {
|
||||||
var groupMgr = repoindex.pageManager.groupManager;
|
var groupMgr = repoindex.pageManager.groupManager;
|
||||||
groupMgr.removeEntries();
|
groupMgr.removeEntries();
|
||||||
|
var incomplete = false;
|
||||||
var groupEntries = groupMgr.entries;
|
var groupEntries = groupMgr.entries;
|
||||||
for(var i1 = 0; i1 < values.length; ++i1) {
|
for(var i1 = 0; i1 < values.length; ++i1) {
|
||||||
var info = values[i1];
|
var info = values[i1];
|
||||||
|
if(info.incomplete) {
|
||||||
|
incomplete = true;
|
||||||
|
}
|
||||||
if(info.repo && info.groups) {
|
if(info.repo && info.groups) {
|
||||||
for(var i2 = 0; i2 < info.groups.length; ++i2) {
|
for(var i2 = 0; i2 < info.groups.length; ++i2) {
|
||||||
var group = info.groups[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;
|
this.hasGroupInfo = true;
|
||||||
groupMgr.useRequestedData();
|
groupMgr.useRequestedData();
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
this.remove = function() {
|
this.remove = function() {
|
||||||
this.rowElement.parent.removeChild(this.rowElement);
|
this.rowElement.parentNode.removeChild(this.rowElement);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hide = function() {
|
this.hide = function() {
|
||||||
|
|
|
@ -171,11 +171,13 @@
|
||||||
if(details.chkd && details.chkd.length) {
|
if(details.chkd && details.chkd.length) {
|
||||||
repoindex.addPackageNames(tb, "Check deps", repoindex.pkgNamesFromDeps(details.chkd));
|
repoindex.addPackageNames(tb, "Check deps", repoindex.pkgNamesFromDeps(details.chkd));
|
||||||
}
|
}
|
||||||
repoindex.addPackageNames(tb, "Required by", details.requ);
|
if(details.requ || details.optf) {
|
||||||
repoindex.addPackageNames(tb, "Optional for", 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, "Conflicts with", repoindex.pkgNamesFromDeps(details.conf));
|
||||||
repoindex.addPackageNames(tb, "Replaces", repoindex.pkgNamesFromDeps(details.repl));
|
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
|
if(entry.info.repo !== "local") { // local repo does no provide package size
|
||||||
repoindex.addField(tb, "Package size", repoindex.makeDataSize(details.csize));
|
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 reason", details.expl ? "explicitly installed" : "installed as dependency");
|
||||||
}
|
}
|
||||||
repoindex.addField(tb, "Install script", repoindex.makeBool(details.scri));
|
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));
|
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
|
// -> update download buttons
|
||||||
if(details.buildAvail || details.srcAvail) {
|
if(details.bav || details.sav) {
|
||||||
var downloadElement = repoindex.addField(tb, "Download");
|
var downloadElement = repoindex.addField(tb, "Download");
|
||||||
var spanElement;
|
var spanElement;
|
||||||
if(details.buildAvail) {
|
if(details.bav) {
|
||||||
spanElement = document.createElement("span");
|
spanElement = document.createElement("span");
|
||||||
var downloadPkgParams = {repo: entry.info.repo, pkg: entry.name, down: "pkg"};
|
var downloadPkgParams = {repo: entry.info.repo, pkg: entry.name, down: "pkg"};
|
||||||
repoindex.setDownloadButton(spanElement, "package", repoindex.makeHash(repoindex.Pages.Packages, downloadPkgParams, true), function() {
|
repoindex.setDownloadButton(spanElement, "package", repoindex.makeHash(repoindex.Pages.Packages, downloadPkgParams, true), function() {
|
||||||
|
@ -203,10 +222,13 @@
|
||||||
});
|
});
|
||||||
downloadElement.appendChild(spanElement);
|
downloadElement.appendChild(spanElement);
|
||||||
}
|
}
|
||||||
if(details.srcAvail) {
|
if(details.srctar && typeof details.srctar === "string") {
|
||||||
spanElement = document.createElement("span");
|
spanElement = document.createElement("span");
|
||||||
var downloadSrcParams = {repo: entry.info.repo, pkg: entry.name, down: "src"};
|
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);
|
downloadElement.appendChild(spanElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
RepoEntry.prototype.constructor = RepoEntry;
|
RepoEntry.prototype.constructor = RepoEntry;
|
||||||
RepoEntry = function(repoName, repoInfo, enabled) {
|
RepoEntry = function(repoName, repoInfo, enabled) {
|
||||||
if(enabled === undefined) {
|
if(enabled === undefined) {
|
||||||
// per default enable all repos with a fix number of packages
|
// per default enable all repos with a fix number of packages except the local database
|
||||||
enabled = repoInfo.packageCount;
|
enabled = repoInfo.packageCount && repoName !== "local";
|
||||||
}
|
}
|
||||||
repoindex.Entry.prototype.constructor.call(this, repoName, repoInfo, enabled);
|
repoindex.Entry.prototype.constructor.call(this, repoName, repoInfo, enabled);
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
this.link.remove = function() {
|
this.link.remove = function() {
|
||||||
this.parent.removeChild(this);
|
this.parentNode.removeChild(this);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
this.createLink();
|
this.createLink();
|
||||||
|
|
Loading…
Reference in New Issue