2015-08-10 22:46:01 +02:00
|
|
|
#include "package.h"
|
|
|
|
#include "database.h"
|
|
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonValue>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QVariant>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace ChronoUtilities;
|
|
|
|
|
|
|
|
namespace PackageManagement {
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
/*!
|
|
|
|
* \cond
|
|
|
|
*/
|
|
|
|
|
|
|
|
inline QString qstr(const char *str)
|
|
|
|
{
|
|
|
|
return QString::fromLocal8Bit(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline QJsonArray qjarry(StringList list)
|
|
|
|
{
|
|
|
|
QJsonArray jsonArray;
|
|
|
|
for(const char *str : list) {
|
|
|
|
jsonArray << qstr(str);
|
|
|
|
}
|
|
|
|
return jsonArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline QJsonArray qjarry(DependencyList list)
|
|
|
|
{
|
|
|
|
QJsonArray jsonArray;
|
|
|
|
for(alpm_depend_t *dep : list) {
|
|
|
|
QJsonObject depObj;
|
|
|
|
depObj.insert(QStringLiteral("name"), dep->name);
|
|
|
|
depObj.insert(QStringLiteral("desc"), dep->desc);
|
|
|
|
depObj.insert(QStringLiteral("ver"), dep->version);
|
|
|
|
switch(dep->mod) {
|
|
|
|
case ALPM_DEP_MOD_ANY:
|
|
|
|
depObj.insert(QStringLiteral("mod"), QStringLiteral("any"));
|
|
|
|
break;
|
|
|
|
case ALPM_DEP_MOD_EQ:
|
|
|
|
depObj.insert(QStringLiteral("mod"), QStringLiteral("eq"));
|
|
|
|
break;
|
|
|
|
case ALPM_DEP_MOD_GE:
|
|
|
|
depObj.insert(QStringLiteral("mod"), QStringLiteral("ge"));
|
|
|
|
break;
|
|
|
|
case ALPM_DEP_MOD_LE:
|
|
|
|
depObj.insert(QStringLiteral("mod"), QStringLiteral("le"));
|
|
|
|
break;
|
|
|
|
case ALPM_DEP_MOD_GT:
|
|
|
|
depObj.insert(QStringLiteral("mod"), QStringLiteral("gt"));
|
|
|
|
break;
|
|
|
|
case ALPM_DEP_MOD_LT:
|
|
|
|
depObj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
jsonArray << depObj;
|
|
|
|
}
|
|
|
|
return jsonArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline QJsonArray qjarry(_alpm_pkgvalidation_t validation)
|
|
|
|
{
|
|
|
|
QJsonArray jsonArray;
|
|
|
|
if(validation & 0x1) {
|
|
|
|
jsonArray << QStringLiteral("none");
|
|
|
|
}
|
|
|
|
if(validation & 0x2) {
|
|
|
|
jsonArray << QStringLiteral("MD5");
|
|
|
|
}
|
|
|
|
if(validation & 0x4) {
|
|
|
|
jsonArray << QStringLiteral("SHA256");
|
|
|
|
}
|
|
|
|
if(validation & 0x8) {
|
|
|
|
jsonArray << QStringLiteral("signature");
|
|
|
|
}
|
|
|
|
if(jsonArray.empty()) {
|
|
|
|
jsonArray << QStringLiteral("unknown");
|
|
|
|
}
|
|
|
|
return jsonArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \endcond
|
|
|
|
*/
|
|
|
|
|
2015-08-10 22:46:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief The PackageVersion class helps parsing package versions.
|
|
|
|
*/
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
/*!
|
|
|
|
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
|
|
|
|
*/
|
|
|
|
PackageVersion::PackageVersion(const QString &versionStr) :
|
|
|
|
PackageVersion(versionStr.toLocal8Bit().data())
|
|
|
|
{}
|
|
|
|
|
2015-08-10 22:46:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
|
|
|
|
*/
|
|
|
|
PackageVersion::PackageVersion(const char *versionStr)
|
|
|
|
{
|
|
|
|
// determine start offsets of version and release
|
|
|
|
const char *versionBeg = nullptr, *releaseBeg = nullptr;
|
|
|
|
for(const char *i = versionStr; ; ++i) {
|
|
|
|
switch(*i) {
|
|
|
|
case 0:
|
|
|
|
goto terminationFound;
|
|
|
|
case ':':
|
|
|
|
if(!versionBeg && !releaseBeg) {
|
|
|
|
versionBeg = i + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
releaseBeg = i + 1;
|
|
|
|
break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
terminationFound:
|
|
|
|
if(versionBeg) {
|
|
|
|
// epoch present
|
|
|
|
epoch = QString::fromLocal8Bit(versionStr, versionBeg - versionStr - 1);
|
|
|
|
if(releaseBeg) {
|
|
|
|
// release present
|
|
|
|
version = QString::fromLocal8Bit(versionBeg, releaseBeg - versionBeg - 1);
|
|
|
|
release = QString::fromLocal8Bit(releaseBeg);
|
|
|
|
} else {
|
|
|
|
version = QString::fromLocal8Bit(versionBeg);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// epoch not present
|
|
|
|
if(releaseBeg) {
|
|
|
|
// release present
|
|
|
|
version = QString::fromLocal8Bit(versionStr, releaseBeg - versionStr - 1);
|
|
|
|
release = QString::fromLocal8Bit(releaseBeg);
|
|
|
|
} else {
|
|
|
|
version = QString::fromLocal8Bit(versionStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Compares two version parts.
|
|
|
|
* \returns Returns 1 if part1 is newer then part2, -1 if part2 is newer then part1 and 0 if both parts are equal.
|
|
|
|
*/
|
|
|
|
PackageVersionPartComparsion PackageVersion::compareParts(const QString &part1, const QString &part2)
|
|
|
|
{
|
|
|
|
static const QRegExp nonAlphanumericPattern(QStringLiteral("[^a-zA-Z\\d:öäüÖÄÜß]"));
|
|
|
|
int part1Pos = 0, part2Pos = 0, part1End, part2End;
|
|
|
|
while(true) {
|
|
|
|
// determine current segments
|
|
|
|
part1End = part1.indexOf(nonAlphanumericPattern, part1Pos);
|
|
|
|
part2End = part2.indexOf(nonAlphanumericPattern, part2Pos);
|
|
|
|
auto segment1 = part1.midRef(part1Pos, part1End);
|
|
|
|
auto segment2 = part2.midRef(part2Pos, part2End);
|
|
|
|
// compare current segments
|
|
|
|
int digit1 = segment1.size();
|
|
|
|
int digit2 = segment2.size();
|
|
|
|
const auto *i1 = segment1.cbegin(), *e1 = segment1.cend();
|
|
|
|
const auto *i2 = segment2.cbegin(), *e2 = segment2.cend();
|
|
|
|
// trim leading zeros
|
|
|
|
for(; i1 < e1 && *i1 == QLatin1Char('0'); ++i1, --digit1);
|
|
|
|
for(; i2 < e2 && *i2 == QLatin1Char('0'); ++i2, --digit2);
|
|
|
|
if(digit1 > digit2) {
|
|
|
|
// segment 1 has more digits -> newer
|
|
|
|
return PackageVersionPartComparsion::Newer;
|
|
|
|
} else if (digit2 > digit1) {
|
|
|
|
// segment 2 has more digits -> newer
|
|
|
|
return PackageVersionPartComparsion::Older;
|
|
|
|
} else {
|
|
|
|
for(; i1 < e1 && i2 < e2; ++i1, ++i2) {
|
|
|
|
if(*i1 > *i2) {
|
|
|
|
// segment 1 digit has higher value -> newer
|
|
|
|
return PackageVersionPartComparsion::Newer;
|
|
|
|
} else if(*i2 > *i1) {
|
|
|
|
// segment 2 digit has higher value -> newer
|
|
|
|
return PackageVersionPartComparsion::Older;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// this segment is equal, look for the next segment
|
|
|
|
if(part1End >= 0 && part2End >= 0) {
|
|
|
|
// there is another segment in both parts
|
|
|
|
part1Pos = part1End + 1;
|
|
|
|
part2Pos = part2End + 1;
|
|
|
|
} else if(part1End >= 0) {
|
|
|
|
// only part 1 has another segment -> it is more specific and hence considered newer
|
|
|
|
return PackageVersionPartComparsion::Newer;
|
|
|
|
} else if(part2End >= 0) {
|
|
|
|
// only part 2 has another segment -> it is more specific and hence considered newer
|
|
|
|
return PackageVersionPartComparsion::Older;
|
|
|
|
} else {
|
|
|
|
// none of the parts has another segment -> parts are equal
|
|
|
|
return PackageVersionPartComparsion::Equal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Compares this version with another version.
|
|
|
|
*
|
|
|
|
* This method distinguishes between software upgrades and package releases. See Alpm::PackageVersionComparsion enum.
|
|
|
|
*/
|
|
|
|
PackageVersionComparsion PackageVersion::compare(const PackageVersion &other) const
|
|
|
|
{
|
|
|
|
if(!epoch.isEmpty() || !other.epoch.isEmpty()) {
|
|
|
|
switch(compareParts(other.epoch, epoch)) {
|
|
|
|
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade;
|
|
|
|
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
|
|
|
|
case PackageVersionPartComparsion::Equal: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch(compareParts(other.version, version)) {
|
|
|
|
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade;
|
|
|
|
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
|
|
|
|
case PackageVersionPartComparsion::Equal: ;
|
|
|
|
}
|
|
|
|
switch(compareParts(other.release, release)) {
|
|
|
|
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::PackageUpgradeOnly;
|
|
|
|
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
|
|
|
|
case PackageVersionPartComparsion::Equal: ;
|
|
|
|
}
|
|
|
|
return PackageVersionComparsion::Equal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief The AurPackage class holds information about AUR packages. It allows to convert the information
|
|
|
|
* to JSON objects used by the network classes and the web interface.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Creates a new instance from the specified "AurJson value".
|
|
|
|
*/
|
|
|
|
AurPackage::AurPackage(const QJsonValue &aurJsonValue) :
|
|
|
|
AurPackage()
|
|
|
|
{
|
|
|
|
QJsonObject obj = aurJsonValue.toObject();
|
|
|
|
m_id = obj.value(QStringLiteral("ID")).toInt(-1);
|
|
|
|
m_categoryId = obj.value(QStringLiteral("CategoryID")).toInt(-1);
|
2015-08-19 02:13:28 +02:00
|
|
|
m_name = obj.value(QStringLiteral("Name")).toString();
|
|
|
|
m_version = obj.value(QStringLiteral("Version")).toString();
|
2015-08-10 22:46:01 +02:00
|
|
|
m_description = obj.value(QStringLiteral("Description")).toString();
|
|
|
|
m_upstreamUrl = obj.value(QStringLiteral("URL")).toString();
|
|
|
|
m_votes = obj.value(QStringLiteral("NumVotes")).toInt(0);
|
2015-08-19 02:13:28 +02:00
|
|
|
m_outOfDate = DateTime::fromTimeStamp(obj.value(QStringLiteral("OutOfDate")).toInt());
|
2015-08-10 22:46:01 +02:00
|
|
|
m_maintainer = obj.value(QStringLiteral("Maintainer")).toString();
|
|
|
|
m_firstSubmitted = DateTime::fromTimeStamp(obj.value(QStringLiteral("FirstSubmitted")).toInt());
|
|
|
|
m_lastModified = DateTime::fromTimeStamp(obj.value(QStringLiteral("LastModified")).toInt());
|
|
|
|
m_license = obj.value(QStringLiteral("License")).toString();
|
|
|
|
m_tarUrl = obj.value(QStringLiteral("URLPath")).toString();
|
|
|
|
}
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
|
2015-08-10 22:46:01 +02:00
|
|
|
|
|
|
|
/*!
|
2015-08-19 02:13:28 +02:00
|
|
|
* \brief Returns basic information about the packages as JSON object.
|
2015-08-10 22:46:01 +02:00
|
|
|
*/
|
2015-08-19 02:13:28 +02:00
|
|
|
QJsonObject AurPackage::basicInfo(bool includeRepoAndName) const
|
2015-08-10 22:46:01 +02:00
|
|
|
{
|
2015-08-19 02:13:28 +02:00
|
|
|
QJsonObject packageInfo;
|
|
|
|
if(includeRepoAndName) {
|
|
|
|
packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur"));
|
|
|
|
packageInfo.insert(QStringLiteral("name"), name());
|
2015-08-10 22:46:01 +02:00
|
|
|
}
|
2015-08-19 02:13:28 +02:00
|
|
|
packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("ver"), version());
|
|
|
|
packageInfo.insert(QStringLiteral("desc"), description());
|
|
|
|
packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("flagdate"), outOfDate().isNull() ? QStringLiteral("none") : qstr(outOfDate().toString().data()));
|
|
|
|
return packageInfo;
|
2015-08-10 22:46:01 +02:00
|
|
|
}
|
|
|
|
|
2015-08-19 02:13:28 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns full information about the package as JSON object.
|
|
|
|
*/
|
|
|
|
QJsonObject AurPackage::fullInfo(bool includeRepoAndName) const
|
2015-08-10 22:46:01 +02:00
|
|
|
{
|
2015-08-19 02:13:28 +02:00
|
|
|
QJsonObject packageInfo;
|
|
|
|
if(includeRepoAndName) {
|
|
|
|
packageInfo.insert(QStringLiteral("repo"), QStringLiteral("aur"));
|
|
|
|
packageInfo.insert(QStringLiteral("name"), name());
|
2015-08-10 22:46:01 +02:00
|
|
|
}
|
2015-08-19 02:13:28 +02:00
|
|
|
packageInfo.insert(QStringLiteral("arch"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("ver"), version());
|
|
|
|
packageInfo.insert(QStringLiteral("desc"), description());
|
|
|
|
packageInfo.insert(QStringLiteral("bdate"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("idate"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("isize"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("url"), upstreamUrl());
|
|
|
|
packageInfo.insert(QStringLiteral("lic"), license());
|
|
|
|
packageInfo.insert(QStringLiteral("grp"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("prov"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("optd"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("deps"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("requ"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("optf"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("conf"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("repl"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("pack"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("expl"), QStringLiteral("n.a."));
|
|
|
|
packageInfo.insert(QStringLiteral("scri"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("sig"), QStringLiteral("TODO"));
|
|
|
|
packageInfo.insert(QStringLiteral("file"), QStringLiteral("n.a."));
|
|
|
|
return packageInfo;
|
2015-08-10 22:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2015-08-19 02:13:28 +02:00
|
|
|
* \brief The AlpmPackage class helps getting information about ALPM packages. It allows to convert the
|
|
|
|
* information to JSON objects used by the network classes and the web interface.
|
2015-08-10 22:46:01 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns basic information about the packages as JSON object.
|
|
|
|
*/
|
|
|
|
QJsonObject AlpmPackage::basicInfo(bool includeRepoAndName) const
|
|
|
|
{
|
|
|
|
QJsonObject packageInfo;
|
|
|
|
if(includeRepoAndName) {
|
|
|
|
packageInfo.insert(QStringLiteral("repo"), qstr(AlpmDataBase(associatedDatabase()).name()));
|
|
|
|
packageInfo.insert(QStringLiteral("name"), qstr(name()));
|
|
|
|
}
|
|
|
|
packageInfo.insert(QStringLiteral("arch"), qstr(architecture()));
|
|
|
|
packageInfo.insert(QStringLiteral("ver"), qstr(version()));
|
|
|
|
packageInfo.insert(QStringLiteral("desc"), qstr(description()));
|
|
|
|
packageInfo.insert(QStringLiteral("bdate"), qstr(buildDate().toString().c_str()));
|
2015-08-19 02:13:28 +02:00
|
|
|
packageInfo.insert(QStringLiteral("flagdate"), QStringLiteral("n.a."));
|
2015-08-10 22:46:01 +02:00
|
|
|
return packageInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns full information about the package as JSON object.
|
|
|
|
*/
|
|
|
|
QJsonObject AlpmPackage::fullInfo(bool includeRepoAndName) const
|
|
|
|
{
|
|
|
|
QJsonObject packageInfo;
|
|
|
|
if(includeRepoAndName) {
|
|
|
|
packageInfo.insert(QStringLiteral("repo"), qstr(AlpmDataBase(associatedDatabase()).name()));
|
|
|
|
packageInfo.insert(QStringLiteral("name"), qstr(name()));
|
|
|
|
}
|
|
|
|
packageInfo.insert(QStringLiteral("arch"), qstr(architecture()));
|
|
|
|
packageInfo.insert(QStringLiteral("ver"), qstr(version()));
|
|
|
|
packageInfo.insert(QStringLiteral("desc"), qstr(description()));
|
|
|
|
packageInfo.insert(QStringLiteral("bdate"), qstr(buildDate().toString().c_str()));
|
|
|
|
packageInfo.insert(QStringLiteral("idate"), qstr(installDate().toString().c_str()));
|
|
|
|
packageInfo.insert(QStringLiteral("isize"), static_cast<long long int>(installedSize()));
|
|
|
|
packageInfo.insert(QStringLiteral("url"), qstr(upstreamUrl()));
|
|
|
|
packageInfo.insert(QStringLiteral("lic"), qjarry(licenses()));
|
|
|
|
packageInfo.insert(QStringLiteral("grp"), qjarry(groups()));
|
|
|
|
packageInfo.insert(QStringLiteral("prov"), qjarry(provides()));
|
|
|
|
packageInfo.insert(QStringLiteral("optd"), qjarry(optionalDependencies()));
|
|
|
|
packageInfo.insert(QStringLiteral("deps"), qjarry(dependencies()));
|
|
|
|
packageInfo.insert(QStringLiteral("requ"), qjarry(requiredBy()));
|
|
|
|
packageInfo.insert(QStringLiteral("optf"), qjarry(optionalFor()));
|
|
|
|
packageInfo.insert(QStringLiteral("conf"), qjarry(conflicts()));
|
|
|
|
packageInfo.insert(QStringLiteral("repl"), qjarry(replaces()));
|
|
|
|
packageInfo.insert(QStringLiteral("pack"), qstr(packager()));
|
|
|
|
packageInfo.insert(QStringLiteral("expl"), installReason() == ALPM_PKG_REASON_EXPLICIT);
|
|
|
|
packageInfo.insert(QStringLiteral("scri"), hasInstallScript());
|
|
|
|
packageInfo.insert(QStringLiteral("sig"), qjarry(validation()));
|
|
|
|
packageInfo.insert(QStringLiteral("file"), qstr(fileName()));
|
|
|
|
QJsonArray fileInfos;
|
|
|
|
alpm_filelist_t *fileList = files();
|
|
|
|
for(alpm_file_t *file = fileList->files, *end = fileList->files + fileList->count; file != end; ++file) {
|
|
|
|
QJsonObject fileInfo;
|
|
|
|
fileInfo.insert(QStringLiteral("name"), qstr(file->name));
|
|
|
|
if(file->mode) {
|
|
|
|
fileInfo.insert(QStringLiteral("mode"), static_cast<int>(file->mode));
|
|
|
|
}
|
|
|
|
if(file->size) {
|
|
|
|
fileInfo.insert(QStringLiteral("size"), static_cast<int>(file->size));
|
|
|
|
}
|
|
|
|
fileInfos << fileInfo;
|
|
|
|
}
|
|
|
|
packageInfo.insert(QStringLiteral("files"), fileInfos);
|
|
|
|
return packageInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|