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