KIO
kdesktopfileactions.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 * Copyright (C) 1999 Waldo Bastian <bastian@kde.org> 00003 * David Faure <faure@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License version 2 as published by the Free Software Foundation; 00008 * 00009 * This library is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 * Library General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU Library General Public License 00015 * along with this library; see the file COPYING.LIB. If not, write to 00016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 * Boston, MA 02110-1301, USA. 00018 **/ 00019 00020 #include "kdesktopfileactions.h" 00021 00022 #include "config-kio.h" 00023 00024 #include "krun.h" 00025 #include "kautomount.h" 00026 #include <kmessageboxwrapper.h> 00027 #include <kdirnotify.h> 00028 #include <kmountpoint.h> 00029 00030 #include <kglobal.h> 00031 #include <kstandarddirs.h> 00032 #include <kdesktopfile.h> 00033 #include <kconfiggroup.h> 00034 #include <klocale.h> 00035 #include "kservice.h" 00036 00037 #ifndef KIO_NO_SOLID 00038 //Solid 00039 #include <solid/devicenotifier.h> 00040 #include <solid/device.h> 00041 #include <solid/deviceinterface.h> 00042 #include <solid/predicate.h> 00043 #include <solid/storageaccess.h> 00044 #include <solid/opticaldrive.h> 00045 #include <solid/opticaldisc.h> 00046 #include <solid/block.h> 00047 #endif 00048 00049 enum BuiltinServiceType { ST_MOUNT = 0x0E1B05B0, ST_UNMOUNT = 0x0E1B05B1 }; // random numbers 00050 00051 static bool runFSDevice( const KUrl& _url, const KDesktopFile &cfg ); 00052 static bool runApplication( const KUrl& _url, const QString & _serviceFile ); 00053 static bool runLink( const KUrl& _url, const KDesktopFile &cfg ); 00054 00055 bool KDesktopFileActions::run( const KUrl& u, bool _is_local ) 00056 { 00057 // It might be a security problem to run external untrusted desktop 00058 // entry files 00059 if ( !_is_local ) 00060 return false; 00061 00062 KDesktopFile cfg( u.path() ); 00063 if ( !cfg.desktopGroup().hasKey("Type") ) 00064 { 00065 QString tmp = i18n("The desktop entry file %1 " 00066 "has no Type=... entry.", u.path() ); 00067 KMessageBoxWrapper::error( 0, tmp); 00068 return false; 00069 } 00070 00071 //kDebug(7000) << "TYPE = " << type.data(); 00072 00073 if ( cfg.hasDeviceType() ) 00074 return runFSDevice( u, cfg ); 00075 else if ( cfg.hasApplicationType() 00076 || (cfg.readType() == "Service" && !cfg.desktopGroup().readEntry("Exec").isEmpty())) // for kio_settings 00077 return runApplication( u, u.toLocalFile() ); 00078 else if ( cfg.hasLinkType() ) 00079 return runLink( u, cfg ); 00080 00081 QString tmp = i18n("The desktop entry of type\n%1\nis unknown.", cfg.readType() ); 00082 KMessageBoxWrapper::error( 0, tmp); 00083 00084 return false; 00085 } 00086 00087 static bool runFSDevice( const KUrl& _url, const KDesktopFile &cfg ) 00088 { 00089 bool retval = false; 00090 00091 QString dev = cfg.readDevice(); 00092 00093 if ( dev.isEmpty() ) 00094 { 00095 QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.", _url.path() ); 00096 KMessageBoxWrapper::error( 0, tmp); 00097 return retval; 00098 } 00099 00100 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev ); 00101 // Is the device already mounted ? 00102 if (mp) { 00103 KUrl mpURL(mp->mountPoint()); 00104 // Open a new window 00105 retval = KRun::runUrl( mpURL, QLatin1String("inode/directory"), 0 /*TODO - window*/ ); 00106 } else { 00107 KConfigGroup cg = cfg.desktopGroup(); 00108 bool ro = cg.readEntry("ReadOnly", false); 00109 QString fstype = cg.readEntry( "FSType" ); 00110 if ( fstype == "Default" ) // KDE-1 thing 00111 fstype.clear(); 00112 QString point = cg.readEntry( "MountPoint" ); 00113 #ifndef Q_WS_WIN 00114 (void) new KAutoMount( ro, fstype.toLatin1(), dev, point, _url.path() ); 00115 #endif 00116 retval = false; 00117 } 00118 00119 return retval; 00120 } 00121 00122 static bool runApplication( const KUrl& , const QString & _serviceFile ) 00123 { 00124 KService s( _serviceFile ); 00125 if ( !s.isValid() ) 00126 // The error message was already displayed, so we can just quit here 00127 // ### KDE4: is this still the case? 00128 return false; 00129 00130 KUrl::List lst; 00131 return KRun::run( s, lst, 0 /*TODO - window*/ ); 00132 } 00133 00134 static bool runLink( const KUrl& _url, const KDesktopFile &cfg ) 00135 { 00136 QString u = cfg.readUrl(); 00137 if ( u.isEmpty() ) 00138 { 00139 QString tmp = i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.", _url.prettyUrl() ); 00140 KMessageBoxWrapper::error( 0, tmp ); 00141 return false; 00142 } 00143 00144 KUrl url ( u ); 00145 KRun* run = new KRun(url,(QWidget*)0); 00146 00147 // X-KDE-LastOpenedWith holds the service desktop entry name that 00148 // was should be preferred for opening this URL if possible. 00149 // This is used by the Recent Documents menu for instance. 00150 QString lastOpenedWidth = cfg.desktopGroup().readEntry( "X-KDE-LastOpenedWith" ); 00151 if ( !lastOpenedWidth.isEmpty() ) 00152 run->setPreferredService( lastOpenedWidth ); 00153 00154 return false; 00155 } 00156 00157 QList<KServiceAction> KDesktopFileActions::builtinServices( const KUrl& _url ) 00158 { 00159 QList<KServiceAction> result; 00160 00161 if ( !_url.isLocalFile() ) 00162 return result; 00163 00164 bool offerMount = false; 00165 bool offerUnmount = false; 00166 00167 KDesktopFile cfg( _url.toLocalFile() ); 00168 if ( cfg.hasDeviceType() ) { // url to desktop file 00169 const QString dev = cfg.readDevice(); 00170 if ( dev.isEmpty() ) { 00171 QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.", _url.toLocalFile() ); 00172 KMessageBoxWrapper::error(0, tmp); 00173 return result; 00174 } 00175 00176 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev ); 00177 if (mp) { 00178 offerUnmount = true; 00179 } 00180 else { 00181 offerMount = true; 00182 } 00183 } 00184 #ifndef KIO_NO_SOLID 00185 else { // url to device 00186 Solid::Predicate predicate(Solid::DeviceInterface::Block, "device", _url.toLocalFile()); 00187 const QList<Solid::Device> devList = Solid::Device::listFromQuery(predicate, QString()); 00188 if (devList.empty()) { 00189 kDebug(7000) << "Device" << _url.toLocalFile() << "not found"; 00190 return result; 00191 } 00192 Solid::Device device = devList[0]; 00193 Solid::StorageAccess *access = device.as<Solid::StorageAccess>(); 00194 Solid::StorageDrive *drive = device.parent().as<Solid::StorageDrive>(); 00195 bool mounted = access && access->isAccessible(); 00196 00197 if ((mounted || device.is<Solid::OpticalDisc>()) && drive && drive->isRemovable()) { 00198 offerUnmount = true; 00199 } 00200 00201 if (!mounted && ((drive && drive->isHotpluggable()) || device.is<Solid::OpticalDisc>())) { 00202 offerMount = true; 00203 } 00204 } 00205 #endif 00206 00207 if (offerMount) { 00208 KServiceAction mount("mount", i18n("Mount"), QString(), QString(), false); 00209 mount.setData(QVariant(ST_MOUNT)); 00210 result.append(mount); 00211 } 00212 00213 if (offerUnmount) { 00214 QString text; 00215 #ifdef HAVE_VOLMGT 00216 /* 00217 * Solaris' volume management can only umount+eject 00218 */ 00219 text = i18n("Eject"); 00220 #else 00221 text = i18n("Unmount"); 00222 #endif 00223 KServiceAction unmount("unmount", text, QString(), QString(), false); 00224 unmount.setData(QVariant(ST_UNMOUNT)); 00225 result.append(unmount); 00226 } 00227 00228 return result; 00229 } 00230 00231 QList<KServiceAction> KDesktopFileActions::userDefinedServices( const QString& path, bool bLocalFiles ) 00232 { 00233 KDesktopFile cfg( path ); 00234 return userDefinedServices( path, cfg, bLocalFiles ); 00235 } 00236 00237 QList<KServiceAction> KDesktopFileActions::userDefinedServices( const QString& path, const KDesktopFile& cfg, bool bLocalFiles, const KUrl::List & file_list ) 00238 { 00239 Q_UNUSED(path); // this was just for debugging; we use service.entryPath() now. 00240 KService service(&cfg); 00241 return userDefinedServices(service, bLocalFiles, file_list); 00242 } 00243 00244 QList<KServiceAction> KDesktopFileActions::userDefinedServices( const KService& service, bool bLocalFiles, const KUrl::List & file_list ) 00245 { 00246 QList<KServiceAction> result; 00247 00248 if (!service.isValid()) // e.g. TryExec failed 00249 return result; 00250 00251 QStringList keys; 00252 const QString actionMenu = service.property("X-KDE-GetActionMenu", QVariant::String).toString(); 00253 if (!actionMenu.isEmpty()) { 00254 const QStringList dbuscall = actionMenu.split(QChar(' ')); 00255 if (dbuscall.count() >= 4) { 00256 const QString& app = dbuscall.at( 0 ); 00257 const QString& object = dbuscall.at( 1 ); 00258 const QString& interface = dbuscall.at( 2 ); 00259 const QString& function = dbuscall.at( 3 ); 00260 00261 QDBusInterface remote( app, object, interface ); 00262 // Do NOT use QDBus::BlockWithGui here. It runs a nested event loop, 00263 // in which timers can fire, leading to crashes like #149736. 00264 QDBusReply<QStringList> reply = remote.call(function, file_list.toStringList()); 00265 keys = reply; // ensures that the reply was a QStringList 00266 if (keys.isEmpty()) 00267 return result; 00268 } else { 00269 kWarning(7000) << "The desktop file" << service.entryPath() 00270 << "has an invalid X-KDE-GetActionMenu entry." 00271 << "Syntax is: app object interface function"; 00272 } 00273 } 00274 00275 // Now, either keys is empty (all actions) or it's set to the actions we want 00276 00277 foreach(const KServiceAction& action, service.actions()) { 00278 if (keys.isEmpty() || keys.contains(action.name())) { 00279 const QString exec = action.exec(); 00280 if (bLocalFiles || exec.contains("%U") || exec.contains("%u")) { 00281 result.append( action ); 00282 } 00283 } 00284 } 00285 00286 return result; 00287 } 00288 00289 void KDesktopFileActions::executeService( const KUrl::List& urls, const KServiceAction& action ) 00290 { 00291 //kDebug(7000) << "EXECUTING Service " << action.name(); 00292 00293 int actionData = action.data().toInt(); 00294 if ( actionData == ST_MOUNT || actionData == ST_UNMOUNT ) { 00295 Q_ASSERT( urls.count() == 1 ); 00296 const QString path = urls.first().toLocalFile(); 00297 //kDebug(7000) << "MOUNT&UNMOUNT"; 00298 00299 KDesktopFile cfg( path ); 00300 if (cfg.hasDeviceType()) { // path to desktop file 00301 const QString dev = cfg.readDevice(); 00302 if ( dev.isEmpty() ) { 00303 QString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.", path ); 00304 KMessageBoxWrapper::error( 0, tmp ); 00305 return; 00306 } 00307 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev ); 00308 00309 if ( actionData == ST_MOUNT ) { 00310 // Already mounted? Strange, but who knows ... 00311 if ( mp ) { 00312 kDebug(7000) << "ALREADY Mounted"; 00313 return; 00314 } 00315 00316 const KConfigGroup group = cfg.desktopGroup(); 00317 bool ro = group.readEntry("ReadOnly", false); 00318 QString fstype = group.readEntry( "FSType" ); 00319 if ( fstype == "Default" ) // KDE-1 thing 00320 fstype.clear(); 00321 QString point = group.readEntry( "MountPoint" ); 00322 #ifndef Q_WS_WIN 00323 (void)new KAutoMount( ro, fstype.toLatin1(), dev, point, path, false ); 00324 #endif 00325 } else if ( actionData == ST_UNMOUNT ) { 00326 // Not mounted? Strange, but who knows ... 00327 if ( !mp ) 00328 return; 00329 00330 #ifndef Q_WS_WIN 00331 (void)new KAutoUnmount( mp->mountPoint(), path ); 00332 #endif 00333 } 00334 } 00335 #ifndef KIO_NO_SOLID 00336 else { // path to device 00337 Solid::Predicate predicate(Solid::DeviceInterface::Block, "device", path); 00338 const QList<Solid::Device> devList = Solid::Device::listFromQuery(predicate, QString()); 00339 if (!devList.empty()) { 00340 Solid::Device device = devList[0]; 00341 if ( actionData == ST_MOUNT ) { 00342 if (device.is<Solid::StorageVolume>()) { 00343 Solid::StorageAccess *access = device.as<Solid::StorageAccess>(); 00344 if (access) { 00345 access->setup(); 00346 } 00347 } 00348 } else if ( actionData == ST_UNMOUNT ) { 00349 if (device.is<Solid::OpticalDisc>()) { 00350 Solid::OpticalDrive *drive = device.parent().as<Solid::OpticalDrive>(); 00351 if (drive != 0) { 00352 drive->eject(); 00353 } 00354 } else if (device.is<Solid::StorageVolume>()) { 00355 Solid::StorageAccess *access = device.as<Solid::StorageAccess>(); 00356 if (access && access->isAccessible()) { 00357 access->teardown(); 00358 } 00359 } 00360 } 00361 } 00362 else { 00363 kDebug(7000) << "Device" << path << "not found"; 00364 } 00365 } 00366 #endif 00367 } else { 00368 kDebug() << action.name() << "first url's path=" << urls.first().path() << "exec=" << action.exec(); 00369 KRun::run( action.exec(), urls, 0, action.text(), action.icon()); 00370 // The action may update the desktop file. Example: eject unmounts (#5129). 00371 org::kde::KDirNotify::emitFilesChanged( urls.toStringList() ); 00372 } 00373 } 00374
KDE 4.6 API Reference