krun.cpp

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