File indexing completed on 2024-11-17 04:55:38
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * SPDX-FileCopyrightText: 2017 Jan Grulich <jgrulich@redhat.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "FlatpakNotifier.h" 0009 0010 #include <glib.h> 0011 0012 #include <QDebug> 0013 #include <QFutureWatcher> 0014 #include <QTimer> 0015 #include <QtConcurrentRun> 0016 0017 using namespace std::chrono_literals; 0018 0019 static void installationChanged(GFileMonitor *monitor, GFile *child, GFile *other_file, GFileMonitorEvent event_type, gpointer self) 0020 { 0021 Q_UNUSED(monitor); 0022 Q_UNUSED(child); 0023 Q_UNUSED(other_file); 0024 Q_UNUSED(event_type); 0025 0026 FlatpakNotifier::Installation *installation = (FlatpakNotifier::Installation *)self; 0027 if (!installation) 0028 return; 0029 0030 FlatpakNotifier *notifier = installation->m_notifier; 0031 notifier->loadRemoteUpdates(installation); 0032 } 0033 0034 FlatpakNotifier::FlatpakNotifier(QObject *parent) 0035 : BackendNotifierModule(parent) 0036 , m_user(this) 0037 , m_system(this) 0038 , m_cancellable(g_cancellable_new()) 0039 { 0040 QTimer *dailyCheck = new QTimer(this); 0041 dailyCheck->setInterval(24h); // refresh at least once every day 0042 connect(dailyCheck, &QTimer::timeout, this, &FlatpakNotifier::recheckSystemUpdateNeeded); 0043 } 0044 0045 FlatpakNotifier::Installation::Installation(FlatpakNotifier *notifier) 0046 : m_notifier(notifier) 0047 { 0048 } 0049 0050 FlatpakNotifier::Installation::~Installation() 0051 { 0052 if (m_monitor) 0053 g_object_unref(m_monitor); 0054 if (m_installation) 0055 g_object_unref(m_installation); 0056 } 0057 0058 FlatpakNotifier::~FlatpakNotifier() 0059 { 0060 g_object_unref(m_cancellable); 0061 } 0062 0063 void FlatpakNotifier::recheckSystemUpdateNeeded() 0064 { 0065 g_autoptr(GError) error = nullptr; 0066 0067 // Load flatpak installation 0068 if (!setupFlatpakInstallations(&error)) { 0069 qWarning() << "Failed to setup flatpak installations: " << error->message; 0070 } else { 0071 // Load updates from remote repositories 0072 loadRemoteUpdates(&m_system); 0073 loadRemoteUpdates(&m_user); 0074 } 0075 } 0076 0077 void FlatpakNotifier::onFetchUpdatesFinished(Installation *installation, bool hasUpdates) 0078 { 0079 if (installation->m_hasUpdates == hasUpdates) { 0080 return; 0081 } 0082 bool hadUpdates = this->hasUpdates(); 0083 installation->m_hasUpdates = hasUpdates; 0084 0085 if (hadUpdates != this->hasUpdates()) { 0086 Q_EMIT foundUpdates(); 0087 } 0088 } 0089 0090 void FlatpakNotifier::loadRemoteUpdates(Installation *installation) 0091 { 0092 auto fw = new QFutureWatcher<bool>(this); 0093 connect(fw, &QFutureWatcher<bool>::finished, this, [this, installation, fw]() { 0094 onFetchUpdatesFinished(installation, fw->result()); 0095 fw->deleteLater(); 0096 }); 0097 fw->setFuture(QtConcurrent::run([installation]() -> bool { 0098 g_autoptr(GCancellable) cancellable = g_cancellable_new(); 0099 g_autoptr(GError) localError = nullptr; 0100 g_autoptr(GPtrArray) fetchedUpdates = flatpak_installation_list_installed_refs_for_update(installation->m_installation, cancellable, &localError); 0101 bool hasUpdates = false; 0102 0103 if (!fetchedUpdates) { 0104 qWarning() << "Failed to get list of installed refs for listing updates: " << localError->message; 0105 return false; 0106 } 0107 for (uint i = 0; !hasUpdates && i < fetchedUpdates->len; i++) { 0108 FlatpakInstalledRef *ref = FLATPAK_INSTALLED_REF(g_ptr_array_index(fetchedUpdates, i)); 0109 const QString refName = QString::fromUtf8(flatpak_ref_get_name(FLATPAK_REF(ref))); 0110 // FIXME right now I can't think of any other filter than this, in FlatpakBackend updates are matched 0111 // with apps so .Locale/.Debug subrefs are not shown and updated automatically. Also this will show 0112 // updates for refs we don't show in Discover if appstream metadata or desktop file for them is not found 0113 if (refName.endsWith(QLatin1String(".Locale")) || refName.endsWith(QLatin1String(".Debug"))) { 0114 continue; 0115 } 0116 hasUpdates = true; 0117 } 0118 return hasUpdates; 0119 })); 0120 } 0121 0122 bool FlatpakNotifier::hasUpdates() 0123 { 0124 return m_system.m_hasUpdates || m_user.m_hasUpdates; 0125 } 0126 0127 bool FlatpakNotifier::Installation::ensureInitialized(std::function<FlatpakInstallation *()> func, GCancellable *cancellable, GError **error) 0128 { 0129 if (!m_installation) { 0130 m_installation = func(); 0131 m_monitor = flatpak_installation_create_monitor(m_installation, cancellable, error); 0132 g_signal_connect(m_monitor, "changed", G_CALLBACK(installationChanged), this); 0133 } 0134 return m_installation && m_monitor; 0135 } 0136 0137 bool FlatpakNotifier::setupFlatpakInstallations(GError **error) 0138 { 0139 if (!m_system.ensureInitialized( 0140 [this, error] { 0141 return flatpak_installation_new_system(m_cancellable, error); 0142 }, 0143 m_cancellable, 0144 error)) 0145 return false; 0146 if (!m_user.ensureInitialized( 0147 [this, error] { 0148 return flatpak_installation_new_user(m_cancellable, error); 0149 }, 0150 m_cancellable, 0151 error)) 0152 return false; 0153 0154 return true; 0155 }