kio Library API Documentation

krun.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Torben Weis <weis@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 #include <assert.h> 00021 #include <stdlib.h> 00022 #include <string.h> 00023 #include <unistd.h> 00024 00025 #include "krun.h" 00026 #include "kuserprofile.h" 00027 #include "kmimetype.h" 00028 #include "kmimemagic.h" 00029 #include "kio/job.h" 00030 #include "kio/global.h" 00031 #include "kio/scheduler.h" 00032 #include "kfile/kopenwith.h" 00033 #include "kfile/krecentdocument.h" 00034 00035 #include <kdatastream.h> 00036 #include <kmessageboxwrapper.h> 00037 #include <kurl.h> 00038 #include <kapplication.h> 00039 #include <kdebug.h> 00040 #include <klocale.h> 00041 #include <kprotocolinfo.h> 00042 #include <kstandarddirs.h> 00043 #include <kprocess.h> 00044 #include <dcopclient.h> 00045 #include <qfile.h> 00046 #include <qfileinfo.h> 00047 #include <qtextstream.h> 00048 #include <qdatetime.h> 00049 #include <qregexp.h> 00050 #include <kwin.h> 00051 #include <kdesktopfile.h> 00052 #include <kstartupinfo.h> 00053 #include <kmacroexpander.h> 00054 #include <kshell.h> 00055 #include <typeinfo> 00056 #include <qwidget.h> 00057 #include <qguardedptr.h> 00058 00059 class KRun::KRunPrivate 00060 { 00061 public: 00062 KRunPrivate() { m_showingError = false; } 00063 00064 bool m_showingError; 00065 bool m_runExecutables; 00066 00067 QString m_preferredService; 00068 QGuardedPtr <QWidget> m_window; 00069 }; 00070 00071 pid_t KRun::runURL( const KURL& u, const QString& _mimetype ) 00072 { 00073 return runURL( u, _mimetype, false, true ); 00074 } 00075 00076 pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile ) 00077 { 00078 return runURL( u, _mimetype, tempFile, true ); 00079 } 00080 00081 bool KRun::isExecutableFile( const KURL& url, const QString &mimetype ) 00082 { 00083 if ( !url.isLocalFile() ) 00084 return false; 00085 QFileInfo file( url.path() ); 00086 if ( file.isExecutable() ) // Got a prospective file to run 00087 { 00088 KMimeType::Ptr mimeType = KMimeType::mimeType( mimetype ); 00089 00090 if ( mimeType->is("application/x-executable") || mimeType->is("application/x-executable-script") ) 00091 return true; 00092 } 00093 return false; 00094 } 00095 00096 // This is called by foundMimeType, since it knows the mimetype of the URL 00097 pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile, bool runExecutables ) 00098 { 00099 bool noRun = false; 00100 bool noAuth = false; 00101 if ( _mimetype == "inode/directory-locked" ) 00102 { 00103 KMessageBoxWrapper::error( 0L, 00104 i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>").arg(u.htmlURL()) ); 00105 return 0; 00106 } 00107 else if ( _mimetype == "application/x-desktop" ) 00108 { 00109 if ( u.isLocalFile() && runExecutables) 00110 return KDEDesktopMimeType::run( u, true ); 00111 } 00112 else if ( isExecutableFile(u, _mimetype) ) 00113 { 00114 if ( u.isLocalFile() && runExecutables) 00115 { 00116 if (kapp->authorize("shell_access")) 00117 { 00118 QString path = u.path(); 00119 shellQuote( path ); 00120 return (KRun::runCommand(path)); // just execute the url as a command 00121 // ## TODO implement deleting the file if tempFile==true 00122 } 00123 else 00124 { 00125 noAuth = true; 00126 } 00127 } 00128 else if (_mimetype == "application/x-executable") 00129 noRun = true; 00130 } 00131 else if ( isExecutable(_mimetype) ) 00132 { 00133 if (!runExecutables) 00134 noRun = true; 00135 00136 if (!kapp->authorize("shell_access")) 00137 noAuth = true; 00138 } 00139 00140 if ( noRun ) 00141 { 00142 KMessageBox::sorry( 0L, 00143 i18n("<qt>The file <b>%1</b> is an executable program. " 00144 "For safety it will not be started.</qt>").arg(u.htmlURL())); 00145 return 0; 00146 } 00147 if ( noAuth ) 00148 { 00149 KMessageBoxWrapper::error( 0L, 00150 i18n("<qt>You do not have permission to run <b>%1</b>.</qt>").arg(u.htmlURL()) ); 00151 return 0; 00152 } 00153 00154 KURL::List lst; 00155 lst.append( u ); 00156 00157 static const QString& app_str = KGlobal::staticQString("Application"); 00158 00159 KService::Ptr offer = KServiceTypeProfile::preferredService( _mimetype, app_str ); 00160 00161 if ( !offer ) 00162 { 00163 // Open-with dialog 00164 // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog ! 00165 // Hmm, in fact KOpenWithDlg::setServiceType already guesses the mimetype from the first URL of the list... 00166 return displayOpenWithDialog( lst, tempFile ); 00167 } 00168 00169 return KRun::run( *offer, lst, tempFile ); 00170 } 00171 00172 bool KRun::displayOpenWithDialog( const KURL::List& lst ) 00173 { 00174 return displayOpenWithDialog( lst, false ); 00175 } 00176 00177 bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles ) 00178 { 00179 if (kapp && !kapp->authorizeKAction("openwith")) 00180 { 00181 // TODO: Better message, i18n freeze :-( 00182 KMessageBox::sorry(0L, i18n("You are not authorized to execute this file.")); 00183 return false; 00184 } 00185 00186 KOpenWithDlg l( lst, i18n("Open with:"), QString::null, 0L ); 00187 if ( l.exec() ) 00188 { 00189 KService::Ptr service = l.service(); 00190 if ( !!service ) 00191 return KRun::run( *service, lst, tempFiles ); 00192 00193 kdDebug(250) << "No service set, running " << l.text() << endl; 00194 return KRun::run( l.text(), lst ); // TODO handle tempFiles 00195 } 00196 return false; 00197 } 00198 00199 void KRun::shellQuote( QString &_str ) 00200 { 00201 // Credits to Walter, says Bernd G. :) 00202 if (_str.isEmpty()) // Don't create an explicit empty parameter 00203 return; 00204 QChar q('\''); 00205 _str.replace(q, "'\\''").prepend(q).append(q); 00206 } 00207 00208 00209 class KRunMX1 : public KMacroExpanderBase { 00210 public: 00211 KRunMX1( const KService &_service ) : 00212 KMacroExpanderBase( '%' ), hasUrls( false ), hasSpec( false ), service( _service ) {} 00213 bool hasUrls:1, hasSpec:1; 00214 00215 protected: 00216 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00217 00218 private: 00219 const KService &service; 00220 }; 00221 00222 int 00223 KRunMX1::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00224 { 00225 uint option = str[pos + 1]; 00226 switch( option ) { 00227 case 'c': 00228 ret << service.name().replace( '%', "%%" ); 00229 break; 00230 case 'k': 00231 ret << service.desktopEntryPath().replace( '%', "%%" ); 00232 break; 00233 case 'i': 00234 ret << "-icon" << service.icon().replace( '%', "%%" ); 00235 break; 00236 case 'm': 00237 ret << "-miniicon" << service.icon().replace( '%', "%%" ); 00238 break; 00239 case 'u': 00240 case 'U': 00241 hasUrls = true; 00242 /* fallthrough */ 00243 case 'f': 00244 case 'F': 00245 case 'n': 00246 case 'N': 00247 case 'd': 00248 case 'D': 00249 case 'v': 00250 hasSpec = true; 00251 /* fallthrough */ 00252 default: 00253 return -2; // subst with same and skip 00254 } 00255 return 2; 00256 } 00257 00258 class KRunMX2 : public KMacroExpanderBase { 00259 public: 00260 KRunMX2( const KURL::List &_urls ) : 00261 KMacroExpanderBase( '%' ), ignFile( false ), urls( _urls ) {} 00262 bool ignFile:1; 00263 00264 protected: 00265 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00266 00267 private: 00268 void subst( int option, const KURL &url, QStringList &ret ); 00269 00270 const KURL::List &urls; 00271 }; 00272 00273 void 00274 KRunMX2::subst( int option, const KURL &url, QStringList &ret ) 00275 { 00276 switch( option ) { 00277 case 'u': 00278 ret << (url.isLocalFile() ? url.path() : url.url()); 00279 break; 00280 case 'd': 00281 ret << url.directory(); 00282 break; 00283 case 'f': 00284 ret << url.path(); 00285 break; 00286 case 'n': 00287 ret << url.fileName(); 00288 break; 00289 case 'v': 00290 if (url.isLocalFile() && QFile::exists( url.path() ) ) 00291 ret << KDesktopFile( url.path(), true ).readEntry( "Dev" ); 00292 break; 00293 } 00294 return; 00295 } 00296 00297 int 00298 KRunMX2::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00299 { 00300 uint option = str[pos + 1]; 00301 switch( option ) { 00302 case 'f': 00303 case 'u': 00304 case 'n': 00305 case 'd': 00306 case 'v': 00307 if( urls.isEmpty() ) { 00308 if (!ignFile) 00309 kdDebug() << "KRun::processDesktopExec: No URLs supplied to single-URL service " << str << endl; 00310 } else if( urls.count() > 1 ) 00311 kdWarning() << "KRun::processDesktopExec: " << urls.count() << " URLs supplied to single-URL service " << str << endl; 00312 else 00313 subst( option, urls.first(), ret ); 00314 break; 00315 case 'F': 00316 case 'U': 00317 case 'N': 00318 case 'D': 00319 option += 'a' - 'A'; 00320 for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) 00321 subst( option, *it, ret ); 00322 break; 00323 case '%': 00324 ret = "%"; 00325 break; 00326 default: 00327 return -2; // subst with same and skip 00328 } 00329 return 2; 00330 } 00331 00332 // BIC: merge with method below 00333 QStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell) { 00334 return processDesktopExec( _service, _urls, has_shell, false ); 00335 } 00336 00337 QStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles) 00338 { 00339 QString exec = _service.exec(); 00340 QStringList result; 00341 00342 KRunMX1 mx1( _service ); 00343 KRunMX2 mx2( _urls ); 00344 00346 QRegExp re("^\\s*(?:/bin/)?sh\\s+-c\\s+(.*)$"); 00347 if (!re.search( exec )) { 00348 exec = re.cap( 1 ).stripWhiteSpace(); 00349 for (uint pos = 0; pos < exec.length(); ) { 00350 QChar c = exec.unicode()[pos]; 00351 if (c != '\'' && c != '"') 00352 goto synerr; // what else can we do? after normal parsing the substs would be insecure 00353 int pos2 = exec.find( c, pos + 1 ) - 1; 00354 if (pos2 < 0) 00355 goto synerr; // quoting error 00356 memcpy( (void *)(exec.unicode() + pos), exec.unicode() + pos + 1, (pos2 - pos) * sizeof(QChar)); 00357 pos = pos2; 00358 exec.remove( pos, 2 ); 00359 } 00360 } 00361 00362 if( !mx1.expandMacrosShellQuote( exec ) ) 00363 goto synerr; // error in shell syntax 00364 00365 // FIXME: the current way of invoking kioexec disables term and su use 00366 00367 // Check if we need "tempexec" (kioexec in fact) 00368 if( tempFiles ) { 00369 result << "kioexec" << "--tempfiles" << exec; 00370 result += _urls.toStringList(); 00371 if (has_shell) 00372 result = KShell::joinArgs( result ); 00373 return result; 00374 } 00375 00376 // Check if we need kioexec 00377 if( !mx1.hasUrls ) { 00378 for( KURL::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it ) 00379 if ( !(*it).isLocalFile() ) { 00380 // We need to run the app through kioexec 00381 result << "kioexec" << exec; 00382 result += _urls.toStringList(); 00383 if (has_shell) 00384 result = KShell::joinArgs( result ); 00385 return result; 00386 } 00387 } 00388 00389 // Did the user forget to append something like '%f'? 00390 // If so, then assume that '%f' is the right choice => the application 00391 // accepts only local files. 00392 if( !mx1.hasSpec ) { 00393 exec += " %f"; 00394 mx2.ignFile = true; 00395 } 00396 00397 mx2.expandMacrosShellQuote( exec ); // syntax was already checked, so don't check return value 00398 00399 /* 00400 1 = need_shell, 2 = terminal, 4 = su, 8 = has_shell 00401 00402 0 << split(cmd) 00403 1 << "sh" << "-c" << cmd 00404 2 << split(term) << "-e" << split(cmd) 00405 3 << split(term) << "-e" << "sh" << "-c" << cmd 00406 00407 4 << "kdesu" << "-u" << user << "-c" << cmd 00408 5 << "kdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd)) 00409 6 << split(term) << "-e" << "su" << user << "-c" << cmd 00410 7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd)) 00411 00412 8 << cmd 00413 9 << cmd 00414 a << term << "-e" << cmd 00415 b << term << "-e" << ("sh -c " + quote(cmd)) 00416 00417 c << "kdesu" << "-u" << user << "-c" << quote(cmd) 00418 d << "kdesu" << "-u" << user << "-c" << quote("sh -c " + quote(cmd)) 00419 e << term << "-e" << "su" << user << "-c" << quote(cmd) 00420 f << term << "-e" << "su" << user << "-c" << quote("sh -c " + quote(cmd)) 00421 00422 "sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh. 00423 this could be optimized with the -s switch of some su versions (e.g., debian linux). 00424 */ 00425 00426 if (_service.terminal()) { 00427 KConfigGroupSaver gs(KGlobal::config(), "General"); 00428 QString terminal = KGlobal::config()->readPathEntry("TerminalApplication", "konsole"); 00429 if (terminal == "konsole") 00430 terminal += " -caption=%c %i %m"; 00431 terminal += " "; 00432 terminal += _service.terminalOptions(); 00433 if( !mx1.expandMacrosShellQuote( terminal ) ) { 00434 kdWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'" << endl; 00435 return QStringList(); 00436 } 00437 mx2.expandMacrosShellQuote( terminal ); 00438 if (has_shell) 00439 result << terminal; 00440 else 00441 result = KShell::splitArgs( terminal ); // assuming that the term spec never needs a shell! 00442 result << "-e"; 00443 } 00444 00445 int err; 00446 if (_service.substituteUid()) { 00447 if (_service.terminal()) 00448 result << "su"; 00449 else 00450 result << "kdesu" << "-u"; 00451 result << _service.username() << "-c"; 00452 KShell::splitArgs(exec, KShell::AbortOnMeta, &err); 00453 if (err == KShell::FoundMeta) { 00454 shellQuote( exec ); 00455 exec.prepend( "/bin/sh -c " ); 00456 } else if (err != KShell::NoError) 00457 goto synerr; 00458 if (has_shell) 00459 shellQuote( exec ); 00460 result << exec; 00461 } else { 00462 if (has_shell) { 00463 if (_service.terminal()) { 00464 KShell::splitArgs(exec, KShell::AbortOnMeta, &err); 00465 if (err == KShell::FoundMeta) { 00466 shellQuote( exec ); 00467 exec.prepend( "/bin/sh -c " ); 00468 } else if (err != KShell::NoError) 00469 goto synerr; 00470 } 00471 result << exec; 00472 } else { 00473 result += KShell::splitArgs(exec, KShell::AbortOnMeta, &err); 00474 if (err == KShell::FoundMeta) 00475 result << "/bin/sh" << "-c" << exec; 00476 else if (err != KShell::NoError) 00477 goto synerr; 00478 } 00479 } 00480 00481 return result; 00482 00483 synerr: 00484 kdWarning() << "KRun: syntax error in command `" << _service.exec() << "', service `" << _service.name() << "'" << endl; 00485 return QStringList(); 00486 } 00487 00488 //static 00489 QString KRun::binaryName( const QString & execLine, bool removePath ) 00490 { 00491 // Remove parameters and/or trailing spaces. 00492 QStringList args = KShell::splitArgs( execLine ); 00493 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) 00494 if (!(*it).contains('=')) 00495 // Remove path if wanted 00496 return removePath ? (*it).mid((*it).findRev('/') + 1) : *it; 00497 return QString::null; 00498 } 00499 00500 static pid_t runCommandInternal( KProcess* proc, const KService* service, const QString& binName, 00501 const QString &execName, const QString & iconName ) 00502 { 00503 if ( service && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() )) 00504 { 00505 KMessageBox::sorry(0, i18n("You are not authorized to execute this file.")); 00506 return 0; 00507 } 00508 QString bin = KRun::binaryName( binName, true ); 00509 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification 00510 bool startup_notify = false; 00511 QCString wmclass; 00512 KStartupInfoId id; 00513 if( service && service->property( "StartupNotify" ).isValid()) 00514 { 00515 startup_notify = service->property( "StartupNotify" ).toBool(); 00516 wmclass = service->property( "StartupWMClass" ).toString().latin1(); 00517 } 00518 else if( service && service->property( "X-KDE-StartupNotify" ).isValid()) 00519 { 00520 startup_notify = service->property( "X-KDE-StartupNotify" ).toBool(); 00521 wmclass = service->property( "X-KDE-WMClass" ).toString().latin1(); 00522 } 00523 else // non-compliant app ( .desktop file ) 00524 { 00525 if( service && service->type() == "Application" ) 00526 { 00527 startup_notify = true; // doesn't have .desktop entries needed 00528 wmclass = "0"; // start as non-compliant 00529 } 00530 } 00531 if( startup_notify ) 00532 { 00533 id.initId(); 00534 id.setupStartupEnv(); 00535 KStartupInfoData data; 00536 data.setHostname(); 00537 data.setBin( bin ); 00538 data.setName( execName.isEmpty() ? service->name() : execName ); 00539 data.setDescription( i18n( "Launching %1" ).arg( data.name())); 00540 data.setIcon( iconName.isEmpty() ? service->icon() : iconName ); 00541 if( !wmclass.isEmpty()) 00542 data.setWMClass( wmclass ); 00543 data.setDesktop( KWin::currentDesktop()); 00544 KStartupInfo::sendStartup( id, data ); 00545 } 00546 pid_t pid = KProcessRunner::run( proc, binName, id ); 00547 if( startup_notify && pid ) 00548 { 00549 KStartupInfoData data; 00550 data.addPid( pid ); 00551 KStartupInfo::sendChange( id, data ); 00552 KStartupInfo::resetStartupEnv(); 00553 } 00554 return pid; 00555 #else 00556 Q_UNUSED( execName ); 00557 Q_UNUSED( iconName ); 00558 return KProcessRunner::run( proc, bin ); 00559 #endif 00560 } 00561 00562 static pid_t runTempService( const KService& _service, const KURL::List& _urls, bool tempFiles ) 00563 { 00564 if (!_urls.isEmpty()) { 00565 kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl; 00566 } 00567 00568 QStringList args; 00569 if ((_urls.count() > 1) && !_service.allowMultipleFiles()) 00570 { 00571 // We need to launch the application N times. That sucks. 00572 // We ignore the result for application 2 to N. 00573 // For the first file we launch the application in the 00574 // usual way. The reported result is based on this 00575 // application. 00576 KURL::List::ConstIterator it = _urls.begin(); 00577 while(++it != _urls.end()) 00578 { 00579 KURL::List singleUrl; 00580 singleUrl.append(*it); 00581 runTempService( _service, singleUrl, tempFiles ); 00582 } 00583 KURL::List singleUrl; 00584 singleUrl.append(_urls.first()); 00585 args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles); 00586 } 00587 else 00588 { 00589 args = KRun::processDesktopExec(_service, _urls, false, tempFiles); 00590 } 00591 kdDebug(7010) << "runTempService: KProcess args=" << args << endl; 00592 00593 KProcess * proc = new KProcess; 00594 *proc << args; 00595 00596 if (!_service.path().isEmpty()) 00597 proc->setWorkingDirectory(_service.path()); 00598 00599 return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ), 00600 _service.name(), _service.icon() ); 00601 } 00602 00603 // BIC merge with method below 00604 pid_t KRun::run( const KService& _service, const KURL::List& _urls ) 00605 { 00606 return run( _service, _urls, false ); 00607 } 00608 00609 pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles ) 00610 { 00611 if (!_service.desktopEntryPath().isEmpty() && 00612 !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath())) 00613 { 00614 KMessageBox::sorry(0, i18n("You are not authorized to execute this service.")); 00615 return 0; 00616 } 00617 00618 if ( !tempFiles ) 00619 { 00620 // Remember we opened those urls, for the "recent documents" menu in kicker 00621 KURL::List::ConstIterator it = _urls.begin(); 00622 for(; it != _urls.end(); ++it) { 00623 //kdDebug(7010) << "KRecentDocument::adding " << (*it).url() << endl; 00624 KRecentDocument::add( *it, _service.desktopEntryName() ); 00625 } 00626 } 00627 00628 if ( tempFiles || _service.desktopEntryPath().isEmpty()) 00629 { 00630 return runTempService(_service, _urls, tempFiles); 00631 } 00632 00633 kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl; 00634 00635 if (!_urls.isEmpty()) { 00636 kdDebug(7010) << "First url " << _urls.first().url() << endl; 00637 } 00638 00639 QString error; 00640 int pid = 0; 00641 00642 int i = KApplication::startServiceByDesktopPath( 00643 _service.desktopEntryPath(), _urls.toStringList(), &error, 0L, &pid 00644 ); 00645 00646 if (i != 0) 00647 { 00648 kdDebug(7010) << error << endl; 00649 KMessageBox::sorry( 0L, error ); 00650 return 0; 00651 } 00652 00653 kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl; 00654 return (pid_t) pid; 00655 } 00656 00657 00658 pid_t KRun::run( const QString& _exec, const KURL::List& _urls, const QString& _name, 00659 const QString& _icon, const QString&, const QString&) 00660 { 00661 KService::Ptr service = new KService(_name, _exec, _icon); 00662 00663 return run(*service, _urls); 00664 } 00665 00666 pid_t KRun::runCommand( QString cmd ) 00667 { 00668 return KRun::runCommand( cmd, QString::null, QString::null ); 00669 } 00670 00671 pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName ) 00672 { 00673 kdDebug(7010) << "runCommand " << cmd << "," << execName << endl; 00674 KProcess * proc = new KProcess; 00675 proc->setUseShell(true); 00676 *proc << cmd; 00677 KService::Ptr service = KService::serviceByDesktopName( binaryName( cmd, true )); 00678 return runCommandInternal( proc, service.data(), binaryName( cmd, false ), execName, iconName ); 00679 } 00680 00681 KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo ) 00682 :m_timer(0,"KRun::timer") 00683 { 00684 init (url, 0, mode, isLocalFile, showProgressInfo); 00685 } 00686 00687 KRun::KRun( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile, 00688 bool showProgressInfo ) 00689 :m_timer(0,"KRun::timer") 00690 { 00691 init (url, window, mode, isLocalFile, showProgressInfo); 00692 } 00693 00694 void KRun::init ( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile, 00695 bool showProgressInfo ) 00696 { 00697 m_bFault = false; 00698 m_bAutoDelete = true; 00699 m_bProgressInfo = showProgressInfo; 00700 m_bFinished = false; 00701 m_job = 0L; 00702 m_strURL = url; 00703 m_bScanFile = false; 00704 m_bIsDirectory = false; 00705 m_bIsLocalFile = isLocalFile; 00706 m_mode = mode; 00707 d = new KRunPrivate; 00708 d->m_runExecutables = true; 00709 d->m_window = window; 00710 00711 // Start the timer. This means we will return to the event 00712 // loop and do initialization afterwards. 00713 // Reason: We must complete the constructor before we do anything else. 00714 m_bInit = true; 00715 connect( &m_timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) ); 00716 m_timer.start( 0, true ); 00717 kdDebug(7010) << " new KRun " << this << " " << url.prettyURL() << " timer=" << &m_timer << endl; 00718 00719 kapp->ref(); 00720 } 00721 00722 void KRun::init() 00723 { 00724 kdDebug(7010) << "INIT called" << endl; 00725 if ( !m_strURL.isValid() ) 00726 { 00727 d->m_showingError = true; 00728 KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) ); 00729 d->m_showingError = false; 00730 m_bFault = true; 00731 m_bFinished = true; 00732 m_timer.start( 0, true ); 00733 return; 00734 } 00735 if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL)) 00736 { 00737 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, m_strURL.prettyURL()); 00738 d->m_showingError = true; 00739 KMessageBoxWrapper::error( d->m_window, msg ); 00740 d->m_showingError = false; 00741 m_bFault = true; 00742 m_bFinished = true; 00743 m_timer.start( 0, true ); 00744 return; 00745 } 00746 00747 if ( !m_bIsLocalFile && m_strURL.isLocalFile() ) 00748 m_bIsLocalFile = true; 00749 00750 QString exec; 00751 if (m_strURL.protocol().startsWith("http")) 00752 { 00753 KConfigGroup cfg(KGlobal::config(), "General"); 00754 exec = cfg.readEntry("BrowserApplication"); 00755 } 00756 00757 if ( m_bIsLocalFile ) 00758 { 00759 if ( m_mode == 0 ) 00760 { 00761 struct stat buff; 00762 if ( stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 ) 00763 { 00764 d->m_showingError = true; 00765 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() ) ); 00766 d->m_showingError = false; 00767 m_bFault = true; 00768 m_bFinished = true; 00769 m_timer.start( 0, true ); 00770 return; 00771 } 00772 m_mode = buff.st_mode; 00773 } 00774 00775 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile ); 00776 assert( mime != 0L ); 00777 kdDebug(7010) << "MIME TYPE is " << mime->name() << endl; 00778 foundMimeType( mime->name() ); 00779 return; 00780 } 00781 else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( m_strURL ) ) { 00782 kdDebug(7010) << "Helper protocol" << endl; 00783 00784 bool ok; 00785 KURL::List urls; 00786 urls.append( m_strURL ); 00787 if (exec.isEmpty()) 00788 { 00789 exec = KProtocolInfo::exec( m_strURL.protocol() ); 00790 run( exec, urls ); 00791 ok = true; 00792 } 00793 else if (exec.startsWith("!")) 00794 { 00795 exec = exec.mid(1); // Literal command 00796 exec += " %u"; 00797 run( exec, urls ); 00798 ok = true; 00799 } 00800 else 00801 { 00802 KService::Ptr service = KService::serviceByStorageId( exec ); 00803 if (service) 00804 { 00805 run( *service, urls ); 00806 ok = true; 00807 } 00808 } 00809 00810 if (ok) 00811 { 00812 m_bFinished = true; 00813 // will emit the error and autodelete this 00814 m_timer.start( 0, true ); 00815 return; 00816 } 00817 } 00818 00819 // Did we already get the information that it is a directory ? 00820 if ( S_ISDIR( m_mode ) ) 00821 { 00822 foundMimeType( "inode/directory" ); 00823 return; 00824 } 00825 00826 // Let's see whether it is a directory 00827 00828 if ( !KProtocolInfo::supportsListing( m_strURL ) ) 00829 { 00830 //kdDebug(7010) << "Protocol has no support for listing" << endl; 00831 // No support for listing => it can't be a directory (example: http) 00832 scanFile(); 00833 return; 00834 } 00835 00836 kdDebug(7010) << "Testing directory (stating)" << endl; 00837 00838 // It may be a directory or a file, let's stat 00839 KIO::StatJob *job = KIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo ); 00840 job->setWindow (d->m_window); 00841 connect( job, SIGNAL( result( KIO::Job * ) ), 00842 this, SLOT( slotStatResult( KIO::Job * ) ) ); 00843 m_job = job; 00844 kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl; 00845 } 00846 00847 KRun::~KRun() 00848 { 00849 kdDebug(7010) << "KRun::~KRun() " << this << endl; 00850 m_timer.stop(); 00851 killJob(); 00852 kapp->deref(); 00853 kdDebug(7010) << "KRun::~KRun() done " << this << endl; 00854 delete d; 00855 } 00856 00857 void KRun::scanFile() 00858 { 00859 kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl; 00860 // First, let's check for well-known extensions 00861 // Not when there is a query in the URL, in any case. 00862 if ( m_strURL.query().isEmpty() ) 00863 { 00864 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL ); 00865 assert( mime != 0L ); 00866 if ( mime->name() != "application/octet-stream" || m_bIsLocalFile ) 00867 { 00868 kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl; 00869 foundMimeType( mime->name() ); 00870 return; 00871 } 00872 } 00873 00874 // No mimetype found, and the URL is not local (or fast mode not allowed). 00875 // We need to apply the 'KIO' method, i.e. either asking the server or 00876 // getting some data out of the file, to know what mimetype it is. 00877 00878 if ( !KProtocolInfo::supportsReading( m_strURL ) ) 00879 { 00880 kdError(7010) << "#### NO SUPPORT FOR READING!" << endl; 00881 m_bFault = true; 00882 m_bFinished = true; 00883 m_timer.start( 0, true ); 00884 return; 00885 } 00886 kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl; 00887 00888 KIO::TransferJob *job = KIO::get( m_strURL, false /*reload*/, m_bProgressInfo ); 00889 job->setWindow (d->m_window); 00890 connect(job, SIGNAL( result(KIO::Job *)), 00891 this, SLOT( slotScanFinished(KIO::Job *))); 00892 connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)), 00893 this, SLOT( slotScanMimeType(KIO::Job *, const QString &))); 00894 m_job = job; 00895 kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl; 00896 } 00897 00898 void KRun::slotTimeout() 00899 { 00900 kdDebug(7010) << this << " slotTimeout called" << endl; 00901 if ( m_bInit ) 00902 { 00903 m_bInit = false; 00904 init(); 00905 return; 00906 } 00907 00908 if ( m_bFault ){ 00909 emit error(); 00910 } 00911 if ( m_bFinished ){ 00912 emit finished(); 00913 } 00914 00915 if ( m_bScanFile ) 00916 { 00917 m_bScanFile = false; 00918 scanFile(); 00919 return; 00920 } 00921 else if ( m_bIsDirectory ) 00922 { 00923 m_bIsDirectory = false; 00924 foundMimeType( "inode/directory" ); 00925 return; 00926 } 00927 00928 if ( m_bAutoDelete ) 00929 { 00930 delete this; 00931 return; 00932 } 00933 } 00934 00935 void KRun::slotStatResult( KIO::Job * job ) 00936 { 00937 m_job = 0L; 00938 if (job->error()) 00939 { 00940 d->m_showingError = true; 00941 kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl; 00942 job->showErrorDialog(); 00943 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 00944 d->m_showingError = false; 00945 00946 m_bFault = true; 00947 m_bFinished = true; 00948 00949 // will emit the error and autodelete this 00950 m_timer.start( 0, true ); 00951 00952 } else { 00953 00954 kdDebug(7010) << "Finished" << endl; 00955 if(!dynamic_cast<KIO::StatJob*>(job)) 00956 kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl; 00957 00958 KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult(); 00959 KIO::UDSEntry::ConstIterator it = entry.begin(); 00960 for( ; it != entry.end(); it++ ) { 00961 if ( (*it).m_uds == KIO::UDS_FILE_TYPE ) 00962 { 00963 if ( S_ISDIR( (mode_t)((*it).m_long) ) ) 00964 m_bIsDirectory = true; // it's a dir 00965 else 00966 m_bScanFile = true; // it's a file 00967 break; 00968 } 00969 } 00970 // We should have found something 00971 assert ( m_bScanFile || m_bIsDirectory ); 00972 00973 // Start the timer. Once we get the timer event this 00974 // protocol server is back in the pool and we can reuse it. 00975 // This gives better performance than starting a new slave 00976 m_timer.start( 0, true ); 00977 } 00978 } 00979 00980 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype ) 00981 { 00982 if ( mimetype.isEmpty() ) 00983 kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug." << endl; 00984 foundMimeType( mimetype ); 00985 m_job = 0; 00986 } 00987 00988 void KRun::slotScanFinished( KIO::Job *job ) 00989 { 00990 m_job = 0; 00991 if (job->error()) 00992 { 00993 d->m_showingError = true; 00994 kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl; 00995 job->showErrorDialog(); 00996 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 00997 d->m_showingError = false; 00998 00999 m_bFault = true; 01000 m_bFinished = true; 01001 01002 // will emit the error and autodelete this 01003 m_timer.start( 0, true ); 01004 } 01005 } 01006 01007 void KRun::foundMimeType( const QString& type ) 01008 { 01009 kdDebug(7010) << "Resulting mime type is " << type << endl; 01010 01011 /* 01012 // Automatically unzip stuff 01013 01014 // Disabled since the new KIO doesn't have filters yet. 01015 01016 if ( type == "application/x-gzip" || 01017 type == "application/x-bzip" || 01018 type == "application/x-bzip2" ) 01019 { 01020 KURL::List lst = KURL::split( m_strURL ); 01021 if ( lst.isEmpty() ) 01022 { 01023 QString tmp = i18n( "Malformed URL" ); 01024 tmp += "\n"; 01025 tmp += m_strURL.url(); 01026 KMessageBoxWrapper::error( 0L, tmp ); 01027 return; 01028 } 01029 01030 if ( type == "application/x-gzip" ) 01031 lst.prepend( KURL( "gzip:/decompress" ) ); 01032 else if ( type == "application/x-bzip" ) 01033 lst.prepend( KURL( "bzip:/decompress" ) ); 01034 else if ( type == "application/x-bzip2" ) 01035 lst.prepend( KURL( "bzip2:/decompress" ) ); 01036 else if ( type == "application/x-tar" ) 01037 lst.prepend( KURL( "tar:/" ) ); 01038 01039 // Move the HTML style reference to the leftmost URL 01040 KURL::List::Iterator it = lst.begin(); 01041 ++it; 01042 (*lst.begin()).setRef( (*it).ref() ); 01043 (*it).setRef( QString::null ); 01044 01045 // Create the new URL 01046 m_strURL = KURL::join( lst ); 01047 01048 kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl; 01049 01050 killJob(); 01051 01052 // We don't know if this is a file or a directory. Let's test this first. 01053 // (For instance a tar.gz is a directory contained inside a file) 01054 // It may be a directory or a file, let's stat 01055 KIO::StatJob *job = KIO::stat( m_strURL, m_bProgressInfo ); 01056 connect( job, SIGNAL( result( KIO::Job * ) ), 01057 this, SLOT( slotStatResult( KIO::Job * ) ) ); 01058 m_job = job; 01059 01060 return; 01061 } 01062 */ 01063 if (m_job && m_job->inherits("KIO::TransferJob")) 01064 { 01065 KIO::TransferJob *job = static_cast<KIO::TransferJob *>(m_job); 01066 job->putOnHold(); 01067 KIO::Scheduler::publishSlaveOnHold(); 01068 m_job = 0; 01069 } 01070 01071 Q_ASSERT( !m_bFinished ); 01072 01073 // Suport for preferred service setting, see setPreferredService 01074 if ( !d->m_preferredService.isEmpty() ) { 01075 kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl; 01076 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService ); 01077 if ( serv && serv->hasServiceType( type ) ) 01078 { 01079 KURL::List lst; 01080 lst.append( m_strURL ); 01081 m_bFinished = KRun::run( *serv, lst ); 01086 } 01087 } 01088 01089 if (!m_bFinished && KRun::runURL( m_strURL, type, false, d->m_runExecutables )){ 01090 m_bFinished = true; 01091 } 01092 else{ 01093 m_bFinished = true; 01094 m_bFault = true; 01095 } 01096 01097 m_timer.start( 0, true ); 01098 } 01099 01100 void KRun::killJob() 01101 { 01102 if ( m_job ) 01103 { 01104 kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl; 01105 m_job->kill(); 01106 m_job = 0L; 01107 } 01108 } 01109 01110 void KRun::abort() 01111 { 01112 kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl; 01113 killJob(); 01114 // If we're showing an error message box, the rest will be done 01115 // after closing the msgbox -> don't autodelete nor emit signals now. 01116 if ( d->m_showingError ) 01117 return; 01118 m_bFault = true; 01119 m_bFinished = true; 01120 m_bInit = false; 01121 m_bScanFile = false; 01122 01123 // will emit the error and autodelete this 01124 m_timer.start( 0, true ); 01125 } 01126 01127 void KRun::setPreferredService( const QString& desktopEntryName ) 01128 { 01129 d->m_preferredService = desktopEntryName; 01130 } 01131 01132 void KRun::setRunExecutables(bool b) 01133 { 01134 d->m_runExecutables = b; 01135 } 01136 01137 bool KRun::isExecutable( const QString& serviceType ) 01138 { 01139 return ( serviceType == "application/x-desktop" || 01140 serviceType == "application/x-executable" || 01141 serviceType == "application/x-msdos-program" || 01142 serviceType == "application/x-shellscript" ); 01143 } 01144 01145 /****************/ 01146 01147 pid_t 01148 KProcessRunner::run(KProcess * p, const QString & binName) 01149 { 01150 return (new KProcessRunner(p, binName))->pid(); 01151 } 01152 01153 #ifdef Q_WS_X11 01154 pid_t 01155 KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id ) 01156 { 01157 return (new KProcessRunner(p, binName, id))->pid(); 01158 } 01159 #endif 01160 01161 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName ) 01162 : QObject(), 01163 process_(p), 01164 binName( _binName ) 01165 { 01166 QObject::connect( 01167 process_, SIGNAL(processExited(KProcess *)), 01168 this, SLOT(slotProcessExited(KProcess *))); 01169 01170 process_->start(); 01171 if ( !process_->pid() ) 01172 slotProcessExited( process_ ); 01173 } 01174 01175 #ifdef Q_WS_X11 01176 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& id ) 01177 : QObject(), 01178 process_(p), 01179 binName( _binName ), 01180 id_( id ) 01181 { 01182 QObject::connect( 01183 process_, SIGNAL(processExited(KProcess *)), 01184 this, SLOT(slotProcessExited(KProcess *))); 01185 01186 process_->start(); 01187 if ( !process_->pid() ) 01188 slotProcessExited( process_ ); 01189 } 01190 #endif 01191 01192 KProcessRunner::~KProcessRunner() 01193 { 01194 delete process_; 01195 } 01196 01197 pid_t 01198 KProcessRunner::pid() const 01199 { 01200 return process_->pid(); 01201 } 01202 01203 void 01204 KProcessRunner::slotProcessExited(KProcess * p) 01205 { 01206 if (p != process_) 01207 return; // Eh ? 01208 01209 kdDebug(7010) << "slotProcessExited " << binName << endl; 01210 kdDebug(7010) << "normalExit " << process_->normalExit() << endl; 01211 kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl; 01212 bool showErr = process_->normalExit() 01213 && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 ); 01214 if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) ) 01215 { 01216 // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist. 01217 // We can't just rely on that, but it's a good hint. 01218 // Before assuming its really so, we'll try to find the binName 01219 // relatively to current directory, and then in the PATH. 01220 if ( !QFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() ) 01221 { 01222 kapp->ref(); 01223 KMessageBox::sorry( 0L, i18n("Could not find the program '%1'").arg( binName ) ); 01224 kapp->deref(); 01225 } 01226 } 01227 #ifdef Q_WS_X11 01228 if( !id_.none()) 01229 { 01230 KStartupInfoData data; 01231 data.addPid( pid()); // announce this pid for the startup notification has finished 01232 data.setHostname(); 01233 KStartupInfo::sendFinish( id_, data ); 01234 } 01235 #endif 01236 delete this; 01237 } 01238 01239 void KRun::virtual_hook( int, void* ) 01240 { /*BASE::virtual_hook( id, data );*/ } 01241 01242 #include "krun.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:29:28 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003