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 <tdeio/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 <tdeapplication.h> 00050 #include <kprocess.h> 00051 #include <kdebug.h> 00052 #include <kdesktopfile.h> 00053 #include <kdirwatch.h> 00054 #include <kiconloader.h> 00055 #include <tdelocale.h> 00056 #include <ksimpleconfig.h> 00057 #include <kstandarddirs.h> 00058 #include <kurl.h> 00059 #include <tdesycoca.h> 00060 #include <kde_file.h> 00061 00062 template class TDESharedPtr<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 TDEStandardDirs 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 tdesycoca, 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 = TDEGlobal::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 = TDEGlobal::staticQString(".desktop"); 00238 static const TQString& dotkdelnk = TDEGlobal::staticQString(".kdelnk"); 00239 static const TQString& dotdirectory = TDEGlobal::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-TDE-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-TDE-AutoEmbed setting and store it in the properties map 00389 TQString XKDEAutoEmbed = TQString::fromLatin1("X-TDE-AutoEmbed"); 00390 if ( config->hasKey( XKDEAutoEmbed ) ) 00391 m_mapProps.insert( XKDEAutoEmbed, TQVariant( config->readBoolEntry( XKDEAutoEmbed ), 0 ) ); 00392 00393 TQString XKDEText = TQString::fromLatin1("X-TDE-text"); 00394 if ( config->hasKey( XKDEText ) ) 00395 m_mapProps.insert( XKDEText, config->readBoolEntry( XKDEText ) ); 00396 00397 TQString XKDEIsAlso = TQString::fromLatin1("X-TDE-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-TDE-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 tdesycoca.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( TDEIcon::Group _group, int _force_size, int _state, 00458 TQString * _path ) const 00459 { 00460 TDEIconLoader *iconLoader=TDEGlobal::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, TDEIcon::Group _group, int _force_size, 00474 int _state, TQString * _path ) const 00475 { 00476 TDEIconLoader *iconLoader=TDEGlobal::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, TDEIcon::Group _group, 00490 int _force_size, int _state, TQString * _path ) 00491 { 00492 TDEIconLoader *iconLoader=TDEGlobal::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 = TDEGlobal::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 return i; 00530 } 00531 00532 TQString KMimeType::favIconForURL( const KURL& url ) 00533 { 00534 // this method will be called quite often, so better not read the config 00535 // again and again. 00536 static bool useFavIcons = true; 00537 static bool check = true; 00538 if ( check ) { 00539 check = false; 00540 TDEConfig *config = TDEGlobal::config(); 00541 TDEConfigGroupSaver cs( config, "HTML Settings" ); 00542 useFavIcons = config->readBoolEntry( "EnableFavicon", true ); 00543 } 00544 00545 if ( url.isLocalFile() || !url.protocol().startsWith("http") 00546 || !useFavIcons ) 00547 return TQString::null; 00548 00549 DCOPRef kded( "kded", "favicons" ); 00550 DCOPReply result = kded.call( "iconForURL(KURL)", url ); 00551 if ( result.isValid() ) 00552 return result; 00553 00554 return TQString::null; 00555 } 00556 00557 TQString KMimeType::parentMimeType() const 00558 { 00559 TQVariant v = property("X-TDE-IsAlso"); 00560 return v.toString(); 00561 } 00562 00563 bool KMimeType::is( const TQString& mimeTypeName ) const 00564 { 00565 if ( name() == mimeTypeName ) 00566 return true; 00567 TQString st = parentMimeType(); 00568 //if (st.isEmpty()) kdDebug(7009)<<"Parent mimetype is empty"<<endl; 00569 while ( !st.isEmpty() ) 00570 { 00571 //kdDebug(7009)<<"Checking parent mime type: "<<st<<endl; 00572 KMimeType::Ptr ptr = KMimeType::mimeType( st ); 00573 if (!ptr) return false; //error 00574 if ( ptr->name() == mimeTypeName ) 00575 return true; 00576 st = ptr->parentMimeType(); 00577 } 00578 return false; 00579 } 00580 00581 int KMimeType::patternsAccuracy() const { 00582 TQVariant v = property("X-TDE-PatternsAccuracy"); 00583 if (!v.isValid()) return 100; 00584 else 00585 return v.toInt(); 00586 } 00587 00588 00589 /******************************************************* 00590 * 00591 * KFolderType 00592 * 00593 ******************************************************/ 00594 00595 TQString KFolderType::icon( const TQString& _url, bool _is_local ) const 00596 { 00597 if ( !_is_local || _url.isEmpty() ) 00598 return KMimeType::icon( _url, _is_local ); 00599 00600 return KFolderType::icon( KURL(_url), _is_local ); 00601 } 00602 00603 TQString KFolderType::icon( const KURL& _url, bool _is_local ) const 00604 { 00605 if ( !_is_local ) 00606 return KMimeType::icon( _url, _is_local ); 00607 00608 KURL u( _url ); 00609 u.addPath( ".directory" ); 00610 00611 TQString icon; 00612 // using TDEStandardDirs as this one checks for path being 00613 // a file instead of a directory 00614 if ( TDEStandardDirs::exists( u.path() ) ) 00615 { 00616 KSimpleConfig cfg( u.path(), true ); 00617 cfg.setDesktopGroup(); 00618 icon = cfg.readEntry( "Icon" ); 00619 TQString empty_icon = cfg.readEntry( "EmptyIcon" ); 00620 00621 if ( !empty_icon.isEmpty() ) 00622 { 00623 bool isempty = false; 00624 DIR *dp = 0L; 00625 struct dirent *ep; 00626 dp = opendir( TQFile::encodeName(_url.path()) ); 00627 if ( dp ) 00628 { 00629 TQValueList<TQCString> entries; 00630 // Note that readdir isn't guaranteed to return "." and ".." first (#79826) 00631 ep=readdir( dp ); if ( ep ) entries.append( ep->d_name ); 00632 ep=readdir( dp ); if ( ep ) entries.append( ep->d_name ); 00633 if ( (ep=readdir( dp )) == 0L ) // third file is NULL entry -> empty directory 00634 isempty = true; 00635 else { 00636 entries.append( ep->d_name ); 00637 if ( readdir( dp ) == 0 ) { // only three 00638 // check if we got "." ".." and ".directory" 00639 isempty = entries.find( "." ) != entries.end() && 00640 entries.find( ".." ) != entries.end() && 00641 entries.find( ".directory" ) != entries.end(); 00642 } 00643 } 00644 if (!isempty && !strcmp(ep->d_name, ".directory")) 00645 isempty = (readdir(dp) == 0L); 00646 closedir( dp ); 00647 } 00648 00649 if ( isempty ) 00650 return empty_icon; 00651 } 00652 } 00653 00654 if ( icon.isEmpty() ) 00655 return KMimeType::icon( _url, _is_local ); 00656 00657 if ( icon.startsWith( "./" ) ) { 00658 // path is relative with respect to the location 00659 // of the .directory file (#73463) 00660 KURL v( _url ); 00661 v.addPath( icon.mid( 2 ) ); 00662 icon = v.path(); 00663 } 00664 00665 return icon; 00666 } 00667 00668 TQString KFolderType::comment( const TQString& _url, bool _is_local ) const 00669 { 00670 if ( !_is_local || _url.isEmpty() ) 00671 return KMimeType::comment( _url, _is_local ); 00672 00673 return KFolderType::comment( KURL(_url), _is_local ); 00674 } 00675 00676 TQString KFolderType::comment( const KURL& _url, bool _is_local ) const 00677 { 00678 if ( !_is_local ) 00679 return KMimeType::comment( _url, _is_local ); 00680 00681 KURL u( _url ); 00682 u.addPath( ".directory" ); 00683 00684 KDesktopFile cfg( u.path(), true ); 00685 TQString comment = cfg.readComment(); 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 TQString KDEDesktopMimeType::icon( const TQString& _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 TQString 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 TQString icon = cfg.readEntry( "Icon" ); 00715 TQString type = cfg.readEntry( "Type" ); 00716 00717 if ( type == "FSDevice" || type == "FSDev") // need to provide FSDev for 00718 // backwards compatibility 00719 { 00720 TQString unmount_icon = cfg.readEntry( "UnmountIcon" ); 00721 TQString dev = cfg.readEntry( "Dev" ); 00722 if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() ) 00723 { 00724 TQString mp = TDEIO::findDeviceMountPoint( dev ); 00725 // Is the device not mounted ? 00726 if ( mp.isNull() ) 00727 return unmount_icon; 00728 } 00729 } else if ( type == "Link" ) { 00730 const TQString emptyIcon = cfg.readEntry( "EmptyIcon" ); 00731 if ( !emptyIcon.isEmpty() ) { 00732 const TQString u = cfg.readPathEntry( "URL" ); 00733 const KURL url( u ); 00734 if ( url.protocol() == "trash" ) { 00735 // We need to find if the trash is empty, preferrably without using a TDEIO job. 00736 // So instead tdeio_trash leaves an entry in its config file for us. 00737 KSimpleConfig trashConfig( "trashrc", true ); 00738 trashConfig.setGroup( "Status" ); 00739 if ( trashConfig.readBoolEntry( "Empty", true ) ) { 00740 return emptyIcon; 00741 } 00742 } 00743 } 00744 } 00745 00746 if ( icon.isEmpty() ) 00747 return KMimeType::icon( _url, _is_local ); 00748 00749 return icon; 00750 } 00751 00752 TQPixmap KDEDesktopMimeType::pixmap( const KURL& _url, TDEIcon::Group _group, int _force_size, 00753 int _state, TQString * _path ) const 00754 { 00755 TQString _icon = icon( _url, _url.isLocalFile() ); 00756 TQPixmap pix = TDEGlobal::iconLoader()->loadIcon( _icon, _group, 00757 _force_size, _state, _path, false ); 00758 if ( pix.isNull() ) 00759 pix = TDEGlobal::iconLoader()->loadIcon( "unknown", _group, 00760 _force_size, _state, _path, false ); 00761 return pix; 00762 } 00763 00764 TQString KDEDesktopMimeType::comment( const TQString& _url, bool _is_local ) const 00765 { 00766 if ( !_is_local || _url.isEmpty() ) 00767 return KMimeType::comment( _url, _is_local ); 00768 00769 KURL u( _url ); 00770 return comment( u, _is_local ); 00771 } 00772 00773 TQString KDEDesktopMimeType::comment( const KURL& _url, bool _is_local ) const 00774 { 00775 if ( !_is_local ) 00776 return KMimeType::comment( _url, _is_local ); 00777 00778 KDesktopFile cfg( _url.path(), true ); 00779 TQString comment = cfg.readComment(); 00780 if ( comment.isEmpty() ) 00781 return KMimeType::comment( _url, _is_local ); 00782 00783 return comment; 00784 } 00785 00786 pid_t KDEDesktopMimeType::run( const KURL& u, bool _is_local ) 00787 { 00788 // It might be a security problem to run external untrusted desktop 00789 // entry files 00790 if ( !_is_local ) 00791 return 0; 00792 00793 KSimpleConfig cfg( u.path(), true ); 00794 cfg.setDesktopGroup(); 00795 TQString type = cfg.readEntry( "Type" ); 00796 if ( type.isEmpty() ) 00797 { 00798 TQString tmp = i18n("The desktop entry file %1 " 00799 "has no Type=... entry.").arg(u.path() ); 00800 KMessageBoxWrapper::error( 0, tmp); 00801 return 0; 00802 } 00803 00804 //kdDebug(7009) << "TYPE = " << type.data() << endl; 00805 00806 if ( type == "FSDevice" ) 00807 return runFSDevice( u, cfg ); 00808 else if ( type == "Application" ) 00809 return runApplication( u, u.path() ); 00810 else if ( type == "Link" ) 00811 { 00812 cfg.setDollarExpansion( true ); // for URL=file:$HOME (Simon) 00813 return runLink( u, cfg ); 00814 } 00815 else if ( type == "MimeType" ) 00816 return runMimeType( u, cfg ); 00817 00818 00819 TQString tmp = i18n("The desktop entry of type\n%1\nis unknown.").arg( type ); 00820 KMessageBoxWrapper::error( 0, tmp); 00821 00822 return 0; 00823 } 00824 00825 pid_t KDEDesktopMimeType::runFSDevice( const KURL& _url, const KSimpleConfig &cfg ) 00826 { 00827 pid_t retval = 0; 00828 00829 TQString dev = cfg.readEntry( "Dev" ); 00830 00831 if ( dev.isEmpty() ) 00832 { 00833 TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() ); 00834 KMessageBoxWrapper::error( 0, tmp); 00835 return retval; 00836 } 00837 00838 TQString mp = TDEIO::findDeviceMountPoint( dev ); 00839 // Is the device already mounted ? 00840 if ( !mp.isNull() ) 00841 { 00842 KURL mpURL; 00843 mpURL.setPath( mp ); 00844 // Open a new window 00845 retval = KRun::runURL( mpURL, TQString::fromLatin1("inode/directory") ); 00846 } 00847 else 00848 { 00849 bool ro = cfg.readBoolEntry( "ReadOnly", false ); 00850 TQString fstype = cfg.readEntry( "FSType" ); 00851 if ( fstype == "Default" ) // KDE-1 thing 00852 fstype = TQString::null; 00853 TQString point = cfg.readEntry( "MountPoint" ); 00854 #ifndef Q_WS_WIN 00855 (void) new KAutoMount( ro, fstype, dev, point, _url.path() ); 00856 #endif 00857 retval = -1; // we don't want to return 0, but we don't want to return a pid 00858 } 00859 00860 return retval; 00861 } 00862 00863 pid_t KDEDesktopMimeType::runApplication( const KURL& , const TQString & _serviceFile ) 00864 { 00865 KService s( _serviceFile ); 00866 if ( !s.isValid() ) 00867 // The error message was already displayed, so we can just quit here 00868 return 0; 00869 00870 KURL::List lst; 00871 return KRun::run( s, lst ); 00872 } 00873 00874 pid_t KDEDesktopMimeType::runLink( const KURL& _url, const KSimpleConfig &cfg ) 00875 { 00876 TQString u = cfg.readPathEntry( "URL" ); 00877 if ( u.isEmpty() ) 00878 { 00879 TQString tmp = i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.").arg( _url.prettyURL() ); 00880 KMessageBoxWrapper::error( 0, tmp ); 00881 return 0; 00882 } 00883 00884 KURL url ( u ); 00885 KRun* run = new KRun(url); 00886 00887 // X-TDE-LastOpenedWith holds the service desktop entry name that 00888 // was should be preferred for opening this URL if possible. 00889 // This is used by the Recent Documents menu for instance. 00890 TQString lastOpenedWidth = cfg.readEntry( "X-TDE-LastOpenedWith" ); 00891 if ( !lastOpenedWidth.isEmpty() ) 00892 run->setPreferredService( lastOpenedWidth ); 00893 00894 return -1; // we don't want to return 0, but we don't want to return a pid 00895 } 00896 00897 pid_t KDEDesktopMimeType::runMimeType( const KURL& url , const KSimpleConfig & ) 00898 { 00899 // Hmm, can't really use keditfiletype since we might be looking 00900 // at the global file, or at a file not in share/mimelnk... 00901 00902 TQStringList args; 00903 args << "openProperties"; 00904 args << url.path(); 00905 00906 int pid; 00907 if ( !TDEApplication::tdeinitExec("kfmclient", args, 0, &pid) ) 00908 return pid; 00909 00910 TDEProcess p; 00911 p << "kfmclient" << args; 00912 p.start(TDEProcess::DontCare); 00913 return p.pid(); 00914 } 00915 00916 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::builtinServices( const KURL& _url ) 00917 { 00918 TQValueList<Service> result; 00919 00920 if ( !_url.isLocalFile() ) 00921 return result; 00922 00923 KSimpleConfig cfg( _url.path(), true ); 00924 cfg.setDesktopGroup(); 00925 TQString type = cfg.readEntry( "Type" ); 00926 00927 if ( type.isEmpty() ) 00928 return result; 00929 00930 if ( type == "FSDevice" ) 00931 { 00932 TQString dev = cfg.readEntry( "Dev" ); 00933 if ( dev.isEmpty() ) 00934 { 00935 TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() ); 00936 KMessageBoxWrapper::error( 0, tmp); 00937 } 00938 else 00939 { 00940 TQString mp = TDEIO::findDeviceMountPoint( dev ); 00941 // not mounted ? 00942 if ( mp.isEmpty() ) 00943 { 00944 Service mount; 00945 mount.m_strName = i18n("Mount"); 00946 mount.m_type = ST_MOUNT; 00947 result.append( mount ); 00948 } 00949 else 00950 { 00951 Service unmount; 00952 #ifdef HAVE_VOLMGT 00953 /* 00954 * Solaris' volume management can only umount+eject 00955 */ 00956 unmount.m_strName = i18n("Eject"); 00957 #else 00958 unmount.m_strName = i18n("Unmount"); 00959 #endif 00960 unmount.m_type = ST_UNMOUNT; 00961 result.append( unmount ); 00962 } 00963 } 00964 } 00965 00966 return result; 00967 } 00968 00969 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, bool bLocalFiles ) 00970 { 00971 KSimpleConfig cfg( path, true ); 00972 return userDefinedServices( path, cfg, bLocalFiles ); 00973 } 00974 00975 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, TDEConfig& cfg, bool bLocalFiles ) 00976 { 00977 return userDefinedServices( path, cfg, bLocalFiles, KURL::List() ); 00978 } 00979 00980 TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, TDEConfig& cfg, bool bLocalFiles, const KURL::List & file_list ) 00981 { 00982 TQValueList<Service> result; 00983 00984 cfg.setDesktopGroup(); 00985 00986 if ( !cfg.hasKey( "Actions" ) && !cfg.hasKey( "X-TDE-GetActionMenu") ) 00987 return result; 00988 00989 if ( cfg.hasKey( "TryExec" ) ) 00990 { 00991 TQString tryexec = cfg.readPathEntry( "TryExec" ); 00992 TQString exe = TDEStandardDirs::findExe( tryexec ); 00993 if (exe.isEmpty()) { 00994 return result; 00995 } 00996 } 00997 00998 TQStringList keys; 00999 01000 if( cfg.hasKey( "X-TDE-GetActionMenu" )) { 01001 TQString dcopcall = cfg.readEntry( "X-TDE-GetActionMenu" ); 01002 const TQCString app = TQString(dcopcall.section(' ', 0,0)).utf8(); 01003 01004 TQByteArray dataToSend; 01005 TQDataStream dataStream(dataToSend, IO_WriteOnly); 01006 dataStream << file_list; 01007 TQCString replyType; 01008 TQByteArray replyData; 01009 TQCString object = TQString(dcopcall.section(' ', 1,-2)).utf8(); 01010 TQString function = dcopcall.section(' ', -1); 01011 if(!function.endsWith("(KURL::List)")) { 01012 kdWarning() << "Desktop file " << path << " contains an invalid X-TDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl; 01013 } else { 01014 if(kapp->dcopClient()->call( app, object, 01015 function.utf8(), 01016 dataToSend, replyType, replyData, true, -1) 01017 && replyType == "TQStringList" ) { 01018 01019 TQDataStream dataStreamIn(replyData, IO_ReadOnly); 01020 dataStreamIn >> keys; 01021 } 01022 } 01023 } 01024 01025 keys += cfg.readListEntry( "Actions", ';' ); //the desktop standard defines ";" as separator! 01026 01027 if ( keys.count() == 0 ) 01028 return result; 01029 01030 TQStringList::ConstIterator it = keys.begin(); 01031 TQStringList::ConstIterator end = keys.end(); 01032 for ( ; it != end; ++it ) 01033 { 01034 //kdDebug(7009) << "CURRENT KEY = " << (*it) << endl; 01035 01036 TQString group = *it; 01037 01038 if (group == "_SEPARATOR_") 01039 { 01040 Service s; 01041 result.append(s); 01042 continue; 01043 } 01044 01045 group.prepend( "Desktop Action " ); 01046 01047 bool bInvalidMenu = false; 01048 01049 if ( cfg.hasGroup( group ) ) 01050 { 01051 cfg.setGroup( group ); 01052 01053 if ( !cfg.hasKey( "Name" ) || !cfg.hasKey( "Exec" ) ) 01054 bInvalidMenu = true; 01055 else 01056 { 01057 TQString exec = cfg.readPathEntry( "Exec" ); 01058 if ( bLocalFiles || exec.contains("%U") || exec.contains("%u") ) 01059 { 01060 Service s; 01061 s.m_strName = cfg.readEntry( "Name" ); 01062 s.m_strIcon = cfg.readEntry( "Icon" ); 01063 s.m_strExec = exec; 01064 s.m_type = ST_USER_DEFINED; 01065 s.m_display = !cfg.readBoolEntry( "NoDisplay" ); 01066 result.append( s ); 01067 } 01068 } 01069 } 01070 else 01071 bInvalidMenu = true; 01072 01073 if ( bInvalidMenu ) 01074 { 01075 TQString tmp = i18n("The desktop entry file\n%1\n has an invalid menu entry\n%2.").arg( path ).arg( *it ); 01076 KMessageBoxWrapper::error( 0, tmp ); 01077 } 01078 } 01079 01080 return result; 01081 } 01082 01083 void KDEDesktopMimeType::executeService( const TQString& _url, KDEDesktopMimeType::Service& _service ) 01084 { 01085 KURL u; 01086 u.setPath(_url); 01087 KURL::List lst; 01088 lst.append( u ); 01089 executeService( lst, _service ); 01090 } 01091 01092 void KDEDesktopMimeType::executeService( const KURL::List& urls, KDEDesktopMimeType::Service& _service ) 01093 { 01094 //kdDebug(7009) << "EXECUTING Service " << _service.m_strName << endl; 01095 01096 if ( _service.m_type == ST_USER_DEFINED ) 01097 { 01098 kdDebug() << "KDEDesktopMimeType::executeService " << _service.m_strName 01099 << " first url's path=" << urls.first().path() << " exec=" << _service.m_strExec << endl; 01100 KRun::run( _service.m_strExec, urls, _service.m_strName, _service.m_strIcon, _service.m_strIcon ); 01101 // The action may update the desktop file. Example: eject unmounts (#5129). 01102 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 01103 allDirNotify.FilesChanged( urls ); 01104 return; 01105 } 01106 else if ( _service.m_type == ST_MOUNT || _service.m_type == ST_UNMOUNT ) 01107 { 01108 Q_ASSERT( urls.count() == 1 ); 01109 TQString path = urls.first().path(); 01110 //kdDebug(7009) << "MOUNT&UNMOUNT" << endl; 01111 01112 KSimpleConfig cfg( path, true ); 01113 cfg.setDesktopGroup(); 01114 TQString dev = cfg.readEntry( "Dev" ); 01115 if ( dev.isEmpty() ) 01116 { 01117 TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( path ); 01118 KMessageBoxWrapper::error( 0, tmp ); 01119 return; 01120 } 01121 TQString mp = TDEIO::findDeviceMountPoint( dev ); 01122 01123 if ( _service.m_type == ST_MOUNT ) 01124 { 01125 // Already mounted? Strange, but who knows ... 01126 if ( !mp.isEmpty() ) 01127 { 01128 kdDebug(7009) << "ALREADY Mounted" << endl; 01129 return; 01130 } 01131 01132 bool ro = cfg.readBoolEntry( "ReadOnly", false ); 01133 TQString fstype = cfg.readEntry( "FSType" ); 01134 if ( fstype == "Default" ) // KDE-1 thing 01135 fstype = TQString::null; 01136 TQString point = cfg.readEntry( "MountPoint" ); 01137 #ifndef Q_WS_WIN 01138 (void)new KAutoMount( ro, fstype, dev, point, path, false ); 01139 #endif 01140 } 01141 else if ( _service.m_type == ST_UNMOUNT ) 01142 { 01143 // Not mounted? Strange, but who knows ... 01144 if ( mp.isEmpty() ) 01145 return; 01146 01147 #ifndef Q_WS_WIN 01148 (void)new KAutoUnmount( mp, path ); 01149 #endif 01150 } 01151 } 01152 else 01153 assert( 0 ); 01154 } 01155 01156 const TQString & KMimeType::defaultMimeType() 01157 { 01158 static const TQString & s_strDefaultMimeType = 01159 TDEGlobal::staticQString( "application/octet-stream" ); 01160 return s_strDefaultMimeType; 01161 } 01162 01163 void KMimeType::virtual_hook( int id, void* data ) 01164 { KServiceType::virtual_hook( id, data ); } 01165 01166 void KFolderType::virtual_hook( int id, void* data ) 01167 { KMimeType::virtual_hook( id, data ); } 01168 01169 void KDEDesktopMimeType::virtual_hook( int id, void* data ) 01170 { KMimeType::virtual_hook( id, data ); } 01171 01172 void KExecMimeType::virtual_hook( int id, void* data ) 01173 { KMimeType::virtual_hook( id, data ); } 01174 01175 #include "kmimetyperesolver.moc" 01176