00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <cstdlib>
00024 #include <fcntl.h>
00025 #include <unistd.h>
00026
00027 #include "kconfig.h"
00028 #include "kconfig_p.h"
00029 #include "kconfigbackend.h"
00030 #include "kconfiggroup.h"
00031 #include <kstringhandler.h>
00032 #include <klocale.h>
00033 #include <kstandarddirs.h>
00034 #include <kurl.h>
00035 #include <kcomponentdata.h>
00036 #include <ktoolinvocation.h>
00037 #include <kaboutdata.h>
00038 #include <kdebug.h>
00039
00040 #include <qbytearray.h>
00041 #include <qfile.h>
00042 #include <qdir.h>
00043 #include <qdatetime.h>
00044 #include <qrect.h>
00045 #include <qsize.h>
00046 #include <qcolor.h>
00047 #include <QtCore/QProcess>
00048 #include <QtCore/QPointer>
00049 #include <QtCore/QSet>
00050 #include <QtCore/QStack>
00051
00052 bool KConfigPrivate::mappingsRegistered=false;
00053
00054 KConfigPrivate::KConfigPrivate(const KComponentData &componentData_, KConfig::OpenFlags flags,
00055 const char* resource)
00056 : openFlags(flags), resourceType(resource), mBackend(0),
00057 bDynamicBackend(true), bDirty(false), bReadDefaults(false),
00058 bFileImmutable(false), bForceGlobal(false), componentData(componentData_),
00059 configState(KConfigBase::NoAccess)
00060 {
00061 sGlobalFileName = componentData.dirs()->saveLocation("config") +
00062 QString::fromLatin1("kdeglobals");
00063 if (wantGlobals()) {
00064 const KStandardDirs *const dirs = componentData.dirs();
00065 foreach(const QString& dir, dirs->findAllResources("config", QLatin1String("kdeglobals")) +
00066 dirs->findAllResources("config", QLatin1String("system.kdeglobals")))
00067 globalFiles.push_front(dir);
00068 }
00069 const QString etc_kderc =
00070 #ifdef Q_WS_WIN
00071 QFile::decodeName( QByteArray(::getenv("WINDIR")) + "\\kde4rc" );
00072 #else
00073 QLatin1String("/etc/kde4rc");
00074 #endif
00075 KEntryMap tmp;
00076
00077 if (KStandardDirs::checkAccess(etc_kderc, R_OK)) {
00078 if (!globalFiles.contains(etc_kderc))
00079 globalFiles.push_front(etc_kderc);
00080
00081 if (!mappingsRegistered) {
00082 KSharedPtr<KConfigBackend> backend = KConfigBackend::create(componentData, etc_kderc, QLatin1String("INI"));
00083 backend->parseConfig( "en_US", tmp, KConfigBackend::ParseDefaults);
00084 }
00085 } else {
00086 globalFiles.push_front(QString());
00087 mappingsRegistered = true;
00088 }
00089
00090 if (!mappingsRegistered) {
00091 const QString kde4rc(QDir::home().filePath(".kde4rc"));
00092 if (KStandardDirs::checkAccess(kde4rc, R_OK)) {
00093 KSharedPtr<KConfigBackend> backend = KConfigBackend::create(componentData, kde4rc, QLatin1String("INI"));
00094 backend->parseConfig( "en_US", tmp, KConfigBackend::ParseOptions());
00095 }
00096 KConfigBackend::registerMappings(tmp);
00097 mappingsRegistered = true;
00098 }
00099 setLocale(KGlobal::hasLocale() ? KGlobal::locale()->language() : KLocale::defaultLanguage());
00100 }
00101
00102
00103 bool KConfigPrivate::lockLocal()
00104 {
00105 if (mBackend) {
00106 if (fileName == QLatin1String("kdeglobals")) {
00107 if (wantGlobals())
00108 return true;
00109 }
00110 return mBackend->lock(componentData);
00111 }
00112
00113 return true;
00114 }
00115
00116 KConfig::KConfig( const QString& file, OpenFlags mode,
00117 const char* resourceType)
00118 : d_ptr(new KConfigPrivate(KGlobal::mainComponent(), mode, resourceType))
00119 {
00120 d_ptr->changeFileName(file, resourceType);
00121
00122
00123 reparseConfiguration();
00124 }
00125
00126 KConfig::KConfig( const KComponentData& componentData, const QString& file, OpenFlags mode,
00127 const char* resourceType)
00128 : d_ptr(new KConfigPrivate(componentData, mode, resourceType))
00129 {
00130 d_ptr->changeFileName(file, resourceType);
00131
00132
00133 reparseConfiguration();
00134 }
00135
00136 KConfig::KConfig(KConfigPrivate &d)
00137 : d_ptr(&d)
00138 {
00139 }
00140
00141 KConfig::~KConfig()
00142 {
00143 Q_D(KConfig);
00144 if (d->bDirty && d->mBackend.isUnique())
00145 sync();
00146 delete d;
00147 }
00148
00149 const KComponentData& KConfig::componentData() const
00150 {
00151 Q_D(const KConfig);
00152 return d->componentData;
00153 }
00154
00155 QStringList KConfig::groupList() const
00156 {
00157 Q_D(const KConfig);
00158 QStringList groups;
00159
00160 foreach (const KEntryKey& key, d->entryMap.keys())
00161 if (key.mKey.isNull() && !key.mGroup.isEmpty() &&
00162 key.mGroup != "<default>" && key.mGroup != "$Version")
00163 groups << QString::fromUtf8(key.mGroup);
00164
00165 return groups;
00166 }
00167
00168 QStringList KConfigPrivate::groupList(const QByteArray& group) const
00169 {
00170 QByteArray theGroup = group + '\x1d';
00171 QSet<QString> groups;
00172
00173 foreach (const KEntryKey& key, entryMap.keys())
00174 if (key.mKey.isNull() && key.mGroup.startsWith(theGroup))
00175 {
00176 QString groupname = QString::fromUtf8(key.mGroup.mid(theGroup.length()));
00177 groups << groupname.left(groupname.indexOf('\x1d'));
00178 }
00179
00180 return groups.toList();
00181 }
00182
00183 QStringList KConfig::keyList(const QString& aGroup) const
00184 {
00185 Q_D(const KConfig);
00186 QStringList keys;
00187 const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
00188
00189 const KEntryMapConstIterator theEnd = d->entryMap.constEnd();
00190 KEntryMapConstIterator it = d->entryMap.findEntry(theGroup);
00191 if (it != theEnd) {
00192 ++it;
00193
00194 QSet<QString> tmp;
00195 for (; it != theEnd && it.key().mGroup == theGroup; ++it) {
00196 const KEntryKey& key = it.key();
00197 if (key.mGroup == theGroup && !key.mKey.isNull() && !it->bDeleted)
00198 tmp << QString::fromUtf8(key.mKey);
00199 }
00200 keys = tmp.toList();
00201 }
00202
00203 return keys;
00204 }
00205
00206 QMap<QString,QString> KConfig::entryMap(const QString& aGroup) const
00207 {
00208 Q_D(const KConfig);
00209 QMap<QString, QString> theMap;
00210 const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
00211
00212 const KEntryMapConstIterator theEnd = d->entryMap.constEnd();
00213 KEntryMapConstIterator it = d->entryMap.findEntry(theGroup, 0, 0);
00214 if (it != theEnd) {
00215 ++it;
00216
00217 for (; it != theEnd && it.key().mGroup == theGroup; ++it) {
00218
00219 if (!it->bDeleted && !it.key().bDefault) {
00220 const QString key = QString::fromUtf8(it.key().mKey.constData());
00221
00222
00223 if (!theMap.contains(key))
00224 theMap.insert(key,QString::fromUtf8(it->mValue.constData()));
00225 }
00226 }
00227 }
00228
00229 return theMap;
00230 }
00231
00232 void KConfig::sync()
00233 {
00234 Q_D(KConfig);
00235
00236 Q_ASSERT(!isImmutable() && !name().isEmpty());
00237
00238 if (d->bDirty && d->mBackend) {
00239 const QByteArray utf8Locale(locale().toUtf8());
00240
00241
00242 if (!d->mBackend->isWritable()) {
00243
00244 d->mBackend->createEnclosing();
00245 }
00246
00247
00248 if (d->configState == ReadWrite && !d->lockLocal()) {
00249 qWarning() << "couldn't lock local file";
00250 return;
00251 }
00252
00253 if (d->wantGlobals()) {
00254 KSharedPtr<KConfigBackend> tmp = KConfigBackend::create(componentData(), d->sGlobalFileName);
00255 if (d->configState == ReadWrite && !tmp->lock(componentData())) {
00256 qWarning() << "couldn't lock global file";
00257 return;
00258 }
00259 tmp->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteGlobal, d->componentData);
00260 if (tmp->isLocked())
00261 tmp->unlock();
00262 }
00263 if (d->mBackend->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteOptions(), d->componentData))
00264 d->bDirty = false;
00265 if (d->mBackend->isLocked())
00266 d->mBackend->unlock();
00267 }
00268 }
00269
00270 void KConfig::markAsClean()
00271 {
00272 Q_D(KConfig);
00273 d->bDirty = false;
00274
00275
00276 const KEntryMapIterator theEnd = d->entryMap.end();
00277 for (KEntryMapIterator it = d->entryMap.begin(); it != theEnd; ++it)
00278 it->bDirty = false;
00279 }
00280
00281 void KConfig::checkUpdate(const QString &id, const QString &updateFile)
00282 {
00283 const KConfigGroup cg(this, "$Version");
00284 const QString cfg_id = updateFile+':'+id;
00285 QStringList ids = cg.readEntry("update_info", QStringList());
00286 if (!ids.contains(cfg_id)) {
00287 KToolInvocation::kdeinitExecWait("kconf_update", QStringList() << "--check" << updateFile);
00288 reparseConfiguration();
00289 }
00290 }
00291
00292 KConfig* KConfig::copyTo(const QString &file, KConfig *config) const
00293 {
00294 Q_D(const KConfig);
00295 if (!config)
00296 config = new KConfig(componentData(), QString(), SimpleConfig);
00297 config->d_func()->changeFileName(file, d->resourceType);
00298 config->d_func()->entryMap = d->entryMap;
00299 config->d_func()->bFileImmutable = false;
00300
00301 const KEntryMapIterator theEnd = config->d_func()->entryMap.end();
00302 for (KEntryMapIterator it = config->d_func()->entryMap.begin(); it != theEnd; ++it)
00303 it->bDirty = true;
00304
00305 return config;
00306 }
00307
00308 QString KConfig::name() const
00309 {
00310 Q_D(const KConfig);
00311 return d->fileName;
00312 }
00313
00314 void KConfigPrivate::changeFileName(const QString& name, const char* type)
00315 {
00316 fileName = name;
00317
00318 QString file;
00319 if (name.isEmpty()) {
00320 if (wantDefaults()) {
00321 const QString appName = componentData.aboutData()->appName();
00322 if (!appName.isEmpty()) {
00323 fileName = appName + QLatin1String("rc");
00324 if (type && *type)
00325 resourceType = type;
00326 file = KStandardDirs::locateLocal(resourceType, fileName, componentData);
00327 }
00328 } else if (wantGlobals()) {
00329 resourceType = "config";
00330 fileName = QLatin1String("kdeglobals");
00331 file = sGlobalFileName;
00332 }
00333 } else if (QDir::isAbsolutePath(fileName))
00334 file = fileName;
00335 else {
00336 if (type && *type)
00337 resourceType = type;
00338 file = KStandardDirs::locateLocal(resourceType, fileName, componentData);
00339
00340 if (fileName == QLatin1String("kdeglobals"))
00341 openFlags |= KConfig::IncludeGlobals;
00342 }
00343
00344 bForceGlobal = (fileName == QLatin1String("kdeglobals"));
00345
00346 if (file.isEmpty()) {
00347 openFlags = KConfig::SimpleConfig;
00348 return;
00349 }
00350
00351 if (bDynamicBackend || !mBackend)
00352 mBackend = KConfigBackend::create(componentData, file);
00353 else
00354 mBackend->setFilePath(file);
00355
00356 configState = mBackend->accessMode();
00357 }
00358
00359 void KConfig::reparseConfiguration()
00360 {
00361 Q_D(KConfig);
00362
00363 if (!d->isReadOnly() && d->bDirty)
00364 sync();
00365
00366 d->entryMap.clear();
00367
00368 d->bFileImmutable = false;
00369
00370
00371 if (d->wantGlobals())
00372 d->parseGlobalFiles();
00373
00374 d->parseConfigFiles();
00375 }
00376
00377 void KConfigPrivate::parseGlobalFiles()
00378 {
00379
00380
00381
00382
00383 const QByteArray utf8Locale = locale.toUtf8();
00384 foreach(const QString& file, globalFiles) {
00385 KConfigBackend::ParseOptions parseOpts = KConfigBackend::ParseGlobal|KConfigBackend::ParseExpansions;
00386 if (file != sGlobalFileName)
00387 parseOpts |= KConfigBackend::ParseDefaults;
00388
00389 KSharedPtr<KConfigBackend> backend = KConfigBackend::create(componentData, file);
00390 if ( backend->parseConfig( utf8Locale, entryMap, parseOpts) == KConfigBackend::ParseImmutable)
00391 break;
00392 }
00393 }
00394
00395 void KConfigPrivate::parseConfigFiles()
00396 {
00397 if (fileName == QLatin1String("kdeglobals") && wantGlobals())
00398 return;
00399
00400
00401 if (mBackend && !fileName.isEmpty()) {
00402
00403
00404 bool allowExecutableValues = (qstrcmp(resourceType, "config") == 0) ||
00405 !fileName.endsWith(".desktop");
00406
00407 bFileImmutable = false;
00408 QList<QString> files;
00409
00410 if (wantDefaults())
00411 foreach (const QString& f, componentData.dirs()->findAllResources(resourceType, fileName))
00412 files.prepend(f);
00413 else
00414 files << mBackend->filePath();
00415
00416 if (!isSimple())
00417 files = extraFiles.toList() + files;
00418
00419
00420
00421 const QByteArray utf8Locale = locale.toUtf8();
00422 foreach(const QString& file, files) {
00423 KConfigBackend::ParseOptions parseOpts;
00424 if (allowExecutableValues)
00425 parseOpts |= KConfigBackend::ParseExpansions;
00426 if (file == mBackend->filePath()) {
00427 KConfigBackend::ParseInfo info = mBackend->parseConfig(utf8Locale, entryMap, parseOpts);
00428 if (info == KConfigBackend::ParseImmutable)
00429 bFileImmutable = true;
00430 else if (info == KConfigBackend::ParseOpenError)
00431 configState = KConfigBase::NoAccess;
00432 else
00433 ;
00434 } else {
00435 parseOpts |= KConfigBackend::ParseDefaults;
00436 KSharedPtr<KConfigBackend> backend = KConfigBackend::create(componentData, file);
00437 bFileImmutable = (backend->parseConfig(utf8Locale, entryMap, parseOpts) ==
00438 KConfigBackend::ParseImmutable);
00439 }
00440
00441 if (bFileImmutable)
00442 break;
00443 }
00444 if (componentData.dirs()->isRestrictedResource(resourceType, fileName))
00445 bFileImmutable = true;
00446 }
00447 }
00448
00449 KConfig::AccessMode KConfig::accessMode() const
00450 {
00451 Q_D(const KConfig);
00452 return d->configState;
00453 }
00454
00455
00456
00457
00458
00459
00460
00461 void KConfig::addConfigSources(const QStringList& files)
00462 {
00463 Q_D(KConfig);
00464 foreach(const QString& file, files)
00465 d->extraFiles.push(file);
00466 }
00467
00468 QString KConfig::locale() const
00469 {
00470 Q_D(const KConfig);
00471 return d->locale;
00472 }
00473
00474 bool KConfigPrivate::setLocale(const QString& aLocale)
00475 {
00476 if (aLocale != locale) {
00477 locale = aLocale;
00478 return true;
00479 }
00480 return false;
00481 }
00482
00483 bool KConfig::setLocale(const QString& locale)
00484 {
00485 Q_D(KConfig);
00486 if (d->setLocale(locale)) {
00487 reparseConfiguration();
00488 return true;
00489 }
00490 return false;
00491 }
00492
00493 void KConfig::setReadDefaults(bool b)
00494 {
00495 Q_D(KConfig);
00496 d->bReadDefaults = b;
00497 }
00498
00499 bool KConfig::readDefaults() const
00500 {
00501 Q_D(const KConfig);
00502 return d->bReadDefaults;
00503 }
00504
00505 bool KConfig::isImmutable() const
00506 {
00507 Q_D(const KConfig);
00508 return d->bFileImmutable;
00509 }
00510
00511 bool KConfig::isGroupImmutableImpl(const QByteArray& aGroup) const
00512 {
00513 Q_D(const KConfig);
00514 return isImmutable()|d->entryMap.getEntryOption(aGroup, 0, 0, KEntryMap::EntryImmutable);
00515 }
00516
00517 void KConfig::setForceGlobal(bool b)
00518 {
00519 Q_D(KConfig);
00520 d->bForceGlobal = b;
00521 }
00522
00523 bool KConfig::forceGlobal() const
00524 {
00525 Q_D(const KConfig);
00526 return d->bForceGlobal;
00527 }
00528
00529 KConfigGroup KConfig::groupImpl(const QByteArray &group)
00530 {
00531 return KConfigGroup(this, group.constData());
00532 }
00533
00534 const KConfigGroup KConfig::groupImpl(const QByteArray &group) const
00535 {
00536 return KConfigGroup(this, group.constData());
00537 }
00538
00539 KEntryMap::EntryOptions convertToOptions(KConfig::WriteConfigFlags flags)
00540 {
00541 KEntryMap::EntryOptions options=0;
00542
00543 if (flags&KConfig::Persistent)
00544 options |= KEntryMap::EntryDirty;
00545 if (flags&KConfig::Global)
00546 options |= KEntryMap::EntryGlobal;
00547 if (flags&KConfig::Localized)
00548 options |= KEntryMap::EntryLocalized;
00549 return options;
00550 }
00551
00552 void KConfig::deleteGroupImpl(const QByteArray &aGroup, WriteConfigFlags flags)
00553 {
00554 Q_D(KConfig);
00555 KEntryMap::EntryOptions options = convertToOptions(flags)|KEntryMap::EntryDeleted;
00556
00557 QByteArray theGroup = aGroup + '\x1d';
00558 QSet<QByteArray> groups;
00559 groups << aGroup;
00560
00561 foreach (const KEntryKey& key, d->entryMap.keys()) {
00562 if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) {
00563 groups << key.mGroup;
00564 }
00565 }
00566
00567 bool dirty = false;
00568 foreach (const QByteArray& group, groups) {
00569 const QStringList keys = keyList(group);
00570 foreach (const QString& key, keys) {
00571 if (d->canWriteEntry(group, key.toUtf8().constData())) {
00572 d->entryMap.setEntry(group, key.toUtf8(), QByteArray(), options);
00573 dirty = true;
00574 }
00575 }
00576 }
00577
00578 if (dirty) {
00579 d->setDirty(true);
00580 }
00581 }
00582
00583 bool KConfig::isConfigWritable(bool warnUser)
00584 {
00585 Q_D(KConfig);
00586 bool allWritable = (d->mBackend.isNull()? false: d->mBackend->isWritable());
00587
00588 if (warnUser && !allWritable) {
00589 QString errorMsg;
00590
00591 errorMsg += i18n("Please contact your system administrator.");
00592 QString cmdToExec = KStandardDirs::findExe(QString("kdialog"));
00593 if (!cmdToExec.isEmpty() && componentData().isValid())
00594 {
00595 QProcess::execute(cmdToExec,QStringList() << "--title" << componentData().componentName()
00596 << "--msgbox" << errorMsg.toLocal8Bit());
00597 }
00598 }
00599
00600 d->configState = allWritable ? ReadWrite : ReadOnly;
00601
00602 return allWritable;
00603 }
00604
00605 void KConfigPrivate::setDirty(bool b)
00606 {
00607 bDirty = b;
00608 }
00609
00610 bool KConfig::hasGroupImpl(const QByteArray& aGroup) const
00611 {
00612 Q_D(const KConfig);
00613 return d->entryMap.hasEntry(aGroup);
00614 }
00615
00616 bool KConfigPrivate::canWriteEntry(const QByteArray& group, const char* key, bool isDefault) const
00617 {
00618 if (bFileImmutable ||
00619 entryMap.getEntryOption(group, key, KEntryMap::SearchLocalized, KEntryMap::EntryImmutable))
00620 return isDefault;
00621 return true;
00622 }
00623
00624 void KConfigPrivate::putData( const QByteArray& group, const char* key,
00625 const QByteArray& value, KConfigBase::WriteConfigFlags flags, bool expand)
00626 {
00627
00628
00629
00630
00631
00632 if( flags & KConfigBase::Persistent )
00633 setDirty(true);
00634
00635 KEntryMap::EntryOptions options = convertToOptions(flags);
00636 if (bForceGlobal)
00637 options |= KEntryMap::EntryGlobal;
00638 if (expand)
00639 options |= KEntryMap::EntryExpansion;
00640
00641 if (value.isNull())
00642 options |= KEntryMap::EntryDeleted;
00643
00644 entryMap.setEntry(group, key, value, options);
00645 }
00646
00647 QByteArray KConfigPrivate::lookupData(const QByteArray& group, const char* key,
00648 KEntryMap::SearchFlags flags) const
00649 {
00650 if (bReadDefaults)
00651 flags |= KEntryMap::SearchDefaults;
00652 const KEntryMapConstIterator it = entryMap.findEntry(group, key, flags);
00653 if (it == entryMap.constEnd())
00654 return QByteArray();
00655 return it->mValue;
00656 }
00657
00658 QString KConfigPrivate::lookupData(const QByteArray& group, const char* key,
00659 KEntryMap::SearchFlags flags, bool *expand) const
00660 {
00661 if (bReadDefaults)
00662 flags |= KEntryMap::SearchDefaults;
00663 return entryMap.getEntry(group, key, QString(), flags, expand);
00664 }
00665
00666 void KConfig::virtual_hook(int , void* )
00667 {
00668
00669 }
00670