1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <qplatformdefs.h> // always first
42
43#include "qmimedatabase.h"
44#include "qmimedatabase_p.h"
45
46#include "qmimeprovider_p.h"
47#include "qmimetype_p.h"
48
49#include <QtCore/QFile>
50#include <QtCore/QFileInfo>
51#include <QtCore/QSet>
52#include <QtCore/QStandardPaths>
53#include <QtCore/QBuffer>
54#include <QtCore/QUrl>
55#include <QtCore/QDebug>
56
57#include <algorithm>
58#include <functional>
59#include <stack>
60
61QT_BEGIN_NAMESPACE
62
63Q_GLOBAL_STATIC(QMimeDatabasePrivate, staticQMimeDatabase)
64
65QMimeDatabasePrivate *QMimeDatabasePrivate::instance()
66{
67 return staticQMimeDatabase();
68}
69
70QMimeDatabasePrivate::QMimeDatabasePrivate()
71 : m_defaultMimeType(QLatin1String("application/octet-stream"))
72{
73}
74
75QMimeDatabasePrivate::~QMimeDatabasePrivate()
76{
77}
78
79#ifdef QT_BUILD_INTERNAL
80Q_CORE_EXPORT
81#else
82static const
83#endif
84int qmime_secondsBetweenChecks = 5;
85
86bool QMimeDatabasePrivate::shouldCheck()
87{
88 if (m_lastCheck.isValid() && m_lastCheck.elapsed() < qmime_secondsBetweenChecks * 1000)
89 return false;
90 m_lastCheck.start();
91 return true;
92}
93
94#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
95#define QT_USE_MMAP
96#endif
97
98void QMimeDatabasePrivate::loadProviders()
99{
100 // We use QStandardPaths every time to check if new files appeared
101 QStringList mimeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory);
102 const auto fdoIterator = std::find_if(mimeDirs.constBegin(), mimeDirs.constEnd(), [](const QString &mimeDir) -> bool {
103 return QFileInfo::exists(mimeDir + QStringLiteral("/packages/freedesktop.org.xml")); }
104 );
105 if (fdoIterator == mimeDirs.constEnd())
106 mimeDirs.prepend(QLatin1String(":/qt-project.org/qmime"));
107 //qDebug() << "mime dirs:" << mimeDirs;
108
109 Providers currentProviders;
110 std::swap(m_providers, currentProviders);
111 m_providers.reserve(mimeDirs.size());
112 for (const QString &mimeDir : qAsConst(mimeDirs)) {
113 const QString cacheFile = mimeDir + QStringLiteral("/mime.cache");
114 QFileInfo fileInfo(cacheFile);
115 // Check if we already have a provider for this dir
116 const auto predicate = [mimeDir](const std::unique_ptr<QMimeProviderBase> &prov)
117 {
118 return prov && prov->directory() == mimeDir;
119 };
120 const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), predicate);
121 if (it == currentProviders.end()) {
122 std::unique_ptr<QMimeProviderBase> provider;
123#if defined(QT_USE_MMAP)
124 if (qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE") && fileInfo.exists()) {
125 provider.reset(new QMimeBinaryProvider(this, mimeDir));
126 //qDebug() << "Created binary provider for" << mimeDir;
127 if (!provider->isValid()) {
128 provider.reset();
129 }
130 }
131#endif
132 if (!provider) {
133 provider.reset(new QMimeXMLProvider(this, mimeDir));
134 //qDebug() << "Created XML provider for" << mimeDir;
135 }
136 m_providers.push_back(std::move(provider));
137 } else {
138 auto provider = std::move(*it); // take provider out of the vector
139 provider->ensureLoaded();
140 if (!provider->isValid()) {
141 provider.reset(new QMimeXMLProvider(this, mimeDir));
142 //qDebug() << "Created XML provider to replace binary provider for" << mimeDir;
143 }
144 m_providers.push_back(std::move(provider));
145 }
146 }
147}
148
149const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
150{
151#ifndef Q_OS_WASM // stub implementation always returns true
152 Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex
153#endif
154 if (m_providers.empty()) {
155 loadProviders();
156 m_lastCheck.start();
157 } else {
158 if (shouldCheck())
159 loadProviders();
160 }
161 return m_providers;
162}
163
164QString QMimeDatabasePrivate::resolveAlias(const QString &nameOrAlias)
165{
166 for (const auto &provider : providers()) {
167 const QString ret = provider->resolveAlias(nameOrAlias);
168 if (!ret.isEmpty())
169 return ret;
170 }
171 return nameOrAlias;
172}
173
174/*!
175 \internal
176 Returns a MIME type or an invalid one if none found
177 */
178QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias)
179{
180 const QString mimeName = resolveAlias(nameOrAlias);
181 for (const auto &provider : providers()) {
182 const QMimeType mime = provider->mimeTypeForName(mimeName);
183 if (mime.isValid())
184 return mime;
185 }
186 return {};
187}
188
189QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName)
190{
191 if (fileName.endsWith(QLatin1Char('/')))
192 return QStringList() << QLatin1String("inode/directory");
193
194 const QString shortName = QFileInfo(fileName).fileName();
195 const QMimeGlobMatchResult result = findByFileName(shortName);
196 QStringList matchingMimeTypes = result.m_matchingMimeTypes;
197 matchingMimeTypes.sort(); // make it deterministic
198 return matchingMimeTypes;
199}
200
201QMimeGlobMatchResult QMimeDatabasePrivate::findByFileName(const QString &fileName)
202{
203 QMimeGlobMatchResult result;
204 // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly.
205 for (const auto &provider : providers())
206 provider->addFileNameMatches(fileName, result);
207 return result;
208}
209
210void QMimeDatabasePrivate::loadMimeTypePrivate(QMimeTypePrivate &mimePrivate)
211{
212 QMutexLocker locker(&mutex);
213 if (mimePrivate.name.isEmpty())
214 return; // invalid mimetype
215 if (!mimePrivate.loaded) { // XML provider sets loaded=true, binary provider does this on demand
216 Q_ASSERT(mimePrivate.fromCache);
217 QMimeBinaryProvider::loadMimeTypePrivate(mimePrivate);
218 }
219}
220
221void QMimeDatabasePrivate::loadGenericIcon(QMimeTypePrivate &mimePrivate)
222{
223 QMutexLocker locker(&mutex);
224 if (mimePrivate.fromCache) {
225 mimePrivate.genericIconName.clear();
226 for (const auto &provider : providers()) {
227 provider->loadGenericIcon(mimePrivate);
228 if (!mimePrivate.genericIconName.isEmpty())
229 break;
230 }
231 }
232}
233
234void QMimeDatabasePrivate::loadIcon(QMimeTypePrivate &mimePrivate)
235{
236 QMutexLocker locker(&mutex);
237 if (mimePrivate.fromCache) {
238 mimePrivate.iconName.clear();
239 for (const auto &provider : providers()) {
240 provider->loadIcon(mimePrivate);
241 if (!mimePrivate.iconName.isEmpty())
242 break;
243 }
244 }
245}
246
247static QString fallbackParent(const QString &mimeTypeName)
248{
249 const QStringRef myGroup = mimeTypeName.leftRef(mimeTypeName.indexOf(QLatin1Char('/')));
250 // All text/* types are subclasses of text/plain.
251 if (myGroup == QLatin1String("text") && mimeTypeName != QLatin1String("text/plain"))
252 return QLatin1String("text/plain");
253 // All real-file mimetypes implicitly derive from application/octet-stream
254 if (myGroup != QLatin1String("inode") &&
255 // ignore non-file extensions
256 myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri")
257 && mimeTypeName != QLatin1String("application/octet-stream")) {
258 return QLatin1String("application/octet-stream");
259 }
260 return QString();
261}
262
263QStringList QMimeDatabasePrivate::mimeParents(const QString &mimeName)
264{
265 QMutexLocker locker(&mutex);
266 return parents(mimeName);
267}
268
269QStringList QMimeDatabasePrivate::parents(const QString &mimeName)
270{
271 Q_ASSERT(!mutex.tryLock());
272 QStringList result;
273 for (const auto &provider : providers())
274 provider->addParents(mimeName, result);
275 if (result.isEmpty()) {
276 const QString parent = fallbackParent(mimeName);
277 if (!parent.isEmpty())
278 result.append(parent);
279 }
280 return result;
281}
282
283QStringList QMimeDatabasePrivate::listAliases(const QString &mimeName)
284{
285 QMutexLocker locker(&mutex);
286 QStringList result;
287 for (const auto &provider : providers())
288 provider->addAliases(mimeName, result);
289 return result;
290}
291
292bool QMimeDatabasePrivate::mimeInherits(const QString &mime, const QString &parent)
293{
294 QMutexLocker locker(&mutex);
295 return inherits(mime, parent);
296}
297
298static inline bool isTextFile(const QByteArray &data)
299{
300 // UTF16 byte order marks
301 static const char bigEndianBOM[] = "\xFE\xFF";
302 static const char littleEndianBOM[] = "\xFF\xFE";
303 if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM))
304 return true;
305
306 // Check the first 128 bytes (see shared-mime spec)
307 const char *p = data.constData();
308 const char *e = p + qMin(128, data.size());
309 for ( ; p < e; ++p) {
310 if ((unsigned char)(*p) < 32 && *p != 9 && *p !=10 && *p != 13)
311 return false;
312 }
313
314 return true;
315}
316
317QMimeType QMimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPtr)
318{
319 if (data.isEmpty()) {
320 *accuracyPtr = 100;
321 return mimeTypeForName(QLatin1String("application/x-zerosize"));
322 }
323
324 *accuracyPtr = 0;
325 QMimeType candidate;
326 for (const auto &provider : providers())
327 provider->findByMagic(data, accuracyPtr, candidate);
328
329 if (candidate.isValid())
330 return candidate;
331
332 if (isTextFile(data)) {
333 *accuracyPtr = 5;
334 return mimeTypeForName(QLatin1String("text/plain"));
335 }
336
337 return mimeTypeForName(defaultMimeType());
338}
339
340QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *accuracyPtr)
341{
342 // First, glob patterns are evaluated. If there is a match with max weight,
343 // this one is selected and we are done. Otherwise, the file contents are
344 // evaluated and the match with the highest value (either a magic priority or
345 // a glob pattern weight) is selected. Matching starts from max level (most
346 // specific) in both cases, even when there is already a suffix matching candidate.
347 *accuracyPtr = 0;
348
349 // Pass 1) Try to match on the file name
350 QMimeGlobMatchResult candidatesByName;
351 if (fileName.endsWith(QLatin1Char('/')))
352 candidatesByName.addMatch(QLatin1String("inode/directory"), 100, QString());
353 else
354 candidatesByName = findByFileName(QFileInfo(fileName).fileName());
355 if (candidatesByName.m_allMatchingMimeTypes.count() == 1) {
356 *accuracyPtr = 100;
357 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
358 if (mime.isValid())
359 return mime;
360 candidatesByName = {};
361 }
362
363 // Extension is unknown, or matches multiple mimetypes.
364 // Pass 2) Match on content, if we can read the data
365 if (device->isOpen()) {
366
367 // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
368 // This is much faster than seeking back and forth into QIODevice.
369 const QByteArray data = device->peek(16384);
370
371 int magicAccuracy = 0;
372 QMimeType candidateByData(findByData(data, &magicAccuracy));
373
374 // Disambiguate conflicting extensions (if magic matching found something)
375 if (candidateByData.isValid() && magicAccuracy > 0) {
376 const QString sniffedMime = candidateByData.name();
377 // If the sniffedMime matches a glob match, use it
378 if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) {
379 *accuracyPtr = 100;
380 return candidateByData;
381 }
382 for (const QString &m : qAsConst(candidatesByName.m_matchingMimeTypes)) {
383 if (inherits(m, sniffedMime)) {
384 // We have magic + pattern pointing to this, so it's a pretty good match
385 *accuracyPtr = 100;
386 return mimeTypeForName(m);
387 }
388 }
389 *accuracyPtr = magicAccuracy;
390 return candidateByData;
391 }
392 }
393
394 if (candidatesByName.m_allMatchingMimeTypes.count() > 1) {
395 candidatesByName.m_matchingMimeTypes.sort(); // make it deterministic
396 *accuracyPtr = 20;
397 const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
398 if (mime.isValid())
399 return mime;
400 }
401
402 return mimeTypeForName(defaultMimeType());
403}
404
405QList<QMimeType> QMimeDatabasePrivate::allMimeTypes()
406{
407 QList<QMimeType> result;
408 for (const auto &provider : providers())
409 provider->addAllMimeTypes(result);
410 return result;
411}
412
413bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent)
414{
415 const QString resolvedParent = resolveAlias(parent);
416 std::stack<QString, QStringList> toCheck;
417 toCheck.push(mime);
418 while (!toCheck.empty()) {
419 if (toCheck.top() == resolvedParent)
420 return true;
421 const QString mimeName = toCheck.top();
422 toCheck.pop();
423 const auto parentList = parents(mimeName);
424 for (const QString &par : parentList)
425 toCheck.push(resolveAlias(par));
426 }
427 return false;
428}
429
430/*!
431 \class QMimeDatabase
432 \inmodule QtCore
433 \brief The QMimeDatabase class maintains a database of MIME types.
434
435 \since 5.0
436
437 The MIME type database is provided by the freedesktop.org shared-mime-info
438 project. If the MIME type database cannot be found on the system, as is the case
439 on most Windows, \macos, and iOS systems, Qt will use its own copy of it.
440
441 Applications which want to define custom MIME types need to install an
442 XML file into the locations searched for MIME definitions.
443 These locations can be queried with
444 \snippet code/src_corelib_mimetype_qmimedatabase.cpp 1
445 On a typical Unix system, this will be /usr/share/mime/packages/, but it is also
446 possible to extend the list of directories by setting the environment variable
447 \c XDG_DATA_DIRS. For instance adding /opt/myapp/share to \c XDG_DATA_DIRS will result
448 in /opt/myapp/share/mime/packages/ being searched for MIME definitions.
449
450 Here is an example of MIME XML:
451 \snippet code/src_corelib_mimetype_qmimedatabase.cpp 2
452
453 For more details about the syntax of XML MIME definitions, including defining
454 "magic" in order to detect MIME types based on data as well, read the
455 Shared Mime Info specification at
456 http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
457
458 On Unix systems, a binary cache is used for more performance. This cache is generated
459 by the command "update-mime-database path", where path would be /opt/myapp/share/mime
460 in the above example. Make sure to run this command when installing the MIME type
461 definition file.
462
463 \threadsafe
464
465 \snippet code/src_corelib_mimetype_qmimedatabase.cpp 0
466
467 \sa QMimeType, {MIME Type Browser Example}
468 */
469
470/*!
471 \fn QMimeDatabase::QMimeDatabase();
472 Constructs a QMimeDatabase object.
473
474 It is perfectly OK to create an instance of QMimeDatabase every time you need to
475 perform a lookup.
476 The parsing of mimetypes is done on demand (when shared-mime-info is installed)
477 or when the very first instance is constructed (when parsing XML files directly).
478 */
479QMimeDatabase::QMimeDatabase() :
480 d(staticQMimeDatabase())
481{
482}
483
484/*!
485 \fn QMimeDatabase::~QMimeDatabase();
486 Destroys the QMimeDatabase object.
487 */
488QMimeDatabase::~QMimeDatabase()
489{
490 d = 0;
491}
492
493/*!
494 \fn QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const;
495 Returns a MIME type for \a nameOrAlias or an invalid one if none found.
496 */
497QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const
498{
499 QMutexLocker locker(&d->mutex);
500
501 return d->mimeTypeForName(nameOrAlias);
502}
503
504/*!
505 Returns a MIME type for \a fileInfo.
506
507 A valid MIME type is always returned.
508
509 The default matching algorithm looks at both the file name and the file
510 contents, if necessary. The file extension has priority over the contents,
511 but the contents will be used if the file extension is unknown, or
512 matches multiple MIME types.
513 If \a fileInfo is a Unix symbolic link, the file that it refers to
514 will be used instead.
515 If the file doesn't match any known pattern or data, the default MIME type
516 (application/octet-stream) is returned.
517
518 When \a mode is set to MatchExtension, only the file name is used, not
519 the file contents. The file doesn't even have to exist. If the file name
520 doesn't match any known pattern, the default MIME type (application/octet-stream)
521 is returned.
522 If multiple MIME types match this file, the first one (alphabetically) is returned.
523
524 When \a mode is set to MatchContent, and the file is readable, only the
525 file contents are used to determine the MIME type. This is equivalent to
526 calling mimeTypeForData with a QFile as input device.
527
528 \a fileInfo may refer to an absolute or relative path.
529
530 \sa QMimeType::isDefault(), mimeTypeForData()
531*/
532QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
533{
534 QMutexLocker locker(&d->mutex);
535
536 if (fileInfo.isDir())
537 return d->mimeTypeForName(QLatin1String("inode/directory"));
538
539 QFile file(fileInfo.absoluteFilePath());
540
541#ifdef Q_OS_UNIX
542 // Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again.
543 // In addition we want to follow symlinks.
544 const QByteArray nativeFilePath = QFile::encodeName(file.fileName());
545 QT_STATBUF statBuffer;
546 if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) {
547 if (S_ISCHR(statBuffer.st_mode))
548 return d->mimeTypeForName(QLatin1String("inode/chardevice"));
549 if (S_ISBLK(statBuffer.st_mode))
550 return d->mimeTypeForName(QLatin1String("inode/blockdevice"));
551 if (S_ISFIFO(statBuffer.st_mode))
552 return d->mimeTypeForName(QLatin1String("inode/fifo"));
553 if (S_ISSOCK(statBuffer.st_mode))
554 return d->mimeTypeForName(QLatin1String("inode/socket"));
555 }
556#endif
557
558 int priority = 0;
559 switch (mode) {
560 case MatchDefault:
561 file.open(QIODevice::ReadOnly); // isOpen() will be tested by method below
562 return d->mimeTypeForFileNameAndData(fileInfo.absoluteFilePath(), &file, &priority);
563 case MatchExtension:
564 locker.unlock();
565 return mimeTypeForFile(fileInfo.absoluteFilePath(), mode);
566 case MatchContent:
567 if (file.open(QIODevice::ReadOnly)) {
568 locker.unlock();
569 return mimeTypeForData(&file);
570 } else {
571 return d->mimeTypeForName(d->defaultMimeType());
572 }
573 default:
574 Q_ASSERT(false);
575 }
576 return d->mimeTypeForName(d->defaultMimeType());
577}
578
579/*!
580 Returns a MIME type for the file named \a fileName using \a mode.
581
582 \overload
583*/
584QMimeType QMimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const
585{
586 if (mode == MatchExtension) {
587 QMutexLocker locker(&d->mutex);
588 const QStringList matches = d->mimeTypeForFileName(fileName);
589 const int matchCount = matches.count();
590 if (matchCount == 0) {
591 return d->mimeTypeForName(d->defaultMimeType());
592 } else if (matchCount == 1) {
593 return d->mimeTypeForName(matches.first());
594 } else {
595 // We have to pick one.
596 return d->mimeTypeForName(matches.first());
597 }
598 } else {
599 // Implemented as a wrapper around mimeTypeForFile(QFileInfo), so no mutex.
600 QFileInfo fileInfo(fileName);
601 return mimeTypeForFile(fileInfo, mode);
602 }
603}
604
605/*!
606 Returns the MIME types for the file name \a fileName.
607
608 If the file name doesn't match any known pattern, an empty list is returned.
609 If multiple MIME types match this file, they are all returned.
610
611 This function does not try to open the file. To also use the content
612 when determining the MIME type, use mimeTypeForFile() or
613 mimeTypeForFileNameAndData() instead.
614
615 \sa mimeTypeForFile()
616*/
617QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) const
618{
619 QMutexLocker locker(&d->mutex);
620
621 const QStringList matches = d->mimeTypeForFileName(fileName);
622 QList<QMimeType> mimes;
623 mimes.reserve(matches.count());
624 for (const QString &mime : matches)
625 mimes.append(d->mimeTypeForName(mime));
626 return mimes;
627}
628/*!
629 Returns the suffix for the file \a fileName, as known by the MIME database.
630
631 This allows to pre-select "tar.bz2" for foo.tar.bz2, but still only
632 "txt" for my.file.with.dots.txt.
633*/
634QString QMimeDatabase::suffixForFileName(const QString &fileName) const
635{
636 QMutexLocker locker(&d->mutex);
637 return d->findByFileName(QFileInfo(fileName).fileName()).m_foundSuffix;
638}
639
640/*!
641 Returns a MIME type for \a data.
642
643 A valid MIME type is always returned. If \a data doesn't match any
644 known MIME type data, the default MIME type (application/octet-stream)
645 is returned.
646*/
647QMimeType QMimeDatabase::mimeTypeForData(const QByteArray &data) const
648{
649 QMutexLocker locker(&d->mutex);
650
651 int accuracy = 0;
652 return d->findByData(data, &accuracy);
653}
654
655/*!
656 Returns a MIME type for the data in \a device.
657
658 A valid MIME type is always returned. If the data in \a device doesn't match any
659 known MIME type data, the default MIME type (application/octet-stream)
660 is returned.
661*/
662QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device) const
663{
664 QMutexLocker locker(&d->mutex);
665
666 int accuracy = 0;
667 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
668 if (device->isOpen()) {
669 // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
670 // This is much faster than seeking back and forth into QIODevice.
671 const QByteArray data = device->peek(16384);
672 const QMimeType result = d->findByData(data, &accuracy);
673 if (openedByUs)
674 device->close();
675 return result;
676 }
677 return d->mimeTypeForName(d->defaultMimeType());
678}
679
680/*!
681 Returns a MIME type for \a url.
682
683 If the URL is a local file, this calls mimeTypeForFile.
684
685 Otherwise the matching is done based on the file name only,
686 except for schemes where file names don't mean much, like HTTP.
687 This method always returns the default mimetype for HTTP URLs,
688 use QNetworkAccessManager to handle HTTP URLs properly.
689
690 A valid MIME type is always returned. If \a url doesn't match any
691 known MIME type data, the default MIME type (application/octet-stream)
692 is returned.
693*/
694QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const
695{
696 if (url.isLocalFile())
697 return mimeTypeForFile(url.toLocalFile());
698
699 const QString scheme = url.scheme();
700 if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto"))
701 return mimeTypeForName(d->defaultMimeType());
702
703 return mimeTypeForFile(url.path(), MatchExtension);
704}
705
706/*!
707 Returns a MIME type for the given \a fileName and \a device data.
708
709 This overload can be useful when the file is remote, and we started to
710 download some of its data in a device. This allows to do full MIME type
711 matching for remote files as well.
712
713 If the device is not open, it will be opened by this function, and closed
714 after the MIME type detection is completed.
715
716 A valid MIME type is always returned. If \a device data doesn't match any
717 known MIME type data, the default MIME type (application/octet-stream)
718 is returned.
719
720 This method looks at both the file name and the file contents,
721 if necessary. The file extension has priority over the contents,
722 but the contents will be used if the file extension is unknown, or
723 matches multiple MIME types.
724*/
725QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const
726{
727 QMutexLocker locker(&d->mutex);
728 int accuracy = 0;
729 const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
730 const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device, &accuracy);
731 if (openedByUs)
732 device->close();
733 return result;
734}
735
736/*!
737 Returns a MIME type for the given \a fileName and device \a data.
738
739 This overload can be useful when the file is remote, and we started to
740 download some of its data. This allows to do full MIME type matching for
741 remote files as well.
742
743 A valid MIME type is always returned. If \a data doesn't match any
744 known MIME type data, the default MIME type (application/octet-stream)
745 is returned.
746
747 This method looks at both the file name and the file contents,
748 if necessary. The file extension has priority over the contents,
749 but the contents will be used if the file extension is unknown, or
750 matches multiple MIME types.
751*/
752QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const
753{
754 QMutexLocker locker(&d->mutex);
755 QBuffer buffer(const_cast<QByteArray *>(&data));
756 buffer.open(QIODevice::ReadOnly);
757 int accuracy = 0;
758 return d->mimeTypeForFileNameAndData(fileName, &buffer, &accuracy);
759}
760
761/*!
762 Returns the list of all available MIME types.
763
764 This can be useful for showing all MIME types to the user, for instance
765 in a MIME type editor. Do not use unless really necessary in other cases
766 though, prefer using the \l {mimeTypeForData()}{mimeTypeForXxx()} methods for performance reasons.
767*/
768QList<QMimeType> QMimeDatabase::allMimeTypes() const
769{
770 QMutexLocker locker(&d->mutex);
771
772 return d->allMimeTypes();
773}
774
775/*!
776 \enum QMimeDatabase::MatchMode
777
778 This enum specifies how matching a file to a MIME type is performed.
779
780 \value MatchDefault Both the file name and content are used to look for a match
781
782 \value MatchExtension Only the file name is used to look for a match
783
784 \value MatchContent The file content is used to look for a match
785*/
786
787QT_END_NAMESPACE
788