File indexing completed on 2024-04-28 05:46:37

0001 /***************************************************************************
0002  *   Copyright (C) 2015 Lukáš Tinkl <lukas@kde.org>                        *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  *                                                                         *
0009  *   This program is distributed in the hope that it will be useful,       *
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0012  *   GNU General Public License for more details.                          *
0013  *                                                                         *
0014  *   You should have received a copy of the GNU General Public License     *
0015  *   along with this program; see the file COPYING. If not, write to       *
0016  *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,  *
0017  *   Boston, MA 02110-1301, USA.                                           *
0018  ***************************************************************************/
0019 
0020 #include <limits.h>
0021 
0022 #include <QDebug>
0023 #include <QTimer>
0024 #include <QDBusReply>
0025 #include <QDBusInterface>
0026 
0027 #include <KLocalizedString>
0028 #include <KFormat>
0029 #include <KNotification>
0030 #include <Solid/Power>
0031 #include <Solid/AcPluggedJob>
0032 #include <KConfigGroup>
0033 #include <KSharedConfig>
0034 
0035 #include "pkupdates.h"
0036 #include "PkStrings.h"
0037 
0038 Q_LOGGING_CATEGORY(PLASMA_PK_UPDATES, "plasma-pk-updates")
0039 
0040 namespace
0041 {
0042     const auto s_pkUpdatesIconName = QStringLiteral("system-software-update");
0043     const auto s_componentName = QStringLiteral("plasma_pk_updates");
0044     const auto s_eventIdUpdatesAvailable = QStringLiteral("updatesAvailable");
0045     const auto s_eventIdUpdatesInstalled = QStringLiteral("updatesInstalled");
0046     const auto s_eventIdRestartRequired = QStringLiteral("restartRequired");
0047     const auto s_eventIdError = QStringLiteral("updateError");
0048 } // namespace {
0049 
0050 PkUpdates::PkUpdates(QObject *parent) :
0051     QObject(parent),
0052     m_isOnBattery(true)
0053 {
0054     setStatusMessage(i18n("Idle"));
0055 
0056     connect(PackageKit::Daemon::global(), &PackageKit::Daemon::changed, this, &PkUpdates::onChanged);
0057     connect(PackageKit::Daemon::global(), &PackageKit::Daemon::updatesChanged, this, &PkUpdates::onUpdatesChanged);
0058     connect(PackageKit::Daemon::global(), &PackageKit::Daemon::networkStateChanged, this, &PkUpdates::networkStateChanged);
0059     connect(Solid::Power::self(), &Solid::Power::resumeFromSuspend, this,
0060             [this] {PackageKit::Daemon::stateHasChanged(QStringLiteral("resume"));});
0061 
0062     connect(Solid::Power::self(), &Solid::Power::acPluggedChanged, this, [this] (bool acPlugged) {
0063             qCDebug(PLASMA_PK_UPDATES) << "acPluggedChanged onBattery:" << m_isOnBattery << "->" << !acPlugged;
0064             if (!acPlugged != m_isOnBattery) {
0065                 m_isOnBattery = !acPlugged;
0066                 emit PkUpdates::isOnBatteryChanged();
0067             }
0068     });
0069     auto acPluggedJob = Solid::Power::self()->isAcPlugged(this);
0070     connect(acPluggedJob , &Solid::Job::result, this, [this] (Solid::Job* job) {
0071         bool acPlugged = static_cast<Solid::AcPluggedJob*>(job)->isPlugged();
0072         qCDebug(PLASMA_PK_UPDATES) << "acPlugged initial state" << acPlugged;
0073         m_isOnBattery = !acPlugged;
0074         emit PkUpdates::isOnBatteryChanged();
0075     });
0076     acPluggedJob->start();
0077 
0078     connect(PackageKit::Daemon::global(), &PackageKit::Daemon::networkStateChanged, this, &PkUpdates::doDelayedCheckUpdates);
0079     connect(this, &PkUpdates::isActiveChanged, this, &PkUpdates::messageChanged);
0080     connect(this, &PkUpdates::networkStateChanged, this, &PkUpdates::messageChanged);
0081 }
0082 
0083 PkUpdates::~PkUpdates()
0084 {
0085     if (m_cacheTrans) {
0086         if (m_cacheTrans->allowCancel())
0087             m_cacheTrans->cancel();
0088         m_cacheTrans->deleteLater();
0089     }
0090     if (m_updatesTrans) {
0091         if (m_updatesTrans->allowCancel())
0092             m_updatesTrans->cancel();
0093         m_updatesTrans->deleteLater();
0094     }
0095     if (m_installTrans) {
0096         m_installTrans->deleteLater();
0097     }
0098     if (m_detailTrans) {
0099         m_detailTrans->deleteLater();
0100     }
0101 }
0102 
0103 int PkUpdates::count() const
0104 {
0105     return m_updateList.count();
0106 }
0107 
0108 int PkUpdates::importantCount() const
0109 {
0110     return m_importantList.count();
0111 }
0112 
0113 int PkUpdates::securityCount() const
0114 {
0115     return m_securityList.count();
0116 }
0117 
0118 bool PkUpdates::isSystemUpToDate() const
0119 {
0120     return m_updateList.isEmpty();
0121 }
0122 
0123 QString PkUpdates::iconName() const
0124 {
0125     if (securityCount() > 0) {
0126         return QStringLiteral("update-high");
0127     } else if (importantCount() > 0) {
0128         return QStringLiteral("update-medium");
0129     } else if (count() > 0) {
0130         return QStringLiteral("update-low");
0131     } else {
0132         return QStringLiteral("update-none");
0133     }
0134 }
0135 
0136 QString PkUpdates::message() const
0137 {
0138     if (isActive()) {
0139         if (m_activity == CheckingUpdates)
0140             return i18n("Checking updates");
0141         else if (m_activity == GettingUpdates)
0142             return i18n("Getting updates");
0143         else if (m_activity == InstallingUpdates)
0144             return i18n("Installing updates");
0145 
0146         return i18n("Working");
0147     } else if (!isSystemUpToDate()) {
0148         QStringList extra;
0149         const QString msg = i18np("You have 1 new update", "You have %1 new updates", count());
0150         if (securityCount() > 0)
0151             extra += i18np("1 security update", "%1 security updates", securityCount());
0152         if (importantCount() > 0)
0153             extra += i18np("1 important update", "%1 important updates", importantCount());
0154 
0155         if (extra.isEmpty())
0156             return msg;
0157         else
0158             return msg + "\n" + i18n("(including %1)", extra.join(i18n(" and ")));
0159     } else if (!isNetworkOnline()) {
0160         return i18n("Your system is offline");
0161     }
0162 
0163     switch (m_lastCheckState) {
0164     case NoCheckDone:
0165         return i18n("Not checked for updates yet");
0166     default:
0167     case CheckFailed:
0168         return i18n("Checking for updates failed");
0169     case CheckSucceeded:
0170         return i18n("Your system is up to date");
0171     }
0172 }
0173 
0174 int PkUpdates::percentage() const
0175 {
0176     return m_percentage;
0177 }
0178 
0179 QString PkUpdates::statusMessage() const
0180 {
0181     return m_statusMessage;
0182 }
0183 
0184 bool PkUpdates::isActive() const
0185 {
0186     return m_activity != Idle;
0187 }
0188 
0189 QVariantMap PkUpdates::packages() const
0190 {
0191     return m_updateList;
0192 }
0193 
0194 bool PkUpdates::isNetworkOnline() const
0195 {
0196     qCDebug(PLASMA_PK_UPDATES) << "Is net online:" << (PackageKit::Daemon::networkState() > PackageKit::Daemon::Network::NetworkOffline);
0197     return (PackageKit::Daemon::networkState() > PackageKit::Daemon::Network::NetworkOffline);
0198 }
0199 
0200 void PkUpdates::doDelayedCheckUpdates()
0201 {
0202     if (m_checkUpdatesWhenNetworkOnline && isNetworkOnline())
0203     {
0204         qCDebug(PLASMA_PK_UPDATES) << "CheckUpdates was delayed. Doing it now";
0205         m_checkUpdatesWhenNetworkOnline = false;
0206         checkUpdates(m_isManualCheck /* manual */);
0207     }
0208 }
0209 
0210 bool PkUpdates::isNetworkMobile() const
0211 {
0212     qCDebug(PLASMA_PK_UPDATES) << "Is net mobile:" << (PackageKit::Daemon::networkState() == PackageKit::Daemon::Network::NetworkMobile);
0213     return (PackageKit::Daemon::networkState() == PackageKit::Daemon::Network::NetworkMobile);
0214 }
0215 
0216 bool PkUpdates::isOnBattery() const
0217 {
0218     qCDebug(PLASMA_PK_UPDATES) << "Is on battery:" << m_isOnBattery;
0219     return m_isOnBattery;
0220 }
0221 
0222 bool PkUpdates::lastCheckSuccessful() const
0223 {
0224     const bool ret = m_lastCheckState == CheckSucceeded;
0225     qCDebug(PLASMA_PK_UPDATES) << "Last check successful:" << ret;
0226     return ret;
0227 }
0228 
0229 void PkUpdates::getUpdateDetails(const QString &pkgID)
0230 {
0231     qCDebug(PLASMA_PK_UPDATES) << "Requesting update details for" << pkgID;
0232     m_detailTrans = PackageKit::Daemon::getUpdateDetail(pkgID);
0233     connect(m_detailTrans.data(), &PackageKit::Transaction::updateDetail, this, &PkUpdates::onUpdateDetail);
0234 }
0235 
0236 QString PkUpdates::timestamp() const
0237 {
0238     const qint64 lastCheck = QDateTime::currentMSecsSinceEpoch() - lastRefreshTimestamp();
0239 
0240     if (lastCheck != -1)
0241         return i18n("Last check: %1 ago", KFormat().formatSpelloutDuration(lastCheck));
0242 
0243     return i18n("Last check: never");
0244 }
0245 
0246 void PkUpdates::checkUpdates(bool manual)
0247 {
0248     m_isManualCheck = manual;
0249 
0250     if (!isNetworkOnline())
0251     {
0252         qCDebug(PLASMA_PK_UPDATES) << "Checking updates delayed. Network is offline";
0253         m_checkUpdatesWhenNetworkOnline = true;
0254         return;
0255     }
0256     qCDebug(PLASMA_PK_UPDATES) << "Checking for updates";
0257 
0258     // ask the Packagekit daemon to refresh the cache
0259     m_cacheTrans = PackageKit::Daemon::refreshCache(false);
0260     setActivity(CheckingUpdates);
0261 
0262     // evaluate the result
0263     connect(m_cacheTrans.data(), &PackageKit::Transaction::statusChanged, this, &PkUpdates::onStatusChanged);
0264     connect(m_cacheTrans.data(), &PackageKit::Transaction::finished, this, &PkUpdates::onFinished);
0265     connect(m_cacheTrans.data(), &PackageKit::Transaction::errorCode, this, &PkUpdates::onRefreshErrorCode);
0266     connect(m_cacheTrans.data(), &PackageKit::Transaction::requireRestart, this, &PkUpdates::onRequireRestart);
0267     connect(m_cacheTrans.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PkUpdates::onRepoSignatureRequired);
0268 }
0269 
0270 qint64 PkUpdates::lastRefreshTimestamp() const
0271 {
0272     KConfigGroup grp(KSharedConfig::openConfig("plasma-pk-updates"), "General");
0273     return grp.readEntry<qint64>("Timestamp", -1);
0274 }
0275 
0276 QString PkUpdates::packageName(const QString &pkgId)
0277 {
0278     return PackageKit::Daemon::packageName(pkgId);
0279 }
0280 
0281 QString PkUpdates::packageVersion(const QString &pkgId)
0282 {
0283     return PackageKit::Daemon::packageVersion(pkgId);
0284 }
0285 
0286 void PkUpdates::getUpdates()
0287 {
0288     m_updatesTrans = PackageKit::Daemon::getUpdates();
0289     setActivity(GettingUpdates);
0290 
0291     m_updateList.clear();
0292     m_importantList.clear();
0293     m_securityList.clear();
0294 
0295     connect(m_updatesTrans.data(), &PackageKit::Transaction::statusChanged, this, &PkUpdates::onStatusChanged);
0296     connect(m_updatesTrans.data(), &PackageKit::Transaction::finished, this, &PkUpdates::onFinished);
0297     connect(m_updatesTrans.data(), &PackageKit::Transaction::errorCode, this, &PkUpdates::onErrorCode);
0298     connect(m_updatesTrans.data(), &PackageKit::Transaction::package, this, &PkUpdates::onPackage);
0299     connect(m_updatesTrans.data(), &PackageKit::Transaction::requireRestart, this, &PkUpdates::onRequireRestart);
0300     connect(m_updatesTrans.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PkUpdates::onRepoSignatureRequired);
0301 }
0302 
0303 void PkUpdates::installUpdates(const QStringList &packageIds, bool simulate, bool untrusted)
0304 {
0305     qCDebug(PLASMA_PK_UPDATES) << "Installing updates" << packageIds << ", simulate:" << simulate << ", untrusted:" << untrusted;
0306 
0307     PackageKit::Transaction::TransactionFlags flags = PackageKit::Transaction::TransactionFlagOnlyTrusted;
0308     if (simulate) {
0309         flags |= PackageKit::Transaction::TransactionFlagSimulate;
0310     } else if (untrusted) {
0311         flags = PackageKit::Transaction::TransactionFlagNone;
0312     }
0313 
0314     m_restartType = PackageKit::Transaction::RestartNone;
0315     m_requiredEulas.clear();
0316     m_packages = packageIds;
0317     m_installTrans = PackageKit::Daemon::updatePackages(m_packages, flags);
0318     setActivity(InstallingUpdates);
0319 
0320     connect(m_installTrans.data(), &PackageKit::Transaction::statusChanged, this, &PkUpdates::onStatusChanged);
0321     connect(m_installTrans.data(), &PackageKit::Transaction::finished, this, &PkUpdates::onFinished);
0322     connect(m_installTrans.data(), &PackageKit::Transaction::errorCode, this, &PkUpdates::onErrorCode);
0323     connect(m_installTrans.data(), &PackageKit::Transaction::package, this, &PkUpdates::onPackageUpdating);
0324     connect(m_installTrans.data(), &PackageKit::Transaction::requireRestart, this, &PkUpdates::onRequireRestart);
0325     connect(m_installTrans.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PkUpdates::onRepoSignatureRequired);
0326     connect(m_installTrans.data(), &PackageKit::Transaction::eulaRequired, this, &PkUpdates::onEulaRequired);
0327 }
0328 
0329 void PkUpdates::onChanged()
0330 {
0331     qCDebug(PLASMA_PK_UPDATES) << "Daemon changed";
0332 }
0333 
0334 void PkUpdates::onUpdatesChanged()
0335 {
0336     qCDebug(PLASMA_PK_UPDATES) << "Updates changed, getting updates!";
0337     getUpdates();
0338 }
0339 
0340 void PkUpdates::onStatusChanged()
0341 {
0342     PackageKit::Transaction * trans;
0343     if ((trans = qobject_cast<PackageKit::Transaction *>(sender()))) {
0344         qCDebug(PLASMA_PK_UPDATES) << "Transaction status changed:"
0345                  << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)trans->status(), "Status")
0346                  << QStringLiteral("(%1%)").arg(trans->percentage());
0347         if (trans->status() == PackageKit::Transaction::StatusFinished)
0348             return;
0349         setStatusMessage(PkStrings::status(trans->status(), trans->speed(), trans->downloadSizeRemaining()));
0350         setPercentage(trans->percentage());
0351     }
0352 }
0353 
0354 void PkUpdates::onPackage(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary)
0355 {
0356     qCDebug(PLASMA_PK_UPDATES) << "Got update package:" << packageID << ", summary:" << summary <<
0357                 ", type:" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)info, "Info");
0358 
0359     switch (info) {
0360     case PackageKit::Transaction::InfoBlocked:
0361         // Blocked updates are not installable updates so there is no
0362         // reason to show/count them
0363         return;
0364     case PackageKit::Transaction::InfoImportant:
0365         m_importantList << packageID;
0366         break;
0367     case PackageKit::Transaction::InfoSecurity:
0368         m_securityList << packageID;
0369         break;
0370     default:
0371         break;
0372     }
0373     m_updateList.insert(packageID, summary);
0374 }
0375 
0376 void PkUpdates::onPackageUpdating(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary)
0377 {
0378     Q_UNUSED(summary)
0379     qCDebug(PLASMA_PK_UPDATES) << "Package updating:" << packageID <<
0380                 ", info:" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)info, "Info");
0381 
0382     const uint percent = m_installTrans->percentage();
0383 
0384     if (percent <= 100) {
0385         setStatusMessage(i18nc("1 installation status, 2 pkg name, 3 percentage", "%1 %2 (%3%)",
0386                                PkStrings::infoPresent(info), PackageKit::Daemon::packageName(packageID), percent));
0387     } else {
0388         setStatusMessage(i18nc("1 installation status, 2 pkg name", "%1 %2",
0389                                PkStrings::infoPresent(info), PackageKit::Daemon::packageName(packageID), percent));
0390     }
0391 
0392     setPercentage(percent);
0393 }
0394 
0395 void PkUpdates::onFinished(PackageKit::Transaction::Exit status, uint runtime)
0396 {
0397     PackageKit::Transaction * trans = qobject_cast<PackageKit::Transaction *>(sender());
0398     if (!trans)
0399         return;
0400 
0401     trans->deleteLater();
0402 
0403     qCDebug(PLASMA_PK_UPDATES) << "Transaction" << trans->tid().path() <<
0404                 "finished with status" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)status, "Exit") <<
0405                 "in" << runtime/1000 << "seconds";
0406 
0407     auto oldLastCheckState = m_lastCheckState;
0408     if (trans->role() == PackageKit::Transaction::RoleRefreshCache) {
0409         m_lastCheckState = status == PackageKit::Transaction::ExitSuccess ? CheckSucceeded : CheckFailed;
0410 
0411         if (m_lastCheckState == CheckSucceeded) {
0412             qCDebug(PLASMA_PK_UPDATES) << "Cache transaction finished successfully";
0413 
0414             // save the timestamp
0415             KConfigGroup grp(KSharedConfig::openConfig("plasma-pk-updates"), "General");
0416             grp.writeEntry("Timestamp", QDateTime::currentDateTime().toMSecsSinceEpoch());
0417             grp.writeEntry("FailedAutoRefeshCount", 0);
0418             grp.sync();
0419 
0420             return;
0421         } else {
0422             qCDebug(PLASMA_PK_UPDATES) << "Cache transaction didn't finish successfully";
0423             emit done();
0424         }
0425     } else if (trans->role() == PackageKit::Transaction::RoleGetUpdates) {
0426         m_lastCheckState = status == PackageKit::Transaction::ExitSuccess ? CheckSucceeded : CheckFailed;
0427 
0428         if (m_lastCheckState == CheckSucceeded) {
0429             qCDebug(PLASMA_PK_UPDATES) << "Check updates transaction finished successfully";
0430             const int upCount = count();
0431             if (upCount != m_lastUpdateCount && m_lastNotification) {
0432                 qCDebug(PLASMA_PK_UPDATES) << "Disposing old update count notification";
0433                 m_lastNotification->close();
0434             }
0435             if (upCount > 0 && upCount != m_lastUpdateCount) {
0436                 m_lastUpdateCount = upCount;
0437                 m_lastNotification = KNotification::event(s_eventIdUpdatesAvailable,
0438                                      QString(),
0439                                      i18np("You have 1 new update", "You have %1 new updates", upCount),
0440                                      s_pkUpdatesIconName, nullptr, KNotification::Persistent,
0441                                      s_componentName);
0442                 connect(m_lastNotification, &KNotification::closed, this, [this] {
0443                     qCDebug(PLASMA_PK_UPDATES) << "Old notification closed";
0444                     m_lastNotification = nullptr;
0445                     m_lastUpdateCount = 0;
0446                 });
0447             }
0448         } else {
0449             qCDebug(PLASMA_PK_UPDATES) << "Check updates transaction didn't finish successfully";
0450         }
0451         qCDebug(PLASMA_PK_UPDATES) << "Total number of updates: " << count();
0452         emit done();
0453     } else if (trans->role() == PackageKit::Transaction::RoleUpdatePackages) {
0454         qCDebug(PLASMA_PK_UPDATES) << "Finished updating packages:" << m_packages;
0455         if (status == PackageKit::Transaction::ExitNeedUntrusted) {
0456             qCDebug(PLASMA_PK_UPDATES) << "Transaction needs untrusted packages";
0457             // restart transaction with "untrusted" flag
0458             installUpdates(m_packages, false /*simulate*/, true /*untrusted*/);
0459             return;
0460         } else if (status == PackageKit::Transaction::ExitEulaRequired) {
0461             qCDebug(PLASMA_PK_UPDATES) << "Acceptance of EULAs required";
0462             promptNextEulaAgreement();
0463             return;
0464         } else if (status == PackageKit::Transaction::ExitSuccess && trans->transactionFlags().testFlag(PackageKit::Transaction::TransactionFlagSimulate)) {
0465             qCDebug(PLASMA_PK_UPDATES) << "Simulation finished with success, restarting the transaction";
0466             installUpdates(m_packages, false /*simulate*/, false /*untrusted*/);
0467             return;
0468         } else if (status == PackageKit::Transaction::ExitSuccess) {
0469             qCDebug(PLASMA_PK_UPDATES) << "Update packages transaction finished successfully";
0470             if (m_lastNotification) {
0471                 m_lastNotification->close();
0472             }
0473             KNotification::event(s_eventIdUpdatesInstalled,
0474                                  i18n("Updates Installed"),
0475                                  i18np("Successfully updated %1 package", "Successfully updated %1 packages", m_packages.count()),
0476                                  s_pkUpdatesIconName, nullptr,
0477                                  KNotification::CloseOnTimeout,
0478                                  s_componentName);
0479             showRestartNotification();
0480             emit updatesInstalled();
0481         } else {
0482             qCDebug(PLASMA_PK_UPDATES) << "Update packages transaction didn't finish successfully";
0483             // just try to refresh cache in case of error, the user might have installed the updates manually meanwhile
0484             checkUpdates(false /* manual */);
0485             return;
0486         }
0487         setActivity(Idle);
0488         return;
0489     } else {
0490         qCDebug(PLASMA_PK_UPDATES) << "Unhandled transaction type:" << PackageKit::Daemon::enumToString<PackageKit::Transaction>(trans->role(), "Role");
0491         setActivity(Idle);
0492         return;
0493     }
0494 
0495     if (oldLastCheckState != m_lastCheckState) {
0496         emit lastCheckSuccessfulChanged();
0497     }
0498 
0499     setActivity(Idle);
0500     emit updatesChanged();
0501 }
0502 
0503 void PkUpdates::onErrorCode(PackageKit::Transaction::Error error, const QString &details)
0504 {
0505     showError(error, details);
0506 }
0507 
0508 void PkUpdates::onRefreshErrorCode(PackageKit::Transaction::Error error, const QString &details)
0509 {
0510     if(!m_isManualCheck) {
0511         auto isTransientError = [] (PackageKit::Transaction::Error error) {
0512             return (error == PackageKit::Transaction::ErrorFailedInitialization) ||
0513                    (error == PackageKit::Transaction::ErrorNotAuthorized) ||
0514                    (error == PackageKit::Transaction::ErrorNoNetwork) ||
0515                    (error == PackageKit::Transaction::ErrorCannotGetLock);
0516         };
0517 
0518         KConfigGroup grp(KSharedConfig::openConfig("plasma-pk-updates"), "General");
0519         auto failCount = grp.readEntry<qint64>("FailedAutoRefeshCount", 0);
0520         failCount += 1;
0521         grp.writeEntry("FailedAutoRefeshCount", failCount);
0522         grp.sync();
0523 
0524         if(failCount <= 1 && isTransientError(error)) {
0525             qDebug(PLASMA_PK_UPDATES) << "Ignoring notification for likely transient error during automatic check";
0526             return;
0527         }
0528     }
0529 
0530     showError(error, details);
0531 }
0532 
0533 void PkUpdates::showError(PackageKit::Transaction::Error error, const QString &details)
0534 {
0535     qWarning() << "PK error:" << details << "type:" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)error, "Error");
0536     if (error == PackageKit::Transaction::ErrorBadGpgSignature || error == PackageKit::Transaction::ErrorNoLicenseAgreement)
0537         return;
0538 
0539     KNotification::event(s_eventIdError, i18n("Update Error"),
0540                          details,
0541                          s_pkUpdatesIconName, nullptr,
0542                          KNotification::Persistent,
0543                          s_componentName);
0544 }
0545 
0546 void PkUpdates::onRequireRestart(PackageKit::Transaction::Restart type, const QString &packageID)
0547 {
0548     PackageKit::Transaction * trans = qobject_cast<PackageKit::Transaction *>(sender());
0549     if (!trans)
0550         return;
0551 
0552     qCDebug(PLASMA_PK_UPDATES) << "RESTART" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)type, "Restart")
0553              << "is required for package" << packageID;
0554 
0555     // Only replace if the new type has a higher place in the list than the current one.
0556     static const PackageKit::Transaction::Restart restart_order[] = {
0557         PackageKit::Transaction::RestartNone,
0558         PackageKit::Transaction::RestartApplication,
0559         PackageKit::Transaction::RestartSession,
0560         PackageKit::Transaction::RestartSecuritySession,
0561         PackageKit::Transaction::RestartSystem,
0562         PackageKit::Transaction::RestartSecuritySystem,
0563     };
0564 
0565     auto prio_current = std::find(std::begin(restart_order), std::end(restart_order), m_restartType),
0566          prio_new = std::find(std::begin(restart_order), std::end(restart_order), type);
0567 
0568     if(prio_new == std::end(restart_order)) {
0569         qWarning() << "Unknown/unhandled restart type" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)type, "Restart");
0570         return;
0571     }
0572 
0573     // Compare the priority
0574     if(std::distance(prio_current, prio_new) <= 0)
0575         return;
0576 
0577     m_restartType = type;
0578     m_restartPackageID = packageID;
0579 }
0580 
0581 void PkUpdates::showRestartNotification()
0582 {
0583     KNotification *notification = nullptr;
0584     bool reboot = false;
0585 
0586     switch(m_restartType)
0587     {
0588     case PackageKit::Transaction::RestartSystem:
0589     case PackageKit::Transaction::RestartSecuritySystem:
0590         notification = new KNotification(s_eventIdRestartRequired, KNotification::Persistent);
0591 
0592         notification->setActions(QStringList{QLatin1String("Restart")});
0593         notification->setTitle(i18n("Restart is required"));
0594         notification->setText(i18n("The computer will have to be restarted after the update for the changes to take effect."));
0595 
0596         reboot = true;
0597         break;
0598     case PackageKit::Transaction::RestartSession:
0599     case PackageKit::Transaction::RestartSecuritySession:
0600         notification = new KNotification(s_eventIdRestartRequired, KNotification::Persistent);
0601 
0602         notification->setActions(QStringList{QLatin1String("Logout")});
0603         notification->setTitle(i18n("Session restart is required"));
0604         notification->setText(i18n("You will need to log out and back in after the update for the changes to take effect."));
0605         break;
0606     case PackageKit::Transaction::RestartNone:
0607     case PackageKit::Transaction::RestartApplication:
0608         return;
0609     default:
0610         qWarning() << "Unknown/unhandled restart type" << PackageKit::Daemon::enumToString<PackageKit::Transaction>((int)m_restartType, "Restart");
0611         return;
0612     }
0613 
0614     notification->setComponentName(s_componentName);
0615     notification->setIconName(s_pkUpdatesIconName);
0616     connect(notification, &KNotification::action1Activated, this, [reboot] () {
0617         QDBusInterface interface("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface", QDBusConnection::sessionBus());
0618         if (reboot) {
0619             interface.asyncCall("logout", 0, 1, 2); // Options: do not ask again | reboot | force
0620         } else {
0621             interface.asyncCall("logout", 0, 0, 2); // Options: do not ask again | logout | force
0622         }
0623     });
0624 
0625     notification->sendEvent();
0626 }
0627 
0628 void PkUpdates::onUpdateDetail(const QString &packageID, const QStringList &updates, const QStringList &obsoletes,
0629                                const QStringList &vendorUrls, const QStringList &bugzillaUrls, const QStringList &cveUrls,
0630                                PackageKit::Transaction::Restart restart, const QString &updateText, const QString &changelog,
0631                                PackageKit::Transaction::UpdateState state, const QDateTime &issued, const QDateTime &updated)
0632 {
0633     Q_UNUSED(updates);
0634     Q_UNUSED(obsoletes);
0635     Q_UNUSED(vendorUrls);
0636     Q_UNUSED(cveUrls);
0637     Q_UNUSED(restart);
0638     Q_UNUSED(changelog);
0639     Q_UNUSED(state);
0640     Q_UNUSED(issued);
0641     Q_UNUSED(updated);
0642 
0643     qCDebug(PLASMA_PK_UPDATES) << "Got update details for" << packageID;
0644 
0645     emit updateDetail(packageID, updateText, bugzillaUrls);
0646 }
0647 
0648 void PkUpdates::onRepoSignatureRequired(const QString &packageID, const QString &repoName, const QString &keyUrl, const QString &keyUserid,
0649                                         const QString &keyId, const QString &keyFingerprint, const QString &keyTimestamp,
0650                                         PackageKit::Transaction::SigType type)
0651 {
0652     Q_UNUSED(repoName);
0653     Q_UNUSED(keyUrl);
0654     Q_UNUSED(keyUserid);
0655     Q_UNUSED(keyId);
0656     Q_UNUSED(keyFingerprint);
0657     Q_UNUSED(keyTimestamp);
0658     Q_UNUSED(type);
0659 
0660     // TODO provide a way to confirm and import GPG keys
0661     qCDebug(PLASMA_PK_UPDATES) << "Repo sig required" << packageID;
0662 }
0663 
0664 void PkUpdates::onEulaRequired(const QString &eulaID, const QString &packageID, const QString &vendor, const QString &licenseAgreement)
0665 {
0666     m_requiredEulas[eulaID] = {packageID, vendor, licenseAgreement};
0667 }
0668 
0669 void PkUpdates::promptNextEulaAgreement()
0670 {
0671     if(m_requiredEulas.empty()) {
0672         // Restart the transaction
0673         installUpdates(m_packages, false, false);
0674         return;
0675     }
0676 
0677     QString eulaID = m_requiredEulas.firstKey();
0678     const EulaData &eula = m_requiredEulas[eulaID];
0679     emit eulaRequired(eulaID, eula.packageID, eula.vendor, eula.licenseAgreement);
0680 }
0681 
0682 void PkUpdates::eulaAgreementResult(const QString &eulaID, bool agreed)
0683 {
0684     if(!agreed) {
0685         qCDebug(PLASMA_PK_UPDATES) << "EULA declined";
0686         // Do the same as the failure case in onFinished
0687         checkUpdates(m_isManualCheck /* manual */);
0688         return;
0689     }
0690 
0691     m_eulaTrans = PackageKit::Daemon::acceptEula(eulaID);
0692     connect(m_eulaTrans.data(), &PackageKit::Transaction::finished, this,
0693             [this, eulaID] (PackageKit::Transaction::Exit exit, uint) {
0694                 if (exit == PackageKit::Transaction::ExitSuccess) {
0695                     m_requiredEulas.remove(eulaID);
0696                     promptNextEulaAgreement();
0697                 } else {
0698                     qCWarning(PLASMA_PK_UPDATES) << "EULA acceptance failed";
0699                 }
0700             }
0701     );
0702 }
0703 
0704 void PkUpdates::setStatusMessage(const QString &message)
0705 {
0706     m_statusMessage = message;
0707     emit statusMessageChanged();
0708 }
0709 
0710 void PkUpdates::setActivity(Activity act)
0711 {
0712     if (act != m_activity) {
0713         m_activity = act;
0714         emit isActiveChanged();
0715     }
0716 }
0717 
0718 void PkUpdates::setPercentage(int value)
0719 {
0720     if (value != m_percentage) {
0721         m_percentage = value;
0722         emit percentageChanged();
0723     }
0724 }