diff --git a/kdecore/services/kservice.cpp b/kdecore/services/kservice.cpp index d67c642a..51d23f1e 100644 --- a/kdecore/services/kservice.cpp +++ b/kdecore/services/kservice.cpp @@ -693,11 +693,11 @@ QString KService::docPath() const return it->toString(); } -bool KService::allowMultipleFiles() const { +bool KService::allowMultipleFiles() const +{ Q_D(const KService); // Can we pass multiple files on the command line or do we have to start the application for every single file ? - return (d->m_strExec.contains( QLatin1String("%F") ) || d->m_strExec.contains( QLatin1String("%U") ) || - d->m_strExec.contains( QLatin1String("%N") ) || d->m_strExec.contains( QLatin1String("%D") )); + return (d->m_strExec.contains( QLatin1String("%F") ) || d->m_strExec.contains( QLatin1String("%U") )); } QStringList KService::categories() const diff --git a/kdeui/kernel/ktoolinvocation.cpp b/kdeui/kernel/ktoolinvocation.cpp index 7bf0dadd..2e2f0428 100644 --- a/kdeui/kernel/ktoolinvocation.cpp +++ b/kdeui/kernel/ktoolinvocation.cpp @@ -43,36 +43,6 @@ #define KTOOLINVOCATION_TIMEOUT 250 -// NOTE: keep in sync with: -// kdelibs/kinit/klauncher_adaptor.h -static inline QString getKLauncherError(const int result, const QString &app) -{ - switch (result) { - case -1: { - return i18n("Application service is not valid or does not support multiple files: %1.", app); - } - case -2: { - return i18n("Application not found: %1.", app); - } - case -3: { - return i18n("Application could not be processed: %1.", app); - } - case -4: { - return i18n("Application failed to start: %1.", app); - } - } - return i18n("Unknown KLauncher error for application: %1.", app); -} - -static inline void printError(const QString &text, QString *error) -{ - if (error) { - *error = text; - } else { - kError() << text; - } -} - K_GLOBAL_STATIC(KToolInvocation, kToolInvocation) KToolInvocation* KToolInvocation::self() @@ -104,113 +74,27 @@ void KToolInvocation::setLaunchEnv(const QString &name, const QString &value) klauncherIface->asyncCall(QString::fromLatin1("setLaunchEnv"), name, value); } -int KToolInvocation::startServiceInternal(const char *_function, - const QString &name, const QStringList &URLs, - QString *error, - const QByteArray &startup_id, - const QString &workdir) +bool KToolInvocation::startServiceForUrl(const QString &url, QWidget *window, bool temp) { - QString function = QString::fromLatin1(_function); - // make sure there is id, so that user timestamp exists - QStringList envs; - if (QX11Info::display()) { - const QString dpystring = QString::fromLatin1(XDisplayString(QX11Info::display())); - envs << QLatin1String("DISPLAY=") + dpystring; - } else { - const QString dpystring = QString::fromLocal8Bit(qgetenv("DISPLAY")); - if (!dpystring.isEmpty()) { - envs << QLatin1String("DISPLAY=") + dpystring; - } - } - - QDBusPendingReply reply; - if (qstrcmp(_function, "kdeinit_exec_with_workdir") == 0) { - reply = klauncherIface->asyncCall( - function, name, URLs, envs, QString::fromLatin1(startup_id, startup_id.size()), workdir - ); - } else { - reply = klauncherIface->asyncCall( - function, name, URLs, envs, QString::fromLatin1(startup_id, startup_id.size()) - ); - } - kDebug() << "Waiting for klauncher call to finish" << function; - while (!reply.isFinished()) { - QCoreApplication::processEvents(QEventLoop::AllEvents, KTOOLINVOCATION_TIMEOUT); - } - kDebug() << "Done waiting for klauncher call to finish" << function; - if (!reply.isValid()) { - printError( - i18n("KLauncher error: %1.", reply.error().message()), - error - ); - return EINVAL; - } - - const int result = reply.value(); - if (result < 0) { - printError( - getKLauncherError(result, name), - error - ); - // compat - return -result; - } else if (result != 0) { - printError( - i18n("Application failed to start: %1.", name), - error - ); - } - return result; + return startServiceInternal( + "start_service_by_url", QString(), QStringList() << url, window, temp + ); } -int KToolInvocation::startServiceByDesktopPath(const QString &name, const QString &URL, - QString *error, const QByteArray &startup_id) +bool KToolInvocation::startServiceByStorageId(const QString &name, const QStringList &URLs, + QWidget *window, bool temp) { - QStringList URLs; - if (!URL.isEmpty()) { - URLs.append(URL); - } - return startServiceInternal("start_service_by_desktop_path", name, URLs, error, startup_id); + return startServiceInternal("start_service_by_storage_id", name, URLs, window, temp); } -int KToolInvocation::startServiceByDesktopPath(const QString &name, const QStringList &URLs, - QString *error, const QByteArray &startup_id) +bool KToolInvocation::startProgram(const QString &name, const QStringList &args, QWidget *window, + bool temp) { - return startServiceInternal("start_service_by_desktop_path", name, URLs, error, startup_id); -} - -int KToolInvocation::startServiceByDesktopName(const QString &name, const QString &URL, - QString *error, const QByteArray &startup_id) -{ - QStringList URLs; - if (!URL.isEmpty()) { - URLs.append(URL); - } - return startServiceInternal("start_service_by_desktop_name", name, URLs, error, startup_id); -} - -int KToolInvocation::startServiceByDesktopName(const QString &name, const QStringList &URLs, - QString *error, const QByteArray &startup_id) -{ - return startServiceInternal("start_service_by_desktop_name", name, URLs, error, startup_id); -} - -int KToolInvocation::kdeinitExec(const QString &name, const QStringList &args, QString *error, - const QByteArray &startup_id) -{ - return startServiceInternal("kdeinit_exec", name, args, error, startup_id); -} - - -int KToolInvocation::kdeinitExecWait(const QString &name, const QStringList &args, QString *error, - const QByteArray &startup_id) -{ - return startServiceInternal("kdeinit_exec_wait", name, args, error, startup_id); + return startServiceInternal("start_program", name, args, window, temp); } void KToolInvocation::invokeHelp(const QString &anchor, - const QString &_appname, - const QByteArray &startup_id) + const QString &_appname) { KUrl url; QString appname; @@ -235,14 +119,12 @@ void KToolInvocation::invokeHelp(const QString &anchor, invokeBrowser(url.url()); } -void KToolInvocation::invokeMailer(const QString &address, const QString &subject, - const QByteArray &startup_id) +void KToolInvocation::invokeMailer(const QString &address, const QString &subject) { - invokeMailer(address, QString(), subject, QString(), QStringList(), startup_id); + invokeMailer(address, QString(), subject, QString(), QStringList()); } -void KToolInvocation::invokeMailer(const KUrl &mailtoURL, const QByteArray &startup_id, - bool allowAttachments) +void KToolInvocation::invokeMailer(const KUrl &mailtoURL, bool allowAttachments) { QString address = mailtoURL.path(); QString subject; @@ -274,7 +156,50 @@ void KToolInvocation::invokeMailer(const KUrl &mailtoURL, const QByteArray &star address = address.isEmpty()? KUrl::fromPercentEncoding((*it).mid(3).toLatin1()): address + comma + KUrl::fromPercentEncoding((*it).mid(3).toLatin1()); } - invokeMailer(address, cc, subject, body, attachURLs, startup_id); + invokeMailer(address, cc, subject, body, attachURLs); +} + +bool KToolInvocation::startServiceInternal(const char *_function, + const QString &name, const QStringList &URLs, + QWidget *window, bool temp, const QString &workdir) +{ + QString function = QString::fromLatin1(_function); + // make sure there is id, so that user timestamp exists + QStringList envs; + if (QX11Info::display()) { + const QString dpystring = QString::fromLatin1(XDisplayString(QX11Info::display())); + envs << QLatin1String("DISPLAY=") + dpystring; + } else { + const QString dpystring = QString::fromLocal8Bit(qgetenv("DISPLAY")); + if (!dpystring.isEmpty()) { + envs << QLatin1String("DISPLAY=") + dpystring; + } + } + + QDBusPendingReply reply; + if (qstrcmp(_function, "start_service_by_url") == 0) { + reply = klauncherIface->asyncCall( + function, URLs.first(), envs, window ? quint64(window->winId()) : 0, temp + ); + } else if (qstrcmp(_function, "start_program_with_workdir") == 0) { + reply = klauncherIface->asyncCall( + function, name, URLs, envs, window ? quint64(window->winId()) : 0, temp, workdir + ); + } else { + reply = klauncherIface->asyncCall( + function, name, URLs, envs, window ? quint64(window->winId()) : 0, temp + ); + } + kDebug() << "Waiting for klauncher call to finish" << function; + while (!reply.isFinished()) { + QCoreApplication::processEvents(QEventLoop::AllEvents, KTOOLINVOCATION_TIMEOUT); + } + kDebug() << "Done waiting for klauncher call to finish" << function; + if (!reply.isValid()) { + kError() << "KLauncher error" << reply.error().message(); + return false; + } + return reply.value(); } #include "moc_ktoolinvocation.cpp" diff --git a/kdeui/kernel/ktoolinvocation.h b/kdeui/kernel/ktoolinvocation.h index 0149e322..9d6e6cbc 100644 --- a/kdeui/kernel/ktoolinvocation.h +++ b/kdeui/kernel/ktoolinvocation.h @@ -47,16 +47,14 @@ public: public Q_SLOTS: /** - * Invokes the KHelpCenter HTML help viewer from docbook sources. + * Invokes the help viewer. * * @param anchor This has to be a defined anchor in your docbook sources. If empty the * main index is loaded * @param appname This allows you to show the help of another application. If empty the * current name() is used - * @param startup_id For app startup notification, "0" for none */ - void invokeHelp(const QString &anchor = QString(), const QString &appname = QString(), - const QByteArray &startup_id = QByteArray()); + void invokeHelp(const QString &anchor = QString(), const QString &appname = QString()); /** * Convenience method; invokes the standard email application. @@ -65,19 +63,16 @@ public Q_SLOTS: * @param subject Subject string. Can be QString(). * @param startup_id Ffor app startup notification, "0" for none */ - void invokeMailer(const QString &address, const QString &subject, - const QByteArray &startup_id = QByteArray()); + void invokeMailer(const QString &address, const QString &subject); /** * Invokes the standard email application. * * @param mailtoURL A mailto URL. - * @param startup_id For app startup notification, "0" for none * @param allowAttachments Whether attachments specified in mailtoURL should be honoured. The * default is false; do not honor requests for attachments. */ - void invokeMailer(const KUrl &mailtoURL, const QByteArray &startup_id = QByteArray(), - bool allowAttachments = false); + void invokeMailer(const KUrl &mailtoURL, bool allowAttachments = false); /** * Convenience method; invokes the standard email application. @@ -89,11 +84,9 @@ public Q_SLOTS: * @param subject Subject string * @param body A string containing the body of the mail * @param attachURLs List of URLs to be attached to the mail. - * @param startup_id For app startup notification, "0" for none */ void invokeMailer(const QString &to, const QString &cc, const QString &subject, - const QString &body, const QStringList &attachURLs = QStringList(), - const QByteArray &startup_id = QByteArray()); + const QString &body, const QStringList &attachURLs = QStringList()); /** * Invokes the user's preferred browser. Note that you should only do this when you know for @@ -101,21 +94,18 @@ public Q_SLOTS: * to an image or anything else than HTML, prefer to use KRun. * * @param url The destination address - * @param startup_id For app startup notification, "0" for none */ - void invokeBrowser(const QString &url, const QByteArray &startup_id = QByteArray()); + void invokeBrowser(const QString &url); /** * Invokes the standard terminal application. * * @param command The command to execute, can be empty. * @param workdir The initial working directory, can be empty. - * @param startup_id For app startup notification, "0" for none * * @since 4.1 */ - void invokeTerminal(const QString &command, const QString &workdir = QString(), - const QByteArray &startup_id = QByteArray()); + void invokeTerminal(const QString &command, const QString &workdir = QString()); public: /** @@ -127,89 +117,39 @@ public: void setLaunchEnv(const QString &name, const QString &value); /** - * Starts a service based on the desktop path of the service, e.g. - * "Applications/konqueror.desktop" or "/home/user/bla/myfile.desktop" + * Starts a service based on the MIME type of the URL, the MIME type is automatically + * determined * - * @param name The path of the desktop file - * @param URL If not empty this URL is passed to the service - * @param error On failure, @p error contains a description of the error that occurred. - * If the pointer is null, the argument will be ignored - * @param startup_id For app startup notification, "0" for none + * @param url The URL to start service for + * @param window Window to use for error reporting and job delegation + * @param temp Whether the URL is temporary file or not * @return an error code indicating success (== 0) or failure (> 0). */ - int startServiceByDesktopPath(const QString &name, const QString &URL, - QString *error = nullptr, - const QByteArray &startup_id = QByteArray()); + bool startServiceForUrl(const QString &url, QWidget *window = nullptr, bool temp = false); /** - * Starts a service based on the desktop path of the service, e..g. - * "Applications/konqueror.desktop" or "/home/user/bla/myfile.desktop" + * Starts a service based on the desktop name or entry path of the service, e.g. "konqueror" * - * @param name The path of the desktop file - * @param URLs If not empty these URLs will be passed to the service - * @param error On failure, @p error contains a description of the error that occurred. - * If the pointer is null, the argument will be ignored - * @param startup_id For app startup notification, "0" for none - * @return an error code indicating success (== 0) or failure (> 0). - */ - int startServiceByDesktopPath(const QString &name, const QStringList &URLs = QStringList(), - QString *error = nullptr, - const QByteArray &startup_id = QByteArray()); - - /** - * Starts a service based on the desktop name of the service, e.g. "konqueror" - * - * @param name The desktop name of the service - * @param URL If not empty this URL is passed to the service - * @param error On failure, @p error contains a description of the error that occurred. - * If the pointer is null, the argument will be ignored - * @param startup_id For app startup notification, "0" for none - * @return an error code indicating success (== 0) or failure (> 0) - */ - int startServiceByDesktopName(const QString &name, const QString &URL, - QString *error = nullptr, - const QByteArray &startup_id = QByteArray()); - - /** - * Starts a service based on the desktop name of the service, e.g. "konqueror" - * - * @param name The desktop name of the service - * @param URLs If not empty these URLs will be passed to the service - * @param error On failure, @p error contains a description of the error that occurred. - * If the pointer is null, the argument will be ignored - * @param startup_id For app startup notification, "0" for none + * @param name The desktop name of the service + * @param urls If not empty these URLs will be passed to the service + * @param window Window to use for error reporting and job delegation + * @param temp Whether any of the URLs is temporary file or not * @return an error code indicating success (== 0) or failure (> 0) */ - int startServiceByDesktopName(const QString &name, const QStringList &URLs = QStringList(), - QString *error = nullptr, - const QByteArray &startup_id = QByteArray()); + bool startServiceByStorageId(const QString &name, const QStringList &urls = QStringList(), + QWidget *window = nullptr, bool temp = false); /** - * Starts a program via kdeinit. + * Starts a program. * - * @param name Name of the program to start - * @param args Arguments to pass to the program - * @param error On failure, @p error contains a description of the error that occurred - * If the pointer is null, the argument will be ignored - * @param startup_id For app startup notification, "0" for none + * @param name Name of the program to start + * @param args Arguments to pass to the program + * @param window Window to use for error reporting and job delegation + * @param temp Whether argument is temporary file or not * @return an error code indicating success (== 0) or failure (> 0) */ - int kdeinitExec(const QString &name, const QStringList &args = QStringList(), - QString *error = nullptr, const QByteArray &startup_id = QByteArray()); - - /** - * Starts a program via kdeinit and wait for it to finish, it behaves similar to the system() - * function. - * - * @param name Name of the program to start - * @param args Arguments to pass to the program - * @param error On failure, @p error contains a description of the error that occurred - * If the pointer is null, the argument will be ignored - * @param startup_id For app startup notification, "0" for none - * @return an error code indicating success (== 0) or failure (> 0) - */ - int kdeinitExecWait(const QString &name, const QStringList &args = QStringList(), - QString *error = nullptr, const QByteArray &startup_id = QByteArray()); + bool startProgram(const QString &name, const QStringList &args = QStringList(), + QWidget *window = nullptr, bool temp = false); private: Q_DISABLE_COPY(KToolInvocation); @@ -217,11 +157,10 @@ private: /** * @internal */ - int startServiceInternal(const char *_function, - const QString &name, const QStringList &URLs, - QString *error, - const QByteArray &startup_id, - const QString &workdir = QString()); + bool startServiceInternal(const char *_function, + const QString &name, const QStringList &urls, + QWidget *window, const bool temp, + const QString &workdir = QString()); QDBusInterface *klauncherIface; }; diff --git a/kdeui/kernel/ktoolinvocation_x11.cpp b/kdeui/kernel/ktoolinvocation_x11.cpp index 902a6907..d14d3190 100644 --- a/kdeui/kernel/ktoolinvocation_x11.cpp +++ b/kdeui/kernel/ktoolinvocation_x11.cpp @@ -110,8 +110,7 @@ static QStringList splitEmailAddressList( const QString & aStr ) void KToolInvocation::invokeMailer(const QString &to, const QString &cc, const QString &subject, const QString &body, - const QStringList &attachURLs, - const QByteArray &startup_id) + const QStringList &attachURLs) { KConfig config(QString::fromLatin1("emaildefaults")); KConfigGroup profileGrp(&config, "General"); @@ -202,90 +201,43 @@ void KToolInvocation::invokeMailer(const QString &to, const QString &cc, } } - QString error; - // TODO this should check if cmd has a .desktop file, and use data from it, together - // with sending more ASN data - if (kdeinitExec(cmd, cmdTokens, &error, startup_id) != 0) { - KMessageBox::queuedMessageBox( - nullptr, KMessageBox::Error, - i18n("Could not launch the mail client:\n\n%1", error), - i18n("Could not launch Mail Client") - ); - } + startProgram(cmd, cmdTokens); } -void KToolInvocation::invokeBrowser(const QString &url, const QByteArray& startup_id) +void KToolInvocation::invokeBrowser(const QString &url) { - QStringList args; - args << url; - QString error; - // This method should launch a webbrowser, preferably without doing a mimetype - // check first, like KRun (i.e. kde-open) would do. + // check first like kde-open would do. const KService::Ptr htmlApp = KMimeTypeTrader::self()->preferredService(QLatin1String("text/html")); if (htmlApp) { - QString error; - const int err = startServiceByDesktopPath(htmlApp->entryPath(), url, &error, startup_id); - if (err != 0) { - KMessageBox::queuedMessageBox( - nullptr, KMessageBox::Error, - // TODO: i18n("Could not launch %1:\n\n%2", htmlApp->exec(), error), - i18n("Could not launch the browser:\n\n%1", error), - i18n("Could not launch Browser") - ); - } + startServiceByStorageId(htmlApp->entryPath(), QStringList() << url); return; } - QString exe = KStandardDirs::findExe(QString::fromLatin1("kde-open")); - if (exe.isEmpty()) { - exe = KStandardDirs::findExe(QString::fromLatin1("xdg-open")); - } - - if (kdeinitExec(exe, args, &error, startup_id) != 0) { - KMessageBox::queuedMessageBox( - nullptr, KMessageBox::Error, - // TODO: i18n("Could not launch %1:\n\n%2", exe, error), - i18n("Could not launch the browser:\n\n%1", error), - i18n("Could not launch Browser") - ); - } + // if one cannot be found then launch the service for the URL MIME type + startServiceForUrl(url); } void KToolInvocation::invokeTerminal(const QString &command, - const QString &workdir, - const QByteArray &startup_id) + const QString &workdir) { KConfigGroup confGroup( KGlobal::config(), "General" ); QString exec = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); - + QStringList cmdTokens = KShell::splitArgs(exec); if (!command.isEmpty()) { if (exec == QLatin1String("konsole")) { - exec += QString::fromLatin1(" --noclose"); - } else if (exec == QLatin1String("xterm")) { - exec += QString::fromLatin1(" -hold"); + cmdTokens << QString::fromLatin1("--noclose"); + } else if (exec.startsWith(QLatin1String("xterm"))) { + cmdTokens << QString::fromLatin1("-hold"); } - exec += QString::fromLatin1(" -e ") + command; + cmdTokens << QString::fromLatin1("-e") << command; } - QStringList cmdTokens = KShell::splitArgs(exec); + QString cmd = cmdTokens.takeFirst(); - if (exec == QLatin1String("konsole") && !workdir.isEmpty()) { - cmdTokens << QString::fromLatin1("--workdir"); - cmdTokens << workdir; - // For other terminals like xterm, we'll simply change the working - // directory before launching them, see below. - } - - QString error; - if (self()->startServiceInternal("kdeinit_exec_with_workdir", - cmd, cmdTokens, &error, startup_id, workdir)) { - KMessageBox::queuedMessageBox( - nullptr, KMessageBox::Error, - i18n("Could not launch the terminal client:\n\n%1", error), - i18n("Could not launch Terminal Client") - ); - } + startServiceInternal( + "start_program_with_workdir", cmd, cmdTokens, nullptr, false, workdir + ); } diff --git a/kinit/klauncher_adaptor.cpp b/kinit/klauncher_adaptor.cpp index 2c4def77..142fa445 100644 --- a/kinit/klauncher_adaptor.cpp +++ b/kinit/klauncher_adaptor.cpp @@ -22,47 +22,50 @@ #include "kautostart.h" #include "kshell.h" #include "kconfiggroup.h" +#include "kmessagebox.h" +#include "kmimetype.h" +#include "kmimetypetrader.h" +#include "kprotocolmanager.h" +#include "kio/netaccess.h" +#include "kio/udsentry.h" #include "kdebug.h" #include #include #include -#include -#include -#include - static const int s_eventstime = 250; static const int s_sleeptime = 50; // NOTE: keep in sync with: // kde-workspace/kwin/effects/startupfeedback/startupfeedback.cpp // kde-workspace/kcontrol/launch/kcmlaunch.cpp static const qint64 s_startuptimeout = 10; // 10sec -// klauncher is the last process to quit in a session (see kde-workspace/startkde.cmake) so 5sec -// for each child process is more than enough +// klauncher is the last process to quit in a session so 5sec for each child process is more than +// enough static const qint64 s_processtimeout = 5000; // 5sec -static inline bool isPIDAlive(const pid_t pid) +static inline void removeTemp(const bool temp, const QStringList &args) { - return (::kill(pid, 0) >= 0); + if (temp) { + foreach (const QString &arg, args) { + if (QFile::exists(arg)) { + kDebug() << "removing temporary file" << arg; + QFile::remove(arg); + } + } + } } -static inline int getExitStatus(const pid_t pid) +static inline void showError(const QString &error, const quint64 window) { - int pidstate = 0; - ::waitpid(pid, &pidstate, WNOHANG); - return WEXITSTATUS(pidstate); -} - -static inline bool isASNValid(const QByteArray &asn) -{ - return (!asn.isEmpty() && asn != "0"); + KMessageBox::errorWId(static_cast(window), error); } KLauncherProcess::KLauncherProcess(QObject *parent) : QProcess(parent), m_kstartupinfo(nullptr), - m_startuptimer(nullptr) + m_startuptimer(nullptr), + m_temp(false) { connect( this, SIGNAL(stateChanged(QProcess::ProcessState)), @@ -70,13 +73,18 @@ KLauncherProcess::KLauncherProcess(QObject *parent) ); } -void KLauncherProcess::setupStartup(const QByteArray &startup_id, const QString &appexe, - const KService::Ptr kservice, const qint64 timeout) +KLauncherProcess::~KLauncherProcess() +{ + removeTemp(m_temp, m_args); +} + +void KLauncherProcess::setupStartup(const QString &appexe, const KService::Ptr kservice, + const qint64 timeout, const bool temp, const QStringList &args) { Q_ASSERT(m_kstartupinfoid.none() == true); QByteArray startupwmclass; if (KRun::checkStartupNotify(kservice.data(), &startupwmclass)) { - m_kstartupinfoid.initId(!isASNValid(startup_id) ? KStartupInfo::createNewStartupId() : startup_id); + m_kstartupinfoid.initId(KStartupInfo::createNewStartupId()); kDebug() << "setting up ASN for" << kservice->entryPath() << m_kstartupinfoid.id(); m_kstartupinfodata.setHostname(); m_kstartupinfodata.setBin(QFileInfo(appexe).fileName()); @@ -88,19 +96,11 @@ void KLauncherProcess::setupStartup(const QByteArray &startup_id, const QString processenv.insert(QString::fromLatin1("DESKTOP_STARTUP_ID"), m_kstartupinfoid.id()); QProcess::setProcessEnvironment(processenv); sendSIStart(timeout); - } else if (isASNValid(startup_id)) { - kDebug() << "setting up ASN for" << startup_id; - m_kstartupinfoid.initId(startup_id); - m_kstartupinfodata.setHostname(); - m_kstartupinfodata.setBin(QFileInfo(appexe).fileName()); - m_kstartupinfodata.setDescription(i18n("Launching %1", m_kstartupinfodata.bin())); - QProcessEnvironment processenv = QProcess::processEnvironment(); - processenv.insert(QString::fromLatin1("DESKTOP_STARTUP_ID"), QString::fromLatin1(startup_id.constData(), startup_id.size())); - QProcess::setProcessEnvironment(processenv); - sendSIStart(timeout); } else { kDebug() << "no ASN for" << appexe; } + m_temp = temp; + m_args = args; } void KLauncherProcess::slotProcessStateChanged(QProcess::ProcessState state) @@ -282,32 +282,18 @@ void KLauncherAdaptor::cleanup() } } -int KLauncherAdaptor::kdeinit_exec(const QString &app, const QStringList &args, const QStringList &envs, const QString& startup_id) +bool KLauncherAdaptor::start_program(const QString &app, const QStringList &args, + const QStringList &envs, quint64 window, bool temp) { - return kdeinit_exec_with_workdir(app, args, envs, startup_id, QDir::currentPath()); + return start_program_with_workdir(app, args, envs, window, temp, QDir::currentPath()); } -int KLauncherAdaptor::kdeinit_exec_wait(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id) +bool KLauncherAdaptor::start_program_with_workdir(const QString &app, const QStringList &args, + const QStringList &envs, quint64 window, + bool temp, const QString &workdir) { qint64 pid = 0; - int result = startProgram(app, args, envs, startup_id, QDir::currentPath(), pid, m_startuptimeout); - if (result != KLauncherAdaptor::NoError) { - return result; - } - kDebug() << "waiting for" << pid; - while (isPIDAlive(pid)) { - QApplication::processEvents(QEventLoop::AllEvents, s_eventstime); - QThread::msleep(s_sleeptime); - } - result = getExitStatus(pid); - kDebug() << "done waiting for" << pid << ", exit status" << result; - return result; -} - -int KLauncherAdaptor::kdeinit_exec_with_workdir(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id, const QString &workdir) -{ - qint64 pid = 0; - return startProgram(app, args, envs, startup_id, workdir, pid, m_startuptimeout); + return startProgram(app, args, envs, window, temp, workdir, pid, m_startuptimeout); } void KLauncherAdaptor::setLaunchEnv(const QString &name, const QString &value) @@ -320,31 +306,31 @@ void KLauncherAdaptor::setLaunchEnv(const QString &name, const QString &value) m_environment.insert(name, value); } -int KLauncherAdaptor::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id) -{ - KService::Ptr kservice = KService::serviceByDesktopName(serviceName); - if (!kservice) { - kWarning() << "invalid service name" << serviceName; - return KLauncherAdaptor::ServiceError; - } - return start_service_by_desktop_path(kservice->entryPath(), urls, envs, startup_id); -} - -int KLauncherAdaptor::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id) +bool KLauncherAdaptor::start_service_by_storage_id(const QString &serviceName, + const QStringList &urls, + const QStringList &envs, quint64 window, + bool temp) { KService::Ptr kservice = KService::serviceByStorageId(serviceName); if (!kservice) { - kWarning() << "invalid service path" << serviceName; - return KLauncherAdaptor::ServiceError; + kError() << "invalid service path" << serviceName; + showError(i18n("Invalid service: %1", serviceName), window); + removeTemp(temp, urls); + return false; } + // TODO: start one service for each if (urls.size() > 1 && !kservice->allowMultipleFiles()) { - kWarning() << "service does not support multiple files" << serviceName; - return KLauncherAdaptor::ServiceError; + kError() << "service does not support multiple files" << serviceName; + showError(i18n("Service does not support multiple files: %1", serviceName), window); + return false; } + // TODO: for applications which do not support URLs - download QStringList programandargs = KRun::processDesktopExec(*kservice, urls); if (programandargs.isEmpty()) { - kWarning() << "could not process service" << kservice->entryPath(); - return KLauncherAdaptor::ArgumentsError; + kError() << "could not process service" << kservice->entryPath(); + showError(i18n("Could not process service: %1", serviceName), window); + removeTemp(temp, urls); + return false; } QString programworkdir = kservice->path(); if (programworkdir.isEmpty()) { @@ -353,7 +339,52 @@ int KLauncherAdaptor::start_service_by_desktop_path(const QString &serviceName, kDebug() << "starting" << kservice->entryPath() << urls; const QString program = programandargs.takeFirst(); qint64 pid = 0; - return startProgram(program, programandargs, envs, QString(), programworkdir, pid, m_startuptimeout, kservice); + return startProgram(program, programandargs, envs, window, temp, programworkdir, pid, m_startuptimeout, kservice); +} + +bool KLauncherAdaptor::start_service_by_url(const QString &url, const QStringList &envs, + quint64 window, bool temp) +{ + const KUrl realurl = KUrl(url); + QString urlmimetype; + if (realurl.isLocalFile()) { + KMimeType::Ptr kmimetype = KMimeType::findByUrl(realurl); + if (kmimetype) { + urlmimetype = kmimetype->name(); + } + } else { + KIO::UDSEntry kioudsentry; + // TODO: unless WId is passed around QWidget::find() will not find external windows + if (!KIO::NetAccess::stat(realurl, kioudsentry, QWidget::find(static_cast(window)))) { + kWarning() << "could not stat URL for MIME type" << url; + urlmimetype = KProtocolManager::defaultMimetype(realurl); + } else { + urlmimetype = kioudsentry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); + } + } + if (urlmimetype.isEmpty()) { + kError() << "invalid MIME type for path" << url; + showError(i18n("Could not determine the MIME type of: %1", url), window); + removeTemp(temp, QStringList() << url); + return false; + } + kDebug() << "MIME type of" << url << "is" << urlmimetype; + if (KRun::isExecutableFile(realurl, urlmimetype)) { + kDebug() << "execuable file" << url; + KMessageBox::sorryWId( + static_cast(window), + i18n("The file %1 is an executable program.
For safety it will not be started.", Qt::escape(realurl.prettyUrl())) + ); + return false; + } + KService::Ptr kservice = KMimeTypeTrader::self()->preferredService(urlmimetype); + if (!kservice) { + kError() << "invalid service for MIME type" << urlmimetype; + showError(i18n("No service can handle: %1", urlmimetype), window); + removeTemp(temp, QStringList() << url); + return false; + } + return start_service_by_storage_id(kservice->entryPath(), QStringList() << url, envs, window, temp); } #ifdef KLAUNCHER_DEBUG @@ -384,14 +415,16 @@ QString KLauncherAdaptor::findExe(const QString &app) const return KStandardDirs::findExe(app, environmentpath); } -int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args, const QStringList &envs, - const QString &startup_id, const QString &workdir, qint64 &pid, - const qint64 timeout, const KService::Ptr kservice) +bool KLauncherAdaptor::startProgram(const QString &app, const QStringList &args, const QStringList &envs, + const quint64 window, const bool temp, const QString &workdir, + qint64 &pid, const qint64 timeout, const KService::Ptr kservice) { const QString appexe = findExe(app); if (appexe.isEmpty()) { - kWarning() << "could not find" << app; - return KLauncherAdaptor::FindError; + kError() << "could not find" << app; + showError(i18n("Could not find the application: %1", app), window); + removeTemp(temp, args); + return false; } KLauncherProcess* process = new KLauncherProcess(this); @@ -411,7 +444,7 @@ int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args, } process->setProcessEnvironment(processenv); process->setWorkingDirectory(workdir); - process->setupStartup(startup_id.toLatin1(), appexe, kservice, timeout); + process->setupStartup(appexe, kservice, timeout, temp, args); kDebug() << "starting" << appexe << args << envs << workdir; process->start(appexe, args); while (process->state() == QProcess::Starting) { @@ -420,11 +453,11 @@ int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args, } if (process->error() == QProcess::FailedToStart || process->error() == QProcess::Crashed) { kWarning() << "could not start" << appexe; - return KLauncherAdaptor::ExecError; + return false; } pid = process->pid(); - return KLauncherAdaptor::NoError; + return true; } #include "moc_klauncher_adaptor.cpp" diff --git a/kinit/klauncher_adaptor.h b/kinit/klauncher_adaptor.h index 2bdc24b4..6f7df070 100644 --- a/kinit/klauncher_adaptor.h +++ b/kinit/klauncher_adaptor.h @@ -33,9 +33,10 @@ class KLauncherProcess : public QProcess Q_OBJECT public: explicit KLauncherProcess(QObject *parent); + ~KLauncherProcess(); - void setupStartup(const QByteArray &startup_id, const QString &appexe, - const KService::Ptr kservice, const qint64 timeout); + void setupStartup(const QString &appexe, const KService::Ptr kservice, const qint64 timeout, + const bool temp, const QStringList &args); private Q_SLOTS: void slotProcessStateChanged(QProcess::ProcessState state); @@ -53,6 +54,8 @@ private: QTimer* m_startuptimer; KStartupInfoId m_kstartupinfoid; KStartupInfoData m_kstartupinfodata; + bool m_temp; + QStringList m_args; }; // Adaptor class for interface org.kde.KLauncher @@ -61,14 +64,6 @@ class KLauncherAdaptor: public QDBusAbstractAdaptor Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KLauncher") public: - enum KLauncherError { - NoError = 0, - ServiceError = -1, - FindError = -2, - ArgumentsError = -3, - ExecError = -4 - }; - KLauncherAdaptor(QObject *parent); ~KLauncherAdaptor(); @@ -83,11 +78,15 @@ public Q_SLOTS: // used by KToolInvocation void setLaunchEnv(const QString &name, const QString &value); - int kdeinit_exec(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id); - int kdeinit_exec_wait(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id); - int kdeinit_exec_with_workdir(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id, const QString &workdir); - int start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id); - int start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id); + bool start_program(const QString &app, const QStringList &args, const QStringList &envs, + quint64 window, bool temp); + bool start_program_with_workdir(const QString &app, const QStringList &args, + const QStringList &envs, quint64 window, bool temp, + const QString &workdir); + bool start_service_by_storage_id(const QString &serviceName, const QStringList &urls, + const QStringList &envs, quint64 window, bool temp); + bool start_service_by_url(const QString &url, const QStringList &envs, quint64 window, + bool temp); // for debugging #ifdef KLAUNCHER_DEBUG @@ -104,9 +103,9 @@ private Q_SLOTS: private: QString findExe(const QString &app) const; - int startProgram(const QString &app, const QStringList &args, const QStringList &envs, - const QString &startup_id, const QString &workdir, qint64 &pid, - const qint64 timeout, const KService::Ptr kservice = KService::Ptr(nullptr)); + bool startProgram(const QString &app, const QStringList &args, const QStringList &envs, + const quint64 window, const bool temp, const QString &workdir, qint64 &pid, + const qint64 timeout, const KService::Ptr kservice = KService::Ptr(nullptr)); QProcessEnvironment m_environment; qint64 m_startuptimeout; diff --git a/kio/bookmarks/kbookmarkmenu.cc b/kio/bookmarks/kbookmarkmenu.cc index 62d3803b..755a13a7 100644 --- a/kio/bookmarks/kbookmarkmenu.cc +++ b/kio/bookmarks/kbookmarkmenu.cc @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include /********************************************************************/ @@ -638,7 +638,7 @@ KBookmarkAction::KBookmarkAction(const KBookmark &bk, KBookmarkOwner *owner, QOb void KBookmarkAction::slotSelected(Qt::MouseButtons mb, Qt::KeyboardModifiers km) { if (!m_pOwner) { - new KRun(bookmark().url(), (QWidget*)0); + KToolInvocation::self()->startServiceForUrl(bookmark().url().url()); } else { m_pOwner->openBookmark(bookmark(), mb, km); } diff --git a/kio/kfile/kdiroperator.cpp b/kio/kfile/kdiroperator.cpp index 446992ae..ab8f9570 100644 --- a/kio/kfile/kdiroperator.cpp +++ b/kio/kfile/kdiroperator.cpp @@ -26,6 +26,7 @@ #include "kfileitem.h" #include "kimagefilepreview.h" #include "knewfilemenu.h" +#include "ktoolinvocation.h" #include @@ -71,7 +72,6 @@ #include #include #include -#include #include #include #include @@ -649,7 +649,7 @@ void KDirOperator::Private::_k_toggleInlinePreviews(bool show) void KDirOperator::Private::_k_slotOpenFileManager() { - new KRun(currUrl, parent); + KToolInvocation::self()->startServiceForUrl(currUrl.url(), parent); } void KDirOperator::Private::_k_slotSortByName() diff --git a/kio/kfile/kpropertiesdialog.cpp b/kio/kfile/kpropertiesdialog.cpp index 006ad85a..37d27177 100644 --- a/kio/kfile/kpropertiesdialog.cpp +++ b/kio/kfile/kpropertiesdialog.cpp @@ -78,6 +78,7 @@ #include "kpreviewprops.h" #include "kmetaprops.h" #include "krun.h" +#include "ktoolinvocation.h" #include "kvbox.h" #include "kacl.h" #include "kconfiggroup.h" @@ -1100,14 +1101,11 @@ void KFilePropsPlugin::slotEditFileType() } else { mime = d->mimeType; } - QString keditfiletype = QString::fromLatin1("keditfiletype"); - KRun::runCommand(keditfiletype -#ifdef Q_WS_X11 - + " --parent " + QString::number( (ulong)properties->window()->winId()) -#endif - + " --caption " + KShell::quoteArg(KGlobal::caption()) - + ' ' + KShell::quoteArg(mime), - keditfiletype, keditfiletype /*unused*/, properties->window()); + QStringList args; + args << "--parent" << QString::number( (ulong)properties->window()->winId()); + args << "--caption" << KGlobal::caption(); + args << mime; + KToolInvocation::self()->startProgram(QLatin1String("keditfiletype"), args, properties->window()); } void KFilePropsPlugin::slotIconChanged() @@ -3256,9 +3254,7 @@ void KDesktopPropsPlugin::slotBrowseExec() return; } - QString path = f.toLocalFile(); - path = KShell::quoteArg(path); - d->w->commandEdit->setText(path); + d->w->commandEdit->setText(KShell::quoteArg(f.toLocalFile())); } void KDesktopPropsPlugin::slotAdvanced() diff --git a/kio/kio/kautomount.cpp b/kio/kio/kautomount.cpp index 2c425bdb..a3e8f6c6 100644 --- a/kio/kio/kautomount.cpp +++ b/kio/kio/kautomount.cpp @@ -18,10 +18,11 @@ #include "kautomount.h" -#include +#include #include #include #include +#include #include #include @@ -112,10 +113,9 @@ void KAutoMountPrivate::slotFinished(QDBusPendingCallWatcher *watcher) return; } - const KUrl url(m_mountPoint); // kDebug(7015) << "KAutoMount: m_strDevice=" << m_strDevice << " -> mountpoint=" << m_mountPoint; if (m_bShowFilemanagerWindow) { - KRun::runUrl(url, "inode/directory", nullptr /*TODO - window*/); + KToolInvocation::self()->startServiceForUrl(m_mountPoint); } // Update the desktop file which is used for mount/unmount (icon change) diff --git a/kio/kio/kdesktopfileactions.cpp b/kio/kio/kdesktopfileactions.cpp index e7a67302..7bebe5e5 100644 --- a/kio/kio/kdesktopfileactions.cpp +++ b/kio/kio/kdesktopfileactions.cpp @@ -21,24 +21,24 @@ #include "krun.h" #include "kautomount.h" +#include "kmimetype.h" +#include "kmessagebox.h" +#include "kdirnotify.h" +#include "kmountpoint.h" +#include "kstandarddirs.h" +#include "kdesktopfile.h" +#include "kconfiggroup.h" +#include "ktoolinvocation.h" +#include "klocale.h" +#include "kservice.h" +#include "kdebug.h" #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - enum BuiltinServiceType { ST_MOUNT = 0x0E1B05B0, ST_UNMOUNT = 0x0E1B05B1 }; // random numbers static bool runFSDevice( const KUrl& _url, const KDesktopFile &cfg ); -static bool runApplication( const KUrl& _url, const QString & _serviceFile ); static bool runLink( const KUrl& _url, const KDesktopFile &cfg ); bool KDesktopFileActions::run( const KUrl& u, bool _is_local ) @@ -64,7 +64,7 @@ bool KDesktopFileActions::run( const KUrl& u, bool _is_local ) return runFSDevice( u, cfg ); else if ( cfg.hasApplicationType() || (cfg.readType() == "Service" && !cfg.desktopGroup().readEntry("Exec").isEmpty())) // for kio_settings - return runApplication( u, u.toLocalFile() ); + return KToolInvocation::self()->startServiceByStorageId( u.toLocalFile() ); else if ( cfg.hasLinkType() ) return runLink( u, cfg ); @@ -94,9 +94,8 @@ static bool runFSDevice( const KUrl& _url, const KDesktopFile &cfg ) KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev ); // Is the device already mounted ? if (mp) { - KUrl mpURL(mp->mountPoint()); // Open a new window - retval = KRun::runUrl( mpURL, QLatin1String("inode/directory"), 0 /*TODO - window*/ ); + retval = KToolInvocation::self()->startServiceForUrl(mp->mountPoint()); } else { KConfigGroup cg = cfg.desktopGroup(); bool ro = cg.readEntry("ReadOnly", false); @@ -108,18 +107,6 @@ static bool runFSDevice( const KUrl& _url, const KDesktopFile &cfg ) return retval; } -static bool runApplication( const KUrl& , const QString & _serviceFile ) -{ - KService s( _serviceFile ); - if ( !s.isValid() ) - // The error message was already displayed, so we can just quit here - // ### KDE4: is this still the case? - return false; - - KUrl::List lst; - return KRun::run( s, lst, 0 /*TODO - window*/ ); -} - static bool runLink( const KUrl& _url, const KDesktopFile &cfg ) { QString u = cfg.readUrl(); @@ -132,17 +119,19 @@ static bool runLink( const KUrl& _url, const KDesktopFile &cfg ) return false; } - KUrl url ( u ); - KRun* run = new KRun(url,(QWidget*)0); - // X-KDE-LastOpenedWith holds the service desktop entry name that - // was should be preferred for opening this URL if possible. + // should be preferred for opening this URL if possible. // This is used by the Recent Documents menu for instance. QString lastOpenedWidth = cfg.desktopGroup().readEntry( "X-KDE-LastOpenedWith" ); - if ( !lastOpenedWidth.isEmpty() ) - run->setPreferredService( lastOpenedWidth ); - - return false; + if ( !lastOpenedWidth.isEmpty() ) { + KService::Ptr service = KService::serviceByStorageId(lastOpenedWidth); + if (!service.isNull()) { + return KToolInvocation::self()->startServiceByStorageId(service->entryPath(), QStringList() << u, nullptr); + } else { + kWarning() << "Last opened with service is not valid" << lastOpenedWidth; + } + } + return KToolInvocation::self()->startServiceForUrl(u, nullptr); } QList KDesktopFileActions::builtinServices( const KUrl& _url ) @@ -291,7 +280,14 @@ void KDesktopFileActions::executeService( const KUrl::List& urls, const KService } } else { kDebug() << action.name() << "first url's path=" << urls.first().toLocalFile() << "exec=" << action.exec(); - KRun::run( action.exec(), urls, 0, action.text(), action.icon()); + KService actionService(action.text(), action.exec(), action.icon()); + QStringList actionArgs = KRun::processDesktopExec(actionService, urls); + if (actionArgs.isEmpty()) { + kWarning() << "empty service command" << action.text() << action.exec(); + } else { + const QString actionProgram = actionArgs.takeFirst(); + KToolInvocation::self()->startProgram(actionProgram, actionArgs); + } // The action may update the desktop file. Example: eject unmounts (#5129). org::kde::KDirNotify::emitFilesChanged( urls.toStringList() ); } diff --git a/kio/kio/kdesktopfileactions.h b/kio/kio/kdesktopfileactions.h index e494bc62..23c927e8 100644 --- a/kio/kio/kdesktopfileactions.h +++ b/kio/kio/kdesktopfileactions.h @@ -90,7 +90,7 @@ namespace KDesktopFileActions * @param _url the url to run * @param _is_local true if the URL is local, false otherwise * @return true on success and false on failure. - * @see KRun::runUrl + * @see KRun, KToolInvocation */ KIO_EXPORT bool run( const KUrl& _url, bool _is_local ); } diff --git a/kio/kio/kfileitem.cpp b/kio/kio/kfileitem.cpp index 33afb9ef..42300d02 100644 --- a/kio/kio/kfileitem.cpp +++ b/kio/kio/kfileitem.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include static bool isKDirShare(const QString &dirpath) @@ -993,7 +994,7 @@ void KFileItem::run(QWidget *parentWidget) const kWarning() << "null item"; return; } - (void) new KRun(targetUrl(), parentWidget, d->m_fileMode, d->m_bIsLocalUrl); + KToolInvocation::self()->startServiceForUrl(targetUrl().url(), parentWidget); } bool KFileItem::cmp(const KFileItem &item) const diff --git a/kio/kio/kfileitemactions.cpp b/kio/kio/kfileitemactions.cpp index 9a5c8f58..29418154 100644 --- a/kio/kio/kfileitemactions.cpp +++ b/kio/kio/kfileitemactions.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -33,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -577,7 +578,7 @@ void KFileItemActionsPrivate::slotRunPreferredApplications() const QStringList mimeTypeList = listMimeTypes(fileItems); const QStringList serviceIdList = listPreferredServiceIds(mimeTypeList, m_traderConstraint); - foreach (const QString serviceId, serviceIdList) { + foreach (const QString &serviceId, serviceIdList) { KFileItemList serviceItems; foreach (const KFileItem& item, fileItems) { const KService::Ptr serv = preferredService(item.mimetype(), m_traderConstraint); @@ -597,7 +598,9 @@ void KFileItemActionsPrivate::slotRunPreferredApplications() KRun::displayOpenWithDialog(serviceItems.urlList(), m_parentWidget); continue; } - KRun::run(*servicePtr, serviceItems.urlList(), m_parentWidget); + KToolInvocation::self()->startServiceByStorageId( + servicePtr->entryPath(), serviceItems.urlList().toStringList(), m_parentWidget + ); } } @@ -628,7 +631,9 @@ void KFileItemActionsPrivate::slotRunApplication(QAction* act) KService::Ptr app = act->data().value(); Q_ASSERT(app); if (app) { - KRun::run(*app, m_props.urlList(), m_parentWidget); + KToolInvocation::self()->startServiceByStorageId( + app->entryPath(), m_props.urlList().toStringList(), m_parentWidget + ); } } diff --git a/kio/kio/kmimetypechooser.cpp b/kio/kio/kmimetypechooser.cpp index 7ee52755..f2eec29d 100644 --- a/kio/kio/kmimetypechooser.cpp +++ b/kio/kio/kmimetypechooser.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -201,14 +201,13 @@ void KMimeTypeChooserPrivate::_k_editMimeType() return; QString mt = (item->parent())->text(0) + '/' + item->text(0); // thanks to libkonq/konq_operations.cc - q->connect( KSycoca::self(), SIGNAL(databaseChanged(QStringList)), - q, SLOT(_k_slotSycocaDatabaseChanged(QStringList)) ); - QString keditfiletype = QString::fromLatin1("keditfiletype"); - KRun::runCommand( keditfiletype - + " --parent " + QString::number( (ulong)q->window()->winId()) - + " --caption " + KShell::quoteArg(KGlobal::caption()) - + ' ' + KShell::quoteArg(mt), - keditfiletype, keditfiletype /*unused*/, q->window()); + q->connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), + q, SLOT(_k_slotSycocaDatabaseChanged(QStringList))); + QStringList args; + args << "--parent" << QString::number((ulong)q->window()->winId()); + args << "--caption" << KGlobal::caption(); + args << mt; + KToolInvocation::self()->startProgram(QLatin1String("keditfiletype"), args, q->window()); } void KMimeTypeChooserPrivate::_k_slotCurrentChanged(QTreeWidgetItem* item) diff --git a/kio/kio/krun.cpp b/kio/kio/krun.cpp index 45fb87fa..5471905b 100644 --- a/kio/kio/krun.cpp +++ b/kio/kio/krun.cpp @@ -1,12 +1,10 @@ -/* This file is part of the KDE libraries - Copyright (C) 2000 Torben Weis - Copyright (C) 2006 David Faure - Copyright (C) 2009 Michael Pyne +/* + This file is part of the KDE libraries + Copyright (C) 2024 Ivailo Monev 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. + 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 @@ -20,563 +18,96 @@ */ #include "krun.h" -#include "krun_p.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#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/netaccess.h" -#include "kfile/kopenwithdialog.h" -#include "kfile/krecentdocument.h" -#include "kdesktopfileactions.h" -#include "kurl.h" -#include "kglobal.h" +#include "kopenwithdialog.h" #include "ktoolinvocation.h" -#include "klocale.h" -#include "kprotocolmanager.h" -#include "kstandarddirs.h" -#include "kprocess.h" -#include "kdesktopfile.h" -#include "kmacroexpander.h" +#include "kmimetypetrader.h" #include "kshell.h" -#include "kde_file.h" -#include "kconfiggroup.h" -#include "kdialog.h" -#include "kstandardguiitem.h" -#include "kguiitem.h" -#include "ksavefile.h" -#include "kpixmapwidget.h" -#include "kmessagebox.h" #include "kdebug.h" -#ifdef Q_WS_X11 -#include -#endif +#include +#include -KRun::KRunPrivate::KRunPrivate(KRun *parent) - : q(parent), - m_showingDialog(false) +bool KRun::displayOpenWithDialog(const KUrl::List &urls, QWidget *window, bool tempFiles) { + KOpenWithDialog dialog(urls, i18n("Open with:"), QString(), window); + dialog.setWindowModality(Qt::WindowModal); + if (dialog.exec()) { + KService::Ptr service = dialog.service(); + if (service) { + return KToolInvocation::self()->startServiceByStorageId(service->entryPath(), urls.toStringList(), window, tempFiles); + } + const QString program = dialog.text(); + if (!program.isEmpty()) { + return KToolInvocation::self()->startProgram(program, urls.toStringList(), window, tempFiles); + } + } + return false; } -void KRun::KRunPrivate::startTimer() +// TODO: this needs a complete rewrite to handle remote URLs +QStringList KRun::processDesktopExec(const KService &service, const KUrl::List &urls) { - m_timer.start(0); + QStringList args = KShell::splitArgs(service.exec()); + if (args.isEmpty()) { + return args; + } + const QStringList urlsstrings = urls.toStringList(); + for (int i = 0; i < args.size(); i++) { + const QString arg = args.at(i); + if (arg.contains(QLatin1String("%f")) || arg.contains(QLatin1String("%u"))) { + if (!urlsstrings.isEmpty()) { + args.replace(i, urlsstrings.first()); + } else { + args.replace(i, QString()); + } + } else if (arg.contains(QLatin1String("%F")) || arg.contains(QLatin1String("%U"))) { + args.replace(i, urlsstrings.join(QLatin1String(" "))); + } else if (arg.contains(QLatin1String("%i"))) { + args.replace(i, service.icon()); + } else if (arg.contains(QLatin1String("%c"))) { + args.replace(i, service.name()); + } else if (arg.contains(QLatin1String("%k"))) { + args.replace(i, service.entryPath()); + } + } + return args; } -// --------------------------------------------------------------------------- - -bool KRun::isExecutableFile(const KUrl& url, const QString &mimetype) +QString KRun::binaryName(const QString &execLine, bool removePath) { + foreach (const QString &arg, KShell::splitArgs(execLine)) { + if (!arg.contains('=')) { + return (removePath ? arg.mid(arg.lastIndexOf('/') + 1) : arg); + } + } + return QString(); +} + +bool KRun::isExecutable(const QString &serviceType) +{ + return ( + serviceType == QLatin1String("application/x-desktop") || + serviceType == QLatin1String("application/x-executable") || + serviceType == QLatin1String("application/x-ms-dos-executable") || + serviceType == QLatin1String("application/x-shellscript") + ); +} + +bool KRun::isExecutableFile(const KUrl &url, const QString &mimetype) +{ + if (isExecutable(mimetype)) { + return true; + } if (!url.isLocalFile()) { return false; } QFileInfo file(url.toLocalFile()); - if (file.isExecutable()) { // Got a prospective file to run - KMimeType::Ptr mimeType = KMimeType::mimeType(mimetype, KMimeType::ResolveAliases); - if (mimeType && (mimeType->is(QLatin1String("application/x-executable")) || - mimeType->is(QLatin1String("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; - if (_mimetype == QLatin1String("application/x-desktop")) { - if (u.isLocalFile() && runExecutables) { - return KDesktopFileActions::run(u, true); - } - } - else if (isExecutableFile(u, _mimetype)) { - if (u.isLocalFile() && runExecutables) { - return (KRun::runCommand(KShell::quoteArg(u.toLocalFile()), QString(), QString(), window, asn, u.directory())); // just execute the url as a command - // ## TODO implement deleting the file if tempFile==true - } - else if (_mimetype == QLatin1String("application/x-executable")) { - noRun = true; - } - } - else if (isExecutable(_mimetype)) { - if (!runExecutables) { - noRun = 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; - } - - 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) -{ - KOpenWithDialog l(lst, i18n("Open with:"), QString(), window); - l.setWindowModality(Qt::WindowModal); - if (l.exec()) { - KService::Ptr service = l.service(); - if (!service) { - kDebug(7010) << "No service set, running " << l.text(); - service = KService::Ptr(new KService(QString() /*name*/, l.text(), QString() /*icon*/)); - } - return KRun::run(*service, lst, window, tempFiles, suggestedFileName, asn); - } - return false; -} - - - -class KRunMX1 : public KMacroExpanderBase -{ -public: - KRunMX1(const KService &_service) : - KMacroExpanderBase('%'), hasUrls(false), hasSpec(false), service(_service) {} - - bool hasUrls: 1, hasSpec: 1; - -protected: - int expandEscapedMacro(const QString &str, int pos, QStringList &ret) final; - -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 << service.icon().replace('%', "%%"); - break; - case 'm': -// ret << "-miniicon" << service.icon().replace( '%', "%%" ); - kWarning() << "-miniicon isn't supported anymore (service" - << service.name() << ')'; - 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: - int expandEscapedMacro(const QString &str, int pos, QStringList &ret) final; - -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.hasFragment() && !url.hasQuery()) ? - url.toLocalFile() : url.url()); - break; - case 'd': - ret << url.directory(); - break; - case 'f': - ret << url.toLocalFile(); - break; - case 'n': - ret << url.fileName(); - break; - case 'v': - if (url.isLocalFile() && QFile::exists(url.toLocalFile())) { - ret << KDesktopFile(url.toLocalFile()).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() << "No URLs supplied to single-URL service" << str; - } - } - else if (urls.count() > 1) { - kWarning() << 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; -} - -static QStringList supportedProtocols(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 (or a KDE service) - const QStringList categories = _service.property("Categories").toStringList(); - if (categories.contains("KDE") - || !_service.isApplication() - || _service.entryPath().isEmpty() /*temp service*/) { - supportedProtocols.append("KIO"); - } - else { // if no KDE app, be a bit over-generic - supportedProtocols.append("http"); - supportedProtocols.append("https"); // #253294 - supportedProtocols.append("ftp"); - } - } - } - kDebug(7010) << "supportedProtocols:" << supportedProtocols; - return supportedProtocols; -} - -static bool isProtocolInSupportedList(const KUrl& url, const QStringList& supportedProtocols) -{ - if (supportedProtocols.contains("KIO")) + if (file.isExecutable()) { return true; - return url.isLocalFile() || supportedProtocols.contains(url.protocol().toLower()); + } + return false; } -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(); - } - - const QString kioexec = KStandardDirs::findExe("kioexec"); - Q_ASSERT(!kioexec.isEmpty()); - - // 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()) { - result << kioexec << "--tempfiles" << exec; - if (!suggestedFileName.isEmpty()) { - result << "--suggestedfilename"; - result << suggestedFileName; - } - result += _urls.toStringList(); - return result; - } - - // Check if we need kioexec - bool useKioexec = false; - if (!mx1.hasUrls) { - for (KUrl::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it) - if (!(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it)) { - useKioexec = true; - kDebug(7010) << "non-local files, application does not support urls, using kioexec"; - break; - } - } else { // app claims to support %u/%U, check which protocols - QStringList appSupportedProtocols = supportedProtocols(_service); - for (KUrl::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it) - if (!isProtocolInSupportedList(*it, appSupportedProtocols) && !KProtocolInfo::isHelperProtocol(*it)) { - useKioexec = true; - kDebug(7010) << "application does not support url, using kioexec:" << *it; - break; - } - } - if (useKioexec) { - // We need to run the app through kioexec - 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 << "kdesudo" << "-u" << user << "-c" << cmd - 5 << "kdesudo" << "-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") { - if (!_service.path().isEmpty()) { - terminal += " --workdir " + KShell::quoteArg(_service.path()); - } - terminal += " --icon '%i' --caption '%c'"; - } - 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("kdesudo") << "-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. - const 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& executable, - const QString &userVisibleName, const QString & iconName, QWidget* window, - const QByteArray& asn) -{ - if (window != NULL) { - window = window->window(); - } - 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.")); - delete proc; - return false; - } - - QString bin = KRun::binaryName(executable, true); -#ifdef Q_WS_X11 - QByteArray wmclass; - KStartupInfoId id; - bool startup_notify = (asn != "0" && KRun::checkStartupNotify(service, &wmclass)); - if (startup_notify) { - id.initId(asn); - id.setupStartupEnv(); - KStartupInfoData data; - data.setHostname(); - data.setBin(bin); - if (!userVisibleName.isEmpty()) { - data.setName(userVisibleName); - } 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); - } - data.setDesktop(KWindowSystem::currentDesktop()); - if(service && !service->entryPath().isEmpty()) - data.setApplicationId(service->entryPath()); - KStartupInfo::sendStartup(id, data); - } - qint64 pid = KProcessRunner::run(proc, executable, id); - if (startup_notify && pid) { - KStartupInfoData data; - data.addPid(pid); - KStartupInfo::sendChange(id, data); - KStartupInfo::resetStartupEnv(); - } - return pid != 0; -#else - Q_UNUSED(userVisibleName); - Q_UNUSED(iconName); - return KProcessRunner::run(proc, bin) != 0; -#endif -} - -// This code is also used in klauncher. bool KRun::checkStartupNotify(const KService *service, QByteArray *wmclass_arg) { if (!service || service->entryPath().isEmpty()) { @@ -592,8 +123,6 @@ bool KRun::checkStartupNotify(const KService *service, QByteArray *wmclass_arg) return false; } - QByteArray wmclass = service->property("StartupWMClass").toString().toLatin1(); - // NOTE: this is using spec properties but probably not for the purpose the properties were // ment for if (service->property("SingleMainWindow").toBool()) { @@ -616,1042 +145,11 @@ bool KRun::checkStartupNotify(const KService *service, QByteArray *wmclass_arg) } } - if (wmclass_arg != NULL) { - *wmclass_arg = wmclass; + if (wmclass_arg) { + *wmclass_arg = service->property("StartupWMClass").toString().toLatin1(); } 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, QByteArray()); - } - 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; - - const QString& path = _service.path(); - if (!path.isEmpty()) { - proc->setWorkingDirectory(path); - } else if (!_urls.isEmpty()) { - const KUrl& url = _urls.first(); - if (url.isLocalFile()) { - proc->setWorkingDirectory(url.directory()); - } - } - - 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 appSupportedProtocols = supportedProtocols(_service); - KUrl::List urls(_urls); - if (!appSupportedProtocols.contains("KIO")) { - for (KUrl::List::Iterator it = urls.begin(); it != urls.end(); ++it) { - const KUrl url = *it; - bool supported = isProtocolInSupportedList(url, appSupportedProtocols); - kDebug(7010) << "Looking at url=" << url << " supported=" << supported; - if (!supported && KProtocolInfo::protocolIsLocal(url.protocol())) { - // 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; -} - -// Simple KDialog that resizes the given text edit after being shown to more -// or less fit the enclosed text. -class SecureMessageDialog : public KDialog -{ - public: - SecureMessageDialog(QWidget *parent) : KDialog(parent), m_textEdit(0) - { - } - - void setTextEdit(QPlainTextEdit *textEdit) - { - m_textEdit = textEdit; - } - - protected: - virtual void showEvent(QShowEvent* e) - { - // Now that we're shown, use our width to calculate a good - // bounding box for the text, and resize m_textEdit appropriately. - KDialog::showEvent(e); - - if(!m_textEdit) - return; - - QSize fudge(20, 24); // About what it sounds like :-/ - - // Form rect with a lot of height for bounding. Use no more than - // 5 lines. - QRect curRect(m_textEdit->rect()); - QFontMetrics metrics(fontMetrics()); - curRect.setHeight(5 * metrics.lineSpacing()); - curRect.setWidth(qMax(curRect.width(), 300)); // At least 300 pixels ok? - - QString text(m_textEdit->toPlainText()); - curRect = metrics.boundingRect(curRect, Qt::TextWordWrap | Qt::TextSingleLine, text); - - // Scroll bars interfere. If we don't think there's enough room, enable - // the vertical scrollbar however. - m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - if(curRect.height() < m_textEdit->height()) { // then we've got room - m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_textEdit->setMaximumHeight(curRect.height() + fudge.height()); - } - - m_textEdit->setMinimumSize(curRect.size() + fudge); - m_textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - updateGeometry(); - } - - private: - QPlainTextEdit *m_textEdit; -}; - -// Helper function to make the given .desktop file executable by ensuring -// that a #!/usr/bin/env xdg-open line is added if necessary and the file has -// the +x bit set for the user. Returns false if either fails. -static bool makeFileExecutable(const QString &fileName) -{ - // Open the file and read the first two characters, check if it's - // #!. If not, create a new file, prepend appropriate lines, and copy - // over. - QFile desktopFile(fileName); - if (!desktopFile.open(QFile::ReadOnly)) { - kError(7010) << "Error opening service" << fileName << desktopFile.errorString(); - return false; - } - - QByteArray header = desktopFile.peek(2); // First two chars of file - if (header.size() == 0) { - kError(7010) << "Error inspecting service" << fileName << desktopFile.errorString(); - return false; // Some kind of error - } - - if (header != "#!") { - // Add header - KSaveFile saveFile; - saveFile.setFileName(fileName); - if (!saveFile.open()) { - kError(7010) << "Unable to open replacement file for" << fileName << saveFile.errorString(); - return false; - } - - QByteArray shebang("#!/usr/bin/env xdg-open\n"); - if (saveFile.write(shebang) != shebang.size()) { - kError(7010) << "Error occurred adding header for" << fileName << saveFile.errorString(); - saveFile.abort(); - return false; - } - - // Now copy the one into the other and then close and reopen desktopFile - QByteArray desktopData(desktopFile.readAll()); - if (desktopData.isEmpty()) { - kError(7010) << "Unable to read service" << fileName << desktopFile.errorString(); - saveFile.abort(); - return false; - } - - if (saveFile.write(desktopData) != desktopData.size()) { - kError(7010) << "Error copying service" << fileName << saveFile.errorString(); - saveFile.abort(); - return false; - } - - desktopFile.close(); - if (!saveFile.finalize()) { // Figures.... - kError(7010) << "Error committing changes to service" << fileName << saveFile.errorString(); - return false; - } - - if (!desktopFile.open(QFile::ReadOnly)) { - kError(7010) << "Error re-opening service" << fileName << desktopFile.errorString(); - return false; - } - } // Add header - - // corresponds to owner on unix, which will have to do since if the user - // isn't the owner we can't change perms anyways. - if (!desktopFile.setPermissions(QFile::ExeUser | desktopFile.permissions())) { - kError(7010) << "Unable to change permissions for" << fileName << desktopFile.errorString(); - return false; - } - - // whew - return true; -} - -// Helper function to make a .desktop file executable if prompted by the user. -// returns true if KRun::run() should continue with execution, false if user declined -// to make the file executable or we failed to make it executable. -static bool makeServiceExecutable(const KService& service, QWidget* window) -{ - KGuiItem continueItem = KStandardGuiItem::cont(); - - SecureMessageDialog *baseDialog = new SecureMessageDialog(window); - - baseDialog->setButtons(KDialog::Ok | KDialog::Cancel); - baseDialog->setButtonGuiItem(KDialog::Ok, continueItem); - baseDialog->setDefaultButton(KDialog::Cancel); - baseDialog->setButtonFocus(KDialog::Cancel); - baseDialog->setCaption(i18nc("Warning about executing unknown .desktop file", "Warning")); - - // Dialog will have explanatory text with a disabled lineedit with the - // Exec= to make it visually distinct. - QWidget *baseWidget = new QWidget(baseDialog); - QHBoxLayout *mainLayout = new QHBoxLayout(baseWidget); - - KPixmapWidget *iconWidget = new KPixmapWidget(baseWidget); - QPixmap warningIcon(KIconLoader::global()->loadIcon("dialog-warning", KIconLoader::NoGroup, KIconLoader::SizeHuge)); - mainLayout->addWidget(iconWidget); - iconWidget->setPixmap(warningIcon); - - QVBoxLayout *contentLayout = new QVBoxLayout; - QString warningMessage = i18nc("program name follows in a line edit below", - "This will start the program:"); - - QLabel *message = new QLabel(warningMessage, baseWidget); - contentLayout->addWidget(message); - - // We can use KStandardDirs::findExe to resolve relative pathnames - // but that gets rid of the command line arguments. - QString program = KStandardDirs::realFilePath(service.exec()); - - QPlainTextEdit *textEdit = new QPlainTextEdit(baseWidget); - textEdit->setPlainText(program); - textEdit->setReadOnly(true); - contentLayout->addWidget(textEdit); - - QLabel *footerLabel = new QLabel(i18n("If you do not trust this program, click Cancel")); - contentLayout->addWidget(footerLabel); - contentLayout->addStretch(0); // Don't allow the text edit to expand - - mainLayout->addLayout(contentLayout); - - baseDialog->setMainWidget(baseWidget); - baseDialog->setTextEdit(textEdit); - - // Constrain maximum size. Minimum size set in - // the dialog's show event. - QSize screenSize = QApplication::desktop()->screen()->size(); - baseDialog->resize(screenSize.width() / 4, 50); - baseDialog->setMaximumHeight(screenSize.height() / 3); - baseDialog->setMaximumWidth(screenSize.width() / 10 * 8); - - int result = baseDialog->exec(); - if (result != KDialog::Accepted) { - return false; - } - - // Assume that service is an absolute path since we're being called (relative paths - // would have been allowed unless Kiosk said no, therefore we already know where the - // .desktop file is. Now add a header to it if it doesn't already have one - // and add the +x bit. - - if (!::makeFileExecutable(service.entryPath())) { - QString serviceName = service.name(); - if(serviceName.isEmpty()) - serviceName = service.genericName(); - - KMessageBox::sorry( - window, - i18n("Unable to make the service %1 executable, aborting execution", serviceName) - ); - - return false; - } - - return true; -} - -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()) && - !::makeServiceExecutable(_service, window)) - { - 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 i = KToolInvocation::self()->startServiceByDesktopPath( - _service.entryPath(), urls.toStringList(), &error, asn - ); - - 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, const QString& workingDirectory) -{ - if (cmd.isEmpty()) { - kWarning() << "Command was empty, nothing to run"; - return false; - } - - const QStringList args = KShell::splitArgs(cmd); - if (args.isEmpty()) { - kWarning() << "Command could not be parsed."; - return false; - } - - const QString bin = args.first(); - return KRun::runCommand(cmd, bin, bin /*iconName*/, window, QByteArray(), workingDirectory); -} - -bool KRun::runCommand(const QString& cmd, const QString &execName, const QString & iconName, - QWidget* window, const QByteArray& asn, const QString& workingDirectory) -{ - kDebug(7010) << "runCommand " << cmd << "," << execName; - KProcess * proc = new KProcess; - proc->setShellCommand(cmd); - if (!workingDirectory.isEmpty()) { - proc->setWorkingDirectory(workingDirectory); - } - QString bin = binaryName(execName, true); - KService::Ptr service = KService::serviceByDesktopName(bin); - return runCommandInternal(proc, service.data(), - execName /*executable to check for in slotProcessExited*/, - execName /*user-visible name*/, - 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; - - // 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())); - startTimer(); - //kDebug(7010) << "new KRun" << q << url << "timer=" << &m_timer; - - KGlobal::ref(); -} - -void KRun::init() -{ - kDebug(7010) << "INIT called"; - if (!d->m_strURL.isValid()) { - d->m_showingDialog = true; - error(i18n("Malformed URL\n%1", d->m_strURL.url())); - d->m_showingDialog = false; - d->m_bFault = true; - d->m_bFinished = true; - d->startTimer(); - return; - } - - if (!d->m_bIsLocalFile && d->m_strURL.isLocalFile()) { - d->m_bIsLocalFile = true; - } - - if (d->m_bIsLocalFile) { - if (d->m_mode == 0) { - KDE_struct_stat buff; - if (KDE::stat(d->m_strURL.toLocalFile(), &buff) == -1) { - d->m_showingDialog = true; - error( - i18n("Unable to run the command specified. " - "The file or folder %1 does not exist." , - Qt::escape(d->m_strURL.prettyUrl())) - ); - d->m_showingDialog = false; - d->m_bFault = true; - d->m_bFinished = true; - d->startTimer(); - return; - } - d->m_mode = buff.st_mode; - } - - KMimeType::Ptr mime = KMimeType::findByUrl(d->m_strURL, d->m_mode); - assert(mime); - kDebug(7010) << "MIME TYPE is " << mime->name(); - if (mime->isDefault() && !QFileInfo(d->m_strURL.toLocalFile()).isReadable()) { - // Unknown mimetype because the file is unreadable, no point in showing an open-with dialog (#261002) - d->m_showingDialog = true; - error(KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->m_strURL.prettyUrl())); - d->m_showingDialog = false; - d->m_bFault = true; - d->m_bFinished = true; - d->startTimer(); - return; - } else { - mimeTypeDetermined(mime->name()); - return; - } - } else if (KProtocolInfo::isHelperProtocol(d->m_strURL)) { - const QString exec = KProtocolInfo::exec(d->m_strURL.protocol()); - kDebug(7010) << "Helper protocol" << exec; - if (exec.isEmpty()) { - mimeTypeDetermined(KProtocolManager::defaultMimetype(d->m_strURL)); - return; - } - - // could be a scheme handler, attempt to launch it - const KService::Ptr service = KMimeTypeTrader::self()->preferredService(QString::fromLatin1("x-scheme-handler/") + d->m_strURL.protocol()); - if (service) { - kDebug(7010) << "Helper protocol service is" << service->name() << service->entryPath(); - QString errorstr; - if (KToolInvocation::self()->startServiceByDesktopPath(service->entryPath(), d->m_strURL.url(), &errorstr, d->m_asn) == 0) { - d->m_bFinished = true; - d->startTimer(); - return; - } else { - kDebug(7010) << "Error starting the service" << errorstr; - } - } - - if (run(exec, KUrl::List() << d->m_strURL, d->m_window, QString(), QString(), d->m_asn)) { - d->m_bFinished = true; - d->startTimer(); - return; - } - } - - // Did we already get the information that it is a directory ? - if (S_ISDIR(d->m_mode)) { - 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) << this; - d->m_timer.stop(); - killJob(); - KGlobal::deref(); - //kDebug(7010) << this << "done"; - delete d; -} - -bool KRun::KRunPrivate::runExecutable(const QString& _exec) -{ - KUrl::List urls; - urls.append(m_strURL); - if (_exec.startsWith('!')) { - QString exec = _exec.mid(1); // Literal command - exec += " %u"; - if (q->run(exec, urls, m_window, QString(), QString(), m_asn)) { - m_bFinished = true; - startTimer(); - return true; - } - } - else { - KService::Ptr service = KService::serviceByStorageId(_exec); - if (service && q->run(*service, urls, m_window, false, QString(), m_asn)) { - m_bFinished = true; - startTimer(); - return true; - } - } - return false; -} - -void KRun::scanFile() -{ - 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->isDefault() || d->m_bIsLocalFile) { - kDebug(7010) << "Scanfile: MIME TYPE is " << 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!"; - d->m_bFault = true; - d->m_bFinished = true; - d->startTimer(); - return; - } - kDebug(7010) << this << " Scanning file " << d->m_strURL.url(); - - KIO::JobFlags flags = d->m_bProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; - KIO::StatJob *job = KIO::stat(d->m_strURL, flags); - job->ui()->setWindow(d->m_window); - connect(job, SIGNAL(result(KJob*)), - this, SLOT(slotScanFinished(KJob*))); - d->m_job = job; - kDebug(7010) << " Job " << job << " is about getting from " << d->m_strURL.url(); -} - -// When arriving in that method there are 5 possible states: -// must_init, must_scan_file, found_dir, done+error or done+success. -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; - mimeTypeDetermined("inode/directory"); - return; - } - } - - if (d->m_bAutoDelete) { - deleteLater(); - } -} - -void KRun::slotStatResult(KJob * job) -{ - d->m_job = 0L; - const int errCode = job->error(); - if (errCode) { - // ERR_NO_CONTENT is not an error, but an indication no further - // actions needs to be taken. - if (errCode != KIO::ERR_NO_CONTENT) { - d->m_showingDialog = true; - kError(7010) << this << "ERROR" << job->error() << job->errorString(); - job->uiDelegate()->showErrorMessage(); - kDebug(7010) << this << " KRun returning from error, starting timer to delete us"; - d->m_showingDialog = false; - d->m_bFault = true; - } - - d->m_bFinished = true; - - // will emit the error and autodelete this - d->startTimer(); - } else { - kDebug(7010) << "Finished"; - - KIO::StatJob* statJob = qobject_cast(job); - if (!statJob) { - kFatal() << "job is a " << typeid(*job).name() << " should be a StatJob"; - } - - // Update our URL in case of a redirection - setUrl(statJob->url()); - - const KIO::UDSEntry entry = statJob->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()) { - 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->startTimer(); - } -} - -void KRun::slotScanFinished(KJob *job) -{ - d->m_job = 0; - const int errCode = job->error(); - if (errCode) { - // ERR_NO_CONTENT is not an error, but an indication no further - // actions needs to be taken. - if (errCode != KIO::ERR_NO_CONTENT) { - d->m_showingDialog = true; - kError(7010) << this << "ERROR (stat):" << job->error() << ' ' << job->errorString(); - job->uiDelegate()->showErrorMessage(); - kDebug(7010) << this << " KRun returning from error, starting timer to delete us"; - d->m_showingDialog = false; - - d->m_bFault = true; - } - - d->m_bFinished = true; - // will emit the error and autodelete this - d->startTimer(); - return; - } - - KIO::StatJob* statJob = qobject_cast(job); - if (!statJob) { - kFatal() << "job is a " << typeid(*job).name() << " should be a StatJob"; - } - - const KIO::UDSEntry entry = statJob->statResult(); - const QString mimetype = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); - if (mimetype.isEmpty()) { - kWarning(7010) << "stat() does not provide a mimetype! Check the implementation of" << url().protocol(); - } - mimeTypeDetermined(mimetype); - d->m_job = 0; -} - -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; - - // We cannot assume that we're finished here. Some reimplementations - // start a KIO job and call setFinished only later. -} - -void KRun::foundMimeType(const QString& type) -{ - kDebug(7010) << "Resulting mime type is " << type; - - KIO::TransferJob *job = qobject_cast(d->m_job); - if (job) { - // Update our URL in case of a redirection - setUrl( job->url() ); - - job->kill(KJob::Quietly); - d->m_job = 0; - } - - Q_ASSERT(!d->m_bFinished); - - // Support 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(type)) { - KUrl::List lst; - lst.append(d->m_strURL); - if (KRun::run(*serv, lst, d->m_window, false, QString(), d->m_asn)) { - setFinished(true); - return; - } - /// Note: if that service failed, we'll go to runUrl below to - /// maybe find another service, even though an error dialog box was - /// already 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. - KMimeType::Ptr mime = KMimeType::mimeType(type, KMimeType::ResolveAliases); - if (!mime) { - kWarning(7010) << "Unknown mimetype " << type; - } - if (mime && mime->is("application/x-desktop") && !d->m_localPath.isEmpty()) { - d->m_strURL = KUrl(); - d->m_strURL.setPath(d->m_localPath); - } - - if (!KRun::runUrl(d->m_strURL, type, d->m_window, false /*tempfile*/, d->m_runExecutables, d->m_suggestedFileName, d->m_asn)) { - d->m_bFault = true; - } - setFinished(true); -} - -void KRun::error(const QString& message) -{ - KMessageBox::error(window(), message); -} - -void KRun::killJob() -{ - if (d->m_job) { - kDebug(7010) << this << "m_job=" << d->m_job; - d->m_job->kill(); - d->m_job = 0L; - } -} - -void KRun::abort() -{ - if (d->m_bFinished) { - return; - } - kDebug(7010) << 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_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->startTimer(); -} - -QWidget* KRun::window() const -{ - return d->m_window; -} - -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::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; - if (finished) - d->startTimer(); -} - -void KRun::setJob(KIO::Job *job) -{ - d->m_job = job; -} - -KIO::Job* KRun::job() -{ - return d->m_job; -} - -bool KRun::isDirectory() const -{ - return d->m_bIsDirectory; -} - -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 -qint64 KProcessRunner::run(KProcess * p, const QString & executable) -{ - return (new KProcessRunner(p, executable))->pid(); -} -#else -qint64 KProcessRunner::run(KProcess * p, const QString & executable, const KStartupInfoId& id) -{ - return (new KProcessRunner(p, executable, id))->pid(); -} -#endif - -#ifndef Q_WS_X11 -KProcessRunner::KProcessRunner(KProcess * p, const QString & executable) -#else -KProcessRunner::KProcessRunner(KProcess * p, const QString & executable, const KStartupInfoId& id) -#endif - : m_process(p), - m_executable(executable), -#ifdef Q_WS_X11 - m_id(id), -#endif - m_pid(0) -{ - connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), - this, SLOT(slotProcessExited(int,QProcess::ExitStatus))); - - m_process->start(); - if (!m_process->waitForStarted()) { - //kDebug() << "wait for started failed, exitCode=" << process->exitCode() - // << "exitStatus=" << process->exitStatus(); - // Note that exitCode is 255 here (the first time), and 0 later on (bug?). - slotProcessExited(255, m_process->exitStatus()); - } else { -#ifdef Q_WS_X11 - m_pid = m_process->pid(); -#endif - } -} - -KProcessRunner::~KProcessRunner() -{ - delete m_process; -} - -qint64 KProcessRunner::pid() const -{ - return m_pid; -} - -void KProcessRunner::terminateStartupNotification() -{ -#ifdef Q_WS_X11 - if (!m_id.none()) { - KStartupInfoData data; - data.addPid(m_pid); // announce this pid for the startup notification has finished - data.setHostname(); - KStartupInfo::sendFinish(m_id, data); - } -#endif - -} - -void -KProcessRunner::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) -{ - kDebug(7010) << m_executable << "exitCode=" << exitCode << "exitStatus=" << exitStatus; - Q_UNUSED(exitStatus); - - terminateStartupNotification(); // do this before the messagebox - if (exitCode != 0 && !m_executable.isEmpty()) { - // Let's see if the error is because the exe doesn't exist. - // When this happens, waitForStarted returns false, but not if kioexec - // was involved, then we come here, that's why the code is here. - // - // We'll try to find the executable relatively to current directory, - // (or with a full path, if m_executable is absolute), and then in the PATH. - if (!QFile::exists(m_executable) && KStandardDirs::findExe(m_executable).isEmpty()) { - KGlobal::ref(); - KMessageBox::sorry(0L, i18n("Could not find the program '%1'", m_executable)); - KGlobal::deref(); - } else { - kDebug() << m_process->readAllStandardError(); - } - } - deleteLater(); -} - #include "moc_krun.cpp" -#include "moc_krun_p.cpp" diff --git a/kio/kio/krun.h b/kio/kio/krun.h index 3136e7cc..6a786fe8 100644 --- a/kio/kio/krun.h +++ b/kio/kio/krun.h @@ -1,22 +1,20 @@ -// -*- 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 file is part of the KDE libraries + Copyright (C) 2024 Ivailo Monev - 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 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. + 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. + 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 @@ -24,19 +22,11 @@ #include -#include -#include -#include -#include +#include #include +#include -class KService; -class KStartupInfo; -class KJob; -namespace KIO -{ -class Job; -} +class KRunPrivate; /** * To open files with their associated applications in KDE, use KRun. @@ -56,249 +46,28 @@ class Job; * * @short Opens files with their associated applications in KDE */ -class KIO_EXPORT KRun : public QObject +class KIO_EXPORT KRun { - 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 = QByteArray()); - - /** - * 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 in - * 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 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; - - /** - * Associated window, as passed to the constructor - * @since 4.9.3 - */ - QWidget* window() 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 = QByteArray()); - - /** - * Open a list of URLs with an executable. - * - * @param exec the name of the executable, for example - * "/usr/bin/netscape %u". - * Don't forget to include the %u if you know that the applications - * supports URLs. Otherwise, non-local urls will first be downloaded - * to a temp file (using kioexec). - * @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 = QByteArray()); - - /** - * 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 = QByteArray()); - - /** - * Run the given shell command and notifies KDE 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. - * @param workingDirectory a working directory, so that a command like - * "kwrite file.txt" finds file.txt from the right place. - * - * @return @c true on success, @c false on error - */ - static bool runCommand(const QString &cmd, QWidget* window, const QString& workingDirectory = QString()); - - /** - * 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 ""). - * @param workingDirectory the working directory for the started process. The default - * (if passing an empty string) is the user's document path. - * @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 = QByteArray(), - const QString& workingDirectory = QString()); - /** * Display the Open-With dialog for those URLs, and run the chosen application. - * @param lst the list of applications to run + * @param urls 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 = QByteArray()); + static bool displayOpenWithDialog(const KUrl::List &urls, QWidget* window, + bool tempFiles = false); /** * 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 + * @param service the service to extract information from. + * @param urls The urls the service should open. * * @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()); + static QStringList processDesktopExec(const KService &service, const KUrl::List &urls); /** * Given a full command line (e.g. the Exec= line from a .desktop file), @@ -307,13 +76,13 @@ public: * @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); + 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); + static bool isExecutable(const QString &serviceType); /** * Returns whether the @p url of @p mimetype is executable. @@ -330,157 +99,9 @@ public: static bool isExecutableFile(const KUrl &url, const QString &mimetype); /** - * @internal + * Returns true if startup notification should be done for the given service, false otherwise */ static bool checkStartupNotify(const KService *service, QByteArray *wmclass_arg); - -Q_SIGNALS: - /** - * Emitted when the operation finished. - * This signal is emitted in all cases of completion, whether successful or with error. - * @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 timer fired, - * in order to move on to the next step. - */ - void slotTimeout(); // KDE5: rename to slotNextStep() or something like that - - /** - * This slot is called when the scan job is finished. - */ - void slotScanFinished(KJob *); - - /** - * 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 runs - * the application associated with this mimetype. - * Reimplement this method to implement a different behavior, - * like opening the component for displaying the URL embedded. - * - * Important: call setFinished(true) once you are done! - * Usually at the end of the foundMimeType reimplementation, but if the - * reimplementation is asynchronous (e.g. uses KIO jobs) then - * it can be called later instead. - */ - virtual void foundMimeType(const QString& type); - - /** - * Called if error occurres. - */ - virtual void error(const QString& message); - - /** - * 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 occurred. - */ - 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 this 'KRun' instance as finished. - */ - void setFinished(bool finished); - - /** - * Sets the job. - */ - void setJob(KIO::Job *job); - - /** - * Returns the job. - */ - KIO::Job* job(); - - /** - * Returns whether it is a directory. - */ - bool isDirectory() 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/kio/kio/krun_p.h b/kio/kio/krun_p.h deleted file mode 100644 index 91545482..00000000 --- a/kio/kio/krun_p.h +++ /dev/null @@ -1,127 +0,0 @@ -// -*- 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_P_H -#define KRUN_P_H - -#include -#include - -#include "kprocess.h" -#include "kstartupinfo.h" - -/** - * @internal - * This class watches a process launched by KRun. - * It sends a notification when the process exits (for the taskbar) - * and it will show an error message if necessary (e.g. "program not found"). - */ -class KProcessRunner : public QObject -{ - Q_OBJECT - - public: - -#ifndef Q_WS_X11 - static qint64 run(KProcess *, const QString & executable); -#else - static qint64 run(KProcess *, const QString & executable, const KStartupInfoId& id); -#endif - - virtual ~KProcessRunner(); - - qint64 pid() const; - - protected Q_SLOTS: - - void slotProcessExited(int, QProcess::ExitStatus); - - private: -#ifndef Q_WS_X11 - KProcessRunner(KProcess *, const QString & binName); -#else - KProcessRunner(KProcess *, const QString & binName, const KStartupInfoId& id); -#endif - - void terminateStartupNotification(); - - KProcess *m_process; - QString m_executable; // can be a full path - KStartupInfoId m_id; - qint64 m_pid; - - Q_DISABLE_COPY(KProcessRunner) -}; - -/** - * @internal - */ -class KRun::KRunPrivate -{ -public: - KRunPrivate(KRun *parent); - - 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 - void startTimer(); - - bool runExecutable(const QString& _exec); - - KRun *q; - bool m_showingDialog; - bool m_runExecutables; - - QString m_preferredService; - 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; -}; - -#endif // KRUN_P_H diff --git a/kio/misc/kmailservice.cpp b/kio/misc/kmailservice.cpp index c1bacd90..13f9bf38 100644 --- a/kio/misc/kmailservice.cpp +++ b/kio/misc/kmailservice.cpp @@ -38,7 +38,7 @@ int main( int argc, char **argv ) if ( args->count() != 1 ) return 1; - KToolInvocation::self()->invokeMailer(KUrl(args->arg(0)), QByteArray(), true); + KToolInvocation::self()->invokeMailer(KUrl(args->arg(0)), true); return 0; } diff --git a/kio/misc/ktelnetservice.cpp b/kio/misc/ktelnetservice.cpp index 233993a4..1d2a2723 100644 --- a/kio/misc/ktelnetservice.cpp +++ b/kio/misc/ktelnetservice.cpp @@ -94,7 +94,7 @@ int main(int argc, char **argv) cmd << QString::number(url.port()); } - KToolInvocation::self()->kdeinitExec(terminal, cmd); + KToolInvocation::self()->startProgram(terminal, cmd); return 0; } diff --git a/kio/tests/CMakeLists.txt b/kio/tests/CMakeLists.txt index 7ef41d19..d3334022 100644 --- a/kio/tests/CMakeLists.txt +++ b/kio/tests/CMakeLists.txt @@ -16,7 +16,6 @@ ENDMACRO(KIO_UNIT_TESTS) # jobtest seems to trigger a ctest problem; jobtest finishes and ctest waits for ever KIO_UNIT_TESTS( - krununittest kprotocolinfotest jobtest jobguitest diff --git a/kio/tests/krununittest.cpp b/kio/tests/krununittest.cpp deleted file mode 100644 index ffb8860a..00000000 --- a/kio/tests/krununittest.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2003 Waldo Bastian - * Copyright (C) 2007, 2009 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 "krununittest.h" -#include "moc_krununittest.cpp" -#include -QTEST_KDEMAIN( KRunUnitTest, NoGUI ) - -#include "krun.h" -#include -#include -#include -#include -#include -#include "kiotesthelper.h" // createTestFile etc. - -void KRunUnitTest::initTestCase() -{ - // testProcessDesktopExec works only if your terminal application is set to "x-term" - KConfigGroup cg(KGlobal::config(), "General"); - cg.writeEntry("TerminalApplication", "x-term"); - - // Determine the full path of sh - this is needed to make testProcessDesktopExecNoFile() - // pass on systems where KStandardDirs::findExe("sh") is not "/bin/sh". - m_sh = KStandardDirs::findExe("sh"); - if (m_sh.isEmpty()) m_sh = "/bin/sh"; -} - -void KRunUnitTest::testBinaryName_data() -{ - QTest::addColumn("execLine"); - QTest::addColumn("removePath"); - QTest::addColumn("expected"); - - QTest::newRow("/usr/bin/ls true") << "/usr/bin/ls" << true << "ls"; - QTest::newRow("/usr/bin/ls false") << "/usr/bin/ls" << false << "/usr/bin/ls"; - QTest::newRow("/path/to/wine \"long argument with path\"") << "/path/to/wine \"long argument with path\"" << true << "wine"; - QTest::newRow("/path/with/a/sp\\ ace/exe arg1 arg2") << "/path/with/a/sp\\ ace/exe arg1 arg2" << true << "exe"; - QTest::newRow("\"progname\" \"arg1\"") << "\"progname\" \"arg1\"" << true << "progname"; - QTest::newRow("'quoted' \"arg1\"") << "'quoted' \"arg1\"" << true << "quoted"; - QTest::newRow(" 'leading space' arg1") << " 'leading space' arg1" << true << "leading space"; -} - -void KRunUnitTest::testBinaryName() -{ - QFETCH(QString, execLine); - QFETCH(bool, removePath); - QFETCH(QString, expected); - QCOMPARE(KRun::binaryName(execLine, removePath), expected); -} - -//static const char *bt(bool tr) { return tr?"true":"false"; } -static void checkPDE(const char* exec, const char* term, const char* sus, - const KUrl::List &urls, bool tf, const QString& b) -{ - QFile out( "kruntest.desktop" ); - if ( !out.open( QIODevice::WriteOnly ) ) - abort(); - QByteArray str ( "[Desktop Entry]\n" - "Type=Application\n" - "Name=just_a_test\n" - "Icon=~/icon.png\n"); - str += QByteArray(exec) + '\n'; - str += QByteArray(term) + '\n'; - str += QByteArray(sus) + '\n'; - out.write( str ); - out.close(); - - KService service(QDir::currentPath() + "/kruntest.desktop"); - /*qDebug() << QString().sprintf( - "processDesktopExec( " - "service = {\nexec = %s\nterminal = %s, terminalOptions = %s\nsubstituteUid = %s, user = %s }," - "\nURLs = { %s },\ntemp_files = %s )", - service.exec().toLatin1().constData(), bt(service.terminal()), service.terminalOptions().toLatin1().constData(), bt(service.substituteUid()), service.username().toLatin1().constData(), - KShell::joinArgs(urls.toStringList()).toLatin1().constData(), bt(tf)); - */ - QCOMPARE(KShell::joinArgs(KRun::processDesktopExec(service,urls,tf)), b); - - QFile::remove("kruntest.desktop"); -} - -void KRunUnitTest::testProcessDesktopExec() -{ - KUrl::List l0; - static const char - * const execs[] = { "Exec=date -u", "Exec=echo $PWD" }, - * const terms[] = { "Terminal=false", "Terminal=true\nTerminalOptions=-T \"%f - %c\"" }, - * const sus[] = { "X-KDE-SubstituteUID=false", "X-KDE-SubstituteUID=true\nX-KDE-Username=sprallo" }, - * const rslts[] = { - "/bin/date -u", // 0 - "/bin/sh -c 'echo $PWD '", // 1 - "x-term -T ' - just_a_test' -e /bin/date -u", // 2 - "x-term -T ' - just_a_test' -e /bin/sh -c 'echo $PWD '", // 3 - /* kdesudo */ " -u sprallo -c '/bin/date -u'", // 4 - /* kdesudo */ " -u sprallo -c '/bin/sh -c '\\''echo $PWD '\\'''", // 5 - "x-term -T ' - just_a_test' -e su sprallo -c '/bin/date -u'", // 6 - "x-term -T ' - just_a_test' -e su sprallo -c '/bin/sh -c '\\''echo $PWD '\\'''", // 7 - }; - - // Find out the full path of the shell which will be used to execute shell commands - KProcess process; - process.setShellCommand(""); - const QString shellPath = process.program().at(0); - - // Arch moved /bin/date to /usr/bin/date... - const QString datePath = KStandardDirs::findExe("date"); - - for (int su = 0; su < 2; su++) - for (int te = 0; te < 2; te++) - for (int ex = 0; ex < 2; ex++) { - int pt = ex+te*2+su*4; - QString exe; - if (pt == 4 || pt == 5) { - exe = KStandardDirs::findExe("kdesudo"); - if (exe.isEmpty()) { - qWarning() << "kdesudo not found, skipping test"; - continue; - } - } - const QString result = QString::fromLatin1(rslts[pt]) - .replace("/bin/sh", shellPath) - .replace("/bin/date", datePath); - checkPDE( execs[ex], terms[te], sus[su], l0, false, exe + result); - } -} - -void KRunUnitTest::testProcessDesktopExecNoFile_data() -{ - QTest::addColumn("execLine"); - QTest::addColumn("urls"); - QTest::addColumn("tempfiles"); - QTest::addColumn("expected"); - - KUrl::List l0; - KUrl::List l1; l1 << KUrl( "file:/tmp" ); - KUrl::List l2; l2 << KUrl( "http://localhost/foo" ); - KUrl::List l3; l3 << KUrl( "file:/local/file" ) << KUrl( "http://remotehost.org/bar" ); - KUrl::List l4; l4 << KUrl( "http://login:password@www.kde.org" ); - - // A real-world use case would be kate. - // But I picked klauncher4 since it's installed by kdelibs - QString klauncher = KStandardDirs::findExe("klauncher4"); - if (klauncher.isEmpty()) klauncher = "klauncher4"; - - QString kioexec = KStandardDirs::findExe("kioexec"); - if (kioexec.isEmpty()) - QSKIP("kioexec not found, kdebase needed", SkipAll); - - QString kmailservice = KStandardDirs::findExe("kmailservice"); - if (kmailservice.isEmpty()) kmailservice = "kmailservice"; - if (!klauncher.isEmpty()) { - QVERIFY(!kmailservice.isEmpty()); - } - - // NOTE: using QString() for concats to avoid QStringBuilder metatype, which is not valid - QTest::newRow("%U l0") << "klauncher4 %U" << l0 << false << klauncher; - QTest::newRow("%U l1") << "klauncher4 %U" << l1 << false << QString(klauncher + " /tmp"); - QTest::newRow("%U l2") << "klauncher4 %U" << l2 << false << QString(klauncher + " http://localhost/foo"); - QTest::newRow("%U l3") << "klauncher4 %U" << l3 << false << QString(klauncher + " /local/file http://remotehost.org/bar"); - - //QTest::newRow("%u l0") << "klauncher4 %u" << l0 << false << klauncher; // gives runtime warning - QTest::newRow("%u l1") << "klauncher4 %u" << l1 << false << QString(klauncher + " /tmp"); - QTest::newRow("%u l2") << "klauncher4 %u" << l2 << false << QString(klauncher + " http://localhost/foo"); - //QTest::newRow("%u l3") << "klauncher4 %u" << l3 << false << klauncher; // gives runtime warning - - QTest::newRow("%F l0") << "klauncher4 %F" << l0 << false << klauncher; - QTest::newRow("%F l1") << "klauncher4 %F" << l1 << false << QString(klauncher + " /tmp"); - QTest::newRow("%F l2") << "klauncher4 %F" << l2 << false << QString(kioexec + " 'klauncher4 %F' http://localhost/foo"); - QTest::newRow("%F l3") << "klauncher4 %F" << l3 << false << QString(kioexec + " 'klauncher4 %F' /local/file http://remotehost.org/bar"); - - QTest::newRow("%F l1 tempfile") << "klauncher4 %F" << l1 << true << QString(kioexec + " --tempfiles 'klauncher4 %F' /tmp"); - - QTest::newRow("sh -c klauncher4 %F") << "sh -c \"klauncher4 \"'\\\"'\"%F\"'\\\"'" - << l1 << false << QString(m_sh + " -c 'klauncher4 \\\"/tmp\\\"'"); - - QTest::newRow("kmailservice %u l1") << "kmailservice %u" << l1 << false << QString(kmailservice + " /tmp"); - QTest::newRow("kmailservice %u l4") << "kmailservice %u" << l4 << false << QString(kmailservice + " http://login:password@www.kde.org"); -} - -void KRunUnitTest::testProcessDesktopExecNoFile() -{ - QFETCH(QString, execLine); - KService service("dummy", execLine, "app"); - QFETCH(KUrl::List, urls); - QFETCH(bool, tempfiles); - QFETCH(QString, expected); - QCOMPARE(KShell::joinArgs(KRun::processDesktopExec(service,urls,tempfiles)), expected); -} - -class KRunImpl : public KRun -{ -public: - KRunImpl(const KUrl& url, bool isLocalFile = false) - : KRun(url, 0, 0, isLocalFile, false) {} - - void foundMimeType(const QString& type) final { - m_mimeType = type; - // don't call KRun::foundMimeType, we don't want to start an app ;-) - setFinished(true); - } - - void error(const QString& message) final { - // don't show message box, this is unit test - Q_UNUSED(message); - } - - QString mimeTypeFound() const { return m_mimeType; } - -private: - QString m_mimeType; -}; - -void KRunUnitTest::testMimeTypeFile() -{ - const QString filePath = homeTmpDir() + "file"; - createTestFile(filePath, true); - KRunImpl* krun = new KRunImpl(filePath, true); - QTest::kWaitForSignal(krun, SIGNAL(finished()), 1000); - QCOMPARE(krun->mimeTypeFound(), QString::fromLatin1("text/plain")); -} - -void KRunUnitTest::testMimeTypeDirectory() -{ - const QString dir = homeTmpDir() + "dir"; - createTestDirectory(dir); - KRunImpl* krun = new KRunImpl(dir, true); - QTest::kWaitForSignal(krun, SIGNAL(finished()), 1000); - QCOMPARE(krun->mimeTypeFound(), QString::fromLatin1("inode/directory")); -} - -void KRunUnitTest::testMimeTypeBrokenLink() -{ - const QString dir = homeTmpDir() + "dir"; - createTestDirectory(dir); - KRunImpl* krun = new KRunImpl(KUrl(dir + "/testlink"), true); - QSignalSpy spyError(krun, SIGNAL(error())); - QTest::kWaitForSignal(krun, SIGNAL(finished()), 1000); - QVERIFY(krun->mimeTypeFound().isEmpty()); - QCOMPARE(spyError.count(), 1); -} - -void KRunUnitTest::testMimeTypeDoesNotExist() -{ - KRunImpl* krun = new KRunImpl(KUrl("/does/not/exist")); - QSignalSpy spyError(krun, SIGNAL(error())); - QTest::kWaitForSignal(krun, SIGNAL(finished()), 1000); - QVERIFY(krun->mimeTypeFound().isEmpty()); - QCOMPARE(spyError.count(), 1); -} diff --git a/kio/tests/krununittest.h b/kio/tests/krununittest.h deleted file mode 100644 index 028be9d1..00000000 --- a/kio/tests/krununittest.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2005, 2009 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 KRUNUNITTEST_H -#define KRUNUNITTEST_H - -#include - -class KRunUnitTest : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - void initTestCase(); - void testBinaryName_data(); - void testBinaryName(); - void testProcessDesktopExec(); - void testProcessDesktopExecNoFile_data(); - void testProcessDesktopExecNoFile(); - - void testMimeTypeFile(); - void testMimeTypeDirectory(); - void testMimeTypeBrokenLink(); - void testMimeTypeDoesNotExist(); - -private: - QString m_sh; - -}; - - -#endif /* KRUNUNITTEST_H */ - diff --git a/plasma/private/associatedapplicationmanager.cpp b/plasma/private/associatedapplicationmanager.cpp index e3a8a440..13766103 100644 --- a/plasma/private/associatedapplicationmanager.cpp +++ b/plasma/private/associatedapplicationmanager.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include namespace Plasma { @@ -79,7 +79,7 @@ AssociatedApplicationManager *AssociatedApplicationManager::self() void AssociatedApplicationManager::setApplication(Plasma::Applet *applet, const QString &application) { - KService::Ptr service = KService::serviceByDesktopName(application); + KService::Ptr service = KService::serviceByStorageId(application); if (service || !KStandardDirs::findExe(application).isNull() || QFile::exists(application)) { d->applicationNames[applet] = application; if (!d->urlLists.contains(applet)) { @@ -106,14 +106,15 @@ KUrl::List AssociatedApplicationManager::urls(const Plasma::Applet *applet) cons void AssociatedApplicationManager::run(Plasma::Applet *applet) { if (d->applicationNames.contains(applet)) { - bool success = KRun::run(d->applicationNames.value(applet), d->urlLists.value(applet), 0); + bool success = KToolInvocation::self()->startServiceByStorageId( + d->applicationNames.value(applet), d->urlLists.value(applet).toStringList() + ); if (!success) { applet->showMessage(KIcon("application-exit"), i18n("There was an error attempting to exec the associated application with this widget."), ButtonOk); } } else if (d->urlLists.contains(applet) && !d->urlLists.value(applet).isEmpty()) { - KRun *krun = new KRun(d->urlLists.value(applet).first(), 0); - krun->setAutoDelete(true); + KToolInvocation::self()->startServiceForUrl(d->urlLists.value(applet).first().url()); } }