You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
11 KiB
375 lines
11 KiB
/* |
|
This file is part of the KDE libraries |
|
SPDX-FileCopyrightText: 1999 Pietro Iglio <iglio@kde.org> |
|
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org> |
|
|
|
SPDX-License-Identifier: LGPL-2.0-or-later |
|
*/ |
|
|
|
#include "kdesktopfile.h" |
|
|
|
#ifndef Q_OS_WIN |
|
#include <unistd.h> |
|
#endif |
|
|
|
#include <QDir> |
|
#include <QFileInfo> |
|
#include <QUrl> |
|
#include <QStandardPaths> |
|
|
|
#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<KDesktopFile *>(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; |
|
}
|
|
|