00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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"
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() ) {
00092 KMimeType::Ptr mimeType = KMimeType::mimeType(mimetype, KMimeType::ResolveAliases);
00093 if ( mimeType && (mimeType->is("application/x-executable") || mimeType->is("application/x-executable-script")) )
00094 return true;
00095 }
00096 return false;
00097 }
00098
00099
00100 bool KRun::runUrl( const KUrl& u, const QString& _mimetype, QWidget* window, bool tempFile, bool runExecutables, const QString& suggestedFileName, const QByteArray& asn )
00101 {
00102 bool noRun = false;
00103 bool noAuth = false;
00104 if ( _mimetype == "inode/directory-locked" )
00105 {
00106 KMessageBoxWrapper::error( window,
00107 i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>", Qt::escape(u.prettyUrl())) );
00108 return false;
00109 }
00110 else if ( _mimetype == "application/x-desktop" )
00111 {
00112 if ( u.isLocalFile() && runExecutables )
00113 return KDesktopFileActions::run( u, true );
00114 }
00115 else if ( isExecutableFile(u, _mimetype) )
00116 {
00117 if ( u.isLocalFile() && runExecutables)
00118 {
00119 if (KAuthorized::authorize("shell_access"))
00120 {
00121 return (KRun::runCommand(KShell::quoteArg(u.path()), QString(), QString(), window, asn));
00122
00123 }
00124 else
00125 {
00126 noAuth = true;
00127 }
00128 }
00129 else if (_mimetype == "application/x-executable")
00130 noRun = true;
00131 }
00132 else if ( isExecutable(_mimetype) )
00133 {
00134 if (!runExecutables)
00135 noRun = true;
00136
00137 if (!KAuthorized::authorize("shell_access"))
00138 noAuth = true;
00139 }
00140
00141 if ( noRun )
00142 {
00143 KMessageBox::sorry( window,
00144 i18n("<qt>The file <b>%1</b> is an executable program. "
00145 "For safety it will not be started.</qt>", Qt::escape(u.prettyUrl())));
00146 return false;
00147 }
00148 if ( noAuth )
00149 {
00150 KMessageBoxWrapper::error( window,
00151 i18n("<qt>You do not have permission to run <b>%1</b>.</qt>", Qt::escape(u.prettyUrl())) );
00152 return false;
00153 }
00154
00155 KUrl::List lst;
00156 lst.append( u );
00157
00158 KService::Ptr offer = KMimeTypeTrader::self()->preferredService( _mimetype );
00159
00160 if ( !offer )
00161 {
00162
00163
00164
00165 return displayOpenWithDialog( lst, window, tempFile, suggestedFileName, asn );
00166 }
00167
00168 return KRun::run( *offer, lst, window, tempFile, suggestedFileName, asn );
00169 }
00170
00171 bool KRun::displayOpenWithDialog( const KUrl::List& lst, QWidget* window, bool tempFiles,
00172 const QString& suggestedFileName, const QByteArray& asn )
00173 {
00174 if (!KAuthorized::authorizeKAction("openwith"))
00175 {
00176 KMessageBox::sorry(window,
00177 i18n("You are not authorized to select an application to open this file."));
00178 return false;
00179 }
00180
00181 #ifdef Q_WS_WIN
00182 KConfigGroup cfgGroup(KGlobal::config(), "KOpenWithDialog Settings");
00183 if (cfgGroup.readEntry("Native", true))
00184 {
00185 return KRun::KRunPrivate::displayNativeOpenWithDialog( lst, window, tempFiles,
00186 suggestedFileName, asn );
00187 }
00188 #endif
00189 KOpenWithDialog l( lst, i18n("Open with:"), QString(), window );
00190 if ( l.exec() )
00191 {
00192 KService::Ptr service = l.service();
00193 if ( service )
00194 return KRun::run( *service, lst, window, tempFiles, suggestedFileName, asn );
00195
00196 kDebug(7010) << "No service set, running " << l.text();
00197 return KRun::run( l.text(), lst, window, false, suggestedFileName, asn );
00198 }
00199 return false;
00200 }
00201
00202 void KRun::shellQuote( QString &_str )
00203 {
00204
00205 if (_str.isEmpty())
00206 return;
00207 QChar q('\'');
00208 _str.replace(q, "'\\''").prepend(q).append(q);
00209 }
00210
00211
00212 class KRunMX1 : public KMacroExpanderBase {
00213 public:
00214 KRunMX1( const KService &_service ) :
00215 KMacroExpanderBase( '%' ), hasUrls( false ), hasSpec( false ), service( _service ) {}
00216 bool hasUrls:1, hasSpec:1;
00217
00218 protected:
00219 virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret );
00220
00221 private:
00222 const KService &service;
00223 };
00224
00225 int
00226 KRunMX1::expandEscapedMacro( const QString &str, int pos, QStringList &ret )
00227 {
00228 uint option = str[pos + 1].unicode();
00229 switch( option ) {
00230 case 'c':
00231 ret << service.name().replace( '%', "%%" );
00232 break;
00233 case 'k':
00234 ret << service.entryPath().replace( '%', "%%" );
00235 break;
00236 case 'i':
00237 ret << "-icon" << service.icon().replace( '%', "%%" );
00238 break;
00239 case 'm':
00240
00241 kWarning() << "-miniicon isn't supported anymore (service"
00242 << service.name() << ')';
00243 break;
00244 case 'u':
00245 case 'U':
00246 hasUrls = true;
00247
00248 case 'f':
00249 case 'F':
00250 case 'n':
00251 case 'N':
00252 case 'd':
00253 case 'D':
00254 case 'v':
00255 hasSpec = true;
00256
00257 default:
00258 return -2;
00259 }
00260 return 2;
00261 }
00262
00263 class KRunMX2 : public KMacroExpanderBase {
00264 public:
00265 KRunMX2( const KUrl::List &_urls ) :
00266 KMacroExpanderBase( '%' ), ignFile( false ), urls( _urls ) {}
00267 bool ignFile:1;
00268
00269 protected:
00270 virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret );
00271
00272 private:
00273 void subst( int option, const KUrl &url, QStringList &ret );
00274
00275 const KUrl::List &urls;
00276 };
00277
00278 void
00279 KRunMX2::subst( int option, const KUrl &url, QStringList &ret )
00280 {
00281 switch( option ) {
00282 case 'u':
00283 ret << ((url.isLocalFile() && url.fragment().isNull() && url.encodedQuery().isNull()) ?
00284 url.toLocalFile() : url.url());
00285 break;
00286 case 'd':
00287 ret << url.directory();
00288 break;
00289 case 'f':
00290 ret << url.path();
00291 break;
00292 case 'n':
00293 ret << url.fileName();
00294 break;
00295 case 'v':
00296 if (url.isLocalFile() && QFile::exists( url.path() ) )
00297 ret << KDesktopFile( url.path() ).desktopGroup().readEntry( "Dev" );
00298 break;
00299 }
00300 return;
00301 }
00302
00303 int
00304 KRunMX2::expandEscapedMacro( const QString &str, int pos, QStringList &ret )
00305 {
00306 uint option = str[pos + 1].unicode();
00307 switch( option ) {
00308 case 'f':
00309 case 'u':
00310 case 'n':
00311 case 'd':
00312 case 'v':
00313 if( urls.isEmpty() ) {
00314 if (!ignFile)
00315 kDebug() << "No URLs supplied to single-URL service" << str;
00316 } else if( urls.count() > 1 )
00317 kWarning() << urls.count() << "URLs supplied to single-URL service" << str;
00318 else
00319 subst( option, urls.first(), ret );
00320 break;
00321 case 'F':
00322 case 'U':
00323 case 'N':
00324 case 'D':
00325 option += 'a' - 'A';
00326 for( KUrl::List::ConstIterator it = urls.begin(); it != urls.end(); ++it )
00327 subst( option, *it, ret );
00328 break;
00329 case '%':
00330 ret = QStringList(QLatin1String("%"));
00331 break;
00332 default:
00333 return -2;
00334 }
00335 return 2;
00336 }
00337
00338 QStringList KRun::processDesktopExec(const KService &_service, const KUrl::List& _urls, bool tempFiles, const QString& suggestedFileName)
00339 {
00340 QString exec = _service.exec();
00341 if ( exec.isEmpty() ) {
00342 kWarning() << "KRun: no Exec field in `" << _service.entryPath() << "' !";
00343 return QStringList();
00344 }
00345
00346 QStringList result;
00347 bool appHasTempFileOption;
00348
00349 KRunMX1 mx1( _service );
00350 KRunMX2 mx2( _urls );
00351
00352 if( !mx1.expandMacrosShellQuote( exec ) ) {
00353 kWarning() << "KRun: syntax error in command" << _service.exec() << ", service" << _service.name();
00354 return QStringList();
00355 }
00356
00357
00358
00359
00360 appHasTempFileOption = tempFiles && _service.property("X-KDE-HasTempFileOption").toBool();
00361 if( tempFiles && !appHasTempFileOption && _urls.size() ) {
00362 const QString kioexec = KStandardDirs::findExe("kioexec");
00363 Q_ASSERT(!kioexec.isEmpty());
00364 result << kioexec << "--tempfiles" << exec;
00365 if ( !suggestedFileName.isEmpty() ) {
00366 result << "--suggestedfilename";
00367 result << suggestedFileName;
00368 }
00369 result += _urls.toStringList();
00370 return result;
00371 }
00372
00373
00374 if( !mx1.hasUrls ) {
00375 for( KUrl::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it )
00376 if ( !(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it) ) {
00377
00378 const QString kioexec = KStandardDirs::findExe("kioexec");
00379 Q_ASSERT(!kioexec.isEmpty());
00380 result << kioexec;
00381 if ( tempFiles )
00382 result << "--tempfiles";
00383 if ( !suggestedFileName.isEmpty() ) {
00384 result << "--suggestedfilename";
00385 result << suggestedFileName;
00386 }
00387 result << exec;
00388 result += _urls.toStringList();
00389 return result;
00390 }
00391 }
00392
00393 if ( appHasTempFileOption )
00394 exec += " --tempfile";
00395
00396
00397
00398
00399 if( !mx1.hasSpec ) {
00400 exec += " %f";
00401 mx2.ignFile = true;
00402 }
00403
00404 mx2.expandMacrosShellQuote( exec );
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423 if (_service.terminal()) {
00424 KConfigGroup cg(KGlobal::config(), "General");
00425 QString terminal = cg.readPathEntry("TerminalApplication", "konsole");
00426 if (terminal == "konsole")
00427 terminal += " -caption=%c %i %m";
00428 terminal += ' ';
00429 terminal += _service.terminalOptions();
00430 if( !mx1.expandMacrosShellQuote( terminal ) ) {
00431 kWarning() << "KRun: syntax error in command" << terminal << ", service" << _service.name();
00432 return QStringList();
00433 }
00434 mx2.expandMacrosShellQuote( terminal );
00435 result = KShell::splitArgs( terminal );
00436 result << "-e";
00437 }
00438
00439 KShell::Errors err;
00440 QStringList execlist = KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
00441 if (err == KShell::NoError && !execlist.isEmpty()) {
00442
00443
00444 const QString exePath = KStandardDirs::findExe(execlist[0]);
00445 if (!exePath.isEmpty())
00446 execlist[0] = exePath;
00447 }
00448 if (_service.substituteUid()) {
00449 if (_service.terminal())
00450 result << "su";
00451 else
00452 result << KStandardDirs::findExe("kdesu") << "-u";
00453 result << _service.username() << "-c";
00454 if (err == KShell::FoundMeta)
00455 exec = "/bin/sh -c " + KShell::quoteArg(exec);
00456 else
00457 exec = KShell::joinArgs(execlist);
00458 result << exec;
00459 } else {
00460 if (err == KShell::FoundMeta)
00461 result << "/bin/sh" << "-c" << exec;
00462 else
00463 result += execlist;
00464 }
00465
00466 return result;
00467 }
00468
00469
00470 QString KRun::binaryName( const QString & execLine, bool removePath )
00471 {
00472
00473 const QStringList args = KShell::splitArgs( execLine );
00474 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it)
00475 if (!(*it).contains('='))
00476
00477 return removePath ? (*it).mid((*it).lastIndexOf('/') + 1) : *it;
00478 return QString();
00479 }
00480
00481 static bool runCommandInternal( KProcess* proc, const KService* service, const QString& binName,
00482 const QString &execName, const QString & iconName, QWidget* window, const QByteArray& asn )
00483 {
00484 if( window != NULL )
00485 window = window->topLevelWidget();
00486 if (service && !service->entryPath().isEmpty()
00487 && !KDesktopFile::isAuthorizedDesktopFile( service->entryPath() ))
00488 {
00489 kWarning() << "No authorization to execute " << service->entryPath();
00490 KMessageBox::sorry( window, i18n("You are not authorized to execute this file."));
00491 return false;
00492 }
00493 QString bin = KRun::binaryName( binName, true );
00494 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification
00495 bool silent;
00496 QByteArray wmclass;
00497 KStartupInfoId id;
00498 bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass ));
00499 if( startup_notify )
00500 {
00501 id.initId( asn );
00502 id.setupStartupEnv();
00503 KStartupInfoData data;
00504 data.setHostname();
00505 data.setBin( bin );
00506 if( !execName.isEmpty())
00507 data.setName( execName );
00508 else if( service && !service->name().isEmpty())
00509 data.setName( service->name());
00510 data.setDescription( i18n( "Launching %1" , data.name()));
00511 if( !iconName.isEmpty())
00512 data.setIcon( iconName );
00513 else if( service && !service->icon().isEmpty())
00514 data.setIcon( service->icon());
00515 if( !wmclass.isEmpty())
00516 data.setWMClass( wmclass );
00517 if( silent )
00518 data.setSilent( KStartupInfoData::Yes );
00519 data.setDesktop( KWindowSystem::currentDesktop());
00520 if( window )
00521 data.setLaunchedBy( window->winId());
00522 KStartupInfo::sendStartup( id, data );
00523 }
00524 int pid = KProcessRunner::run( proc, binName, id );
00525 if( startup_notify && pid )
00526 {
00527 KStartupInfoData data;
00528 data.addPid( pid );
00529 KStartupInfo::sendChange( id, data );
00530 KStartupInfo::resetStartupEnv();
00531 }
00532 return pid != 0;
00533 #else
00534 Q_UNUSED( execName );
00535 Q_UNUSED( iconName );
00536 return KProcessRunner::run( proc, bin ) != 0;
00537 #endif
00538 }
00539
00540
00541 bool KRun::checkStartupNotify( const QString& , const KService* service, bool* silent_arg, QByteArray* wmclass_arg )
00542 {
00543 bool silent = false;
00544 QByteArray wmclass;
00545 if( service && service->property( "StartupNotify" ).isValid())
00546 {
00547 silent = !service->property( "StartupNotify" ).toBool();
00548 wmclass = service->property( "StartupWMClass" ).toString().toLatin1();
00549 }
00550 else if( service && service->property( "X-KDE-StartupNotify" ).isValid())
00551 {
00552 silent = !service->property( "X-KDE-StartupNotify" ).toBool();
00553 wmclass = service->property( "X-KDE-WMClass" ).toString().toLatin1();
00554 }
00555 else
00556 {
00557 if( service )
00558 {
00559 if( service->isApplication() )
00560 wmclass = "0";
00561 else
00562 return false;
00563 }
00564 else
00565 {
00566 #if 0
00567
00568
00569
00570 wmclass = "0";
00571 silent = true;
00572 #else // That unfortunately doesn't work, when the launched non-compliant application
00573
00574 return false;
00575 #endif
00576 }
00577 }
00578 if( silent_arg != NULL )
00579 *silent_arg = silent;
00580 if( wmclass_arg != NULL )
00581 *wmclass_arg = wmclass;
00582 return true;
00583 }
00584
00585 static bool runTempService( const KService& _service, const KUrl::List& _urls, QWidget* window,
00586 bool tempFiles, const QString& suggestedFileName, const QByteArray& asn )
00587 {
00588 if (!_urls.isEmpty()) {
00589 kDebug(7010) << "runTempService: first url " << _urls.first().url();
00590 }
00591
00592 QStringList args;
00593 if ((_urls.count() > 1) && !_service.allowMultipleFiles())
00594 {
00595
00596
00597
00598
00599
00600 KUrl::List::ConstIterator it = _urls.begin();
00601 while(++it != _urls.end())
00602 {
00603 KUrl::List singleUrl;
00604 singleUrl.append(*it);
00605 runTempService( _service, singleUrl, window, tempFiles, suggestedFileName, "" );
00606 }
00607 KUrl::List singleUrl;
00608 singleUrl.append(_urls.first());
00609 args = KRun::processDesktopExec( _service, singleUrl, tempFiles, suggestedFileName );
00610 }
00611 else
00612 {
00613 args = KRun::processDesktopExec(_service, _urls, tempFiles, suggestedFileName );
00614 }
00615 if (args.isEmpty()) {
00616 KMessageBox::sorry(window, i18n("Error processing Exec field in %1", _service.entryPath()) );
00617 return false;
00618 }
00619 kDebug(7010) << "runTempService: KProcess args=" << args;
00620
00621 KProcess * proc = new KProcess;
00622 *proc << args;
00623
00624 if (!_service.path().isEmpty())
00625 proc->setWorkingDirectory(_service.path());
00626
00627 return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ),
00628 _service.name(), _service.icon(), window, asn );
00629 }
00630
00631
00632 static KUrl::List resolveURLs( const KUrl::List& _urls, const KService& _service )
00633 {
00634
00635
00636 QStringList supportedProtocols = _service.property("X-KDE-Protocols").toStringList();
00637 KRunMX1 mx1( _service );
00638 QString exec = _service.exec();
00639 if ( mx1.expandMacrosShellQuote( exec ) && !mx1.hasUrls ) {
00640 Q_ASSERT( supportedProtocols.isEmpty() );
00641 } else {
00642 if ( supportedProtocols.isEmpty() )
00643 {
00644
00645 QStringList categories = _service.property("Categories").toStringList();
00646 if ( categories.contains("KDE") )
00647 supportedProtocols.append( "KIO" );
00648 else {
00649 supportedProtocols.append( "http");
00650 supportedProtocols.append( "ftp");
00651 }
00652 }
00653 }
00654 kDebug(7010) << "supportedProtocols:" << supportedProtocols;
00655
00656 KUrl::List urls( _urls );
00657 if ( !supportedProtocols.contains( "KIO" ) ) {
00658 for( KUrl::List::Iterator it = urls.begin(); it != urls.end(); ++it ) {
00659 const KUrl url = *it;
00660 bool supported = url.isLocalFile() || supportedProtocols.contains( url.protocol().toLower() );
00661 kDebug(7010) << "Looking at url=" << url << " supported=" << supported;
00662 if ( !supported && KProtocolInfo::protocolClass(url.protocol()) == ":local" )
00663 {
00664
00665 KUrl localURL = KIO::NetAccess::mostLocalUrl( url, 0 );
00666 if ( localURL != url ) {
00667 *it = localURL;
00668 kDebug(7010) << "Changed to " << localURL;
00669 }
00670 }
00671 }
00672 }
00673 return urls;
00674 }
00675
00676 bool KRun::run( const KService& _service, const KUrl::List& _urls, QWidget* window,
00677 bool tempFiles, const QString& suggestedFileName, const QByteArray& asn )
00678 {
00679 if (!_service.entryPath().isEmpty() &&
00680 !KDesktopFile::isAuthorizedDesktopFile( _service.entryPath()))
00681 {
00682 kWarning() << "No authorization to execute " << _service.entryPath();
00683 KMessageBox::sorry( window, i18n("You are not authorized to execute this service.") );
00684 return false;
00685 }
00686
00687 if ( !tempFiles )
00688 {
00689
00690 KUrl::List::ConstIterator it = _urls.begin();
00691 for(; it != _urls.end(); ++it) {
00692
00693 KRecentDocument::add( *it, _service.desktopEntryName() );
00694 }
00695 }
00696
00697 if ( tempFiles || _service.entryPath().isEmpty() || !suggestedFileName.isEmpty() )
00698 {
00699 return runTempService( _service, _urls, window, tempFiles, suggestedFileName, asn );
00700 }
00701
00702 kDebug(7010) << "KRun::run " << _service.entryPath();
00703
00704 if (!_urls.isEmpty()) {
00705 kDebug(7010) << "First url " << _urls.first().url();
00706 }
00707
00708
00709 const KUrl::List urls = resolveURLs( _urls, _service );
00710
00711 QString error;
00712 int pid = 0;
00713
00714 QByteArray myasn = asn;
00715
00716 if( window != NULL )
00717 {
00718 if( myasn.isEmpty())
00719 myasn = KStartupInfo::createNewStartupId();
00720 if( myasn != "0" )
00721 {
00722 KStartupInfoId id;
00723 id.initId( myasn );
00724 KStartupInfoData data;
00725 data.setLaunchedBy( window->winId());
00726 KStartupInfo::sendChange( id, data );
00727 }
00728 }
00729
00730 int i = KToolInvocation::startServiceByDesktopPath(
00731 _service.entryPath(), urls.toStringList(), &error, 0L, &pid, myasn
00732 );
00733
00734 if (i != 0)
00735 {
00736 kDebug(7010) << error;
00737 KMessageBox::sorry( window, error );
00738 return false;
00739 }
00740
00741 kDebug(7010) << "startServiceByDesktopPath worked fine";
00742 return true;
00743 }
00744
00745
00746 bool KRun::run( const QString& _exec, const KUrl::List& _urls, QWidget* window, const QString& _name,
00747 const QString& _icon, const QByteArray& asn )
00748 {
00749 KService::Ptr service(new KService(_name, _exec, _icon));
00750
00751 return run( *service, _urls, window, false, QString(), asn );
00752 }
00753
00754 bool KRun::runCommand( const QString &cmd, QWidget* window )
00755 {
00756 QString bin = binaryName( cmd, true );
00757 return KRun::runCommand( cmd, bin, bin, window, "" );
00758 }
00759
00760 bool KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName, QWidget* window, const QByteArray& asn )
00761 {
00762 kDebug(7010) << "runCommand " << cmd << "," << execName;
00763 KProcess * proc = new KProcess;
00764 proc->setShellCommand( cmd );
00765 QString bin = binaryName( execName, true );
00766 KService::Ptr service = KService::serviceByDesktopName( bin );
00767 return runCommandInternal( proc, service.data(), bin, execName, iconName, window, asn );
00768 }
00769
00770 KRun::KRun( const KUrl& url, QWidget* window, mode_t mode, bool isLocalFile,
00771 bool showProgressInfo, const QByteArray& asn )
00772 : d(new KRunPrivate(this))
00773 {
00774 d->m_timer.setObjectName( "KRun::timer" );
00775 d->m_timer.setSingleShot( true );
00776 d->init (url, window, mode, isLocalFile, showProgressInfo, asn );
00777 }
00778
00779 void KRun::KRunPrivate::init ( const KUrl& url, QWidget* window, mode_t mode, bool isLocalFile,
00780 bool showProgressInfo, const QByteArray& asn )
00781 {
00782 m_bFault = false;
00783 m_bAutoDelete = true;
00784 m_bProgressInfo = showProgressInfo;
00785 m_bFinished = false;
00786 m_job = 0L;
00787 m_strURL = url;
00788 m_bScanFile = false;
00789 m_bIsDirectory = false;
00790 m_bIsLocalFile = isLocalFile;
00791 m_mode = mode;
00792 m_runExecutables = true;
00793 m_window = window;
00794 m_asn = asn;
00795 q->setEnableExternalBrowser(true);
00796
00797
00798
00799
00800 m_bInit = true;
00801 q->connect( &m_timer, SIGNAL( timeout() ), q, SLOT( slotTimeout() ) );
00802 startTimer();
00803
00804
00805 KGlobal::ref();
00806 }
00807
00808 void KRun::init()
00809 {
00810 kDebug(7010) << "INIT called";
00811 if ( !d->m_strURL.isValid() )
00812 {
00813
00814 d->m_showingDialog = true;
00815 KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1", d->m_strURL.url() ) );
00816 d->m_showingDialog = false;
00817 d->m_bFault = true;
00818 d->m_bFinished = true;
00819 d->startTimer();
00820 return;
00821 }
00822 if ( !KAuthorized::authorizeUrlAction( "open", KUrl(), d->m_strURL) )
00823 {
00824 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->m_strURL.prettyUrl());
00825 d->m_showingDialog = true;
00826 KMessageBoxWrapper::error( d->m_window, msg );
00827 d->m_showingDialog = false;
00828 d->m_bFault = true;
00829 d->m_bFinished = true;
00830 d->startTimer();
00831 return;
00832 }
00833
00834 if ( !d->m_bIsLocalFile && d->m_strURL.isLocalFile() )
00835 d->m_bIsLocalFile = true;
00836
00837 QString exec;
00838 if (d->m_strURL.protocol().startsWith("http"))
00839 {
00840 exec = d->m_externalBrowser;
00841 }
00842
00843 if ( d->m_bIsLocalFile )
00844 {
00845 if ( d->m_mode == 0 )
00846 {
00847 KDE_struct_stat buff;
00848 if ( KDE_stat( QFile::encodeName(d->m_strURL.path()), &buff ) == -1 )
00849 {
00850 d->m_showingDialog = true;
00851 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()) ) );
00852 d->m_showingDialog = false;
00853 d->m_bFault = true;
00854 d->m_bFinished = true;
00855 d->startTimer();
00856 return;
00857 }
00858 d->m_mode = buff.st_mode;
00859 }
00860
00861 KMimeType::Ptr mime = KMimeType::findByUrl( d->m_strURL, d->m_mode, d->m_bIsLocalFile );
00862 assert( mime );
00863 kDebug(7010) << "MIME TYPE is " << mime->name();
00864 mimeTypeDetermined( mime->name() );
00865 return;
00866 }
00867 else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( d->m_strURL ) ) {
00868 kDebug(7010) << "Helper protocol";
00869
00870 bool ok = false;
00871 KUrl::List urls;
00872 urls.append( d->m_strURL );
00873 if (exec.isEmpty())
00874 {
00875 exec = KProtocolInfo::exec( d->m_strURL.protocol() );
00876 if (exec.isEmpty())
00877 {
00878 mimeTypeDetermined(KProtocolManager::defaultMimetype(d->m_strURL));
00879 return;
00880 }
00881 run( exec, urls, d->m_window, false, QString(), d->m_asn );
00882 ok = true;
00883 }
00884 else if (exec.startsWith('!'))
00885 {
00886 exec = exec.mid(1);
00887 exec += " %u";
00888 run( exec, urls, d->m_window, false, QString(), d->m_asn );
00889 ok = true;
00890 }
00891 else
00892 {
00893 KService::Ptr service = KService::serviceByStorageId( exec );
00894 if (service)
00895 {
00896 run( *service, urls, d->m_window, false, QString(), d->m_asn );
00897 ok = true;
00898 }
00899 }
00900
00901 if (ok)
00902 {
00903 d->m_bFinished = true;
00904
00905 d->startTimer();
00906 return;
00907 }
00908 }
00909
00910
00911 if ( S_ISDIR( d->m_mode ) )
00912 {
00913 mimeTypeDetermined( "inode/directory" );
00914 return;
00915 }
00916
00917
00918
00919 if ( !KProtocolManager::supportsListing( d->m_strURL ) )
00920 {
00921
00922
00923 scanFile();
00924 return;
00925 }
00926
00927 kDebug(7010) << "Testing directory (stating)";
00928
00929
00930 KIO::JobFlags flags = d->m_bProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
00931 KIO::StatJob *job = KIO::stat( d->m_strURL, KIO::StatJob::SourceSide, 0 , flags );
00932 job->ui()->setWindow (d->m_window);
00933 connect( job, SIGNAL( result( KJob * ) ),
00934 this, SLOT( slotStatResult( KJob * ) ) );
00935 d->m_job = job;
00936 kDebug(7010) << " Job " << job << " is about stating " << d->m_strURL.url();
00937 }
00938
00939 KRun::~KRun()
00940 {
00941
00942 d->m_timer.stop();
00943 killJob();
00944 KGlobal::deref();
00945
00946 delete d;
00947 }
00948
00949 void KRun::scanFile()
00950 {
00951 kDebug(7010) << d->m_strURL;
00952
00953
00954 if ( d->m_strURL.query().isEmpty() )
00955 {
00956 KMimeType::Ptr mime = KMimeType::findByUrl( d->m_strURL );
00957 assert( mime );
00958 if ( mime->name() != "application/octet-stream" || d->m_bIsLocalFile )
00959 {
00960 kDebug(7010) << "Scanfile: MIME TYPE is " << mime->name();
00961 mimeTypeDetermined( mime->name() );
00962 return;
00963 }
00964 }
00965
00966
00967
00968
00969
00970 if ( !KProtocolManager::supportsReading( d->m_strURL ) )
00971 {
00972 kError(7010) << "#### NO SUPPORT FOR READING!";
00973 d->m_bFault = true;
00974 d->m_bFinished = true;
00975 d->startTimer();
00976 return;
00977 }
00978 kDebug(7010) << this << " Scanning file " << d->m_strURL.url();
00979
00980 KIO::JobFlags flags = d->m_bProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
00981 KIO::TransferJob *job = KIO::get( d->m_strURL, KIO::NoReload , flags );
00982 job->ui()->setWindow (d->m_window);
00983 connect(job, SIGNAL( result(KJob *)),
00984 this, SLOT( slotScanFinished(KJob *)));
00985 connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)),
00986 this, SLOT( slotScanMimeType(KIO::Job *, const QString &)));
00987 d->m_job = job;
00988 kDebug(7010) << " Job " << job << " is about getting from " << d->m_strURL.url();
00989 }
00990
00991 void KRun::slotTimeout()
00992 {
00993 kDebug(7010) << this << " slotTimeout called";
00994 if ( d->m_bInit )
00995 {
00996 d->m_bInit = false;
00997 init();
00998 return;
00999 }
01000
01001 if ( d->m_bFault ) {
01002 emit error();
01003 }
01004 if ( d->m_bFinished ) {
01005 emit finished();
01006 }
01007 else
01008 {
01009 if ( d->m_bScanFile )
01010 {
01011 d->m_bScanFile = false;
01012 scanFile();
01013 return;
01014 }
01015 else if ( d->m_bIsDirectory )
01016 {
01017 d->m_bIsDirectory = false;
01018 mimeTypeDetermined( "inode/directory" );
01019 return;
01020 }
01021 }
01022
01023 if ( d->m_bAutoDelete )
01024 {
01025 deleteLater();
01026 return;
01027 }
01028 }
01029
01030 void KRun::slotStatResult( KJob * job )
01031 {
01032 d->m_job = 0L;
01033 if (job->error())
01034 {
01035 d->m_showingDialog = true;
01036 kError(7010) << this << "ERROR" << job->error() << ' ' << job->errorString();
01037 job->uiDelegate()->showErrorMessage();
01038
01039 d->m_showingDialog = false;
01040
01041 d->m_bFault = true;
01042 d->m_bFinished = true;
01043
01044
01045 d->startTimer();
01046
01047 } else {
01048
01049 kDebug(7010) << "Finished";
01050 if(!qobject_cast<KIO::StatJob*>(job))
01051 kFatal() << "job is a " << typeid(*job).name() << " should be a StatJob";
01052
01053 const KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult();
01054 const mode_t mode = entry.numberValue( KIO::UDSEntry::UDS_FILE_TYPE );
01055 if ( S_ISDIR( mode ) )
01056 d->m_bIsDirectory = true;
01057 else
01058 d->m_bScanFile = true;
01059
01060 d->m_localPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
01061
01062
01063 const QString knownMimeType = entry.stringValue( KIO::UDSEntry::UDS_MIME_TYPE ) ;
01064
01065 if ( !knownMimeType.isEmpty() )
01066 {
01067 mimeTypeDetermined( knownMimeType );
01068 d->m_bFinished = true;
01069 }
01070
01071
01072 assert ( d->m_bScanFile || d->m_bIsDirectory );
01073
01074
01075
01076
01077 d->startTimer();
01078 }
01079 }
01080
01081 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype )
01082 {
01083 if ( mimetype.isEmpty() )
01084 kWarning(7010) << "MimetypeJob didn't find a mimetype! Probably a kioslave bug.";
01085 mimeTypeDetermined( mimetype );
01086 d->m_job = 0;
01087 }
01088
01089 void KRun::slotScanFinished( KJob *job )
01090 {
01091 d->m_job = 0;
01092 if (job->error())
01093 {
01094 d->m_showingDialog = true;
01095 kError(7010) << this << "ERROR (stat):" << job->error() << ' ' << job->errorString();
01096 job->uiDelegate()->showErrorMessage();
01097
01098 d->m_showingDialog = false;
01099
01100 d->m_bFault = true;
01101 d->m_bFinished = true;
01102
01103
01104 d->startTimer();
01105 }
01106 }
01107
01108 void KRun::mimeTypeDetermined(const QString& mimeType)
01109 {
01110
01111
01112 Q_ASSERT(!d->m_showingDialog);
01113 d->m_showingDialog = true;
01114
01115 foundMimeType(mimeType);
01116
01117 d->m_showingDialog = false;
01118 }
01119
01120 void KRun::foundMimeType( const QString& type )
01121 {
01122 kDebug(7010) << "Resulting mime type is " << type;
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176 KIO::TransferJob *job = qobject_cast<KIO::TransferJob *>( d->m_job );
01177 if ( job )
01178 {
01179 job->putOnHold();
01180 KIO::Scheduler::publishSlaveOnHold();
01181 d->m_job = 0;
01182 }
01183
01184 Q_ASSERT( !d->m_bFinished );
01185
01186 KMimeType::Ptr mime = KMimeType::mimeType(type, KMimeType::ResolveAliases);
01187 if ( !mime )
01188 kWarning(7010) << "Unknown mimetype " << type;
01189
01190
01191 if ( !d->m_preferredService.isEmpty() ) {
01192 kDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService;
01193 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService );
01194 if ( serv && serv->hasMimeType( mime.data() ) )
01195 {
01196 KUrl::List lst;
01197 lst.append( d->m_strURL );
01198 d->m_bFinished = KRun::run( *serv, lst, d->m_window, false, QString(), d->m_asn );
01203 }
01204 }
01205
01206
01207 if ( mime && mime->is( "application/x-desktop" ) && !d->m_localPath.isEmpty() )
01208 {
01209 d->m_strURL = KUrl();
01210 d->m_strURL.setPath( d->m_localPath );
01211 }
01212
01213 if (!d->m_bFinished && KRun::runUrl( d->m_strURL, type, d->m_window, false , d->m_runExecutables, d->m_suggestedFileName, d->m_asn )){
01214 d->m_bFinished = true;
01215 }
01216 else{
01217 d->m_bFinished = true;
01218 d->m_bFault = true;
01219 }
01220
01221 d->startTimer();
01222 }
01223
01224 void KRun::killJob()
01225 {
01226 if ( d->m_job )
01227 {
01228 kDebug(7010) << this << "m_job=" << d->m_job;
01229 d->m_job->kill();
01230 d->m_job = 0L;
01231 }
01232 }
01233
01234 void KRun::abort()
01235 {
01236 kDebug(7010) << this << "m_showingDialog=" << d->m_showingDialog;
01237 killJob();
01238
01239
01240 if ( d->m_showingDialog )
01241 return;
01242 d->m_bFault = true;
01243 d->m_bFinished = true;
01244 d->m_bInit = false;
01245 d->m_bScanFile = false;
01246
01247
01248 d->startTimer();
01249 }
01250
01251 bool KRun::hasError() const
01252 {
01253 return d->m_bFault;
01254 }
01255
01256 bool KRun::hasFinished() const
01257 {
01258 return d->m_bFinished;
01259 }
01260
01261 bool KRun::autoDelete() const
01262 {
01263 return d->m_bAutoDelete;
01264 }
01265
01266 void KRun::setAutoDelete(bool b)
01267 {
01268 d->m_bAutoDelete = b;
01269 }
01270
01271 void KRun::setEnableExternalBrowser(bool b)
01272 {
01273 if (b)
01274 d->m_externalBrowser = KConfigGroup(KGlobal::config(), "General").readEntry("BrowserApplication");
01275 else
01276 d->m_externalBrowser.clear();
01277 }
01278
01279 void KRun::setPreferredService( const QString& desktopEntryName )
01280 {
01281 d->m_preferredService = desktopEntryName;
01282 }
01283
01284 void KRun::setRunExecutables(bool b)
01285 {
01286 d->m_runExecutables = b;
01287 }
01288
01289 void KRun::setSuggestedFileName( const QString& fileName )
01290 {
01291 d->m_suggestedFileName = fileName;
01292 }
01293
01294 QString KRun::suggestedFileName() const
01295 {
01296 return d->m_suggestedFileName;
01297 }
01298
01299 bool KRun::isExecutable( const QString& serviceType )
01300 {
01301 return ( serviceType == "application/x-desktop" ||
01302 serviceType == "application/x-executable" ||
01303 serviceType == "application/x-ms-dos-executable" ||
01304 serviceType == "application/x-shellscript" );
01305 }
01306
01307 void KRun::setUrl( const KUrl &url )
01308 {
01309 d->m_strURL = url;
01310 }
01311
01312 KUrl KRun::url() const
01313 {
01314 return d->m_strURL;
01315 }
01316
01317 void KRun::setError( bool error )
01318 {
01319 d->m_bFault = error;
01320 }
01321
01322 void KRun::setProgressInfo( bool progressInfo )
01323 {
01324 d->m_bProgressInfo = progressInfo;
01325 }
01326
01327 bool KRun::progressInfo() const
01328 {
01329 return d->m_bProgressInfo;
01330 }
01331
01332 void KRun::setFinished( bool finished )
01333 {
01334 d->m_bFinished = finished;
01335
01336 }
01337
01338 void KRun::setJob( KIO::Job *job )
01339 {
01340 d->m_job = job;
01341 }
01342
01343 KIO::Job* KRun::job()
01344 {
01345 return d->m_job;
01346 }
01347
01348 QTimer& KRun::timer()
01349 {
01350 return d->m_timer;
01351 }
01352
01353 void KRun::setDoScanFile( bool scanFile )
01354 {
01355 d->m_bScanFile = scanFile;
01356 }
01357
01358 bool KRun::doScanFile() const
01359 {
01360 return d->m_bScanFile;
01361 }
01362
01363 void KRun::setIsDirecory( bool isDirectory )
01364 {
01365 d->m_bIsDirectory = isDirectory;
01366 }
01367
01368 bool KRun::isDirectory() const
01369 {
01370 return d->m_bIsDirectory;
01371 }
01372
01373 void KRun::setInitializeNextAction( bool initialize )
01374 {
01375 d->m_bInit = initialize;
01376 }
01377
01378 bool KRun::initializeNextAction() const
01379 {
01380 return d->m_bInit;
01381 }
01382
01383 void KRun::setIsLocalFile( bool isLocalFile )
01384 {
01385 d->m_bIsLocalFile = isLocalFile;
01386 }
01387
01388 bool KRun::isLocalFile() const
01389 {
01390 return d->m_bIsLocalFile;
01391 }
01392
01393 void KRun::setMode( mode_t mode )
01394 {
01395 d->m_mode = mode;
01396 }
01397
01398 mode_t KRun::mode() const
01399 {
01400 return d->m_mode;
01401 }
01402
01403
01404
01405 #ifndef Q_WS_X11
01406 int KProcessRunner::run(KProcess * p, const QString & binName)
01407 {
01408 return (new KProcessRunner(p, binName))->pid();
01409 }
01410 #else
01411 int KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id)
01412 {
01413 return (new KProcessRunner(p, binName, id))->pid();
01414 }
01415 #endif
01416
01417 #ifndef Q_WS_X11
01418 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName)
01419 #else
01420 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& _id) :
01421 id(_id)
01422 #endif
01423 {
01424 process = p;
01425 binName = _binName;
01426 connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
01427 this, SLOT(slotProcessExited(int, QProcess::ExitStatus)));
01428
01429 process->start();
01430 if (!process->waitForStarted()) {
01431 slotProcessExited(127, QProcess::CrashExit);
01432 }
01433 }
01434
01435 KProcessRunner::~KProcessRunner()
01436 {
01437 delete process;
01438 }
01439
01440 int KProcessRunner::pid() const
01441 {
01442 return process ? process->pid() : 0;
01443 }
01444
01445 void
01446 KProcessRunner::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus)
01447 {
01448 kDebug(7010) << "slotProcessExited " << binName;
01449 kDebug(7010) << "normalExit " << (exitStatus == QProcess::NormalExit);
01450 kDebug(7010) << "exitCode " << exitCode;
01451 bool showErr = exitStatus == QProcess::NormalExit
01452 && (exitCode == 127 || exitCode == 1);
01453 if (!binName.isEmpty() && (showErr || pid() == 0 )) {
01454
01455
01456
01457
01458 if (!QFile(binName).exists() && KStandardDirs::findExe(binName).isEmpty()) {
01459 KGlobal::ref();
01460 KMessageBox::sorry(0L, i18n("Could not find the program '%1'", binName));
01461 KGlobal::deref();
01462 }
01463 }
01464 #ifdef Q_WS_X11
01465 if (!id.none()) {
01466 KStartupInfoData data;
01467 data.addPid(pid());
01468 data.setHostname();
01469 KStartupInfo::sendFinish(id, data);
01470 }
01471 #endif
01472 deleteLater();
01473 }
01474
01475 #include "krun.moc"
01476 #include "krun_p.moc"