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 
29 class KDesktopFilePrivate : public KConfigPrivate
30 {
31 public:
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 
55 KDesktopFile::~KDesktopFile() = default;
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 
237 bool 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());
263  if (!te.isEmpty() && QStandardPaths::findExecutable(te).isEmpty()) {
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 }
QList::const_reverse_iterator crbegin() const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool hasLinkType() const
Checks whether there is a "Type=Link" entry.
QString readIcon() const
Returns the value of the "Icon=" entry.
QStringList readActions() const
Returns a list of the "Actions=" entries.
CaseSensitivity
QString name() const
Returns the filename used to store the configuration.
Definition: kconfig.cpp:564
QStringView mid(qsizetype start) const const
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
static bool isAuthorizedDesktopFile(const QString &path)
Checks whether the user is authorized to run this desktop file.
The central class of the KDE configuration data system.
Definition: kconfig.h:55
QString readType() const
Returns the value of the "Type=" entry.
bool hasActionGroup(const QString &group) const
Returns true if the action group exists, false otherwise.
bool hasGroup(const QString &group) const
Returns true if the specified group is known about.
Definition: kconfigbase.cpp:16
QString writableLocation(QStandardPaths::StandardLocation type)
void reparseConfiguration()
Updates the state of this object to match the persistent storage.
Definition: kconfig.cpp:652
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool hasApplicationType() const
Checks whether there is an entry "Type=Application".
static Q_INVOKABLE bool authorize(const QString &action)
Returns whether the user is permitted to perform a certain action.
QString findExecutable(const QString &executableName, const QStringList &paths)
QStringList standardLocations(QStandardPaths::StandardLocation type)
QString fileName() const
Returns the name of the .desktop file that was used to construct this KDesktopFile.
QString toString(QUrl::FormattingOptions options) const const
KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName)
Constructs a KDesktopFile object.
bool noDisplay() const
Whether the entry should be suppressed in menus.
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
QString readPath() const
Returns the value of the "Path=" entry.
int length() const const
QList< KDesktopFileAction > actions() const
bool hasDeviceType() const
Checks whether there is an entry "Type=FSDevice".
QList::const_iterator cend() const const
static bool isDesktopFile(const QString &path)
Checks whether this is really a desktop file.
Class for representing an Action of a desktop file.
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
Definition: kconfiggroup.h:216
QString readComment() const
Returns the value of the "Comment=" entry.
QString readGenericName() const
Returns the value of the "GenericName=" entry.
QString readDocPath() const
Returns the value of the "X-DocPath=" Or "DocPath=" entry.
KConfigGroup actionGroup(const QString &group)
Sets the desktop action group.
QString readName() const
Returns the value of the "Name=" entry.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString readUrl() const
Returns the value of the "URL=" entry.
~KDesktopFile() override
Destructs the KDesktopFile object.
KConfigGroup desktopGroup() const
Returns the main config group (named "Desktop Entry") in a .desktop file.
QString path(const QString &relativePath)
bool tryExec() const
Checks whether the TryExec field contains a binary which is found on the local system.
QList::const_iterator cbegin() const const
QStringList readMimeTypes() const
Returns a list of the "MimeType=" entries.
bool isRelativePath(const QString &path)
KConfigGroup group(const QString &group)
Returns an object for the named subgroup.
Definition: kconfigbase.cpp:21
QString mid(int position, int n) const const
KDesktopFile * copyTo(const QString &file) const
Copies all entries from this config object to a new KDesktopFile object that will save itself to file...
static QString locateLocal(const QString &path)
Returns the location where changes for the .desktop file path should be written to.
Q_D(Todo)
QString canonicalFilePath() const const
QList::const_reverse_iterator crend() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 04:07:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.