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

source code of qtbase/src/corelib/mimetypes/qmimedatabase.cpp