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( 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
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));
00126
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
00167
00168
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 );
00202 }
00203 return false;
00204 }
00205
00206 void KRun::shellQuote( QString &_str )
00207 {
00208
00209 if (_str.isEmpty())
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
00245 kWarning() << "-miniicon isn't supported anymore (service"
00246 << service.name() << ')';
00247 break;
00248 case 'u':
00249 case 'U':
00250 hasUrls = true;
00251
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
00261 default:
00262 return -2;
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;
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 ) ) {
00357 kWarning() << "KRun: syntax error in command" << _service.exec() << ", service" << _service.name();
00358 return QStringList();
00359 }
00360
00361
00362
00363
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
00378 if( !mx1.hasUrls ) {
00379 for( KUrl::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it )
00380 if ( !(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it) ) {
00381
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
00401
00402
00403 if( !mx1.hasSpec ) {
00404 exec += " %f";
00405 mx2.ignFile = true;
00406 }
00407
00408 mx2.expandMacrosShellQuote( exec );
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
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 );
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()) {
00450
00451
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
00478 QString KRun::binaryName( const QString & execLine, bool removePath )
00479 {
00480
00481 const QStringList args = KShell::splitArgs( execLine );
00482 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it)
00483 if (!(*it).contains('='))
00484
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
00549 bool KRun::checkStartupNotify( const QString& , 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
00564 {
00565 if( service )
00566 {
00567 if( service->isApplication() )
00568 wmclass = "0";
00569 else
00570 return false;
00571 }
00572 else
00573 {
00574 #if 0
00575
00576
00577
00578 wmclass = "0";
00579 silent = true;
00580 #else // That unfortunately doesn't work, when the launched non-compliant application
00581
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
00604
00605
00606
00607
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
00640 static KUrl::List resolveURLs( const KUrl::List& _urls, const KService& _service )
00641 {
00642
00643
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() );
00649 } else {
00650 if ( supportedProtocols.isEmpty() )
00651 {
00652
00653 QStringList categories = _service.property("Categories").toStringList();
00654 if ( categories.contains("KDE") )
00655 supportedProtocols.append( "KIO" );
00656 else {
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
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
00698 KUrl::List::ConstIterator it = _urls.begin();
00699 for(; it != _urls.end(); ++it) {
00700
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
00717 const KUrl::List urls = resolveURLs( _urls, _service );
00718
00719 QString error;
00720 int pid = 0;
00721
00722 QByteArray myasn = asn;
00723
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
00806
00807
00808 m_bInit = true;
00809 q->connect( &m_timer, SIGNAL( timeout() ), q, SLOT( slotTimeout() ) );
00810 startTimer();
00811
00812
00813 KGlobal::ref();
00814 }
00815
00816 void KRun::init()
00817 {
00818 kDebug(7010) << "INIT called";
00819 if ( !d->m_strURL.isValid() )
00820 {
00821
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);
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
00913 d->startTimer();
00914 return;
00915 }
00916 }
00917
00918
00919 if ( S_ISDIR( d->m_mode ) )
00920 {
00921 mimeTypeDetermined( "inode/directory" );
00922 return;
00923 }
00924
00925
00926
00927 if ( !KProtocolManager::supportsListing( d->m_strURL ) )
00928 {
00929
00930
00931 scanFile();
00932 return;
00933 }
00934
00935 kDebug(7010) << "Testing directory (stating)";
00936
00937
00938 KIO::JobFlags flags = d->m_bProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
00939 KIO::StatJob *job = KIO::stat( d->m_strURL, KIO::StatJob::SourceSide, 0 , 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
00950 d->m_timer.stop();
00951 killJob();
00952 KGlobal::deref();
00953
00954 delete d;
00955 }
00956
00957 void KRun::scanFile()
00958 {
00959 kDebug(7010) << d->m_strURL;
00960
00961
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
00975
00976
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 , 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
01047 d->m_showingDialog = false;
01048
01049 d->m_bFault = true;
01050 d->m_bFinished = true;
01051
01052
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;
01065 else
01066 d->m_bScanFile = true;
01067
01068 d->m_localPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
01069
01070
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
01080 assert ( d->m_bScanFile || d->m_bIsDirectory );
01081
01082
01083
01084
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
01106 d->m_showingDialog = false;
01107
01108 d->m_bFault = true;
01109 d->m_bFinished = true;
01110
01111
01112 d->startTimer();
01113 }
01114 }
01115
01116 void KRun::mimeTypeDetermined(const QString& mimeType)
01117 {
01118
01119
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
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
01177
01178
01179
01180
01181
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
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
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 , 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
01247
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
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
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
01463
01464
01465
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());
01476 data.setHostname();
01477 KStartupInfo::sendFinish(id, data);
01478 }
01479 #endif
01480 deleteLater();
01481 }
01482
01483 #include "krun.moc"
01484 #include "krun_p.moc"