QMimeDatabase: handle glob-deleteall tags
According to the Freedesktop spec[1], a mimetype that has glob-deleteall overwrites other glob-pattern definitions for a mimetype if it is in a higher precedence dir, the default order is (from high to low) ~/.local/share/mime, /usr/local/share/mime, /usr/share/mime. Or if the XDG_DATA_DIRS env var is set, then it takes precedence. The QMime*ProviderS in m_providers are constructed/stored in that same order, high to low). For QMimeXMLProvider, we can just clear the glob patterns associated with those mimetypes from the lists/maps. For the QMimeBinaryProvider however, we can't change the binary (mmap'ed) cache file, instead check mimetype names against the exclusion list before modifying a QMimeGlobMatchResult. [1] https://specifications.freedesktop.org/shared-mime-info-spec/latest/ar01s02.html This test uses XDG_DATA_DIRS so only viable when USE_XDG_DATA_DIRS is defined. Fixes: QTBUG-101755 Change-Id: Icadbdf1027155296377c5a6ab3be8e41b6668325 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: David Faure <david.faure@kdab.com> (cherry picked from commit 9547ef58c707a11d5affcbcfa553f3aca5334087) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
db0169bcd9
commit
dc2ff09602
@ -142,6 +142,19 @@ void QMimeDatabasePrivate::loadProviders()
|
|||||||
m_providers.push_back(std::move(*it));
|
m_providers.push_back(std::move(*it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle mimetypes with glob-deleteall tags (from XML providers)
|
||||||
|
auto it = m_providers.begin();
|
||||||
|
const auto end = m_providers.end();
|
||||||
|
for (;it != end; ++it) {
|
||||||
|
const QStringList &list = (*it)->m_mimeTypesWithDeletedGlobs;
|
||||||
|
if (list.isEmpty())
|
||||||
|
continue;
|
||||||
|
// Each Provider affects Providers with lower precedence
|
||||||
|
auto nextIt = it + 1;
|
||||||
|
for (; nextIt != end; ++nextIt)
|
||||||
|
(*nextIt)->excludeMimeTypeGlobs(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
|
const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
|
||||||
|
@ -51,6 +51,12 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
static inline void appendIfNew(QStringList &list, const QString &str)
|
||||||
|
{
|
||||||
|
if (!list.contains(str))
|
||||||
|
list.push_back(str);
|
||||||
|
}
|
||||||
|
|
||||||
QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory)
|
QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory)
|
||||||
: m_db(db), m_directory(directory)
|
: m_db(db), m_directory(directory)
|
||||||
{
|
{
|
||||||
@ -228,6 +234,17 @@ void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobM
|
|||||||
matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName);
|
matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QMimeBinaryProvider::isMimeTypeGlobsExcluded(const char *mimeTypeName)
|
||||||
|
{
|
||||||
|
return m_mimeTypesWithExcludedGlobs.contains(QLatin1StringView(mimeTypeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QMimeBinaryProvider::excludeMimeTypeGlobs(const QStringList &toExclude)
|
||||||
|
{
|
||||||
|
for (const auto &mt : toExclude)
|
||||||
|
appendIfNew(m_mimeTypesWithExcludedGlobs, mt);
|
||||||
|
}
|
||||||
|
|
||||||
void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName)
|
void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName)
|
||||||
{
|
{
|
||||||
const int numGlobs = cacheFile->getUint32(off);
|
const int numGlobs = cacheFile->getUint32(off);
|
||||||
@ -243,8 +260,10 @@ void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile
|
|||||||
|
|
||||||
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
||||||
//qDebug() << pattern << mimeType << weight << caseSensitive;
|
//qDebug() << pattern << mimeType << weight << caseSensitive;
|
||||||
QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
|
if (isMimeTypeGlobsExcluded(mimeType))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
|
||||||
if (glob.matchFileName(fileName))
|
if (glob.matchFileName(fileName))
|
||||||
result.addMatch(QLatin1StringView(mimeType), weight, pattern);
|
result.addMatch(QLatin1StringView(mimeType), weight, pattern);
|
||||||
}
|
}
|
||||||
@ -281,6 +300,8 @@ bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result,
|
|||||||
break;
|
break;
|
||||||
const int mimeTypeOffset = cacheFile->getUint32(childOff + 4);
|
const int mimeTypeOffset = cacheFile->getUint32(childOff + 4);
|
||||||
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
||||||
|
if (isMimeTypeGlobsExcluded(mimeType))
|
||||||
|
continue;
|
||||||
const int flagsAndWeight = cacheFile->getUint32(childOff + 8);
|
const int flagsAndWeight = cacheFile->getUint32(childOff + 8);
|
||||||
const int weight = flagsAndWeight & 0xff;
|
const int weight = flagsAndWeight & 0xff;
|
||||||
const bool caseSensitive = flagsAndWeight & 0x100;
|
const bool caseSensitive = flagsAndWeight & 0x100;
|
||||||
@ -716,6 +737,7 @@ void QMimeXMLProvider::ensureLoaded()
|
|||||||
m_parents.clear();
|
m_parents.clear();
|
||||||
m_mimeTypeGlobs.clear();
|
m_mimeTypeGlobs.clear();
|
||||||
m_magicMatchers.clear();
|
m_magicMatchers.clear();
|
||||||
|
m_mimeTypesWithDeletedGlobs.clear();
|
||||||
|
|
||||||
//qDebug() << "Loading" << m_allFiles;
|
//qDebug() << "Loading" << m_allFiles;
|
||||||
|
|
||||||
@ -767,9 +789,31 @@ void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern &glob)
|
|||||||
void QMimeXMLProvider::addMimeType(const QMimeType &mt)
|
void QMimeXMLProvider::addMimeType(const QMimeType &mt)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!mt.d.data()->fromCache);
|
Q_ASSERT(!mt.d.data()->fromCache);
|
||||||
|
|
||||||
|
QString name = mt.name();
|
||||||
|
if (mt.d->hasGlobDeleteAll)
|
||||||
|
appendIfNew(m_mimeTypesWithDeletedGlobs, name);
|
||||||
m_nameMimeTypeMap.insert(mt.name(), mt);
|
m_nameMimeTypeMap.insert(mt.name(), mt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
\a toExclude is a list of mime type names that should have the the glob patterns
|
||||||
|
associated with them cleared (because there are mime types with the same names
|
||||||
|
in a higher precedence Provider that have glob-deleteall tags).
|
||||||
|
|
||||||
|
This method is called from QMimeDatabasePrivate::loadProviders() to exclude mime
|
||||||
|
type glob patterns in lower precedence Providers.
|
||||||
|
*/
|
||||||
|
void QMimeXMLProvider::excludeMimeTypeGlobs(const QStringList &toExclude)
|
||||||
|
{
|
||||||
|
for (const auto &mt : toExclude) {
|
||||||
|
auto it = m_nameMimeTypeMap.find(mt);
|
||||||
|
if (it != m_nameMimeTypeMap.end())
|
||||||
|
it->d->globPatterns.clear();
|
||||||
|
m_mimeTypeGlobs.removeMimeType(mt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QMimeXMLProvider::addParents(const QString &mime, QStringList &result)
|
void QMimeXMLProvider::addParents(const QString &mime, QStringList &result)
|
||||||
{
|
{
|
||||||
for (const QString &parent : m_parents.value(mime)) {
|
for (const QString &parent : m_parents.value(mime)) {
|
||||||
|
@ -48,11 +48,38 @@ public:
|
|||||||
virtual void loadIcon(QMimeTypePrivate &) {}
|
virtual void loadIcon(QMimeTypePrivate &) {}
|
||||||
virtual void loadGenericIcon(QMimeTypePrivate &) {}
|
virtual void loadGenericIcon(QMimeTypePrivate &) {}
|
||||||
virtual void ensureLoaded() {}
|
virtual void ensureLoaded() {}
|
||||||
|
virtual void excludeMimeTypeGlobs(const QStringList &) {}
|
||||||
|
|
||||||
QString directory() const { return m_directory; }
|
QString directory() const { return m_directory; }
|
||||||
|
|
||||||
QMimeDatabasePrivate *m_db;
|
QMimeDatabasePrivate *m_db;
|
||||||
QString m_directory;
|
QString m_directory;
|
||||||
|
|
||||||
|
/*
|
||||||
|
MimeTypes with "glob-deleteall" tags are handled differently by each provider
|
||||||
|
sub-class:
|
||||||
|
- QMimeBinaryProvider parses glob-deleteall tags lazily, i.e. only when loadMimeTypePrivate()
|
||||||
|
is called, and clears the glob patterns associated with mimetypes that have this tag
|
||||||
|
- QMimeXMLProvider parses glob-deleteall from the the start, i.e. when a XML file is
|
||||||
|
parsed with QMimeTypeParser
|
||||||
|
|
||||||
|
The two lists below are used to let both provider types (XML and Binary) communicate
|
||||||
|
about mimetypes with glob-deleteall.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
List of mimetypes in _this_ Provider that have a "glob-deleteall" tag,
|
||||||
|
glob patterns for those mimetypes should be ignored in all _other_ lower
|
||||||
|
precedence Providers.
|
||||||
|
*/
|
||||||
|
QStringList m_mimeTypesWithDeletedGlobs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
List of mimetypes with glob patterns that are "overwritten" in _this_ Provider,
|
||||||
|
by a "glob-deleteall" tag in a mimetype definition in a _higher precedence_
|
||||||
|
Provider. With QMimeBinaryProvider, we can't change the data in the binary mmap'ed
|
||||||
|
file, hence the need for this list.
|
||||||
|
*/
|
||||||
|
QStringList m_mimeTypesWithExcludedGlobs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -77,6 +104,7 @@ public:
|
|||||||
void loadIcon(QMimeTypePrivate &) override;
|
void loadIcon(QMimeTypePrivate &) override;
|
||||||
void loadGenericIcon(QMimeTypePrivate &) override;
|
void loadGenericIcon(QMimeTypePrivate &) override;
|
||||||
void ensureLoaded() override;
|
void ensureLoaded() override;
|
||||||
|
void excludeMimeTypeGlobs(const QStringList &toExclude) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CacheFile;
|
struct CacheFile;
|
||||||
@ -86,6 +114,7 @@ private:
|
|||||||
int firstOffset, const QString &fileName, qsizetype charPos,
|
int firstOffset, const QString &fileName, qsizetype charPos,
|
||||||
bool caseSensitiveCheck);
|
bool caseSensitiveCheck);
|
||||||
bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data);
|
bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data);
|
||||||
|
bool isMimeTypeGlobsExcluded(const char *name);
|
||||||
QLatin1StringView iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime);
|
QLatin1StringView iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime);
|
||||||
void loadMimeTypeList();
|
void loadMimeTypeList();
|
||||||
bool checkCacheChanged();
|
bool checkCacheChanged();
|
||||||
@ -96,6 +125,7 @@ private:
|
|||||||
bool m_mimetypeListLoaded;
|
bool m_mimetypeListLoaded;
|
||||||
struct MimeTypeExtra
|
struct MimeTypeExtra
|
||||||
{
|
{
|
||||||
|
// Both retrieved on demand in loadMimeTypePrivate
|
||||||
QHash<QString, QString> localeComments;
|
QHash<QString, QString> localeComments;
|
||||||
QStringList globPatterns;
|
QStringList globPatterns;
|
||||||
};
|
};
|
||||||
@ -133,6 +163,7 @@ public:
|
|||||||
|
|
||||||
// Called by the mimetype xml parser
|
// Called by the mimetype xml parser
|
||||||
void addMimeType(const QMimeType &mt);
|
void addMimeType(const QMimeType &mt);
|
||||||
|
void excludeMimeTypeGlobs(const QStringList &toExclude) override;
|
||||||
void addGlobPattern(const QMimeGlobPattern &glob);
|
void addGlobPattern(const QMimeGlobPattern &glob);
|
||||||
void addParent(const QString &child, const QString &parent);
|
void addParent(const QString &child, const QString &parent);
|
||||||
void addAlias(const QString &alias, const QString &name);
|
void addAlias(const QString &alias, const QString &name);
|
||||||
|
@ -39,6 +39,7 @@ public:
|
|||||||
|
|
||||||
bool loaded; // QSharedData leaves a 4 byte gap, so don't put 8 byte members first
|
bool loaded; // QSharedData leaves a 4 byte gap, so don't put 8 byte members first
|
||||||
bool fromCache; // true if this comes from the binary provider
|
bool fromCache; // true if this comes from the binary provider
|
||||||
|
bool hasGlobDeleteAll = false; // true if the mimetype has a glob-deleteall tag
|
||||||
QString name;
|
QString name;
|
||||||
LocaleHash localeComments;
|
LocaleHash localeComments;
|
||||||
QString genericIconName;
|
QString genericIconName;
|
||||||
|
@ -215,6 +215,7 @@ bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString
|
|||||||
break;
|
break;
|
||||||
case ParseGlobDeleteAll:
|
case ParseGlobDeleteAll:
|
||||||
data.globPatterns.clear();
|
data.globPatterns.clear();
|
||||||
|
data.hasGlobDeleteAll = true;
|
||||||
break;
|
break;
|
||||||
case ParseSubClass: {
|
case ParseSubClass: {
|
||||||
const QString inheritsFrom = atts.value(QLatin1StringView(mimeTypeAttributeC)).toString();
|
const QString inheritsFrom = atts.value(QLatin1StringView(mimeTypeAttributeC)).toString();
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="video/webm">
|
||||||
|
<glob-deleteall/>
|
||||||
|
<glob pattern="*.videowebm"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
@ -129,7 +129,9 @@ void tst_QMimeDatabase::initTestCase()
|
|||||||
const QString globalPackageDir = m_globalXdgDir + QStringLiteral("/mime/packages");
|
const QString globalPackageDir = m_globalXdgDir + QStringLiteral("/mime/packages");
|
||||||
QVERIFY(here.mkpath(globalPackageDir));
|
QVERIFY(here.mkpath(globalPackageDir));
|
||||||
|
|
||||||
qputenv("XDG_DATA_DIRS", QFile::encodeName(m_globalXdgDir));
|
QString overrideDir = QFINDTESTDATA("mimetypes-override/");
|
||||||
|
QByteArray env = QFile::encodeName(overrideDir) + ':' + QFile::encodeName(m_globalXdgDir);
|
||||||
|
qputenv("XDG_DATA_DIRS", env);
|
||||||
qDebug() << "\nGlobal XDG_DATA_DIRS: " << m_globalXdgDir;
|
qDebug() << "\nGlobal XDG_DATA_DIRS: " << m_globalXdgDir;
|
||||||
|
|
||||||
const QString freeDesktopXml = QStringLiteral("freedesktop.org.xml");
|
const QString freeDesktopXml = QStringLiteral("freedesktop.org.xml");
|
||||||
@ -299,6 +301,15 @@ void tst_QMimeDatabase::mimeTypesForFileName_data()
|
|||||||
QTest::newRow("non_ascii") << QString::fromUtf8("AİİA.pdf") << (QStringList() << "application/pdf");
|
QTest::newRow("non_ascii") << QString::fromUtf8("AİİA.pdf") << (QStringList() << "application/pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QStringList mimeTypeNames(const QList<QMimeType> &mimes)
|
||||||
|
{
|
||||||
|
QStringList mimeNames;
|
||||||
|
mimeNames.reserve(mimes.size());
|
||||||
|
for (const auto &mime : mimes)
|
||||||
|
mimeNames.append(mime.name());
|
||||||
|
return mimeNames;
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QMimeDatabase::mimeTypesForFileName()
|
void tst_QMimeDatabase::mimeTypesForFileName()
|
||||||
{
|
{
|
||||||
QFETCH(QString, fileName);
|
QFETCH(QString, fileName);
|
||||||
@ -311,6 +322,24 @@ void tst_QMimeDatabase::mimeTypesForFileName()
|
|||||||
QCOMPARE(mimeNames, expectedMimeTypes);
|
QCOMPARE(mimeNames, expectedMimeTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QMimeDatabase::mimeTypesForFileName_glob_deleteall()
|
||||||
|
{
|
||||||
|
#if !defined(USE_XDG_DATA_DIRS)
|
||||||
|
QSKIP("This test requires XDG_DATA_DIRS");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QMimeDatabase mdb;
|
||||||
|
QList<QMimeType> mimes = mdb.mimeTypesForFileName(u"foo.webm"_s);
|
||||||
|
|
||||||
|
// "*.webm" glob pattern is deleted with "glob-deleteall"
|
||||||
|
QVERIFY2(mimes.isEmpty(), qPrintable(mimeTypeNames(mimes).join(u',')));
|
||||||
|
mimes = mdb.mimeTypesForFileName(u"foo.videowebm"_s);
|
||||||
|
QCOMPARE(mimes.size(), 1);
|
||||||
|
QCOMPARE(mimes.at(0).globPatterns(), QStringList{"*.videowebm"});
|
||||||
|
// Custom "*.videowebm" pattern is used instead
|
||||||
|
QCOMPARE(mimes.at(0).name(), u"video/webm");
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QMimeDatabase::inheritance()
|
void tst_QMimeDatabase::inheritance()
|
||||||
{
|
{
|
||||||
QMimeDatabase db;
|
QMimeDatabase db;
|
||||||
|
@ -25,6 +25,7 @@ private slots:
|
|||||||
void mimeTypeForFileName();
|
void mimeTypeForFileName();
|
||||||
void mimeTypesForFileName_data();
|
void mimeTypesForFileName_data();
|
||||||
void mimeTypesForFileName();
|
void mimeTypesForFileName();
|
||||||
|
void mimeTypesForFileName_glob_deleteall();
|
||||||
void inheritance();
|
void inheritance();
|
||||||
void aliases();
|
void aliases();
|
||||||
void listAliases_data();
|
void listAliases_data();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user