QMimeDatabase: fix handling of conflicting globs
This code always intended to follow the recommended checking order from the spec, which says "if multiple globs match, use file contents". But if the globs had different weights, it would discard globs with lower weights, and then wrongly conclude, if there is only one glob left, that there was no ambiguity. The correct way is rather: remember that multiple globs matched, do determination from contents, and if that didn't work, *then* use (one of) the highest-weight glob(s). This fixes PGP-encrypted *.asc files being detected as text/plain rather than application/pgp-encrypted. (https://bugs.kde.org/show_bug.cgi?id=346754) Change-Id: I734459daf9f502baa95ebb89432819964e0ce304 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
f163912b5d
commit
6722147696
@ -108,12 +108,12 @@ QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias)
|
||||
return provider()->mimeTypeForName(provider()->resolveAlias(nameOrAlias));
|
||||
}
|
||||
|
||||
QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName, QString *foundSuffix)
|
||||
QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName)
|
||||
{
|
||||
if (fileName.endsWith(QLatin1Char('/')))
|
||||
return QStringList() << QLatin1String("inode/directory");
|
||||
|
||||
QStringList matchingMimeTypes = provider()->findByFileName(QFileInfo(fileName).fileName(), foundSuffix);
|
||||
QStringList matchingMimeTypes = provider()->findByFileName(QFileInfo(fileName).fileName()).m_matchingMimeTypes;
|
||||
matchingMimeTypes.sort(); // make it deterministic
|
||||
return matchingMimeTypes;
|
||||
}
|
||||
@ -168,13 +168,17 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa
|
||||
*accuracyPtr = 0;
|
||||
|
||||
// Pass 1) Try to match on the file name
|
||||
QStringList candidatesByName = mimeTypeForFileName(fileName);
|
||||
if (candidatesByName.count() == 1) {
|
||||
QMimeGlobMatchResult candidatesByName;
|
||||
if (fileName.endsWith(QLatin1Char('/')))
|
||||
candidatesByName.addMatch(QLatin1String("inode/directory"), 100, QString());
|
||||
else
|
||||
candidatesByName = provider()->findByFileName(QFileInfo(fileName).fileName());
|
||||
if (candidatesByName.m_allMatchingMimeTypes.count() == 1) {
|
||||
*accuracyPtr = 100;
|
||||
const QMimeType mime = mimeTypeForName(candidatesByName.at(0));
|
||||
const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
|
||||
if (mime.isValid())
|
||||
return mime;
|
||||
candidatesByName.clear();
|
||||
candidatesByName = {};
|
||||
}
|
||||
|
||||
// Extension is unknown, or matches multiple mimetypes.
|
||||
@ -193,7 +197,7 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa
|
||||
// "for glob_match in glob_matches:"
|
||||
// "if glob_match is subclass or equal to sniffed_type, use glob_match"
|
||||
const QString sniffedMime = candidateByData.name();
|
||||
for (const QString &m : qAsConst(candidatesByName)) {
|
||||
for (const QString &m : qAsConst(candidatesByName.m_matchingMimeTypes)) {
|
||||
if (inherits(m, sniffedMime)) {
|
||||
// We have magic + pattern pointing to this, so it's a pretty good match
|
||||
*accuracyPtr = 100;
|
||||
@ -205,9 +209,10 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa
|
||||
}
|
||||
}
|
||||
|
||||
if (candidatesByName.count() > 1) {
|
||||
if (candidatesByName.m_allMatchingMimeTypes.count() > 1) {
|
||||
candidatesByName.m_matchingMimeTypes.sort(); // make it deterministic
|
||||
*accuracyPtr = 20;
|
||||
const QMimeType mime = mimeTypeForName(candidatesByName.at(0));
|
||||
const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
|
||||
if (mime.isValid())
|
||||
return mime;
|
||||
}
|
||||
@ -455,9 +460,7 @@ QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) co
|
||||
QString QMimeDatabase::suffixForFileName(const QString &fileName) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
QString foundSuffix;
|
||||
d->mimeTypeForFileName(fileName, &foundSuffix);
|
||||
return foundSuffix;
|
||||
return d->provider()->findByFileName(QFileInfo(fileName).fileName()).m_foundSuffix;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
QMimeType mimeTypeForName(const QString &nameOrAlias);
|
||||
QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *priorityPtr);
|
||||
QMimeType findByData(const QByteArray &data, int *priorityPtr);
|
||||
QStringList mimeTypeForFileName(const QString &fileName, QString *foundSuffix = 0);
|
||||
QStringList mimeTypeForFileName(const QString &fileName);
|
||||
|
||||
mutable QMimeProviderBase *m_provider;
|
||||
const QString m_defaultMimeType;
|
||||
|
@ -58,9 +58,13 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QString &pattern)
|
||||
{
|
||||
// Is this a lower-weight pattern than the last match? Skip this match then.
|
||||
if (weight < m_weight)
|
||||
if (m_allMatchingMimeTypes.contains(mimeType))
|
||||
return;
|
||||
// Is this a lower-weight pattern than the last match? Skip this match then.
|
||||
if (weight < m_weight) {
|
||||
m_allMatchingMimeTypes.append(mimeType);
|
||||
return;
|
||||
}
|
||||
bool replace = weight > m_weight;
|
||||
if (!replace) {
|
||||
// Compare the length of the match
|
||||
@ -79,6 +83,7 @@ void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const Q
|
||||
}
|
||||
if (!m_matchingMimeTypes.contains(mimeType)) {
|
||||
m_matchingMimeTypes.append(mimeType);
|
||||
m_allMatchingMimeTypes.append(mimeType);
|
||||
if (pattern.startsWith(QLatin1String("*.")))
|
||||
m_foundSuffix = pattern.mid(2);
|
||||
}
|
||||
@ -201,35 +206,32 @@ void QMimeGlobPatternList::match(QMimeGlobMatchResult &result,
|
||||
}
|
||||
}
|
||||
|
||||
QStringList QMimeAllGlobPatterns::matchingGlobs(const QString &fileName, QString *foundSuffix) const
|
||||
QMimeGlobMatchResult QMimeAllGlobPatterns::matchingGlobs(const QString &fileName) const
|
||||
{
|
||||
// First try the high weight matches (>50), if any.
|
||||
QMimeGlobMatchResult result;
|
||||
m_highWeightGlobs.match(result, fileName);
|
||||
if (result.m_matchingMimeTypes.isEmpty()) {
|
||||
|
||||
// Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
|
||||
// (which is most of them, so this optimization is definitely worth it)
|
||||
const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
|
||||
if (lastDot != -1) { // if no '.', skip the extension lookup
|
||||
const int ext_len = fileName.length() - lastDot - 1;
|
||||
const QString simpleExtension = fileName.right(ext_len).toLower();
|
||||
// (toLower because fast patterns are always case-insensitive and saved as lowercase)
|
||||
// Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
|
||||
// (which is most of them, so this optimization is definitely worth it)
|
||||
const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
|
||||
if (lastDot != -1) { // if no '.', skip the extension lookup
|
||||
const int ext_len = fileName.length() - lastDot - 1;
|
||||
const QString simpleExtension = fileName.right(ext_len).toLower();
|
||||
// (toLower because fast patterns are always case-insensitive and saved as lowercase)
|
||||
|
||||
const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension);
|
||||
const QString simplePattern = QLatin1String("*.") + simpleExtension;
|
||||
for (const QString &mime : matchingMimeTypes)
|
||||
result.addMatch(mime, 50, simplePattern);
|
||||
// Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
|
||||
// at least those with weight 50.
|
||||
}
|
||||
|
||||
// Finally, try the low weight matches (<=50)
|
||||
m_lowWeightGlobs.match(result, fileName);
|
||||
const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension);
|
||||
const QString simplePattern = QLatin1String("*.") + simpleExtension;
|
||||
for (const QString &mime : matchingMimeTypes)
|
||||
result.addMatch(mime, 50, simplePattern);
|
||||
// Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
|
||||
// at least those with weight 50.
|
||||
}
|
||||
if (foundSuffix)
|
||||
*foundSuffix = result.m_foundSuffix;
|
||||
return result.m_matchingMimeTypes;
|
||||
|
||||
// Finally, try the low weight matches (<=50)
|
||||
m_lowWeightGlobs.match(result, fileName);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::clear()
|
||||
|
@ -68,7 +68,8 @@ struct QMimeGlobMatchResult
|
||||
|
||||
void addMatch(const QString &mimeType, int weight, const QString &pattern);
|
||||
|
||||
QStringList m_matchingMimeTypes;
|
||||
QStringList m_matchingMimeTypes; // only those with highest weight
|
||||
QStringList m_allMatchingMimeTypes;
|
||||
int m_weight;
|
||||
int m_matchingPatternLength;
|
||||
QString m_foundSuffix;
|
||||
@ -153,7 +154,7 @@ public:
|
||||
|
||||
void addGlob(const QMimeGlobPattern &glob);
|
||||
void removeMimeType(const QString &mimeType);
|
||||
QStringList matchingGlobs(const QString &fileName, QString *foundSuffix) const;
|
||||
QMimeGlobMatchResult matchingGlobs(const QString &fileName) const;
|
||||
void clear();
|
||||
|
||||
PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain"
|
||||
|
@ -287,13 +287,13 @@ QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name)
|
||||
return mimeTypeForNameUnchecked(name);
|
||||
}
|
||||
|
||||
QStringList QMimeBinaryProvider::findByFileName(const QString &fileName, QString *foundSuffix)
|
||||
QMimeGlobMatchResult QMimeBinaryProvider::findByFileName(const QString &fileName)
|
||||
{
|
||||
checkCache();
|
||||
if (fileName.isEmpty())
|
||||
return QStringList();
|
||||
const QString lowerFileName = fileName.toLower();
|
||||
QMimeGlobMatchResult result;
|
||||
if (fileName.isEmpty())
|
||||
return result;
|
||||
const QString lowerFileName = fileName.toLower();
|
||||
// TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly.
|
||||
for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) {
|
||||
matchGlobList(result, cacheFile, cacheFile->getUint32(PosLiteralListOffset), fileName);
|
||||
@ -305,9 +305,7 @@ QStringList QMimeBinaryProvider::findByFileName(const QString &fileName, QString
|
||||
if (result.m_matchingMimeTypes.isEmpty())
|
||||
matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true);
|
||||
}
|
||||
if (foundSuffix)
|
||||
*foundSuffix = result.m_foundSuffix;
|
||||
return result.m_matchingMimeTypes;
|
||||
return result;
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName)
|
||||
@ -728,12 +726,11 @@ QMimeType QMimeXMLProvider::mimeTypeForName(const QString &name)
|
||||
return m_nameMimeTypeMap.value(name);
|
||||
}
|
||||
|
||||
QStringList QMimeXMLProvider::findByFileName(const QString &fileName, QString *foundSuffix)
|
||||
QMimeGlobMatchResult QMimeXMLProvider::findByFileName(const QString &fileName)
|
||||
{
|
||||
ensureLoaded();
|
||||
|
||||
const QStringList matchingMimeTypes = m_mimeTypeGlobs.matchingGlobs(fileName, foundSuffix);
|
||||
return matchingMimeTypes;
|
||||
return m_mimeTypeGlobs.matchingGlobs(fileName);
|
||||
}
|
||||
|
||||
QMimeType QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
#ifndef QT_NO_MIMETYPE
|
||||
|
||||
#include "qmimeglobpattern_p.h"
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qset.h>
|
||||
#include <QtCore/qelapsedtimer.h>
|
||||
@ -72,7 +73,7 @@ public:
|
||||
|
||||
virtual bool isValid() = 0;
|
||||
virtual QMimeType mimeTypeForName(const QString &name) = 0;
|
||||
virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix) = 0;
|
||||
virtual QMimeGlobMatchResult findByFileName(const QString &fileName) = 0;
|
||||
virtual QStringList parents(const QString &mime) = 0;
|
||||
virtual QString resolveAlias(const QString &name) = 0;
|
||||
virtual QStringList listAliases(const QString &name) = 0;
|
||||
@ -99,7 +100,7 @@ public:
|
||||
|
||||
virtual bool isValid() Q_DECL_OVERRIDE;
|
||||
virtual QMimeType mimeTypeForName(const QString &name) Q_DECL_OVERRIDE;
|
||||
virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix) Q_DECL_OVERRIDE;
|
||||
virtual QMimeGlobMatchResult findByFileName(const QString &fileName) Q_DECL_OVERRIDE;
|
||||
virtual QStringList parents(const QString &mime) Q_DECL_OVERRIDE;
|
||||
virtual QString resolveAlias(const QString &name) Q_DECL_OVERRIDE;
|
||||
virtual QStringList listAliases(const QString &name) Q_DECL_OVERRIDE;
|
||||
@ -142,7 +143,7 @@ public:
|
||||
|
||||
virtual bool isValid() Q_DECL_OVERRIDE;
|
||||
virtual QMimeType mimeTypeForName(const QString &name) Q_DECL_OVERRIDE;
|
||||
virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix) Q_DECL_OVERRIDE;
|
||||
virtual QMimeGlobMatchResult findByFileName(const QString &fileName) Q_DECL_OVERRIDE;
|
||||
virtual QStringList parents(const QString &mime) Q_DECL_OVERRIDE;
|
||||
virtual QString resolveAlias(const QString &name) Q_DECL_OVERRIDE;
|
||||
virtual QStringList listAliases(const QString &name) Q_DECL_OVERRIDE;
|
||||
|
Loading…
Reference in New Issue
Block a user