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

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