diff --git a/libdiscover/backends/PackageKitBackend/PKTransaction.cpp b/libdiscover/backends/PackageKitBackend/PKTransaction.cpp index 063b5253..fb8f7e53 100644 --- a/libdiscover/backends/PackageKitBackend/PKTransaction.cpp +++ b/libdiscover/backends/PackageKitBackend/PKTransaction.cpp @@ -1,309 +1,309 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PKTransaction.h" #include "PackageKitBackend.h" #include "PackageKitResource.h" #include "PackageKitMessages.h" #include "utils.h" #include "LocalFilePKResource.h" #include "libdiscover_backend_debug.h" #include #include #include #include #include #include #include PKTransaction::PKTransaction(const QVector& apps, Transaction::Role role) : Transaction(apps.first(), apps.first(), role) , m_apps(apps) { Q_ASSERT(!apps.contains(nullptr)); foreach(auto r, apps) { PackageKitResource* res = qobject_cast(r); - m_pkgnames.unite(res->allPackageNames().toSet()); + m_pkgnames.unite(kToSet(res->allPackageNames())); } QTimer::singleShot(0, this, &PKTransaction::start); } static QStringList packageIds(const QVector& res, std::function func) { QStringList ret; foreach(auto r, res) { ret += func(qobject_cast(r)); } ret.removeDuplicates(); return ret; } void PKTransaction::start() { trigger(PackageKit::Transaction::TransactionFlagSimulate); } void PKTransaction::trigger(PackageKit::Transaction::TransactionFlags flags) { if (m_trans) m_trans->deleteLater(); m_newPackageStates.clear(); if (m_apps.size() == 1 && qobject_cast(m_apps.at(0))) { auto app = qobject_cast(m_apps.at(0)); m_trans = PackageKit::Daemon::installFile(QUrl(app->packageName()).toLocalFile(), flags); connect(m_trans.data(), &PackageKit::Transaction::finished, this, [this, app](PackageKit::Transaction::Exit status) { const bool simulate = m_trans->transactionFlags() & PackageKit::Transaction::TransactionFlagSimulate; if (!simulate && status == PackageKit::Transaction::ExitSuccess) { app->markInstalled(); } }); } else switch (role()) { case Transaction::ChangeAddonsRole: case Transaction::InstallRole: { const QStringList ids = packageIds(m_apps, [](PackageKitResource* r){return r->availablePackageId(); }); if (ids.isEmpty()) { //FIXME this state shouldn't exist qWarning() << "Installing no packages found!"; for(auto app : m_apps) { qCDebug(LIBDISCOVER_BACKEND_LOG) << "app" << app << app->state() << static_cast(app)->packages(); } setStatus(Transaction::DoneWithErrorStatus); return; } m_trans = PackageKit::Daemon::installPackages(ids, flags); } break; case Transaction::RemoveRole: //see bug #315063 m_trans = PackageKit::Daemon::removePackages(packageIds(m_apps, [](PackageKitResource* r){return r->installedPackageId(); }), true /*allowDeps*/, false, flags); break; }; Q_ASSERT(m_trans); // connect(m_trans.data(), &PackageKit::Transaction::statusChanged, this, [this]() { qCDebug(LIBDISCOVER_BACKEND_LOG) << "state..." << m_trans->status(); }); connect(m_trans.data(), &PackageKit::Transaction::package, this, &PKTransaction::packageResolved); connect(m_trans.data(), &PackageKit::Transaction::finished, this, &PKTransaction::cleanup); connect(m_trans.data(), &PackageKit::Transaction::errorCode, this, &PKTransaction::errorFound); connect(m_trans.data(), &PackageKit::Transaction::mediaChangeRequired, this, &PKTransaction::mediaChange); connect(m_trans.data(), &PackageKit::Transaction::requireRestart, this, &PKTransaction::requireRestart); connect(m_trans.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PKTransaction::repoSignatureRequired); connect(m_trans.data(), &PackageKit::Transaction::percentageChanged, this, &PKTransaction::progressChanged); connect(m_trans.data(), &PackageKit::Transaction::statusChanged, this, &PKTransaction::statusChanged); connect(m_trans.data(), &PackageKit::Transaction::eulaRequired, this, &PKTransaction::eulaRequired); connect(m_trans.data(), &PackageKit::Transaction::allowCancelChanged, this, &PKTransaction::cancellableChanged); connect(m_trans.data(), &PackageKit::Transaction::speedChanged, this, [this]() { setDownloadSpeed(m_trans->speed()); }); setCancellable(m_trans->allowCancel()); } void PKTransaction::statusChanged() { setStatus(m_trans->status() == PackageKit::Transaction::StatusDownload ? Transaction::DownloadingStatus : Transaction::CommittingStatus); progressChanged(); } int percentageWithStatus(PackageKit::Transaction::Status status, uint percentage); void PKTransaction::progressChanged() { auto percent = m_trans->percentage(); if (percent == 101) { qWarning() << "percentage cannot be calculated"; percent = 50; } const auto processedPercentage = percentageWithStatus(m_trans->status(), qBound(0, percent, 100)); if (processedPercentage >= 0) setProgress(processedPercentage); } void PKTransaction::cancellableChanged() { setCancellable(m_trans->allowCancel()); } void PKTransaction::cancel() { if (!m_trans) { setStatus(CancelledStatus); } else if (m_trans->allowCancel()) { m_trans->cancel(); } else { qWarning() << "trying to cancel a non-cancellable transaction: " << resource()->name(); } } void PKTransaction::cleanup(PackageKit::Transaction::Exit exit, uint runtime) { Q_UNUSED(runtime) const bool cancel = !m_proceedFunctions.isEmpty() || exit == PackageKit::Transaction::ExitCancelled; const bool failed = exit == PackageKit::Transaction::ExitFailed || exit == PackageKit::Transaction::ExitUnknown; const bool simulate = m_trans->transactionFlags() & PackageKit::Transaction::TransactionFlagSimulate; disconnect(m_trans, nullptr, this, nullptr); m_trans = nullptr; const auto backend = qobject_cast(resource()->backend()); if (!cancel && !failed && simulate) { auto packagesToRemove = m_newPackageStates.value(PackageKit::Transaction::InfoRemoving); QMutableListIterator i(packagesToRemove); QSet removedResources; while (i.hasNext()) { const auto pkgname = PackageKit::Daemon::packageName(i.next()); removedResources.unite(backend->resourcesByPackageName(pkgname)); if (m_pkgnames.contains(pkgname)) { i.remove(); } } - removedResources.subtract(kVectorToSet(m_apps)); + removedResources.subtract(kToSet(m_apps)); if (!packagesToRemove.isEmpty() || !removedResources.isEmpty()) { QString msg = QLatin1String("
  • ") + PackageKitResource::joinPackages(packagesToRemove, QLatin1String("
  • "), {}); if (!removedResources.isEmpty()) { const QStringList removedResourcesStr = kTransform(removedResources, [](AbstractResource* a) { return a->name(); }); msg += QLatin1Char('\n'); msg += removedResourcesStr.join(QLatin1String("
  • ")); } msg += QStringLiteral("
"); Q_EMIT proceedRequest(i18n("Confirm package removal"), i18np("This action will also remove the following package:\n%2", "This action will also remove the following packages:\n%2", packagesToRemove.count(), msg)); } else { proceed(); } return; } this->submitResolve(); if (failed) setStatus(Transaction::DoneWithErrorStatus); else if (cancel) setStatus(Transaction::CancelledStatus); else setStatus(Transaction::DoneStatus); } void PKTransaction::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 PKTransaction::proceed() { if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else { trigger(PackageKit::Transaction::TransactionFlagOnlyTrusted); } } void PKTransaction::packageResolved(PackageKit::Transaction::Info info, const QString& packageId) { m_newPackageStates[info].append(packageId); } void PKTransaction::submitResolve() { const auto backend = qobject_cast(resource()->backend()); QStringList needResolving; for(auto it = m_newPackageStates.constBegin(), itEnd = m_newPackageStates.constEnd(); it != itEnd; ++it) { auto state = it.key(); if (state == PackageKit::Transaction::InfoInstalling) state = PackageKit::Transaction::InfoInstalled; if (state == PackageKit::Transaction::InfoRemoving) state = PackageKit::Transaction::InfoAvailable; if (state != PackageKit::Transaction::InfoInstalled && state != PackageKit::Transaction::InfoAvailable) continue; foreach(const auto &pkgid, it.value()) { const auto resources = backend->resourcesByPackageName(PackageKit::Daemon::packageName(pkgid)); for(auto res: resources) { auto r = qobject_cast(res); r->clearPackageIds(); r->addPackageId(state, pkgid, true); } } } } PackageKit::Transaction* PKTransaction::transaction() { return m_trans; } void PKTransaction::eulaRequired(const QString& eulaID, const QString& packageID, const QString& vendor, const QString& licenseAgreement) { m_proceedFunctions << [eulaID](){ return PackageKit::Daemon::acceptEula(eulaID); }; 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)); } void PKTransaction::errorFound(PackageKit::Transaction::Error err, const QString& error) { if (err == PackageKit::Transaction::ErrorNoLicenseAgreement || err == PackageKit::Transaction::ErrorTransactionCancelled) return; qWarning() << "PackageKit error:" << err << PackageKitMessages::errorMessage(err) << error; Q_EMIT passiveMessage(PackageKitMessages::errorMessage(err)); } void PKTransaction::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)); } void PKTransaction::requireRestart(PackageKit::Transaction::Restart restart, const QString& pkgid) { Q_EMIT passiveMessage(PackageKitMessages::restartMessage(restart, pkgid)); } void PKTransaction::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); }; } diff --git a/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp b/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp index e9607210..ccf52f59 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp @@ -1,744 +1,743 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitBackend.h" #include "PackageKitSourcesBackend.h" #include "PackageKitResource.h" #include "PackageKitUpdater.h" #include "AppPackageKitResource.h" #include "PKTransaction.h" #include "LocalFilePKResource.h" #include "TransactionSet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "config-paths.h" #include "libdiscover_backend_debug.h" DISCOVER_BACKEND_PLUGIN(PackageKitBackend) template static void setWhenAvailable(const QDBusPendingReply& pending, W func, QObject* parent) { QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(pending, parent); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, parent, [func](QDBusPendingCallWatcher* watcher) { watcher->deleteLater(); QDBusPendingReply reply = *watcher; func(reply.value()); }); } QString PackageKitBackend::locateService(const QString &filename) { return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("applications/")+filename); } PackageKitBackend::PackageKitBackend(QObject* parent) : AbstractResourcesBackend(parent) , m_appdata(new AppStream::Pool) , m_updater(new PackageKitUpdater(this)) , m_refresher(nullptr) , m_isFetching(0) , m_reviews(AppStreamIntegration::global()->reviews()) { QTimer* t = new QTimer(this); connect(t, &QTimer::timeout, this, &PackageKitBackend::checkForUpdates); t->setInterval(60 * 60 * 1000); t->setSingleShot(false); t->start(); m_delayedDetailsFetch.setSingleShot(true); m_delayedDetailsFetch.setInterval(100); connect(&m_delayedDetailsFetch, &QTimer::timeout, this, &PackageKitBackend::performDetailsFetch); connect(PackageKit::Daemon::global(), &PackageKit::Daemon::restartScheduled, m_updater, &PackageKitUpdater::enableNeedsReboot); connect(PackageKit::Daemon::global(), &PackageKit::Daemon::isRunningChanged, this, &PackageKitBackend::checkDaemonRunning); connect(m_reviews.data(), &OdrsReviewsBackend::ratingsReady, this, [this] { m_reviews->emitRatingFetched(this, kTransform>(m_packages.packages.values(), [] (AbstractResource* r) { return r; })); }); auto proxyWatch = new QFileSystemWatcher(this); proxyWatch->addPath(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/kioslaverc")); connect(proxyWatch, &QFileSystemWatcher::fileChanged, this, [this](){ KProtocolManager::reparseConfiguration(); updateProxy(); }); SourcesModel::global()->addSourcesBackend(new PackageKitSourcesBackend(this)); reloadPackageList(); acquireFetching(true); setWhenAvailable(PackageKit::Daemon::getTimeSinceAction(PackageKit::Transaction::RoleRefreshCache), [this](uint timeSince) { if (timeSince > 3600) checkForUpdates(); else fetchUpdates(); acquireFetching(false); }, this); } PackageKitBackend::~PackageKitBackend() { m_threadPool.waitForDone(200); m_threadPool.clear(); } void PackageKitBackend::updateProxy() { if (PackageKit::Daemon::isRunning()) { static bool everHad = KProtocolManager::useProxy(); if (!everHad && !KProtocolManager::useProxy()) return; everHad = KProtocolManager::useProxy(); PackageKit::Daemon::global()->setProxy(KProtocolManager::proxyFor(QStringLiteral("http")), KProtocolManager::proxyFor(QStringLiteral("https")), KProtocolManager::proxyFor(QStringLiteral("ftp")), KProtocolManager::proxyFor(QStringLiteral("socks")), {}, {}); } } bool PackageKitBackend::isFetching() const { return m_isFetching; } void PackageKitBackend::acquireFetching(bool f) { if (f) m_isFetching++; else m_isFetching--; if ((!f && m_isFetching==0) || (f && m_isFetching==1)) { emit fetchingChanged(); } Q_ASSERT(m_isFetching>=0); } struct DelayedAppStreamLoad { QVector components; QHash missingComponents; bool correct = true; }; static DelayedAppStreamLoad loadAppStream(AppStream::Pool* appdata) { DelayedAppStreamLoad ret; QString error; ret.correct = appdata->load(&error); if (!ret.correct) { qWarning() << "Could not open the AppStream metadata pool" << error; } const auto components = appdata->components(); ret.components.reserve(components.size()); foreach(const AppStream::Component& component, components) { if (component.kind() == AppStream::Component::KindFirmware) continue; const auto pkgNames = component.packageNames(); if (pkgNames.isEmpty()) { const auto entries = component.launchable(AppStream::Launchable::KindDesktopId).entries(); if (component.kind() == AppStream::Component::KindDesktopApp && !entries.isEmpty()) { const QString file = PackageKitBackend::locateService(entries.first()); if (!file.isEmpty()) { ret.missingComponents[file] = component; } } } else { ret.components << component; } } return ret; } void PackageKitBackend::reloadPackageList() { acquireFetching(true); if (m_refresher) { disconnect(m_refresher.data(), &PackageKit::Transaction::finished, this, &PackageKitBackend::reloadPackageList); } m_appdata.reset(new AppStream::Pool); auto fw = new QFutureWatcher(this); connect(fw, &QFutureWatcher::finished, this, [this, fw]() { const auto data = fw->result(); fw->deleteLater(); if (!data.correct && m_packages.packages.isEmpty()) { QTimer::singleShot(0, this, [this]() { Q_EMIT passiveMessage(i18n("Please make sure that Appstream is properly set up on your system")); }); } QStringList neededPackages; neededPackages.reserve(data.components.size()); for (const auto &component: data.components) { const auto pkgNames = component.packageNames(); addComponent(component, pkgNames); neededPackages << pkgNames; } for (auto it = data.missingComponents.constBegin(), itEnd = data.missingComponents.constEnd(); it != itEnd; ++it) { acquireFetching(true); const auto file = it.key(); const auto component = it.value(); auto trans = PackageKit::Daemon::searchFiles(file); connect(trans, &PackageKit::Transaction::package, this, [trans](PackageKit::Transaction::Info info, const QString &packageID){ if (info == PackageKit::Transaction::InfoInstalled) trans->setProperty("installedPackage", packageID); }); connect(trans, &PackageKit::Transaction::finished, this, [this, trans, component](PackageKit::Transaction::Exit status) { const auto pkgidVal = trans->property("installedPackage"); if (status == PackageKit::Transaction::ExitSuccess && !pkgidVal.isNull()) { const auto pkgid = pkgidVal.toString(); auto res = addComponent(component, {PackageKit::Daemon::packageName(pkgid)}); res->clearPackageIds(); res->addPackageId(PackageKit::Transaction::InfoInstalled, pkgid, true); } acquireFetching(false); }); } if (!neededPackages.isEmpty()) { neededPackages.removeDuplicates(); resolvePackages(neededPackages); } else { qCDebug(LIBDISCOVER_BACKEND_LOG) << "empty appstream db"; if (PackageKit::Daemon::backendName() == QLatin1String("aptcc") || PackageKit::Daemon::backendName().isEmpty()) { checkForUpdates(); } } acquireFetching(false); if (!m_appstreamInitialized) { m_appstreamInitialized = true; Q_EMIT loadedAppStream(); } }); fw->setFuture(QtConcurrent::run(&m_threadPool, &loadAppStream, m_appdata.get())); } AppPackageKitResource* PackageKitBackend::addComponent(const AppStream::Component& component, const QStringList& pkgNames) { Q_ASSERT(isFetching()); Q_ASSERT(!pkgNames.isEmpty()); AppPackageKitResource* res = qobject_cast(m_packages.packages[component.id()]); if (!res) { res = new AppPackageKitResource(component, pkgNames.at(0), this); m_packages.packages[component.id()] = res; } else { res->clearPackageIds(); } foreach (const QString& pkg, pkgNames) { m_packages.packageToApp[pkg] += component.id(); } foreach (const QString& pkg, component.extends()) { m_packages.extendedBy[pkg] += res; } return res; } void PackageKitBackend::resolvePackages(const QStringList &packageNames) { PackageKit::Transaction * tArch = PackageKit::Daemon::resolve(packageNames, PackageKit::Transaction::FilterArch); connect(tArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageArch); connect(tArch, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); PackageKit::Transaction * tNotArch = PackageKit::Daemon::resolve(packageNames, PackageKit::Transaction::FilterNotArch); connect(tNotArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageNotArch); connect(tNotArch, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); TransactionSet* merge = new TransactionSet({tArch, tNotArch}); connect(merge, &TransactionSet::allFinished, this, &PackageKitBackend::getPackagesFinished); } void PackageKitBackend::fetchUpdates() { if (m_updater->isProgressing()) return; m_getUpdatesTransaction = PackageKit::Daemon::getUpdates(); connect(m_getUpdatesTransaction, &PackageKit::Transaction::finished, this, &PackageKitBackend::getUpdatesFinished); connect(m_getUpdatesTransaction, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageToUpdate); connect(m_getUpdatesTransaction, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); connect(m_getUpdatesTransaction, &PackageKit::Transaction::percentageChanged, this, &PackageKitBackend::fetchingUpdatesProgressChanged); m_updatesPackageId.clear(); m_hasSecurityUpdates = false; m_updater->setProgressing(true); fetchingUpdatesProgressChanged(); } void PackageKitBackend::addPackageArch(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary) { addPackage(info, packageId, summary, true); } void PackageKitBackend::addPackageNotArch(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary) { addPackage(info, packageId, summary, false); } void PackageKitBackend::addPackage(PackageKit::Transaction::Info info, const QString &packageId, const QString &summary, bool arch) { if(PackageKit::Daemon::packageArch(packageId) == QLatin1String("source")) { // We do not add source packages, they make little sense here. If source is needed, // we are going to have to consider that in some other way, some other time // If we do not ignore them here, e.g. openSuse entirely fails at installing applications return; } const QString packageName = PackageKit::Daemon::packageName(packageId); QSet r = resourcesByPackageName(packageName); if (r.isEmpty()) { auto pk = new PackageKitResource(packageName, summary, this); r = { pk }; m_packagesToAdd.insert(pk); } foreach(auto res, r) static_cast(res)->addPackageId(info, packageId, arch); } void PackageKitBackend::getPackagesFinished() { for(auto it = m_packages.packages.cbegin(); it != m_packages.packages.cend(); ++it) { auto pkr = qobject_cast(it.value()); if (pkr->packages().isEmpty()) { // qWarning() << "Failed to find package for" << it.key(); m_packagesToDelete += pkr; } } includePackagesToAdd(); } void PackageKitBackend::includePackagesToAdd() { if (m_packagesToAdd.isEmpty() && m_packagesToDelete.isEmpty()) return; acquireFetching(true); foreach(PackageKitResource* res, m_packagesToAdd) { m_packages.packages[res->packageName()] = res; } foreach(PackageKitResource* res, m_packagesToDelete) { const auto pkgs = m_packages.packageToApp.value(res->packageName(), {res->packageName()}); foreach(const auto &pkg, pkgs) { auto res = m_packages.packages.take(pkg); if (res) { if (AppPackageKitResource* ares = qobject_cast(res)) { const auto extends = res->extends(); for(const auto &ext: extends) m_packages.extendedBy[ext].removeAll(ares); } emit resourceRemoved(res); res->deleteLater(); } } } m_packagesToAdd.clear(); m_packagesToDelete.clear(); acquireFetching(false); } void PackageKitBackend::transactionError(PackageKit::Transaction::Error, const QString& message) { qWarning() << "Transaction error: " << message << sender(); Q_EMIT passiveMessage(message); } void PackageKitBackend::packageDetails(const PackageKit::Details& details) { const QSet resources = resourcesByPackageName(PackageKit::Daemon::packageName(details.packageId())); if (resources.isEmpty()) qWarning() << "couldn't find package for" << details.packageId(); foreach(AbstractResource* res, resources) { qobject_cast(res)->setDetails(details); } } QSet PackageKitBackend::resourcesByPackageName(const QString& name) const { return resourcesByPackageNames>({name}); } template T PackageKitBackend::resourcesByPackageNames(const QStringList &pkgnames) const { T ret; ret.reserve(pkgnames.size()); for(const QString &name : pkgnames) { const QStringList names = m_packages.packageToApp.value(name, QStringList(name)); foreach(const QString& name, names) { AbstractResource* res = m_packages.packages.value(name); if (res) ret += res; } } return ret; } void PackageKitBackend::checkForUpdates() { if (PackageKit::Daemon::global()->offline()->updateTriggered()) { qCDebug(LIBDISCOVER_BACKEND_LOG) << "Won't be checking for updates again, the system needs a reboot to apply the fetched offline updates."; return; } if (!m_refresher) { acquireFetching(true); m_refresher = PackageKit::Daemon::refreshCache(false); connect(m_refresher.data(), &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); connect(m_refresher.data(), &PackageKit::Transaction::finished, this, [this]() { m_refresher = nullptr; fetchUpdates(); acquireFetching(false); }); } else { qWarning() << "already resetting"; } } QList PackageKitBackend::componentsById(const QString& id) const { Q_ASSERT(m_appstreamInitialized); return m_appdata->componentsById(id); } ResultsStream* PackageKitBackend::search(const AbstractResourcesBackend::Filters& filter) { if (!filter.resourceUrl.isEmpty()) { return findResourceByPackageName(filter.resourceUrl); } else if (!filter.extends.isEmpty()) { const auto ext = kTransform>(m_packages.extendedBy.value(filter.extends), [](AppPackageKitResource* a){ return a; }); return new ResultsStream(QStringLiteral("PackageKitStream-extends"), ext); } else if (filter.search.isEmpty()) { return new ResultsStream(QStringLiteral("PackageKitStream-all"), kFilter>(m_packages.packages, [](AbstractResource* res) { return res->type() != AbstractResource::Technical && !qobject_cast(res)->extendsItself(); })); } else { auto stream = new ResultsStream(QStringLiteral("PackageKitStream-search")); const auto f = [this, stream, filter] () { const QList components = m_appdata->search(filter.search); const QStringList ids = kTransform(components, [](const AppStream::Component& comp) { return comp.id(); }); if (!ids.isEmpty()) { const auto resources = kFilter>(resourcesByPackageNames>(ids), [](AbstractResource* res){ return !qobject_cast(res)->extendsItself(); }); Q_EMIT stream->resourcesFound(resources); } PackageKit::Transaction * tArch = PackageKit::Daemon::resolve(filter.search, PackageKit::Transaction::FilterArch); connect(tArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageArch); connect(tArch, &PackageKit::Transaction::package, stream, [stream](PackageKit::Transaction::Info /*info*/, const QString &packageId){ stream->setProperty("packageId", packageId); }); connect(tArch, &PackageKit::Transaction::finished, stream, [stream, ids, this](PackageKit::Transaction::Exit status) { getPackagesFinished(); if (status == PackageKit::Transaction::Exit::ExitSuccess) { const auto packageId = stream->property("packageId"); if (!packageId.isNull()) { const auto res = resourcesByPackageNames>({PackageKit::Daemon::packageName(packageId.toString())}); Q_EMIT stream->resourcesFound(kFilter>(res, [ids](AbstractResource* res){ return !ids.contains(res->appstreamId()); })); } } stream->finish(); }, Qt::QueuedConnection); }; if (!m_appstreamInitialized) { connect(this, &PackageKitBackend::loadedAppStream, stream, f); } else { QTimer::singleShot(0, this, f); } return stream; } } ResultsStream * PackageKitBackend::findResourceByPackageName(const QUrl& url) { if (url.isLocalFile()) { QMimeDatabase db; const auto mime = db.mimeTypeForUrl(url); if ( mime.inherits(QStringLiteral("application/vnd.debian.binary-package")) || mime.inherits(QStringLiteral("application/x-rpm")) || mime.inherits(QStringLiteral("application/x-tar")) || mime.inherits(QStringLiteral("application/x-xz-compressed-tar")) ) { return new ResultsStream(QStringLiteral("PackageKitStream-localpkg"), QVector{ new LocalFilePKResource(url, this)}); } } else if (url.scheme() == QLatin1String("appstream")) { static const QMap deprecatedAppstreamIds = { { QStringLiteral("org.kde.krita.desktop"), QStringLiteral("krita.desktop") }, { QStringLiteral("org.kde.digikam.desktop"), QStringLiteral("digikam.desktop") }, { QStringLiteral("org.kde.ktorrent.desktop"), QStringLiteral("ktorrent.desktop") }, { QStringLiteral("org.kde.gcompris.desktop"), QStringLiteral("gcompris.desktop") }, { QStringLiteral("org.kde.kmymoney.desktop"), QStringLiteral("kmymoney.desktop") }, { QStringLiteral("org.kde.kolourpaint.desktop"), QStringLiteral("kolourpaint.desktop") }, { QStringLiteral("org.blender.blender.desktop"), QStringLiteral("blender.desktop") }, }; const auto appstreamId = AppStreamUtils::appstreamId(url); if (appstreamId.isEmpty()) Q_EMIT passiveMessage(i18n("Malformed appstream url '%1'", url.toDisplayString())); else { auto stream = new ResultsStream(QStringLiteral("PackageKitStream-appstream-url")); const auto f = [this, appstreamId, stream] () { AbstractResource* pkg = nullptr; const auto deprecatedHost = deprecatedAppstreamIds.value(appstreamId); //try this as fallback for (auto it = m_packages.packages.constBegin(), itEnd = m_packages.packages.constEnd(); it != itEnd; ++it) { if (it.key().compare(appstreamId, Qt::CaseInsensitive) == 0 || it.key().compare(deprecatedHost, Qt::CaseInsensitive) == 0 || (appstreamId.endsWith(QLatin1String(".desktop")) && appstreamId.compare(it.key()+QLatin1String(".desktop"), Qt::CaseInsensitive) == 0)) { pkg = it.value(); break; } } if (pkg) Q_EMIT stream->resourcesFound({pkg}); stream->finish(); // if (!pkg) // qCDebug(LIBDISCOVER_BACKEND_LOG) << "could not find" << host << deprecatedHost; }; if (!m_appstreamInitialized) { connect(this, &PackageKitBackend::loadedAppStream, stream, f); } else { QTimer::singleShot(0, this, f); } return stream; } } return new ResultsStream(QStringLiteral("PackageKitStream-unknown-url"), {}); } bool PackageKitBackend::hasSecurityUpdates() const { return m_hasSecurityUpdates; } int PackageKitBackend::updatesCount() const { if (PackageKit::Daemon::global()->offline()->updateTriggered()) return 0; int ret = 0; QSet packages; const auto toUpgrade = upgradeablePackages(); for(auto res: toUpgrade) { const auto packageName = res->packageName(); if (packages.contains(packageName)) { continue; } packages.insert(packageName); ret += 1; } return ret; } Transaction* PackageKitBackend::installApplication(AbstractResource* app, const AddonList& addons) { Transaction* t = nullptr; if(!addons.addonsToInstall().isEmpty()) { QVector appsToInstall = resourcesByPackageNames>(addons.addonsToInstall()); if(!app->isInstalled()) appsToInstall << app; t = new PKTransaction(appsToInstall, Transaction::ChangeAddonsRole); } else if (!app->isInstalled()) t = installApplication(app); if (!addons.addonsToRemove().isEmpty()) { const auto appsToRemove = resourcesByPackageNames>(addons.addonsToRemove()); t = new PKTransaction(appsToRemove, Transaction::RemoveRole); } return t; } Transaction* PackageKitBackend::installApplication(AbstractResource* app) { return new PKTransaction({app}, Transaction::InstallRole); } Transaction* PackageKitBackend::removeApplication(AbstractResource* app) { Q_ASSERT(!isFetching()); return new PKTransaction({app}, Transaction::RemoveRole); } QSet PackageKitBackend::upgradeablePackages() const { if (isFetching() || !m_packagesToAdd.isEmpty()) { return {}; } QSet ret; ret.reserve(m_updatesPackageId.size()); Q_FOREACH (const QString& pkgid, m_updatesPackageId) { const QString pkgname = PackageKit::Daemon::packageName(pkgid); const auto pkgs = resourcesByPackageName(pkgname); if (pkgs.isEmpty()) { qWarning() << "couldn't find resource for" << pkgid; } ret.unite(pkgs); } return kFilter>(ret, [] (AbstractResource* res) { return !static_cast(res)->extendsItself(); }); } void PackageKitBackend::addPackageToUpdate(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary) { if (info == PackageKit::Transaction::InfoBlocked) { return; } if (info == PackageKit::Transaction::InfoRemoving || info == PackageKit::Transaction::InfoObsoleting) { // Don't try updating packages which need to be removed return; } if (info == PackageKit::Transaction::InfoSecurity) m_hasSecurityUpdates = true; m_updatesPackageId += packageId; addPackage(info, packageId, summary, true); } void PackageKitBackend::getUpdatesFinished(PackageKit::Transaction::Exit, uint) { if (!m_updatesPackageId.isEmpty()) { resolvePackages(kTransform(m_updatesPackageId, [](const QString &pkgid) { return PackageKit::Daemon::packageName(pkgid); })); fetchDetails(m_updatesPackageId); } m_updater->setProgressing(false); includePackagesToAdd(); emit updatesCountChanged(); } bool PackageKitBackend::isPackageNameUpgradeable(const PackageKitResource* res) const { return !upgradeablePackageId(res).isEmpty(); } QString PackageKitBackend::upgradeablePackageId(const PackageKitResource* res) const { QString name = res->packageName(); foreach (const QString& pkgid, m_updatesPackageId) { if (PackageKit::Daemon::packageName(pkgid) == name) return pkgid; } return QString(); } void PackageKitBackend::fetchDetails(const QSet& pkgid) { if (!m_delayedDetailsFetch.isActive()) { m_delayedDetailsFetch.start(); } m_packageNamesToFetchDetails += pkgid; } void PackageKitBackend::performDetailsFetch() { Q_ASSERT(!m_packageNamesToFetchDetails.isEmpty()); - const auto ids = m_packageNamesToFetchDetails.toList(); + const auto ids = m_packageNamesToFetchDetails.values(); PackageKit::Transaction* transaction = PackageKit::Daemon::getDetails(ids); connect(transaction, &PackageKit::Transaction::details, this, &PackageKitBackend::packageDetails); connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); m_packageNamesToFetchDetails.clear(); } void PackageKitBackend::checkDaemonRunning() { if (!PackageKit::Daemon::isRunning()) { qWarning() << "PackageKit stopped running!"; } else updateProxy(); } AbstractBackendUpdater* PackageKitBackend::backendUpdater() const { return m_updater; } QVector PackageKitBackend::extendedBy(const QString& id) const { return m_packages.extendedBy[id]; } AbstractReviewsBackend* PackageKitBackend::reviewsBackend() const { return m_reviews.data(); } QString PackageKitBackend::displayName() const { return AppStreamIntegration::global()->osRelease()->prettyName(); } int PackageKitBackend::fetchingUpdatesProgress() const { if (!m_getUpdatesTransaction) return 0; if (m_getUpdatesTransaction->status() == PackageKit::Transaction::StatusWait || m_getUpdatesTransaction->status() == PackageKit::Transaction::StatusUnknown) { return m_getUpdatesTransaction->property("lastPercentage").toInt(); } int percentage = percentageWithStatus(m_getUpdatesTransaction->status(), m_getUpdatesTransaction->percentage()); m_getUpdatesTransaction->setProperty("lastPercentage", percentage); return percentage; } - #include "PackageKitBackend.moc" diff --git a/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp b/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp index c0325d86..827f7118 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp @@ -1,453 +1,459 @@ /*************************************************************************** * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitUpdater.h" #include "PackageKitMessages.h" #include #include #include #include #include #include #include #include #include "libdiscover_backend_debug.h" +#include "utils.h" 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_LOG) << "Status not present" << status << "among" << statuses .keys() << percentage; return -1; } percentage = (idx * 100 + percentage) / 2 /*the maximum in statuses*/; } qCDebug(LIBDISCOVER_BACKEND_LOG) << "reporting progress with status:" << status << percentage << was; return percentage; } PackageKitUpdater::PackageKitUpdater(PackageKitBackend * parent) : AbstractBackendUpdater(parent), m_transaction(nullptr), m_backend(parent), m_isCancelable(false), m_isProgressing(false), m_percentage(0), m_lastUpdate() { fetchLastUpdateTime(); } PackageKitUpdater::~PackageKitUpdater() { } void PackageKitUpdater::prepare() { if (PackageKit::Daemon::global()->offline()->updateTriggered()) { m_toUpgrade.clear(); m_allUpgradeable.clear(); enableNeedsReboot(); return; } Q_ASSERT(!m_transaction); m_toUpgrade = m_backend->upgradeablePackages(); m_allUpgradeable = m_toUpgrade; } void PackageKitUpdater::setupTransaction(PackageKit::Transaction::TransactionFlags flags) { m_packagesModified.clear(); - auto pkgs = involvedPackages(m_toUpgrade).toList(); + 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::requireRestart, this, &PackageKitUpdater::requireRestart); 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::percentageChanged, this, &PackageKitUpdater::percentageChanged); connect(m_transaction.data(), &PackageKit::Transaction::itemProgress, this, &PackageKitUpdater::itemProgress); connect(m_transaction.data(), &PackageKit::Transaction::speedChanged, this, [this] { Q_EMIT downloadSpeedChanged(downloadSpeed()); }); } QSet PackageKitUpdater::packagesForPackageId(const QSet& pkgids) const { QSet packages; packages.reserve(pkgids.size()); foreach(const QString& pkgid, pkgids) { packages += PackageKit::Daemon::packageName(pkgid); } QSet ret; foreach (AbstractResource * res, m_allUpgradeable) { PackageKitResource* pres = qobject_cast(res); - if (packages.contains(pres->allPackageNames().toSet())) { + if (packages.contains(kToSet(pres->allPackageNames()))) { ret.insert(res); } } return ret; } QSet PackageKitUpdater::involvedPackages(const QSet& packages) const { QSet packageIds; packageIds.reserve(packages.size()); foreach (AbstractResource * res, packages) { PackageKitResource * app = qobject_cast(res); QString pkgid = m_backend->upgradeablePackageId(app); if (pkgid.isEmpty()) { qWarning() << "no upgradeablePackageId for" << app; continue; } packageIds.insert(pkgid); } 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 { if (qEnvironmentVariableIsSet("PK_OFFLINE_UPDATE")) return true; KConfigGroup group(KSharedConfig::openConfig(), "Software"); return group.readEntry("UseOfflineUpdates", false); } void PackageKitUpdater::setUseOfflineUpdates(bool use) { // To enable from command line use: // kwriteconfig5 --file discoverrc --group Software --key UseOfflineUpdates true KConfigGroup group(KSharedConfig::openConfig(), "Software"); group.writeEntry("UseOfflineUpdates", use); } void PackageKitUpdater::start() { Q_ASSERT(!isProgressing()); setupTransaction(PackageKit::Transaction::TransactionFlagSimulate); setProgressing(true); } void PackageKitUpdater::finished(PackageKit::Transaction::Exit exit, uint /*time*/) { // qCDebug(LIBDISCOVER_BACKEND_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) { const auto toremove = m_packagesModified.value(PackageKit::Transaction::InfoRemoving); if (!toremove.isEmpty()) { const auto toinstall = QStringList() << m_packagesModified.value(PackageKit::Transaction::InfoInstalling) << m_packagesModified.value(PackageKit::Transaction::InfoUpdating); Q_EMIT proceedRequest(i18n("Packages to remove"), i18n("The following packages will be removed by the update:\n
  • %1
\nin order to install:\n
  • %2
", PackageKitResource::joinPackages(toremove, QStringLiteral("
  • "), {}), PackageKitResource::joinPackages(toinstall, QStringLiteral("
  • "), {}) )); } else { proceed(); } return; } setProgressing(false); m_backend->fetchUpdates(); fetchLastUpdateTime(); if (useOfflineUpdates() && exit == PackageKit::Transaction::ExitSuccess) { PackageKit::Daemon::global()->offline()->trigger(PackageKit::Offline::ActionReboot); Q_EMIT passiveMessage(i18n("Please restart the computer to finish the update")); } } void PackageKitUpdater::cancellableChanged() { if (m_isCancelable != m_transaction->allowCancel()) { m_isCancelable = m_transaction->allowCancel(); emit cancelableChanged(m_isCancelable); } } void PackageKitUpdater::percentageChanged() { const auto actualPercentage = percentageWithStatus(m_transaction->status(), m_transaction->percentage()); if (actualPercentage >= 0 && m_percentage != actualPercentage) { m_percentage = actualPercentage; 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& apps) { - QSet pkgs = involvedPackages(apps.toSet()); + QSet pkgs = involvedPackages(kToSet(apps)); m_toUpgrade.subtract(packagesForPackageId(pkgs)); } void PackageKitUpdater::addResources(const QList& apps) { - QSet pkgs = involvedPackages(apps.toSet()); + QSet pkgs = involvedPackages(kToSet(apps)); m_toUpgrade.unite(packagesForPackageId(pkgs)); } QList PackageKitUpdater::toUpdate() const { - return m_toUpgrade.toList(); + return m_toUpgrade +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + .values(); +#else + .toList(); +#endif } bool PackageKitUpdater::isMarked(AbstractResource* res) const { return m_toUpgrade.contains(res); } 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) return; Q_EMIT passiveMessage(QStringLiteral("%1\n%2").arg(PackageKitMessages::errorMessage(err), error)); 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)); } void PackageKitUpdater::requireRestart(PackageKit::Transaction::Restart restart, const QString& pkgid) { Q_EMIT passiveMessage(PackageKitMessages::restartMessage(restart, pkgid)); } void PackageKitUpdater::eulaRequired(const QString& eulaID, const QString& packageID, const QString& vendor, const QString& licenseAgreement) { m_proceedFunctions << [eulaID](){ return PackageKit::Daemon::acceptEula(eulaID); }; 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)); } void PackageKitUpdater::setProgressing(bool progressing) { if (m_isProgressing != progressing) { m_isProgressing = progressing; 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_LOG) << "unknown packagekit status" << t; return AbstractBackendUpdater::None; } Q_UNREACHABLE(); } void PackageKitUpdater::itemProgress(const QString& itemID, PackageKit::Transaction::Status status, uint percentage) { const auto res = packagesForPackageId({itemID}); foreach(auto r, res) { Q_EMIT resourceProgressed(r, percentage, toUpdateState(status)); } } void PackageKitUpdater::fetchChangelog() const { QStringList pkgids; foreach(AbstractResource* res, m_allUpgradeable) { pkgids += static_cast(res)->availablePackageId(); } Q_ASSERT(!pkgids.isEmpty()); PackageKit::Transaction* t = PackageKit::Daemon::getUpdatesDetails(pkgids); connect(t, &PackageKit::Transaction::updateDetail, this, &PackageKitUpdater::updateDetail); connect(t, &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) { auto res = packagesForPackageId({packageID}); foreach(auto r, res) { static_cast(r)->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 (AbstractResource * res : m_toUpgrade) { PackageKitResource * app = qobject_cast(res); QString pkgid = m_backend->upgradeablePackageId(app); if (!donePkgs.contains(pkgid)) { donePkgs.insert(pkgid); ret += app->size(); } } return ret; } quint64 PackageKitUpdater::downloadSpeed() const { return m_transaction ? m_transaction->speed() : 0; } diff --git a/libdiscover/backends/SnapBackend/SnapResource.cpp b/libdiscover/backends/SnapBackend/SnapResource.cpp index c299fc2f..b83136a4 100644 --- a/libdiscover/backends/SnapBackend/SnapResource.cpp +++ b/libdiscover/backends/SnapBackend/SnapResource.cpp @@ -1,545 +1,545 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "SnapResource.h" #include "SnapBackend.h" #include #include #include #include #include #include #ifdef SNAP_MARKDOWN #include #endif #include QDebug operator<<(QDebug debug, const QSnapdPlug& plug) { QDebugStateSaver saver(debug); debug.nospace() << "QSnapdPlug("; debug.nospace() << "name:" << plug.name() << ','; debug.nospace() << "snap:" << plug.snap() << ','; debug.nospace() << "label:" << plug.label() << ','; debug.nospace() << "interface:" << plug.interface() << ','; - debug.nospace() << "connectionCount:" << plug.connectionCount(); +// debug.nospace() << "connectionCount:" << plug.connectionSlotCount(); debug.nospace() << ')'; return debug; } QDebug operator<<(QDebug debug, const QSnapdSlot& slot) { QDebugStateSaver saver(debug); debug.nospace() << "QSnapdSlot("; debug.nospace() << "name:" << slot.name() << ','; debug.nospace() << "label:" << slot.label() << ','; debug.nospace() << "snap:" << slot.snap() << ','; debug.nospace() << "interface:" << slot.interface() << ','; - debug.nospace() << "connectionCount:" << slot.connectionCount(); +// debug.nospace() << "connectionCount:" << slot.connectionSlotCount(); debug.nospace() << ')'; return debug; } QDebug operator<<(QDebug debug, const QSnapdPlug* plug) { QDebugStateSaver saver(debug); debug.nospace() << "*" << *plug; return debug; } QDebug operator<<(QDebug debug, const QSnapdSlot* slot) { QDebugStateSaver saver(debug); debug.nospace() << "*" << *slot; return debug; } const QStringList SnapResource::m_objects({ QStringLiteral("qrc:/qml/PermissionsButton.qml") #ifdef SNAP_CHANNELS , QStringLiteral("qrc:/qml/ChannelsButton.qml") #endif }); SnapResource::SnapResource(QSharedPointer snap, AbstractResource::State state, SnapBackend* backend) : AbstractResource(backend) , m_state(state) , m_snap(snap) { setObjectName(snap->name()); } QSnapdClient * SnapResource::client() const { auto backend = qobject_cast(parent()); return backend->client(); } QString SnapResource::availableVersion() const { return installedVersion(); } QStringList SnapResource::categories() { return { QStringLiteral("Application") }; } QString SnapResource::comment() { return m_snap->summary(); } int SnapResource::size() { // return isInstalled() ? m_snap->installedSize() : m_snap->downloadSize(); return m_snap->downloadSize(); } QVariant SnapResource::icon() const { if (m_icon.isNull()) { m_icon = [this]() -> QVariant { const auto iconPath = m_snap->icon(); if (iconPath.isEmpty()) return QStringLiteral("package-x-generic"); if (!iconPath.startsWith(QLatin1Char('/'))) return QUrl(iconPath); auto req = client()->getIcon(packageName()); connect(req, &QSnapdGetIconRequest::complete, this, &SnapResource::gotIcon); req->runAsync(); return {}; }(); } return m_icon; } void SnapResource::gotIcon() { auto req = qobject_cast(sender()); if (req->error()) { qWarning() << "icon error" << req->errorString(); return; } auto icon = req->icon(); QBuffer buffer; buffer.setData(icon->data()); QImageReader reader(&buffer); auto theIcon = QVariant::fromValue(reader.read()); if (theIcon != m_icon) { m_icon = theIcon; Q_EMIT iconChanged(); } } QString SnapResource::installedVersion() const { return m_snap->version(); } QJsonArray SnapResource::licenses() { return { QJsonObject{ {QStringLiteral("name"), m_snap->license()} } }; } #ifdef SNAP_MARKDOWN static QString serialize_node (QSnapdMarkdownNode &node); static QString serialize_children (QSnapdMarkdownNode &node) { QString result; for (int i = 0; i < node.childCount (); i++) { QScopedPointer child (node.child (i)); result += serialize_node (*child); } return result; } static QString serialize_node (QSnapdMarkdownNode &node) { switch (node.type ()) { case QSnapdMarkdownNode::NodeTypeText: return node.text().toHtmlEscaped(); case QSnapdMarkdownNode::NodeTypeParagraph: return QLatin1String("

    ") + serialize_children (node) + QLatin1String("

    \n"); case QSnapdMarkdownNode::NodeTypeUnorderedList: return QLatin1String("
      \n") + serialize_children (node) + QLatin1String("
    \n"); case QSnapdMarkdownNode::NodeTypeListItem: if (node.childCount () == 0) return QLatin1String("
  • \n"); if (node.childCount () == 1) { QScopedPointer child (node.child (0)); if (child->type () == QSnapdMarkdownNode::NodeTypeParagraph) return QLatin1String("
  • ") + serialize_children (*child) + QLatin1String("
  • \n"); } return QLatin1String("
  • \n") + serialize_children (node) + QLatin1String("
  • \n"); case QSnapdMarkdownNode::NodeTypeCodeBlock: return QLatin1String("
    ") + serialize_children (node) + QLatin1String("
    \n"); case QSnapdMarkdownNode::NodeTypeCodeSpan: return QLatin1String("") + serialize_children (node) + QLatin1String(""); case QSnapdMarkdownNode::NodeTypeEmphasis: return QLatin1String("") + serialize_children (node) + QLatin1String(""); case QSnapdMarkdownNode::NodeTypeStrongEmphasis: return QLatin1String("") + serialize_children (node) + QLatin1String(""); case QSnapdMarkdownNode::NodeTypeUrl: return serialize_children (node); default: return QString(); } } #endif QString SnapResource::longDescription() { #ifdef SNAP_MARKDOWN QSnapdMarkdownParser parser (QSnapdMarkdownParser::MarkdownVersion0); QList nodes = parser.parse (m_snap->description()); QString result; for (int i = 0; i < nodes.size (); i++) result += serialize_node (nodes[i]); return result; #else return m_snap->description(); #endif } QString SnapResource::name() const { return m_snap->title().isEmpty() ? m_snap->name() : m_snap->title(); } QString SnapResource::origin() const { return QStringLiteral("Snap"); } QString SnapResource::packageName() const { return m_snap->name(); } QString SnapResource::section() { return QStringLiteral("snap"); } AbstractResource::State SnapResource::state() { return m_state; } void SnapResource::setState(AbstractResource::State state) { if (m_state != state) { m_state = state; Q_EMIT stateChanged(); } } void SnapResource::fetchChangelog() { QString log; emit changelogFetched(log); } void SnapResource::fetchScreenshots() { QList screenshots; #ifdef SNAP_MEDIA for(int i = 0, c = m_snap->mediaCount(); i media(m_snap->media(i)); if (media->type() == QLatin1String("screenshot")) screenshots << QUrl(media->url()); } #else for(int i = 0, c = m_snap->screenshotCount(); i screenshot(m_snap->screenshot(i)); screenshots << QUrl(screenshot->url()); } #endif Q_EMIT screenshotsFetched(screenshots, screenshots); } void SnapResource::invokeApplication() const { QProcess::startDetached(QStringLiteral("snap"), {QStringLiteral("run"), packageName()}); } AbstractResource::Type SnapResource::type() const { return m_snap->snapType() != QLatin1String("app") ? Application : Technical; } void SnapResource::setSnap(const QSharedPointer& snap) { Q_ASSERT(snap->name() == m_snap->name()); if (m_snap == snap) return; const bool newSize = m_snap->installedSize() != snap->installedSize() || m_snap->downloadSize() != snap->downloadSize(); m_snap = snap; if (newSize) Q_EMIT sizeChanged(); Q_EMIT newSnap(); } QDate SnapResource::releaseDate() const { return {}; } class PlugsModel : public QStandardItemModel { public: enum Roles { PlugNameRole = Qt::UserRole + 1, SlotSnapRole, SlotNameRole }; PlugsModel(SnapResource* res, SnapBackend* backend, QObject* parent) : QStandardItemModel(parent) , m_res(res) , m_backend(backend) { setItemRoleNames(roleNames().unite( { {Qt::CheckStateRole, "checked"} } )); auto req = backend->client()->getInterfaces(); req->runSync(); QHash> slotsForInterface; for (int i = 0; islotCount(); ++i) { const auto slot = req->slot(i); slot->setParent(this); slotsForInterface[slot->interface()].append(slot); } const auto snap = m_res->snap(); for (int i = 0; iplugCount(); ++i) { const QScopedPointer plug(req->plug(i)); if (plug->snap() == snap->name()) { if (plug->interface() == QLatin1String("content")) continue; const auto theSlots = slotsForInterface.value(plug->interface()); for (auto slot: theSlots) { auto item = new QStandardItem; if (plug->label().isEmpty()) item->setText(plug->name()); else item->setText(i18n("%1 - %2", plug->name(), plug->label())); // qDebug() << "xxx" << plug->name() << plug->label() << plug->interface() << slot->snap() << "slot:" << slot->name() << slot->snap() << slot->interface() << slot->label(); item->setCheckable(true); item->setCheckState(plug->connectionCount()>0 ? Qt::Checked : Qt::Unchecked); item->setData(plug->name(), PlugNameRole); item->setData(slot->snap(), SlotSnapRole); item->setData(slot->name(), SlotNameRole); appendRow(item); } } } } private: bool setData(const QModelIndex & index, const QVariant & value, int role) override { if (role != Qt::CheckStateRole) return QStandardItemModel::setData(index, value, role); auto item = itemFromIndex(index); Q_ASSERT(item); const QString plugName = item->data(PlugNameRole).toString(); const QString slotSnap = item->data(SlotSnapRole).toString(); const QString slotName = item->data(SlotNameRole).toString(); QSnapdRequest* req; const auto snap = m_res->snap(); if (item->checkState() == Qt::Checked) { req = m_backend->client()->disconnectInterface(snap->name(), plugName, slotSnap, slotName); } else { req = m_backend->client()->connectInterface(snap->name(), plugName, slotSnap, slotName); } req->runSync(); if (req->error()) { qWarning() << "snapd error" << req->errorString(); Q_EMIT m_res->backend()->passiveMessage(req->errorString()); } return req->error() == QSnapdRequest::NoError; } SnapResource* const m_res; SnapBackend* const m_backend; }; QAbstractItemModel* SnapResource::plugs(QObject* p) { if (!isInstalled()) return new QStandardItemModel(p); return new PlugsModel(this, qobject_cast(parent()), p); } QString SnapResource::appstreamId() const { const QStringList ids #if defined(SNAP_COMMON_IDS) = m_snap->commonIds() #endif ; return ids.isEmpty() ? QLatin1String("io.snapcraft.") + m_snap->name() + QLatin1Char('-') + m_snap->id() : ids.first(); } QString SnapResource::channel() const { #ifdef SNAP_PUBLISHER auto req = client()->getSnap(packageName()); #else auto req = client()->listOne(packageName()); #endif req->runSync(); return req->error() ? QString() : req->snap()->trackingChannel(); } QString SnapResource::author() const { #ifdef SNAP_PUBLISHER QString author = m_snap->publisherDisplayName(); if (m_snap->publisherValidation() == QSnapdEnums::PublisherValidationVerified) { author += QStringLiteral(" ✅"); } #else QString author; #endif return author; } void SnapResource::setChannel(const QString& channelName) { #ifdef SNAP_CHANNELS Q_ASSERT(isInstalled()); auto request = client()->switchChannel(m_snap->name(), channelName); const auto currentChannel = channel(); auto dest = new CallOnDestroy([this, currentChannel]() { const auto newChannel = channel(); if (newChannel != currentChannel) { Q_EMIT channelChanged(newChannel); } }); request->runAsync(); connect(request, &QSnapdRequest::complete, dest, &QObject::deleteLater); #endif } void SnapResource::refreshSnap() { auto request = client()->find(QSnapdClient::FindFlag::MatchName, m_snap->name()); connect(request, &QSnapdRequest::complete, this, [this, request](){ if (request->error()) { qWarning() << "error" << request->error() << ": " << request->errorString(); return; } Q_ASSERT(request->snapCount() == 1); setSnap(QSharedPointer(request->snap(0))); }); request->runAsync(); } #ifdef SNAP_CHANNELS class Channels : public QObject { Q_OBJECT Q_PROPERTY(QList channels READ channels NOTIFY channelsChanged) public: Channels(SnapResource* res, QObject* parent) : QObject(parent), m_res(res) { if (res->snap()->channelCount() == 0) res->refreshSnap(); else refreshChannels(); connect(res, &SnapResource::newSnap, this, &Channels::refreshChannels); } void refreshChannels() { qDeleteAll(m_channels); m_channels.clear(); auto s = m_res->snap(); for(int i=0, c=s->channelCount(); ichannel(i); channel->setParent(this); m_channels << channel; } Q_EMIT channelsChanged(); } QList channels() const { return m_channels; } Q_SIGNALS: void channelsChanged(); private: QList m_channels; SnapResource* const m_res; }; #endif QObject * SnapResource::channels(QObject* parent) { #ifdef SNAP_CHANNELS return new Channels(this, parent); #else return nullptr; #endif } #include "SnapResource.moc" diff --git a/libdiscover/backends/SnapBackend/SnapTransaction.cpp b/libdiscover/backends/SnapBackend/SnapTransaction.cpp index 568f1847..dcf85391 100644 --- a/libdiscover/backends/SnapBackend/SnapTransaction.cpp +++ b/libdiscover/backends/SnapBackend/SnapTransaction.cpp @@ -1,131 +1,131 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "SnapTransaction.h" #include "SnapBackend.h" #include "SnapResource.h" #include #include #include #include #include #include #include "libsnapclient/config-paths.h" #include "utils.h" SnapTransaction::SnapTransaction(QSnapdClient* client, SnapResource* app, Role role, AbstractResource::State newState) : Transaction(app, app, role) , m_client(client) , m_app(app) , m_newState(newState) { if (role == RemoveRole) setRequest(m_client->remove(app->packageName())); else setRequest(m_client->install(app->packageName())); } void SnapTransaction::cancel() { m_request->cancel(); } void SnapTransaction::finishTransaction() { switch(m_request->error()) { case QSnapdRequest::NoError: static_cast(m_app->backend())->refreshStates(); setStatus(DoneStatus); m_app->setState(m_newState); break; case QSnapdRequest::NeedsClassic: setStatus(SetupStatus); if (role() == Transaction::InstallRole) { Q_EMIT proceedRequest(m_app->name(), i18n("This snap application needs security confinement measures disabled.")); return; } break; case QSnapdRequest::AuthDataRequired: { setStatus(SetupStatus); QProcess* p = new QProcess; p->setProgram(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/discover/SnapMacaroonDialog")); p->start(); - connect(p, static_cast(&QProcess::finished), this, [this, p] (int code) { + connect(p, static_cast(&QProcess::finished), this, [this, p] (int code, QProcess::ExitStatus status) { p->deleteLater(); if (code != 0) { - qWarning() << "login failed... code:" << code << p->readAll(); + qWarning() << "login failed... code:" << code << status << p->readAll(); Q_EMIT passiveMessage(m_request->errorString()); setStatus(DoneWithErrorStatus); return; } const auto doc = QJsonDocument::fromJson(p->readAllStandardOutput()); const auto result = doc.object(); const auto macaroon = result[QStringLiteral("macaroon")].toString(); const auto discharges = kTransform(result[QStringLiteral("discharges")].toArray(), [](const QJsonValue& val) { return val.toString(); }); static_cast(m_app->backend())->client()->setAuthData(new QSnapdAuthData(macaroon, discharges)); m_request->runAsync(); }); } return; default: setStatus(DoneWithErrorStatus); qDebug() << "snap error" << m_request << m_request->error() << m_request->errorString(); Q_EMIT passiveMessage(m_request->errorString()); break; } } void SnapTransaction::proceed() { setRequest(m_client->install(QSnapdClient::Classic, m_app->packageName())); } void SnapTransaction::setRequest(QSnapdRequest* req) { m_request.reset(req); setCancellable(true); connect(m_request.data(), &QSnapdRequest::progress, this, &SnapTransaction::progressed); connect(m_request.data(), &QSnapdRequest::complete, this, &SnapTransaction::finishTransaction); setStatus(CommittingStatus); m_request->runAsync(); } void SnapTransaction::progressed() { const auto change = m_request->change(); int percentage = 0, count = 0; auto status = SetupStatus; for(int i = 0, c = change->taskCount(); itask(i); if (task->kind() == QLatin1String("download-snap")) { status = task->status() == QLatin1String("doing") || task->status() == QLatin1String("do") ? DownloadingStatus : CommittingStatus; } else if (task->kind() == QLatin1String("clear-snap")) { status = CommittingStatus; } percentage += (100 * task->progressDone()) / task->progressTotal(); } setProgress(percentage / qMax(count, 1)); setStatus(status); } diff --git a/libdiscover/backends/SnapBackend/libsnapclient/SnapAuthHelper.cpp b/libdiscover/backends/SnapBackend/libsnapclient/SnapAuthHelper.cpp index 5ed820d3..c1e4195f 100644 --- a/libdiscover/backends/SnapBackend/libsnapclient/SnapAuthHelper.cpp +++ b/libdiscover/backends/SnapBackend/libsnapclient/SnapAuthHelper.cpp @@ -1,82 +1,80 @@ /*************************************************************************** * Copyright © 2016 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include using namespace KAuth; class SnapAuthHelper : public QObject { Q_OBJECT QSnapdClient m_client; public: - SnapAuthHelper() { - m_client.connect()->runAsync(); - } + SnapAuthHelper() {} public Q_SLOTS: KAuth::ActionReply login(const QVariantMap &args) { const QString user = args[QStringLiteral("user")].toString() , pass = args[QStringLiteral("password")].toString() , otp = args[QStringLiteral("otp")].toString(); QScopedPointer req(otp.isEmpty() ? m_client.login(user, pass) : m_client.login(user, pass, otp)); req->runSync(); ActionReply reply; bool otpMode = false; QByteArray replyData; if (req->error() == QSnapdRequest::NoError) { const auto auth = req->authData(); replyData = QJsonDocument(QJsonObject{ {QStringLiteral("macaroon"), auth->macaroon()}, {QStringLiteral("discharges"), QJsonArray::fromStringList(auth->discharges())} }).toJson(); reply = ActionReply::SuccessReply(); } else { otpMode = req->error() == QSnapdConnectRequest::TwoFactorRequired; reply = ActionReply::InvalidActionReply(); reply.setErrorDescription(req->errorString()); } reply.setData({ { QStringLiteral("reply"), replyData }, { QStringLiteral("otpMode"), otpMode } }); return reply; } }; KAUTH_HELPER_MAIN("org.kde.discover.libsnapclient", SnapAuthHelper) #include "SnapAuthHelper.moc" diff --git a/libdiscover/resources/ResourcesProxyModel.cpp b/libdiscover/resources/ResourcesProxyModel.cpp index ce458d20..8ec8ab51 100644 --- a/libdiscover/resources/ResourcesProxyModel.cpp +++ b/libdiscover/resources/ResourcesProxyModel.cpp @@ -1,601 +1,601 @@ /*************************************************************************** * Copyright © 2010 Jonathan Thomas * * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "ResourcesProxyModel.h" #include "libdiscover_debug.h" #include #include #include "ResourcesModel.h" #include "AbstractResource.h" #include "AbstractResourcesBackend.h" #include #include #include ResourcesProxyModel::ResourcesProxyModel(QObject *parent) : QAbstractListModel(parent) , m_sortRole(NameRole) , m_sortOrder(Qt::AscendingOrder) , m_sortByRelevancy(false) , m_roles({ { NameRole, "name" }, { IconRole, "icon" }, { CommentRole, "comment" }, { StateRole, "state" }, { RatingRole, "rating" }, { RatingPointsRole, "ratingPoints" }, { RatingCountRole, "ratingCount" }, { SortableRatingRole, "sortableRating" }, { InstalledRole, "isInstalled" }, { ApplicationRole, "application" }, { OriginRole, "origin" }, { DisplayOriginRole, "displayOrigin" }, { CanUpgrade, "canUpgrade" }, { PackageNameRole, "packageName" }, { CategoryRole, "category" }, { CategoryDisplayRole, "categoryDisplay" }, { SectionRole, "section" }, { MimeTypes, "mimetypes" }, { LongDescriptionRole, "longDescription" }, { SourceIconRole, "sourceIcon" }, { SizeRole, "size" }, { ReleaseDateRole, "releaseDate" } }) , m_currentStream(nullptr) { // new QAbstractItemModelTester(this, this); connect(ResourcesModel::global(), &ResourcesModel::backendsChanged, this, &ResourcesProxyModel::invalidateFilter); connect(ResourcesModel::global(), &ResourcesModel::backendDataChanged, this, &ResourcesProxyModel::refreshBackend); connect(ResourcesModel::global(), &ResourcesModel::resourceDataChanged, this, &ResourcesProxyModel::refreshResource); connect(ResourcesModel::global(), &ResourcesModel::resourceRemoved, this, &ResourcesProxyModel::removeResource); connect(this, &QAbstractItemModel::modelReset, this, &ResourcesProxyModel::countChanged); connect(this, &QAbstractItemModel::rowsInserted, this, &ResourcesProxyModel::countChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &ResourcesProxyModel::countChanged); } void ResourcesProxyModel::componentComplete() { m_setup = true; invalidateFilter(); } QHash ResourcesProxyModel::roleNames() const { return m_roles; } void ResourcesProxyModel::setSortRole(Roles sortRole) { if (sortRole != m_sortRole) { Q_ASSERT(roleNames().contains(sortRole)); m_sortRole = sortRole; Q_EMIT sortRoleChanged(sortRole); invalidateSorting(); } } void ResourcesProxyModel::setSortOrder(Qt::SortOrder sortOrder) { if (sortOrder != m_sortOrder) { m_sortOrder = sortOrder; Q_EMIT sortRoleChanged(sortOrder); invalidateSorting(); } } void ResourcesProxyModel::setSearch(const QString &_searchText) { // 1-character searches are painfully slow. >= 2 chars are fine, though const QString searchText = _searchText.count() <= 1 ? QString() : _searchText; const bool diff = searchText != m_filters.search; if (diff) { m_filters.search = searchText; if (m_sortByRelevancy == searchText.isEmpty()) { m_sortByRelevancy = !searchText.isEmpty(); Q_EMIT sortByRelevancyChanged(m_sortByRelevancy); } invalidateFilter(); Q_EMIT searchChanged(m_filters.search); } } void ResourcesProxyModel::removeDuplicates(QVector& resources) { const auto cab = ResourcesModel::global()->currentApplicationBackend(); QHash::iterator> storedIds; for(auto it = m_displayedResources.begin(); it != m_displayedResources.end(); ++it) { const auto appstreamid = (*it)->appstreamId(); if (appstreamid.isEmpty()) { continue; } auto at = storedIds.find(appstreamid); if (at == storedIds.end()) { storedIds[appstreamid] = it; } else { qCWarning(LIBDISCOVER_LOG) << "We should have sanitized the displayed resources. There is a bug"; Q_UNREACHABLE(); } } QHash::iterator> ids; for(auto it = resources.begin(); it != resources.end(); ) { const auto appstreamid = (*it)->appstreamId(); if (appstreamid.isEmpty()) { ++it; continue; } auto at = storedIds.find(appstreamid); if (at == storedIds.end()) { auto at = ids.find(appstreamid); if (at == ids.end()) { ids[appstreamid] = it; ++it; } else { if ((*it)->backend() == cab) { qSwap(*it, **at); } it = resources.erase(it); } } else { if ((*it)->backend() == cab) { **at = *it; auto pos = index(*at - m_displayedResources.begin(), 0); Q_EMIT dataChanged(pos, pos); } it = resources.erase(it); } } } void ResourcesProxyModel::addResources(const QVector& _res) { auto res = _res; m_filters.filterJustInCase(res); if (res.isEmpty()) return; if (!m_sortByRelevancy) std::sort(res.begin(), res.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); sortedInsertion(res); fetchSubcategories(); } void ResourcesProxyModel::invalidateSorting() { if (m_displayedResources.isEmpty()) return; if (!m_sortByRelevancy) { beginResetModel(); std::sort(m_displayedResources.begin(), m_displayedResources.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); endResetModel(); } } QString ResourcesProxyModel::lastSearch() const { return m_filters.search; } void ResourcesProxyModel::setOriginFilter(const QString &origin) { if (origin == m_filters.origin) return; m_filters.origin = origin; invalidateFilter(); } QString ResourcesProxyModel::originFilter() const { return m_filters.origin; } void ResourcesProxyModel::setFiltersFromCategory(Category *category) { if(category==m_filters.category) return; m_filters.category = category; invalidateFilter(); emit categoryChanged(); } void ResourcesProxyModel::fetchSubcategories() { - auto cats = kVectorToSet(m_filters.category ? m_filters.category->subCategories() : CategoryModel::global()->rootCategories()); + auto cats = kToSet(m_filters.category ? m_filters.category->subCategories() : CategoryModel::global()->rootCategories()); const int count = rowCount(); QSet done; for (int i=0; icategoryObjects(kSetToVector(cats)); done.unite(found); cats.subtract(found); } const QVariantList ret = kTransform(done, [](Category* cat) { return QVariant::fromValue(cat); }); if (ret != m_subcategories) { m_subcategories = ret; Q_EMIT subcategoriesChanged(m_subcategories); } } QVariantList ResourcesProxyModel::subcategories() const { return m_subcategories; } void ResourcesProxyModel::invalidateFilter() { if (!m_setup || ResourcesModel::global()->backends().isEmpty()) { return; } if (m_currentStream) { qCWarning(LIBDISCOVER_LOG) << "last stream isn't over yet" << m_filters << this; delete m_currentStream; } m_currentStream = ResourcesModel::global()->search(m_filters); Q_EMIT busyChanged(true); if (!m_displayedResources.isEmpty()) { beginResetModel(); m_displayedResources.clear(); endResetModel(); } connect(m_currentStream, &AggregatedResultsStream::resourcesFound, this, &ResourcesProxyModel::addResources); connect(m_currentStream, &AggregatedResultsStream::finished, this, [this]() { m_currentStream = nullptr; Q_EMIT busyChanged(false); }); } int ResourcesProxyModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_displayedResources.count(); } bool ResourcesProxyModel::lessThan(AbstractResource* leftPackage, AbstractResource* rightPackage) const { auto role = m_sortRole; Qt::SortOrder order = m_sortOrder; QVariant leftValue; QVariant rightValue; //if we're comparing two equal values, we want the model sorted by application name if(role != NameRole) { leftValue = roleToValue(leftPackage, role); rightValue = roleToValue(rightPackage, role); if (leftValue == rightValue) { role = NameRole; order = Qt::AscendingOrder; } } bool ret; if(role == NameRole) { ret = leftPackage->nameSortKey().compare(rightPackage->nameSortKey()) < 0; } else if(role == CanUpgrade) { ret = leftValue.toBool(); } else { ret = leftValue < rightValue; } return ret != (order != Qt::AscendingOrder); } Category* ResourcesProxyModel::filteredCategory() const { return m_filters.category; } void ResourcesProxyModel::setStateFilter(AbstractResource::State s) { if (s != m_filters.state) { m_filters.state = s; invalidateFilter(); emit stateFilterChanged(); } } AbstractResource::State ResourcesProxyModel::stateFilter() const { return m_filters.state; } QString ResourcesProxyModel::mimeTypeFilter() const { return m_filters.mimetype; } void ResourcesProxyModel::setMimeTypeFilter(const QString& mime) { if (m_filters.mimetype != mime) { m_filters.mimetype = mime; invalidateFilter(); } } QString ResourcesProxyModel::extends() const { return m_filters.extends; } void ResourcesProxyModel::setExtends(const QString& extends) { if (m_filters.extends != extends) { m_filters.extends = extends; invalidateFilter(); } } void ResourcesProxyModel::setFilterMinimumState(bool filterMinimumState) { if (filterMinimumState != m_filters.filterMinimumState) { m_filters.filterMinimumState = filterMinimumState; invalidateFilter(); Q_EMIT filterMinimumStateChanged(m_filters.filterMinimumState); } } bool ResourcesProxyModel::filterMinimumState() const { return m_filters.filterMinimumState; } QUrl ResourcesProxyModel::resourcesUrl() const { return m_filters.resourceUrl; } void ResourcesProxyModel::setResourcesUrl(const QUrl& resourcesUrl) { if (m_filters.resourceUrl != resourcesUrl) { m_filters.resourceUrl = resourcesUrl; invalidateFilter(); } } bool ResourcesProxyModel::allBackends() const { return m_filters.allBackends; } void ResourcesProxyModel::setAllBackends(bool allBackends) { m_filters.allBackends = allBackends; } QVariant ResourcesProxyModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } AbstractResource* const resource = m_displayedResources[index.row()]; return roleToValue(resource, role); } QVariant ResourcesProxyModel::roleToValue(AbstractResource* resource, int role) const { switch(role) { case ApplicationRole: return QVariant::fromValue(resource); case RatingPointsRole: case RatingRole: case RatingCountRole: case SortableRatingRole: { Rating* const rating = resource->rating(); const int idx = Rating::staticMetaObject.indexOfProperty(roleNames().value(role).constData()); Q_ASSERT(idx >= 0); auto prop = Rating::staticMetaObject.property(idx); if (rating) return prop.read(rating); else { QVariant val(0); val.convert(prop.type()); return val; } } case Qt::DecorationRole: case Qt::DisplayRole: case Qt::StatusTipRole: case Qt::ToolTipRole: return QVariant(); default: { QByteArray roleText = roleNames().value(role); if(Q_UNLIKELY(roleText.isEmpty())) { qCDebug(LIBDISCOVER_LOG) << "unsupported role" << role; return {}; } static const QMetaObject* m = &AbstractResource::staticMetaObject; int propidx = roleText.isEmpty() ? -1 : m->indexOfProperty(roleText.constData()); if(Q_UNLIKELY(propidx < 0)) { qCWarning(LIBDISCOVER_LOG) << "unknown role:" << role << roleText; return QVariant(); } else return m->property(propidx).read(resource); } } } bool ResourcesProxyModel::isSorted(const QVector & resources) { auto last = resources.constFirst(); for(auto it = resources.constBegin()+1, itEnd = resources.constEnd(); it != itEnd; ++it) { if(!lessThan(last, *it)) { return false; } last = *it; } return true; } void ResourcesProxyModel::sortedInsertion(const QVector & _res) { auto resources = _res; Q_ASSERT(!resources.isEmpty()); if (!m_filters.allBackends) { removeDuplicates(resources); if (resources.isEmpty()) return; } if (m_sortByRelevancy || m_displayedResources.isEmpty()) { // Q_ASSERT(m_sortByRelevancy || isSorted(resources)); int rows = rowCount(); beginInsertRows({}, rows, rows+resources.count()-1); m_displayedResources += resources; endInsertRows(); return; } for(auto resource: qAsConst(resources)) { const auto finder = [this](AbstractResource* resource, AbstractResource* res){ return lessThan(resource, res); }; const auto it = std::upper_bound(m_displayedResources.constBegin(), m_displayedResources.constEnd(), resource, finder); const auto newIdx = it == m_displayedResources.constEnd() ? m_displayedResources.count() : (it - m_displayedResources.constBegin()); if ((it-1) != m_displayedResources.constEnd() && *(it-1) == resource) continue; beginInsertRows({}, newIdx, newIdx); m_displayedResources.insert(newIdx, resource); endInsertRows(); // Q_ASSERT(isSorted(resources)); } } void ResourcesProxyModel::refreshResource(AbstractResource* resource, const QVector& properties) { const auto residx = m_displayedResources.indexOf(resource); if (residx<0) { if (!m_sortByRelevancy && m_filters.shouldFilter(resource)) { sortedInsertion({resource}); } return; } if (!m_filters.shouldFilter(resource)) { beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); return; } const QModelIndex idx = index(residx, 0); Q_ASSERT(idx.isValid()); const auto roles = propertiesToRoles(properties); if (!m_sortByRelevancy && roles.contains(m_sortRole)) { beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); sortedInsertion({resource}); } else emit dataChanged(idx, idx, roles); } void ResourcesProxyModel::removeResource(AbstractResource* resource) { const auto residx = m_displayedResources.indexOf(resource); if (residx < 0) return; beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); } void ResourcesProxyModel::refreshBackend(AbstractResourcesBackend* backend, const QVector& properties) { auto roles = propertiesToRoles(properties); const int count = m_displayedResources.count(); bool found = false; for(int i = 0; ibackend()) continue; int j = i+1; for(; jbackend(); ++j) {} Q_EMIT dataChanged(index(i, 0), index(j-1, 0), roles); i = j; found = true; } if (found && properties.contains(m_roles.value(m_sortRole))) { invalidateSorting(); } } QVector ResourcesProxyModel::propertiesToRoles(const QVector& properties) const { QVector roles = kTransform>(properties, [this](const QByteArray& arr) { return roleNames().key(arr, -1); }); roles.removeAll(-1); return roles; } int ResourcesProxyModel::indexOf(AbstractResource* res) { return m_displayedResources.indexOf(res); } AbstractResource * ResourcesProxyModel::resourceAt(int row) const { return m_displayedResources[row]; } bool ResourcesProxyModel::canFetchMore(const QModelIndex& parent) const { Q_ASSERT(!parent.isValid()); return m_currentStream; } void ResourcesProxyModel::fetchMore(const QModelIndex& parent) { Q_ASSERT(!parent.isValid()); if (!m_currentStream) return; Q_EMIT m_currentStream->fetchMore(); } bool ResourcesProxyModel::sortByRelevancy() const { return m_sortByRelevancy; } diff --git a/libdiscover/resources/StandardBackendUpdater.cpp b/libdiscover/resources/StandardBackendUpdater.cpp index 2251c4d5..f8b2f085 100644 --- a/libdiscover/resources/StandardBackendUpdater.cpp +++ b/libdiscover/resources/StandardBackendUpdater.cpp @@ -1,299 +1,291 @@ /* * Copyright (C) 2012 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "ResourcesModel.h" #include #include #include #include #include "libdiscover_debug.h" #include "utils.h" #include #include StandardBackendUpdater::StandardBackendUpdater(AbstractResourcesBackend* parent) : AbstractBackendUpdater(parent) , m_backend(parent) , m_settingUp(false) , m_progress(0) , m_lastUpdate(QDateTime()) { connect(m_backend, &AbstractResourcesBackend::fetchingChanged, this, &StandardBackendUpdater::refreshUpdateable); connect(m_backend, &AbstractResourcesBackend::resourcesChanged, this, &StandardBackendUpdater::resourcesChanged); connect(m_backend, &AbstractResourcesBackend::resourceRemoved, this, [this](AbstractResource* resource){ m_upgradeable.remove(resource); m_toUpgrade.remove(resource); }); connect(TransactionModel::global(), &TransactionModel::transactionRemoved, this, &StandardBackendUpdater::transactionRemoved); connect(TransactionModel::global(), &TransactionModel::transactionAdded, this, &StandardBackendUpdater::transactionAdded); m_timer.setSingleShot(true); m_timer.setInterval(10); connect(&m_timer, &QTimer::timeout, this, &StandardBackendUpdater::refreshUpdateable); } void StandardBackendUpdater::resourcesChanged(AbstractResource* res, const QVector& props) { if (props.contains("state") && (res->state() == AbstractResource::Upgradeable || m_upgradeable.contains(res))) m_timer.start(); } bool StandardBackendUpdater::hasUpdates() const { return !m_upgradeable.isEmpty(); } void StandardBackendUpdater::start() { m_settingUp = true; emit progressingChanged(true); setProgress(0); auto upgradeList = m_toUpgrade.values(); std::sort(upgradeList.begin(), upgradeList.end(), [](const AbstractResource* a, const AbstractResource* b){ return a->name() < b->name(); }); const bool couldCancel = m_canCancel; foreach(AbstractResource* res, upgradeList) { m_pendingResources += res; auto t = m_backend->installApplication(res); t->setVisible(false); t->setProperty("updater", QVariant::fromValue(this)); connect(t, &Transaction::downloadSpeedChanged, this, [this](){ Q_EMIT downloadSpeedChanged(downloadSpeed()); }); connect(this, &StandardBackendUpdater::cancelTransaction, t, &Transaction::cancel); TransactionModel::global()->addTransaction(t); m_canCancel |= t->isCancellable(); } if (m_canCancel != couldCancel) { Q_EMIT cancelableChanged(m_canCancel); } m_settingUp = false; if(m_pendingResources.isEmpty()) { cleanup(); } else { setProgress(1); } } void StandardBackendUpdater::cancel() { Q_EMIT cancelTransaction(); } void StandardBackendUpdater::transactionAdded(Transaction* newTransaction) { if (!m_pendingResources.contains(newTransaction->resource())) return; connect(newTransaction, &Transaction::progressChanged, this, &StandardBackendUpdater::transactionProgressChanged); connect(newTransaction, &Transaction::statusChanged, this, &StandardBackendUpdater::transactionProgressChanged); } AbstractBackendUpdater::State toUpdateState(Transaction* t) { switch(t->status()) { case Transaction::SetupStatus: case Transaction::QueuedStatus: return AbstractBackendUpdater::None; case Transaction::DownloadingStatus: return AbstractBackendUpdater::Downloading; case Transaction::CommittingStatus: return AbstractBackendUpdater::Installing; case Transaction::DoneStatus: case Transaction::DoneWithErrorStatus: case Transaction::CancelledStatus: return AbstractBackendUpdater::Done; } Q_UNREACHABLE(); } void StandardBackendUpdater::transactionProgressChanged() { Transaction* t = qobject_cast(sender()); Q_EMIT resourceProgressed(t->resource(), t->progress(), toUpdateState(t)); refreshProgress(); } void StandardBackendUpdater::transactionRemoved(Transaction* t) { const bool fromOurBackend = t->resource() && t->resource()->backend()==m_backend; if (!fromOurBackend) { return; } const bool found = fromOurBackend && m_pendingResources.remove(t->resource()); if(found && !m_settingUp) { refreshProgress(); if(m_pendingResources.isEmpty()) { cleanup(); } } refreshUpdateable(); } void StandardBackendUpdater::refreshProgress() { int allProgresses = (m_toUpgrade.size() - m_pendingResources.size()) * 100; for (auto t: transactions()) { allProgresses += t->progress(); } setProgress(allProgresses / m_toUpgrade.size()); } void StandardBackendUpdater::refreshUpdateable() { if (m_backend->isFetching() || !m_backend->isValid()) { return; } if (isProgressing()) { m_timer.start(1000); return; } m_settingUp = true; Q_EMIT progressingChanged(true); AbstractResourcesBackend::Filters f; f.state = AbstractResource::Upgradeable; m_upgradeable.clear(); auto r = m_backend->search(f); connect(r, &ResultsStream::resourcesFound, this, [this](const QVector &resources){ for(auto res : resources) if (res->state() == AbstractResource::Upgradeable) m_upgradeable.insert(res); }); connect(r, &ResultsStream::destroyed, this, [this](){ m_settingUp = false; Q_EMIT updatesCountChanged(updatesCount()); Q_EMIT progressingChanged(false); }); } qreal StandardBackendUpdater::progress() const { return m_progress; } void StandardBackendUpdater::setProgress(qreal p) { if(p>m_progress || p<0) { m_progress = p; emit progressChanged(p); } } void StandardBackendUpdater::prepare() { m_lastUpdate = QDateTime::currentDateTime(); m_toUpgrade = m_upgradeable; } int StandardBackendUpdater::updatesCount() const { return m_upgradeable.count(); } void StandardBackendUpdater::addResources(const QList< AbstractResource* >& apps) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - const QSet upgradeableApps(apps.begin(), apps.end()); -#else - const QSet upgradeableApps = apps.toSet(); -#endif + const QSet upgradeableApps = kToSet(apps); Q_ASSERT(m_upgradeable.contains(upgradeableApps)); m_toUpgrade += upgradeableApps; } void StandardBackendUpdater::removeResources(const QList< AbstractResource* >& apps) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - const QSet upgradeableApps(apps.begin(), apps.end()); -#else - const QSet upgradeableApps = apps.toSet(); -#endif + const QSet upgradeableApps = kToSet(apps); Q_ASSERT(m_upgradeable.contains(upgradeableApps)); Q_ASSERT(m_toUpgrade.contains(upgradeableApps)); m_toUpgrade -= upgradeableApps; } void StandardBackendUpdater::cleanup() { m_lastUpdate = QDateTime::currentDateTime(); m_toUpgrade.clear(); refreshUpdateable(); emit progressingChanged(false); } QList StandardBackendUpdater::toUpdate() const { return m_toUpgrade.values(); } bool StandardBackendUpdater::isMarked(AbstractResource* res) const { return m_toUpgrade.contains(res); } QDateTime StandardBackendUpdater::lastUpdate() const { return m_lastUpdate; } bool StandardBackendUpdater::isCancelable() const { return m_canCancel; } bool StandardBackendUpdater::isProgressing() const { return m_settingUp || !m_pendingResources.isEmpty(); } double StandardBackendUpdater::updateSize() const { double ret = 0.; for(AbstractResource* res: m_toUpgrade) { ret += res->size(); } return ret; } QVector StandardBackendUpdater::transactions() const { const auto trans = TransactionModel::global()->transactions(); return kFilter>(trans, [this](Transaction* t) { return t->property("updater").value() == this; }); } quint64 StandardBackendUpdater::downloadSpeed() const { quint64 ret = 0; const auto trans = transactions(); for(Transaction* t: trans) { ret += t->downloadSpeed(); } return ret; } diff --git a/libdiscover/utils.h b/libdiscover/utils.h index f2be0ad5..58fd6843 100644 --- a/libdiscover/utils.h +++ b/libdiscover/utils.h @@ -1,120 +1,134 @@ /*************************************************************************** * Copyright ?? 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef UTILS_H #define UTILS_H #include #include #include class OneTimeAction : public QObject { public: OneTimeAction(const std::function &func, QObject* parent) : QObject(parent), m_function(func) {} void trigger() { m_function(); deleteLater(); } private: std::function m_function; }; template static T kTransform(const Q &input, _UnaryOperation op) { T ret; ret.reserve(input.size()); for(const auto& v : input) { ret += op(v); } return ret; } template static T kFilter(const Q &input, _UnaryOperation op) { T ret; for(const auto& v : input) { if (op(v)) ret += v; } return ret; } template static int kIndexOf(const Q& list, W func) { int i = 0; for (auto it = list.constBegin(), itEnd = list.constEnd(); it!=itEnd; ++it) { if (func(*it)) return i; ++i; } return -1; } template static bool kContains(const Q& list, W func) { return kIndexOf(list, func) != -1; } template static QVector kSetToVector(const QSet & set) { QVector ret; ret.reserve(set.size()); for(auto &x: set) ret.append(x); return ret; } template -static QSet kVectorToSet(const QVector & set) +static QSet kToSet(const QVector & set) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + return QSet(set.begin(), set.end()); +#else QSet ret; ret.reserve(set.size()); for(auto &x: set) ret.insert(x); return ret; +#endif +} + +template +static QSet kToSet(const QList & set) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + return QSet(set.begin(), set.end()); +#else + return set.toSet(); +#endif } class ElapsedDebug : private QElapsedTimer { public: ElapsedDebug(const QString &name = QStringLiteral("")) : m_name(name) { start(); } ~ElapsedDebug() { qDebug("elapsed %s: %lld!", m_name.toUtf8().constData(), elapsed()); } void step(const QString &step) { qDebug("step %s(%s): %lld!", m_name.toUtf8().constData(), qPrintable(step), elapsed()); } QString m_name; }; class CallOnDestroy : public QObject { public: CallOnDestroy(std::function f) : m_func(std::move(f)) {} ~CallOnDestroy() { m_func(); } private: std::function m_func; }; #endif