krun.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Torben Weis <weis@kde.org> 00003 Copyright (C) 2006 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 as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "krun.h" 00022 00023 #include <assert.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <unistd.h> 00027 #include <typeinfo> 00028 00029 #include <tqwidget.h> 00030 #include <tqguardedptr.h> 00031 00032 #include "kuserprofile.h" 00033 #include "kmimetype.h" 00034 #include "kmimemagic.h" 00035 #include "tdeio/job.h" 00036 #include "tdeio/global.h" 00037 #include "tdeio/scheduler.h" 00038 #include "tdeio/netaccess.h" 00039 #include "tdefile/kopenwith.h" 00040 #include "tdefile/tderecentdocument.h" 00041 00042 #include <kdatastream.h> 00043 #include <kmessageboxwrapper.h> 00044 #include <kurl.h> 00045 #include <tdeapplication.h> 00046 #include <kdebug.h> 00047 #include <tdelocale.h> 00048 #include <kprotocolinfo.h> 00049 #include <kstandarddirs.h> 00050 #include <kprocess.h> 00051 #include <dcopclient.h> 00052 #include <tqfile.h> 00053 #include <tqfileinfo.h> 00054 #include <tqtextstream.h> 00055 #include <tqdatetime.h> 00056 #include <tqregexp.h> 00057 #include <kdesktopfile.h> 00058 #include <tdestartupinfo.h> 00059 #include <kmacroexpander.h> 00060 #include <kshell.h> 00061 #include <kde_file.h> 00062 #include <kstringhandler.h> 00063 00064 #ifdef Q_WS_X11 00065 #include <twin.h> 00066 #endif 00067 00068 class KRun::KRunPrivate 00069 { 00070 public: 00071 KRunPrivate() { m_showingError = false; } 00072 00073 bool m_showingError; 00074 bool m_runExecutables; 00075 00076 TQString m_preferredService; 00077 TQString m_externalBrowser; 00078 TQString m_localPath; 00079 TQString m_suggestedFileName; 00080 TQGuardedPtr <TQWidget> m_window; 00081 TQCString m_asn; 00082 }; 00083 00084 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype ) 00085 { 00086 return runURL( u, _mimetype, false, true, TQString::null ); 00087 } 00088 00089 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile ) 00090 { 00091 return runURL( u, _mimetype, tempFile, true, TQString::null ); 00092 } 00093 00094 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile, bool runExecutables ) 00095 { 00096 return runURL( u, _mimetype, tempFile, runExecutables, TQString::null ); 00097 } 00098 00099 bool KRun::isExecutableFile( const KURL& url, const TQString &mimetype ) 00100 { 00101 if ( !url.isLocalFile() ) 00102 return false; 00103 TQFileInfo file( url.path() ); 00104 if ( file.isExecutable() ) // Got a prospective file to run 00105 { 00106 KMimeType::Ptr mimeType = KMimeType::mimeType( mimetype ); 00107 00108 if ( mimeType->is("application/x-executable") || mimeType->is("application/x-executable-script") ) 00109 return true; 00110 } 00111 return false; 00112 } 00113 00114 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile, bool runExecutables, const TQString& suggestedFileName ) 00115 { 00116 return runURL( u, _mimetype, NULL, "", tempFile, runExecutables, suggestedFileName ); 00117 } 00118 00119 // This is called by foundMimeType, since it knows the mimetype of the URL 00120 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, TQWidget* window, const TQCString& asn, 00121 bool tempFile, bool runExecutables, const TQString& suggestedFileName ) 00122 { 00123 bool noRun = false; 00124 bool noAuth = false; 00125 if ( _mimetype == "inode/directory-locked" ) 00126 { 00127 KMessageBoxWrapper::error( window, 00128 i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>").arg(u.htmlURL()) ); 00129 return 0; 00130 } 00131 else if ( (_mimetype == "application/x-desktop") || 00132 (_mimetype == "media/builtin-mydocuments") || 00133 (_mimetype == "media/builtin-mycomputer") || 00134 (_mimetype == "media/builtin-mynetworkplaces") || 00135 (_mimetype == "media/builtin-printers") || 00136 (_mimetype == "media/builtin-trash") || 00137 (_mimetype == "media/builtin-webbrowser") ) 00138 { 00139 if ( u.isLocalFile() && runExecutables ) 00140 return KDEDesktopMimeType::run( u, true ); 00141 } 00142 else if ( isExecutableFile(u, _mimetype) ) 00143 { 00144 if ( u.isLocalFile() && runExecutables) 00145 { 00146 if (kapp->authorize("shell_access")) 00147 { 00148 TQString path = u.path(); 00149 shellQuote( path ); 00150 return (KRun::runCommand(path, TQString::null, TQString::null, window, asn)); // just execute the url as a command 00151 // ## TODO implement deleting the file if tempFile==true 00152 } 00153 else 00154 { 00155 noAuth = true; 00156 } 00157 } 00158 else if (_mimetype == "application/x-executable") 00159 noRun = true; 00160 } 00161 else if ( isExecutable(_mimetype) ) 00162 { 00163 if (!runExecutables) 00164 noRun = true; 00165 00166 if (!kapp->authorize("shell_access")) 00167 noAuth = true; 00168 } 00169 00170 if ( noRun ) 00171 { 00172 KMessageBox::sorry( window, 00173 i18n("<qt>The file <b>%1</b> is an executable program. " 00174 "For safety it will not be started.</qt>").arg(u.htmlURL())); 00175 return 0; 00176 } 00177 if ( noAuth ) 00178 { 00179 KMessageBoxWrapper::error( window, 00180 i18n("<qt>You do not have permission to run <b>%1</b>.</qt>").arg(u.htmlURL()) ); 00181 return 0; 00182 } 00183 00184 KURL::List lst; 00185 lst.append( u ); 00186 00187 static const TQString& app_str = TDEGlobal::staticQString("Application"); 00188 00189 KService::Ptr offer = KServiceTypeProfile::preferredService( _mimetype, app_str ); 00190 00191 if ( !offer ) 00192 { 00193 // Open-with dialog 00194 // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog ! 00195 // Hmm, in fact KOpenWithDlg::setServiceType already guesses the mimetype from the first URL of the list... 00196 return displayOpenWithDialog( lst, tempFile, suggestedFileName ); 00197 } 00198 00199 return KRun::run( *offer, lst, window, asn, tempFile, suggestedFileName ); 00200 } 00201 00202 bool KRun::displayOpenWithDialog( const KURL::List& lst ) 00203 { 00204 return displayOpenWithDialog( lst, false, TQString::null ); 00205 } 00206 00207 bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles ) 00208 { 00209 return displayOpenWithDialog( lst, tempFiles, TQString::null ); 00210 } 00211 00212 bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles, const TQString& suggestedFileName ) 00213 { 00214 if (kapp && !kapp->authorizeTDEAction("openwith")) 00215 { 00216 // TODO: Better message, i18n freeze :-( 00217 KMessageBox::sorry(0L, i18n("You are not authorized to open this file.")); 00218 return false; 00219 } 00220 00221 KOpenWithDlg l( lst, i18n("Open with:"), TQString::null, 0L ); 00222 if ( l.exec() ) 00223 { 00224 KService::Ptr service = l.service(); 00225 if ( !!service ) 00226 return KRun::run( *service, lst, 0 /*window*/, tempFiles, suggestedFileName ); 00227 00228 kdDebug(7010) << "No service set, running " << l.text() << endl; 00229 return KRun::run( l.text(), lst, suggestedFileName ); // TODO handle tempFiles 00230 } 00231 return false; 00232 } 00233 00234 void KRun::shellQuote( TQString &_str ) 00235 { 00236 // Credits to Walter, says Bernd G. :) 00237 if (_str.isEmpty()) // Don't create an explicit empty parameter 00238 return; 00239 TQChar q('\''); 00240 _str.replace(q, "'\\''").prepend(q).append(q); 00241 } 00242 00243 00244 class KRunMX1 : public KMacroExpanderBase { 00245 public: 00246 KRunMX1( const KService &_service ) : 00247 KMacroExpanderBase( '%' ), hasUrls( false ), hasSpec( false ), service( _service ) {} 00248 bool hasUrls:1, hasSpec:1; 00249 00250 protected: 00251 virtual int expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ); 00252 00253 private: 00254 const KService &service; 00255 }; 00256 00257 int 00258 KRunMX1::expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ) 00259 { 00260 uint option = str[pos + 1]; 00261 switch( option ) { 00262 case 'c': 00263 ret << service.name().replace( '%', "%%" ); 00264 break; 00265 case 'k': 00266 ret << service.desktopEntryPath().replace( '%', "%%" ); 00267 break; 00268 case 'i': 00269 ret << "-icon" << service.icon().replace( '%', "%%" ); 00270 break; 00271 case 'm': 00272 ret << "-miniicon" << service.icon().replace( '%', "%%" ); 00273 break; 00274 case 'u': 00275 case 'U': 00276 hasUrls = true; 00277 /* fallthrough */ 00278 case 'f': 00279 case 'F': 00280 case 'n': 00281 case 'N': 00282 case 'd': 00283 case 'D': 00284 case 'v': 00285 hasSpec = true; 00286 /* fallthrough */ 00287 default: 00288 return -2; // subst with same and skip 00289 } 00290 return 2; 00291 } 00292 00293 class KRunMX2 : public KMacroExpanderBase { 00294 public: 00295 KRunMX2( const KURL::List &_urls ) : 00296 KMacroExpanderBase( '%' ), ignFile( false ), urls( _urls ) {} 00297 bool ignFile:1; 00298 00299 protected: 00300 virtual int expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ); 00301 00302 private: 00303 void subst( int option, const KURL &url, TQStringList &ret ); 00304 00305 const KURL::List &urls; 00306 }; 00307 00308 void 00309 KRunMX2::subst( int option, const KURL &url, TQStringList &ret ) 00310 { 00311 switch( option ) { 00312 case 'u': 00313 ret << url.pathOrURL(); 00314 break; 00315 case 'd': 00316 ret << url.directory(); 00317 break; 00318 case 'f': 00319 ret << url.path(); 00320 break; 00321 case 'n': 00322 ret << url.fileName(); 00323 break; 00324 case 'v': 00325 if (url.isLocalFile() && TQFile::exists( url.path() ) ) 00326 ret << KDesktopFile( url.path(), true ).readEntry( "Dev" ); 00327 break; 00328 } 00329 return; 00330 } 00331 00332 int 00333 KRunMX2::expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ) 00334 { 00335 uint option = str[pos + 1]; 00336 switch( option ) { 00337 case 'f': 00338 case 'u': 00339 case 'n': 00340 case 'd': 00341 case 'v': 00342 if( urls.isEmpty() ) { 00343 if (!ignFile) 00344 kdDebug() << "KRun::processDesktopExec: No URLs supplied to single-URL service " << str << endl; 00345 } else if( urls.count() > 1 ) 00346 kdWarning() << "KRun::processDesktopExec: " << urls.count() << " URLs supplied to single-URL service " << str << endl; 00347 else 00348 subst( option, urls.first(), ret ); 00349 break; 00350 case 'F': 00351 case 'U': 00352 case 'N': 00353 case 'D': 00354 option += 'a' - 'A'; 00355 for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) 00356 subst( option, *it, ret ); 00357 break; 00358 case '%': 00359 ret = "%"; 00360 break; 00361 default: 00362 return -2; // subst with same and skip 00363 } 00364 return 2; 00365 } 00366 00367 // BIC: merge methods below 00368 TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell) { 00369 return processDesktopExec( _service, _urls, has_shell, false, TQString::null ); 00370 } 00371 00372 TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles) 00373 { 00374 return processDesktopExec( _service, _urls, has_shell, tempFiles, TQString::null ); 00375 } 00376 00377 TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles, const TQString& suggestedFileName) 00378 { 00379 TQString exec = _service.exec(); 00380 TQStringList result; 00381 bool appHasTempFileOption; 00382 00383 KRunMX1 mx1( _service ); 00384 KRunMX2 mx2( _urls ); 00385 00387 TQRegExp re("^\\s*(?:/bin/)?sh\\s+-c\\s+(.*)$"); 00388 if (!re.search( exec )) { 00389 exec = TQString(re.cap( 1 )).stripWhiteSpace(); 00390 for (uint pos = 0; pos < exec.length(); ) { 00391 TQChar c = exec.unicode()[pos]; 00392 if (c != '\'' && c != '"') 00393 goto synerr; // what else can we do? after normal parsing the substs would be insecure 00394 int pos2 = exec.find( c, pos + 1 ) - 1; 00395 if (pos2 < 0) 00396 goto synerr; // quoting error 00397 memcpy( (void *)(exec.unicode() + pos), exec.unicode() + pos + 1, (pos2 - pos) * sizeof(TQChar)); 00398 pos = pos2; 00399 exec.remove( pos, 2 ); 00400 } 00401 } 00402 00403 if( !mx1.expandMacrosShellQuote( exec ) ) 00404 goto synerr; // error in shell syntax 00405 00406 // FIXME: the current way of invoking tdeioexec disables term and su use 00407 00408 // Check if we need "tempexec" (tdeioexec in fact) 00409 appHasTempFileOption = tempFiles && _service.property("X-TDE-HasTempFileOption").toBool(); 00410 if( tempFiles && !appHasTempFileOption && _urls.size() ) { 00411 result << "tdeioexec" << "--tempfiles" << exec; 00412 result += _urls.toStringList(); 00413 if (has_shell) 00414 result = KShell::joinArgs( result ); 00415 return result; 00416 } 00417 00418 // Check if we need tdeioexec 00419 if( !mx1.hasUrls ) { 00420 for( KURL::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it ) 00421 if ( !(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it) ) { 00422 // We need to run the app through tdeioexec 00423 result << "tdeioexec"; 00424 if ( tempFiles ) 00425 result << "--tempfiles"; 00426 if ( !suggestedFileName.isEmpty() ) { 00427 result << "--suggestedfilename"; 00428 result << suggestedFileName; 00429 } 00430 result << exec; 00431 result += _urls.toStringList(); 00432 if (has_shell) 00433 result = KShell::joinArgs( result ); 00434 return result; 00435 } 00436 } 00437 00438 if ( appHasTempFileOption ) 00439 exec += " --tempfile"; 00440 00441 // Did the user forget to append something like '%f'? 00442 // If so, then assume that '%f' is the right choice => the application 00443 // accepts only local files. 00444 if( !mx1.hasSpec ) { 00445 exec += " %f"; 00446 mx2.ignFile = true; 00447 } 00448 00449 mx2.expandMacrosShellQuote( exec ); // syntax was already checked, so don't check return value 00450 00451 /* 00452 1 = need_shell, 2 = terminal, 4 = su, 8 = has_shell 00453 00454 0 << split(cmd) 00455 1 << "sh" << "-c" << cmd 00456 2 << split(term) << "-e" << split(cmd) 00457 3 << split(term) << "-e" << "sh" << "-c" << cmd 00458 00459 4 << "tdesu" << "-u" << user << "-c" << cmd 00460 5 << "tdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd)) 00461 6 << split(term) << "-e" << "su" << user << "-c" << cmd 00462 7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd)) 00463 00464 8 << cmd 00465 9 << cmd 00466 a << term << "-e" << cmd 00467 b << term << "-e" << ("sh -c " + quote(cmd)) 00468 00469 c << "tdesu" << "-u" << user << "-c" << quote(cmd) 00470 d << "tdesu" << "-u" << user << "-c" << quote("sh -c " + quote(cmd)) 00471 e << term << "-e" << "su" << user << "-c" << quote(cmd) 00472 f << term << "-e" << "su" << user << "-c" << quote("sh -c " + quote(cmd)) 00473 00474 "sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh. 00475 this could be optimized with the -s switch of some su versions (e.g., debian linux). 00476 */ 00477 00478 if (_service.terminal()) { 00479 TDEConfigGroupSaver gs(TDEGlobal::config(), "General"); 00480 TQString terminal = TDEGlobal::config()->readPathEntry("TerminalApplication", "konsole"); 00481 if (terminal == "konsole") 00482 terminal += " -caption=%c %i %m"; 00483 terminal += " "; 00484 terminal += _service.terminalOptions(); 00485 if( !mx1.expandMacrosShellQuote( terminal ) ) { 00486 kdWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'" << endl; 00487 return TQStringList(); 00488 } 00489 mx2.expandMacrosShellQuote( terminal ); 00490 if (has_shell) 00491 result << terminal; 00492 else 00493 result = KShell::splitArgs( terminal ); // assuming that the term spec never needs a shell! 00494 result << "-e"; 00495 } 00496 00497 int err; 00498 if (_service.substituteUid()) { 00499 if (_service.terminal()) 00500 result << "su"; 00501 else 00502 result << "tdesu" << "-u"; 00503 result << _service.username() << "-c"; 00504 KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); 00505 if (err == KShell::FoundMeta) { 00506 shellQuote( exec ); 00507 exec.prepend( "/bin/sh -c " ); 00508 } else if (err != KShell::NoError) 00509 goto synerr; 00510 if (has_shell) 00511 shellQuote( exec ); 00512 result << exec; 00513 } else { 00514 if (has_shell) { 00515 if (_service.terminal()) { 00516 KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); 00517 if (err == KShell::FoundMeta) { 00518 shellQuote( exec ); 00519 exec.prepend( "/bin/sh -c " ); 00520 } else if (err != KShell::NoError) 00521 goto synerr; 00522 } 00523 result << exec; 00524 } else { 00525 result += KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); 00526 if (err == KShell::FoundMeta) 00527 result << "/bin/sh" << "-c" << exec; 00528 else if (err != KShell::NoError) 00529 goto synerr; 00530 } 00531 } 00532 00533 return result; 00534 00535 synerr: 00536 kdWarning() << "KRun: syntax error in command `" << _service.exec() << "', service `" << _service.name() << "'" << endl; 00537 return TQStringList(); 00538 } 00539 00540 //static 00541 TQString KRun::binaryName( const TQString & execLine, bool removePath ) 00542 { 00543 // Remove parameters and/or trailing spaces. 00544 TQStringList args = KShell::splitArgs( execLine ); 00545 for (TQStringList::ConstIterator it = args.begin(); it != args.end(); ++it) 00546 if (!(*it).contains('=')) 00547 // Remove path if wanted 00548 return removePath ? (*it).mid(TQString(*it).findRev('/') + 1) : *it; 00549 return TQString(); 00550 } 00551 00552 static pid_t runCommandInternal( TDEProcess* proc, const KService* service, const TQString& binName, 00553 const TQString &execName, const TQString & iconName, TQWidget* window, TQCString asn ) 00554 { 00555 if (service && !service->desktopEntryPath().isEmpty() 00556 && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() )) 00557 { 00558 kdWarning() << "No authorization to execute " << service->desktopEntryPath() << endl; 00559 KMessageBox::sorry(window, i18n("You are not authorized to execute this file.")); 00560 return 0; 00561 } 00562 TQString bin = KRun::binaryName( binName, true ); 00563 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification 00564 bool silent; 00565 TQCString wmclass; 00566 TDEStartupInfoId id; 00567 bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass )); 00568 if( startup_notify ) 00569 { 00570 id.initId( asn ); 00571 id.setupStartupEnv(); 00572 TDEStartupInfoData data; 00573 data.setHostname(); 00574 data.setBin( bin ); 00575 if( !execName.isEmpty()) 00576 data.setName( execName ); 00577 else if( service && !service->name().isEmpty()) 00578 data.setName( service->name()); 00579 data.setDescription( i18n( "Launching %1" ).arg( data.name())); 00580 if( !iconName.isEmpty()) 00581 data.setIcon( iconName ); 00582 else if( service && !service->icon().isEmpty()) 00583 data.setIcon( service->icon()); 00584 if( !wmclass.isEmpty()) 00585 data.setWMClass( wmclass ); 00586 if( silent ) 00587 data.setSilent( TDEStartupInfoData::Yes ); 00588 data.setDesktop( KWin::currentDesktop()); 00589 if( window ) 00590 data.setLaunchedBy( window->winId()); 00591 TDEStartupInfo::sendStartup( id, data ); 00592 } 00593 pid_t pid = TDEProcessRunner::run( proc, binName, id ); 00594 if( startup_notify && pid ) 00595 { 00596 TDEStartupInfoData data; 00597 data.addPid( pid ); 00598 TDEStartupInfo::sendChange( id, data ); 00599 TDEStartupInfo::resetStartupEnv(); 00600 } 00601 return pid; 00602 #else 00603 Q_UNUSED( execName ); 00604 Q_UNUSED( iconName ); 00605 return TDEProcessRunner::run( proc, bin ); 00606 #endif 00607 } 00608 00609 // This code is also used in tdelauncher. 00610 bool KRun::checkStartupNotify( const TQString& /*binName*/, const KService* service, bool* silent_arg, TQCString* wmclass_arg ) 00611 { 00612 bool silent = false; 00613 TQCString wmclass; 00614 if( service && service->property( "StartupNotify" ).isValid()) 00615 { 00616 silent = !service->property( "StartupNotify" ).toBool(); 00617 wmclass = service->property( "StartupWMClass" ).toString().latin1(); 00618 } 00619 else if( service && service->property( "X-TDE-StartupNotify" ).isValid()) 00620 { 00621 silent = !service->property( "X-TDE-StartupNotify" ).toBool(); 00622 wmclass = service->property( "X-TDE-WMClass" ).toString().latin1(); 00623 } 00624 else // non-compliant app 00625 { 00626 if( service ) 00627 { 00628 if( service->type() == "Application" ) 00629 wmclass = "0"; // doesn't have .desktop entries needed, start as non-compliant 00630 else 00631 return false; // no startup notification at all 00632 } 00633 else 00634 { 00635 #if 0 00636 // Create startup notification even for apps for which there shouldn't be any, 00637 // just without any visual feedback. This will ensure they'll be positioned on the proper 00638 // virtual desktop, and will get user timestamp from the ASN ID. 00639 wmclass = "0"; 00640 silent = true; 00641 #else // That unfortunately doesn't work, when the launched non-compliant application 00642 // launches another one that is compliant and there is any delay inbetween (bnc:#343359) 00643 return false; 00644 #endif 00645 } 00646 } 00647 if( silent_arg != NULL ) 00648 *silent_arg = silent; 00649 if( wmclass_arg != NULL ) 00650 *wmclass_arg = wmclass; 00651 return true; 00652 } 00653 00654 static pid_t runTempService( const KService& _service, const KURL::List& _urls, TQWidget* window, 00655 const TQCString& asn, bool tempFiles, const TQString& suggestedFileName ) 00656 { 00657 if (!_urls.isEmpty()) { 00658 kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl; 00659 } 00660 00661 TQStringList args; 00662 if ((_urls.count() > 1) && !_service.allowMultipleFiles()) 00663 { 00664 // We need to launch the application N times. That sucks. 00665 // We ignore the result for application 2 to N. 00666 // For the first file we launch the application in the 00667 // usual way. The reported result is based on this 00668 // application. 00669 KURL::List::ConstIterator it = _urls.begin(); 00670 while(++it != _urls.end()) 00671 { 00672 KURL::List singleUrl; 00673 singleUrl.append(*it); 00674 runTempService( _service, singleUrl, window, "", tempFiles, suggestedFileName ); 00675 } 00676 KURL::List singleUrl; 00677 singleUrl.append(_urls.first()); 00678 args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles, suggestedFileName); 00679 } 00680 else 00681 { 00682 args = KRun::processDesktopExec(_service, _urls, false, tempFiles, suggestedFileName); 00683 } 00684 kdDebug(7010) << "runTempService: TDEProcess args=" << args << endl; 00685 00686 TDEProcess * proc = new TDEProcess; 00687 *proc << args; 00688 00689 if (!_service.path().isEmpty()) 00690 proc->setWorkingDirectory(_service.path()); 00691 00692 return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ), 00693 _service.name(), _service.icon(), window, asn ); 00694 } 00695 00696 // WARNING: don't call this from processDesktopExec, since tdelauncher uses that too... 00697 static KURL::List resolveURLs( const KURL::List& _urls, const KService& _service ) 00698 { 00699 // Check which protocols the application supports. 00700 // This can be a list of actual protocol names, or just TDEIO for KDE apps. 00701 TQStringList supportedProtocols = _service.property("X-TDE-Protocols").toStringList(); 00702 KRunMX1 mx1( _service ); 00703 TQString exec = _service.exec(); 00704 if ( mx1.expandMacrosShellQuote( exec ) && !mx1.hasUrls ) { 00705 Q_ASSERT( supportedProtocols.isEmpty() ); // huh? If you support protocols you need %u or %U... 00706 } else { 00707 if ( supportedProtocols.isEmpty() ) 00708 { 00709 // compat mode: assume TDEIO if not set and it's a KDE app 00710 TQStringList categories = _service.property("Categories").toStringList(); 00711 if (( categories.find("TDE") != categories.end() ) && ( categories.find("KDE") != categories.end() )) 00712 supportedProtocols.append( "TDEIO" ); 00713 else { // if no KDE app, be a bit over-generic 00714 supportedProtocols.append( "http"); 00715 supportedProtocols.append( "ftp"); 00716 } 00717 } 00718 } 00719 kdDebug(7010) << "supportedProtocols:" << supportedProtocols << endl; 00720 00721 KURL::List urls( _urls ); 00722 if ( supportedProtocols.find( "TDEIO" ) == supportedProtocols.end() ) { 00723 for( KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it ) { 00724 const KURL url = *it; 00725 bool supported = url.isLocalFile() || supportedProtocols.find( url.protocol().lower() ) != supportedProtocols.end(); 00726 kdDebug(7010) << "Looking at url=" << url << " supported=" << supported << endl; 00727 if ( !supported && KProtocolInfo::protocolClass(url.protocol()) == ":local" ) 00728 { 00729 // Maybe we can resolve to a local URL? 00730 KURL localURL = TDEIO::NetAccess::mostLocalURL( url, 0 ); 00731 if ( localURL != url ) { 00732 *it = localURL; 00733 kdDebug(7010) << "Changed to " << localURL << endl; 00734 } 00735 } 00736 } 00737 } 00738 return urls; 00739 } 00740 00741 // BIC merge methods below 00742 pid_t KRun::run( const KService& _service, const KURL::List& _urls ) 00743 { 00744 return run( _service, _urls, 0, false, TQString::null ); 00745 } 00746 00747 pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles ) 00748 { 00749 return run( _service, _urls, 0, tempFiles, TQString::null ); 00750 } 00751 00752 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles ) 00753 { 00754 return run( _service, _urls, window, "", tempFiles, TQString::null ); 00755 } 00756 00757 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, const TQCString& asn, bool tempFiles ) 00758 { 00759 return run( _service, _urls, window, asn, tempFiles, TQString::null ); 00760 } 00761 00762 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles, const TQString& suggestedFileName ) 00763 { 00764 return run( _service, _urls, window, "", tempFiles, suggestedFileName ); 00765 } 00766 00767 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, const TQCString& asn, 00768 bool tempFiles, const TQString& suggestedFileName ) 00769 { 00770 if (!_service.desktopEntryPath().isEmpty() && 00771 !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath())) 00772 { 00773 kdWarning() << "No authorization to execute " << _service.desktopEntryPath() << endl; 00774 KMessageBox::sorry(window, i18n("You are not authorized to execute this service.")); 00775 return 0; 00776 } 00777 00778 if ( !tempFiles ) 00779 { 00780 // Remember we opened those urls, for the "recent documents" menu in kicker 00781 KURL::List::ConstIterator it = _urls.begin(); 00782 for(; it != _urls.end(); ++it) { 00783 //kdDebug(7010) << "TDERecentDocument::adding " << (*it).url() << endl; 00784 TDERecentDocument::add( *it, _service.desktopEntryName() ); 00785 } 00786 } 00787 00788 if ( tempFiles || _service.desktopEntryPath().isEmpty() || !suggestedFileName.isEmpty() ) 00789 { 00790 return runTempService(_service, _urls, window, asn, tempFiles, suggestedFileName); 00791 } 00792 00793 kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl; 00794 00795 if (!_urls.isEmpty()) { 00796 kdDebug(7010) << "First url " << _urls.first().url() << endl; 00797 } 00798 00799 // Resolve urls if needed, depending on what the app supports 00800 const KURL::List urls = resolveURLs( _urls, _service ); 00801 00802 TQString error; 00803 int pid = 0; 00804 00805 TQCString myasn = asn; 00806 // startServiceByDesktopPath() doesn't take TQWidget*, add it to the startup info now 00807 if( window != NULL ) 00808 { 00809 if( myasn.isEmpty()) 00810 myasn = TDEStartupInfo::createNewStartupId(); 00811 if( myasn != "0" ) 00812 { 00813 TDEStartupInfoId id; 00814 id.initId( myasn ); 00815 TDEStartupInfoData data; 00816 data.setLaunchedBy( window->winId()); 00817 TDEStartupInfo::sendChange( id, data ); 00818 } 00819 } 00820 00821 int i = TDEApplication::startServiceByDesktopPath( 00822 _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid, myasn 00823 ); 00824 00825 if (i != 0) 00826 { 00827 kdDebug(7010) << error << endl; 00828 KMessageBox::sorry( window, error ); 00829 return 0; 00830 } 00831 00832 kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl; 00833 return (pid_t) pid; 00834 } 00835 00836 00837 pid_t KRun::run( const TQString& _exec, const KURL::List& _urls, const TQString& _name, 00838 const TQString& _icon, const TQString&, const TQString&) 00839 { 00840 KService::Ptr service = new KService(_name, _exec, _icon); 00841 00842 return run(*service, _urls); 00843 } 00844 00845 pid_t KRun::runCommand( TQString cmd ) 00846 { 00847 return KRun::runCommand( cmd, TQString::null, TQString::null, NULL, "" ); 00848 } 00849 00850 pid_t KRun::runCommand( const TQString& cmd, const TQString &execName, const TQString & iconName ) 00851 { 00852 return KRun::runCommand( cmd, execName, iconName, NULL, "" ); 00853 } 00854 00855 pid_t KRun::runCommand( const TQString& cmd, const TQString &execName, const TQString & iconName, 00856 TQWidget* window, const TQCString& asn ) 00857 { 00858 kdDebug(7010) << "runCommand " << cmd << "," << execName << endl; 00859 TDEProcess * proc = new TDEProcess; 00860 proc->setUseShell(true); 00861 *proc << cmd; 00862 KService::Ptr service = KService::serviceByDesktopName( binaryName( execName, true ) ); 00863 TQString bin = binaryName( cmd, false ); 00864 int pos = bin.findRev( '/' ); 00865 if (pos != -1) { 00866 proc->setWorkingDirectory( bin.mid(0, pos) ); 00867 } 00868 return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName, window, asn ); 00869 } 00870 00871 KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo ) 00872 :m_timer(0,"KRun::timer") 00873 { 00874 init (url, 0, "", mode, isLocalFile, showProgressInfo); 00875 } 00876 00877 KRun::KRun( const KURL& url, TQWidget* window, mode_t mode, bool isLocalFile, 00878 bool showProgressInfo ) 00879 :m_timer(0,"KRun::timer") 00880 { 00881 init (url, window, "", mode, isLocalFile, showProgressInfo); 00882 } 00883 00884 KRun::KRun( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode, bool isLocalFile, 00885 bool showProgressInfo ) 00886 :m_timer(0,"KRun::timer") 00887 { 00888 init (url, window, asn, mode, isLocalFile, showProgressInfo); 00889 } 00890 00891 void KRun::init ( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode, bool isLocalFile, 00892 bool showProgressInfo ) 00893 { 00894 m_bFault = false; 00895 m_bAutoDelete = true; 00896 m_bProgressInfo = showProgressInfo; 00897 m_bFinished = false; 00898 m_job = 0L; 00899 m_strURL = url; 00900 m_bScanFile = false; 00901 m_bIsDirectory = false; 00902 m_bIsLocalFile = isLocalFile; 00903 m_mode = mode; 00904 d = new KRunPrivate; 00905 d->m_runExecutables = true; 00906 d->m_window = window; 00907 d->m_asn = asn; 00908 setEnableExternalBrowser(true); 00909 00910 // Start the timer. This means we will return to the event 00911 // loop and do initialization afterwards. 00912 // Reason: We must complete the constructor before we do anything else. 00913 m_bInit = true; 00914 connect( &m_timer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotTimeout() ) ); 00915 m_timer.start( 0, true ); 00916 kdDebug(7010) << " new KRun " << this << " " << url.prettyURL() << " timer=" << &m_timer << endl; 00917 00918 kapp->ref(); 00919 } 00920 00921 void KRun::init() 00922 { 00923 kdDebug(7010) << "INIT called" << endl; 00924 00925 bool bypassErrorMessage = false; 00926 00927 if (m_strURL.url().startsWith("$(")) { 00928 // check for environment variables and make necessary translations 00929 TQString aValue = m_strURL.url(); 00930 int nDollarPos = aValue.find( '$' ); 00931 00932 while( nDollarPos != -1 && nDollarPos+1 < static_cast<int>(aValue.length())) { 00933 // there is at least one $ 00934 if( (aValue)[nDollarPos+1] != '$' ) { 00935 uint nEndPos = nDollarPos+1; 00936 // the next character is no $ 00937 TQString aVarName; 00938 if (aValue[nEndPos]=='{') 00939 { 00940 while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!='}') ) 00941 nEndPos++; 00942 nEndPos++; 00943 aVarName = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); 00944 } 00945 else 00946 { 00947 while ( nEndPos <= aValue.length() && (aValue[nEndPos].isNumber() 00948 || aValue[nEndPos].isLetter() || aValue[nEndPos]=='_' ) ) 00949 nEndPos++; 00950 aVarName = aValue.mid( nDollarPos+1, nEndPos-nDollarPos-1 ); 00951 } 00952 const char* pEnv = 0; 00953 if (!aVarName.isEmpty()) 00954 pEnv = getenv( aVarName.ascii() ); 00955 if( pEnv ) { 00956 // !!! Sergey A. Sukiyazov <corwin@micom.don.ru> !!! 00957 // A environment variables may contain values in 8bit 00958 // locale cpecified encoding or in UTF8 encoding. 00959 aValue.replace( nDollarPos, nEndPos-nDollarPos, KStringHandler::from8Bit( pEnv ) ); 00960 } else 00961 aValue.remove( nDollarPos, nEndPos-nDollarPos ); 00962 } else { 00963 // remove one of the dollar signs 00964 aValue.remove( nDollarPos, 1 ); 00965 nDollarPos++; 00966 } 00967 nDollarPos = aValue.find( '$', nDollarPos ); 00968 } 00969 m_strURL = KURL(aValue); 00970 bypassErrorMessage = true; 00971 } 00972 00973 if ( !m_strURL.isValid() ) 00974 { 00975 if (bypassErrorMessage == false) { 00976 d->m_showingError = true; 00977 KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) ); 00978 d->m_showingError = false; 00979 } 00980 m_bFault = true; 00981 m_bFinished = true; 00982 m_timer.start( 0, true ); 00983 return; 00984 } 00985 if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL)) 00986 { 00987 TQString msg = TDEIO::buildErrorString(TDEIO::ERR_ACCESS_DENIED, m_strURL.prettyURL()); 00988 d->m_showingError = true; 00989 KMessageBoxWrapper::error( d->m_window, msg ); 00990 d->m_showingError = false; 00991 m_bFault = true; 00992 m_bFinished = true; 00993 m_timer.start( 0, true ); 00994 return; 00995 } 00996 00997 if ( !m_bIsLocalFile && m_strURL.isLocalFile() ) 00998 m_bIsLocalFile = true; 00999 01000 TQString exec; 01001 if (m_strURL.protocol().startsWith("http")) 01002 { 01003 exec = d->m_externalBrowser; 01004 } 01005 01006 if ( m_bIsLocalFile ) 01007 { 01008 if ( m_mode == 0 ) 01009 { 01010 KDE_struct_stat buff; 01011 if ( KDE_stat( TQFile::encodeName(m_strURL.path()), &buff ) == -1 ) 01012 { 01013 d->m_showingError = true; 01014 KMessageBoxWrapper::error( d->m_window, i18n( "<qt>Unable to run the command specified. The file or folder <b>%1</b> does not exist.</qt>" ).arg( m_strURL.htmlURL() ) ); 01015 d->m_showingError = false; 01016 m_bFault = true; 01017 m_bFinished = true; 01018 m_timer.start( 0, true ); 01019 return; 01020 } 01021 m_mode = buff.st_mode; 01022 } 01023 01024 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile ); 01025 assert( mime != 0L ); 01026 kdDebug(7010) << "MIME TYPE is " << mime->name() << endl; 01027 foundMimeType( mime->name() ); 01028 return; 01029 } 01030 else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( m_strURL ) ) { 01031 kdDebug(7010) << "Helper protocol" << endl; 01032 01033 bool ok = false; 01034 KURL::List urls; 01035 if (!((m_strURL.protocol().startsWith("http")) && (m_strURL.url() == "http://default.browser"))) 01036 urls.append( m_strURL ); 01037 if (exec.isEmpty()) 01038 { 01039 exec = KProtocolInfo::exec( m_strURL.protocol() ); 01040 if (exec.isEmpty()) 01041 { 01042 foundMimeType(KProtocolInfo::defaultMimetype(m_strURL)); 01043 return; 01044 } 01045 run( exec, urls ); 01046 ok = true; 01047 } 01048 else if (exec.startsWith("!")) 01049 { 01050 exec = exec.mid(1); // Literal command 01051 exec += " %u"; 01052 run( exec, urls ); 01053 ok = true; 01054 } 01055 else 01056 { 01057 KService::Ptr service = KService::serviceByStorageId( exec ); 01058 if (service) 01059 { 01060 run( *service, urls, d->m_window, d->m_asn ); 01061 ok = true; 01062 } 01063 } 01064 01065 if (ok) 01066 { 01067 m_bFinished = true; 01068 // will emit the error and autodelete this 01069 m_timer.start( 0, true ); 01070 return; 01071 } 01072 } 01073 01074 if ((m_strURL.protocol().startsWith("http")) && (m_strURL.url() == "http://default.browser")) { 01075 KURL::List urls; 01076 run( "kfmclient openProfile webbrowsing", urls ); 01077 m_bFinished = true; 01078 // will emit the error and autodelete this 01079 m_timer.start( 0, true ); 01080 return; 01081 } 01082 01083 // Did we already get the information that it is a directory ? 01084 if ( S_ISDIR( m_mode ) ) 01085 { 01086 foundMimeType( "inode/directory" ); 01087 return; 01088 } 01089 01090 // Let's see whether it is a directory 01091 01092 if ( !KProtocolInfo::supportsListing( m_strURL ) ) 01093 { 01094 //kdDebug(7010) << "Protocol has no support for listing" << endl; 01095 // No support for listing => it can't be a directory (example: http) 01096 scanFile(); 01097 return; 01098 } 01099 01100 kdDebug(7010) << "Testing directory (stating)" << endl; 01101 01102 // It may be a directory or a file, let's stat 01103 TDEIO::StatJob *job = TDEIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo ); 01104 job->setWindow (d->m_window); 01105 connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), 01106 this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) ); 01107 m_job = job; 01108 kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl; 01109 } 01110 01111 KRun::~KRun() 01112 { 01113 kdDebug(7010) << "KRun::~KRun() " << this << endl; 01114 m_timer.stop(); 01115 killJob(); 01116 kapp->deref(); 01117 kdDebug(7010) << "KRun::~KRun() done " << this << endl; 01118 delete d; 01119 } 01120 01121 void KRun::scanFile() 01122 { 01123 kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl; 01124 // First, let's check for well-known extensions 01125 // Not when there is a query in the URL, in any case. 01126 if ( m_strURL.query().isEmpty() ) 01127 { 01128 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL ); 01129 assert( mime != 0L ); 01130 if ( mime->name() != "application/octet-stream" || m_bIsLocalFile ) 01131 { 01132 kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl; 01133 foundMimeType( mime->name() ); 01134 return; 01135 } 01136 } 01137 01138 // No mimetype found, and the URL is not local (or fast mode not allowed). 01139 // We need to apply the 'TDEIO' method, i.e. either asking the server or 01140 // getting some data out of the file, to know what mimetype it is. 01141 01142 if ( !KProtocolInfo::supportsReading( m_strURL ) ) 01143 { 01144 kdError(7010) << "#### NO SUPPORT FOR READING!" << endl; 01145 m_bFault = true; 01146 m_bFinished = true; 01147 m_timer.start( 0, true ); 01148 return; 01149 } 01150 kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl; 01151 01152 TDEIO::TransferJob *job = TDEIO::get( m_strURL, false /*reload*/, m_bProgressInfo ); 01153 job->setWindow (d->m_window); 01154 connect(job, TQT_SIGNAL( result(TDEIO::Job *)), 01155 this, TQT_SLOT( slotScanFinished(TDEIO::Job *))); 01156 connect(job, TQT_SIGNAL( mimetype(TDEIO::Job *, const TQString &)), 01157 this, TQT_SLOT( slotScanMimeType(TDEIO::Job *, const TQString &))); 01158 m_job = job; 01159 kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl; 01160 } 01161 01162 void KRun::slotTimeout() 01163 { 01164 kdDebug(7010) << this << " slotTimeout called" << endl; 01165 if ( m_bInit ) 01166 { 01167 m_bInit = false; 01168 init(); 01169 return; 01170 } 01171 01172 if ( m_bFault ) { 01173 emit error(); 01174 } 01175 if ( m_bFinished ) { 01176 emit finished(); 01177 } 01178 else 01179 { 01180 if ( m_bScanFile ) 01181 { 01182 m_bScanFile = false; 01183 scanFile(); 01184 return; 01185 } 01186 else if ( m_bIsDirectory ) 01187 { 01188 m_bIsDirectory = false; 01189 foundMimeType( "inode/directory" ); 01190 return; 01191 } 01192 } 01193 01194 if ( m_bAutoDelete ) 01195 { 01196 delete this; 01197 return; 01198 } 01199 } 01200 01201 void KRun::slotStatResult( TDEIO::Job * job ) 01202 { 01203 m_job = 0L; 01204 if (job->error()) 01205 { 01206 d->m_showingError = true; 01207 kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl; 01208 job->showErrorDialog(); 01209 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 01210 d->m_showingError = false; 01211 01212 m_bFault = true; 01213 m_bFinished = true; 01214 01215 // will emit the error and autodelete this 01216 m_timer.start( 0, true ); 01217 01218 } else { 01219 01220 kdDebug(7010) << "Finished" << endl; 01221 if(!dynamic_cast<TDEIO::StatJob*>(job)) 01222 kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl; 01223 01224 TQString knownMimeType; 01225 TDEIO::UDSEntry entry = ((TDEIO::StatJob*)job)->statResult(); 01226 TDEIO::UDSEntry::ConstIterator it = entry.begin(); 01227 for( ; it != entry.end(); it++ ) { 01228 switch( (*it).m_uds ) { 01229 case TDEIO::UDS_FILE_TYPE: 01230 if ( S_ISDIR( (mode_t)((*it).m_long) ) ) 01231 m_bIsDirectory = true; // it's a dir 01232 else 01233 m_bScanFile = true; // it's a file 01234 break; 01235 case TDEIO::UDS_MIME_TYPE: // mimetype already known? (e.g. print:/manager) 01236 knownMimeType = (*it).m_str; 01237 break; 01238 case TDEIO::UDS_LOCAL_PATH: 01239 d->m_localPath = (*it).m_str; 01240 break; 01241 default: 01242 break; 01243 } 01244 } 01245 if ( !knownMimeType.isEmpty() ) 01246 { 01247 foundMimeType( knownMimeType ); 01248 m_bFinished = true; 01249 } 01250 01251 // We should have found something 01252 assert ( m_bScanFile || m_bIsDirectory ); 01253 01254 // Start the timer. Once we get the timer event this 01255 // protocol server is back in the pool and we can reuse it. 01256 // This gives better performance than starting a new slave 01257 m_timer.start( 0, true ); 01258 } 01259 } 01260 01261 void KRun::slotScanMimeType( TDEIO::Job *, const TQString &mimetype ) 01262 { 01263 if ( mimetype.isEmpty() ) 01264 kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a tdeioslave bug." << endl; 01265 foundMimeType( mimetype ); 01266 m_job = 0; 01267 } 01268 01269 void KRun::slotScanFinished( TDEIO::Job *job ) 01270 { 01271 m_job = 0; 01272 if (job->error()) 01273 { 01274 d->m_showingError = true; 01275 kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl; 01276 job->showErrorDialog(); 01277 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 01278 d->m_showingError = false; 01279 01280 m_bFault = true; 01281 m_bFinished = true; 01282 01283 // will emit the error and autodelete this 01284 m_timer.start( 0, true ); 01285 } 01286 } 01287 01288 void KRun::foundMimeType( const TQString& type ) 01289 { 01290 kdDebug(7010) << "Resulting mime type is " << type << endl; 01291 01292 /* 01293 // Automatically unzip stuff 01294 01295 // Disabled since the new TDEIO doesn't have filters yet. 01296 01297 if ( type == "application/x-gzip" || 01298 type == "application/x-bzip" || 01299 type == "application/x-bzip2" ) 01300 { 01301 KURL::List lst = KURL::split( m_strURL ); 01302 if ( lst.isEmpty() ) 01303 { 01304 TQString tmp = i18n( "Malformed URL" ); 01305 tmp += "\n"; 01306 tmp += m_strURL.url(); 01307 KMessageBoxWrapper::error( 0L, tmp ); 01308 return; 01309 } 01310 01311 if ( type == "application/x-gzip" ) 01312 lst.prepend( KURL( "gzip:/decompress" ) ); 01313 else if ( type == "application/x-bzip" ) 01314 lst.prepend( KURL( "bzip:/decompress" ) ); 01315 else if ( type == "application/x-bzip2" ) 01316 lst.prepend( KURL( "bzip2:/decompress" ) ); 01317 else if ( type == "application/x-tar" ) 01318 lst.prepend( KURL( "tar:/" ) ); 01319 01320 // Move the HTML style reference to the leftmost URL 01321 KURL::List::Iterator it = lst.begin(); 01322 ++it; 01323 (*lst.begin()).setRef( (*it).ref() ); 01324 (*it).setRef( TQString::null ); 01325 01326 // Create the new URL 01327 m_strURL = KURL::join( lst ); 01328 01329 kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl; 01330 01331 killJob(); 01332 01333 // We don't know if this is a file or a directory. Let's test this first. 01334 // (For instance a tar.gz is a directory contained inside a file) 01335 // It may be a directory or a file, let's stat 01336 TDEIO::StatJob *job = TDEIO::stat( m_strURL, m_bProgressInfo ); 01337 connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), 01338 this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) ); 01339 m_job = job; 01340 01341 return; 01342 } 01343 */ 01344 TDEIO::TransferJob *job = ::tqqt_cast<TDEIO::TransferJob *>( m_job ); 01345 if ( job ) 01346 { 01347 job->putOnHold(); 01348 TDEIO::Scheduler::publishSlaveOnHold(); 01349 m_job = 0; 01350 } 01351 01352 Q_ASSERT( !m_bFinished ); 01353 01354 // Suport for preferred service setting, see setPreferredService 01355 if ( !d->m_preferredService.isEmpty() ) { 01356 kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl; 01357 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService ); 01358 if ( serv && serv->hasServiceType( type ) ) 01359 { 01360 KURL::List lst; 01361 lst.append( m_strURL ); 01362 m_bFinished = KRun::run( *serv, lst, d->m_window, d->m_asn ); 01367 } 01368 } 01369 01370 // Resolve .desktop files from media:/, remote:/, applications:/ etc. 01371 if ( ((type == "application/x-desktop") || 01372 (type == "media/builtin-mydocuments") || 01373 (type == "media/builtin-mycomputer") || 01374 (type == "media/builtin-mynetworkplaces") || 01375 (type == "media/builtin-printers") || 01376 (type == "media/builtin-trash") || 01377 (type == "media/builtin-webbrowser")) /* or inheriting? */ && (!d->m_localPath.isEmpty()) ) 01378 { 01379 m_strURL = KURL(); 01380 m_strURL.setPath( d->m_localPath ); 01381 } 01382 01383 if (!m_bFinished && KRun::runURL( m_strURL, type, d->m_window, d->m_asn, false, d->m_runExecutables, d->m_suggestedFileName )){ 01384 m_bFinished = true; 01385 } 01386 else{ 01387 m_bFinished = true; 01388 m_bFault = true; 01389 } 01390 01391 m_timer.start( 0, true ); 01392 } 01393 01394 void KRun::killJob() 01395 { 01396 if ( m_job ) 01397 { 01398 kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl; 01399 m_job->kill(); 01400 m_job = 0L; 01401 } 01402 } 01403 01404 void KRun::abort() 01405 { 01406 kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl; 01407 killJob(); 01408 // If we're showing an error message box, the rest will be done 01409 // after closing the msgbox -> don't autodelete nor emit signals now. 01410 if ( d->m_showingError ) 01411 return; 01412 m_bFault = true; 01413 m_bFinished = true; 01414 m_bInit = false; 01415 m_bScanFile = false; 01416 01417 // will emit the error and autodelete this 01418 m_timer.start( 0, true ); 01419 } 01420 01421 void KRun::setEnableExternalBrowser(bool b) 01422 { 01423 if (b) 01424 d->m_externalBrowser = TDEConfigGroup(TDEGlobal::config(), "General").readEntry("BrowserApplication"); 01425 else 01426 d->m_externalBrowser = TQString::null; 01427 } 01428 01429 void KRun::setPreferredService( const TQString& desktopEntryName ) 01430 { 01431 d->m_preferredService = desktopEntryName; 01432 } 01433 01434 void KRun::setRunExecutables(bool b) 01435 { 01436 d->m_runExecutables = b; 01437 } 01438 01439 void KRun::setSuggestedFileName( const TQString& fileName ) 01440 { 01441 d->m_suggestedFileName = fileName; 01442 } 01443 01444 bool KRun::isExecutable( const TQString& serviceType ) 01445 { 01446 return ( serviceType == "application/x-desktop" || 01447 serviceType == "media/builtin-mydocuments" || 01448 serviceType == "media/builtin-mycomputer" || 01449 serviceType == "media/builtin-mynetworkplaces" || 01450 serviceType == "media/builtin-printers" || 01451 serviceType == "media/builtin-trash" || 01452 serviceType == "media/builtin-webbrowser" || 01453 serviceType == "application/x-executable" || 01454 serviceType == "application/x-msdos-program" || 01455 serviceType == "application/x-shellscript" ); 01456 } 01457 01458 /****************/ 01459 01460 pid_t 01461 TDEProcessRunner::run(TDEProcess * p, const TQString & binName) 01462 { 01463 return (new TDEProcessRunner(p, binName))->pid(); 01464 } 01465 01466 #ifdef Q_WS_X11 01467 pid_t 01468 TDEProcessRunner::run(TDEProcess * p, const TQString & binName, const TDEStartupInfoId& id ) 01469 { 01470 return (new TDEProcessRunner(p, binName, id))->pid(); 01471 } 01472 #endif 01473 01474 TDEProcessRunner::TDEProcessRunner(TDEProcess * p, const TQString & _binName ) 01475 : TQObject(), 01476 process_(p), 01477 binName( _binName ) 01478 { 01479 TQObject::connect( 01480 process_, TQT_SIGNAL(processExited(TDEProcess *)), 01481 this, TQT_SLOT(slotProcessExited(TDEProcess *))); 01482 01483 process_->start(); 01484 if ( !process_->pid() ) 01485 slotProcessExited( process_ ); 01486 } 01487 01488 #ifdef Q_WS_X11 01489 TDEProcessRunner::TDEProcessRunner(TDEProcess * p, const TQString & _binName, const TDEStartupInfoId& id ) 01490 : TQObject(), 01491 process_(p), 01492 binName( _binName ), 01493 id_( id ) 01494 { 01495 TQObject::connect( 01496 process_, TQT_SIGNAL(processExited(TDEProcess *)), 01497 this, TQT_SLOT(slotProcessExited(TDEProcess *))); 01498 01499 process_->start(); 01500 if ( !process_->pid() ) 01501 slotProcessExited( process_ ); 01502 } 01503 #endif 01504 01505 TDEProcessRunner::~TDEProcessRunner() 01506 { 01507 delete process_; 01508 } 01509 01510 pid_t 01511 TDEProcessRunner::pid() const 01512 { 01513 return process_->pid(); 01514 } 01515 01516 void 01517 TDEProcessRunner::slotProcessExited(TDEProcess * p) 01518 { 01519 if (p != process_) 01520 return; // Eh ? 01521 01522 kdDebug(7010) << "slotProcessExited " << binName << endl; 01523 kdDebug(7010) << "normalExit " << process_->normalExit() << endl; 01524 kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl; 01525 bool showErr = process_->normalExit() 01526 && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 ); 01527 if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) ) 01528 { 01529 // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist. 01530 // We can't just rely on that, but it's a good hint. 01531 // Before assuming its really so, we'll try to find the binName 01532 // relatively to current directory, and then in the PATH. 01533 if ( !TQFile( binName ).exists() && TDEStandardDirs::findExe( binName ).isEmpty() ) 01534 { 01535 kapp->ref(); 01536 KMessageBox::sorry( 0L, i18n("Could not find the program '%1'").arg( binName ) ); 01537 kapp->deref(); 01538 } 01539 } 01540 #ifdef Q_WS_X11 01541 if( !id_.none()) 01542 { 01543 TDEStartupInfoData data; 01544 data.addPid( pid()); // announce this pid for the startup notification has finished 01545 data.setHostname(); 01546 TDEStartupInfo::sendFinish( id_, data ); 01547 } 01548 #endif 01549 deleteLater(); 01550 } 01551 01552 void KRun::virtual_hook( int, void* ) 01553 { /*BASE::virtual_hook( id, data );*/ } 01554 01555 #include "krun.moc"