1 /* This file is part of the KDE libraries
2 * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
3 * David Faure <faure@kde.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License version 2 as published by the Free Software Foundation;
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #include "kdesktopfileactions.h"
22 #include "kautomount.h"
23 #include <kmessageboxwrapper.h>
24 #include <kdirnotify.h>
25 #include <kmountpoint.h>
28 #include <kstandarddirs.h>
29 #include <kdesktopfile.h>
30 #include <kconfiggroup.h>
34 enum BuiltinServiceType
{ ST_MOUNT
= 0x0E1B05B0, ST_UNMOUNT
= 0x0E1B05B1 }; // random numbers
36 static bool runFSDevice( const KUrl
& _url
, const KDesktopFile
&cfg
);
37 static bool runApplication( const KUrl
& _url
, const QString
& _serviceFile
);
38 static bool runLink( const KUrl
& _url
, const KDesktopFile
&cfg
);
40 bool KDesktopFileActions::run( const KUrl
& u
, bool _is_local
)
42 // It might be a security problem to run external untrusted desktop
47 KDesktopFile
cfg( u
.path() );
48 if ( !cfg
.desktopGroup().hasKey("Type") )
50 QString tmp
= i18n("The desktop entry file %1 "
51 "has no Type=... entry.", u
.path() );
52 KMessageBoxWrapper::error( 0, tmp
);
56 //kDebug(7000) << "TYPE = " << type.data();
58 if ( cfg
.hasDeviceType() )
59 return runFSDevice( u
, cfg
);
60 else if ( cfg
.hasApplicationType()
61 || (cfg
.readType() == "Service" && !cfg
.desktopGroup().readEntry("Exec").isEmpty())) // for kio_settings
62 return runApplication( u
, u
.toLocalFile() );
63 else if ( cfg
.hasLinkType() )
64 return runLink( u
, cfg
);
66 QString tmp
= i18n("The desktop entry of type\n%1\nis unknown.", cfg
.readType() );
67 KMessageBoxWrapper::error( 0, tmp
);
72 static bool runFSDevice( const KUrl
& _url
, const KDesktopFile
&cfg
)
76 QString dev
= cfg
.readDevice();
80 QString tmp
= i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.", _url
.path() );
81 KMessageBoxWrapper::error( 0, tmp
);
85 KMountPoint::Ptr mp
= KMountPoint::currentMountPoints().findByDevice( dev
);
86 // Is the device already mounted ?
88 KUrl
mpURL(mp
->mountPoint());
90 retval
= KRun::runUrl( mpURL
, QLatin1String("inode/directory"), 0 /*TODO - window*/ );
92 KConfigGroup cg
= cfg
.desktopGroup();
93 bool ro
= cg
.readEntry("ReadOnly", false);
94 QString fstype
= cg
.readEntry( "FSType" );
95 if ( fstype
== "Default" ) // KDE-1 thing
97 QString point
= cg
.readEntry( "MountPoint" );
99 (void) new KAutoMount( ro
, fstype
.toLatin1(), dev
, point
, _url
.path() );
107 static bool runApplication( const KUrl
& , const QString
& _serviceFile
)
109 KService
s( _serviceFile
);
111 // The error message was already displayed, so we can just quit here
112 // ### KDE4: is this still the case?
116 return KRun::run( s
, lst
, 0 /*TODO - window*/ );
119 static bool runLink( const KUrl
& _url
, const KDesktopFile
&cfg
)
121 QString u
= cfg
.readUrl();
124 QString tmp
= i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.", _url
.prettyUrl() );
125 KMessageBoxWrapper::error( 0, tmp
);
130 KRun
* run
= new KRun(url
,(QWidget
*)0);
132 // X-KDE-LastOpenedWith holds the service desktop entry name that
133 // was should be preferred for opening this URL if possible.
134 // This is used by the Recent Documents menu for instance.
135 QString lastOpenedWidth
= cfg
.desktopGroup().readEntry( "X-KDE-LastOpenedWith" );
136 if ( !lastOpenedWidth
.isEmpty() )
137 run
->setPreferredService( lastOpenedWidth
);
142 QList
<KServiceAction
> KDesktopFileActions::builtinServices( const KUrl
& _url
)
144 QList
<KServiceAction
> result
;
146 if ( !_url
.isLocalFile() )
149 KDesktopFile
cfg( _url
.toLocalFile() );
150 QString type
= cfg
.readType();
152 if ( type
.isEmpty() )
155 if ( cfg
.hasDeviceType() ) {
156 const QString dev
= cfg
.readDevice();
157 if ( dev
.isEmpty() ) {
158 QString tmp
= i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.", _url
.toLocalFile() );
159 KMessageBoxWrapper::error(0, tmp
);
161 KMountPoint::Ptr mp
= KMountPoint::currentMountPoints().findByDevice( dev
);
164 KServiceAction
mount("mount", i18n("Mount"), QString(), QString(), false);
165 mount
.setData(QVariant(ST_MOUNT
));
166 result
.append(mount
);
171 * Solaris' volume management can only umount+eject
173 text
= i18n("Eject");
175 text
= i18n("Unmount");
177 KServiceAction
unmount("unmount", text
, QString(), QString(), false);
178 unmount
.setData(QVariant(ST_UNMOUNT
));
179 result
.append(unmount
);
187 QList
<KServiceAction
> KDesktopFileActions::userDefinedServices( const QString
& path
, bool bLocalFiles
)
189 KDesktopFile
cfg( path
);
190 return userDefinedServices( path
, cfg
, bLocalFiles
);
193 QList
<KServiceAction
> KDesktopFileActions::userDefinedServices( const QString
& path
, const KDesktopFile
& cfg
, bool bLocalFiles
, const KUrl::List
& file_list
)
195 Q_UNUSED(path
); // this was just for debugging; we use service.entryPath() now.
196 KService
service(&cfg
);
197 return userDefinedServices(service
, bLocalFiles
, file_list
);
200 QList
<KServiceAction
> KDesktopFileActions::userDefinedServices( const KService
& service
, bool bLocalFiles
, const KUrl::List
& file_list
)
202 QList
<KServiceAction
> result
;
204 if (!service
.isValid()) // e.g. TryExec failed
208 const QString actionMenu
= service
.property("X-KDE-GetActionMenu", QVariant::String
).toString();
209 if (!actionMenu
.isEmpty()) {
210 const QStringList dbuscall
= actionMenu
.split(QChar(' '));
211 if (dbuscall
.count() >= 4) {
212 const QString
& app
= dbuscall
.at( 0 );
213 const QString
& object
= dbuscall
.at( 1 );
214 const QString
& interface
= dbuscall
.at( 2 );
215 const QString
& function
= dbuscall
.at( 3 );
217 QDBusInterface
remote( app
, object
, interface
);
218 // Do NOT use QDBus::BlockWithGui here. It runs a nested event loop,
219 // in which timers can fire, leading to crashes like #149736.
220 QDBusReply
<QStringList
> reply
= remote
.call(function
, file_list
.toStringList());
221 keys
= reply
; // ensures that the reply was a QStringList
225 kWarning(7000) << "The desktop file" << service
.entryPath()
226 << "has an invalid X-KDE-GetActionMenu entry."
227 << "Syntax is: app object interface function";
231 // Now, either keys is empty (all actions) or it's set to the actions we want
233 foreach(const KServiceAction
& action
, service
.actions()) {
234 if (keys
.isEmpty() || keys
.contains(action
.name())) {
235 const QString exec
= action
.exec();
236 if (bLocalFiles
|| exec
.contains("%U") || exec
.contains("%u")) {
237 result
.append( action
);
245 void KDesktopFileActions::executeService( const KUrl::List
& urls
, const KServiceAction
& action
)
247 //kDebug(7000) << "EXECUTING Service " << action.name();
249 int actionData
= action
.data().toInt();
250 if ( actionData
== ST_MOUNT
|| actionData
== ST_UNMOUNT
) {
251 Q_ASSERT( urls
.count() == 1 );
252 const QString path
= urls
.first().toLocalFile();
253 //kDebug(7000) << "MOUNT&UNMOUNT";
255 KDesktopFile
cfg( path
);
256 const QString dev
= cfg
.readDevice();
257 if ( dev
.isEmpty() ) {
258 QString tmp
= i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.", path
);
259 KMessageBoxWrapper::error( 0, tmp
);
262 KMountPoint::Ptr mp
= KMountPoint::currentMountPoints().findByDevice( dev
);
264 if ( actionData
== ST_MOUNT
) {
265 // Already mounted? Strange, but who knows ...
267 kDebug(7000) << "ALREADY Mounted";
271 const KConfigGroup group
= cfg
.desktopGroup();
272 bool ro
= group
.readEntry("ReadOnly", false);
273 QString fstype
= group
.readEntry( "FSType" );
274 if ( fstype
== "Default" ) // KDE-1 thing
276 QString point
= group
.readEntry( "MountPoint" );
278 (void)new KAutoMount( ro
, fstype
.toLatin1(), dev
, point
, path
, false );
280 } else if ( actionData
== ST_UNMOUNT
) {
281 // Not mounted? Strange, but who knows ...
286 (void)new KAutoUnmount( mp
->mountPoint(), path
);
290 kDebug() << action
.name() << "first url's path=" << urls
.first().path() << "exec=" << action
.exec();
291 KRun::run( action
.exec(), urls
, 0, action
.text(), action
.icon(), "0" /*no startup notification for actions*/ );
292 // The action may update the desktop file. Example: eject unmounts (#5129).
293 org::kde::KDirNotify::emitFilesChanged( urls
.toStringList() );