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