1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 Torben Weis <weis@kde.org>
3 Copyright (C) 2006 David Faure <faure@kde.org>
4 Copyright (C) 2009 Michael Pyne <michael.pyne@kdemail.net>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
34 #include <QtGui/QWidget>
35 #include <QtGui/QLabel>
36 #include <QtGui/QVBoxLayout>
37 #include <QtGui/QHBoxLayout>
38 #include <QtGui/QPlainTextEdit>
39 #include <QtGui/QApplication>
40 #include <QtGui/QDesktopWidget>
42 #include "kmimetypetrader.h"
43 #include "kmimetype.h"
44 #include "kio/jobclasses.h" // for KIO::JobFlags
46 #include "kio/jobuidelegate.h"
47 #include "kio/global.h"
48 #include "kio/scheduler.h"
49 #include "kio/netaccess.h"
50 #include "kfile/kopenwithdialog.h"
51 #include "kfile/krecentdocument.h"
52 #include "kdesktopfileactions.h"
54 #include <kauthorized.h>
55 #include <kmessageboxwrapper.h>
58 #include <ktoolinvocation.h>
59 #include <kauthorized.h>
62 #include <kprotocolmanager.h>
63 #include <kstandarddirs.h>
65 #include <QtCore/QFile>
66 #include <QtCore/QFileInfo>
67 #include <QtCore/QTextIStream>
68 #include <QtCore/QDate>
69 #include <QtCore/QRegExp>
70 #include <kdesktopfile.h>
71 #include <kmacroexpander.h>
73 #include <QTextDocument>
75 #include <kconfiggroup.h>
77 #include <kstandardguiitem.h>
79 #include <ksavefile.h>
82 #include <kwindowsystem.h>
85 KRun::KRunPrivate::KRunPrivate(KRun
*parent
)
87 m_showingDialog(false)
91 void KRun::KRunPrivate::startTimer()
96 // ---------------------------------------------------------------------------
98 bool KRun::isExecutableFile(const KUrl
& url
, const QString
&mimetype
)
100 if (!url
.isLocalFile()) {
103 QFileInfo
file(url
.toLocalFile());
104 if (file
.isExecutable()) { // Got a prospective file to run
105 KMimeType::Ptr mimeType
= KMimeType::mimeType(mimetype
, KMimeType::ResolveAliases
);
106 if (mimeType
&& (mimeType
->is(QLatin1String("application/x-executable")) ||
108 mimeType
->is(QLatin1String("application/x-ms-dos-executable")) ||
110 mimeType
->is(QLatin1String("application/x-executable-script")))
119 // This is called by foundMimeType, since it knows the mimetype of the URL
120 bool KRun::runUrl(const KUrl
& u
, const QString
& _mimetype
, QWidget
* window
, bool tempFile
, bool runExecutables
, const QString
& suggestedFileName
, const QByteArray
& asn
)
124 if (_mimetype
== QLatin1String("inode/directory-locked")) {
125 KMessageBoxWrapper::error(window
,
126 i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>", Qt::escape(u
.prettyUrl())));
129 else if (_mimetype
== QLatin1String("application/x-desktop")) {
130 if (u
.isLocalFile() && runExecutables
) {
131 return KDesktopFileActions::run(u
, true);
134 else if (isExecutableFile(u
, _mimetype
)) {
135 if (u
.isLocalFile() && runExecutables
) {
136 if (KAuthorized::authorize("shell_access")) {
137 return (KRun::runCommand(KShell::quoteArg(u
.toLocalFile()), QString(), QString(), window
, asn
)); // just execute the url as a command
138 // ## TODO implement deleting the file if tempFile==true
144 else if (_mimetype
== QLatin1String("application/x-executable")) {
148 else if (isExecutable(_mimetype
)) {
149 if (!runExecutables
) {
153 if (!KAuthorized::authorize("shell_access")) {
159 KMessageBox::sorry(window
,
160 i18n("<qt>The file <b>%1</b> is an executable program. "
161 "For safety it will not be started.</qt>", Qt::escape(u
.prettyUrl())));
165 KMessageBoxWrapper::error(window
,
166 i18n("<qt>You do not have permission to run <b>%1</b>.</qt>", Qt::escape(u
.prettyUrl())));
173 KService::Ptr offer
= KMimeTypeTrader::self()->preferredService(_mimetype
);
177 // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog !
178 // Hmm, in fact KOpenWithDialog::setServiceType already guesses the mimetype from the first URL of the list...
179 return displayOpenWithDialog(lst
, window
, tempFile
, suggestedFileName
, asn
);
182 return KRun::run(*offer
, lst
, window
, tempFile
, suggestedFileName
, asn
);
185 bool KRun::displayOpenWithDialog(const KUrl::List
& lst
, QWidget
* window
, bool tempFiles
,
186 const QString
& suggestedFileName
, const QByteArray
& asn
)
188 if (!KAuthorized::authorizeKAction("openwith")) {
189 KMessageBox::sorry(window
,
190 i18n("You are not authorized to select an application to open this file."));
195 KConfigGroup
cfgGroup(KGlobal::config(), "KOpenWithDialog Settings");
196 if (cfgGroup
.readEntry("Native", true)) {
197 return KRun::KRunPrivate::displayNativeOpenWithDialog(lst
, window
, tempFiles
,
198 suggestedFileName
, asn
);
201 KOpenWithDialog
l(lst
, i18n("Open with:"), QString(), window
);
203 KService::Ptr service
= l
.service();
205 return KRun::run(*service
, lst
, window
, tempFiles
, suggestedFileName
, asn
);
208 kDebug(7010) << "No service set, running " << l
.text();
209 return KRun::run(l
.text(), lst
, window
, false, suggestedFileName
, asn
); // TODO handle tempFiles
214 void KRun::shellQuote(QString
&_str
)
216 // Credits to Walter, says Bernd G. :)
217 if (_str
.isEmpty()) { // Don't create an explicit empty parameter
221 _str
.replace(q
, "'\\''").prepend(q
).append(q
);
225 class KRunMX1
: public KMacroExpanderBase
228 KRunMX1(const KService
&_service
) :
229 KMacroExpanderBase('%'), hasUrls(false), hasSpec(false), service(_service
) {}
231 bool hasUrls
: 1, hasSpec
: 1;
234 virtual int expandEscapedMacro(const QString
&str
, int pos
, QStringList
&ret
);
237 const KService
&service
;
241 KRunMX1::expandEscapedMacro(const QString
&str
, int pos
, QStringList
&ret
)
243 uint option
= str
[pos
+ 1].unicode();
246 ret
<< service
.name().replace('%', "%%");
249 ret
<< service
.entryPath().replace('%', "%%");
252 ret
<< "-icon" << service
.icon().replace('%', "%%");
255 // ret << "-miniicon" << service.icon().replace( '%', "%%" );
256 kWarning() << "-miniicon isn't supported anymore (service"
257 << service
.name() << ')';
273 return -2; // subst with same and skip
278 class KRunMX2
: public KMacroExpanderBase
281 KRunMX2(const KUrl::List
&_urls
) :
282 KMacroExpanderBase('%'), ignFile(false), urls(_urls
) {}
287 virtual int expandEscapedMacro(const QString
&str
, int pos
, QStringList
&ret
);
290 void subst(int option
, const KUrl
&url
, QStringList
&ret
);
292 const KUrl::List
&urls
;
296 KRunMX2::subst(int option
, const KUrl
&url
, QStringList
&ret
)
300 ret
<< ((url
.isLocalFile() && url
.fragment().isNull() && url
.encodedQuery().isNull()) ?
301 url
.toLocalFile() : url
.url());
304 ret
<< url
.directory();
310 ret
<< url
.fileName();
313 if (url
.isLocalFile() && QFile::exists(url
.toLocalFile())) {
314 ret
<< KDesktopFile(url
.path()).desktopGroup().readEntry("Dev");
322 KRunMX2::expandEscapedMacro(const QString
&str
, int pos
, QStringList
&ret
)
324 uint option
= str
[pos
+ 1].unicode();
331 if (urls
.isEmpty()) {
333 kDebug() << "No URLs supplied to single-URL service" << str
;
336 else if (urls
.count() > 1) {
337 kWarning() << urls
.count() << "URLs supplied to single-URL service" << str
;
340 subst(option
, urls
.first(), ret
);
348 for (KUrl::List::ConstIterator it
= urls
.begin(); it
!= urls
.end(); ++it
)
349 subst(option
, *it
, ret
);
352 ret
= QStringList(QLatin1String("%"));
355 return -2; // subst with same and skip
360 QStringList
KRun::processDesktopExec(const KService
&_service
, const KUrl::List
& _urls
, bool tempFiles
, const QString
& suggestedFileName
)
362 QString exec
= _service
.exec();
363 if (exec
.isEmpty()) {
364 kWarning() << "KRun: no Exec field in `" << _service
.entryPath() << "' !";
365 return QStringList();
369 bool appHasTempFileOption
;
371 KRunMX1
mx1(_service
);
374 if (!mx1
.expandMacrosShellQuote(exec
)) { // Error in shell syntax
375 kWarning() << "KRun: syntax error in command" << _service
.exec() << ", service" << _service
.name();
376 return QStringList();
379 // FIXME: the current way of invoking kioexec disables term and su use
381 // Check if we need "tempexec" (kioexec in fact)
382 appHasTempFileOption
= tempFiles
&& _service
.property("X-KDE-HasTempFileOption").toBool();
383 if (tempFiles
&& !appHasTempFileOption
&& _urls
.size()) {
384 const QString kioexec
= KStandardDirs::findExe("kioexec");
385 Q_ASSERT(!kioexec
.isEmpty());
386 result
<< kioexec
<< "--tempfiles" << exec
;
387 if (!suggestedFileName
.isEmpty()) {
388 result
<< "--suggestedfilename";
389 result
<< suggestedFileName
;
391 result
+= _urls
.toStringList();
395 // Check if we need kioexec
397 for (KUrl::List::ConstIterator it
= _urls
.begin(); it
!= _urls
.end(); ++it
)
398 if (!(*it
).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it
)) {
399 // We need to run the app through kioexec
400 const QString kioexec
= KStandardDirs::findExe("kioexec");
401 Q_ASSERT(!kioexec
.isEmpty());
404 result
<< "--tempfiles";
406 if (!suggestedFileName
.isEmpty()) {
407 result
<< "--suggestedfilename";
408 result
<< suggestedFileName
;
411 result
+= _urls
.toStringList();
416 if (appHasTempFileOption
) {
417 exec
+= " --tempfile";
420 // Did the user forget to append something like '%f'?
421 // If so, then assume that '%f' is the right choice => the application
422 // accepts only local files.
428 mx2
.expandMacrosShellQuote(exec
); // syntax was already checked, so don't check return value
431 1 = need_shell, 2 = terminal, 4 = su
434 1 << "sh" << "-c" << cmd
435 2 << split(term) << "-e" << split(cmd)
436 3 << split(term) << "-e" << "sh" << "-c" << cmd
438 4 << "kdesu" << "-u" << user << "-c" << cmd
439 5 << "kdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd))
440 6 << split(term) << "-e" << "su" << user << "-c" << cmd
441 7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd))
443 "sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh.
444 this could be optimized with the -s switch of some su versions (e.g., debian linux).
447 if (_service
.terminal()) {
448 KConfigGroup
cg(KGlobal::config(), "General");
449 QString terminal
= cg
.readPathEntry("TerminalApplication", "konsole");
450 if (terminal
== "konsole") {
451 if (!_service
.path().isEmpty()) {
452 terminal
+= " --workdir " + KShell::quoteArg(_service
.path());
454 terminal
+= " -caption=%c %i %m";
457 terminal
+= _service
.terminalOptions();
458 if (!mx1
.expandMacrosShellQuote(terminal
)) {
459 kWarning() << "KRun: syntax error in command" << terminal
<< ", service" << _service
.name();
460 return QStringList();
462 mx2
.expandMacrosShellQuote(terminal
);
463 result
= KShell::splitArgs(terminal
); // assuming that the term spec never needs a shell!
468 QStringList execlist
= KShell::splitArgs(exec
, KShell::AbortOnMeta
| KShell::TildeExpand
, &err
);
469 if (err
== KShell::NoError
&& !execlist
.isEmpty()) { // mx1 checked for syntax errors already
470 // Resolve the executable to ensure that helpers in lib/kde4/libexec/ are found.
471 // Too bad for commands that need a shell - they must reside in $PATH.
472 const QString exePath
= KStandardDirs::findExe(execlist
[0]);
473 if (!exePath
.isEmpty()) {
474 execlist
[0] = exePath
;
477 if (_service
.substituteUid()) {
478 if (_service
.terminal()) {
482 result
<< KStandardDirs::findExe("kdesu") << "-u";
485 result
<< _service
.username() << "-c";
486 if (err
== KShell::FoundMeta
) {
487 exec
= "/bin/sh -c " + KShell::quoteArg(exec
);
490 exec
= KShell::joinArgs(execlist
);
495 if (err
== KShell::FoundMeta
) {
496 result
<< "/bin/sh" << "-c" << exec
;
507 QString
KRun::binaryName(const QString
& execLine
, bool removePath
)
509 // Remove parameters and/or trailing spaces.
510 const QStringList args
= KShell::splitArgs(execLine
);
511 for (QStringList::ConstIterator it
= args
.begin(); it
!= args
.end(); ++it
)
512 if (!(*it
).contains('=')) {
513 // Remove path if wanted
514 return removePath
? (*it
).mid((*it
).lastIndexOf('/') + 1) : *it
;
519 static bool runCommandInternal(KProcess
* proc
, const KService
* service
, const QString
& executable
,
520 const QString
&userVisibleName
, const QString
& iconName
, QWidget
* window
,
521 const QByteArray
& asn
)
523 if (window
!= NULL
) {
524 window
= window
->topLevelWidget();
526 if (service
&& !service
->entryPath().isEmpty()
527 && !KDesktopFile::isAuthorizedDesktopFile(service
->entryPath()))
529 kWarning() << "No authorization to execute " << service
->entryPath();
530 KMessageBox::sorry(window
, i18n("You are not authorized to execute this file."));
535 QString bin
= KRun::binaryName(executable
, true);
536 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification
540 bool startup_notify
= (asn
!= "0" && KRun::checkStartupNotify(QString() /*unused*/, service
, &silent
, &wmclass
));
541 if (startup_notify
) {
543 id
.setupStartupEnv();
544 KStartupInfoData data
;
547 if (!userVisibleName
.isEmpty()) {
548 data
.setName(userVisibleName
);
550 else if (service
&& !service
->name().isEmpty()) {
551 data
.setName(service
->name());
553 data
.setDescription(i18n("Launching %1" , data
.name()));
554 if (!iconName
.isEmpty()) {
555 data
.setIcon(iconName
);
557 else if (service
&& !service
->icon().isEmpty()) {
558 data
.setIcon(service
->icon());
560 if (!wmclass
.isEmpty()) {
561 data
.setWMClass(wmclass
);
564 data
.setSilent(KStartupInfoData::Yes
);
566 data
.setDesktop(KWindowSystem::currentDesktop());
568 data
.setLaunchedBy(window
->winId());
570 KStartupInfo::sendStartup(id
, data
);
572 int pid
= KProcessRunner::run(proc
, executable
, id
);
573 if (startup_notify
&& pid
) {
574 KStartupInfoData data
;
576 KStartupInfo::sendChange(id
, data
);
577 KStartupInfo::resetStartupEnv();
581 Q_UNUSED(userVisibleName
);
583 return KProcessRunner::run(proc
, bin
) != 0;
587 // This code is also used in klauncher.
588 bool KRun::checkStartupNotify(const QString
& /*binName*/, const KService
* service
, bool* silent_arg
, QByteArray
* wmclass_arg
)
592 if (service
&& service
->property("StartupNotify").isValid()) {
593 silent
= !service
->property("StartupNotify").toBool();
594 wmclass
= service
->property("StartupWMClass").toString().toLatin1();
596 else if (service
&& service
->property("X-KDE-StartupNotify").isValid()) {
597 silent
= !service
->property("X-KDE-StartupNotify").toBool();
598 wmclass
= service
->property("X-KDE-WMClass").toString().toLatin1();
600 else { // non-compliant app
602 if (service
->isApplication()) {
603 wmclass
= "0"; // doesn't have .desktop entries needed, start as non-compliant
606 return false; // no startup notification at all
611 // Create startup notification even for apps for which there shouldn't be any,
612 // just without any visual feedback. This will ensure they'll be positioned on the proper
613 // virtual desktop, and will get user timestamp from the ASN ID.
616 #else // That unfortunately doesn't work, when the launched non-compliant application
617 // launches another one that is compliant and there is any delay inbetween (bnc:#343359)
622 if (silent_arg
!= NULL
) {
623 *silent_arg
= silent
;
625 if (wmclass_arg
!= NULL
) {
626 *wmclass_arg
= wmclass
;
631 static bool runTempService(const KService
& _service
, const KUrl::List
& _urls
, QWidget
* window
,
632 bool tempFiles
, const QString
& suggestedFileName
, const QByteArray
& asn
)
634 if (!_urls
.isEmpty()) {
635 kDebug(7010) << "runTempService: first url " << _urls
.first().url();
639 if ((_urls
.count() > 1) && !_service
.allowMultipleFiles()) {
640 // We need to launch the application N times. That sucks.
641 // We ignore the result for application 2 to N.
642 // For the first file we launch the application in the
643 // usual way. The reported result is based on this
645 KUrl::List::ConstIterator it
= _urls
.begin();
646 while (++it
!= _urls
.end()) {
647 KUrl::List singleUrl
;
648 singleUrl
.append(*it
);
649 runTempService(_service
, singleUrl
, window
, tempFiles
, suggestedFileName
, QByteArray());
651 KUrl::List singleUrl
;
652 singleUrl
.append(_urls
.first());
653 args
= KRun::processDesktopExec(_service
, singleUrl
, tempFiles
, suggestedFileName
);
656 args
= KRun::processDesktopExec(_service
, _urls
, tempFiles
, suggestedFileName
);
658 if (args
.isEmpty()) {
659 KMessageBox::sorry(window
, i18n("Error processing Exec field in %1", _service
.entryPath()));
662 kDebug(7010) << "runTempService: KProcess args=" << args
;
664 KProcess
* proc
= new KProcess
;
667 if (!_service
.path().isEmpty()) {
668 proc
->setWorkingDirectory(_service
.path());
671 return runCommandInternal(proc
, &_service
, KRun::binaryName(_service
.exec(), false),
672 _service
.name(), _service
.icon(), window
, asn
);
675 // WARNING: don't call this from processDesktopExec, since klauncher uses that too...
676 static KUrl::List
resolveURLs(const KUrl::List
& _urls
, const KService
& _service
)
678 // Check which protocols the application supports.
679 // This can be a list of actual protocol names, or just KIO for KDE apps.
680 QStringList supportedProtocols
= _service
.property("X-KDE-Protocols").toStringList();
681 KRunMX1
mx1(_service
);
682 QString exec
= _service
.exec();
683 if (mx1
.expandMacrosShellQuote(exec
) && !mx1
.hasUrls
) {
684 Q_ASSERT(supportedProtocols
.isEmpty()); // huh? If you support protocols you need %u or %U...
687 if (supportedProtocols
.isEmpty()) {
688 // compat mode: assume KIO if not set and it's a KDE app
689 QStringList categories
= _service
.property("Categories").toStringList();
690 if (categories
.contains("KDE")) {
691 supportedProtocols
.append("KIO");
693 else { // if no KDE app, be a bit over-generic
694 supportedProtocols
.append("http");
695 supportedProtocols
.append("ftp");
699 kDebug(7010) << "supportedProtocols:" << supportedProtocols
;
701 KUrl::List
urls(_urls
);
702 if (!supportedProtocols
.contains("KIO")) {
703 for (KUrl::List::Iterator it
= urls
.begin(); it
!= urls
.end(); ++it
) {
704 const KUrl url
= *it
;
705 bool supported
= url
.isLocalFile() || supportedProtocols
.contains(url
.protocol().toLower());
706 kDebug(7010) << "Looking at url=" << url
<< " supported=" << supported
;
707 if (!supported
&& KProtocolInfo::protocolClass(url
.protocol()) == ":local") {
708 // Maybe we can resolve to a local URL?
709 KUrl localURL
= KIO::NetAccess::mostLocalUrl(url
, 0);
710 if (localURL
!= url
) {
712 kDebug(7010) << "Changed to " << localURL
;
720 // Simple KDialog that resizes the given text edit after being shown to more
721 // or less fit the enclosed text.
722 class SecureMessageDialog
: public KDialog
725 SecureMessageDialog(QWidget
*parent
) : KDialog(parent
), m_textEdit(0)
729 void setTextEdit(QPlainTextEdit
*textEdit
)
731 m_textEdit
= textEdit
;
735 virtual void showEvent(QShowEvent
* e
)
737 // Now that we're shown, use our width to calculate a good
738 // bounding box for the text, and resize m_textEdit appropriately.
739 KDialog::showEvent(e
);
744 QSize
fudge(20, 24); // About what it sounds like :-/
746 // Form rect with a lot of height for bounding. Use no more than
748 QRect
curRect(m_textEdit
->rect());
749 QFontMetrics
metrics(fontMetrics());
750 curRect
.setHeight(5 * metrics
.lineSpacing());
751 curRect
.setWidth(qMax(curRect
.width(), 300)); // At least 300 pixels ok?
753 QString
text(m_textEdit
->toPlainText());
754 curRect
= metrics
.boundingRect(curRect
, Qt::TextWordWrap
| Qt::TextSingleLine
, text
);
756 // Scroll bars interfere. If we don't think there's enough room, enable
757 // the vertical scrollbar however.
758 m_textEdit
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
759 if(curRect
.height() < m_textEdit
->height()) { // then we've got room
760 m_textEdit
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
761 m_textEdit
->setMaximumHeight(curRect
.height() + fudge
.height());
764 m_textEdit
->setMinimumSize(curRect
.size() + fudge
);
765 m_textEdit
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Minimum
);
770 QPlainTextEdit
*m_textEdit
;
773 // Helper function to make the given .desktop file executable by ensuring
774 // that a #!/usr/bin/env xdg-open line is added if necessary and the file has
775 // the +x bit set for the user. Returns false if either fails.
776 static bool makeFileExecutable(const QString
&fileName
)
778 // Open the file and read the first two characters, check if it's
779 // #!. If not, create a new file, prepend appropriate lines, and copy
781 QFile
desktopFile(fileName
);
782 if (!desktopFile
.open(QFile::ReadOnly
)) {
783 kError(7010) << "Error opening service" << fileName
<< desktopFile
.errorString();
787 QByteArray header
= desktopFile
.peek(2); // First two chars of file
788 if (header
.size() == 0) {
789 kError(7010) << "Error inspecting service" << fileName
<< desktopFile
.errorString();
790 return false; // Some kind of error
793 if (header
!= "#!") {
796 saveFile
.setFileName(fileName
);
797 if (!saveFile
.open()) {
798 kError(7010) << "Unable to open replacement file for" << fileName
<< saveFile
.errorString();
802 QByteArray
shebang("#!/usr/bin/env xdg-open\n");
803 if (saveFile
.write(shebang
) != shebang
.size()) {
804 kError(7010) << "Error occurred adding header for" << fileName
<< saveFile
.errorString();
809 // Now copy the one into the other and then close and reopen desktopFile
810 QByteArray
desktopData(desktopFile
.readAll());
811 if (desktopData
.isEmpty()) {
812 kError(7010) << "Unable to read service" << fileName
<< desktopFile
.errorString();
817 if (saveFile
.write(desktopData
) != desktopData
.size()) {
818 kError(7010) << "Error copying service" << fileName
<< saveFile
.errorString();
824 if (!saveFile
.finalize()) { // Figures....
825 kError(7010) << "Error committing changes to service" << fileName
<< saveFile
.errorString();
829 if (!desktopFile
.open(QFile::ReadOnly
)) {
830 kError(7010) << "Error re-opening service" << fileName
<< desktopFile
.errorString();
835 // corresponds to owner on unix, which will have to do since if the user
836 // isn't the owner we can't change perms anyways.
837 if (!desktopFile
.setPermissions(QFile::ExeUser
| desktopFile
.permissions())) {
838 kError(7010) << "Unable to change permissions for" << fileName
<< desktopFile
.errorString();
846 // Helper function to make a .desktop file executable if prompted by the user.
847 // returns true if KRun::run() should continue with execution, false if user declined
848 // to make the file executable or we failed to make it executable.
849 static bool makeServiceExecutable(const KService
& service
, QWidget
* window
)
851 if (!KAuthorized::authorize("run_desktop_files")) {
852 kWarning() << "No authorization to execute " << service
.entryPath();
853 KMessageBox::sorry(window
, i18n("You are not authorized to execute this service."));
854 return false; // Don't circumvent the Kiosk
857 KGuiItem continueItem
= KStandardGuiItem::cont();
859 SecureMessageDialog
*baseDialog
= new SecureMessageDialog(window
);
861 baseDialog
->setButtons(KDialog::Ok
| KDialog::Cancel
);
862 baseDialog
->setButtonGuiItem(KDialog::Ok
, continueItem
);
863 baseDialog
->setDefaultButton(KDialog::Cancel
);
864 baseDialog
->setButtonFocus(KDialog::Cancel
);
865 baseDialog
->setCaption(i18nc("Warning about executing unknown .desktop file", "Warning"));
867 // Dialog will have explanatory text with a disabled lineedit with the
868 // Exec= to make it visually distinct.
869 QWidget
*baseWidget
= new QWidget(baseDialog
);
870 QHBoxLayout
*mainLayout
= new QHBoxLayout(baseWidget
);
872 QLabel
*iconLabel
= new QLabel(baseWidget
);
873 QPixmap
warningIcon(KIconLoader::global()->loadIcon("dialog-warning", KIconLoader::NoGroup
, KIconLoader::SizeHuge
));
874 mainLayout
->addWidget(iconLabel
);
875 iconLabel
->setPixmap(warningIcon
);
877 QVBoxLayout
*contentLayout
= new QVBoxLayout
;
878 QString warningMessage
= i18nc("program name follows in a line edit below",
879 "This will start the program:");
881 QLabel
*message
= new QLabel(warningMessage
, baseWidget
);
882 contentLayout
->addWidget(message
);
884 // We can use KStandardDirs::findExe to resolve relative pathnames
885 // but that gets rid of the command line arguments.
886 QString program
= KStandardDirs::realFilePath(service
.exec());
888 QPlainTextEdit
*textEdit
= new QPlainTextEdit(baseWidget
);
889 textEdit
->setPlainText(program
);
890 textEdit
->setReadOnly(true);
891 contentLayout
->addWidget(textEdit
);
893 QLabel
*footerLabel
= new QLabel(i18n("If you do not trust this program, click Cancel"));
894 contentLayout
->addWidget(footerLabel
);
895 contentLayout
->addStretch(0); // Don't allow the text edit to expand
897 mainLayout
->addLayout(contentLayout
);
899 baseDialog
->setMainWidget(baseWidget
);
900 baseDialog
->setTextEdit(textEdit
);
902 // Constrain maximum size. Minimum size set in
903 // the dialog's show event.
904 QSize screenSize
= QApplication::desktop()->screen()->size();
905 baseDialog
->resize(screenSize
.width() / 4, 50);
906 baseDialog
->setMaximumHeight(screenSize
.height() / 3);
907 baseDialog
->setMaximumWidth(screenSize
.width() / 10 * 8);
909 int result
= baseDialog
->exec();
910 if (result
!= KDialog::Accepted
) {
914 // Assume that service is an absolute path since we're being called (relative paths
915 // would have been allowed unless Kiosk said no, therefore we already know where the
916 // .desktop file is. Now add a header to it if it doesn't already have one
917 // and add the +x bit.
919 if (!::makeFileExecutable(service
.entryPath())) {
920 QString serviceName
= service
.name();
921 if(serviceName
.isEmpty())
922 serviceName
= service
.genericName();
926 i18n("Unable to make the service %1 executable, aborting execution", serviceName
)
935 bool KRun::run(const KService
& _service
, const KUrl::List
& _urls
, QWidget
* window
,
936 bool tempFiles
, const QString
& suggestedFileName
, const QByteArray
& asn
)
938 if (!_service
.entryPath().isEmpty() &&
939 !KDesktopFile::isAuthorizedDesktopFile(_service
.entryPath()) &&
940 !::makeServiceExecutable(_service
, window
))
946 // Remember we opened those urls, for the "recent documents" menu in kicker
947 KUrl::List::ConstIterator it
= _urls
.begin();
948 for (; it
!= _urls
.end(); ++it
) {
949 //kDebug(7010) << "KRecentDocument::adding " << (*it).url();
950 KRecentDocument::add(*it
, _service
.desktopEntryName());
954 if (tempFiles
|| _service
.entryPath().isEmpty() || !suggestedFileName
.isEmpty()) {
955 return runTempService(_service
, _urls
, window
, tempFiles
, suggestedFileName
, asn
);
958 kDebug(7010) << "KRun::run " << _service
.entryPath();
960 if (!_urls
.isEmpty()) {
961 kDebug(7010) << "First url " << _urls
.first().url();
964 // Resolve urls if needed, depending on what the app supports
965 const KUrl::List urls
= resolveURLs(_urls
, _service
);
970 QByteArray myasn
= asn
;
971 // startServiceByDesktopPath() doesn't take QWidget*, add it to the startup info now
972 if (window
!= NULL
) {
973 if (myasn
.isEmpty()) {
974 myasn
= KStartupInfo::createNewStartupId();
979 KStartupInfoData data
;
980 data
.setLaunchedBy(window
->winId());
981 KStartupInfo::sendChange(id
, data
);
985 int i
= KToolInvocation::startServiceByDesktopPath(
986 _service
.entryPath(), urls
.toStringList(), &error
, 0L, &pid
, myasn
990 kDebug(7010) << error
;
991 KMessageBox::sorry(window
, error
);
995 kDebug(7010) << "startServiceByDesktopPath worked fine";
1000 bool KRun::run(const QString
& _exec
, const KUrl::List
& _urls
, QWidget
* window
, const QString
& _name
,
1001 const QString
& _icon
, const QByteArray
& asn
)
1003 KService::Ptr
service(new KService(_name
, _exec
, _icon
));
1005 return run(*service
, _urls
, window
, false, QString(), asn
);
1008 bool KRun::runCommand(const QString
&cmd
, QWidget
* window
)
1010 const QString bin
= KShell::splitArgs(cmd
).first();
1011 return KRun::runCommand(cmd
, bin
, bin
/*iconName*/, window
, QByteArray());
1014 bool KRun::runCommand(const QString
& cmd
, const QString
&execName
, const QString
& iconName
, QWidget
* window
, const QByteArray
& asn
)
1016 kDebug(7010) << "runCommand " << cmd
<< "," << execName
;
1017 KProcess
* proc
= new KProcess
;
1018 proc
->setShellCommand(cmd
);
1019 QString bin
= binaryName(execName
, true);
1020 KService::Ptr service
= KService::serviceByDesktopName(bin
);
1021 return runCommandInternal(proc
, service
.data(),
1022 execName
/*executable to check for in slotProcessExited*/,
1023 execName
/*user-visible name*/,
1024 iconName
, window
, asn
);
1027 KRun::KRun(const KUrl
& url
, QWidget
* window
, mode_t mode
, bool isLocalFile
,
1028 bool showProgressInfo
, const QByteArray
& asn
)
1029 : d(new KRunPrivate(this))
1031 d
->m_timer
.setObjectName("KRun::timer");
1032 d
->m_timer
.setSingleShot(true);
1033 d
->init(url
, window
, mode
, isLocalFile
, showProgressInfo
, asn
);
1036 void KRun::KRunPrivate::init(const KUrl
& url
, QWidget
* window
, mode_t mode
, bool isLocalFile
,
1037 bool showProgressInfo
, const QByteArray
& asn
)
1040 m_bAutoDelete
= true;
1041 m_bProgressInfo
= showProgressInfo
;
1042 m_bFinished
= false;
1045 m_bScanFile
= false;
1046 m_bIsDirectory
= false;
1047 m_bIsLocalFile
= isLocalFile
;
1049 m_runExecutables
= true;
1052 q
->setEnableExternalBrowser(true);
1054 // Start the timer. This means we will return to the event
1055 // loop and do initialization afterwards.
1056 // Reason: We must complete the constructor before we do anything else.
1058 q
->connect(&m_timer
, SIGNAL(timeout()), q
, SLOT(slotTimeout()));
1060 //kDebug(7010) << "new KRun" << q << url << "timer=" << &m_timer;
1067 kDebug(7010) << "INIT called";
1068 if (!d
->m_strURL
.isValid()) {
1069 // TODO KDE5: call virtual method on error (see BrowserRun::init)
1070 d
->m_showingDialog
= true;
1071 KMessageBoxWrapper::error(d
->m_window
, i18n("Malformed URL\n%1", d
->m_strURL
.url()));
1072 d
->m_showingDialog
= false;
1074 d
->m_bFinished
= true;
1078 if (!KAuthorized::authorizeUrlAction("open", KUrl(), d
->m_strURL
)) {
1079 QString msg
= KIO::buildErrorString(KIO::ERR_ACCESS_DENIED
, d
->m_strURL
.prettyUrl());
1080 d
->m_showingDialog
= true;
1081 KMessageBoxWrapper::error(d
->m_window
, msg
);
1082 d
->m_showingDialog
= false;
1084 d
->m_bFinished
= true;
1089 if (!d
->m_bIsLocalFile
&& d
->m_strURL
.isLocalFile()) {
1090 d
->m_bIsLocalFile
= true;
1094 if (d
->m_strURL
.protocol().startsWith("http")) {
1095 exec
= d
->m_externalBrowser
;
1098 if (d
->m_bIsLocalFile
) {
1099 if (d
->m_mode
== 0) {
1100 KDE_struct_stat buff
;
1101 if (KDE::stat(d
->m_strURL
.path(), &buff
) == -1) {
1102 d
->m_showingDialog
= true;
1103 KMessageBoxWrapper::error(d
->m_window
,
1104 i18n("<qt>Unable to run the command specified. "
1105 "The file or folder <b>%1</b> does not exist.</qt>" ,
1106 Qt::escape(d
->m_strURL
.prettyUrl())));
1107 d
->m_showingDialog
= false;
1109 d
->m_bFinished
= true;
1113 d
->m_mode
= buff
.st_mode
;
1116 KMimeType::Ptr mime
= KMimeType::findByUrl(d
->m_strURL
, d
->m_mode
, d
->m_bIsLocalFile
);
1118 kDebug(7010) << "MIME TYPE is " << mime
->name();
1119 mimeTypeDetermined(mime
->name());
1122 else if (!exec
.isEmpty() || KProtocolInfo::isHelperProtocol(d
->m_strURL
)) {
1123 kDebug(7010) << "Helper protocol";
1127 urls
.append(d
->m_strURL
);
1128 if (exec
.isEmpty()) {
1129 exec
= KProtocolInfo::exec(d
->m_strURL
.protocol());
1130 if (exec
.isEmpty()) {
1131 mimeTypeDetermined(KProtocolManager::defaultMimetype(d
->m_strURL
));
1134 run(exec
, urls
, d
->m_window
, false, QString(), d
->m_asn
);
1137 else if (exec
.startsWith('!')) {
1138 exec
= exec
.mid(1); // Literal command
1140 run(exec
, urls
, d
->m_window
, false, QString(), d
->m_asn
);
1144 KService::Ptr service
= KService::serviceByStorageId(exec
);
1146 run(*service
, urls
, d
->m_window
, false, QString(), d
->m_asn
);
1152 d
->m_bFinished
= true;
1153 // will emit the error and autodelete this
1159 // Did we already get the information that it is a directory ?
1160 if (S_ISDIR(d
->m_mode
)) {
1161 mimeTypeDetermined("inode/directory");
1165 // Let's see whether it is a directory
1167 if (!KProtocolManager::supportsListing(d
->m_strURL
)) {
1168 //kDebug(7010) << "Protocol has no support for listing";
1169 // No support for listing => it can't be a directory (example: http)
1174 kDebug(7010) << "Testing directory (stating)";
1176 // It may be a directory or a file, let's stat
1177 KIO::JobFlags flags
= d
->m_bProgressInfo
? KIO::DefaultFlags
: KIO::HideProgressInfo
;
1178 KIO::StatJob
*job
= KIO::stat(d
->m_strURL
, KIO::StatJob::SourceSide
, 0 /* no details */, flags
);
1179 job
->ui()->setWindow(d
->m_window
);
1180 connect(job
, SIGNAL(result(KJob
*)),
1181 this, SLOT(slotStatResult(KJob
*)));
1183 kDebug(7010) << " Job " << job
<< " is about stating " << d
->m_strURL
.url();
1188 //kDebug(7010) << this;
1192 //kDebug(7010) << this << "done";
1196 void KRun::scanFile()
1198 kDebug(7010) << d
->m_strURL
;
1199 // First, let's check for well-known extensions
1200 // Not when there is a query in the URL, in any case.
1201 if (d
->m_strURL
.query().isEmpty()) {
1202 KMimeType::Ptr mime
= KMimeType::findByUrl(d
->m_strURL
);
1204 if (mime
->name() != "application/octet-stream" || d
->m_bIsLocalFile
) {
1205 kDebug(7010) << "Scanfile: MIME TYPE is " << mime
->name();
1206 mimeTypeDetermined(mime
->name());
1211 // No mimetype found, and the URL is not local (or fast mode not allowed).
1212 // We need to apply the 'KIO' method, i.e. either asking the server or
1213 // getting some data out of the file, to know what mimetype it is.
1215 if (!KProtocolManager::supportsReading(d
->m_strURL
)) {
1216 kError(7010) << "#### NO SUPPORT FOR READING!";
1218 d
->m_bFinished
= true;
1222 kDebug(7010) << this << " Scanning file " << d
->m_strURL
.url();
1224 KIO::JobFlags flags
= d
->m_bProgressInfo
? KIO::DefaultFlags
: KIO::HideProgressInfo
;
1225 KIO::TransferJob
*job
= KIO::get(d
->m_strURL
, KIO::NoReload
/*reload*/, flags
);
1226 job
->ui()->setWindow(d
->m_window
);
1227 connect(job
, SIGNAL(result(KJob
*)),
1228 this, SLOT(slotScanFinished(KJob
*)));
1229 connect(job
, SIGNAL(mimetype(KIO::Job
*, const QString
&)),
1230 this, SLOT(slotScanMimeType(KIO::Job
*, const QString
&)));
1232 kDebug(7010) << " Job " << job
<< " is about getting from " << d
->m_strURL
.url();
1235 void KRun::slotTimeout()
1237 kDebug(7010) << this << " slotTimeout called";
1247 if (d
->m_bFinished
) {
1251 if (d
->m_bScanFile
) {
1252 d
->m_bScanFile
= false;
1256 else if (d
->m_bIsDirectory
) {
1257 d
->m_bIsDirectory
= false;
1258 mimeTypeDetermined("inode/directory");
1263 if (d
->m_bAutoDelete
) {
1269 void KRun::slotStatResult(KJob
* job
)
1273 d
->m_showingDialog
= true;
1274 kError(7010) << this << "ERROR" << job
->error() << ' ' << job
->errorString();
1275 job
->uiDelegate()->showErrorMessage();
1276 //kDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us";
1277 d
->m_showingDialog
= false;
1280 d
->m_bFinished
= true;
1282 // will emit the error and autodelete this
1288 kDebug(7010) << "Finished";
1289 if (!qobject_cast
<KIO::StatJob
*>(job
)) {
1290 kFatal() << "job is a " << typeid(*job
).name() << " should be a StatJob";
1293 const KIO::UDSEntry entry
= ((KIO::StatJob
*)job
)->statResult();
1294 const mode_t mode
= entry
.numberValue(KIO::UDSEntry::UDS_FILE_TYPE
);
1295 if (S_ISDIR(mode
)) {
1296 d
->m_bIsDirectory
= true; // it's a dir
1299 d
->m_bScanFile
= true; // it's a file
1302 d
->m_localPath
= entry
.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH
);
1304 // mimetype already known? (e.g. print:/manager)
1305 const QString knownMimeType
= entry
.stringValue(KIO::UDSEntry::UDS_MIME_TYPE
) ;
1307 if (!knownMimeType
.isEmpty()) {
1308 mimeTypeDetermined(knownMimeType
);
1309 d
->m_bFinished
= true;
1312 // We should have found something
1313 assert(d
->m_bScanFile
|| d
->m_bIsDirectory
);
1315 // Start the timer. Once we get the timer event this
1316 // protocol server is back in the pool and we can reuse it.
1317 // This gives better performance than starting a new slave
1322 void KRun::slotScanMimeType(KIO::Job
*, const QString
&mimetype
)
1324 if (mimetype
.isEmpty()) {
1325 kWarning(7010) << "MimetypeJob didn't find a mimetype! Probably a kioslave bug.";
1327 mimeTypeDetermined(mimetype
);
1331 void KRun::slotScanFinished(KJob
*job
)
1335 d
->m_showingDialog
= true;
1336 kError(7010) << this << "ERROR (stat):" << job
->error() << ' ' << job
->errorString();
1337 job
->uiDelegate()->showErrorMessage();
1338 //kDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us";
1339 d
->m_showingDialog
= false;
1342 d
->m_bFinished
= true;
1344 // will emit the error and autodelete this
1349 void KRun::mimeTypeDetermined(const QString
& mimeType
)
1351 // foundMimeType reimplementations might show a dialog box;
1352 // make sure some timer doesn't kill us meanwhile (#137678, #156447)
1353 Q_ASSERT(!d
->m_showingDialog
);
1354 d
->m_showingDialog
= true;
1356 foundMimeType(mimeType
);
1358 d
->m_showingDialog
= false;
1361 void KRun::foundMimeType(const QString
& type
)
1363 kDebug(7010) << "Resulting mime type is " << type
;
1366 // Automatically unzip stuff
1368 // Disabled since the new KIO doesn't have filters yet.
1370 if ( type == "application/x-gzip" ||
1371 type == "application/x-bzip" ||
1372 type == "application/x-bzip" )
1374 KUrl::List lst = KUrl::split( m_strURL );
1375 if ( lst.isEmpty() )
1377 QString tmp = i18n( "Malformed URL" );
1379 tmp += m_strURL.url();
1380 KMessageBoxWrapper::error( 0L, tmp );
1384 if ( type == "application/x-gzip" )
1385 lst.prepend( KUrl( "gzip:/decompress" ) );
1386 else if ( type == "application/x-bzip" )
1387 lst.prepend( KUrl( "bzip:/decompress" ) );
1388 else if ( type == "application/x-bzip" )
1389 lst.prepend( KUrl( "bzip2:/decompress" ) );
1390 else if ( type == "application/x-tar" )
1391 lst.prepend( KUrl( "tar:/" ) );
1393 // Move the HTML style reference to the leftmost URL
1394 KUrl::List::Iterator it = lst.begin();
1396 (*lst.begin()).setRef( (*it).ref() );
1397 (*it).setRef( QString() );
1399 // Create the new URL
1400 m_strURL = KUrl::join( lst );
1402 kDebug(7010) << "Now trying with " << debugString(m_strURL.url());
1406 // We don't know if this is a file or a directory. Let's test this first.
1407 // (For instance a tar.gz is a directory contained inside a file)
1408 // It may be a directory or a file, let's stat
1409 KIO::StatJob *job = KIO::stat( m_strURL, m_bProgressInfo );
1410 connect( job, SIGNAL( result( KJob * ) ),
1411 this, SLOT( slotStatResult( KJob * ) ) );
1417 KIO::TransferJob
*job
= qobject_cast
<KIO::TransferJob
*>(d
->m_job
);
1420 KIO::Scheduler::publishSlaveOnHold();
1424 Q_ASSERT(!d
->m_bFinished
);
1426 KMimeType::Ptr mime
= KMimeType::mimeType(type
, KMimeType::ResolveAliases
);
1428 kWarning(7010) << "Unknown mimetype " << type
;
1431 // Support for preferred service setting, see setPreferredService
1432 if (!d
->m_preferredService
.isEmpty()) {
1433 kDebug(7010) << "Attempting to open with preferred service: " << d
->m_preferredService
;
1434 KService::Ptr serv
= KService::serviceByDesktopName(d
->m_preferredService
);
1435 if (serv
&& serv
->hasMimeType(mime
.data())) {
1437 lst
.append(d
->m_strURL
);
1438 d
->m_bFinished
= KRun::run(*serv
, lst
, d
->m_window
, false, QString(), d
->m_asn
);
1439 /// Note: the line above means that if that service failed, we'll
1440 /// go to runUrl to maybe find another service, even though a dialog
1441 /// box was displayed. That's good if runUrl tries another service,
1442 /// but it's not good if it tries the same one :}
1446 // Resolve .desktop files from media:/, remote:/, applications:/ etc.
1447 if (mime
&& mime
->is("application/x-desktop") && !d
->m_localPath
.isEmpty()) {
1448 d
->m_strURL
= KUrl();
1449 d
->m_strURL
.setPath(d
->m_localPath
);
1452 if (!d
->m_bFinished
&& KRun::runUrl(d
->m_strURL
, type
, d
->m_window
, false /*tempfile*/, d
->m_runExecutables
, d
->m_suggestedFileName
, d
->m_asn
)) {
1453 d
->m_bFinished
= true;
1456 d
->m_bFinished
= true;
1463 void KRun::killJob()
1466 kDebug(7010) << this << "m_job=" << d
->m_job
;
1474 kDebug(7010) << this << "m_showingDialog=" << d
->m_showingDialog
;
1476 // If we're showing an error message box, the rest will be done
1477 // after closing the msgbox -> don't autodelete nor emit signals now.
1478 if (d
->m_showingDialog
) {
1482 d
->m_bFinished
= true;
1484 d
->m_bScanFile
= false;
1486 // will emit the error and autodelete this
1490 bool KRun::hasError() const
1495 bool KRun::hasFinished() const
1497 return d
->m_bFinished
;
1500 bool KRun::autoDelete() const
1502 return d
->m_bAutoDelete
;
1505 void KRun::setAutoDelete(bool b
)
1507 d
->m_bAutoDelete
= b
;
1510 void KRun::setEnableExternalBrowser(bool b
)
1513 d
->m_externalBrowser
= KConfigGroup(KGlobal::config(), "General").readEntry("BrowserApplication");
1516 d
->m_externalBrowser
.clear();
1520 void KRun::setPreferredService(const QString
& desktopEntryName
)
1522 d
->m_preferredService
= desktopEntryName
;
1525 void KRun::setRunExecutables(bool b
)
1527 d
->m_runExecutables
= b
;
1530 void KRun::setSuggestedFileName(const QString
& fileName
)
1532 d
->m_suggestedFileName
= fileName
;
1535 QString
KRun::suggestedFileName() const
1537 return d
->m_suggestedFileName
;
1540 bool KRun::isExecutable(const QString
& serviceType
)
1542 return (serviceType
== "application/x-desktop" ||
1543 serviceType
== "application/x-executable" ||
1544 serviceType
== "application/x-ms-dos-executable" ||
1545 serviceType
== "application/x-shellscript");
1548 void KRun::setUrl(const KUrl
&url
)
1553 KUrl
KRun::url() const
1558 void KRun::setError(bool error
)
1560 d
->m_bFault
= error
;
1563 void KRun::setProgressInfo(bool progressInfo
)
1565 d
->m_bProgressInfo
= progressInfo
;
1568 bool KRun::progressInfo() const
1570 return d
->m_bProgressInfo
;
1573 void KRun::setFinished(bool finished
)
1575 d
->m_bFinished
= finished
;
1576 // TODO d->startTimer(); (and later on remove it from callers...)
1579 void KRun::setJob(KIO::Job
*job
)
1584 KIO::Job
* KRun::job()
1589 QTimer
& KRun::timer()
1594 void KRun::setDoScanFile(bool scanFile
)
1596 d
->m_bScanFile
= scanFile
;
1599 bool KRun::doScanFile() const
1601 return d
->m_bScanFile
;
1604 void KRun::setIsDirecory(bool isDirectory
)
1606 d
->m_bIsDirectory
= isDirectory
;
1609 bool KRun::isDirectory() const
1611 return d
->m_bIsDirectory
;
1614 void KRun::setInitializeNextAction(bool initialize
)
1616 d
->m_bInit
= initialize
;
1619 bool KRun::initializeNextAction() const
1624 void KRun::setIsLocalFile(bool isLocalFile
)
1626 d
->m_bIsLocalFile
= isLocalFile
;
1629 bool KRun::isLocalFile() const
1631 return d
->m_bIsLocalFile
;
1634 void KRun::setMode(mode_t mode
)
1639 mode_t
KRun::mode() const
1647 int KProcessRunner::run(KProcess
* p
, const QString
& executable
)
1649 return (new KProcessRunner(p
, executable
))->pid();
1652 int KProcessRunner::run(KProcess
* p
, const QString
& executable
, const KStartupInfoId
& id
)
1654 return (new KProcessRunner(p
, executable
, id
))->pid();
1659 KProcessRunner::KProcessRunner(KProcess
* p
, const QString
& executable
)
1661 KProcessRunner::KProcessRunner(KProcess
* p
, const QString
& executable
, const KStartupInfoId
& _id
) :
1667 m_executable
= executable
;
1668 connect(process
, SIGNAL(finished(int, QProcess::ExitStatus
)),
1669 this, SLOT(slotProcessExited(int, QProcess::ExitStatus
)));
1672 if (!process
->waitForStarted()) {
1673 //kDebug() << "wait for started failed, exitCode=" << process->exitCode()
1674 // << "exitStatus=" << process->exitStatus();
1675 // Note that exitCode is 255 here (the first time), and 0 later on (bug?).
1676 slotProcessExited(255, process
->exitStatus());
1680 m_pid
= process
->pid();
1685 KProcessRunner::~KProcessRunner()
1690 int KProcessRunner::pid() const
1695 void KProcessRunner::terminateStartupNotification()
1699 KStartupInfoData data
;
1700 data
.addPid(m_pid
); // announce this pid for the startup notification has finished
1702 KStartupInfo::sendFinish(id
, data
);
1709 KProcessRunner::slotProcessExited(int exitCode
, QProcess::ExitStatus exitStatus
)
1711 kDebug(7010) << m_executable
<< "exitCode=" << exitCode
<< "exitStatus=" << exitStatus
;
1712 Q_UNUSED(exitStatus
);
1714 terminateStartupNotification(); // do this before the messagebox
1715 if (exitCode
!= 0 && !m_executable
.isEmpty()) {
1716 // Let's see if the error is because the exe doesn't exist.
1717 // When this happens, waitForStarted returns false, but not if kioexec
1718 // was involved, then we come here, that's why the code is here.
1720 // We'll try to find the executable relatively to current directory,
1721 // (or with a full path, if m_executable is absolute), and then in the PATH.
1722 if (!QFile(m_executable
).exists() && KStandardDirs::findExe(m_executable
).isEmpty()) {
1724 KMessageBox::sorry(0L, i18n("Could not find the program '%1'", m_executable
));
1728 kDebug() << process
->readAllStandardError();
1735 #include "krun_p.moc"