kmimetype.cpp

00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
00003  *                     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 // $Id$
00020 
00021 #include <config.h>
00022 
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 
00026 #include <assert.h>
00027 #include <dirent.h>
00028 #include <errno.h>
00029 #include <stddef.h>
00030 #include <unistd.h>
00031 #include <stdlib.h>
00032 
00033 #include <kmountpoint.h>
00034 
00035 #include <kprotocolinfo.h>
00036 #include <kio/global.h>
00037 #include "kmimetype.h"
00038 #include "kservicetypefactory.h"
00039 #include "kmimemagic.h"
00040 #include "kservice.h"
00041 #include "krun.h"
00042 #include "kautomount.h"
00043 #include <kdirnotify_stub.h>
00044 
00045 #include <qstring.h>
00046 #include <qfile.h>
00047 #include <kmessageboxwrapper.h>
00048 
00049 #include <dcopclient.h>
00050 #include <dcopref.h>
00051 #include <kapplication.h>
00052 #include <kprocess.h>
00053 #include <kdebug.h>
00054 #include <kdesktopfile.h>
00055 #include <kdirwatch.h>
00056 #include <kiconloader.h>
00057 #include <klocale.h>
00058 #include <ksimpleconfig.h>
00059 #include <kstandarddirs.h>
00060 #include <kurl.h>
00061 #include <ksycoca.h>
00062 #include <kde_file.h>
00063 
00064 template class KSharedPtr<KMimeType>;
00065 template class QValueList<KMimeType::Ptr>;
00066 
00067 KMimeType::Ptr KMimeType::s_pDefaultType = 0L;
00068 bool KMimeType::s_bChecked = false;
00069 
00070 void KMimeType::buildDefaultType()
00071 {
00072   assert ( !s_pDefaultType );
00073   // Try to find the default type
00074   KServiceType * mime = KServiceTypeFactory::self()->
00075         findServiceTypeByName( defaultMimeType() );
00076 
00077   if (mime && mime->isType( KST_KMimeType ))
00078   {
00079       s_pDefaultType = KMimeType::Ptr((KMimeType *) mime);
00080   }
00081   else
00082   {
00083      errorMissingMimeType( defaultMimeType() );
00084      KStandardDirs stdDirs;
00085      QString sDefaultMimeType = stdDirs.resourceDirs("mime").first()+defaultMimeType()+".desktop";
00086      s_pDefaultType = new KMimeType( sDefaultMimeType, defaultMimeType(),
00087                                      "unknown", "mime", QStringList() );
00088   }
00089 }
00090 
00091 KMimeType::Ptr KMimeType::defaultMimeTypePtr()
00092 {
00093   if ( !s_pDefaultType ) // we need a default type first
00094     buildDefaultType();
00095   return s_pDefaultType;
00096 }
00097 
00098 // Check for essential mimetypes
00099 void KMimeType::checkEssentialMimeTypes()
00100 {
00101   if ( s_bChecked ) // already done
00102     return;
00103   if ( !s_pDefaultType ) // we need a default type first
00104     buildDefaultType();
00105 
00106   s_bChecked = true; // must be done before building mimetypes
00107 
00108   // No Mime-Types installed ?
00109   // Lets do some rescue here.
00110   if ( !KServiceTypeFactory::self()->checkMimeTypes() )
00111   {
00112     KMessageBoxWrapper::error( 0L, i18n( "No mime types installed." ) );
00113     return; // no point in going any further
00114   }
00115 
00116   if ( KMimeType::mimeType( "inode/directory" ) == s_pDefaultType )
00117     errorMissingMimeType( "inode/directory" );
00118   if ( KMimeType::mimeType( "inode/directory-locked" ) == s_pDefaultType )
00119     errorMissingMimeType( "inode/directory-locked" );
00120   if ( KMimeType::mimeType( "inode/blockdevice" ) == s_pDefaultType )
00121     errorMissingMimeType( "inode/blockdevice" );
00122   if ( KMimeType::mimeType( "inode/chardevice" ) == s_pDefaultType )
00123     errorMissingMimeType( "inode/chardevice" );
00124   if ( KMimeType::mimeType( "inode/socket" ) == s_pDefaultType )
00125     errorMissingMimeType( "inode/socket" );
00126   if ( KMimeType::mimeType( "inode/fifo" ) == s_pDefaultType )
00127     errorMissingMimeType( "inode/fifo" );
00128   if ( KMimeType::mimeType( "application/x-shellscript" ) == s_pDefaultType )
00129     errorMissingMimeType( "application/x-shellscript" );
00130   if ( KMimeType::mimeType( "application/x-executable" ) == s_pDefaultType )
00131     errorMissingMimeType( "application/x-executable" );
00132   if ( KMimeType::mimeType( "application/x-desktop" ) == s_pDefaultType )
00133     errorMissingMimeType( "application/x-desktop" );
00134 }
00135 
00136 void KMimeType::errorMissingMimeType( const QString& _type )
00137 {
00138   QString tmp = i18n( "Could not find mime type\n%1" ).arg( _type );
00139 
00140   KMessageBoxWrapper::sorry( 0, tmp );
00141 }
00142 
00143 KMimeType::Ptr KMimeType::mimeType( const QString& _name )
00144 {
00145   KServiceType * mime = KServiceTypeFactory::self()->findServiceTypeByName( _name );
00146 
00147   if ( !mime || !mime->isType( KST_KMimeType ) )
00148   {
00149     // When building ksycoca, findServiceTypeByName doesn't create an object
00150     // but returns one from a dict.
00151     if ( !KSycoca::self()->isBuilding() )
00152         delete mime;
00153     if ( !s_pDefaultType )
00154       buildDefaultType();
00155     return s_pDefaultType;
00156   }
00157 
00158   // We got a mimetype
00159   return KMimeType::Ptr((KMimeType *) mime);
00160 }
00161 
00162 KMimeType::List KMimeType::allMimeTypes()
00163 {
00164   return KServiceTypeFactory::self()->allMimeTypes();
00165 }
00166 
00167 KMimeType::Ptr KMimeType::findByURL( const KURL& _url, mode_t _mode,
00168                                      bool _is_local_file, bool _fast_mode )
00169 {
00170   checkEssentialMimeTypes();
00171   QString path = _url.path();
00172 
00173   if ( !_fast_mode && !_is_local_file && _url.isLocalFile() )
00174     _is_local_file = true;
00175 
00176   if ( !_fast_mode && _is_local_file && (_mode == 0 || _mode == (mode_t)-1) )
00177   {
00178     KDE_struct_stat buff;
00179     if ( KDE_stat( QFile::encodeName(path), &buff ) != -1 )
00180       _mode = buff.st_mode;
00181   }
00182 
00183   // Look at mode_t first
00184   if ( S_ISDIR( _mode ) )
00185   {
00186     // Special hack for local files. We want to see whether we
00187     // are allowed to enter the directory
00188     if ( _is_local_file )
00189     {
00190       if ( access( QFile::encodeName(path), R_OK ) == -1 )
00191         return mimeType( "inode/directory-locked" );
00192     }
00193     return mimeType( "inode/directory" );
00194   }
00195   if ( S_ISCHR( _mode ) )
00196     return mimeType( "inode/chardevice" );
00197   if ( S_ISBLK( _mode ) )
00198     return mimeType( "inode/blockdevice" );
00199   if ( S_ISFIFO( _mode ) )
00200     return mimeType( "inode/fifo" );
00201   if ( S_ISSOCK( _mode ) )
00202     return mimeType( "inode/socket" );
00203   // KMimeMagic can do that better for local files
00204   if ( !_is_local_file && S_ISREG( _mode ) && ( _mode & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) )
00205     return mimeType( "application/x-executable" );
00206 
00207   QString fileName ( _url.fileName() );
00208 
00209   static const QString& slash = KGlobal::staticQString("/");
00210   if ( ! fileName.isNull() && !path.endsWith( slash ) )
00211   {
00212       // Try to find it out by looking at the filename
00213       KMimeType::Ptr mime = KServiceTypeFactory::self()->findFromPattern( fileName );
00214       if ( mime )
00215       {
00216         // Found something - can we trust it ? (e.g. don't trust *.pl over HTTP, could be anything)
00217         if ( _is_local_file || _url.hasSubURL() || // Explicitly trust suburls
00218              KProtocolInfo::determineMimetypeFromExtension( _url.protocol() ) )
00219         {
00220             if ( _is_local_file && !_fast_mode ) {
00221                 if ( mime->patternsAccuracy()<100 )
00222                 {
00223                     KMimeMagicResult* result =
00224                             KMimeMagic::self()->findFileType( path );
00225 
00226                     if ( result && result->isValid() )
00227                         return mimeType( result->mimeType() );
00228                 }
00229             }
00230 
00231             return mime;
00232         }
00233       }
00234 
00235       static const QString& dotdesktop = KGlobal::staticQString(".desktop");
00236       static const QString& dotkdelnk = KGlobal::staticQString(".kdelnk");
00237       static const QString& dotdirectory = KGlobal::staticQString(".directory");
00238 
00239       // Another filename binding, hardcoded, is .desktop:
00240       if ( fileName.endsWith( dotdesktop ) )
00241         return mimeType( "application/x-desktop" );
00242       // Another filename binding, hardcoded, is .kdelnk;
00243       // this is preserved for backwards compatibility
00244       if ( fileName.endsWith( dotkdelnk ) )
00245         return mimeType( "application/x-desktop" );
00246       // .directory files are detected as x-desktop by mimemagic
00247       // but don't have a Type= entry. Better cheat and say they are text files
00248       if ( fileName == dotdirectory )
00249         return mimeType( "text/plain" );
00250     }
00251 
00252   if ( !_is_local_file || _fast_mode )
00253   {
00254     QString def = KProtocolInfo::defaultMimetype( _url );
00255     if ( !def.isEmpty() && def != defaultMimeType() )
00256     {
00257        // The protocol says it always returns a given mimetype (e.g. text/html for "man:")
00258        return mimeType( def );
00259     }
00260     if ( path.endsWith( slash ) || path.isEmpty() )
00261     {
00262       // We have no filename at all. Maybe the protocol has a setting for
00263       // which mimetype this means (e.g. directory).
00264       // For HTTP (def==defaultMimeType()) we don't assume anything,
00265       // because of redirections (e.g. freshmeat downloads).
00266       if ( def.isEmpty() )
00267       {
00268           // Assume inode/directory, if the protocol supports listing.
00269           if ( KProtocolInfo::supportsListing( _url ) )
00270               return mimeType( QString::fromLatin1("inode/directory") );
00271           else
00272               return defaultMimeTypePtr(); // == 'no idea', e.g. for "data:,foo/"
00273       }
00274     }
00275 
00276     // No more chances for non local URLs
00277     return defaultMimeTypePtr();
00278   }
00279 
00280   // Do some magic for local files
00281   //kdDebug(7009) << QString("Mime Type finding for '%1'").arg(path) << endl;
00282   KMimeMagicResult* result = KMimeMagic::self()->findFileType( path );
00283 
00284   // If we still did not find it, we must assume the default mime type
00285   if ( !result || !result->isValid() )
00286     return defaultMimeTypePtr();
00287 
00288   // The mimemagic stuff was successful
00289   return mimeType( result->mimeType() );
00290 }
00291 
00292 KMimeType::Ptr KMimeType::findByURL( const KURL& _url, mode_t _mode,
00293                                      bool _is_local_file, bool _fast_mode,
00294                                      bool *accurate)
00295 {
00296     KMimeType::Ptr mime = findByURL(_url, _mode, _is_local_file, _fast_mode);
00297     if (accurate) *accurate = !(_fast_mode) || ((mime->patternsAccuracy() == 100) && mime != defaultMimeTypePtr());
00298     return mime;
00299 }
00300 
00301 KMimeType::Ptr KMimeType::diagnoseFileName(const QString &fileName, QString &pattern)
00302 {
00303   return KServiceTypeFactory::self()->findFromPattern( fileName, &pattern );
00304 }
00305 
00306 KMimeType::Ptr KMimeType::findByPath( const QString& path, mode_t mode, bool fast_mode )
00307 {
00308     KURL u;
00309     u.setPath(path);
00310     return findByURL( u, mode, true, fast_mode );
00311 }
00312 
00313 KMimeType::Ptr KMimeType::findByContent( const QByteArray &data, int *accuracy )
00314 {
00315   KMimeMagicResult *result = KMimeMagic::self()->findBufferType(data);
00316   if (accuracy)
00317       *accuracy = result->accuracy();
00318   return mimeType( result->mimeType() );
00319 }
00320 
00321 KMimeType::Ptr KMimeType::findByFileContent( const QString &fileName, int *accuracy )
00322 {
00323   KMimeMagicResult *result = KMimeMagic::self()->findFileType(fileName);
00324   if (accuracy)
00325       *accuracy = result->accuracy();
00326   return mimeType( result->mimeType() );
00327 }
00328 
00329 #define GZIP_MAGIC1 0x1f
00330 #define GZIP_MAGIC2 0x8b
00331 
00332 KMimeType::Format KMimeType::findFormatByFileContent( const QString &fileName )
00333 {
00334   KMimeType::Format result;
00335   result.compression = Format::NoCompression;
00336   KMimeType::Ptr mime = findByPath(fileName);
00337   if (mime->name() == defaultMimeType())
00338      mime = findByFileContent(fileName);
00339 
00340   result.text = mime->name().startsWith("text/");
00341   QVariant v = mime->property("X-KDE-text");
00342   if (v.isValid())
00343      result.text = v.toBool();
00344 
00345   if (mime->name().startsWith("inode/"))
00346      return result;
00347 
00348   QFile f(fileName);
00349   if (f.open(IO_ReadOnly))
00350   {
00351      unsigned char buf[10+1];
00352      int l = f.readBlock((char *)buf, 10);
00353      if ((l > 2) && (buf[0] == GZIP_MAGIC1) && (buf[1] == GZIP_MAGIC2))
00354         result.compression = Format::GZipCompression;
00355   }
00356   return result;
00357 }
00358 
00359 KMimeType::KMimeType( const QString & _fullpath, const QString& _type, const QString& _icon,
00360                       const QString& _comment, const QStringList& _patterns )
00361   : KServiceType( _fullpath, _type, _icon, _comment )
00362 {
00363   m_lstPatterns = _patterns;
00364 }
00365 
00366 KMimeType::KMimeType( const QString & _fullpath ) : KServiceType( _fullpath )
00367 {
00368   KDesktopFile _cfg( _fullpath, true );
00369   init ( &_cfg );
00370 
00371   if ( !isValid() )
00372     kdWarning(7009) << "mimetype not valid '" << m_strName << "' (missing entry in the file ?)" << endl;
00373 }
00374 
00375 KMimeType::KMimeType( KDesktopFile *config ) : KServiceType( config )
00376 {
00377   init( config );
00378 
00379   if ( !isValid() )
00380     kdWarning(7009) << "mimetype not valid '" << m_strName << "' (missing entry in the file ?)" << endl;
00381 }
00382 
00383 void KMimeType::init( KDesktopFile * config )
00384 {
00385   config->setDesktopGroup();
00386   m_lstPatterns = config->readListEntry( "Patterns", ';' );
00387 
00388   // Read the X-KDE-AutoEmbed setting and store it in the properties map
00389   QString XKDEAutoEmbed = QString::fromLatin1("X-KDE-AutoEmbed");
00390   if ( config->hasKey( XKDEAutoEmbed ) )
00391     m_mapProps.insert( XKDEAutoEmbed, QVariant( config->readBoolEntry( XKDEAutoEmbed ), 0 ) );
00392 
00393   QString XKDEText = QString::fromLatin1("X-KDE-text");
00394   if ( config->hasKey( XKDEText ) )
00395     m_mapProps.insert( XKDEText, config->readBoolEntry( XKDEText ) );
00396 
00397   QString XKDEIsAlso = QString::fromLatin1("X-KDE-IsAlso");
00398   if ( config->hasKey( XKDEIsAlso ) ) {
00399     QString inherits = config->readEntry( XKDEIsAlso );
00400     if ( inherits != name() )
00401         m_mapProps.insert( XKDEIsAlso, inherits );
00402     else
00403         kdWarning(7009) << "Error: " << inherits << " inherits from itself!!!!" << endl;
00404   }
00405 
00406   QString XKDEPatternsAccuracy = QString::fromLatin1("X-KDE-PatternsAccuracy");
00407   if ( config->hasKey( XKDEPatternsAccuracy ) )
00408     m_mapProps.insert( XKDEPatternsAccuracy, config->readEntry( XKDEPatternsAccuracy ) );
00409 
00410 }
00411 
00412 KMimeType::KMimeType( QDataStream& _str, int offset ) : KServiceType( _str, offset )
00413 {
00414   loadInternal( _str ); // load our specific stuff
00415 }
00416 
00417 void KMimeType::load( QDataStream& _str )
00418 {
00419   KServiceType::load( _str );
00420   loadInternal( _str );
00421 }
00422 
00423 void KMimeType::loadInternal( QDataStream& _str )
00424 {
00425   // kdDebug(7009) << "KMimeType::load( QDataStream& ) : loading list of patterns" << endl;
00426   _str >> m_lstPatterns;
00427 }
00428 
00429 void KMimeType::save( QDataStream& _str )
00430 {
00431   KServiceType::save( _str );
00432   // Warning adding/removing fields here involves a binary incompatible change - update version
00433   // number in ksycoca.h
00434   _str << m_lstPatterns;
00435 }
00436 
00437 QVariant KMimeType::property( const QString& _name ) const
00438 {
00439   if ( _name == "Patterns" )
00440     return QVariant( m_lstPatterns );
00441 
00442   return KServiceType::property( _name );
00443 }
00444 
00445 QStringList KMimeType::propertyNames() const
00446 {
00447   QStringList res = KServiceType::propertyNames();
00448   res.append( "Patterns" );
00449 
00450   return res;
00451 }
00452 
00453 KMimeType::~KMimeType()
00454 {
00455 }
00456 
00457 QPixmap KMimeType::pixmap( KIcon::Group _group, int _force_size, int _state,
00458                            QString * _path ) const
00459 {
00460   KIconLoader *iconLoader=KGlobal::iconLoader();
00461   QString iconName=icon( QString::null, false );
00462   if (!iconLoader->extraDesktopThemesAdded())
00463   {
00464     QPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
00465     if (!pixmap.isNull() ) return pixmap;
00466 
00467     iconLoader->addExtraDesktopThemes();
00468   }
00469 
00470   return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
00471 }
00472 
00473 QPixmap KMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size,
00474                            int _state, QString * _path ) const
00475 {
00476   KIconLoader *iconLoader=KGlobal::iconLoader();
00477   QString iconName=icon( _url, _url.isLocalFile() );
00478   if (!iconLoader->extraDesktopThemesAdded())
00479   {
00480     QPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
00481     if (!pixmap.isNull() ) return pixmap;
00482 
00483     iconLoader->addExtraDesktopThemes();
00484   }
00485 
00486   return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
00487 }
00488 
00489 QPixmap KMimeType::pixmapForURL( const KURL & _url, mode_t _mode, KIcon::Group _group,
00490                                  int _force_size, int _state, QString * _path )
00491 {
00492   KIconLoader *iconLoader=KGlobal::iconLoader();
00493   QString iconName = iconForURL( _url, _mode );
00494 
00495   if (!iconLoader->extraDesktopThemesAdded())
00496   {
00497     QPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
00498     if (!pixmap.isNull() ) return pixmap;
00499 
00500     iconLoader->addExtraDesktopThemes();
00501   }
00502 
00503   return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
00504 
00505 }
00506 
00507 QString KMimeType::iconForURL( const KURL & _url, mode_t _mode )
00508 {
00509     const KMimeType::Ptr mt = findByURL( _url, _mode, _url.isLocalFile(),
00510                                          false /*HACK*/);
00511     static const QString& unknown = KGlobal::staticQString("unknown");
00512     const QString mimeTypeIcon = mt->icon( _url, _url.isLocalFile() );
00513     QString i = mimeTypeIcon;
00514 
00515     // if we don't find an icon, maybe we can use the one for the protocol
00516     if ( i == unknown || i.isEmpty() || mt == defaultMimeTypePtr()
00517         // and for the root of the protocol (e.g. trash:/) the protocol icon has priority over the mimetype icon
00518         || _url.path().length() <= 1 )
00519     {
00520         i = favIconForURL( _url ); // maybe there is a favicon?
00521 
00522         if ( i.isEmpty() )
00523             i = KProtocolInfo::icon( _url.protocol() );
00524 
00525         // root of protocol: if we found nothing, revert to mimeTypeIcon (which is usually "folder")
00526         if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) )
00527             i = mimeTypeIcon;
00528     }
00529     return i;
00530 }
00531 
00532 QString KMimeType::favIconForURL( const KURL& url )
00533 {
00534     // this method will be called quite often, so better not read the config
00535     // again and again.
00536     static bool useFavIcons = true;
00537     static bool check = true;
00538     if ( check ) {
00539         check = false;
00540         KConfig *config = KGlobal::config();
00541         KConfigGroupSaver cs( config, "HTML Settings" );
00542         useFavIcons = config->readBoolEntry( "EnableFavicon", true );
00543     }
00544 
00545     if ( url.isLocalFile() || !url.protocol().startsWith("http")
00546          || !useFavIcons )
00547         return QString::null;
00548 
00549     DCOPRef kded( "kded", "favicons" );
00550     DCOPReply result = kded.call( "iconForURL(KURL)", url );
00551     if ( result.isValid() )
00552         return result;
00553 
00554     return QString::null;
00555 }
00556 
00557 QString KMimeType::parentMimeType() const
00558 {
00559   QVariant v = property("X-KDE-IsAlso");
00560   return v.toString();
00561 }
00562 
00563 bool KMimeType::is( const QString& mimeTypeName ) const
00564 {
00565   if ( name() == mimeTypeName )
00566       return true;
00567   QString st = parentMimeType();
00568   //if (st.isEmpty()) kdDebug(7009)<<"Parent mimetype is empty"<<endl;
00569   while ( !st.isEmpty() )
00570   {
00571       //kdDebug(7009)<<"Checking parent mime type: "<<st<<endl;
00572       KMimeType::Ptr ptr = KMimeType::mimeType( st );
00573       if (!ptr) return false; //error
00574       if ( ptr->name() == mimeTypeName )
00575           return true;
00576       st = ptr->parentMimeType();
00577   }
00578   return false;
00579 }
00580 
00581 int KMimeType::patternsAccuracy() const {
00582   QVariant v = property("X-KDE-PatternsAccuracy");
00583   if (!v.isValid()) return 100;
00584   else
00585       return v.toInt();
00586 }
00587 
00588 
00589 /*******************************************************
00590  *
00591  * KFolderType
00592  *
00593  ******************************************************/
00594 
00595 QString KFolderType::icon( const QString& _url, bool _is_local ) const
00596 {
00597   if ( !_is_local || _url.isEmpty() )
00598     return KMimeType::icon( _url, _is_local );
00599 
00600   return KFolderType::icon( KURL(_url), _is_local );
00601 }
00602 
00603 QString KFolderType::icon( const KURL& _url, bool _is_local ) const
00604 {
00605   if ( !_is_local )
00606     return KMimeType::icon( _url, _is_local );
00607 
00608   KURL u( _url );
00609   u.addPath( ".directory" );
00610 
00611   QString icon;
00612   // using KStandardDirs as this one checks for path being
00613   // a file instead of a directory
00614   if ( KStandardDirs::exists( u.path() ) )
00615   {
00616     KSimpleConfig cfg( u.path(), true );
00617     cfg.setDesktopGroup();
00618     icon = cfg.readEntry( "Icon" );
00619     QString empty_icon = cfg.readEntry( "EmptyIcon" );
00620 
00621     if ( !empty_icon.isEmpty() )
00622     {
00623       bool isempty = false;
00624       DIR *dp = 0L;
00625       struct dirent *ep;
00626       dp = opendir( QFile::encodeName(_url.path()) );
00627       if ( dp )
00628       {
00629         QValueList<QCString> entries;
00630         // Note that readdir isn't guaranteed to return "." and ".." first (#79826)
00631         ep=readdir( dp ); if ( ep ) entries.append( ep->d_name );
00632         ep=readdir( dp ); if ( ep ) entries.append( ep->d_name );
00633         if ( (ep=readdir( dp )) == 0L ) // third file is NULL entry -> empty directory
00634           isempty = true;
00635         else {
00636           entries.append( ep->d_name );
00637           if ( readdir( dp ) == 0 ) { // only three
00638             // check if we got "." ".." and ".directory"
00639             isempty = entries.find( "." ) != entries.end() &&
00640                       entries.find( ".." ) != entries.end() &&
00641                       entries.find( ".directory" ) != entries.end();
00642           }
00643         }
00644         if (!isempty && !strcmp(ep->d_name, ".directory"))
00645           isempty = (readdir(dp) == 0L);
00646         closedir( dp );
00647       }
00648 
00649       if ( isempty )
00650         return empty_icon;
00651     }
00652   }
00653 
00654   if ( icon.isEmpty() )
00655     return KMimeType::icon( _url, _is_local );
00656 
00657   if ( icon.startsWith( "./" ) ) {
00658     // path is relative with respect to the location
00659     // of the .directory file (#73463)
00660     KURL v( _url );
00661     v.addPath( icon.mid( 2 ) );
00662     icon = v.path();
00663   }
00664 
00665   return icon;
00666 }
00667 
00668 QString KFolderType::comment( const QString& _url, bool _is_local ) const
00669 {
00670   if ( !_is_local || _url.isEmpty() )
00671     return KMimeType::comment( _url, _is_local );
00672 
00673   return KFolderType::comment( KURL(_url), _is_local );
00674 }
00675 
00676 QString KFolderType::comment( const KURL& _url, bool _is_local ) const
00677 {
00678   if ( !_is_local )
00679     return KMimeType::comment( _url, _is_local );
00680 
00681   KURL u( _url );
00682   u.addPath( ".directory" );
00683 
00684   KSimpleConfig cfg( u.path(), true );
00685   cfg.setDesktopGroup();
00686   QString comment = cfg.readEntry( "Comment" );
00687   if ( comment.isEmpty() )
00688     return KMimeType::comment( _url, _is_local );
00689 
00690   return comment;
00691 }
00692 
00693 /*******************************************************
00694  *
00695  * KDEDesktopMimeType
00696  *
00697  ******************************************************/
00698 
00699 QString KDEDesktopMimeType::icon( const QString& _url, bool _is_local ) const
00700 {
00701   if ( !_is_local || _url.isEmpty() )
00702     return KMimeType::icon( _url, _is_local );
00703 
00704   KURL u( _url );
00705   return icon( u, _is_local );
00706 }
00707 
00708 QString KDEDesktopMimeType::icon( const KURL& _url, bool _is_local ) const
00709 {
00710   if ( !_is_local )
00711     return KMimeType::icon( _url, _is_local );
00712 
00713   KSimpleConfig cfg( _url.path(), true );
00714   cfg.setDesktopGroup();
00715   QString icon = cfg.readEntry( "Icon" );
00716   QString type = cfg.readEntry( "Type" );
00717 
00718   if ( type == "FSDevice" || type == "FSDev") // need to provide FSDev for
00719                                               // backwards compatibility
00720   {
00721     QString unmount_icon = cfg.readEntry( "UnmountIcon" );
00722     QString dev = cfg.readEntry( "Dev" );
00723     if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() )
00724     {
00725       QString mp = KIO::findDeviceMountPoint( dev );
00726       bool mbSupermount = false;
00727       if ( mp.isNull() )
00728     {
00729       
00730       KMountPoint::List mountPoints = KMountPoint::currentMountPoints();
00731       for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 
00732           it != mountPoints.end(); ++it)
00733         {
00734           if( (*it)->mountType()=="supermount" && ((*it)->mountedFrom()==dev))
00735         {
00736           mbSupermount = true;
00737           break;
00738         }
00739         }
00740     }
00741 
00742       // Is the device not mounted ?
00743       if ( mp.isNull() && !mbSupermount )
00744         return unmount_icon;
00745     }
00746   } else if ( type == "Link" ) {
00747       const QString emptyIcon = cfg.readEntry( "EmptyIcon" );
00748       if ( !emptyIcon.isEmpty() ) {
00749           const QString u = cfg.readPathEntry( "URL" );
00750           const KURL url( u );
00751           if ( url.protocol() == "trash" ) {
00752               // We need to find if the trash is empty, preferrably without using a KIO job.
00753               // So instead kio_trash leaves an entry in its config file for us.
00754               KSimpleConfig trashConfig( "trashrc", true );
00755               trashConfig.setGroup( "Status" );
00756               if ( trashConfig.readBoolEntry( "Empty", true ) ) {
00757                   return emptyIcon;
00758               }
00759           }
00760       }
00761   }
00762 
00763   if ( icon.isEmpty() )
00764     return KMimeType::icon( _url, _is_local );
00765 
00766   return icon;
00767 }
00768 
00769 QPixmap KDEDesktopMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size,
00770                                     int _state, QString * _path ) const
00771 {
00772   QString _icon = icon( _url, _url.isLocalFile() );
00773   QPixmap pix = KGlobal::iconLoader()->loadIcon( _icon, _group,
00774         _force_size, _state, _path, false );
00775   if ( pix.isNull() )
00776       pix = KGlobal::iconLoader()->loadIcon( "unknown", _group,
00777         _force_size, _state, _path, false );
00778   return pix;
00779 }
00780 
00781 QString KDEDesktopMimeType::comment( const QString& _url, bool _is_local ) const
00782 {
00783   if ( !_is_local || _url.isEmpty() )
00784     return KMimeType::comment( _url, _is_local );
00785 
00786   KURL u( _url );
00787   return comment( u, _is_local );
00788 }
00789 
00790 QString KDEDesktopMimeType::comment( const KURL& _url, bool _is_local ) const
00791 {
00792   if ( !_is_local )
00793     return KMimeType::comment( _url, _is_local );
00794 
00795   KSimpleConfig cfg( _url.path(), true );
00796   cfg.setDesktopGroup();
00797   QString comment = cfg.readEntry( "Comment" );
00798   if ( comment.isEmpty() )
00799     return KMimeType::comment( _url, _is_local );
00800 
00801   return comment;
00802 }
00803 
00804 pid_t KDEDesktopMimeType::run( const KURL& u, bool _is_local )
00805 {
00806   // It might be a security problem to run external untrusted desktop
00807   // entry files
00808   if ( !_is_local )
00809     return 0;
00810 
00811   KSimpleConfig cfg( u.path(), true );
00812   cfg.setDesktopGroup();
00813   QString type = cfg.readEntry( "Type" );
00814   if ( type.isEmpty() )
00815   {
00816     QString tmp = i18n("The desktop entry file %1 "
00817                        "has no Type=... entry.").arg(u.path() );
00818     KMessageBoxWrapper::error( 0, tmp);
00819     return 0;
00820   }
00821 
00822   //kdDebug(7009) << "TYPE = " << type.data() << endl;
00823 
00824   if ( type == "FSDevice" )
00825     return runFSDevice( u, cfg );
00826   else if ( type == "Application" )
00827     return runApplication( u, u.path() );
00828   else if ( type == "Link" )
00829   {
00830     cfg.setDollarExpansion( true ); // for URL=file:$HOME (Simon)
00831     return runLink( u, cfg );
00832   }
00833   else if ( type == "MimeType" )
00834     return runMimeType( u, cfg );
00835 
00836 
00837   QString tmp = i18n("The desktop entry of type\n%1\nis unknown.").arg( type );
00838   KMessageBoxWrapper::error( 0, tmp);
00839 
00840   return 0;
00841 }
00842 
00843 pid_t KDEDesktopMimeType::runFSDevice( const KURL& _url, const KSimpleConfig &cfg )
00844 {
00845   pid_t retval = 0;
00846 
00847   QString dev = cfg.readEntry( "Dev" );
00848 
00849   if ( dev.isEmpty() )
00850   {
00851     QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() );
00852     KMessageBoxWrapper::error( 0, tmp);
00853     return retval;
00854   }
00855 
00856   QString mp = KIO::findDeviceMountPoint( dev );
00857   if ( mp.isNull() )
00858     {
00859       KMountPoint::List mountPoints = KMountPoint::currentMountPoints();
00860       for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 
00861       it != mountPoints.end(); ++it)
00862     {
00863       if( (*it)->mountType()=="supermount" && ((*it)->mountedFrom()==dev))
00864         {
00865           mp = (*it)->mountPoint();
00866           break;
00867         }
00868     }
00869     }
00870   // Is the device already mounted ?
00871   if ( !mp.isNull() )
00872   {
00873     KURL mpURL;
00874     mpURL.setPath( mp );
00875     // Open a new window
00876     retval = KRun::runURL( mpURL, QString::fromLatin1("inode/directory") );
00877   }
00878   else
00879   {
00880     bool ro = cfg.readBoolEntry( "ReadOnly", false );
00881     QString fstype = cfg.readEntry( "FSType" );
00882     if ( fstype == "Default" ) // KDE-1 thing
00883       fstype = QString::null;
00884     QString point = cfg.readEntry( "MountPoint" );
00885 #ifndef Q_WS_WIN
00886     (void) new KAutoMount( ro, fstype, dev, point, _url.path() );
00887 #endif
00888     retval = -1; // we don't want to return 0, but we don't want to return a pid
00889   }
00890 
00891   return retval;
00892 }
00893 
00894 pid_t KDEDesktopMimeType::runApplication( const KURL& , const QString & _serviceFile )
00895 {
00896   KService s( _serviceFile );
00897   if ( !s.isValid() )
00898     // The error message was already displayed, so we can just quit here
00899     return 0;
00900 
00901   KURL::List lst;
00902   return KRun::run( s, lst );
00903 }
00904 
00905 pid_t KDEDesktopMimeType::runLink( const KURL& _url, const KSimpleConfig &cfg )
00906 {
00907   QString u = cfg.readPathEntry( "URL" );
00908   if ( u.isEmpty() )
00909   {
00910     QString tmp = i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.").arg( _url.prettyURL() );
00911     KMessageBoxWrapper::error( 0, tmp );
00912     return 0;
00913   }
00914 
00915   KURL url ( u );
00916   KRun* run = new KRun(url);
00917 
00918   // X-KDE-LastOpenedWith holds the service desktop entry name that
00919   // was should be preferred for opening this URL if possible.
00920   // This is used by the Recent Documents menu for instance.
00921   QString lastOpenedWidth = cfg.readEntry( "X-KDE-LastOpenedWith" );
00922   if ( !lastOpenedWidth.isEmpty() )
00923       run->setPreferredService( lastOpenedWidth );
00924 
00925   return -1; // we don't want to return 0, but we don't want to return a pid
00926 }
00927 
00928 pid_t KDEDesktopMimeType::runMimeType( const KURL& url , const KSimpleConfig & )
00929 {
00930   // Hmm, can't really use keditfiletype since we might be looking
00931   // at the global file, or at a file not in share/mimelnk...
00932 
00933   QStringList args;
00934   args << "openProperties";
00935   args << url.path();
00936 
00937   int pid;
00938   if ( !KApplication::kdeinitExec("kfmclient", args, 0, &pid) )
00939       return pid;
00940 
00941   KProcess p;
00942   p << "kfmclient" << args;
00943   p.start(KProcess::DontCare);
00944   return p.pid();
00945 }
00946 
00947 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::builtinServices( const KURL& _url )
00948 {
00949   QValueList<Service> result;
00950 
00951   if ( !_url.isLocalFile() )
00952     return result;
00953 
00954   KSimpleConfig cfg( _url.path(), true );
00955   cfg.setDesktopGroup();
00956   QString type = cfg.readEntry( "Type" );
00957 
00958   if ( type.isEmpty() )
00959     return result;
00960 
00961   if ( type == "FSDevice" )
00962   {
00963     QString dev = cfg.readEntry( "Dev" );
00964     if ( dev.isEmpty() )
00965     {
00966       QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() );
00967       KMessageBoxWrapper::error( 0, tmp);
00968     }
00969     else
00970     {
00971       QString mp = KIO::findDeviceMountPoint( dev );
00972       bool mbSupermount = false;
00973       if ( mp.isEmpty() )
00974     {
00975       KMountPoint::List mountPoints = KMountPoint::currentMountPoints();
00976       for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 
00977           it != mountPoints.end(); ++it)
00978         {
00979           if( (*it)->mountType()=="supermount" && ((*it)->mountedFrom()==dev))
00980         {
00981           mbSupermount = true;
00982           break;
00983         }
00984         }
00985     }
00986       if( !mbSupermount )
00987     {
00988       // not mounted ?
00989       if ( mp.isEmpty() )
00990       {
00991         Service mount;
00992         mount.m_strName = i18n("Mount");
00993         mount.m_type = ST_MOUNT;
00994         result.append( mount );
00995       }
00996       else
00997       {
00998         Service unmount;
00999 #ifdef HAVE_VOLMGT
01000         /*
01001          *  Solaris' volume management can only umount+eject
01002          */
01003         unmount.m_strName = i18n("Eject");
01004 #else
01005         unmount.m_strName = i18n("Unmount");
01006 #endif
01007         unmount.m_type = ST_UNMOUNT;
01008         result.append( unmount );
01009       }
01010     }
01011   }
01012   }
01013   return result;
01014 }
01015 
01016 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const QString& path, bool bLocalFiles )
01017 {
01018   KSimpleConfig cfg( path, true );
01019   return userDefinedServices( path, cfg, bLocalFiles );
01020 }
01021 
01022 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const QString& path, KConfig& cfg, bool bLocalFiles )
01023 {
01024  return userDefinedServices( path, cfg, bLocalFiles, KURL::List() );
01025 }
01026 
01027 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const QString& path, KConfig& cfg, bool bLocalFiles, const KURL::List & file_list )
01028 {
01029   QValueList<Service> result;
01030 
01031   cfg.setDesktopGroup();
01032 
01033   if ( !cfg.hasKey( "Actions" ) && !cfg.hasKey( "X-KDE-GetActionMenu") )
01034     return result;
01035 
01036   if ( cfg.hasKey( "TryExec" ) )
01037   {
01038       QString tryexec = cfg.readPathEntry( "TryExec" );
01039       QString exe =  KStandardDirs::findExe( tryexec );
01040       if (exe.isEmpty()) {
01041           return result;
01042       }
01043   }
01044 
01045   QStringList keys;
01046 
01047   if( cfg.hasKey( "X-KDE-GetActionMenu" )) {
01048     QString dcopcall = cfg.readEntry( "X-KDE-GetActionMenu" );
01049     const QCString app = dcopcall.section(' ', 0,0).utf8();
01050 
01051     QByteArray dataToSend;
01052     QDataStream dataStream(dataToSend, IO_WriteOnly);
01053     dataStream << file_list;
01054     QCString replyType;
01055     QByteArray replyData;
01056     QCString object =    dcopcall.section(' ', 1,-2).utf8();
01057     QString function =  dcopcall.section(' ', -1);
01058     if(!function.endsWith("(KURL::List)")) {
01059       kdWarning() << "Desktop file " << path << " contains an invalid X-KDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl;
01060     } else {
01061       if(kapp->dcopClient()->call( app, object,
01062                    function.utf8(),
01063                    dataToSend, replyType, replyData, true, -1)
01064         && replyType == "QStringList" ) {
01065 
01066         QDataStream dataStreamIn(replyData, IO_ReadOnly);
01067         dataStreamIn >> keys;
01068       }
01069     }
01070   }
01071 
01072   keys += cfg.readListEntry( "Actions", ';' ); //the desktop standard defines ";" as separator!
01073 
01074   if ( keys.count() == 0 )
01075     return result;
01076 
01077   QStringList::ConstIterator it = keys.begin();
01078   QStringList::ConstIterator end = keys.end();
01079   for ( ; it != end; ++it )
01080   {
01081     //kdDebug(7009) << "CURRENT KEY = " << (*it) << endl;
01082 
01083     QString group = *it;
01084 
01085     if (group == "_SEPARATOR_")
01086     {
01087         Service s;
01088         result.append(s);
01089         continue;
01090     }
01091 
01092     group.prepend( "Desktop Action " );
01093 
01094     bool bInvalidMenu = false;
01095 
01096     if ( cfg.hasGroup( group ) )
01097     {
01098       cfg.setGroup( group );
01099 
01100       if ( !cfg.hasKey( "Name" ) || !cfg.hasKey( "Exec" ) )
01101         bInvalidMenu = true;
01102       else
01103       {
01104         QString exec = cfg.readPathEntry( "Exec" );
01105         if ( bLocalFiles || exec.contains("%U") || exec.contains("%u") )
01106         {
01107           Service s;
01108           s.m_strName = cfg.readEntry( "Name" );
01109           s.m_strIcon = cfg.readEntry( "Icon" );
01110           s.m_strExec = exec;
01111           s.m_type = ST_USER_DEFINED;
01112           s.m_display = !cfg.readBoolEntry( "NoDisplay" );
01113           result.append( s );
01114         }
01115       }
01116     }
01117     else
01118       bInvalidMenu = true;
01119 
01120     if ( bInvalidMenu )
01121     {
01122       QString tmp = i18n("The desktop entry file\n%1\n has an invalid menu entry\n%2.").arg( path ).arg( *it );
01123       KMessageBoxWrapper::error( 0, tmp );
01124     }
01125   }
01126 
01127   return result;
01128 }
01129 
01130 void KDEDesktopMimeType::executeService( const QString& _url, KDEDesktopMimeType::Service& _service )
01131 {
01132     KURL u;
01133     u.setPath(_url);
01134     KURL::List lst;
01135     lst.append( u );
01136     executeService( lst, _service );
01137 }
01138 
01139 void KDEDesktopMimeType::executeService( const KURL::List& urls, KDEDesktopMimeType::Service& _service )
01140 {
01141   //kdDebug(7009) << "EXECUTING Service " << _service.m_strName << endl;
01142 
01143   if ( _service.m_type == ST_USER_DEFINED )
01144   {
01145     kdDebug() << "KDEDesktopMimeType::executeService " << _service.m_strName
01146               << " first url's path=" << urls.first().path() << " exec=" << _service.m_strExec << endl;
01147     KRun::run( _service.m_strExec, urls, _service.m_strName, _service.m_strIcon, _service.m_strIcon );
01148     // The action may update the desktop file. Example: eject unmounts (#5129).
01149     KDirNotify_stub allDirNotify("*", "KDirNotify*");
01150     allDirNotify.FilesChanged( urls );
01151     return;
01152   }
01153   else if ( _service.m_type == ST_MOUNT || _service.m_type == ST_UNMOUNT )
01154   {
01155     Q_ASSERT( urls.count() == 1 );
01156     QString path = urls.first().path();
01157     //kdDebug(7009) << "MOUNT&UNMOUNT" << endl;
01158 
01159     KSimpleConfig cfg( path, true );
01160     cfg.setDesktopGroup();
01161     QString dev = cfg.readEntry( "Dev" );
01162     if ( dev.isEmpty() )
01163     {
01164       QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( path );
01165       KMessageBoxWrapper::error( 0, tmp );
01166       return;
01167     }
01168     QString mp = KIO::findDeviceMountPoint( dev );
01169 
01170     if ( _service.m_type == ST_MOUNT )
01171     {
01172       // Already mounted? Strange, but who knows ...
01173       if ( !mp.isEmpty() )
01174       {
01175         kdDebug(7009) << "ALREADY Mounted" << endl;
01176         return;
01177       }
01178 
01179       bool ro = cfg.readBoolEntry( "ReadOnly", false );
01180       QString fstype = cfg.readEntry( "FSType" );
01181       if ( fstype == "Default" ) // KDE-1 thing
01182           fstype = QString::null;
01183       QString point = cfg.readEntry( "MountPoint" );
01184 #ifndef Q_WS_WIN
01185       (void)new KAutoMount( ro, fstype, dev, point, path, false );
01186 #endif
01187     }
01188     else if ( _service.m_type == ST_UNMOUNT )
01189     {
01190       // Not mounted? Strange, but who knows ...
01191       if ( mp.isEmpty() )
01192         return;
01193 
01194 #ifndef Q_WS_WIN
01195       (void)new KAutoUnmount( mp, path );
01196 #endif
01197     }
01198   }
01199   else
01200     assert( 0 );
01201 }
01202 
01203 const QString & KMimeType::defaultMimeType()
01204 {
01205     static const QString & s_strDefaultMimeType =
01206         KGlobal::staticQString( "application/octet-stream" );
01207     return s_strDefaultMimeType;
01208 }
01209 
01210 void KMimeType::virtual_hook( int id, void* data )
01211 { KServiceType::virtual_hook( id, data ); }
01212 
01213 void KFolderType::virtual_hook( int id, void* data )
01214 { KMimeType::virtual_hook( id, data ); }
01215 
01216 void KDEDesktopMimeType::virtual_hook( int id, void* data )
01217 { KMimeType::virtual_hook( id, data ); }
01218 
01219 void KExecMimeType::virtual_hook( int id, void* data )
01220 { KMimeType::virtual_hook( id, data ); }
01221 
01222 #include "kmimetyperesolver.moc"
01223 
KDE Home | KDE Accessibility Home | Description of Access Keys