File indexing completed on 2024-04-21 14:55:39

0001 /*  This file is part of the KDE libraries
0002  *  Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
0003  *                2000-2007 David Faure <faure@kde.org>
0004  *
0005  *  This library is free software; you can redistribute it and/or
0006  *  modify it under the terms of the GNU Library General Public
0007  *  License version 2 as published by the Free Software Foundation;
0008  *
0009  *  This library is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  *  Library General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU Library General Public License
0015  *  along with this library; see the file COPYING.LIB.  If not, write to
0016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  *  Boston, MA 02110-1301, USA.
0018  **/
0019 
0020 #include "kmimetype.h"
0021 #include "kmimetype_p.h"
0022 #include "kmimetyperepository_p.h"
0023 #include "qmimedatabase.h"
0024 
0025 #include <kdebug.h>
0026 #include <kde_file.h> // KDE::stat
0027 #include <kdeversion.h> // KDE_MAKE_VERSION
0028 #include <klocalizedstring.h>
0029 #include <qstandardpaths.h>
0030 #include <kurl.h>
0031 #include <kio/global.h>
0032 
0033 #include <QFile>
0034 
0035 #ifdef Q_OS_WIN
0036 #include <windows.h>
0037 #endif
0038 
0039 #ifndef S_ISSOCK
0040 #define S_ISSOCK(x) false
0041 #endif
0042 
0043 extern int servicesDebugArea();
0044 
0045 template class QExplicitlySharedDataPointer<KMimeType>;
0046 
0047 KMimeType::Ptr KMimeType::defaultMimeTypePtr()
0048 {
0049     return KMimeTypeRepository::self()->defaultMimeTypePtr();
0050 }
0051 
0052 bool KMimeType::isDefault() const
0053 {
0054     return name() == defaultMimeType();
0055 }
0056 
0057 KMimeType::Ptr KMimeType::mimeType(const QString &name, FindByNameOption options)
0058 {
0059     Q_UNUSED(options);
0060     QMimeDatabase db;
0061     QMimeType mime = db.mimeTypeForName(name);
0062     if (mime.isValid()) {
0063         return KMimeType::Ptr(new KMimeType(mime));
0064     } else {
0065         return KMimeType::Ptr();
0066     }
0067 }
0068 
0069 KMimeType::List KMimeType::allMimeTypes()
0070 {
0071     // This could be done faster...
0072     KMimeType::List lst;
0073     QMimeDatabase db;
0074     Q_FOREACH (const QMimeType &mimeType, db.allMimeTypes()) {
0075         Q_ASSERT(!mimeType.name().startsWith(QLatin1String("x-scheme-handler")));
0076         lst.append(KMimeType::Ptr(new KMimeType(mimeType)));
0077     }
0078     return lst;
0079 }
0080 
0081 // TODO used outside of kmimetype?
0082 bool KMimeType::isBufferBinaryData(const QByteArray &data)
0083 {
0084     // Check the first 32 bytes (see shared-mime spec)
0085     const char *p = data.data();
0086     const int end = qMin(32, data.size());
0087     for (int i = 0; i < end; ++i) {
0088         if ((unsigned char)(p[i]) < 32 && p[i] != 9 && p[i] != 10 && p[i] != 13) { // ASCII control character
0089             return true;
0090         }
0091     }
0092     return false;
0093 }
0094 
0095 // the windows-specific code, and the locked directory, are missing from QMimeDatabase.
0096 static KMimeType::Ptr findFromMode(const QString &path /*only used if is_local_file*/,
0097                                    mode_t mode /*0 if unknown*/,
0098                                    bool is_local_file)
0099 {
0100     if (is_local_file && (mode == 0 || mode == (mode_t) - 1)) {
0101         KDE_struct_stat buff;
0102         if (KDE::stat(path, &buff) != -1) {
0103             mode = buff.st_mode;
0104         }
0105     }
0106 
0107     if (S_ISDIR(mode)) {
0108         // KDE4 TODO: use an overlay instead
0109 #if 0
0110         // Special hack for local files. We want to see whether we
0111         // are allowed to enter the directory
0112         if (is_local_file) {
0113             if (KDE::access(path, R_OK) == -1) {
0114                 return KMimeType::mimeType("inode/directory-locked");
0115             }
0116         }
0117 #endif
0118         return KMimeType::mimeType(QLatin1String("inode/directory"));
0119     }
0120     if (S_ISCHR(mode)) {
0121         return KMimeType::mimeType(QLatin1String("inode/chardevice"));
0122     }
0123     if (S_ISBLK(mode)) {
0124         return KMimeType::mimeType(QLatin1String("inode/blockdevice"));
0125     }
0126     if (S_ISFIFO(mode)) {
0127         return KMimeType::mimeType(QLatin1String("inode/fifo"));
0128     }
0129     if (S_ISSOCK(mode)) {
0130         return KMimeType::mimeType(QLatin1String("inode/socket"));
0131     }
0132 #ifdef Q_OS_WIN
0133     // FIXME: distinguish between mounted & unmounted
0134     int size = path.size();
0135     if (size == 2 || size == 3) {
0136         //GetDriveTypeW is not defined in wince
0137 #ifndef _WIN32_WCE
0138         unsigned int type = GetDriveTypeW((LPCWSTR) path.utf16());
0139         switch (type) {
0140         case DRIVE_REMOVABLE:
0141             return KMimeType::mimeType(QLatin1String("media/floppy_mounted"));
0142         case DRIVE_FIXED:
0143             return KMimeType::mimeType(QLatin1String("media/hdd_mounted"));
0144         case DRIVE_REMOTE:
0145             return KMimeType::mimeType(QLatin1String("media/smb_mounted"));
0146         case DRIVE_CDROM:
0147             return KMimeType::mimeType(QLatin1String("media/cdrom_mounted"));
0148         case DRIVE_RAMDISK:
0149             return KMimeType::mimeType(QLatin1String("media/hdd_mounted"));
0150         default:
0151             break;
0152         };
0153 #else
0154         return KMimeType::mimeType(QLatin1String("media/hdd_mounted"));
0155 #endif
0156     }
0157 #endif
0158     // remote executable file? stop here (otherwise findFromContent can do that better for local files)
0159     if (!is_local_file && S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
0160         return KMimeType::mimeType(QLatin1String("application/x-executable"));
0161     }
0162 
0163     return KMimeType::Ptr();
0164 }
0165 
0166 /*
0167  Note: in KDE we want the file views to sniff in a delayed manner.
0168  So there's also a fast mode which is:
0169   if no glob matches, or if more than one glob matches, use default mimetype and mark as "can be refined".
0170 */
0171 
0172 KMimeType::Ptr KMimeType::findByUrl(const QUrl &url, mode_t mode,
0173                                     bool is_local_file, bool fast_mode,
0174                                     int *accuracy)
0175 {
0176     Q_UNUSED(mode); // special devices only matter locally; caller can use S_ISDIR by itself.
0177     Q_UNUSED(is_local_file); // QUrl can tell us...
0178     QMimeDatabase db;
0179     if (accuracy) {
0180         *accuracy = 80;    // not supported anymore; was it really used for anything?
0181     }
0182     if (fast_mode) {
0183         return KMimeType::Ptr(new KMimeType(db.mimeTypeForFile(url.path(), QMimeDatabase::MatchExtension)));
0184     }
0185     return KMimeType::Ptr(new KMimeType(db.mimeTypeForUrl(url)));
0186 }
0187 
0188 KMimeType::Ptr KMimeType::findByPath(const QString &path, mode_t mode,
0189                                      bool fast_mode, int *accuracy)
0190 {
0191     Q_UNUSED(mode); // special devices only matter locally; caller can use S_ISDIR by itself.
0192     QMimeDatabase db;
0193     if (accuracy) {
0194         *accuracy = 80;    // not supported anymore; was it really used for anything?
0195     }
0196     if (fast_mode) {
0197         return KMimeType::Ptr(new KMimeType(db.mimeTypeForFile(path, QMimeDatabase::MatchExtension)));
0198     }
0199     return KMimeType::Ptr(new KMimeType(db.mimeTypeForFile(path)));
0200 }
0201 
0202 KMimeType::Ptr KMimeType::findByNameAndContent(const QString &name, const QByteArray &data,
0203         mode_t mode, int *accuracy)
0204 {
0205     Q_UNUSED(mode); // If we have data, then this is a regular file anyway...
0206     if (accuracy) {
0207         *accuracy = 80;    // not supported anymore; was it really used for anything?
0208     }
0209     QMimeDatabase db;
0210     return KMimeType::Ptr(new KMimeType(db.mimeTypeForFileNameAndData(name, data)));
0211 }
0212 
0213 KMimeType::Ptr KMimeType::findByNameAndContent(const QString &name, QIODevice *device,
0214         mode_t mode, int *accuracy)
0215 {
0216     Q_UNUSED(mode); // If we have data, then this is a regular file anyway...
0217     if (accuracy) {
0218         *accuracy = 80;    // not supported anymore; was it really used for anything?
0219     }
0220     QMimeDatabase db;
0221     return KMimeType::Ptr(new KMimeType(db.mimeTypeForFileNameAndData(name, device)));
0222 }
0223 
0224 QString KMimeType::extractKnownExtension(const QString &fileName)
0225 {
0226     QMimeDatabase db;
0227     return db.suffixForFileName(fileName);
0228 }
0229 
0230 KMimeType::Ptr KMimeType::findByContent(const QByteArray &data, int *accuracy)
0231 {
0232     QMimeDatabase db;
0233     if (accuracy) {
0234         *accuracy = 80;    // not supported anymore; was it really used for anything?
0235     }
0236     return KMimeType::Ptr(new KMimeType(db.mimeTypeForData(data)));
0237 }
0238 
0239 KMimeType::Ptr KMimeType::findByContent(QIODevice *device, int *accuracy)
0240 {
0241     QMimeDatabase db;
0242     if (accuracy) {
0243         *accuracy = 80;    // not supported anymore; was it really used for anything?
0244     }
0245     return KMimeType::Ptr(new KMimeType(db.mimeTypeForData(device)));
0246 }
0247 
0248 KMimeType::Ptr KMimeType::findByFileContent(const QString &fileName, int *accuracy)
0249 {
0250     QFile device(fileName);
0251 #if 1
0252     // Look at mode first
0253     KMimeType::Ptr mimeFromMode = findFromMode(fileName, 0, true);
0254     if (mimeFromMode) {
0255         if (accuracy) {
0256             *accuracy = 100;
0257         }
0258         return mimeFromMode;
0259     }
0260 #endif
0261     QMimeDatabase db;
0262     KMimeType::Ptr mime(new KMimeType(db.mimeTypeForData(&device)));
0263     if (accuracy) {
0264         *accuracy = mime->isDefault() ? 0 : 80;    // not supported anymore; was it really used for anything?
0265     }
0266     return mime;
0267 }
0268 
0269 bool KMimeType::isBinaryData(const QString &fileName)
0270 {
0271     QFile file(fileName);
0272     if (!file.open(QIODevice::ReadOnly)) {
0273         return false;    // err, whatever
0274     }
0275     const QByteArray data = file.read(32);
0276     return isBufferBinaryData(data);
0277 }
0278 
0279 KMimeType::KMimeType(const QMimeType &mime)
0280     : d_ptr(new KMimeTypePrivate(mime))
0281 {
0282 }
0283 
0284 KMimeType::~KMimeType()
0285 {
0286     delete d_ptr;
0287 }
0288 
0289 QString KMimeType::favIconForUrl(const QUrl &url)
0290 {
0291     return KIO::favIconForUrl(url);
0292 }
0293 
0294 QString KMimeType::iconNameForUrl(const QUrl &url, mode_t mode)
0295 {
0296     Q_UNUSED(mode)
0297     return KIO::iconNameForUrl(url);
0298 }
0299 
0300 QString KMimeType::comment() const
0301 {
0302     return d_ptr->m_qmime.comment();
0303 }
0304 
0305 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0306 QString KMimeType::parentMimeType() const
0307 {
0308     const QStringList parents = d_ptr->m_qmime.parentMimeTypes();
0309     if (!parents.isEmpty()) {
0310         return parents.first();
0311     }
0312     return QString();
0313 }
0314 #endif
0315 
0316 bool KMimeType::is(const QString &mimeTypeName) const
0317 {
0318     return d_ptr->m_qmime.inherits(mimeTypeName);
0319 }
0320 
0321 QStringList KMimeType::parentMimeTypes() const
0322 {
0323     return d_ptr->m_qmime.parentMimeTypes();
0324 }
0325 
0326 QStringList KMimeType::allParentMimeTypes() const
0327 {
0328     return d_ptr->m_qmime.allAncestors();
0329 }
0330 
0331 QString KMimeType::defaultMimeType()
0332 {
0333     return QLatin1String("application/octet-stream");
0334 }
0335 
0336 QString KMimeType::iconName() const
0337 {
0338     return d_ptr->m_qmime.iconName();
0339 }
0340 
0341 QStringList KMimeType::patterns() const
0342 {
0343     return d_ptr->m_qmime.globPatterns();
0344 }
0345 
0346 // TODO MOVE TO keditfiletype/mimetypedata.cpp
0347 QString KMimeType::userSpecifiedIconName() const
0348 {
0349     //d->ensureXmlDataLoaded();
0350     //return d->m_iconName;
0351     return QString();
0352 }
0353 
0354 int KMimeType::sharedMimeInfoVersion()
0355 {
0356     return KMimeTypeRepository::self()->sharedMimeInfoVersion();
0357 }
0358 
0359 QString KMimeType::mainExtension() const
0360 {
0361 #if 1 // HACK START - can be removed once shared-mime-info >= 0.70 is used/required.
0362     // The idea was: first usable pattern from m_lstPatterns.
0363     // But update-mime-database makes a mess of the order of the patterns,
0364     // because it uses a hash internally.
0365     static const struct {
0366         const char *mime;
0367         const char *extension;
0368     } s_hardcodedMimes[] = {
0369         { "text/plain", ".txt" }
0370     };
0371     if (patterns().count() > 1) {
0372         const QByteArray me = name().toLatin1();
0373         for (uint i = 0; i < sizeof(s_hardcodedMimes) / sizeof(*s_hardcodedMimes); ++i) {
0374             if (me == s_hardcodedMimes[i].mime) {
0375                 return QString::fromLatin1(s_hardcodedMimes[i].extension);
0376             }
0377         }
0378     }
0379 #endif // HACK END
0380 
0381     Q_FOREACH (const QString &pattern, patterns()) {
0382         // Skip if if looks like: README or *. or *.*
0383         // or *.JP*G or *.JP?
0384         if (pattern.startsWith(QLatin1String("*.")) &&
0385                 pattern.length() > 2 &&
0386                 pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) {
0387             return pattern.mid(1);
0388         }
0389     }
0390     // TODO we should also look into the parent mimetype's patterns, no?
0391     return QString();
0392 }
0393 
0394 bool KMimeType::matchFileName(const QString &filename, const QString &pattern)
0395 {
0396     return KMimeTypeRepository::matchFileName(filename, pattern);
0397 }
0398 
0399 /*
0400 int KMimeTypePrivate::serviceOffersOffset() const
0401 {
0402     return KMimeTypeFactory::self()->serviceOffersOffset(name());
0403 }
0404 */
0405 
0406 QString KMimeType::name() const
0407 {
0408     return d_ptr->m_qmime.name();
0409 }