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 <tqstringlist.h>
00047 #include <tqvaluevector.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 TQValueVector<Kleo::DN::Attribute>();
00234 
00235   TQValueVector<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( TQString::fromUtf8( pair.key ),
00249                            TQString::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 TQValueVector<Kleo::DN::Attribute>();
00264 }
00265 
00266 static TQValueVector<Kleo::DN::Attribute>
00267 parse_dn( const TQString & dn ) {
00268   return parse_dn( (const unsigned char*)dn.utf8().data() );
00269 }
00270 
00271 static TQString dn_escape( const TQString & s ) {
00272     TQString result;
00273     for ( unsigned int i = 0, end = s.length() ; i != end ; ++i ) {
00274         const TQChar ch = s[i];
00275         switch ( ch.unicode() ) {
00276         case ',':
00277         case '+':
00278         case '"':
00279         case '\\':
00280         case '<':
00281         case '>':
00282         case ';':
00283             result += '\\';
00284             // fall through
00285         default:
00286             result += ch;
00287         }
00288     }
00289     return result;
00290 }
00291 
00292 static QString
00293 serialise( const TQValueVector<Kleo::DN::Attribute> & dn ) {
00294   TQStringList result;
00295   for ( TQValueVector<Kleo::DN::Attribute>::const_iterator it = dn.begin() ; it != dn.end() ; ++it )
00296     if ( !(*it).name().isEmpty() && !(*it).value().isEmpty() )
00297       result.push_back( (*it).name().stripWhiteSpace() + '=' + dn_escape( (*it).value().stripWhiteSpace() ) );
00298   return result.join( "," );
00299 }
00300 
00301 static Kleo::DN::Attribute::List
00302 reorder_dn( const Kleo::DN::Attribute::List & dn ) {
00303   const TQStringList & attrOrder = Kleo::DNAttributeMapper::instance()->attributeOrder();
00304 
00305   Kleo::DN::Attribute::List unknownEntries;
00306   Kleo::DN::Attribute::List result;
00307   unknownEntries.reserve( dn.size() );
00308   result.reserve( dn.size() );
00309 
00310   // find all unknown entries in their order of appearance
00311   for ( Kleo::DN::const_iterator it = dn.begin(); it != dn.end(); ++it )
00312     if ( attrOrder.find( (*it).name() ) == attrOrder.end() )
00313       unknownEntries.push_back( *it );
00314 
00315   // process the known attrs in the desired order
00316   for ( TQStringList::const_iterator oit = attrOrder.begin() ; oit != attrOrder.end() ; ++oit )
00317     if ( *oit == "_X_" ) {
00318       // insert the unknown attrs
00319       std::copy( unknownEntries.begin(), unknownEntries.end(),
00320          std::back_inserter( result ) );
00321       unknownEntries.clear(); // don't produce dup's
00322     } else {
00323       for ( Kleo::DN::const_iterator dnit = dn.begin() ; dnit != dn.end() ; ++dnit )
00324     if ( (*dnit).name() == *oit )
00325       result.push_back( *dnit );
00326     }
00327   
00328   return result;
00329 }
00330 
00331 //
00332 //
00333 // class DN
00334 //
00335 //
00336 
00337 Kleo::DN::DN() {
00338   d = new Private();
00339   d->ref();
00340 }
00341 
00342 Kleo::DN::DN( const TQString & dn ) {
00343   d = new Private();
00344   d->ref();
00345   d->attributes = parse_dn( dn );
00346 }
00347 
00348 Kleo::DN::DN( const char * utf8DN ) {
00349   d = new Private();
00350   d->ref();
00351   if ( utf8DN )
00352     d->attributes = parse_dn( (const unsigned char*)utf8DN );
00353 }
00354 
00355 Kleo::DN::DN( const DN & other )
00356   : d( other.d )
00357 {
00358   if ( d ) d->ref();
00359 }
00360 
00361 Kleo::DN::~DN() {
00362   if ( d ) d->unref();
00363 }
00364 
00365 const Kleo::DN & Kleo::DN::operator=( const DN & that ) {
00366   if ( this->d == that.d )
00367     return *this;
00368 
00369   if ( that.d )
00370     that.d->ref();
00371   if ( this->d )
00372     this->d->unref();
00373 
00374   this->d = that.d;
00375 
00376   return *this;
00377 }
00378 
00379 TQString Kleo::DN::prettyDN() const {
00380   if ( !d )
00381     return TQString::null;
00382   if ( d->reorderedAttributes.empty() )
00383     d->reorderedAttributes = reorder_dn( d->attributes );
00384   return serialise( d->reorderedAttributes );
00385 }
00386 
00387 TQString Kleo::DN::dn() const {
00388   return d ? serialise( d->attributes ) : TQString::null ;
00389 }
00390 
00391 // static
00392 TQString Kleo::DN::escape( const TQString & value ) {
00393     return dn_escape( value );
00394 }
00395 
00396 void Kleo::DN::detach() {
00397   if ( !d ) {
00398     d = new Kleo::DN::Private();
00399     d->ref();
00400   } else if ( d->refCount() > 1 ) {
00401     Kleo::DN::Private * d_save = d;
00402     d = new Kleo::DN::Private( *d );
00403     d->ref();
00404     d_save->unref();
00405   }
00406 }
00407 
00408 void Kleo::DN::append( const Attribute & attr ) {
00409   detach();
00410   d->attributes.push_back( attr );
00411   d->reorderedAttributes.clear();
00412 }
00413 
00414 TQString Kleo::DN::operator[]( const TQString & attr ) const {
00415   if ( !d )
00416     return TQString::null;
00417   const TQString attrUpper = attr.upper();
00418   for ( TQValueVector<Attribute>::const_iterator it = d->attributes.begin() ;
00419     it != d->attributes.end() ; ++it )
00420     if ( (*it).name() == attrUpper )
00421       return (*it).value();
00422   return TQString::null;
00423 }
00424 
00425 static TQValueVector<Kleo::DN::Attribute> empty;
00426 
00427 Kleo::DN::const_iterator Kleo::DN::begin() const {
00428   return d ? d->attributes.begin() : empty.begin() ;
00429 }
00430 
00431 Kleo::DN::const_iterator Kleo::DN::end() const {
00432   return d ? d->attributes.end() : empty.end() ;
00433 }
00434 
00435 
00437 
00438 namespace {
00439   struct ltstr {
00440     bool operator()( const char * s1, const char * s2 ) const {
00441       return qstrcmp( s1, s2 ) < 0 ;
00442     }
00443   };
00444 }
00445 
00446 static const char * defaultOrder[] = {
00447   "CN", "L", "_X_", "OU", "O", "C"
00448 };
00449 
00450 std::pair<const char*,const char*> attributeLabels[] = {
00451 #define MAKE_PAIR(x,y) std::pair<const char*,const char*>( x, y )
00452   MAKE_PAIR( "CN", I18N_NOOP("Common name") ),
00453   MAKE_PAIR( "SN", I18N_NOOP("Surname") ),
00454   MAKE_PAIR( "GN", I18N_NOOP("Given name") ),
00455   MAKE_PAIR( "L",  I18N_NOOP("Location") ),
00456   MAKE_PAIR( "T",  I18N_NOOP("Title") ),
00457   MAKE_PAIR( "OU", I18N_NOOP("Organizational unit") ),
00458   MAKE_PAIR( "O",  I18N_NOOP("Organization") ),
00459   MAKE_PAIR( "PC", I18N_NOOP("Postal code") ),
00460   MAKE_PAIR( "C",  I18N_NOOP("Country code") ),
00461   MAKE_PAIR( "SP", I18N_NOOP("State or province") ),
00462   MAKE_PAIR( "DC", I18N_NOOP("Domain component") ),
00463   MAKE_PAIR( "BC", I18N_NOOP("Business category") ),
00464   MAKE_PAIR( "EMAIL", I18N_NOOP("Email address") ),
00465   MAKE_PAIR( "MAIL", I18N_NOOP("Mail address") ),
00466   MAKE_PAIR( "MOBILE", I18N_NOOP("Mobile phone number") ),
00467   MAKE_PAIR( "TEL", I18N_NOOP("Telephone number") ),
00468   MAKE_PAIR( "FAX", I18N_NOOP("Fax number") ),
00469   MAKE_PAIR( "STREET", I18N_NOOP("Street address") ),
00470   MAKE_PAIR( "UID", I18N_NOOP("Unique ID") )
00471 #undef MAKE_PAIR
00472 };
00473 static const unsigned int numAttributeLabels = sizeof attributeLabels / sizeof *attributeLabels ;
00474 
00475 class Kleo::DNAttributeMapper::Private {
00476 public:
00477   Private();
00478   std::map<const char*,const char*,ltstr> map;
00479   TQStringList attributeOrder;
00480 };
00481 
00482 Kleo::DNAttributeMapper::Private::Private()
00483   : map( attributeLabels, attributeLabels + numAttributeLabels ) {}
00484 
00485 Kleo::DNAttributeMapper::DNAttributeMapper() {
00486   d = new Private();
00487   const KConfigGroup config( kapp->config(), "DN" );
00488   d->attributeOrder = config.readListEntry( "AttributeOrder" );
00489   if ( d->attributeOrder.empty() )
00490     std::copy( defaultOrder, defaultOrder + sizeof defaultOrder / sizeof *defaultOrder,
00491            std::back_inserter( d->attributeOrder ) );
00492   mSelf = this;
00493 }
00494 
00495 Kleo::DNAttributeMapper::~DNAttributeMapper() {
00496   mSelf = 0;
00497   delete d; d = 0;
00498 }
00499 
00500 Kleo::DNAttributeMapper * Kleo::DNAttributeMapper::mSelf = 0;
00501 
00502 const Kleo::DNAttributeMapper * Kleo::DNAttributeMapper::instance() {
00503   if ( !mSelf )
00504     (void)new DNAttributeMapper();
00505   return mSelf;
00506 }
00507 
00508 TQString Kleo::DNAttributeMapper::name2label( const TQString & s ) const {
00509   const std::map<const char*,const char*,ltstr>::const_iterator it
00510     = d->map.find( s.stripWhiteSpace().upper().latin1() );
00511   if ( it == d->map.end() )
00512     return TQString::null;
00513   return i18n( it->second );
00514 }
00515 
00516 TQStringList Kleo::DNAttributeMapper::names() const {
00517   TQStringList result;
00518   for ( std::map<const char*,const char*,ltstr>::const_iterator it = d->map.begin() ; it != d->map.end() ; ++it )
00519     result.push_back( it->first );
00520   return result;
00521 }
00522 
00523 const TQStringList & Kleo::DNAttributeMapper::attributeOrder() const {
00524   return d->attributeOrder;
00525 }
00526 
00527 void Kleo::DNAttributeMapper::setAttributeOrder( const TQStringList & order ) {
00528   d->attributeOrder = order;
00529   if ( order.empty() )
00530     std::copy( defaultOrder, defaultOrder + sizeof defaultOrder / sizeof *defaultOrder,
00531            std::back_inserter( d->attributeOrder ) );
00532   KConfigGroup config( kapp->config(), "DN" );
00533   config.writeEntry( "AttributeOrder", order );
00534 }
00535 
00536 Kleo::DNAttributeOrderConfigWidget * Kleo::DNAttributeMapper::configWidget( TQWidget * parent, const char * name ) const {
00537   return new DNAttributeOrderConfigWidget( mSelf, parent, name );
00538 }