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