You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
539 lines
14 KiB
539 lines
14 KiB
/*
|
|
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 <tqstringlist.h>
|
|
#include <tqvaluevector.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 TQValueVector<Kleo::DN::Attribute>();
|
|
|
|
TQValueVector<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( TQString::fromUtf8( pair.key ),
|
|
TQString::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 TQValueVector<Kleo::DN::Attribute>();
|
|
}
|
|
|
|
static TQValueVector<Kleo::DN::Attribute>
|
|
parse_dn( const TQString & dn ) {
|
|
return parse_dn( (const unsigned char*)dn.utf8().data() );
|
|
}
|
|
|
|
static TQString dn_escape( const TQString & s ) {
|
|
TQString result;
|
|
for ( unsigned int i = 0, end = s.length() ; i != end ; ++i ) {
|
|
const TQChar 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 TQValueVector<Kleo::DN::Attribute> & dn ) {
|
|
TQStringList result;
|
|
for ( TQValueVector<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 TQStringList & 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 ( TQStringList::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 TQString & 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;
|
|
}
|
|
|
|
TQString Kleo::DN::prettyDN() const {
|
|
if ( !d )
|
|
return TQString::null;
|
|
if ( d->reorderedAttributes.empty() )
|
|
d->reorderedAttributes = reorder_dn( d->attributes );
|
|
return serialise( d->reorderedAttributes );
|
|
}
|
|
|
|
TQString Kleo::DN::dn() const {
|
|
return d ? serialise( d->attributes ) : TQString::null ;
|
|
}
|
|
|
|
// static
|
|
TQString Kleo::DN::escape( const TQString & 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();
|
|
}
|
|
|
|
TQString Kleo::DN::operator[]( const TQString & attr ) const {
|
|
if ( !d )
|
|
return TQString::null;
|
|
const TQString attrUpper = attr.upper();
|
|
for ( TQValueVector<Attribute>::const_iterator it = d->attributes.begin() ;
|
|
it != d->attributes.end() ; ++it )
|
|
if ( (*it).name() == attrUpper )
|
|
return (*it).value();
|
|
return TQString::null;
|
|
}
|
|
|
|
static TQValueVector<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;
|
|
TQStringList 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;
|
|
}
|
|
|
|
TQString Kleo::DNAttributeMapper::name2label( const TQString & 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 TQString::null;
|
|
return i18n( it->second );
|
|
}
|
|
|
|
TQStringList Kleo::DNAttributeMapper::names() const {
|
|
TQStringList 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 TQStringList & Kleo::DNAttributeMapper::attributeOrder() const {
|
|
return d->attributeOrder;
|
|
}
|
|
|
|
void Kleo::DNAttributeMapper::setAttributeOrder( const TQStringList & 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( TQWidget * parent, const char * name ) const {
|
|
return new DNAttributeOrderConfigWidget( mSelf, parent, name );
|
|
}
|