• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • 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     if (beginning.isEmpty()) {
00238         // check if we can really read the data; also provide enough data for most rules
00239         const qint64 dataNeeded = qMin(deviceSize, (qint64) 16384);
00240         beginning.resize(dataNeeded);
00241         if (!device->seek(0) || device->read(beginning.data(), dataNeeded) == -1) {
00242             return defaultMimeTypePtr(); // don't bother detecting unreadable file
00243         }
00244     }
00245 
00246     m_mutex.lockForWrite();
00247     if (!m_magicFilesParsed) {
00248         parseMagic();
00249         m_magicFilesParsed = true;
00250     }
00251     m_mutex.unlock();
00252 
00253     // Apply magic rules
00254     {
00255         QReadLocker lock(&m_mutex);
00256         Q_FOREACH ( const KMimeMagicRule& rule, m_magicRules ) {
00257             if (rule.match(device, deviceSize, beginning)) {
00258                 if (accuracy)
00259                     *accuracy = rule.priority();
00260                 return findMimeTypeByName(rule.mimetype());
00261             }
00262         }
00263     }
00264 
00265     // Do fallback code so that we never return 0
00266     // Nothing worked, check if the file contents looks like binary or text
00267     if (!KMimeType::isBufferBinaryData(beginning)) {
00268         if (accuracy)
00269             *accuracy = 5;
00270         return findMimeTypeByName(QLatin1String("text/plain"));
00271     }
00272     if (accuracy)
00273         *accuracy = 0;
00274     return defaultMimeTypePtr();
00275 }
00276 
00277 static QString fallbackParent(const QString& mimeTypeName)
00278 {
00279     const QString myGroup = mimeTypeName.left(mimeTypeName.indexOf(QLatin1Char('/')));
00280     // All text/* types are subclasses of text/plain.
00281     if (myGroup == QLatin1String("text") && mimeTypeName != QLatin1String("text/plain"))
00282         return QLatin1String("text/plain");
00283     // All real-file mimetypes implicitly derive from application/octet-stream
00284     if (myGroup != QLatin1String("inode") &&
00285         // kde extensions
00286         myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri")
00287         && mimeTypeName != QLatin1String("application/octet-stream")) {
00288         return QLatin1String("application/octet-stream");
00289     }
00290     return QString();
00291 }
00292 
00293 QStringList KMimeTypeRepository::parents(const QString& mime)
00294 {
00295     QWriteLocker lock(&m_mutex);
00296     if (!m_parentsMapLoaded) {
00297         m_parentsMapLoaded = true;
00298         Q_ASSERT(m_parents.isEmpty());
00299 
00300         const QStringList subclassFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("subclasses"));
00301         //kDebug() << subclassFiles;
00302         Q_FOREACH(const QString& fileName, subclassFiles) {
00303 
00304             QFile qfile( fileName );
00305             //kDebug(7021) << "Now parsing" << fileName;
00306             if (qfile.open(QIODevice::ReadOnly)) {
00307                 QTextStream stream(&qfile);
00308                 stream.setCodec("ISO 8859-1");
00309                 while (!stream.atEnd()) {
00310                     const QString line = stream.readLine();
00311                     if (line.isEmpty() || line[0] == QLatin1Char('#'))
00312                         continue;
00313                     const int pos = line.indexOf(QLatin1Char(' '));
00314                     if (pos == -1) // syntax error
00315                         continue;
00316                     const QString derivedTypeName = line.left(pos);
00317                     KMimeType::Ptr derivedType = findMimeTypeByName(derivedTypeName, KMimeType::ResolveAliases);
00318                     if (!derivedType)
00319                         kWarning(7012) << fileName << " refers to unknown mimetype " << derivedTypeName;
00320                     else {
00321                         const QString parentTypeName = line.mid(pos+1);
00322                         Q_ASSERT(!parentTypeName.isEmpty());
00323                         //derivedType->setParentMimeType(parentTypeName);
00324                         m_parents[derivedTypeName].append(parentTypeName);
00325                     }
00326                 }
00327             }
00328         }
00329     }
00330     QStringList parents = m_parents.value(mime);
00331 
00332     if (parents.isEmpty()) {
00333         const QString myParent = fallbackParent(mime);
00334         if (!myParent.isEmpty())
00335             parents.append(myParent);
00336     }
00337 
00338     return parents;
00339 }
00340 
00341 #include <arpa/inet.h> // for ntohs
00342 #include <kstandarddirs.h>
00343 #include <QFile>
00344 
00345 // Sort them in descending order of priority
00346 static bool mimeMagicRuleCompare(const KMimeMagicRule& lhs, const KMimeMagicRule& rhs) {
00347     return lhs.priority() > rhs.priority();
00348 }
00349 
00350 // Caller must hold m_mutex
00351 void KMimeTypeRepository::parseMagic()
00352 {
00353     const QStringList magicFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("magic"));
00354     //kDebug() << magicFiles;
00355     QListIterator<QString> magicIter( magicFiles );
00356     magicIter.toBack();
00357     while (magicIter.hasPrevious()) { // global first, then local. Turns out it doesn't matter though.
00358         const QString fileName = magicIter.previous();
00359         QFile magicFile(fileName);
00360         //kDebug(servicesDebugArea()) << "Now parsing " << fileName;
00361         if (magicFile.open(QIODevice::ReadOnly))
00362             m_magicRules += parseMagicFile(&magicFile, fileName);
00363     }
00364     qSort(m_magicRules.begin(), m_magicRules.end(), mimeMagicRuleCompare);
00365 }
00366 
00367 static char readNumber(qint64& value, QIODevice* file)
00368 {
00369     char ch;
00370     while (file->getChar(&ch)) {
00371         if (ch < '0' || ch > '9')
00372             return ch;
00373         value = 10 * value + ch - '0';
00374     }
00375     // eof
00376     return '\0';
00377 }
00378 
00379 
00380 #define MAKE_LITTLE_ENDIAN16(val) val = (quint16)(((quint16)(val) << 8)|((quint16)(val) >> 8))
00381 
00382 #define MAKE_LITTLE_ENDIAN32(val) \
00383    val = (((quint32)(val) & 0xFF000000U) >> 24) | \
00384          (((quint32)(val) & 0x00FF0000U) >> 8) | \
00385          (((quint32)(val) & 0x0000FF00U) << 8) | \
00386          (((quint32)(val) & 0x000000FFU) << 24)
00387 
00388 QList<KMimeMagicRule> KMimeTypeRepository::parseMagicFile(QIODevice* file, const QString& fileName) const
00389 {
00390     QList<KMimeMagicRule> rules;
00391     QByteArray header = file->read(12);
00392     if (header != QByteArray::fromRawData("MIME-Magic\0\n", 12)) {
00393         kWarning(servicesDebugArea()) << "Invalid magic file " << fileName << " starts with " << header;
00394         return rules;
00395     }
00396     QList<KMimeMagicMatch> matches; // toplevel matches (indent==0)
00397     int priority = 0; // to avoid warning
00398     QString mimeTypeName;
00399 
00400     Q_FOREVER {
00401         char ch = '\0';
00402         bool chOk = file->getChar(&ch);
00403 
00404         if (!chOk || ch == '[') {
00405             // Finish previous section
00406             if (!mimeTypeName.isEmpty()) {
00407                 rules.append(KMimeMagicRule(mimeTypeName, priority, matches));
00408                 matches.clear();
00409                 mimeTypeName.clear();
00410             }
00411             if (file->atEnd())
00412                 break; // done
00413 
00414             // Parse new section
00415             const QString line = QString::fromLatin1(file->readLine());
00416             const int pos = line.indexOf(QLatin1Char(':'));
00417             if (pos == -1) { // syntax error
00418                 kWarning(servicesDebugArea()) << "Syntax error in " << mimeTypeName
00419                                << " ':' not present in section name" << endl;
00420                 break;
00421             }
00422             priority = line.left(pos).toInt();
00423             mimeTypeName = line.mid(pos+1);
00424             mimeTypeName = mimeTypeName.left(mimeTypeName.length()-2); // remove ']\n'
00425             //kDebug(servicesDebugArea()) << "New rule for " << mimeTypeName
00426             //             << " with priority " << priority << endl;
00427         } else {
00428             // Parse line in the section
00429             // [ indent ] ">" start-offset "=" value
00430             //   [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
00431             qint64 indent = 0;
00432             if (ch != '>') {
00433                 indent = ch - '0';
00434                 ch = readNumber(indent, file);
00435                 if (ch != '>') {
00436                     kWarning(servicesDebugArea()) << "Invalid magic file " << fileName << " '>' not found, got " << ch << " at pos " << file->pos();
00437                     break;
00438                 }
00439             }
00440 
00441             KMimeMagicMatch match;
00442             match.m_rangeStart = 0;
00443             ch = readNumber(match.m_rangeStart, file);
00444             if (ch != '=') {
00445                 kWarning(servicesDebugArea()) << "Invalid magic file " << fileName << " '=' not found";
00446                 break;
00447             }
00448 
00449             char lengthBuffer[2];
00450             if (file->read(lengthBuffer, 2) != 2)
00451                 break;
00452             const short valueLength = ntohs(*(short*)lengthBuffer);
00453             //kDebug() << "indent=" << indent << " rangeStart=" << match.m_rangeStart
00454             //         << " valueLength=" << valueLength << endl;
00455 
00456             match.m_data.resize(valueLength);
00457             if (file->read(match.m_data.data(), valueLength) != valueLength)
00458                 break;
00459 
00460             match.m_rangeLength = 1;
00461             bool invalidLine = false;
00462 
00463             if (!file->getChar(&ch))
00464                 break;
00465             qint64 wordSize = 1;
00466 
00467             Q_FOREVER {
00468                 // We get 'ch' before coming here, or as part of the parsing in each case below.
00469                 switch (ch) {
00470                 case '\n':
00471                     break;
00472                 case '&':
00473                     match.m_mask.resize(valueLength);
00474                     if (file->read(match.m_mask.data(), valueLength) != valueLength)
00475                         invalidLine = true;
00476                     if (!file->getChar(&ch))
00477                         invalidLine = true;
00478                     break;
00479                 case '~': {
00480                     wordSize = 0;
00481                     ch = readNumber(wordSize, file);
00482                     //kDebug() << "wordSize=" << wordSize;
00483                     break;
00484                 }
00485                 case '+':
00486                     // Parse range length
00487                     match.m_rangeLength = 0;
00488                     ch = readNumber(match.m_rangeLength, file);
00489                     if (ch == '\n')
00490                         break;
00491                     // fall-through intended
00492                 default:
00493                     // "If an unknown character is found where a newline is expected
00494                     // then the whole line should be ignored (there will be no binary
00495                     // data after the new character, so the next line starts after the
00496                     // next "\n" character). This is for future extensions.", says spec
00497                     while (ch != '\n' && !file->atEnd()) {
00498                         file->getChar(&ch);
00499                     }
00500                     invalidLine = true;
00501                     kDebug(servicesDebugArea()) << "invalid line - garbage found - ch=" << ch;
00502                     break;
00503                 }
00504                 if (ch == '\n' || invalidLine)
00505                     break;
00506             }
00507             if (!invalidLine) {
00508                 // Finish match, doing byte-swapping on little endian hosts
00509 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
00510                 if (wordSize > 1) {
00511                     //kDebug() << "data before swapping: " << match.m_data;;
00512                     if ((wordSize != 2 && wordSize != 4) || (valueLength % wordSize != 0))
00513                         continue; // invalid word size
00514                     char* data = match.m_data.data();
00515                     char* mask = match.m_mask.data();
00516                     for (int i = 0; i < valueLength; i += wordSize) {
00517                         if (wordSize == 2)
00518                             MAKE_LITTLE_ENDIAN16( *((quint16 *) data + i) );
00519                         else if (wordSize == 4)
00520                             MAKE_LITTLE_ENDIAN32( *((quint32 *) data + i) );
00521                         if (!match.m_mask.isEmpty()) {
00522                             if (wordSize == 2)
00523                                 MAKE_LITTLE_ENDIAN16( *((quint16 *) mask + i) );
00524                             else if (wordSize == 4)
00525                                 MAKE_LITTLE_ENDIAN32( *((quint32 *) mask + i) );
00526                         }
00527                     }
00528                     //kDebug() << "data after swapping: " << match.m_data;
00529                 }
00530 #endif
00531                 // Append match at the right place depending on indent:
00532                 if (indent == 0) {
00533                     matches.append(match);
00534                 } else {
00535                     KMimeMagicMatch* m = &matches.last();
00536                     Q_ASSERT(m);
00537                     for (int i = 1 /* nothing to do for indent==1 */; i < indent; ++i) {
00538                         m = &m->m_subMatches.last();
00539                         Q_ASSERT(m);
00540                     }
00541                     m->m_subMatches.append(match);
00542                 }
00543             }
00544         }
00545     }
00546     return rules;
00547 }
00548 
00549 const KMimeTypeRepository::AliasesMap& KMimeTypeRepository::aliases()
00550 {
00551     QWriteLocker lock(&m_mutex);
00552     if (!m_aliasFilesParsed) {
00553         m_aliasFilesParsed = true;
00554 
00555         const QStringList aliasFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QLatin1String("aliases"));
00556         Q_FOREACH(const QString& fileName, aliasFiles) {
00557             QFile qfile(fileName);
00558             //kDebug(7021) << "Now parsing" << fileName;
00559             if (qfile.open(QIODevice::ReadOnly)) {
00560                 QTextStream stream(&qfile);
00561                 stream.setCodec("ISO 8859-1");
00562                 while (!stream.atEnd()) {
00563                     const QString line = stream.readLine();
00564                     if (line.isEmpty() || line[0] == QLatin1Char('#'))
00565                         continue;
00566                     const int pos = line.indexOf(QLatin1Char(' '));
00567                     if (pos == -1) // syntax error
00568                         continue;
00569                     const QString aliasTypeName = line.left(pos);
00570                     const QString parentTypeName = line.mid(pos+1);
00571                     Q_ASSERT(!aliasTypeName.isEmpty());
00572                     Q_ASSERT(!parentTypeName.isEmpty());
00573 
00574                     const KMimeType::Ptr realMimeType =
00575                         findMimeTypeByName(aliasTypeName, KMimeType::DontResolveAlias);
00576                     if (realMimeType) {
00577                         //kDebug(servicesDebugArea()) << "Ignoring alias" << aliasTypeName << "because also defined as a real mimetype";
00578                     } else {
00579                         m_aliases.insert(aliasTypeName, parentTypeName);
00580                     }
00581                 }
00582             }
00583         }
00584     }
00585     return m_aliases;
00586 }
00587 
00588 // Caller must lock m_mutex for write
00589 void KMimeTypeRepository::parseGlobs()
00590 {
00591     if (!m_globsFilesParsed) {
00592         m_globsFilesParsed = true;
00593         KMimeGlobsFileParser parser;
00594         m_globs = parser.parseGlobs();
00595     }
00596 }
00597 
00598 QStringList KMimeTypeRepository::patternsForMimetype(const QString& mimeType)
00599 {
00600     QWriteLocker lock(&m_mutex);
00601     if (!m_patternsMapCalculated) {
00602         m_patternsMapCalculated = true;
00603         parseGlobs();
00604         m_patterns = m_globs.patternsMap();
00605     }
00606     return m_patterns.value(mimeType);
00607 }
00608 
00609 static void errorMissingMimeTypes( const QStringList& _types )
00610 {
00611     KMessage::message( KMessage::Error, i18np( "Could not find mime type <resource>%2</resource>",
00612                 "Could not find mime types:\n<resource>%2</resource>", _types.count(), _types.join(QLatin1String("</resource>\n<resource>")) ) );
00613 }
00614 
00615 void KMimeTypeRepository::checkEssentialMimeTypes()
00616 {
00617     QWriteLocker lock(&m_mutex);
00618     if (m_mimeTypesChecked) // already done
00619         return;
00620 
00621     m_mimeTypesChecked = true; // must be done before building mimetypes
00622 
00623     // No Mime-Types installed ?
00624     // Lets do some rescue here.
00625     if (!checkMimeTypes()) {
00626         // Note that this messagebox is queued, so it will only be shown once getting back to the event loop
00627 
00628         // No mimetypes installed? Are you setting XDG_DATA_DIRS without including /usr/share in it?
00629         KMessage::message(KMessage::Error, i18n("No mime types installed. "
00630             "Check that shared-mime-info is installed, and that XDG_DATA_DIRS is not set, or includes /usr/share."));
00631         return; // no point in going any further
00632     }
00633 
00634     QStringList missingMimeTypes;
00635 
00636     if (!KMimeType::mimeType(QLatin1String("inode/directory")))
00637         missingMimeTypes.append(QLatin1String("inode/directory"));
00638 #ifndef Q_OS_WIN
00639     //if (!KMimeType::mimeType(QLatin1String("inode/directory-locked")))
00640     //  missingMimeTypes.append(QLatin1String("inode/directory-locked"));
00641     if (!KMimeType::mimeType(QLatin1String("inode/blockdevice")))
00642         missingMimeTypes.append(QLatin1String("inode/blockdevice"));
00643     if (!KMimeType::mimeType(QLatin1String("inode/chardevice")))
00644         missingMimeTypes.append(QLatin1String("inode/chardevice"));
00645     if (!KMimeType::mimeType(QLatin1String("inode/socket")))
00646         missingMimeTypes.append(QLatin1String("inode/socket"));
00647     if (!KMimeType::mimeType(QLatin1String("inode/fifo")))
00648         missingMimeTypes.append(QLatin1String("inode/fifo"));
00649 #endif
00650     if (!KMimeType::mimeType(QLatin1String("application/x-shellscript")))
00651         missingMimeTypes.append(QLatin1String("application/x-shellscript"));
00652     if (!KMimeType::mimeType(QLatin1String("application/x-executable")))
00653         missingMimeTypes.append(QLatin1String("application/x-executable"));
00654     if (!KMimeType::mimeType(QLatin1String("application/x-desktop")))
00655         missingMimeTypes.append(QLatin1String("application/x-desktop"));
00656 
00657     if (!missingMimeTypes.isEmpty())
00658         errorMissingMimeTypes(missingMimeTypes);
00659 }
00660 
00661 KMimeType::Ptr KMimeTypeRepository::defaultMimeTypePtr()
00662 {
00663     QWriteLocker lock(&m_mutex);
00664     if (!m_defaultMimeType) {
00665         // Try to find the default type
00666         KMimeType::Ptr mime = findMimeTypeByName(KMimeType::defaultMimeType());
00667         if (mime) {
00668             m_defaultMimeType = mime;
00669         } else {
00670             const QString defaultMimeType = KMimeType::defaultMimeType();
00671             errorMissingMimeTypes(QStringList(defaultMimeType));
00672             const QString pathDefaultMimeType = KGlobal::dirs()->locateLocal("xdgdata-mime", defaultMimeType+QLatin1String(".xml"));
00673             m_defaultMimeType = new KMimeType(pathDefaultMimeType, defaultMimeType, QLatin1String("mime"));
00674         }
00675     }
00676     return m_defaultMimeType;
00677 
00678 }
00679 
00680 bool KMimeTypeRepository::useFavIcons()
00681 {
00682     // this method will be called quite often, so better not read the config
00683     // again and again.
00684     m_mutex.lockForWrite();
00685     if (!m_useFavIconsChecked) {
00686         m_useFavIconsChecked = true;
00687         KConfigGroup cg( KGlobal::config(), "HTML Settings" );
00688         m_useFavIcons = cg.readEntry("EnableFavicon", true);
00689     }
00690     m_mutex.unlock();
00691     return m_useFavIcons;
00692 }
00693 
00694 static void addPlatformSpecificPkgConfigPath(QStringList& paths)
00695 {
00696 #if defined (Q_OS_FREEBSD)
00697     paths << QLatin1String("/usr/local/libdata/pkgconfig"); // FreeBSD
00698 #elif defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_SOLARIS)
00699     paths << QLatin1String("/usr/local/lib/pkgconfig"); // {Net,Open}BSD/OpenSolaris
00700 #elif defined (Q_OS_UNIX)
00701     paths << QLatin1String("/usr/share/pkgconfig"); // Linux and all other unix
00702 #endif
00703 }
00704 
00705 static int mimeDataBaseVersion()
00706 {
00707     // TODO: Remove the #idef'ed code below once the issue is fixed either
00708     // in QProcess or the shared-mime-info utility provides its version number.
00709 #ifdef Q_OS_UNIX
00710     // Try to read the version number from the shared-mime-info.pc file
00711     QStringList paths;
00712     const QByteArray pkgConfigPath = qgetenv("PKG_CONFIG_PATH");
00713     if (!pkgConfigPath.isEmpty()) {
00714         paths << QFile::decodeName(pkgConfigPath).split(QLatin1Char(':'), QString::SkipEmptyParts);
00715     }
00716 
00717     // Add platform specific hard-coded default paths to the list...
00718     addPlatformSpecificPkgConfigPath(paths);
00719 
00720     Q_FOREACH(const QString& path, paths) {
00721         const QString fileName = path + QLatin1String("/shared-mime-info.pc");
00722         if (!QFile::exists(fileName)) {
00723             continue;
00724         }
00725 
00726         QFile file (fileName);
00727         if (!file.open(QIODevice::ReadOnly)) {
00728             break;
00729         }
00730 
00731         while (!file.atEnd()) {
00732             const QByteArray line = file.readLine().simplified();
00733             if (!line.startsWith("Version")) { // krazy:exclude=strings
00734                 continue;
00735             }
00736             QRegExp versionRe(QString::fromLatin1("Version: (\\d+)\\.(\\d+)(\\.(\\d+))?"));
00737             if (versionRe.indexIn(QString::fromLocal8Bit(line)) > -1) {
00738                 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
00739             }
00740         }
00741     }
00742 #endif
00743 
00744     // Execute "update-mime-database -v" to determine version number.
00745     // NOTE: On *nix, the code below is known to cause freezes/hangs in apps
00746     // that block signals. See https://bugs.kde.org/show_bug.cgi?id=260719.
00747     const QString umd = KStandardDirs::findExe(QString::fromLatin1("update-mime-database"));
00748     if (umd.isEmpty()) {
00749         kWarning(servicesDebugArea()) << "update-mime-database not found!";
00750         return -1;
00751     }
00752 
00753     QProcess smi;
00754     smi.start(umd, QStringList() << QString::fromLatin1("-v"));
00755     smi.waitForStarted();
00756     smi.waitForFinished();
00757     const QString out = QString::fromLocal8Bit(smi.readAllStandardError());
00758     QRegExp versionRe(QString::fromLatin1("update-mime-database \\(shared-mime-info\\) (\\d+)\\.(\\d+)(\\.(\\d+))?"));
00759     if (versionRe.indexIn(out) > -1) {
00760         return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
00761     }
00762 
00763     kWarning(servicesDebugArea()) << "Unexpected version scheme from update-mime-database -v: got" << out;
00764     return -1;
00765 }
00766 
00767 int KMimeTypeRepository::sharedMimeInfoVersion()
00768 {
00769     m_mutex.lockForWrite();
00770     if (m_sharedMimeInfoVersion == 0)
00771         m_sharedMimeInfoVersion = mimeDataBaseVersion();
00772     m_mutex.unlock();
00773     return m_sharedMimeInfoVersion;
00774 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:49:31 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal