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"))
57using namespace std::chrono_literals;
63 if (m_lastCheck.isValid() && m_lastCheck.durationElapsed() < qmime_secondsBetweenChecks)
72 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral(
"mime"),
73 QStandardPaths::LocateDirectory);
74 dirs.append(u":/qt-project.org/qmime"_s);
78#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
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); }
92 Providers currentProviders;
93 std::swap(m_providers, currentProviders);
95 m_providers.reserve(mimeDirs.size() + (needInternalDB ? 1 : 0));
97 for (
const QString &mimeDir : mimeDirs) {
98 const QString cacheFile = mimeDir +
"/mime.cache"_L1;
100 const auto predicate = [mimeDir](
const std::unique_ptr<QMimeProviderBase> &prov)
102 return prov && prov->directory() == mimeDir;
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));
111 if (!provider->isValid()) {
117 provider.reset(
new QMimeXMLProvider(
this, mimeDir));
120 m_providers.push_back(std::move(provider));
122 auto provider = std::move(*it);
123 provider->ensureLoaded();
124 if (!provider->isValid()) {
125 provider.reset(
new QMimeXMLProvider(
this, mimeDir));
128 m_providers.push_back(std::move(provider));
133 if (needInternalDB) {
135 const auto isInternal = [](
const std::unique_ptr<QMimeProviderBase> &prov)
137 return prov && prov->isInternalDatabase();
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)));
143 m_providers.push_back(std::move(*it));
147 auto it = m_providers.begin();
148 (*it)->setOverrideProvider(
nullptr);
150 const auto end = m_providers.end();
151 for (; it != end; ++it)
152 (*it)->setOverrideProvider((it - 1)->get());
157 QMutexLocker locker(&mutex);
159 m_lastCheck.invalidate();
165 Q_ASSERT(!mutex.tryLock());
167 if (m_providers.empty()) {
179 for (
const auto &provider : providers()) {
180 const QString ret = provider->resolveAlias(nameOrAlias);
188
189
190
193 const QString mimeName = resolveAlias(nameOrAlias);
194 for (
const auto &provider : providers()) {
195 if (provider->knowsMimeType(mimeName))
196 return QMimeType(QMimeTypePrivate(mimeName));
203 if (fileName.endsWith(u'/'))
204 return { directoryMimeType() };
206 const QMimeGlobMatchResult result = findByFileName(fileName);
207 QStringList matchingMimeTypes = result.m_matchingMimeTypes;
208 matchingMimeTypes.sort();
209 return matchingMimeTypes;
214 QMimeGlobMatchResult result;
215 const QString fileNameExcludingPath = QFileSystemEntry(fileName).fileName();
216 for (
const auto &provider : providers())
217 provider->addFileNameMatches(fileNameExcludingPath, result);
223 QMutexLocker locker(&mutex);
224 for (
const auto &provider : providers()) {
225 auto comments = provider->localeComments(name);
226 if (!comments.isEmpty())
235 QMutexLocker locker(&mutex);
236 QStringList patterns;
237 const auto &providerList = providers();
240 for (
auto rit = providerList.rbegin(); rit != providerList.rend(); ++rit) {
241 auto *provider = rit->get();
242 if (provider->hasGlobDeleteAll(name))
244 patterns += provider->globPatterns(name);
251 QMutexLocker locker(&mutex);
252 for (
const auto &provider : providers()) {
253 QString genericIconName = provider->genericIcon(name);
254 if (!genericIconName.isEmpty())
255 return genericIconName;
262 QMutexLocker locker(&mutex);
263 for (
const auto &provider : providers()) {
264 QString iconName = provider->icon(name);
265 if (!iconName.isEmpty())
273 const QStringView myGroup = QStringView{mimeTypeName}.left(mimeTypeName.indexOf(u'/'));
275 if (myGroup !=
"inode"_L1 &&
277 myGroup !=
"all"_L1 && myGroup !=
"fonts"_L1 && myGroup !=
"print"_L1 && myGroup !=
"uri"_L1
278 && mimeTypeName != defaultMimeType()) {
279 return defaultMimeType();
286 QMutexLocker locker(&mutex);
287 return parents(mimeName);
293 Q_ASSERT(!mutex.tryLock());
296 for (
const auto &provider : providers())
297 provider->addParents(mimeName, result);
302 if (mimeName.startsWith(
"text/"_L1) && mimeName != plainTextMimeType()
303 && !result.contains(plainTextMimeType())) {
304 result.append(plainTextMimeType());
307 if (result.isEmpty()) {
308 const QString parent = fallbackParent(mimeName);
309 if (!parent.isEmpty())
310 result.append(parent);
317 QMutexLocker locker(&mutex);
319 for (
const auto &provider : providers())
320 provider->addAliases(mimeName, result);
326 QMutexLocker locker(&mutex);
327 return inherits(mime, parent);
333 static const char bigEndianBOM[] =
"\xFE\xFF";
334 static const char littleEndianBOM[] =
"\xFF\xFE";
335 if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM))
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)
351 if (data.isEmpty()) {
353 return mimeTypeForName(QStringLiteral(
"application/x-zerosize"));
357 for (
const auto &provider : providers())
358 provider->findByMagic(data, result);
362 return QMimeType(QMimeTypePrivate(result.candidate));
367 return mimeTypeForName(plainTextMimeType());
370 return mimeTypeForName(defaultMimeType());
376 bool hasRules =
false;
377 for (
const auto &provider : providers()) {
378 const QMimeMagicCheckResult result = provider->checkMagicRules(mime, data);
381 hasRules = hasRules || result.hasRules;
395 QMimeGlobMatchResult candidatesByName = findByFileName(fileName);
396 if (candidatesByName.m_allMatchingMimeTypes.size() == 1) {
397 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
400 candidatesByName = {};
405 const auto matchOnContent = [
this, &candidatesByName](QIODevice *device) {
406 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
407 if (device->isOpen()) {
415 int magicAccuracy = 0;
416 QMimeType candidateByData(findByData(data, &magicAccuracy));
419 if (candidateByData.isValid() && magicAccuracy > 0) {
420 const QString sniffedMime = candidateByData.name();
422 if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime))
423 return candidateByData;
425 for (
const QString &m : std::as_const(candidatesByName.m_allMatchingMimeTypes)) {
426 if (inherits(m, sniffedMime)) {
428 return mimeTypeForName(m);
431 if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) {
433 return candidateByData;
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))
447 if (!remaining.isEmpty())
448 candidates =
std::move(remaining);
452 if (candidatesByName.m_allMatchingMimeTypes.size() > 1) {
453 candidatesByName.m_matchingMimeTypes.sort();
454 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
459 return mimeTypeForName(defaultMimeType());
463 return matchOnContent(device);
465 QFile fallbackFile(fileName);
466 return matchOnContent(&fallbackFile);
471 const QStringList matches = mimeTypeForFileName(fileName);
472 if (matches.isEmpty()) {
473 return mimeTypeForName(defaultMimeType());
476 return mimeTypeForName(matches.first());
483 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
484 if (device->isOpen()) {
488 QMimeType result = findByData(data, &accuracy);
493 return mimeTypeForName(defaultMimeType());
497 const QFileInfo &fileInfo,
498 QMimeDatabase::MatchMode mode)
502 }
else if (fileInfo.isNativePath()) {
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"));
520 }
else if (fileInfo.isDir()) {
521 return mimeTypeForName(directoryMimeType());
525 case QMimeDatabase::MatchDefault:
527 case QMimeDatabase::MatchExtension:
528 return mimeTypeForFileExtension(fileName);
529 case QMimeDatabase::MatchContent: {
530 QFile file(fileName);
531 return mimeTypeForData(&file);
535 return mimeTypeForFileNameAndData(fileName,
nullptr);
540 QList<QMimeType> result;
541 for (
const auto &provider : providers())
542 provider->addAllMimeTypes(result);
548 const QString resolvedParent = resolveAlias(parent);
549 QDuplicateTracker<QString> seen;
550 std::stack<QString, QStringList> toCheck;
552 while (!toCheck.empty()) {
553 if (toCheck.top() == resolvedParent)
555 const QString mimeName = toCheck.top();
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);
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
646
647
648
649
650
651
652
655
656
657
658
659
660
661
662
663QMimeDatabase::QMimeDatabase() :
664 d(staticQMimeDatabase())
669
670
671
672QMimeDatabase::~QMimeDatabase()
678
679
680
681QMimeType QMimeDatabase::mimeTypeForName(
const QString &nameOrAlias)
const
683 QMutexLocker locker(&d->mutex);
685 return d->mimeTypeForName(nameOrAlias);
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716QMimeType QMimeDatabase::mimeTypeForFile(
const QFileInfo &fileInfo, MatchMode mode)
const
718 QMutexLocker locker(&d->mutex);
720 return d->mimeTypeForFile(fileInfo.filePath(), fileInfo, mode);
724
725
726
727
728QMimeType QMimeDatabase::mimeTypeForFile(
const QString &fileName, MatchMode mode)
const
730 QMutexLocker locker(&d->mutex);
732 if (mode == MatchExtension) {
733 return d->mimeTypeForFileExtension(fileName);
735 QFileInfo fileInfo(fileName);
736 return d->mimeTypeForFile(fileName, fileInfo, mode);
741
742
743
744
745
746
747
748
749
750
751
752QList<QMimeType> QMimeDatabase::mimeTypesForFileName(
const QString &fileName)
const
754 QMutexLocker locker(&d->mutex);
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));
764
765
766
767
768
769QString QMimeDatabase::suffixForFileName(
const QString &fileName)
const
771 QMutexLocker locker(&d->mutex);
772 const qsizetype suffixLength = d->findByFileName(fileName).m_knownSuffixLength;
773 return fileName.right(suffixLength);
777
778
779
780
781
782
783QMimeType QMimeDatabase::mimeTypeForData(
const QByteArray &data)
const
785 QMutexLocker locker(&d->mutex);
788 return d->findByData(data, &accuracy);
792
793
794
795
796
797
798QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device)
const
800 QMutexLocker locker(&d->mutex);
802 return d->mimeTypeForData(device);
806
807
808
809
810
811
812
813
814
815
816
817
818
819QMimeType QMimeDatabase::mimeTypeForUrl(
const QUrl &url)
const
821 if (url.isLocalFile())
822 return mimeTypeForFile(url.toLocalFile());
824 const QString scheme = url.scheme();
825 if (scheme.startsWith(
"http"_L1) || scheme ==
"mailto"_L1)
826 return mimeTypeForName(d->defaultMimeType());
828 return mimeTypeForFile(url.path(), MatchExtension);
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850QMimeType QMimeDatabase::mimeTypeForFileNameAndData(
const QString &fileName, QIODevice *device)
const
852 QMutexLocker locker(&d->mutex);
854 if (fileName.endsWith(u'/'))
855 return d->mimeTypeForName(directoryMimeType());
857 const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device);
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877QMimeType QMimeDatabase::mimeTypeForFileNameAndData(
const QString &fileName,
const QByteArray &data)
const
879 QMutexLocker locker(&d->mutex);
881 if (fileName.endsWith(u'/'))
882 return d->mimeTypeForName(directoryMimeType());
884 QBuffer buffer(
const_cast<QByteArray *>(&data));
885 buffer.open(QIODevice::ReadOnly);
886 return d->mimeTypeForFileNameAndData(fileName, &buffer);
890
891
892
893
894
895
896QList<QMimeType> QMimeDatabase::allMimeTypes()
const
898 QMutexLocker locker(&d->mutex);
900 return d->allMimeTypes();
904
905
906
907
908
909
910
911
912
913
914
915
916void QMimeDatabase::reload()
918 QMimeDatabasePrivate::instance()->reload();
922
923
924
925
926
927
928
929
930
931
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 QStringList locateMimeDirectories()
static QString directoryMimeType()
static QString plainTextMimeType()
static constexpr auto qmime_secondsBetweenChecks