diff options
author | David Faure <david.faure@kdab.com> | 2015-08-25 14:30:07 +0200 |
---|---|---|
committer | David Faure <david.faure@kdab.com> | 2015-09-04 20:00:22 +0000 |
commit | 5e41f4137dd42a9a639be8743ae95c8e159bd4e0 (patch) | |
tree | 458a432ed04ddd9556a97358c2bbf9212fecbf57 | |
parent | 880a8aa7e99bb91e7a815cadde72bb5230c815ea (diff) |
QMimeDatabase: warn instead of asserting on bad magic.
An invalid mime magic definition could lead to an assert. Replaced with
a qWarning. Move all checking to the QMimeMagicRule constructor, and do
keep invalid rules since they are need to parse child rules.
Unit test added, with QTest::ignoreMessage when using the XML backend
(there's no warning from update-mime-database when using the cache).
Also make it easier to add more shared mime info files for tests.
Task-number: QTBUG-44319
Done-with: Eike Ziller <eike.ziller@theqtcompany.com>
Change-Id: Ie39a160a106b650cdcee88778fa7eff9e932a988
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
10 files changed, 159 insertions, 82 deletions
diff --git a/src/corelib/mimetypes/qmimemagicrule.cpp b/src/corelib/mimetypes/qmimemagicrule.cpp index c8508ac0d2..6a3a429179 100644 --- a/src/corelib/mimetypes/qmimemagicrule.cpp +++ b/src/corelib/mimetypes/qmimemagicrule.cpp @@ -38,6 +38,7 @@ #ifndef QT_NO_MIMETYPE +#include "qmimetypeparser_p.h" #include <QtCore/QList> #include <QtCore/QDebug> #include <qendian.h> @@ -231,26 +232,53 @@ static inline QByteArray makePattern(const QByteArray &value) return pattern; } -QMimeMagicRule::QMimeMagicRule(QMimeMagicRule::Type theType, +// Evaluate a magic match rule like +// <match value="must be converted with BinHex" type="string" offset="11"/> +// <match value="0x9501" type="big16" offset="0:64"/> + +QMimeMagicRule::QMimeMagicRule(const QString &typeStr, const QByteArray &theValue, - int theStartPos, - int theEndPos, - const QByteArray &theMask) : + const QString &offsets, + const QByteArray &theMask, + QString *errorString) : d(new QMimeMagicRulePrivate) { - Q_ASSERT(!theValue.isEmpty()); - - d->type = theType; d->value = theValue; - d->startPos = theStartPos; - d->endPos = theEndPos; d->mask = theMask; d->matchFunction = 0; + d->type = QMimeMagicRule::type(typeStr.toLatin1()); + if (d->type == Invalid) { + *errorString = QStringLiteral("Type %s is not supported").arg(typeStr); + } + + // Parse for offset as "1" or "1:10" + const int colonIndex = offsets.indexOf(QLatin1Char(':')); + const QString startPosStr = colonIndex == -1 ? offsets : offsets.mid(0, colonIndex); + const QString endPosStr = colonIndex == -1 ? offsets : offsets.mid(colonIndex + 1); + if (!QMimeTypeParserBase::parseNumber(startPosStr, &d->startPos, errorString) || + !QMimeTypeParserBase::parseNumber(endPosStr, &d->endPos, errorString)) { + d->type = Invalid; + return; + } + + if (d->value.isEmpty()) { + d->type = Invalid; + if (errorString) + *errorString = QLatin1String("Invalid empty magic rule value"); + return; + } + if (d->type >= Host16 && d->type <= Byte) { bool ok; d->number = d->value.toUInt(&ok, 0); // autodetect - Q_ASSERT(ok); + if (!ok) { + d->type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule value \"%1\"").arg( + QString::fromLatin1(d->value)); + return; + } d->numberMask = !d->mask.isEmpty() ? d->mask.toUInt(&ok, 0) : 0; // autodetect } @@ -259,9 +287,23 @@ QMimeMagicRule::QMimeMagicRule(QMimeMagicRule::Type theType, d->pattern = makePattern(d->value); d->pattern.squeeze(); if (!d->mask.isEmpty()) { - Q_ASSERT(d->mask.size() >= 4 && d->mask.startsWith("0x")); - d->mask = QByteArray::fromHex(QByteArray::fromRawData(d->mask.constData() + 2, d->mask.size() - 2)); - Q_ASSERT(d->mask.size() == d->pattern.size()); + if (d->mask.size() < 4 || !d->mask.startsWith("0x")) { + d->type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule mask \"%1\"").arg( + QString::fromLatin1(d->mask)); + return; + } + const QByteArray &tempMask = QByteArray::fromHex(QByteArray::fromRawData( + d->mask.constData() + 2, d->mask.size() - 2)); + if (tempMask.size() != d->pattern.size()) { + d->type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule mask size \"%1\"").arg( + QString::fromLatin1(d->mask)); + return; + } + d->mask = tempMask; } else { d->mask.fill(char(-1), d->pattern.size()); } diff --git a/src/corelib/mimetypes/qmimemagicrule_p.h b/src/corelib/mimetypes/qmimemagicrule_p.h index 03ac1d1de9..6b64bfcc10 100644 --- a/src/corelib/mimetypes/qmimemagicrule_p.h +++ b/src/corelib/mimetypes/qmimemagicrule_p.h @@ -61,7 +61,8 @@ class QMimeMagicRule public: enum Type { Invalid = 0, String, Host16, Host32, Big16, Big32, Little16, Little32, Byte }; - QMimeMagicRule(Type type, const QByteArray &value, int startPos, int endPos, const QByteArray &mask = QByteArray()); + QMimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets, + const QByteArray &mask, QString *errorString); QMimeMagicRule(const QMimeMagicRule &other); ~QMimeMagicRule(); diff --git a/src/corelib/mimetypes/qmimetypeparser.cpp b/src/corelib/mimetypes/qmimetypeparser.cpp index 9610162c4f..8a8b97655a 100644 --- a/src/corelib/mimetypes/qmimetypeparser.cpp +++ b/src/corelib/mimetypes/qmimetypeparser.cpp @@ -153,8 +153,8 @@ QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState curren return ParseError; } -// Parse int number from an (attribute) string) -static bool parseNumber(const QString &n, int *target, QString *errorMessage) +// Parse int number from an (attribute) string +bool QMimeTypeParserBase::parseNumber(const QString &n, int *target, QString *errorMessage) { bool ok; *target = n.toInt(&ok); @@ -165,37 +165,14 @@ static bool parseNumber(const QString &n, int *target, QString *errorMessage) return true; } -// Evaluate a magic match rule like -// <match value="must be converted with BinHex" type="string" offset="11"/> -// <match value="0x9501" type="big16" offset="0:64"/> #ifndef QT_NO_XMLSTREAMREADER -static bool createMagicMatchRule(const QXmlStreamAttributes &atts, - QString *errorMessage, QMimeMagicRule *&rule) +static QMimeMagicRule *createMagicMatchRule(const QXmlStreamAttributes &atts, QString *errorMessage) { const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString(); - QMimeMagicRule::Type magicType = QMimeMagicRule::type(type.toLatin1()); - if (magicType == QMimeMagicRule::Invalid) { - qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData()); - return true; - } const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString(); - if (value.isEmpty()) { - *errorMessage = QString::fromLatin1("Empty match value detected."); - return false; - } - // Parse for offset as "1" or "1:10" - int startPos, endPos; - const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); - const int colonIndex = offsetS.indexOf(QLatin1Char(':')); - const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex); - const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1); - if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage)) - return false; + const QString offsets = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); const QString mask = atts.value(QLatin1String(matchMaskAttributeC)).toString(); - - rule = new QMimeMagicRule(magicType, value.toUtf8(), startPos, endPos, mask.toLatin1()); - - return true; + return new QMimeMagicRule(type, value.toUtf8(), offsets, mask.toLatin1(), errorMessage); } #endif @@ -283,9 +260,10 @@ bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString } break; case ParseMagicMatchRule: { - QMimeMagicRule *rule = 0; - if (!createMagicMatchRule(atts, errorMessage, rule)) - return false; + QString magicErrorMessage; + QMimeMagicRule *rule = createMagicMatchRule(atts, &magicErrorMessage); + if (!rule->isValid()) + qWarning("QMimeDatabase: Error parsing %s\n%s", qPrintable(fileName), qPrintable(magicErrorMessage)); QList<QMimeMagicRule> *ruleList; if (currentRules.isEmpty()) ruleList = &rules; diff --git a/src/corelib/mimetypes/qmimetypeparser_p.h b/src/corelib/mimetypes/qmimetypeparser_p.h index 2be4380cee..3a2e6b8a14 100644 --- a/src/corelib/mimetypes/qmimetypeparser_p.h +++ b/src/corelib/mimetypes/qmimetypeparser_p.h @@ -66,6 +66,8 @@ public: bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); + static bool parseNumber(const QString &n, int *target, QString *errorMessage); + protected: virtual bool process(const QMimeType &t, QString *errorMessage) = 0; virtual bool process(const QMimeGlobPattern &t, QString *errorMessage) = 0; diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml new file mode 100644 index 0000000000..04204d8763 --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/invalid-magic1"> + <comment>wrong value for byte type</comment> + <magic> + <match value="foo" type="byte" offset="0"/> + </magic> + </mime-type> +</mime-info> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml new file mode 100644 index 0000000000..f18075482e --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/invalid-magic2"> + <comment>mask doesn't start with 0x</comment> + <magic> + <match value="foo" type="string" mask="ffff" offset="0"/> + </magic> + </mime-type> +</mime-info> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml new file mode 100644 index 0000000000..0e2508cc0f --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/invalid-magic3"> + <comment>mask has wrong size</comment> + <magic> + <match value="foo" type="string" mask="0xffff" offset="0"/> + </magic> + </mime-type> +</mime-info> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc b/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc index 4654a61660..29666627a1 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc +++ b/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc @@ -4,5 +4,8 @@ <file alias="qml-again.xml">qml-again.xml</file> <file alias="text-x-objcsrc.xml">text-x-objcsrc.xml</file> <file alias="test.qml">test.qml</file> + <file>invalid-magic1.xml</file> + <file>invalid-magic2.xml</file> + <file>invalid-magic3.xml</file> </qresource> </RCC> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 8bc90d917e..763bb58602 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -45,9 +45,16 @@ #include <QtTest/QtTest> -static const char yastFileName[] ="yast2-metapackage-handler-mimetypes.xml"; -static const char qmlAgainFileName[] ="qml-again.xml"; -static const char textXObjCSrcFileName[] ="text-x-objcsrc.xml"; +static const char *const additionalMimeFiles[] = { + "yast2-metapackage-handler-mimetypes.xml", + "qml-again.xml", + "text-x-objcsrc.xml", + "invalid-magic1.xml", + "invalid-magic2.xml", + "invalid-magic3.xml", + 0 +}; + #define RESOURCE_PREFIX ":/qt-project.org/qmime/" void initializeLang() @@ -149,12 +156,12 @@ void tst_QMimeDatabase::initTestCase() qWarning("%s", qPrintable(testSuiteWarning())); errorMessage = QString::fromLatin1("Cannot find '%1'"); - m_yastMimeTypes = QLatin1String(RESOURCE_PREFIX) + yastFileName; - QVERIFY2(QFile::exists(m_yastMimeTypes), qPrintable(errorMessage.arg(yastFileName))); - m_qmlAgainFileName = QLatin1String(RESOURCE_PREFIX) + qmlAgainFileName; - QVERIFY2(QFile::exists(m_qmlAgainFileName), qPrintable(errorMessage.arg(qmlAgainFileName))); - m_textXObjCSrcFileName = QLatin1String(RESOURCE_PREFIX) + textXObjCSrcFileName; - QVERIFY2(QFile::exists(m_textXObjCSrcFileName), qPrintable(errorMessage.arg(textXObjCSrcFileName))); + for (uint i = 0; i < sizeof additionalMimeFiles / sizeof additionalMimeFiles[0] - 1; i++) { + const QString resourceFilePath = QString::fromLatin1(RESOURCE_PREFIX) + QLatin1String(additionalMimeFiles[i]); + QVERIFY2(QFile::exists(resourceFilePath), qPrintable(errorMessage.arg(resourceFilePath))); + m_additionalMimeFileNames.append(QLatin1String(additionalMimeFiles[i])); + m_additionalMimeFilePaths.append(resourceFilePath); + } initTestCaseInternal(); m_isUsingCacheProvider = !qEnvironmentVariableIsSet("QT_NO_MIME_CACHE"); @@ -859,6 +866,14 @@ static void checkHasMimeType(const QString &mimeType) QVERIFY(found); } +static void ignoreInvalidMimetypeWarnings(const QString &mimeDir) +{ + const QByteArray basePath = QFile::encodeName(mimeDir) + "/packages/"; + QTest::ignoreMessage(QtWarningMsg, ("QMimeDatabase: Error parsing " + basePath + "invalid-magic1.xml\nInvalid magic rule value \"foo\"").constData()); + QTest::ignoreMessage(QtWarningMsg, ("QMimeDatabase: Error parsing " + basePath + "invalid-magic2.xml\nInvalid magic rule mask \"ffff\"").constData()); + QTest::ignoreMessage(QtWarningMsg, ("QMimeDatabase: Error parsing " + basePath + "invalid-magic3.xml\nInvalid magic rule mask size \"0xffff\"").constData()); +} + QT_BEGIN_NAMESPACE extern Q_CORE_EXPORT int qmime_secondsBetweenChecks; // see qmimeprovider.cpp QT_END_NAMESPACE @@ -879,23 +894,21 @@ void tst_QMimeDatabase::installNewGlobalMimeType() const QString mimeDir = m_globalXdgDir + QLatin1String("/mime"); const QString destDir = mimeDir + QLatin1String("/packages/"); - const QString destFile = destDir + QLatin1String(yastFileName); - QFile::remove(destFile); - const QString destQmlFile = destDir + QLatin1String(qmlAgainFileName); - QFile::remove(destQmlFile); - const QString destTextXObjCSrcFile = destDir + QLatin1String(textXObjCSrcFileName); - QFile::remove(destTextXObjCSrcFile); - //qDebug() << destFile; - if (!QFileInfo(destDir).isDir()) QVERIFY(QDir(m_globalXdgDir).mkpath(destDir)); + QString errorMessage; - QVERIFY2(copyResourceFile(m_yastMimeTypes, destFile, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(copyResourceFile(m_qmlAgainFileName, destQmlFile, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(copyResourceFile(m_textXObjCSrcFileName, destTextXObjCSrcFile, &errorMessage), qPrintable(errorMessage)); + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) { + const QString destFile = destDir + m_additionalMimeFileNames.at(i); + QFile::remove(destFile); + QVERIFY2(copyResourceFile(m_additionalMimeFilePaths.at(i), destFile, &errorMessage), qPrintable(errorMessage)); + } if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(mimeDir)) QSKIP("shared-mime-info not found, skipping mime.cache test"); + if (!m_isUsingCacheProvider) + ignoreInvalidMimetypeWarnings(mimeDir); + QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), QString::fromLatin1("text/x-SuSE-ymu")); QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); @@ -916,10 +929,9 @@ void tst_QMimeDatabase::installNewGlobalMimeType() qDebug() << objcsrc.globPatterns(); } - // Now test removing it again - QVERIFY(QFile::remove(destFile)); - QVERIFY(QFile::remove(destQmlFile)); - QVERIFY(QFile::remove(destTextXObjCSrcFile)); + // Now test removing the mimetype definitions again + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) + QFile::remove(destDir + m_additionalMimeFileNames.at(i)); if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(mimeDir)) QSKIP("shared-mime-info not found, skipping mime.cache test"); QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), @@ -936,23 +948,35 @@ void tst_QMimeDatabase::installNewLocalMimeType() qmime_secondsBetweenChecks = 0; QMimeDatabase db; + + // Check that we're starting clean QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); + QVERIFY(!db.mimeTypeForName(QLatin1String("text/invalid-magic1")).isValid()); const QString destDir = m_localMimeDir + QLatin1String("/packages/"); - QDir().mkpath(destDir); - const QString destFile = destDir + QLatin1String(yastFileName); - QFile::remove(destFile); - const QString destQmlFile = destDir + QLatin1String(qmlAgainFileName); - QFile::remove(destQmlFile); + QVERIFY(QDir().mkpath(destDir)); QString errorMessage; - QVERIFY2(copyResourceFile(m_yastMimeTypes, destFile, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(copyResourceFile(m_qmlAgainFileName, destQmlFile, &errorMessage), qPrintable(errorMessage)); - if (m_isUsingCacheProvider && !runUpdateMimeDatabase(m_localMimeDir)) { + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) { + const QString destFile = destDir + m_additionalMimeFileNames.at(i); + QFile::remove(destFile); + QVERIFY2(copyResourceFile(m_additionalMimeFilePaths.at(i), destFile, &errorMessage), qPrintable(errorMessage)); + } + if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(m_localMimeDir)) { const QString skipWarning = QStringLiteral("shared-mime-info not found, skipping mime.cache test (") + QDir::toNativeSeparators(m_localMimeDir) + QLatin1Char(')'); QSKIP(qPrintable(skipWarning)); } + if (!m_isUsingCacheProvider) + ignoreInvalidMimetypeWarnings(m_localMimeDir); + + QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); + + // These mimetypes have invalid magic, but still do exist. + QVERIFY(db.mimeTypeForName(QLatin1String("text/invalid-magic1")).isValid()); + QVERIFY(db.mimeTypeForName(QLatin1String("text/invalid-magic2")).isValid()); + QVERIFY(db.mimeTypeForName(QLatin1String("text/invalid-magic3")).isValid()); + QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), QString::fromLatin1("text/x-SuSE-ymu")); QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); @@ -968,8 +992,8 @@ void tst_QMimeDatabase::installNewLocalMimeType() QString::fromLatin1("text/x-qml")); // Now test removing the local mimetypes again (note, this leaves a mostly-empty mime.cache file) - QVERIFY(QFile::remove(destFile)); - QVERIFY(QFile::remove(destQmlFile)); + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) + QFile::remove(destDir + m_additionalMimeFileNames.at(i)); if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(m_localMimeDir)) QSKIP("shared-mime-info not found, skipping mime.cache test"); QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h index f12beafe86..2827bd2dc4 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h @@ -36,6 +36,7 @@ #include <QtCore/QObject> #include <QtCore/QTemporaryDir> +#include <QtCore/QStringList> class tst_QMimeDatabase : public QObject { @@ -92,9 +93,8 @@ private: QString m_globalXdgDir; QString m_localMimeDir; - QString m_yastMimeTypes; - QString m_qmlAgainFileName; - QString m_textXObjCSrcFileName; + QStringList m_additionalMimeFileNames; + QStringList m_additionalMimeFilePaths; QTemporaryDir m_temporaryDir; QString m_testSuite; bool m_isUsingCacheProvider; |