6#include <qplatformdefs.h>
14#include <private/qduplicatetracker_p.h>
15#include <private/qfilesystementry_p.h>
17#include <QtCore/QFile>
18#include <QtCore/QFileInfo>
19#include <QtCore/QStandardPaths>
20#include <QtCore/QBuffer>
22#include <QtCore/QDebug>
30using namespace Qt::StringLiterals;
34 return QStringLiteral(
"inode/directory");
38 return QStringLiteral(
"text/plain");
45 return staticQMimeDatabase();
49 : m_defaultMimeType(QStringLiteral(
"application/octet-stream"))
58#ifdef QT_BUILD_INTERNAL
67 if (m_lastCheck.isValid() && m_lastCheck.elapsed() < qmime_secondsBetweenChecks * 1000)
76 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral(
"mime"),
77 QStandardPaths::LocateDirectory);
78 dirs.append(u":/qt-project.org/qmime"_s);
82#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
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); }
96 Providers currentProviders;
97 std::swap(m_providers, currentProviders);
99 m_providers.reserve(mimeDirs.size() + (needInternalDB ? 1 : 0));
101 for (
const QString &mimeDir : mimeDirs) {
102 const QString cacheFile = mimeDir +
"/mime.cache"_L1;
104 const auto predicate = [mimeDir](
const std::unique_ptr<QMimeProviderBase> &prov)
106 return prov && prov->directory() == mimeDir;
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));
115 if (!provider->isValid()) {
121 provider.reset(
new QMimeXMLProvider(
this, mimeDir));
124 m_providers.push_back(std::move(provider));
126 auto provider = std::move(*it);
127 provider->ensureLoaded();
128 if (!provider->isValid()) {
129 provider.reset(
new QMimeXMLProvider(
this, mimeDir));
132 m_providers.push_back(std::move(provider));
137 if (needInternalDB) {
139 const auto isInternal = [](
const std::unique_ptr<QMimeProviderBase> &prov)
141 return prov && prov->isInternalDatabase();
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)));
147 m_providers.push_back(std::move(*it));
151 auto it = m_providers.begin();
152 (*it)->setOverrideProvider(
nullptr);
154 const auto end = m_providers.end();
155 for (; it != end; ++it)
156 (*it)->setOverrideProvider((it - 1)->get());
162 Q_ASSERT(!mutex.tryLock());
164 if (m_providers.empty()) {
176 for (
const auto &provider : providers()) {
177 const QString ret = provider->resolveAlias(nameOrAlias);
185
186
187
190 const QString mimeName = resolveAlias(nameOrAlias);
191 for (
const auto &provider : providers()) {
192 if (provider->knowsMimeType(mimeName))
193 return QMimeType(QMimeTypePrivate(mimeName));
200 if (fileName.endsWith(u'/'))
201 return { directoryMimeType() };
203 const QMimeGlobMatchResult result = findByFileName(fileName);
204 QStringList matchingMimeTypes = result.m_matchingMimeTypes;
205 matchingMimeTypes.sort();
206 return matchingMimeTypes;
211 QMimeGlobMatchResult result;
212 const QString fileNameExcludingPath = QFileSystemEntry(fileName).fileName();
213 for (
const auto &provider : providers())
214 provider->addFileNameMatches(fileNameExcludingPath, result);
220 QMutexLocker locker(&mutex);
221 for (
const auto &provider : providers()) {
222 auto comments = provider->localeComments(name);
223 if (!comments.isEmpty())
232 QMutexLocker locker(&mutex);
233 QStringList patterns;
234 const auto &providerList = providers();
237 for (
auto rit = providerList.rbegin(); rit != providerList.rend(); ++rit) {
238 auto *provider = rit->get();
239 if (provider->hasGlobDeleteAll(name))
241 patterns += provider->globPatterns(name);
248 QMutexLocker locker(&mutex);
249 for (
const auto &provider : providers()) {
250 QString genericIconName = provider->genericIcon(name);
251 if (!genericIconName.isEmpty())
252 return genericIconName;
259 QMutexLocker locker(&mutex);
260 for (
const auto &provider : providers()) {
261 QString iconName = provider->icon(name);
262 if (!iconName.isEmpty())
270 const QStringView myGroup = QStringView{mimeTypeName}.left(mimeTypeName.indexOf(u'/'));
272 if (myGroup !=
"inode"_L1 &&
274 myGroup !=
"all"_L1 && myGroup !=
"fonts"_L1 && myGroup !=
"print"_L1 && myGroup !=
"uri"_L1
275 && mimeTypeName != defaultMimeType()) {
276 return defaultMimeType();
283 QMutexLocker locker(&mutex);
284 return parents(mimeName);
290 Q_ASSERT(!mutex.tryLock());
293 for (
const auto &provider : providers())
294 provider->addParents(mimeName, result);
299 if (mimeName.startsWith(
"text/"_L1) && mimeName != plainTextMimeType()
300 && !result.contains(plainTextMimeType())) {
301 result.append(plainTextMimeType());
304 if (result.isEmpty()) {
305 const QString parent = fallbackParent(mimeName);
306 if (!parent.isEmpty())
307 result.append(parent);
314 QMutexLocker locker(&mutex);
316 for (
const auto &provider : providers())
317 provider->addAliases(mimeName, result);
323 QMutexLocker locker(&mutex);
324 return inherits(mime, parent);
330 static const char bigEndianBOM[] =
"\xFE\xFF";
331 static const char littleEndianBOM[] =
"\xFF\xFE";
332 if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM))
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)
348 if (data.isEmpty()) {
350 return mimeTypeForName(QStringLiteral(
"application/x-zerosize"));
354 for (
const auto &provider : providers())
355 provider->findByMagic(data, result);
359 return QMimeType(QMimeTypePrivate(result.candidate));
364 return mimeTypeForName(plainTextMimeType());
367 return mimeTypeForName(defaultMimeType());
373 bool hasRules =
false;
374 for (
const auto &provider : providers()) {
375 const QMimeMagicCheckResult result = provider->checkMagicRules(mime, data);
378 hasRules = hasRules || result.hasRules;
392 QMimeGlobMatchResult candidatesByName = findByFileName(fileName);
393 if (candidatesByName.m_allMatchingMimeTypes.size() == 1) {
394 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
397 candidatesByName = {};
402 const auto matchOnContent = [
this, &candidatesByName](QIODevice *device) {
403 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
404 if (device->isOpen()) {
412 int magicAccuracy = 0;
413 QMimeType candidateByData(findByData(data, &magicAccuracy));
416 if (candidateByData.isValid() && magicAccuracy > 0) {
417 const QString sniffedMime = candidateByData.name();
419 if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime))
420 return candidateByData;
422 for (
const QString &m : std::as_const(candidatesByName.m_allMatchingMimeTypes)) {
423 if (inherits(m, sniffedMime)) {
425 return mimeTypeForName(m);
428 if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) {
430 return candidateByData;
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))
444 if (!remaining.isEmpty())
445 candidates =
std::move(remaining);
449 if (candidatesByName.m_allMatchingMimeTypes.size() > 1) {
450 candidatesByName.m_matchingMimeTypes.sort();
451 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
456 return mimeTypeForName(defaultMimeType());
460 return matchOnContent(device);
462 QFile fallbackFile(fileName);
463 return matchOnContent(&fallbackFile);
468 const QStringList matches = mimeTypeForFileName(fileName);
469 if (matches.isEmpty()) {
470 return mimeTypeForName(defaultMimeType());
473 return mimeTypeForName(matches.first());
480 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
481 if (device->isOpen()) {
485 QMimeType result = findByData(data, &accuracy);
490 return mimeTypeForName(defaultMimeType());
494 const QFileInfo &fileInfo,
495 QMimeDatabase::MatchMode mode)
499 }
else if (fileInfo.isNativePath()) {
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"));
517 }
else if (fileInfo.isDir()) {
518 return mimeTypeForName(directoryMimeType());
522 case QMimeDatabase::MatchDefault:
524 case QMimeDatabase::MatchExtension:
525 return mimeTypeForFileExtension(fileName);
526 case QMimeDatabase::MatchContent: {
527 QFile file(fileName);
528 return mimeTypeForData(&file);
532 return mimeTypeForFileNameAndData(fileName,
nullptr);
537 QList<QMimeType> result;
538 for (
const auto &provider : providers())
539 provider->addAllMimeTypes(result);
545 const QString resolvedParent = resolveAlias(parent);
546 QDuplicateTracker<QString> seen;
547 std::stack<QString, QStringList> toCheck;
549 while (!toCheck.empty()) {
550 if (toCheck.top() == resolvedParent)
552 const QString mimeName = toCheck.top();
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);
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
648
649
650
651
652
653
654
655
656QMimeDatabase::QMimeDatabase() :
657 d(staticQMimeDatabase())
662
663
664
665QMimeDatabase::~QMimeDatabase()
671
672
673
674QMimeType QMimeDatabase::mimeTypeForName(
const QString &nameOrAlias)
const
676 QMutexLocker locker(&d->mutex);
678 return d->mimeTypeForName(nameOrAlias);
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709QMimeType QMimeDatabase::mimeTypeForFile(
const QFileInfo &fileInfo, MatchMode mode)
const
711 QMutexLocker locker(&d->mutex);
713 return d->mimeTypeForFile(fileInfo.filePath(), fileInfo, mode);
717
718
719
720
721QMimeType QMimeDatabase::mimeTypeForFile(
const QString &fileName, MatchMode mode)
const
723 QMutexLocker locker(&d->mutex);
725 if (mode == MatchExtension) {
726 return d->mimeTypeForFileExtension(fileName);
728 QFileInfo fileInfo(fileName);
729 return d->mimeTypeForFile(fileName, fileInfo, mode);
734
735
736
737
738
739
740
741
742
743
744
745QList<QMimeType> QMimeDatabase::mimeTypesForFileName(
const QString &fileName)
const
747 QMutexLocker locker(&d->mutex);
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));
757
758
759
760
761
762QString QMimeDatabase::suffixForFileName(
const QString &fileName)
const
764 QMutexLocker locker(&d->mutex);
765 const qsizetype suffixLength = d->findByFileName(fileName).m_knownSuffixLength;
766 return fileName.right(suffixLength);
770
771
772
773
774
775
776QMimeType QMimeDatabase::mimeTypeForData(
const QByteArray &data)
const
778 QMutexLocker locker(&d->mutex);
781 return d->findByData(data, &accuracy);
785
786
787
788
789
790
791QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device)
const
793 QMutexLocker locker(&d->mutex);
795 return d->mimeTypeForData(device);
799
800
801
802
803
804
805
806
807
808
809
810
811
812QMimeType QMimeDatabase::mimeTypeForUrl(
const QUrl &url)
const
814 if (url.isLocalFile())
815 return mimeTypeForFile(url.toLocalFile());
817 const QString scheme = url.scheme();
818 if (scheme.startsWith(
"http"_L1) || scheme ==
"mailto"_L1)
819 return mimeTypeForName(d->defaultMimeType());
821 return mimeTypeForFile(url.path(), MatchExtension);
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843QMimeType QMimeDatabase::mimeTypeForFileNameAndData(
const QString &fileName, QIODevice *device)
const
845 QMutexLocker locker(&d->mutex);
847 if (fileName.endsWith(u'/'))
848 return d->mimeTypeForName(directoryMimeType());
850 const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device);
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870QMimeType QMimeDatabase::mimeTypeForFileNameAndData(
const QString &fileName,
const QByteArray &data)
const
872 QMutexLocker locker(&d->mutex);
874 if (fileName.endsWith(u'/'))
875 return d->mimeTypeForName(directoryMimeType());
877 QBuffer buffer(
const_cast<QByteArray *>(&data));
878 buffer.open(QIODevice::ReadOnly);
879 return d->mimeTypeForFileNameAndData(fileName, &buffer);
883
884
885
886
887
888
889QList<QMimeType> QMimeDatabase::allMimeTypes()
const
891 QMutexLocker locker(&d->mutex);
893 return d->allMimeTypes();
897
898
899
900
901
902
903
904
905
906
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)
@ InternalDatabaseAvailable
static bool isTextFile(const QByteArray &data)
static Q_CONSTINIT const int qmime_secondsBetweenChecks
static QStringList locateMimeDirectories()
static QString directoryMimeType()
static QString plainTextMimeType()