krun.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Torben Weis <weis@kde.org> 00003 Copyright (C) 2006 David Faure <faure@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "krun.h" 00022 00023 #include <assert.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <unistd.h> 00027 #include <typeinfo> 00028 00029 #include <tqwidget.h> 00030 #include <tqguardedptr.h> 00031 00032 #include "kuserprofile.h" 00033 #include "kmimetype.h" 00034 #include "kmimemagic.h" 00035 #include "tdeio/job.h" 00036 #include "tdeio/global.h" 00037 #include "tdeio/scheduler.h" 00038 #include "tdeio/netaccess.h" 00039 #include "tdefile/kopenwith.h" 00040 #include "tdefile/tderecentdocument.h" 00041 00042 #include <kdatastream.h> 00043 #include <kmessageboxwrapper.h> 00044 #include <kurl.h> 00045 #include <tdeapplication.h> 00046 #include <kdebug.h> 00047 #include <tdelocale.h> 00048 #include <kprotocolinfo.h> 00049 #include <kstandarddirs.h> 00050 #include <kprocess.h> 00051 #include <dcopclient.h> 00052 #include <tqfile.h> 00053 #include <tqfileinfo.h> 00054 #include <tqtextstream.h> 00055 #include <tqdatetime.h> 00056 #include <tqregexp.h> 00057 #include <kdesktopfile.h> 00058 #include <tdestartupinfo.h> 00059 #include <kmacroexpander.h> 00060 #include <kshell.h> 00061 #include <kde_file.h> 00062 #include <kstringhandler.h> 00063 00064 #ifdef Q_WS_X11 00065 #include <twin.h> 00066 #endif 00067 00068 class KRun::KRunPrivate 00069 { 00070 public: 00071 KRunPrivate() { m_showingError = false; } 00072 00073 bool m_showingError; 00074 bool m_runExecutables; 00075 00076 TQString m_preferredService; 00077 TQString m_externalBrowser; 00078 TQString m_localPath; 00079 TQString m_suggestedFileName; 00080 TQGuardedPtr <TQWidget> m_window; 00081 TQCString m_asn; 00082 }; 00083 00084 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype ) 00085 { 00086 return runURL( u, _mimetype, false, true, TQString::null ); 00087 } 00088 00089 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile ) 00090 { 00091 return runURL( u, _mimetype, tempFile, true, TQString::null ); 00092 } 00093 00094 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile, bool runExecutables ) 00095 { 00096 return runURL( u, _mimetype, tempFile, runExecutables, TQString::null ); 00097 } 00098 00099 bool KRun::isExecutableFile( const KURL& url, const TQString &mimetype ) 00100 { 00101 if ( !url.isLocalFile() ) 00102 return false; 00103 TQFileInfo file( url.path() ); 00104 if ( file.isExecutable() ) // Got a prospective file to run 00105 { 00106 KMimeType::Ptr mimeType = KMimeType::mimeType( mimetype ); 00107 00108 if ( mimeType->is("application/x-executable") || mimeType->is("application/x-executable-script") ) 00109 return true; 00110 } 00111 return false; 00112 } 00113 00114 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile, bool runExecutables, const TQString& suggestedFileName ) 00115 { 00116 return runURL( u, _mimetype, NULL, "", tempFile, runExecutables, suggestedFileName ); 00117 } 00118 00119 // This is called by foundMimeType, since it knows the mimetype of the URL 00120 pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, TQWidget* window, const TQCString& asn, 00121 bool tempFile, bool runExecutables, const TQString& suggestedFileName ) 00122 { 00123 bool noRun = false; 00124 bool noAuth = false; 00125 if ( _mimetype == "inode/directory-locked" ) 00126 { 00127 KMessageBoxWrapper::error( window, 00128 i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>").arg(u.htmlURL()) ); 00129 return 0; 00130 } 00131 else if ( (_mimetype == "application/x-desktop") || 00132 (_mimetype == "media/builtin-mydocuments") || 00133 (_mimetype == "media/builtin-mycomputer") || 00134 (_mimetype == "media/builtin-mynetworkplaces") || 00135 (_mimetype == "media/builtin-printers") || 00136 (_mimetype == "media/builtin-trash") || 00137 (_mimetype == "media/builtin-webbrowser") ) 00138 { 00139 if ( u.isLocalFile() && runExecutables ) 00140 return KDEDesktopMimeType::run( u, true ); 00141 } 00142 else if ( isExecutableFile(u, _mimetype) ) 00143 { 00144 if ( u.isLocalFile() && runExecutables) 00145 { 00146 if (kapp->authorize("shell_access")) 00147 { 00148 TQString path = u.path(); 00149 shellQuote( path ); 00150 return (KRun::runCommand(path, TQString::null, TQString::null, window, asn)); // just execute the url as a command 00151 // ## TODO implement deleting the file if tempFile==true 00152 } 00153 else 00154 { 00155 noAuth = true; 00156 } 00157 } 00158 else if (_mimetype == "application/x-executable") 00159 noRun = true; 00160 } 00161 else if ( isExecutable(_mimetype) ) 00162 { 00163 if (!runExecutables) 00164 noRun = true; 00165 00166 if (!kapp->authorize("shell_access")) 00167 noAuth = true; 00168 } 00169 00170 if ( noRun ) 00171 { 00172 KMessageBox::sorry( window, 00173 i18n("<qt>The file <b>%1</b> is an executable program. " 00174 "For safety it will not be started.</qt>").arg(u.htmlURL())); 00175 return 0; 00176 } 00177 if ( noAuth ) 00178 { 00179 KMessageBoxWrapper::error( window, 00180 i18n("<qt>You do not have permission to run <b>%1</b>.</qt>").arg(u.htmlURL()) ); 00181 return 0; 00182 } 00183 00184 KURL::List lst; 00185 lst.append( u ); 00186 00187 static const TQString& app_str = TDEGlobal::staticQString("Application"); 00188 00189 KService::Ptr offer = KServiceTypeProfile::preferredService( _mimetype, app_str ); 00190 00191 if ( !offer ) 00192 { 00193 // Open-with dialog 00194 // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog ! 00195 // Hmm, in fact KOpenWithDlg::setServiceType already guesses the mimetype from the first URL of the list... 00196 return displayOpenWithDialog( lst, tempFile, suggestedFileName ); 00197 } 00198 00199 return KRun::run( *offer, lst, window, asn, tempFile, suggestedFileName ); 00200 } 00201 00202 bool KRun::displayOpenWithDialog( const KURL::List& lst ) 00203 { 00204 return displayOpenWithDialog( lst, false, TQString::null ); 00205 } 00206 00207 bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles ) 00208 { 00209 return displayOpenWithDialog( lst, tempFiles, TQString::null ); 00210 } 00211 00212 bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles, const TQString& suggestedFileName ) 00213 { 00214 if (kapp && !kapp->authorizeTDEAction("openwith")) 00215 { 00216 // TODO: Better message, i18n freeze :-( 00217 KMessageBox::sorry(0L, i18n("You are not authorized to open this file.")); 00218 return false; 00219 } 00220 00221 KOpenWithDlg l( lst, i18n("Open with:"), TQString::null, 0L ); 00222 if ( l.exec() ) 00223 { 00224 KService::Ptr service = l.service(); 00225 if ( !!service ) 00226 return KRun::run( *service, lst, 0 /*window*/, tempFiles, suggestedFileName ); 00227 00228 kdDebug(7010) << "No service set, running " << l.text() << endl; 00229 return KRun::run( l.text(), lst, suggestedFileName ); // TODO handle tempFiles 00230 } 00231 return false; 00232 } 00233 00234 void KRun::shellQuote( TQString &_str ) 00235 { 00236 // Credits to Walter, says Bernd G. :) 00237 if (_str.isEmpty()) // Don't create an explicit empty parameter 00238 return; 00239 TQChar q('\''); 00240 _str.replace(q, "'\\''").prepend(q).append(q); 00241 } 00242 00243 00244 class KRunMX1 : public KMacroExpanderBase { 00245 public: 00246 KRunMX1( const KService &_service ) : 00247 KMacroExpanderBase( '%' ), hasUrls( false ), hasSpec( false ), service( _service ) {} 00248 bool hasUrls:1, hasSpec:1; 00249 00250 protected: 00251 virtual int expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ); 00252 00253 private: 00254 const KService &service; 00255 }; 00256 00257 int 00258 KRunMX1::expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ) 00259 { 00260 uint option = str[pos + 1]; 00261 switch( option ) { 00262 case 'c': 00263 ret << service.name().replace( '%', "%%" ); 00264 break; 00265 case 'k': 00266 ret << service.desktopEntryPath().replace( '%', "%%" ); 00267 break; 00268 case 'i': 00269 ret << "-icon" << service.icon().replace( '%', "%%" ); 00270 break; 00271 case 'm': 00272 ret << "-miniicon" << service.icon().replace( '%', "%%" ); 00273 break; 00274 case 'u': 00275 case 'U': 00276 hasUrls = true; 00277 /* fallthrough */ 00278 case 'f': 00279 case 'F': 00280 case 'n': 00281 case 'N': 00282 case 'd': 00283 case 'D': 00284 case 'v': 00285 hasSpec = true; 00286 /* fallthrough */ 00287 default: 00288 return -2; // subst with same and skip 00289 } 00290 return 2; 00291 } 00292 00293 class KRunMX2 : public KMacroExpanderBase { 00294 public: 00295 KRunMX2( const KURL::List &_urls ) : 00296 KMacroExpanderBase( '%' ), ignFile( false ), urls( _urls ) {} 00297 bool ignFile:1; 00298 00299 protected: 00300 virtual int expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ); 00301 00302 private: 00303 void subst( int option, const KURL &url, TQStringList &ret ); 00304 00305 const KURL::List &urls; 00306 }; 00307 00308 void 00309 KRunMX2::subst( int option, const KURL &url, TQStringList &ret ) 00310 { 00311 switch( option ) { 00312 case 'u': 00313 ret << url.pathOrURL(); 00314 break; 00315 case 'd': 00316 ret << url.directory(); 00317 break; 00318 case 'f': 00319 ret << url.path(); 00320 break; 00321 case 'n': 00322 ret << url.fileName(); 00323 break; 00324 case 'v': 00325 if (url.isLocalFile() && TQFile::exists( url.path() ) ) 00326 ret << KDesktopFile( url.path(), true ).readEntry( "Dev" ); 00327 break; 00328 } 00329 return; 00330 } 00331 00332 int 00333 KRunMX2::expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret ) 00334 { 00335 uint option = str[pos + 1]; 00336 switch( option ) { 00337 case 'f': 00338 case 'u': 00339 case 'n': 00340 case 'd': 00341 case 'v': 00342 if( urls.isEmpty() ) { 00343 if (!ignFile) 00344 kdDebug() << "KRun::processDesktopExec: No URLs supplied to single-URL service " << str << endl; 00345 } else if( urls.count() > 1 ) 00346 kdWarning() << "KRun::processDesktopExec: " << urls.count() << " URLs supplied to single-URL service " << str << endl; 00347 else 00348 subst( option, urls.first(), ret ); 00349 break; 00350 case 'F': 00351 case 'U': 00352 case 'N': 00353 case 'D': 00354 option += 'a' - 'A'; 00355 for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) 00356 subst( option, *it, ret ); 00357 break; 00358 case '%': 00359 ret = "%"; 00360 break; 00361 default: 00362 return -2; // subst with same and skip 00363 } 00364 return 2; 00365 } 00366 00367 // BIC: merge methods below 00368 TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell) { 00369 return processDesktopExec( _service, _urls, has_shell, false, TQString::null ); 00370 } 00371 00372 TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles) 00373 { 00374 return processDesktopExec( _service, _urls, has_shell, tempFiles, TQString::null ); 00375 } 00376 00377 TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles, const TQString& suggestedFileName) 00378 { 00379 TQString exec = _service.exec(); 00380 TQStringList result; 00381 bool appHasTempFileOption; 00382 00383 KRunMX1 mx1( _service ); 00384 KRunMX2 mx2( _urls ); 00385 00387 TQRegExp re("^\\s*(?:/bin/)?sh\\s+-c\\s+(.*)$"); 00388 if (!re.search( exec )) { 00389 exec = TQString(re.cap( 1 )).stripWhiteSpace(); 00390 for (uint pos = 0; pos < exec.length(); ) { 00391 TQChar c = exec.unicode()[pos]; 00392 if (c != '\'' && c != '"') 00393 goto synerr; // what else can we do? after normal parsing the substs would be insecure 00394 int pos2 = exec.find( c, pos + 1 ) - 1; 00395 if (pos2 < 0) 00396 goto synerr; // quoting error 00397 memcpy( (void *)(exec.unicode() + pos), exec.unicode() + pos + 1, (pos2 - pos) * sizeof(TQChar)); 00398 pos = pos2; 00399 exec.remove( pos, 2 ); 00400 } 00401 } 00402 00403 if( !mx1.expandMacrosShellQuote( exec ) ) 00404 goto synerr; // error in shell syntax 00405 00406 // FIXME: the current way of invoking tdeioexec disables term and su use 00407 00408 // Check if we need "tempexec" (tdeioexec in fact) 00409 appHasTempFileOption = tempFiles && _service.property("X-TDE-HasTempFileOption").toBool(); 00410 if( tempFiles && !appHasTempFileOption && _urls.size() ) { 00411 result << "tdeioexec" << "--tempfiles" << exec; 00412 result += _urls.toStringList(); 00413 if (has_shell) 00414 result = KShell::joinArgs( result ); 00415 return result; 00416 } 00417 00418 // Check if we need tdeioexec 00419 if( !mx1.hasUrls ) { 00420 for( KURL::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it ) 00421 if ( !(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it) ) { 00422 // We need to run the app through tdeioexec 00423 result << "tdeioexec"; 00424 if ( tempFiles ) 00425 result << "--tempfiles"; 00426 if ( !suggestedFileName.isEmpty() ) { 00427 result << "--suggestedfilename"; 00428 result << suggestedFileName; 00429 } 00430 result << exec; 00431 result += _urls.toStringList(); 00432 if (has_shell) 00433 result = KShell::joinArgs( result ); 00434 return result; 00435 } 00436 } 00437 00438 if ( appHasTempFileOption ) 00439 exec += " --tempfile"; 00440 00441 // Did the user forget to append something like '%f'? 00442 // If so, then assume that '%f' is the right choice => the application 00443 // accepts only local files. 00444 if( !mx1.hasSpec ) { 00445 exec += " %f"; 00446 mx2.ignFile = true; 00447 } 00448 00449 mx2.expandMacrosShellQuote( exec ); // syntax was already checked, so don't check return value 00450 00451 /* 00452 1 = need_shell, 2 = terminal, 4 = su, 8 = has_shell 00453 00454 0 << split(cmd) 00455 1 << "sh" << "-c" << cmd 00456 2 << split(term) << "-e" << split(cmd) 00457 3 << split(term) << "-e" << "sh" << "-c" << cmd 00458 00459 4 << "tdesu" << "-u" << user << "-c" << cmd 00460 5 << "tdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd)) 00461 6 << split(term) << "-e" << "su" << user << "-c" << cmd 00462 7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd)) 00463 00464 8 << cmd 00465 9 << cmd 00466 a << term << "-e" << cmd 00467 b << term << "-e" << ("sh -c " + quote(cmd)) 00468 00469 c << "tdesu" << "-u" << user << "-c" << quote(cmd) 00470 d << "tdesu" << "-u" << user << "-c" << quote("sh -c " + quote(cmd)) 00471 e << term << "-e" << "su" << user << "-c" << quote(cmd) 00472 f << term << "-e" << "su" << user << "-c" << quote("sh -c " + quote(cmd)) 00473 00474 "sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh. 00475 this could be optimized with the -s switch of some su versions (e.g., debian linux). 00476 */ 00477 00478 if (_service.terminal()) { 00479 TDEConfigGroupSaver gs(TDEGlobal::config(), "General"); 00480 TQString terminal = TDEGlobal::config()->readPathEntry("TerminalApplication", "konsole"); 00481 if (terminal == "konsole") 00482 terminal += " -caption=%c %i %m"; 00483 terminal += " "; 00484 terminal += _service.terminalOptions(); 00485 if( !mx1.expandMacrosShellQuote( terminal ) ) { 00486 kdWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'" << endl; 00487 return TQStringList(); 00488 } 00489 mx2.expandMacrosShellQuote( terminal ); 00490 if (has_shell) 00491 result << terminal; 00492 else 00493 result = KShell::splitArgs( terminal ); // assuming that the term spec never needs a shell! 00494 result << "-e"; 00495 } 00496 00497 int err; 00498 if (_service.substituteUid()) { 00499 if (_service.terminal()) 00500 result << "su"; 00501 else 00502 result << "tdesu" << "-u"; 00503 result << _service.username() << "-c"; 00504 KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); 00505 if (err == KShell::FoundMeta) { 00506 shellQuote( exec ); 00507 exec.prepend( "/bin/sh -c " ); 00508 } else if (err != KShell::NoError) 00509 goto synerr; 00510 if (has_shell) 00511 shellQuote( exec ); 00512 result << exec; 00513 } else { 00514 if (has_shell) { 00515 if (_service.terminal()) { 00516 KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); 00517 if (err == KShell::FoundMeta) { 00518 shellQuote( exec ); 00519 exec.prepend( "/bin/sh -c " ); 00520 } else if (err != KShell::NoError) 00521 goto synerr; 00522 } 00523 result << exec; 00524 } else { 00525 result += KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); 00526 if (err == KShell::FoundMeta) 00527 result << "/bin/sh" << "-c" << exec; 00528 else if (err != KShell::NoError) 00529 goto synerr; 00530 } 00531 } 00532 00533 return result; 00534 00535 synerr: 00536 kdWarning() << "KRun: syntax error in command `" << _service.exec() << "', service `" << _service.name() << "'" << endl; 00537 return TQStringList(); 00538 } 00539 00540 //static 00541 TQString KRun::binaryName( const TQString & execLine, bool removePath ) 00542 { 00543 // Remove parameters and/or trailing spaces. 00544 TQStringList args = KShell::splitArgs( execLine ); 00545 for (TQStringList::ConstIterator it = args.begin(); it != args.end(); ++it) 00546 if (!(*it).contains('=')) 00547 // Remove path if wanted 00548 return removePath ? (*it).mid(TQString(*it).findRev('/') + 1) : *it; 00549 return TQString(); 00550 } 00551 00552 static pid_t runCommandInternal( TDEProcess* proc, const KService* service, const TQString& binName, 00553 const TQString &execName, const TQString & iconName, TQWidget* window, TQCString asn ) 00554 { 00555 if (service && !service->desktopEntryPath().isEmpty() 00556 && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() )) 00557 { 00558 kdWarning() << "No authorization to execute " << service->desktopEntryPath() << endl; 00559 KMessageBox::sorry(window, i18n("You are not authorized to execute this file.")); 00560 return 0; 00561 } 00562 TQString bin = KRun::binaryName( binName, true ); 00563 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification 00564 bool silent; 00565 TQCString wmclass; 00566 TDEStartupInfoId id; 00567 bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass )); 00568 if( startup_notify ) 00569 { 00570 id.initId( asn ); 00571 id.setupStartupEnv(); 00572 TDEStartupInfoData data; 00573 data.setHostname(); 00574 data.setBin( bin ); 00575 if( !execName.isEmpty()) 00576 data.setName( execName ); 00577 else if( service && !service->name().isEmpty()) 00578 data.setName( service->name()); 00579 data.setDescription( i18n( "Launching %1" ).arg( data.name())); 00580 if( !iconName.isEmpty()) 00581 data.setIcon( iconName ); 00582 else if( service && !service->icon().isEmpty()) 00583 data.setIcon( service->icon()); 00584 if( !wmclass.isEmpty()) 00585 data.setWMClass( wmclass ); 00586 if( silent ) 00587 data.setSilent( TDEStartupInfoData::Yes ); 00588 data.setDesktop( KWin::currentDesktop()); 00589 if( window ) 00590 data.setLaunchedBy( window->winId()); 00591 TDEStartupInfo::sendStartup( id, data ); 00592 } 00593 pid_t pid = TDEProcessRunner::run( proc, binName, id ); 00594 if( startup_notify && pid ) 00595 { 00596 TDEStartupInfoData data; 00597 data.addPid( pid ); 00598 TDEStartupInfo::sendChange( id, data ); 00599 TDEStartupInfo::resetStartupEnv(); 00600 } 00601 return pid; 00602 #else 00603 Q_UNUSED( execName ); 00604 Q_UNUSED( iconName ); 00605 return TDEProcessRunner::run( proc, bin ); 00606 #endif 00607 } 00608 00609 // This code is also used in tdelauncher. 00610 bool KRun::checkStartupNotify( const TQString& /*binName*/, const KService* service, bool* silent_arg, TQCString* wmclass_arg ) 00611 { 00612 bool silent = false; 00613 TQCString wmclass; 00614 if( service && service->property( "StartupNotify" ).isValid()) 00615 { 00616 silent = !service->property( "StartupNotify" ).toBool(); 00617 wmclass = service->property( "StartupWMClass" ).toString().latin1(); 00618 } 00619 else if( service && service->property( "X-TDE-StartupNotify" ).isValid()) 00620 { 00621 silent = !service->property( "X-TDE-StartupNotify" ).toBool(); 00622 wmclass = service->property( "X-TDE-WMClass" ).toString().latin1(); 00623 } 00624 else // non-compliant app 00625 { 00626 if( service ) 00627 { 00628 if( service->type() == "Application" ) 00629 wmclass = "0"; // doesn't have .desktop entries needed, start as non-compliant 00630 else 00631 return false; // no startup notification at all 00632 } 00633 else 00634 { 00635 #if 0 00636 // Create startup notification even for apps for which there shouldn't be any, 00637 // just without any visual feedback. This will ensure they'll be positioned on the proper 00638 // virtual desktop, and will get user timestamp from the ASN ID. 00639 wmclass = "0"; 00640 silent = true; 00641 #else // That unfortunately doesn't work, when the launched non-compliant application 00642 // launches another one that is compliant and there is any delay inbetween (bnc:#343359) 00643 return false; 00644 #endif 00645 } 00646 } 00647 if( silent_arg != NULL ) 00648 *silent_arg = silent; 00649 if( wmclass_arg != NULL ) 00650 *wmclass_arg = wmclass; 00651 return true; 00652 } 00653 00654 static pid_t runTempService( const KService& _service, const KURL::List& _urls, TQWidget* window, 00655 const TQCString& asn, bool tempFiles, const TQString& suggestedFileName ) 00656 { 00657 if (!_urls.isEmpty()) { 00658 kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl; 00659 } 00660 00661 TQStringList args; 00662 if ((_urls.count() > 1) && !_service.allowMultipleFiles()) 00663 { 00664 // We need to launch the application N times. That sucks. 00665 // We ignore the result for application 2 to N. 00666 // For the first file we launch the application in the 00667 // usual way. The reported result is based on this 00668 // application. 00669 KURL::List::ConstIterator it = _urls.begin(); 00670 while(++it != _urls.end()) 00671 { 00672 KURL::List singleUrl; 00673 singleUrl.append(*it); 00674 runTempService( _service, singleUrl, window, "", tempFiles, suggestedFileName ); 00675 } 00676 KURL::List singleUrl; 00677 singleUrl.append(_urls.first()); 00678 args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles, suggestedFileName); 00679 } 00680 else 00681 { 00682 args = KRun::processDesktopExec(_service, _urls, false, tempFiles, suggestedFileName); 00683 } 00684 kdDebug(7010) << "runTempService: TDEProcess args=" << args << endl; 00685 00686 TDEProcess * proc = new TDEProcess; 00687 *proc << args; 00688 00689 if (!_service.path().isEmpty()) 00690 proc->setWorkingDirectory(_service.path()); 00691 00692 return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ), 00693 _service.name(), _service.icon(), window, asn ); 00694 } 00695 00696 // WARNING: don't call this from processDesktopExec, since tdelauncher uses that too... 00697 static KURL::List resolveURLs( const KURL::List& _urls, const KService& _service ) 00698 { 00699 // Check which protocols the application supports. 00700 // This can be a list of actual protocol names, or just TDEIO for KDE apps. 00701 TQStringList supportedProtocols = _service.property("X-TDE-Protocols").toStringList(); 00702 KRunMX1 mx1( _service ); 00703 TQString exec = _service.exec(); 00704 if ( mx1.expandMacrosShellQuote( exec ) && !mx1.hasUrls ) { 00705 Q_ASSERT( supportedProtocols.isEmpty() ); // huh? If you support protocols you need %u or %U... 00706 } else { 00707 if ( supportedProtocols.isEmpty() ) 00708 { 00709 // compat mode: assume TDEIO if not set and it's a KDE app 00710 TQStringList categories = _service.property("Categories").toStringList(); 00711 if (( categories.find("TDE") != categories.end() ) && ( categories.find("KDE") != categories.end() )) 00712 supportedProtocols.append( "TDEIO" ); 00713 else { // if no KDE app, be a bit over-generic 00714 supportedProtocols.append( "http"); 00715 supportedProtocols.append( "ftp"); 00716 } 00717 } 00718 } 00719 kdDebug(7010) << "supportedProtocols:" << supportedProtocols << endl; 00720 00721 KURL::List urls( _urls ); 00722 if ( supportedProtocols.find( "TDEIO" ) == supportedProtocols.end() ) { 00723 for( KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it ) { 00724 const KURL url = *it; 00725 bool supported = url.isLocalFile() || supportedProtocols.find( url.protocol().lower() ) != supportedProtocols.end(); 00726 kdDebug(7010) << "Looking at url=" << url << " supported=" << supported << endl; 00727 if ( !supported && KProtocolInfo::protocolClass(url.protocol()) == ":local" ) 00728 { 00729 // Maybe we can resolve to a local URL? 00730 KURL localURL = TDEIO::NetAccess::mostLocalURL( url, 0 ); 00731 if ( localURL != url ) { 00732 *it = localURL; 00733 kdDebug(7010) << "Changed to " << localURL << endl; 00734 } 00735 } 00736 } 00737 } 00738 return urls; 00739 } 00740 00741 // BIC merge methods below 00742 pid_t KRun::run( const KService& _service, const KURL::List& _urls ) 00743 { 00744 return run( _service, _urls, 0, false, TQString::null ); 00745 } 00746 00747 pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles ) 00748 { 00749 return run( _service, _urls, 0, tempFiles, TQString::null ); 00750 } 00751 00752 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles ) 00753 { 00754 return run( _service, _urls, window, "", tempFiles, TQString::null ); 00755 } 00756 00757 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, const TQCString& asn, bool tempFiles ) 00758 { 00759 return run( _service, _urls, window, asn, tempFiles, TQString::null ); 00760 } 00761 00762 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles, const TQString& suggestedFileName ) 00763 { 00764 return run( _service, _urls, window, "", tempFiles, suggestedFileName ); 00765 } 00766 00767 pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, const TQCString& asn, 00768 bool tempFiles, const TQString& suggestedFileName ) 00769 { 00770 if (!_service.desktopEntryPath().isEmpty() && 00771 !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath())) 00772 { 00773 kdWarning() << "No authorization to execute " << _service.desktopEntryPath() << endl; 00774 KMessageBox::sorry(window, i18n("You are not authorized to execute this service.")); 00775 return 0; 00776 } 00777 00778 if ( !tempFiles ) 00779 { 00780 // Remember we opened those urls, for the "recent documents" menu in kicker 00781 KURL::List::ConstIterator it = _urls.begin(); 00782 for(; it != _urls.end(); ++it) { 00783 //kdDebug(7010) << "TDERecentDocument::adding " << (*it).url() << endl; 00784 TDERecentDocument::add( *it, _service.desktopEntryName() ); 00785 } 00786 } 00787 00788 if ( tempFiles || _service.desktopEntryPath().isEmpty() || !suggestedFileName.isEmpty() ) 00789 { 00790 return runTempService(_service, _urls, window, asn, tempFiles, suggestedFileName); 00791 } 00792 00793 kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl; 00794 00795 if (!_urls.isEmpty()) { 00796 kdDebug(7010) << "First url " << _urls.first().url() << endl; 00797 } 00798 00799 // Resolve urls if needed, depending on what the app supports 00800 const KURL::List urls = resolveURLs( _urls, _service ); 00801 00802 TQString error; 00803 int pid = 0; 00804 00805 TQCString myasn = asn; 00806 // startServiceByDesktopPath() doesn't take TQWidget*, add it to the startup info now 00807 if( window != NULL ) 00808 { 00809 if( myasn.isEmpty()) 00810 myasn = TDEStartupInfo::createNewStartupId(); 00811 if( myasn != "0" ) 00812 { 00813 TDEStartupInfoId id; 00814 id.initId( myasn ); 00815 TDEStartupInfoData data; 00816 data.setLaunchedBy( window->winId()); 00817 TDEStartupInfo::sendChange( id, data ); 00818 } 00819 } 00820 00821 int i = TDEApplication::startServiceByDesktopPath( 00822 _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid, myasn 00823 ); 00824 00825 if (i != 0) 00826 { 00827 kdDebug(7010) << error << endl; 00828 KMessageBox::sorry( window, error ); 00829 return 0; 00830 } 00831 00832 kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl; 00833 return (pid_t) pid; 00834 } 00835 00836 00837 pid_t KRun::run( const TQString& _exec, const KURL::List& _urls, const TQString& _name, 00838 const TQString& _icon, const TQString&, const TQString&) 00839 { 00840 KService::Ptr service = new KService(_name, _exec, _icon); 00841 00842 return run(*service, _urls); 00843 } 00844 00845 pid_t KRun::runCommand( TQString cmd ) 00846 { 00847 return KRun::runCommand( cmd, TQString::null, TQString::null, NULL, "" ); 00848 } 00849 00850 pid_t KRun::runCommand( const TQString& cmd, const TQString &execName, const TQString & iconName ) 00851 { 00852 return KRun::runCommand( cmd, execName, iconName, NULL, "" ); 00853 } 00854 00855 pid_t KRun::runCommand( const TQString& cmd, const TQString &execName, const TQString & iconName, 00856 TQWidget* window, const TQCString& asn ) 00857 { 00858 kdDebug(7010) << "runCommand " << cmd << "," << execName << endl; 00859 TDEProcess * proc = new TDEProcess; 00860 proc->setUseShell(true); 00861 *proc << cmd; 00862 KService::Ptr service = KService::serviceByDesktopName( binaryName( execName, true ) ); 00863 TQString bin = binaryName( cmd, false ); 00864 int pos = bin.findRev( '/' ); 00865 if (pos != -1) { 00866 proc->setWorkingDirectory( bin.mid(0, pos) ); 00867 } 00868 return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName, window, asn ); 00869 } 00870 00871 KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo ) 00872 :m_timer(0,"KRun::timer") 00873 { 00874 init (url, 0, "", mode, isLocalFile, showProgressInfo); 00875 } 00876 00877 KRun::KRun( const KURL& url, TQWidget* window, mode_t mode, bool isLocalFile, 00878 bool showProgressInfo ) 00879 :m_timer(0,"KRun::timer") 00880 { 00881 init (url, window, "", mode, isLocalFile, showProgressInfo); 00882 } 00883 00884 KRun::KRun( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode, bool isLocalFile, 00885 bool showProgressInfo ) 00886 :m_timer(0,"KRun::timer") 00887 { 00888 init (url, window, asn, mode, isLocalFile, showProgressInfo); 00889 } 00890 00891 void KRun::init ( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode, bool isLocalFile, 00892 bool showProgressInfo ) 00893 { 00894 m_bFault = false; 00895 m_bAutoDelete = true; 00896 m_bProgressInfo = showProgressInfo; 00897 m_bFinished = false; 00898 m_job = 0L; 00899 m_strURL = url; 00900 m_bScanFile = false; 00901 m_bIsDirectory = false; 00902 m_bIsLocalFile = isLocalFile; 00903 m_mode = mode; 00904 d = new KRunPrivate; 00905 d->m_runExecutables = true; 00906 d->m_window = window; 00907 d->m_asn = asn; 00908 setEnableExternalBrowser(true); 00909 00910 // Start the timer. This means we will return to the event 00911 // loop and do initialization afterwards. 00912 // Reason: We must complete the constructor before we do anything else. 00913 m_bInit = true; 00914 connect( &m_timer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotTimeout() ) ); 00915 m_timer.start( 0, true ); 00916 kdDebug(7010) << " new KRun " << this << " " << url.prettyURL() << " timer=" << &m_timer << endl; 00917 00918 kapp->ref(); 00919 } 00920 00921 void KRun::init() 00922 { 00923 kdDebug(7010) << "INIT called" << endl; 00924 00925 bool bypassErrorMessage = false; 00926 00927 if (m_strURL.url().startsWith("$(")) { 00928 // check for environment variables and make necessary translations 00929 TQString aValue = m_strURL.url(); 00930 int nDollarPos = aValue.find( '$' ); 00931 00932 while( nDollarPos != -1 && nDollarPos+1 < static_cast<int>(aValue.length())) { 00933 // there is at least one $ 00934 if( (aValue)[nDollarPos+1] == '(' ) { 00935 uint nEndPos = nDollarPos+1; 00936 // the next character is no $ 00937 while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!=')') ) 00938 nEndPos++; 00939 nEndPos++; 00940 TQString cmd = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); 00941 00942 TQString result; 00943 FILE *fs = popen(TQFile::encodeName(cmd).data(), "r"); 00944 if (fs) 00945 { 00946 { 00947 TQTextStream ts(fs, IO_ReadOnly); 00948 result = ts.read().stripWhiteSpace(); 00949 } 00950 pclose(fs); 00951 } 00952 aValue.replace( nDollarPos, nEndPos-nDollarPos, result ); 00953 } else if( (aValue)[nDollarPos+1] != '$' ) { 00954 uint nEndPos = nDollarPos+1; 00955 // the next character is no $ 00956 TQString aVarName; 00957 if (aValue[nEndPos]=='{') 00958 { 00959 while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!='}') ) 00960 nEndPos++; 00961 nEndPos++; 00962 aVarName = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); 00963 } 00964 else 00965 { 00966 while ( nEndPos <= aValue.length() && (aValue[nEndPos].isNumber() 00967 || aValue[nEndPos].isLetter() || aValue[nEndPos]=='_' ) ) 00968 nEndPos++; 00969 aVarName = aValue.mid( nDollarPos+1, nEndPos-nDollarPos-1 ); 00970 } 00971 const char* pEnv = 0; 00972 if (!aVarName.isEmpty()) 00973 pEnv = getenv( aVarName.ascii() ); 00974 if( pEnv ) { 00975 // !!! Sergey A. Sukiyazov <corwin@micom.don.ru> !!! 00976 // A environment variables may contain values in 8bit 00977 // locale cpecified encoding or in UTF8 encoding. 00978 aValue.replace( nDollarPos, nEndPos-nDollarPos, KStringHandler::from8Bit( pEnv ) ); 00979 } else 00980 aValue.remove( nDollarPos, nEndPos-nDollarPos ); 00981 } else { 00982 // remove one of the dollar signs 00983 aValue.remove( nDollarPos, 1 ); 00984 nDollarPos++; 00985 } 00986 nDollarPos = aValue.find( '$', nDollarPos ); 00987 } 00988 m_strURL = KURL(aValue); 00989 bypassErrorMessage = true; 00990 } 00991 00992 if ( !m_strURL.isValid() ) 00993 { 00994 if (bypassErrorMessage == false) { 00995 d->m_showingError = true; 00996 KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) ); 00997 d->m_showingError = false; 00998 } 00999 m_bFault = true; 01000 m_bFinished = true; 01001 m_timer.start( 0, true ); 01002 return; 01003 } 01004 if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL)) 01005 { 01006 TQString msg = TDEIO::buildErrorString(TDEIO::ERR_ACCESS_DENIED, m_strURL.prettyURL()); 01007 d->m_showingError = true; 01008 KMessageBoxWrapper::error( d->m_window, msg ); 01009 d->m_showingError = false; 01010 m_bFault = true; 01011 m_bFinished = true; 01012 m_timer.start( 0, true ); 01013 return; 01014 } 01015 01016 if ( !m_bIsLocalFile && m_strURL.isLocalFile() ) 01017 m_bIsLocalFile = true; 01018 01019 TQString exec; 01020 if (m_strURL.protocol().startsWith("http")) 01021 { 01022 exec = d->m_externalBrowser; 01023 } 01024 01025 if ( m_bIsLocalFile ) 01026 { 01027 if ( m_mode == 0 ) 01028 { 01029 KDE_struct_stat buff; 01030 if ( KDE_stat( TQFile::encodeName(m_strURL.path()), &buff ) == -1 ) 01031 { 01032 d->m_showingError = true; 01033 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() ) ); 01034 d->m_showingError = false; 01035 m_bFault = true; 01036 m_bFinished = true; 01037 m_timer.start( 0, true ); 01038 return; 01039 } 01040 m_mode = buff.st_mode; 01041 } 01042 01043 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile ); 01044 assert( mime != 0L ); 01045 kdDebug(7010) << "MIME TYPE is " << mime->name() << endl; 01046 foundMimeType( mime->name() ); 01047 return; 01048 } 01049 else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( m_strURL ) ) { 01050 kdDebug(7010) << "Helper protocol" << endl; 01051 01052 bool ok = false; 01053 KURL::List urls; 01054 if (!((m_strURL.protocol().startsWith("http")) && (m_strURL.url() == "http://default.browser"))) 01055 urls.append( m_strURL ); 01056 if (exec.isEmpty()) 01057 { 01058 exec = KProtocolInfo::exec( m_strURL.protocol() ); 01059 if (exec.isEmpty()) 01060 { 01061 foundMimeType(KProtocolInfo::defaultMimetype(m_strURL)); 01062 return; 01063 } 01064 run( exec, urls ); 01065 ok = true; 01066 } 01067 else if (exec.startsWith("!")) 01068 { 01069 exec = exec.mid(1); // Literal command 01070 exec += " %u"; 01071 run( exec, urls ); 01072 ok = true; 01073 } 01074 else 01075 { 01076 KService::Ptr service = KService::serviceByStorageId( exec ); 01077 if (service) 01078 { 01079 run( *service, urls, d->m_window, d->m_asn ); 01080 ok = true; 01081 } 01082 } 01083 01084 if (ok) 01085 { 01086 m_bFinished = true; 01087 // will emit the error and autodelete this 01088 m_timer.start( 0, true ); 01089 return; 01090 } 01091 } 01092 01093 if ((m_strURL.protocol().startsWith("http")) && (m_strURL.url() == "http://default.browser")) { 01094 KURL::List urls; 01095 run( "kfmclient openProfile webbrowsing", urls ); 01096 m_bFinished = true; 01097 // will emit the error and autodelete this 01098 m_timer.start( 0, true ); 01099 return; 01100 } 01101 01102 // Did we already get the information that it is a directory ? 01103 if ( S_ISDIR( m_mode ) ) 01104 { 01105 foundMimeType( "inode/directory" ); 01106 return; 01107 } 01108 01109 // Let's see whether it is a directory 01110 01111 if ( !KProtocolInfo::supportsListing( m_strURL ) ) 01112 { 01113 //kdDebug(7010) << "Protocol has no support for listing" << endl; 01114 // No support for listing => it can't be a directory (example: http) 01115 scanFile(); 01116 return; 01117 } 01118 01119 kdDebug(7010) << "Testing directory (stating)" << endl; 01120 01121 // It may be a directory or a file, let's stat 01122 TDEIO::StatJob *job = TDEIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo ); 01123 job->setWindow (d->m_window); 01124 connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), 01125 this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) ); 01126 m_job = job; 01127 kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl; 01128 } 01129 01130 KRun::~KRun() 01131 { 01132 kdDebug(7010) << "KRun::~KRun() " << this << endl; 01133 m_timer.stop(); 01134 killJob(); 01135 kapp->deref(); 01136 kdDebug(7010) << "KRun::~KRun() done " << this << endl; 01137 delete d; 01138 } 01139 01140 void KRun::scanFile() 01141 { 01142 kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl; 01143 // First, let's check for well-known extensions 01144 // Not when there is a query in the URL, in any case. 01145 if ( m_strURL.query().isEmpty() ) 01146 { 01147 KMimeType::Ptr mime = KMimeType::findByURL( m_strURL ); 01148 assert( mime != 0L ); 01149 if ( mime->name() != "application/octet-stream" || m_bIsLocalFile ) 01150 { 01151 kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl; 01152 foundMimeType( mime->name() ); 01153 return; 01154 } 01155 } 01156 01157 // No mimetype found, and the URL is not local (or fast mode not allowed). 01158 // We need to apply the 'TDEIO' method, i.e. either asking the server or 01159 // getting some data out of the file, to know what mimetype it is. 01160 01161 if ( !KProtocolInfo::supportsReading( m_strURL ) ) 01162 { 01163 kdError(7010) << "#### NO SUPPORT FOR READING!" << endl; 01164 m_bFault = true; 01165 m_bFinished = true; 01166 m_timer.start( 0, true ); 01167 return; 01168 } 01169 kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl; 01170 01171 TDEIO::TransferJob *job = TDEIO::get( m_strURL, false /*reload*/, m_bProgressInfo ); 01172 job->setWindow (d->m_window); 01173 connect(job, TQT_SIGNAL( result(TDEIO::Job *)), 01174 this, TQT_SLOT( slotScanFinished(TDEIO::Job *))); 01175 connect(job, TQT_SIGNAL( mimetype(TDEIO::Job *, const TQString &)), 01176 this, TQT_SLOT( slotScanMimeType(TDEIO::Job *, const TQString &))); 01177 m_job = job; 01178 kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl; 01179 } 01180 01181 void KRun::slotTimeout() 01182 { 01183 kdDebug(7010) << this << " slotTimeout called" << endl; 01184 if ( m_bInit ) 01185 { 01186 m_bInit = false; 01187 init(); 01188 return; 01189 } 01190 01191 if ( m_bFault ) { 01192 emit error(); 01193 } 01194 if ( m_bFinished ) { 01195 emit finished(); 01196 } 01197 else 01198 { 01199 if ( m_bScanFile ) 01200 { 01201 m_bScanFile = false; 01202 scanFile(); 01203 return; 01204 } 01205 else if ( m_bIsDirectory ) 01206 { 01207 m_bIsDirectory = false; 01208 foundMimeType( "inode/directory" ); 01209 return; 01210 } 01211 } 01212 01213 if ( m_bAutoDelete ) 01214 { 01215 delete this; 01216 return; 01217 } 01218 } 01219 01220 void KRun::slotStatResult( TDEIO::Job * job ) 01221 { 01222 m_job = 0L; 01223 if (job->error()) 01224 { 01225 d->m_showingError = true; 01226 kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl; 01227 job->showErrorDialog(); 01228 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 01229 d->m_showingError = false; 01230 01231 m_bFault = true; 01232 m_bFinished = true; 01233 01234 // will emit the error and autodelete this 01235 m_timer.start( 0, true ); 01236 01237 } else { 01238 01239 kdDebug(7010) << "Finished" << endl; 01240 if(!dynamic_cast<TDEIO::StatJob*>(job)) 01241 kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl; 01242 01243 TQString knownMimeType; 01244 TDEIO::UDSEntry entry = ((TDEIO::StatJob*)job)->statResult(); 01245 TDEIO::UDSEntry::ConstIterator it = entry.begin(); 01246 for( ; it != entry.end(); it++ ) { 01247 switch( (*it).m_uds ) { 01248 case TDEIO::UDS_FILE_TYPE: 01249 if ( S_ISDIR( (mode_t)((*it).m_long) ) ) 01250 m_bIsDirectory = true; // it's a dir 01251 else 01252 m_bScanFile = true; // it's a file 01253 break; 01254 case TDEIO::UDS_MIME_TYPE: // mimetype already known? (e.g. print:/manager) 01255 knownMimeType = (*it).m_str; 01256 break; 01257 case TDEIO::UDS_LOCAL_PATH: 01258 d->m_localPath = (*it).m_str; 01259 break; 01260 default: 01261 break; 01262 } 01263 } 01264 if ( !knownMimeType.isEmpty() ) 01265 { 01266 foundMimeType( knownMimeType ); 01267 m_bFinished = true; 01268 } 01269 01270 // We should have found something 01271 assert ( m_bScanFile || m_bIsDirectory ); 01272 01273 // Start the timer. Once we get the timer event this 01274 // protocol server is back in the pool and we can reuse it. 01275 // This gives better performance than starting a new slave 01276 m_timer.start( 0, true ); 01277 } 01278 } 01279 01280 void KRun::slotScanMimeType( TDEIO::Job *, const TQString &mimetype ) 01281 { 01282 if ( mimetype.isEmpty() ) 01283 kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a tdeioslave bug." << endl; 01284 foundMimeType( mimetype ); 01285 m_job = 0; 01286 } 01287 01288 void KRun::slotScanFinished( TDEIO::Job *job ) 01289 { 01290 m_job = 0; 01291 if (job->error()) 01292 { 01293 d->m_showingError = true; 01294 kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl; 01295 job->showErrorDialog(); 01296 //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl; 01297 d->m_showingError = false; 01298 01299 m_bFault = true; 01300 m_bFinished = true; 01301 01302 // will emit the error and autodelete this 01303 m_timer.start( 0, true ); 01304 } 01305 } 01306 01307 void KRun::foundMimeType( const TQString& type ) 01308 { 01309 kdDebug(7010) << "Resulting mime type is " << type << endl; 01310 01311 /* 01312 // Automatically unzip stuff 01313 01314 // Disabled since the new TDEIO doesn't have filters yet. 01315 01316 if ( type == "application/x-gzip" || 01317 type == "application/x-bzip" || 01318 type == "application/x-bzip2" ) 01319 { 01320 KURL::List lst = KURL::split( m_strURL ); 01321 if ( lst.isEmpty() ) 01322 { 01323 TQString tmp = i18n( "Malformed URL" ); 01324 tmp += "\n"; 01325 tmp += m_strURL.url(); 01326 KMessageBoxWrapper::error( 0L, tmp ); 01327 return; 01328 } 01329 01330 if ( type == "application/x-gzip" ) 01331 lst.prepend( KURL( "gzip:/decompress" ) ); 01332 else if ( type == "application/x-bzip" ) 01333 lst.prepend( KURL( "bzip:/decompress" ) ); 01334 else if ( type == "application/x-bzip2" ) 01335 lst.prepend( KURL( "bzip2:/decompress" ) ); 01336 else if ( type == "application/x-tar" ) 01337 lst.prepend( KURL( "tar:/" ) ); 01338 01339 // Move the HTML style reference to the leftmost URL 01340 KURL::List::Iterator it = lst.begin(); 01341 ++it; 01342 (*lst.begin()).setRef( (*it).ref() ); 01343 (*it).setRef( TQString::null ); 01344 01345 // Create the new URL 01346 m_strURL = KURL::join( lst ); 01347 01348 kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl; 01349 01350 killJob(); 01351 01352 // We don't know if this is a file or a directory. Let's test this first. 01353 // (For instance a tar.gz is a directory contained inside a file) 01354 // It may be a directory or a file, let's stat 01355 TDEIO::StatJob *job = TDEIO::stat( m_strURL, m_bProgressInfo ); 01356 connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), 01357 this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) ); 01358 m_job = job; 01359 01360 return; 01361 } 01362 */ 01363 TDEIO::TransferJob *job = ::tqqt_cast<TDEIO::TransferJob *>( m_job ); 01364 if ( job ) 01365 { 01366 job->putOnHold(); 01367 TDEIO::Scheduler::publishSlaveOnHold(); 01368 m_job = 0; 01369 } 01370 01371 Q_ASSERT( !m_bFinished ); 01372 01373 // Suport for preferred service setting, see setPreferredService 01374 if ( !d->m_preferredService.isEmpty() ) { 01375 kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl; 01376 KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService ); 01377 if ( serv && serv->hasServiceType( type ) ) 01378 { 01379 KURL::List lst; 01380 lst.append( m_strURL ); 01381 m_bFinished = KRun::run( *serv, lst, d->m_window, d->m_asn ); 01386 } 01387 } 01388 01389 // Resolve .desktop files from media:/, remote:/, applications:/ etc. 01390 if ( ((type == "application/x-desktop") || 01391 (type == "media/builtin-mydocuments") || 01392 (type == "media/builtin-mycomputer") || 01393 (type == "media/builtin-mynetworkplaces") || 01394 (type == "media/builtin-printers") || 01395 (type == "media/builtin-trash") || 01396 (type == "media/builtin-webbrowser")) /* or inheriting? */ && (!d->m_localPath.isEmpty()) ) 01397 { 01398 m_strURL = KURL(); 01399 m_strURL.setPath( d->m_localPath ); 01400 } 01401 01402 if (!m_bFinished && KRun::runURL( m_strURL, type, d->m_window, d->m_asn, false, d->m_runExecutables, d->m_suggestedFileName )){ 01403 m_bFinished = true; 01404 } 01405 else{ 01406 m_bFinished = true; 01407 m_bFault = true; 01408 } 01409 01410 m_timer.start( 0, true ); 01411 } 01412 01413 void KRun::killJob() 01414 { 01415 if ( m_job ) 01416 { 01417 kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl; 01418 m_job->kill(); 01419 m_job = 0L; 01420 } 01421 } 01422 01423 void KRun::abort() 01424 { 01425 kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl; 01426 killJob(); 01427 // If we're showing an error message box, the rest will be done 01428 // after closing the msgbox -> don't autodelete nor emit signals now. 01429 if ( d->m_showingError ) 01430 return; 01431 m_bFault = true; 01432 m_bFinished = true; 01433 m_bInit = false; 01434 m_bScanFile = false; 01435 01436 // will emit the error and autodelete this 01437 m_timer.start( 0, true ); 01438 } 01439 01440 void KRun::setEnableExternalBrowser(bool b) 01441 { 01442 if (b) 01443 d->m_externalBrowser = TDEConfigGroup(TDEGlobal::config(), "General").readEntry("BrowserApplication"); 01444 else 01445 d->m_externalBrowser = TQString::null; 01446 } 01447 01448 void KRun::setPreferredService( const TQString& desktopEntryName ) 01449 { 01450 d->m_preferredService = desktopEntryName; 01451 } 01452 01453 void KRun::setRunExecutables(bool b) 01454 { 01455 d->m_runExecutables = b; 01456 } 01457 01458 void KRun::setSuggestedFileName( const TQString& fileName ) 01459 { 01460 d->m_suggestedFileName = fileName; 01461 } 01462 01463 bool KRun::isExecutable( const TQString& serviceType ) 01464 { 01465 return ( serviceType == "application/x-desktop" || 01466 serviceType == "media/builtin-mydocuments" || 01467 serviceType == "media/builtin-mycomputer" || 01468 serviceType == "media/builtin-mynetworkplaces" || 01469 serviceType == "media/builtin-printers" || 01470 serviceType == "media/builtin-trash" || 01471 serviceType == "media/builtin-webbrowser" || 01472 serviceType == "application/x-executable" || 01473 serviceType == "application/x-msdos-program" || 01474 serviceType == "application/x-shellscript" ); 01475 } 01476 01477 /****************/ 01478 01479 pid_t 01480 TDEProcessRunner::run(TDEProcess * p, const TQString & binName) 01481 { 01482 return (new TDEProcessRunner(p, binName))->pid(); 01483 } 01484 01485 #ifdef Q_WS_X11 01486 pid_t 01487 TDEProcessRunner::run(TDEProcess * p, const TQString & binName, const TDEStartupInfoId& id ) 01488 { 01489 return (new TDEProcessRunner(p, binName, id))->pid(); 01490 } 01491 #endif 01492 01493 TDEProcessRunner::TDEProcessRunner(TDEProcess * p, const TQString & _binName ) 01494 : TQObject(), 01495 process_(p), 01496 binName( _binName ) 01497 { 01498 TQObject::connect( 01499 process_, TQT_SIGNAL(processExited(TDEProcess *)), 01500 this, TQT_SLOT(slotProcessExited(TDEProcess *))); 01501 01502 process_->start(); 01503 if ( !process_->pid() ) 01504 slotProcessExited( process_ ); 01505 } 01506 01507 #ifdef Q_WS_X11 01508 TDEProcessRunner::TDEProcessRunner(TDEProcess * p, const TQString & _binName, const TDEStartupInfoId& id ) 01509 : TQObject(), 01510 process_(p), 01511 binName( _binName ), 01512 id_( id ) 01513 { 01514 TQObject::connect( 01515 process_, TQT_SIGNAL(processExited(TDEProcess *)), 01516 this, TQT_SLOT(slotProcessExited(TDEProcess *))); 01517 01518 process_->start(); 01519 if ( !process_->pid() ) 01520 slotProcessExited( process_ ); 01521 } 01522 #endif 01523 01524 TDEProcessRunner::~TDEProcessRunner() 01525 { 01526 delete process_; 01527 } 01528 01529 pid_t 01530 TDEProcessRunner::pid() const 01531 { 01532 return process_->pid(); 01533 } 01534 01535 void 01536 TDEProcessRunner::slotProcessExited(TDEProcess * p) 01537 { 01538 if (p != process_) 01539 return; // Eh ? 01540 01541 kdDebug(7010) << "slotProcessExited " << binName << endl; 01542 kdDebug(7010) << "normalExit " << process_->normalExit() << endl; 01543 kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl; 01544 bool showErr = process_->normalExit() 01545 && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 ); 01546 if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) ) 01547 { 01548 // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist. 01549 // We can't just rely on that, but it's a good hint. 01550 // Before assuming its really so, we'll try to find the binName 01551 // relatively to current directory, and then in the PATH. 01552 if ( !TQFile( binName ).exists() && TDEStandardDirs::findExe( binName ).isEmpty() ) 01553 { 01554 kapp->ref(); 01555 KMessageBox::sorry( 0L, i18n("Could not find the program '%1'").arg( binName ) ); 01556 kapp->deref(); 01557 } 01558 } 01559 #ifdef Q_WS_X11 01560 if( !id_.none()) 01561 { 01562 TDEStartupInfoData data; 01563 data.addPid( pid()); // announce this pid for the startup notification has finished 01564 data.setHostname(); 01565 TDEStartupInfo::sendFinish( id_, data ); 01566 } 01567 #endif 01568 deleteLater(); 01569 } 01570 01571 void KRun::virtual_hook( int, void* ) 01572 { /*BASE::virtual_hook( id, data );*/ } 01573 01574 #include "krun.moc"
Trinity API Reference