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 return mimeType(mimeList.first()); 00207 } 00208 } 00209 } 00210 00211 if ( device && !device->isOpen() ) { 00212 if ( !device->open(QIODevice::ReadOnly) ) { 00213 device = 0; 00214 } 00215 } 00216 00217 // Try the magic matches (if we can read the data) 00218 QByteArray beginning; 00219 if ( device ) { 00220 int magicAccuracy; 00221 KMimeType::Ptr mime = KMimeTypeRepository::self()->findFromContent(device, &magicAccuracy, beginning); 00222 // mime can't be 0, except in case of install problems. 00223 // However we get magicAccuracy==0 for octet-stream, i.e. no magic match found. 00224 //kDebug(servicesDebugArea()) << "findFromContent said" << (mime?mime->name():QString()) << "with accuracy" << magicAccuracy; 00225 if (mime && magicAccuracy > 0) { 00226 00227 // Disambiguate conflicting extensions (if magic found something and the magicrule was <80) 00228 if (magicAccuracy < 80 && !mimeList.isEmpty()) { 00229 // "for glob_match in glob_matches:" 00230 // "if glob_match is subclass or equal to sniffed_type, use glob_match" 00231 const QString sniffedMime = mime->name(); 00232 foreach(const QString &m, mimeList) { 00233 KMimeType::Ptr mimeFromPattern = KMimeType::mimeType(m); 00234 //kDebug(servicesDebugArea()) << "sniffedMime=" << sniffedMime << "mimeFromPattern=" << mimeFromPattern->name(); 00235 if (mimeFromPattern->is(sniffedMime)) { 00236 // We have magic + pattern pointing to this, so it's a pretty good match 00237 if (accuracy) 00238 *accuracy = 100; 00239 return mimeFromPattern; 00240 } 00241 } 00242 } 00243 00244 if (accuracy) 00245 *accuracy = magicAccuracy; 00246 return mime; 00247 } 00248 } 00249 00250 // Not a local file, or no magic allowed, or magic found nothing 00251 00252 // Maybe we had multiple matches from globs? 00253 if (!mimeList.isEmpty()) { 00254 if (accuracy) 00255 *accuracy = 20; 00256 // We have to pick one... 00257 // At least make this deterministic 00258 qSort(mimeList.begin(), mimeList.end()); 00259 return mimeType(mimeList.first()); 00260 } 00261 00262 // Find a fallback from the protocol 00263 if (accuracy) 00264 *accuracy = 10; 00265 // ## this breaks with proxying; find a way to move proxying info to kdecore's kprotocolinfo? 00266 // ## or hardcode the only case of proxying that we ever had? (ftp-over-http) 00267 KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() ); 00268 QString def; 00269 if (prot) 00270 def = prot->defaultMimeType(); 00271 if ( !def.isEmpty() && def != defaultMimeType() ) { 00272 // The protocol says it always returns a given mimetype (e.g. text/html for "man:") 00273 KMimeType::Ptr mime = mimeType( def ); 00274 if (mime) 00275 return mime; 00276 } 00277 if ( path.endsWith( QLatin1Char('/') ) || path.isEmpty() ) { 00278 // We have no filename at all. Maybe the protocol has a setting for 00279 // which mimetype this means (e.g. directory). 00280 // For HTTP (def==defaultMimeType()) we don't assume anything, 00281 // because of redirections (e.g. freshmeat downloads). 00282 if ( def.isEmpty() ) { 00283 // Assume inode/directory, if the protocol supports listing. 00284 KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() ); 00285 if ( prot && prot->supportsListing() ) { 00286 KMimeType::Ptr mime = mimeType( QLatin1String("inode/directory") ); 00287 if (mime) { // only 0 if no mimetypes installed 00288 return mime; 00289 } 00290 } else 00291 return defaultMimeTypePtr(); // == 'no idea', e.g. for "data:,foo/" 00292 } 00293 } 00294 00295 if (accuracy) 00296 *accuracy = 0; 00297 return defaultMimeTypePtr(); 00298 } 00299 00300 KMimeType::Ptr KMimeType::findByUrl( const KUrl& url, mode_t mode, 00301 bool is_local_file, bool fast_mode, 00302 int *accuracy ) 00303 { 00304 if ( !is_local_file && url.isLocalFile() ) 00305 is_local_file = true; 00306 if (is_local_file && !fast_mode) { 00307 QFile file(url.toLocalFile()); 00308 return findByUrlHelper(url, mode, is_local_file, &file, accuracy); 00309 } 00310 return findByUrlHelper(url, mode, is_local_file, 0, accuracy); 00311 } 00312 00313 KMimeType::Ptr KMimeType::findByPath( const QString& path, mode_t mode, 00314 bool fast_mode, int* accuracy ) 00315 { 00316 KUrl url; 00317 url.setPath(path); 00318 return findByUrl(url, mode, true, fast_mode, accuracy); 00319 } 00320 00321 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, const QByteArray& data, 00322 mode_t mode, int* accuracy ) 00323 { 00324 KUrl url; 00325 url.setPath(name); 00326 QBuffer buffer(const_cast<QByteArray *>(&data)); 00327 return findByUrlHelper(url, mode, false, &buffer, accuracy); 00328 } 00329 00330 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, QIODevice* device, 00331 mode_t mode, int* accuracy ) 00332 { 00333 KUrl url; 00334 url.setPath(name); 00335 return findByUrlHelper(url, mode, false, device, accuracy); 00336 } 00337 00338 QString KMimeType::extractKnownExtension(const QString &fileName) 00339 { 00340 QString pattern; 00341 KMimeTypeRepository::self()->findFromFileName( fileName, &pattern ); 00342 return pattern; 00343 } 00344 00345 KMimeType::Ptr KMimeType::findByContent( const QByteArray &data, int *accuracy ) 00346 { 00347 QBuffer buffer(const_cast<QByteArray *>(&data)); 00348 buffer.open(QIODevice::ReadOnly); 00349 QByteArray cache; 00350 return KMimeTypeRepository::self()->findFromContent(&buffer, accuracy, cache); 00351 } 00352 00353 KMimeType::Ptr KMimeType::findByContent( QIODevice* device, int* accuracy ) 00354 { 00355 QByteArray cache; 00356 return KMimeTypeRepository::self()->findFromContent(device, accuracy, cache); 00357 } 00358 00359 KMimeType::Ptr KMimeType::findByFileContent( const QString &fileName, int *accuracy ) 00360 { 00361 checkEssentialMimeTypes(); 00362 00363 QFile device(fileName); 00364 // Look at mode first 00365 KMimeType::Ptr mimeFromMode = findFromMode( fileName, 0, true ); 00366 if (mimeFromMode) { 00367 if (accuracy) 00368 *accuracy = 100; 00369 return mimeFromMode; 00370 } 00371 if (!device.open(QIODevice::ReadOnly)) { 00372 if (accuracy) 00373 *accuracy = 0; 00374 return KMimeType::defaultMimeTypePtr(); 00375 } 00376 00377 QByteArray cache; 00378 return KMimeTypeRepository::self()->findFromContent(&device, accuracy, cache); 00379 } 00380 00381 bool KMimeType::isBinaryData( const QString &fileName ) 00382 { 00383 QFile file(fileName); 00384 if (!file.open(QIODevice::ReadOnly)) 00385 return false; // err, whatever 00386 const QByteArray data = file.read(32); 00387 return isBufferBinaryData(data); 00388 } 00389 00390 KMimeType::KMimeType( KMimeTypePrivate &dd, const QString& name, 00391 const QString& comment ) 00392 : KServiceType( dd, name, comment ) 00393 { 00394 } 00395 00396 KMimeType::KMimeType( const QString & fullpath, const QString& name, 00397 const QString& comment ) 00398 : KServiceType( *new KMimeTypePrivate(fullpath), name, comment ) 00399 { 00400 } 00401 00402 KMimeType::KMimeType( KMimeTypePrivate &dd) 00403 : KServiceType(dd) 00404 { 00405 } 00406 00407 KMimeType::KMimeType( QDataStream& _str, int offset ) 00408 : KServiceType( *new KMimeTypePrivate(_str, offset )) 00409 { 00410 } 00411 00412 void KMimeTypePrivate::save( QDataStream& _str ) 00413 { 00414 KServiceTypePrivate::save( _str ); 00415 // Warning adding fields here involves a binary incompatible change - update version 00416 // number in ksycoca.h. Never remove fields. 00417 _str << m_lstPatterns << QString() << QStringList() << m_iconName; 00418 } 00419 00420 QVariant KMimeTypePrivate::property( const QString& _name ) const 00421 { 00422 if ( _name == QLatin1String("Patterns") ) 00423 return QVariant( m_lstPatterns ); 00424 if ( _name == QLatin1String("Icon") ) 00425 return QVariant( iconName(KUrl()) ); 00426 00427 return KServiceTypePrivate::property( _name ); 00428 } 00429 00430 QStringList KMimeTypePrivate::propertyNames() const 00431 { 00432 QStringList res = KServiceTypePrivate::propertyNames(); 00433 res.append( QString::fromLatin1("Patterns") ); 00434 res.append( QString::fromLatin1("Icon") ); 00435 return res; 00436 } 00437 00438 KMimeType::~KMimeType() 00439 { 00440 } 00441 00442 QString KMimeType::iconNameForUrl( const KUrl & _url, mode_t mode ) 00443 { 00444 const KMimeType::Ptr mt = findByUrl( _url, mode, _url.isLocalFile(), 00445 false /*HACK*/); 00446 if (!mt) { 00447 return QString(); 00448 } 00449 static const QString& unknown = KGlobal::staticQString("unknown"); 00450 const QString mimeTypeIcon = mt->iconName( _url ); 00451 QString i = mimeTypeIcon; 00452 00453 // if we don't find an icon, maybe we can use the one for the protocol 00454 if ( i == unknown || i.isEmpty() || mt->name() == defaultMimeType() 00455 // and for the root of the protocol (e.g. trash:/) the protocol icon has priority over the mimetype icon 00456 || _url.path().length() <= 1 ) 00457 { 00458 i = favIconForUrl( _url ); // maybe there is a favicon? 00459 00460 if ( i.isEmpty() ) 00461 i = KProtocolInfo::icon( _url.protocol() ); 00462 00463 // root of protocol: if we found nothing, revert to mimeTypeIcon (which is usually "folder") 00464 if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) ) 00465 i = mimeTypeIcon; 00466 } 00467 return !i.isEmpty() ? i : unknown; 00468 } 00469 00470 QString KMimeType::favIconForUrl( const KUrl& url ) 00471 { 00472 if (url.isLocalFile() 00473 || !url.protocol().startsWith(QLatin1String("http")) 00474 || !KMimeTypeRepository::self()->useFavIcons()) 00475 return QString(); 00476 00477 QDBusInterface kded( QString::fromLatin1("org.kde.kded"), 00478 QString::fromLatin1("/modules/favicons"), 00479 QString::fromLatin1("org.kde.FavIcon") ); 00480 QDBusReply<QString> result = kded.call( QString::fromLatin1("iconForUrl"), url.url() ); 00481 return result; // default is QString() 00482 } 00483 00484 QString KMimeType::comment( const KUrl &url) const 00485 { 00486 Q_D(const KMimeType); 00487 return d->comment(url); 00488 } 00489 00490 #ifndef KDE_NO_DEPRECATED 00491 QString KMimeType::parentMimeType() const 00492 { 00493 const QStringList parents = parentMimeTypes(); 00494 if (!parents.isEmpty()) 00495 return parents.first(); 00496 return QString(); 00497 } 00498 #endif 00499 00500 bool KMimeTypePrivate::inherits(const QString& mime) const 00501 { 00502 QStack<QString> toCheck; 00503 toCheck.push(m_strName); 00504 while (!toCheck.isEmpty()) { 00505 const QString current = toCheck.pop(); 00506 if (current == mime) 00507 return true; 00508 Q_FOREACH(const QString& parent, KMimeTypeRepository::self()->parents(current)) { 00509 toCheck.push(parent); 00510 } 00511 } 00512 return false; 00513 } 00514 00515 bool KMimeType::is( const QString& mimeTypeName ) const 00516 { 00517 Q_D(const KMimeType); 00518 if (name() == mimeTypeName) 00519 return true; 00520 const QString mime = KMimeTypeRepository::self()->canonicalName(mimeTypeName); 00521 return d->inherits(mime); 00522 } 00523 00524 QStringList KMimeType::parentMimeTypes() const 00525 { 00526 Q_D(const KMimeType); 00527 return KMimeTypeRepository::self()->parents(d->m_strName); 00528 } 00529 00530 static void collectParentMimeTypes(const QString& mime, QStringList& allParents) 00531 { 00532 QStringList parents = KMimeTypeRepository::self()->parents(mime); 00533 Q_FOREACH(const QString& parent, parents) { 00534 // I would use QSet, but since order matters I better not 00535 if (!allParents.contains(parent)) 00536 allParents.append(parent); 00537 } 00538 // We want a breadth-first search, so that the least-specific parent (octet-stream) is last 00539 // This means iterating twice, unfortunately. 00540 Q_FOREACH(const QString& parent, parents) { 00541 collectParentMimeTypes(parent, allParents); 00542 } 00543 } 00544 00545 QStringList KMimeType::allParentMimeTypes() const 00546 { 00547 Q_D(const KMimeType); 00548 QStringList allParents; 00549 const QString canonical = KMimeTypeRepository::self()->resolveAlias(name()); 00550 if (!canonical.isEmpty()) 00551 allParents.append(canonical); 00552 collectParentMimeTypes(d->m_strName, allParents); 00553 return allParents; 00554 } 00555 00556 QString KMimeType::defaultMimeType() 00557 { 00558 static const QString & s_strDefaultMimeType = 00559 KGlobal::staticQString( "application/octet-stream" ); 00560 return s_strDefaultMimeType; 00561 } 00562 00563 QString KMimeType::iconName( const KUrl& url) const 00564 { 00565 Q_D(const KMimeType); 00566 return d->iconName(url); 00567 } 00568 00569 QStringList KMimeType::patterns() const 00570 { 00571 Q_D(const KMimeType); 00572 d->ensureXmlDataLoaded(); 00573 return d->m_lstPatterns; 00574 } 00575 00576 // loads comment, icon, mainPattern, m_lstPatterns 00577 void KMimeTypePrivate::ensureXmlDataLoaded() const 00578 { 00579 if (m_xmlDataLoaded) 00580 return; 00581 00582 m_xmlDataLoaded = true; 00583 00584 const QString file = m_strName + QLatin1String(".xml"); 00585 const QStringList mimeFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", file); 00586 if (mimeFiles.isEmpty()) { 00587 kWarning() << "No file found for" << file << ", even though the file appeared in a directory listing."; 00588 kWarning() << "Either it was just removed, or the directory doesn't have executable permission..."; 00589 kWarning() << KGlobal::dirs()->resourceDirs("xdgdata-mime"); 00590 return; 00591 } 00592 00593 QString comment; 00594 QString mainPattern; 00595 const QStringList languageList = KGlobal::locale()->languageList(); 00596 QString preferredLanguage = languageList.first(); 00597 QMap<QString, QString> commentsByLanguage; 00598 00599 QListIterator<QString> mimeFilesIter(mimeFiles); 00600 mimeFilesIter.toBack(); 00601 while (mimeFilesIter.hasPrevious()) { // global first, then local. 00602 const QString fullPath = mimeFilesIter.previous(); 00603 QFile qfile(fullPath); 00604 if (!qfile.open(QFile::ReadOnly)) 00605 continue; 00606 00607 QXmlStreamReader xml(&qfile); 00608 if (xml.readNextStartElement()) { 00609 if (xml.name() != "mime-type") { 00610 continue; 00611 } 00612 const QString name = xml.attributes().value(QLatin1String("type")).toString(); 00613 if (name.isEmpty()) 00614 continue; 00615 if (name != m_strName) { 00616 kWarning() << "Got name" << name << "in file" << file << "expected" << m_strName; 00617 } 00618 00619 while (xml.readNextStartElement()) { 00620 const QStringRef tag = xml.name(); 00621 if (tag == "comment") { 00622 if (!comment.isEmpty()) { // already found, skip this one 00623 xml.skipCurrentElement(); 00624 continue; 00625 } 00626 QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString(); 00627 const QString text = xml.readElementText(); 00628 if (lang.isEmpty()) { 00629 lang = QLatin1String("en_US"); 00630 } 00631 if (lang == preferredLanguage) { 00632 comment = text; 00633 } else { 00634 commentsByLanguage.insert(lang, text); 00635 } 00636 continue; // we called readElementText, so we're at the EndElement already. 00637 } else if (tag == "icon") { // as written out by shared-mime-info >= 0.40 00638 m_iconName = xml.attributes().value(QLatin1String("name")).toString(); 00639 } else if (tag == "glob-deleteall") { // as written out by shared-mime-info >= 0.70 00640 mainPattern.clear(); 00641 m_lstPatterns.clear(); 00642 } else if (tag == "glob") { // as written out by shared-mime-info >= 0.70 00643 const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString(); 00644 if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) { 00645 mainPattern = pattern; 00646 } 00647 if (!m_lstPatterns.contains(pattern)) 00648 m_lstPatterns.append(pattern); 00649 } 00650 xml.skipCurrentElement(); 00651 } 00652 if (xml.name() != "mime-type") { 00653 kFatal() << "Programming error in KMimeType XML loading, please create a bug report on http://bugs.kde.org and attach the file" << fullPath; 00654 } 00655 } 00656 } 00657 00658 if (comment.isEmpty()) { 00659 Q_FOREACH(const QString& lang, languageList) { 00660 const QString comm = commentsByLanguage.value(lang); 00661 if (!comm.isEmpty()) { 00662 comment = comm; 00663 break; 00664 } 00665 const int pos = lang.indexOf(QLatin1Char('_')); 00666 if (pos != -1) { 00667 // "pt_BR" not found? try just "pt" 00668 const QString shortLang = lang.left(pos); 00669 const QString comm = commentsByLanguage.value(shortLang); 00670 if (!comm.isEmpty()) { 00671 comment = comm; 00672 break; 00673 } 00674 } 00675 } 00676 if (comment.isEmpty()) { 00677 kWarning() << "Missing <comment> field in" << file; 00678 } 00679 } 00680 m_strComment = comment; 00681 00682 const bool globsInXml = (KMimeType::sharedMimeInfoVersion() >= KDE_MAKE_VERSION(0, 70, 0)); 00683 if (globsInXml) { 00684 if (!mainPattern.isEmpty() && m_lstPatterns.first() != mainPattern) { 00685 // ensure it's first in the list of patterns 00686 m_lstPatterns.removeAll(mainPattern); 00687 m_lstPatterns.prepend(mainPattern); 00688 } 00689 } else { 00690 // Fallback: get the patterns from the globs file 00691 m_lstPatterns = KMimeTypeRepository::self()->patternsForMimetype(m_strName); 00692 } 00693 } 00694 00695 QString KMimeType::userSpecifiedIconName() const 00696 { 00697 Q_D(const KMimeType); 00698 d->ensureXmlDataLoaded(); 00699 return d->m_iconName; 00700 } 00701 00702 int KMimeType::sharedMimeInfoVersion() 00703 { 00704 return KMimeTypeRepository::self()->sharedMimeInfoVersion(); 00705 } 00706 00707 QString KMimeType::mainExtension() const 00708 { 00709 Q_D(const KMimeType); 00710 00711 #if 1 // HACK START - can be removed once shared-mime-info >= 0.70 is used/required. 00712 // The idea was: first usable pattern from m_lstPatterns. 00713 // But update-mime-database makes a mess of the order of the patterns, 00714 // because it uses a hash internally. 00715 static const struct { const char* mime; const char* extension; } s_hardcodedMimes[] = { 00716 { "text/plain", ".txt" } }; 00717 if (d->m_lstPatterns.count() > 1) { 00718 const QByteArray me = name().toLatin1(); 00719 for (uint i = 0; i < sizeof(s_hardcodedMimes)/sizeof(*s_hardcodedMimes); ++i) { 00720 if (me == s_hardcodedMimes[i].mime) 00721 return QString::fromLatin1(s_hardcodedMimes[i].extension); 00722 } 00723 } 00724 #endif // HACK END 00725 00726 Q_FOREACH(const QString& pattern, patterns()) { 00727 // Skip if if looks like: README or *. or *.* 00728 // or *.JP*G or *.JP? 00729 if (pattern.startsWith(QLatin1String("*.")) && 00730 pattern.length() > 2 && 00731 pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) { 00732 return pattern.mid(1); 00733 } 00734 } 00735 // TODO we should also look into the parent mimetype's patterns, no? 00736 return QString(); 00737 } 00738 00739 int KMimeTypePrivate::serviceOffersOffset() const 00740 { 00741 return KMimeTypeFactory::self()->serviceOffersOffset(name()); 00742 }
KDE 4.6 API Reference