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