• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kleopatra

configreader.cpp

Go to the documentation of this file.
00001 /* -*- mode: c++; c-basic-offset:4 -*-
00002     configreader.cpp
00003 
00004     This file is part of Kleopatra, the KDE keymanager
00005     Copyright (c) 2007 Klarälvdalens Datakonsult AB
00006 
00007     Kleopatra is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     Kleopatra is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021     In addition, as a special exception, the copyright holders give
00022     permission to link the code of this program with any edition of
00023     the Qt library by Trolltech AS, Norway (or with modified versions
00024     of Qt that use the same license as Qt), and distribute linked
00025     combinations including the two.  You must obey the GNU General
00026     Public License in all respects for all of the code used other than
00027     Qt.  If you modify this file, you may extend this exception to
00028     your version of the file, but you are not obligated to do so.  If
00029     you do not wish to do so, delete this exception statement from
00030     your version.
00031 */
00032 
00033 #include "configreader.h"
00034 #include "configuration.h"
00035 #include "exception.h"
00036 
00037 #include <gpgme++/global.h>
00038 #include <gpgme++/engineinfo.h>
00039 
00040 #include <KLocale>
00041 #include <KStandardDirs>
00042 
00043 #include <QBuffer>
00044 #include <QByteArray>
00045 #include <QDebug>
00046 #include <QMap>
00047 #include <QProcess>
00048 #include <QStringList>
00049 #include <QFile>
00050 
00051 #include <cassert>
00052 #include <memory>
00053 
00054 namespace {
00055  
00056 struct GpgConfResult {
00057     QByteArray stdOut;
00058     QByteArray stdErr;
00059 };
00060 
00061 static const int GPGCONF_FLAG_GROUP = 1;
00062 static const int GPGCONF_FLAG_OPTIONAL = 2;
00063 static const int GPGCONF_FLAG_LIST = 4;
00064 static const int GPGCONF_FLAG_RUNTIME = 8;
00065 static const int GPGCONF_FLAG_DEFAULT = 16; // fixed default value available
00066 static const int GPGCONF_FLAG_DEFAULT_DESC = 32; // runtime default value available
00067 static const int GPGCONF_FLAG_NOARG_DESC = 64; // option with optional arg; special meaning if no arg set
00068 static const int GPGCONF_FLAG_NO_CHANGE = 128; // readonly
00069 // Change size of mFlags bitfield if adding new values here
00070 
00071 
00072 // gpgconf arg type number -> CryptoConfigEntry arg type enum mapping
00073 static ConfigEntry::ArgType knownArgType( int argType, bool& ok ) {
00074     ok = true;
00075     switch( argType )
00076     {
00077     case 0: // none
00078         return ConfigEntry::None;
00079     case 1: // string
00080         return ConfigEntry::String;
00081     case 2: // int32
00082         return ConfigEntry::Int;
00083     case 3: // uint32
00084         return ConfigEntry::UInt;
00085     case 32: // pathname
00086         return ConfigEntry::Path;
00087     case 33: // ldap server
00088         return ConfigEntry::LdapUrl;
00089     default:
00090         ok = false;
00091         return ConfigEntry::None;
00092     }
00093 }
00094 
00095 }
00096 
00097 class ConfigReader::Private
00098 {
00099 public:
00100     GpgConfResult runGpgConf( const QStringList& args ) const;
00101     GpgConfResult runGpgConf( const QString& arg ) const;
00102 
00103     QMap<QString,QString> readComponentInfo() const;
00104     void readEntriesForComponent( ConfigComponent* component ) const;
00105     ConfigEntry* createEntryFromParsedLine( const QStringList& lst ) const;
00106     void readConfConf( Config* cfg ) const;
00107 };
00108  
00109 ConfigReader::ConfigReader() : d( new Private )
00110 {
00111 }
00112 
00113 ConfigReader::~ConfigReader()
00114 {
00115     delete d;
00116 }
00117 
00118 
00119 Config* ConfigReader::readConfig() const
00120 {
00121     std::auto_ptr<Config> cfg( new Config );
00122     const QMap<QString,QString> componentInfo = d->readComponentInfo();
00123 
00124     Q_FOREACH ( const QString i, componentInfo.keys() )
00125     {
00126         std::auto_ptr<ConfigComponent> component( new ConfigComponent( i ) );
00127         component->setDescription( componentInfo[i] );
00128         d->readEntriesForComponent( component.get() );
00129         cfg->addComponent( component.release() );
00130     }
00131     d->readConfConf( cfg.get() );
00132     return cfg.release();
00133 }
00134 
00135 ConfigEntry* ConfigReader::Private::createEntryFromParsedLine( const QStringList& parsedLine ) const
00136 {
00137     // Format: NAME:FLAGS:LEVEL:DESCRIPTION:TYPE:ALT-TYPE:ARGNAME:DEFAULT:ARGDEF:VALUE
00138     assert( parsedLine.count() >= 10 ); // called checked for it already
00139     QStringList::const_iterator it = parsedLine.begin();
00140     const QString name = *it++;
00141     std::auto_ptr<ConfigEntry> entry( new ConfigEntry( name ) );
00142     const int flags = (*it++).toInt();
00143     const int level = (*it++).toInt();
00144     Q_UNUSED( level );
00145     entry->setDescription( *it++ );
00146     bool ok;
00147     // we keep the real (int) arg type, since it influences the parsing (e.g. for ldap urls)
00148     uint realArgType = (*it++).toInt();
00149     ConfigEntry::ArgType argType = ::knownArgType( realArgType, ok );
00150     if ( !ok && !(*it).isEmpty() ) {
00151     // use ALT-TYPE
00152         realArgType = (*it).toInt();
00153         argType = ::knownArgType( realArgType, ok );
00154     }
00155     entry->setArgType( argType, flags & GPGCONF_FLAG_LIST ? ConfigEntry::List : ConfigEntry::NoList );
00156     if ( !ok )
00157         qWarning() <<"Unsupported datatype:" << parsedLine[4] <<" :" << *it <<" for" << parsedLine[0];
00158     entry->unsetDirty();
00159     return entry.release();
00160 }
00161 
00162 void ConfigReader::Private::readEntriesForComponent( ConfigComponent* component ) const
00163 {
00164     assert( component );
00165     QStringList args;
00166     args << "--list-options" << component->name();
00167     GpgConfResult res = runGpgConf( args );
00168 
00169     std::auto_ptr<ConfigGroup> currentGroup;
00170 
00171     QBuffer buf( &res.stdOut );
00172     buf.open( QIODevice::ReadOnly );
00173     while( buf.canReadLine() ) {
00174         QString line = QString::fromUtf8( buf.readLine() );
00175         if ( line.endsWith( '\n' ) )
00176             line.chop( 1 );
00177         if ( line.endsWith( '\r' ) )
00178             line.chop( 1 );
00179         //kDebug(5150) <<"GOT LINE:" << line;
00180         // Format: NAME:FLAGS:LEVEL:DESCRIPTION:TYPE:ALT-TYPE:ARGNAME:DEFAULT:ARGDEF:VALUE
00181         const QStringList lst = line.split( ':' );
00182         if ( lst.count() >= 10 ) {
00183             const int flags = lst[1].toInt();
00184             const int level = lst[2].toInt();
00185             if ( level > 2 ) // invisible or internal -> skip it;
00186                 continue;
00187             if ( flags & GPGCONF_FLAG_GROUP ) {
00188                 if ( currentGroup.get() && !currentGroup->isEmpty() ) // only add non-empty groups
00189                     component->addGroup( currentGroup.release() );
00190                 else {
00191                     currentGroup.reset();
00192                 }
00193             //else
00194             //  kDebug(5150) <<"Discarding empty group" << mCurrentGroupName;
00195                 currentGroup.reset( new ConfigGroup( lst[0] ) );
00196                 currentGroup->setDescription( lst[3] );
00197                 //currentGroup->setLevel( level );
00198             } else {
00199                 // normal entry
00200                 if ( !currentGroup.get() ) {  // first toplevel entry -> create toplevel group
00201                     currentGroup.reset( new ConfigGroup( "<nogroup>" ) );
00202                 }
00203                 currentGroup->addEntry( createEntryFromParsedLine( lst ) );
00204             }
00205         } else {
00206             // This happens on lines like
00207             // dirmngr[31465]: error opening `/home/dfaure/.gnupg/dirmngr_ldapservers.conf': No such file or directory
00208             // so let's not bother the user with it.
00209             //kWarning(5150) <<"Parse error on gpgconf --list-options output:" << line;
00210         }
00211     }
00212     if ( currentGroup.get() && !currentGroup->isEmpty() )
00213         component->addGroup( currentGroup.release() );
00214 }
00215 
00216 void ConfigReader::Private::readConfConf( Config* cfg ) const
00217 {
00218     GpgConfResult res = runGpgConf( "--list-config" );
00219     QBuffer buf( &(res.stdOut) );
00220     buf.open( QIODevice::ReadOnly | QIODevice::Text );
00221     while ( buf.canReadLine() )
00222     {
00223         QString line = buf.readLine();
00224         if ( line.endsWith( '\n' ) )
00225             line.chop( 1 );
00226         if ( line.endsWith( '\r' ) )
00227             line.chop( 1 );
00228         const QStringList lst = line.split( ':' );
00229         if ( lst.isEmpty() || lst[0] != "r" ) // only parse 'r'-type value entries
00230             continue;
00231 
00232         if ( lst.count() < 8 )
00233         { 
00234             throw MalformedGpgConfOutputException( i18n( "Parse error on gpgconf --list-config output:", line ) );
00235         }
00236         ConfigComponent* const component = cfg->component( lst[3] );
00237         if ( !component )
00238         {
00239             throw MalformedGpgConfOutputException( i18n( "gpgconf --list-config: Unknown component: %1", lst[3] ) );
00240         }
00241         ConfigEntry* const entry = component->entry( lst[4] );
00242         if ( !entry )
00243         {
00244             throw MalformedGpgConfOutputException( i18n( "gpgconf --list-config: Unknown entry: %1:%2", lst[3], lst[4] ) );
00245         }
00246         const QString flag = lst[5];
00247         const QString value = lst[6];
00248         if ( !value.isEmpty() && !value.startsWith( '\"' ) )
00249         {
00250             throw MalformedGpgConfOutputException( i18n( "gpgconf --list-config: Invalid entry: value must start with '\"': %1", lst[6] ) );
00251         }
00252         if ( !lst[6].isEmpty() )
00253             entry->setValueFromRawString( lst[6].mid( 1 ) );
00254         
00255         if ( flag == QLatin1String( "no-change" ) )
00256             entry->setMutability( ConfigEntry::NoChange );
00257         else if ( flag == QLatin1String( "change" ) )
00258             entry->setMutability( ConfigEntry::Change );
00259         else if ( flag == QLatin1String( "default" ) )
00260             entry->setUseBuiltInDefault( true );
00261     }
00262     buf.close();
00263 }
00264 
00265 QMap<QString, QString> ConfigReader::Private::readComponentInfo() const
00266 {
00267     GpgConfResult res = runGpgConf( "--list-components" );
00268     QBuffer buf( &(res.stdOut) );
00269     buf.open( QIODevice::ReadOnly );
00270     QMap<QString, QString> components;
00271     while( buf.canReadLine() ) {
00272         QString line = QString::fromUtf8( buf.readLine() );
00273         if ( line.endsWith( '\n' ) )
00274             line.chop( 1 );
00275         if ( line.endsWith( '\r' ) )
00276             line.chop( 1 );
00277         //kDebug(5150) <<"GOT LINE:" << line;
00278         // Format: NAME:DESCRIPTION
00279         const QStringList lst = line.split( ':' );
00280         if ( lst.count() >= 2 ) {
00281             components[lst[0]] = lst[1];
00282         } else {
00283             throw MalformedGpgConfOutputException( i18n( "Parse error on gpgconf --list-components. output:", line ) );
00284         }
00285     }
00286     return components;
00287 }
00288 
00289 GpgConfResult ConfigReader::Private::runGpgConf( const QString& arg ) const
00290 {
00291     return runGpgConf( QStringList( arg ) );
00292 }
00293 
00294 static QString gpgConfPath() {
00295     const GpgME::EngineInfo ei = GpgME::engineInfo( GpgME::GpgConfEngine );
00296     return ei.fileName() ? QFile::decodeName( ei.fileName() ) : KStandardDirs::findExe( "gpgconf" ) ;
00297 }
00298 
00299 GpgConfResult ConfigReader::Private::runGpgConf( const QStringList& args ) const
00300 {
00301     QProcess process;
00302     process.start( gpgConfPath(), args );
00303 
00304     process.waitForStarted();
00305     process.waitForFinished();
00306     
00307     if ( process.exitStatus() != QProcess::NormalExit ) {
00308         switch ( process.error() )
00309         {
00310         case QProcess::FailedToStart:
00311             throw GpgConfRunException( -2, i18n( "gpgconf not found or cannot be started" ) );
00312         case QProcess::Crashed:
00313             throw GpgConfRunException( -1, i18n( "gpgconf terminated unexpectedly" ) );
00314         case QProcess::Timedout:
00315             throw GpgConfRunException( -3, i18n( "timeout while executing gpgconf" ) );
00316         case QProcess::WriteError:
00317             throw GpgConfRunException( -4, i18n( "error while writing to gpgconf" ) );
00318         case QProcess::ReadError:
00319             throw GpgConfRunException( -5, i18n( "error while reading from gpgconf" ) );
00320         case QProcess::UnknownError:
00321         default:
00322             throw GpgConfRunException( -6, i18n( "Unknown error while executing gpgconf" ) );
00323         }
00324     }
00325 
00326     GpgConfResult res;
00327     res.stdOut = process.readAllStandardOutput();
00328     res.stdErr = process.readAllStandardError();
00329     return res;
00330 }
00331 

kleopatra

Skip menu "kleopatra"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

kdepim

Skip menu "kdepim"
  • akonadi
  •   clients
  •   kabc
  •   kcal
  •   kcm
  • akregator
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt
  • kdgantt1
  • kjots
  • kleopatra
  • kmail
  • kmobiletools
  • knode
  • knotes
  • kontact
  • kontactinterfaces
  • korganizer
  •   korgac
  • kpilot
  • ktimetracker
  • libkdepim
  • libkholidays
  • libkleo
  • libkpgp
  • maildir
Generated for kdepim by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal