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