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 <tqstring.h> 00044 #include <tqfile.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 TQValueList<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 TQString sDefaultMimeType = stdDirs.resourceDirs("mime").first()+defaultMimeType()+".desktop"; 00084 s_pDefaultType = new KMimeType( sDefaultMimeType, defaultMimeType(), 00085 "unknown", "mime", TQStringList() ); 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 TQString& _type ) 00135 { 00136 TQString 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 TQString& _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 TQString 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( TQFile::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( TQFile::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 TQString fileName ( _url.fileName() ); 00206 00207 static const TQString& 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 KMimeType::Ptr resultMime = mimeType( result->mimeType() ); 00226 if (resultMime->patternsAccuracy() > 0) { 00227 return resultMime; 00228 } 00229 } 00230 } 00231 } 00232 00233 return mime; 00234 } 00235 } 00236 00237 static const TQString& dotdesktop = KGlobal::staticQString(".desktop"); 00238 static const TQString& dotkdelnk = KGlobal::staticQString(".kdelnk"); 00239 static const TQString& dotdirectory = KGlobal::staticQString(".directory"); 00240 00241 // Another filename binding, hardcoded, is .desktop: 00242 if ( fileName.endsWith( dotdesktop ) ) 00243 return mimeType( "application/x-desktop" ); 00244 // Another filename binding, hardcoded, is .kdelnk; 00245 // this is preserved for backwards compatibility 00246 if ( fileName.endsWith( dotkdelnk ) ) 00247 return mimeType( "application/x-desktop" ); 00248 // .directory files are detected as x-desktop by mimemagic 00249 // but don't have a Type= entry. Better cheat and say they are text files 00250 if ( fileName == dotdirectory ) 00251 return mimeType( "text/plain" ); 00252 } 00253 00254 if ( !_is_local_file || _fast_mode ) 00255 { 00256 TQString def = KProtocolInfo::defaultMimetype( _url ); 00257 if ( !def.isEmpty() && def != defaultMimeType() ) 00258 { 00259 // The protocol says it always returns a given mimetype (e.g. text/html for "man:") 00260 return mimeType( def ); 00261 } 00262 if ( path.endsWith( slash ) || path.isEmpty() ) 00263 { 00264 // We have no filename at all. Maybe the protocol has a setting for 00265 // which mimetype this means (e.g. directory). 00266 // For HTTP (def==defaultMimeType()) we don't assume anything, 00267 // because of redirections (e.g. freshmeat downloads). 00268 if ( def.isEmpty() ) 00269 { 00270 // Assume inode/directory, if the protocol supports listing. 00271 if ( KProtocolInfo::supportsListing( _url ) ) 00272 return mimeType( TQString::fromLatin1("inode/directory") ); 00273 else 00274 return defaultMimeTypePtr(); // == 'no idea', e.g. for "data:,foo/" 00275 } 00276 } 00277 00278 // No more chances for non local URLs 00279 return defaultMimeTypePtr(); 00280 } 00281 00282 // Do some magic for local files 00283 //kdDebug(7009) << TQString("Mime Type finding for '%1'").arg(path) << endl; 00284 KMimeMagicResult* result = KMimeMagic::self()->findFileType( path ); 00285 00286 // If we still did not find it, we must assume the default mime type 00287 if ( !result || !result->isValid() ) 00288 return defaultMimeTypePtr(); 00289 00290 // The mimemagic stuff was successful 00291 return mimeType( result->mimeType() ); 00292 } 00293 00294 KMimeType::Ptr KMimeType::findByURL( const KURL& _url, mode_t _mode, 00295 bool _is_local_file, bool _fast_mode, 00296 bool *accurate) 00297 { 00298 KMimeType::Ptr mime = findByURL(_url, _mode, _is_local_file, _fast_mode); 00299 if (accurate) *accurate = !(_fast_mode) || ((mime->patternsAccuracy() == 100) && mime != defaultMimeTypePtr()); 00300 return mime; 00301 } 00302 00303 KMimeType::Ptr KMimeType::diagnoseFileName(const TQString &fileName, TQString &pattern) 00304 { 00305 return KServiceTypeFactory::self()->findFromPattern( fileName, &pattern ); 00306 } 00307 00308 KMimeType::Ptr KMimeType::findByPath( const TQString& path, mode_t mode, bool fast_mode ) 00309 { 00310 KURL u; 00311 u.setPath(path); 00312 return findByURL( u, mode, true, fast_mode ); 00313 } 00314 00315 KMimeType::Ptr KMimeType::findByContent( const TQByteArray &data, int *accuracy ) 00316 { 00317 KMimeMagicResult *result = KMimeMagic::self()->findBufferType(data); 00318 if (accuracy) 00319 *accuracy = result->accuracy(); 00320 return mimeType( result->mimeType() ); 00321 } 00322 00323 KMimeType::Ptr KMimeType::findByFileContent( const TQString &fileName, int *accuracy ) 00324 { 00325 KMimeMagicResult *result = KMimeMagic::self()->findFileType(fileName); 00326 if (accuracy) 00327 *accuracy = result->accuracy(); 00328 return mimeType( result->mimeType() ); 00329 } 00330 00331 #define GZIP_MAGIC1 0x1f 00332 #define GZIP_MAGIC2 0x8b 00333 00334 KMimeType::Format KMimeType::findFormatByFileContent( const TQString &fileName ) 00335 { 00336 KMimeType::Format result; 00337 result.compression = Format::NoCompression; 00338 KMimeType::Ptr mime = findByPath(fileName); 00339 00340 result.text = mime->name().startsWith("text/"); 00341 TQVariant 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 TQFile 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 TQString & _fullpath, const TQString& _type, const TQString& _icon, 00360 const TQString& _comment, const TQStringList& _patterns ) 00361 : KServiceType( _fullpath, _type, _icon, _comment ) 00362 { 00363 m_lstPatterns = _patterns; 00364 } 00365 00366 KMimeType::KMimeType( const TQString & _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 TQString XKDEAutoEmbed = TQString::fromLatin1("X-KDE-AutoEmbed"); 00390 if ( config->hasKey( XKDEAutoEmbed ) ) 00391 m_mapProps.insert( XKDEAutoEmbed, TQVariant( config->readBoolEntry( XKDEAutoEmbed ), 0 ) ); 00392 00393 TQString XKDEText = TQString::fromLatin1("X-KDE-text"); 00394 if ( config->hasKey( XKDEText ) ) 00395 m_mapProps.insert( XKDEText, config->readBoolEntry( XKDEText ) ); 00396 00397 TQString XKDEIsAlso = TQString::fromLatin1("X-KDE-IsAlso"); 00398 if ( config->hasKey( XKDEIsAlso ) ) { 00399 TQString 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 TQString XKDEPatternsAccuracy = TQString::fromLatin1("X-KDE-PatternsAccuracy"); 00407 if ( config->hasKey( XKDEPatternsAccuracy ) ) 00408 m_mapProps.insert( XKDEPatternsAccuracy, config->readEntry( XKDEPatternsAccuracy ) ); 00409 00410 } 00411 00412 KMimeType::KMimeType( TQDataStream& _str, int offset ) : KServiceType( _str, offset ) 00413 { 00414 loadInternal( _str ); // load our specific stuff 00415 } 00416 00417 void KMimeType::load( TQDataStream& _str ) 00418 { 00419 KServiceType::load( _str ); 00420 loadInternal( _str ); 00421 } 00422 00423 void KMimeType::loadInternal( TQDataStream& _str ) 00424 { 00425 // kdDebug(7009) << "KMimeType::load( TQDataStream& ) : loading list of patterns" << endl; 00426 _str >> m_lstPatterns; 00427 } 00428 00429 void KMimeType::save( TQDataStream& _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 TQVariant KMimeType::property( const TQString& _name ) const 00438 { 00439 if ( _name == "Patterns" ) 00440 return TQVariant( m_lstPatterns ); 00441 00442 return KServiceType::property( _name ); 00443 } 00444 00445 TQStringList KMimeType::propertyNames() const 00446 { 00447 TQStringList res = KServiceType::propertyNames(); 00448 res.append( "Patterns" ); 00449 00450 return res; 00451 } 00452 00453 KMimeType::~KMimeType() 00454 { 00455 } 00456 00457 TQPixmap KMimeType::pixmap( KIcon::Group _group, int _force_size, int _state, 00458 TQString * _path ) const 00459 { 00460 KIconLoader *iconLoader=KGlobal::iconLoader(); 00461 TQString iconName=icon( TQString::null, false ); 00462 if (!iconLoader->extraDesktopThemesAdded()) 00463 { 00464 TQPixmap 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 TQPixmap KMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size, 00474 int _state, TQString * _path ) const 00475 { 00476 KIconLoader *iconLoader=KGlobal::iconLoader(); 00477 TQString iconName=icon( _url, _url.isLocalFile() ); 00478 if (!iconLoader->extraDesktopThemesAdded()) 00479 { 00480 TQPixmap 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 TQPixmap KMimeType::pixmapForURL( const KURL & _url, mode_t _mode, KIcon::Group _group, 00490 int _force_size, int _state, TQString * _path ) 00491 { 00492 KIconLoader *iconLoader=KGlobal::iconLoader(); 00493 TQString iconName = iconForURL( _url, _mode ); 00494 00495 if (!iconLoader->extraDesktopThemesAdded()) 00496 { 00497 TQPixmap 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 TQString 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 TQString& unknown = KGlobal::staticQString("unknown"); 00512 const TQString mimeTypeIcon = mt->icon( _url, _url.isLocalFile() ); 00513 TQString 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 // special case: root directory (/) -- Gitea issue #128 00530 if ( _url == KURL("file:///") ) 00531 i = "folder_red"; 00532 } 00533 return i; 00534 } 00535 00536 TQString KMimeType::favIconForURL( const KURL& url ) 00537 { 00538 // this method will be called quite often, so better not read the config 00539 // again and again. 00540 static bool useFavIcons = true; 00541 static bool check = true; 00542 if ( check ) { 00543 check = false; 00544 KConfig *config = KGlobal::config(); 00545 KConfigGroupSaver cs( config, "HTML Settings" ); 00546 useFavIcons = config->readBoolEntry( "EnableFavicon", true ); 00547 } 00548 00549 if ( url.isLocalFile() || !url.protocol().startsWith("http") 00550 || !useFavIcons ) 00551 return TQString::null; 00552 00553 DCOPRef kded( "kded", "favicons" ); 00554 DCOPReply result = kded.call( "iconForURL(KURL)", url ); 00555 if ( result.isValid() ) 00556 return result; 00557 00558 return TQString::null; 00559 } 00560 00561 TQString KMimeType::parentMimeType() const 00562 { 00563 TQVariant v = property("X-KDE-IsAlso"); 00564 return v.toString(); 00565 } 00566 00567 bool KMimeType::is( const TQString& mimeTypeName ) const 00568 { 00569 if ( name() == mimeTypeName ) 00570 return true; 00571 TQString st = parentMimeType(); 00572 //if (st.isEmpty()) kdDebug(7009)<<"Parent mimetype is empty"<<endl; 00573 while ( !st.isEmpty() ) 00574 { 00575 //kdDebug(7009)<<"Checking parent mime type: "<<st<<endl; 00576 KMimeType::Ptr ptr = KMimeType::mimeType( st ); 00577 if (!ptr) return false; //error 00578 if ( ptr->name() == mimeTypeName ) 00579 return true; 00580 st = ptr->parentMimeType(); 00581 } 00582 return false; 00583 } 00584 00585 int KMimeType::patternsAccuracy() const { 00586 TQVariant v = property("X-KDE-PatternsAccuracy"); 00587 if (!v.isValid()) return 100; 00588 else 00589 return v.toInt(); 00590 } 00591 00592 00593 /******************************************************* 00594 * 00595 * KFolderType 00596 * 00597 ******************************************************/ 00598 00599 TQString KFolderType::icon( const TQString& _url, bool _is_local ) const 00600 { 00601 if ( !_is_local || _url.isEmpty() ) 00602 return KMimeType::icon( _url, _is_local ); 00603 00604 return KFolderType::icon( KURL(_url), _is_local ); 00605 } 00606 00607 TQString KFolderType::icon( const KURL& _url, bool _is_local ) const 00608 { 00609 if ( !_is_local ) 00610 return KMimeType::icon( _url, _is_local ); 00611 00612 KURL u( _url ); 00613 u.addPath( ".directory" ); 00614 00615 TQString icon; 00616 // using KStandardDirs as this one checks for path being 00617 // a file instead of a directory 00618 if ( KStandardDirs::exists( u.path() ) ) 00619 { 00620 KSimpleConfig cfg( u.path(), true ); 00621 cfg.setDesktopGroup(); 00622 icon = cfg.readEntry( "Icon" ); 00623 TQString empty_icon = cfg.readEntry( "EmptyIcon" ); 00624 00625 if ( !empty_icon.isEmpty() ) 00626 { 00627 bool isempty = false; 00628 DIR *dp = 0L; 00629 struct dirent *ep; 00630 dp = opendir( TQFile::encodeName(_url.path()) ); 00631 if ( dp ) 00632 { 00633 TQValueList<TQCString> entries; 00634 // Note that readdir isn't guaranteed to return "." and ".." first (#79826) 00635 ep=readdir( dp ); if ( ep ) entries.append( ep->d_name ); 00636 ep=readdir( dp ); if ( ep ) entries.append( ep->d_name ); 00637 if ( (ep=readdir( dp )) == 0L ) // third file is NULL entry -> empty directory 00638 isempty = true; 00639 else { 00640 entries.append( ep->d_name ); 00641 if ( readdir( dp ) == 0 ) { // only three 00642 // check if we got "." ".." and ".directory" 00643 isempty = entries.find( "." ) != entries.end() && 00644 entries.find( ".." ) != entries.end() && 00645 entries.find( ".directory" ) != entries.end(); 00646 } 00647 } 00648 if (!isempty && !strcmp(ep->d_name, ".directory")) 00649 isempty = (readdir(dp) == 0L); 00650 closedir( dp ); 00651 } 00652 00653 if ( isempty ) 00654 return empty_icon; 00655 } 00656 } 00657 00658 if ( icon.isEmpty() ) 00659 return KMimeType::icon( _url, _is_local ); 00660 00661 if ( icon.startsWith( "./" ) ) { 00662 // path is relative with respect to the location 00663 // of the .directory file (#73463) 00664 KURL v( _url ); 00665 v.addPath( icon.mid( 2 ) ); 00666 icon = v.path(); 00667 } 00668 00669 return icon; 00670 } 00671 00672 TQString KFolderType::comment( const TQString& _url, bool _is_local ) const 00673 { 00674 if ( !_is_local || _url.isEmpty() ) 00675 return KMimeType::comment( _url, _is_local ); 00676 00677 return KFolderType::comment( KURL(_url), _is_local ); 00678 } 00679 00680 TQString KFolderType::comment( const KURL& _url, bool _is_local ) const 00681 { 00682 if ( !_is_local ) 00683 return KMimeType::comment( _url, _is_local ); 00684 00685 KURL u( _url ); 00686 u.addPath( ".directory" ); 00687 00688 KDesktopFile cfg( u.path(), true ); 00689 TQString comment = cfg.readComment(); 00690 if ( comment.isEmpty() ) 00691 return KMimeType::comment( _url, _is_local ); 00692 00693 return comment; 00694 } 00695 00696 /******************************************************* 00697 * 00698 * KDEDesktopMimeType 00699 * 00700 ******************************************************/ 00701 00702 TQString KDEDesktopMimeType::icon( const TQString& _url, bool _is_local ) const 00703 { 00704 if ( !_is_local || _url.isEmpty() ) 00705 return KMimeType::icon( _url, _is_local ); 00706 00707 KURL u( _url ); 00708 return icon( u, _is_local ); 00709 } 00710 00711 TQString KDEDesktopMimeType::icon( const KURL& _url, bool _is_local ) const 00712 { 00713 if ( !_is_local ) 00714 return KMimeType::icon( _url, _is_local ); 00715 00716 KSimpleConfig cfg( _url.path(), true ); 00717 cfg.setDesktopGroup(); 00718 TQString icon = cfg.readEntry( "Icon" ); 00719 TQString type = cfg.readEntry( "Type" ); 00720 00721 if ( type == "FSDevice" || type == "FSDev") // need to provide FSDev for 00722 // backwards compatibility 00723 { 00724 TQString unmount_icon = cfg.readEntry( "UnmountIcon" ); 00725 TQString dev = cfg.readEntry( "Dev" ); 00726 if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() ) 00727 { 00728 TQString mp = KIO::findDeviceMountPoint( dev ); 00729 // Is the device not mounted ? 00730 if ( mp.isNull() ) 00731 return unmount_icon; 00732 } 00733 } else if ( type == "Link" ) { 00734 const TQString emptyIcon = cfg.readEntry( "EmptyIcon" ); 00735 if ( !emptyIcon.isEmpty() ) { 00736 const TQString u = cfg.readPathEntry( "URL" ); 00737 const KURL url( u ); 00738 if ( url.protocol() == "trash" ) { 00739 // We need to find if the trash is empty, preferrably without using a KIO job. 00740 // So instead kio_trash leaves an entry in its config file for us. 00741 KSimpleConfig trashConfig( "trashrc", true ); 00742 trashConfig.setGroup( "Status" ); 00743 if ( trashConfig.readBoolEntry( "Empty", true ) ) { 00744 return emptyIcon; 00745 } 00746 } 00747 } 00748 } 00749 00750 if ( icon.isEmpty() ) 00751 return KMimeType::icon( _url, _is_local ); 00752 00753 return icon; 00754 } 00755 00756 TQPixmap KDEDesktopMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size, 00757 int _state, TQString * _path ) const 00758 { 00759 TQString _icon = icon( _url, _url.isLocalFile() ); 00760 TQPixmap pix = KGlobal::iconLoader()->loadIcon( _icon, _group, 00761 _force_size, _state, _path, false ); 00762 if ( pix.isNull() ) 00763 pix = KGlobal::iconLoader()->loadIcon( "unknown", _group, 00764 _force_size, _state, _path, false ); 00765 return pix; 00766 } 00767 00768 TQString KDEDesktopMimeType::comment( const TQString& _url, bool _is_local ) const 00769 { 00770 if ( !_is_local || _url.isEmpty() ) 00771 return KMimeType::comment( _url, _is_local ); 00772 00773 KURL u( _url ); 00774 return comment( u, _is_local ); 00775 } 00776 00777 TQString KDEDesktopMimeType::comment( const KURL& _url, bool _is_local ) const 00778 { 00779 if ( !_is_local ) 00780 return KMimeType::comment( _url, _is_local ); 00781 00782 KDesktopFile cfg( _url.path(), true ); 00783 TQString comment = cfg.readComment(); 00784 if ( comment.isEmpty() ) 00785 return KMimeType::comment( _url, _is_local ); 00786 00787 return comment; 00788 } 00789 00790 pid_t KDEDesktopMimeType::run( const KURL& u, bool _is_local ) 00791 { 00792 // It might be a security problem to run external untrusted desktop 00793 // entry files 00794 if ( !_is_local ) 00795 return 0; 00796 00797 KSimpleConfig cfg( u.path(), true ); 00798 cfg.setDesktopGroup(); 00799 TQString type = cfg.readEntry( "Type" ); 00800 if ( type.isEmpty() ) 00801 { 00802 TQString tmp = i18n("The desktop entry file %1 " 00803 "has no Type=... entry.").arg(u.path() ); 00804 KMessageBoxWrapper::error( 0, tmp); 00805 return 0; 00806 } 00807 00808 //kdDebug(7009) << "TYPE = " << type.data() << endl; 00809 00810 if ( type == "FSDevice" ) 00811 return runFSDevice( u, cfg ); 00812 else if ( type == "Application" ) 00813 return runApplication( u, u.path() ); 00814 else if ( type == "Link" ) 00815 { 00816 cfg.setDollarExpansion( true ); // for URL=file:$HOME (Simon) 00817 return runLink( u, cfg ); 00818 } 00819 else if ( type == "MimeType" ) 00820 return runMimeType( u, cfg ); 00821 00822 00823 TQString tmp = i18n("The desktop entry of type\n%1\nis unknown.").arg( type ); 00824 KMessageBoxWrapper::error( 0, tmp); 00825 00826 return 0; 00827 } 00828 00829 pid_t KDEDesktopMimeType::runFSDevice( const KURL& _url, const KSimpleConfig &cfg ) 00830 { 00831 pid_t retval = 0; 00832 00833 TQString dev = cfg.readEntry( "Dev" ); 00834 00835 if ( dev.isEmpty() ) 00836 { 00837 TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() ); 00838 KMessageBoxWrapper::error( 0, tmp); 00839 return retval; 00840 } 00841 00842 TQString mp = KIO::findDeviceMountPoint( dev ); 00843 // Is the device already mounted ? 00844 if ( !mp.isNull() ) 00845 { 00846 KURL mpURL; 00847 mpURL.setPath( mp ); 00848 // Open a new window 00849 retval = KRun::runURL( mpURL, TQString::fromLatin1("inode/directory") ); 00850 } 00851 else 00852 { 00853 bool ro = cfg.readBoolEntry( "ReadOnly", false ); 00854 TQString fstype = cfg.readEntry( "FSType" ); 00855 if ( fstype == "Default" ) // KDE-1 thing 00856 fstype = TQString::null; 00857 TQString point = cfg.readEntry( "MountPoint" ); 00858 #ifndef Q_WS_WIN 00859 (void) new KAutoMount( ro, fstype, dev, point, _url.path() ); 00860 #endif 00861 retval = -1; // we don't want to return 0, but we don't want to return a pid 00862 } 00863 00864 return retval; 00865 } 00866 00867 pid_t KDEDesktopMimeType::runApplication( const KURL& , const TQString & _serviceFile ) 00868 { 00869 KService s( _serviceFile ); 00870 if ( !s.isValid() ) 00871 // The error message was already displayed, so we can just quit here 00872 return 0; 00873 00874 KURL::List lst; 00875 return KRun::run( s, lst ); 00876 } 00877 00878 pid_t KDEDesktopMimeType::runLink( const KURL& _url, const KSimpleConfig &cfg ) 00879 { 00880 TQString u = cfg.readPathEntry( "URL" ); 00881 if ( u.isEmpty() ) 00882 { 00883 TQString tmp = i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.").arg( _url.prettyURL() ); 00884 KMessageBoxWrapper::error( 0, tmp ); 00885 return 0; 00886 } 00887 00888 KURL url ( u ); 00889 KRun* run = new KRun(url); 00890 00891 // X-KDE-LastOpenedWith holds the service desktop entry name that 00892 // was should be preferred for opening this URL if possible. 00893 // This is used by the Recent Documents menu for instance. 00894 TQString lastOpenedWidth = cfg.readEntry( "X-KDE-LastOpenedWith" ); 00895 if ( !lastOpenedWidth.isEmpty() ) 00896 run->setPreferredService( lastOpenedWidth ); 00897 00898 return -1; // we don't want to return 0, but we don't want to return a pid 00899 } 00900 00901 pid_t KDEDesktopMimeType::runMimeType( const KURL& url , const KSimpleConfig & ) 00902 { 00903 // Hmm, can't really use keditfiletype since we might be looking 00904 // at the global file, or at a file not in share/mimelnk... 00905 00906 TQStringList args; 00907 args << "openProperties"; 00908 args << url.path(); 00909 00910 int pid; 00911 if ( !KApplication::kdeinitExec("kfmclient", args, 0, &pid) ) 00912 return pid; 00913 00914 KProcess p; 00915 p << "kfmclient" << args; 00916 p.start(KProcess::DontCare); 00917 return p.pid(); 00918 } 00919 00920 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::builtinServices( const KURL& _url ) 00921 { 00922 TQValueList<Service> result; 00923 00924 if ( !_url.isLocalFile() ) 00925 return result; 00926 00927 KSimpleConfig cfg( _url.path(), true ); 00928 cfg.setDesktopGroup(); 00929 TQString type = cfg.readEntry( "Type" ); 00930 00931 if ( type.isEmpty() ) 00932 return result; 00933 00934 if ( type == "FSDevice" ) 00935 { 00936 TQString dev = cfg.readEntry( "Dev" ); 00937 if ( dev.isEmpty() ) 00938 { 00939 TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() ); 00940 KMessageBoxWrapper::error( 0, tmp); 00941 } 00942 else 00943 { 00944 TQString mp = KIO::findDeviceMountPoint( dev ); 00945 // not mounted ? 00946 if ( mp.isEmpty() ) 00947 { 00948 Service mount; 00949 mount.m_strName = i18n("Mount"); 00950 mount.m_type = ST_MOUNT; 00951 result.append( mount ); 00952 } 00953 else 00954 { 00955 Service unmount; 00956 #ifdef HAVE_VOLMGT 00957 /* 00958 * Solaris' volume management can only umount+eject 00959 */ 00960 unmount.m_strName = i18n("Eject"); 00961 #else 00962 unmount.m_strName = i18n("Unmount"); 00963 #endif 00964 unmount.m_type = ST_UNMOUNT; 00965 result.append( unmount ); 00966 } 00967 } 00968 } 00969 00970 return result; 00971 } 00972 00973 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, bool bLocalFiles ) 00974 { 00975 KSimpleConfig cfg( path, true ); 00976 return userDefinedServices( path, cfg, bLocalFiles ); 00977 } 00978 00979 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, KConfig& cfg, bool bLocalFiles ) 00980 { 00981 return userDefinedServices( path, cfg, bLocalFiles, KURL::List() ); 00982 } 00983 00984 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, KConfig& cfg, bool bLocalFiles, const KURL::List & file_list ) 00985 { 00986 TQValueList<Service> result; 00987 00988 cfg.setDesktopGroup(); 00989 00990 if ( !cfg.hasKey( "Actions" ) && !cfg.hasKey( "X-KDE-GetActionMenu") ) 00991 return result; 00992 00993 if ( cfg.hasKey( "TryExec" ) ) 00994 { 00995 TQString tryexec = cfg.readPathEntry( "TryExec" ); 00996 TQString exe = KStandardDirs::findExe( tryexec ); 00997 if (exe.isEmpty()) { 00998 return result; 00999 } 01000 } 01001 01002 TQStringList keys; 01003 01004 if( cfg.hasKey( "X-KDE-GetActionMenu" )) { 01005 TQString dcopcall = cfg.readEntry( "X-KDE-GetActionMenu" ); 01006 const TQCString app = TQString(dcopcall.section(' ', 0,0)).utf8(); 01007 01008 TQByteArray dataToSend; 01009 TQDataStream dataStream(dataToSend, IO_WriteOnly); 01010 dataStream << file_list; 01011 TQCString replyType; 01012 TQByteArray replyData; 01013 TQCString object = TQString(dcopcall.section(' ', 1,-2)).utf8(); 01014 TQString function = dcopcall.section(' ', -1); 01015 if(!function.endsWith("(KURL::List)")) { 01016 kdWarning() << "Desktop file " << path << " contains an invalid X-KDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl; 01017 } else { 01018 if(kapp->dcopClient()->call( app, object, 01019 function.utf8(), 01020 dataToSend, replyType, replyData, true, -1) 01021 && replyType == "TQStringList" ) { 01022 01023 TQDataStream dataStreamIn(replyData, IO_ReadOnly); 01024 dataStreamIn >> keys; 01025 } 01026 } 01027 } 01028 01029 keys += cfg.readListEntry( "Actions", ';' ); //the desktop standard defines ";" as separator! 01030 01031 if ( keys.count() == 0 ) 01032 return result; 01033 01034 TQStringList::ConstIterator it = keys.begin(); 01035 TQStringList::ConstIterator end = keys.end(); 01036 for ( ; it != end; ++it ) 01037 { 01038 //kdDebug(7009) << "CURRENT KEY = " << (*it) << endl; 01039 01040 TQString group = *it; 01041 01042 if (group == "_SEPARATOR_") 01043 { 01044 Service s; 01045 result.append(s); 01046 continue; 01047 } 01048 01049 group.prepend( "Desktop Action " ); 01050 01051 bool bInvalidMenu = false; 01052 01053 if ( cfg.hasGroup( group ) ) 01054 { 01055 cfg.setGroup( group ); 01056 01057 if ( !cfg.hasKey( "Name" ) || !cfg.hasKey( "Exec" ) ) 01058 bInvalidMenu = true; 01059 else 01060 { 01061 TQString exec = cfg.readPathEntry( "Exec" ); 01062 if ( bLocalFiles || exec.contains("%U") || exec.contains("%u") ) 01063 { 01064 Service s; 01065 s.m_strName = cfg.readEntry( "Name" ); 01066 s.m_strIcon = cfg.readEntry( "Icon" ); 01067 s.m_strExec = exec; 01068 s.m_type = ST_USER_DEFINED; 01069 s.m_display = !cfg.readBoolEntry( "NoDisplay" ); 01070 result.append( s ); 01071 } 01072 } 01073 } 01074 else 01075 bInvalidMenu = true; 01076 01077 if ( bInvalidMenu ) 01078 { 01079 TQString tmp = i18n("The desktop entry file\n%1\n has an invalid menu entry\n%2.").arg( path ).arg( *it ); 01080 KMessageBoxWrapper::error( 0, tmp ); 01081 } 01082 } 01083 01084 return result; 01085 } 01086 01087 void KDEDesktopMimeType::executeService( const TQString& _url, KDEDesktopMimeType::Service& _service ) 01088 { 01089 KURL u; 01090 u.setPath(_url); 01091 KURL::List lst; 01092 lst.append( u ); 01093 executeService( lst, _service ); 01094 } 01095 01096 void KDEDesktopMimeType::executeService( const KURL::List& urls, KDEDesktopMimeType::Service& _service ) 01097 { 01098 //kdDebug(7009) << "EXECUTING Service " << _service.m_strName << endl; 01099 01100 if ( _service.m_type == ST_USER_DEFINED ) 01101 { 01102 kdDebug() << "KDEDesktopMimeType::executeService " << _service.m_strName 01103 << " first url's path=" << urls.first().path() << " exec=" << _service.m_strExec << endl; 01104 KRun::run( _service.m_strExec, urls, _service.m_strName, _service.m_strIcon, _service.m_strIcon ); 01105 // The action may update the desktop file. Example: eject unmounts (#5129). 01106 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 01107 allDirNotify.FilesChanged( urls ); 01108 return; 01109 } 01110 else if ( _service.m_type == ST_MOUNT || _service.m_type == ST_UNMOUNT ) 01111 { 01112 Q_ASSERT( urls.count() == 1 ); 01113 TQString path = urls.first().path(); 01114 //kdDebug(7009) << "MOUNT&UNMOUNT" << endl; 01115 01116 KSimpleConfig cfg( path, true ); 01117 cfg.setDesktopGroup(); 01118 TQString dev = cfg.readEntry( "Dev" ); 01119 if ( dev.isEmpty() ) 01120 { 01121 TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( path ); 01122 KMessageBoxWrapper::error( 0, tmp ); 01123 return; 01124 } 01125 TQString mp = KIO::findDeviceMountPoint( dev ); 01126 01127 if ( _service.m_type == ST_MOUNT ) 01128 { 01129 // Already mounted? Strange, but who knows ... 01130 if ( !mp.isEmpty() ) 01131 { 01132 kdDebug(7009) << "ALREADY Mounted" << endl; 01133 return; 01134 } 01135 01136 bool ro = cfg.readBoolEntry( "ReadOnly", false ); 01137 TQString fstype = cfg.readEntry( "FSType" ); 01138 if ( fstype == "Default" ) // KDE-1 thing 01139 fstype = TQString::null; 01140 TQString point = cfg.readEntry( "MountPoint" ); 01141 #ifndef Q_WS_WIN 01142 (void)new KAutoMount( ro, fstype, dev, point, path, false ); 01143 #endif 01144 } 01145 else if ( _service.m_type == ST_UNMOUNT ) 01146 { 01147 // Not mounted? Strange, but who knows ... 01148 if ( mp.isEmpty() ) 01149 return; 01150 01151 #ifndef Q_WS_WIN 01152 (void)new KAutoUnmount( mp, path ); 01153 #endif 01154 } 01155 } 01156 else 01157 assert( 0 ); 01158 } 01159 01160 const TQString & KMimeType::defaultMimeType() 01161 { 01162 static const TQString & s_strDefaultMimeType = 01163 KGlobal::staticQString( "application/octet-stream" ); 01164 return s_strDefaultMimeType; 01165 } 01166 01167 void KMimeType::virtual_hook( int id, void* data ) 01168 { KServiceType::virtual_hook( id, data ); } 01169 01170 void KFolderType::virtual_hook( int id, void* data ) 01171 { KMimeType::virtual_hook( id, data ); } 01172 01173 void KDEDesktopMimeType::virtual_hook( int id, void* data ) 01174 { KMimeType::virtual_hook( id, data ); } 01175 01176 void KExecMimeType::virtual_hook( int id, void* data ) 01177 { KMimeType::virtual_hook( id, data ); } 01178 01179 #include "kmimetyperesolver.moc" 01180