/* This file is part of the KDE libraries Copyright (c) 1999 Pietro Iglio Copyright (c) 1999 Preston Brown This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdesktopfile.h" #ifndef Q_OS_WIN #include #endif #include #include #include #include #include "kauthorized.h" #include "kconfig_p.h" #include "kconfiggroup.h" #include "kconfigini_p.h" #include "kconfig_core_log_settings.h" class KDesktopFilePrivate : public KConfigPrivate { public: KDesktopFilePrivate(QStandardPaths::StandardLocation resourceType, const QString &fileName); KConfigGroup desktopGroup; }; KDesktopFilePrivate::KDesktopFilePrivate(QStandardPaths::StandardLocation resourceType, const QString &fileName) : KConfigPrivate(KConfig::NoGlobals, resourceType) { mBackend = new KConfigIniBackend(); bDynamicBackend = false; changeFileName(fileName); } KDesktopFile::KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName) : KConfig(*new KDesktopFilePrivate(resourceType, fileName)) { Q_D(KDesktopFile); reparseConfiguration(); d->desktopGroup = KConfigGroup(this, "Desktop Entry"); } KDesktopFile::KDesktopFile(const QString &fileName) : KConfig(*new KDesktopFilePrivate(QStandardPaths::ApplicationsLocation, fileName)) { Q_D(KDesktopFile); reparseConfiguration(); d->desktopGroup = KConfigGroup(this, "Desktop Entry"); } KDesktopFile::~KDesktopFile() { } KConfigGroup KDesktopFile::desktopGroup() const { Q_D(const KDesktopFile); return d->desktopGroup; } QString KDesktopFile::locateLocal(const QString &path) { QString relativePath; QChar plus(QLatin1Char('/')); // Relative to config? (e.g. for autostart) const QStringList lstGenericConfigLocation = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); for (const QString &dir : lstGenericConfigLocation) { if (path.startsWith(dir + plus)) { relativePath = path.mid(dir.length() + 1); return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + relativePath; } } // Relative to xdg data dir? (much more common) const QStringList lstGenericDataLocation = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &dir : lstGenericDataLocation) { if (path.startsWith(dir + plus)) { relativePath = path.mid(dir.length() + 1); } } if (relativePath.isEmpty()) { // What now? The desktop file doesn't come from XDG_DATA_DIRS. Use filename only and hope for the best. relativePath = path.mid(path.lastIndexOf(QLatin1Char('/')) + 1); } return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + relativePath; } bool KDesktopFile::isDesktopFile(const QString &path) { return path.endsWith(QLatin1String(".desktop")); } bool KDesktopFile::isAuthorizedDesktopFile(const QString &path) { if (path.isEmpty()) { return false; // Empty paths are not ok. } if (QDir::isRelativePath(path)) { return true; // Relative paths are ok. } const QString realPath = QFileInfo(path).canonicalFilePath(); if (realPath.isEmpty()) { return false; // File doesn't exist. } #ifndef Q_OS_WIN const Qt::CaseSensitivity sensitivity = Qt::CaseSensitive; #else const Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; #endif // Check if the .desktop file is installed as part of KDE or XDG. const QStringList appsDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); for (const QString &prefix : appsDirs) { if (QDir(prefix).exists() && realPath.startsWith(QFileInfo(prefix).canonicalFilePath(), sensitivity)) { return true; } } const QString servicesDir = QStringLiteral("kservices5/"); // KGlobal::dirs()->xdgDataRelativePath("services") const QStringList lstGenericDataLocation = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &xdgDataPrefix : lstGenericDataLocation) { if (QDir(xdgDataPrefix).exists()) { const QString prefix = QFileInfo(xdgDataPrefix).canonicalFilePath(); if (realPath.startsWith(prefix + QLatin1Char('/') + servicesDir, sensitivity)) { return true; } } } const QString autostartDir = QStringLiteral("autostart/"); const QStringList lstConfigPath = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); for (const QString &xdgDataPrefix : lstConfigPath) { if (QDir(xdgDataPrefix).exists()) { const QString prefix = QFileInfo(xdgDataPrefix).canonicalFilePath(); if (realPath.startsWith(prefix + QLatin1Char('/') + autostartDir, sensitivity)) { return true; } } } // Forbid desktop files outside of standard locations if kiosk is set so if (!KAuthorized::authorize(QStringLiteral("run_desktop_files"))) { qCWarning(KCONFIG_CORE_LOG) << "Access to '" << path << "' denied because of 'run_desktop_files' restriction."; return false; } // Not otherwise permitted, so only allow if the file is executable, or if // owned by root (uid == 0) QFileInfo entryInfo(path); if (entryInfo.isExecutable() || entryInfo.ownerId() == 0) { return true; } qCWarning(KCONFIG_CORE_LOG) << "Access to '" << path << "' denied, not owned by root, executable flag not set."; return false; } QString KDesktopFile::readType() const { Q_D(const KDesktopFile); return d->desktopGroup.readEntry("Type", QString()); } QString KDesktopFile::readIcon() const { Q_D(const KDesktopFile); return d->desktopGroup.readEntry("Icon", QString()); } QString KDesktopFile::readName() const { Q_D(const KDesktopFile); return d->desktopGroup.readEntry("Name", QString()); } QString KDesktopFile::readComment() const { Q_D(const KDesktopFile); return d->desktopGroup.readEntry("Comment", QString()); } QString KDesktopFile::readGenericName() const { Q_D(const KDesktopFile); return d->desktopGroup.readEntry("GenericName", QString()); } QString KDesktopFile::readPath() const { Q_D(const KDesktopFile); // NOT readPathEntry, it is not XDG-compliant: it performs // various expansions, like $HOME. Note that the expansion // behaviour still happens if the "e" flag is set, maintaining // backwards compatibility. return d->desktopGroup.readEntry("Path", QString()); } QString KDesktopFile::readDevice() const { Q_D(const KDesktopFile); return d->desktopGroup.readEntry("Dev", QString()); } QString KDesktopFile::readUrl() const { Q_D(const KDesktopFile); if (hasDeviceType()) { return d->desktopGroup.readEntry("MountPoint", QString()); } else { // NOT readPathEntry (see readPath()) QString url = d->desktopGroup.readEntry("URL", QString()); if (!url.isEmpty() && !QDir::isRelativePath(url)) { // Handle absolute paths as such (i.e. we need to escape them) return QUrl::fromLocalFile(url).toString(); } return url; } } QStringList KDesktopFile::readActions() const { Q_D(const KDesktopFile); return d->desktopGroup.readXdgListEntry("Actions"); } QStringList KDesktopFile::readMimeTypes() const { Q_D(const KDesktopFile); return d->desktopGroup.readXdgListEntry("MimeType"); } KConfigGroup KDesktopFile::actionGroup(const QString &group) { return KConfigGroup(this, QLatin1String("Desktop Action ") + group); } const KConfigGroup KDesktopFile::actionGroup(const QString &group) const { return const_cast(this)->actionGroup(group); } bool KDesktopFile::hasActionGroup(const QString &group) const { return hasGroup(QString(QLatin1String("Desktop Action ") + group).toUtf8().constData()); } bool KDesktopFile::hasLinkType() const { return readType() == QLatin1String("Link"); } bool KDesktopFile::hasApplicationType() const { return readType() == QLatin1String("Application"); } bool KDesktopFile::hasDeviceType() const { return readType() == QLatin1String("FSDevice"); } bool KDesktopFile::tryExec() const { Q_D(const KDesktopFile); // Test for TryExec and "X-KDE-AuthorizeAction" // NOT readPathEntry (see readPath()) QString te = d->desktopGroup.readEntry("TryExec", QString()); if (!te.isEmpty()) { if (QStandardPaths::findExecutable(te).isEmpty()) { return false; } } const QStringList list = d->desktopGroup.readEntry("X-KDE-AuthorizeAction", QStringList()); if (!list.isEmpty()) { for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { if (!KAuthorized::authorize((*it).trimmed())) { return false; } } } // See also KService::username() bool su = d->desktopGroup.readEntry("X-KDE-SubstituteUID", false); if (su) { QString user = d->desktopGroup.readEntry("X-KDE-Username", QString()); if (user.isEmpty()) { user = QString::fromLocal8Bit(qgetenv("ADMIN_ACCOUNT")); } if (user.isEmpty()) { user = QStringLiteral("root"); } if (!KAuthorized::authorize(QLatin1String("user/") + user)) { return false; } } return true; } /** * @return the filename as passed to the constructor. */ //QString KDesktopFile::fileName() const { return backEnd->fileName(); } /** * @return the resource type as passed to the constructor. */ //QString //KDesktopFile::resource() const { return backEnd->resource(); } #if KCONFIGCORE_BUILD_DEPRECATED_SINCE(5, 42) QStringList KDesktopFile::sortOrder() const { Q_D(const KDesktopFile); return d->desktopGroup.readXdgListEntry("SortOrder"); } #endif //void KDesktopFile::virtual_hook( int id, void* data ) //{ KConfig::virtual_hook( id, data ); } QString KDesktopFile::readDocPath() const { Q_D(const KDesktopFile); return d->desktopGroup.readPathEntry("X-DocPath", QString()); } KDesktopFile *KDesktopFile::copyTo(const QString &file) const { KDesktopFile *config = new KDesktopFile(QString()); this->KConfig::copyTo(file, config); // config->setDesktopGroup(); return config; } QStandardPaths::StandardLocation KDesktopFile::resource() const { Q_D(const KDesktopFile); return d->resourceType; } QString KDesktopFile::fileName() const { return name(); } bool KDesktopFile::noDisplay() const { Q_D(const KDesktopFile); if (d->desktopGroup.readEntry("NoDisplay", false)) { return true; } if (d->desktopGroup.hasKey("OnlyShowIn")) { if (!d->desktopGroup.readXdgListEntry("OnlyShowIn").contains(QLatin1String("KDE"))) { return true; } } if (d->desktopGroup.hasKey("NotShowIn")) { if (d->desktopGroup.readXdgListEntry("NotShowIn").contains(QLatin1String("KDE"))) { return true; } } return false; }