kio Library API Documentation

kservice.cpp

00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999 - 2001 Waldo Bastian <bastian@kde.org>
00003  *  Copyright (C) 1999        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., 59 Temple Place - Suite 330,
00017  *  Boston, MA 02111-1307, USA.
00018  **/
00019 
00020 // $Id: kservice.cpp,v 1.125.2.2 2003/07/09 16:11:28 waba Exp $
00021 
00022 #include <config.h>
00023 
00024 #include "kservice.h"
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 
00028 #include <stddef.h>
00029 #include <unistd.h>
00030 #include <stdlib.h>
00031 
00032 #include <qstring.h>
00033 #include <qfile.h>
00034 #include <qtl.h>
00035 
00036 #include <ksimpleconfig.h>
00037 #include <kapplication.h>
00038 #include <kdebug.h>
00039 #include <kdesktopfile.h>
00040 #include <kglobal.h>
00041 #include <kiconloader.h>
00042 #include <klocale.h>
00043 #include <kconfigbase.h>
00044 #include <dcopclient.h>
00045 
00046 #include "kservicefactory.h"
00047 #include "kservicetypefactory.h"
00048 #include "kservicetype.h"
00049 #include "kuserprofile.h"
00050 #include "ksycoca.h"
00051 
00052 class KService::KServicePrivate
00053 {
00054 public:
00055   QStringList categories;
00056 };
00057 
00058 KService::KService( const QString & _name, const QString &_exec, const QString &_icon)
00059  : KSycocaEntry( QString::null)
00060 {
00061   d = new KServicePrivate;
00062   m_bValid = true;
00063   m_bDeleted = false;
00064   m_strType = "Application";
00065   m_strName = _name;
00066   m_strExec = _exec;
00067   m_strIcon = _icon;
00068   m_bTerminal = false;
00069   m_bAllowAsDefault = true;
00070   m_initialPreference = 10;
00071 }
00072 
00073 
00074 KService::KService( const QString & _fullpath )
00075  : KSycocaEntry( _fullpath)
00076 {
00077   KDesktopFile config( _fullpath );
00078 
00079   init(&config);
00080 }
00081 
00082 KService::KService( KDesktopFile *config )
00083  : KSycocaEntry( config->filename())
00084 {
00085   init(config);
00086 }
00087 
00088 void
00089 KService::init( KDesktopFile *config )
00090 {
00091   d = new KServicePrivate;
00092   m_bValid = true;
00093 
00094   bool absPath = (entryPath()[0] == '/');
00095 
00096   config->setDesktopGroup();
00097   config->setDollarExpansion( true ); // mainly for Exec and Path
00098   if(absPath && access(QFile::encodeName(entryPath()), R_OK))
00099   {
00100     m_bValid = false;
00101     return;
00102   }
00103   QMap<QString, QString> entryMap = config->entryMap(config->group());
00104 
00105   entryMap.remove("Encoding"); // reserved as part of Desktop Entry Standard
00106   entryMap.remove("Version");  // reserved as part of Desktop Entry Standard
00107 
00108   m_bDeleted = config->readBoolEntry( "Hidden", false );
00109   entryMap.remove("Hidden");
00110   if (m_bDeleted)
00111   {
00112     m_bValid = false;
00113     return;
00114   }
00115   m_strType = config->readEntry( "Type" );
00116   entryMap.remove("Type");
00117   if ( m_strType.isEmpty() )
00118   {
00119     /*kdWarning(7012) << "The desktop entry file " << entryPath()
00120                     << " has no Type=... entry."
00121                     << " It should be \"Application\" or \"Service\"" << endl;
00122     m_bValid = false;
00123     return;*/
00124     m_strType = "Application";
00125   } else if ( m_strType != "Application" && m_strType != "Service" )
00126   {
00127     kdWarning(7012) << "The desktop entry file " << entryPath()
00128                     << " has Type=" << m_strType
00129                     << " instead of \"Application\" or \"Service\"" << endl;
00130     m_bValid = false;
00131     return;
00132   }
00133 
00134   // In case Try Exec is set, check if the application is available
00135   if (!config->tryExec()) {
00136       m_bDeleted = true;
00137       m_bValid = false;
00138       return;
00139   }
00140 
00141   QString resource = config->resource();
00142 
00143   if ( (m_strType == "Application") &&
00144        (!resource.isEmpty()) &&
00145        (resource != "apps") &&
00146        !absPath)
00147   {
00148     kdWarning(7012) << "The desktop entry file " << entryPath()
00149            << " has Type=" << m_strType << " but is located under \"" << resource
00150            << "\" instead of \"apps\"" << endl;
00151     m_bValid = false;
00152     return;
00153   }
00154 
00155   if ( (m_strType == "Service") &&
00156        (!resource.isEmpty()) &&
00157        (resource != "services") &&
00158        !absPath)
00159   {
00160     kdWarning(7012) << "The desktop entry file " << entryPath()
00161            << " has Type=" << m_strType << " but is located under \"" << resource
00162            << "\" instead of \"services\"" << endl;
00163     m_bValid = false;
00164     return;
00165   }
00166 
00167   QString name = entryPath();
00168   int pos = name.findRev('/');
00169   if (pos != -1)
00170      name = name.mid(pos+1);
00171   pos = name.find('.');
00172   if (pos != -1)
00173      name = name.left(pos);
00174 
00175   m_strExec = config->readPathEntry( "Exec" );
00176   entryMap.remove("Exec");
00177   m_strName = config->readEntry( "Name" );
00178   //kdDebug() << "parsing " << entryPath() << " Name=" << m_strName << endl;
00179   entryMap.remove("Name");
00180   if ( m_strName.isEmpty() )
00181   {
00182     m_bValid = false;
00183     return;
00184   }
00185 
00186   m_strIcon = config->readEntry( "Icon", "unknown" );
00187   entryMap.remove("Icon");
00188   m_bTerminal = (config->readBoolEntry( "Terminal" )); // should be a property IMHO
00189   entryMap.remove("Terminal");
00190   m_strTerminalOptions = config->readEntry( "TerminalOptions" ); // should be a property IMHO
00191   entryMap.remove("TerminalOptions");
00192   m_strPath = config->readPathEntry( "Path" );
00193   entryMap.remove("Path");
00194   m_strComment = config->readEntry( "Comment" );
00195   entryMap.remove("Comment");
00196   m_strGenName = config->readEntry( "GenericName" );
00197   entryMap.remove("GenericName");
00198   m_lstKeywords = config->readListEntry("Keywords");
00199   entryMap.remove("Keywords");
00200   d->categories = config->readListEntry("Categories", ';');
00201   entryMap.remove("Categories");
00202   m_strLibrary = config->readEntry( "X-KDE-Library" );
00203   entryMap.remove("X-KDE-Library");
00204   m_strInit = config->readEntry("X-KDE-Init" );
00205   entryMap.remove("X-KDE-Init");
00206 
00207   m_lstServiceTypes = config->readListEntry( "ServiceTypes" );
00208   entryMap.remove("ServiceTypes");
00209   // For compatibility with KDE 1.x
00210   m_lstServiceTypes += config->readListEntry( "MimeType", ';' );
00211   entryMap.remove("MimeType");
00212 
00213   if ( m_strType == "Application" && !m_lstServiceTypes.contains("Application") )
00214     // Applications implement the service type "Application" ;-)
00215     m_lstServiceTypes += "Application";
00216 
00217   QString dcopServiceType = config->readEntry("X-DCOP-ServiceType").lower();
00218   entryMap.remove("X-DCOP-ServiceType");
00219   if (dcopServiceType == "unique")
00220      m_DCOPServiceType = DCOP_Unique;
00221   else if (dcopServiceType == "multi")
00222      m_DCOPServiceType = DCOP_Multi;
00223   else if (dcopServiceType == "wait")
00224      m_DCOPServiceType = DCOP_Wait;
00225   else
00226      m_DCOPServiceType = DCOP_None;
00227 
00228   m_strDesktopEntryName = name.lower();
00229 
00230   m_bAllowAsDefault = config->readBoolEntry( "AllowDefault", true );
00231   entryMap.remove("AllowDefault");
00232 
00233   m_initialPreference = config->readNumEntry( "InitialPreference", 1 );
00234   entryMap.remove("InitialPreference");
00235 
00236   // Store all additional entries in the property map.
00237   // A QMap<QString,QString> would be easier for this but we can't
00238   // brake BC, so we have to store it in m_mapProps.
00239 //  qWarning("Path = %s", entryPath().latin1());
00240   QMap<QString,QString>::ConstIterator it = entryMap.begin();
00241   for( ; it != entryMap.end();++it)
00242   {
00243 //     qWarning("   Key = %s Data = %s", it.key().latin1(), it.data().latin1());
00244      m_mapProps.insert( it.key(), QVariant( it.data()));
00245   }
00246 }
00247 
00248 KService::KService( QDataStream& _str, int offset ) : KSycocaEntry( _str, offset )
00249 {
00250   d = new KServicePrivate;
00251   load( _str );
00252 }
00253 
00254 KService::~KService()
00255 {
00256   //debug("KService::~KService()");
00257   delete d;
00258 }
00259 
00260 QPixmap KService::pixmap( KIcon::Group _group, int _force_size, int _state, QString * _path ) const
00261 {
00262   KIconLoader *iconLoader=KGlobal::iconLoader();
00263   if (!iconLoader->extraDesktopThemesAdded())
00264   {
00265       QPixmap pixmap=iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path, true );
00266       if (!pixmap.isNull() ) return pixmap;
00267 
00268       iconLoader->addExtraDesktopThemes();
00269   }
00270   
00271   return iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path );
00272 }
00273 
00274 void KService::load( QDataStream& s )
00275 {
00276   // dummies are here because of fields that were removed, to keep bin compat.
00277   // Feel free to re-use, but fields for Applications only (not generic services)
00278   // should rather be added to application.desktop
00279   Q_INT8 def, term, dummy1, dummy2;
00280   Q_INT8 dst, initpref;
00281   QString dummyStr1, dummyStr2;
00282   int dummyI1, dummyI2;
00283   Q_UINT32 dummyUI32;
00284 
00285   // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x!
00286   // !! This data structure should remain binary compatible at all times !!
00287   // You may add new fields at the end. Make sure to update the version
00288   // number in ksycoca.h
00289   s >> m_strType >> m_strName >> m_strExec >> m_strIcon
00290     >> term >> m_strTerminalOptions
00291     >> m_strPath >> m_strComment >> m_lstServiceTypes >> def >> m_mapProps
00292     >> m_strLibrary >> dummyI1 >> dummyI2
00293     >> dst
00294     >> m_strDesktopEntryName
00295     >> dummy1 >> dummyStr1 >> initpref >> dummyStr2 >> dummy2
00296     >> m_lstKeywords >> m_strInit >> dummyUI32 >> m_strGenName 
00297     >> d->categories;
00298 
00299   m_bAllowAsDefault = def;
00300   m_bTerminal = term;
00301   m_DCOPServiceType = (DCOPServiceType_t) dst;
00302   m_initialPreference = initpref;
00303 
00304   m_bValid = true;
00305 }
00306 
00307 void KService::save( QDataStream& s )
00308 {
00309   KSycocaEntry::save( s );
00310   Q_INT8 def = m_bAllowAsDefault, initpref = m_initialPreference;
00311   Q_INT8 term = m_bTerminal;
00312   Q_INT8 dst = (Q_INT8) m_DCOPServiceType;
00313   Q_INT8 dummy1 = 0, dummy2 = 0; // see ::load
00314   QString dummyStr1, dummyStr2;
00315   int dummyI1 = 0, dummyI2 = 0;
00316   Q_UINT32 dummyUI32 = 0;
00317 
00318   // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x!
00319   // !! This data structure should remain binary compatible at all times !!
00320   // You may add new fields at the end. Make sure to update the version
00321   // number in ksycoca.h
00322   s << m_strType << m_strName << m_strExec << m_strIcon
00323     << term << m_strTerminalOptions
00324     << m_strPath << m_strComment << m_lstServiceTypes << def << m_mapProps
00325     << m_strLibrary << dummyI1 << dummyI2
00326     << dst
00327     << m_strDesktopEntryName
00328     << dummy1 << dummyStr1 << initpref << dummyStr2 << dummy2
00329     << m_lstKeywords << m_strInit << dummyUI32 << m_strGenName 
00330     << d->categories;
00331 }
00332 
00333 bool KService::hasServiceType( const QString& _servicetype ) const
00334 {
00335   if (!m_bValid) return false; // safety test
00336 
00337   //kdDebug(7012) << "Testing " << m_strDesktopEntryName << " for " << _servicetype << endl;
00338 
00339   // For each service type we are associated with, if it doesn't
00340   // match then we try its parent service types.
00341   QStringList::ConstIterator it = m_lstServiceTypes.begin();
00342   for( ; it != m_lstServiceTypes.end(); ++it )
00343   {
00344       //kdDebug(7012) << "    has " << (*it) << endl;
00345       KServiceType::Ptr ptr = KServiceType::serviceType( *it );
00346       if ( ptr && ptr->inherits( _servicetype ) )
00347           return true;
00348   }
00349   return false;
00350 }
00351 
00352 class KServiceReadProperty : public KConfigBase
00353 {
00354 public:
00355    KServiceReadProperty(const QString &_key, const QCString &_value)
00356         : key(_key), value(_value) { }
00357 
00358    bool internalHasGroup(const QCString &) const { /*qDebug("hasGroup(const QCString &)");*/ return false; }
00359 
00360    QStringList groupList() const { return QStringList(); }
00361 
00362    QMap<QString,QString> entryMap(const QString &) const
00363       { return QMap<QString,QString>(); }
00364 
00365    void reparseConfiguration() { }
00366 
00367    KEntryMap internalEntryMap( const QString &) const { return KEntryMap(); }
00368 
00369    KEntryMap internalEntryMap() const { return KEntryMap(); }
00370 
00371    void putData(const KEntryKey &, const KEntry&, bool) { }
00372 
00373    KEntry lookupData(const KEntryKey &) const
00374    { KEntry entry; entry.mValue = value; return entry; }
00375 protected:
00376    QString key;
00377    QCString value;
00378 };
00379 
00380 QVariant KService::property( const QString& _name ) const
00381 {
00382   if ( _name == "Type" )
00383     return QVariant( m_strType );
00384   else if ( _name == "Name" )
00385     return QVariant( m_strName );
00386   else if ( _name == "Exec" )
00387     return QVariant( m_strExec );
00388   else if ( _name == "Icon" )
00389     return QVariant( m_strIcon );
00390   else if ( _name == "Terminal" )
00391     return QVariant( static_cast<int>(m_bTerminal) );
00392   else if ( _name == "TerminalOptions" )
00393     return QVariant( m_strTerminalOptions );
00394   else if ( _name == "Path" )
00395     return QVariant( m_strPath );
00396   else if ( _name == "Comment" )
00397     return QVariant( m_strComment );
00398   else if ( _name == "GenericName" )
00399     return QVariant( m_strGenName );
00400   else if ( _name == "ServiceTypes" )
00401     return QVariant( m_lstServiceTypes );
00402   else if ( _name == "AllowAsDefault" )
00403     return QVariant( static_cast<int>(m_bAllowAsDefault) );
00404   else if ( _name == "InitialPreference" )
00405     return QVariant( m_initialPreference );
00406   else if ( _name == "Library" )
00407     return QVariant( m_strLibrary );
00408   else if ( _name == "DesktopEntryPath" )
00409     return QVariant( entryPath() );
00410   else if ( _name == "DesktopEntryName")
00411     return QVariant( m_strDesktopEntryName );
00412   else if ( _name == "Categories")
00413     return QVariant( d->categories );
00414   else if ( _name == "Keywords")
00415     return QVariant( m_lstKeywords );
00416   
00417   // Ok we need to convert the property from a QString to its real type.
00418   // First we need to ask KServiceTypeFactory what the type of this property
00419   // is supposed to be.
00420   // Then we use a homebuild class based on KBaseConfig to convert the QString.
00421   // For some often used property types we do the conversion ourselves.
00422 
00423   QVariant::Type t = KServiceTypeFactory::self()->findPropertyTypeByName(_name);
00424   if (t == QVariant::Invalid)
00425   {
00426     kdDebug(7012) << "Request for unknown property '" << _name << "'\n";
00427     return QVariant(); // Unknown property: Invalid variant.
00428   }
00429 
00430   QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( _name );
00431   if ( (it == m_mapProps.end()) || (!it.data().isValid()))
00432   {
00433      //kdDebug(7012) << "Property not found " << _name << endl;
00434      return QVariant(); // No property set.
00435   }
00436 
00437   switch(t)
00438   {
00439     case QVariant::String:
00440         return it.data();
00441     case QVariant::Bool:
00442     case QVariant::Int:
00443         {
00444            QString aValue = it.data().toString();
00445            int val = 0;
00446            if (aValue == "true" || aValue == "on" || aValue == "yes")
00447               val = 1;
00448            else
00449            {
00450               bool bOK;
00451               val = aValue.toInt( &bOK );
00452               if( !bOK )
00453                  val = 0;
00454            }
00455            if (t == QVariant::Bool)
00456            {
00457               if (val)
00458                  return QVariant(true);
00459               else
00460                  return QVariant(false);
00461            }
00462            return QVariant(val);
00463         }
00464     default:
00465         // All others
00466         KServiceReadProperty ksrp(_name, it.data().toString().utf8());
00467         return ksrp.readPropertyEntry(_name, t);
00468   }
00469 }
00470 
00471 QStringList KService::propertyNames() const
00472 {
00473   QStringList res;
00474 
00475   QMap<QString,QVariant>::ConstIterator it = m_mapProps.begin();
00476   for( ; it != m_mapProps.end(); ++it )
00477     res.append( it.key() );
00478 
00479   res.append( "Type" );
00480   res.append( "Name" );
00481   res.append( "Comment" );
00482   res.append( "GenericName" );
00483   res.append( "Icon" );
00484   res.append( "Exec" );
00485   res.append( "Terminal" );
00486   res.append( "TerminalOptions" );
00487   res.append( "Path" );
00488   res.append( "ServiceTypes" );
00489   res.append( "AllowAsDefault" );
00490   res.append( "InitialPreference" );
00491   res.append( "Library" );
00492   res.append( "DesktopEntryPath" );
00493   res.append( "DesktopEntryName" );
00494   res.append( "Keywords" );
00495   res.append( "Categories" );
00496 
00497   return res;
00498 }
00499 
00500 KService::List KService::allServices()
00501 {
00502   return KServiceFactory::self()->allServices();
00503 }
00504 
00505 KService::Ptr KService::serviceByName( const QString& _name )
00506 {
00507   KService * s = KServiceFactory::self()->findServiceByName( _name );
00508   return KService::Ptr( s );
00509 }
00510 
00511 KService::Ptr KService::serviceByDesktopPath( const QString& _name )
00512 {
00513   KService * s = KServiceFactory::self()->findServiceByDesktopPath( _name );
00514   return KService::Ptr( s );
00515 }
00516 
00517 KService::Ptr KService::serviceByDesktopName( const QString& _name )
00518 {
00519   KService * s = KServiceFactory::self()->findServiceByDesktopName( _name );
00520   return KService::Ptr( s );
00521 }
00522 
00523 KService::List KService::allInitServices()
00524 {
00525   return KServiceFactory::self()->allInitServices();
00526 }
00527 
00528 bool KService::substituteUid() const {
00529   QVariant v = property("X-KDE-SubstituteUID");
00530   return v.isValid() && v.toBool();
00531 }
00532 
00533 QString KService::username() const {
00534   // See also KDesktopFile::tryExec()
00535   QString user;
00536   QVariant v = property("X-KDE-Username");
00537   user = v.isValid() ? v.toString() : QString::null;
00538   if (user.isEmpty())
00539      user = ::getenv("ADMIN_ACCOUNT");
00540   if (user.isEmpty())
00541      user = "root";
00542   return user;
00543 }
00544 
00545 bool KService::noDisplay() const {
00546   QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( "NoDisplay" );
00547   if ( (it == m_mapProps.end()) || (!it.data().isValid()))
00548   {
00549      return false;
00550   }
00551 
00552   QString aValue = it.data().toString();
00553   if (aValue == "true" || aValue == "on" || aValue == "yes")
00554      return true;
00555   else
00556      return false;
00557 }
00558 
00559 QString KService::parentApp() const {
00560   QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( "X-KDE-ParentApp" );
00561   if ( (it == m_mapProps.end()) || (!it.data().isValid()))
00562   {
00563      return QString::null;
00564   }
00565 
00566   return it.data().toString();
00567 }
00568 
00569 bool KService::allowMultipleFiles() const {
00570   // Can we pass multiple files on the command line or do we have to start the application for every single file ?
00571   if ( m_strExec.find( "%F" ) != -1 || m_strExec.find( "%U" ) != -1 ||
00572        m_strExec.find( "%N" ) != -1 || m_strExec.find( "%D" ) != -1 )
00573     return true;
00574   else
00575     return false;
00576 }
00577 
00578 QStringList KService::categories() const
00579 {
00580   return d->categories;
00581 }
00582 
00583 
00584 void KService::virtual_hook( int id, void* data )
00585 { KSycocaEntry::virtual_hook( id, data ); }
00586 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:14:17 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001