KDECore
kmimetype.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 * Copyright (C) 1999 Waldo Bastian <bastian@kde.org> 00003 * 2000-2007 David Faure <faure@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License version 2 as published by the Free Software Foundation; 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 "kmimetype.h" 00021 #include "kmimetype_p.h" 00022 #include "kmimetypefactory.h" 00023 #include "kmimetyperepository_p.h" 00024 00025 #include <kdebug.h> 00026 #include <kde_file.h> // KDE::stat 00027 #include <kdeversion.h> // KDE_MAKE_VERSION 00028 #include <klocale.h> 00029 #include <kprotocolinfo.h> 00030 #include <kprotocolinfofactory.h> 00031 #include <kstandarddirs.h> 00032 #include <kurl.h> 00033 00034 #include <QtCore/QFile> 00035 #include <QtDBus/QtDBus> 00036 #include <QBuffer> 00037 00038 extern int servicesDebugArea(); 00039 00040 template class KSharedPtr<KMimeType>; 00041 00042 KMimeType::Ptr KMimeType::defaultMimeTypePtr() 00043 { 00044 return KMimeTypeRepository::self()->defaultMimeTypePtr(); 00045 } 00046 00047 bool KMimeType::isDefault() const 00048 { 00049 return name() == defaultMimeType(); 00050 } 00051 00052 void KMimeType::checkEssentialMimeTypes() 00053 { 00054 KMimeTypeRepository::self()->checkEssentialMimeTypes(); 00055 } 00056 00057 KMimeType::Ptr KMimeType::mimeType(const QString& name, FindByNameOption options) 00058 { 00059 return KMimeTypeRepository::self()->findMimeTypeByName(name, options); 00060 } 00061 00062 KMimeType::List KMimeType::allMimeTypes() 00063 { 00064 // This could be done faster... 00065 KMimeType::List lst; 00066 Q_FOREACH(const QString& mimeType, KMimeTypeFactory::self()->allMimeTypes()) { 00067 if (!mimeType.startsWith(QLatin1String("x-scheme-handler"))) 00068 lst.append(KMimeType::mimeType(mimeType)); 00069 } 00070 return lst; 00071 } 00072 00073 bool KMimeType::isBufferBinaryData(const QByteArray& data) 00074 { 00075 // Check the first 32 bytes (see shared-mime spec) 00076 const char* p = data.data(); 00077 const int end = qMin(32, data.size()); 00078 for (int i = 0; i < end; ++i) { 00079 if ((unsigned char)(p[i]) < 32 && p[i] != 9 && p[i] != 10 && p[i] != 13) // ASCII control character 00080 return true; 00081 } 00082 return false; 00083 } 00084 00085 static KMimeType::Ptr findFromMode( const QString& path /*only used if is_local_file*/, 00086 mode_t mode /*0 if unknown*/, 00087 bool is_local_file ) 00088 { 00089 if ( is_local_file && (mode == 0 || mode == (mode_t)-1) ) { 00090 KDE_struct_stat buff; 00091 if ( KDE::stat( path, &buff ) != -1 ) 00092 mode = buff.st_mode; 00093 } 00094 00095 if ( S_ISDIR( mode ) ) { 00096 // KDE4 TODO: use an overlay instead 00097 #if 0 00098 // Special hack for local files. We want to see whether we 00099 // are allowed to enter the directory 00100 if ( is_local_file ) 00101 { 00102 if ( KDE::access( path, R_OK ) == -1 ) 00103 return KMimeType::mimeType( "inode/directory-locked" ); 00104 } 00105 #endif 00106 return KMimeType::mimeType( QLatin1String("inode/directory") ); 00107 } 00108 if ( S_ISCHR( mode ) ) 00109 return KMimeType::mimeType( QLatin1String("inode/chardevice") ); 00110 if ( S_ISBLK( mode ) ) 00111 return KMimeType::mimeType( QLatin1String("inode/blockdevice") ); 00112 if ( S_ISFIFO( mode ) ) 00113 return KMimeType::mimeType( QLatin1String("inode/fifo") ); 00114 if ( S_ISSOCK( mode ) ) 00115 return KMimeType::mimeType( QLatin1String("inode/socket") ); 00116 #ifdef Q_OS_WIN 00117 // FIXME: distinguish between mounted & unmounted 00118 int size = path.size(); 00119 if ( size == 2 || size == 3 ) { 00120 //GetDriveTypeW is not defined in wince 00121 #ifndef _WIN32_WCE 00122 unsigned int type = GetDriveTypeW( (LPCWSTR) path.utf16() ); 00123 switch( type ) { 00124 case DRIVE_REMOVABLE: 00125 return KMimeType::mimeType( QLatin1String("media/floppy_mounted") ); 00126 case DRIVE_FIXED: 00127 return KMimeType::mimeType( QLatin1String("media/hdd_mounted") ); 00128 case DRIVE_REMOTE: 00129 return KMimeType::mimeType( QLatin1String("media/smb_mounted") ); 00130 case DRIVE_CDROM: 00131 return KMimeType::mimeType( QLatin1String("media/cdrom_mounted") ); 00132 case DRIVE_RAMDISK: 00133 return KMimeType::mimeType( QLatin1String("media/hdd_mounted") ); 00134 default: 00135 break; 00136 }; 00137 #else 00138 return KMimeType::mimeType( QLatin1String("media/hdd_mounted") ); 00139 #endif 00140 } 00141 #endif 00142 // remote executable file? stop here (otherwise findFromContent can do that better for local files) 00143 if ( !is_local_file && S_ISREG( mode ) && ( mode & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) ) 00144 return KMimeType::mimeType( QLatin1String("application/x-executable") ); 00145 00146 return KMimeType::Ptr(); 00147 } 00148 00149 /* 00150 00151 As agreed on the XDG list (and unlike the current shared-mime spec): 00152 00153 Glob-matching should prefer derived mimetype over base mimetype, and longer matches 00154 over shorter ones. However if two globs of the same length match the file, and the two 00155 matches are not related in the inheritance tree, then we have a "glob conflict", which 00156 will be resolved below. 00157 00158 If only one glob matches, use that 00159 00160 If no glob matches, sniff and use that 00161 00162 If several globs matches, and sniffing gives a result we do: 00163 if sniffed prio >= 80, use sniffed type 00164 for glob_match in glob_matches: 00165 if glob_match is subclass or equal to sniffed_type, use glob_match 00166 00167 If several globs matches, and sniffing fails, or doesn't help: 00168 fall back to the first glob match 00169 00170 This algorithm only sniffs when there is some uncertainty with the 00171 extension matching (thus, it's usable for a file manager). 00172 00173 Note: in KDE we want the file views to sniff in a delayed manner. 00174 So there's also a fast mode which is: 00175 if no glob matches, or if more than one glob matches, use default mimetype and mark as "can be refined". 00176 00177 */ 00178 00179 KMimeType::Ptr KMimeType::findByUrlHelper( const KUrl& _url, mode_t mode, 00180 bool is_local_file, 00181 QIODevice* device, 00182 int* accuracy ) 00183 { 00184 checkEssentialMimeTypes(); 00185 const QString path = is_local_file ? _url.toLocalFile() : _url.path(); 00186 00187 if (accuracy) 00188 *accuracy = 100; 00189 00190 // Look at mode first 00191 KMimeType::Ptr mimeFromMode = findFromMode( path, mode, is_local_file ); 00192 if (mimeFromMode) 00193 return mimeFromMode; 00194 00195 // First try to find out by looking at the filename (if there's one) 00196 const QString fileName( _url.fileName() ); 00197 QStringList mimeList; 00198 if ( !fileName.isEmpty() && !path.endsWith( QLatin1Char('/') ) ) { 00199 // and if we can trust it (e.g. don't trust *.pl over HTTP, could be anything) 00200 if ( is_local_file || _url.hasSubUrl() || // Explicitly trust suburls 00201 KProtocolInfo::determineMimetypeFromExtension( _url.protocol() ) ) { 00202 mimeList = KMimeTypeRepository::self()->findFromFileName( fileName ); 00203 // Found one glob match exactly: OK, use that. 00204 // We disambiguate multiple glob matches by sniffing, below. 00205 if ( mimeList.count() == 1 ) { 00206 const QString selectedMime = mimeList.at(0); 00207 KMimeType::Ptr mime = mimeType(selectedMime); 00208 if (!mime) { 00209 // #265188 - this can happen when an old globs file is lying around after 00210 // the packages xml file was removed. 00211 kWarning() << "Glob file refers to" << selectedMime << "but this mimetype does not exist!"; 00212 mimeList.clear(); 00213 } else { 00214 return mime; 00215 } 00216 } 00217 } 00218 } 00219 00220 if ( device && !device->isOpen() ) { 00221 if ( !device->open(QIODevice::ReadOnly) ) { 00222 device = 0; 00223 } 00224 } 00225 00226 // Try the magic matches (if we can read the data) 00227 QByteArray beginning; 00228 if ( device ) { 00229 int magicAccuracy; 00230 KMimeType::Ptr mime = KMimeTypeRepository::self()->findFromContent(device, &magicAccuracy, beginning); 00231 // mime can't be 0, except in case of install problems. 00232 // However we get magicAccuracy==0 for octet-stream, i.e. no magic match found. 00233 //kDebug(servicesDebugArea()) << "findFromContent said" << (mime?mime->name():QString()) << "with accuracy" << magicAccuracy; 00234 if (mime && magicAccuracy > 0) { 00235 00236 // Disambiguate conflicting extensions (if magic found something and the magicrule was <80) 00237 if (magicAccuracy < 80 && !mimeList.isEmpty()) { 00238 // "for glob_match in glob_matches:" 00239 // "if glob_match is subclass or equal to sniffed_type, use glob_match" 00240 const QString sniffedMime = mime->name(); 00241 foreach(const QString &m, mimeList) { 00242 KMimeType::Ptr mimeFromPattern = KMimeType::mimeType(m); 00243 //kDebug(servicesDebugArea()) << "sniffedMime=" << sniffedMime << "mimeFromPattern=" << mimeFromPattern->name(); 00244 if (mimeFromPattern && mimeFromPattern->is(sniffedMime)) { 00245 // We have magic + pattern pointing to this, so it's a pretty good match 00246 if (accuracy) 00247 *accuracy = 100; 00248 return mimeFromPattern; 00249 } 00250 } 00251 } 00252 00253 if (accuracy) 00254 *accuracy = magicAccuracy; 00255 return mime; 00256 } 00257 } 00258 00259 // Not a local file, or no magic allowed, or magic found nothing 00260 00261 // Maybe we had multiple matches from globs? 00262 if (!mimeList.isEmpty()) { 00263 if (accuracy) 00264 *accuracy = 20; 00265 // We have to pick one... 00266 // At least make this deterministic 00267 qSort(mimeList.begin(), mimeList.end()); 00268 Q_FOREACH(const QString& mimeName, mimeList) { 00269 KMimeType::Ptr mime = mimeType(mimeName); 00270 if (!mime) 00271 kWarning() << "Glob file refers to" << mimeName << "but this mimetype does not exist!"; 00272 else 00273 return mime; 00274 } 00275 } 00276 00277 // Find a fallback from the protocol 00278 if (accuracy) 00279 *accuracy = 10; 00280 // ## this breaks with proxying; find a way to move proxying info to kdecore's kprotocolinfo? 00281 // ## or hardcode the only case of proxying that we ever had? (ftp-over-http) 00282 KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() ); 00283 QString def; 00284 if (prot) 00285 def = prot->defaultMimeType(); 00286 if ( !def.isEmpty() && def != defaultMimeType() ) { 00287 // The protocol says it always returns a given mimetype (e.g. text/html for "man:") 00288 KMimeType::Ptr mime = mimeType( def ); 00289 if (mime) 00290 return mime; 00291 } 00292 if ( path.endsWith( QLatin1Char('/') ) || path.isEmpty() ) { 00293 // We have no filename at all. Maybe the protocol has a setting for 00294 // which mimetype this means (e.g. directory). 00295 // For HTTP (def==defaultMimeType()) we don't assume anything, 00296 // because of redirections (e.g. freshmeat downloads). 00297 if ( def.isEmpty() ) { 00298 // Assume inode/directory, if the protocol supports listing. 00299 KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() ); 00300 if ( prot && prot->supportsListing() ) { 00301 KMimeType::Ptr mime = mimeType( QLatin1String("inode/directory") ); 00302 if (mime) { // only 0 if no mimetypes installed 00303 return mime; 00304 } 00305 } else 00306 return defaultMimeTypePtr(); // == 'no idea', e.g. for "data:,foo/" 00307 } 00308 } 00309 00310 if (accuracy) 00311 *accuracy = 0; 00312 return defaultMimeTypePtr(); 00313 } 00314 00315 KMimeType::Ptr KMimeType::findByUrl( const KUrl& url, mode_t mode, 00316 bool is_local_file, bool fast_mode, 00317 int *accuracy ) 00318 { 00319 if ( !is_local_file && url.isLocalFile() ) 00320 is_local_file = true; 00321 if (is_local_file && !fast_mode) { 00322 QFile file(url.toLocalFile()); 00323 return findByUrlHelper(url, mode, is_local_file, &file, accuracy); 00324 } 00325 return findByUrlHelper(url, mode, is_local_file, 0, accuracy); 00326 } 00327 00328 KMimeType::Ptr KMimeType::findByPath( const QString& path, mode_t mode, 00329 bool fast_mode, int* accuracy ) 00330 { 00331 KUrl url; 00332 url.setPath(path); 00333 return findByUrl(url, mode, true, fast_mode, accuracy); 00334 } 00335 00336 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, const QByteArray& data, 00337 mode_t mode, int* accuracy ) 00338 { 00339 KUrl url; 00340 url.setPath(name); 00341 QBuffer buffer(const_cast<QByteArray *>(&data)); 00342 return findByUrlHelper(url, mode, false, &buffer, accuracy); 00343 } 00344 00345 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, QIODevice* device, 00346 mode_t mode, int* accuracy ) 00347 { 00348 KUrl url; 00349 url.setPath(name); 00350 return findByUrlHelper(url, mode, false, device, accuracy); 00351 } 00352 00353 QString KMimeType::extractKnownExtension(const QString &fileName) 00354 { 00355 QString pattern; 00356 KMimeTypeRepository::self()->findFromFileName( fileName, &pattern ); 00357 return pattern; 00358 } 00359 00360 KMimeType::Ptr KMimeType::findByContent( const QByteArray &data, int *accuracy ) 00361 { 00362 QBuffer buffer(const_cast<QByteArray *>(&data)); 00363 buffer.open(QIODevice::ReadOnly); 00364 QByteArray cache; 00365 return KMimeTypeRepository::self()->findFromContent(&buffer, accuracy, cache); 00366 } 00367 00368 KMimeType::Ptr KMimeType::findByContent( QIODevice* device, int* accuracy ) 00369 { 00370 QByteArray cache; 00371 return KMimeTypeRepository::self()->findFromContent(device, accuracy, cache); 00372 } 00373 00374 KMimeType::Ptr KMimeType::findByFileContent( const QString &fileName, int *accuracy ) 00375 { 00376 checkEssentialMimeTypes(); 00377 00378 QFile device(fileName); 00379 // Look at mode first 00380 KMimeType::Ptr mimeFromMode = findFromMode( fileName, 0, true ); 00381 if (mimeFromMode) { 00382 if (accuracy) 00383 *accuracy = 100; 00384 return mimeFromMode; 00385 } 00386 if (!device.open(QIODevice::ReadOnly)) { 00387 if (accuracy) 00388 *accuracy = 0; 00389 return KMimeType::defaultMimeTypePtr(); 00390 } 00391 00392 QByteArray cache; 00393 return KMimeTypeRepository::self()->findFromContent(&device, accuracy, cache); 00394 } 00395 00396 bool KMimeType::isBinaryData( const QString &fileName ) 00397 { 00398 QFile file(fileName); 00399 if (!file.open(QIODevice::ReadOnly)) 00400 return false; // err, whatever 00401 const QByteArray data = file.read(32); 00402 return isBufferBinaryData(data); 00403 } 00404 00405 KMimeType::KMimeType( KMimeTypePrivate &dd, const QString& name, 00406 const QString& comment ) 00407 : KServiceType( dd, name, comment ) 00408 { 00409 } 00410 00411 KMimeType::KMimeType( const QString & fullpath, const QString& name, 00412 const QString& comment ) 00413 : KServiceType( *new KMimeTypePrivate(fullpath), name, comment ) 00414 { 00415 } 00416 00417 KMimeType::KMimeType( KMimeTypePrivate &dd) 00418 : KServiceType(dd) 00419 { 00420 } 00421 00422 KMimeType::KMimeType( QDataStream& _str, int offset ) 00423 : KServiceType( *new KMimeTypePrivate(_str, offset )) 00424 { 00425 } 00426 00427 void KMimeTypePrivate::save( QDataStream& _str ) 00428 { 00429 KServiceTypePrivate::save( _str ); 00430 // Warning adding fields here involves a binary incompatible change - update version 00431 // number in ksycoca.h. Never remove fields. 00432 _str << m_lstPatterns << QString() << QStringList() << m_iconName; 00433 } 00434 00435 QVariant KMimeTypePrivate::property( const QString& _name ) const 00436 { 00437 if ( _name == QLatin1String("Patterns") ) 00438 return QVariant( m_lstPatterns ); 00439 if ( _name == QLatin1String("Icon") ) 00440 return QVariant( iconName(KUrl()) ); 00441 00442 return KServiceTypePrivate::property( _name ); 00443 } 00444 00445 QStringList KMimeTypePrivate::propertyNames() const 00446 { 00447 QStringList res = KServiceTypePrivate::propertyNames(); 00448 res.append( QString::fromLatin1("Patterns") ); 00449 res.append( QString::fromLatin1("Icon") ); 00450 return res; 00451 } 00452 00453 KMimeType::~KMimeType() 00454 { 00455 } 00456 00457 QString KMimeType::iconNameForUrl( const KUrl & _url, mode_t mode ) 00458 { 00459 const KMimeType::Ptr mt = findByUrl( _url, mode, _url.isLocalFile(), 00460 false /*HACK*/); 00461 if (!mt) { 00462 return QString(); 00463 } 00464 static const QString& unknown = KGlobal::staticQString("unknown"); 00465 const QString mimeTypeIcon = mt->iconName( _url ); 00466 QString i = mimeTypeIcon; 00467 00468 // if we don't find an icon, maybe we can use the one for the protocol 00469 if ( i == unknown || i.isEmpty() || mt->name() == defaultMimeType() 00470 // and for the root of the protocol (e.g. trash:/) the protocol icon has priority over the mimetype icon 00471 || _url.path().length() <= 1 ) 00472 { 00473 i = favIconForUrl( _url ); // maybe there is a favicon? 00474 00475 if ( i.isEmpty() ) 00476 i = KProtocolInfo::icon( _url.protocol() ); 00477 00478 // root of protocol: if we found nothing, revert to mimeTypeIcon (which is usually "folder") 00479 if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) ) 00480 i = mimeTypeIcon; 00481 } 00482 return !i.isEmpty() ? i : unknown; 00483 } 00484 00485 QString KMimeType::favIconForUrl( const KUrl& url ) 00486 { 00487 if (url.isLocalFile() 00488 || !url.protocol().startsWith(QLatin1String("http")) 00489 || !KMimeTypeRepository::self()->useFavIcons()) 00490 return QString(); 00491 00492 QDBusInterface kded( QString::fromLatin1("org.kde.kded"), 00493 QString::fromLatin1("/modules/favicons"), 00494 QString::fromLatin1("org.kde.FavIcon") ); 00495 QDBusReply<QString> result = kded.call( QString::fromLatin1("iconForUrl"), url.url() ); 00496 return result; // default is QString() 00497 } 00498 00499 QString KMimeType::comment( const KUrl &url) const 00500 { 00501 Q_D(const KMimeType); 00502 return d->comment(url); 00503 } 00504 00505 #ifndef KDE_NO_DEPRECATED 00506 QString KMimeType::parentMimeType() const 00507 { 00508 const QStringList parents = parentMimeTypes(); 00509 if (!parents.isEmpty()) 00510 return parents.first(); 00511 return QString(); 00512 } 00513 #endif 00514 00515 bool KMimeTypePrivate::inherits(const QString& mime) const 00516 { 00517 QStack<QString> toCheck; 00518 toCheck.push(m_strName); 00519 while (!toCheck.isEmpty()) { 00520 const QString current = toCheck.pop(); 00521 if (current == mime) 00522 return true; 00523 Q_FOREACH(const QString& parent, KMimeTypeRepository::self()->parents(current)) { 00524 toCheck.push(parent); 00525 } 00526 } 00527 return false; 00528 } 00529 00530 bool KMimeType::is( const QString& mimeTypeName ) const 00531 { 00532 Q_D(const KMimeType); 00533 if (name() == mimeTypeName) 00534 return true; 00535 const QString mime = KMimeTypeRepository::self()->canonicalName(mimeTypeName); 00536 return d->inherits(mime); 00537 } 00538 00539 QStringList KMimeType::parentMimeTypes() const 00540 { 00541 Q_D(const KMimeType); 00542 return KMimeTypeRepository::self()->parents(d->m_strName); 00543 } 00544 00545 static void collectParentMimeTypes(const QString& mime, QStringList& allParents) 00546 { 00547 QStringList parents = KMimeTypeRepository::self()->parents(mime); 00548 Q_FOREACH(const QString& parent, parents) { 00549 // I would use QSet, but since order matters I better not 00550 if (!allParents.contains(parent)) 00551 allParents.append(parent); 00552 } 00553 // We want a breadth-first search, so that the least-specific parent (octet-stream) is last 00554 // This means iterating twice, unfortunately. 00555 Q_FOREACH(const QString& parent, parents) { 00556 collectParentMimeTypes(parent, allParents); 00557 } 00558 } 00559 00560 QStringList KMimeType::allParentMimeTypes() const 00561 { 00562 Q_D(const KMimeType); 00563 QStringList allParents; 00564 const QString canonical = KMimeTypeRepository::self()->resolveAlias(name()); 00565 if (!canonical.isEmpty()) 00566 allParents.append(canonical); 00567 collectParentMimeTypes(d->m_strName, allParents); 00568 return allParents; 00569 } 00570 00571 QString KMimeType::defaultMimeType() 00572 { 00573 static const QString & s_strDefaultMimeType = 00574 KGlobal::staticQString( "application/octet-stream" ); 00575 return s_strDefaultMimeType; 00576 } 00577 00578 QString KMimeType::iconName( const KUrl& url) const 00579 { 00580 Q_D(const KMimeType); 00581 return d->iconName(url); 00582 } 00583 00584 QStringList KMimeType::patterns() const 00585 { 00586 Q_D(const KMimeType); 00587 d->ensureXmlDataLoaded(); 00588 return d->m_lstPatterns; 00589 } 00590 00591 // loads comment, icon, mainPattern, m_lstPatterns 00592 void KMimeTypePrivate::ensureXmlDataLoaded() const 00593 { 00594 if (m_xmlDataLoaded) 00595 return; 00596 00597 m_xmlDataLoaded = true; 00598 00599 const QString file = m_strName + QLatin1String(".xml"); 00600 const QStringList mimeFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", file); 00601 if (mimeFiles.isEmpty()) { 00602 kWarning() << "No file found for" << file << ", even though the file appeared in a directory listing."; 00603 kWarning() << "Either it was just removed, or the directory doesn't have executable permission..."; 00604 kWarning() << KGlobal::dirs()->resourceDirs("xdgdata-mime"); 00605 return; 00606 } 00607 00608 QString comment; 00609 QString mainPattern; 00610 const QStringList languageList = KGlobal::locale()->languageList(); 00611 QString preferredLanguage = languageList.first(); 00612 QMap<QString, QString> commentsByLanguage; 00613 00614 QListIterator<QString> mimeFilesIter(mimeFiles); 00615 mimeFilesIter.toBack(); 00616 while (mimeFilesIter.hasPrevious()) { // global first, then local. 00617 const QString fullPath = mimeFilesIter.previous(); 00618 QFile qfile(fullPath); 00619 if (!qfile.open(QFile::ReadOnly)) 00620 continue; 00621 00622 QXmlStreamReader xml(&qfile); 00623 if (xml.readNextStartElement()) { 00624 if (xml.name() != "mime-type") { 00625 continue; 00626 } 00627 const QString name = xml.attributes().value(QLatin1String("type")).toString(); 00628 if (name.isEmpty()) 00629 continue; 00630 if (name != m_strName) { 00631 kWarning() << "Got name" << name << "in file" << file << "expected" << m_strName; 00632 } 00633 00634 while (xml.readNextStartElement()) { 00635 const QStringRef tag = xml.name(); 00636 if (tag == "comment") { 00637 QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString(); 00638 const QString text = xml.readElementText(); 00639 if (lang.isEmpty()) { 00640 lang = QLatin1String("en_US"); 00641 } 00642 if (lang == preferredLanguage) { 00643 comment = text; 00644 } else { 00645 commentsByLanguage.insert(lang, text); 00646 } 00647 continue; // we called readElementText, so we're at the EndElement already. 00648 } else if (tag == "icon") { // as written out by shared-mime-info >= 0.40 00649 m_iconName = xml.attributes().value(QLatin1String("name")).toString(); 00650 } else if (tag == "glob-deleteall") { // as written out by shared-mime-info >= 0.70 00651 mainPattern.clear(); 00652 m_lstPatterns.clear(); 00653 } else if (tag == "glob") { // as written out by shared-mime-info >= 0.70 00654 const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString(); 00655 if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) { 00656 mainPattern = pattern; 00657 } 00658 if (!m_lstPatterns.contains(pattern)) 00659 m_lstPatterns.append(pattern); 00660 } 00661 xml.skipCurrentElement(); 00662 } 00663 if (xml.name() != "mime-type") { 00664 kFatal() << "Programming error in KMimeType XML loading, please create a bug report on http://bugs.kde.org and attach the file" << fullPath; 00665 } 00666 } 00667 } 00668 00669 if (comment.isEmpty()) { 00670 Q_FOREACH(const QString& lang, languageList) { 00671 const QString comm = commentsByLanguage.value(lang); 00672 if (!comm.isEmpty()) { 00673 comment = comm; 00674 break; 00675 } 00676 const int pos = lang.indexOf(QLatin1Char('_')); 00677 if (pos != -1) { 00678 // "pt_BR" not found? try just "pt" 00679 const QString shortLang = lang.left(pos); 00680 const QString comm = commentsByLanguage.value(shortLang); 00681 if (!comm.isEmpty()) { 00682 comment = comm; 00683 break; 00684 } 00685 } 00686 } 00687 if (comment.isEmpty()) { 00688 kWarning() << "Missing <comment> field in" << file; 00689 } 00690 } 00691 m_strComment = comment; 00692 00693 const bool globsInXml = (KMimeType::sharedMimeInfoVersion() >= KDE_MAKE_VERSION(0, 70, 0)); 00694 if (globsInXml) { 00695 if (!mainPattern.isEmpty() && m_lstPatterns.first() != mainPattern) { 00696 // ensure it's first in the list of patterns 00697 m_lstPatterns.removeAll(mainPattern); 00698 m_lstPatterns.prepend(mainPattern); 00699 } 00700 } else { 00701 // Fallback: get the patterns from the globs file 00702 m_lstPatterns = KMimeTypeRepository::self()->patternsForMimetype(m_strName); 00703 } 00704 } 00705 00706 QString KMimeType::userSpecifiedIconName() const 00707 { 00708 Q_D(const KMimeType); 00709 d->ensureXmlDataLoaded(); 00710 return d->m_iconName; 00711 } 00712 00713 int KMimeType::sharedMimeInfoVersion() 00714 { 00715 return KMimeTypeRepository::self()->sharedMimeInfoVersion(); 00716 } 00717 00718 QString KMimeType::mainExtension() const 00719 { 00720 Q_D(const KMimeType); 00721 00722 #if 1 // HACK START - can be removed once shared-mime-info >= 0.70 is used/required. 00723 // The idea was: first usable pattern from m_lstPatterns. 00724 // But update-mime-database makes a mess of the order of the patterns, 00725 // because it uses a hash internally. 00726 static const struct { const char* mime; const char* extension; } s_hardcodedMimes[] = { 00727 { "text/plain", ".txt" } }; 00728 if (d->m_lstPatterns.count() > 1) { 00729 const QByteArray me = name().toLatin1(); 00730 for (uint i = 0; i < sizeof(s_hardcodedMimes)/sizeof(*s_hardcodedMimes); ++i) { 00731 if (me == s_hardcodedMimes[i].mime) 00732 return QString::fromLatin1(s_hardcodedMimes[i].extension); 00733 } 00734 } 00735 #endif // HACK END 00736 00737 Q_FOREACH(const QString& pattern, patterns()) { 00738 // Skip if if looks like: README or *. or *.* 00739 // or *.JP*G or *.JP? 00740 if (pattern.startsWith(QLatin1String("*.")) && 00741 pattern.length() > 2 && 00742 pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) { 00743 return pattern.mid(1); 00744 } 00745 } 00746 // TODO we should also look into the parent mimetype's patterns, no? 00747 return QString(); 00748 } 00749 00750 bool KMimeType::matchFileName( const QString &filename, const QString &pattern ) 00751 { 00752 return KMimeTypeRepository::matchFileName( filename, pattern ); 00753 } 00754 00755 int KMimeTypePrivate::serviceOffersOffset() const 00756 { 00757 return KMimeTypeFactory::self()->serviceOffersOffset(name()); 00758 }
KDE 4.7 API Reference