diff --git a/khtml/khtml_run.cpp b/khtml/khtml_run.cpp index 61f0397e32..971c490c03 100644 --- a/khtml/khtml_run.cpp +++ b/khtml/khtml_run.cpp @@ -1,104 +1,93 @@ /* This file is part of the KDE project * * Copyright (C) 1998, 1999 Torben Weis * 1999 Lars Knoll * 1999 Antti Koivisto * 2000 Simon Hausmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "khtml_run.h" #include "khtmlpart_p.h" #include #include #include #include "khtml_ext.h" #include KHTMLRun::KHTMLRun( KHTMLPart *part, khtml::ChildFrame *child, const KUrl &url, const KParts::OpenUrlArguments& args, const KParts::BrowserArguments &browserArgs, bool hideErrorDialog ) : KParts::BrowserRun( url, args, browserArgs, part, part->widget() ? part->widget()->topLevelWidget() : 0, false, false, hideErrorDialog ), m_child( child ) { // Don't use an external browser for parts of a webpage we are rendering. (iframes at least are one example) setEnableExternalBrowser(false); // get the wheel to start spinning part->started(0L); } //KHTMLPart *KHTMLRun::htmlPart() const //{ return static_cast(part()); } void KHTMLRun::foundMimeType( const QString &_type ) { + //kDebug(6050) << this << _type; Q_ASSERT(!hasFinished()); QString mimeType = _type; // this ref comes from the job, we lose it when using KIO again - // Disable autoDelete for processObjectRequest(), because it may open a - // dialog which would start an event loop, causing the autoDelete timer - // slot to be called before leaving processObjectRequest() - bool autoDeleteWasEnabled = autoDelete(); - setAutoDelete( false ); bool requestProcessed = static_cast(part())->processObjectRequest( m_child, KRun::url(), mimeType ); - if (autoDeleteWasEnabled ) { - // Reenable autoDelete and restart timer to make sure our instance - // eventually get deleted - setAutoDelete( true ); - timer().start( 0 ); - } - if ( requestProcessed ) setFinished( true ); else { if ( hasFinished() ) // abort was called (this happens with the activex fallback for instance) return; // Couldn't embed -> call BrowserRun::handleNonEmbeddable() KParts::BrowserRun::NonEmbeddableResult res = handleNonEmbeddable( mimeType ); if ( res == KParts::BrowserRun::Delayed ) return; setFinished( res == KParts::BrowserRun::Handled ); if ( hasFinished() ) { // saved or canceled -> flag completed m_child->m_bCompleted = true; static_cast(part())->checkCompleted(); } } - if ( hasFinished() ) - { + if ( hasFinished() ) { + kDebug() << "finished"; timer().setSingleShot( true ); timer().start( 0 ); return; } - //kDebug(6050) << "KHTMLRun::foundMimeType " << _type << " couldn't open"; + //kDebug(6050) << _type << " couldn't open"; KRun::foundMimeType( mimeType ); // "open" is finished -> flag completed m_child->m_bCompleted = true; static_cast(part())->checkCompleted(); } void KHTMLRun::save( const KUrl & url, const QString & suggestedFilename ) { KHTMLPopupGUIClient::saveURL( part()->widget(), i18n( "Save As" ), url, arguments().metaData(), QString(), 0, suggestedFilename ); } #include "khtml_run.moc" diff --git a/kio/kio/krun.cpp b/kio/kio/krun.cpp index b75663cc10..de2bb66125 100644 --- a/kio/kio/krun.cpp +++ b/kio/kio/krun.cpp @@ -1,1490 +1,1512 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "krun.h" #include "krun_p.h" #include #include #include #include #include #include #include #include #include #include "kmimetypetrader.h" #include "kmimetype.h" #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/jobuidelegate.h" #include "kio/global.h" #include "kio/scheduler.h" #include "kio/netaccess.h" #include "kfile/kopenwithdialog.h" #include "kfile/krecentdocument.h" #include "kdesktopfileactions.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_WS_X11 #include #endif class KRun::KRunPrivate { public: KRunPrivate(KRun *parent) : q(parent), - m_showingError(false) + m_showingDialog(false) { } void init (const KUrl& url, QWidget* window, mode_t mode, bool isLocalFile, bool showProgressInfo, const QByteArray& asn); + // This helper method makes debugging easier: a single breakpoint for all + // the code paths that start the timer - at least from KRun itself. + // TODO: add public method startTimer() and deprecate timer() accessor, + // starting is the only valid use of the timer in subclasses (BrowserRun, KHTMLRun and KonqRun) + void startTimer() { + m_timer.start(0); + } + KRun *q; - bool m_showingError; + bool m_showingDialog; bool m_runExecutables; QString m_preferredService; QString m_externalBrowser; QString m_localPath; QString m_suggestedFileName; QPointer m_window; QByteArray m_asn; KUrl m_strURL; bool m_bFault; bool m_bAutoDelete; bool m_bProgressInfo; bool m_bFinished; KIO::Job * m_job; QTimer m_timer; /** * Used to indicate that the next action is to scan the file. * This action is invoked from slotTimeout. */ bool m_bScanFile; bool m_bIsDirectory; /** * Used to indicate that the next action is to initialize. * This action is invoked from slotTimeout */ bool m_bInit; bool m_bIsLocalFile; mode_t m_mode; }; bool KRun::isExecutableFile( const KUrl& url, const QString &mimetype ) { if ( !url.isLocalFile() ) return false; QFileInfo file( url.path() ); if ( file.isExecutable() ) // Got a prospective file to run { KMimeType::Ptr mimeType = KMimeType::mimeType( mimetype ); if ( mimeType->is("application/x-executable") || mimeType->is("application/x-executable-script") ) return true; } return false; } // This is called by foundMimeType, since it knows the mimetype of the URL bool KRun::runUrl( const KUrl& u, const QString& _mimetype, QWidget* window, bool tempFile, bool runExecutables, const QString& suggestedFileName, const QByteArray& asn ) { bool noRun = false; bool noAuth = false; if ( _mimetype == "inode/directory-locked" ) { KMessageBoxWrapper::error( window, i18n("Unable to enter %1.\nYou do not have access rights to this location.", Qt::escape(u.prettyUrl())) ); return false; } else if ( _mimetype == "application/x-desktop" ) { if ( u.isLocalFile() && runExecutables ) return KDesktopFileActions::run( u, true ); } else if ( isExecutableFile(u, _mimetype) ) { if ( u.isLocalFile() && runExecutables) { if (KAuthorized::authorize("shell_access")) { return (KRun::runCommand(KShell::quoteArg(u.path()), QString(), QString(), window, asn)); // just execute the url as a command // ## TODO implement deleting the file if tempFile==true } else { noAuth = true; } } else if (_mimetype == "application/x-executable") noRun = true; } else if ( isExecutable(_mimetype) ) { if (!runExecutables) noRun = true; if (!KAuthorized::authorize("shell_access")) noAuth = true; } if ( noRun ) { KMessageBox::sorry( window, i18n("The file %1 is an executable program. " "For safety it will not be started.", Qt::escape(u.prettyUrl()))); return false; } if ( noAuth ) { KMessageBoxWrapper::error( window, i18n("You do not have permission to run %1.", Qt::escape(u.prettyUrl())) ); return false; } KUrl::List lst; lst.append( u ); KService::Ptr offer = KMimeTypeTrader::self()->preferredService( _mimetype ); if ( !offer ) { // Open-with dialog // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog ! // Hmm, in fact KOpenWithDialog::setServiceType already guesses the mimetype from the first URL of the list... return displayOpenWithDialog( lst, window, tempFile, suggestedFileName, asn ); } return KRun::run( *offer, lst, window, tempFile, suggestedFileName, asn ); } bool KRun::displayOpenWithDialog( const KUrl::List& lst, QWidget* window, bool tempFiles, const QString& suggestedFileName, const QByteArray& asn ) { if (!KAuthorized::authorizeKAction("openwith")) { KMessageBox::sorry(window, i18n("You are not authorized to select an application to open this file.")); return false; } KOpenWithDialog l( lst, i18n("Open with:"), QString(), window ); if ( l.exec() ) { KService::Ptr service = l.service(); if ( service ) return KRun::run( *service, lst, window, tempFiles, suggestedFileName, asn ); kDebug(7010) << "No service set, running " << l.text(); return KRun::run( l.text(), lst, window, false, suggestedFileName, asn ); // TODO handle tempFiles } return false; } void KRun::shellQuote( QString &_str ) { // Credits to Walter, says Bernd G. :) if (_str.isEmpty()) // Don't create an explicit empty parameter return; QChar q('\''); _str.replace(q, "'\\''").prepend(q).append(q); } class KRunMX1 : public KMacroExpanderBase { public: KRunMX1( const KService &_service ) : KMacroExpanderBase( '%' ), hasUrls( false ), hasSpec( false ), service( _service ) {} bool hasUrls:1, hasSpec:1; protected: virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret ); private: const KService &service; }; int KRunMX1::expandEscapedMacro( const QString &str, int pos, QStringList &ret ) { uint option = str[pos + 1].unicode(); switch( option ) { case 'c': ret << service.name().replace( '%', "%%" ); break; case 'k': ret << service.entryPath().replace( '%', "%%" ); break; case 'i': ret << "-icon" << service.icon().replace( '%', "%%" ); break; case 'm': // ret << "-miniicon" << service.icon().replace( '%', "%%" ); kWarning() << "-miniicon isn't supported anymore (service " << service.name() << ')' << endl; break; case 'u': case 'U': hasUrls = true; /* fallthrough */ case 'f': case 'F': case 'n': case 'N': case 'd': case 'D': case 'v': hasSpec = true; /* fallthrough */ default: return -2; // subst with same and skip } return 2; } class KRunMX2 : public KMacroExpanderBase { public: KRunMX2( const KUrl::List &_urls ) : KMacroExpanderBase( '%' ), ignFile( false ), urls( _urls ) {} bool ignFile:1; protected: virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret ); private: void subst( int option, const KUrl &url, QStringList &ret ); const KUrl::List &urls; }; void KRunMX2::subst( int option, const KUrl &url, QStringList &ret ) { switch( option ) { case 'u': ret << ((url.isLocalFile() && url.fragment().isNull() && url.encodedQuery().isNull()) ? url.toLocalFile() : url.url()); break; case 'd': ret << url.directory(); break; case 'f': ret << url.path(); break; case 'n': ret << url.fileName(); break; case 'v': if (url.isLocalFile() && QFile::exists( url.path() ) ) ret << KDesktopFile( url.path() ).desktopGroup().readEntry( "Dev" ); break; } return; } int KRunMX2::expandEscapedMacro( const QString &str, int pos, QStringList &ret ) { uint option = str[pos + 1].unicode(); switch( option ) { case 'f': case 'u': case 'n': case 'd': case 'v': if( urls.isEmpty() ) { if (!ignFile) kDebug() << "KRun::processDesktopExec: No URLs supplied to single-URL service " << str; } else if( urls.count() > 1 ) kWarning() << "KRun::processDesktopExec: " << urls.count() << " URLs supplied to single-URL service " << str; else subst( option, urls.first(), ret ); break; case 'F': case 'U': case 'N': case 'D': option += 'a' - 'A'; for( KUrl::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) subst( option, *it, ret ); break; case '%': ret = QStringList(QLatin1String("%")); break; default: return -2; // subst with same and skip } return 2; } QStringList KRun::processDesktopExec(const KService &_service, const KUrl::List& _urls, bool tempFiles, const QString& suggestedFileName) { QString exec = _service.exec(); if ( exec.isEmpty() ) { kWarning() << "KRun: no Exec field in `" << _service.entryPath() << "' !"; return QStringList(); } QStringList result; bool appHasTempFileOption; KRunMX1 mx1( _service ); KRunMX2 mx2( _urls ); if( !mx1.expandMacrosShellQuote( exec ) ) { // Error in shell syntax kWarning() << "KRun: syntax error in command" << _service.exec() << ", service" << _service.name(); return QStringList(); } // FIXME: the current way of invoking kioexec disables term and su use // Check if we need "tempexec" (kioexec in fact) appHasTempFileOption = tempFiles && _service.property("X-KDE-HasTempFileOption").toBool(); if( tempFiles && !appHasTempFileOption && _urls.size() ) { const QString kioexec = KStandardDirs::findExe("kioexec"); Q_ASSERT(!kioexec.isEmpty()); result << kioexec << "--tempfiles" << exec; if ( !suggestedFileName.isEmpty() ) { result << "--suggestedfilename"; result << suggestedFileName; } result += _urls.toStringList(); return result; } // Check if we need kioexec if( !mx1.hasUrls ) { for( KUrl::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it ) if ( !(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it) ) { // We need to run the app through kioexec const QString kioexec = KStandardDirs::findExe("kioexec"); Q_ASSERT(!kioexec.isEmpty()); result << kioexec; if ( tempFiles ) result << "--tempfiles"; if ( !suggestedFileName.isEmpty() ) { result << "--suggestedfilename"; result << suggestedFileName; } result << exec; result += _urls.toStringList(); return result; } } if ( appHasTempFileOption ) exec += " --tempfile"; // Did the user forget to append something like '%f'? // If so, then assume that '%f' is the right choice => the application // accepts only local files. if( !mx1.hasSpec ) { exec += " %f"; mx2.ignFile = true; } mx2.expandMacrosShellQuote( exec ); // syntax was already checked, so don't check return value /* 1 = need_shell, 2 = terminal, 4 = su 0 << split(cmd) 1 << "sh" << "-c" << cmd 2 << split(term) << "-e" << split(cmd) 3 << split(term) << "-e" << "sh" << "-c" << cmd 4 << "kdesu" << "-u" << user << "-c" << cmd 5 << "kdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd)) 6 << split(term) << "-e" << "su" << user << "-c" << cmd 7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd)) "sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh. this could be optimized with the -s switch of some su versions (e.g., debian linux). */ if (_service.terminal()) { KConfigGroup cg(KGlobal::config(), "General"); QString terminal = cg.readPathEntry("TerminalApplication", "konsole"); if (terminal == "konsole") terminal += " -caption=%c %i %m"; terminal += ' '; terminal += _service.terminalOptions(); if( !mx1.expandMacrosShellQuote( terminal ) ) { kWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'"; return QStringList(); } mx2.expandMacrosShellQuote( terminal ); result = KShell::splitArgs( terminal ); // assuming that the term spec never needs a shell! result << "-e"; } KShell::Errors err; QStringList execlist = KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err); if (err == KShell::NoError && !execlist.isEmpty()) { // mx1 checked for syntax errors already // Resolve the executable to ensure that helpers in lib/kde4/libexec/ are found. // Too bad for commands that need a shell - they must reside in $PATH. const QString exePath = KStandardDirs::findExe(execlist[0]); if (!exePath.isEmpty()) execlist[0] = exePath; } if (_service.substituteUid()) { if (_service.terminal()) result << "su"; else result << KStandardDirs::findExe("kdesu") << "-u"; result << _service.username() << "-c"; if (err == KShell::FoundMeta) exec = "/bin/sh -c " + KShell::quoteArg(exec); else exec = KShell::joinArgs(execlist); result << exec; } else { if (err == KShell::FoundMeta) result << "/bin/sh" << "-c" << exec; else result += execlist; } return result; } //static QString KRun::binaryName( const QString & execLine, bool removePath ) { // Remove parameters and/or trailing spaces. QStringList args = KShell::splitArgs( execLine ); for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) if (!(*it).contains('=')) // Remove path if wanted return removePath ? (*it).mid((*it).lastIndexOf('/') + 1) : *it; return QString(); } static bool runCommandInternal( KProcess* proc, const KService* service, const QString& binName, const QString &execName, const QString & iconName, QWidget* window, const QByteArray& asn ) { if( window != NULL ) window = window->topLevelWidget(); if (service && !service->entryPath().isEmpty() && !KDesktopFile::isAuthorizedDesktopFile( service->entryPath() )) { kWarning() << "No authorization to execute " << service->entryPath(); KMessageBox::sorry( window, i18n("You are not authorized to execute this file.")); return false; } QString bin = KRun::binaryName( binName, true ); #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification bool silent; QByteArray wmclass; KStartupInfoId id; bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass )); if( startup_notify ) { id.initId( asn ); id.setupStartupEnv(); KStartupInfoData data; data.setHostname(); data.setBin( bin ); if( !execName.isEmpty()) data.setName( execName ); else if( service && !service->name().isEmpty()) data.setName( service->name()); data.setDescription( i18n( "Launching %1" , data.name())); if( !iconName.isEmpty()) data.setIcon( iconName ); else if( service && !service->icon().isEmpty()) data.setIcon( service->icon()); if( !wmclass.isEmpty()) data.setWMClass( wmclass ); if( silent ) data.setSilent( KStartupInfoData::Yes ); data.setDesktop( KWindowSystem::currentDesktop()); if( window ) data.setLaunchedBy( window->winId()); KStartupInfo::sendStartup( id, data ); } int pid = KProcessRunner::run( proc, binName, id ); if( startup_notify && pid ) { KStartupInfoData data; data.addPid( pid ); KStartupInfo::sendChange( id, data ); KStartupInfo::resetStartupEnv(); } return pid != 0; #else Q_UNUSED( execName ); Q_UNUSED( iconName ); return KProcessRunner::run( proc, bin ) != 0; #endif } // This code is also used in klauncher. bool KRun::checkStartupNotify( const QString& /*binName*/, const KService* service, bool* silent_arg, QByteArray* wmclass_arg ) { bool silent = false; QByteArray wmclass; if( service && service->property( "StartupNotify" ).isValid()) { silent = !service->property( "StartupNotify" ).toBool(); wmclass = service->property( "StartupWMClass" ).toString().toLatin1(); } else if( service && service->property( "X-KDE-StartupNotify" ).isValid()) { silent = !service->property( "X-KDE-StartupNotify" ).toBool(); wmclass = service->property( "X-KDE-WMClass" ).toString().toLatin1(); } else // non-compliant app { if( service ) { if( service->isApplication() ) wmclass = "0"; // doesn't have .desktop entries needed, start as non-compliant else return false; // no startup notification at all } else { #if 0 // Create startup notification even for apps for which there shouldn't be any, // just without any visual feedback. This will ensure they'll be positioned on the proper // virtual desktop, and will get user timestamp from the ASN ID. wmclass = "0"; silent = true; #else // That unfortunately doesn't work, when the launched non-compliant application // launches another one that is compliant and there is any delay inbetween (bnc:#343359) return false; #endif } } if( silent_arg != NULL ) *silent_arg = silent; if( wmclass_arg != NULL ) *wmclass_arg = wmclass; return true; } static bool runTempService( const KService& _service, const KUrl::List& _urls, QWidget* window, bool tempFiles, const QString& suggestedFileName, const QByteArray& asn ) { if (!_urls.isEmpty()) { kDebug(7010) << "runTempService: first url " << _urls.first().url(); } QStringList args; if ((_urls.count() > 1) && !_service.allowMultipleFiles()) { // We need to launch the application N times. That sucks. // We ignore the result for application 2 to N. // For the first file we launch the application in the // usual way. The reported result is based on this // application. KUrl::List::ConstIterator it = _urls.begin(); while(++it != _urls.end()) { KUrl::List singleUrl; singleUrl.append(*it); runTempService( _service, singleUrl, window, tempFiles, suggestedFileName, "" ); } KUrl::List singleUrl; singleUrl.append(_urls.first()); args = KRun::processDesktopExec( _service, singleUrl, tempFiles, suggestedFileName ); } else { args = KRun::processDesktopExec(_service, _urls, tempFiles, suggestedFileName ); } if (args.isEmpty()) { KMessageBox::sorry(window, i18n("Error processing Exec field in %1", _service.entryPath()) ); return false; } kDebug(7010) << "runTempService: KProcess args=" << args; KProcess * proc = new KProcess; *proc << args; if (!_service.path().isEmpty()) proc->setWorkingDirectory(_service.path()); return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ), _service.name(), _service.icon(), window, asn ); } // WARNING: don't call this from processDesktopExec, since klauncher uses that too... static KUrl::List resolveURLs( const KUrl::List& _urls, const KService& _service ) { // Check which protocols the application supports. // This can be a list of actual protocol names, or just KIO for KDE apps. QStringList supportedProtocols = _service.property("X-KDE-Protocols").toStringList(); KRunMX1 mx1( _service ); QString exec = _service.exec(); if ( mx1.expandMacrosShellQuote( exec ) && !mx1.hasUrls ) { Q_ASSERT( supportedProtocols.isEmpty() ); // huh? If you support protocols you need %u or %U... } else { if ( supportedProtocols.isEmpty() ) { // compat mode: assume KIO if not set and it's a KDE app QStringList categories = _service.property("Categories").toStringList(); if ( categories.contains("KDE") ) supportedProtocols.append( "KIO" ); else { // if no KDE app, be a bit over-generic supportedProtocols.append( "http"); supportedProtocols.append( "ftp"); } } } kDebug(7010) << "supportedProtocols:" << supportedProtocols; KUrl::List urls( _urls ); if ( !supportedProtocols.contains( "KIO" ) ) { for( KUrl::List::Iterator it = urls.begin(); it != urls.end(); ++it ) { const KUrl url = *it; bool supported = url.isLocalFile() || supportedProtocols.contains( url.protocol().toLower() ); kDebug(7010) << "Looking at url=" << url << " supported=" << supported; if ( !supported && KProtocolInfo::protocolClass(url.protocol()) == ":local" ) { // Maybe we can resolve to a local URL? KUrl localURL = KIO::NetAccess::mostLocalUrl( url, 0 ); if ( localURL != url ) { *it = localURL; kDebug(7010) << "Changed to " << localURL; } } } } return urls; } bool KRun::run( const KService& _service, const KUrl::List& _urls, QWidget* window, bool tempFiles, const QString& suggestedFileName, const QByteArray& asn ) { if (!_service.entryPath().isEmpty() && !KDesktopFile::isAuthorizedDesktopFile( _service.entryPath())) { kWarning() << "No authorization to execute " << _service.entryPath(); KMessageBox::sorry( window, i18n("You are not authorized to execute this service.") ); return false; } if ( !tempFiles ) { // Remember we opened those urls, for the "recent documents" menu in kicker KUrl::List::ConstIterator it = _urls.begin(); for(; it != _urls.end(); ++it) { //kDebug(7010) << "KRecentDocument::adding " << (*it).url(); KRecentDocument::add( *it, _service.desktopEntryName() ); } } if ( tempFiles || _service.entryPath().isEmpty() || !suggestedFileName.isEmpty() ) { return runTempService( _service, _urls, window, tempFiles, suggestedFileName, asn ); } kDebug(7010) << "KRun::run " << _service.entryPath(); if (!_urls.isEmpty()) { kDebug(7010) << "First url " << _urls.first().url(); } // Resolve urls if needed, depending on what the app supports const KUrl::List urls = resolveURLs( _urls, _service ); QString error; int pid = 0; QByteArray myasn = asn; // startServiceByDesktopPath() doesn't take QWidget*, add it to the startup info now if( window != NULL ) { if( myasn.isEmpty()) myasn = KStartupInfo::createNewStartupId(); if( myasn != "0" ) { KStartupInfoId id; id.initId( myasn ); KStartupInfoData data; data.setLaunchedBy( window->winId()); KStartupInfo::sendChange( id, data ); } } int i = KToolInvocation::startServiceByDesktopPath( _service.entryPath(), urls.toStringList(), &error, 0L, &pid, myasn ); if (i != 0) { kDebug(7010) << error; KMessageBox::sorry( window, error ); return false; } kDebug(7010) << "startServiceByDesktopPath worked fine"; return true; } bool KRun::run( const QString& _exec, const KUrl::List& _urls, QWidget* window, const QString& _name, const QString& _icon, const QByteArray& asn ) { KService::Ptr service(new KService(_name, _exec, _icon)); return run( *service, _urls, window, false, QString(), asn ); } bool KRun::runCommand( const QString &cmd, QWidget* window ) { QString bin = binaryName( cmd, true ); return KRun::runCommand( cmd, bin, bin, window, "" ); } bool KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName, QWidget* window, const QByteArray& asn ) { kDebug(7010) << "runCommand " << cmd << "," << execName; KProcess * proc = new KProcess; proc->setShellCommand( cmd ); QString bin = binaryName( execName, true ); KService::Ptr service = KService::serviceByDesktopName( bin ); return runCommandInternal( proc, service.data(), bin, execName, iconName, window, asn ); } KRun::KRun( const KUrl& url, QWidget* window, mode_t mode, bool isLocalFile, bool showProgressInfo, const QByteArray& asn ) : d(new KRunPrivate(this)) { d->m_timer.setObjectName( "KRun::timer" ); d->m_timer.setSingleShot( true ); d->init (url, window, mode, isLocalFile, showProgressInfo, asn ); } void KRun::KRunPrivate::init ( const KUrl& url, QWidget* window, mode_t mode, bool isLocalFile, bool showProgressInfo, const QByteArray& asn ) { m_bFault = false; m_bAutoDelete = true; m_bProgressInfo = showProgressInfo; m_bFinished = false; m_job = 0L; m_strURL = url; m_bScanFile = false; m_bIsDirectory = false; m_bIsLocalFile = isLocalFile; m_mode = mode; m_runExecutables = true; m_window = window; m_asn = asn; q->setEnableExternalBrowser(true); // Start the timer. This means we will return to the event // loop and do initialization afterwards. // Reason: We must complete the constructor before we do anything else. m_bInit = true; q->connect( &m_timer, SIGNAL( timeout() ), q, SLOT( slotTimeout() ) ); - m_timer.start( 0 ); + startTimer(); kDebug(7010) << " new KRun " << q << " " << url.prettyUrl() << " timer=" << &m_timer; KGlobal::ref(); } void KRun::init() { kDebug(7010) << "INIT called"; if ( !d->m_strURL.isValid() ) { - d->m_showingError = true; + // TODO KDE5: call virtual method on error (see BrowserRun::init) + d->m_showingDialog = true; KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1", d->m_strURL.url() ) ); - d->m_showingError = false; + d->m_showingDialog = false; d->m_bFault = true; d->m_bFinished = true; - d->m_timer.start( 0 ); + d->startTimer(); return; } if ( !KAuthorized::authorizeUrlAction( "open", KUrl(), d->m_strURL) ) { QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->m_strURL.prettyUrl()); - d->m_showingError = true; + d->m_showingDialog = true; KMessageBoxWrapper::error( d->m_window, msg ); - d->m_showingError = false; + d->m_showingDialog = false; d->m_bFault = true; d->m_bFinished = true; - d->m_timer.start( 0 ); + d->startTimer(); return; } if ( !d->m_bIsLocalFile && d->m_strURL.isLocalFile() ) d->m_bIsLocalFile = true; QString exec; if (d->m_strURL.protocol().startsWith("http")) { exec = d->m_externalBrowser; } if ( d->m_bIsLocalFile ) { if ( d->m_mode == 0 ) { KDE_struct_stat buff; if ( KDE_stat( QFile::encodeName(d->m_strURL.path()), &buff ) == -1 ) { - d->m_showingError = true; + d->m_showingDialog = true; KMessageBoxWrapper::error( d->m_window, i18n( "Unable to run the command specified. The file or folder %1 does not exist." , Qt::escape(d->m_strURL.prettyUrl()) ) ); - d->m_showingError = false; + d->m_showingDialog = false; d->m_bFault = true; d->m_bFinished = true; - d->m_timer.start( 0 ); + d->startTimer(); return; } d->m_mode = buff.st_mode; } KMimeType::Ptr mime = KMimeType::findByUrl( d->m_strURL, d->m_mode, d->m_bIsLocalFile ); assert( mime ); kDebug(7010) << "MIME TYPE is " << mime->name(); - foundMimeType( mime->name() ); + mimeTypeDetermined( mime->name() ); return; } else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( d->m_strURL ) ) { kDebug(7010) << "Helper protocol"; bool ok = false; KUrl::List urls; urls.append( d->m_strURL ); if (exec.isEmpty()) { exec = KProtocolInfo::exec( d->m_strURL.protocol() ); if (exec.isEmpty()) { - foundMimeType(KProtocolManager::defaultMimetype(d->m_strURL)); + mimeTypeDetermined(KProtocolManager::defaultMimetype(d->m_strURL)); return; } run( exec, urls, d->m_window, false, QString(), d->m_asn ); ok = true; } else if (exec.startsWith('!')) { exec = exec.mid(1); // Literal command exec += " %u"; run( exec, urls, d->m_window, false, QString(), d->m_asn ); ok = true; } else { KService::Ptr service = KService::serviceByStorageId( exec ); if (service) { run( *service, urls, d->m_window, false, QString(), d->m_asn ); ok = true; } } if (ok) { d->m_bFinished = true; // will emit the error and autodelete this - d->m_timer.start( 0 ); + d->startTimer(); return; } } // Did we already get the information that it is a directory ? if ( S_ISDIR( d->m_mode ) ) { - foundMimeType( "inode/directory" ); + mimeTypeDetermined( "inode/directory" ); return; } // Let's see whether it is a directory if ( !KProtocolManager::supportsListing( d->m_strURL ) ) { //kDebug(7010) << "Protocol has no support for listing"; // No support for listing => it can't be a directory (example: http) scanFile(); return; } kDebug(7010) << "Testing directory (stating)"; // It may be a directory or a file, let's stat KIO::JobFlags flags = d->m_bProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; KIO::StatJob *job = KIO::stat( d->m_strURL, KIO::StatJob::SourceSide, 0 /* no details */, flags ); job->ui()->setWindow (d->m_window); connect( job, SIGNAL( result( KJob * ) ), this, SLOT( slotStatResult( KJob * ) ) ); d->m_job = job; kDebug(7010) << " Job " << job << " is about stating " << d->m_strURL.url(); } KRun::~KRun() { kDebug(7010) << "KRun::~KRun() " << this; d->m_timer.stop(); killJob(); KGlobal::deref(); kDebug(7010) << "KRun::~KRun() done " << this; delete d; } void KRun::scanFile() { - kDebug(7010) << "###### KRun::scanFile " << d->m_strURL.url(); + kDebug(7010) << d->m_strURL; // First, let's check for well-known extensions // Not when there is a query in the URL, in any case. if ( d->m_strURL.query().isEmpty() ) { KMimeType::Ptr mime = KMimeType::findByUrl( d->m_strURL ); assert( mime ); if ( mime->name() != "application/octet-stream" || d->m_bIsLocalFile ) { kDebug(7010) << "Scanfile: MIME TYPE is " << mime->name(); - foundMimeType( mime->name() ); + mimeTypeDetermined( mime->name() ); return; } } // No mimetype found, and the URL is not local (or fast mode not allowed). // We need to apply the 'KIO' method, i.e. either asking the server or // getting some data out of the file, to know what mimetype it is. if ( !KProtocolManager::supportsReading( d->m_strURL ) ) { kError(7010) << "#### NO SUPPORT FOR READING!" << endl; d->m_bFault = true; d->m_bFinished = true; - d->m_timer.start( 0 ); + d->startTimer(); return; } kDebug(7010) << this << " Scanning file " << d->m_strURL.url(); KIO::JobFlags flags = d->m_bProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; KIO::TransferJob *job = KIO::get( d->m_strURL, KIO::NoReload /*reload*/, flags ); job->ui()->setWindow (d->m_window); connect(job, SIGNAL( result(KJob *)), this, SLOT( slotScanFinished(KJob *))); connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)), this, SLOT( slotScanMimeType(KIO::Job *, const QString &))); d->m_job = job; kDebug(7010) << " Job " << job << " is about getting from " << d->m_strURL.url(); } void KRun::slotTimeout() { kDebug(7010) << this << " slotTimeout called"; if ( d->m_bInit ) { d->m_bInit = false; init(); return; } if ( d->m_bFault ) { emit error(); } if ( d->m_bFinished ) { emit finished(); } else { if ( d->m_bScanFile ) { d->m_bScanFile = false; scanFile(); return; } else if ( d->m_bIsDirectory ) { d->m_bIsDirectory = false; - foundMimeType( "inode/directory" ); + mimeTypeDetermined( "inode/directory" ); return; } } if ( d->m_bAutoDelete ) { delete this; return; } } void KRun::slotStatResult( KJob * job ) { d->m_job = 0L; if (job->error()) { - d->m_showingError = true; + d->m_showingDialog = true; kError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl; job->uiDelegate()->showErrorMessage(); //kDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us"; - d->m_showingError = false; + d->m_showingDialog = false; d->m_bFault = true; d->m_bFinished = true; // will emit the error and autodelete this - d->m_timer.start( 0 ); + d->startTimer(); } else { kDebug(7010) << "Finished"; if(!qobject_cast(job)) kFatal() << "job is a " << typeid(*job).name() << " should be a StatJob"; const KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult(); const mode_t mode = entry.numberValue( KIO::UDSEntry::UDS_FILE_TYPE ); if ( S_ISDIR( mode ) ) d->m_bIsDirectory = true; // it's a dir else d->m_bScanFile = true; // it's a file d->m_localPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH ); // mimetype already known? (e.g. print:/manager) const QString knownMimeType = entry.stringValue( KIO::UDSEntry::UDS_MIME_TYPE ) ; if ( !knownMimeType.isEmpty() ) { - foundMimeType( knownMimeType ); + mimeTypeDetermined( knownMimeType ); d->m_bFinished = true; } // We should have found something assert ( d->m_bScanFile || d->m_bIsDirectory ); // Start the timer. Once we get the timer event this // protocol server is back in the pool and we can reuse it. // This gives better performance than starting a new slave - d->m_timer.start( 0 ); + d->startTimer(); } } void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype ) { if ( mimetype.isEmpty() ) kWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug."; - foundMimeType( mimetype ); + mimeTypeDetermined( mimetype ); d->m_job = 0; } void KRun::slotScanFinished( KJob *job ) { d->m_job = 0; if (job->error()) { - d->m_showingError = true; + d->m_showingDialog = true; kError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl; job->uiDelegate()->showErrorMessage(); //kDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us"; - d->m_showingError = false; + d->m_showingDialog = false; d->m_bFault = true; d->m_bFinished = true; // will emit the error and autodelete this - d->m_timer.start( 0 ); + d->startTimer(); } } +void KRun::mimeTypeDetermined(const QString& mimeType) +{ + // foundMimeType reimplementations might show a dialog box; + // make sure some timer doesn't kill us meanwhile (#137678, #156447) + Q_ASSERT(!d->m_showingDialog); + d->m_showingDialog = true; + + foundMimeType(mimeType); + + d->m_showingDialog = false; +} + void KRun::foundMimeType( const QString& type ) { kDebug(7010) << "Resulting mime type is " << type; /* // Automatically unzip stuff // Disabled since the new KIO doesn't have filters yet. if ( type == "application/x-gzip" || type == "application/x-bzip" || type == "application/x-bzip" ) { KUrl::List lst = KUrl::split( m_strURL ); if ( lst.isEmpty() ) { QString tmp = i18n( "Malformed URL" ); tmp += "\n"; tmp += m_strURL.url(); KMessageBoxWrapper::error( 0L, tmp ); return; } if ( type == "application/x-gzip" ) lst.prepend( KUrl( "gzip:/decompress" ) ); else if ( type == "application/x-bzip" ) lst.prepend( KUrl( "bzip:/decompress" ) ); else if ( type == "application/x-bzip" ) lst.prepend( KUrl( "bzip2:/decompress" ) ); else if ( type == "application/x-tar" ) lst.prepend( KUrl( "tar:/" ) ); // Move the HTML style reference to the leftmost URL KUrl::List::Iterator it = lst.begin(); ++it; (*lst.begin()).setRef( (*it).ref() ); (*it).setRef( QString() ); // Create the new URL m_strURL = KUrl::join( lst ); kDebug(7010) << "Now trying with " << debugString(m_strURL.url()); killJob(); // We don't know if this is a file or a directory. Let's test this first. // (For instance a tar.gz is a directory contained inside a file) // It may be a directory or a file, let's stat KIO::StatJob *job = KIO::stat( m_strURL, m_bProgressInfo ); connect( job, SIGNAL( result( KJob * ) ), this, SLOT( slotStatResult( KJob * ) ) ); m_job = job; return; } */ KIO::TransferJob *job = qobject_cast( d->m_job ); if ( job ) { job->putOnHold(); KIO::Scheduler::publishSlaveOnHold(); d->m_job = 0; } Q_ASSERT( !d->m_bFinished ); KMimeType::Ptr mime = KMimeType::mimeType( type ); if ( !mime ) kWarning(7010) << "Unknown mimetype " << type; // Suport for preferred service setting, see setPreferredService if ( !d->m_preferredService.isEmpty() ) { kDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService; KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService ); if ( serv && serv->hasMimeType( mime.data() ) ) { KUrl::List lst; lst.append( d->m_strURL ); d->m_bFinished = KRun::run( *serv, lst, d->m_window, false, QString(), d->m_asn ); /// Note: the line above means that if that service failed, we'll /// go to runUrl to maybe find another service, even though a dialog /// box was displayed. That's good if runUrl tries another service, /// but it's not good if it tries the same one :} } } // Resolve .desktop files from media:/, remote:/, applications:/ etc. if ( mime && mime->is( "application/x-desktop" ) && !d->m_localPath.isEmpty() ) { d->m_strURL = KUrl(); d->m_strURL.setPath( d->m_localPath ); } if (!d->m_bFinished && KRun::runUrl( d->m_strURL, type, d->m_window, false /*tempfile*/, d->m_runExecutables, d->m_suggestedFileName, d->m_asn )){ d->m_bFinished = true; } else{ d->m_bFinished = true; d->m_bFault = true; } - d->m_timer.start( 0 ); + d->startTimer(); } void KRun::killJob() { if ( d->m_job ) { kDebug(7010) << "KRun::killJob run=" << this << " m_job=" << d->m_job; d->m_job->kill(); d->m_job = 0L; } } void KRun::abort() { - kDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError; + kDebug(7010) << "KRun::abort " << this << " m_showingDialog=" << d->m_showingDialog; killJob(); // If we're showing an error message box, the rest will be done // after closing the msgbox -> don't autodelete nor emit signals now. - if ( d->m_showingError ) + if ( d->m_showingDialog ) return; d->m_bFault = true; d->m_bFinished = true; d->m_bInit = false; d->m_bScanFile = false; // will emit the error and autodelete this - d->m_timer.start( 0 ); + d->startTimer(); } bool KRun::hasError() const { return d->m_bFault; } bool KRun::hasFinished() const { return d->m_bFinished; } bool KRun::autoDelete() const { return d->m_bAutoDelete; } void KRun::setAutoDelete(bool b) { d->m_bAutoDelete = b; } void KRun::setEnableExternalBrowser(bool b) { if (b) d->m_externalBrowser = KConfigGroup(KGlobal::config(), "General").readEntry("BrowserApplication"); else d->m_externalBrowser.clear(); } void KRun::setPreferredService( const QString& desktopEntryName ) { d->m_preferredService = desktopEntryName; } void KRun::setRunExecutables(bool b) { d->m_runExecutables = b; } void KRun::setSuggestedFileName( const QString& fileName ) { d->m_suggestedFileName = fileName; } QString KRun::suggestedFileName() const { return d->m_suggestedFileName; } bool KRun::isExecutable( const QString& serviceType ) { return ( serviceType == "application/x-desktop" || serviceType == "application/x-executable" || serviceType == "application/x-ms-dos-executable" || serviceType == "application/x-shellscript" ); } void KRun::setUrl( const KUrl &url ) { d->m_strURL = url; } KUrl KRun::url() const { return d->m_strURL; } void KRun::setError( bool error ) { d->m_bFault = error; } void KRun::setProgressInfo( bool progressInfo ) { d->m_bProgressInfo = progressInfo; } bool KRun::progressInfo() const { return d->m_bProgressInfo; } void KRun::setFinished( bool finished ) { d->m_bFinished = finished; + // TODO d->startTimer(); (and later on remove it from callers...) } void KRun::setJob( KIO::Job *job ) { d->m_job = job; } KIO::Job* KRun::job() { return d->m_job; } QTimer& KRun::timer() { return d->m_timer; } void KRun::setDoScanFile( bool scanFile ) { d->m_bScanFile = scanFile; } bool KRun::doScanFile() const { return d->m_bScanFile; } void KRun::setIsDirecory( bool isDirectory ) { d->m_bIsDirectory = isDirectory; } bool KRun::isDirectory() const { return d->m_bIsDirectory; } void KRun::setInitializeNextAction( bool initialize ) { d->m_bInit = initialize; } bool KRun::initializeNextAction() const { return d->m_bInit; } void KRun::setIsLocalFile( bool isLocalFile ) { d->m_bIsLocalFile = isLocalFile; } bool KRun::isLocalFile() const { return d->m_bIsLocalFile; } void KRun::setMode( mode_t mode ) { d->m_mode = mode; } mode_t KRun::mode() const { return d->m_mode; } /****************/ #ifndef Q_WS_X11 int KProcessRunner::run(KProcess * p, const QString & binName) { return (new KProcessRunner(p, binName))->pid(); } #else int KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id) { return (new KProcessRunner(p, binName, id))->pid(); } #endif #ifndef Q_WS_X11 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName) #else KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& _id) : id(_id) #endif { process = p; binName = _binName; connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotProcessExited(int, QProcess::ExitStatus))); process->start(); if (!process->waitForStarted()) { slotProcessExited(127, QProcess::CrashExit); } } KProcessRunner::~KProcessRunner() { delete process; } int KProcessRunner::pid() const { return process ? process->pid() : 0; } void KProcessRunner::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) { kDebug(7010) << "slotProcessExited " << binName; kDebug(7010) << "normalExit " << (exitStatus == QProcess::NormalExit); kDebug(7010) << "exitCode " << exitCode; bool showErr = exitStatus == QProcess::NormalExit && (exitCode == 127 || exitCode == 1); if (!binName.isEmpty() && (showErr || pid() == 0 )) { // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist. // We can't just rely on that, but it's a good hint. // Before assuming its really so, we'll try to find the binName // relatively to current directory, and then in the PATH. if (!QFile(binName).exists() && KStandardDirs::findExe(binName).isEmpty()) { KGlobal::ref(); KMessageBox::sorry(0L, i18n("Could not find the program '%1'", binName)); KGlobal::deref(); } } #ifdef Q_WS_X11 if (!id.none()) { KStartupInfoData data; data.addPid(pid()); // announce this pid for the startup notification has finished data.setHostname(); KStartupInfo::sendFinish(id, data); } #endif deleteLater(); } #include "krun.moc" #include "krun_p.moc" diff --git a/kio/kio/krun.h b/kio/kio/krun.h index 418b531fd6..7340f0c3c3 100644 --- a/kio/kio/krun.h +++ b/kio/kio/krun.h @@ -1,501 +1,512 @@ // -*- mode: c++; c-basic-offset: 2 -*- /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KRUN_H #define KRUN_H #include #include #include #include #include #include class KService; class KStartupInfo; class KJob; namespace KIO { class Job; } /** * To open files with their associated applications in KDE, use KRun. * * It can execute any desktop entry, as well as any file, using * the default application or another application "bound" to the file type * (or URL protocol). * * In that example, the mimetype of the file is not known by the application, * so a KRun instance must be created. It will determine the mimetype by itself. * If the mimetype is known, or if you even know the service (application) to * use for this file, use one of the static methods. * * By default KRun uses auto deletion. It causes the KRun instance to delete * itself when the it finished its task. If you allocate the KRun * object on the stack you must disable auto deletion, otherwise it will crash. * * @short Opens files with their associated applications in KDE */ class KIO_EXPORT KRun : public QObject { Q_OBJECT public: /** * @param url the URL of the file or directory to 'run' * * @param window * The top-level widget of the app that invoked this object. * It is used to make sure private information like passwords * are properly handled per application. * * @param mode The @p st_mode field of struct stat. If * you don't know this set it to 0. * * @param isLocalFile * If this parameter is set to @p false then @p url is * examined to find out whether it is a local URL or * not. This flag is just used to improve speed, since the * function KUrl::isLocalFile is a bit slow. * * @param showProgressInfo * Whether to show progress information when determining the * type of the file (i.e. when using KIO::stat and KIO::mimetype) * Before you set this to false to avoid a dialog box, think about * a very slow FTP server... * It is always better to provide progress info in such cases. * @param asn * Application startup notification id, if available (otherwise ""). */ KRun( const KUrl& url, QWidget* window, mode_t mode = 0, bool isLocalFile = false, bool showProgressInfo = true, const QByteArray& asn = "" ); /** * Destructor. Don't call it yourself, since a KRun object auto-deletes * itself. */ virtual ~KRun(); /** * Abort this KRun. This kills any jobs launched by it, * and leads to deletion if auto-deletion is on. * This is much safer than deleting the KRun (in case it's * currently showing an error dialog box, for instance) */ void abort(); /** * Returns true if the KRun instance has an error. * @return true when an error occurred * @see error() */ bool hasError() const; /** * Returns true if the KRun instance has finished. * @return true if the KRun instance has finished * @see finished() */ bool hasFinished() const; /** * Checks whether auto delete is activated. * Auto-deletion causes the KRun instance to delete itself * when it finished its task. * By default auto deletion is on. * @return true if auto deletion is on, false otherwise */ bool autoDelete() const; /** * Enables or disabled auto deletion. * Auto deletion causes the KRun instance to delete itself * when it finished its task. If you allocate the KRun * object on the stack you must disable auto deletion. * By default auto deletion is on. * @param b true to enable auto deletion, false to disable */ void setAutoDelete(bool b); /** * Set the preferred service for opening this URL, after * its mimetype will have been found by KRun. IMPORTANT: the service is * only used if its configuration says it can handle this mimetype. * This is used for instance for the X-KDE-LastOpenedWith key, for * the recent documents list. * @param desktopEntryName the desktopEntryName of the service, e.g. "kate". */ void setPreferredService( const QString& desktopEntryName ); /** * Sets whether executables, .desktop files or shell scripts should * be run by KRun. This is enabled by default. * @param b whether to run executable files or not. * @see isExecutable() */ void setRunExecutables(bool b); /** * Sets whether the external webbrowser setting should be honoured. * This is enabled by default. * This should only be disabled in webbrowser applications. * @param b whether to enable the external browser or not. */ void setEnableExternalBrowser(bool b); /** * Sets the file name to use in the case of downloading the file to a tempfile * in order to give to a non-url-aware application. Some apps rely on the extension * to determine the mimetype of the file. Usually the file name comes from the URL, * but in the case of the HTTP Content-Disposition header, we need to override the * file name. */ void setSuggestedFileName( const QString& fileName ); /** * Suggested file name given by the server (e.g. HTTP content-disposition) */ QString suggestedFileName() const; /** * Open a list of URLs with a certain service (application). * * @param service the service to run * @param urls the list of URLs, can be empty (app launched * without argument) * @param window The top-level widget of the app that invoked this object. * @param tempFiles if true and urls are local files, they will be deleted * when the application exits. * @param suggestedFileName see setSuggestedFileName * @param asn Application startup notification id, if any (otherwise ""). * @return @c true on success, @c false on error */ static bool run( const KService& service, const KUrl::List& urls, QWidget* window, bool tempFiles = false, const QString& suggestedFileName = QString(), const QByteArray& asn = "" ); /** * Open a list of URLs with. * * @param exec the name of the executable, for example * "/usr/bin/netscape". * @param urls the list of URLs to open, can be empty (app launched without argument) * @param window The top-level widget of the app that invoked this object. * @param name the logical name of the application, for example * "Netscape 4.06". * @param icon the icon which should be used by the application. * @param asn Application startup notification id, if any (otherwise ""). * @return @c true on success, @c false on error */ static bool run( const QString& exec, const KUrl::List& urls, QWidget* window, const QString& name = QString(), const QString& icon = QString(), const QByteArray& asn = "" ); /** * Open the given URL. * * This function is used after the mime type * is found out. It will search for all services which can handle * the mime type and call run() afterwards. * @param url the URL to open * @param mimetype the mime type of the resource * @param window The top-level widget of the app that invoked this object. * @param tempFile if true and url is a local file, it will be deleted * when the launched application exits. * @param runExecutables if false then local .desktop files, * executables and shell scripts will not be run. * See also isExecutable(). * @param suggestedFileName see setSuggestedFileName * @param asn Application startup notification id, if any (otherwise ""). * @return @c true on success, @c false on error */ static bool runUrl( const KUrl& url, const QString& mimetype, QWidget* window, bool tempFile = false , bool runExecutables = true, const QString& suggestedFileName = QString(), const QByteArray& asn = "" ); /** * Run the given shell command and notifies kicker of the starting * of the application. If the program to be called doesn't exist, * an error box will be displayed. * * Use only when you know the full command line. Otherwise use the other * static methods, or KRun's constructor. * * @p cmd must be a shell command. You must not append "&" * to it, since the function will do that for you. * @param window The top-level widget of the app that invoked this object. * * @return @c true on success, @c false on error */ static bool runCommand( const QString &cmd, QWidget* window ); /** * Same as the other runCommand(), but it also takes the name of the * binary, to display an error message in case it couldn't find it. * * @param cmd must be a shell command. You must not append "&" * to it, since the function will do that for you. * @param execName the name of the executable * @param icon icon for app starting notification * @param window The top-level widget of the app that invoked this object. * @param asn Application startup notification id, if any (otherwise ""). * @return @c true on success, @c false on error */ static bool runCommand( const QString& cmd, const QString & execName, const QString & icon, QWidget* window, const QByteArray& asn = "" ); /** * Display the Open-With dialog for those URLs, and run the chosen application. * @param lst the list of applications to run * @param window The top-level widget of the app that invoked this object. * @param tempFiles if true and lst are local files, they will be deleted * when the application exits. * @param suggestedFileName see setSuggestedFileName * @param asn Application startup notification id, if any (otherwise ""). * @return false if the dialog was canceled */ static bool displayOpenWithDialog( const KUrl::List& lst, QWidget* window, bool tempFiles = false, const QString& suggestedFileName = QString(), const QByteArray& asn = "" ); /** * Quotes a string for the shell. * An empty string will @em not be quoted. * * @deprecated Use KShell::quoteArg() instead. @em Note that this function * behaves differently for empty arguments and returns the result * differently. * * @param str the string to quote. The quoted string will be written here */ static KDE_DEPRECATED void shellQuote( QString &str ); /** * Processes a Exec= line as found in .desktop files. * @param _service the service to extract information from. * @param _urls The urls the service should open. * @param tempFiles if true and urls are local files, they will be deleted * when the application exits. * @param suggestedFileName see setSuggestedFileName * * @return a list of arguments suitable for KProcess::setProgram(). */ static QStringList processDesktopExec(const KService &_service, const KUrl::List &_urls, bool tempFiles = false, const QString& suggestedFileName = QString() ); /** * Given a full command line (e.g. the Exec= line from a .desktop file), * extract the name of the binary being run. * @param execLine the full command line * @param removePath if true, remove a (relative or absolute) path. E.g. /usr/bin/ls becomes ls. * @return the name of the binary to run */ static QString binaryName( const QString & execLine, bool removePath ); /** * Returns whether @p serviceType refers to an executable program instead * of a data file. */ static bool isExecutable( const QString& serviceType ); /** * Returns whether the @p url of @p mimetype is executable. * To be executable the file must pass the following rules: * -# Must reside on the local filesystem. * -# Must be marked as executable for the user by the filesystem. * -# The mime type must inherit application/x-executable or application/x-executable-script. * To allow a script to run when the above rules are satisfied add the entry * @code * X-KDE-IsAlso=application/x-executable-script * @endcode * to the mimetype's desktop file. */ static bool isExecutableFile( const KUrl& url, const QString &mimetype ); /** * @internal */ static bool checkStartupNotify( const QString& binName, const KService* service, bool* silent_arg, QByteArray* wmclass_arg ); Q_SIGNALS: /** * Emitted when the operation finished. * @see hasFinished() */ void finished(); /** * Emitted when the operation had an error. * @see hasError() */ void error(); protected Q_SLOTS: /** * All following protected slots are used by subclasses of KRun! */ /** * This slot is called whenever the internal time has * a timeout. */ void slotTimeout(); /** * This slot is called when the scan job is finished. */ void slotScanFinished( KJob * ); /** * This slot is called when the scan job has found out * the mime type. */ void slotScanMimeType( KIO::Job *, const QString &type ); + /** + * Call this from subclasses when you have determined the mimetype. + * It will call foundMimeType, but also sets up protection against deletion during message boxes. + * @since 4.0.2 + */ + void mimeTypeDetermined(const QString& mimeType); + /** * This slot is called when the 'stat' job has finished. */ virtual void slotStatResult( KJob * ); protected: /** * All following protected methods are used by subclasses of KRun! */ /** * Initializes the krun object. */ virtual void init(); /** * Start scanning a file. */ virtual void scanFile(); /** - * Called if the mimetype has been detected. The function checks - * whether the document and appends the gzip protocol to the - * URL. Otherwise runUrl is called to finish the job. + * Called if the mimetype has been detected. The function runs + * the application associated with this mimetype. + * Reimplement this method to implement a different behavior, + * like opening the component for displaying the URL embedded. */ virtual void foundMimeType( const QString& type ); /** * Kills the file scanning job. */ virtual void killJob(); /** * Sets the url. */ void setUrl( const KUrl &url ); /** * Returns the url. */ KUrl url() const; /** * Sets whether an error has occured. */ void setError( bool error ); /** * Sets whether progress information shall be shown. */ void setProgressInfo( bool progressInfo ); /** * Returns whether progress information are shown. */ bool progressInfo() const; /** - * Marks the job as finished. + * Marks this 'KRun' instance as finished. */ void setFinished( bool finished ); /** * Sets the job. */ void setJob( KIO::Job *job ); /** * Returns the job. */ KIO::Job* job(); /** * Returns the timer object. */ QTimer& timer(); /** - * Sets whether the file shall be scanned. + * Indicate that the next action is to scan the file. + * @deprecated not useful in public API */ - void setDoScanFile( bool scanFile ); + KDE_DEPRECATED void setDoScanFile( bool scanFile ); /** * Returns whether the file shall be scanned. + * @deprecated not useful in public API */ - bool doScanFile() const; + KDE_DEPRECATED bool doScanFile() const; /** * Sets whether it is a directory. + * @deprecated typo in the name, and not useful as a public method */ - void setIsDirecory( bool isDirectory ); + KDE_DEPRECATED void setIsDirecory( bool isDirectory ); /** * Returns whether it is a directory. */ bool isDirectory() const; /** - * Returns whether the next action shall be initialized. + * @deprecated not useful in public API */ - void setInitializeNextAction( bool initialize ); + KDE_DEPRECATED void setInitializeNextAction( bool initialize ); /** - * Returns whether the next action shall be initialized. + * @deprecated not useful in public API */ - bool initializeNextAction() const; + KDE_DEPRECATED bool initializeNextAction() const; /** * Sets whether it is a local file. */ void setIsLocalFile( bool isLocalFile ); /** * Returns whether it is a local file. */ bool isLocalFile() const; /** * Sets the file mode. */ void setMode( mode_t mode ); /** * Returns the file mode. */ mode_t mode() const; private: class KRunPrivate; KRunPrivate* const d; }; #endif diff --git a/kparts/browserrun.cpp b/kparts/browserrun.cpp index 3b3f682e61..a3f545ffd5 100644 --- a/kparts/browserrun.cpp +++ b/kparts/browserrun.cpp @@ -1,541 +1,541 @@ /* This file is part of the KDE project * * Copyright (C) 2002 David Faure * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "browserrun.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KParts; class BrowserRun::BrowserRunPrivate { public: bool m_bHideErrorDialog; bool m_bRemoveReferrer; bool m_bTrustedSource; KParts::OpenUrlArguments m_args; KParts::BrowserArguments m_browserArgs; KParts::ReadOnlyPart *m_part; // QGuardedPtr? QPointer m_window; QString m_mimeType; QString m_contentDisposition; }; BrowserRun::BrowserRun( const KUrl& url, const KParts::OpenUrlArguments& args, const KParts::BrowserArguments& browserArgs, KParts::ReadOnlyPart *part, QWidget* window, bool removeReferrer, bool trustedSource, bool hideErrorDialog ) : KRun( url, window, 0 /*mode*/, false /*is_local_file known*/, false /* no GUI */ ), d(new BrowserRunPrivate) { d->m_bHideErrorDialog = hideErrorDialog; d->m_bRemoveReferrer = removeReferrer; d->m_bTrustedSource = trustedSource; d->m_args = args; d->m_browserArgs = browserArgs; d->m_part = part; d->m_window = window; } BrowserRun::~BrowserRun() { delete d; } KParts::ReadOnlyPart* BrowserRun::part() const { return d->m_part; } KUrl BrowserRun::url() const { return KRun::url(); } void BrowserRun::init() { if ( d->m_bHideErrorDialog ) { // ### KRun doesn't call a virtual method when it finds out that the URL // is either malformed, or points to a non-existing local file... // So we need to reimplement some of the checks, to handle d->m_bHideErrorDialog if ( !KRun::url().isValid() ) { redirectToError( KIO::ERR_MALFORMED_URL, KRun::url().url() ); return; } if ( !isLocalFile() && !hasError() && KRun::url().isLocalFile() ) setIsLocalFile( true ); if ( isLocalFile() ) { struct stat buff; if ( stat( QFile::encodeName(KRun::url().toLocalFile()), &buff ) == -1 ) { kDebug(1000) << "BrowserRun::init:" << KRun::url().toLocalFile() << "doesn't exist."; redirectToError( KIO::ERR_DOES_NOT_EXIST, KRun::url().toLocalFile() ); return; } setMode( buff.st_mode ); // while we're at it, save it for KRun::init() to use it } } KRun::init(); } void BrowserRun::scanFile() { kDebug(1000) << "BrowserRun::scanfile" << KRun::url(); // Let's check for well-known extensions // Not when there is a query in the URL, in any case. // Optimization for http/https, findByURL doesn't trust extensions over http. if ( KRun::url().query().isEmpty() && !KRun::url().protocol().startsWith("http") ) { KMimeType::Ptr mime = KMimeType::findByUrl( KRun::url() ); assert( mime ); if ( mime->name() != "application/octet-stream" || isLocalFile() ) { kDebug(1000) << "Scanfile: MIME TYPE is" << mime->name(); - foundMimeType( mime->name() ); + mimeTypeDetermined( mime->name() ); return; } } QMap& metaData = d->m_args.metaData(); if ( d->m_part ) { const QString proto = d->m_part->url().protocol().toLower(); if (proto == "https" || proto == "webdavs") { metaData.insert("main_frame_request", "TRUE" ); metaData.insert("ssl_was_in_use", "TRUE" ); metaData.insert("ssl_activate_warnings", "TRUE" ); } else if (proto == "http" || proto == "webdav") { metaData.insert("ssl_activate_warnings", "TRUE" ); metaData.insert("ssl_was_in_use", "FALSE" ); } // Set the PropagateHttpHeader meta-data if it has not already been set... if (!metaData.contains("PropagateHttpHeader")) metaData.insert("PropagateHttpHeader", "TRUE"); } KIO::TransferJob *job; if ( d->m_browserArgs.doPost() && KRun::url().protocol().startsWith("http")) { job = KIO::http_post( KRun::url(), d->m_browserArgs.postData, KIO::HideProgressInfo ); job->addMetaData( "content-type", d->m_browserArgs.contentType() ); } else { job = KIO::get(KRun::url(), - d->m_args.reload() ? KIO::Reload : KIO::NoReload, + d->m_args.reload() ? KIO::Reload : KIO::NoReload, KIO::HideProgressInfo); } if ( d->m_bRemoveReferrer ) metaData.remove("referrer"); job->addMetaData( metaData ); job->ui()->setWindow( d->m_window ); connect( job, SIGNAL( result( KJob *)), this, SLOT( slotBrowserScanFinished(KJob *))); connect( job, SIGNAL( mimetype( KIO::Job *, const QString &)), this, SLOT( slotBrowserMimetype(KIO::Job *, const QString &))); setJob( job ); } void BrowserRun::slotBrowserScanFinished(KJob *job) { kDebug(1000) << "BrowserRun::slotBrowserScanFinished"; if ( job->error() == KIO::ERR_IS_DIRECTORY ) { // It is in fact a directory. This happens when HTTP redirects to FTP. // Due to the "protocol doesn't support listing" code in BrowserRun, we // assumed it was a file. kDebug(1000) << "It is in fact a directory!"; // Update our URL in case of a redirection KRun::setUrl( static_cast(job)->url() ); setJob( 0 ); - foundMimeType( "inode/directory" ); + mimeTypeDetermined( "inode/directory" ); } else { if ( job->error() ) handleError( job ); else KRun::slotScanFinished(job); } } void BrowserRun::slotBrowserMimetype( KIO::Job *_job, const QString &type ) { Q_ASSERT( _job == KRun::job() ); KIO::TransferJob *job = static_cast(KRun::job()); // Update our URL in case of a redirection //kDebug(1000) << "old URL=" << KRun::url(); //kDebug(1000) << "new URL=" << job->url(); setUrl( job->url() ); kDebug(1000) << "slotBrowserMimetype: found" << type << "for" << KRun::url(); // Suggested filename given by the server (e.g. HTTP content-disposition) // When set, we should really be saving instead of embedding const QString suggestedFileName = job->queryMetaData("content-disposition-filename"); setSuggestedFileName(suggestedFileName); // store it (in KRun) //kDebug(1000) << "suggestedFileName=" << suggestedFileName; d->m_contentDisposition = job->queryMetaData("content-disposition-type"); // Make a copy to avoid a dead reference QString _type = type; job->putOnHold(); setJob( 0 ); - foundMimeType( _type ); + mimeTypeDetermined( _type ); } BrowserRun::NonEmbeddableResult BrowserRun::handleNonEmbeddable( const QString& _mimeType ) { QString mimeType( _mimeType ); Q_ASSERT( !hasFinished() ); // only come here if the mimetype couldn't be embedded // Support for saving remote files. if ( mimeType != "inode/directory" && // dirs can't be saved !KRun::url().isLocalFile() ) { if ( isTextExecutable(mimeType) ) mimeType = QLatin1String("text/plain"); // view, don't execute kDebug(1000) << "BrowserRun: ask for saving"; KService::Ptr offer = KMimeTypeTrader::self()->preferredService(mimeType, "Application"); // ... -> ask whether to save KParts::BrowserRun::AskSaveResult res = askSave( KRun::url(), offer, mimeType, suggestedFileName() ); if ( res == KParts::BrowserRun::Save ) { save( KRun::url(), suggestedFileName() ); kDebug(1000) << "BrowserRun::handleNonEmbeddable: Save: returning Handled"; setFinished( true ); return Handled; } else if ( res == KParts::BrowserRun::Cancel ) { // saving done or canceled kDebug(1000) << "BrowserRun::handleNonEmbeddable: Cancel: returning Handled"; setFinished( true ); return Handled; } else // "Open" chosen (done by KRun::foundMimeType, called when returning NotHandled) { // If we were in a POST, we can't just pass a URL to an external application. // We must save the data to a tempfile first. if ( d->m_browserArgs.doPost() ) { kDebug(1000) << "BrowserRun: request comes from a POST, can't pass a URL to another app, need to save"; d->m_mimeType = mimeType; QString extension; QString fileName = suggestedFileName().isEmpty() ? KRun::url().fileName() : suggestedFileName(); int extensionPos = fileName.lastIndexOf( '.' ); if ( extensionPos != -1 ) extension = fileName.mid( extensionPos ); // keep the '.' KTemporaryFile tempFile; tempFile.setSuffix(extension); tempFile.setAutoRemove(false); tempFile.open(); KUrl destURL; destURL.setPath( tempFile.fileName() ); KIO::Job *job = KIO::file_copy( KRun::url(), destURL, 0600, KIO::Overwrite ); job->ui()->setWindow(d->m_window); connect( job, SIGNAL(result(KJob *)), this, SLOT(slotCopyToTempFileResult(KJob *)) ); return Delayed; // We'll continue after the job has finished } } } // Check if running is allowed if ( !d->m_bTrustedSource && // ... and untrusted source... !allowExecution( mimeType, KRun::url() ) ) // ...and the user said no (for executables etc.) { setFinished( true ); return Handled; } KIO::SimpleJob::removeOnHold(); // Kill any slave that was put on hold. return NotHandled; } //static bool BrowserRun::allowExecution( const QString &mimeType, const KUrl &url ) { if ( !KRun::isExecutable( mimeType ) ) return true; if ( !url.isLocalFile() ) // Don't permit to execute remote files return false; return ( KMessageBox::warningContinueCancel( 0, i18n( "Do you really want to execute '%1'?", url.prettyUrl() ), i18n("Execute File?"), KGuiItem(i18n("Execute")) ) == KMessageBox::Continue ); } static QString makeQuestion( const KUrl& url, const QString& mimeType, const QString& suggestedFileName ) { QString surl = KStringHandler::csqueeze( url.prettyUrl() ); KMimeType::Ptr mime = KMimeType::mimeType( mimeType ); QString comment = mimeType; // Test if the mimeType is not recognize as octet-stream. // If so then keep mime-type as comment if (mime && mime->name() != KMimeType::defaultMimeType()) { // The mime-type is known so display the comment instead of mime-type comment = mime->comment(); } // The strange order in the i18n() calls below is due to the possibility // of surl containing a '%' if ( suggestedFileName.isEmpty() ) return i18n("Open '%2'?\nType: %1", comment, surl); else return i18n("Open '%3'?\nName: %2\nType: %1", comment, suggestedFileName, surl); } //static // TODO should take a QWidget* parent argument BrowserRun::AskSaveResult BrowserRun::askSave( const KUrl & url, KService::Ptr offer, const QString& mimeType, const QString & suggestedFileName ) { // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC // NOTE: Keep this function in sync with kdebase/kcontrol/filetypes/filetypedetails.cpp // FileTypeDetails::updateAskSave() QString question = makeQuestion( url, mimeType, suggestedFileName ); // Text used for the open button QString openText = (offer && !offer->name().isEmpty()) ? i18n("&Open with '%1'", offer->name()) : i18n("&Open With..."); int choice = KMessageBox::questionYesNoCancel( 0, question, url.host(), KStandardGuiItem::saveAs(), KGuiItem(openText), KStandardGuiItem::cancel(), QLatin1String("askSave")+ mimeType ); // dontAskAgainName, KEEP IN SYNC!!! return choice == KMessageBox::Yes ? Save : ( choice == KMessageBox::No ? Open : Cancel ); // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC } //static // TODO should take a QWidget* parent argument BrowserRun::AskSaveResult BrowserRun::askEmbedOrSave( const KUrl & url, const QString& mimeType, const QString & suggestedFileName, int flags ) { // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC // NOTE: Keep this function in sync with kdebase/kcontrol/filetypes/filetypedetails.cpp // FileTypeDetails::updateAskSave() KMimeType::Ptr mime = KMimeType::mimeType( mimeType ); // Don't ask for: // - html (even new tabs would ask, due to about:blank!) // - dirs obviously (though not common over HTTP :), // - images (reasoning: no need to save, most of the time, because fast to see) // e.g. postscript is different, because takes longer to read, so // it's more likely that the user might want to save it. // - multipart/* ("server push", see kmultipart) // - other strange 'internal' mimetypes like print/manager... // KEEP IN SYNC!!! if (flags != (int)AttachmentDisposition && ( mime->is( "text/html" ) || mime->is( "application/xml" ) || mime->is( "inode/directory" ) || mimeType.startsWith( "image" ) || mime->is( "multipart/x-mixed-replace" ) || mime->is( "multipart/replace" ) || mimeType.startsWith( "print" ) ) ) return Open; QString question = makeQuestion( url, mimeType, suggestedFileName ); // don't use KStandardGuiItem::open() here which has trailing ellipsis! int choice = KMessageBox::questionYesNoCancel( 0, question, url.host(), KStandardGuiItem::saveAs(), KGuiItem( i18n( "&Open" ), "document-open"), KStandardGuiItem::cancel(), QLatin1String("askEmbedOrSave")+ mimeType ); // dontAskAgainName, KEEP IN SYNC!!! return choice == KMessageBox::Yes ? Save : ( choice == KMessageBox::No ? Open : Cancel ); // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC } // Default implementation, overridden in KHTMLRun void BrowserRun::save( const KUrl & url, const QString & suggestedFileName ) { simpleSave( url, suggestedFileName, d->m_window ); } void BrowserRun::simpleSave( const KUrl & url, const QString & suggestedFileName, QWidget* window ) { // DownloadManager <-> konqueror integration // find if the integration is enabled // the empty key means no integration // only use the downloadmanager for non-local urls if ( !url.isLocalFile() ) { KConfigGroup cfg = KSharedConfig::openConfig("konquerorrc", KConfig::NoGlobals)->group("HTML Settings"); QString downloadManger = cfg.readPathEntry("DownloadManager", QString()); if (!downloadManger.isEmpty()) { // then find the download manager location kDebug(1000) << "Using: "<setOperationMode( KFileDialog::Saving ); dlg->setCaption(i18n("Save As")); dlg->setSelection( suggestedFileName.isEmpty() ? url.fileName() : suggestedFileName ); if ( dlg->exec() ) { KUrl destURL( dlg->selectedUrl() ); if ( destURL.isValid() ) { KIO::Job *job = KIO::copy( url, destURL ); job->ui()->setWindow (window); job->ui()->setAutoErrorHandlingEnabled( true ); } } delete dlg; } void BrowserRun::slotStatResult( KJob *job ) { if ( job->error() ) { kDebug(1000) << "BrowserRun::slotStatResult:" << job->errorString(); handleError( job ); } else KRun::slotStatResult( job ); } void BrowserRun::handleError( KJob * job ) { if ( !job ) { // Shouldn't happen, see docu. kWarning(1000) << "BrowserRun::handleError called with job=0! hideErrorDialog=" << d->m_bHideErrorDialog; return; } if (d->m_bHideErrorDialog && job->error() != KIO::ERR_NO_CONTENT) { redirectToError( job->error(), job->errorText() ); return; } // Reuse code in KRun, to benefit from d->m_showingError etc. KRun::slotStatResult( job ); } void BrowserRun::redirectToError( int error, const QString& errorText ) { /** * To display this error in KHTMLPart instead of inside a dialog box, * we tell konq that the mimetype is text/html, and we redirect to * an error:/ URL that sends the info to khtml. * * The format of the error:/ URL is error:/?query#url, * where two variables are passed in the query: * error = int kio error code, errText = QString error text from kio * The sub-url is the URL that we were trying to open. */ KUrl newURL(QString("error:/?error=%1&errText=%2") .arg( error ) .arg( QString::fromUtf8( QUrl::toPercentEncoding( errorText ) ) ) ); KUrl runURL = KRun::url(); runURL.setPass( QString() ); // don't put the password in the error URL KUrl::List lst; lst << newURL << runURL; KRun::setUrl( KUrl::join( lst ) ); //kDebug(1202) << "BrowserRun::handleError KRun::url()=" << ; setJob( 0 ); - foundMimeType( "text/html" ); + mimeTypeDetermined( "text/html" ); } void BrowserRun::slotCopyToTempFileResult(KJob *job) { if ( job->error() ) { job->uiDelegate()->showErrorMessage(); } else { // Same as KRun::foundMimeType but with a different URL (void) (KRun::runUrl( static_cast(job)->destUrl(), d->m_mimeType, d->m_window )); } setError( true ); // see above setFinished( true ); timer().start( 0 ); } bool BrowserRun::isTextExecutable( const QString &mimeType ) { return ( mimeType == "application/x-desktop" || mimeType == "application/x-shellscript" ); } bool BrowserRun::hideErrorDialog() const { return d->m_bHideErrorDialog; } QString BrowserRun::contentDisposition() const { return d->m_contentDisposition; } KParts::OpenUrlArguments& KParts::BrowserRun::arguments() { return d->m_args; } KParts::BrowserArguments& KParts::BrowserRun::browserArguments() { return d->m_browserArgs; } #include "browserrun.moc"