#include "./package.h" #include "./alpmdatabase.h" #include "./utilities.h" #include "./repository.h" #include #include #include #include #include #include #include #include #include using namespace std; using namespace std::placeholders; using namespace CppUtilities; namespace RepoIndex { /*! * \class The Package class holds meta information about an * Arch Linux package. */ /*! * \brief Constructs a new package instance. * * Since it is intenced to use the Package class as base class only, * this constructor is protected. */ Package::Package(const QString &name, Repository *repository) : m_origin(PackageOrigin::Unknown), m_repository(repository), m_hasGeneralInfo(false), m_hasAllGeneralInfo(false), m_name(name), m_requiredByComputed(false), m_hasInstallScript(UnknownTrueFalse::Unknown), m_hasBuildRelatedMetaData(false), m_hasInstallRelatedMetaData(false), m_validationMethods(PackageValidation::Unknown), m_installReason(InstallStatus::None), m_id(-1), m_categoryId(-1), m_votes(-1) { // initialization must be done in derived class } /*! * \brief Writes the package-type-specific cache header. */ void Package::writeSpecificCacheHeader(QDataStream &out) { Q_UNUSED(out) } /*! * \brief Restores the package-type-specific cache header. */ void Package::restoreSpecificCacheHeader(QDataStream &in) { Q_UNUSED(in) } /*! * \brief Destroys the package. */ Package::~Package() {} /*! * \brief Computes required-by and optional-for fields. * * Sources the specified \a relevantRepositores for packages depending on this package. */ void Package::computeRequiredBy(const QList &relevantRepositories) { if(m_requiredByComputed) { m_requiredBy.clear(); m_optionalFor.clear(); } for(const Repository *repo : relevantRepositories) { for(const auto &pkgEntry : repo->packages()) { for(const auto &dep : pkgEntry.second->dependencies()) { if(dep.name == m_name) { m_requiredBy << pkgEntry.first; break; } } for(const auto &dep : pkgEntry.second->optionalDependencies()) { if(dep.name == m_name) { m_optionalFor << pkgEntry.first; break; } } } } m_requiredByComputed = true; } /*! * \brief Checks whether the specified \a name and the specified \a version match the specified \a dependency. */ bool Package::matches(const QString &name, const QString &version, const Dependency &dependency) { if(name == dependency.name) { if(dependency.version.isEmpty()) { return true; } else { PackageVersionComparsion cmp; switch(dependency.mode) { case DependencyMode::Any: return true; case DependencyMode::Equal: return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::Equal; case DependencyMode::GreatherEqual: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThanSyncVersion; case DependencyMode::LessEqual: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade; case DependencyMode::GreatherThen: return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::NewerThanSyncVersion; case DependencyMode::LessThen: return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade; default: ; } } } return false; } /*! * \brief Checks whether the package matches the specified \a dependency. */ bool Package::matches(const Dependency &dependency) { // check whether package matches "directly" if(matches(name(), version(), dependency)) { return true; } else { // check whether at least one of the provides matches for(const auto &provide : provides()) { if(matches(provide.name, provide.version, dependency)) { return true; } } return false; } } /*! * \cond */ namespace Utilities { inline void put(QJsonObject &obj, const QString &key, const QJsonValue &value) { obj.insert(key, value); } inline void put(QJsonObject &obj, const QString &key, const DateTime dateTime) { if(!dateTime.isNull()) { put(obj, key, QString::fromLocal8Bit(dateTime.toString().data())); } } inline void put(QJsonObject &obj, const QString &key, const QStringList &values) { put(obj, key, QJsonArray::fromStringList(values)); } void put(QJsonObject &obj, const QString &key, const QList &dependencies) { QJsonArray jsonArray; for(const auto &dependency : dependencies) { jsonArray << dependency.toJson(); } put(obj, key, jsonArray); } QDataStream &operator <<(QDataStream &out, const QList dependencies) { out << static_cast(dependencies.count()); for(const auto &dependency : dependencies) { out << dependency.name << dependency.version << static_cast(dependency.mode); } return out; } QDataStream &operator >>(QDataStream &in, QList &dependencies) { quint32 size; in >> size; for(quint32 i = 0; i < size; ++i) { QString name; in >> name; QString version; in >> version; quint32 mode; in >> mode; dependencies << Dependency(name, version, static_cast(mode)); } return in; } inline QDataStream &operator <<(QDataStream &out, const DateTime dateTime) { return out << static_cast(dateTime.totalTicks()); } inline QDataStream &operator >>(QDataStream &in, DateTime &dateTime) { quint64 ticks; in >> ticks; dateTime = DateTime(ticks); return in; } QDataStream &operator <<(QDataStream &out, const map fileMap) { out << static_cast(fileMap.size()); for(const auto &entry : fileMap) { out << entry.first << entry.second; } return out; } QDataStream &operator >>(QDataStream &in, map &fileMap) { quint32 size; in >> size; for(quint32 i = 0; i < size; ++i) { QString path; in >> path; QByteArray data; in >> data; fileMap.emplace(path, data); } return in; } QDataStream &operator <<(QDataStream &out, const QJsonArray &jsonArray) { QJsonDocument doc; doc.setArray(jsonArray); out << doc.toBinaryData(); return out; } QDataStream &operator >>(QDataStream &in, QJsonArray &jsonArray) { QByteArray data; in >> data; QJsonDocument doc = QJsonDocument::fromBinaryData(data); jsonArray = doc.array(); return in; } } /*! * \endcond */ using namespace Utilities; /*! * \brief Returns basic information about the packages as JSON object. */ QJsonObject Package::basicInfo(bool includeRepoAndName) const { QJsonObject info; if(includeRepoAndName) { if(repository()) { put(info, QStringLiteral("repo"), repository()->name()); } put(info, QStringLiteral("name"), name()); } put(info, QStringLiteral("ver"), version()); put(info, QStringLiteral("desc"), description()); put(info, QStringLiteral("fdate"), outOfDate()); put(info, QStringLiteral("arch"), buildArchitecture()); put(info, QStringLiteral("bdate"), buildDate()); put(info, QStringLiteral("archs"), architectures()); return info; } /*! * \brief Returns full information about the package as JSON object. */ QJsonObject Package::detailedInfo() const { QJsonObject info; put(info, QStringLiteral("iav"), hasInstallRelatedMetaData()); put(info, QStringLiteral("bav"), hasBuildRelatedMetaData()); put(info, QStringLiteral("sav"), hasSourceRelatedMetaData()); put(info, QStringLiteral("idate"), installDate()); put(info, QStringLiteral("isize"), QJsonValue(static_cast(installedSize()))); put(info, QStringLiteral("csize"), QJsonValue(static_cast(packageSize()))); put(info, QStringLiteral("url"), upstreamUrl()); put(info, QStringLiteral("lic"), licenses()); put(info, QStringLiteral("grp"), groups()); put(info, QStringLiteral("prov"), provides()); put(info, QStringLiteral("deps"), dependencies()); put(info, QStringLiteral("optd"), optionalDependencies()); put(info, QStringLiteral("mkd"), makeDependencies()); put(info, QStringLiteral("chkd"), checkDependencies()); if(isRequiredByComputed()) { put(info, QStringLiteral("requ"), requiredBy()); put(info, QStringLiteral("optf"), optionalFor()); } put(info, QStringLiteral("conf"), conflicts()); put(info, QStringLiteral("repl"), replaces()); put(info, QStringLiteral("pack"), packager()); put(info, QStringLiteral("expl"), QJsonValue(installReason() == InstallStatus::Explicit)); if(hasInstallScript() != UnknownTrueFalse::Unknown) { put(info, QStringLiteral("scri"), QJsonValue(hasInstallScript() == UnknownTrueFalse::True)); } put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods())); put(info, QStringLiteral("file"), fileName()); put(info, QStringLiteral("files"), files()); put(info, QStringLiteral("fsub"), firstSubmitted()); put(info, QStringLiteral("lmod"), lastModified()); if(!maintainer().isEmpty()) { put(info, QStringLiteral("main"), maintainer()); } if(!tarUrl().isEmpty()) { put(info, QStringLiteral("srctar"), tarUrl()); } if(votes() >= 0) { put(info, QStringLiteral("votes"), votes()); } return info; } QJsonObject Package::simpleInfo() const { QJsonObject info; put(info, QStringLiteral("name"), name()); put(info, QStringLiteral("ver"), version()); put(info, QStringLiteral("desc"), description()); put(info, QStringLiteral("flagdate"), outOfDate()); put(info, QStringLiteral("arch"), buildArchitecture()); put(info, QStringLiteral("bdate"), buildDate()); put(info, QStringLiteral("url"), upstreamUrl()); put(info, QStringLiteral("lic"), licenses()); put(info, QStringLiteral("packer"), packager()); return info; } /*! * \brief Writes the package contents to the specified data stream. */ void Package::writeToCacheStream(QDataStream &out) { out << static_cast(m_origin) << m_timeStamp; // general info out << m_hasGeneralInfo << m_hasAllGeneralInfo << m_name << m_version << m_description << m_upstreamUrl << m_licenses << m_groups << m_dependencies << m_optionalDependencies << m_conflicts << m_provides << m_replaces << m_requiredByComputed << m_requiredBy << m_optionalFor << static_cast(m_hasInstallScript); // build related meta data out << m_hasBuildRelatedMetaData << m_fileName << m_files << m_buildDate << m_packager << m_md5 << m_sha256 << m_buildArchitecture << m_packageSize << m_makeDependencies; // installation related meta data out << m_hasInstallRelatedMetaData << m_installDate << m_installedSize << m_backupFiles << static_cast(m_validationMethods) << static_cast(m_installReason); // source related meta data out << m_hasSourceRelatedMetaData << m_baseName << m_architectures << m_id << m_categoryId << m_votes << m_outOfDate << m_maintainer << m_firstSubmitted << m_lastModified << m_tarUrl << m_sourceFiles; // write specific header auto headerStart = out.device()->pos(); out.device()->seek(headerStart + 4); writeSpecificCacheHeader(out); auto headerEnd = out.device()->pos(); out.device()->seek(headerStart); out << static_cast(headerEnd - headerStart - 4); out.device()->seek(headerEnd); // no extended header out << static_cast(0); } void Package::restoreFromCacheStream(QDataStream &in) { qint32 tmp; // origin in >> tmp; m_origin = static_cast(tmp); // TODO: validate value in >> m_timeStamp; // general info unsigned char tmp2; in >> m_hasGeneralInfo >> m_hasAllGeneralInfo >> m_name >> m_version >> m_description >> m_upstreamUrl >> m_licenses >> m_groups >> m_dependencies >> m_optionalDependencies >> m_conflicts >> m_provides >> m_replaces >> m_requiredByComputed >> m_requiredBy >> m_optionalFor >> tmp2; m_hasInstallScript = static_cast(tmp2); // TODO: validate value // build related meta data in >> m_hasBuildRelatedMetaData >> m_fileName >> m_files >> m_buildDate >> m_packager >> m_md5 >> m_sha256 >> m_buildArchitecture >> m_packageSize >> m_makeDependencies; // installation related meta data in >> m_hasInstallRelatedMetaData >> m_installDate >> m_installedSize >> m_backupFiles; in >> tmp; m_validationMethods = static_cast(tmp); // TODO: validate value in >> tmp; m_installReason = static_cast(tmp); // TODO: validate value // source related meta data in >> m_hasSourceRelatedMetaData >> m_baseName >> m_architectures >> m_id >> m_categoryId >> m_votes >> m_outOfDate >> m_maintainer >> m_firstSubmitted >> m_lastModified >> m_tarUrl >> m_sourceFiles; // specific header quint32 headerSize; in >> headerSize; quint64 headerEnd = in.device()->pos() + headerSize; restoreSpecificCacheHeader(in); in.device()->seek(headerEnd); if(in.status() == QDataStream::Ok) { // skip extended header in >> headerSize; quint64 headerEnd = in.device()->pos() + headerSize; in.device()->seek(headerEnd); } } /*! * \brief Puts the specified .SRCINFO/.PKGINFO key value pairs; clears current values before inserting new values. * \remarks * - This method should only be called by the associated repository because the associated repository might * has to handle a possible name change. * - Actual parsing of .SRCINFO/.PKGINFO is done in Repository::addPackagesFromSrcInfo() because one info file * applies to multiple packages in case of split packages. */ void Package::putInfo(const QList > &baseInfo, const QList > &pkgInfo, bool includesSourceRelatedMetaData, bool includesBuildRelatedMetaData) { // clear current values m_baseName.clear(); m_description.clear(); m_upstreamUrl.clear(); m_packager.clear(); m_licenses.clear(); m_dependencies.clear(); m_optionalDependencies.clear(); m_conflicts.clear(); m_provides.clear(); m_replaces.clear(); if(includesBuildRelatedMetaData) { m_buildArchitecture.clear(); } if(includesSourceRelatedMetaData) { m_architectures.clear(); m_makeDependencies.clear(); m_checkDependencies.clear(); m_installedSize = 0; m_buildDate = DateTime(); m_packager.clear(); } m_hasInstallScript = UnknownTrueFalse::False; // prevent overwriting these crucial fields with empty values QString name; PackageVersion version; // read specified key value pairs const auto infos = {baseInfo, pkgInfo}; for(const auto &info : infos) { for(const auto &pair : info) { const auto &field = pair.first; if(field == QLatin1String("pkgbase")) { m_baseName.clear(); } else if(field == QLatin1String("pkgname")) { name.clear(); } else if(field == QLatin1String("epoch")) { version.epoch.clear(); } else if(field == QLatin1String("pkgver")) { version.version.clear(); } else if(field == QLatin1String("pkgrel")) { version.release.clear(); } else if(field == QLatin1String("pkgdesc")) { m_description.clear(); } else if(field == QLatin1String("url")) { m_upstreamUrl.clear(); } else if(field == QLatin1String("arch")) { if(includesSourceRelatedMetaData) { m_architectures.clear(); } if(includesBuildRelatedMetaData) { m_buildArchitecture.clear(); } } else if(field == QLatin1String("license")) { m_licenses.clear(); } else if(field == QLatin1String("depends")) { m_dependencies.clear(); } else if(field == QLatin1String("makedepends")) { m_makeDependencies.clear(); } else if(field == QLatin1String("checkdepends")) { m_checkDependencies.clear(); } else if(field == QLatin1String("optdepends")) { m_optionalDependencies.clear(); } else if(field == QLatin1String("conflicts")) { m_conflicts.clear(); } else if(field == QLatin1String("provides")) { m_provides.clear(); } else if(field == QLatin1String("replaces")) { m_replaces.clear(); } else if(field == QLatin1String("source")) { // currently not used } else if(field == QLatin1String("size")) { m_installedSize = 0; } else if(field == QLatin1String("builddate")) { m_buildDate = DateTime(); } else if(field == QLatin1String("packager")) { m_packager.clear(); } } for(const auto &pair : info) { const auto &field = pair.first; const auto &value = pair.second; if(field == QLatin1String("pkgbase")) { m_baseName = value; } else if(field == QLatin1String("pkgname")) { name = value; } else if(field == QLatin1String("epoch")) { version.epoch = value; } else if(field == QLatin1String("pkgver")) { version.version = value; } else if(field == QLatin1String("pkgrel")) { version.release = value; } else if(field == QLatin1String("pkgdesc")) { m_description = value; } else if(field == QLatin1String("url")) { m_upstreamUrl = value; } else if(field == QLatin1String("arch")) { if(includesSourceRelatedMetaData) { m_architectures << value; } if(includesBuildRelatedMetaData) { m_buildArchitecture = value; } } else if(field == QLatin1String("license")) { m_licenses << value; } else if(field == QLatin1String("depends")) { m_dependencies << Dependency(value); } else if(field == QLatin1String("makedepends")) { m_makeDependencies << Dependency(value); } else if(field == QLatin1String("checkdepends")) { m_checkDependencies << Dependency(value); } else if(field == QLatin1String("optdepends")) { m_optionalDependencies << Dependency(value); } else if(field == QLatin1String("conflicts")) { m_conflicts << Dependency(value); } else if(field == QLatin1String("provides")) { m_provides << Dependency(value); } else if(field == QLatin1String("replaces")) { m_replaces << Dependency(value); } else if(field == QLatin1String("source")) { // currently not used } else if(field == QLatin1String("size")) { m_installedSize = value.toUInt(); } else if(field == QLatin1String("builddate")) { m_buildDate = DateTime::fromTimeStampGmt(value.toUInt()); } else if(field == QLatin1String("packager")) { m_packager = value; } else if(field == QLatin1String("install")) { m_hasInstallScript = UnknownTrueFalse::True; } } } // ensure crucial information is still present (use old values rather than no values) if(!name.isEmpty()) { m_name = name; } if(!version.version.isEmpty()) { m_version = version.toString(); } // use the name as base name if the base name hasn't been specified explicitely if(includesSourceRelatedMetaData && m_baseName.isEmpty()) { m_baseName = m_name; } // consider general information as complete m_hasGeneralInfo = m_hasAllGeneralInfo = true; m_hasSourceRelatedMetaData = includesSourceRelatedMetaData; m_hasBuildRelatedMetaData = includesBuildRelatedMetaData; } /*! * \cond */ #define PUT_DEPS(lhs, rhs) \ lhs.reserve(rhs.size()); \ for(const QString &dep : rhs) { \ lhs << Dependency(dep); \ } \ const map Package::m_descMap { {QStringLiteral("NAME"), &Package::setName}, {QStringLiteral("VERSION"), &Package::setVersion}, {QStringLiteral("DESC"), &Package::setDescription}, {QStringLiteral("URL"), &Package::setUrl}, {QStringLiteral("ARCH"), &Package::setArch}, {QStringLiteral("LICENSE"), &Package::setLicenses}, {QStringLiteral("DEPENDS"), &Package::setDepends}, {QStringLiteral("MAKEDEPENDS"), &Package::setMakeDepends}, {QStringLiteral("CHECKDEPENDS"), &Package::setCheckDepends}, {QStringLiteral("OPTDEPENDS"), &Package::setOptDepends}, {QStringLiteral("CONFLICTS"), &Package::setConflicts}, {QStringLiteral("PROVIDES"), &Package::setProvides}, {QStringLiteral("REPLACES"), &Package::setReplaces}, {QStringLiteral("BUILDDATE"), &Package::setBuildDate}, {QStringLiteral("INSTALLDATE"), &Package::setInstallDate}, {QStringLiteral("ISIZE"), &Package::setInstalledSize}, {QStringLiteral("SIZE"), &Package::setInstalledSize}, {QStringLiteral("CSIZE"), &Package::setPackageSize}, {QStringLiteral("PACKAGER"), &Package::setPackager}, {QStringLiteral("MD5SUM"), &Package::setMd5}, {QStringLiteral("SHA256SUM"), &Package::setSha256}, {QStringLiteral("PGPSIG"), &Package::setPgpSignature}, {QStringLiteral("FILES"), &Package::setFiles}, {QStringLiteral("REASON"), &Package::setInstallReason}, {QStringLiteral("VALIDATION"), &Package::setValidation}, {QStringLiteral("GROUPS"), &Package::setGroups}, {QStringLiteral("FILENAME"), &Package::setFileName} }; void Package::setName(const QStringList &values) { if(!values.isEmpty() && !values.back().isEmpty()) { m_name = values.back(); } } void Package::setVersion(const QStringList &values) { if(!values.isEmpty() && !values.back().isEmpty()) { m_version = values.back(); } } void Package::setDescription(const QStringList &values) { if(!values.isEmpty()) { m_description = values.back(); } } void Package::setUrl(const QStringList &values) { if(!values.isEmpty()) { m_upstreamUrl = values.back(); } } void Package::setArch(const QStringList &values) { if(!values.isEmpty()) { m_buildArchitecture = values.back(); } } void Package::setLicenses(const QStringList &values) { m_licenses = values; } void Package::setDepends(const QStringList &values) { PUT_DEPS(m_dependencies, values); } void Package::setMakeDepends(const QStringList &values) { PUT_DEPS(m_makeDependencies, values); } void Package::setCheckDepends(const QStringList &values) { PUT_DEPS(m_checkDependencies, values); } void Package::setOptDepends(const QStringList &values) { PUT_DEPS(m_optionalDependencies, values); } void Package::setConflicts(const QStringList &values) { PUT_DEPS(m_conflicts, values); } void Package::setProvides(const QStringList &values) { PUT_DEPS(m_provides, values); } void Package::setReplaces(const QStringList &values) { PUT_DEPS(m_replaces, values); } void Package::setBuildDate(const QStringList &values) { if(!values.isEmpty()) { m_buildDate = DateTime::fromTimeStampGmt(values.back().toUInt()); } } void Package::setInstallDate(const QStringList &values) { if(!values.isEmpty()) { m_installDate = DateTime::fromTimeStampGmt(values.back().toUInt()); } } void Package::setInstalledSize(const QStringList &values) { if(!values.empty()) { m_installedSize = values.back().toUInt(); m_hasInstallRelatedMetaData = true; } } void Package::setPackageSize(const QStringList &values) { if(!values.empty()) { m_packageSize = values.back().toUInt(); } } void Package::setPackager(const QStringList &values) { if(!values.isEmpty()) { m_packager = values.back(); } } void Package::setMd5(const QStringList &values) { if(!values.isEmpty()) { if(!(m_md5 = values.back()).isEmpty()) { m_validationMethods |= PackageValidation::Md5Sum; } else { m_validationMethods &= ~PackageValidation::Md5Sum; } } } void Package::setSha256(const QStringList &values) { if(!values.isEmpty()) { 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) { m_files = QJsonArray(); for(const QString &value : values) { QJsonObject fileObj; fileObj.insert(QStringLiteral("name"), value); m_files << fileObj; } } void Package::setValidation(const QStringList &values) { if(!values.isEmpty()) { for(const QString &value : values) { if(value == QLatin1String("md5")) { m_validationMethods = m_validationMethods | PackageValidation::Md5Sum; } else if(value == QLatin1String("sha256")) { m_validationMethods = m_validationMethods | PackageValidation::Sha256Sum; } else if(value == QLatin1String("pgp")) { m_validationMethods = m_validationMethods | PackageValidation::PgpSignature; } else { // TODO: error handling (imporant?) } } } m_hasInstallRelatedMetaData = true; } void Package::setGroups(const QStringList &values) { m_groups = values; } void Package::setFileName(const QStringList &values) { if(!values.isEmpty()) { m_fileName = values.back(); } } void Package::setInstallReason(const QStringList &values) { if(!values.isEmpty()) { m_installReason = values.back().toUInt() == 1 ? InstallStatus::AsDependency : InstallStatus::Explicit; } } /*! * \endcond */ /*! * \brief Puts the specified desc/depends/files key value(s) pairs; clears current values before inserting new values. * \remarks * - This method should only be called by the associated repository because the associated repository might * has to handle a possible name change. * - Actual parsing of desc/depends/files is done in Repository::addPackagesFromSrcInfo() because one info file * applies to multiple packages in case of split packages. */ void Package::putDescription(const QString &name, const QList > &description, PackageOrigin origin) { // set name if(!name.isEmpty()) { m_name = name; } // clear current meta data m_origin = origin; m_hasInstallRelatedMetaData = origin == PackageOrigin::LocalDb; m_fileName.clear(); m_description.clear(); m_upstreamUrl.clear(); m_buildArchitecture.clear(); m_licenses.clear(); m_dependencies.clear(); m_makeDependencies.clear(); m_checkDependencies.clear(); m_optionalDependencies.clear(); m_conflicts.clear(); m_provides.clear(); m_replaces.clear(); m_buildDate = m_installDate = DateTime(); m_packageSize = m_installedSize = 0; m_files = QJsonArray(); m_md5.clear(); m_sha256.clear(); m_installReason = origin == PackageOrigin::LocalDb ? InstallStatus::Explicit : InstallStatus::None; m_validationMethods = PackageValidation::Unknown; // asign fields for(const auto &pair : description) { try { (this->*m_descMap.at(pair.first))(pair.second); } catch(const out_of_range &) { } } // description provides source related meta data, too (except pkgbase, TODO: special flag required?) m_hasBuildRelatedMetaData = m_hasSourceRelatedMetaData = true; } /*! * \brief Adds a source file with the specified \a path and \a data. */ void Package::putSourceFile(const QString &path, const QByteArray &data) { m_sourceFiles.insert(make_pair(path, data)); } /*! * \class PackageVersion * \brief The PackageVersion class helps parsing package versions. */ /*! * \brief Constructs a new PackageVersion instance from the specified \a versionStr. */ PackageVersion::PackageVersion(const QString &versionStr) { // determine start offsets of version and release const ushort *str = versionStr.utf16(), *versionBeg = nullptr, *releaseBeg = nullptr; for(const auto *i = str; ; ++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::fromUtf16(str, versionBeg - str - 1); if(releaseBeg) { // release present version = QString::fromUtf16(versionBeg, releaseBeg - versionBeg - 1); release = QString::fromUtf16(releaseBeg); } else { version = QString::fromUtf16(versionBeg); release = QStringLiteral("1"); } } else { // epoch not present epoch = QStringLiteral("0"); if(releaseBeg) { // release present version = QString::fromUtf16(str, releaseBeg - str - 1); release = QString::fromUtf16(releaseBeg); } else { version = QString::fromUtf16(str); release = QStringLiteral("1"); } } } /*! * \brief Constructs an empty package version. */ PackageVersion::PackageVersion() {} /*! * \brief Returns the string representation of the package version: epoch:version-release */ QString RepoIndex::PackageVersion::toString() const { if(epoch.isEmpty() || epoch == QLatin1String("0")) { return version % QChar('-') % (release.isEmpty() ? QStringLiteral("1") : release); } else { return epoch % QChar(':') % version % QChar('-') % (release.isEmpty() ? QStringLiteral("1") : release); } } /*! * \brief Compares two version parts. */ 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 >= 0 ? part1End - part1Pos : -1); auto segment2 = part2.midRef(part2Pos, part2End >= 0 ? part2End - part2Pos : -1); // 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 { // check whether epoch differs if(!epoch.isEmpty() || !other.epoch.isEmpty()) { switch(compareParts(other.epoch, epoch)) { case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade; case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThanSyncVersion; case PackageVersionPartComparsion::Equal: ; } } // check whether upstream version differs switch(compareParts(other.version, version)) { case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade; case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThanSyncVersion; case PackageVersionPartComparsion::Equal: ; } // check whether package version differs if(release.isEmpty() && !other.release.isEmpty()) { // only consider package release if both versions specify it (otherwise consider packages equal) switch(compareParts(other.release, release)) { case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::PackageUpgradeOnly; case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThanSyncVersion; case PackageVersionPartComparsion::Equal: ; } } // no difference -> equal return PackageVersionComparsion::Equal; } /*! * \brief Constructs a dependency from the specified string. * \remarks \a dependency might have version suffix. */ Dependency::Dependency(const QString &dependency) { int descrBeg = dependency.lastIndexOf(QChar(':')); QStringRef actualDependency; if(descrBeg > 0) { description = dependency.midRef(descrBeg + 1).trimmed().toString(); actualDependency = dependency.midRef(0, descrBeg); } else { actualDependency = QStringRef(&dependency); } int suffixBeg; if((suffixBeg = actualDependency.lastIndexOf(QLatin1String(">="))) > 0) { mode = DependencyMode::GreatherEqual; } else if((suffixBeg = actualDependency.lastIndexOf(QLatin1String("<="))) > 0) { mode = DependencyMode::LessEqual; } else if((suffixBeg = actualDependency.lastIndexOf(QChar('='))) > 0) { mode = DependencyMode::Equal; } else if((suffixBeg = actualDependency.lastIndexOf(QChar('<'))) > 0) { mode = DependencyMode::LessThen; } else if((suffixBeg = actualDependency.lastIndexOf(QChar('>'))) > 0) { mode = DependencyMode::GreatherThen; } else { mode = DependencyMode::Any; } switch(mode) { case DependencyMode::Any: name = actualDependency.toString(); break; case DependencyMode::GreatherEqual: case DependencyMode::LessEqual: name = actualDependency.mid(0, suffixBeg).toString(); version = actualDependency.mid(suffixBeg + 2).toString(); break; case DependencyMode::Equal: case DependencyMode::LessThen: case DependencyMode::GreatherThen: name = actualDependency.mid(0, suffixBeg).toString(); version = actualDependency.mid(suffixBeg + 1).toString(); break; default: ; } } QString Dependency::toString() const { switch(mode) { case DependencyMode::Equal: return name % QChar('=') % version; case DependencyMode::GreatherEqual: return name % QChar('>') % QChar('=') % version; case DependencyMode::LessEqual: return name % QChar('<') % QChar('=') % version; case DependencyMode::GreatherThen: return name % QChar('>') % version; case DependencyMode::LessThen: return name % QChar('<') % version; default: return name; } } /*! * \brief Returns a JSON object for the current instance. */ QJsonObject Dependency::toJson() const { QJsonObject obj; obj.insert(QStringLiteral("name"), name); obj.insert(QStringLiteral("ver"), version); switch(mode) { case DependencyMode::Any: obj.insert(QStringLiteral("mod"), QStringLiteral("any")); break; case DependencyMode::Equal: obj.insert(QStringLiteral("mod"), QStringLiteral("eq")); break; case DependencyMode::GreatherEqual: obj.insert(QStringLiteral("mod"), QStringLiteral("ge")); break; case DependencyMode::LessEqual: obj.insert(QStringLiteral("mod"), QStringLiteral("le")); break; case DependencyMode::GreatherThen: obj.insert(QStringLiteral("mod"), QStringLiteral("gt")); break; case DependencyMode::LessThen: obj.insert(QStringLiteral("mod"), QStringLiteral("lt")); break; default: ; } if(!description.isEmpty()) { obj.insert(QStringLiteral("desc"), description); } return obj; } }