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");
00474 if( terminal.isEmpty() )
00475 {
00476 if( !KStandardDirs::findExe( "konsole" ).isEmpty() )
00477 terminal = "konsole";
00478 else
00479 terminal = "xvt";
00480 }
00481 if (terminal == "konsole")
00482 terminal += " -caption=%c %i %m";
00483 terminal += " ";
00484 terminal += _service.terminalOptions();
00485 if( !mx1.expandMacrosShellQuote( terminal ) ) {
00486 kdWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'" << endl;
00487 return QStringList();
00488 }
00489 mx2.expandMacrosShellQuote( terminal );
00490 if (has_shell)
00491 result << terminal;
00492 else
00493 result = KShell::splitArgs( terminal );
00494 result << "-e";
00495 }
00496
00497 int err;
00498 if (_service.substituteUid()) {
00499 if (_service.terminal())
00500 result << "su";
00501 else
00502 result << "kdesu" << "-u";
00503 result << _service.username() << "-c";
00504 KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
00505 if (err == KShell::FoundMeta) {
00506 shellQuote( exec );
00507 exec.prepend( "/bin/sh -c " );
00508 } else if (err != KShell::NoError)
00509 goto synerr;
00510 if (has_shell)
00511 shellQuote( exec );
00512 result << exec;
00513 } else {
00514 if (has_shell) {
00515 if (_service.terminal()) {
00516 KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
00517 if (err == KShell::FoundMeta) {
00518 shellQuote( exec );
00519 exec.prepend( "/bin/sh -c " );
00520 } else if (err != KShell::NoError)
00521 goto synerr;
00522 }
00523 result << exec;
00524 } else {
00525 result += KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
00526 if (err == KShell::FoundMeta)
00527 result << "/bin/sh" << "-c" << exec;
00528 else if (err != KShell::NoError)
00529 goto synerr;
00530 }
00531 }
00532
00533 return result;
00534
00535 synerr:
00536 kdWarning() << "KRun: syntax error in command `" << _service.exec() << "', service `" << _service.name() << "'" << endl;
00537 return QStringList();
00538 }
00539
00540
00541 QString KRun::binaryName( const QString & execLine, bool removePath )
00542 {
00543
00544 QStringList args = KShell::splitArgs( execLine );
00545 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it)
00546 if (!(*it).contains('='))
00547
00548 return removePath ? (*it).mid((*it).findRev('/') + 1) : *it;
00549 return QString::null;
00550 }
00551
00552 static pid_t runCommandInternal( KProcess* proc, const KService* service, const QString& binName,
00553 const QString &execName, const QString & iconName, QWidget* window, QCString asn )
00554 {
00555 if (service && !service->desktopEntryPath().isEmpty()
00556 && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() ))
00557 {
00558 kdWarning() << "No authorization to execute " << service->desktopEntryPath() << endl;
00559 KMessageBox::sorry(window, i18n("You are not authorized to execute this file."));
00560 return 0;
00561 }
00562 QString bin = KRun::binaryName( binName, true );
00563 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification
00564 bool silent;
00565 QCString wmclass;
00566 KStartupInfoId id;
00567 bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass ));
00568 if( startup_notify )
00569 {
00570 id.initId( asn );
00571 id.setupStartupEnv();
00572 KStartupInfoData data;
00573 data.setHostname();
00574 data.setBin( bin );
00575 if( !execName.isEmpty())
00576 data.setName( execName );
00577 else if( service && !service->name().isEmpty())
00578 data.setName( service->name());
00579 data.setDescription( i18n( "Launching %1" ).arg( data.name()));
00580 if( !iconName.isEmpty())
00581 data.setIcon( iconName );
00582 else if( service && !service->icon().isEmpty())
00583 data.setIcon( service->icon());
00584 if( !wmclass.isEmpty())
00585 data.setWMClass( wmclass );
00586 if( silent )
00587 data.setSilent( KStartupInfoData::Yes );
00588 data.setDesktop( KWin::currentDesktop());
00589 if( window )
00590 data.setLaunchedBy( window->winId());
00591 KStartupInfo::sendStartup( id, data );
00592 }
00593 pid_t pid = KProcessRunner::run( proc, binName, id );
00594 if( startup_notify && pid )
00595 {
00596 KStartupInfoData data;
00597 data.addPid( pid );
00598 KStartupInfo::sendChange( id, data );
00599 KStartupInfo::resetStartupEnv();
00600 }
00601 return pid;
00602 #else
00603 Q_UNUSED( execName );
00604 Q_UNUSED( iconName );
00605 return KProcessRunner::run( proc, bin );
00606 #endif
00607 }
00608
00609
00610 bool KRun::checkStartupNotify( const QString& , const KService* service, bool* silent_arg, QCString* wmclass_arg )
00611 {
00612 bool silent = false;
00613 QCString wmclass;
00614 if( service && service->property( "StartupNotify" ).isValid())
00615 {
00616 silent = !service->property( "StartupNotify" ).toBool();
00617 wmclass = service->property( "StartupWMClass" ).toString().latin1();
00618 }
00619 else if( service && service->property( "X-KDE-StartupNotify" ).isValid())
00620 {
00621 silent = !service->property( "X-KDE-StartupNotify" ).toBool();
00622 wmclass = service->property( "X-KDE-WMClass" ).toString().latin1();
00623 }
00624 else
00625 {
00626 if( service )
00627 {
00628 if( service->type() == "Application" )
00629 wmclass = "0";
00630 else
00631 return false;
00632 }
00633 else
00634 {
00635
00636
00637 wmclass = "0";
00638 silent = true;
00639 }
00640 }
00641 if( silent_arg != NULL )
00642 *silent_arg = silent;
00643 if( wmclass_arg != NULL )
00644 *wmclass_arg = wmclass;
00645 return true;
00646 }
00647
00648 static pid_t runTempService( const KService& _service, const KURL::List& _urls, QWidget* window,
00649 const QCString& asn, bool tempFiles, const QString& suggestedFileName )
00650 {
00651 if (!_urls.isEmpty()) {
00652 kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl;
00653 }
00654
00655 QStringList args;
00656 if ((_urls.count() > 1) && !_service.allowMultipleFiles())
00657 {
00658
00659
00660
00661
00662
00663 KURL::List::ConstIterator it = _urls.begin();
00664 while(++it != _urls.end())
00665 {
00666 KURL::List singleUrl;
00667 singleUrl.append(*it);
00668 runTempService( _service, singleUrl, window, "", tempFiles, suggestedFileName );
00669 }
00670 KURL::List singleUrl;
00671 singleUrl.append(_urls.first());
00672 args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles, suggestedFileName);
00673 }
00674 else
00675 {
00676 args = KRun::processDesktopExec(_service, _urls, false, tempFiles, suggestedFileName);
00677 }
00678 kdDebug(7010) << "runTempService: KProcess args=" << args << endl;
00679
00680 KProcess * proc = new KProcess;
00681 *proc << args;
00682
00683 if (!_service.path().isEmpty())
00684 proc->setWorkingDirectory(_service.path());
00685
00686 return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ),
00687 _service.name(), _service.icon(), window, asn );
00688 }
00689
00690
00691 static KURL::List resolveURLs( const KURL::List& _urls, const KService& _service )
00692 {
00693
00694
00695 QStringList supportedProtocols = _service.property("X-KDE-Protocols").toStringList();
00696 KRunMX1 mx1( _service );
00697 QString exec = _service.exec();
00698 if ( mx1.expandMacrosShellQuote( exec ) && !mx1.hasUrls ) {
00699 Q_ASSERT( supportedProtocols.isEmpty() );
00700 } else {
00701 if ( supportedProtocols.isEmpty() )
00702 {
00703
00704 QStringList categories = _service.property("Categories").toStringList();
00705 if ( categories.find("KDE") != categories.end() )
00706 supportedProtocols.append( "KIO" );
00707 else {
00708 supportedProtocols.append( "http");
00709 supportedProtocols.append( "ftp");
00710 }
00711 }
00712 }
00713 kdDebug(7010) << "supportedProtocols:" << supportedProtocols << endl;
00714
00715 KURL::List urls( _urls );
00716 if ( supportedProtocols.find( "KIO" ) == supportedProtocols.end() ) {
00717 for( KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it ) {
00718 const KURL url = *it;
00719 bool supported = url.isLocalFile() || supportedProtocols.find( url.protocol().lower() ) != supportedProtocols.end();
00720 kdDebug(7010) << "Looking at url=" << url << " supported=" << supported << endl;
00721 if ( !supported && KProtocolInfo::protocolClass(url.protocol()) == ":local" )
00722 {
00723
00724 KURL localURL = KIO::NetAccess::mostLocalURL( url, 0 );
00725 if ( localURL != url ) {
00726 *it = localURL;
00727 kdDebug(7010) << "Changed to " << localURL << endl;
00728 }
00729 }
00730 }
00731 }
00732 return urls;
00733 }
00734
00735
00736 pid_t KRun::run( const KService& _service, const KURL::List& _urls )
00737 {
00738 return run( _service, _urls, 0, false, QString::null );
00739 }
00740
00741 pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles )
00742 {
00743 return run( _service, _urls, 0, tempFiles, QString::null );
00744 }
00745
00746 pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, bool tempFiles )
00747 {
00748 return run( _service, _urls, window, "", tempFiles, QString::null );
00749 }
00750
00751 pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, const QCString& asn, bool tempFiles )
00752 {
00753 return run( _service, _urls, window, asn, tempFiles, QString::null );
00754 }
00755
00756 pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, bool tempFiles, const QString& suggestedFileName )
00757 {
00758 return run( _service, _urls, window, "", tempFiles, suggestedFileName );
00759 }
00760
00761 pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, const QCString& asn,
00762 bool tempFiles, const QString& suggestedFileName )
00763 {
00764 if (!_service.desktopEntryPath().isEmpty() &&
00765 !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath()))
00766 {
00767 kdWarning() << "No authorization to execute " << _service.desktopEntryPath() << endl;
00768 KMessageBox::sorry(window, i18n("You are not authorized to execute this service."));
00769 return 0;
00770 }
00771
00772 if ( !tempFiles )
00773 {
00774
00775 KURL::List::ConstIterator it = _urls.begin();
00776 for(; it != _urls.end(); ++it) {
00777
00778 KRecentDocument::add( *it, _service.desktopEntryName() );
00779 }
00780 }
00781
00782 if ( tempFiles || _service.desktopEntryPath().isEmpty() || !suggestedFileName.isEmpty() )
00783 {
00784 return runTempService(_service, _urls, window, asn, tempFiles, suggestedFileName);
00785 }
00786
00787 kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl;
00788
00789 if (!_urls.isEmpty()) {
00790 kdDebug(7010) << "First url " << _urls.first().url() << endl;
00791 }
00792
00793
00794 const KURL::List urls = resolveURLs( _urls, _service );
00795
00796 QString error;
00797 int pid = 0;
00798
00799 QCString myasn = asn;
00800
00801 if( window != NULL )
00802 {
00803 if( myasn.isEmpty())
00804 myasn = KStartupInfo::createNewStartupId();
00805 if( myasn != "0" )
00806 {
00807 KStartupInfoId id;
00808 id.initId( myasn );
00809 KStartupInfoData data;
00810 data.setLaunchedBy( window->winId());
00811 KStartupInfo::sendChange( id, data );
00812 }
00813 }
00814
00815 int i = KApplication::startServiceByDesktopPath(
00816 _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid, myasn
00817 );
00818
00819 if (i != 0)
00820 {
00821 kdDebug(7010) << error << endl;
00822 KMessageBox::sorry( window, error );
00823 return 0;
00824 }
00825
00826 kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl;
00827 return (pid_t) pid;
00828 }
00829
00830
00831 pid_t KRun::run( const QString& _exec, const KURL::List& _urls, const QString& _name,
00832 const QString& _icon, const QString&, const QString&)
00833 {
00834 KService::Ptr service = new KService(_name, _exec, _icon);
00835
00836 return run(*service, _urls);
00837 }
00838
00839 pid_t KRun::runCommand( QString cmd )
00840 {
00841 return KRun::runCommand( cmd, QString::null, QString::null, NULL, "" );
00842 }
00843
00844 pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName )
00845 {
00846 return KRun::runCommand( cmd, execName, iconName, NULL, "" );
00847 }
00848
00849 pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName,
00850 QWidget* window, const QCString& asn )
00851 {
00852 kdDebug(7010) << "runCommand " << cmd << "," << execName << endl;
00853 KProcess * proc = new KProcess;
00854 proc->setUseShell(true);
00855 *proc << cmd;
00856 KService::Ptr service = KService::serviceByDesktopName( binaryName( execName, true ) );
00857 return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName,
00858 window, asn );
00859 }
00860
00861 KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo )
00862 :m_timer(0,"KRun::timer")
00863 {
00864 init (url, 0, "", mode, isLocalFile, showProgressInfo);
00865 }
00866
00867 KRun::KRun( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile,
00868 bool showProgressInfo )
00869 :m_timer(0,"KRun::timer")
00870 {
00871 init (url, window, "", mode, isLocalFile, showProgressInfo);
00872 }
00873
00874 KRun::KRun( const KURL& url, QWidget* window, const QCString& asn, mode_t mode, bool isLocalFile,
00875 bool showProgressInfo )
00876 :m_timer(0,"KRun::timer")
00877 {
00878 init (url, window, asn, mode, isLocalFile, showProgressInfo);
00879 }
00880
00881 void KRun::init ( const KURL& url, QWidget* window, const QCString& asn, mode_t mode, bool isLocalFile,
00882 bool showProgressInfo )
00883 {
00884 m_bFault = false;
00885 m_bAutoDelete = true;
00886 m_bProgressInfo = showProgressInfo;
00887 m_bFinished = false;
00888 m_job = 0L;
00889 m_strURL = url;
00890 m_bScanFile = false;
00891 m_bIsDirectory = false;
00892 m_bIsLocalFile = isLocalFile;
00893 m_mode = mode;
00894 d = new KRunPrivate;
00895 d->m_runExecutables = true;
00896 d->m_window = window;
00897 d->m_asn = asn;
00898 setEnableExternalBrowser(true);
00899
00900
00901
00902
00903 m_bInit = true;
00904 connect( &m_timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) );
00905 m_timer.start( 0, true );
00906 kdDebug(7010) << " new KRun " << this << " " << url.prettyURL() << " timer=" << &m_timer << endl;
00907
00908 kapp->ref();
00909 }
00910
00911 void KRun::init()
00912 {
00913 kdDebug(7010) << "INIT called" << endl;
00914 if ( !m_strURL.isValid() )
00915 {
00916 d->m_showingError = true;
00917 KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) );
00918 d->m_showingError = false;
00919 m_bFault = true;
00920 m_bFinished = true;
00921 m_timer.start( 0, true );
00922 return;
00923 }
00924 if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL))
00925 {
00926 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, m_strURL.prettyURL());
00927 d->m_showingError = true;
00928 KMessageBoxWrapper::error( d->m_window, msg );
00929 d->m_showingError = false;
00930 m_bFault = true;
00931 m_bFinished = true;
00932 m_timer.start( 0, true );
00933 return;
00934 }
00935
00936 if ( !m_bIsLocalFile && m_strURL.isLocalFile() )
00937 m_bIsLocalFile = true;
00938
00939 QString exec;
00940 if (m_strURL.protocol().startsWith("http"))
00941 {
00942 exec = d->m_externalBrowser;
00943 }
00944
00945 if ( m_bIsLocalFile )
00946 {
00947 if ( m_mode == 0 )
00948 {
00949 KDE_struct_stat buff;
00950 if ( KDE_stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 )
00951 {
00952 d->m_showingError = true;
00953 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() ) );
00954 d->m_showingError = false;
00955 m_bFault = true;
00956 m_bFinished = true;
00957 m_timer.start( 0, true );
00958 return;
00959 }
00960 m_mode = buff.st_mode;
00961 }
00962
00963 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile );
00964 assert( mime != 0L );
00965 kdDebug(7010) << "MIME TYPE is " << mime->name() << endl;
00966 foundMimeType( mime->name() );
00967 return;
00968 }
00969 else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( m_strURL ) ) {
00970 kdDebug(7010) << "Helper protocol" << endl;
00971
00972 bool ok = false;
00973 KURL::List urls;
00974 urls.append( m_strURL );
00975 if (exec.isEmpty())
00976 {
00977 exec = KProtocolInfo::exec( m_strURL.protocol() );
00978 if (exec.isEmpty())
00979 {
00980 foundMimeType(KProtocolInfo::defaultMimetype(m_strURL));
00981 return;
00982 }
00983 run( exec, urls );
00984 ok = true;
00985 }
00986 else if (exec.startsWith("!"))
00987 {
00988 exec = exec.mid(1);
00989 exec += " %u";
00990 run( exec, urls );
00991 ok = true;
00992 }
00993 else
00994 {
00995 KService::Ptr service = KService::serviceByStorageId( exec );
00996 if (service)
00997 {
00998 run( *service, urls, d->m_window, d->m_asn );
00999 ok = true;
01000 }
01001 }
01002
01003 if (ok)
01004 {
01005 m_bFinished = true;
01006
01007 m_timer.start( 0, true );
01008 return;
01009 }
01010 }
01011
01012
01013 if ( S_ISDIR( m_mode ) )
01014 {
01015 foundMimeType( "inode/directory" );
01016 return;
01017 }
01018
01019
01020
01021 if ( !KProtocolInfo::supportsListing( m_strURL ) )
01022 {
01023
01024
01025 scanFile();
01026 return;
01027 }
01028
01029 kdDebug(7010) << "Testing directory (stating)" << endl;
01030
01031
01032 KIO::StatJob *job = KIO::stat( m_strURL, true, 0 , m_bProgressInfo );
01033 job->setWindow (d->m_window);
01034 connect( job, SIGNAL( result( KIO::Job * ) ),
01035 this, SLOT( slotStatResult( KIO::Job * ) ) );
01036 m_job = job;
01037 kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl;
01038 }
01039
01040 KRun::~KRun()
01041 {
01042 kdDebug(7010) << "KRun::~KRun() " << this << endl;
01043 m_timer.stop();
01044 killJob();
01045 kapp->deref();
01046 kdDebug(7010) << "KRun::~KRun() done " << this << endl;
01047 delete d;
01048 }
01049
01050 void KRun::scanFile()
01051 {
01052 kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl;
01053
01054
01055 if ( m_strURL.query().isEmpty() )
01056 {
01057 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL );
01058 assert( mime != 0L );
01059 if ( mime->name() != "application/octet-stream" || m_bIsLocalFile )
01060 {
01061 kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl;
01062 foundMimeType( mime->name() );
01063 return;
01064 }
01065 }
01066
01067
01068
01069
01070
01071 if ( !KProtocolInfo::supportsReading( m_strURL ) )
01072 {
01073 kdError(7010) << "#### NO SUPPORT FOR READING!" << endl;
01074 m_bFault = true;
01075 m_bFinished = true;
01076 m_timer.start( 0, true );
01077 return;
01078 }
01079 kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl;
01080
01081 KIO::TransferJob *job = KIO::get( m_strURL, false , m_bProgressInfo );
01082 job->setWindow (d->m_window);
01083 connect(job, SIGNAL( result(KIO::Job *)),
01084 this, SLOT( slotScanFinished(KIO::Job *)));
01085 connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)),
01086 this, SLOT( slotScanMimeType(KIO::Job *, const QString &)));
01087 m_job = job;
01088 kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl;
01089 }
01090
01091 void KRun::slotTimeout()
01092 {
01093 kdDebug(7010) << this << " slotTimeout called" << endl;
01094 if ( m_bInit )
01095 {
01096 m_bInit = false;
01097 init();
01098 return;
01099 }
01100
01101 if ( m_bFault ) {
01102 emit error();
01103 }
01104 if ( m_bFinished ) {
01105 emit finished();
01106 }
01107 else
01108 {
01109 if ( m_bScanFile )
01110 {
01111 m_bScanFile = false;
01112 scanFile();
01113 return;
01114 }
01115 else if ( m_bIsDirectory )
01116 {
01117 m_bIsDirectory = false;
01118 foundMimeType( "inode/directory" );
01119 return;
01120 }
01121 }
01122
01123 if ( m_bAutoDelete )
01124 {
01125 delete this;
01126 return;
01127 }
01128 }
01129
01130 void KRun::slotStatResult( KIO::Job * job )
01131 {
01132 m_job = 0L;
01133 if (job->error())
01134 {
01135 d->m_showingError = true;
01136 kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl;
01137 job->showErrorDialog();
01138
01139 d->m_showingError = false;
01140
01141 m_bFault = true;
01142 m_bFinished = true;
01143
01144
01145 m_timer.start( 0, true );
01146
01147 } else {
01148
01149 kdDebug(7010) << "Finished" << endl;
01150 if(!dynamic_cast<KIO::StatJob*>(job))
01151 kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl;
01152
01153 QString knownMimeType;
01154 KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult();
01155 KIO::UDSEntry::ConstIterator it = entry.begin();
01156 for( ; it != entry.end(); it++ ) {
01157 switch( (*it).m_uds ) {
01158 case KIO::UDS_FILE_TYPE:
01159 if ( S_ISDIR( (mode_t)((*it).m_long) ) )
01160 m_bIsDirectory = true;
01161 else
01162 m_bScanFile = true;
01163 break;
01164 case KIO::UDS_MIME_TYPE:
01165 knownMimeType = (*it).m_str;
01166 break;
01167 case KIO::UDS_LOCAL_PATH:
01168 d->m_localPath = (*it).m_str;
01169 break;
01170 default:
01171 break;
01172 }
01173 }
01174 if ( !knownMimeType.isEmpty() )
01175 {
01176 foundMimeType( knownMimeType );
01177 m_bFinished = true;
01178 }
01179
01180
01181 assert ( m_bScanFile || m_bIsDirectory );
01182
01183
01184
01185
01186 m_timer.start( 0, true );
01187 }
01188 }
01189
01190 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype )
01191 {
01192 if ( mimetype.isEmpty() )
01193 kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug." << endl;
01194 foundMimeType( mimetype );
01195 m_job = 0;
01196 }
01197
01198 void KRun::slotScanFinished( KIO::Job *job )
01199 {
01200 m_job = 0;
01201 if (job->error())
01202 {
01203 d->m_showingError = true;
01204 kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl;
01205 job->showErrorDialog();
01206
01207 d->m_showingError = false;
01208
01209 m_bFault = true;
01210 m_bFinished = true;
01211
01212
01213 m_timer.start( 0, true );
01214 }
01215 }
01216
01217 void KRun::foundMimeType( const QString& type )
01218 {
01219 kdDebug(7010) << "Resulting mime type is " << type << endl;
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
01273 KIO::TransferJob *job = ::qt_cast<KIO::TransferJob *>( m_job );
01274 if ( job )
01275 {
01276 job->putOnHold();
01277 KIO::Scheduler::publishSlaveOnHold();
01278 m_job = 0;
01279 }
01280
01281 Q_ASSERT( !m_bFinished );
01282
01283
01284 if ( !d->m_preferredService.isEmpty() ) {
01285 kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl;
01286 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService );
01287 if ( serv && serv->hasServiceType( type ) )
01288 {
01289 KURL::List lst;
01290 lst.append( m_strURL );
01291 m_bFinished = KRun::run( *serv, lst, d->m_window, d->m_asn );
01296 }
01297 }
01298
01299
01300 if ( type == "application/x-desktop" && !d->m_localPath.isEmpty() )
01301 {
01302 m_strURL = KURL();
01303 m_strURL.setPath( d->m_localPath );
01304 }
01305
01306 if (!m_bFinished && KRun::runURL( m_strURL, type, d->m_window, d->m_asn, false, d->m_runExecutables, d->m_suggestedFileName )){
01307 m_bFinished = true;
01308 }
01309 else{
01310 m_bFinished = true;
01311 m_bFault = true;
01312 }
01313
01314 m_timer.start( 0, true );
01315 }
01316
01317 void KRun::killJob()
01318 {
01319 if ( m_job )
01320 {
01321 kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl;
01322 m_job->kill();
01323 m_job = 0L;
01324 }
01325 }
01326
01327 void KRun::abort()
01328 {
01329 kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl;
01330 killJob();
01331
01332
01333 if ( d->m_showingError )
01334 return;
01335 m_bFault = true;
01336 m_bFinished = true;
01337 m_bInit = false;
01338 m_bScanFile = false;
01339
01340
01341 m_timer.start( 0, true );
01342 }
01343
01344 void KRun::setEnableExternalBrowser(bool b)
01345 {
01346 if (b)
01347 d->m_externalBrowser = KConfigGroup(KGlobal::config(), "General").readEntry("BrowserApplication");
01348 else
01349 d->m_externalBrowser = QString::null;
01350 }
01351
01352 void KRun::setPreferredService( const QString& desktopEntryName )
01353 {
01354 d->m_preferredService = desktopEntryName;
01355 }
01356
01357 void KRun::setRunExecutables(bool b)
01358 {
01359 d->m_runExecutables = b;
01360 }
01361
01362 void KRun::setSuggestedFileName( const QString& fileName )
01363 {
01364 d->m_suggestedFileName = fileName;
01365 }
01366
01367 bool KRun::isExecutable( const QString& serviceType )
01368 {
01369 return ( serviceType == "application/x-desktop" ||
01370 serviceType == "application/x-executable" ||
01371 serviceType == "application/x-msdos-program" ||
01372 serviceType == "application/x-shellscript" );
01373 }
01374
01375
01376
01377 pid_t
01378 KProcessRunner::run(KProcess * p, const QString & binName)
01379 {
01380 return (new KProcessRunner(p, binName))->pid();
01381 }
01382
01383 #ifdef Q_WS_X11
01384 pid_t
01385 KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id )
01386 {
01387 return (new KProcessRunner(p, binName, id))->pid();
01388 }
01389 #endif
01390
01391 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName )
01392 : QObject(),
01393 process_(p),
01394 binName( _binName )
01395 {
01396 QObject::connect(
01397 process_, SIGNAL(processExited(KProcess *)),
01398 this, SLOT(slotProcessExited(KProcess *)));
01399
01400 process_->start();
01401 if ( !process_->pid() )
01402 slotProcessExited( process_ );
01403 }
01404
01405 #ifdef Q_WS_X11
01406 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& id )
01407 : QObject(),
01408 process_(p),
01409 binName( _binName ),
01410 id_( id )
01411 {
01412 QObject::connect(
01413 process_, SIGNAL(processExited(KProcess *)),
01414 this, SLOT(slotProcessExited(KProcess *)));
01415
01416 process_->start();
01417 if ( !process_->pid() )
01418 slotProcessExited( process_ );
01419 }
01420 #endif
01421
01422 KProcessRunner::~KProcessRunner()
01423 {
01424 delete process_;
01425 }
01426
01427 pid_t
01428 KProcessRunner::pid() const
01429 {
01430 return process_->pid();
01431 }
01432
01433 void
01434 KProcessRunner::slotProcessExited(KProcess * p)
01435 {
01436 if (p != process_)
01437 return;
01438
01439 kdDebug(7010) << "slotProcessExited " << binName << endl;
01440 kdDebug(7010) << "normalExit " << process_->normalExit() << endl;
01441 kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl;
01442 bool showErr = process_->normalExit()
01443 && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 );
01444 if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) )
01445 {
01446
01447
01448
01449
01450 if ( !QFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() )
01451 {
01452 kapp->ref();
01453 KMessageBox::sorry( 0L, i18n("Could not find the program '%1'").arg( binName ) );
01454 kapp->deref();
01455 }
01456 }
01457 #ifdef Q_WS_X11
01458 if( !id_.none())
01459 {
01460 KStartupInfoData data;
01461 data.addPid( pid());
01462 data.setHostname();
01463 KStartupInfo::sendFinish( id_, data );
01464 }
01465 #endif
01466 deleteLater();
01467 }
01468
01469 void KRun::virtual_hook( int, void* )
01470 { }
01471
01472 #include "krun.moc"