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