File indexing completed on 2024-05-12 15:34:12

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