Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qstandardpaths_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2020 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qstandardpaths.h"
6#include <qdir.h>
7#include <qfile.h>
8#include <qhash.h>
9#include <qtextstream.h>
10#if QT_CONFIG(regularexpression)
11#include <qregularexpression.h>
12#endif
13#include <private/qfilesystemengine_p.h>
14#include <errno.h>
15#include <stdlib.h>
16
17#ifndef QT_BOOTSTRAPPED
18#include <qcoreapplication.h>
19#endif
20
21#ifndef QT_NO_STANDARDPATHS
22
24
25using namespace Qt::StringLiterals;
26
28{
29#ifndef QT_BOOTSTRAPPED
31 if (!org.isEmpty())
32 path += u'/' + org;
34 if (!appName.isEmpty())
35 path += u'/' + appName;
36#else
38#endif
39}
40
41#if QT_CONFIG(regularexpression)
43{
44 switch (type) {
46 return "DESKTOP"_L1;
48 return "DOCUMENTS"_L1;
50 return "PICTURES"_L1;
52 return "MUSIC"_L1;
54 return "VIDEOS"_L1;
56 return "DOWNLOAD"_L1;
58 return "PUBLICSHARE"_L1;
60 return "TEMPLATES"_L1;
61 default:
62 return {};
63 }
64}
65#endif
66
67static QByteArray unixPermissionsText(QFile::Permissions permissions)
68{
69 mode_t perms = 0;
70 if (permissions & QFile::ReadOwner)
71 perms |= S_IRUSR;
72 if (permissions & QFile::WriteOwner)
73 perms |= S_IWUSR;
74 if (permissions & QFile::ExeOwner)
75 perms |= S_IXUSR;
76 if (permissions & QFile::ReadGroup)
77 perms |= S_IRGRP;
78 if (permissions & QFile::WriteGroup)
79 perms |= S_IWGRP;
80 if (permissions & QFile::ExeGroup)
81 perms |= S_IXGRP;
82 if (permissions & QFile::ReadOther)
83 perms |= S_IROTH;
84 if (permissions & QFile::WriteOther)
85 perms |= S_IWOTH;
86 if (permissions & QFile::ExeOther)
87 perms |= S_IXOTH;
88 return '0' + QByteArray::number(perms, 8);
89}
90
91static bool checkXdgRuntimeDir(const QString &xdgRuntimeDir)
92{
93 auto describeMetaData = [](const QFileSystemMetaData &metaData) -> QByteArray {
94 if (!metaData.exists())
95 return "a broken symlink";
96
97 QByteArray description;
98 if (metaData.isLink())
99 description = "a symbolic link to ";
100
101 if (metaData.isFile())
102 description += "a regular file";
103 else if (metaData.isDirectory())
104 description += "a directory";
105 else if (metaData.isSequential())
106 description += "a character device, socket or FIFO";
107 else
108 description += "a block device";
109
110 description += " permissions " + unixPermissionsText(metaData.permissions());
111
112 return description
113 + " owned by UID " + QByteArray::number(metaData.userId())
114 + " GID " + QByteArray::number(metaData.groupId());
115 };
116
117 // http://standards.freedesktop.org/basedir-spec/latest/
118 const uint myUid = uint(geteuid());
119 const QFile::Permissions wantedPerms = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
120 const QFileSystemMetaData::MetaDataFlags statFlags = QFileSystemMetaData::PosixStatFlags
122 QFileSystemMetaData metaData;
123 QFileSystemEntry entry(xdgRuntimeDir);
124
125 // Check that the xdgRuntimeDir is a directory by attempting to create it.
126 // A stat() before mkdir() that concluded it doesn't exist is a meaningless
127 // result: we'd race against someone else attempting to create it.
128 // ### QFileSystemEngine::createDirectory cannot take the extra mode argument.
129 if (QT_MKDIR(entry.nativeFilePath(), 0700) == 0)
130 return true;
131 if (errno != EEXIST) {
132 qErrnoWarning("QStandardPaths: error creating runtime directory '%ls'",
133 qUtf16Printable(xdgRuntimeDir));
134 return false;
135 }
136
137 // We use LinkType to force an lstat(), but fillMetaData() still returns error
138 // on broken symlinks.
139 if (!QFileSystemEngine::fillMetaData(entry, metaData, statFlags) && !metaData.isLink()) {
140 qErrnoWarning("QStandardPaths: error obtaining permissions of runtime directory '%ls'",
141 qUtf16Printable(xdgRuntimeDir));
142 return false;
143 }
144
145 // Checks:
146 // - is a directory
147 // - is not a symlink (even is pointing to a directory)
148 if (metaData.isLink() || !metaData.isDirectory()) {
149 qWarning("QStandardPaths: runtime directory '%ls' is not a directory, but %s",
150 qUtf16Printable(xdgRuntimeDir), describeMetaData(metaData).constData());
151 return false;
152 }
153
154 // - "The directory MUST be owned by the user"
155 if (metaData.userId() != myUid) {
156 qWarning("QStandardPaths: runtime directory '%ls' is not owned by UID %d, but %s",
157 qUtf16Printable(xdgRuntimeDir), myUid, describeMetaData(metaData).constData());
158 return false;
159 }
160
161 // "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700."
162 if (metaData.permissions() != wantedPerms) {
163 qWarning("QStandardPaths: wrong permissions on runtime directory %ls, %s instead of %s",
164 qUtf16Printable(xdgRuntimeDir),
165 unixPermissionsText(metaData.permissions()).constData(),
166 unixPermissionsText(wantedPerms).constData());
167 return false;
168 }
169
170 return true;
171}
172
174{
175 switch (type) {
176 case HomeLocation:
177 return QDir::homePath();
178 case TempLocation:
179 return QDir::tempPath();
180 case CacheLocation:
182 {
183 QString xdgCacheHome;
184 if (isTestModeEnabled()) {
185 xdgCacheHome = QDir::homePath() + "/.qttest/cache"_L1;
186 } else {
187 // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
188 xdgCacheHome = QFile::decodeName(qgetenv("XDG_CACHE_HOME"));
189 if (!xdgCacheHome.startsWith(u'/'))
190 xdgCacheHome.clear(); // spec says relative paths should be ignored
191
192 if (xdgCacheHome.isEmpty())
193 xdgCacheHome = QDir::homePath() + "/.cache"_L1;
194 }
196 appendOrganizationAndApp(xdgCacheHome);
197 return xdgCacheHome;
198 }
199 case StateLocation:
201 {
202 QString xdgStateHome;
203 if (isTestModeEnabled()) {
204 xdgStateHome = QDir::homePath() + "/.qttest/state"_L1;
205 } else {
206 // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
207 xdgStateHome = QFile::decodeName(qgetenv("XDG_STATE_HOME"));
208 if (!xdgStateHome.startsWith(u'/'))
209 xdgStateHome.clear(); // spec says relative paths should be ignored
210
211 if (xdgStateHome.isEmpty())
212 xdgStateHome = QDir::homePath() + "/.local/state"_L1;
213 }
215 appendOrganizationAndApp(xdgStateHome);
216 return xdgStateHome;
217 }
218 case AppDataLocation:
221 {
222 QString xdgDataHome;
223 if (isTestModeEnabled()) {
224 xdgDataHome = QDir::homePath() + "/.qttest/share"_L1;
225 } else {
226 xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
227 if (!xdgDataHome.startsWith(u'/'))
228 xdgDataHome.clear(); // spec says relative paths should be ignored
229
230 if (xdgDataHome.isEmpty())
231 xdgDataHome = QDir::homePath() + "/.local/share"_L1;
232 }
234 appendOrganizationAndApp(xdgDataHome);
235 return xdgDataHome;
236 }
237 case ConfigLocation:
240 {
241 QString xdgConfigHome;
242 if (isTestModeEnabled()) {
243 xdgConfigHome = QDir::homePath() + "/.qttest/config"_L1;
244 } else {
245 // http://standards.freedesktop.org/basedir-spec/latest/
246 xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
247 if (!xdgConfigHome.startsWith(u'/'))
248 xdgConfigHome.clear(); // spec says relative paths should be ignored
249
250 if (xdgConfigHome.isEmpty())
251 xdgConfigHome = QDir::homePath() + "/.config"_L1;
252 }
253 if (type == AppConfigLocation)
254 appendOrganizationAndApp(xdgConfigHome);
255 return xdgConfigHome;
256 }
257 case RuntimeLocation:
258 {
259 QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
260 if (!xdgRuntimeDir.startsWith(u'/'))
261 xdgRuntimeDir.clear(); // spec says relative paths should be ignored
262
263 bool fromEnv = !xdgRuntimeDir.isEmpty();
264 if (xdgRuntimeDir.isEmpty() || !checkXdgRuntimeDir(xdgRuntimeDir)) {
265 // environment variable not set or is set to something unsuitable
266 const uint myUid = uint(geteuid());
267 const QString userName = QFileSystemEngine::resolveUserName(myUid);
268 xdgRuntimeDir = QDir::tempPath() + "/runtime-"_L1 + userName;
269
270 if (!fromEnv) {
271#ifndef Q_OS_WASM
272 qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%ls'", qUtf16Printable(xdgRuntimeDir));
273#endif
274 }
275
276 if (!checkXdgRuntimeDir(xdgRuntimeDir))
277 xdgRuntimeDir.clear();
278 }
279
280 return xdgRuntimeDir;
281 }
282 default:
283 break;
284 }
285
286#if QT_CONFIG(regularexpression)
287 // http://www.freedesktop.org/wiki/Software/xdg-user-dirs
288 QString xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
289 if (!xdgConfigHome.startsWith(u'/'))
290 xdgConfigHome.clear(); // spec says relative paths should be ignored
291
292 if (xdgConfigHome.isEmpty())
293 xdgConfigHome = QDir::homePath() + "/.config"_L1;
294 QFile file(xdgConfigHome + "/user-dirs.dirs"_L1);
295 const QLatin1StringView key = xdg_key_name(type);
296 if (!key.isEmpty() && !isTestModeEnabled() && file.open(QIODevice::ReadOnly)) {
298 // Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
299 static const QRegularExpression exp(u"^XDG_(.*)_DIR=(.*)$"_s);
301 while (!stream.atEnd()) {
302 const QString &line = stream.readLine();
304 if (match.hasMatch() && match.capturedView(1) == key) {
305 QStringView value = match.capturedView(2);
306 if (value.size() > 2
307 && value.startsWith(u'\"')
308 && value.endsWith(u'\"'))
309 value = value.mid(1, value.size() - 2);
310 // value can start with $HOME
311 if (value.startsWith("$HOME"_L1))
312 result = QDir::homePath() + value.mid(5);
313 else
314 result = value.toString();
315 if (result.size() > 1 && result.endsWith(u'/'))
316 result.chop(1);
317 }
318 }
319 if (!result.isNull())
320 return result;
321 }
322#endif // QT_CONFIG(regularexpression)
323
325 switch (type) {
326 case DesktopLocation:
327 path = QDir::homePath() + "/Desktop"_L1;
328 break;
330 path = QDir::homePath() + "/Documents"_L1;
331 break;
332 case PicturesLocation:
333 path = QDir::homePath() + "/Pictures"_L1;
334 break;
335
336 case FontsLocation:
338 break;
339
340 case MusicLocation:
341 path = QDir::homePath() + "/Music"_L1;
342 break;
343
344 case MoviesLocation:
345 path = QDir::homePath() + "/Videos"_L1;
346 break;
347 case DownloadLocation:
348 path = QDir::homePath() + "/Downloads"_L1;
349 break;
351 path = writableLocation(GenericDataLocation) + "/applications"_L1;
352 break;
353
355 path = QDir::homePath() + "/Public"_L1;
356 break;
357
359 path = QDir::homePath() + "/Templates"_L1;
360 break;
361
362 default:
363 break;
364 }
365
366 return path;
367}
368
369static QStringList dirsList(const QString &xdgEnvVar)
370{
371 QStringList dirs;
372 // http://standards.freedesktop.org/basedir-spec/latest/
373 // Normalize paths, skip relative paths (the spec says relative paths
374 // should be ignored)
375 for (const auto dir : qTokenize(xdgEnvVar, u':'))
376 if (dir.startsWith(u'/'))
377 dirs.push_back(QDir::cleanPath(dir.toString()));
378
379 // Remove duplicates from the list, there's no use for duplicated paths
380 // in XDG_* env vars - if whatever is being looked for is not found in
381 // the given directory the first time, it won't be there the second time.
382 // Plus duplicate paths causes problems for example for mimetypes,
383 // where duplicate paths here lead to duplicated mime types returned
384 // for a file, eg "text/plain,text/plain" instead of "text/plain"
385 dirs.removeDuplicates();
386
387 return dirs;
388}
389
391{
392 // http://standards.freedesktop.org/basedir-spec/latest/
393 QString xdgDataDirsEnv = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
394
395 QStringList dirs = dirsList(xdgDataDirsEnv);
396 if (dirs.isEmpty())
397 dirs = QStringList{u"/usr/local/share"_s, u"/usr/share"_s};
398
399 return dirs;
400}
401
403{
404 // http://standards.freedesktop.org/basedir-spec/latest/
405 const QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS"));
406
408 if (dirs.isEmpty())
409 dirs.push_back(u"/etc/xdg"_s);
410
411 return dirs;
412}
413
415{
416 QStringList dirs;
417 switch (type) {
418 case ConfigLocation:
420 dirs = xdgConfigDirs();
421 break;
423 dirs = xdgConfigDirs();
424 for (int i = 0; i < dirs.size(); ++i)
426 break;
428 dirs = xdgDataDirs();
429 break;
431 dirs = xdgDataDirs();
432 for (int i = 0; i < dirs.size(); ++i)
433 dirs[i].append("/applications"_L1);
434 break;
435 case AppDataLocation:
437 dirs = xdgDataDirs();
438 for (int i = 0; i < dirs.size(); ++i)
440 break;
441 case FontsLocation:
442 dirs += QDir::homePath() + "/.fonts"_L1;
443 dirs += xdgDataDirs();
444 for (int i = 1; i < dirs.size(); ++i)
445 dirs[i].append("/fonts"_L1);
446 break;
447 default:
448 break;
449 }
450 const QString localDir = writableLocation(type);
451 dirs.prepend(localDir);
452 return dirs;
453}
454
456
457#endif // QT_NO_STANDARDPATHS
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QString organizationName
the name of the organization that wrote this application
QString applicationName
the name of this application
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2133
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
static QString homePath()
Returns the absolute path of the user's home directory.
Definition qdir.cpp:2103
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, QFileSystemMetaData::MetaDataFlags what)
static QString resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
QFile::Permissions permissions() const
uint userId() const
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
static bool isTestModeEnabled()
static QStringList standardLocations(StandardLocation type)
static QString writableLocation(StandardLocation type)
StandardLocation
This enum describes the different locations that can be queried using methods such as QStandardPaths:...
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
\inmodule QtCore
list append(new Employee("Blackpool", "Stephen"))
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
GLuint64 key
GLenum type
GLuint entry
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
static void appendOrganizationAndApp(QString &path)
static QByteArray unixPermissionsText(QFile::Permissions permissions)
static QStringList dirsList(const QString &xdgEnvVar)
static bool checkXdgRuntimeDir(const QString &xdgRuntimeDir)
static QStringList xdgConfigDirs()
static void appendOrganizationAndApp(QString &path)
static QStringList xdgDataDirs()
#define qUtf16Printable(string)
Definition qstring.h:1543
constexpr auto qTokenize(Haystack &&h, Needle &&n, Flags...flags) noexcept(QtPrivate::Tok::is_nothrow_constructible_from< Haystack, Needle >::value) -> decltype(QtPrivate::Tok::TokenizerResult< Haystack, Needle >{std::forward< Haystack >(h), std::forward< Needle >(n), flags...})
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
unsigned int uint
Definition qtypes.h:34
QFile file
[0]
QString dir
[11]