Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qmimedatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include <qplatformdefs.h> // always first
7
10
12#include "qmimetype_p.h"
13
14#include <private/qduplicatetracker_p.h>
15#include <private/qfilesystementry_p.h>
16
17#include <QtCore/QFile>
18#include <QtCore/QFileInfo>
19#include <QtCore/QStandardPaths>
20#include <QtCore/QBuffer>
21#include <QtCore/QUrl>
22#include <QtCore/QDebug>
23
24#include <algorithm>
25#include <functional>
26#include <stack>
27
28QT_BEGIN_NAMESPACE
29
30using namespace Qt::StringLiterals;
31
33{
34 return QStringLiteral("inode/directory");
35}
37{
38 return QStringLiteral("text/plain");
39}
40
41Q_GLOBAL_STATIC(QMimeDatabasePrivate, staticQMimeDatabase)
42
44{
45 return staticQMimeDatabase();
46}
47
48QMimeDatabasePrivate::QMimeDatabasePrivate()
49 : m_defaultMimeType(QStringLiteral("application/octet-stream"))
50{
51}
52
56
58#ifdef QT_BUILD_INTERNAL
60#else
61static const
62#endif
64
65bool QMimeDatabasePrivate::shouldCheck()
66{
67 if (m_lastCheck.isValid() && m_lastCheck.elapsed() < qmime_secondsBetweenChecks * 1000)
68 return false;
69 m_lastCheck.start();
70 return true;
71}
72
74{
75 QStringList dirs =
76 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("mime"),
77 QStandardPaths::LocateDirectory);
78 dirs.append(u":/qt-project.org/qmime"_s);
79 return dirs;
80}
81
82#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
83# define QT_USE_MMAP
84#endif
85
86void QMimeDatabasePrivate::loadProviders()
87{
88 // We use QStandardPaths every time to check if new files appeared
89 const QStringList mimeDirs = locateMimeDirectories();
90 const auto fdoIterator = std::find_if(mimeDirs.constBegin(), mimeDirs.constEnd(), [](const QString &mimeDir) -> bool {
91 return QFileInfo::exists(mimeDir + "/packages/freedesktop.org.xml"_L1); }
92 );
93 const bool needInternalDB = QMimeXMLProvider::InternalDatabaseAvailable && fdoIterator == mimeDirs.constEnd();
94 //qDebug() << "mime dirs:" << mimeDirs;
95
96 Providers currentProviders;
97 std::swap(m_providers, currentProviders);
98
99 m_providers.reserve(mimeDirs.size() + (needInternalDB ? 1 : 0));
100
101 for (const QString &mimeDir : mimeDirs) {
102 const QString cacheFile = mimeDir + "/mime.cache"_L1;
103 // Check if we already have a provider for this dir
104 const auto predicate = [mimeDir](const std::unique_ptr<QMimeProviderBase> &prov)
105 {
106 return prov && prov->directory() == mimeDir;
107 };
108 const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), predicate);
109 if (it == currentProviders.end()) {
110 std::unique_ptr<QMimeProviderBase> provider;
111#if defined(QT_USE_MMAP)
112 if (qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE") && QFileInfo::exists(cacheFile)) {
113 provider.reset(new QMimeBinaryProvider(this, mimeDir));
114 //qDebug() << "Created binary provider for" << mimeDir;
115 if (!provider->isValid()) {
116 provider.reset();
117 }
118 }
119#endif
120 if (!provider) {
121 provider.reset(new QMimeXMLProvider(this, mimeDir));
122 //qDebug() << "Created XML provider for" << mimeDir;
123 }
124 m_providers.push_back(std::move(provider));
125 } else {
126 auto provider = std::move(*it); // take provider out of the vector
127 provider->ensureLoaded();
128 if (!provider->isValid()) {
129 provider.reset(new QMimeXMLProvider(this, mimeDir));
130 //qDebug() << "Created XML provider to replace binary provider for" << mimeDir;
131 }
132 m_providers.push_back(std::move(provider));
133 }
134 }
135 // mimeDirs is sorted "most local first, most global last"
136 // so the internal XML DB goes at the end
137 if (needInternalDB) {
138 // Check if we already have a provider for the InternalDatabase
139 const auto isInternal = [](const std::unique_ptr<QMimeProviderBase> &prov)
140 {
141 return prov && prov->isInternalDatabase();
142 };
143 const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), isInternal);
144 if (it == currentProviders.end()) {
145 m_providers.push_back(Providers::value_type(new QMimeXMLProvider(this, QMimeXMLProvider::InternalDatabase)));
146 } else {
147 m_providers.push_back(std::move(*it));
148 }
149 }
150
151 auto it = m_providers.begin();
152 (*it)->setOverrideProvider(nullptr);
153 ++it;
154 const auto end = m_providers.end();
155 for (; it != end; ++it)
156 (*it)->setOverrideProvider((it - 1)->get());
157}
158
159const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
160{
161#if QT_CONFIG(thread) // stub implementation always returns true
162 Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex
163#endif
164 if (m_providers.empty()) {
165 loadProviders();
166 m_lastCheck.start();
167 } else {
168 if (shouldCheck())
169 loadProviders();
170 }
171 return m_providers;
172}
173
174QString QMimeDatabasePrivate::resolveAlias(const QString &nameOrAlias)
175{
176 for (const auto &provider : providers()) {
177 const QString ret = provider->resolveAlias(nameOrAlias);
178 if (!ret.isEmpty())
179 return ret;
180 }
181 return nameOrAlias;
182}
183
184/*!
185 \internal
186 Returns a MIME type or an invalid one if none found
187 */
188QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias)
189{
190 const QString mimeName = resolveAlias(nameOrAlias);
191 for (const auto &provider : providers()) {
192 if (provider->knowsMimeType(mimeName))
193 return QMimeType(QMimeTypePrivate(mimeName));
194 }
195 return {};
196}
197
199{
200 if (fileName.endsWith(u'/'))
201 return { directoryMimeType() };
202
203 const QMimeGlobMatchResult result = findByFileName(fileName);
204 QStringList matchingMimeTypes = result.m_matchingMimeTypes;
205 matchingMimeTypes.sort(); // make it deterministic
206 return matchingMimeTypes;
207}
208
209QMimeGlobMatchResult QMimeDatabasePrivate::findByFileName(const QString &fileName)
210{
211 QMimeGlobMatchResult result;
212 const QString fileNameExcludingPath = QFileSystemEntry(fileName).fileName();
213 for (const auto &provider : providers())
214 provider->addFileNameMatches(fileNameExcludingPath, result);
215 return result;
216}
217
219{
220 QMutexLocker locker(&mutex);
221 for (const auto &provider : providers()) {
222 auto comments = provider->localeComments(name);
223 if (!comments.isEmpty())
224 return comments; // maybe we want to merge in comments from more global providers, in
225 // case of more translations?
226 }
227 return {};
228}
229
231{
232 QMutexLocker locker(&mutex);
233 QStringList patterns;
234 const auto &providerList = providers();
235 // reverse iteration because we start from most global, add up, clear if delete-all, and add up
236 // again.
237 for (auto rit = providerList.rbegin(); rit != providerList.rend(); ++rit) {
238 auto *provider = rit->get();
239 if (provider->hasGlobDeleteAll(name))
240 patterns.clear();
241 patterns += provider->globPatterns(name);
242 }
243 return patterns;
244}
245
247{
248 QMutexLocker locker(&mutex);
249 for (const auto &provider : providers()) {
250 QString genericIconName = provider->genericIcon(name);
251 if (!genericIconName.isEmpty())
252 return genericIconName;
253 }
254 return {};
255}
256
257QString QMimeDatabasePrivate::icon(const QString &name)
258{
259 QMutexLocker locker(&mutex);
260 for (const auto &provider : providers()) {
261 QString iconName = provider->icon(name);
262 if (!iconName.isEmpty())
263 return iconName;
264 }
265 return {};
266}
267
268QString QMimeDatabasePrivate::fallbackParent(const QString &mimeTypeName) const
269{
270 const QStringView myGroup = QStringView{mimeTypeName}.left(mimeTypeName.indexOf(u'/'));
271 // All real-file mimetypes implicitly derive from application/octet-stream
272 if (myGroup != "inode"_L1 &&
273 // ignore non-file extensions
274 myGroup != "all"_L1 && myGroup != "fonts"_L1 && myGroup != "print"_L1 && myGroup != "uri"_L1
275 && mimeTypeName != defaultMimeType()) {
276 return defaultMimeType();
277 }
278 return QString();
279}
280
282{
283 QMutexLocker locker(&mutex);
284 return parents(mimeName);
285}
286
287QStringList QMimeDatabasePrivate::parents(const QString &mimeName)
288{
289#if QT_CONFIG(thread) // stub implementation always returns true
290 Q_ASSERT(!mutex.tryLock());
291#endif
292 QStringList result;
293 for (const auto &provider : providers())
294 provider->addParents(mimeName, result);
295
296 // Implicit rule from the spec: all text/* types are subclasses of text/plain. It holds even
297 // for types that declare other parents, and shared-mime-info >= 2.5 relies on that rather
298 // than listing text/plain (e.g. text/x-shellscript only declares application/x-executable).
299 if (mimeName.startsWith("text/"_L1) && mimeName != plainTextMimeType()
300 && !result.contains(plainTextMimeType())) {
301 result.append(plainTextMimeType());
302 }
303
304 if (result.isEmpty()) {
305 const QString parent = fallbackParent(mimeName);
306 if (!parent.isEmpty())
307 result.append(parent);
308 }
309 return result;
310}
311
313{
314 QMutexLocker locker(&mutex);
315 QStringList result;
316 for (const auto &provider : providers())
317 provider->addAliases(mimeName, result);
318 return result;
319}
320
321bool QMimeDatabasePrivate::mimeInherits(const QString &mime, const QString &parent)
322{
323 QMutexLocker locker(&mutex);
324 return inherits(mime, parent);
325}
326
327static inline bool isTextFile(const QByteArray &data)
328{
329 // UTF16 byte order marks
330 static const char bigEndianBOM[] = "\xFE\xFF";
331 static const char littleEndianBOM[] = "\xFF\xFE";
332 if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM))
333 return true;
334
335 // Check the first 128 bytes (see shared-mime spec)
336 const char *p = data.constData();
337 const char *e = p + qMin(128, data.size());
338 for ( ; p < e; ++p) {
339 if (static_cast<unsigned char>(*p) < 32 && *p != 9 && *p !=10 && *p != 13)
340 return false;
341 }
342
343 return true;
344}
345
346QMimeType QMimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPtr)
347{
348 if (data.isEmpty()) {
349 *accuracyPtr = 100;
350 return mimeTypeForName(QStringLiteral("application/x-zerosize"));
351 }
352
353 QMimeMagicResult result;
354 for (const auto &provider : providers())
355 provider->findByMagic(data, result);
356
357 if (result.isValid()) {
358 *accuracyPtr = result.accuracy;
359 return QMimeType(QMimeTypePrivate(result.candidate));
360 }
361
362 if (isTextFile(data)) {
363 *accuracyPtr = 5;
364 return mimeTypeForName(plainTextMimeType());
365 }
366
367 return mimeTypeForName(defaultMimeType());
368}
369
370// Whether the mimetype has magic rules, none of which match the data
371bool QMimeDatabasePrivate::magicRulesReject(const QString &mime, const QByteArray &data)
372{
373 bool hasRules = false;
374 for (const auto &provider : providers()) {
375 const QMimeMagicCheckResult result = provider->checkMagicRules(mime, data);
376 if (result.matched)
377 return false;
378 hasRules = hasRules || result.hasRules;
379 }
380 return hasRules;
381}
382
383QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device)
384{
385 // First, glob patterns are evaluated. If there is a match with max weight,
386 // this one is selected and we are done. Otherwise, the file contents are
387 // evaluated and the match with the highest value (either a magic priority or
388 // a glob pattern weight) is selected. Matching starts from max level (most
389 // specific) in both cases, even when there is already a suffix matching candidate.
390
391 // Pass 1) Try to match on the file name
392 QMimeGlobMatchResult candidatesByName = findByFileName(fileName);
393 if (candidatesByName.m_allMatchingMimeTypes.size() == 1) {
394 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
395 if (mime.isValid())
396 return mime;
397 candidatesByName = {};
398 }
399
400 // Extension is unknown, or matches multiple mimetypes.
401 // Pass 2) Match on content, if we can read the data
402 const auto matchOnContent = [this, &candidatesByName](QIODevice *device) {
403 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
404 if (device->isOpen()) {
405 // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
406 // This is much faster than seeking back and forth into QIODevice.
407 const QByteArray data = device->peek(16384);
408
409 if (openedByUs)
410 device->close();
411
412 int magicAccuracy = 0;
413 QMimeType candidateByData(findByData(data, &magicAccuracy));
414
415 // Disambiguate conflicting extensions (if magic matching found something)
416 if (candidateByData.isValid() && magicAccuracy > 0) {
417 const QString sniffedMime = candidateByData.name();
418 // If the sniffedMime matches a highest-weight glob match, use it
419 if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime))
420 return candidateByData;
421
422 for (const QString &m : std::as_const(candidatesByName.m_allMatchingMimeTypes)) {
423 if (inherits(m, sniffedMime)) {
424 // We have magic + pattern pointing to this, so it's a pretty good match
425 return mimeTypeForName(m);
426 }
427 }
428 if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) {
429 // No glob, use magic
430 return candidateByData;
431 }
432 }
433
434 // Several globs match and magic didn't settle it. Drop the candidates
435 // whose own magic rules fail on this data, e.g. text/x-matlab for a *.m
436 // file that doesn't look like matlab. Unless that leaves nothing.
437 QStringList &candidates = candidatesByName.m_matchingMimeTypes;
438 if (candidates.size() > 1) {
439 QStringList remaining;
440 for (const QString &m : std::as_const(candidates)) {
441 if (!magicRulesReject(m, data))
442 remaining.append(m);
443 }
444 if (!remaining.isEmpty())
445 candidates = std::move(remaining);
446 }
447 }
448
449 if (candidatesByName.m_allMatchingMimeTypes.size() > 1) {
450 candidatesByName.m_matchingMimeTypes.sort(); // make it deterministic
451 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
452 if (mime.isValid())
453 return mime;
454 }
455
456 return mimeTypeForName(defaultMimeType());
457 };
458
459 if (device)
460 return matchOnContent(device);
461
462 QFile fallbackFile(fileName);
463 return matchOnContent(&fallbackFile);
464}
465
466QMimeType QMimeDatabasePrivate::mimeTypeForFileExtension(const QString &fileName)
467{
468 const QStringList matches = mimeTypeForFileName(fileName);
469 if (matches.isEmpty()) {
470 return mimeTypeForName(defaultMimeType());
471 } else {
472 // We have to pick one in case of multiple matches.
473 return mimeTypeForName(matches.first());
474 }
475}
476
477QMimeType QMimeDatabasePrivate::mimeTypeForData(QIODevice *device)
478{
479 int accuracy = 0;
480 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
481 if (device->isOpen()) {
482 // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
483 // This is much faster than seeking back and forth into QIODevice.
484 const QByteArray data = device->peek(16384);
485 QMimeType result = findByData(data, &accuracy);
486 if (openedByUs)
487 device->close();
488 return result;
489 }
490 return mimeTypeForName(defaultMimeType());
491}
492
493QMimeType QMimeDatabasePrivate::mimeTypeForFile(const QString &fileName,
494 const QFileInfo &fileInfo,
495 QMimeDatabase::MatchMode mode)
496{
497 if (false) {
498#ifdef Q_OS_UNIX
499 } else if (fileInfo.isNativePath()) {
500 // If this is a local file, we'll want to do a stat() ourselves so we can
501 // detect additional inode types. In addition we want to follow symlinks.
502 const QByteArray nativeFilePath = QFile::encodeName(fileName);
503 QT_STATBUF statBuffer;
504 if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) {
505 if (S_ISDIR(statBuffer.st_mode))
506 return mimeTypeForName(directoryMimeType());
507 if (S_ISCHR(statBuffer.st_mode))
508 return mimeTypeForName(QStringLiteral("inode/chardevice"));
509 if (S_ISBLK(statBuffer.st_mode))
510 return mimeTypeForName(QStringLiteral("inode/blockdevice"));
511 if (S_ISFIFO(statBuffer.st_mode))
512 return mimeTypeForName(QStringLiteral("inode/fifo"));
513 if (S_ISSOCK(statBuffer.st_mode))
514 return mimeTypeForName(QStringLiteral("inode/socket"));
515 }
516#endif
517 } else if (fileInfo.isDir()) {
518 return mimeTypeForName(directoryMimeType());
519 }
520
521 switch (mode) {
522 case QMimeDatabase::MatchDefault:
523 break;
524 case QMimeDatabase::MatchExtension:
525 return mimeTypeForFileExtension(fileName);
526 case QMimeDatabase::MatchContent: {
527 QFile file(fileName);
528 return mimeTypeForData(&file);
529 }
530 }
531 // MatchDefault:
532 return mimeTypeForFileNameAndData(fileName, nullptr);
533}
534
536{
537 QList<QMimeType> result;
538 for (const auto &provider : providers())
539 provider->addAllMimeTypes(result);
540 return result;
541}
542
543bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent)
544{
545 const QString resolvedParent = resolveAlias(parent);
546 QDuplicateTracker<QString> seen;
547 std::stack<QString, QStringList> toCheck;
548 toCheck.push(mime);
549 while (!toCheck.empty()) {
550 if (toCheck.top() == resolvedParent)
551 return true;
552 const QString mimeName = toCheck.top();
553 toCheck.pop();
554 const auto parentList = parents(mimeName);
555 for (const QString &par : parentList) {
556 const QString resolvedPar = resolveAlias(par);
557 if (!seen.hasSeen(resolvedPar))
558 toCheck.push(resolvedPar);
559 }
560 }
561 return false;
562}
563
564/*!
565 \class QMimeDatabase
566 \inmodule QtCore
567 \brief The QMimeDatabase class maintains a database of MIME types.
568
569 \since 5.0
570
571 The MIME type database is provided by the freedesktop.org shared-mime-info
572 project. If the MIME type database cannot be found on the system, as is the case
573 on most Windows, \macos, and iOS systems, Qt will use its own copy of it.
574
575 Applications which want to define custom MIME types need to install an
576 XML file into the locations searched for MIME definitions.
577 These locations can be queried with
578 \snippet code/src_corelib_mimetype_qmimedatabase.cpp 1
579 On a typical Unix system, this will be /usr/share/mime/packages/, but it is also
580 possible to extend the list of directories by setting the environment variable
581 \c XDG_DATA_DIRS. For instance adding /opt/myapp/share to \c XDG_DATA_DIRS will result
582 in /opt/myapp/share/mime/packages/ being searched for MIME definitions.
583
584 Here is an example of MIME XML:
585 \snippet code/src_corelib_mimetype_qmimedatabase.cpp 2
586
587 For more details about the syntax of XML MIME definitions, including defining
588 "magic" in order to detect MIME types based on data as well, read the
589 Shared Mime Info specification at
590 http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
591
592 On Unix systems, a binary cache is used for more performance. This cache is generated
593 by the command "update-mime-database path", where path would be /opt/myapp/share/mime
594 in the above example. Make sure to run this command when installing the MIME type
595 definition file.
596
597 \threadsafe
598
599 \snippet code/src_corelib_mimetype_qmimedatabase.cpp 0
600
601 \sa QMimeType, {MIME Type Browser}
602
603 \section1 Security Considerations
604
605 \section2 Blocking calls
606 All QMimeDatabase objects share the same database, protected by a single
607 process-wide mutex. Every lookup holds this mutex while performing disk
608 I/O. For example, when loading or checking MIME definitions.
609
610 As a result, one slow or unresponsive read can block every thread using
611 QMimeDatabase. This is especially problematic when files or MIME
612 definition directories are on slow storage, such as a network mount.
613
614 To keep the UI responsive, perform the lookups in worker threads and update
615 the UI from a continuation. This example handles a list of files:
616
617 \code
618 // Called from the main thread
619 const QStringList paths = { "/mnt/share/report.pdf", "/mnt/share/data.csv",
620 "/mnt/share/photo.jpg", "/mnt/share/notes.txt" };
621 QtConcurrent::mapped(paths, [](const QString &path) {
622 // Runs in a worker thread, once per file
623 return QMimeDatabase().mimeTypeForFile(path).name();
624 }).then(this, [this, paths](QFuture<QString> future) {
625 // Runs in the main thread, once every lookup has finished
626 const QStringList names = future.results(); // same order as paths
627 for (qsizetype i = 0; i < paths.size(); ++i)
628 updateUi(paths.at(i), names.at(i));
629 });
630 \endcode
631
632 The code example requires the Qt Concurrent module. \c QtConcurrent::mapped()
633 runs the lambda once per file. Each invocation of the lambda runs on a thread
634 from the \c QThreadPool pool. The function returns a single \c QFuture
635 containing the lambda's return value for each file, in the same order as
636 the input list. \l {QFuture::then()}{then()} attaches a continuation to
637 that QFuture, allowing to chain multiple asynchronous computations.
638 Once all asynchronous computations have finished, the continuation runs in
639 the thread of the context object, here the main thread. The continuation takes
640 the QFuture itself and reads all results with \l {QFuture::results()}{results()}.
641
642 \note This pattern moves the blocking calls to worker threads, it does not
643 remove them. The lookups still run one at a time, because they share the mutex.
644 While a worker holds the mutex, every other QMimeDatabase call waits.
645 */
646
647/*!
648 \fn QMimeDatabase::QMimeDatabase();
649 Constructs a QMimeDatabase object.
650
651 It is perfectly OK to create an instance of QMimeDatabase every time you need to
652 perform a lookup.
653 The parsing of mimetypes is done on demand (when shared-mime-info is installed)
654 or when the very first instance is constructed (when parsing XML files directly).
655 */
656QMimeDatabase::QMimeDatabase() :
657 d(staticQMimeDatabase())
658{
659}
660
661/*!
662 \fn QMimeDatabase::~QMimeDatabase();
663 Destroys the QMimeDatabase object.
664 */
665QMimeDatabase::~QMimeDatabase()
666{
667 d = nullptr;
668}
669
670/*!
671 \fn QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const;
672 Returns a MIME type for \a nameOrAlias or an invalid one if none found.
673 */
674QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const
675{
676 QMutexLocker locker(&d->mutex);
677
678 return d->mimeTypeForName(nameOrAlias);
679}
680
681/*!
682 Returns a MIME type for \a fileInfo.
683
684 A valid MIME type is always returned.
685
686 The default matching algorithm looks at both the file name and the file
687 contents, if necessary. The file extension has priority over the contents,
688 but the contents will be used if the file extension is unknown, or
689 matches multiple MIME types.
690 If \a fileInfo is a Unix symbolic link, the file that it refers to
691 will be used instead.
692 If the file doesn't match any known pattern or data, the default MIME type
693 (application/octet-stream) is returned.
694
695 When \a mode is set to MatchExtension, only the file name is used, not
696 the file contents. The file doesn't even have to exist. If the file name
697 doesn't match any known pattern, the default MIME type (application/octet-stream)
698 is returned.
699 If multiple MIME types match this file, the first one (alphabetically) is returned.
700
701 When \a mode is set to MatchContent, and the file is readable, only the
702 file contents are used to determine the MIME type. This is equivalent to
703 calling mimeTypeForData with a QFile as input device.
704
705 \a fileInfo may refer to an absolute or relative path.
706
707 \sa QMimeType::isDefault(), mimeTypeForData()
708*/
709QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
710{
711 QMutexLocker locker(&d->mutex);
712
713 return d->mimeTypeForFile(fileInfo.filePath(), fileInfo, mode);
714}
715
716/*!
717 Returns a MIME type for the file named \a fileName using \a mode.
718
719 \overload
720*/
721QMimeType QMimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const
722{
723 QMutexLocker locker(&d->mutex);
724
725 if (mode == MatchExtension) {
726 return d->mimeTypeForFileExtension(fileName);
727 } else {
728 QFileInfo fileInfo(fileName);
729 return d->mimeTypeForFile(fileName, fileInfo, mode);
730 }
731}
732
733/*!
734 Returns the MIME types for the file name \a fileName.
735
736 If the file name doesn't match any known pattern, an empty list is returned.
737 If multiple MIME types match this file, they are all returned.
738
739 This function does not try to open the file. To also use the content
740 when determining the MIME type, use mimeTypeForFile() or
741 mimeTypeForFileNameAndData() instead.
742
743 \sa mimeTypeForFile()
744*/
745QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) const
746{
747 QMutexLocker locker(&d->mutex);
748
749 const QStringList matches = d->mimeTypeForFileName(fileName);
750 QList<QMimeType> mimes;
751 mimes.reserve(matches.size());
752 for (const QString &mime : matches)
753 mimes.append(d->mimeTypeForName(mime));
754 return mimes;
755}
756/*!
757 Returns the suffix for the file \a fileName, as known by the MIME database.
758
759 This allows to pre-select "tar.bz2" for foo.tar.bz2, but still only
760 "txt" for my.file.with.dots.txt.
761*/
762QString QMimeDatabase::suffixForFileName(const QString &fileName) const
763{
764 QMutexLocker locker(&d->mutex);
765 const qsizetype suffixLength = d->findByFileName(fileName).m_knownSuffixLength;
766 return fileName.right(suffixLength);
767}
768
769/*!
770 Returns a MIME type for \a data.
771
772 A valid MIME type is always returned. If \a data doesn't match any
773 known MIME type data, the default MIME type (application/octet-stream)
774 is returned.
775*/
776QMimeType QMimeDatabase::mimeTypeForData(const QByteArray &data) const
777{
778 QMutexLocker locker(&d->mutex);
779
780 int accuracy = 0;
781 return d->findByData(data, &accuracy);
782}
783
784/*!
785 Returns a MIME type for the data in \a device.
786
787 A valid MIME type is always returned. If the data in \a device doesn't match any
788 known MIME type data, the default MIME type (application/octet-stream)
789 is returned.
790*/
791QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device) const
792{
793 QMutexLocker locker(&d->mutex);
794
795 return d->mimeTypeForData(device);
796}
797
798/*!
799 Returns a MIME type for \a url.
800
801 If the URL is a local file, this calls mimeTypeForFile.
802
803 Otherwise the matching is done based on the file name only,
804 except for schemes where file names don't mean much, like HTTP.
805 This method always returns the default mimetype for HTTP URLs,
806 use QNetworkAccessManager to handle HTTP URLs properly.
807
808 A valid MIME type is always returned. If \a url doesn't match any
809 known MIME type data, the default MIME type (application/octet-stream)
810 is returned.
811*/
812QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const
813{
814 if (url.isLocalFile())
815 return mimeTypeForFile(url.toLocalFile());
816
817 const QString scheme = url.scheme();
818 if (scheme.startsWith("http"_L1) || scheme == "mailto"_L1)
819 return mimeTypeForName(d->defaultMimeType());
820
821 return mimeTypeForFile(url.path(), MatchExtension);
822}
823
824/*!
825 Returns a MIME type for the given \a fileName and \a device data.
826
827 This overload can be useful when the file is remote, and we started to
828 download some of its data in a device. This allows to do full MIME type
829 matching for remote files as well.
830
831 If the device is not open, it will be opened by this function, and closed
832 after the MIME type detection is completed.
833
834 A valid MIME type is always returned. If \a device data doesn't match any
835 known MIME type data, the default MIME type (application/octet-stream)
836 is returned.
837
838 This method looks at both the file name and the file contents,
839 if necessary. The file extension has priority over the contents,
840 but the contents will be used if the file extension is unknown, or
841 matches multiple MIME types.
842*/
843QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const
844{
845 QMutexLocker locker(&d->mutex);
846
847 if (fileName.endsWith(u'/'))
848 return d->mimeTypeForName(directoryMimeType());
849
850 const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device);
851 return result;
852}
853
854/*!
855 Returns a MIME type for the given \a fileName and device \a data.
856
857 This overload can be useful when the file is remote, and we started to
858 download some of its data. This allows to do full MIME type matching for
859 remote files as well.
860
861 A valid MIME type is always returned. If \a data doesn't match any
862 known MIME type data, the default MIME type (application/octet-stream)
863 is returned.
864
865 This method looks at both the file name and the file contents,
866 if necessary. The file extension has priority over the contents,
867 but the contents will be used if the file extension is unknown, or
868 matches multiple MIME types.
869*/
870QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const
871{
872 QMutexLocker locker(&d->mutex);
873
874 if (fileName.endsWith(u'/'))
875 return d->mimeTypeForName(directoryMimeType());
876
877 QBuffer buffer(const_cast<QByteArray *>(&data));
878 buffer.open(QIODevice::ReadOnly);
879 return d->mimeTypeForFileNameAndData(fileName, &buffer);
880}
881
882/*!
883 Returns the list of all available MIME types.
884
885 This can be useful for showing all MIME types to the user, for instance
886 in a MIME type editor. Do not use unless really necessary in other cases
887 though, prefer using the \l {mimeTypeForData()}{mimeTypeForXxx()} methods for performance reasons.
888*/
889QList<QMimeType> QMimeDatabase::allMimeTypes() const
890{
891 QMutexLocker locker(&d->mutex);
892
893 return d->allMimeTypes();
894}
895
896/*!
897 \enum QMimeDatabase::MatchMode
898
899 This enum specifies how matching a file to a MIME type is performed.
900
901 \value MatchDefault Both the file name and content are used to look for a match
902
903 \value MatchExtension Only the file name is used to look for a match
904
905 \value MatchContent The file content is used to look for a match
906*/
907
908QT_END_NAMESPACE
QString resolveAlias(const QString &nameOrAlias)
QStringList listAliases(const QString &mimeName)
QList< QMimeType > allMimeTypes()
QString genericIcon(const QString &name)
QMimeTypePrivate::LocaleHash localeComments(const QString &name)
bool mimeInherits(const QString &mime, const QString &parent)
QMimeType mimeTypeForFileExtension(const QString &fileName)
QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device)
QMimeType mimeTypeForData(QIODevice *device)
QStringList mimeTypeForFileName(const QString &fileName)
bool inherits(const QString &mime, const QString &parent)
QMimeType mimeTypeForName(const QString &nameOrAlias)
QStringList mimeParents(const QString &mimeName)
QStringList globPatterns(const QString &name)
QString icon(const QString &name)
static QMimeDatabasePrivate * instance()
QMimeType mimeTypeForFile(const QString &fileName, const QFileInfo &fileInfo, QMimeDatabase::MatchMode mode)
QStringList parents(const QString &mimeName)
QMimeGlobMatchResult findByFileName(const QString &fileName)
bool magicRulesReject(const QString &mime, const QByteArray &data)
QMimeType findByData(const QByteArray &data, int *priorityPtr)
static bool isTextFile(const QByteArray &data)
static Q_CONSTINIT const int qmime_secondsBetweenChecks
static QStringList locateMimeDirectories()
static QString directoryMimeType()
static QString plainTextMimeType()
bool isValid() const