/* * SPDX-FileCopyrightText: 2013 Lukas Appelhans * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "PackageKitUpdater.h" #include "PackageKitMessages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libdiscover_backend_packagekit_debug.h" #include "pk-offline-private.h" #include "utils.h" using namespace Qt::StringLiterals; int percentageWithStatus(PackageKit::Transaction::Status status, uint percentage) { const auto was = percentage; if (status != PackageKit::Transaction::StatusUnknown) { static const QMap statuses = { {PackageKit::Transaction::Status::StatusDownload, 0}, {PackageKit::Transaction::Status::StatusInstall, 1}, {PackageKit::Transaction::Status::StatusRemove, 1}, {PackageKit::Transaction::Status::StatusLoadingCache, 1}, {PackageKit::Transaction::Status::StatusUpdate, 1}, }; const auto idx = statuses.value(status, -1); if (idx < 0) { qCDebug(LIBDISCOVER_BACKEND_PACKAGEKIT_LOG) << "Status not present" << status << "among" << statuses.keys() << percentage; return -1; } percentage = (idx * 100 + percentage) / 2 /*the maximum in statuses*/; } qCDebug(LIBDISCOVER_BACKEND_PACKAGEKIT_LOG) << "reporting progress with status:" << status << percentage << was; return percentage; } static void kRemoveDuplicates(QJsonArray &input, std::function fetchKey) { QSet ret; for (auto it = input.begin(); it != input.end();) { const auto key = fetchKey(*it); if (!ret.contains(key)) { ret << key; ++it; } else { it = input.erase(it); } } } class SystemUpgrade : public AbstractResource { Q_OBJECT public: SystemUpgrade(PackageKitBackend *backend) : AbstractResource(backend) , m_backend(backend) , m_updateSizeTimer(new QTimer(this)) { connect(m_backend, &AbstractResourcesBackend::resourceRemoved, this, [this](AbstractResource *resource) { m_resources.remove(resource); }); m_updateSizeTimer->setInterval(100); m_updateSizeTimer->setSingleShot(true); connect(m_updateSizeTimer, &QTimer::timeout, this, &SystemUpgrade::refreshResource); } QString packageName() const override { return QStringLiteral("discover-offline-upgrade"); } QString name() const override { if (isDistroUpgrade()) { return i18nc("distro upgrade: name version", "%1 %2", AppStreamIntegration::global()->osRelease()->name(), m_distroUpgrade->version()); } return i18n("System upgrade"); } QString comment() override { return upgradeText(); } QVariant icon() const override { return QStringLiteral("system-upgrade"); } bool canExecute() const override { return false; } void invokeApplication() const override { } State state() override { return Upgradeable; } bool hasCategory(const QString & /*category*/) const override { return {}; } AbstractResource::Type type() const override { return System; } bool isRemovable() const override { return false; } QVector withoutDuplicates() const { QVector ret; QSet donePkgs; for (auto resource : std::as_const(m_resources)) { const auto pkResource = qobject_cast(resource); const auto pkgName = pkResource->packageName(); if (!donePkgs.contains(pkgName)) { donePkgs.insert(pkgName); ret += pkResource; } } return ret; } quint64 size() override { quint64 ret = 0; if (isDistroUpgrade()) { return ret; } const auto resources = withoutDuplicates(); for (auto pkResource : resources) { ret += pkResource->size(); } return ret; } QJsonArray licenses() override { QJsonArray ret; for (auto res : std::as_const(m_resources)) { ret += res->licenses(); } kRemoveDuplicates(ret, [](const QJsonValueRef &val) -> QString { return val.toObject()[QLatin1String("name")].toString(); }); return ret; } QString section() override { return {}; } QString origin() const override { return {}; } QString author() const override { return {}; } QList addonsInformation() override { return {}; } QString upgradeText() const override { return i18np("1 package will be upgraded", "%1 packages will be upgraded", withoutDuplicates().count()); } QString longDescription() override { QStringList changes; const auto resources = withoutDuplicates(); for (auto resource : resources) { const auto changelog = resource->changelog(); if (changelog.isEmpty()) { changes += i18n("

%1

Upgrade to new version %2
No release notes provided", resource->packageName(), resource->availableVersion()); } else { changes += i18n("

%1

Upgrade to new version %2
Release notes:
%3
", resource->packageName(), resource->availableVersion(), changelog); } } changes.sort(); if (isDistroUpgrade()) { changes.prepend(m_distroUpgrade->description()); } return changes.join(QString()); } void fetchChangelog() override { if (isDistroUpgrade()) { return; } for (auto resource : std::as_const(m_resources)) { resource->fetchUpdateDetails(); } Q_EMIT changelogFetched({}); } QString installedVersion() const override { return i18n("Present"); } QString availableVersion() const override { return i18n("Future"); } QString sourceIcon() const override { return QStringLiteral("package-x-generic"); } QDate releaseDate() const override { return {}; } QSet resources() const { return m_resources; } QSet allPackageNames() const { QSet ret; for (auto resource : std::as_const(m_resources)) { ret += kToSet(qobject_cast(resource)->allPackageNames()); } return ret; } void refreshResource() { Q_EMIT m_backend->resourcesChanged(this, {"size", "license"}); Q_EMIT updateSizeChanged(); } void setCandidates(const QSet &candidates) { const auto toDisconnect = (m_resources - candidates); for (auto resource : toDisconnect) { disconnect(resource, &AbstractResource::sizeChanged, this, &SystemUpgrade::startIfStopped); disconnect(resource, &AbstractResource::changelogFetched, this, &SystemUpgrade::startIfStopped); } const auto newCandidates = (candidates - m_resources); m_resources = candidates; for (auto resource : newCandidates) { connect(resource, &AbstractResource::sizeChanged, this, &SystemUpgrade::startIfStopped); connect(resource, &AbstractResource::changelogFetched, this, &SystemUpgrade::startIfStopped); } } void startIfStopped() { if (!m_updateSizeTimer->isActive()) { m_updateSizeTimer->start(); } } void setDistroUpgrade(const AppStream::Release &release) { m_distroUpgrade = release; } void clearDistroUpgrade() { m_distroUpgrade = std::nullopt; Q_EMIT m_backend->inlineMessageChanged({}); } bool isDistroUpgrade() const { return m_distroUpgrade.has_value(); } const AppStream::Release &getDistroUpgrade() { return m_distroUpgrade.value(); } Q_SIGNALS: void updateSizeChanged(); private: QSet m_resources; PackageKitBackend *const m_backend; QTimer *m_updateSizeTimer; std::optional m_distroUpgrade; }; PackageKitUpdater::PackageKitUpdater(PackageKitBackend *parent) : AbstractBackendUpdater(parent) , m_transaction(nullptr) , m_backend(parent) , m_isCancelable(false) , m_isProgressing(false) , m_percentage(0) , m_lastUpdate() , m_upgrade(new SystemUpgrade(m_backend)) { fetchLastUpdateTime(); } PackageKitUpdater::~PackageKitUpdater() { } void PackageKitUpdater::prepare() { auto offline = PackageKit::Daemon::global()->offline(); if (offline->updateTriggered() || offline->upgradeTriggered()) { m_toUpgrade.clear(); m_allUpgradeable.clear(); setNeedsReboot(true); return; } if (QFile::exists(QStringLiteral(PK_OFFLINE_RESULTS_FILENAME))) { qCDebug(LIBDISCOVER_BACKEND_PACKAGEKIT_LOG) << "Removed offline results file"; offline->clearResults(); } Q_ASSERT(!m_transaction); const auto candidates = m_backend->upgradeablePackages(); if (useOfflineUpdates() && !candidates.isEmpty()) { m_upgrade->setCandidates(candidates); m_toUpgrade = {m_upgrade}; connect(m_upgrade, &SystemUpgrade::updateSizeChanged, this, &PackageKitUpdater::checkFreeSpace); } else { m_toUpgrade = candidates; } checkFreeSpace(); m_allUpgradeable = m_toUpgrade; } void PackageKitUpdater::checkFreeSpace() { auto job = KIO::fileSystemFreeSpace(QUrl::fromLocalFile(QStringLiteral("/usr"))); connect(job, &KJob::result, this, [this, job]() { if (job->availableSize() < updateSize()) { setErrorMessage(i18nc("@info:status %1 is a formatted disk space string e.g. '240 MiB'", "Not enough space to perform the update; only %1 of space are available.", KFormat().formatByteSize(job->availableSize()))); } }); } void PackageKitUpdater::setupTransaction(PackageKit::Transaction::TransactionFlags flags) { m_packagesModified.clear(); if (m_toUpgrade.contains(m_upgrade) && m_upgrade->isDistroUpgrade()) { const QString &upgradeVersion = m_upgrade->getDistroUpgrade().version(); m_transaction = PackageKit::Daemon::upgradeSystem(upgradeVersion, PackageKit::Transaction::UpgradeKind::UpgradeKindComplete, flags); m_transaction->setHints(m_backend->globalHints() << QStringLiteral("cache-age=86400" /* 24*60*60 */)); } else { auto pkgs = involvedPackages(m_toUpgrade).values(); pkgs.sort(); m_transaction = PackageKit::Daemon::updatePackages(pkgs, flags); } m_isCancelable = m_transaction->allowCancel(); cancellableChanged(); connect(m_transaction.data(), &PackageKit::Transaction::finished, this, &PackageKitUpdater::finished); connect(m_transaction.data(), &PackageKit::Transaction::package, this, &PackageKitUpdater::packageResolved); connect(m_transaction.data(), &PackageKit::Transaction::errorCode, this, &PackageKitUpdater::errorFound); connect(m_transaction.data(), &PackageKit::Transaction::mediaChangeRequired, this, &PackageKitUpdater::mediaChange); connect(m_transaction.data(), &PackageKit::Transaction::eulaRequired, this, &PackageKitUpdater::eulaRequired); connect(m_transaction.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PackageKitUpdater::repoSignatureRequired); connect(m_transaction.data(), &PackageKit::Transaction::allowCancelChanged, this, &PackageKitUpdater::cancellableChanged); connect(m_transaction.data(), &PackageKit::Transaction::itemProgress, this, &PackageKitUpdater::itemProgress); connect(m_transaction.data(), &PackageKit::Transaction::speedChanged, this, [this] { Q_EMIT downloadSpeedChanged(downloadSpeed()); }); if (!(flags & PackageKit::Transaction::TransactionFlagSimulate)) { connect(m_transaction.data(), &PackageKit::Transaction::percentageChanged, this, &PackageKitUpdater::percentageChanged); if (m_toUpgrade.contains(m_upgrade)) { connect(m_transaction, &PackageKit::Transaction::percentageChanged, this, [this] { if (m_transaction->status() == PackageKit::Transaction::StatusDownload) { Q_EMIT resourceProgressed(m_upgrade, m_transaction->percentage(), Downloading); } }); } } } QSet PackageKitUpdater::packagesForPackageId(const QSet &pkgids) const { const auto packages = kTransform>(pkgids, [](const QString &pkgid) { return PackageKit::Daemon::packageName(pkgid); }); QSet ret; for (auto resource : std::as_const(m_allUpgradeable)) { QSet allPackageNames; if (auto upgrade = qobject_cast(resource)) { allPackageNames = upgrade->allPackageNames(); } else if (auto pkResource = qobject_cast(resource)) { allPackageNames = kToSet(pkResource->allPackageNames()); } if (!allPackageNames.isEmpty() && packages.contains(allPackageNames)) { ret.insert(resource); } } return ret; } QSet PackageKitUpdater::involvedPackages(const QSet &packages) const { QSet packageIds; packageIds.reserve(packages.size()); for (auto resource : packages) { if (auto upgrade = qobject_cast(resource)) { packageIds = involvedPackages(upgrade->resources()); continue; } auto pkResource = qobject_cast(resource); const QSet ids = m_backend->upgradeablePackageId(pkResource); if (ids.isEmpty()) { qWarning() << "no upgradeablePackageId for" << pkResource; continue; } packageIds.unite(ids); } return packageIds; } void PackageKitUpdater::processProceedFunction() { auto t = m_proceedFunctions.takeFirst()(); connect(t, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status) { if (status != PackageKit::Transaction::Exit::ExitSuccess) { qWarning() << "transaction failed" << sender() << status; cancel(); return; } if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else { start(); } }); } void PackageKitUpdater::proceed() { if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else if (useOfflineUpdates()) { setupTransaction(PackageKit::Transaction::TransactionFlagOnlyTrusted | PackageKit::Transaction::TransactionFlagOnlyDownload); } else { setupTransaction(PackageKit::Transaction::TransactionFlagOnlyTrusted); } } bool PackageKitUpdater::useOfflineUpdates() const { return m_useOfflineUpdates || m_upgrade->isDistroUpgrade() || qEnvironmentVariableIntValue("PK_OFFLINE_UPDATE"); } void PackageKitUpdater::setOfflineUpdates(bool use) { m_useOfflineUpdates = use; } void PackageKitUpdater::setDistroUpgrade(const AppStream::Release &release) { m_upgrade->setDistroUpgrade(release); } void PackageKitUpdater::clearDistroUpgrade() { m_upgrade->clearDistroUpgrade(); } bool PackageKitUpdater::isDistroUpgrade() const { return m_upgrade->isDistroUpgrade(); } void PackageKitUpdater::start() { Q_ASSERT(!isProgressing()); setupTransaction(PackageKit::Transaction::TransactionFlagSimulate); setProgressing(true); if (useOfflineUpdates()) { setNeedsReboot(true); } } void PackageKitUpdater::finished(PackageKit::Transaction::Exit exit, uint /*time*/) { // qCDebug(LIBDISCOVER_BACKEND_PACKAGEKIT_LOG) << "update finished!" << exit << time; if (!m_proceedFunctions.isEmpty()) { return; } const bool cancel = exit == PackageKit::Transaction::ExitCancelled; const bool simulate = m_transaction->transactionFlags() & PackageKit::Transaction::TransactionFlagSimulate; disconnect(m_transaction, nullptr, this, nullptr); m_transaction = nullptr; if (!cancel && simulate) { auto toremoveOrig = m_packagesModified.value(PackageKit::Transaction::InfoRemoving); #ifdef QPK_CHECK_VERSION #if QPK_CHECK_VERSION(1, 1, 2) toremoveOrig += m_packagesModified.value(PackageKit::Transaction::InfoRemove); #endif #endif auto toremove = toremoveOrig; auto toinstall = QStringList() << m_packagesModified.value(PackageKit::Transaction::InfoInstalling) << m_packagesModified.value(PackageKit::Transaction::InfoUpdating); #ifdef QPK_CHECK_VERSION #if QPK_CHECK_VERSION(1, 1, 2) toinstall << m_packagesModified.value(PackageKit::Transaction::InfoInstall); #endif #endif // some backends will treat upgrades as removal + install, which makes for terrible error messages. for (auto it = toremove.begin(), itEnd = toremove.end(); it != itEnd;) { const QString name = PackageKit::Transaction::packageName(*it); auto itInstall = std::find_if(toinstall.begin(), toinstall.end(), [&](const QString &pkgid) { return name == PackageKit::Transaction::packageName(pkgid); }); if (itInstall != toinstall.end()) { toinstall.erase(itInstall); it = toremove.erase(it); } else { ++it; } }; if (PackageKit::Daemon::backendName() == QLatin1String("dnf")) { // Fedora has some packages that it uninstalls then eventually creates on its own. No need to // notify about these. toremove = kFilter(toremove, [](const QString &pkgid) { return !PackageKit::Transaction::packageName(pkgid).startsWith(QLatin1String("kmod")); }); } if (!toremove.isEmpty()) { QStringList criticals; for (const auto &pkgid : std::as_const(toremove)) { auto resources = kFilter>(m_backend->resourcesByPackageName(pkgid), [](AbstractResource *resource) { return static_cast(resource)->isCritical(); }); criticals << kTransform(resources, [](AbstractResource *resource) { return resource->name(); }); if (!criticals.isEmpty()) { break; } } if (!criticals.isEmpty()) { const QString msg = i18n( "This update cannot be completed as it would remove the following software which is critical to the system's operation:" "
  • %1
" "If you believe this is an error, please report it as a bug to the packagers of your distribution.", criticals.constFirst()); Q_EMIT distroErrorMessage(msg); } else { Q_EMIT proceedRequest( i18n("Confirm Changes"), i18n("The following packages will be removed by the update:
  • %1

in order to install:
  • %2
", PackageKitResource::joinPackages(toremove, QStringLiteral("
  • "), {}), PackageKitResource::joinPackages(toinstall, QStringLiteral("
  • "), {}))); } } else { proceed(); } return; } if (!m_toUpgrade.contains(m_upgrade) || !m_upgrade->isDistroUpgrade()) { // If the distro-upgrade was prepared successfully, fetching updates now would invalidate it. // If the distro-upgrade failed, fetching updates now would find nothing and we would propose to reboot. setProgressing(false); m_backend->fetchUpdates(); fetchLastUpdateTime(); } else if (exit != PackageKit::Transaction::ExitSuccess) { Q_EMIT resourceProgressed(m_upgrade, 0, None); setNeedsReboot(false); setProgressing(false); } if (useOfflineUpdates() && exit == PackageKit::Transaction::ExitSuccess) { if (m_upgrade->isDistroUpgrade()) { QDBusPendingReply reply = PackageKit::Daemon::global()->offline()->triggerUpgrade(PackageKit::Offline::ActionReboot); // Call may fail because of authorization reply.waitForFinished(); if (reply.isError()) { Q_EMIT resourceProgressed(m_upgrade, 0, None); setNeedsReboot(false); setProgressing(false); Q_EMIT passiveMessage(reply.error().message()); return; } m_backend->clear(); setProgressing(false); } else { PackageKit::Daemon::global()->offline()->trigger(m_offlineUpdateAction); } enableReadyToReboot(); } } void PackageKitUpdater::cancellableChanged() { if (m_isCancelable != m_transaction->allowCancel()) { m_isCancelable = m_transaction->allowCancel(); Q_EMIT cancelableChanged(m_isCancelable); } } void PackageKitUpdater::percentageChanged() { const int percentage = m_transaction->percentage(); if (percentage > 100) { return; } const auto actualPercentage = useOfflineUpdates() ? percentage : percentageWithStatus(m_transaction->status(), percentage); if (actualPercentage >= 0 && m_percentage != actualPercentage) { m_percentage = actualPercentage; Q_EMIT progressChanged(m_percentage); } } bool PackageKitUpdater::hasUpdates() const { return m_backend->updatesCount() > 0; } qreal PackageKitUpdater::progress() const { return m_percentage; } void PackageKitUpdater::removeResources(const QList &resources) { const QSet pkgs = involvedPackages(kToSet(resources)); m_toUpgrade.subtract(packagesForPackageId(pkgs)); } void PackageKitUpdater::addResources(const QList &resources) { const QSet pkgs = involvedPackages(kToSet(resources)); m_toUpgrade.unite(packagesForPackageId(pkgs)); } QList PackageKitUpdater::toUpdate() const { return m_toUpgrade.values(); } bool PackageKitUpdater::isMarked(AbstractResource *resource) const { return m_toUpgrade.contains(resource); } QDateTime PackageKitUpdater::lastUpdate() const { return m_lastUpdate; } bool PackageKitUpdater::isCancelable() const { return m_isCancelable; } bool PackageKitUpdater::isProgressing() const { return m_isProgressing; } void PackageKitUpdater::cancel() { if (m_transaction) { m_transaction->cancel(); } else { setProgressing(false); } } void PackageKitUpdater::errorFound(PackageKit::Transaction::Error err, const QString &error) { if (err == PackageKit::Transaction::ErrorNoLicenseAgreement || err == PackageKit::Transaction::ErrorTransactionCancelled || err == PackageKit::Transaction::ErrorNotAuthorized) { return; } QString finalMessage = xi18nc("@info", "%1:%2", PackageKitMessages::errorMessage(err, QString()), error); Q_EMIT passiveMessage(finalMessage); qWarning() << "Error happened" << err << error; } void PackageKitUpdater::mediaChange(PackageKit::Transaction::MediaType media, const QString &type, const QString &text) { Q_UNUSED(media) Q_EMIT passiveMessage(i18n("Media Change of type '%1' is requested.\n%2", type, text)); } EulaHandling handleEula(const QString &eulaID, const QString &licenseAgreement) { KConfigGroup group(KSharedConfig::openConfig(), u"EULA"_s); auto licenseGroup = group.group(eulaID); QCryptographicHash hash(QCryptographicHash::Sha256); hash.addData(licenseAgreement.toUtf8()); QByteArray hashHex = hash.result().toHex(); EulaHandling ret; ret.request = licenseGroup.readEntry("Hash", QByteArray()) != hashHex; if (!ret.request) { ret.proceedFunction = [eulaID] { return PackageKit::Daemon::acceptEula(eulaID); }; } else { ret.proceedFunction = [eulaID, hashHex] { KConfigGroup group(KSharedConfig::openConfig(), u"EULA"_s); KConfigGroup licenseGroup = group.group(eulaID); licenseGroup.writeEntry("Hash", hashHex); return PackageKit::Daemon::acceptEula(eulaID); }; } return ret; } void PackageKitUpdater::eulaRequired(const QString &eulaID, const QString &packageID, const QString &vendor, const QString &licenseAgreement) { const auto handle = handleEula(eulaID, licenseAgreement); m_proceedFunctions << handle.proceedFunction; if (handle.request) { Q_EMIT proceedRequest(i18n("Accept EULA"), i18n("The package %1 and its vendor %2 require that you accept their license:\n %3", PackageKit::Daemon::packageName(packageID), vendor, licenseAgreement)); } else { proceed(); } } void PackageKitUpdater::setProgressing(bool progressing) { if (m_isProgressing != progressing) { m_isProgressing = progressing; Q_EMIT progressingChanged(m_isProgressing); } } void PackageKitUpdater::fetchLastUpdateTime() { QDBusPendingReply transaction = PackageKit::Daemon::global()->getTimeSinceAction(PackageKit::Transaction::RoleGetUpdates); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(transaction, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &PackageKitUpdater::lastUpdateTimeReceived); } void PackageKitUpdater::lastUpdateTimeReceived(QDBusPendingCallWatcher *w) { QDBusPendingReply reply = w->reply(); if (reply.isError()) { qWarning() << "Error when fetching the last update time" << reply.error(); } else { m_lastUpdate = QDateTime::currentDateTime().addSecs(-int(reply.value())); } w->deleteLater(); } AbstractBackendUpdater::State toUpdateState(PackageKit::Transaction::Status t) { switch (t) { case PackageKit::Transaction::StatusUnknown: case PackageKit::Transaction::StatusDownload: return AbstractBackendUpdater::Downloading; case PackageKit::Transaction::StatusDepResolve: case PackageKit::Transaction::StatusSigCheck: case PackageKit::Transaction::StatusTestCommit: case PackageKit::Transaction::StatusInstall: case PackageKit::Transaction::StatusCommit: return AbstractBackendUpdater::Installing; case PackageKit::Transaction::StatusFinished: case PackageKit::Transaction::StatusCancel: return AbstractBackendUpdater::Done; default: qCDebug(LIBDISCOVER_BACKEND_PACKAGEKIT_LOG) << "unknown packagekit status" << t; return AbstractBackendUpdater::None; } Q_UNREACHABLE(); } void PackageKitUpdater::itemProgress(const QString &itemID, PackageKit::Transaction::Status status, uint percentage) { const auto resources = packagesForPackageId({itemID}); for (auto resource : resources) { Q_EMIT resourceProgressed(resource, percentage, toUpdateState(status)); } } void PackageKitUpdater::fetchChangelog() const { QStringList pkgids; for (auto resource : std::as_const(m_allUpgradeable)) { if (auto upgrade = qobject_cast(resource)) { upgrade->fetchChangelog(); } else { pkgids += static_cast(resource)->availablePackageId(); } } Q_ASSERT(!pkgids.isEmpty()); PackageKit::Transaction *transaction = PackageKit::Daemon::getUpdatesDetails(pkgids); connect(transaction, &PackageKit::Transaction::updateDetail, this, &PackageKitUpdater::updateDetail); connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitUpdater::errorFound); } void PackageKitUpdater::updateDetail(const QString &packageID, const QStringList &updates, const QStringList &obsoletes, const QStringList &vendorUrls, const QStringList &bugzillaUrls, const QStringList &cveUrls, PackageKit::Transaction::Restart restart, const QString &updateText, const QString &changelog, PackageKit::Transaction::UpdateState state, const QDateTime &issued, const QDateTime &updated) { const auto resources = packagesForPackageId({packageID}); for (auto resource : resources) { auto pkResource = static_cast(resource); pkResource->updateDetail(packageID, updates, obsoletes, vendorUrls, bugzillaUrls, cveUrls, restart, updateText, changelog, state, issued, updated); } } void PackageKitUpdater::packageResolved(PackageKit::Transaction::Info info, const QString &packageId) { m_packagesModified[info] << packageId; } void PackageKitUpdater::repoSignatureRequired(const QString &packageID, const QString &repoName, const QString &keyUrl, const QString &keyUserid, const QString &keyId, const QString &keyFingerprint, const QString &keyTimestamp, PackageKit::Transaction::SigType type) { Q_EMIT proceedRequest(i18n("Missing signature for %1 in %2", packageID, repoName), i18n("Do you trust the following key?\n\nUrl: %1\nUser: %2\nKey: %3\nFingerprint: %4\nTimestamp: %4\n", keyUrl, keyUserid, keyFingerprint, keyTimestamp)); m_proceedFunctions << [type, keyId, packageID]() { return PackageKit::Daemon::installSignature(type, keyId, packageID); }; } double PackageKitUpdater::updateSize() const { double ret = 0.; QSet donePkgs; for (auto resource : std::as_const(m_toUpgrade)) { if (auto upgrade = qobject_cast(resource)) { ret += upgrade->size(); } else if (auto pkResource = qobject_cast(resource)) { QString pkgname = pkResource->packageName(); if (!donePkgs.contains(pkgname)) { donePkgs.insert(pkgname); ret += pkResource->size(); } } } return ret; } quint64 PackageKitUpdater::downloadSpeed() const { return m_transaction ? m_transaction->speed() : 0; } void PackageKitUpdater::setOfflineUpdateAction(PackageKit::Offline::Action action) { m_offlineUpdateAction = action; if (PackageKit::Daemon::global()->offline()->updateTriggered()) { PackageKit::Daemon::global()->offline()->trigger(action); } } #include "PackageKitUpdater.moc" #include "moc_PackageKitUpdater.cpp"