fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kio / kio / kdesktopfileactions.cpp
blobe1bd3b44bb3c7e679871a878b2ffd913a5940161
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.
18 **/
20 #include "kdesktopfileactions.h"
21 #include "krun.h"
22 #include "kautomount.h"
23 #include <kmessageboxwrapper.h>
24 #include <kdirnotify.h>
25 #include <kmountpoint.h>
27 #include <kglobal.h>
28 #include <kstandarddirs.h>
29 #include <kdesktopfile.h>
30 #include <kconfiggroup.h>
31 #include <klocale.h>
32 #include "kservice.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
43 // entry files
44 if ( !_is_local )
45 return false;
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);
53 return false;
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);
69 return false;
72 static bool runFSDevice( const KUrl& _url, const KDesktopFile &cfg )
74 bool retval = false;
76 QString dev = cfg.readDevice();
78 if ( dev.isEmpty() )
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);
82 return retval;
85 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev );
86 // Is the device already mounted ?
87 if (mp) {
88 KUrl mpURL(mp->mountPoint());
89 // Open a new window
90 retval = KRun::runUrl( mpURL, QLatin1String("inode/directory"), 0 /*TODO - window*/ );
91 } else {
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
96 fstype.clear();
97 QString point = cg.readEntry( "MountPoint" );
98 #ifndef Q_WS_WIN
99 (void) new KAutoMount( ro, fstype.toLatin1(), dev, point, _url.path() );
100 #endif
101 retval = false;
104 return retval;
107 static bool runApplication( const KUrl& , const QString & _serviceFile )
109 KService s( _serviceFile );
110 if ( !s.isValid() )
111 // The error message was already displayed, so we can just quit here
112 // ### KDE4: is this still the case?
113 return false;
115 KUrl::List lst;
116 return KRun::run( s, lst, 0 /*TODO - window*/ );
119 static bool runLink( const KUrl& _url, const KDesktopFile &cfg )
121 QString u = cfg.readUrl();
122 if ( u.isEmpty() )
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 );
126 return false;
129 KUrl url ( u );
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 );
139 return false;
142 QList<KServiceAction> KDesktopFileActions::builtinServices( const KUrl& _url )
144 QList<KServiceAction> result;
146 if ( !_url.isLocalFile() )
147 return result;
149 KDesktopFile cfg( _url.toLocalFile() );
150 QString type = cfg.readType();
152 if ( type.isEmpty() )
153 return result;
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);
160 } else {
161 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev );
162 // not mounted ?
163 if ( !mp ) {
164 KServiceAction mount("mount", i18n("Mount"), QString(), QString(), false);
165 mount.setData(QVariant(ST_MOUNT));
166 result.append(mount);
167 } else {
168 QString text;
169 #ifdef HAVE_VOLMGT
171 * Solaris' volume management can only umount+eject
173 text = i18n("Eject");
174 #else
175 text = i18n("Unmount");
176 #endif
177 KServiceAction unmount("unmount", text, QString(), QString(), false);
178 unmount.setData(QVariant(ST_UNMOUNT));
179 result.append(unmount);
184 return result;
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
205 return result;
207 QStringList keys;
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
222 if (keys.isEmpty())
223 return result;
224 } else {
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 );
242 return result;
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 );
260 return;
262 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev );
264 if ( actionData == ST_MOUNT ) {
265 // Already mounted? Strange, but who knows ...
266 if ( mp ) {
267 kDebug(7000) << "ALREADY Mounted";
268 return;
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
275 fstype.clear();
276 QString point = group.readEntry( "MountPoint" );
277 #ifndef Q_WS_WIN
278 (void)new KAutoMount( ro, fstype.toLatin1(), dev, point, path, false );
279 #endif
280 } else if ( actionData == ST_UNMOUNT ) {
281 // Not mounted? Strange, but who knows ...
282 if ( !mp )
283 return;
285 #ifndef Q_WS_WIN
286 (void)new KAutoUnmount( mp->mountPoint(), path );
287 #endif
289 } else {
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() );