tdeconfigbackend.cpp
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00004 Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include <config.h> 00023 00024 #include <unistd.h> 00025 #include <ctype.h> 00026 #ifdef HAVE_SYS_MMAN_H 00027 #include <sys/mman.h> 00028 #endif 00029 #include <sys/types.h> 00030 #ifdef HAVE_SYS_STAT_H 00031 #include <sys/stat.h> 00032 #endif 00033 #include <fcntl.h> 00034 #include <signal.h> 00035 #include <setjmp.h> 00036 00037 #include <tqdir.h> 00038 #include <tqfileinfo.h> 00039 #include <tqtextcodec.h> 00040 #include <tqtextstream.h> 00041 00042 #include "tdeconfigbackend.h" 00043 #include "tdeconfigbase.h" 00044 #include <tdeapplication.h> 00045 #include <tdeglobal.h> 00046 #include <kprocess.h> 00047 #include <tdelocale.h> 00048 #include <kstandarddirs.h> 00049 #include <ksavefile.h> 00050 #include <kurl.h> 00051 #include <kde_file.h> 00052 00053 extern bool checkAccess(const TQString& pathname, int mode); 00054 /* translate escaped escape sequences to their actual values. */ 00055 static TQCString printableToString(const char *str, int l) 00056 { 00057 // Strip leading white-space. 00058 while((l>0) && 00059 ((*str == ' ') || (*str == '\t') || (*str == '\r'))) 00060 { 00061 str++; l--; 00062 } 00063 00064 // Strip trailing white-space. 00065 while((l>0) && 00066 ((str[l-1] == ' ') || (str[l-1] == '\t') || (str[l-1] == '\r'))) 00067 { 00068 l--; 00069 } 00070 00071 TQCString result(l + 1); 00072 char *r = result.data(); 00073 00074 for(int i = 0; i < l;i++, str++) 00075 { 00076 if (*str == '\\') 00077 { 00078 i++, str++; 00079 if (i >= l) // End of line. (Line ends with single slash) 00080 { 00081 *r++ = '\\'; 00082 break; 00083 } 00084 switch(*str) 00085 { 00086 case 's': 00087 *r++ = ' '; 00088 break; 00089 case 't': 00090 *r++ = '\t'; 00091 break; 00092 case 'n': 00093 *r++ = '\n'; 00094 break; 00095 case 'r': 00096 *r++ = '\r'; 00097 break; 00098 case '\\': 00099 *r++ = '\\'; 00100 break; 00101 default: 00102 *r++ = '\\'; 00103 *r++ = *str; 00104 } 00105 } 00106 else 00107 { 00108 *r++ = *str; 00109 } 00110 } 00111 result.truncate(r-result.data()); 00112 return result; 00113 } 00114 00115 static TQCString stringToPrintable(const TQCString& str){ 00116 TQCString result(str.length()*2); // Maximum 2x as long as source string 00117 char *r = const_cast<TQCString&>(result).data(); 00118 char *s = const_cast<TQCString&>(str).data(); 00119 00120 if (!s) return TQCString(""); 00121 00122 // Escape leading space 00123 if (*s == ' ') 00124 { 00125 *r++ = '\\'; *r++ = 's'; 00126 s++; 00127 } 00128 00129 if (*s) 00130 { 00131 while(*s) 00132 { 00133 if (*s == '\n') 00134 { 00135 *r++ = '\\'; *r++ = 'n'; 00136 } 00137 else if (*s == '\t') 00138 { 00139 *r++ = '\\'; *r++ = 't'; 00140 } 00141 else if (*s == '\r') 00142 { 00143 *r++ = '\\'; *r++ = 'r'; 00144 } 00145 else if (*s == '\\') 00146 { 00147 *r++ = '\\'; *r++ = '\\'; 00148 } 00149 else 00150 { 00151 *r++ = *s; 00152 } 00153 s++; 00154 } 00155 // Escape trailing space 00156 if (*(r-1) == ' ') 00157 { 00158 *(r-1) = '\\'; *r++ = 's'; 00159 } 00160 } 00161 00162 result.truncate(r - result.data()); 00163 return result; 00164 } 00165 00166 static TQCString decodeGroup(const char*s, int l) 00167 { 00168 TQCString result(l); 00169 char *r = result.data(); 00170 00171 l--; // Correct for trailing \0 00172 while(l) 00173 { 00174 if ((*s == '[') && (l > 1)) 00175 { 00176 if ((*(s+1) == '[')) 00177 { 00178 l--; 00179 s++; 00180 } 00181 } 00182 if ((*s == ']') && (l > 1)) 00183 { 00184 if ((*(s+1) == ']')) 00185 { 00186 l--; 00187 s++; 00188 } 00189 } 00190 *r++ = *s++; 00191 l--; 00192 } 00193 result.truncate(r - result.data()); 00194 return result; 00195 } 00196 00197 static TQCString encodeGroup(const TQCString &str) 00198 { 00199 int l = str.length(); 00200 TQCString result(l*2+1); 00201 char *r = const_cast<TQCString&>(result).data(); 00202 char *s = const_cast<TQCString&>(str).data(); 00203 while(l) 00204 { 00205 if ((*s == '[') || (*s == ']')) 00206 *r++ = *s; 00207 *r++ = *s++; 00208 l--; 00209 } 00210 result.truncate(r - result.data()); 00211 return result; 00212 } 00213 00214 static TQCString encodeKey(const char* key) 00215 { 00216 TQCString newKey(key); 00217 00218 newKey.replace('[', "%5b"); 00219 newKey.replace(']', "%5d"); 00220 00221 return newKey; 00222 } 00223 00224 static TQCString decodeKey(const char* key) 00225 { 00226 TQCString newKey(key); 00227 00228 newKey.replace("%5b", "["); 00229 newKey.replace("%5d", "]"); 00230 00231 return newKey; 00232 } 00233 00234 class TDEConfigBackEnd::TDEConfigBackEndPrivate 00235 { 00236 public: 00237 TQDateTime localLastModified; 00238 uint localLastSize; 00239 TDELockFile::Ptr localLockFile; 00240 TDELockFile::Ptr globalLockFile; 00241 }; 00242 00243 void TDEConfigBackEnd::changeFileName(const TQString &_fileName, 00244 const char * _resType, 00245 bool _useKDEGlobals) 00246 { 00247 mfileName = _fileName; 00248 resType = _resType; 00249 useKDEGlobals = _useKDEGlobals; 00250 if (mfileName.isEmpty()) { 00251 mLocalFileName = TQString::null; 00252 } 00253 else if (!TQDir::isRelativePath(mfileName)) { 00254 mLocalFileName = mfileName; 00255 } 00256 else { 00257 mLocalFileName = TDEGlobal::dirs()->saveLocation(resType, TQString(), false) + mfileName; 00258 } 00259 00260 if (useKDEGlobals) { 00261 mGlobalFileName = TDEGlobal::dirs()->saveLocation("config", TQString(), false) + TQString::fromLatin1("kdeglobals"); 00262 } 00263 else { 00264 mGlobalFileName = TQString::null; 00265 } 00266 00267 d->localLastModified = TQDateTime(); 00268 d->localLastSize = 0; 00269 d->localLockFile = 0; 00270 d->globalLockFile = 0; 00271 } 00272 00273 TDELockFile::Ptr TDEConfigBackEnd::lockFile(bool bGlobal) 00274 { 00275 if (bGlobal) 00276 { 00277 if (d->globalLockFile) 00278 return d->globalLockFile; 00279 00280 if (!mGlobalFileName.isEmpty()) 00281 { 00282 d->globalLockFile = new TDELockFile(mGlobalFileName+".lock"); 00283 return d->globalLockFile; 00284 } 00285 } 00286 else 00287 { 00288 if (d->localLockFile) 00289 return d->localLockFile; 00290 00291 if (!mLocalFileName.isEmpty()) 00292 { 00293 d->localLockFile = new TDELockFile(mLocalFileName+".lock"); 00294 return d->localLockFile; 00295 } 00296 } 00297 return 0; 00298 } 00299 00300 TDEConfigBackEnd::TDEConfigBackEnd(TDEConfigBase *_config, 00301 const TQString &_fileName, 00302 const char * _resType, 00303 bool _useKDEGlobals) 00304 : pConfig(_config), bFileImmutable(false), mConfigState(TDEConfigBase::NoAccess), mFileMode(-1) 00305 { 00306 d = new TDEConfigBackEndPrivate; 00307 changeFileName(_fileName, _resType, _useKDEGlobals); 00308 } 00309 00310 TDEConfigBackEnd::~TDEConfigBackEnd() 00311 { 00312 delete d; 00313 } 00314 00315 void TDEConfigBackEnd::setFileWriteMode(int mode) 00316 { 00317 mFileMode = mode; 00318 } 00319 00320 bool TDEConfigINIBackEnd::parseConfigFiles() 00321 { 00322 // Check if we can write to the local file. 00323 mConfigState = TDEConfigBase::ReadOnly; 00324 if (!mLocalFileName.isEmpty() && !pConfig->isReadOnly()) 00325 { 00326 if (checkAccess(mLocalFileName, W_OK)) 00327 { 00328 mConfigState = TDEConfigBase::ReadWrite; 00329 } 00330 else 00331 { 00332 // Create the containing dir, maybe it wasn't there 00333 KURL path; 00334 path.setPath(mLocalFileName); 00335 TQString dir=path.directory(); 00336 TDEStandardDirs::makeDir(dir); 00337 00338 if (checkAccess(mLocalFileName, W_OK)) 00339 { 00340 mConfigState = TDEConfigBase::ReadWrite; 00341 } 00342 } 00343 TQFileInfo info(mLocalFileName); 00344 d->localLastModified = info.lastModified(); 00345 d->localLastSize = info.size(); 00346 } 00347 00348 // Parse all desired files from the least to the most specific. 00349 bFileImmutable = false; 00350 00351 // Parse the general config files 00352 if (useKDEGlobals) { 00353 TQStringList tdercs = TDEGlobal::dirs()-> 00354 findAllResources("config", TQString::fromLatin1("kdeglobals")); 00355 00356 #ifdef Q_WS_WIN 00357 TQString etc_tderc = TQFile::decodeName( TQCString(getenv("WINDIR")) + "\\tderc" ); 00358 #else 00359 TQString etc_tderc = TQString::fromLatin1("/etc/tderc"); 00360 #endif 00361 00362 if (checkAccess(etc_tderc, R_OK)) 00363 tdercs += etc_tderc; 00364 00365 tdercs += TDEGlobal::dirs()-> 00366 findAllResources("config", TQString::fromLatin1("system.kdeglobals")); 00367 00368 TQStringList::ConstIterator it; 00369 00370 for (it = tdercs.fromLast(); it != tdercs.end(); --it) { 00371 00372 TQFile aConfigFile( *it ); 00373 if (!aConfigFile.open( IO_ReadOnly )) 00374 continue; 00375 parseSingleConfigFile( aConfigFile, 0L, true, (*it != mGlobalFileName) ); 00376 aConfigFile.close(); 00377 if (bFileImmutable) 00378 break; 00379 } 00380 } 00381 00382 bool bReadFile = !mfileName.isEmpty(); 00383 while(bReadFile) { 00384 bReadFile = false; 00385 TQString bootLanguage; 00386 if (useKDEGlobals && localeString.isEmpty() && !TDEGlobal::_locale) { 00387 // Boot strap language 00388 bootLanguage = TDELocale::_initLanguage(pConfig); 00389 setLocaleString(bootLanguage.utf8()); 00390 } 00391 00392 bFileImmutable = false; 00393 TQStringList list; 00394 if ( !TQDir::isRelativePath(mfileName) ) 00395 list << mfileName; 00396 else 00397 list = TDEGlobal::dirs()->findAllResources(resType, mfileName); 00398 00399 TQStringList::ConstIterator it; 00400 00401 for (it = list.fromLast(); it != list.end(); --it) { 00402 00403 TQFile aConfigFile( *it ); 00404 // we can already be sure that this file exists 00405 bool bIsLocal = (*it == mLocalFileName); 00406 if (aConfigFile.open( IO_ReadOnly )) { 00407 parseSingleConfigFile( aConfigFile, 0L, false, !bIsLocal ); 00408 aConfigFile.close(); 00409 if (bFileImmutable) 00410 break; 00411 } 00412 } 00413 if (TDEGlobal::dirs()->isRestrictedResource(resType, mfileName)) 00414 bFileImmutable = true; 00415 TQString currentLanguage; 00416 if (!bootLanguage.isEmpty()) 00417 { 00418 currentLanguage = TDELocale::_initLanguage(pConfig); 00419 // If the file changed the language, we need to read the file again 00420 // with the new language setting. 00421 if (bootLanguage != currentLanguage) 00422 { 00423 bReadFile = true; 00424 setLocaleString(currentLanguage.utf8()); 00425 } 00426 } 00427 } 00428 if (bFileImmutable) 00429 mConfigState = TDEConfigBase::ReadOnly; 00430 00431 return true; 00432 } 00433 00434 #ifdef HAVE_MMAP 00435 #ifdef SIGBUS 00436 static sigjmp_buf mmap_jmpbuf; 00437 struct sigaction mmap_old_sigact; 00438 00439 extern "C" { 00440 static void mmap_sigbus_handler(int) 00441 { 00442 siglongjmp (mmap_jmpbuf, 1); 00443 } 00444 } 00445 #endif 00446 #endif 00447 00448 extern bool kde_kiosk_exception; 00449 00450 void TDEConfigINIBackEnd::parseSingleConfigFile(TQFile &rFile, 00451 KEntryMap *pWriteBackMap, 00452 bool bGlobal, bool bDefault) 00453 { 00454 const char *s; // May get clobbered by sigsetjump, but we don't use them afterwards. 00455 const char *eof; // May get clobbered by sigsetjump, but we don't use them afterwards. 00456 TQByteArray data; 00457 00458 if (!rFile.isOpen()) // come back, if you have real work for us ;-> 00459 return; 00460 00461 //using kdDebug() here leads to an infinite loop 00462 //remove this for the release, aleXXX 00463 //tqWarning("Parsing %s, global = %s default = %s", 00464 // rFile.name().latin1(), bGlobal ? "true" : "false", bDefault ? "true" : "false"); 00465 00466 TQCString aCurrentGroup("<default>"); 00467 00468 unsigned int ll = localeString.length(); 00469 00470 #ifdef HAVE_MMAP 00471 static volatile const char *map; 00472 map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE, 00473 rFile.handle(), 0); 00474 00475 if ( map != MAP_FAILED ) 00476 { 00477 s = (const char*) map; 00478 eof = s + rFile.size(); 00479 00480 #ifdef SIGBUS 00481 struct sigaction act; 00482 act.sa_handler = mmap_sigbus_handler; 00483 sigemptyset( &act.sa_mask ); 00484 #ifdef SA_ONESHOT 00485 act.sa_flags = SA_ONESHOT; 00486 #else 00487 act.sa_flags = SA_RESETHAND; 00488 #endif 00489 sigaction( SIGBUS, &act, &mmap_old_sigact ); 00490 00491 if (sigsetjmp (mmap_jmpbuf, 1)) 00492 { 00493 tqWarning("SIGBUS while reading %s", rFile.name().latin1()); 00494 munmap(( char* )map, rFile.size()); 00495 sigaction (SIGBUS, &mmap_old_sigact, 0); 00496 return; 00497 } 00498 #endif 00499 } 00500 else 00501 #endif 00502 { 00503 rFile.at(0); 00504 data = rFile.readAll(); 00505 s = data.data(); 00506 eof = s + data.size(); 00507 } 00508 00509 bool fileOptionImmutable = false; 00510 bool groupOptionImmutable = false; 00511 bool groupSkip = false; 00512 bool foundGettextDomain = false; 00513 TQCString gettextDomain; 00514 00515 int line = 0; 00516 for(; s < eof; s++) 00517 { 00518 line++; 00519 00520 while((s < eof) && isspace(*s) && (*s != '\n')) 00521 s++; //skip leading whitespace, shouldn't happen too often 00522 00523 //skip empty lines, lines starting with # 00524 if ((s < eof) && ((*s == '\n') || (*s == '#'))) 00525 { 00526 sktoeol: //skip till end-of-line 00527 while ((s < eof) && (*s != '\n')) 00528 s++; 00529 continue; // Empty or comment or no keyword 00530 } 00531 const char *startLine = s; 00532 00533 if (*s == '[') //group 00534 { 00535 // In a group [[ and ]] have a special meaning 00536 while ((s < eof) && (*s != '\n')) 00537 { 00538 if (*s == ']') 00539 { 00540 if ((s+1 < eof) && (*(s+1) == ']')) 00541 s++; // Skip "]]" 00542 else 00543 break; 00544 } 00545 00546 s++; // Search till end of group 00547 } 00548 const char *e = s; 00549 while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file 00550 if ((e >= eof) || (*e != ']')) 00551 { 00552 fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line); 00553 continue; 00554 } 00555 // group found; get the group name by taking everything in 00556 // between the brackets 00557 if ((e-startLine == 3) && 00558 (startLine[1] == '$') && 00559 (startLine[2] == 'i')) 00560 { 00561 if (!kde_kiosk_exception) 00562 fileOptionImmutable = true; 00563 continue; 00564 } 00565 00566 aCurrentGroup = decodeGroup(startLine + 1, e - startLine); 00567 //cout<<"found group ["<<aCurrentGroup<<"]"<<endl; 00568 00569 // Backwards compatibility 00570 if (aCurrentGroup == "KDE Desktop Entry") 00571 aCurrentGroup = "Desktop Entry"; 00572 00573 groupOptionImmutable = fileOptionImmutable; 00574 00575 e++; 00576 if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$')) // Option follows 00577 { 00578 if ((*e == 'i') && !kde_kiosk_exception) 00579 { 00580 groupOptionImmutable = true; 00581 } 00582 } 00583 00584 KEntryKey groupKey(aCurrentGroup, 0); 00585 KEntry entry = pConfig->lookupData(groupKey); 00586 groupSkip = entry.bImmutable; 00587 00588 if (groupSkip && !bDefault) 00589 continue; 00590 00591 entry.bImmutable |= groupOptionImmutable; 00592 pConfig->putData(groupKey, entry, false); 00593 00594 if (pWriteBackMap) 00595 { 00596 // add the special group key indicator 00597 (*pWriteBackMap)[groupKey] = entry; 00598 } 00599 00600 continue; 00601 } 00602 if (groupSkip && !bDefault) 00603 goto sktoeol; // Skip entry 00604 00605 00606 bool optionImmutable = groupOptionImmutable; 00607 bool optionDeleted = false; 00608 bool optionExpand = false; 00609 const char *endOfKey = 0, *locale = 0, *elocale = 0; 00610 for (; (s < eof) && (*s != '\n'); s++) 00611 { 00612 if (*s == '=') //find the equal sign 00613 { 00614 if (!endOfKey) 00615 endOfKey = s; 00616 goto haveeq; 00617 } 00618 if (*s == '[') //find the locale or options. 00619 { 00620 const char *option; 00621 const char *eoption; 00622 endOfKey = s; 00623 option = ++s; 00624 for (;; s++) 00625 { 00626 if ((s >= eof) || (*s == '\n') || (*s == '=')) { 00627 fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line); 00628 goto sktoeol; 00629 } 00630 if (*s == ']') 00631 break; 00632 } 00633 eoption = s; 00634 if (*option != '$') 00635 { 00636 // Locale 00637 if (locale) { 00638 fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line); 00639 goto sktoeol; 00640 } 00641 locale = option; 00642 elocale = eoption; 00643 } 00644 else 00645 { 00646 // Option 00647 while (option < eoption) 00648 { 00649 option++; 00650 if ((*option == 'i') && !kde_kiosk_exception) 00651 optionImmutable = true; 00652 else if (*option == 'e') 00653 optionExpand = true; 00654 else if (*option == 'd') 00655 { 00656 optionDeleted = true; 00657 goto haveeq; 00658 } 00659 else if (*option == ']') 00660 break; 00661 } 00662 } 00663 } 00664 } 00665 fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line); 00666 continue; 00667 00668 haveeq: 00669 for (endOfKey--; ; endOfKey--) 00670 { 00671 if (endOfKey < startLine) 00672 { 00673 fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line); 00674 goto sktoeol; 00675 } 00676 if (!isspace(*endOfKey)) 00677 break; 00678 } 00679 00680 const char *st = ++s; 00681 while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file 00682 00683 if (locale) { 00684 unsigned int cl = static_cast<unsigned int>(elocale - locale); 00685 if ((ll != cl) || memcmp(locale, localeString.data(), ll)) 00686 { 00687 // backward compatibility. C == en_US 00688 if ( cl != 1 || ll != 5 || *locale != 'C' || memcmp(localeString.data(), "en_US", 5)) { 00689 //cout<<"mismatched locale '"<<TQCString(locale, elocale-locale +1)<<"'"<<endl; 00690 // We can ignore this one 00691 if (!pWriteBackMap) 00692 continue; // We just ignore it 00693 // We just store it as is to be able to write it back later. 00694 endOfKey = elocale; 00695 locale = 0; 00696 } 00697 } 00698 } 00699 00700 // insert the key/value line 00701 TQCString key(startLine, endOfKey - startLine + 2); 00702 TQCString val = printableToString(st, s - st); 00703 //tqDebug("found key '%s' with value '%s'", key.data(), val.data()); 00704 00705 if (TQString(key.data()) == "X-Ubuntu-Gettext-Domain") { 00706 gettextDomain = val.data(); 00707 foundGettextDomain = true; 00708 } 00709 00710 KEntryKey aEntryKey(aCurrentGroup, decodeKey(key)); 00711 aEntryKey.bLocal = (locale != 0); 00712 aEntryKey.bDefault = bDefault; 00713 00714 KEntry aEntry; 00715 aEntry.mValue = val; 00716 aEntry.bGlobal = bGlobal; 00717 aEntry.bImmutable = optionImmutable; 00718 aEntry.bDeleted = optionDeleted; 00719 aEntry.bExpand = optionExpand; 00720 aEntry.bNLS = (locale != 0); 00721 00722 if (pWriteBackMap) { 00723 // don't insert into the config object but into the temporary 00724 // scratchpad map 00725 pWriteBackMap->insert(aEntryKey, aEntry); 00726 } else { 00727 // directly insert value into config object 00728 // no need to specify localization; if the key we just 00729 // retrieved was localized already, no need to localize it again. 00730 pConfig->putData(aEntryKey, aEntry, false); 00731 } 00732 } 00733 // Look up translations using TDELocale 00734 // https://launchpad.net/distros/ubuntu/+spec/langpacks-desktopfiles-kde 00735 // This calls TDELocale up to 10 times for each config file (and each TDEConfig has up to 4 files) 00736 // so I'll see how much of a performance hit it is 00737 // it also only acts on the last group in a file 00738 // Ideas: only translate most important fields, only translate "Desktop Entry" files, 00739 // do translation per TDEConfig not per single file 00740 if (!pWriteBackMap) { 00741 TQFile file("file.txt"); 00742 if (foundGettextDomain) { 00743 00744 TDELocale locale(gettextDomain); 00745 00746 TQString language = locale.language(); 00747 translateKey(locale, aCurrentGroup, TQCString("Name")); 00748 translateKey(locale, aCurrentGroup, TQCString("Comment")); 00749 translateKey(locale, aCurrentGroup, TQCString("Language")); 00750 translateKey(locale, aCurrentGroup, TQCString("Keywords")); 00751 translateKey(locale, aCurrentGroup, TQCString("About")); 00752 translateKey(locale, aCurrentGroup, TQCString("Description")); 00753 translateKey(locale, aCurrentGroup, TQCString("GenericName")); 00754 translateKey(locale, aCurrentGroup, TQCString("Query")); 00755 translateKey(locale, aCurrentGroup, TQCString("ExtraNames")); 00756 translateKey(locale, aCurrentGroup, TQCString("X-TDE-Submenu")); 00757 } 00758 } 00759 00760 00761 if (fileOptionImmutable) 00762 bFileImmutable = true; 00763 00764 #ifdef HAVE_MMAP 00765 if (map) 00766 { 00767 munmap(( char* )map, rFile.size()); 00768 #ifdef SIGBUS 00769 sigaction (SIGBUS, &mmap_old_sigact, 0); 00770 #endif 00771 } 00772 #endif 00773 } 00774 00775 void TDEConfigINIBackEnd::translateKey(TDELocale& locale, TQCString currentGroup, TQCString key) { 00776 KEntryKey entryKey = KEntryKey(currentGroup, key); 00777 KEntry entry = pConfig->lookupData(entryKey); 00778 if (TQString(entry.mValue) != "") { 00779 TQString orig = key + "=" + entry.mValue; 00780 TQString translate = locale.translate(key + "=" + entry.mValue); 00781 if (TQString::compare(orig, translate) != 0) { 00782 translate = translate.mid(key.length() + 1); 00783 entry.mValue = translate.utf8(); 00784 entryKey.bLocal = true; 00785 entry.bNLS = true; 00786 pConfig->putData(entryKey, entry, false); 00787 } 00788 } 00789 } 00790 00791 void TDEConfigINIBackEnd::sync(bool bMerge) 00792 { 00793 // write-sync is only necessary if there are dirty entries 00794 if (!pConfig->isDirty()) 00795 return; 00796 00797 bool bEntriesLeft = true; 00798 00799 // find out the file to write to (most specific writable file) 00800 // try local app-specific file first 00801 00802 if (!mfileName.isEmpty()) { 00803 // Create the containing dir if needed 00804 if ((resType!="config") && !TQDir::isRelativePath(mLocalFileName)) 00805 { 00806 KURL path; 00807 path.setPath(mLocalFileName); 00808 TQString dir=path.directory(); 00809 TDEStandardDirs::makeDir(dir); 00810 } 00811 00812 // Can we allow the write? We can, if the program 00813 // doesn't run SUID. But if it runs SUID, we must 00814 // check if the user would be allowed to write if 00815 // it wasn't SUID. 00816 if (checkAccess(mLocalFileName, W_OK)) { 00817 // File is writable 00818 TDELockFile::Ptr lf; 00819 00820 bool mergeLocalFile = bMerge; 00821 // Check if the file has been updated since. 00822 if (mergeLocalFile) 00823 { 00824 lf = lockFile(false); // Lock file for local file 00825 if (lf && lf->isLocked()) 00826 lf = 0; // Already locked, we don't need to lock/unlock again 00827 00828 if (lf) 00829 { 00830 lf->lock( TDELockFile::LockForce ); 00831 // But what if the locking failed? Ignore it for now... 00832 } 00833 00834 TQFileInfo info(mLocalFileName); 00835 if ((d->localLastSize == info.size()) && 00836 (d->localLastModified == info.lastModified())) 00837 { 00838 // Not changed, don't merge. 00839 mergeLocalFile = false; 00840 } 00841 else 00842 { 00843 // Changed... 00844 d->localLastModified = TQDateTime(); 00845 d->localLastSize = 0; 00846 } 00847 } 00848 00849 bEntriesLeft = writeConfigFile( mLocalFileName, false, mergeLocalFile ); 00850 00851 // Only if we didn't have to merge anything can we use our in-memory state 00852 // the next time around. Otherwise the config-file may contain entries 00853 // that are different from our in-memory state which means we will have to 00854 // do a merge from then on. 00855 // We do not automatically update the in-memory state with the on-disk 00856 // state when writing the config to disk. We only do so when 00857 // KCOnfig::reparseConfiguration() is called. 00858 // For KDE 4.0 we may wish to reconsider that. 00859 if (!mergeLocalFile) 00860 { 00861 TQFileInfo info(mLocalFileName); 00862 d->localLastModified = info.lastModified(); 00863 d->localLastSize = info.size(); 00864 } 00865 if (lf) lf->unlock(); 00866 } 00867 } 00868 00869 // only write out entries to the kdeglobals file if there are any 00870 // entries marked global (indicated by bEntriesLeft) and 00871 // the useKDEGlobals flag is set. 00872 if (bEntriesLeft && useKDEGlobals) { 00873 00874 // can we allow the write? (see above) 00875 if (checkAccess ( mGlobalFileName, W_OK )) { 00876 TDELockFile::Ptr lf = lockFile(true); // Lock file for global file 00877 if (lf && lf->isLocked()) 00878 lf = 0; // Already locked, we don't need to lock/unlock again 00879 00880 if (lf) 00881 { 00882 lf->lock( TDELockFile::LockForce ); 00883 // But what if the locking failed? Ignore it for now... 00884 } 00885 writeConfigFile( mGlobalFileName, true, bMerge ); // Always merge 00886 if (lf) lf->unlock(); 00887 } 00888 } 00889 00890 } 00891 00892 static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const TQCString &localeString) 00893 { 00894 // now write out all other groups. 00895 TQCString currentGroup; 00896 for (KEntryMapConstIterator aIt = entryMap.begin(); 00897 aIt != entryMap.end(); ++aIt) 00898 { 00899 const KEntryKey &key = aIt.key(); 00900 00901 // Either proces the default group or all others 00902 if ((key.mGroup != "<default>") == defaultGroup) 00903 continue; // Skip 00904 00905 // Skip default values and group headers. 00906 if ((key.bDefault) || key.mKey.isEmpty()) 00907 continue; // Skip 00908 00909 const KEntry ¤tEntry = *aIt; 00910 00911 KEntryMapConstIterator aTestIt = aIt; 00912 ++aTestIt; 00913 bool hasDefault = (aTestIt != entryMap.end()); 00914 if (hasDefault) 00915 { 00916 const KEntryKey &defaultKey = aTestIt.key(); 00917 if ((!defaultKey.bDefault) || 00918 (defaultKey.mKey != key.mKey) || 00919 (defaultKey.mGroup != key.mGroup) || 00920 (defaultKey.bLocal != key.bLocal)) 00921 hasDefault = false; 00922 } 00923 00924 00925 if (hasDefault) 00926 { 00927 // Entry had a default value 00928 if ((currentEntry.mValue == (*aTestIt).mValue) && 00929 (currentEntry.bDeleted == (*aTestIt).bDeleted)) 00930 continue; // Same as default, don't write. 00931 } 00932 else 00933 { 00934 // Entry had no default value. 00935 if (currentEntry.bDeleted) 00936 continue; // Don't write deleted entries if there is no default. 00937 } 00938 00939 if (!defaultGroup && (currentGroup != key.mGroup)) { 00940 if (!firstEntry) 00941 fprintf(pStream, "\n"); 00942 currentGroup = key.mGroup; 00943 fprintf(pStream, "[%s]\n", encodeGroup(currentGroup).data()); 00944 } 00945 00946 firstEntry = false; 00947 // it is data for a group 00948 fputs(encodeKey(key.mKey.data()), pStream); // Key 00949 00950 if ( currentEntry.bNLS ) 00951 { 00952 fputc('[', pStream); 00953 fputs(localeString.data(), pStream); 00954 fputc(']', pStream); 00955 } 00956 00957 if (currentEntry.bDeleted) 00958 { 00959 fputs("[$d]\n", pStream); // Deleted 00960 } 00961 else 00962 { 00963 if (currentEntry.bImmutable || currentEntry.bExpand) 00964 { 00965 fputc('[', pStream); 00966 fputc('$', pStream); 00967 if (currentEntry.bImmutable) 00968 fputc('i', pStream); 00969 if (currentEntry.bExpand) 00970 fputc('e', pStream); 00971 00972 fputc(']', pStream); 00973 } 00974 fputc('=', pStream); 00975 fputs(stringToPrintable(currentEntry.mValue).data(), pStream); 00976 fputc('\n', pStream); 00977 } 00978 } // for loop 00979 } 00980 00981 bool TDEConfigINIBackEnd::getEntryMap(KEntryMap &aTempMap, bool bGlobal, 00982 TQFile *mergeFile) 00983 { 00984 bool bEntriesLeft = false; 00985 bFileImmutable = false; 00986 00987 // Read entries from disk 00988 if (mergeFile && mergeFile->open(IO_ReadOnly)) 00989 { 00990 // fill the temporary structure with entries from the file 00991 parseSingleConfigFile(*mergeFile, &aTempMap, bGlobal, false ); 00992 00993 if (bFileImmutable) // File has become immutable on disk 00994 return bEntriesLeft; 00995 } 00996 00997 KEntryMap aMap = pConfig->internalEntryMap(); 00998 00999 // augment this structure with the dirty entries from the config object 01000 for (KEntryMapIterator aIt = aMap.begin(); 01001 aIt != aMap.end(); ++aIt) 01002 { 01003 const KEntry ¤tEntry = *aIt; 01004 if(aIt.key().bDefault) 01005 { 01006 aTempMap.replace(aIt.key(), currentEntry); 01007 continue; 01008 } 01009 01010 if (mergeFile && !currentEntry.bDirty) 01011 continue; 01012 01013 // only write back entries that have the same 01014 // "globality" as the file 01015 if (currentEntry.bGlobal != bGlobal) 01016 { 01017 // wrong "globality" - might have to be saved later 01018 bEntriesLeft = true; 01019 continue; 01020 } 01021 01022 // put this entry from the config object into the 01023 // temporary map, possibly replacing an existing entry 01024 KEntryMapIterator aIt2 = aTempMap.find(aIt.key()); 01025 if (aIt2 != aTempMap.end() && (*aIt2).bImmutable) 01026 continue; // Bail out if the on-disk entry is immutable 01027 01028 aTempMap.insert(aIt.key(), currentEntry, true); 01029 } // loop 01030 01031 return bEntriesLeft; 01032 } 01033 01034 /* antlarr: KDE 4.0: make the first parameter "const TQString &" */ 01035 bool TDEConfigINIBackEnd::writeConfigFile(TQString filename, bool bGlobal, 01036 bool bMerge) 01037 { 01038 // is the config object read-only? 01039 if (pConfig->isReadOnly()) 01040 return true; // pretend we wrote it 01041 01042 KEntryMap aTempMap; 01043 TQFile *mergeFile = (bMerge ? new TQFile(filename) : 0); 01044 bool bEntriesLeft = getEntryMap(aTempMap, bGlobal, mergeFile); 01045 delete mergeFile; 01046 if (bFileImmutable) 01047 return true; // pretend we wrote it 01048 01049 // OK now the temporary map should be full of ALL entries. 01050 // write it out to disk. 01051 01052 // Check if file exists: 01053 int fileMode = -1; 01054 bool createNew = true; 01055 01056 KDE_struct_stat buf; 01057 if (KDE_stat(TQFile::encodeName(filename), &buf) == 0) 01058 { 01059 if (buf.st_uid == getuid()) 01060 { 01061 // Preserve file mode if file exists and is owned by user. 01062 fileMode = buf.st_mode & 0777; 01063 } 01064 else 01065 { 01066 // File is not owned by user: 01067 // Don't create new file but write to existing file instead. 01068 createNew = false; 01069 } 01070 } 01071 01072 KSaveFile *pConfigFile = 0; 01073 FILE *pStream = 0; 01074 01075 if (createNew) 01076 { 01077 pConfigFile = new KSaveFile( filename, 0600 ); 01078 01079 if (pConfigFile->status() != 0) 01080 { 01081 delete pConfigFile; 01082 return bEntriesLeft; 01083 } 01084 01085 if (!bGlobal && (fileMode == -1)) 01086 fileMode = mFileMode; 01087 01088 if (fileMode != -1) 01089 { 01090 fchmod(pConfigFile->handle(), fileMode); 01091 } 01092 01093 pStream = pConfigFile->fstream(); 01094 } 01095 else 01096 { 01097 // Open existing file. 01098 // We use open() to ensure that we call without O_CREAT. 01099 int fd = KDE_open( TQFile::encodeName(filename), O_WRONLY | O_TRUNC ); 01100 if (fd < 0) 01101 { 01102 return bEntriesLeft; 01103 } 01104 pStream = KDE_fdopen( fd, "w"); 01105 if (!pStream) 01106 { 01107 close(fd); 01108 return bEntriesLeft; 01109 } 01110 } 01111 01112 writeEntries(pStream, aTempMap); 01113 01114 if (pConfigFile) 01115 { 01116 bool bEmptyFile = (ftell(pStream) == 0); 01117 if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) ) 01118 { 01119 // File is empty and doesn't have special permissions: delete it. 01120 ::unlink(TQFile::encodeName(filename)); 01121 pConfigFile->abort(); 01122 } 01123 else 01124 { 01125 // Normal case: Close the file 01126 pConfigFile->close(); 01127 } 01128 delete pConfigFile; 01129 } 01130 else 01131 { 01132 fclose(pStream); 01133 } 01134 01135 return bEntriesLeft; 01136 } 01137 01138 void TDEConfigINIBackEnd::writeEntries(FILE *pStream, const KEntryMap &aTempMap) 01139 { 01140 bool firstEntry = true; 01141 01142 // Write default group 01143 ::writeEntries(pStream, aTempMap, true, firstEntry, localeString); 01144 01145 // Write all other groups 01146 ::writeEntries(pStream, aTempMap, false, firstEntry, localeString); 01147 } 01148 01149 void TDEConfigBackEnd::virtual_hook( int, void* ) 01150 { /*BASE::virtual_hook( id, data );*/ } 01151 01152 void TDEConfigINIBackEnd::virtual_hook( int id, void* data ) 01153 { TDEConfigBackEnd::virtual_hook( id, data ); } 01154 01155 bool TDEConfigBackEnd::checkConfigFilesWritable(bool warnUser) 01156 { 01157 // WARNING: Do NOT use the event loop as it may not exist at this time. 01158 bool allWritable = true; 01159 TQString errorMsg; 01160 if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) ) 01161 { 01162 errorMsg = i18n("Will not save configuration.\n"); 01163 allWritable = false; 01164 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mLocalFileName); 01165 } 01166 // We do not have an immutability flag for kdeglobals. However, making kdeglobals mutable while making 01167 // the local config file immutable is senseless. 01168 if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) ) 01169 { 01170 if ( errorMsg.isEmpty() ) 01171 errorMsg = i18n("Will not save configuration.\n"); 01172 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mGlobalFileName); 01173 allWritable = false; 01174 } 01175 01176 if (warnUser && !allWritable) 01177 { 01178 // Note: We don't ask the user if we should not ask this question again because we can't save the answer. 01179 errorMsg += i18n("Please contact your system administrator."); 01180 TQString cmdToExec = TDEStandardDirs::findExe(TQString("kdialog")); 01181 TDEApplication *app = kapp; 01182 if (!cmdToExec.isEmpty() && app) 01183 { 01184 TDEProcess lprocess; 01185 lprocess << cmdToExec << "--title" << app->instanceName() << "--msgbox" << TQCString(errorMsg.local8Bit()); 01186 lprocess.start( TDEProcess::Block ); 01187 } 01188 } 01189 return allWritable; 01190 }