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