File indexing completed on 2024-04-14 15:48:52
0001 /*************************************************************************** 0002 * Copyright (C) 2008 by Trever Fischer * 0003 * wm161@wm161.net * 0004 * Copyright (C) 2008-2011 by Daniel Nicoletti * 0005 * dantti12@gmail.com * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify * 0008 * it under the terms of the GNU General Public License as published by * 0009 * the Free Software Foundation; either version 2 of the License, or * 0010 * (at your option) any later version. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, * 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0015 * GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License * 0018 * along with this program; see the file COPYING. If not, write to * 0019 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0020 * Boston, MA 02110-1301, USA. * 0021 ***************************************************************************/ 0022 0023 #include "Updater.h" 0024 0025 #include "ApperdThread.h" 0026 0027 #include <Daemon> 0028 0029 #include <PkStrings.h> 0030 #include <PkIcons.h> 0031 #include <Enum.h> 0032 0033 #include <QDBusServiceWatcher> 0034 #include <QDBusMessage> 0035 0036 #include <KLocalizedString> 0037 #include <KNotification> 0038 #include <KActionCollection> 0039 #include <KToolInvocation> 0040 0041 #include <QLoggingCategory> 0042 0043 Q_DECLARE_LOGGING_CATEGORY(APPER_DAEMON) 0044 0045 #define UPDATES_ICON "system-software-update" 0046 0047 using namespace PackageKit; 0048 0049 Updater::Updater(QObject* parent) : 0050 QObject(parent), 0051 m_getUpdatesT(nullptr) 0052 { 0053 // in case registration fails due to another user or application running 0054 // keep an eye on it so we can register when available 0055 auto watcher = new QDBusServiceWatcher(QLatin1String("org.kde.ApperUpdaterIcon"), 0056 QDBusConnection::sessionBus(), 0057 QDBusServiceWatcher::WatchForOwnerChange, 0058 this); 0059 connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &Updater::serviceOwnerChanged); 0060 0061 m_hasAppletIconified = ApperdThread::nameHasOwner(QLatin1String("org.kde.ApperUpdaterIcon"), 0062 QDBusConnection::sessionBus()); 0063 } 0064 0065 Updater::~Updater() 0066 { 0067 } 0068 0069 void Updater::setConfig(const QVariantHash &configs) 0070 { 0071 m_configs = configs; 0072 } 0073 0074 void Updater::setSystemReady() 0075 { 0076 // System ready changed, maybe we can auto 0077 // install some updates 0078 m_systemReady = true; 0079 getUpdateFinished(); 0080 } 0081 0082 void Updater::checkForUpdates(bool systemReady) 0083 { 0084 m_systemReady = systemReady; 0085 0086 // Skip the check if one is already running or 0087 // the plasmoid is in Icon form and the auto update type is None 0088 if (m_getUpdatesT) { 0089 return; 0090 } 0091 0092 m_updateList.clear(); 0093 m_importantList.clear(); 0094 m_securityList.clear(); 0095 m_getUpdatesT = Daemon::getUpdates(); 0096 connect(m_getUpdatesT, &Transaction::package, this, &Updater::packageToUpdate); 0097 connect(m_getUpdatesT, &Transaction::finished, this, &Updater::getUpdateFinished); 0098 } 0099 0100 void Updater::packageToUpdate(Transaction::Info info, const QString &packageID, const QString &summary) 0101 { 0102 Q_UNUSED(summary) 0103 0104 switch (info) { 0105 case Transaction::InfoBlocked: 0106 // Blocked updates are not instalable updates so there is no 0107 // reason to show/count them 0108 return; 0109 case Transaction::InfoImportant: 0110 m_importantList << packageID; 0111 break; 0112 case Transaction::InfoSecurity: 0113 m_securityList << packageID; 0114 break; 0115 default: 0116 break; 0117 } 0118 m_updateList << packageID; 0119 } 0120 0121 void Updater::getUpdateFinished() 0122 { 0123 m_getUpdatesT = nullptr; 0124 if (!m_updateList.isEmpty()) { 0125 auto transaction = qobject_cast<Transaction*>(sender()); 0126 0127 bool different = false; 0128 if (m_oldUpdateList.size() != m_updateList.size()) { 0129 different = true; 0130 } else { 0131 // The lists have the same size let's make sure 0132 // all the packages are the same 0133 const QStringList updates = m_updateList; 0134 for (const QString &packageId : updates) { 0135 if (!m_oldUpdateList.contains(packageId)) { 0136 different = true; 0137 break; 0138 } 0139 } 0140 } 0141 0142 // sender is not a transaction when we systemReady has changed 0143 // if the lists are the same don't show 0144 // a notification or try to upgrade again 0145 if (transaction && !different) { 0146 return; 0147 } 0148 0149 uint updateType = m_configs[QLatin1String(CFG_AUTO_UP)].value<uint>(); 0150 if (m_systemReady && updateType == Enum::All) { 0151 // update all 0152 bool ret; 0153 ret = updatePackages(m_updateList, 0154 false, 0155 QLatin1String("plasmagik"), 0156 i18n("Updates are being automatically installed.")); 0157 if (ret) { 0158 return; 0159 } 0160 } else if (m_systemReady && updateType == Enum::Security && !m_securityList.isEmpty()) { 0161 // Defaults to security 0162 bool ret; 0163 ret = updatePackages(m_securityList, 0164 false, 0165 QLatin1String(UPDATES_ICON), 0166 i18n("Security updates are being automatically installed.")); 0167 if (ret) { 0168 return; 0169 } 0170 } else if (m_systemReady && updateType == Enum::DownloadOnly) { 0171 // Download all updates 0172 bool ret; 0173 ret = updatePackages(m_updateList, 0174 true, 0175 QLatin1String("download"), 0176 i18n("Updates are being automatically downloaded.")); 0177 if (ret) { 0178 return; 0179 } 0180 } else if (!m_systemReady && 0181 (updateType == Enum::All || 0182 updateType == Enum::DownloadOnly || 0183 (updateType == Enum::Security && !m_securityList.isEmpty()))) { 0184 qCDebug(APPER_DAEMON) << "Not auto updating or downloading, as we might be on battery or mobile connection"; 0185 } 0186 0187 // If an erro happened to create the auto update 0188 // transaction show the update list 0189 if (transaction) { 0190 // The transaction is not valid if the systemReady changed 0191 showUpdatesPopup(); 0192 } 0193 } else { 0194 m_oldUpdateList.clear(); 0195 } 0196 } 0197 0198 void Updater::autoUpdatesFinished(PkTransaction::ExitStatus status) 0199 { 0200 auto notify = new KNotification(QLatin1String("UpdatesComplete")); 0201 notify->setComponentName(QLatin1String("apperd")); 0202 if (status == PkTransaction::Success) { 0203 if (sender()->property("DownloadOnly").toBool()) { 0204 // We finished downloading show the updates to the user 0205 showUpdatesPopup(); 0206 } else { 0207 QIcon icon = QIcon::fromTheme(QLatin1String("task-complete")); 0208 // use of QSize does the right thing 0209 notify->setPixmap(icon.pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE)); 0210 notify->setText(i18n("System update was successful.")); 0211 notify->sendEvent(); 0212 } 0213 } else { 0214 QIcon icon = QIcon::fromTheme(QLatin1String("dialog-cancel")); 0215 // use of QSize does the right thing 0216 notify->setPixmap(icon.pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE)); 0217 notify->setText(i18n("The software update failed.")); 0218 notify->sendEvent(); 0219 0220 // show updates popup 0221 showUpdatesPopup(); 0222 } 0223 } 0224 0225 void Updater::reviewUpdates() 0226 { 0227 if (m_hasAppletIconified) { 0228 QDBusMessage message; 0229 message = QDBusMessage::createMethodCall(QLatin1String("org.kde.ApperUpdaterIcon"), 0230 QLatin1String("/"), 0231 QLatin1String("org.kde.ApperUpdaterIcon"), 0232 QLatin1String("ReviewUpdates")); 0233 QDBusMessage reply = QDBusConnection::sessionBus().call(message); 0234 if (reply.type() == QDBusMessage::ReplyMessage) { 0235 return; 0236 } 0237 qCWarning(APPER_DAEMON) << "Message did not receive a reply"; 0238 } 0239 0240 // This must be called from the main thread... 0241 KToolInvocation::startServiceByDesktopName(QLatin1String("apper_updates")); 0242 } 0243 0244 void Updater::installUpdates() 0245 { 0246 bool ret; 0247 ret = updatePackages(m_updateList, false); 0248 if (ret) { 0249 return; 0250 } 0251 0252 reviewUpdates(); 0253 } 0254 0255 void Updater::serviceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner) 0256 { 0257 Q_UNUSED(service) 0258 Q_UNUSED(oldOwner) 0259 0260 m_hasAppletIconified = !newOwner.isEmpty(); 0261 } 0262 0263 void Updater::showUpdatesPopup() 0264 { 0265 m_oldUpdateList = m_updateList; 0266 0267 auto notify = new KNotification(QLatin1String("ShowUpdates"), nullptr, KNotification::Persistent); 0268 notify->setComponentName(QLatin1String("apperd")); 0269 connect(notify, &KNotification::action1Activated, this, &Updater::reviewUpdates); 0270 connect(notify, &KNotification::action2Activated, this, &Updater::installUpdates); 0271 notify->setTitle(i18np("There is one new update", "There are %1 new updates", m_updateList.size())); 0272 QString text; 0273 const QStringList updates = m_updateList; 0274 for (const QString &packageId : updates) { 0275 const QString packageName = Transaction::packageName(packageId); 0276 if (text.length() + packageName.length() > 150) { 0277 text.append(QLatin1String(" ...")); 0278 break; 0279 } else if (!text.isNull()) { 0280 text.append(QLatin1String(", ")); 0281 } 0282 text.append(packageName); 0283 } 0284 notify->setText(text); 0285 0286 QStringList actions; 0287 actions << i18n("Review"); 0288 if (m_hasAppletIconified) { 0289 actions << i18n("Install"); 0290 } 0291 notify->setActions(actions); 0292 0293 // use of QSize does the right thing 0294 notify->setPixmap(QIcon::fromTheme(QLatin1String("system-software-update")).pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE)); 0295 notify->sendEvent(); 0296 } 0297 0298 bool Updater::updatePackages(const QStringList &packages, bool downloadOnly, const QString &icon, const QString &msg) 0299 { 0300 m_oldUpdateList = m_updateList; 0301 0302 // Defaults to security 0303 auto transaction = new PkTransaction; 0304 transaction->setProperty("DownloadOnly", downloadOnly); 0305 transaction->enableJobWatcher(true); 0306 transaction->updatePackages(packages, downloadOnly); 0307 connect(transaction, &PkTransaction::finished, this, &Updater::autoUpdatesFinished); 0308 if (!icon.isNull()) { 0309 KNotification *notify; 0310 if (downloadOnly) { 0311 notify = new KNotification(QLatin1String("DownloadingUpdates")); 0312 } else { 0313 notify = new KNotification(QLatin1String("AutoInstallingUpdates")); 0314 } 0315 notify->setComponentName(QLatin1String("apperd")); 0316 notify->setText(msg); 0317 // use of QSize does the right thing 0318 notify->setPixmap(QIcon::fromTheme(icon).pixmap(QSize(KPK_ICON_SIZE, KPK_ICON_SIZE))); 0319 notify->sendEvent(); 0320 } 0321 0322 return true; 0323 } 0324 0325 #include "moc_Updater.cpp"