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"); 00420 if( terminal.isEmpty() ) 00421 { 00422 if( !KStandardDirs::findExe( "konsole" ).isEmpty() ) 00423 terminal = "konsole"; 00424 else 00425 terminal = "xvt"; 00426 } 00427 if (terminal == "konsole") 00428 terminal += " -caption=%c %i %m"; 00429 terminal += " "; 00430 terminal += _service.terminalOptions(); 00431 if( !mx1.expandMacrosShellQuote( terminal ) ) { 00432 kdWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'" << endl; 00433 return QStringList(); 00434 } 00435 mx2.expandMacrosShellQuote( terminal ); 00436 if (has_shell) 00437 result << terminal; 00438 else 00439 result = KShell::splitArgs( terminal ); // assuming that the term spec never needs a shell! 00440 result << "-e"; 00441 } 00442 00443 int err; 00444 if (_service.substituteUid()) { 00445 if (_service.terminal()) 00446 result << "su"; 00447 else 00448 result << "kdesu" << "-u"; 00449 result << _service.username() << "-c"; 00450 KShell::splitArgs(exec, KShell::AbortOnMeta, &err); 00451 if (err == KShell::FoundMeta) { 00452 shellQuote( exec ); 00453 exec.prepend( "/bin/sh -c " ); 00454 } else if (err != KShell::NoError) 00455 goto synerr; 00456 if (has_shell) 00457 shellQuote( exec ); 00458 result << exec; 00459 } else { 00460 if (has_shell) { 00461 if (_service.terminal()) { 00462 KShell::splitArgs(exec, KShell::AbortOnMeta, &err); 00463 if (err == KShell::FoundMeta) { 00464 shellQuote( exec ); 00465 exec.prepend( "/bin/sh -c " ); 00466 } else if (err != KShell::NoError) 00467 goto synerr; 00468 } 00469 result << exec; 00470 } else { 00471 result += KShell::splitArgs(exec, KShell::AbortOnMeta, &err); 00472 if (err == KShell::FoundMeta) 00473 result << "/bin/sh" << "-c" << exec; 00474 else if (err != KShell::NoError) 00475 goto synerr; 00476 } 00477 } 00478 00479 return result; 00480 00481 synerr: 00482 kdWarning() << "KRun: syntax error in command `" << _service.exec() << "', service `" << _service.name() << "'" << endl; 00483 return QStringList(); 00484 } 00485 00486 //static 00487 QString KRun::binaryName( const QString & execLine, bool removePath ) 00488 { 00489 // Remove parameters and/or trailing spaces. 00490 QStringList args = KShell::splitArgs( execLine ); 00491 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) 00492 if (!(*it).contains('=')) 00493 // Remove path if wanted 00494 return removePath ? (*it).mid((*it).findRev('/') + 1) : *it; 00495 return QString::null; 00496 } 00497 00498 static pid_t runCommandInternal( KProcess* proc, const KService* service, const QString& binName, 00499 const QString &execName, const QString & iconName ) 00500 { 00501 if ( service && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() )) 00502 { 00503 KMessageBox::sorry(0, i18n("You are not authorized to execute this file.")); 00504 return 0; 00505 } 00506 QString bin = KRun::binaryName( binName, true ); 00507 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification 00508 bool startup_notify = false; 00509 QCString wmclass; 00510 KStartupInfoId id; 00511 if( service && service->property( "StartupNotify" ).isValid()) 00512 { 00513 startup_notify = service->property( "StartupNotify" ).toBool(); 00514 wmclass = service->property( "StartupWMClass" ).toString().latin1(); 00515 } 00516 else if( service && service->property( "X-KDE-StartupNotify" ).isValid()) 00517 { 00518 startup_notify = service->property( "X-KDE-StartupNotify" ).toBool(); 00519 wmclass = service->property( "X-KDE-WMClass" ).toString().latin1(); 00520 } 00521 else // non-compliant app ( .desktop file ) 00522 { 00523 if( service && service->type() == "Application" ) 00524 { 00525 startup_notify = true; // doesn't have .desktop entries needed 00526 wmclass = "0"; // start as non-compliant 00527 } 00528 } 00529 if( startup_notify ) 00530 { 00531 id.initId(); 00532 id.setupStartupEnv(); 00533 KStartupInfoData data; 00534 data.setHostname(); 00535 data.setBin( bin ); 00536 data.setName( execName.isEmpty() ? service->name() : execName ); 00537 data.setDescription( i18n( "Launching %1" ).arg( data.name())); 00538 data.setIcon( iconName.isEmpty() ? service->icon() : iconName ); 00539 #ifdef Q_WS_X11 00540 data.setTimestamp( qt_x_user_time ); 00541 #endif 00542 if( !wmclass.isEmpty()) 00543 data.setWMClass( wmclass ); 00544 data.setDesktop( KWin::currentDesktop()); 00545 KStartupInfo::sendStartup( id, data ); 00546 } 00547 pid_t pid = KProcessRunner::run( proc, binName, id ); 00548 if( startup_notify && pid ) 00549 { 00550 KStartupInfoData data; 00551 data.addPid( pid ); 00552 KStartupInfo::sendChange( id, data ); 00553 KStartupInfo::resetStartupEnv(); 00554 } 00555 return pid; 00556 #else 00557 Q_UNUSED( execName ); 00558 Q_UNUSED( iconName ); 00559 return KProcessRunner::run( proc, bin ); 00560 #endif 00561 } 00562 00563 static pid_t runTempService( const KService& _service, const KURL::List& _urls, bool tempFiles ) 00564 { 00565 if (!_urls.isEmpty()) { 00566 kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl; 00567 } 00568 00569 QStringList args; 00570 if ((_urls.count() > 1) && !_service.allowMultipleFiles()) 00571 { 00572 // We need to launch the application N times. That sucks. 00573 // We ignore the result for application 2 to N. 00574 // For the first file we launch the application in the 00575 // usual way. The reported result is based on this 00576 // application. 00577 KURL::List::ConstIterator it = _urls.begin(); 00578 while(++it != _urls.end()) 00579 { 00580 KURL::List singleUrl; 00581 singleUrl.append(*it); 00582 runTempService( _service, singleUrl, tempFiles ); 00583 } 00584 KURL::List singleUrl; 00585 singleUrl.append(_urls.first()); 00586 args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles); 00587 } 00588 else 00589 { 00590 args = KRun::processDesktopExec(_service, _urls, false, tempFiles); 00591 } 00592 kdDebug(7010) << "runTempService: KProcess args=" << args << endl; 00593 00594 KProcess * proc = new KProcess; 00595 *proc << args; 00596 00597 if (!_service.path().isEmpty()) 00598 proc->setWorkingDirectory(_service.path()); 00599 00600 return runCommandInternal( proc, &_service, _service.exec(), _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 00749 m_bIsLocalFile = true; 00750 00751 if ( m_bIsLocalFile ) 00752 { 00753 if ( m_mode == 0 ) 00754 { 00755 struct stat buff; 00756 if ( stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 ) 00757 { 00758 d->m_showingError = true; 00759 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() ) ); 00760 d->m_showingError = false; 00761 m_bFault = true; 00762 m_bFinished = true; 00763 m_timer.start( 0, true ); 00764 return; 00765 } 00766 m_mode = buff.st_mode; 00767 } 00768 00769 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile ); 00770 assert( mime != 0L ); 00771 kdDebug(7010) << "MIME TYPE is " << mime->name() << endl; 00772 foundMimeType( mime->name() ); 00773 return; 00774 } 00775 else if ( KProtocolInfo::isHelperProtocol( m_strURL ) ) { 00776 kdDebug(7010) << "Helper protocol" << endl; 00777 00778 KURL::List urls; 00779 urls.append( m_strURL ); 00780 QString exec = KProtocolInfo::exec( m_strURL.protocol() ); 00781 run( exec, urls ); 00782 00783 m_bFinished = true; 00784 // will emit the error and autodelete this 00785 m_timer.start( 0, true ); 00786 return; 00787 } 00788 00789 // Did we already get the information that it is a directory ? 00790 if ( S_ISDIR( m_mode ) ) 00791 { 00792 foundMimeType( "inode/directory" ); 00793 return; 00794 } 00795 00796 // Let's see whether it is a directory 00797 00798 if ( !KProtocolInfo::supportsListing( m_strURL ) ) 00799 { 00800 //kdDebug(7010) << "Protocol has no support for listing" << endl; 00801 // No support for listing => it can't be a directory (example: http) 00802 scanFile(); 00803 return; 00804 } 00805 00806 kdDebug(7010) << "Testing directory (stating)" << endl; 00807 00808 // It may be a directory or a file, let's stat 00809 KIO::StatJob *job = KIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo ); 00810 job->setWindow (d->m_window); 00811 connect( job, SIGNAL( result( KIO::Job * ) ), 00812 this, SLOT( slotStatResult( KIO::Job * ) ) ); 00813 m_job = job; 00814 kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl; 00815 } 00816 00817 KRun::~KRun() 00818 { 00819 kdDebug(7010) << "KRun::~KRun() " << this << endl; 00820 m_timer.stop(); 00821 killJob(); 00822 kapp->deref(); 00823 kdDebug(7010) << "KRun::~KRun() done " << this << endl; 00824 delete d; 00825 } 00826 00827 void KRun::scanFile() 00828 { 00829 kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl; 00830 // First, let's check for well-known extensions 00831 // Not when there is a query in the URL, in any case. 00832 if ( m_strURL.query().isEmpty() ) 00833 { 00834 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL ); 00835 assert( mime != 0L ); 00836 if ( mime->name() != "application/octet-stream" || m_bIsLocalFile ) 00837 { 00838 kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl; 00839 foundMimeType( mime->name() ); 00840 return; 00841 } 00842 } 00843 00844 // No mimetype found, and the URL is not local (or fast mode not allowed). 00845 // We need to apply the 'KIO' method, i.e. either asking the server or 00846 // getting some data out of the file, to know what mimetype it is. 00847 00848 if ( !KProtocolInfo::supportsReading( m_strURL ) ) 00849 { 00850 kdError(7010) << "#### NO SUPPORT FOR READING!" << endl; 00851 m_bFault = true; 00852 m_bFinished = true; 00853 m_timer.start( 0, true ); 00854 return; 00855 } 00856 kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl; 00857 00858 KIO::TransferJob *job = KIO::get( m_strURL, false /*reload*/, m_bProgressInfo ); 00859 job->setWindow (d->m_window); 00860 connect(job, SIGNAL( result(KIO::Job *)), 00861 this, SLOT( slotScanFinished(KIO::Job *))); 00862 connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)), 00863 this, SLOT( slotScanMimeType(KIO::Job *, const QString &))); 00864 m_job = job; 00865 kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl; 00866 } 00867 00868 void KRun::slotTimeout() 00869 { 00870 kdDebug(7010) << this << " slotTimeout called" << endl; 00871 if ( m_bInit ) 00872 { 00873 m_bInit = false; 00874 init(); 00875 return; 00876 } 00877 00878 if ( m_bFault ){ 00879 emit error(); 00880 } 00881 if ( m_bFinished ){ 00882 emit finished(); 00883 } 00884 00885 if ( m_bScanFile ) 00886 { 00887 m_bScanFile = false; 00888 scanFile(); 00889 return; 00890 } 00891 else if ( m_bIsDirectory ) 00892 { 00893 m_bIsDirectory = false; 00894 foundMimeType( "inode/directory" ); 00895 return; 00896 } 00897 00898 if ( m_bAutoDelete ) 00899 { 00900 delete this; 00901 return; 00902 } 00903 } 00904 00905 void KRun::slotStatResult( KIO::Job * job ) 00906 { 00907 m_job = 0L; 00908 if (job->error()) 00909 { 00910 d->m_showingError = true; 00911 kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl; 00912 job->showErrorDialog(); 00913 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 00914 d->m_showingError = false; 00915 00916 m_bFault = true; 00917 m_bFinished = true; 00918 00919 // will emit the error and autodelete this 00920 m_timer.start( 0, true ); 00921 00922 } else { 00923 00924 kdDebug(7010) << "Finished" << endl; 00925 if(!dynamic_cast<KIO::StatJob*>(job)) 00926 kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl; 00927 00928 KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult(); 00929 KIO::UDSEntry::ConstIterator it = entry.begin(); 00930 for( ; it != entry.end(); it++ ) { 00931 if ( (*it).m_uds == KIO::UDS_FILE_TYPE ) 00932 { 00933 if ( S_ISDIR( (mode_t)((*it).m_long) ) ) 00934 m_bIsDirectory = true; // it's a dir 00935 else 00936 m_bScanFile = true; // it's a file 00937 break; 00938 } 00939 } 00940 // We should have found something 00941 assert ( m_bScanFile || m_bIsDirectory ); 00942 00943 // Start the timer. Once we get the timer event this 00944 // protocol server is back in the pool and we can reuse it. 00945 // This gives better performance than starting a new slave 00946 m_timer.start( 0, true ); 00947 } 00948 } 00949 00950 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype ) 00951 { 00952 if ( mimetype.isEmpty() ) 00953 kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug." << endl; 00954 foundMimeType( mimetype ); 00955 m_job = 0; 00956 } 00957 00958 void KRun::slotScanFinished( KIO::Job *job ) 00959 { 00960 m_job = 0; 00961 if (job->error()) 00962 { 00963 d->m_showingError = true; 00964 kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl; 00965 job->showErrorDialog(); 00966 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 00967 d->m_showingError = false; 00968 00969 m_bFault = true; 00970 m_bFinished = true; 00971 00972 // will emit the error and autodelete this 00973 m_timer.start( 0, true ); 00974 } 00975 } 00976 00977 void KRun::foundMimeType( const QString& type ) 00978 { 00979 kdDebug(7010) << "Resulting mime type is " << type << endl; 00980 00981 /* 00982 // Automatically unzip stuff 00983 00984 // Disabled since the new KIO doesn't have filters yet. 00985 00986 if ( type == "application/x-gzip" || 00987 type == "application/x-bzip" || 00988 type == "application/x-bzip2" ) 00989 { 00990 KURL::List lst = KURL::split( m_strURL ); 00991 if ( lst.isEmpty() ) 00992 { 00993 QString tmp = i18n( "Malformed URL" ); 00994 tmp += "\n"; 00995 tmp += m_strURL.url(); 00996 KMessageBoxWrapper::error( 0L, tmp ); 00997 return; 00998 } 00999 01000 if ( type == "application/x-gzip" ) 01001 lst.prepend( KURL( "gzip:/decompress" ) ); 01002 else if ( type == "application/x-bzip" ) 01003 lst.prepend( KURL( "bzip:/decompress" ) ); 01004 else if ( type == "application/x-bzip2" ) 01005 lst.prepend( KURL( "bzip2:/decompress" ) ); 01006 else if ( type == "application/x-tar" ) 01007 lst.prepend( KURL( "tar:/" ) ); 01008 01009 // Move the HTML style reference to the leftmost URL 01010 KURL::List::Iterator it = lst.begin(); 01011 ++it; 01012 (*lst.begin()).setRef( (*it).ref() ); 01013 (*it).setRef( QString::null ); 01014 01015 // Create the new URL 01016 m_strURL = KURL::join( lst ); 01017 01018 kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl; 01019 01020 killJob(); 01021 01022 // We don't know if this is a file or a directory. Let's test this first. 01023 // (For instance a tar.gz is a directory contained inside a file) 01024 // It may be a directory or a file, let's stat 01025 KIO::StatJob *job = KIO::stat( m_strURL, m_bProgressInfo ); 01026 connect( job, SIGNAL( result( KIO::Job * ) ), 01027 this, SLOT( slotStatResult( KIO::Job * ) ) ); 01028 m_job = job; 01029 01030 return; 01031 } 01032 */ 01033 if (m_job && m_job->inherits("KIO::TransferJob")) 01034 { 01035 KIO::TransferJob *job = static_cast<KIO::TransferJob *>(m_job); 01036 job->putOnHold(); 01037 KIO::Scheduler::publishSlaveOnHold(); 01038 m_job = 0; 01039 } 01040 01041 Q_ASSERT( !m_bFinished ); 01042 01043 // Suport for preferred service setting, see setPreferredService 01044 if ( !d->m_preferredService.isEmpty() ) { 01045 kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl; 01046 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService ); 01047 if ( serv && serv->hasServiceType( type ) ) 01048 { 01049 KURL::List lst; 01050 lst.append( m_strURL ); 01051 m_bFinished = KRun::run( *serv, lst ); 01056 } 01057 } 01058 01059 if (!m_bFinished && KRun::runURL( m_strURL, type, false, d->m_runExecutables )){ 01060 m_bFinished = true; 01061 } 01062 else{ 01063 m_bFinished = true; 01064 m_bFault = true; 01065 } 01066 01067 m_timer.start( 0, true ); 01068 } 01069 01070 void KRun::killJob() 01071 { 01072 if ( m_job ) 01073 { 01074 kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl; 01075 m_job->kill(); 01076 m_job = 0L; 01077 } 01078 } 01079 01080 void KRun::abort() 01081 { 01082 kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl; 01083 killJob(); 01084 // If we're showing an error message box, the rest will be done 01085 // after closing the msgbox -> don't autodelete nor emit signals now. 01086 if ( d->m_showingError ) 01087 return; 01088 m_bFault = true; 01089 m_bFinished = true; 01090 m_bInit = false; 01091 m_bScanFile = false; 01092 01093 // will emit the error and autodelete this 01094 m_timer.start( 0, true ); 01095 } 01096 01097 void KRun::setPreferredService( const QString& desktopEntryName ) 01098 { 01099 d->m_preferredService = desktopEntryName; 01100 } 01101 01102 void KRun::setRunExecutables(bool b) 01103 { 01104 d->m_runExecutables = b; 01105 } 01106 01107 bool KRun::isExecutable( const QString& serviceType ) 01108 { 01109 return ( serviceType == "application/x-desktop" || 01110 serviceType == "application/x-executable" || 01111 serviceType == "application/x-msdos-program" || 01112 serviceType == "application/x-shellscript" ); 01113 } 01114 01115 /****************/ 01116 01117 pid_t 01118 KProcessRunner::run(KProcess * p, const QString & binName) 01119 { 01120 return (new KProcessRunner(p, binName))->pid(); 01121 } 01122 01123 #ifdef Q_WS_X11 01124 pid_t 01125 KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id ) 01126 { 01127 return (new KProcessRunner(p, binName, id))->pid(); 01128 } 01129 #endif 01130 01131 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName ) 01132 : QObject(), 01133 process_(p), 01134 binName( _binName ) 01135 { 01136 QObject::connect( 01137 process_, SIGNAL(processExited(KProcess *)), 01138 this, SLOT(slotProcessExited(KProcess *))); 01139 01140 process_->start(); 01141 if ( !process_->pid() ) 01142 slotProcessExited( process_ ); 01143 } 01144 01145 #ifdef Q_WS_X11 01146 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& id ) 01147 : QObject(), 01148 process_(p), 01149 binName( _binName ), 01150 id_( id ) 01151 { 01152 QObject::connect( 01153 process_, SIGNAL(processExited(KProcess *)), 01154 this, SLOT(slotProcessExited(KProcess *))); 01155 01156 process_->start(); 01157 if ( !process_->pid() ) 01158 slotProcessExited( process_ ); 01159 } 01160 #endif 01161 01162 KProcessRunner::~KProcessRunner() 01163 { 01164 delete process_; 01165 } 01166 01167 pid_t 01168 KProcessRunner::pid() const 01169 { 01170 return process_->pid(); 01171 } 01172 01173 void 01174 KProcessRunner::slotProcessExited(KProcess * p) 01175 { 01176 if (p != process_) 01177 return; // Eh ? 01178 01179 kdDebug(7010) << "slotProcessExited " << binName << endl; 01180 kdDebug(7010) << "normalExit " << process_->normalExit() << endl; 01181 kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl; 01182 bool showErr = process_->normalExit() 01183 && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 ); 01184 if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) ) 01185 { 01186 // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist. 01187 // We can't just rely on that, but it's a good hint. 01188 // Before assuming its really so, we'll try to find the binName 01189 // relatively to current directory, and then in the PATH. 01190 if ( !QFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() ) 01191 { 01192 kapp->ref(); 01193 KMessageBox::sorry( 0L, i18n("Couldn't find the program '%1'").arg( binName ) ); 01194 kapp->deref(); 01195 } 01196 } 01197 #ifdef Q_WS_X11 01198 if( !id_.none()) 01199 { 01200 KStartupInfoData data; 01201 data.addPid( pid()); // announce this pid for the startup notification has finished 01202 data.setHostname(); 01203 KStartupInfo::sendFinish( id_, data ); 01204 } 01205 #endif 01206 delete this; 01207 } 01208 01209 void KRun::virtual_hook( int, void* ) 01210 { /*BASE::virtual_hook( id, data );*/ } 01211 01212 #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 Fri Oct 8 11:14:59 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003