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 ) {
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 if (!_service.desktopEntryPath().isEmpty() &&
00697 !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath()))
00698 {
00699 kdWarning() << "No authorization to execute " << _service.desktopEntryPath() << endl;
00700 KMessageBox::sorry(0, i18n("You are not authorized to execute this service."));
00701 return 0;
00702 }
00703
00704 if ( !tempFiles )
00705 {
00706
00707 KURL::List::ConstIterator it = _urls.begin();
00708 for(; it != _urls.end(); ++it) {
00709
00710 KRecentDocument::add( *it, _service.desktopEntryName() );
00711 }
00712 }
00713
00714 if ( tempFiles || _service.desktopEntryPath().isEmpty())
00715 {
00716 return runTempService(_service, _urls, tempFiles);
00717 }
00718
00719 kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl;
00720
00721 if (!_urls.isEmpty()) {
00722 kdDebug(7010) << "First url " << _urls.first().url() << endl;
00723 }
00724
00725
00726 const KURL::List urls = resolveURLs( _urls, _service );
00727
00728 QString error;
00729 int pid = 0;
00730
00731 int i = KApplication::startServiceByDesktopPath(
00732 _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid
00733 );
00734
00735 if (i != 0)
00736 {
00737 kdDebug(7010) << error << endl;
00738 KMessageBox::sorry( 0L, error );
00739 return 0;
00740 }
00741
00742 kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl;
00743 return (pid_t) pid;
00744 }
00745
00746
00747 pid_t KRun::run( const QString& _exec, const KURL::List& _urls, const QString& _name,
00748 const QString& _icon, const QString&, const QString&)
00749 {
00750 KService::Ptr service = new KService(_name, _exec, _icon);
00751
00752 return run(*service, _urls);
00753 }
00754
00755 pid_t KRun::runCommand( QString cmd )
00756 {
00757 return KRun::runCommand( cmd, QString::null, QString::null );
00758 }
00759
00760 pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName )
00761 {
00762 kdDebug(7010) << "runCommand " << cmd << "," << execName << endl;
00763 KProcess * proc = new KProcess;
00764 proc->setUseShell(true);
00765 *proc << cmd;
00766 KService::Ptr service = KService::serviceByDesktopName( binaryName( execName, true ) );
00767 return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName );
00768 }
00769
00770 KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo )
00771 :m_timer(0,"KRun::timer")
00772 {
00773 init (url, 0, mode, isLocalFile, showProgressInfo);
00774 }
00775
00776 KRun::KRun( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile,
00777 bool showProgressInfo )
00778 :m_timer(0,"KRun::timer")
00779 {
00780 init (url, window, mode, isLocalFile, showProgressInfo);
00781 }
00782
00783 void KRun::init ( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile,
00784 bool showProgressInfo )
00785 {
00786 m_bFault = false;
00787 m_bAutoDelete = true;
00788 m_bProgressInfo = showProgressInfo;
00789 m_bFinished = false;
00790 m_job = 0L;
00791 m_strURL = url;
00792 m_bScanFile = false;
00793 m_bIsDirectory = false;
00794 m_bIsLocalFile = isLocalFile;
00795 m_mode = mode;
00796 d = new KRunPrivate;
00797 d->m_runExecutables = true;
00798 d->m_window = window;
00799 setEnableExternalBrowser(true);
00800
00801
00802
00803
00804 m_bInit = true;
00805 connect( &m_timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) );
00806 m_timer.start( 0, true );
00807 kdDebug(7010) << " new KRun " << this << " " << url.prettyURL() << " timer=" << &m_timer << endl;
00808
00809 kapp->ref();
00810 }
00811
00812 void KRun::init()
00813 {
00814 kdDebug(7010) << "INIT called" << endl;
00815 if ( !m_strURL.isValid() )
00816 {
00817 d->m_showingError = true;
00818 KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) );
00819 d->m_showingError = false;
00820 m_bFault = true;
00821 m_bFinished = true;
00822 m_timer.start( 0, true );
00823 return;
00824 }
00825 if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL))
00826 {
00827 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, m_strURL.prettyURL());
00828 d->m_showingError = true;
00829 KMessageBoxWrapper::error( d->m_window, msg );
00830 d->m_showingError = false;
00831 m_bFault = true;
00832 m_bFinished = true;
00833 m_timer.start( 0, true );
00834 return;
00835 }
00836
00837 if ( !m_bIsLocalFile && m_strURL.isLocalFile() )
00838 m_bIsLocalFile = true;
00839
00840 QString exec;
00841 if (m_strURL.protocol().startsWith("http"))
00842 {
00843 exec = d->m_externalBrowser;
00844 }
00845
00846 if ( m_bIsLocalFile )
00847 {
00848 if ( m_mode == 0 )
00849 {
00850 KDE_struct_stat buff;
00851 if ( KDE_stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 )
00852 {
00853 d->m_showingError = true;
00854 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() ) );
00855 d->m_showingError = false;
00856 m_bFault = true;
00857 m_bFinished = true;
00858 m_timer.start( 0, true );
00859 return;
00860 }
00861 m_mode = buff.st_mode;
00862 }
00863
00864 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile );
00865 assert( mime != 0L );
00866 kdDebug(7010) << "MIME TYPE is " << mime->name() << endl;
00867 foundMimeType( mime->name() );
00868 return;
00869 }
00870 else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( m_strURL ) ) {
00871 kdDebug(7010) << "Helper protocol" << endl;
00872
00873 bool ok = false;
00874 KURL::List urls;
00875 urls.append( m_strURL );
00876 if (exec.isEmpty())
00877 {
00878 exec = KProtocolInfo::exec( m_strURL.protocol() );
00879 if (exec.isEmpty())
00880 {
00881 foundMimeType(KProtocolInfo::defaultMimetype(m_strURL));
00882 return;
00883 }
00884 run( exec, urls );
00885 ok = true;
00886 }
00887 else if (exec.startsWith("!"))
00888 {
00889 exec = exec.mid(1);
00890 exec += " %u";
00891 run( exec, urls );
00892 ok = true;
00893 }
00894 else
00895 {
00896 KService::Ptr service = KService::serviceByStorageId( exec );
00897 if (service)
00898 {
00899 run( *service, urls );
00900 ok = true;
00901 }
00902 }
00903
00904 if (ok)
00905 {
00906 m_bFinished = true;
00907
00908 m_timer.start( 0, true );
00909 return;
00910 }
00911 }
00912
00913
00914 if ( S_ISDIR( m_mode ) )
00915 {
00916 foundMimeType( "inode/directory" );
00917 return;
00918 }
00919
00920
00921
00922 if ( !KProtocolInfo::supportsListing( m_strURL ) )
00923 {
00924
00925
00926 scanFile();
00927 return;
00928 }
00929
00930 kdDebug(7010) << "Testing directory (stating)" << endl;
00931
00932
00933 KIO::StatJob *job = KIO::stat( m_strURL, true, 0 , m_bProgressInfo );
00934 job->setWindow (d->m_window);
00935 connect( job, SIGNAL( result( KIO::Job * ) ),
00936 this, SLOT( slotStatResult( KIO::Job * ) ) );
00937 m_job = job;
00938 kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl;
00939 }
00940
00941 KRun::~KRun()
00942 {
00943 kdDebug(7010) << "KRun::~KRun() " << this << endl;
00944 m_timer.stop();
00945 killJob();
00946 kapp->deref();
00947 kdDebug(7010) << "KRun::~KRun() done " << this << endl;
00948 delete d;
00949 }
00950
00951 void KRun::scanFile()
00952 {
00953 kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl;
00954
00955
00956 if ( m_strURL.query().isEmpty() )
00957 {
00958 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL );
00959 assert( mime != 0L );
00960 if ( mime->name() != "application/octet-stream" || m_bIsLocalFile )
00961 {
00962 kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl;
00963 foundMimeType( mime->name() );
00964 return;
00965 }
00966 }
00967
00968
00969
00970
00971
00972 if ( !KProtocolInfo::supportsReading( m_strURL ) )
00973 {
00974 kdError(7010) << "#### NO SUPPORT FOR READING!" << endl;
00975 m_bFault = true;
00976 m_bFinished = true;
00977 m_timer.start( 0, true );
00978 return;
00979 }
00980 kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl;
00981
00982 KIO::TransferJob *job = KIO::get( m_strURL, false , m_bProgressInfo );
00983 job->setWindow (d->m_window);
00984 connect(job, SIGNAL( result(KIO::Job *)),
00985 this, SLOT( slotScanFinished(KIO::Job *)));
00986 connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)),
00987 this, SLOT( slotScanMimeType(KIO::Job *, const QString &)));
00988 m_job = job;
00989 kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl;
00990 }
00991
00992 void KRun::slotTimeout()
00993 {
00994 kdDebug(7010) << this << " slotTimeout called" << endl;
00995 if ( m_bInit )
00996 {
00997 m_bInit = false;
00998 init();
00999 return;
01000 }
01001
01002 if ( m_bFault ) {
01003 emit error();
01004 }
01005 if ( m_bFinished ) {
01006 emit finished();
01007 }
01008 else
01009 {
01010 if ( m_bScanFile )
01011 {
01012 m_bScanFile = false;
01013 scanFile();
01014 return;
01015 }
01016 else if ( m_bIsDirectory )
01017 {
01018 m_bIsDirectory = false;
01019 foundMimeType( "inode/directory" );
01020 return;
01021 }
01022 }
01023
01024 if ( m_bAutoDelete )
01025 {
01026 delete this;
01027 return;
01028 }
01029 }
01030
01031 void KRun::slotStatResult( KIO::Job * job )
01032 {
01033 m_job = 0L;
01034 if (job->error())
01035 {
01036 d->m_showingError = true;
01037 kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl;
01038 job->showErrorDialog();
01039
01040 d->m_showingError = false;
01041
01042 m_bFault = true;
01043 m_bFinished = true;
01044
01045
01046 m_timer.start( 0, true );
01047
01048 } else {
01049
01050 kdDebug(7010) << "Finished" << endl;
01051 if(!dynamic_cast<KIO::StatJob*>(job))
01052 kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl;
01053
01054 QString knownMimeType;
01055 KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult();
01056 KIO::UDSEntry::ConstIterator it = entry.begin();
01057 for( ; it != entry.end(); it++ ) {
01058 switch( (*it).m_uds ) {
01059 case KIO::UDS_FILE_TYPE:
01060 if ( S_ISDIR( (mode_t)((*it).m_long) ) )
01061 m_bIsDirectory = true;
01062 else
01063 m_bScanFile = true;
01064 break;
01065 case KIO::UDS_MIME_TYPE:
01066 knownMimeType = (*it).m_str;
01067 break;
01068 case KIO::UDS_LOCAL_PATH:
01069 d->m_localPath = (*it).m_str;
01070 break;
01071 default:
01072 break;
01073 }
01074 }
01075 if ( !knownMimeType.isEmpty() )
01076 {
01077 foundMimeType( knownMimeType );
01078 m_bFinished = true;
01079 }
01080
01081
01082 assert ( m_bScanFile || m_bIsDirectory );
01083
01084
01085
01086
01087 m_timer.start( 0, true );
01088 }
01089 }
01090
01091 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype )
01092 {
01093 if ( mimetype.isEmpty() )
01094 kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug." << endl;
01095 foundMimeType( mimetype );
01096 m_job = 0;
01097 }
01098
01099 void KRun::slotScanFinished( KIO::Job *job )
01100 {
01101 m_job = 0;
01102 if (job->error())
01103 {
01104 d->m_showingError = true;
01105 kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl;
01106 job->showErrorDialog();
01107
01108 d->m_showingError = false;
01109
01110 m_bFault = true;
01111 m_bFinished = true;
01112
01113
01114 m_timer.start( 0, true );
01115 }
01116 }
01117
01118 void KRun::foundMimeType( const QString& type )
01119 {
01120 kdDebug(7010) << "Resulting mime type is " << type << endl;
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174 KIO::TransferJob *job = ::qt_cast<KIO::TransferJob *>( m_job );
01175 if ( job )
01176 {
01177 job->putOnHold();
01178 KIO::Scheduler::publishSlaveOnHold();
01179 m_job = 0;
01180 }
01181
01182 Q_ASSERT( !m_bFinished );
01183
01184
01185 if ( !d->m_preferredService.isEmpty() ) {
01186 kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl;
01187 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService );
01188 if ( serv && serv->hasServiceType( type ) )
01189 {
01190 KURL::List lst;
01191 lst.append( m_strURL );
01192 m_bFinished = KRun::run( *serv, lst );
01197 }
01198 }
01199
01200
01201 if ( type == "application/x-desktop" && !d->m_localPath.isEmpty() )
01202 {
01203 m_strURL = KURL();
01204 m_strURL.setPath( d->m_localPath );
01205 }
01206
01207 if (!m_bFinished && KRun::runURL( m_strURL, type, false, d->m_runExecutables )){
01208 m_bFinished = true;
01209 }
01210 else{
01211 m_bFinished = true;
01212 m_bFault = true;
01213 }
01214
01215 m_timer.start( 0, true );
01216 }
01217
01218 void KRun::killJob()
01219 {
01220 if ( m_job )
01221 {
01222 kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl;
01223 m_job->kill();
01224 m_job = 0L;
01225 }
01226 }
01227
01228 void KRun::abort()
01229 {
01230 kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl;
01231 killJob();
01232
01233
01234 if ( d->m_showingError )
01235 return;
01236 m_bFault = true;
01237 m_bFinished = true;
01238 m_bInit = false;
01239 m_bScanFile = false;
01240
01241
01242 m_timer.start( 0, true );
01243 }
01244
01245 void KRun::setEnableExternalBrowser(bool b)
01246 {
01247 if (b)
01248 d->m_externalBrowser = KConfigGroup(KGlobal::config(), "General").readEntry("BrowserApplication");
01249 else
01250 d->m_externalBrowser = QString::null;
01251 }
01252
01253 void KRun::setPreferredService( const QString& desktopEntryName )
01254 {
01255 d->m_preferredService = desktopEntryName;
01256 }
01257
01258 void KRun::setRunExecutables(bool b)
01259 {
01260 d->m_runExecutables = b;
01261 }
01262
01263 bool KRun::isExecutable( const QString& serviceType )
01264 {
01265 return ( serviceType == "application/x-desktop" ||
01266 serviceType == "application/x-executable" ||
01267 serviceType == "application/x-msdos-program" ||
01268 serviceType == "application/x-shellscript" );
01269 }
01270
01271
01272
01273 pid_t
01274 KProcessRunner::run(KProcess * p, const QString & binName)
01275 {
01276 return (new KProcessRunner(p, binName))->pid();
01277 }
01278
01279 #ifdef Q_WS_X11
01280 pid_t
01281 KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id )
01282 {
01283 return (new KProcessRunner(p, binName, id))->pid();
01284 }
01285 #endif
01286
01287 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName )
01288 : QObject(),
01289 process_(p),
01290 binName( _binName )
01291 {
01292 QObject::connect(
01293 process_, SIGNAL(processExited(KProcess *)),
01294 this, SLOT(slotProcessExited(KProcess *)));
01295
01296 process_->start();
01297 if ( !process_->pid() )
01298 slotProcessExited( process_ );
01299 }
01300
01301 #ifdef Q_WS_X11
01302 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& id )
01303 : QObject(),
01304 process_(p),
01305 binName( _binName ),
01306 id_( id )
01307 {
01308 QObject::connect(
01309 process_, SIGNAL(processExited(KProcess *)),
01310 this, SLOT(slotProcessExited(KProcess *)));
01311
01312 process_->start();
01313 if ( !process_->pid() )
01314 slotProcessExited( process_ );
01315 }
01316 #endif
01317
01318 KProcessRunner::~KProcessRunner()
01319 {
01320 delete process_;
01321 }
01322
01323 pid_t
01324 KProcessRunner::pid() const
01325 {
01326 return process_->pid();
01327 }
01328
01329 void
01330 KProcessRunner::slotProcessExited(KProcess * p)
01331 {
01332 if (p != process_)
01333 return;
01334
01335 kdDebug(7010) << "slotProcessExited " << binName << endl;
01336 kdDebug(7010) << "normalExit " << process_->normalExit() << endl;
01337 kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl;
01338 bool showErr = process_->normalExit()
01339 && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 );
01340 if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) )
01341 {
01342
01343
01344
01345
01346 if ( !QFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() )
01347 {
01348 kapp->ref();
01349 KMessageBox::sorry( 0L, i18n("Could not find the program '%1'").arg( binName ) );
01350 kapp->deref();
01351 }
01352 }
01353 #ifdef Q_WS_X11
01354 if( !id_.none())
01355 {
01356 KStartupInfoData data;
01357 data.addPid( pid());
01358 data.setHostname();
01359 KStartupInfo::sendFinish( id_, data );
01360 }
01361 #endif
01362 deleteLater();
01363 }
01364
01365 void KRun::virtual_hook( int, void* )
01366 { }
01367
01368 #include "krun.moc"