kleopatra
configreader.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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;
00066 static const int GPGCONF_FLAG_DEFAULT_DESC = 32;
00067 static const int GPGCONF_FLAG_NOARG_DESC = 64;
00068 static const int GPGCONF_FLAG_NO_CHANGE = 128;
00069
00070
00071
00072
00073 static ConfigEntry::ArgType knownArgType( int argType, bool& ok ) {
00074 ok = true;
00075 switch( argType )
00076 {
00077 case 0:
00078 return ConfigEntry::None;
00079 case 1:
00080 return ConfigEntry::String;
00081 case 2:
00082 return ConfigEntry::Int;
00083 case 3:
00084 return ConfigEntry::UInt;
00085 case 32:
00086 return ConfigEntry::Path;
00087 case 33:
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
00138 assert( parsedLine.count() >= 10 );
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
00148 uint realArgType = (*it++).toInt();
00149 ConfigEntry::ArgType argType = ::knownArgType( realArgType, ok );
00150 if ( !ok && !(*it).isEmpty() ) {
00151
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
00180
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 )
00186 continue;
00187 if ( flags & GPGCONF_FLAG_GROUP ) {
00188 if ( currentGroup.get() && !currentGroup->isEmpty() )
00189 component->addGroup( currentGroup.release() );
00190 else {
00191 currentGroup.reset();
00192 }
00193
00194
00195 currentGroup.reset( new ConfigGroup( lst[0] ) );
00196 currentGroup->setDescription( lst[3] );
00197
00198 } else {
00199
00200 if ( !currentGroup.get() ) {
00201 currentGroup.reset( new ConfigGroup( "<nogroup>" ) );
00202 }
00203 currentGroup->addEntry( createEntryFromParsedLine( lst ) );
00204 }
00205 } else {
00206
00207
00208
00209
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" )
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
00278
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