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