File indexing completed on 2023-09-24 04:04:54
0001 /* This file is part of the KDE libraries 0002 Copyright (C) 1999 Sirtaj Singh Kang <taj@kde.org> 0003 Copyright (C) 1999,2007 Stephan Kulow <coolo@kde.org> 0004 Copyright (C) 1999 Waldo Bastian <bastian@kde.org> 0005 Copyright (C) 2009 David Faure <faure@kde.org> 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License version 2 as published by the Free Software Foundation. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 /* 0023 * Author: Stephan Kulow <coolo@kde.org> and Sirtaj Singh Kang <taj@kde.org> 0024 * Generated: Thu Mar 5 16:05:28 EST 1998 0025 */ 0026 0027 #include "kstandarddirs.h" 0028 #include "kconfig.h" 0029 #include "kconfiggroup.h" 0030 #include "kdebug.h" 0031 #include "kglobal.h" 0032 #include "kshell.h" 0033 #include "kuser.h" 0034 #include "kde_file.h" 0035 #include "kkernel_win.h" 0036 #include "kkernel_mac.h" 0037 #include "klocalizedstring.h" 0038 #include <kservice_export.h> 0039 0040 #include <config-kstandarddirs.h> 0041 #include <config-kdelibs4support.h> 0042 #include <stdlib.h> 0043 #include <assert.h> 0044 #include <errno.h> 0045 #if HAVE_SYS_STAT_H 0046 #include <sys/stat.h> 0047 #endif 0048 #if HAVE_UNISTD_H 0049 #include <unistd.h> 0050 #endif 0051 #include <sys/param.h> 0052 #include <sys/types.h> 0053 #include <dirent.h> 0054 #include <pwd.h> 0055 #include <grp.h> 0056 #ifdef Q_OS_WIN 0057 #include <windows.h> 0058 #ifdef _WIN32_WCE 0059 #include <basetyps.h> 0060 #endif 0061 #ifdef Q_OS_WIN64 0062 // FIXME: did not find a reliable way to fix with kdewin mingw header 0063 #define interface struct 0064 #endif 0065 #include <shlobj.h> 0066 #include <QVarLengthArray> 0067 #endif 0068 0069 #include <QMutex> 0070 #include <QRegExp> 0071 #include <QDir> 0072 #include <QFileInfo> 0073 #include <QSettings> 0074 #include <QCoreApplication> 0075 0076 #ifndef MAXPATHLEN 0077 # define MAXPATHLEN 4096 0078 #endif 0079 0080 #ifdef Q_OS_WIN 0081 static const Qt::CaseSensitivity cs = Qt::CaseInsensitive; 0082 #else 0083 static const Qt::CaseSensitivity cs = Qt::CaseSensitive; 0084 #endif 0085 0086 class Q_DECL_HIDDEN KStandardDirs::KStandardDirsPrivate 0087 { 0088 public: 0089 KStandardDirsPrivate(KStandardDirs *qq) 0090 : m_restrictionsActive(false), 0091 m_checkRestrictions(true), 0092 m_cacheMutex(QMutex::Recursive), // resourceDirs is recursive 0093 q(qq) 0094 { } 0095 0096 bool hasDataRestrictions(const QString &relPath) const; 0097 QStringList resourceDirs(const char *type, const QString &subdirForRestrictions); 0098 0099 bool m_restrictionsActive : 1; 0100 bool m_checkRestrictions : 1; 0101 QMap<QByteArray, bool> m_restrictions; 0102 0103 QStringList xdgdata_prefixes; 0104 QStringList xdgconf_prefixes; 0105 QStringList m_prefixes; 0106 0107 // Directory dictionaries 0108 QMap<QByteArray, QStringList> m_absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global 0109 QMap<QByteArray, QStringList> m_relatives; // Same with relative paths 0110 // The search path is "all relative paths" < "all absolute paths", from most priority to least priority. 0111 0112 // Caches (protected by mutex in const methods, cf ctor docu) 0113 QMap<QByteArray, QStringList> m_dircache; 0114 QMap<QByteArray, QString> m_savelocations; 0115 QMutex m_cacheMutex; 0116 0117 KStandardDirs *q; 0118 }; 0119 0120 /* If you add a new resource type here, make sure to 0121 * 1) regenerate using "kdesdk/scripts/generate_string_table.pl types < tmpfile" with the data below 0122 * in tmpfile (no empty line at the beginning or at the end!) 0123 * 2) update the KStandardDirs class documentation 0124 * 3) update the list in kde-config.cpp 0125 * 4) update the installPath() function below 0126 0127 data 0128 . 0129 html 0130 doc/HTML 0131 icon 0132 icons 0133 config 0134 config 0135 pixmap 0136 pixmaps 0137 sound 0138 sounds 0139 locale 0140 locale 0141 services 0142 kservices5 0143 servicetypes 0144 kservicetypes5 0145 wallpaper 0146 wallpapers 0147 templates 0148 templates 0149 exe 0150 bin 0151 module 0152 %lib/plugins/kf5 0153 qtplugins 0154 %lib/plugins 0155 kcfg 0156 config.kcfg 0157 emoticons 0158 emoticons 0159 xdgdata 0160 . 0161 xdgdata-apps 0162 applications 0163 xdgdata-icon 0164 icons 0165 xdgdata-pixmap 0166 pixmaps 0167 xdgdata-dirs 0168 desktop-directories 0169 xdgdata-mime 0170 mime 0171 xdgconf 0172 . 0173 xdgconf-menu 0174 menus 0175 xdgconf-autostart 0176 autostart 0177 */ 0178 0179 static const char types_string[] = 0180 "data\0" 0181 ".\0" 0182 "html\0" 0183 "doc/HTML\0" 0184 "icon\0" 0185 "icons\0" 0186 "config\0" 0187 "pixmap\0" 0188 "pixmaps\0" 0189 "sound\0" 0190 "sounds\0" 0191 "locale\0" 0192 "services\0" 0193 "kservices5\0" 0194 "servicetypes\0" 0195 "kservicetypes5\0" 0196 "wallpaper\0" 0197 "wallpapers\0" 0198 "templates\0" 0199 "exe\0" 0200 "bin\0" 0201 "module\0" 0202 "%lib/plugins/kf5\0" 0203 "qtplugins\0" 0204 "%lib/plugins\0" 0205 "kcfg\0" 0206 "config.kcfg\0" 0207 "emoticons\0" 0208 "xdgdata\0" 0209 "xdgdata-apps\0" 0210 "applications\0" 0211 "xdgdata-icon\0" 0212 "xdgdata-pixmap\0" 0213 "xdgdata-dirs\0" 0214 "desktop-directories\0" 0215 "xdgdata-mime\0" 0216 "mime\0" 0217 "xdgconf\0" 0218 "xdgconf-menu\0" 0219 "menus\0" 0220 "xdgconf-autostart\0" 0221 "autostart\0" 0222 "\0"; 0223 0224 static const int types_indices[] = { 0225 0, 5, 7, 12, 21, 26, 32, 32, 0226 39, 46, 54, 60, 67, 67, 74, 83, 0227 94, 107, 122, 132, 143, 143, 153, 157, 0228 161, 168, 185, 195, 208, 213, 225, 225, 0229 235, 5, 243, 256, 269, 26, 282, 46, 0230 297, 310, 330, 343, 348, 5, 356, 369, 0231 375, 393, -1 0232 }; 0233 0234 static void tokenize(QStringList &token, const QString &str, 0235 const QString &delim); 0236 0237 enum BasePrefix { XdgConf, XdgData, KdePrefixes }; 0238 static BasePrefix basePrefixForResource(const char *type) 0239 { 0240 // KF5: We now use xdgdata_prefixes for every resource in share/*, 0241 // i.e. everything except exe, lib, config and xdgconf... 0242 0243 // TODO: exe: replaced with $PATH 0244 // lib: unused as is, right? 0245 // module: based on lib, but mostly replaced with QT_PLUGIN_PATH (+"/kf5") 0246 0247 const QByteArray typeBa(type); 0248 if (typeBa.startsWith("xdgconf") || typeBa == "config") { 0249 return XdgConf; 0250 } else if (typeBa == "exe" || typeBa == "lib") { 0251 return KdePrefixes; 0252 } else { // was: if (typeBa.startsWith("xdgdata") || typeBa == "data") 0253 return XdgData; 0254 } 0255 } 0256 0257 #ifdef Q_OS_WIN 0258 QString getKde4Prefix(); 0259 #endif 0260 0261 static QString relativeInstallPath(const char *type) 0262 { 0263 Q_ASSERT(type != nullptr); 0264 0265 switch (type[0]) { 0266 case 'c': 0267 if (strcmp("config", type) == 0) { 0268 return QFile::decodeName(CONFIG_INSTALL_DIR "/"); 0269 } 0270 break; 0271 case 'k': 0272 if (strcmp("kcfg", type) == 0) { 0273 return QFile::decodeName(KCFG_INSTALL_DIR "/"); 0274 } 0275 if (strcmp("kdedir", type) == 0) { 0276 return QString::fromLatin1(""); // not null! 0277 } 0278 break; 0279 case 'd': 0280 if (strcmp("data", type) == 0) { 0281 return QFile::decodeName(DATA_INSTALL_DIR "/"); 0282 } 0283 break; 0284 case 'e': 0285 if (strcmp("exe", type) == 0) { 0286 return QFile::decodeName(BIN_INSTALL_DIR "/"); 0287 } 0288 break; 0289 case 'h': 0290 if (strcmp("html", type) == 0) { 0291 return QFile::decodeName(HTML_INSTALL_DIR "/"); 0292 } 0293 break; 0294 case 'i': 0295 if (strcmp("icon", type) == 0) { 0296 return QFile::decodeName(ICON_INSTALL_DIR "/"); 0297 } 0298 if (strcmp("include", type) == 0) { 0299 return QFile::decodeName(INCLUDE_INSTALL_DIR "/"); 0300 } 0301 break; 0302 case 'l': 0303 if (strcmp("lib", type) == 0) { 0304 return QFile::decodeName(LIB_INSTALL_DIR "/"); 0305 } 0306 if (strcmp("libexec", type) == 0) { 0307 return QFile::decodeName(LIBEXEC_INSTALL_DIR "/"); 0308 } 0309 if (strcmp("locale", type) == 0) { 0310 return QFile::decodeName(LOCALE_INSTALL_DIR "/"); 0311 } 0312 break; 0313 case 'm': 0314 if (strcmp("module", type) == 0) { 0315 return QFile::decodeName(PLUGIN_INSTALL_DIR "/"); 0316 } 0317 break; 0318 case 'q': 0319 if (strcmp("qtplugins", type) == 0) { 0320 return QFile::decodeName(PLUGIN_INSTALL_DIR "/"); 0321 } 0322 break; 0323 case 's': 0324 if (strcmp("services", type) == 0) { 0325 return QFile::decodeName(SERVICES_INSTALL_DIR "/"); 0326 } 0327 if (strcmp("servicetypes", type) == 0) { 0328 return QFile::decodeName(SERVICETYPES_INSTALL_DIR "/"); 0329 } 0330 if (strcmp("sound", type) == 0) { 0331 return QFile::decodeName(SOUND_INSTALL_DIR "/"); 0332 } 0333 break; 0334 case 't': 0335 if (strcmp("templates", type) == 0) { 0336 return QFile::decodeName(TEMPLATES_INSTALL_DIR "/"); 0337 } 0338 break; 0339 case 'w': 0340 if (strcmp("wallpaper", type) == 0) { 0341 return QFile::decodeName(WALLPAPER_INSTALL_DIR "/"); 0342 } 0343 break; 0344 case 'x': 0345 if (strcmp("xdgconf-menu", type) == 0) { 0346 return QFile::decodeName(SYSCONF_INSTALL_DIR "/xdg/menus/"); 0347 } 0348 if (strcmp("xdgdata-apps", type) == 0) { 0349 return QFile::decodeName(XDG_APPS_INSTALL_DIR "/"); 0350 } 0351 if (strcmp("xdgdata-dirs", type) == 0) { 0352 return QFile::decodeName(XDG_DIRECTORY_INSTALL_DIR "/"); 0353 } 0354 break; 0355 } 0356 return QString(); 0357 } 0358 0359 QString KStandardDirs::installPath(const char *type) 0360 { 0361 const QString relPath = relativeInstallPath(type); 0362 if (relPath.isNull()) { 0363 return QString(); 0364 } else if (QDir::isAbsolutePath(relPath)) { 0365 return relPath; 0366 } else { 0367 #ifdef Q_OS_WIN 0368 return getKde4Prefix() + relPath; 0369 #else 0370 return QFile::decodeName(CMAKE_INSTALL_PREFIX "/") + relPath; 0371 #endif 0372 } 0373 } 0374 0375 KStandardDirs::KStandardDirs() 0376 : d(new KStandardDirsPrivate(this)) 0377 { 0378 addKDEDefaults(); 0379 } 0380 0381 KStandardDirs::~KStandardDirs() 0382 { 0383 delete d; 0384 } 0385 0386 bool KStandardDirs::isRestrictedResource(const char *type, const QString &relPath) const 0387 { 0388 if (!d->m_restrictionsActive) { 0389 return false; 0390 } 0391 0392 if (d->m_restrictions.value(type, false)) { 0393 return true; 0394 } 0395 0396 if (strcmp(type, "data") == 0 && d->hasDataRestrictions(relPath)) { 0397 return true; 0398 } 0399 0400 return false; 0401 } 0402 0403 bool KStandardDirs::KStandardDirsPrivate::hasDataRestrictions(const QString &relPath) const 0404 { 0405 QString key; 0406 const int i = relPath.indexOf(QLatin1Char('/')); 0407 if (i != -1) { 0408 key = QString::fromLatin1("data_") + relPath.left(i); 0409 } else { 0410 key = QString::fromLatin1("data_") + relPath; 0411 } 0412 0413 return m_restrictions.value(key.toLatin1(), false); 0414 } 0415 0416 QStringList KStandardDirs::allTypes() const 0417 { 0418 QStringList list; 0419 for (int i = 0; types_indices[i] != -1; i += 2) { 0420 list.append(QLatin1String(types_string + types_indices[i])); 0421 } 0422 // Those are added manually by addKDEDefaults 0423 list.append(QString::fromLatin1("lib")); 0424 //list.append(QString::fromLatin1("home")); // undocumented on purpose, said Waldo in r113855. 0425 0426 // Those are handled by resourceDirs() itself 0427 list.append(QString::fromLatin1("socket")); 0428 list.append(QString::fromLatin1("tmp")); 0429 list.append(QString::fromLatin1("cache")); 0430 // Those are handled by installPath() 0431 list.append(QString::fromLatin1("include")); 0432 0433 // If you add anything here, make sure kde-config.cpp has a description for it. 0434 0435 return list; 0436 } 0437 0438 static void priorityAdd(QStringList &prefixes, const QString &dir, bool priority) 0439 { 0440 if (priority && !prefixes.isEmpty()) { 0441 // Add in front but behind the most-local prefix 0442 QStringList::iterator it = prefixes.begin(); 0443 ++it; 0444 prefixes.insert(it, dir); 0445 } else { 0446 prefixes.append(dir); 0447 } 0448 } 0449 0450 void KStandardDirs::addPrefix(const QString &_dir) 0451 { 0452 addPrefix(_dir, false); 0453 } 0454 0455 void KStandardDirs::addPrefix(const QString &_dir, bool priority) 0456 { 0457 if (_dir.isEmpty()) { 0458 return; 0459 } 0460 0461 QString dir = _dir; 0462 if (dir.at(dir.length() - 1) != QLatin1Char('/')) { 0463 dir += QLatin1Char('/'); 0464 } 0465 0466 if (!d->m_prefixes.contains(dir, cs)) { 0467 priorityAdd(d->m_prefixes, dir, priority); 0468 d->m_dircache.clear(); 0469 } 0470 } 0471 0472 void KStandardDirs::addXdgConfigPrefix(const QString &_dir) 0473 { 0474 addXdgConfigPrefix(_dir, false); 0475 } 0476 0477 void KStandardDirs::addXdgConfigPrefix(const QString &_dir, bool priority) 0478 { 0479 if (_dir.isEmpty()) { 0480 return; 0481 } 0482 0483 QString dir = _dir; 0484 if (dir.at(dir.length() - 1) != QLatin1Char('/')) { 0485 dir += QLatin1Char('/'); 0486 } 0487 0488 if (!d->xdgconf_prefixes.contains(dir, cs)) { 0489 priorityAdd(d->xdgconf_prefixes, dir, priority); 0490 d->m_dircache.clear(); 0491 } 0492 } 0493 0494 void KStandardDirs::addXdgDataPrefix(const QString &_dir) 0495 { 0496 addXdgDataPrefix(_dir, false); 0497 } 0498 0499 void KStandardDirs::addXdgDataPrefix(const QString &_dir, bool priority) 0500 { 0501 if (_dir.isEmpty()) { 0502 return; 0503 } 0504 0505 QString dir = _dir; 0506 if (dir.at(dir.length() - 1) != QLatin1Char('/')) { 0507 dir += QLatin1Char('/'); 0508 } 0509 0510 if (!d->xdgdata_prefixes.contains(dir, cs)) { 0511 priorityAdd(d->xdgdata_prefixes, dir, priority); 0512 d->m_dircache.clear(); 0513 } 0514 } 0515 0516 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0517 QString KStandardDirs::kfsstnd_prefixes() 0518 { 0519 return d->m_prefixes.join(QString(QLatin1Char(':'))); 0520 } 0521 0522 bool KStandardDirs::addResourceType(const char *type, 0523 const QString &relativename, 0524 bool priority) 0525 { 0526 return addResourceType(type, nullptr, relativename, priority); 0527 } 0528 #endif 0529 0530 bool KStandardDirs::addResourceType(const char *type, 0531 const char *basetype, 0532 const QString &relativename, 0533 bool priority) 0534 { 0535 if (relativename.isEmpty()) { 0536 return false; 0537 } 0538 0539 QString copy = relativename; 0540 if (basetype) { 0541 copy = QLatin1Char('%') + QString::fromLatin1(basetype) + QLatin1Char('/'); 0542 if (relativename != QLatin1String("/")) { 0543 copy += relativename; 0544 } 0545 } 0546 0547 if (!copy.endsWith(QLatin1Char('/'))) { 0548 copy += QLatin1Char('/'); 0549 } 0550 0551 QByteArray typeBa = type; 0552 QStringList &rels = d->m_relatives[typeBa]; // find or insert 0553 0554 if (!rels.contains(copy, cs)) { 0555 if (priority) { 0556 rels.prepend(copy); 0557 } else { 0558 rels.append(copy); 0559 } 0560 // clean the caches 0561 d->m_dircache.remove(typeBa); 0562 d->m_savelocations.remove(typeBa); 0563 return true; 0564 } 0565 return false; 0566 } 0567 0568 bool KStandardDirs::addResourceDir(const char *type, 0569 const QString &absdir, 0570 bool priority) 0571 { 0572 if (absdir.isEmpty() || !type) { 0573 return false; 0574 } 0575 // find or insert entry in the map 0576 QString copy = absdir; 0577 if (copy.at(copy.length() - 1) != QLatin1Char('/')) { 0578 copy += QLatin1Char('/'); 0579 } 0580 0581 QByteArray typeBa = type; 0582 QStringList &paths = d->m_absolutes[typeBa]; 0583 if (!paths.contains(copy, cs)) { 0584 if (priority) { 0585 paths.prepend(copy); 0586 } else { 0587 paths.append(copy); 0588 } 0589 // clean the caches 0590 d->m_dircache.remove(typeBa); 0591 d->m_savelocations.remove(typeBa); 0592 return true; 0593 } 0594 return false; 0595 } 0596 0597 QString KStandardDirs::findResource(const char *type, 0598 const QString &_filename) const 0599 { 0600 if (!QDir::isRelativePath(_filename)) { 0601 // absolute dirs are absolute dirs, right? :-/ 0602 return KLocalizedString::localizedFilePath(_filename); // -- almost. 0603 } 0604 0605 #if 0 0606 kDebug(180) << "Find resource: " << type; 0607 for (QStringList::ConstIterator pit = m_prefixes.begin(); 0608 pit != m_prefixes.end(); 0609 ++pit) { 0610 kDebug(180) << "Prefix: " << *pit; 0611 } 0612 #endif 0613 0614 QString filename(_filename); 0615 #ifdef Q_OS_WIN 0616 if (strcmp(type, "exe") == 0) { 0617 if (!filename.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) { 0618 filename += QLatin1String(".exe"); 0619 } 0620 } 0621 #endif 0622 const QString dir = findResourceDir(type, filename); 0623 if (dir.isEmpty()) { 0624 return dir; 0625 } else { 0626 return KLocalizedString::localizedFilePath(dir + filename); 0627 } 0628 } 0629 0630 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0631 static quint32 updateHash(const QString &file, quint32 hash) 0632 { 0633 KDE_struct_stat buff; 0634 if ((KDE::access(file, R_OK) == 0) && (KDE::stat(file, &buff) == 0) && (S_ISREG(buff.st_mode))) { 0635 hash = hash + static_cast<quint32>(buff.st_ctime); 0636 } 0637 return hash; 0638 } 0639 0640 quint32 KStandardDirs::calcResourceHash(const char *type, 0641 const QString &filename, 0642 SearchOptions options) const 0643 { 0644 quint32 hash = 0; 0645 0646 if (!QDir::isRelativePath(filename)) { 0647 // absolute dirs are absolute dirs, right? :-/ 0648 return updateHash(filename, hash); 0649 } 0650 QStringList candidates = d->resourceDirs(type, filename); 0651 0652 foreach (const QString &candidate, candidates) { 0653 hash = updateHash(candidate + filename, hash); 0654 if (!(options & Recursive) && hash) { 0655 return hash; 0656 } 0657 } 0658 return hash; 0659 } 0660 #endif 0661 0662 QStringList KStandardDirs::findDirs(const char *type, 0663 const QString &reldir) const 0664 { 0665 QDir testdir; 0666 QStringList list; 0667 if (!QDir::isRelativePath(reldir)) { 0668 testdir.setPath(reldir); 0669 if (testdir.exists()) { 0670 if (reldir.endsWith(QLatin1Char('/'))) { 0671 list.append(reldir); 0672 } else { 0673 list.append(reldir + QLatin1Char('/')); 0674 } 0675 } 0676 return list; 0677 } 0678 0679 const QStringList candidates = d->resourceDirs(type, reldir); 0680 0681 for (QStringList::ConstIterator it = candidates.begin(); 0682 it != candidates.end(); ++it) { 0683 testdir.setPath(*it + reldir); 0684 if (testdir.exists()) { 0685 list.append(testdir.absolutePath() + QLatin1Char('/')); 0686 } 0687 } 0688 0689 return list; 0690 } 0691 0692 QString KStandardDirs::findResourceDir(const char *type, 0693 const QString &_filename) const 0694 { 0695 #ifndef NDEBUG 0696 if (_filename.isEmpty()) { 0697 kWarning() << "filename for type " << type << " in KStandardDirs::findResourceDir is not supposed to be empty!!"; 0698 return QString(); 0699 } 0700 #endif 0701 0702 QString filename(_filename); 0703 #ifdef Q_OS_WIN 0704 if (strcmp(type, "exe") == 0) { 0705 if (!filename.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) { 0706 filename += QLatin1String(".exe"); 0707 } 0708 } 0709 #endif 0710 const QStringList candidates = d->resourceDirs(type, filename); 0711 0712 for (QStringList::ConstIterator it = candidates.begin(); 0713 it != candidates.end(); ++it) { 0714 if (exists(*it + filename)) { 0715 return *it; 0716 } 0717 } 0718 0719 #ifndef NDEBUG 0720 if (false && strcmp(type, "locale")) { 0721 kDebug(180) << "KStdDirs::findResDir(): can't find \"" << filename << "\" in type \"" << type << "\"."; 0722 } 0723 #endif 0724 0725 return QString(); 0726 } 0727 0728 bool KStandardDirs::exists(const QString &fullPath) 0729 { 0730 #ifdef Q_OS_WIN 0731 // access() and stat() give a stupid error message to the user 0732 // if the path is not accessible at all (e.g. no disk in A:/ and 0733 // we do stat("A:/.directory") 0734 if (fullPath.endsWith(QLatin1Char('/'))) { 0735 return QDir(fullPath).exists(); 0736 } 0737 return QFileInfo(fullPath).exists(); 0738 #else 0739 KDE_struct_stat buff; 0740 QByteArray cFullPath = QFile::encodeName(fullPath); 0741 if (access(cFullPath, R_OK) == 0 && KDE_stat(cFullPath, &buff) == 0) { 0742 if (!fullPath.endsWith(QLatin1Char('/'))) { 0743 if (S_ISREG(buff.st_mode)) { 0744 return true; 0745 } 0746 } else if (S_ISDIR(buff.st_mode)) { 0747 return true; 0748 } 0749 } 0750 return false; 0751 #endif 0752 } 0753 0754 static void lookupDirectory(const QString &path, const QString &relPart, 0755 const QRegExp ®exp, 0756 QStringList &list, 0757 QStringList &relList, 0758 bool recursive, bool unique) 0759 { 0760 const QString pattern = regexp.pattern(); 0761 if (recursive || pattern.contains(QLatin1Char('?')) || pattern.contains(QLatin1Char('*'))) { 0762 if (path.isEmpty()) { //for sanity 0763 return; 0764 } 0765 #ifdef Q_OS_WIN 0766 QString path_ = path + QLatin1String("*.*"); 0767 WIN32_FIND_DATA findData; 0768 HANDLE hFile = FindFirstFile((LPWSTR)path_.utf16(), &findData); 0769 if (hFile == INVALID_HANDLE_VALUE) { 0770 return; 0771 } 0772 do { 0773 const int len = wcslen(findData.cFileName); 0774 if (!(findData.cFileName[0] == '.' && 0775 findData.cFileName[1] == '\0') && 0776 !(findData.cFileName[0] == '.' && 0777 findData.cFileName[1] == '.' && 0778 findData.cFileName[2] == '\0') && 0779 (findData.cFileName[len - 1] != '~')) { 0780 QString fn = QString::fromUtf16((const unsigned short *)findData.cFileName); 0781 if (!recursive && !regexp.exactMatch(fn)) { 0782 continue; // No match 0783 } 0784 QString pathfn = path + fn; 0785 bool bIsDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY); 0786 if (recursive) { 0787 if (bIsDir) { 0788 lookupDirectory(pathfn + QLatin1Char('/'), 0789 relPart + fn + QLatin1Char('/'), 0790 regexp, list, relList, recursive, unique); 0791 } 0792 if (!regexp.exactMatch(fn)) { 0793 continue; // No match 0794 } 0795 } 0796 if (!bIsDir) { 0797 if (!unique || !relList.contains(relPart + fn, cs)) { 0798 list.append(pathfn); 0799 relList.append(relPart + fn); 0800 } 0801 } 0802 } 0803 } while (FindNextFile(hFile, &findData) != 0); 0804 FindClose(hFile); 0805 #else 0806 // We look for a set of files. 0807 DIR *dp = opendir(QFile::encodeName(path)); 0808 if (!dp) { 0809 return; 0810 } 0811 0812 assert(path.endsWith(QLatin1Char('/'))); 0813 0814 struct dirent *ep; 0815 0816 while ((ep = readdir(dp)) != nullptr) { 0817 QString fn(QFile::decodeName(ep->d_name)); 0818 if (fn == QString::fromLatin1(".") || fn == QString::fromLatin1("..") || fn.at(fn.length() - 1) == QLatin1Char('~')) { 0819 continue; 0820 } 0821 0822 if (!recursive && !regexp.exactMatch(fn)) { 0823 continue; // No match 0824 } 0825 0826 bool isDir; 0827 bool isReg; 0828 0829 QString pathfn = path + fn; 0830 #if HAVE_DIRENT_D_TYPE 0831 isDir = ep->d_type == DT_DIR; 0832 isReg = ep->d_type == DT_REG; 0833 0834 if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK) 0835 #endif 0836 { 0837 KDE_struct_stat buff; 0838 if (KDE::stat(pathfn, &buff) != 0) { 0839 kDebug(180) << "Error stat'ing " << pathfn << " : " << perror; 0840 continue; // Couldn't stat (e.g. no read permissions) 0841 } 0842 isReg = S_ISREG(buff.st_mode); 0843 isDir = S_ISDIR(buff.st_mode); 0844 } 0845 0846 if (recursive) { 0847 if (isDir) { 0848 lookupDirectory(pathfn + QLatin1Char('/'), relPart + fn + QLatin1Char('/'), regexp, list, relList, recursive, unique); 0849 } 0850 if (!regexp.exactMatch(fn)) { 0851 continue; // No match 0852 } 0853 } 0854 if (isReg) { 0855 if (!unique || !relList.contains(relPart + fn, cs)) { 0856 list.append(pathfn); 0857 relList.append(relPart + fn); 0858 } 0859 } 0860 } 0861 closedir(dp); 0862 #endif 0863 } else { 0864 // We look for a single file. 0865 QString fn = pattern; 0866 QString pathfn = path + fn; 0867 KDE_struct_stat buff; 0868 if (KDE::stat(pathfn, &buff) != 0) { 0869 return; // File not found 0870 } 0871 if (S_ISREG(buff.st_mode)) { 0872 if (!unique || !relList.contains(relPart + fn, cs)) { 0873 list.append(pathfn); 0874 relList.append(relPart + fn); 0875 } 0876 } 0877 } 0878 } 0879 0880 static void lookupPrefix(const QString &prefix, const QString &relpath, 0881 const QString &relPart, 0882 const QRegExp ®exp, 0883 QStringList &list, 0884 QStringList &relList, 0885 bool recursive, bool unique) 0886 { 0887 if (relpath.isEmpty()) { 0888 if (recursive) { 0889 Q_ASSERT(prefix != QLatin1String("/")); // we don't want to recursively list the whole disk! 0890 } 0891 lookupDirectory(prefix, relPart, regexp, list, 0892 relList, recursive, unique); 0893 return; 0894 } 0895 QString path; 0896 QString rest; 0897 0898 int slash = relpath.indexOf(QLatin1Char('/')); 0899 if (slash < 0) { 0900 rest = relpath.left(relpath.length() - 1); 0901 } else { 0902 path = relpath.left(slash); 0903 rest = relpath.mid(slash + 1); 0904 } 0905 0906 if (prefix.isEmpty()) { //for sanity 0907 return; 0908 } 0909 #ifndef Q_OS_WIN 0910 // what does this assert check ? 0911 assert(prefix.endsWith(QLatin1Char('/'))); 0912 #endif 0913 if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?'))) { 0914 0915 QRegExp pathExp(path, Qt::CaseSensitive, QRegExp::Wildcard); 0916 0917 #ifdef Q_OS_WIN 0918 QString prefix_ = prefix + QLatin1String("*.*"); 0919 WIN32_FIND_DATA findData; 0920 HANDLE hFile = FindFirstFile((LPWSTR)prefix_.utf16(), &findData); 0921 if (hFile == INVALID_HANDLE_VALUE) { 0922 return; 0923 } 0924 do { 0925 const int len = wcslen(findData.cFileName); 0926 if (!(findData.cFileName[0] == '.' && 0927 findData.cFileName[1] == '\0') && 0928 !(findData.cFileName[0] == '.' && 0929 findData.cFileName[1] == '.' && 0930 findData.cFileName[2] == '\0') && 0931 (findData.cFileName[len - 1] != '~')) { 0932 const QString fn = QString::fromUtf16((const unsigned short *)findData.cFileName); 0933 if (!pathExp.exactMatch(fn)) { 0934 continue; // No match 0935 } 0936 if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) 0937 lookupPrefix(prefix + fn + QLatin1Char('/'), 0938 rest, relPart + fn + QLatin1Char('/'), 0939 regexp, list, relList, recursive, unique); 0940 } 0941 } while (FindNextFile(hFile, &findData) != 0); 0942 FindClose(hFile); 0943 #else 0944 DIR *dp = opendir(QFile::encodeName(prefix)); 0945 if (!dp) { 0946 return; 0947 } 0948 0949 struct dirent *ep; 0950 0951 while ((ep = readdir(dp)) != nullptr) { 0952 QString fn(QFile::decodeName(ep->d_name)); 0953 if (fn == QLatin1String(".") || fn == QLatin1String("..") || fn.at(fn.length() - 1) == QLatin1Char('~')) { 0954 continue; 0955 } 0956 0957 if (!pathExp.exactMatch(fn)) { 0958 continue; // No match 0959 } 0960 QString rfn = relPart + fn; 0961 fn = prefix + fn; 0962 0963 bool isDir; 0964 0965 #if HAVE_DIRENT_D_TYPE 0966 isDir = ep->d_type == DT_DIR; 0967 0968 if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK) 0969 #endif 0970 { 0971 QString pathfn = path + fn; 0972 KDE_struct_stat buff; 0973 if (KDE::stat(fn, &buff) != 0) { 0974 kDebug(180) << "Error stat'ing " << fn << " : " << perror; 0975 continue; // Couldn't stat (e.g. no read permissions) 0976 } 0977 isDir = S_ISDIR(buff.st_mode); 0978 } 0979 if (isDir) { 0980 lookupPrefix(fn + QLatin1Char('/'), rest, rfn + QLatin1Char('/'), regexp, list, relList, recursive, unique); 0981 } 0982 } 0983 0984 closedir(dp); 0985 #endif 0986 } else { 0987 // Don't stat, if the dir doesn't exist we will find out 0988 // when we try to open it. 0989 lookupPrefix(prefix + path + QLatin1Char('/'), rest, 0990 relPart + path + QLatin1Char('/'), regexp, list, 0991 relList, recursive, unique); 0992 } 0993 } 0994 0995 QStringList 0996 KStandardDirs::findAllResources(const char *type, 0997 const QString &filter, 0998 SearchOptions options, 0999 QStringList &relList) const 1000 { 1001 QString filterPath; 1002 QString filterFile; 1003 1004 if (!filter.isEmpty()) { 1005 int slash = filter.lastIndexOf(QLatin1Char('/')); 1006 if (slash < 0) { 1007 filterFile = filter; 1008 } else { 1009 filterPath = filter.left(slash + 1); 1010 filterFile = filter.mid(slash + 1); 1011 } 1012 } 1013 1014 QStringList candidates; 1015 if (!QDir::isRelativePath(filter)) { // absolute path 1016 #ifdef Q_OS_WIN 1017 candidates << filterPath.left(3); //e.g. "C:\" 1018 filterPath = filterPath.mid(3); 1019 #else 1020 candidates << QString::fromLatin1("/"); 1021 filterPath = filterPath.mid(1); 1022 #endif 1023 } else { 1024 candidates = d->resourceDirs(type, filter); 1025 } 1026 1027 if (filterFile.isEmpty()) { 1028 filterFile = QString(QLatin1Char('*')); 1029 } 1030 1031 QRegExp regExp(filterFile, Qt::CaseSensitive, QRegExp::Wildcard); 1032 1033 QStringList list; 1034 foreach (const QString &candidate, candidates) { 1035 lookupPrefix(candidate, filterPath, QString(), regExp, list, 1036 relList, options & Recursive, options & NoDuplicates); 1037 } 1038 1039 return list; 1040 } 1041 1042 QStringList 1043 KStandardDirs::findAllResources(const char *type, 1044 const QString &filter, 1045 SearchOptions options) const 1046 { 1047 QStringList relList; 1048 return findAllResources(type, filter, options, relList); 1049 } 1050 1051 // ####### KDE4: should this be removed, in favor of QDir::canonicalPath()? 1052 // aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist 1053 // and this method is often used with the expectation for it to work 1054 // even if the directory doesn't exist. so ... no, we can't drop this 1055 // yet 1056 QString 1057 KStandardDirs::realPath(const QString &dirname) 1058 { 1059 #ifdef Q_OS_WIN 1060 const QString strRet = realFilePath(dirname); 1061 if (!strRet.endsWith(QLatin1Char('/'))) { 1062 return strRet + QLatin1Char('/'); 1063 } 1064 return strRet; 1065 #else 1066 if (dirname.isEmpty() || (dirname.size() == 1 && dirname.at(0) == QLatin1Char('/'))) { 1067 return dirname; 1068 } 1069 1070 if (dirname.at(0) != QLatin1Char('/')) { 1071 qWarning("realPath called with a relative path '%s', please fix", qPrintable(dirname)); 1072 return dirname; 1073 } 1074 1075 char realpath_buffer[MAXPATHLEN + 1]; 1076 memset(realpath_buffer, 0, MAXPATHLEN + 1); 1077 1078 /* If the path contains symlinks, get the real name */ 1079 if (realpath(QFile::encodeName(dirname).constData(), realpath_buffer) != nullptr) { 1080 // success, use result from realpath 1081 int len = strlen(realpath_buffer); 1082 realpath_buffer[len] = '/'; 1083 realpath_buffer[len + 1] = 0; 1084 return QFile::decodeName(realpath_buffer); 1085 } 1086 1087 // Does not exist yet; resolve symlinks in parent dirs then. 1088 // This ensures that once the directory exists, it will still be resolved 1089 // the same way, so that the general rule that KStandardDirs always returns 1090 // canonical paths stays true, and app code can compare paths more easily. 1091 QString dir = dirname; 1092 if (!dir.endsWith(QLatin1Char('/'))) { 1093 dir += QLatin1Char('/'); 1094 } 1095 QString relative; 1096 while (!KStandardDirs::exists(dir)) { 1097 //qDebug() << "does not exist:" << dir; 1098 const int pos = dir.lastIndexOf(QLatin1Char('/'), -2); 1099 Q_ASSERT(pos >= 0); // what? even "/" doesn't exist? 1100 relative.prepend(dir.mid(pos + 1)); // keep "subdir/" 1101 dir = dir.left(pos + 1); 1102 Q_ASSERT(dir.endsWith(QLatin1Char('/'))); 1103 } 1104 Q_ASSERT(!relative.isEmpty()); // infinite recursion ahead 1105 if (!relative.isEmpty()) { 1106 //qDebug() << "done, resolving" << dir << "and adding" << relative; 1107 dir = realPath(dir) + relative; 1108 } 1109 return dir; 1110 #endif 1111 } 1112 1113 // ####### KDE4: should this be removed, in favor of QFileInfo::canonicalFilePath()? 1114 // aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist 1115 // and this method is often used with the expectation for it to work 1116 // even if the directory doesn't exist. so ... no, we can't drop this 1117 // yet 1118 QString 1119 KStandardDirs::realFilePath(const QString &filename) 1120 { 1121 #ifdef Q_OS_WIN 1122 LPCWSTR lpIn = (LPCWSTR)filename.utf16(); 1123 QVarLengthArray<WCHAR, MAX_PATH> buf(MAX_PATH); 1124 DWORD len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL); 1125 if (len > (DWORD)buf.size()) { 1126 buf.resize(len); 1127 len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL); 1128 } 1129 if (len == 0) { 1130 return QString(); 1131 } 1132 return QString::fromUtf16((const unsigned short *)buf.data()).replace(QLatin1Char('\\'), QLatin1Char('/')); 1133 #else 1134 char realpath_buffer[MAXPATHLEN + 1]; 1135 memset(realpath_buffer, 0, MAXPATHLEN + 1); 1136 1137 /* If the path contains symlinks, get the real name */ 1138 if (realpath(QFile::encodeName(filename).constData(), realpath_buffer) != nullptr) { 1139 // success, use result from realpath 1140 return QFile::decodeName(realpath_buffer); 1141 } 1142 1143 return filename; 1144 #endif 1145 } 1146 1147 QStringList KStandardDirs::resourceDirs(const char *type) const 1148 { 1149 return d->resourceDirs(type, QString()); 1150 } 1151 1152 QStringList KStandardDirs::KStandardDirsPrivate::resourceDirs(const char *type, const QString &subdirForRestrictions) 1153 { 1154 QMutexLocker lock(&m_cacheMutex); 1155 const bool dataRestrictionActive = m_restrictionsActive 1156 && (strcmp(type, "data") == 0) 1157 && hasDataRestrictions(subdirForRestrictions); 1158 1159 QMap<QByteArray, QStringList>::const_iterator dirCacheIt = m_dircache.constFind(type); 1160 1161 QStringList candidates; 1162 1163 if (dirCacheIt != m_dircache.constEnd() && !dataRestrictionActive) { 1164 //qDebug() << this << "resourceDirs(" << type << "), in cache already"; 1165 candidates = *dirCacheIt; 1166 } else { // filling cache 1167 //qDebug() << this << "resourceDirs(" << type << "), not in cache"; 1168 q->addResourceDir("socket", QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)); 1169 q->addResourceDir("tmp", QStandardPaths::writableLocation(QStandardPaths::TempLocation)); 1170 q->addResourceDir("cache", QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)); 1171 1172 QDir testdir; 1173 1174 bool restrictionActive = false; 1175 if (m_restrictionsActive) { 1176 if (dataRestrictionActive) { 1177 restrictionActive = true; 1178 } 1179 if (m_restrictions.value("all", false)) { 1180 restrictionActive = true; 1181 } else if (m_restrictions.value(type, false)) { 1182 restrictionActive = true; 1183 } 1184 } 1185 1186 const QStringList dirs = m_relatives.value(type); 1187 QString typeInstallPath; 1188 if (strcmp(type, "xdgdata-apps") == 0) { 1189 // If the resource is xdgdata-apps, we never actually want the 1190 // install directory (which is probably a subdirectory of 1191 // CMAKE_INSTALL_PREFIX/share/applications/), but instead we want 1192 // CMAKE_INSTALL_PREFIX/share/applications/ itself. 1193 #ifdef Q_OS_WIN 1194 typeInstallPath = getKde4Prefix() + QLatin1String("share/applications/"); 1195 #else 1196 typeInstallPath = QFile::decodeName(CMAKE_INSTALL_PREFIX "/") + QLatin1String("share/applications/"); 1197 #endif 1198 } else { 1199 typeInstallPath = installPath(type); // could be empty 1200 } 1201 const QString installdir = typeInstallPath.isEmpty() ? QString() : realPath(typeInstallPath); 1202 const QString installprefix = installPath("kdedir"); 1203 if (!dirs.isEmpty()) { 1204 bool local = true; 1205 1206 for (QStringList::ConstIterator it = dirs.constBegin(); 1207 it != dirs.constEnd(); ++it) { 1208 if ((*it).startsWith(QLatin1Char('%'))) { 1209 // grab the "data" from "%data/apps" 1210 const int pos = (*it).indexOf(QLatin1Char('/')); 1211 QString rel = (*it).mid(1, pos - 1); 1212 QString rest = (*it).mid(pos + 1); 1213 const QStringList basedirs = resourceDirs(rel.toUtf8().constData(), subdirForRestrictions); 1214 for (QStringList::ConstIterator it2 = basedirs.begin(); 1215 it2 != basedirs.end(); ++it2) { 1216 const QString path = realPath(*it2 + rest); 1217 testdir.setPath(path); 1218 if ((local || testdir.exists()) && !candidates.contains(path, cs)) { 1219 candidates.append(path); 1220 } 1221 local = false; 1222 } 1223 } 1224 } 1225 1226 const QStringList *prefixList = nullptr; 1227 const BasePrefix basePrefix = basePrefixForResource(type); 1228 if (basePrefix == XdgConf) { 1229 prefixList = &(xdgconf_prefixes); 1230 } else if (basePrefix == XdgData) { 1231 prefixList = &(xdgdata_prefixes); 1232 } else if (basePrefix == KdePrefixes) { 1233 prefixList = &m_prefixes; 1234 } 1235 1236 for (QStringList::ConstIterator pit = prefixList->begin(); 1237 pit != prefixList->end(); 1238 ++pit) { 1239 if ((*pit).compare(installprefix, cs) != 0 || installdir.isEmpty()) { 1240 for (QStringList::ConstIterator it = dirs.constBegin(); 1241 it != dirs.constEnd(); ++it) { 1242 if ((*it).startsWith(QLatin1Char('%'))) { 1243 continue; 1244 } 1245 const QString path = realPath(*pit + *it); 1246 testdir.setPath(path); 1247 if (local && restrictionActive) { 1248 continue; 1249 } 1250 if ((local || testdir.exists()) && !candidates.contains(path, cs)) { 1251 candidates.append(path); 1252 } 1253 } 1254 local = false; 1255 } else { 1256 // we have a custom install path, so use this instead of <installprefix>/<relative dir> 1257 testdir.setPath(installdir); 1258 if (testdir.exists() && ! candidates.contains(installdir, cs)) { 1259 candidates.append(installdir); 1260 } 1261 } 1262 } 1263 } 1264 1265 // make sure we find the path where it's installed 1266 if (!installdir.isEmpty()) { 1267 bool ok = true; 1268 foreach (const QString &s, candidates) { 1269 if (installdir.startsWith(s, cs)) { 1270 ok = false; 1271 break; 1272 } 1273 } 1274 if (ok) { 1275 candidates.append(installdir); 1276 } 1277 } 1278 1279 const QStringList absDirs = m_absolutes.value(type); 1280 for (QStringList::ConstIterator it = absDirs.constBegin(); 1281 it != absDirs.constEnd(); ++it) { 1282 testdir.setPath(*it); 1283 if (testdir.exists()) { 1284 const QString filename = realPath(*it); 1285 if (!candidates.contains(filename, cs)) { 1286 candidates.append(filename); 1287 } 1288 } 1289 } 1290 1291 // Insert result into the cache for next time. 1292 // Exception: data_subdir restrictions are per-subdir, so we can't store such results 1293 if (!dataRestrictionActive) { 1294 //qDebug() << this << "Inserting" << type << candidates << "into dircache"; 1295 m_dircache.insert(type, candidates); 1296 } 1297 } 1298 1299 #if 0 1300 kDebug(180) << "found dirs for resource" << type << ":" << candidates; 1301 #endif 1302 1303 return candidates; 1304 } 1305 1306 #ifdef Q_OS_WIN 1307 static QStringList executableExtensions() 1308 { 1309 QStringList ret = QString::fromLocal8Bit(qgetenv("PATHEXT")).split(QLatin1Char(';')); 1310 if (!ret.contains(QLatin1String(".exe"), Qt::CaseInsensitive)) { 1311 // If %PATHEXT% does not contain .exe, it is either empty, malformed, or distorted in ways that we cannot support, anyway. 1312 ret.clear(); 1313 ret << QLatin1String(".exe") 1314 << QLatin1String(".com") 1315 << QLatin1String(".bat") 1316 << QLatin1String(".cmd"); 1317 } 1318 return ret; 1319 } 1320 #endif 1321 1322 QStringList KStandardDirs::systemPaths(const QString &pstr) 1323 { 1324 QStringList tokens; 1325 QString p = pstr; 1326 1327 if (p.isEmpty()) { 1328 p = QString::fromLocal8Bit(qgetenv("PATH")); 1329 } 1330 1331 QString delimiters(QLatin1Char(KPATH_SEPARATOR)); 1332 delimiters += QLatin1Char('\b'); 1333 tokenize(tokens, p, delimiters); 1334 1335 QStringList exePaths; 1336 1337 // split path using : or \b as delimiters 1338 for (int i = 0; i < tokens.count(); i++) { 1339 exePaths << KShell::tildeExpand(tokens[ i ]); 1340 } 1341 1342 return exePaths; 1343 } 1344 1345 #ifdef Q_OS_MAC 1346 static QString getBundle(const QString &path, bool ignore) 1347 { 1348 //kDebug(180) << "getBundle(" << path << ", " << ignore << ") called"; 1349 QFileInfo info; 1350 QString bundle = path; 1351 bundle += QLatin1String(".app/Contents/MacOS/") + bundle.section(QLatin1Char('/'), -1); 1352 info.setFile(bundle); 1353 FILE *file; 1354 if (file = fopen(info.absoluteFilePath().toUtf8().constData(), "r")) { 1355 fclose(file); 1356 struct stat _stat; 1357 if ((stat(info.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) { 1358 return QString(); 1359 } 1360 if (ignore || (_stat.st_mode & S_IXUSR)) { 1361 if (((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK)) { 1362 //kDebug(180) << "getBundle(): returning " << bundle; 1363 return bundle; 1364 } 1365 } 1366 } 1367 return QString(); 1368 } 1369 #endif 1370 1371 static QString checkExecutable(const QString &path, bool ignoreExecBit) 1372 { 1373 #ifdef Q_OS_MAC 1374 QString bundle = getBundle(path, ignoreExecBit); 1375 if (!bundle.isEmpty()) { 1376 //kDebug(180) << "findExe(): returning " << bundle; 1377 return bundle; 1378 } 1379 #endif 1380 QFileInfo info(path); 1381 QFileInfo orig = info; 1382 #if defined(Q_OS_DARWIN) || defined(Q_OS_MAC) 1383 FILE *file; 1384 if (file = fopen(orig.absoluteFilePath().toUtf8().constData(), "r")) { 1385 fclose(file); 1386 struct stat _stat; 1387 if ((stat(orig.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) { 1388 return QString(); 1389 } 1390 if (ignoreExecBit || (_stat.st_mode & S_IXUSR)) { 1391 if (((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK)) { 1392 orig.makeAbsolute(); 1393 return orig.filePath(); 1394 } 1395 } 1396 } 1397 return QString(); 1398 #else 1399 if (info.exists() && info.isSymLink()) { 1400 info = QFileInfo(info.canonicalFilePath()); 1401 } 1402 if (info.exists() && (ignoreExecBit || info.isExecutable()) && info.isFile()) { 1403 // return absolute path, but without symlinks resolved in order to prevent 1404 // problems with executables that work differently depending on name they are 1405 // run as (for example gunzip) 1406 orig.makeAbsolute(); 1407 return QDir::cleanPath(orig.filePath()); 1408 } 1409 //kDebug(180) << "checkExecutable(): failed, returning empty string"; 1410 return QString(); 1411 #endif 1412 } 1413 1414 // KDE5 TODO: remove IgnoreExecBit almost unused 1415 QString KStandardDirs::findExe(const QString &appname, 1416 const QString &pstr, 1417 SearchOptions options) 1418 { 1419 //kDebug(180) << "findExe(" << appname << ", pstr, " << ignoreExecBit << ") called"; 1420 1421 #ifdef Q_OS_WIN 1422 QStringList executable_extensions = executableExtensions(); 1423 if (!executable_extensions.contains(appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep), Qt::CaseInsensitive)) { 1424 QString found_exe; 1425 foreach (const QString &extension, executable_extensions) { 1426 found_exe = findExe(appname + extension, pstr, options); 1427 if (!found_exe.isEmpty()) { 1428 return found_exe; 1429 } 1430 } 1431 return QString(); 1432 } 1433 #endif 1434 QFileInfo info; 1435 1436 // absolute or relative path? 1437 if (QDir::isAbsolutePath(appname)) { 1438 //kDebug(180) << "findExe(): absolute path given"; 1439 QString path = checkExecutable(appname, options & IgnoreExecBit); 1440 return path; 1441 } 1442 1443 //kDebug(180) << "findExe(): relative path given"; 1444 1445 QString p = installPath("libexec") + appname; 1446 QString result = checkExecutable(p, options & IgnoreExecBit); 1447 if (!result.isEmpty()) { 1448 //kDebug(180) << "findExe(): returning " << result; 1449 return result; 1450 } 1451 1452 //kDebug(180) << "findExe(): checking system paths"; 1453 const QStringList exePaths = systemPaths(pstr); 1454 for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it) { 1455 p = (*it) + QLatin1Char('/'); 1456 p += appname; 1457 1458 // Check for executable in this tokenized path 1459 result = checkExecutable(p, options & IgnoreExecBit); 1460 if (!result.isEmpty()) { 1461 //kDebug(180) << "findExe(): returning " << result; 1462 return result; 1463 } 1464 } 1465 1466 // Not found in PATH, look into the KDE-specific bin dir ("exe" resource) 1467 p = installPath("exe"); 1468 p += appname; 1469 result = checkExecutable(p, options & IgnoreExecBit); 1470 if (!result.isEmpty()) { 1471 //kDebug(180) << "findExe(): returning " << result; 1472 return result; 1473 } 1474 1475 // If we reach here, the executable wasn't found. 1476 // So return empty string. 1477 1478 //kDebug(180) << "findExe(): failed, nothing matched"; 1479 return QString(); 1480 } 1481 1482 // TODO: very rarely used. Only known example is kdevelop looking for all qmakes/cmakes in the path. 1483 int KStandardDirs::findAllExe(QStringList &list, const QString &appname, 1484 const QString &pstr, SearchOptions options) 1485 { 1486 #ifdef Q_OS_WIN 1487 QStringList executable_extensions = executableExtensions(); 1488 if (!executable_extensions.contains(appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep), Qt::CaseInsensitive)) { 1489 int total = 0; 1490 foreach (const QString &extension, executable_extensions) { 1491 total += findAllExe(list, appname + extension, pstr, options); 1492 } 1493 return total; 1494 } 1495 #endif 1496 QFileInfo info; 1497 QString p; 1498 list.clear(); 1499 1500 const QStringList exePaths = systemPaths(pstr); 1501 for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it) { 1502 p = (*it) + QLatin1Char('/'); 1503 p += appname; 1504 1505 #ifdef Q_OS_MAC 1506 QString bundle = getBundle(p, (options & IgnoreExecBit)); 1507 if (!bundle.isEmpty()) { 1508 //kDebug(180) << "findExe(): returning " << bundle; 1509 list.append(bundle); 1510 } 1511 #endif 1512 1513 info.setFile(p); 1514 1515 if (info.exists() && ((options & IgnoreExecBit) || info.isExecutable()) 1516 && info.isFile()) { 1517 list.append(p); 1518 } 1519 } 1520 1521 return list.count(); 1522 } 1523 1524 static inline QString equalizePath(QString &str) 1525 { 1526 #ifdef Q_OS_WIN 1527 // filter pathes through QFileInfo to have always 1528 // the same case for drive letters 1529 QFileInfo f(str); 1530 if (f.isAbsolute()) { 1531 return f.absoluteFilePath(); 1532 } else 1533 #endif 1534 return str; 1535 } 1536 1537 static void tokenize(QStringList &tokens, const QString &str, 1538 const QString &delim) 1539 { 1540 const int len = str.length(); 1541 QString token; 1542 1543 for (int index = 0; index < len; index++) { 1544 if (delim.contains(str[index])) { 1545 tokens.append(equalizePath(token)); 1546 token.clear(); 1547 } else { 1548 token += str[index]; 1549 } 1550 } 1551 if (!token.isEmpty()) { 1552 tokens.append(equalizePath(token)); 1553 } 1554 } 1555 1556 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 1557 QString KStandardDirs::kde_default(const char *type) 1558 { 1559 return QString(QLatin1Char('%')) + QString::fromLatin1(type) + QLatin1Char('/'); 1560 } 1561 #endif 1562 1563 QString KStandardDirs::saveLocation(const char *type, 1564 const QString &suffix, 1565 bool create) const 1566 { 1567 QMutexLocker lock(&d->m_cacheMutex); 1568 QString path = d->m_savelocations.value(type); 1569 if (path.isEmpty()) { 1570 QStringList dirs = d->m_relatives.value(type); 1571 if (dirs.isEmpty() && ( 1572 (strcmp(type, "socket") == 0) || 1573 (strcmp(type, "tmp") == 0) || 1574 (strcmp(type, "cache") == 0))) { 1575 (void) resourceDirs(type); // Generate socket|tmp|cache resource. 1576 dirs = d->m_relatives.value(type); // Search again. 1577 } 1578 if (!dirs.isEmpty()) { 1579 path = dirs.first(); 1580 1581 if (path.startsWith(QLatin1Char('%'))) { 1582 // grab the "data" from "%data/apps" 1583 const int pos = path.indexOf(QLatin1Char('/')); 1584 QString rel = path.mid(1, pos - 1); 1585 QString rest = path.mid(pos + 1); 1586 QString basepath = saveLocation(rel.toUtf8().constData(), QString(), create); 1587 path = basepath + rest; 1588 } else { 1589 if (path == QLatin1String("./")) { 1590 path.clear(); 1591 } 1592 // Check for existence of typed directory + suffix 1593 const BasePrefix basePrefix = basePrefixForResource(type); 1594 if (basePrefix == XdgConf) { 1595 path = realPath(d->xdgconf_prefixes.first() + path); 1596 } else if (basePrefix == XdgData) { 1597 path = realPath(d->xdgdata_prefixes.first() + path); 1598 } else { 1599 path = realPath(d->m_prefixes.first() + path); 1600 } 1601 } 1602 } else { 1603 dirs = d->m_absolutes.value(type); 1604 if (dirs.isEmpty()) { 1605 qFatal("KStandardDirs: The resource type %s is not registered", type); 1606 } else { 1607 path = realPath(dirs.first()); 1608 } 1609 } 1610 1611 d->m_savelocations.insert(type, path.endsWith(QLatin1Char('/')) ? path : path + QLatin1Char('/')); 1612 } 1613 QString fullPath = path + suffix; 1614 1615 KDE_struct_stat st; 1616 if (KDE::stat(fullPath, &st) != 0 || !(S_ISDIR(st.st_mode))) { 1617 if (!create) { 1618 #ifndef NDEBUG 1619 // Too much noise from kbuildsycoca4 -- it's fine if this happens from KConfig 1620 // when parsing global files without a local equivalent. 1621 //kDebug(180) << QString("save location %1 doesn't exist").arg(fullPath); 1622 #endif 1623 return fullPath; 1624 } 1625 if (!makeDir(fullPath, 0700)) { 1626 return fullPath; 1627 } 1628 d->m_dircache.remove(type); 1629 } 1630 if (!fullPath.endsWith(QLatin1Char('/'))) { 1631 fullPath += QLatin1Char('/'); 1632 } 1633 return fullPath; 1634 } 1635 1636 QString KStandardDirs::relativeLocation(const char *type, const QString &absPath) const 1637 { 1638 QString fullPath = absPath; 1639 int i = absPath.lastIndexOf(QLatin1Char('/')); 1640 if (i != -1) { 1641 fullPath = realFilePath(absPath); // Normalize 1642 } 1643 1644 const QStringList candidates = resourceDirs(type); 1645 1646 for (QStringList::ConstIterator it = candidates.begin(); 1647 it != candidates.end(); ++it) { 1648 if (fullPath.startsWith(*it, cs)) { 1649 return fullPath.mid((*it).length()); 1650 } 1651 } 1652 return absPath; 1653 } 1654 1655 bool KStandardDirs::makeDir(const QString &dir, int mode) 1656 { 1657 // we want an absolute path 1658 if (QDir::isRelativePath(dir)) { 1659 return false; 1660 } 1661 1662 #ifdef Q_OS_WIN 1663 return QDir().mkpath(dir); 1664 #else 1665 QString target = dir; 1666 uint len = target.length(); 1667 1668 // append trailing slash if missing 1669 if (dir.at(len - 1) != QLatin1Char('/')) { 1670 target += QLatin1Char('/'); 1671 } 1672 1673 QString base; 1674 uint i = 1; 1675 1676 while (i < len) { 1677 KDE_struct_stat st; 1678 int pos = target.indexOf(QLatin1Char('/'), i); 1679 base += target.mid(i - 1, pos - i + 1); 1680 QByteArray baseEncoded = QFile::encodeName(base); 1681 // bail out if we encountered a problem 1682 if (KDE_stat(baseEncoded, &st) != 0) { 1683 // Directory does not exist.... 1684 // Or maybe a dangling symlink ? 1685 if (KDE_lstat(baseEncoded, &st) == 0) { 1686 (void)unlink(baseEncoded); // try removing 1687 } 1688 1689 if (KDE_mkdir(baseEncoded, static_cast<mode_t>(mode)) != 0) { 1690 baseEncoded.prepend("trying to create local folder "); 1691 perror(baseEncoded.constData()); 1692 return false; // Couldn't create it :-( 1693 } 1694 } 1695 i = pos + 1; 1696 } 1697 return true; 1698 #endif 1699 } 1700 1701 static QString readEnvPath(const char *env) 1702 { 1703 QByteArray c_path; 1704 #ifndef _WIN32_WCE 1705 c_path = qgetenv(env); 1706 if (c_path.isEmpty()) { 1707 return QString(); 1708 } 1709 #else 1710 bool ok; 1711 QString retval = getWin32RegistryValue(HKEY_LOCAL_MACHINE, "Software\\kde", "KDEDIRS", &ok); 1712 if (!ok) { 1713 return QString(); 1714 } else { 1715 c_path = retval.toLatin1(); 1716 } 1717 #endif 1718 return QDir::fromNativeSeparators(QFile::decodeName(c_path)); 1719 } 1720 1721 #ifdef __linux__ 1722 static QString executablePrefix() 1723 { 1724 char path_buffer[MAXPATHLEN + 1]; 1725 path_buffer[MAXPATHLEN] = 0; 1726 int length = readlink("/proc/self/exe", path_buffer, MAXPATHLEN); 1727 if (length == -1) { 1728 return QString(); 1729 } 1730 1731 path_buffer[length] = '\0'; 1732 1733 QString path = QFile::decodeName(path_buffer); 1734 1735 if (path.isEmpty()) { 1736 return QString(); 1737 } 1738 1739 int pos = path.lastIndexOf(QLatin1Char('/')); // Skip filename 1740 if (pos <= 0) { 1741 return QString(); 1742 } 1743 pos = path.lastIndexOf(QLatin1Char('/'), pos - 1); // Skip last directory 1744 if (pos <= 0) { 1745 return QString(); 1746 } 1747 1748 return path.left(pos); 1749 } 1750 #endif 1751 1752 void KStandardDirs::addResourcesFrom_krcdirs() 1753 { 1754 QString localFile = QDir::currentPath() + QLatin1String("/.krcdirs"); 1755 if (!QFile::exists(localFile)) { 1756 return; 1757 } 1758 1759 QSettings iniFile(localFile, QSettings::IniFormat); 1760 iniFile.beginGroup(QString::fromLatin1("KStandardDirs")); 1761 const QStringList resources = iniFile.allKeys(); 1762 foreach (const QString &key, resources) { 1763 QDir path(iniFile.value(key).toString()); 1764 if (!path.exists()) { 1765 continue; 1766 } 1767 1768 if (path.makeAbsolute()) { 1769 addResourceDir(key.toLatin1(), path.path(), false); 1770 } 1771 } 1772 } 1773 1774 void KStandardDirs::addKDEDefaults() 1775 { 1776 addResourcesFrom_krcdirs(); 1777 1778 QStringList kdedirList; 1779 // begin KDEDIRS 1780 QString kdedirs = readEnvPath("KDEDIRS"); 1781 1782 if (!kdedirs.isEmpty()) { 1783 tokenize(kdedirList, kdedirs, QString(QLatin1Char(KPATH_SEPARATOR))); 1784 } 1785 kdedirList.append(installPath("kdedir")); 1786 1787 QString execPrefix(QFile::decodeName(EXEC_INSTALL_PREFIX)); 1788 if (!execPrefix.isEmpty() && !kdedirList.contains(execPrefix, cs)) { 1789 kdedirList.append(execPrefix); 1790 } 1791 #ifdef __linux__ 1792 const QString linuxExecPrefix = executablePrefix(); 1793 if (!linuxExecPrefix.isEmpty()) { 1794 kdedirList.append(linuxExecPrefix); 1795 } 1796 #endif 1797 1798 #if 0 // No longer applicable in KDE Frameworks 5 1799 // We treat root differently to prevent a "su" shell messing up the 1800 // file permissions in the user's home directory. 1801 QString localKdeDir = readEnvPath(getuid() ? "KDEHOME" : "KDEROOTHOME"); 1802 if (!localKdeDir.isEmpty()) { 1803 if (!localKdeDir.endsWith(QLatin1Char('/'))) { 1804 localKdeDir += QLatin1Char('/'); 1805 } 1806 } else { 1807 #if defined(Q_OS_MAC) 1808 localKdeDir = QDir::homePath() + QLatin1String("/Library/Preferences/KDE/"); 1809 #elif defined(Q_OS_WIN) 1810 #ifndef _WIN32_WCE 1811 WCHAR wPath[MAX_PATH + 1]; 1812 if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wPath) == S_OK) { 1813 localKdeDir = QDir::fromNativeSeparators(QString::fromUtf16((const ushort *) wPath)) + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/'); 1814 } else { 1815 #endif 1816 localKdeDir = QDir::homePath() + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/'); 1817 #ifndef _WIN32_WCE 1818 } 1819 #endif 1820 #else 1821 localKdeDir = QDir::homePath() + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/'); 1822 #endif 1823 } 1824 1825 if (localKdeDir != QLatin1String("-/")) { 1826 localKdeDir = KShell::tildeExpand(localKdeDir); 1827 addPrefix(localKdeDir); 1828 } 1829 #endif 1830 1831 #ifdef Q_OS_MAC 1832 // Adds the "Contents" directory of the current application bundle to 1833 // the search path. This way bundled resources can be found. 1834 QDir bundleDir(mac_app_filename()); 1835 if (bundleDir.dirName() == QLatin1String("MacOS")) { // just to be sure we're in a bundle 1836 bundleDir.cdUp(); 1837 // now dirName should be "Contents". In there we can find our normal 1838 // dir-structure, beginning with "share" 1839 addPrefix(bundleDir.absolutePath()); 1840 } 1841 #endif 1842 1843 QStringList::ConstIterator end(kdedirList.end()); 1844 for (QStringList::ConstIterator it = kdedirList.constBegin(); 1845 it != kdedirList.constEnd(); ++it) { 1846 const QString dir = KShell::tildeExpand(*it); 1847 addPrefix(dir); 1848 } 1849 // end KDEDIRS 1850 1851 // begin XDG_CONFIG_XXX 1852 QStringList xdgdirList; 1853 QString xdgdirs = readEnvPath("XDG_CONFIG_DIRS"); 1854 if (!xdgdirs.isEmpty()) { 1855 tokenize(xdgdirList, xdgdirs, QString(QLatin1Char(KPATH_SEPARATOR))); 1856 } else { 1857 xdgdirList.clear(); 1858 xdgdirList.append(QString::fromLatin1("/etc/xdg")); 1859 #ifdef Q_OS_WIN 1860 xdgdirList.append(installPath("kdedir") + QString::fromLatin1("etc/xdg")); 1861 #else 1862 xdgdirList.append(QFile::decodeName(KDESYSCONFDIR "/xdg")); 1863 #endif 1864 } 1865 1866 QString localXdgDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); 1867 addXdgConfigPrefix(localXdgDir); 1868 1869 for (QStringList::ConstIterator it = xdgdirList.constBegin(); 1870 it != xdgdirList.constEnd(); ++it) { 1871 QString dir = KShell::tildeExpand(*it); 1872 addXdgConfigPrefix(dir); 1873 } 1874 // end XDG_CONFIG_XXX 1875 1876 // begin XDG_DATA_XXX 1877 QStringList kdedirDataDirs; 1878 for (QStringList::ConstIterator it = kdedirList.constBegin(); 1879 it != kdedirList.constEnd(); ++it) { 1880 QString dir = *it; 1881 if (!dir.endsWith(QLatin1Char('/'))) { 1882 dir += QLatin1Char('/'); 1883 } 1884 kdedirDataDirs.append(dir + QLatin1String("share/")); 1885 } 1886 1887 xdgdirList.clear(); 1888 xdgdirs = readEnvPath("XDG_DATA_DIRS"); 1889 if (!xdgdirs.isEmpty()) { 1890 tokenize(xdgdirList, xdgdirs, QString(QLatin1Char(KPATH_SEPARATOR))); 1891 // Ensure the kdedirDataDirs are in there too, 1892 // otherwise resourceDirs() will add kdedir/share/applications/kde5 1893 // as returned by installPath(), and that's incorrect. 1894 Q_FOREACH (const QString &dir, kdedirDataDirs) { 1895 if (!xdgdirList.contains(dir, cs)) { 1896 xdgdirList.append(dir); 1897 } 1898 } 1899 } else { 1900 xdgdirList = kdedirDataDirs; 1901 #ifndef Q_OS_WIN 1902 xdgdirList.append(QString::fromLatin1("/usr/local/share/")); 1903 xdgdirList.append(QString::fromLatin1("/usr/share/")); 1904 #endif 1905 } 1906 1907 localXdgDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); 1908 addXdgDataPrefix(localXdgDir); 1909 1910 for (QStringList::ConstIterator it = xdgdirList.constBegin(); 1911 it != xdgdirList.constEnd(); ++it) { 1912 QString dir = KShell::tildeExpand(*it); 1913 addXdgDataPrefix(dir); 1914 } 1915 // end XDG_DATA_XXX 1916 1917 addResourceType("lib", nullptr, LIB_INSTALL_DIR "/"); 1918 1919 addResourceType("qtplugins", "lib", "plugins"); 1920 1921 uint index = 0; 1922 while (types_indices[index] != -1) { 1923 addResourceType(types_string + types_indices[index], nullptr, types_string + types_indices[index + 1], true); 1924 index += 2; 1925 } 1926 1927 // config resource: the XDG paths (xdg/config) have more priority than the KDE4 paths (share/config) 1928 addResourceType("config", "xdgconf", "/", true); 1929 1930 addResourceType("exe", "lib", "libexec", true); 1931 1932 addResourceDir("home", QDir::homePath(), false); 1933 1934 addResourceType("autostart", "xdgconf-autostart", "/"); // merge them, start with xdg autostart 1935 addResourceType("autostart", nullptr, "share/autostart"); // KDE ones are higher priority - KDE 5: deprecated, use xdgconf-autostart 1936 1937 QString appName = QCoreApplication::applicationName(); 1938 addResourceType("appdata", "data", appName + QLatin1Char('/'), true); 1939 } 1940 1941 static QStringList lookupProfiles(const QString &mapFile) 1942 { 1943 QStringList profiles; 1944 1945 if (mapFile.isEmpty() || !QFile::exists(mapFile)) { 1946 profiles << QString::fromLatin1("default"); 1947 return profiles; 1948 } 1949 1950 struct passwd *pw = getpwuid(geteuid()); 1951 if (!pw) { 1952 profiles << QString::fromLatin1("default"); 1953 return profiles; // Not good 1954 } 1955 1956 QByteArray user = pw->pw_name; 1957 1958 gid_t sup_gids[512]; 1959 int sup_gids_nr = getgroups(512, sup_gids); 1960 1961 KConfig mapCfgFile(mapFile); 1962 KConfigGroup mapCfg(&mapCfgFile, "Users"); 1963 if (mapCfg.hasKey(user.constData())) { 1964 profiles = mapCfg.readEntry(user.constData(), QStringList()); 1965 return profiles; 1966 } 1967 1968 const KConfigGroup generalGrp(&mapCfgFile, "General"); 1969 const QStringList groups = generalGrp.readEntry("groups", QStringList()); 1970 1971 const KConfigGroup groupsGrp(&mapCfgFile, "Groups"); 1972 1973 for (QStringList::ConstIterator it = groups.begin(); 1974 it != groups.end(); ++it) { 1975 QByteArray grp = (*it).toUtf8(); 1976 // Check if user is in this group 1977 struct group *grp_ent = getgrnam(grp); 1978 if (!grp_ent) { 1979 continue; 1980 } 1981 gid_t gid = grp_ent->gr_gid; 1982 if (pw->pw_gid == gid) { 1983 // User is in this group --> add profiles 1984 profiles += groupsGrp.readEntry(*it, QStringList()); 1985 } else { 1986 for (int i = 0; i < sup_gids_nr; i++) { 1987 if (sup_gids[i] == gid) { 1988 // User is in this group --> add profiles 1989 profiles += groupsGrp.readEntry(*it, QStringList()); 1990 break; 1991 } 1992 } 1993 } 1994 } 1995 1996 if (profiles.isEmpty()) { 1997 profiles << QString::fromLatin1("default"); 1998 } 1999 return profiles; 2000 } 2001 2002 extern KDELIBS4SUPPORT_DEPRECATED_EXPORT bool kde_kiosk_admin; 2003 2004 bool KStandardDirs::addCustomized(KConfig *config) 2005 { 2006 if (!d->m_checkRestrictions) { // there are already customized entries 2007 return false; // we just quit and hope they are the right ones 2008 } 2009 2010 // save the numbers of config directories. If this changes, 2011 // we will return true to give KConfig a chance to reparse 2012 int configdirs = resourceDirs("config").count(); 2013 2014 if (true) { 2015 // reading the prefixes in 2016 QString group = QLatin1String("Directories"); 2017 KConfigGroup cg(config, group); 2018 2019 QString kioskAdmin = cg.readEntry("kioskAdmin"); 2020 if (!kioskAdmin.isEmpty() && !kde_kiosk_admin) { 2021 int i = kioskAdmin.indexOf(QLatin1Char(':')); 2022 QString user = kioskAdmin.left(i); 2023 QString host = kioskAdmin.mid(i + 1); 2024 2025 KUser thisUser; 2026 char hostname[ 256 ]; 2027 hostname[ 0 ] = '\0'; 2028 if (!gethostname(hostname, 255)) { 2029 hostname[sizeof(hostname) - 1] = '\0'; 2030 } 2031 2032 if ((user == thisUser.loginName()) && 2033 (host.isEmpty() || (host == QLatin1String(hostname)))) { 2034 kde_kiosk_admin = true; 2035 } 2036 } 2037 2038 bool readProfiles = true; 2039 2040 if (kde_kiosk_admin && !qgetenv("KDE_KIOSK_NO_PROFILES").isEmpty()) { 2041 readProfiles = false; 2042 } 2043 2044 QString userMapFile = cg.readEntry("userProfileMapFile"); 2045 QString profileDirsPrefix = cg.readEntry("profileDirsPrefix"); 2046 if (!profileDirsPrefix.isEmpty() && !profileDirsPrefix.endsWith(QLatin1Char('/'))) { 2047 profileDirsPrefix.append(QLatin1Char('/')); 2048 } 2049 2050 QStringList profiles; 2051 if (readProfiles) { 2052 profiles = lookupProfiles(userMapFile); 2053 } 2054 QString profile; 2055 2056 bool priority = false; 2057 while (true) { 2058 KConfigGroup cg(config, group); 2059 const QStringList list = cg.readEntry("prefixes", QStringList()); 2060 for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { 2061 addPrefix(*it, priority); 2062 addXdgConfigPrefix(*it + QLatin1String("/etc/xdg"), priority); 2063 addXdgDataPrefix(*it + QLatin1String("/share"), priority); 2064 } 2065 // If there are no prefixes defined, check if there is a directory 2066 // for this profile under <profileDirsPrefix> 2067 if (list.isEmpty() && !profile.isEmpty() && !profileDirsPrefix.isEmpty()) { 2068 QString dir = profileDirsPrefix + profile; 2069 addPrefix(dir, priority); 2070 addXdgConfigPrefix(dir + QLatin1String("/etc/xdg"), priority); 2071 addXdgDataPrefix(dir + QLatin1String("/share"), priority); 2072 } 2073 2074 // iterating over all entries in the group Directories 2075 // to find entries that start with dir_$type 2076 const QMap<QString, QString> entries = config->entryMap(group); 2077 for (QMap<QString, QString>::ConstIterator it2 = entries.begin(); 2078 it2 != entries.end(); ++it2) { 2079 const QString key = it2.key(); 2080 if (key.startsWith(QLatin1String("dir_"))) { 2081 // generate directory list, there may be more than 1. 2082 const QStringList dirs = (*it2).split(QString(QLatin1Char(','))); 2083 QStringList::ConstIterator sIt(dirs.begin()); 2084 QString resType = key.mid(4); 2085 for (; sIt != dirs.end(); ++sIt) { 2086 addResourceDir(resType.toLatin1(), *sIt, priority); 2087 } 2088 } 2089 } 2090 if (profiles.isEmpty()) { 2091 break; 2092 } 2093 profile = profiles.back(); 2094 group = QString::fromLatin1("Directories-%1").arg(profile); 2095 profiles.pop_back(); 2096 priority = true; 2097 } 2098 } 2099 2100 // Process KIOSK restrictions. 2101 if (!kde_kiosk_admin || qgetenv("KDE_KIOSK_NO_RESTRICTIONS").isEmpty()) { 2102 KConfigGroup cg(config, "KDE Resource Restrictions"); 2103 const QMap<QString, QString> entries = cg.entryMap(); 2104 for (QMap<QString, QString>::ConstIterator it2 = entries.begin(); 2105 it2 != entries.end(); ++it2) { 2106 const QString key = it2.key(); 2107 if (!cg.readEntry(key, true)) { 2108 d->m_restrictionsActive = true; 2109 const QByteArray cKey = key.toLatin1(); 2110 d->m_restrictions.insert(cKey, true); 2111 d->m_dircache.remove(cKey); 2112 d->m_savelocations.remove(cKey); 2113 } 2114 } 2115 } 2116 2117 // check if the number of config dirs changed 2118 bool configDirsChanged = (resourceDirs("config").count() != configdirs); 2119 // If the config dirs changed, we check kiosk restrictions again. 2120 d->m_checkRestrictions = configDirsChanged; 2121 // return true if the number of config dirs changed: reparse config file 2122 return configDirsChanged; 2123 } 2124 2125 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 2126 QString KStandardDirs::localkdedir() const 2127 { 2128 // Return the prefix to use for saving 2129 return realPath(d->m_prefixes.first()); 2130 } 2131 2132 QString KStandardDirs::localxdgdatadir() const 2133 { 2134 // Return the prefix to use for saving 2135 return realPath(d->xdgdata_prefixes.first()); 2136 } 2137 2138 QString KStandardDirs::localxdgconfdir() const 2139 { 2140 // Return the prefix to use for saving 2141 return realPath(d->xdgconf_prefixes.first()); 2142 } 2143 #endif // KDELIBS4SUPPORT_NO_DEPRECATED 2144 2145 // just to make code more readable without macros 2146 QString KStandardDirs::locate(const char *type, 2147 const QString &filename) 2148 { 2149 return KGlobal::dirs()->findResource(type, filename); 2150 } 2151 2152 QString KStandardDirs::locateLocal(const char *type, 2153 const QString &filename) 2154 { 2155 return KGlobal::dirs()->locateLocal(type, filename, true); 2156 } 2157 2158 QString KStandardDirs::locateLocal(const char *type, 2159 const QString &filename, bool createDir) 2160 { 2161 // try to find slashes. If there are some, we have to 2162 // create the subdir first 2163 int slash = filename.lastIndexOf(QLatin1Char('/')) + 1; 2164 if (!slash) { // only one filename 2165 return KGlobal::dirs()->saveLocation(type, QString(), createDir) + filename; 2166 } 2167 2168 // split path from filename 2169 QString dir = filename.left(slash); 2170 QString file = filename.mid(slash); 2171 return KGlobal::dirs()->saveLocation(type, dir, createDir) + file; 2172 } 2173 2174 bool KStandardDirs::checkAccess(const QString &pathname, int mode) 2175 { 2176 int accessOK = KDE::access(pathname, mode); 2177 if (accessOK == 0) { 2178 return true; // OK, I can really access the file 2179 } 2180 2181 // else 2182 // if we want to write the file would be created. Check, if the 2183 // user may write to the directory to create the file. 2184 if ((mode & W_OK) == 0) { 2185 return false; // Check for write access is not part of mode => bail out 2186 } 2187 2188 if (!KDE::access(pathname, F_OK)) { // if it already exists 2189 return false; 2190 } 2191 2192 //strip the filename (everything until '/' from the end 2193 QString dirName(pathname); 2194 int pos = dirName.lastIndexOf(QLatin1Char('/')); 2195 if (pos == -1) { 2196 return false; // No path in argument. This is evil, we won't allow this 2197 } else if (pos == 0) { // don't turn e.g. /root into an empty string 2198 pos = 1; 2199 } 2200 2201 dirName.truncate(pos); // strip everything starting from the last '/' 2202 2203 accessOK = KDE::access(dirName, W_OK); 2204 // -?- Can I write to the accessed diretory 2205 if (accessOK == 0) { 2206 return true; // Yes 2207 } else { 2208 return false; // No 2209 } 2210 } 2211