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