diff options
Diffstat (limited to 'certmanager/lib/kleo/dn.cpp')
-rw-r--r-- | certmanager/lib/kleo/dn.cpp | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/certmanager/lib/kleo/dn.cpp b/certmanager/lib/kleo/dn.cpp new file mode 100644 index 00000000..9ba2209f --- /dev/null +++ b/certmanager/lib/kleo/dn.cpp @@ -0,0 +1,538 @@ +/* + dn.cpp + + This file is part of libkleopatra, the KDE keymanagement library + Copyright (c) 2004 Klarälvdalens Datakonsult AB + + DN parsing: + Copyright (c) 2002 g10 Code GmbH + Copyright (c) 2004 Klarälvdalens Datakonsult AB + + Libkleopatra is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + Libkleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "dn.h" + +#include "oidmap.h" +#include "ui/dnattributeorderconfigwidget.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <klocale.h> + +#include <qstringlist.h> +#include <qvaluevector.h> + +#include <iostream> +#include <iterator> +#include <algorithm> +#include <map> + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +struct Kleo::DN::Private { + Private() : mRefCount( 0 ) {} + Private( const Private & other ) + : attributes( other.attributes ), + reorderedAttributes( other.reorderedAttributes ), + mRefCount( 0 ) + { + + } + + int ref() { + return ++mRefCount; + } + + int unref() { + if ( --mRefCount <= 0 ) { + delete this; + return 0; + } else + return mRefCount; + } + + int refCount() const { return mRefCount; } + + DN::Attribute::List attributes; + DN::Attribute::List reorderedAttributes; +private: + int mRefCount; +}; + +namespace { + struct DnPair { + char * key; + char * value; + }; +} + +// copied from CryptPlug and adapted to work on DN::Attribute::List: + +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + +static char * +trim_trailing_spaces( char *string ) +{ + char *p, *mark; + + for( mark = NULL, p = string; *p; p++ ) { + if( isspace( *p ) ) { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + if( mark ) + *mark = '\0' ; + + return string ; +} + +/* Parse a DN and return an array-ized one. This is not a validating + parser and it does not support any old-stylish syntax; gpgme is + expected to return only rfc2253 compatible strings. */ +static const unsigned char * +parse_dn_part (DnPair *array, const unsigned char *string) +{ + const unsigned char *s, *s1; + size_t n; + char *p; + + /* parse attributeType */ + for (s = string+1; *s && *s != '='; s++) + ; + if (!*s) + return NULL; /* error */ + n = s - string; + if (!n) + return NULL; /* empty key */ + p = (char*)malloc (n+1); + + + memcpy (p, string, n); + p[n] = 0; + trim_trailing_spaces ((char*)p); + // map OIDs to their names: + for ( unsigned int i = 0 ; i < numOidMaps ; ++i ) + if ( !strcasecmp ((char*)p, oidmap[i].oid) ) { + free( p ); + p = strdup( oidmap[i].name ); + break; + } + array->key = p; + string = s + 1; + + if (*string == '#') + { /* hexstring */ + string++; + for (s=string; hexdigitp (s); s++) + s++; + n = s - string; + if (!n || (n & 1)) + return NULL; /* empty or odd number of digits */ + n /= 2; + array->value = p = (char*)malloc (n+1); + + + for (s1=string; n; s1 += 2, n--) + *p++ = xtoi_2 (s1); + *p = 0; + } + else + { /* regular v3 quoted string */ + for (n=0, s=string; *s; s++) + { + if (*s == '\\') + { /* pair */ + s++; + if (*s == ',' || *s == '=' || *s == '+' + || *s == '<' || *s == '>' || *s == '#' || *s == ';' + || *s == '\\' || *s == '\"' || *s == ' ') + n++; + else if (hexdigitp (s) && hexdigitp (s+1)) + { + s++; + n++; + } + else + return NULL; /* invalid escape sequence */ + } + else if (*s == '\"') + return NULL; /* invalid encoding */ + else if (*s == ',' || *s == '=' || *s == '+' + || *s == '<' || *s == '>' || *s == '#' || *s == ';' ) + break; + else + n++; + } + + array->value = p = (char*)malloc (n+1); + + + for (s=string; n; s++, n--) + { + if (*s == '\\') + { + s++; + if (hexdigitp (s)) + { + *p++ = xtoi_2 (s); + s++; + } + else + *p++ = *s; + } + else + *p++ = *s; + } + *p = 0; + } + return s; +} + + +/* Parse a DN and return an array-ized one. This is not a validating + parser and it does not support any old-stylish syntax; gpgme is + expected to return only rfc2253 compatible strings. */ +static Kleo::DN::Attribute::List +parse_dn( const unsigned char * string ) { + if ( !string ) + return QValueVector<Kleo::DN::Attribute>(); + + QValueVector<Kleo::DN::Attribute> result; + while (*string) + { + while (*string == ' ') + string++; + if (!*string) + break; /* ready */ + + DnPair pair = { 0, 0 }; + string = parse_dn_part (&pair, string); + if (!string) + goto failure; + if ( pair.key && pair.value ) + result.push_back( Kleo::DN::Attribute( QString::fromUtf8( pair.key ), + QString::fromUtf8( pair.value ) ) ); + free( pair.key ); + free( pair.value ); + + while (*string == ' ') + string++; + if (*string && *string != ',' && *string != ';' && *string != '+') + goto failure; /* invalid delimiter */ + if (*string) + string++; + } + return result; + +failure: + return QValueVector<Kleo::DN::Attribute>(); +} + +static QValueVector<Kleo::DN::Attribute> +parse_dn( const QString & dn ) { + return parse_dn( (const unsigned char*)dn.utf8().data() ); +} + +static QString dn_escape( const QString & s ) { + QString result; + for ( unsigned int i = 0, end = s.length() ; i != end ; ++i ) { + const QChar ch = s[i]; + switch ( ch.unicode() ) { + case ',': + case '+': + case '"': + case '\\': + case '<': + case '>': + case ';': + result += '\\'; + // fall through + default: + result += ch; + } + } + return result; +} + +static QString +serialise( const QValueVector<Kleo::DN::Attribute> & dn ) { + QStringList result; + for ( QValueVector<Kleo::DN::Attribute>::const_iterator it = dn.begin() ; it != dn.end() ; ++it ) + if ( !(*it).name().isEmpty() && !(*it).value().isEmpty() ) + result.push_back( (*it).name().stripWhiteSpace() + '=' + dn_escape( (*it).value().stripWhiteSpace() ) ); + return result.join( "," ); +} + +static Kleo::DN::Attribute::List +reorder_dn( const Kleo::DN::Attribute::List & dn ) { + const QStringList & attrOrder = Kleo::DNAttributeMapper::instance()->attributeOrder(); + + Kleo::DN::Attribute::List unknownEntries; + Kleo::DN::Attribute::List result; + unknownEntries.reserve( dn.size() ); + result.reserve( dn.size() ); + + // find all unknown entries in their order of appearance + for ( Kleo::DN::const_iterator it = dn.begin(); it != dn.end(); ++it ) + if ( attrOrder.find( (*it).name() ) == attrOrder.end() ) + unknownEntries.push_back( *it ); + + // process the known attrs in the desired order + for ( QStringList::const_iterator oit = attrOrder.begin() ; oit != attrOrder.end() ; ++oit ) + if ( *oit == "_X_" ) { + // insert the unknown attrs + std::copy( unknownEntries.begin(), unknownEntries.end(), + std::back_inserter( result ) ); + unknownEntries.clear(); // don't produce dup's + } else { + for ( Kleo::DN::const_iterator dnit = dn.begin() ; dnit != dn.end() ; ++dnit ) + if ( (*dnit).name() == *oit ) + result.push_back( *dnit ); + } + + return result; +} + +// +// +// class DN +// +// + +Kleo::DN::DN() { + d = new Private(); + d->ref(); +} + +Kleo::DN::DN( const QString & dn ) { + d = new Private(); + d->ref(); + d->attributes = parse_dn( dn ); +} + +Kleo::DN::DN( const char * utf8DN ) { + d = new Private(); + d->ref(); + if ( utf8DN ) + d->attributes = parse_dn( (const unsigned char*)utf8DN ); +} + +Kleo::DN::DN( const DN & other ) + : d( other.d ) +{ + if ( d ) d->ref(); +} + +Kleo::DN::~DN() { + if ( d ) d->unref(); +} + +const Kleo::DN & Kleo::DN::operator=( const DN & that ) { + if ( this->d == that.d ) + return *this; + + if ( that.d ) + that.d->ref(); + if ( this->d ) + this->d->unref(); + + this->d = that.d; + + return *this; +} + +QString Kleo::DN::prettyDN() const { + if ( !d ) + return QString::null; + if ( d->reorderedAttributes.empty() ) + d->reorderedAttributes = reorder_dn( d->attributes ); + return serialise( d->reorderedAttributes ); +} + +QString Kleo::DN::dn() const { + return d ? serialise( d->attributes ) : QString::null ; +} + +// static +QString Kleo::DN::escape( const QString & value ) { + return dn_escape( value ); +} + +void Kleo::DN::detach() { + if ( !d ) { + d = new Kleo::DN::Private(); + d->ref(); + } else if ( d->refCount() > 1 ) { + Kleo::DN::Private * d_save = d; + d = new Kleo::DN::Private( *d ); + d->ref(); + d_save->unref(); + } +} + +void Kleo::DN::append( const Attribute & attr ) { + detach(); + d->attributes.push_back( attr ); + d->reorderedAttributes.clear(); +} + +QString Kleo::DN::operator[]( const QString & attr ) const { + if ( !d ) + return QString::null; + const QString attrUpper = attr.upper(); + for ( QValueVector<Attribute>::const_iterator it = d->attributes.begin() ; + it != d->attributes.end() ; ++it ) + if ( (*it).name() == attrUpper ) + return (*it).value(); + return QString::null; +} + +static QValueVector<Kleo::DN::Attribute> empty; + +Kleo::DN::const_iterator Kleo::DN::begin() const { + return d ? d->attributes.begin() : empty.begin() ; +} + +Kleo::DN::const_iterator Kleo::DN::end() const { + return d ? d->attributes.end() : empty.end() ; +} + + +///////////////////// + +namespace { + struct ltstr { + bool operator()( const char * s1, const char * s2 ) const { + return qstrcmp( s1, s2 ) < 0 ; + } + }; +} + +static const char * defaultOrder[] = { + "CN", "L", "_X_", "OU", "O", "C" +}; + +std::pair<const char*,const char*> attributeLabels[] = { +#define MAKE_PAIR(x,y) std::pair<const char*,const char*>( x, y ) + MAKE_PAIR( "CN", I18N_NOOP("Common name") ), + MAKE_PAIR( "SN", I18N_NOOP("Surname") ), + MAKE_PAIR( "GN", I18N_NOOP("Given name") ), + MAKE_PAIR( "L", I18N_NOOP("Location") ), + MAKE_PAIR( "T", I18N_NOOP("Title") ), + MAKE_PAIR( "OU", I18N_NOOP("Organizational unit") ), + MAKE_PAIR( "O", I18N_NOOP("Organization") ), + MAKE_PAIR( "PC", I18N_NOOP("Postal code") ), + MAKE_PAIR( "C", I18N_NOOP("Country code") ), + MAKE_PAIR( "SP", I18N_NOOP("State or province") ), + MAKE_PAIR( "DC", I18N_NOOP("Domain component") ), + MAKE_PAIR( "BC", I18N_NOOP("Business category") ), + MAKE_PAIR( "EMAIL", I18N_NOOP("Email address") ), + MAKE_PAIR( "MAIL", I18N_NOOP("Mail address") ), + MAKE_PAIR( "MOBILE", I18N_NOOP("Mobile phone number") ), + MAKE_PAIR( "TEL", I18N_NOOP("Telephone number") ), + MAKE_PAIR( "FAX", I18N_NOOP("Fax number") ), + MAKE_PAIR( "STREET", I18N_NOOP("Street address") ), + MAKE_PAIR( "UID", I18N_NOOP("Unique ID") ) +#undef MAKE_PAIR +}; +static const unsigned int numAttributeLabels = sizeof attributeLabels / sizeof *attributeLabels ; + +class Kleo::DNAttributeMapper::Private { +public: + Private(); + std::map<const char*,const char*,ltstr> map; + QStringList attributeOrder; +}; + +Kleo::DNAttributeMapper::Private::Private() + : map( attributeLabels, attributeLabels + numAttributeLabels ) {} + +Kleo::DNAttributeMapper::DNAttributeMapper() { + d = new Private(); + const KConfigGroup config( kapp->config(), "DN" ); + d->attributeOrder = config.readListEntry( "AttributeOrder" ); + if ( d->attributeOrder.empty() ) + std::copy( defaultOrder, defaultOrder + sizeof defaultOrder / sizeof *defaultOrder, + std::back_inserter( d->attributeOrder ) ); + mSelf = this; +} + +Kleo::DNAttributeMapper::~DNAttributeMapper() { + mSelf = 0; + delete d; d = 0; +} + +Kleo::DNAttributeMapper * Kleo::DNAttributeMapper::mSelf = 0; + +const Kleo::DNAttributeMapper * Kleo::DNAttributeMapper::instance() { + if ( !mSelf ) + (void)new DNAttributeMapper(); + return mSelf; +} + +QString Kleo::DNAttributeMapper::name2label( const QString & s ) const { + const std::map<const char*,const char*,ltstr>::const_iterator it + = d->map.find( s.stripWhiteSpace().upper().latin1() ); + if ( it == d->map.end() ) + return QString::null; + return i18n( it->second ); +} + +QStringList Kleo::DNAttributeMapper::names() const { + QStringList result; + for ( std::map<const char*,const char*,ltstr>::const_iterator it = d->map.begin() ; it != d->map.end() ; ++it ) + result.push_back( it->first ); + return result; +} + +const QStringList & Kleo::DNAttributeMapper::attributeOrder() const { + return d->attributeOrder; +} + +void Kleo::DNAttributeMapper::setAttributeOrder( const QStringList & order ) { + d->attributeOrder = order; + if ( order.empty() ) + std::copy( defaultOrder, defaultOrder + sizeof defaultOrder / sizeof *defaultOrder, + std::back_inserter( d->attributeOrder ) ); + KConfigGroup config( kapp->config(), "DN" ); + config.writeEntry( "AttributeOrder", order ); +} + +Kleo::DNAttributeOrderConfigWidget * Kleo::DNAttributeMapper::configWidget( QWidget * parent, const char * name ) const { + return new DNAttributeOrderConfigWidget( mSelf, parent, name ); +} |