kio Library API Documentation

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., 59 Temple Place - Suite 330,
00017  *  Boston, MA 02111-1307, 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   QString type = (result && result->isValid())?
00317     result->mimeType() : defaultMimeType();
00318   if (accuracy)
00319       *accuracy = result->accuracy();
00320   return mimeType( result->mimeType() );
00321 }
00322 
00323 KMimeType::Ptr KMimeType::findByFileContent( const QString &fileName, int *accuracy )
00324 {
00325   KMimeMagicResult *result = KMimeMagic::self()->findFileType(fileName);
00326   QString type = (result && result->isValid())?
00327     result->mimeType() : defaultMimeType();
00328   if (accuracy)
00329       *accuracy = result->accuracy();
00330   return mimeType( result->mimeType() );
00331 }
00332 
00333 #define GZIP_MAGIC1 0x1f
00334 #define GZIP_MAGIC2 0x8b
00335 
00336 KMimeType::Format KMimeType::findFormatByFileContent( const QString &fileName )
00337 {
00338   KMimeType::Format result;
00339   result.compression = Format::NoCompression;
00340   KMimeType::Ptr mime = findByPath(fileName);
00341   if (mime->name() == "application/octet-stream")
00342      mime =  findByFileContent(fileName);
00343 
00344   result.text = mime->name().startsWith("text/");
00345   QVariant v = mime->property("X-KDE-text");
00346   if (v.isValid())
00347      result.text = v.toBool();
00348 
00349   if (mime->name().startsWith("inode/"))
00350      return result;
00351 
00352   QFile f(fileName);
00353   if (f.open(IO_ReadOnly))
00354   {
00355      unsigned char buf[10+1];
00356      int l = f.readBlock((char *)buf, 10);
00357      if ((l > 2) && (buf[0] == GZIP_MAGIC1) && (buf[1] == GZIP_MAGIC2))
00358         result.compression = Format::GZipCompression;
00359   }
00360   return result;
00361 }
00362 
00363 KMimeType::KMimeType( const QString & _fullpath, const QString& _type, const QString& _icon,
00364                       const QString& _comment, const QStringList& _patterns )
00365   : KServiceType( _fullpath, _type, _icon, _comment )
00366 {
00367   m_lstPatterns = _patterns;
00368 }
00369 
00370 KMimeType::KMimeType( const QString & _fullpath ) : KServiceType( _fullpath )
00371 {
00372   KDesktopFile _cfg( _fullpath, true );
00373   init ( &_cfg );
00374 
00375   if ( !isValid() )
00376     kdWarning(7009) << "mimetype not valid '" << m_strName << "' (missing entry in the file ?)" << endl;
00377 }
00378 
00379 KMimeType::KMimeType( KDesktopFile *config ) : KServiceType( config )
00380 {
00381   init( config );
00382 
00383   if ( !isValid() )
00384     kdWarning(7009) << "mimetype not valid '" << m_strName << "' (missing entry in the file ?)" << endl;
00385 }
00386 
00387 void KMimeType::init( KDesktopFile * config )
00388 {
00389   config->setDesktopGroup();
00390   m_lstPatterns = config->readListEntry( "Patterns", ';' );
00391 
00392   // Read the X-KDE-AutoEmbed setting and store it in the properties map
00393   QString XKDEAutoEmbed = QString::fromLatin1("X-KDE-AutoEmbed");
00394   if ( config->hasKey( XKDEAutoEmbed ) )
00395     m_mapProps.insert( XKDEAutoEmbed, QVariant( config->readBoolEntry( XKDEAutoEmbed ), 0 ) );
00396 
00397   QString XKDEText = QString::fromLatin1("X-KDE-text");
00398   if ( config->hasKey( XKDEText ) )
00399     m_mapProps.insert( XKDEText, config->readBoolEntry( XKDEText ) );
00400 
00401   QString XKDEIsAlso = QString::fromLatin1("X-KDE-IsAlso");
00402   if ( config->hasKey( XKDEIsAlso ) )
00403     m_mapProps.insert( XKDEIsAlso, config->readEntry( XKDEIsAlso ) );
00404 
00405   QString XKDEPatternsAccuracy = QString::fromLatin1("X-KDE-PatternsAccuracy");
00406   if ( config->hasKey( XKDEPatternsAccuracy ) )
00407     m_mapProps.insert( XKDEPatternsAccuracy, config->readEntry( XKDEPatternsAccuracy ) );
00408 
00409 }
00410 
00411 KMimeType::KMimeType( QDataStream& _str, int offset ) : KServiceType( _str, offset )
00412 {
00413   loadInternal( _str ); // load our specific stuff
00414 }
00415 
00416 void KMimeType::load( QDataStream& _str )
00417 {
00418   KServiceType::load( _str );
00419   loadInternal( _str );
00420 }
00421 
00422 void KMimeType::loadInternal( QDataStream& _str )
00423 {
00424   // kdDebug(7009) << "KMimeType::load( QDataStream& ) : loading list of patterns" << endl;
00425   _str >> m_lstPatterns;
00426 }
00427 
00428 void KMimeType::save( QDataStream& _str )
00429 {
00430   KServiceType::save( _str );
00431   // Warning adding/removing fields here involves a binary incompatible change - update version
00432   // number in ksycoca.h
00433   _str << m_lstPatterns;
00434 }
00435 
00436 QVariant KMimeType::property( const QString& _name ) const
00437 {
00438   if ( _name == "Patterns" )
00439     return QVariant( m_lstPatterns );
00440 
00441   return KServiceType::property( _name );
00442 }
00443 
00444 QStringList KMimeType::propertyNames() const
00445 {
00446   QStringList res = KServiceType::propertyNames();
00447   res.append( "Patterns" );
00448 
00449   return res;
00450 }
00451 
00452 KMimeType::~KMimeType()
00453 {
00454 }
00455 
00456 QPixmap KMimeType::pixmap( KIcon::Group _group, int _force_size, int _state,
00457                            QString * _path ) const
00458 {
00459   KIconLoader *iconLoader=KGlobal::iconLoader();
00460   QString iconName=icon( QString::null, false );
00461   if (!iconLoader->extraDesktopThemesAdded())
00462   {
00463     QPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
00464     if (!pixmap.isNull() ) return pixmap;
00465 
00466     iconLoader->addExtraDesktopThemes();
00467   }
00468 
00469   return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
00470 }
00471 
00472 QPixmap KMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size,
00473                            int _state, QString * _path ) const
00474 {
00475   KIconLoader *iconLoader=KGlobal::iconLoader();
00476   QString iconName=icon( _url, _url.isLocalFile() );
00477   if (!iconLoader->extraDesktopThemesAdded())
00478   {
00479     QPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
00480     if (!pixmap.isNull() ) return pixmap;
00481 
00482     iconLoader->addExtraDesktopThemes();
00483   }
00484 
00485   return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
00486 }
00487 
00488 QPixmap KMimeType::pixmapForURL( const KURL & _url, mode_t _mode, KIcon::Group _group,
00489                                  int _force_size, int _state, QString * _path )
00490 {
00491   KIconLoader *iconLoader=KGlobal::iconLoader();
00492   QString iconName = iconForURL( _url, _mode );
00493 
00494   if (!iconLoader->extraDesktopThemesAdded())
00495   {
00496     QPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
00497     if (!pixmap.isNull() ) return pixmap;
00498 
00499     iconLoader->addExtraDesktopThemes();
00500   }
00501 
00502   return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
00503 
00504 }
00505 
00506 QString KMimeType::iconForURL( const KURL & _url, mode_t _mode )
00507 {
00508     const KMimeType::Ptr mt = findByURL( _url, _mode, _url.isLocalFile(),
00509                                          false /*HACK*/);
00510     static const QString& unknown = KGlobal::staticQString("unknown");
00511     const QString mimeTypeIcon = mt->icon( _url, _url.isLocalFile() );
00512     QString i = mimeTypeIcon;
00513 
00514     // if we don't find an icon, maybe we can use the one for the protocol
00515     if ( i == unknown || i.isEmpty() || mt == defaultMimeTypePtr()
00516         // and for the root of the protocol (e.g. trash:/) the protocol icon has priority over the mimetype icon
00517         || _url.path().length() <= 1 ) 
00518     {
00519         i = favIconForURL( _url ); // maybe there is a favicon?
00520 
00521         if ( i.isEmpty() )
00522             i = KProtocolInfo::icon( _url.protocol() );
00523 
00524         // root of protocol: if we found nothing, revert to mimeTypeIcon (which is usually "folder")
00525         if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) )
00526             i = mimeTypeIcon;
00527     }
00528     return i;
00529 }
00530 
00531 QString KMimeType::favIconForURL( const KURL& url )
00532 {
00533     // this method will be called quite often, so better not read the config
00534     // again and again.
00535     static bool useFavIcons = true;
00536     static bool check = true;
00537     if ( check ) {
00538         check = false;
00539         KConfig *config = KGlobal::config();
00540         KConfigGroupSaver cs( config, "HTML Settings" );
00541         useFavIcons = config->readBoolEntry( "EnableFavicon", true );
00542     }
00543 
00544     if ( url.isLocalFile() || !url.protocol().startsWith("http")
00545          || !useFavIcons )
00546         return QString::null;
00547 
00548     DCOPRef kded( "kded", "favicons" );
00549     DCOPReply result = kded.call( "iconForURL(KURL)", url );
00550     if ( result.isValid() )
00551         return result;
00552 
00553     return QString::null;
00554 }
00555 
00556 QString KMimeType::parentMimeType() const
00557 {
00558   QVariant v = property("X-KDE-IsAlso");
00559   return v.toString();
00560 }
00561 
00562 bool KMimeType::is( const QString& mimeTypeName ) const
00563 {
00564   if ( name() == mimeTypeName )
00565       return true;
00566   QString st = parentMimeType();
00567   //if (st.isEmpty()) kdDebug(7009)<<"Parent mimetype is empty"<<endl;
00568   while ( !st.isEmpty() )
00569   {
00570       //kdDebug(7009)<<"Checking parent mime type: "<<st<<endl;
00571       KMimeType::Ptr ptr = KMimeType::mimeType( st );
00572       if (!ptr) return false; //error
00573       if ( ptr->name() == mimeTypeName )
00574           return true;
00575       st = ptr->parentMimeType();
00576   }
00577   return false;
00578 }
00579 
00580 int KMimeType::patternsAccuracy() const {
00581   QVariant v = property("X-KDE-PatternsAccuracy");
00582   if (!v.isValid()) return 100;
00583   else
00584       return v.toInt();
00585 }
00586 
00587 
00588 /*******************************************************
00589  *
00590  * KFolderType
00591  *
00592  ******************************************************/
00593 
00594 QString KFolderType::icon( const QString& _url, bool _is_local ) const
00595 {
00596   if ( !_is_local || _url.isEmpty() )
00597     return KMimeType::icon( _url, _is_local );
00598 
00599   return KFolderType::icon( KURL(_url), _is_local );
00600 }
00601 
00602 QString KFolderType::icon( const KURL& _url, bool _is_local ) const
00603 {
00604   if ( !_is_local )
00605     return KMimeType::icon( _url, _is_local );
00606 
00607   KURL u( _url );
00608   u.addPath( ".directory" );
00609 
00610   QString icon;
00611   // using KStandardDirs as this one checks for path being
00612   // a file instead of a directory
00613   if ( KStandardDirs::exists( u.path() ) )
00614   {
00615     KSimpleConfig cfg( u.path(), true );
00616     cfg.setDesktopGroup();
00617     icon = cfg.readEntry( "Icon" );
00618     QString empty_icon = cfg.readEntry( "EmptyIcon" );
00619 
00620     if ( !empty_icon.isEmpty() )
00621     {
00622       bool isempty = false;
00623       DIR *dp = 0L;
00624       struct dirent *ep;
00625       dp = opendir( QFile::encodeName(_url.path()) );
00626       if ( dp )
00627       {
00628         QValueList<QCString> entries;
00629         // Note that readdir isn't guaranteed to return "." and ".." first (#79826)
00630         ep=readdir( dp ); if ( ep ) entries.append( ep->d_name );
00631         ep=readdir( dp ); if ( ep ) entries.append( ep->d_name );
00632         if ( (ep=readdir( dp )) == 0L ) // third file is NULL entry -> empty directory
00633           isempty = true;
00634         else {
00635           entries.append( ep->d_name );
00636           if ( readdir( dp ) == 0 ) { // only three
00637             // check if we got "." ".." and ".directory"
00638             isempty = entries.find( "." ) != entries.end() &&
00639                       entries.find( ".." ) != entries.end() &&
00640                       entries.find( ".directory" ) != entries.end();
00641           }
00642         }
00643         if (!isempty && !strcmp(ep->d_name, ".directory"))
00644           isempty = (readdir(dp) == 0L);
00645         closedir( dp );
00646       }
00647 
00648       if ( isempty )
00649         return empty_icon;
00650     }
00651   }
00652 
00653   if ( icon.isEmpty() )
00654     return KMimeType::icon( _url, _is_local );
00655 
00656   if ( icon.startsWith( "./" ) ) {
00657     // path is relative with respect to the location
00658     // of the .directory file (#73463)
00659     KURL v( _url );
00660     v.addPath( icon.mid( 2 ) );
00661     icon = v.path();
00662   }
00663 
00664   return icon;
00665 }
00666 
00667 QString KFolderType::comment( const QString& _url, bool _is_local ) const
00668 {
00669   if ( !_is_local || _url.isEmpty() )
00670     return KMimeType::comment( _url, _is_local );
00671 
00672   return KFolderType::comment( KURL(_url), _is_local );
00673 }
00674 
00675 QString KFolderType::comment( const KURL& _url, bool _is_local ) const
00676 {
00677   if ( !_is_local )
00678     return KMimeType::comment( _url, _is_local );
00679 
00680   KURL u( _url );
00681   u.addPath( ".directory" );
00682 
00683   KSimpleConfig cfg( u.path(), true );
00684   cfg.setDesktopGroup();
00685   QString comment = cfg.readEntry( "Comment" );
00686   if ( comment.isEmpty() )
00687     return KMimeType::comment( _url, _is_local );
00688 
00689   return comment;
00690 }
00691 
00692 /*******************************************************
00693  *
00694  * KDEDesktopMimeType
00695  *
00696  ******************************************************/
00697 
00698 QString KDEDesktopMimeType::icon( const QString& _url, bool _is_local ) const
00699 {
00700   if ( !_is_local || _url.isEmpty() )
00701     return KMimeType::icon( _url, _is_local );
00702 
00703   KURL u( _url );
00704   return icon( u, _is_local );
00705 }
00706 
00707 QString KDEDesktopMimeType::icon( const KURL& _url, bool _is_local ) const
00708 {
00709   if ( !_is_local )
00710     return KMimeType::icon( _url, _is_local );
00711 
00712   KSimpleConfig cfg( _url.path(), true );
00713   cfg.setDesktopGroup();
00714   QString icon = cfg.readEntry( "Icon" );
00715   QString type = cfg.readEntry( "Type" );
00716 
00717   if ( type == "FSDevice" || type == "FSDev") // need to provide FSDev for
00718                                               // backwards compatibility
00719   {
00720     QString unmount_icon = cfg.readEntry( "UnmountIcon" );
00721     QString dev = cfg.readEntry( "Dev" );
00722     if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() )
00723     {
00724       QString mp = KIO::findDeviceMountPoint( dev );
00725       bool mbSupermount = false;
00726       if ( mp.isNull() )
00727     {
00728       
00729       KMountPoint::List mountPoints = KMountPoint::currentMountPoints();
00730       for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 
00731           it != mountPoints.end(); ++it)
00732         {
00733           if( (*it)->mountType()=="supermount" && ((*it)->mountedFrom()==dev))
00734         {
00735           mbSupermount = true;
00736           break;
00737         }
00738         }
00739     }
00740 
00741       // Is the device not mounted ?
00742       if ( mp.isNull() && !mbSupermount )
00743         return unmount_icon;
00744     }
00745   } else if ( type == "Link" ) {
00746       const QString emptyIcon = cfg.readEntry( "EmptyIcon" );
00747       if ( !emptyIcon.isEmpty() ) {
00748           const QString u = cfg.readPathEntry( "URL" );
00749           const KURL url( u );
00750           if ( url.protocol() == "trash" ) {
00751               // We need to find if the trash is empty, preferrably without using a KIO job.
00752               // So instead kio_trash leaves an entry in its config file for us.
00753               KSimpleConfig trashConfig( "trashrc", true );
00754               trashConfig.setGroup( "Status" );
00755               if ( trashConfig.readBoolEntry( "Empty", true ) ) {
00756                   return emptyIcon;
00757               }
00758           }
00759       }
00760   }
00761 
00762   if ( icon.isEmpty() )
00763     return KMimeType::icon( _url, _is_local );
00764 
00765   return icon;
00766 }
00767 
00768 QPixmap KDEDesktopMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size,
00769                                     int _state, QString * _path ) const
00770 {
00771   QString _icon = icon( _url, _url.isLocalFile() );
00772   QPixmap pix = KGlobal::iconLoader()->loadIcon( _icon, _group,
00773         _force_size, _state, _path, false );
00774   if ( pix.isNull() )
00775       pix = KGlobal::iconLoader()->loadIcon( "unknown", _group,
00776         _force_size, _state, _path, false );
00777   return pix;
00778 }
00779 
00780 QString KDEDesktopMimeType::comment( const QString& _url, bool _is_local ) const
00781 {
00782   if ( !_is_local || _url.isEmpty() )
00783     return KMimeType::comment( _url, _is_local );
00784 
00785   KURL u( _url );
00786   return comment( u, _is_local );
00787 }
00788 
00789 QString KDEDesktopMimeType::comment( const KURL& _url, bool _is_local ) const
00790 {
00791   if ( !_is_local )
00792     return KMimeType::comment( _url, _is_local );
00793 
00794   KSimpleConfig cfg( _url.path(), true );
00795   cfg.setDesktopGroup();
00796   QString comment = cfg.readEntry( "Comment" );
00797   if ( comment.isEmpty() )
00798     return KMimeType::comment( _url, _is_local );
00799 
00800   return comment;
00801 }
00802 
00803 pid_t KDEDesktopMimeType::run( const KURL& u, bool _is_local )
00804 {
00805   // It might be a security problem to run external untrusted desktop
00806   // entry files
00807   if ( !_is_local )
00808     return 0;
00809 
00810   KSimpleConfig cfg( u.path(), true );
00811   cfg.setDesktopGroup();
00812   QString type = cfg.readEntry( "Type" );
00813   if ( type.isEmpty() )
00814   {
00815     QString tmp = i18n("The desktop entry file %1 "
00816                        "has no Type=... entry.").arg(u.path() );
00817     KMessageBoxWrapper::error( 0, tmp);
00818     return 0;
00819   }
00820 
00821   //kdDebug(7009) << "TYPE = " << type.data() << endl;
00822 
00823   if ( type == "FSDevice" )
00824     return runFSDevice( u, cfg );
00825   else if ( type == "Application" )
00826     return runApplication( u, u.path() );
00827   else if ( type == "Link" )
00828   {
00829     cfg.setDollarExpansion( true ); // for URL=file:$HOME (Simon)
00830     return runLink( u, cfg );
00831   }
00832   else if ( type == "MimeType" )
00833     return runMimeType( u, cfg );
00834 
00835 
00836   QString tmp = i18n("The desktop entry of type\n%1\nis unknown.").arg( type );
00837   KMessageBoxWrapper::error( 0, tmp);
00838 
00839   return 0;
00840 }
00841 
00842 pid_t KDEDesktopMimeType::runFSDevice( const KURL& _url, const KSimpleConfig &cfg )
00843 {
00844   pid_t retval = 0;
00845 
00846   QString dev = cfg.readEntry( "Dev" );
00847 
00848   if ( dev.isEmpty() )
00849   {
00850     QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() );
00851     KMessageBoxWrapper::error( 0, tmp);
00852     return retval;
00853   }
00854 
00855   QString mp = KIO::findDeviceMountPoint( dev );
00856   if ( mp.isNull() )
00857     {
00858       KMountPoint::List mountPoints = KMountPoint::currentMountPoints();
00859       for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 
00860       it != mountPoints.end(); ++it)
00861     {
00862       if( (*it)->mountType()=="supermount" && ((*it)->mountedFrom()==dev))
00863         {
00864           mp = (*it)->mountPoint();
00865           break;
00866         }
00867     }
00868     }
00869   // Is the device already mounted ?
00870   if ( !mp.isNull() )
00871   {
00872     KURL mpURL;
00873     mpURL.setPath( mp );
00874     // Open a new window
00875     retval = KRun::runURL( mpURL, QString::fromLatin1("inode/directory") );
00876   }
00877   else
00878   {
00879     bool ro = cfg.readBoolEntry( "ReadOnly", false );
00880     QString fstype = cfg.readEntry( "FSType" );
00881     if ( fstype == "Default" ) // KDE-1 thing
00882       fstype = QString::null;
00883     QString point = cfg.readEntry( "MountPoint" );
00884 #ifndef Q_WS_WIN
00885     (void) new KAutoMount( ro, fstype, dev, point, _url.path() );
00886 #endif
00887     retval = -1; // we don't want to return 0, but we don't want to return a pid
00888   }
00889 
00890   return retval;
00891 }
00892 
00893 pid_t KDEDesktopMimeType::runApplication( const KURL& , const QString & _serviceFile )
00894 {
00895   KService s( _serviceFile );
00896   if ( !s.isValid() )
00897     // The error message was already displayed, so we can just quit here
00898     return 0;
00899 
00900   KURL::List lst;
00901   return KRun::run( s, lst );
00902 }
00903 
00904 pid_t KDEDesktopMimeType::runLink( const KURL& _url, const KSimpleConfig &cfg )
00905 {
00906   QString u = cfg.readPathEntry( "URL" );
00907   if ( u.isEmpty() )
00908   {
00909     QString tmp = i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.").arg( _url.prettyURL() );
00910     KMessageBoxWrapper::error( 0, tmp );
00911     return 0;
00912   }
00913 
00914   KURL url ( u );
00915   KRun* run = new KRun(url);
00916 
00917   // X-KDE-LastOpenedWith holds the service desktop entry name that
00918   // was should be preferred for opening this URL if possible.
00919   // This is used by the Recent Documents menu for instance.
00920   QString lastOpenedWidth = cfg.readEntry( "X-KDE-LastOpenedWith" );
00921   if ( !lastOpenedWidth.isEmpty() )
00922       run->setPreferredService( lastOpenedWidth );
00923 
00924   return -1; // we don't want to return 0, but we don't want to return a pid
00925 }
00926 
00927 pid_t KDEDesktopMimeType::runMimeType( const KURL& url , const KSimpleConfig & )
00928 {
00929   // Hmm, can't really use keditfiletype since we might be looking
00930   // at the global file, or at a file not in share/mimelnk...
00931 
00932   QStringList args;
00933   args << "openProperties";
00934   args << url.path();
00935 
00936   int pid;
00937   if ( !KApplication::kdeinitExec("kfmclient", args, 0, &pid) )
00938       return pid;
00939 
00940   KProcess p;
00941   p << "kfmclient" << args;
00942   p.start(KProcess::DontCare);
00943   return p.pid();
00944 }
00945 
00946 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::builtinServices( const KURL& _url )
00947 {
00948   QValueList<Service> result;
00949 
00950   if ( !_url.isLocalFile() )
00951     return result;
00952 
00953   KSimpleConfig cfg( _url.path(), true );
00954   cfg.setDesktopGroup();
00955   QString type = cfg.readEntry( "Type" );
00956 
00957   if ( type.isEmpty() )
00958     return result;
00959 
00960   if ( type == "FSDevice" )
00961   {
00962     QString dev = cfg.readEntry( "Dev" );
00963     if ( dev.isEmpty() )
00964     {
00965       QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() );
00966       KMessageBoxWrapper::error( 0, tmp);
00967     }
00968     else
00969     {
00970       QString mp = KIO::findDeviceMountPoint( dev );
00971       bool mbSupermount = false;
00972       if ( mp.isEmpty() )
00973     {
00974       KMountPoint::List mountPoints = KMountPoint::currentMountPoints();
00975       for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 
00976           it != mountPoints.end(); ++it)
00977         {
00978           if( (*it)->mountType()=="supermount" && ((*it)->mountedFrom()==dev))
00979         {
00980           mbSupermount = true;
00981           break;
00982         }
00983         }
00984     }
00985       if( !mbSupermount )
00986     {
00987       // not mounted ?
00988       if ( mp.isEmpty() )
00989       {
00990         Service mount;
00991         mount.m_strName = i18n("Mount");
00992         mount.m_type = ST_MOUNT;
00993         result.append( mount );
00994       }
00995       else
00996       {
00997         Service unmount;
00998 #ifdef HAVE_VOLMGT
00999         /*
01000          *  Solaris' volume management can only umount+eject
01001          */
01002         unmount.m_strName = i18n("Eject");
01003 #else
01004         unmount.m_strName = i18n("Unmount");
01005 #endif
01006         unmount.m_type = ST_UNMOUNT;
01007         result.append( unmount );
01008       }
01009     }
01010   }
01011   }
01012   return result;
01013 }
01014 
01015 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const QString& path, bool bLocalFiles )
01016 {
01017   KSimpleConfig cfg( path, true );
01018   return userDefinedServices( path, cfg, bLocalFiles );
01019 }
01020 
01021 QValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const QString& path, KConfig& cfg, bool bLocalFiles )
01022 {
01023   QValueList<Service> result;
01024 
01025   cfg.setDesktopGroup();
01026 
01027   if ( !cfg.hasKey( "Actions" ) )
01028     return result;
01029 
01030   if ( cfg.hasKey( "TryExec" ) )
01031   {
01032       QString tryexec = cfg.readPathEntry( "TryExec" );
01033       QString exe =  KStandardDirs::findExe( tryexec );
01034       if (exe.isEmpty()) {
01035           return result;
01036       }
01037   }
01038 
01039   QStringList keys = cfg.readListEntry( "Actions", ';' ); //the desktop standard defines ";" as separator!
01040 
01041   if ( keys.count() == 0 )
01042     return result;
01043 
01044   QStringList::ConstIterator it = keys.begin();
01045   QStringList::ConstIterator end = keys.end();
01046   for ( ; it != end; ++it )
01047   {
01048     //kdDebug(7009) << "CURRENT KEY = " << (*it) << endl;
01049 
01050     QString group = *it;
01051 
01052     if (group == "_SEPARATOR_")
01053     {
01054         Service s;
01055         result.append(s);
01056         continue;
01057     }
01058 
01059     group.prepend( "Desktop Action " );
01060 
01061     bool bInvalidMenu = false;
01062 
01063     if ( cfg.hasGroup( group ) )
01064     {
01065       cfg.setGroup( group );
01066 
01067       if ( !cfg.hasKey( "Name" ) || !cfg.hasKey( "Exec" ) )
01068         bInvalidMenu = true;
01069       else
01070       {
01071         QString exec = cfg.readPathEntry( "Exec" );
01072         if ( bLocalFiles || exec.contains("%U") || exec.contains("%u") )
01073         {
01074           Service s;
01075           s.m_strName = cfg.readEntry( "Name" );
01076           s.m_strIcon = cfg.readEntry( "Icon" );
01077           s.m_strExec = exec;
01078           s.m_type = ST_USER_DEFINED;
01079           s.m_display = !cfg.readBoolEntry( "NoDisplay" );
01080           result.append( s );
01081         }
01082       }
01083     }
01084     else
01085       bInvalidMenu = true;
01086 
01087     if ( bInvalidMenu )
01088     {
01089       QString tmp = i18n("The desktop entry file\n%1\n has an invalid menu entry\n%2.").arg( path ).arg( *it );
01090       KMessageBoxWrapper::error( 0, tmp );
01091     }
01092   }
01093 
01094   return result;
01095 }
01096 
01097 void KDEDesktopMimeType::executeService( const QString& _url, KDEDesktopMimeType::Service& _service )
01098 {
01099     KURL u;
01100     u.setPath(_url);
01101     KURL::List lst;
01102     lst.append( u );
01103     executeService( lst, _service );
01104 }
01105 
01106 void KDEDesktopMimeType::executeService( const KURL::List& urls, KDEDesktopMimeType::Service& _service )
01107 {
01108   //kdDebug(7009) << "EXECUTING Service " << _service.m_strName << endl;
01109 
01110   if ( _service.m_type == ST_USER_DEFINED )
01111   {
01112     kdDebug() << "KDEDesktopMimeType::executeService " << _service.m_strName
01113               << " first url's path=" << urls.first().path() << " exec=" << _service.m_strExec << endl;
01114     KRun::run( _service.m_strExec, urls, _service.m_strName, _service.m_strIcon, _service.m_strIcon );
01115     // The action may update the desktop file. Example: eject unmounts (#5129).
01116     KDirNotify_stub allDirNotify("*", "KDirNotify*");
01117     allDirNotify.FilesChanged( urls );
01118     return;
01119   }
01120   else if ( _service.m_type == ST_MOUNT || _service.m_type == ST_UNMOUNT )
01121   {
01122     Q_ASSERT( urls.count() == 1 );
01123     QString path = urls.first().path();
01124     //kdDebug(7009) << "MOUNT&UNMOUNT" << endl;
01125 
01126     KSimpleConfig cfg( path, true );
01127     cfg.setDesktopGroup();
01128     QString dev = cfg.readEntry( "Dev" );
01129     if ( dev.isEmpty() )
01130     {
01131       QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( path );
01132       KMessageBoxWrapper::error( 0, tmp );
01133       return;
01134     }
01135     QString mp = KIO::findDeviceMountPoint( dev );
01136 
01137     if ( _service.m_type == ST_MOUNT )
01138     {
01139       // Already mounted? Strange, but who knows ...
01140       if ( !mp.isEmpty() )
01141       {
01142         kdDebug(7009) << "ALREADY Mounted" << endl;
01143         return;
01144       }
01145 
01146       bool ro = cfg.readBoolEntry( "ReadOnly", false );
01147       QString fstype = cfg.readEntry( "FSType" );
01148       if ( fstype == "Default" ) // KDE-1 thing
01149           fstype = QString::null;
01150       QString point = cfg.readEntry( "MountPoint" );
01151 #ifndef Q_WS_WIN
01152       (void)new KAutoMount( ro, fstype, dev, point, path, false );
01153 #endif
01154     }
01155     else if ( _service.m_type == ST_UNMOUNT )
01156     {
01157       // Not mounted? Strange, but who knows ...
01158       if ( mp.isEmpty() )
01159         return;
01160 
01161 #ifndef Q_WS_WIN
01162       (void)new KAutoUnmount( mp, path );
01163 #endif
01164     }
01165   }
01166   else
01167     assert( 0 );
01168 }
01169 
01170 const QString & KMimeType::defaultMimeType()
01171 {
01172     static const QString & s_strDefaultMimeType =
01173         KGlobal::staticQString( "application/octet-stream" );
01174     return s_strDefaultMimeType;
01175 }
01176 
01177 void KMimeType::virtual_hook( int id, void* data )
01178 { KServiceType::virtual_hook( id, data ); }
01179 
01180 void KFolderType::virtual_hook( int id, void* data )
01181 { KMimeType::virtual_hook( id, data ); }
01182 
01183 void KDEDesktopMimeType::virtual_hook( int id, void* data )
01184 { KMimeType::virtual_hook( id, data ); }
01185 
01186 void KExecMimeType::virtual_hook( int id, void* data )
01187 { KMimeType::virtual_hook( id, data ); }
01188 
01189 #include "kmimetyperesolver.moc"
01190 
KDE Logo
This file is part of the documentation for kio Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Jul 20 13:55:15 2006 by doxygen 1.4.4 written by Dimitri van Heesch, © 1997-2003