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
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.