• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

kmimetyperepository.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 2006-2007, 2010 David Faure <faure@kde.org>
00003  *
00004  *  This library is free software; you can redistribute it and/or
00005  *  modify it under the terms of the GNU Library General Public
00006  *  License as published by the Free Software Foundation; either
00007  *  version 2 of the License, or (at your option) any later version.
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  */
00019 
00020 #include "kmimetyperepository_p.h"
00021 #include <kstandarddirs.h>
00022 #include <ksharedconfig.h>
00023 #include <kconfiggroup.h>
00024 #include "kmimetype.h"
00025 #include <kdeversion.h> // KDE_MAKE_VERSION
00026 #include <kmessage.h>
00027 #include <klocale.h>
00028 #include "kfoldermimetype.h"
00029 #include <QFile>
00030 #include <QProcess>
00031 
00032 extern int servicesDebugArea();
00033 
00034 KMimeTypeRepository * KMimeTypeRepository::self()
00035 {
00036     K_GLOBAL_STATIC(KMimeTypeRepository, s_self)
00037     return s_self;
00038 }
00039 
00040 KMimeTypeRepository::KMimeTypeRepository()
00041     : m_parentsMapLoaded(false),
00042       m_magicFilesParsed(false),
00043       m_aliasFilesParsed(false),
00044       m_globsFilesParsed(false),
00045       m_patternsMapCalculated(false),
00046       m_mimeTypesChecked(false),
00047       m_useFavIcons(true),
00048       m_useFavIconsChecked(false),
00049       m_sharedMimeInfoVersion(0),
00050       m_mutex(QReadWriteLock::Recursive)
00051 {
00052 }
00053 
00054 KMimeTypeRepository::~KMimeTypeRepository()
00055 {
00056 }
00057 
00058 KMimeType::Ptr KMimeTypeRepository::findMimeTypeByName(const QString &_name, KMimeType::FindByNameOption options)
00059 {
00060     QString name = _name;
00061     if (options & KMimeType::ResolveAliases) {
00062         name = canonicalName(name);
00063     }
00064 
00065     const QString filename = name + QLatin1String(".xml");
00066 
00067     if (KStandardDirs::locate("xdgdata-mime", filename).isEmpty()) {
00068         return KMimeType::Ptr(); // Not found
00069     }
00070 
00071     if (name == QLatin1String("inode/directory"))
00072         return KMimeType::Ptr(new KFolderMimeType(filename, name, QString() /*comment*/));
00073     else
00074         return KMimeType::Ptr(new KMimeType(filename, name, QString() /*comment*/));
00075 }
00076 
00077 bool KMimeTypeRepository::checkMimeTypes()
00078 {
00079     // check if there are mimetypes
00080     const QStringList globFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("globs"));
00081     return !globFiles.isEmpty();
00082 }
00083 
00084 QString KMimeTypeRepository::resolveAlias(const QString& mime)
00085 {
00086     return aliases().value(mime);
00087 }
00088 
00089 QString KMimeTypeRepository::canonicalName(const QString& mime)
00090 {
00091     QString c = resolveAlias(mime);
00092     if (c.isEmpty())
00093         return mime;
00094     return c;
00095 }
00096 
00097 bool KMimeTypeRepository::matchFileName( const QString &filename, const QString &pattern )
00098 {
00099     const int pattern_len = pattern.length();
00100     if (!pattern_len)
00101         return false;
00102     const int len = filename.length();
00103 
00104     const int starCount = pattern.count(QLatin1Char('*'));
00105 
00106     // Patterns like "*~", "*.extension"
00107     if (pattern[0] == QLatin1Char('*')  && pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 1)
00108     {
00109         if ( len + 1 < pattern_len ) return false;
00110 
00111         const QChar *c1 = pattern.unicode() + pattern_len - 1;
00112         const QChar *c2 = filename.unicode() + len - 1;
00113         int cnt = 1;
00114         while (cnt < pattern_len && *c1-- == *c2--)
00115             ++cnt;
00116         return cnt == pattern_len;
00117     }
00118 
00119     // Patterns like "README*" (well this is currently the only one like that...)
00120     if (starCount == 1 && pattern[pattern_len - 1] == QLatin1Char('*')) {
00121         if ( len + 1 < pattern_len ) return false;
00122         if (pattern[0] == QLatin1Char('*'))
00123             return filename.indexOf(pattern.mid(1, pattern_len - 2)) != -1;
00124 
00125         const QChar *c1 = pattern.unicode();
00126         const QChar *c2 = filename.unicode();
00127         int cnt = 1;
00128         while (cnt < pattern_len && *c1++ == *c2++)
00129            ++cnt;
00130         return cnt == pattern_len;
00131     }
00132 
00133     // Names without any wildcards like "README"
00134     if (pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 0 && pattern.indexOf(QLatin1Char('?')))
00135         return (pattern == filename);
00136 
00137     // Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method
00138     QRegExp rx(pattern);
00139     rx.setPatternSyntax(QRegExp::Wildcard);
00140     return rx.exactMatch(filename);
00141 }
00142 
00143 // Helper for findFromFileName
00144 void KMimeTypeRepository::findFromOtherPatternList(QStringList& matchingMimeTypes,
00145                                                    const QString &fileName,
00146                                                    QString& foundExt,
00147                                                    bool highWeight)
00148 {
00149     KMimeGlobsFileParser::GlobList& patternList = highWeight ? m_globs.m_highWeightGlobs : m_globs.m_lowWeightGlobs;
00150 
00151     int matchingPatternLength = 0;
00152     qint32 lastMatchedWeight = 0;
00153     if (!highWeight && !matchingMimeTypes.isEmpty()) {
00154         // We found matches in the fast pattern dict already:
00155         matchingPatternLength = foundExt.length() + 2; // *.foo -> length=5
00156         lastMatchedWeight = 50;
00157     }
00158 
00159     // "Applications MUST match globs case-insensitively, except when the case-sensitive
00160     // attribute is set to true."
00161     // KMimeGlobsFileParser takes care of putting case-insensitive patterns in lowercase.
00162     const QString lowerCaseFileName = fileName.toLower();
00163 
00164     KMimeGlobsFileParser::GlobList::const_iterator it = patternList.constBegin();
00165     const KMimeGlobsFileParser::GlobList::const_iterator end = patternList.constEnd();
00166     for ( ; it != end; ++it ) {
00167         const KMimeGlobsFileParser::Glob& glob = *it;
00168         if ( matchFileName( (glob.flags & CaseSensitive) ? fileName : lowerCaseFileName, glob.pattern ) ) {
00169             // Is this a lower-weight pattern than the last match? Stop here then.
00170             if (glob.weight < lastMatchedWeight)
00171                 break;
00172             if (lastMatchedWeight > 0 && glob.weight > lastMatchedWeight) // can't happen
00173                 kWarning(servicesDebugArea()) << "Assumption failed; globs2 weights not sorted correctly"
00174                                << glob.weight << ">" << lastMatchedWeight;
00175             // Is this a shorter or a longer match than an existing one, or same length?
00176             if (glob.pattern.length() < matchingPatternLength) {
00177                 continue; // too short, ignore
00178             } else if (glob.pattern.length() > matchingPatternLength) {
00179                 // longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2)
00180                 matchingMimeTypes.clear();
00181                 // remember the new "longer" length
00182                 matchingPatternLength = glob.pattern.length();
00183             }
00184             matchingMimeTypes.push_back(glob.mimeType);
00185             if (glob.pattern.startsWith(QLatin1String("*.")))
00186                 foundExt = glob.pattern.mid(2);
00187         }
00188     }
00189 }
00190 
00191 QStringList KMimeTypeRepository::findFromFileName(const QString &fileName, QString *pMatchingExtension)
00192 {
00193     m_mutex.lockForWrite();
00194     parseGlobs();
00195     m_mutex.unlock();
00196 
00197     QReadLocker lock(&m_mutex);
00198     // First try the high weight matches (>50), if any.
00199     QStringList matchingMimeTypes;
00200     QString foundExt;
00201     findFromOtherPatternList(matchingMimeTypes, fileName, foundExt, true);
00202     if (matchingMimeTypes.isEmpty()) {
00203 
00204         // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
00205         // (which is most of them, so this optimization is definitely worth it)
00206         const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
00207         if (lastDot != -1) { // if no '.', skip the extension lookup
00208             const int ext_len = fileName.length() - lastDot - 1;
00209             const QString simpleExtension = fileName.right( ext_len ).toLower();
00210             // (toLower because fast matterns are always case-insensitive and saved as lowercase)
00211 
00212             matchingMimeTypes = m_globs.m_fastPatterns.value(simpleExtension);
00213             if (!matchingMimeTypes.isEmpty()) {
00214                 foundExt = simpleExtension;
00215                 // Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
00216                 // at least those with weight 50.
00217             }
00218         }
00219 
00220         // Finally, try the low weight matches (<=50)
00221         findFromOtherPatternList(matchingMimeTypes, fileName, foundExt, false);
00222     }
00223     if (pMatchingExtension)
00224         *pMatchingExtension = foundExt;
00225     return matchingMimeTypes;
00226 }
00227 
00228 KMimeType::Ptr KMimeTypeRepository::findFromContent(QIODevice* device, int* accuracy, QByteArray& beginning)
00229 {
00230     Q_ASSERT(device->isOpen());
00231     const qint64 deviceSize = device->size();
00232     if (deviceSize == 0) {
00233         if (accuracy)
00234             *accuracy = 100;
00235         return findMimeTypeByName(QLatin1String("application/x-zerosize"));
00236     }
00237 
00238     m_mutex.lockForWrite();
00239     if (!m_magicFilesParsed) {
00240         parseMagic();
00241         m_magicFilesParsed = true;
00242     }
00243     m_mutex.unlock();
00244 
00245     // Apply magic rules
00246     {
00247         QReadLocker lock(&m_mutex);
00248         Q_FOREACH ( const KMimeMagicRule& rule, m_magicRules ) {
00249             if (rule.match(device, deviceSize, beginning)) {
00250                 if (accuracy)
00251                     *accuracy = rule.priority();
00252                 return findMimeTypeByName(rule.mimetype());
00253             }
00254         }
00255     }
00256 
00257     // Do fallback code so that we never return 0
00258     // Nothing worked, check if the file contents looks like binary or text
00259     if (!KMimeType::isBufferBinaryData(beginning)) {
00260         if (accuracy)
00261             *accuracy = 5;
00262         return findMimeTypeByName(QLatin1String("text/plain"));
00263     }
00264     if (accuracy)
00265         *accuracy = 0;
00266     return defaultMimeTypePtr();
00267 }
00268 
00269 static QString fallbackParent(const QString& mimeTypeName)
00270 {
00271     const QString myGroup = mimeTypeName.left(mimeTypeName.indexOf(QLatin1Char('/')));
00272     // All text/* types are subclasses of text/plain.
00273     if (myGroup == QLatin1String("text") && mimeTypeName != QLatin1String("text/plain"))
00274         return QLatin1String("text/plain");
00275     // All real-file mimetypes implicitly derive from application/octet-stream
00276     if (myGroup != QLatin1String("inode") &&
00277         // kde extensions
00278         myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri")
00279         && mimeTypeName != QLatin1String("application/octet-stream")) {
00280         return QLatin1String("application/octet-stream");
00281     }
00282     return QString();
00283 }
00284 
00285 QStringList KMimeTypeRepository::parents(const QString& mime)
00286 {
00287     QWriteLocker lock(&m_mutex);
00288     if (!m_parentsMapLoaded) {
00289         m_parentsMapLoaded = true;
00290         Q_ASSERT(m_parents.isEmpty());
00291 
00292         const QStringList subclassFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("subclasses"));
00293         //kDebug() << subclassFiles;
00294         Q_FOREACH(const QString& fileName, subclassFiles) {
00295 
00296             QFile qfile( fileName );
00297             //kDebug(7021) << "Now parsing" << fileName;
00298             if (qfile.open(QIODevice::ReadOnly)) {
00299                 QTextStream stream(&qfile);
00300                 stream.setCodec("ISO 8859-1");
00301                 while (!stream.atEnd()) {
00302                     const QString line = stream.readLine();
00303                     if (line.isEmpty() || line[0] == QLatin1Char('#'))
00304                         continue;
00305                     const int pos = line.indexOf(QLatin1Char(' '));
00306                     if (pos == -1) // syntax error
00307                         continue;
00308                     const QString derivedTypeName = line.left(pos);
00309                     KMimeType::Ptr derivedType = findMimeTypeByName(derivedTypeName, KMimeType::ResolveAliases);
00310                     if (!derivedType)
00311                         kWarning(7012) << fileName << " refers to unknown mimetype " << derivedTypeName;
00312                     else {
00313                         const QString parentTypeName = line.mid(pos+1);
00314                         Q_ASSERT(!parentTypeName.isEmpty());
00315                         //derivedType->setParentMimeType(parentTypeName);
00316                         m_parents[derivedTypeName].append(parentTypeName);
00317                     }
00318                 }
00319             }
00320         }
00321     }
00322     QStringList parents = m_parents.value(mime);
00323 
00324     if (parents.isEmpty()) {
00325         const QString myParent = fallbackParent(mime);
00326         if (!myParent.isEmpty())
00327             parents.append(myParent);
00328     }
00329 
00330     return parents;
00331 }
00332 
00333 #include <arpa/inet.h> // for ntohs
00334 #include <kstandarddirs.h>
00335 #include <QFile>
00336 
00337 // Sort them in descending order of priority
00338 static bool mimeMagicRuleCompare(const KMimeMagicRule& lhs, const KMimeMagicRule& rhs) {
00339     return lhs.priority() > rhs.priority();
00340 }
00341 
00342 // Caller must hold m_mutex
00343 void KMimeTypeRepository::parseMagic()
00344 {
00345     const QStringList magicFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("magic"));
00346     //kDebug() << magicFiles;
00347     QListIterator<QString> magicIter( magicFiles );
00348     magicIter.toBack();
00349     while (magicIter.hasPrevious()) { // global first, then local. Turns out it doesn't matter though.
00350         const QString fileName = magicIter.previous();
00351         QFile magicFile(fileName);
00352         //kDebug(servicesDebugArea()) << "Now parsing " << fileName;
00353         if (magicFile.open(QIODevice::ReadOnly))
00354             m_magicRules += parseMagicFile(&magicFile, fileName);
00355     }
00356     qSort(m_magicRules.begin(), m_magicRules.end(), mimeMagicRuleCompare);
00357 }
00358 
00359 static char readNumber(qint64& value, QIODevice* file)
00360 {
00361     char ch;
00362     while (file->getChar(&ch)) {
00363         if (ch < '0' || ch > '9')
00364             return ch;
00365         value = 10 * value + ch - '0';
00366     }
00367     // eof
00368     return '\0';
00369 }
00370 
00371 
00372 #define MAKE_LITTLE_ENDIAN16(val) val = (quint16)(((quint16)(val) << 8)|((quint16)(val) >> 8))
00373 
00374 #define MAKE_LITTLE_ENDIAN32(val) \
00375    val = (((quint32)(val) & 0xFF000000U) >> 24) | \
00376          (((quint32)(val) & 0x00FF0000U) >> 8) | \
00377          (((quint32)(val) & 0x0000FF00U) << 8) | \
00378          (((quint32)(val) & 0x000000FFU) << 24)
00379 
00380 QList<KMimeMagicRule> KMimeTypeRepository::parseMagicFile(QIODevice* file, const QString& fileName) const
00381 {
00382     QList<KMimeMagicRule> rules;
00383     QByteArray header = file->read(12);
00384     if (header != QByteArray::fromRawData("MIME-Magic\0\n", 12)) {
00385         kWarning(servicesDebugArea()) << "Invalid magic file " << fileName << " starts with " << header;
00386         return rules;
00387     }
00388     QList<KMimeMagicMatch> matches; // toplevel matches (indent==0)
00389     int priority = 0; // to avoid warning
00390     QString mimeTypeName;
00391 
00392     Q_FOREVER {
00393         char ch = '\0';
00394         bool chOk = file->getChar(&ch);
00395 
00396         if (!chOk || ch == '[') {
00397             // Finish previous section
00398             if (!mimeTypeName.isEmpty()) {
00399                 rules.append(KMimeMagicRule(mimeTypeName, priority, matches));
00400                 matches.clear();
00401                 mimeTypeName.clear();
00402             }
00403             if (file->atEnd())
00404                 break; // done
00405 
00406             // Parse new section
00407             const QString line = QString::fromLatin1(file->readLine());
00408             const int pos = line.indexOf(QLatin1Char(':'));
00409             if (pos == -1) { // syntax error
00410                 kWarning(servicesDebugArea()) << "Syntax error in " << mimeTypeName
00411                                << " ':' not present in section name" << endl;
00412                 break;
00413             }
00414             priority = line.left(pos).toInt();
00415             mimeTypeName = line.mid(pos+1);
00416             mimeTypeName = mimeTypeName.left(mimeTypeName.length()-2); // remove ']\n'
00417             //kDebug(servicesDebugArea()) << "New rule for " << mimeTypeName
00418             //             << " with priority " << priority << endl;
00419         } else {
00420             // Parse line in the section
00421             // [ indent ] ">" start-offset "=" value
00422             //   [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
00423             qint64 indent = 0;
00424             if (ch != '>') {
00425                 indent = ch - '0';
00426                 ch = readNumber(indent, file);
00427                 if (ch != '>') {
00428                     kWarning(servicesDebugArea()) << "Invalid magic file " << fileName << " '>' not found, got " << ch << " at pos " << file->pos();
00429                     break;
00430                 }
00431             }
00432 
00433             KMimeMagicMatch match;
00434             match.m_rangeStart = 0;
00435             ch = readNumber(match.m_rangeStart, file);
00436             if (ch != '=') {
00437                 kWarning(servicesDebugArea()) << "Invalid magic file " << fileName << " '=' not found";
00438                 break;
00439             }
00440 
00441             char lengthBuffer[2];
00442             if (file->read(lengthBuffer, 2) != 2)
00443                 break;
00444             const short valueLength = ntohs(*(short*)lengthBuffer);
00445             //kDebug() << "indent=" << indent << " rangeStart=" << match.m_rangeStart
00446             //         << " valueLength=" << valueLength << endl;
00447 
00448             match.m_data.resize(valueLength);
00449             if (file->read(match.m_data.data(), valueLength) != valueLength)
00450                 break;
00451 
00452             match.m_rangeLength = 1;
00453             bool invalidLine = false;
00454 
00455             if (!file->getChar(&ch))
00456                 break;
00457             qint64 wordSize = 1;
00458 
00459             Q_FOREVER {
00460                 // We get 'ch' before coming here, or as part of the parsing in each case below.
00461                 switch (ch) {
00462                 case '\n':
00463                     break;
00464                 case '&':
00465                     match.m_mask.resize(valueLength);
00466                     if (file->read(match.m_mask.data(), valueLength) != valueLength)
00467                         invalidLine = true;
00468                     if (!file->getChar(&ch))
00469                         invalidLine = true;
00470                     break;
00471                 case '~': {
00472                     wordSize = 0;
00473                     ch = readNumber(wordSize, file);
00474                     //kDebug() << "wordSize=" << wordSize;
00475                     break;
00476                 }
00477                 case '+':
00478                     // Parse range length
00479                     match.m_rangeLength = 0;
00480                     ch = readNumber(match.m_rangeLength, file);
00481                     if (ch == '\n')
00482                         break;
00483                     // fall-through intended
00484                 default:
00485                     // "If an unknown character is found where a newline is expected
00486                     // then the whole line should be ignored (there will be no binary
00487                     // data after the new character, so the next line starts after the
00488                     // next "\n" character). This is for future extensions.", says spec
00489                     while (ch != '\n' && !file->atEnd()) {
00490                         file->getChar(&ch);
00491                     }
00492                     invalidLine = true;
00493                     kDebug(servicesDebugArea()) << "invalid line - garbage found - ch=" << ch;
00494                     break;
00495                 }
00496                 if (ch == '\n' || invalidLine)
00497                     break;
00498             }
00499             if (!invalidLine) {
00500                 // Finish match, doing byte-swapping on little endian hosts
00501 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
00502                 if (wordSize > 1) {
00503                     //kDebug() << "data before swapping: " << match.m_data;;
00504                     if ((wordSize != 2 && wordSize != 4) || (valueLength % wordSize != 0))
00505                         continue; // invalid word size
00506                     char* data = match.m_data.data();
00507                     char* mask = match.m_mask.data();
00508                     for (int i = 0; i < valueLength; i += wordSize) {
00509                         if (wordSize == 2)
00510                             MAKE_LITTLE_ENDIAN16( *((quint16 *) data + i) );
00511                         else if (wordSize == 4)
00512                             MAKE_LITTLE_ENDIAN32( *((quint32 *) data + i) );
00513                         if (!match.m_mask.isEmpty()) {
00514                             if (wordSize == 2)
00515                                 MAKE_LITTLE_ENDIAN16( *((quint16 *) mask + i) );
00516                             else if (wordSize == 4)
00517                                 MAKE_LITTLE_ENDIAN32( *((quint32 *) mask + i) );
00518                         }
00519                     }
00520                     //kDebug() << "data after swapping: " << match.m_data;
00521                 }
00522 #endif
00523                 // Append match at the right place depending on indent:
00524                 if (indent == 0) {
00525                     matches.append(match);
00526                 } else {
00527                     KMimeMagicMatch* m = &matches.last();
00528                     Q_ASSERT(m);
00529                     for (int i = 1 /* nothing to do for indent==1 */; i < indent; ++i) {
00530                         m = &m->m_subMatches.last();
00531                         Q_ASSERT(m);
00532                     }
00533                     m->m_subMatches.append(match);
00534                 }
00535             }
00536         }
00537     }
00538     return rules;
00539 }
00540 
00541 const KMimeTypeRepository::AliasesMap& KMimeTypeRepository::aliases()
00542 {
00543     QWriteLocker lock(&m_mutex);
00544     if (!m_aliasFilesParsed) {
00545         m_aliasFilesParsed = true;
00546 
00547         const QStringList aliasFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("aliases"));
00548         Q_FOREACH(const QString& fileName, aliasFiles) {
00549             QFile qfile(fileName);
00550             //kDebug(7021) << "Now parsing" << fileName;
00551             if (qfile.open(QIODevice::ReadOnly)) {
00552                 QTextStream stream(&qfile);
00553                 stream.setCodec("ISO 8859-1");
00554                 while (!stream.atEnd()) {
00555                     const QString line = stream.readLine();
00556                     if (line.isEmpty() || line[0] == QLatin1Char('#'))
00557                         continue;
00558                     const int pos = line.indexOf(QLatin1Char(' '));
00559                     if (pos == -1) // syntax error
00560                         continue;
00561                     const QString aliasTypeName = line.left(pos);
00562                     const QString parentTypeName = line.mid(pos+1);
00563                     Q_ASSERT(!aliasTypeName.isEmpty());
00564                     Q_ASSERT(!parentTypeName.isEmpty());
00565 
00566                     const KMimeType::Ptr realMimeType =
00567                         findMimeTypeByName(aliasTypeName, KMimeType::DontResolveAlias);
00568                     if (realMimeType) {
00569                         //kDebug(servicesDebugArea()) << "Ignoring alias" << aliasTypeName << "because also defined as a real mimetype";
00570                     } else {
00571                         m_aliases.insert(aliasTypeName, parentTypeName);
00572                     }
00573                 }
00574             }
00575         }
00576     }
00577     return m_aliases;
00578 }
00579 
00580 // Caller must lock m_mutex for write
00581 void KMimeTypeRepository::parseGlobs()
00582 {
00583     if (!m_globsFilesParsed) {
00584         m_globsFilesParsed = true;
00585         KMimeGlobsFileParser parser;
00586         m_globs = parser.parseGlobs();
00587     }
00588 }
00589 
00590 QStringList KMimeTypeRepository::patternsForMimetype(const QString& mimeType)
00591 {
00592     QWriteLocker lock(&m_mutex);
00593     if (!m_patternsMapCalculated) {
00594         m_patternsMapCalculated = true;
00595         parseGlobs();
00596         m_patterns = m_globs.patternsMap();
00597     }
00598     return m_patterns.value(mimeType);
00599 }
00600 
00601 static void errorMissingMimeTypes( const QStringList& _types )
00602 {
00603     KMessage::message( KMessage::Error, i18np( "Could not find mime type <resource>%2</resource>",
00604                 "Could not find mime types:\n<resource>%2</resource>", _types.count(), _types.join(QLatin1String("</resource>\n<resource>")) ) );
00605 }
00606 
00607 void KMimeTypeRepository::checkEssentialMimeTypes()
00608 {
00609     QWriteLocker lock(&m_mutex);
00610     if (m_mimeTypesChecked) // already done
00611         return;
00612 
00613     m_mimeTypesChecked = true; // must be done before building mimetypes
00614 
00615     // No Mime-Types installed ?
00616     // Lets do some rescue here.
00617     if (!checkMimeTypes()) {
00618         // Note that this messagebox is queued, so it will only be shown once getting back to the event loop
00619 
00620         // No mimetypes installed? Are you setting XDG_DATA_DIRS without including /usr/share in it?
00621         KMessage::message(KMessage::Error, i18n("No mime types installed. "
00622             "Check that shared-mime-info is installed, and that XDG_DATA_DIRS is not set, or includes /usr/share."));
00623         return; // no point in going any further
00624     }
00625 
00626     QStringList missingMimeTypes;
00627 
00628     if (!KMimeType::mimeType(QLatin1String("inode/directory")))
00629         missingMimeTypes.append(QLatin1String("inode/directory"));
00630 #ifndef Q_OS_WIN
00631     //if (!KMimeType::mimeType(QLatin1String("inode/directory-locked")))
00632     //  missingMimeTypes.append(QLatin1String("inode/directory-locked"));
00633     if (!KMimeType::mimeType(QLatin1String("inode/blockdevice")))
00634         missingMimeTypes.append(QLatin1String("inode/blockdevice"));
00635     if (!KMimeType::mimeType(QLatin1String("inode/chardevice")))
00636         missingMimeTypes.append(QLatin1String("inode/chardevice"));
00637     if (!KMimeType::mimeType(QLatin1String("inode/socket")))
00638         missingMimeTypes.append(QLatin1String("inode/socket"));
00639     if (!KMimeType::mimeType(QLatin1String("inode/fifo")))
00640         missingMimeTypes.append(QLatin1String("inode/fifo"));
00641 #endif
00642     if (!KMimeType::mimeType(QLatin1String("application/x-shellscript")))
00643         missingMimeTypes.append(QLatin1String("application/x-shellscript"));
00644     if (!KMimeType::mimeType(QLatin1String("application/x-executable")))
00645         missingMimeTypes.append(QLatin1String("application/x-executable"));
00646     if (!KMimeType::mimeType(QLatin1String("application/x-desktop")))
00647         missingMimeTypes.append(QLatin1String("application/x-desktop"));
00648 
00649     if (!missingMimeTypes.isEmpty())
00650         errorMissingMimeTypes(missingMimeTypes);
00651 }
00652 
00653 KMimeType::Ptr KMimeTypeRepository::defaultMimeTypePtr()
00654 {
00655     QWriteLocker lock(&m_mutex);
00656     if (!m_defaultMimeType) {
00657         // Try to find the default type
00658         KMimeType::Ptr mime = findMimeTypeByName(KMimeType::defaultMimeType());
00659         if (mime) {
00660             m_defaultMimeType = mime;
00661         } else {
00662             const QString defaultMimeType = KMimeType::defaultMimeType();
00663             errorMissingMimeTypes(QStringList(defaultMimeType));
00664             const QString pathDefaultMimeType = KGlobal::dirs()->resourceDirs("xdgdata-mime").first()+defaultMimeType+QLatin1String(".xml");
00665             m_defaultMimeType = new KMimeType(pathDefaultMimeType, defaultMimeType, QLatin1String("mime"));
00666         }
00667     }
00668     return m_defaultMimeType;
00669 
00670 }
00671 
00672 bool KMimeTypeRepository::useFavIcons()
00673 {
00674     // this method will be called quite often, so better not read the config
00675     // again and again.
00676     m_mutex.lockForWrite();
00677     if (!m_useFavIconsChecked) {
00678         m_useFavIconsChecked = true;
00679         KConfigGroup cg( KGlobal::config(), "HTML Settings" );
00680         m_useFavIcons = cg.readEntry("EnableFavicon", true);
00681     }
00682     m_mutex.unlock();
00683     return m_useFavIcons;
00684 }
00685 
00686 static void addPlatformSpecificPkgConfigPath(QStringList& paths)
00687 {
00688 #if defined (Q_OS_FREEBSD)
00689     paths << QLatin1String("/usr/local/libdata/pkgconfig"); // FreeBSD
00690 #elif defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_SOLARIS)
00691     paths << QLatin1String("/usr/local/lib/pkgconfig"); // {Net,Open}BSD/OpenSolaris
00692 #elif defined (Q_OS_UNIX)
00693     paths << QLatin1String("/usr/share/pkgconfig"); // Linux and all other unix
00694 #endif
00695 }
00696 
00697 static int mimeDataBaseVersion()
00698 {
00699     // TODO: Remove the #idef'ed code below once the issue is fixed either
00700     // in QProcess or the shared-mime-info utility provides its version number.
00701 #ifdef Q_OS_UNIX
00702     // Try to read the version number from the shared-mime-info.pc file
00703     QStringList paths;
00704     const QByteArray pkgConfigPath = qgetenv("PKG_CONFIG_PATH");
00705     if (!pkgConfigPath.isEmpty()) {
00706         paths << QFile::decodeName(pkgConfigPath).split(QLatin1Char(':'), QString::SkipEmptyParts);
00707     }
00708 
00709     // Add platform specific hard-coded default paths to the list...
00710     addPlatformSpecificPkgConfigPath(paths);
00711 
00712     Q_FOREACH(const QString& path, paths) {
00713         const QString fileName = path + QLatin1String("/shared-mime-info.pc");
00714         if (!QFile::exists(fileName)) {
00715             continue;
00716         }
00717 
00718         QFile file (fileName);
00719         if (!file.open(QIODevice::ReadOnly)) {
00720             break;
00721         }
00722 
00723         while (!file.atEnd()) {
00724             const QByteArray line = file.readLine().simplified();
00725             if (!line.startsWith("Version")) {
00726                 continue;
00727             }
00728             QRegExp versionRe(QString::fromLatin1("Version: (\\d+)\\.(\\d+)(\\.(\\d+))?"));
00729             if (versionRe.indexIn(QString::fromLocal8Bit(line)) > -1) {
00730                 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
00731             }
00732         }
00733     }
00734 #endif
00735 
00736     // Execute "update-mime-database -v" to determine version number.
00737     // NOTE: On *nix, the code below is known to cause freezes/hangs in apps
00738     // that block signals. See https://bugs.kde.org/show_bug.cgi?id=260719.
00739     const QString umd = KStandardDirs::findExe(QString::fromLatin1("update-mime-database"));
00740     if (umd.isEmpty()) {
00741         kWarning(servicesDebugArea()) << "update-mime-database not found!";
00742         return -1;
00743     }
00744 
00745     QProcess smi;
00746     smi.start(umd, QStringList() << QString::fromLatin1("-v"));
00747     smi.waitForStarted();
00748     smi.waitForFinished();
00749     const QString out = QString::fromLocal8Bit(smi.readAllStandardError());
00750     QRegExp versionRe(QString::fromLatin1("update-mime-database \\(shared-mime-info\\) (\\d+)\\.(\\d+)(\\.(\\d+))?"));
00751     if (versionRe.indexIn(out) > -1) {
00752         return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
00753     }
00754 
00755     kWarning(servicesDebugArea()) << "Unexpected version scheme from update-mime-database -v: got" << out;
00756     return -1;
00757 }
00758 
00759 int KMimeTypeRepository::sharedMimeInfoVersion()
00760 {
00761     m_mutex.lockForWrite();
00762     if (m_sharedMimeInfoVersion == 0)
00763         m_sharedMimeInfoVersion = mimeDataBaseVersion();
00764     m_mutex.unlock();
00765     return m_sharedMimeInfoVersion;
00766 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal