certmanager/lib

dn.cpp

00001 /*
00002     dn.cpp
00003 
00004     This file is part of libkleopatra, the KDE keymanagement library
00005     Copyright (c) 2004 Klarälvdalens Datakonsult AB
00006 
00007     DN parsing:
00008     Copyright (c) 2002 g10 Code GmbH
00009     Copyright (c) 2004 Klarälvdalens Datakonsult AB
00010 
00011     Libkleopatra is free software; you can redistribute it and/or
00012     modify it under the terms of the GNU General Public License as
00013     published by the Free Software Foundation; either version 2 of the
00014     License, or (at your option) any later version.
00015 
00016     Libkleopatra is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019     General Public License for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with this program; if not, write to the Free Software
00023     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00024 
00025     In addition, as a special exception, the copyright holders give
00026     permission to link the code of this program with any edition of
00027     the Qt library by Trolltech AS, Norway (or with modified versions
00028     of Qt that use the same license as Qt), and distribute linked
00029     combinations including the two.  You must obey the GNU General
00030     Public License in all respects for all of the code used other than
00031     Qt.  If you modify this file, you may extend this exception to
00032     your version of the file, but you are not obligated to do so.  If
00033     you do not wish to do so, delete this exception statement from
00034     your version.
00035 */
00036 
00037 #include "dn.h"
00038 
00039 #include "oidmap.h"
00040 #include "ui/dnattributeorderconfigwidget.h"
00041 
00042 #include <kapplication.h>
00043 #include <kconfig.h>
00044 #include <klocale.h>
00045 
00046 #include <qstringlist.h>
00047 #include <qvaluevector.h>
00048 
00049 #include <iostream>
00050 #include <iterator>
00051 #include <algorithm>
00052 #include <map>
00053 
00054 #include <string.h>
00055 #include <ctype.h>
00056 #include <stdlib.h>
00057 
00058 struct Kleo::DN::Private {
00059   Private() : mRefCount( 0 ) {}
00060   Private( const Private & other )
00061     : attributes( other.attributes ),
00062       reorderedAttributes( other.reorderedAttributes ),
00063       mRefCount( 0 )
00064   {
00065 
00066   }
00067 
00068   int ref() {
00069     return ++mRefCount;
00070   }
00071 
00072   int unref() {
00073     if ( --mRefCount <= 0 ) {
00074       delete this;
00075       return 0;
00076     } else
00077       return mRefCount;
00078   }
00079 
00080   int refCount() const { return mRefCount; }
00081 
00082   DN::Attribute::List attributes;
00083   DN::Attribute::List reorderedAttributes;
00084 private:
00085   int mRefCount;
00086 };
00087 
00088 namespace {
00089   struct DnPair {
00090     char * key;
00091     char * value;
00092   };
00093 }
00094 
00095 // copied from CryptPlug and adapted to work on DN::Attribute::List:
00096 
00097 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
00098 #define hexdigitp(a) (digitp (a)                     \
00099                       || (*(a) >= 'A' && *(a) <= 'F')  \
00100                       || (*(a) >= 'a' && *(a) <= 'f'))
00101 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
00102                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
00103 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
00104 
00105 static char *
00106 trim_trailing_spaces( char *string )
00107 {
00108     char *p, *mark;
00109 
00110     for( mark = NULL, p = string; *p; p++ ) {
00111     if( isspace( *p ) ) {
00112         if( !mark )
00113         mark = p;
00114     }
00115     else
00116         mark = NULL;
00117     }
00118     if( mark )
00119     *mark = '\0' ;
00120 
00121     return string ;
00122 }
00123 
00124 /* Parse a DN and return an array-ized one.  This is not a validating
00125    parser and it does not support any old-stylish syntax; gpgme is
00126    expected to return only rfc2253 compatible strings. */
00127 static const unsigned char *
00128 parse_dn_part (DnPair *array, const unsigned char *string)
00129 {
00130   const unsigned char *s, *s1;
00131   size_t n;
00132   char *p;
00133 
00134   /* parse attributeType */
00135   for (s = string+1; *s && *s != '='; s++)
00136     ;
00137   if (!*s)
00138     return NULL; /* error */
00139   n = s - string;
00140   if (!n)
00141     return NULL; /* empty key */
00142   p = (char*)malloc (n+1);
00143   
00144   
00145   memcpy (p, string, n);
00146   p[n] = 0;
00147   trim_trailing_spaces ((char*)p);
00148   // map OIDs to their names:
00149   for ( unsigned int i = 0 ; i < numOidMaps ; ++i )
00150     if ( !strcasecmp ((char*)p, oidmap[i].oid) ) {
00151       free( p );
00152       p = strdup( oidmap[i].name );
00153       break;
00154     }
00155   array->key = p;
00156   string = s + 1;
00157 
00158   if (*string == '#')
00159     { /* hexstring */
00160       string++;
00161       for (s=string; hexdigitp (s); s++)
00162         s++;
00163       n = s - string;
00164       if (!n || (n & 1))
00165         return NULL; /* empty or odd number of digits */
00166       n /= 2;
00167       array->value = p = (char*)malloc (n+1);
00168       
00169       
00170       for (s1=string; n; s1 += 2, n--)
00171         *p++ = xtoi_2 (s1);
00172       *p = 0;
00173    }
00174   else
00175     { /* regular v3 quoted string */
00176       for (n=0, s=string; *s; s++)
00177         {
00178           if (*s == '\\')
00179             { /* pair */
00180               s++;
00181               if (*s == ',' || *s == '=' || *s == '+'
00182                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
00183                   || *s == '\\' || *s == '\"' || *s == ' ')
00184                 n++;
00185               else if (hexdigitp (s) && hexdigitp (s+1))
00186                 {
00187                   s++;
00188                   n++;
00189                 }
00190               else
00191                 return NULL; /* invalid escape sequence */
00192             }
00193           else if (*s == '\"')
00194             return NULL; /* invalid encoding */
00195           else if (*s == ',' || *s == '=' || *s == '+'
00196                    || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
00197             break; 
00198           else
00199             n++;
00200         }
00201 
00202       array->value = p = (char*)malloc (n+1);
00203       
00204       
00205       for (s=string; n; s++, n--)
00206         {
00207           if (*s == '\\')
00208             { 
00209               s++;
00210               if (hexdigitp (s))
00211                 {
00212                   *p++ = xtoi_2 (s);
00213                   s++;
00214                 }
00215               else
00216                 *p++ = *s;
00217             }
00218           else
00219             *p++ = *s;
00220         }
00221       *p = 0;
00222     }
00223   return s;
00224 }
00225 
00226 
00227 /* Parse a DN and return an array-ized one.  This is not a validating
00228    parser and it does not support any old-stylish syntax; gpgme is
00229    expected to return only rfc2253 compatible strings. */
00230 static Kleo::DN::Attribute::List
00231 parse_dn( const unsigned char * string ) {
00232   if ( !string )
00233     return QValueVector<Kleo::DN::Attribute>();
00234 
00235   QValueVector<Kleo::DN::Attribute> result;
00236   while (*string)
00237     {
00238       while (*string == ' ')
00239         string++;
00240       if (!*string)
00241         break; /* ready */
00242 
00243       DnPair pair = { 0, 0 };
00244       string = parse_dn_part (&pair, string);
00245       if (!string)
00246     goto failure;
00247       if ( pair.key && pair.value )
00248     result.push_back( Kleo::DN::Attribute( QString::fromUtf8( pair.key ),
00249                            QString::fromUtf8( pair.value ) ) );
00250       free( pair.key );
00251       free( pair.value );
00252 
00253       while (*string == ' ')
00254         string++;
00255       if (*string && *string != ',' && *string != ';' && *string != '+')
00256         goto failure; /* invalid delimiter */
00257       if (*string)
00258         string++;
00259     }
00260   return result;
00261 
00262 failure:
00263   return QValueVector<Kleo::DN::Attribute>();
00264 }
00265 
00266 static QValueVector<Kleo::DN::Attribute>
00267 parse_dn( const QString & dn ) {
00268   return parse_dn( (const unsigned char*)dn.utf8().data() );
00269 }
00270 
00271 static QString
00272 serialise( const QValueVector<Kleo::DN::Attribute> & dn ) {
00273   QStringList result;
00274   for ( QValueVector<Kleo::DN::Attribute>::const_iterator it = dn.begin() ; it != dn.end() ; ++it )
00275     if ( !(*it).name().isEmpty() && !(*it).value().isEmpty() )
00276       result.push_back( (*it).name().stripWhiteSpace() + '=' + (*it).value().stripWhiteSpace() );
00277   return result.join( "," );
00278 }
00279 
00280 static Kleo::DN::Attribute::List
00281 reorder_dn( const Kleo::DN::Attribute::List & dn ) {
00282   const QStringList & attrOrder = Kleo::DNAttributeMapper::instance()->attributeOrder();
00283 
00284   Kleo::DN::Attribute::List unknownEntries;
00285   Kleo::DN::Attribute::List result;
00286   unknownEntries.reserve( dn.size() );
00287   result.reserve( dn.size() );
00288 
00289   // find all unknown entries in their order of appearance
00290   for ( Kleo::DN::const_iterator it = dn.begin(); it != dn.end(); ++it )
00291     if ( attrOrder.find( (*it).name() ) == attrOrder.end() )
00292       unknownEntries.push_back( *it );
00293 
00294   // process the known attrs in the desired order
00295   for ( QStringList::const_iterator oit = attrOrder.begin() ; oit != attrOrder.end() ; ++oit )
00296     if ( *oit == "_X_" ) {
00297       // insert the unknown attrs
00298       std::copy( unknownEntries.begin(), unknownEntries.end(),
00299          std::back_inserter( result ) );
00300       unknownEntries.clear(); // don't produce dup's
00301     } else {
00302       for ( Kleo::DN::const_iterator dnit = dn.begin() ; dnit != dn.end() ; ++dnit )
00303     if ( (*dnit).name() == *oit )
00304       result.push_back( *dnit );
00305     }
00306   
00307   return result;
00308 }
00309 
00310 //
00311 //
00312 // class DN
00313 //
00314 //
00315 
00316 Kleo::DN::DN() {
00317   d = new Private();
00318   d->ref();
00319 }
00320 
00321 Kleo::DN::DN( const QString & dn ) {
00322   d = new Private();
00323   d->ref();
00324   d->attributes = parse_dn( dn );
00325 }
00326 
00327 Kleo::DN::DN( const char * utf8DN ) {
00328   d = new Private();
00329   d->ref();
00330   if ( utf8DN )
00331     d->attributes = parse_dn( (const unsigned char*)utf8DN );
00332 }
00333 
00334 Kleo::DN::DN( const DN & other )
00335   : d( other.d )
00336 {
00337   if ( d ) d->ref();
00338 }
00339 
00340 Kleo::DN::~DN() {
00341   if ( d ) d->unref();
00342 }
00343 
00344 const Kleo::DN & Kleo::DN::operator=( const DN & that ) {
00345   if ( this->d == that.d )
00346     return *this;
00347 
00348   if ( that.d )
00349     that.d->ref();
00350   if ( this->d )
00351     this->d->unref();
00352 
00353   this->d = that.d;
00354 
00355   return *this;
00356 }
00357 
00358 QString Kleo::DN::prettyDN() const {
00359   if ( !d )
00360     return QString::null;
00361   if ( d->reorderedAttributes.empty() )
00362     d->reorderedAttributes = reorder_dn( d->attributes );
00363   return serialise( d->reorderedAttributes );
00364 }
00365 
00366 QString Kleo::DN::dn() const {
00367   return d ? serialise( d->attributes ) : QString::null ;
00368 }
00369 
00370 void Kleo::DN::detach() {
00371   if ( !d ) {
00372     d = new Kleo::DN::Private();
00373     d->ref();
00374   } else if ( d->refCount() > 1 ) {
00375     Kleo::DN::Private * d_save = d;
00376     d = new Kleo::DN::Private( *d );
00377     d->ref();
00378     d_save->unref();
00379   }
00380 }
00381 
00382 void Kleo::DN::append( const Attribute & attr ) {
00383   detach();
00384   d->attributes.push_back( attr );
00385   d->reorderedAttributes.clear();
00386 }
00387 
00388 QString Kleo::DN::operator[]( const QString & attr ) const {
00389   if ( !d )
00390     return QString::null;
00391   const QString attrUpper = attr.upper();
00392   for ( QValueVector<Attribute>::const_iterator it = d->attributes.begin() ;
00393     it != d->attributes.end() ; ++it )
00394     if ( (*it).name() == attrUpper )
00395       return (*it).value();
00396   return QString::null;
00397 }
00398 
00399 static QValueVector<Kleo::DN::Attribute> empty;
00400 
00401 Kleo::DN::const_iterator Kleo::DN::begin() const {
00402   return d ? d->attributes.begin() : empty.begin() ;
00403 }
00404 
00405 Kleo::DN::const_iterator Kleo::DN::end() const {
00406   return d ? d->attributes.end() : empty.end() ;
00407 }
00408 
00409 
00411 
00412 namespace {
00413   struct ltstr {
00414     bool operator()( const char * s1, const char * s2 ) const {
00415       return qstrcmp( s1, s2 ) < 0 ;
00416     }
00417   };
00418 }
00419 
00420 static const char * defaultOrder[] = {
00421   "CN", "L", "_X_", "OU", "O", "C"
00422 };
00423 
00424 std::pair<const char*,const char*> attributeLabels[] = {
00425 #define MAKE_PAIR(x,y) std::pair<const char*,const char*>( x, y )
00426   MAKE_PAIR( "CN", I18N_NOOP("Common name") ),
00427   MAKE_PAIR( "SN", I18N_NOOP("Surname") ),
00428   MAKE_PAIR( "GN", I18N_NOOP("Given name") ),
00429   MAKE_PAIR( "L",  I18N_NOOP("Location") ),
00430   MAKE_PAIR( "T",  I18N_NOOP("Title") ),
00431   MAKE_PAIR( "OU", I18N_NOOP("Organizational unit") ),
00432   MAKE_PAIR( "O",  I18N_NOOP("Organization") ),
00433   MAKE_PAIR( "PC", I18N_NOOP("Postal code") ),
00434   MAKE_PAIR( "C",  I18N_NOOP("Country code") ),
00435   MAKE_PAIR( "SP", I18N_NOOP("State or province") ),
00436   MAKE_PAIR( "DC", I18N_NOOP("Domain component") ),
00437   MAKE_PAIR( "BC", I18N_NOOP("Business category") ),
00438   MAKE_PAIR( "EMAIL", I18N_NOOP("Email address") ),
00439   MAKE_PAIR( "MAIL", I18N_NOOP("Mail address") ),
00440   MAKE_PAIR( "MOBILE", I18N_NOOP("Mobile phone number") ),
00441   MAKE_PAIR( "TEL", I18N_NOOP("Telephone number") ),
00442   MAKE_PAIR( "FAX", I18N_NOOP("Fax number") ),
00443   MAKE_PAIR( "STREET", I18N_NOOP("Street address") ),
00444   MAKE_PAIR( "UID", I18N_NOOP("Unique ID") )
00445 #undef MAKE_PAIR
00446 };
00447 static const unsigned int numAttributeLabels = sizeof attributeLabels / sizeof *attributeLabels ;
00448 
00449 class Kleo::DNAttributeMapper::Private {
00450 public:
00451   Private();
00452   std::map<const char*,const char*,ltstr> map;
00453   QStringList attributeOrder;
00454 };
00455 
00456 Kleo::DNAttributeMapper::Private::Private()
00457   : map( attributeLabels, attributeLabels + numAttributeLabels ) {}
00458 
00459 Kleo::DNAttributeMapper::DNAttributeMapper() {
00460   d = new Private();
00461   const KConfigGroup config( kapp->config(), "DN" );
00462   d->attributeOrder = config.readListEntry( "AttributeOrder" );
00463   if ( d->attributeOrder.empty() )
00464     std::copy( defaultOrder, defaultOrder + sizeof defaultOrder / sizeof *defaultOrder,
00465            std::back_inserter( d->attributeOrder ) );
00466   mSelf = this;
00467 }
00468 
00469 Kleo::DNAttributeMapper::~DNAttributeMapper() {
00470   mSelf = 0;
00471   delete d; d = 0;
00472 }
00473 
00474 Kleo::DNAttributeMapper * Kleo::DNAttributeMapper::mSelf = 0;
00475 
00476 const Kleo::DNAttributeMapper * Kleo::DNAttributeMapper::instance() {
00477   if ( !mSelf )
00478     (void)new DNAttributeMapper();
00479   return mSelf;
00480 }
00481 
00482 QString Kleo::DNAttributeMapper::name2label( const QString & s ) const {
00483   const std::map<const char*,const char*,ltstr>::const_iterator it
00484     = d->map.find( s.stripWhiteSpace().upper().latin1() );
00485   if ( it == d->map.end() )
00486     return QString::null;
00487   return i18n( it->second );
00488 }
00489 
00490 QStringList Kleo::DNAttributeMapper::names() const {
00491   QStringList result;
00492   for ( std::map<const char*,const char*,ltstr>::const_iterator it = d->map.begin() ; it != d->map.end() ; ++it )
00493     result.push_back( it->first );
00494   return result;
00495 }
00496 
00497 const QStringList & Kleo::DNAttributeMapper::attributeOrder() const {
00498   return d->attributeOrder;
00499 }
00500 
00501 void Kleo::DNAttributeMapper::setAttributeOrder( const QStringList & order ) {
00502   d->attributeOrder = order;
00503   if ( order.empty() )
00504     std::copy( defaultOrder, defaultOrder + sizeof defaultOrder / sizeof *defaultOrder,
00505            std::back_inserter( d->attributeOrder ) );
00506   KConfigGroup config( kapp->config(), "DN" );
00507   config.writeEntry( "AttributeOrder", order );
00508 }
00509 
00510 Kleo::DNAttributeOrderConfigWidget * Kleo::DNAttributeMapper::configWidget( QWidget * parent, const char * name ) const {
00511   return new DNAttributeOrderConfigWidget( mSelf, parent, name );
00512 }
KDE Home | KDE Accessibility Home | Description of Access Keys