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