KConfig

kdesktopfile.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1999 Pietro Iglio <iglio@kde.org>
4 SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kdesktopfile.h"
10
11#include "kauthorized.h"
12#include "kconfig_core_log_settings.h"
13#include "kconfig_p.h"
14#include "kconfiggroup.h"
15#include "kconfigini_p.h"
16#include "kdesktopfileaction.h"
17
18#include <QDir>
19#include <QFileInfo>
20#include <QStandardPaths>
21#include <QUrl>
22
23#ifndef Q_OS_WIN
24#include <unistd.h>
25#endif
26
27#include <algorithm>
28
29class KDesktopFilePrivate : public KConfigPrivate
30{
31public:
32 KDesktopFilePrivate(QStandardPaths::StandardLocation resourceType, const QString &fileName)
33 : KConfigPrivate(KConfig::NoGlobals, resourceType)
34 {
35 mBackend = new KConfigIniBackend();
36 bDynamicBackend = false;
37 changeFileName(fileName);
38 }
39 KConfigGroup desktopGroup;
40};
41
43 : KConfig(*new KDesktopFilePrivate(resourceType, fileName))
44{
47 d->desktopGroup = KConfigGroup(this, QStringLiteral("Desktop Entry"));
48}
49
51 : KDesktopFile(QStandardPaths::ApplicationsLocation, fileName)
52{
53}
54
56
58{
59 Q_D(const KDesktopFile);
60 return d->desktopGroup;
61}
62
64{
65 static const QLatin1Char slash('/');
66
67 // Relative to config? (e.g. for autostart)
69 // Iterate from the last item since some items may be subfolders of others.
70 auto it = std::find_if(genericConfig.crbegin(), genericConfig.crend(), [&path](const QString &dir) {
71 return path.startsWith(dir + slash);
72 });
73 if (it != genericConfig.crend()) {
75 }
76
77 QString relativePath;
78 // Relative to xdg data dir? (much more common)
80 for (const QString &dir : lstGenericDataLocation) {
81 if (path.startsWith(dir + slash)) {
82 relativePath = path.mid(dir.length() + 1);
83 }
84 }
85 if (relativePath.isEmpty()) {
86 // What now? The desktop file doesn't come from XDG_DATA_DIRS. Use filename only and hope for the best.
87 relativePath = path.mid(path.lastIndexOf(slash) + 1);
88 }
90}
91
93{
94 return path.endsWith(QLatin1String(".desktop"));
95}
96
98{
99 if (path.isEmpty()) {
100 return false; // Empty paths are not ok.
101 }
102
103 if (QDir::isRelativePath(path)) {
104 return true; // Relative paths are ok.
105 }
106
107 const QString realPath = QFileInfo(path).canonicalFilePath();
108 if (realPath.isEmpty()) {
109 return false; // File doesn't exist.
110 }
111
112#ifndef Q_OS_WIN
113 static constexpr Qt::CaseSensitivity sensitivity = Qt::CaseSensitive;
114#else
115 static constexpr Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive;
116#endif
117
118 // Check if the .desktop file is installed as part of KDE or XDG.
120 auto it = std::find_if(appsDirs.cbegin(), appsDirs.cend(), [&realPath](const QString &prefix) {
121 QFileInfo info(prefix);
122 return info.exists() && info.isDir() && realPath.startsWith(info.canonicalFilePath(), sensitivity);
123 });
124 if (it != appsDirs.cend()) {
125 return true;
126 }
127
128 const QString autostartDir = QStringLiteral("autostart/");
130 auto configIt = std::find_if(lstConfigPath.cbegin(), lstConfigPath.cend(), [&realPath, &autostartDir](const QString &xdgDataPrefix) {
131 QFileInfo info(xdgDataPrefix);
132 if (info.exists() && info.isDir()) {
133 const QString prefix = info.canonicalFilePath();
134 return realPath.startsWith(prefix + QLatin1Char('/') + autostartDir, sensitivity);
135 }
136 return false;
137 });
138 if (configIt != lstConfigPath.cend()) {
139 return true;
140 }
141
142 // Forbid desktop files outside of standard locations if kiosk is set so
143 if (!KAuthorized::authorize(QStringLiteral("run_desktop_files"))) {
144 qCWarning(KCONFIG_CORE_LOG) << "Access to" << path << "denied because of 'run_desktop_files' restriction.";
145 return false;
146 }
147
148 // Not otherwise permitted, so only allow if the file is executable, or if
149 // owned by root (uid == 0)
150 QFileInfo entryInfo(path);
151 if (entryInfo.isExecutable() || entryInfo.ownerId() == 0) {
152 return true;
153 }
154
155 qCInfo(KCONFIG_CORE_LOG) << "Access to" << path << "denied, not owned by root and executable flag not set.";
156 return false;
157}
158
160{
161 Q_D(const KDesktopFile);
162 return d->desktopGroup.readEntry("Type", QString());
163}
164
166{
167 Q_D(const KDesktopFile);
168 return d->desktopGroup.readEntry("Icon", QString());
169}
170
172{
173 Q_D(const KDesktopFile);
174 return d->desktopGroup.readEntry("Name", QString());
175}
176
178{
179 Q_D(const KDesktopFile);
180 return d->desktopGroup.readEntry("Comment", QString());
181}
182
184{
185 Q_D(const KDesktopFile);
186 return d->desktopGroup.readEntry("GenericName", QString());
187}
188
190{
191 Q_D(const KDesktopFile);
192 // NOT readPathEntry, it is not XDG-compliant: it performs
193 // various expansions, like $HOME. Note that the expansion
194 // behaviour still happens if the "e" flag is set, maintaining
195 // backwards compatibility.
196 return d->desktopGroup.readEntry("Path", QString());
197}
198
200{
201 Q_D(const KDesktopFile);
202 if (hasDeviceType()) {
203 return d->desktopGroup.readEntry("MountPoint", QString());
204 } else {
205 // NOT readPathEntry (see readPath())
206 QString url = d->desktopGroup.readEntry("URL", QString());
207 if (!url.isEmpty() && !QDir::isRelativePath(url)) {
208 // Handle absolute paths as such (i.e. we need to escape them)
209 return QUrl::fromLocalFile(url).toString();
210 }
211 return url;
212 }
213}
214
216{
217 Q_D(const KDesktopFile);
218 return d->desktopGroup.readXdgListEntry("Actions");
219}
220
222{
223 Q_D(const KDesktopFile);
224 return d->desktopGroup.readXdgListEntry("MimeType");
225}
226
228{
229 return KConfigGroup(this, QLatin1String("Desktop Action ") + group);
230}
231
233{
234 return const_cast<KDesktopFile *>(this)->actionGroup(group);
235}
236
237bool KDesktopFile::hasActionGroup(const QString &group) const
238{
239 return hasGroup(QString(QLatin1String("Desktop Action ") + group));
240}
241
243{
244 return readType() == QLatin1String("Link");
245}
246
248{
249 return readType() == QLatin1String("Application");
250}
251
253{
254 return readType() == QLatin1String("FSDevice");
255}
256
258{
259 Q_D(const KDesktopFile);
260 // Test for TryExec and "X-KDE-AuthorizeAction"
261 // NOT readPathEntry (see readPath())
262 const QString te = d->desktopGroup.readEntry("TryExec", QString());
264 return false;
265 }
266 const QStringList list = d->desktopGroup.readEntry("X-KDE-AuthorizeAction", QStringList());
267 const auto isNotAuthorized = std::any_of(list.cbegin(), list.cend(), [](const QString &action) {
268 return !KAuthorized::authorize(action.trimmed());
269 });
270 if (isNotAuthorized) {
271 return false;
272 }
273
274 // See also KService::username()
275 if (const bool su = d->desktopGroup.readEntry("X-KDE-SubstituteUID", false)) {
276 QString user = d->desktopGroup.readEntry("X-KDE-Username", QString());
277 if (user.isEmpty()) {
278 user = qEnvironmentVariable("ADMIN_ACCOUNT"), QStringLiteral("root");
279 }
280 if (!KAuthorized::authorize(QLatin1String("user/") + user)) {
281 return false;
282 }
283 }
284
285 return true;
286}
287
289{
290 Q_D(const KDesktopFile);
291 return d->desktopGroup.readPathEntry("X-DocPath", QString());
292}
293
295{
296 KDesktopFile *config = new KDesktopFile(QString());
297 this->KConfig::copyTo(file, config);
298 return config;
299}
300
302{
303 return name();
304}
305
307{
308 Q_D(const KDesktopFile);
309 return d->desktopGroup.readEntry("NoDisplay", false);
310}
311
313{
314 QList<KDesktopFileAction> desktopFileActions;
315 const QStringList actionKeys = readActions();
316 for (const QString &actionKey : actionKeys) {
317 const KConfigGroup grp = actionGroup(actionKey);
318 desktopFileActions << KDesktopFileAction(actionKey, grp.readEntry("Name"), grp.readEntry("Icon"), grp.readEntry("Exec"), fileName());
319 }
320 return desktopFileActions;
321}
static Q_INVOKABLE bool authorize(const QString &action)
Returns whether the user is permitted to perform a certain action.
bool hasGroup(const QString &group) const
Returns true if the specified group is known about.
KConfigGroup group(const QString &group)
Returns an object for the named subgroup.
A class for one specific group in a KConfig object.
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
The central class of the KDE configuration data system.
Definition kconfig.h:56
void reparseConfiguration()
Updates the state of this object to match the persistent storage.
Definition kconfig.cpp:652
QString name() const
Returns the filename used to store the configuration.
Definition kconfig.cpp:564
KConfig * copyTo(const QString &file, KConfig *config=nullptr) const
Copies all entries from this config object to a new config object that will save itself to file.
Definition kconfig.cpp:546
Class for representing an Action of a desktop file.
KDE Desktop File Management.
bool hasApplicationType() const
Checks whether there is an entry "Type=Application".
QString readPath() const
Returns the value of the "Path=" entry.
QString fileName() const
Returns the name of the .desktop file that was used to construct this KDesktopFile.
KConfigGroup actionGroup(const QString &group)
Sets the desktop action group.
QString readGenericName() const
Returns the value of the "GenericName=" entry.
QStringList readMimeTypes() const
Returns a list of the "MimeType=" entries.
bool hasDeviceType() const
Checks whether there is an entry "Type=FSDevice".
QString readName() const
Returns the value of the "Name=" entry.
KDesktopFile * copyTo(const QString &file) const
Copies all entries from this config object to a new KDesktopFile object that will save itself to file...
bool noDisplay() const
Whether the entry should be suppressed in menus.
QString readComment() const
Returns the value of the "Comment=" entry.
QList< KDesktopFileAction > actions() const
KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName)
Constructs a KDesktopFile object.
~KDesktopFile() override
Destructs the KDesktopFile object.
static bool isDesktopFile(const QString &path)
Checks whether this is really a desktop file.
bool hasLinkType() const
Checks whether there is a "Type=Link" entry.
bool tryExec() const
Checks whether the TryExec field contains a binary which is found on the local system.
KConfigGroup desktopGroup() const
Returns the main config group (named "Desktop Entry") in a .desktop file.
static QString locateLocal(const QString &path)
Returns the location where changes for the .desktop file path should be written to.
QString readDocPath() const
Returns the value of the "X-DocPath=" Or "DocPath=" entry.
QString readUrl() const
Returns the value of the "URL=" entry.
QString readIcon() const
Returns the value of the "Icon=" entry.
static bool isAuthorizedDesktopFile(const QString &path)
Checks whether the user is authorized to run this desktop file.
bool hasActionGroup(const QString &group) const
Returns true if the action group exists, false otherwise.
QString readType() const
Returns the value of the "Type=" entry.
QStringList readActions() const
Returns a list of the "Actions=" entries.
QString path(const QString &relativePath)
bool isRelativePath(const QString &path)
QString canonicalFilePath() const const
const_iterator cbegin() const const
const_iterator cend() const const
const_reverse_iterator crbegin() const const
const_reverse_iterator crend() const const
QString findExecutable(const QString &executableName, const QStringList &paths)
QStringList standardLocations(StandardLocation type)
QString writableLocation(StandardLocation type)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QStringView mid(qsizetype start, qsizetype length) const const
CaseSensitivity
QUrl fromLocalFile(const QString &localFile)
QString toString(FormattingOptions options) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:27 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.