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 "qmimetype.h"
42 
43 #include "qmimetype_p.h"
44 #include "qmimedatabase_p.h"
45 #include "qmimeprovider_p.h"
46 
47 #include "qmimeglobpattern_p.h"
48 
49 #include <QtCore/QDebug>
50 #include <QtCore/QLocale>
51 #include <QtCore/QHashFunctions>
52 
53 #include <memory>
54 
55 QT_BEGIN_NAMESPACE
56 
QMimeTypePrivate()57 QMimeTypePrivate::QMimeTypePrivate()
58     : loaded(false), fromCache(false)
59 {}
60 
QMimeTypePrivate(const QMimeType & other)61 QMimeTypePrivate::QMimeTypePrivate(const QMimeType &other)
62       : loaded(other.d->loaded),
63         name(other.d->name),
64         localeComments(other.d->localeComments),
65         genericIconName(other.d->genericIconName),
66         iconName(other.d->iconName),
67         globPatterns(other.d->globPatterns)
68 {}
69 
clear()70 void QMimeTypePrivate::clear()
71 {
72     name.clear();
73     localeComments.clear();
74     genericIconName.clear();
75     iconName.clear();
76     globPatterns.clear();
77 }
78 
addGlobPattern(const QString & pattern)79 void QMimeTypePrivate::addGlobPattern(const QString &pattern)
80 {
81     globPatterns.append(pattern);
82 }
83 
84 /*!
85     \class QMimeType
86     \inmodule QtCore
87     \ingroup shared
88     \brief The QMimeType class describes types of file or data, represented by a MIME type string.
89 
90     \since 5.0
91 
92     For instance a file named "readme.txt" has the MIME type "text/plain".
93     The MIME type can be determined from the file name, or from the file
94     contents, or from both. MIME type determination can also be done on
95     buffers of data not coming from files.
96 
97     Determining the MIME type of a file can be useful to make sure your
98     application supports it. It is also useful in file-manager-like applications
99     or widgets, in order to display an appropriate \l {QMimeType::iconName}{icon} for the file, or even
100     the descriptive \l {QMimeType::comment()}{comment} in detailed views.
101 
102     To check if a file has the expected MIME type, you should use inherits()
103     rather than a simple string comparison based on the name(). This is because
104     MIME types can inherit from each other: for instance a C source file is
105     a specific type of plain text file, so text/x-csrc inherits text/plain.
106 
107     \sa QMimeDatabase, {MIME Type Browser Example}
108  */
109 
110 /*!
111     \fn QMimeType &QMimeType::operator=(QMimeType &&other)
112 
113     Move-assigns \a other to this QMimeType instance.
114 
115     \since 5.2
116 */
117 
118 /*!
119     \fn QMimeType::QMimeType();
120     Constructs this QMimeType object initialized with default property values that indicate an invalid MIME type.
121  */
QMimeType()122 QMimeType::QMimeType() :
123         d(new QMimeTypePrivate())
124 {
125 }
126 
127 /*!
128     \fn QMimeType::QMimeType(const QMimeType &other);
129     Constructs this QMimeType object as a copy of \a other.
130  */
QMimeType(const QMimeType & other)131 QMimeType::QMimeType(const QMimeType &other) :
132         d(other.d)
133 {
134 }
135 
136 /*!
137     \fn QMimeType &QMimeType::operator=(const QMimeType &other);
138     Assigns the data of \a other to this QMimeType object, and returns a reference to this object.
139  */
operator =(const QMimeType & other)140 QMimeType &QMimeType::operator=(const QMimeType &other)
141 {
142     if (d != other.d)
143         d = other.d;
144     return *this;
145 }
146 
147 /*!
148     \fn QMimeType::QMimeType(const QMimeTypePrivate &dd);
149     Assigns the data of the QMimeTypePrivate \a dd to this QMimeType object, and returns a reference to this object.
150     \internal
151  */
QMimeType(const QMimeTypePrivate & dd)152 QMimeType::QMimeType(const QMimeTypePrivate &dd) :
153         d(new QMimeTypePrivate(dd))
154 {
155 }
156 
157 /*!
158     \fn void QMimeType::swap(QMimeType &other);
159     Swaps QMimeType \a other with this QMimeType object.
160 
161     This operation is very fast and never fails.
162 
163     The swap() method helps with the implementation of assignment
164     operators in an exception-safe way. For more information consult
165     \l {http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap}
166     {More C++ Idioms - Copy-and-swap}.
167  */
168 
169 /*!
170     \fn QMimeType::~QMimeType();
171     Destroys the QMimeType object, and releases the d pointer.
172  */
~QMimeType()173 QMimeType::~QMimeType()
174 {
175 }
176 
177 /*!
178     \fn bool QMimeType::operator==(const QMimeType &other) const;
179     Returns \c true if \a other equals this QMimeType object, otherwise returns \c false.
180     The name is the unique identifier for a mimetype, so two mimetypes with
181     the same name, are equal.
182  */
operator ==(const QMimeType & other) const183 bool QMimeType::operator==(const QMimeType &other) const
184 {
185     return d == other.d || d->name == other.d->name;
186 }
187 
188 /*!
189     \since 5.6
190     \relates QMimeType
191 
192     Returns the hash value for \a key, using
193     \a seed to seed the calculation.
194  */
qHash(const QMimeType & key,uint seed)195 uint qHash(const QMimeType &key, uint seed) noexcept
196 {
197     return qHash(key.d->name, seed);
198 }
199 
200 /*!
201     \fn bool QMimeType::operator!=(const QMimeType &other) const;
202     Returns \c true if \a other does not equal this QMimeType object, otherwise returns \c false.
203  */
204 
205 /*!
206     \property QMimeType::valid
207     \brief \c true if the QMimeType object contains valid data, \c false otherwise
208 
209     A valid MIME type has a non-empty name().
210     The invalid MIME type is the default-constructed QMimeType.
211 
212     While this property was introduced in 5.10, the
213     corresponding accessor method has always been there.
214  */
isValid() const215 bool QMimeType::isValid() const
216 {
217     return !d->name.isEmpty();
218 }
219 
220 /*!
221     \property QMimeType::isDefault
222     \brief \c true if this MIME type is the default MIME type which
223     applies to all files: application/octet-stream.
224 
225     While this property was introduced in 5.10, the
226     corresponding accessor method has always been there.
227  */
isDefault() const228 bool QMimeType::isDefault() const
229 {
230     return d->name == QMimeDatabasePrivate::instance()->defaultMimeType();
231 }
232 
233 /*!
234     \property QMimeType::name
235     \brief the name of the MIME type
236 
237     While this property was introduced in 5.10, the
238     corresponding accessor method has always been there.
239  */
name() const240 QString QMimeType::name() const
241 {
242     return d->name;
243 }
244 
245 /*!
246     \property QMimeType::comment
247     \brief the description of the MIME type to be displayed on user interfaces
248 
249     The default language (QLocale().name()) is used to select the appropriate translation.
250 
251     While this property was introduced in 5.10, the
252     corresponding accessor method has always been there.
253  */
comment() const254 QString QMimeType::comment() const
255 {
256     QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
257 
258     QStringList languageList;
259     languageList << QLocale().name();
260     languageList << QLocale().uiLanguages();
261     languageList << QLatin1String("default"); // use the default locale if possible.
262     for (const QString &language : qAsConst(languageList)) {
263         const QString lang = language == QLatin1String("C") ? QLatin1String("en_US") : language;
264         const QString comm = d->localeComments.value(lang);
265         if (!comm.isEmpty())
266             return comm;
267         const int pos = lang.indexOf(QLatin1Char('_'));
268         if (pos != -1) {
269             // "pt_BR" not found? try just "pt"
270             const QString shortLang = lang.left(pos);
271             const QString commShort = d->localeComments.value(shortLang);
272             if (!commShort.isEmpty())
273                 return commShort;
274         }
275     }
276 
277     // Use the mimetype name as fallback
278     return d->name;
279 }
280 
281 /*!
282     \property QMimeType::genericIconName
283     \brief the file name of a generic icon that represents the MIME type
284 
285     This should be used if the icon returned by iconName() cannot be found on
286     the system. It is used for categories of similar types (like spreadsheets
287     or archives) that can use a common icon.
288     The freedesktop.org Icon Naming Specification lists a set of such icon names.
289 
290     The icon name can be given to QIcon::fromTheme() in order to load the icon.
291 
292     While this property was introduced in 5.10, the
293     corresponding accessor method has always been there.
294  */
genericIconName() const295 QString QMimeType::genericIconName() const
296 {
297     QMimeDatabasePrivate::instance()->loadGenericIcon(const_cast<QMimeTypePrivate&>(*d));
298     if (d->genericIconName.isEmpty()) {
299         // From the spec:
300         // If the generic icon name is empty (not specified by the mimetype definition)
301         // then the mimetype is used to generate the generic icon by using the top-level
302         // media type (e.g.  "video" in "video/ogg") and appending "-x-generic"
303         // (i.e. "video-x-generic" in the previous example).
304         const QString group = name();
305         QStringRef groupRef(&group);
306         const int slashindex = groupRef.indexOf(QLatin1Char('/'));
307         if (slashindex != -1)
308             groupRef = groupRef.left(slashindex);
309         return groupRef + QLatin1String("-x-generic");
310     }
311     return d->genericIconName;
312 }
313 
make_default_icon_name_from_mimetype_name(QString iconName)314 static QString make_default_icon_name_from_mimetype_name(QString iconName)
315 {
316     const int slashindex = iconName.indexOf(QLatin1Char('/'));
317     if (slashindex != -1)
318         iconName[slashindex] = QLatin1Char('-');
319     return iconName;
320 }
321 
322 /*!
323     \property QMimeType::iconName
324     \brief the file name of an icon image that represents the MIME type
325 
326     The icon name can be given to QIcon::fromTheme() in order to load the icon.
327 
328     While this property was introduced in 5.10, the
329     corresponding accessor method has always been there.
330  */
iconName() const331 QString QMimeType::iconName() const
332 {
333     QMimeDatabasePrivate::instance()->loadIcon(const_cast<QMimeTypePrivate&>(*d));
334     if (d->iconName.isEmpty()) {
335         return make_default_icon_name_from_mimetype_name(name());
336     }
337     return d->iconName;
338 }
339 
340 /*!
341     \property QMimeType::globPatterns
342     \brief the list of glob matching patterns
343 
344     While this property was introduced in 5.10, the
345     corresponding accessor method has always been there.
346  */
globPatterns() const347 QStringList QMimeType::globPatterns() const
348 {
349     QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
350     return d->globPatterns;
351 }
352 
353 /*!
354     \property QMimeType::parentMimeTypes
355     \brief the names of parent MIME types
356 
357     A type is a subclass of another type if any instance of the first type is
358     also an instance of the second. For example, all image/svg+xml files are also
359     text/xml, text/plain and application/octet-stream files. Subclassing is about
360     the format, rather than the category of the data (for example, there is no
361     'generic spreadsheet' class that all spreadsheets inherit from).
362     Conversely, the parent mimetype of image/svg+xml is text/xml.
363 
364     A mimetype can have multiple parents. For instance application/x-perl
365     has two parents: application/x-executable and text/plain. This makes
366     it possible to both execute perl scripts, and to open them in text editors.
367 
368     While this property was introduced in 5.10, the
369     corresponding accessor method has always been there.
370 */
parentMimeTypes() const371 QStringList QMimeType::parentMimeTypes() const
372 {
373     return QMimeDatabasePrivate::instance()->mimeParents(d->name);
374 }
375 
collectParentMimeTypes(const QString & mime,QStringList & allParents)376 static void collectParentMimeTypes(const QString &mime, QStringList &allParents)
377 {
378     const QStringList parents = QMimeDatabasePrivate::instance()->mimeParents(mime);
379     for (const QString &parent : parents) {
380         // I would use QSet, but since order matters I better not
381         if (!allParents.contains(parent))
382             allParents.append(parent);
383     }
384     // We want a breadth-first search, so that the least-specific parent (octet-stream) is last
385     // This means iterating twice, unfortunately.
386     for (const QString &parent : parents)
387         collectParentMimeTypes(parent, allParents);
388 }
389 
390 /*!
391     \property QMimeType::allAncestors
392     \brief the names of direct and indirect parent MIME types
393 
394     Return all the parent mimetypes of this mimetype, direct and indirect.
395     This includes the parent(s) of its parent(s), etc.
396 
397     For instance, for image/svg+xml the list would be:
398     application/xml, text/plain, application/octet-stream.
399 
400     Note that application/octet-stream is the ultimate parent for all types
401     of files (but not directories).
402 
403     While this property was introduced in 5.10, the
404     corresponding accessor method has always been there.
405 */
allAncestors() const406 QStringList QMimeType::allAncestors() const
407 {
408     QStringList allParents;
409     collectParentMimeTypes(d->name, allParents);
410     return allParents;
411 }
412 
413 /*!
414     \property QMimeType::aliases
415     \brief the list of aliases of this mimetype
416 
417     For instance, for text/csv, the returned list would be:
418     text/x-csv, text/x-comma-separated-values.
419 
420     Note that all QMimeType instances refer to proper mimetypes,
421     never to aliases directly.
422 
423     The order of the aliases in the list is undefined.
424 
425     While this property was introduced in 5.10, the
426     corresponding accessor method has always been there.
427 */
aliases() const428 QStringList QMimeType::aliases() const
429 {
430     return QMimeDatabasePrivate::instance()->listAliases(d->name);
431 }
432 
433 /*!
434     \property QMimeType::suffixes
435     \brief the known suffixes for the MIME type
436 
437     No leading dot is included, so for instance this would return "jpg", "jpeg" for image/jpeg.
438 
439     While this property was introduced in 5.10, the
440     corresponding accessor method has always been there.
441  */
suffixes() const442 QStringList QMimeType::suffixes() const
443 {
444     QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
445 
446     QStringList result;
447     for (const QString &pattern : qAsConst(d->globPatterns)) {
448         // Not a simple suffix if it looks like: README or *. or *.* or *.JP*G or *.JP?
449         if (pattern.startsWith(QLatin1String("*.")) &&
450             pattern.length() > 2 &&
451             pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) {
452             const QString suffix = pattern.mid(2);
453             result.append(suffix);
454         }
455     }
456 
457     return result;
458 }
459 
460 /*!
461     \property QMimeType::preferredSuffix
462     \brief the preferred suffix for the MIME type
463 
464     No leading dot is included, so for instance this would return "pdf" for application/pdf.
465     The return value can be empty, for mime types which do not have any suffixes associated.
466 
467     While this property was introduced in 5.10, the
468     corresponding accessor method has always been there.
469  */
preferredSuffix() const470 QString QMimeType::preferredSuffix() const
471 {
472     if (isDefault()) // workaround for unwanted *.bin suffix for octet-stream, https://bugs.freedesktop.org/show_bug.cgi?id=101667, fixed upstream in 1.10
473         return QString();
474     const QStringList suffixList = suffixes();
475     return suffixList.isEmpty() ? QString() : suffixList.at(0);
476 }
477 
478 /*!
479     \property QMimeType::filterString
480     \brief a filter string usable for a file dialog
481 
482     While this property was introduced in 5.10, the
483     corresponding accessor method has always been there.
484 */
filterString() const485 QString QMimeType::filterString() const
486 {
487     QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
488     QString filter;
489 
490     if (!d->globPatterns.empty()) {
491         filter += comment() + QLatin1String(" (");
492         for (int i = 0; i < d->globPatterns.size(); ++i) {
493             if (i != 0)
494                 filter += QLatin1Char(' ');
495             filter += d->globPatterns.at(i);
496         }
497         filter +=  QLatin1Char(')');
498     }
499 
500     return filter;
501 }
502 
503 /*!
504     \fn bool QMimeType::inherits(const QString &mimeTypeName) const;
505     Returns \c true if this mimetype is \a mimeTypeName,
506     or inherits \a mimeTypeName (see parentMimeTypes()),
507     or \a mimeTypeName is an alias for this mimetype.
508 
509     This method has been made invokable from QML since 5.10.
510  */
inherits(const QString & mimeTypeName) const511 bool QMimeType::inherits(const QString &mimeTypeName) const
512 {
513     if (d->name == mimeTypeName)
514         return true;
515     return QMimeDatabasePrivate::instance()->mimeInherits(d->name, mimeTypeName);
516 }
517 
518 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug debug,const QMimeType & mime)519 QDebug operator<<(QDebug debug, const QMimeType &mime)
520 {
521     QDebugStateSaver saver(debug);
522     if (!mime.isValid()) {
523         debug.nospace() << "QMimeType(invalid)";
524     } else {
525         debug.nospace() << "QMimeType(" << mime.name() << ")";
526     }
527     return debug;
528 }
529 #endif
530 
531 QT_END_NAMESPACE
532