• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

krun.cpp

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

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal