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

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
0004     SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "ksharedconfig.h"
0010 #include "kconfig_p.h"
0011 #include "kconfiggroup.h"
0012 #include <QCoreApplication>
0013 #include <QThread>
0014 #include <QThreadStorage>
0015 
0016 void _k_globalMainConfigSync();
0017 
0018 using SharedConfigList = QList<KSharedConfig *>;
0019 
0020 class GlobalSharedConfig
0021 {
0022 public:
0023     GlobalSharedConfig()
0024         : wasTestModeEnabled(false)
0025     {
0026         if (!qApp || QThread::currentThread() == qApp->thread()) {
0027             // We want to force the sync() before the QCoreApplication
0028             // instance is gone. Otherwise we trigger a QLockFile::lock()
0029             // after QCoreApplication is gone, calling qAppName() for a non
0030             // existent app...
0031             qAddPostRoutine(&_k_globalMainConfigSync);
0032         }
0033         // In other threads, QThreadStorage takes care of deleting the GlobalSharedConfigList when
0034         // the thread exits.
0035     }
0036 
0037     SharedConfigList configList;
0038     // in addition to the list, we need to hold the main config,
0039     // so that it's not created and destroyed all the time.
0040     KSharedConfigPtr mainConfig;
0041     bool wasTestModeEnabled;
0042 };
0043 
0044 static QThreadStorage<GlobalSharedConfig *> s_storage;
0045 template<typename T>
0046 T *perThreadGlobalStatic()
0047 {
0048     if (!s_storage.hasLocalData()) {
0049         s_storage.setLocalData(new T);
0050     }
0051     return s_storage.localData();
0052 }
0053 
0054 // Q_GLOBAL_STATIC(GlobalSharedConfigList, globalSharedConfigList), but per thread:
0055 static GlobalSharedConfig *globalSharedConfig()
0056 {
0057     return perThreadGlobalStatic<GlobalSharedConfig>();
0058 }
0059 
0060 void _k_globalMainConfigSync()
0061 {
0062     if (KSharedConfigPtr mainConfig = globalSharedConfig()->mainConfig) {
0063         mainConfig->sync();
0064     }
0065 }
0066 
0067 KSharedConfigPtr KSharedConfig::openConfig(const QString &_fileName, OpenFlags flags, QStandardPaths::StandardLocation resType)
0068 {
0069     QString fileName(_fileName);
0070     GlobalSharedConfig *global = globalSharedConfig();
0071     if (fileName.isEmpty() && !flags.testFlag(KConfig::SimpleConfig)) {
0072         // Determine the config file name that KConfig will make up (see KConfigPrivate::changeFileName)
0073         fileName = KConfig::mainConfigName();
0074     }
0075 
0076     if (!global->wasTestModeEnabled && QStandardPaths::isTestModeEnabled()) {
0077         global->wasTestModeEnabled = true;
0078         global->configList.clear();
0079         global->mainConfig = nullptr;
0080     }
0081 
0082     for (auto *cfg : std::as_const(global->configList)) {
0083         if (cfg->name() == fileName && cfg->d_ptr->openFlags == flags && cfg->locationType() == resType) {
0084             return KSharedConfigPtr(cfg);
0085         }
0086     }
0087 
0088     KSharedConfigPtr ptr(new KSharedConfig(fileName, flags, resType));
0089 
0090     if (_fileName.isEmpty() && flags == FullConfig && resType == QStandardPaths::GenericConfigLocation) {
0091         global->mainConfig = ptr;
0092 
0093         const bool isMainThread = !qApp || QThread::currentThread() == qApp->thread();
0094         static bool userWarned = false;
0095         if (isMainThread && !userWarned) {
0096             userWarned = true;
0097             const bool isReadOnly = qEnvironmentVariableIsEmpty("KDE_HOME_READONLY");
0098             if (isReadOnly && QCoreApplication::applicationName() != QLatin1String("kdialog")) {
0099                 if (ptr->group(QStringLiteral("General")).readEntry(QStringLiteral("warn_unwritable_config"), true)) {
0100                     ptr->isConfigWritable(true);
0101                 }
0102             }
0103         }
0104     }
0105 
0106     return ptr;
0107 }
0108 
0109 KSharedConfig::Ptr KSharedConfig::openStateConfig(const QString &_fileName)
0110 {
0111     // KF6 TODO: port this to XDG_STATE_HOME (default ~/.local/state)
0112     // See https://gitlab.freedesktop.org/xdg/xdg-specs/-/blob/master/basedir/basedir-spec.xml
0113     QString fileName(_fileName);
0114 
0115     if (fileName.isEmpty()) {
0116         fileName = QCoreApplication::applicationName() + QLatin1String("staterc");
0117     }
0118 
0119     return openConfig(fileName, SimpleConfig, QStandardPaths::AppDataLocation);
0120 }
0121 
0122 KSharedConfig::KSharedConfig(const QString &fileName, OpenFlags flags, QStandardPaths::StandardLocation resType)
0123     : KConfig(fileName, flags, resType)
0124 {
0125     globalSharedConfig()->configList.append(this);
0126 }
0127 
0128 KSharedConfig::~KSharedConfig()
0129 {
0130     if (s_storage.hasLocalData()) {
0131         globalSharedConfig()->configList.removeAll(this);
0132     }
0133 }
0134 
0135 KConfigGroup KSharedConfig::groupImpl(const QString &groupName)
0136 {
0137     KSharedConfigPtr ptr(this);
0138     return KConfigGroup(ptr, groupName);
0139 }
0140 
0141 const KConfigGroup KSharedConfig::groupImpl(const QString &groupName) const
0142 {
0143     const KSharedConfigPtr ptr(const_cast<KSharedConfig *>(this));
0144     return KConfigGroup(ptr, groupName);
0145 }