1 | /* |
2 | This file is part of the KDE libraries |
3 | Copyright (c) 1999 Pietro Iglio <iglio@kde.org> |
4 | Copyright (c) 1999 Preston Brown <pbrown@kde.org> |
5 | |
6 | This library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Library General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2 of the License, or (at your option) any later version. |
10 | |
11 | This library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Library General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Library General Public License |
17 | along with this library; see the file COPYING.LIB. If not, write to |
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | Boston, MA 02110-1301, USA. |
20 | */ |
21 | |
22 | #include "kdesktopfile.h" |
23 | |
24 | #include <unistd.h> |
25 | |
26 | #include <QtCore/QDir> |
27 | #include <QtCore/QFileInfo> |
28 | |
29 | #include "kconfig_p.h" |
30 | #include "kdebug.h" |
31 | #include "kurl.h" |
32 | #include "kconfiggroup.h" |
33 | #include "kauthorized.h" |
34 | #include "kstandarddirs.h" |
35 | #include "kconfigini_p.h" |
36 | #include "kde_file.h" |
37 | |
38 | class KDesktopFilePrivate : public KConfigPrivate |
39 | { |
40 | public: |
41 | KDesktopFilePrivate(const char * resourceType, const QString &fileName); |
42 | KConfigGroup desktopGroup; |
43 | }; |
44 | |
45 | KDesktopFilePrivate::KDesktopFilePrivate(const char * resourceType, const QString &fileName) |
46 | : KConfigPrivate(KGlobal::mainComponent(), KConfig::NoGlobals, resourceType) |
47 | { |
48 | mBackend = new KConfigIniBackend(); |
49 | bDynamicBackend = false; |
50 | changeFileName(fileName, resourceType); |
51 | } |
52 | |
53 | KDesktopFile::KDesktopFile(const char * resourceType, const QString &fileName) |
54 | : KConfig(*new KDesktopFilePrivate(resourceType, fileName)) |
55 | { |
56 | Q_D(KDesktopFile); |
57 | reparseConfiguration(); |
58 | d->desktopGroup = KConfigGroup(this, "Desktop Entry" ); |
59 | } |
60 | |
61 | KDesktopFile::KDesktopFile(const QString &fileName) |
62 | : KConfig(*new KDesktopFilePrivate("apps" , fileName)) // TODO KDE5: default to xdgdata-apps instead of apps |
63 | { |
64 | Q_D(KDesktopFile); |
65 | reparseConfiguration(); |
66 | d->desktopGroup = KConfigGroup(this, "Desktop Entry" ); |
67 | } |
68 | |
69 | KDesktopFile::~KDesktopFile() |
70 | { |
71 | } |
72 | |
73 | KConfigGroup KDesktopFile::desktopGroup() const |
74 | { |
75 | Q_D(const KDesktopFile); |
76 | return d->desktopGroup; |
77 | } |
78 | |
79 | QString KDesktopFile::locateLocal(const QString &path) |
80 | { |
81 | QString local; |
82 | if (path.endsWith(QLatin1String(".directory" ))) |
83 | { |
84 | local = path; |
85 | if (!QDir::isRelativePath(local)) |
86 | { |
87 | // Relative wrt apps? |
88 | local = KGlobal::dirs()->relativeLocation("apps" , path); |
89 | } |
90 | |
91 | if (QDir::isRelativePath(local)) |
92 | { |
93 | local = KStandardDirs::locateLocal("apps" , local); // Relative to apps |
94 | } |
95 | else |
96 | { |
97 | // XDG Desktop menu items come with absolute paths, we need to |
98 | // extract their relative path and then build a local path. |
99 | local = KGlobal::dirs()->relativeLocation("xdgdata-dirs" , local); |
100 | if (!QDir::isRelativePath(local)) |
101 | { |
102 | // Hm, that didn't work... |
103 | // What now? Use filename only and hope for the best. |
104 | local = path.mid(path.lastIndexOf(QLatin1Char('/'))+1); |
105 | } |
106 | local = KStandardDirs::locateLocal("xdgdata-dirs" , local); |
107 | } |
108 | } |
109 | else |
110 | { |
111 | if (QDir::isRelativePath(path)) |
112 | { |
113 | local = KStandardDirs::locateLocal("apps" , path); // Relative to apps |
114 | } |
115 | else |
116 | { |
117 | // XDG Desktop menu items come with absolute paths, we need to |
118 | // extract their relative path and then build a local path. |
119 | local = KGlobal::dirs()->relativeLocation("xdgdata-apps" , path); |
120 | if (!QDir::isRelativePath(local)) |
121 | { |
122 | // What now? Use filename only and hope for the best. |
123 | local = path.mid(path.lastIndexOf(QLatin1Char('/'))+1); |
124 | } |
125 | local = KStandardDirs::locateLocal("xdgdata-apps" , local); |
126 | } |
127 | } |
128 | return local; |
129 | } |
130 | |
131 | bool KDesktopFile::isDesktopFile(const QString& path) |
132 | { |
133 | return (path.length() > 8 |
134 | && path.endsWith(QLatin1String(".desktop" ))); |
135 | } |
136 | |
137 | bool KDesktopFile::isAuthorizedDesktopFile(const QString& path) |
138 | { |
139 | if (path.isEmpty()) |
140 | return false; // Empty paths are not ok. |
141 | |
142 | if (QDir::isRelativePath(path)) |
143 | return true; // Relative paths are ok. |
144 | |
145 | KStandardDirs *dirs = KGlobal::dirs(); |
146 | QStringList kdePrefixes; |
147 | kdePrefixes += dirs->resourceDirs("apps" ); |
148 | kdePrefixes += dirs->resourceDirs("services" ); |
149 | kdePrefixes += dirs->resourceDirs("xdgdata-apps" ); |
150 | kdePrefixes += dirs->resourceDirs("autostart" ); |
151 | |
152 | const QString realPath = KStandardDirs::realPath(path); |
153 | |
154 | // Check if the .desktop file is installed as part of KDE or XDG. |
155 | foreach (const QString &prefix, kdePrefixes) { |
156 | #ifndef Q_OS_WIN |
157 | if (realPath.startsWith(prefix)) |
158 | #else |
159 | if (realPath.startsWith(prefix, Qt::CaseInsensitive)) |
160 | #endif |
161 | return true; |
162 | } |
163 | |
164 | // Forbid desktop files outside of standard locations if kiosk is set so |
165 | if (!KAuthorized::authorize(QLatin1String("run_desktop_files" ))) { |
166 | kWarning() << "Access to '" << path << "' denied because of 'run_desktop_files' restriction." << endl; |
167 | return false; |
168 | } |
169 | |
170 | // Not otherwise permitted, so only allow if the file is executable, or if |
171 | // owned by root (uid == 0) |
172 | QFileInfo entryInfo( path ); |
173 | if (entryInfo.isExecutable() || entryInfo.ownerId() == 0) |
174 | return true; |
175 | |
176 | kWarning() << "Access to '" << path << "' denied, not owned by root, executable flag not set." << endl; |
177 | return false; |
178 | } |
179 | |
180 | QString KDesktopFile::readType() const |
181 | { |
182 | Q_D(const KDesktopFile); |
183 | return d->desktopGroup.readEntry("Type" , QString()); |
184 | } |
185 | |
186 | QString KDesktopFile::readIcon() const |
187 | { |
188 | Q_D(const KDesktopFile); |
189 | return d->desktopGroup.readEntry("Icon" , QString()); |
190 | } |
191 | |
192 | QString KDesktopFile::readName() const |
193 | { |
194 | Q_D(const KDesktopFile); |
195 | return d->desktopGroup.readEntry("Name" , QString()); |
196 | } |
197 | |
198 | QString KDesktopFile::() const |
199 | { |
200 | Q_D(const KDesktopFile); |
201 | return d->desktopGroup.readEntry("Comment" , QString()); |
202 | } |
203 | |
204 | QString KDesktopFile::readGenericName() const |
205 | { |
206 | Q_D(const KDesktopFile); |
207 | return d->desktopGroup.readEntry("GenericName" , QString()); |
208 | } |
209 | |
210 | QString KDesktopFile::readPath() const |
211 | { |
212 | Q_D(const KDesktopFile); |
213 | // NOT readPathEntry, it is not XDG-compliant. Path entries written by |
214 | // KDE4 will be still treated as such, though. |
215 | return d->desktopGroup.readEntry("Path" , QString()); |
216 | } |
217 | |
218 | QString KDesktopFile::readDevice() const |
219 | { |
220 | Q_D(const KDesktopFile); |
221 | return d->desktopGroup.readEntry("Dev" , QString()); |
222 | } |
223 | |
224 | QString KDesktopFile::readUrl() const |
225 | { |
226 | Q_D(const KDesktopFile); |
227 | if (hasDeviceType()) { |
228 | return d->desktopGroup.readEntry("MountPoint" , QString()); |
229 | } else { |
230 | // NOT readPathEntry (see readPath()) |
231 | QString url = d->desktopGroup.readEntry("URL" , QString()); |
232 | if ( !url.isEmpty() && !QDir::isRelativePath(url) ) |
233 | { |
234 | // Handle absolute paths as such (i.e. we need to escape them) |
235 | return KUrl(url).url(); |
236 | } |
237 | return url; |
238 | } |
239 | } |
240 | |
241 | QStringList KDesktopFile::readActions() const |
242 | { |
243 | Q_D(const KDesktopFile); |
244 | return d->desktopGroup.readXdgListEntry("Actions" ); |
245 | } |
246 | |
247 | KConfigGroup KDesktopFile::actionGroup(const QString &group) |
248 | { |
249 | return KConfigGroup(this, QLatin1String("Desktop Action " ) + group); |
250 | } |
251 | |
252 | const KConfigGroup KDesktopFile::actionGroup(const QString& group) const |
253 | { |
254 | return const_cast<KDesktopFile*>(this)->actionGroup(group); |
255 | } |
256 | |
257 | bool KDesktopFile::hasActionGroup(const QString &group) const |
258 | { |
259 | return hasGroup(QString(QLatin1String("Desktop Action " ) + group).toUtf8().constData()); |
260 | } |
261 | |
262 | bool KDesktopFile::hasLinkType() const |
263 | { |
264 | return readType() == QLatin1String("Link" ); |
265 | } |
266 | |
267 | bool KDesktopFile::hasApplicationType() const |
268 | { |
269 | return readType() == QLatin1String("Application" ); |
270 | } |
271 | |
272 | bool KDesktopFile::hasMimeTypeType() const |
273 | { |
274 | return readType() == QLatin1String("MimeType" ); |
275 | } |
276 | |
277 | bool KDesktopFile::hasDeviceType() const |
278 | { |
279 | return readType() == QLatin1String("FSDevice" ); |
280 | } |
281 | |
282 | bool KDesktopFile::tryExec() const |
283 | { |
284 | Q_D(const KDesktopFile); |
285 | // Test for TryExec and "X-KDE-AuthorizeAction" |
286 | // NOT readPathEntry (see readPath()) |
287 | QString te = d->desktopGroup.readEntry("TryExec" , QString()); |
288 | |
289 | if (!te.isEmpty()) { |
290 | if (!QDir::isRelativePath(te)) { |
291 | if (KDE::access(te, X_OK)) |
292 | return false; |
293 | } else { |
294 | // !!! Sergey A. Sukiyazov <corwin@micom.don.ru> !!! |
295 | // Environment PATH may contain filenames in 8bit locale specified |
296 | // encoding (Like a filenames). |
297 | const QStringList dirs = QFile::decodeName(qgetenv("PATH" )) |
298 | .split(QLatin1Char(KPATH_SEPARATOR), QString::SkipEmptyParts); |
299 | QStringList::ConstIterator it(dirs.begin()); |
300 | bool match = false; |
301 | for (; it != dirs.end(); ++it) { |
302 | QString fName = *it + QLatin1Char(KDIR_SEPARATOR) + te; |
303 | if (KDE::access(fName, X_OK) == 0) |
304 | { |
305 | match = true; |
306 | break; |
307 | } |
308 | } |
309 | // didn't match at all |
310 | if (!match) |
311 | return false; |
312 | } |
313 | } |
314 | const QStringList list = d->desktopGroup.readEntry("X-KDE-AuthorizeAction" , QStringList()); |
315 | if (!list.isEmpty()) |
316 | { |
317 | for(QStringList::ConstIterator it = list.begin(); |
318 | it != list.end(); |
319 | ++it) |
320 | { |
321 | if (!KAuthorized::authorize((*it).trimmed())) |
322 | return false; |
323 | } |
324 | } |
325 | |
326 | // See also KService::username() |
327 | bool su = d->desktopGroup.readEntry("X-KDE-SubstituteUID" , false); |
328 | if (su) |
329 | { |
330 | QString user = d->desktopGroup.readEntry("X-KDE-Username" , QString()); |
331 | if (user.isEmpty()) |
332 | user = QString::fromLocal8Bit(qgetenv("ADMIN_ACCOUNT" )); |
333 | if (user.isEmpty()) |
334 | user = QString::fromLatin1("root" ); |
335 | if (!KAuthorized::authorize(QString::fromLatin1("user/" )+user)) |
336 | return false; |
337 | } |
338 | |
339 | return true; |
340 | } |
341 | |
342 | /** |
343 | * @return the filename as passed to the constructor. |
344 | */ |
345 | //QString KDesktopFile::fileName() const { return backEnd->fileName(); } |
346 | |
347 | /** |
348 | * @return the resource type as passed to the constructor. |
349 | */ |
350 | //QString |
351 | //KDesktopFile::resource() const { return backEnd->resource(); } |
352 | |
353 | QStringList |
354 | KDesktopFile::sortOrder() const |
355 | { |
356 | Q_D(const KDesktopFile); |
357 | return d->desktopGroup.readEntry("SortOrder" , QStringList()); |
358 | } |
359 | |
360 | //void KDesktopFile::virtual_hook( int id, void* data ) |
361 | //{ KConfig::virtual_hook( id, data ); } |
362 | |
363 | QString KDesktopFile::readDocPath() const |
364 | { |
365 | Q_D(const KDesktopFile); |
366 | //legacy entry in kde3 apps |
367 | if(d->desktopGroup.hasKey( "DocPath" )) |
368 | return d->desktopGroup.readPathEntry( "DocPath" , QString() ); |
369 | return d->desktopGroup.readPathEntry( "X-DocPath" , QString() ); |
370 | } |
371 | |
372 | KDesktopFile* KDesktopFile::copyTo(const QString &file) const |
373 | { |
374 | KDesktopFile *config = new KDesktopFile(QString()); |
375 | this->KConfig::copyTo(file, config); |
376 | // config->setDesktopGroup(); |
377 | return config; |
378 | } |
379 | |
380 | const char *KDesktopFile::resource() const |
381 | { |
382 | Q_D(const KDesktopFile); |
383 | return d->resourceType; |
384 | } |
385 | |
386 | QString KDesktopFile::fileName() const |
387 | { |
388 | return name(); |
389 | } |
390 | |
391 | bool KDesktopFile::noDisplay() const |
392 | { |
393 | Q_D(const KDesktopFile); |
394 | if (d->desktopGroup.readEntry("NoDisplay" , false)) { |
395 | return true; |
396 | } |
397 | if (d->desktopGroup.hasKey("OnlyShowIn" )) { |
398 | if (!d->desktopGroup.readXdgListEntry("OnlyShowIn" ).contains(QLatin1String("KDE" ))) |
399 | return true; |
400 | } |
401 | if (d->desktopGroup.hasKey("NotShowIn" )) { |
402 | if (d->desktopGroup.readXdgListEntry("NotShowIn" ).contains(QLatin1String("KDE" ))) |
403 | return true; |
404 | } |
405 | return false; |
406 | } |
407 | |