File indexing completed on 2024-05-19 13:35:27
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 }