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 }
KDE 4.6 API Reference