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