00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kmimetype.h"
00021 #include "kmimetype_p.h"
00022 #include "kmimetypefactory.h"
00023
00024 #include <kconfig.h>
00025 #include <kconfiggroup.h>
00026 #include <kdebug.h>
00027 #include <kdesktopfile.h>
00028 #include <kde_file.h>
00029 #include <klocale.h>
00030 #include <kmessage.h>
00031 #include <kprotocolinfo.h>
00032 #include <kprotocolinfofactory.h>
00033 #include <kstandarddirs.h>
00034 #include <ksycoca.h>
00035 #include <kurl.h>
00036
00037 #include <QtCore/QMutableSetIterator>
00038 #include <QtCore/QCharRef>
00039 #include <QtCore/QFile>
00040 #include <QtDBus/QtDBus>
00041 #include <QBuffer>
00042
00043 #include <sys/types.h>
00044 #include <sys/stat.h>
00045
00046 #include <assert.h>
00047 #include <errno.h>
00048 #include <stddef.h>
00049 #include <unistd.h>
00050 #include <stdlib.h>
00051
00052 template class KSharedPtr<KMimeType>;
00053
00054 static KMimeType::Ptr s_pDefaultMimeType;
00055
00056 static void errorMissingMimeTypes( const QStringList& _types )
00057 {
00058 KMessage::message( KMessage::Error, i18np( "Could not find mime type <resource>%2</resource>",
00059 "Could not find mime types:\n<resource>%2</resource>", _types.count(), _types.join("</resource>\n<resource>") ) );
00060 }
00061
00062 void KMimeTypePrivate::loadInternal( QDataStream& _str )
00063 {
00064 QString oldParentMimeTypeString;
00065 _str >> m_lstPatterns >> oldParentMimeTypeString >> m_parentMimeTypes;
00066
00067
00068 if (!oldParentMimeTypeString.isEmpty() && m_parentMimeTypes.isEmpty())
00069 m_parentMimeTypes.append(oldParentMimeTypeString);
00070 }
00071
00076 void KMimeType::buildDefaultType()
00077 {
00078 assert ( !s_pDefaultMimeType );
00079
00080 KMimeType::Ptr mime = KMimeTypeFactory::self()->
00081 findMimeTypeByName( KMimeType::defaultMimeType() );
00082
00083 if (mime)
00084 {
00085 s_pDefaultMimeType = mime;
00086 }
00087 else
00088 {
00089 QString defaultMimeType = KMimeType::defaultMimeType();
00090 errorMissingMimeTypes( QStringList(defaultMimeType) );
00091 QString sDefaultMimeType = KGlobal::dirs()->resourceDirs("xdgdata-mime").first()+defaultMimeType+".xml";
00092 s_pDefaultMimeType = new KMimeType( sDefaultMimeType, defaultMimeType, "mime" );
00093 }
00094 }
00095
00096 KMimeType::Ptr KMimeType::defaultMimeTypePtr()
00097 {
00098 if ( !s_pDefaultMimeType )
00099 buildDefaultType();
00100 return s_pDefaultMimeType;
00101 }
00102
00103 bool KMimeType::isDefault() const
00104 {
00105 return this == defaultMimeTypePtr().data();
00106 }
00107
00111 void KMimeType::checkEssentialMimeTypes()
00112 {
00113 static bool s_bChecked = false;
00114
00115 if ( s_bChecked )
00116 return;
00117 if ( !s_pDefaultMimeType )
00118 KMimeType::buildDefaultType();
00119
00120 s_bChecked = true;
00121
00122
00123
00124 if ( !KMimeTypeFactory::self()->checkMimeTypes() )
00125 {
00126
00127
00128
00129 KMessage::message( KMessage::Error, i18n( "No mime types installed. Check that shared-mime-info is installed, and that XDG_DATA_DIRS is not set, or includes /usr/share." ) );
00130 return;
00131 }
00132
00133 QStringList missingMimeTypes;
00134
00135 #ifndef Q_OS_WIN
00136 if ( !KMimeType::mimeType( "inode/directory" ) )
00137 missingMimeTypes.append( "inode/directory" );
00138
00139
00140 if ( !KMimeType::mimeType( "inode/blockdevice" ) )
00141 missingMimeTypes.append( "inode/blockdevice" );
00142 if ( !KMimeType::mimeType( "inode/chardevice" ) )
00143 missingMimeTypes.append( "inode/chardevice" );
00144 if ( !KMimeType::mimeType( "inode/socket" ) )
00145 missingMimeTypes.append( "inode/socket" );
00146 if ( !KMimeType::mimeType( "inode/fifo" ) )
00147 missingMimeTypes.append( "inode/fifo" );
00148 #endif
00149 if ( !KMimeType::mimeType( "application/x-shellscript" ) )
00150 missingMimeTypes.append( "application/x-shellscript" );
00151 if ( !KMimeType::mimeType( "application/x-executable" ) )
00152 missingMimeTypes.append( "application/x-executable" );
00153 if ( !KMimeType::mimeType( "application/x-desktop" ) )
00154 missingMimeTypes.append( "application/x-desktop" );
00155
00156 if (!missingMimeTypes.isEmpty())
00157 errorMissingMimeTypes(missingMimeTypes);
00158 }
00159
00160 KMimeType::Ptr KMimeType::mimeType( const QString& _name, FindByNameOption options )
00161 {
00162 return KMimeTypeFactory::self()->findMimeTypeByName( _name, options );
00163 }
00164
00165 KMimeType::List KMimeType::allMimeTypes()
00166 {
00167 return KMimeTypeFactory::self()->allMimeTypes();
00168 }
00169
00170 bool KMimeType::isBufferBinaryData(const QByteArray& data)
00171 {
00172
00173 const char* p = data.data();
00174 const int end = qMin(32, data.size());
00175 for (int i = 0; i < end; ++i) {
00176 if (p[i] < 32 && p[i] != 9 && p[i] != 10 && p[i] != 13)
00177 return true;
00178 }
00179 return false;
00180 }
00181
00182 static KMimeType::Ptr findFromMode( const QString& path ,
00183 mode_t mode ,
00184 bool is_local_file )
00185 {
00186 if ( is_local_file && (mode == 0 || mode == (mode_t)-1) ) {
00187 KDE_struct_stat buff;
00188 if ( KDE_stat( QFile::encodeName(path), &buff ) != -1 )
00189 mode = buff.st_mode;
00190 }
00191
00192 if ( S_ISDIR( mode ) ) {
00193
00194 #if 0
00195
00196
00197 if ( is_local_file )
00198 {
00199 if ( access( QFile::encodeName(path), R_OK ) == -1 )
00200 return KMimeType::mimeType( "inode/directory-locked" );
00201 }
00202 #endif
00203 return KMimeType::mimeType( "inode/directory" );
00204 }
00205 if ( S_ISCHR( mode ) )
00206 return KMimeType::mimeType( "inode/chardevice" );
00207 if ( S_ISBLK( mode ) )
00208 return KMimeType::mimeType( "inode/blockdevice" );
00209 if ( S_ISFIFO( mode ) )
00210 return KMimeType::mimeType( "inode/fifo" );
00211 if ( S_ISSOCK( mode ) )
00212 return KMimeType::mimeType( "inode/socket" );
00213 #ifdef Q_OS_WIN
00214
00215 int size = path.size();
00216 if ( size == 2 || size == 3 ) {
00217 unsigned int type = GetDriveTypeW( (LPCWSTR) path.utf16() );
00218 switch( type ) {
00219 case DRIVE_REMOVABLE:
00220 return KMimeType::mimeType( "media/floppy_mounted" );
00221 case DRIVE_FIXED:
00222 return KMimeType::mimeType( "media/hdd_mounted" );
00223 case DRIVE_REMOTE:
00224 return KMimeType::mimeType( "media/smb_mounted" );
00225 case DRIVE_CDROM:
00226 return KMimeType::mimeType( "media/cdrom_mounted" );
00227 case DRIVE_RAMDISK:
00228 return KMimeType::mimeType( "media/hdd_mounted" );
00229 default:
00230 break;
00231 };
00232 }
00233 #endif
00234
00235 if ( !is_local_file && S_ISREG( mode ) && ( mode & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) )
00236 return KMimeType::mimeType( "application/x-executable" );
00237
00238 return KMimeType::Ptr();
00239 }
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 KMimeType::Ptr KMimeType::findByUrlHelper( const KUrl& _url, mode_t mode,
00272 bool is_local_file,
00273 QIODevice* device,
00274 int* accuracy )
00275 {
00276 checkEssentialMimeTypes();
00277 const QString path = _url.path();
00278
00279 if (accuracy)
00280 *accuracy = 100;
00281
00282
00283 KMimeType::Ptr mimeFromMode = findFromMode( path, mode, is_local_file );
00284 if (mimeFromMode)
00285 return mimeFromMode;
00286
00287
00288 const QString fileName( _url.fileName() );
00289 QList<KMimeType::Ptr> mimeList;
00290 if ( !fileName.isEmpty() && !path.endsWith( '/' ) ) {
00291
00292 if ( is_local_file || _url.hasSubUrl() ||
00293 KProtocolInfo::determineMimetypeFromExtension( _url.protocol() ) ) {
00294 mimeList = KMimeTypeFactory::self()->findFromFileName( fileName );
00295
00296
00297 if ( mimeList.count() == 1 ) {
00298 return mimeList.first();
00299 }
00300 }
00301 }
00302
00303 if ( device && !device->isOpen() ) {
00304 if ( !device->open(QIODevice::ReadOnly) ) {
00305 device = 0;
00306 }
00307 }
00308
00309
00310 QByteArray beginning;
00311 if ( device ) {
00312 int magicAccuracy;
00313 KMimeType::Ptr mime = KMimeTypeFactory::self()->findFromContent(
00314 device, KMimeTypeFactory::AllRules, &magicAccuracy, beginning );
00315
00316
00317
00318 if (mime && magicAccuracy > 0) {
00319
00320
00321 if (magicAccuracy < 80 && !mimeList.isEmpty()) {
00322
00323
00324 const QString sniffedMime = mime->name();
00325 foreach(const KMimeType::Ptr &mimeFromPattern, mimeList) {
00326
00327 if (mimeFromPattern->is(sniffedMime)) {
00328
00329 if (accuracy)
00330 *accuracy = 100;
00331 return mimeFromPattern;
00332 }
00333 }
00334 }
00335
00336 if (accuracy)
00337 *accuracy = magicAccuracy;
00338 return mime;
00339 }
00340 }
00341
00342
00343
00344
00345 if (!mimeList.isEmpty()) {
00346 if (accuracy)
00347 *accuracy = 20;
00348
00349 return mimeList.first();
00350 }
00351
00352
00353 if (accuracy)
00354 *accuracy = 10;
00355
00356
00357 KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() );
00358 QString def;
00359 if (prot)
00360 def = prot->defaultMimeType();
00361 if ( !def.isEmpty() && def != defaultMimeType() ) {
00362
00363 KMimeType::Ptr mime = mimeType( def );
00364 if (mime)
00365 return mime;
00366 }
00367 if ( path.endsWith( '/' ) || path.isEmpty() ) {
00368
00369
00370
00371
00372 if ( def.isEmpty() ) {
00373
00374 KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() );
00375 if ( prot && prot->supportsListing() ) {
00376 KMimeType::Ptr mime = mimeType( QLatin1String("inode/directory") );
00377 if (mime) {
00378 return mime;
00379 }
00380 } else
00381 return defaultMimeTypePtr();
00382 }
00383 }
00384
00385 if (accuracy)
00386 *accuracy = 0;
00387 return defaultMimeTypePtr();
00388 }
00389
00390 KMimeType::Ptr KMimeType::findByUrl( const KUrl& url, mode_t mode,
00391 bool is_local_file, bool fast_mode,
00392 int *accuracy )
00393 {
00394 if ( !is_local_file && url.isLocalFile() )
00395 is_local_file = true;
00396 if (is_local_file && !fast_mode) {
00397 QFile file(url.path());
00398 return findByUrlHelper(url, mode, is_local_file, &file, accuracy);
00399 }
00400 return findByUrlHelper(url, mode, is_local_file, 0, accuracy);
00401 }
00402
00403 KMimeType::Ptr KMimeType::findByPath( const QString& path, mode_t mode,
00404 bool fast_mode, int* accuracy )
00405 {
00406 KUrl url;
00407 url.setPath(path);
00408 return findByUrl(url, mode, true, fast_mode, accuracy);
00409 }
00410
00411 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, const QByteArray& data,
00412 mode_t mode, int* accuracy )
00413 {
00414 KUrl url;
00415 url.setPath(name);
00416 QBuffer buffer(const_cast<QByteArray *>(&data));
00417 return findByUrlHelper(url, mode, false, &buffer, accuracy);
00418 }
00419
00420 QString KMimeType::extractKnownExtension(const QString &fileName)
00421 {
00422 QString pattern;
00423 KMimeTypeFactory::self()->findFromFileName( fileName, &pattern );
00424 return pattern;
00425 }
00426
00427 KMimeType::Ptr KMimeType::findByContent( const QByteArray &data, int *accuracy )
00428 {
00429 QBuffer buffer(const_cast<QByteArray *>(&data));
00430 buffer.open(QIODevice::ReadOnly);
00431 QByteArray cache;
00432 return KMimeTypeFactory::self()->findFromContent(
00433 &buffer, KMimeTypeFactory::AllRules, accuracy, cache );
00434 }
00435
00436 KMimeType::Ptr KMimeType::findByFileContent( const QString &fileName, int *accuracy )
00437 {
00438 checkEssentialMimeTypes();
00439
00440 QFile device(fileName);
00441
00442 KMimeType::Ptr mimeFromMode = findFromMode( fileName, 0, true );
00443 if (mimeFromMode) {
00444 if (accuracy)
00445 *accuracy = 100;
00446 return mimeFromMode;
00447 }
00448 if (!device.open(QIODevice::ReadOnly)) {
00449 if (accuracy)
00450 *accuracy = 0;
00451 return KMimeType::defaultMimeTypePtr();
00452 }
00453
00454 QByteArray cache;
00455 return KMimeTypeFactory::self()->findFromContent(
00456 &device, KMimeTypeFactory::AllRules, accuracy, cache );
00457 }
00458
00459 bool KMimeType::isBinaryData( const QString &fileName )
00460 {
00461 QFile file(fileName);
00462 if (!file.open(QIODevice::ReadOnly))
00463 return false;
00464 const QByteArray data = file.read(32);
00465 return isBufferBinaryData(data);
00466 }
00467
00468 KMimeType::KMimeType( KMimeTypePrivate &dd, const QString& name,
00469 const QString& comment )
00470 : KServiceType( dd, name, comment )
00471 {
00472 }
00473
00474 KMimeType::KMimeType( const QString & fullpath, const QString& name,
00475 const QString& comment )
00476 : KServiceType( *new KMimeTypePrivate(fullpath), name, comment )
00477 {
00478 }
00479
00480 KMimeType::KMimeType( KMimeTypePrivate &dd)
00481 : KServiceType(dd)
00482 {
00483 }
00484
00485 KMimeType::KMimeType( QDataStream& _str, int offset )
00486 : KServiceType( *new KMimeTypePrivate(_str, offset ))
00487 {
00488 }
00489
00490 void KMimeTypePrivate::save( QDataStream& _str )
00491 {
00492 KServiceTypePrivate::save( _str );
00493
00494
00495 _str << m_lstPatterns << QString() << m_parentMimeTypes;
00496 }
00497
00498 QVariant KMimeTypePrivate::property( const QString& _name ) const
00499 {
00500 if ( _name == "Patterns" )
00501 return QVariant( m_lstPatterns );
00502 if ( _name == "Icon" )
00503 return QVariant( iconName(KUrl()) );
00504
00505 return KServiceTypePrivate::property( _name );
00506 }
00507
00508 QStringList KMimeTypePrivate::propertyNames() const
00509 {
00510 QStringList res = KServiceTypePrivate::propertyNames();
00511 res.append( "Patterns" );
00512 res.append( "Icon" );
00513 return res;
00514 }
00515
00516 KMimeType::~KMimeType()
00517 {
00518 }
00519
00520 QString KMimeType::iconNameForUrl( const KUrl & _url, mode_t mode )
00521 {
00522 const KMimeType::Ptr mt = findByUrl( _url, mode, _url.isLocalFile(),
00523 false );
00524 if (!mt) {
00525 return QString();
00526 }
00527 static const QString& unknown = KGlobal::staticQString("unknown");
00528 const QString mimeTypeIcon = mt->iconName( _url );
00529 QString i = mimeTypeIcon;
00530
00531
00532 if ( i == unknown || i.isEmpty() || mt == defaultMimeTypePtr()
00533
00534 || _url.path().length() <= 1 )
00535 {
00536 i = favIconForUrl( _url );
00537
00538 if ( i.isEmpty() )
00539 i = KProtocolInfo::icon( _url.protocol() );
00540
00541
00542 if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) )
00543 i = mimeTypeIcon;
00544 }
00545 return !i.isEmpty() ? i : unknown;
00546 }
00547
00548 QString KMimeType::favIconForUrl( const KUrl& url )
00549 {
00550
00551
00552 static bool useFavIcons = true;
00553 static bool check = true;
00554 if ( check ) {
00555 check = false;
00556 KConfigGroup cg( KGlobal::config(), "HTML Settings" );
00557 useFavIcons = cg.readEntry("EnableFavicon", true);
00558 }
00559
00560 if ( url.isLocalFile() || !url.protocol().startsWith("http")
00561 || !useFavIcons )
00562 return QString();
00563
00564 QDBusInterface kded( "org.kde.kded", "/modules/favicons", "org.kde.FavIcon" );
00565 QDBusReply<QString> result = kded.call( "iconForUrl", url.url() );
00566 return result;
00567 }
00568
00569 QString KMimeType::comment( const KUrl &url) const
00570 {
00571 Q_D(const KMimeType);
00572 return d->comment(url);
00573 }
00574
00575 QString KMimeType::parentMimeType() const
00576 {
00577 Q_D(const KMimeType);
00578
00579 if (!d->m_parentMimeTypes.isEmpty())
00580 return d->m_parentMimeTypes.first();
00581 return d->fallbackParent();
00582 }
00583
00584 QString KMimeTypePrivate::fallbackParent() const
00585 {
00586 const QString myGroup = m_strName.left(m_strName.indexOf('/'));
00587
00588 if (myGroup == "text" && m_strName != "text/plain")
00589 return "text/plain";
00590
00591 if (myGroup != "inode" &&
00592
00593 myGroup != "all" && myGroup != "fonts" && myGroup != "print" && myGroup != "uri"
00594 && m_strName != "application/octet-stream") {
00595 return "application/octet-stream";
00596 }
00597 return QString();
00598 }
00599
00600 bool KMimeTypePrivate::inherits(KMimeType::Ptr mime) const
00601 {
00602 if (mime && m_strName == mime->d_func()->m_strName) {
00603 return true;
00604 }
00605 foreach( const QString& parent, parentMimeTypes() ) {
00606 KMimeType::Ptr parentMime = KMimeTypeFactory::self()->findMimeTypeByName(parent);
00607 if (!parentMime)
00608 return false;
00609 if (parentMime->d_func()->inherits(mime))
00610 return true;
00611 }
00612 return false;
00613 }
00614
00615 bool KMimeType::is( const QString& mimeTypeName ) const
00616 {
00617 Q_D(const KMimeType);
00618 if (name() == mimeTypeName)
00619 return true;
00620 KMimeType::Ptr mime = KMimeTypeFactory::self()->findMimeTypeByName(mimeTypeName, KMimeType::ResolveAliases);
00621 return d->inherits(mime);
00622 }
00623
00624 QStringList KMimeType::parentMimeTypes() const
00625 {
00626 Q_D(const KMimeType);
00627 return d->parentMimeTypes();
00628 }
00629
00630 QStringList KMimeTypePrivate::parentMimeTypes() const
00631 {
00632 QStringList parents = m_parentMimeTypes;
00633 if (parents.isEmpty()) {
00634 const QString myParent = fallbackParent();
00635 if (!myParent.isEmpty())
00636 parents.append(myParent);
00637 }
00638 return parents;
00639 }
00640
00641 void KMimeTypePrivate::collectParentMimeTypes(QStringList& allParents) const
00642 {
00643 QStringList parents = parentMimeTypes();
00644 Q_FOREACH(const QString& parent, parents) {
00645
00646 if (!allParents.contains(parent))
00647 allParents.append(parent);
00648 }
00649
00650
00651 Q_FOREACH(const QString& parent, parents) {
00652 KMimeType::Ptr parentMime = KMimeTypeFactory::self()->findMimeTypeByName(parent);
00653 if (parentMime)
00654 parentMime->d_func()->collectParentMimeTypes(allParents);
00655 }
00656 }
00657
00658 QStringList KMimeType::allParentMimeTypes() const
00659 {
00660 QStringList allParents;
00661 const QString canonical = KMimeTypeFactory::self()->resolveAlias(name());
00662 if (!canonical.isEmpty())
00663 allParents.append(canonical);
00664 d_func()->collectParentMimeTypes(allParents);
00665 return allParents;
00666 }
00667
00668 QString KMimeType::defaultMimeType()
00669 {
00670 static const QString & s_strDefaultMimeType =
00671 KGlobal::staticQString( "application/octet-stream" );
00672 return s_strDefaultMimeType;
00673 }
00674
00675 QString KMimeType::iconName( const KUrl& url) const
00676 {
00677 Q_D(const KMimeType);
00678 return d->iconName(url);
00679 }
00680
00681 QStringList KMimeType::patterns() const
00682 {
00683 Q_D(const KMimeType);
00684 return d->m_lstPatterns;
00685 }
00686
00687 void KMimeType::setPatterns(const QStringList& patterns)
00688 {
00689 Q_D(KMimeType);
00690 d->m_lstPatterns = patterns;
00691 }
00692
00693 void KMimeType::setParentMimeType(const QString& parent)
00694 {
00695 Q_D(KMimeType);
00696
00697 d->m_parentMimeTypes.append(parent);
00698 }
00699
00700 void KMimeType::internalClearData()
00701 {
00702 Q_D(KMimeType);
00703
00704 d->m_parentMimeTypes.clear();
00705 d->m_lstPatterns.clear();
00706 }