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