File indexing completed on 2024-05-12 03:54:26

0001 /*
0002     SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
0003     SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kconfigwatcher.h"
0009 
0010 #include "config-kconfig.h"
0011 #include "kconfig_core_log_settings.h"
0012 
0013 #if KCONFIG_USE_DBUS
0014 #include <QDBusConnection>
0015 #include <QDBusMessage>
0016 #include <QDBusMetaType>
0017 
0018 #include "dbussanitizer_p.h"
0019 #endif
0020 
0021 #include <QDebug>
0022 #include <QHash>
0023 #include <QThreadStorage>
0024 
0025 class KConfigWatcherPrivate
0026 {
0027 public:
0028     KSharedConfig::Ptr m_config;
0029 };
0030 
0031 KConfigWatcher::Ptr KConfigWatcher::create(const KSharedConfig::Ptr &config)
0032 {
0033     static QThreadStorage<QHash<KSharedConfig *, QWeakPointer<KConfigWatcher>>> watcherList;
0034 
0035     auto c = config.data();
0036     KConfigWatcher::Ptr watcher;
0037 
0038     if (!watcherList.localData().contains(c)) {
0039         watcher = KConfigWatcher::Ptr(new KConfigWatcher(config));
0040 
0041         watcherList.localData().insert(c, watcher.toWeakRef());
0042 
0043         QObject::connect(watcher.data(), &QObject::destroyed, [c]() {
0044             watcherList.localData().remove(c);
0045         });
0046     }
0047     return watcherList.localData().value(c).toStrongRef();
0048 }
0049 
0050 KConfigWatcher::KConfigWatcher(const KSharedConfig::Ptr &config)
0051     : QObject(nullptr)
0052     , d(new KConfigWatcherPrivate)
0053 {
0054     Q_ASSERT(config);
0055     d->m_config = config;
0056 
0057 #if KCONFIG_USE_DBUS
0058 
0059     if (config->name().isEmpty()) {
0060         return;
0061     }
0062 
0063     // Watching absolute paths is not supported and also makes no sense.
0064     const bool isAbsolutePath = config->name().at(0) == QLatin1Char('/');
0065     if (isAbsolutePath) {
0066         qCWarning(KCONFIG_CORE_LOG) << "Watching absolute paths is not supported" << config->name();
0067         return;
0068     }
0069 
0070     qDBusRegisterMetaType<QByteArrayList>();
0071     qDBusRegisterMetaType<QHash<QString, QByteArrayList>>();
0072 
0073     QStringList watchedPaths = d->m_config->additionalConfigSources();
0074     for (QString &file : watchedPaths) {
0075         file.prepend(QLatin1Char('/'));
0076     }
0077     watchedPaths.prepend(kconfigDBusSanitizePath(QLatin1Char('/') + d->m_config->name()));
0078 
0079     if (d->m_config->openFlags() & KConfig::IncludeGlobals) {
0080         watchedPaths << QStringLiteral("/kdeglobals");
0081     }
0082 
0083     for (const QString &path : std::as_const(watchedPaths)) {
0084         QDBusConnection::sessionBus().connect(QString(),
0085                                               path,
0086                                               QStringLiteral("org.kde.kconfig.notify"),
0087                                               QStringLiteral("ConfigChanged"),
0088                                               this,
0089                                               // clang-format off
0090                                               SLOT(onConfigChangeNotification(QHash<QString,QByteArrayList>))
0091                                               // clang-format on
0092         );
0093     }
0094 #else
0095     qCWarning(KCONFIG_CORE_LOG) << "Use of KConfigWatcher without DBus support. You will not receive updates";
0096 #endif
0097 }
0098 
0099 KConfigWatcher::~KConfigWatcher() = default;
0100 
0101 KSharedConfig::Ptr KConfigWatcher::config() const
0102 {
0103     return d->m_config;
0104 }
0105 
0106 void KConfigWatcher::onConfigChangeNotification(const QHash<QString, QByteArrayList> &changes)
0107 {
0108     // should we ever need it we can determine the file changed with  QDbusContext::message().path(), but it doesn't seem too useful
0109 
0110     d->m_config->reparseConfiguration();
0111 
0112     for (auto it = changes.constBegin(); it != changes.constEnd(); it++) {
0113         KConfigGroup group = d->m_config->group(QString()); // top level group
0114         const auto parts = it.key().split(QLatin1Char('\x1d')); // magic char, see KConfig
0115         for (const QString &groupName : parts) {
0116             group = group.group(groupName);
0117         }
0118         Q_EMIT configChanged(group, it.value());
0119     }
0120 }
0121 
0122 #include "moc_kconfigwatcher.cpp"