]> git.sur5r.net Git - openldap/commitdiff
C portability from HEAD
authorKurt Zeilenga <kurt@openldap.org>
Sun, 24 Feb 2002 00:24:43 +0000 (00:24 +0000)
committerKurt Zeilenga <kurt@openldap.org>
Sun, 24 Feb 2002 00:24:43 +0000 (00:24 +0000)
libraries/libldap/getdn.c
libraries/liblunicode/ucstr.c
servers/slapd/acl.c
servers/slapd/backglue.c [new file with mode: 0644]
servers/slapd/entry.c
servers/slapd/sets.c [new file with mode: 0644]
servers/slapd/tools/slapadd.c [new file with mode: 0644]

index 2ea0d037c4eabcf78c9b76a441c8d589a86770ba..e0d36154a6baf8d60edc471ee2b709d0dcb2f1bf 100644 (file)
@@ -1,33 +1,83 @@
+/* $OpenLDAP$ */
 /*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/*  Portions
  *  Copyright (c) 1994 Regents of the University of Michigan.
  *  All rights reserved.
  *
  *  getdn.c
  */
 
-#ifndef lint 
-static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
-#endif
+#include "portable.h"
 
 #include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#ifdef MACOS
-#include <stdlib.h>
-#include "macos.h"
-#else /* MACOS */
-#if defined( DOS ) || defined( _WIN32 )
-#include <malloc.h>
-#include "msdos.h"
-#else /* DOS */
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif /* DOS */
-#endif /* MACOS */
-
-#include "lber.h"
-#include "ldap.h"
 
+#include <ac/stdlib.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_schema.h"
+
+/* extension to UFN that turns trailing "dc=value" rdns in DNS style,
+ * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
+#define DC_IN_UFN
+#define PRETTY_ESCAPE
+
+/* parsing/printing routines */
+static int str2strval( const char *str, ber_len_t stoplen, struct berval *val, 
+               const char **next, unsigned flags, unsigned *retFlags );
+static int DCE2strval( const char *str, struct berval *val, 
+               const char **next, unsigned flags );
+static int IA52strval( const char *str, struct berval *val, 
+               const char **next, unsigned flags );
+static int quotedIA52strval( const char *str, struct berval *val, 
+               const char **next, unsigned flags );
+static int hexstr2binval( const char *str, struct berval *val, 
+               const char **next, unsigned flags );
+static int hexstr2bin( const char *str, char *c );
+static int byte2hexpair( const char *val, char *pair );
+static int binval2hexstr( struct berval *val, char *str );
+static int strval2strlen( struct berval *val, unsigned flags, 
+               ber_len_t *len );
+static int strval2str( struct berval *val, char *str, unsigned flags, 
+               ber_len_t *len );
+static int strval2IA5strlen( struct berval *val, unsigned flags,
+               ber_len_t *len );
+static int strval2IA5str( struct berval *val, char *str, unsigned flags, 
+               ber_len_t *len );
+static int strval2DCEstrlen( struct berval *val, unsigned flags,
+               ber_len_t *len );
+static int strval2DCEstr( struct berval *val, char *str, unsigned flags, 
+               ber_len_t *len );
+static int strval2ADstrlen( struct berval *val, unsigned flags,
+               ber_len_t *len );
+static int strval2ADstr( struct berval *val, char *str, unsigned flags, 
+               ber_len_t *len );
+static int dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN );
+
+/* AVA helpers */
+static LDAPAVA * ldapava_new(
+       const struct berval *attr, const struct berval *val, unsigned flags );
+
+/* Higher level helpers */
+static int rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len,
+               int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
+static int rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len,
+               int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
+static int rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len  );
+static int rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len );
+static int rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flag, ber_len_t *len, int first );
+static int rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first );
+
+/*
+ * RFC 1823 ldap_get_dn
+ */
 char *
 ldap_get_dn( LDAP *ld, LDAPMessage *entry )
 {
@@ -42,7 +92,7 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry )
        }
 
        tmp = *entry->lm_ber;   /* struct copy */
-       if ( ber_scanf( &tmp, "{a", &dn ) == LBER_ERROR ) {
+       if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
                ld->ld_errno = LDAP_DECODING_ERROR;
                return( NULL );
        }
@@ -50,220 +100,3142 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry )
        return( dn );
 }
 
+/*
+ * RFC 1823 ldap_dn2ufn
+ */
 char *
-ldap_dn2ufn( char *dn )
+ldap_dn2ufn( LDAP_CONST char *dn )
 {
-       char    *p, *ufn, *r;
-       int     state;
+       char    *out = NULL;
 
        Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
 
-       if ( ldap_is_dns_dn( dn ) || ( p = strchr( dn, '=' )) == NULL )
-               return( strdup( dn ));
+       ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
+               &out, LDAP_DN_FORMAT_UFN );
+       
+       return( out );
+}
+
+/*
+ * RFC 1823 ldap_explode_dn
+ */
+char **
+ldap_explode_dn( LDAP_CONST char *dn, int notypes )
+{
+       LDAPDN  *tmpDN;
+       char    **values = NULL;
+       int     iRDN;
+       unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
+       
+       Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
+
+       if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP ) 
+                       != LDAP_SUCCESS ) {
+               return NULL;
+       }
 
-       ufn = strdup( ++p );
+       if( tmpDN == NULL ) {
+               values = LDAP_MALLOC( sizeof( char * ) );
+               if( values == NULL ) return NULL;
 
-#define INQUOTE                1
-#define OUTQUOTE       2
-       state = OUTQUOTE;
-       for ( p = ufn, r = ufn; *p; p++ ) {
-               switch ( *p ) {
-               case '\\':
-                       if ( *++p == '\0' )
-                               p--;
-                       else {
-                               *r++ = '\\';
-                               *r++ = *p;
-                       }
-                       break;
-               case '"':
-                       if ( state == INQUOTE )
-                               state = OUTQUOTE;
-                       else
-                               state = INQUOTE;
-                       *r++ = *p;
-                       break;
-               case ';':
-               case ',':
-                       if ( state == OUTQUOTE )
-                               *r++ = ',';
-                       else
-                               *r++ = *p;
-                       break;
-               case '=':
-                       if ( state == INQUOTE )
-                               *r++ = *p;
-                       else {
-                               char    *rsave = r;
-
-                               *r-- = '\0';
-                               while ( !isspace( *r ) && *r != ';'
-                                   && *r != ',' && r > ufn )
-                                       r--;
-                               r++;
-
-                               if ( strcasecmp( r, "c" )
-                                   && strcasecmp( r, "o" )
-                                   && strcasecmp( r, "ou" )
-                                   && strcasecmp( r, "st" )
-                                   && strcasecmp( r, "l" )
-                                   && strcasecmp( r, "cn" ) ) {
-                                       r = rsave;
-                                       *r++ = '=';
-                               }
-                       }
-                       break;
-               default:
-                       *r++ = *p;
-                       break;
-               }
+               values[0] = NULL;
+               return values;
+       }
+
+       for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ );
+
+       values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
+       if ( values == NULL ) {
+               ldap_dnfree( tmpDN );
+               return NULL;
+       }
+
+       for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ ) {
+               ldap_rdn2str( tmpDN[ 0 ][ iRDN ], &values[ iRDN ], flag );
        }
-       *r = '\0';
+       ldap_dnfree( tmpDN );
+       values[ iRDN ] = NULL;
 
-       return( ufn );
+       return values;
 }
 
 char **
-ldap_explode_dns( char *dn )
+ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
 {
-       int     ncomps, maxcomps;
-       char    *s;
-       char    **rdns;
+       LDAPRDN         *tmpRDN;
+       char            **values = NULL;
+       const char      *p;
+       int             iAVA;
+       
+       Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
 
-       if ( (rdns = (char **) malloc( 8 * sizeof(char *) )) == NULL ) {
+       /*
+        * we only parse the first rdn
+        * FIXME: we prefer efficiency over checking if the _ENTIRE_
+        * dn can be parsed
+        */
+       if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP ) 
+                       != LDAP_SUCCESS ) {
                return( NULL );
        }
 
-       maxcomps = 8;
-       ncomps = 0;
-       for ( s = strtok( dn, "@." ); s != NULL; s = strtok( NULL, "@." ) ) {
-               if ( ncomps == maxcomps ) {
-                       maxcomps *= 2;
-                       if ( (rdns = (char **) realloc( rdns, maxcomps *
-                           sizeof(char *) )) == NULL ) {
-                               return( NULL );
+       for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) ;
+       values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
+       if ( values == NULL ) {
+               ldap_rdnfree( tmpRDN );
+               return( NULL );
+       }
+
+       for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) {
+               ber_len_t       l = 0, vl, al = 0;
+               char            *str;
+               LDAPAVA         *ava = tmpRDN[ 0 ][ iAVA ];
+               
+               if ( ava->la_flags == LDAP_AVA_BINARY ) {
+                       vl = 1 + 2 * ava->la_value.bv_len;
+
+               } else {
+                       if ( strval2strlen( &ava->la_value, 
+                                               ava->la_flags, &vl ) ) {
+                               goto error_return;
                        }
                }
-               rdns[ncomps++] = strdup( s );
+               
+               if ( !notypes ) {
+                       al = ava->la_attr.bv_len;
+                       l = vl + ava->la_attr.bv_len + 1;
+
+                       str = LDAP_MALLOC( l + 1 );
+                       AC_MEMCPY( str, ava->la_attr.bv_val, 
+                                       ava->la_attr.bv_len );
+                       str[ al++ ] = '=';
+
+               } else {
+                       l = vl;
+                       str = LDAP_MALLOC( l + 1 );
+               }
+               
+               if ( ava->la_flags == LDAP_AVA_BINARY ) {
+                       str[ al++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
+                               goto error_return;
+                       }
+
+               } else {
+                       if ( strval2str( &ava->la_value, &str[ al ], 
+                                       ava->la_flags, &vl ) ) {
+                               goto error_return;
+                       }
+               }
+
+               str[ l ] = '\0';
+               values[ iAVA ] = str;
        }
-       rdns[ncomps] = NULL;
+       values[ iAVA ] = NULL;
 
-       return( rdns );
+       ldap_rdnfree( tmpRDN );
+
+       return( values );
+
+error_return:;
+       LBER_VFREE( values );
+       ldap_rdnfree( tmpRDN );
+       return( NULL );
 }
 
-char **
-ldap_explode_dn( char *dn, int notypes )
+char *
+ldap_dn2dcedn( LDAP_CONST char *dn )
 {
-       char    *p, *q, *rdnstart, **rdns = NULL;
-       int     state, count = 0, endquote, len;
+       char    *out = NULL;
 
-       Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
+
+       ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
+                                  &out, LDAP_DN_FORMAT_DCE );
+
+       return( out );
+}
+
+char *
+ldap_dcedn2dn( LDAP_CONST char *dce )
+{
+       char    *out = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
+
+       ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
+
+       return( out );
+}
+
+char *
+ldap_dn2ad_canonical( LDAP_CONST char *dn )
+{
+       char    *out = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 );
+
+       ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
+                      &out, LDAP_DN_FORMAT_AD_CANONICAL );
+
+       return( out );
+}
+
+/*
+ * function that changes the string representation of dnin
+ * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
+ * 
+ * fin can be one of:
+ *     LDAP_DN_FORMAT_LDAP             (rfc 2253 and ldapbis liberal, 
+ *                                     plus some rfc 1779)
+ *     LDAP_DN_FORMAT_LDAPV3           (rfc 2253 and ldapbis)
+ *     LDAP_DN_FORMAT_LDAPV2           (rfc 1779)
+ *     LDAP_DN_FORMAT_DCE              (?)
+ *
+ * fout can be any of the above except
+ *     LDAP_DN_FORMAT_LDAP
+ * plus:
+ *     LDAP_DN_FORMAT_UFN              (rfc 1781, partial and with extensions)
+ *     LDAP_DN_FORMAT_AD_CANONICAL     (?)
+ */
+int
+ldap_dn_normalize( LDAP_CONST char *dnin,
+       unsigned fin, char **dnout, unsigned fout )
+{
+       int     rc;
+       LDAPDN  *tmpDN = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 );
+
+       assert( dnout );
+
+       *dnout = NULL;
+
+       if ( dnin == NULL ) {
+               return( LDAP_SUCCESS );
+       }
 
-       if ( ldap_is_dns_dn( dn ) ) {
-               return( ldap_explode_dns( dn ) );
+       rc = ldap_str2dn( dnin , &tmpDN, fin );
+       if ( rc != LDAP_SUCCESS ) {
+               return( rc );
        }
 
-       rdnstart = dn;
-       p = dn-1;
-       state = OUTQUOTE;
+       rc = ldap_dn2str( tmpDN, dnout, fout );
 
-       do {
+       ldap_dnfree( tmpDN );
 
-               ++p;
-               switch ( *p ) {
-               case '\\':
-                       if ( *++p == '\0' )
-                               p--;
-                       break;
-               case '"':
-                       if ( state == INQUOTE )
-                               state = OUTQUOTE;
-                       else
-                               state = INQUOTE;
-                       break;
-               case ';':
-               case ',':
-               case '\0':
-                       if ( state == OUTQUOTE ) {
-                               ++count;
-                               if ( rdns == NULL ) {
-                                       if (( rdns = (char **)malloc( 8
-                                                * sizeof( char *))) == NULL )
-                                               return( NULL );
-                               } else if ( count >= 8 ) {
-                                       if (( rdns = (char **)realloc( rdns,
-                                               (count+1) * sizeof( char *)))
-                                               == NULL )
-                                               return( NULL );
-                               }
-                               rdns[ count ] = NULL;
-                               endquote = 0;
-                               if ( notypes ) {
-                                       for ( q = rdnstart;
-                                           q < p && *q != '='; ++q ) {
-                                               ;
-                                       }
-                                       if ( q < p ) {
-                                               rdnstart = ++q;
-                                       }
-                                       if ( *rdnstart == '"' ) {
-                                               ++rdnstart;
-                                       }
-                                       
-                                       if ( *(p-1) == '"' ) {
-                                               endquote = 1;
-                                               --p;
-                                       }
+       return( rc );
+}
+
+/* States */
+#define B4AVA                  0x0000
+
+/* #define     B4ATTRTYPE              0x0001 */
+#define B4OIDATTRTYPE          0x0002
+#define B4STRINGATTRTYPE       0x0003
+
+#define B4AVAEQUALS            0x0100
+#define B4AVASEP               0x0200
+#define B4RDNSEP               0x0300
+#define GOTAVA                 0x0400
+
+#define B4ATTRVALUE            0x0010
+#define B4STRINGVALUE          0x0020
+#define B4IA5VALUEQUOTED       0x0030
+#define B4IA5VALUE             0x0040
+#define B4BINARYVALUE          0x0050
+
+/*
+ * Helpers (mostly from slap.h)
+ * c is assumed to Unicode in an ASCII compatible format (UTF-8)
+ * Macros assume "C" Locale (ASCII)
+ */
+#define LDAP_DN_ASCII_SPACE(c) \
+       ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
+#define LDAP_DN_ASCII_LOWER(c)         LDAP_LOWER(c)
+#define LDAP_DN_ASCII_UPPER(c)         LDAP_UPPER(c)
+#define LDAP_DN_ASCII_ALPHA(c)         LDAP_ALPHA(c)
+
+#define LDAP_DN_ASCII_DIGIT(c)         LDAP_DIGIT(c)
+#define LDAP_DN_ASCII_LCASE_HEXALPHA(c)        LDAP_HEXLOWER(c)
+#define LDAP_DN_ASCII_UCASE_HEXALPHA(c)        LDAP_HEXUPPER(c)
+#define LDAP_DN_ASCII_HEXDIGIT(c)      LDAP_HEX(c)
+#define LDAP_DN_ASCII_ALNUM(c)         LDAP_ALNUM(c)
+#define LDAP_DN_ASCII_PRINTABLE(c)     ( (c) >= ' ' && (c) <= '~' )
+
+/* attribute type */
+#define LDAP_DN_OID_LEADCHAR(c)                LDAP_DIGIT(c)
+#define LDAP_DN_DESC_LEADCHAR(c)       LDAP_ALPHA(c)
+#define LDAP_DN_DESC_CHAR(c)           LDAP_LDH(c)
+#define LDAP_DN_LANG_SEP(c)            ( (c) == ';' )
+#define LDAP_DN_ATTRDESC_CHAR(c) \
+       ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
+
+/* special symbols */
+#define LDAP_DN_AVA_EQUALS(c)          ( (c) == '=' )
+#define LDAP_DN_AVA_SEP(c)             ( (c) == '+' )
+#define LDAP_DN_RDN_SEP(c)             ( (c) == ',' )
+#define LDAP_DN_RDN_SEP_V2(c)          ( LDAP_DN_RDN_SEP(c) || (c) == ';' )
+#define LDAP_DN_OCTOTHORPE(c)          ( (c) == '#' )
+#define LDAP_DN_QUOTES(c)              ( (c) == '\"' )
+#define LDAP_DN_ESCAPE(c)              ( (c) == '\\' )
+#define LDAP_DN_VALUE_END(c) \
+       ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) )
+#define LDAP_DN_NE(c) \
+       ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
+         || LDAP_DN_QUOTES(c) || (c) == '<' || (c) == '>' )
+#define LDAP_DN_MAYESCAPE(c) \
+       ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
+         || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_NEEDESCAPE(c) \
+       ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
+#define LDAP_DN_NEEDESCAPE_LEAD(c)     LDAP_DN_MAYESCAPE(c)
+#define LDAP_DN_NEEDESCAPE_TRAIL(c) \
+       ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
+#define LDAP_DN_WILLESCAPE_CHAR(c) \
+       ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_IS_PRETTY(f)           ( (f) & LDAP_DN_PRETTY )
+#define LDAP_DN_WILLESCAPE_HEX(f, c) \
+       ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
+
+/* LDAPv2 */
+#define        LDAP_DN_VALUE_END_V2(c) \
+       ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) )
+/* RFC 1779 */
+#define        LDAP_DN_V2_SPECIAL(c) \
+         ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \
+           || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \
+           || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_V2_PAIR(c) \
+         ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) )
+
+/*
+ * DCE (mostly from Luke Howard and IBM implementation for AIX)
+ *
+ * From: "Application Development Guide - Directory Services" (FIXME: add link?)
+ * Here escapes and valid chars for GDS are considered; as soon as more
+ * specific info is found, the macros will be updated.
+ *
+ * Chars:      'a'-'z', 'A'-'Z', '0'-'9', 
+ *             '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '.
+ *
+ * Metachars:  '/', ',', '=', '\'.
+ *
+ * the '\' is used to escape other metachars.
+ *
+ * Assertion:          '='
+ * RDN separator:      '/'
+ * AVA separator:      ','
+ * 
+ * Attribute types must start with alphabetic chars and can contain 
+ * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
+ */
+#define LDAP_DN_RDN_SEP_DCE(c)         ( (c) == '/' )
+#define LDAP_DN_AVA_SEP_DCE(c)         ( (c) == ',' )
+#define LDAP_DN_ESCAPE_DCE(c)          ( LDAP_DN_ESCAPE(c) )
+#define        LDAP_DN_VALUE_END_DCE(c) \
+       ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) )
+#define LDAP_DN_NEEDESCAPE_DCE(c) \
+       ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* AD Canonical */
+#define LDAP_DN_RDN_SEP_AD(c)          ( (c) == '/' )
+#define LDAP_DN_ESCAPE_AD(c)           ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_AVA_SEP_AD(c)          ( (c) == ',' )  /* assume same as DCE */
+#define        LDAP_DN_VALUE_END_AD(c) \
+       ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) )
+#define LDAP_DN_NEEDESCAPE_AD(c) \
+       ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* generics */
+#define LDAP_DN_HEXPAIR(s) \
+       ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
+/* better look at the AttributeDescription? */
+
+/* FIXME: no composite rdn or non-"dc" types, right?
+ * (what about "dc" in OID form?) */
+/* FIXME: we do not allow binary values in domain, right? */
+/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
+/* NOTE: don't use strcasecmp() as it is locale specific! */
+#define        LDAP_DC_ATTR    "dc"
+#define        LDAP_DC_ATTRU   "DC"
+#define LDAP_DN_IS_RDN_DC( r ) \
+       ( (r) && (r)[0][0] && !(r)[0][1] \
+         && ((r)[0][0]->la_flags == LDAP_AVA_STRING) \
+         && ((r)[0][0]->la_attr.bv_len == 2) \
+         && (((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
+               || ((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
+         && (((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
+               || ((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
+
+/* Composite rules */
+#define LDAP_DN_ALLOW_ONE_SPACE(f) \
+       ( LDAP_DN_LDAPV2(f) \
+         || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
+#define LDAP_DN_ALLOW_SPACES(f) \
+       ( LDAP_DN_LDAPV2(f) \
+         || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
+#define LDAP_DN_LDAP(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
+#define LDAP_DN_LDAPV3(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
+#define LDAP_DN_LDAPV2(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 )
+#define LDAP_DN_DCE(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE )
+#define LDAP_DN_UFN(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN )
+#define LDAP_DN_ADC(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL )
+#define LDAP_DN_FORMAT(f)              ( (f) & LDAP_DN_FORMAT_MASK )
+
+/*
+ * LDAPAVA helpers (will become part of the API for operations 
+ * on structural representations of DNs).
+ */
+LDAPAVA *
+ldapava_new( const struct berval *attr, const struct berval *val, 
+               unsigned flags )
+{
+       LDAPAVA *ava;
+
+       assert( attr );
+       assert( val );
+
+       ava = LDAP_MALLOC( sizeof( LDAPAVA ) + attr->bv_len + 1 );
+       
+       /* should we test it? */
+       if ( ava == NULL ) {
+               return( NULL );
+       }
+
+       ava->la_attr.bv_len = attr->bv_len;
+       ava->la_attr.bv_val = (char *)(ava+1);
+       AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
+       ava->la_attr.bv_val[attr->bv_len] = '\0';
+
+       ava->la_value = *val;
+       ava->la_flags = flags;
+
+       ava->la_private = NULL;
+
+       return( ava );
+}
+
+void
+ldap_avafree( LDAPAVA *ava )
+{
+       assert( ava );
+
+#if 0
+       /* ava's private must be freed by caller
+        * (at present let's skip this check because la_private
+        * basically holds static data) */
+       assert( ava->la_private == NULL );
+#endif
+
+#if 0
+       /* la_attr is now contiguous with ava, not freed separately */
+       LDAP_FREE( ava->la_attr.bv_val );
+#endif
+       LDAP_FREE( ava->la_value.bv_val );
+
+       LDAP_FREE( ava );
+}
+
+void
+ldap_rdnfree( LDAPRDN *rdn )
+{
+       int iAVA;
+       
+       if ( rdn == NULL ) {
+               return;
+       }
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               ldap_avafree( rdn[ 0 ][ iAVA ] );
+       }
+
+       LDAP_FREE( rdn );
+}
+
+void
+ldap_dnfree( LDAPDN *dn )
+{
+       int iRDN;
+       
+       if ( dn == NULL ) {
+               return;
+       }
+
+       for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+               ldap_rdnfree( dn[ 0 ][ iRDN ] );
+       }
+
+       LDAP_FREE( dn );
+}
+
+/*
+ * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
+ * into a structural representation of the DN, by separating attribute
+ * types and values encoded in the more appropriate form, which is
+ * string or OID for attribute types and binary form of the BER encoded
+ * value or Unicode string. Formats different from LDAPv3 are parsed
+ * according to their own rules and turned into the more appropriate
+ * form according to LDAPv3.
+ *
+ * NOTE: I realize the code is getting spaghettish; it is rather
+ * experimental and will hopefully turn into something more simple
+ * and readable as soon as it works as expected.
+ */
+
+/*
+ * Default sizes of AVA and RDN static working arrays; if required
+ * the are dynamically resized.  The values can be tuned in case
+ * of special requirements (e.g. very deep DN trees or high number 
+ * of AVAs per RDN).
+ */
+#define        TMP_AVA_SLOTS   8
+#define        TMP_RDN_SLOTS   32
+
+int
+ldap_str2dn( LDAP_CONST char *str, LDAPDN **dn, unsigned flags )
+{
+       struct berval   bv;
+
+       assert( str );
+
+       bv.bv_len = strlen( str );
+       bv.bv_val = (char *) str;
+       
+       return ldap_bv2dn( &bv, dn, flags );
+}
+
+int
+ldap_bv2dn( struct berval *bv, LDAPDN **dn, unsigned flags )
+{
+       const char      *p;
+       int             rc = LDAP_DECODING_ERROR;
+       int             nrdns = 0;
+
+       LDAPDN          *newDN = NULL;
+       LDAPRDN         *newRDN = NULL, *tmpDN_[TMP_RDN_SLOTS], **tmpDN = tmpDN_;
+       int             num_slots = TMP_RDN_SLOTS;
+       char            *str = bv->bv_val;
+       char            *end = str + bv->bv_len;
+       
+       assert( bv );
+       assert( bv->bv_val );
+       assert( dn );
+
+       Debug( LDAP_DEBUG_TRACE, "=> ldap_bv2dn(%s,%u)\n%s", str, flags, "" );
+
+       *dn = NULL;
+
+       switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAP:
+       case LDAP_DN_FORMAT_LDAPV3:
+       case LDAP_DN_FORMAT_LDAPV2:
+       case LDAP_DN_FORMAT_DCE:
+               break;
+
+       /* unsupported in str2dn */
+       case LDAP_DN_FORMAT_UFN:
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               return LDAP_PARAM_ERROR;
+
+       case LDAP_DN_FORMAT_LBER:
+       default:
+               return LDAP_PARAM_ERROR;
+       }
+
+       if ( bv->bv_len == 0 ) {
+               return LDAP_SUCCESS;
+       }
+
+       if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+               /* value must have embedded NULs */
+               return LDAP_DECODING_ERROR;
+       }
+
+       p = str;
+       if ( LDAP_DN_DCE( flags ) ) {
+               
+               /* 
+                * (from Luke Howard: thnx) A RDN separator is required
+                * at the beginning of an (absolute) DN.
+                */
+               if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+                       goto parsing_error;
+               }
+               p++;
+
+       /*
+        * actually we do not want to accept by default the DCE form,
+        * we do not want to auto-detect it
+        */
+#if 0
+       } else if ( LDAP_DN_LDAP( flags ) ) {
+               /*
+                * if dn starts with '/' let's make it a DCE dn
+                */
+               if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+                       flags |= LDAP_DN_FORMAT_DCE;
+                       p++;
+               }
+#endif
+       }
+
+       for ( ; p < end; p++ ) {
+               int             err;
+               struct berval   tmpbv;
+               tmpbv.bv_len = bv->bv_len - ( p - str );
+               tmpbv.bv_val = (char *)p;
+               
+               err = ldap_bv2rdn( &tmpbv, &newRDN, (char **) &p, flags );
+               if ( err != LDAP_SUCCESS ) {
+                       goto parsing_error;
+               }
+
+               /* 
+                * We expect a rdn separator
+                */
+               if ( p < end && p[ 0 ] ) {
+                       switch ( LDAP_DN_FORMAT( flags ) ) {
+                       case LDAP_DN_FORMAT_LDAPV3:
+                               if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
+                                       rc = LDAP_DECODING_ERROR;
+                                       goto parsing_error;
+                               }
+                               break;
+       
+                       case LDAP_DN_FORMAT_LDAP:
+                       case LDAP_DN_FORMAT_LDAPV2:
+                               if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
+                                       rc = LDAP_DECODING_ERROR;
+                                       goto parsing_error;
                                }
+                               break;
+       
+                       case LDAP_DN_FORMAT_DCE:
+                               if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+                                       rc = LDAP_DECODING_ERROR;
+                                       goto parsing_error;
+                               }
+                               break;
+                       }
+               }
+
 
-                               len = p - rdnstart;
-                               if (( rdns[ count-1 ] = (char *)calloc( 1,
-                                   len + 1 )) != NULL ) {
-                                       SAFEMEMCPY( rdns[ count-1 ], rdnstart,
-                                           len );
-                                       rdns[ count-1 ][ len ] = '\0';
+               tmpDN[nrdns++] = newRDN;
+               newRDN = NULL;
+
+               /*
+                * make the static RDN array dynamically rescalable
+                */
+               if ( nrdns == num_slots ) {
+                       LDAPRDN **tmp;
+
+                       if ( tmpDN == tmpDN_ ) {
+                               tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPRDN * ) );
+                               if ( tmp == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       goto parsing_error;
                                }
+                               AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
 
-                               /*
-                                *  Don't forget to increment 'p' back to where
-                                *  it should be.  If we don't, then we will
-                                *  never get past an "end quote."
-                                */
-                               if ( endquote == 1 )
-                                       p++;
+                       } else {
+                               tmp = LDAP_REALLOC( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ) );
+                               if ( tmp == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       goto parsing_error;
+                               }
+                       }
+
+                       tmpDN = tmp;
+                       num_slots *= 2;
+               }
+                               
+               if ( p >= end || p[ 0 ] == '\0' ) {
+                       /* 
+                        * the DN is over, phew
+                        */
+                       newDN = (LDAPDN *)LDAP_MALLOC( sizeof(LDAPDN) +
+                               sizeof(LDAPRDN *) * (nrdns+1));
+                       if ( newDN == NULL ) {
+                               rc = LDAP_NO_MEMORY;
+                               goto parsing_error;
+                       } else {
+                               int i;
+
+                               newDN[0] = (LDAPRDN **)(newDN+1);
 
-                               rdnstart = *p ? p + 1 : p;
-                               while ( isspace( *rdnstart ))
-                                       ++rdnstart;
+                               if ( LDAP_DN_DCE( flags ) ) {
+                                       /* add in reversed order */
+                                       for ( i=0; i<nrdns; i++ )
+                                               newDN[0][i] = tmpDN[nrdns-1-i];
+                               } else {
+                                       for ( i=0; i<nrdns; i++ )
+                                               newDN[0][i] = tmpDN[i];
+                               }
+                               newDN[0][nrdns] = NULL;
+                               rc = LDAP_SUCCESS;
                        }
-                       break;
+                       goto return_result;
                }
-       } while ( *p );
+       }
+       
+parsing_error:;
+       if ( newRDN ) {
+               ldap_rdnfree( newRDN );
+       }
 
-       return( rdns );
-}
+       for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
+               ldap_rdnfree( tmpDN[nrdns] );
+       }
 
+return_result:;
+
+       if ( tmpDN != tmpDN_ ) {
+               LDAP_FREE( tmpDN );
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= ldap_bv2dn(%s,%u)=%d\n", str, flags, rc );
+       *dn = newDN;
+       
+       return( rc );
+}
 
+/*
+ * ldap_str2rdn
+ *
+ * Parses a relative DN according to flags up to a rdn separator 
+ * or to the end of str.
+ * Returns the rdn and a pointer to the string continuation, which
+ * corresponds to the rdn separator or to '\0' in case the string is over.
+ */
 int
-ldap_is_dns_dn( char *dn )
+ldap_str2rdn( LDAP_CONST char *str, LDAPRDN **rdn,
+       char **n_in, unsigned flags )
 {
-       return( dn[ 0 ] != '\0' && strchr( dn, '=' ) == NULL &&
-           strchr( dn, ',' ) == NULL );
-}
+       struct berval   bv;
 
+       assert( str );
+       assert( str[ 0 ] != '\0' );     /* FIXME: is this required? */
 
-#if defined( ultrix ) || defined( NeXT )
+       bv.bv_len = strlen( str );
+       bv.bv_val = (char *) str;
 
-char *strdup( char *s )
+       return ldap_bv2rdn( &bv, rdn, n_in, flags );
+}
+
+int
+ldap_bv2rdn( struct berval *bv, LDAPRDN **rdn,
+       char **n_in, unsigned flags )
 {
-       char    *p;
+       const char      **n = (const char **) n_in;
+       const char      *p;
+       int             navas = 0;
+       int             state = B4AVA;
+       int             rc = LDAP_DECODING_ERROR;
+       int             attrTypeEncoding = LDAP_AVA_STRING, 
+                       attrValueEncoding = LDAP_AVA_STRING;
 
-       if ( (p = (char *) malloc( strlen( s ) + 1 )) == NULL )
-               return( NULL );
+       struct berval   attrType = { 0, NULL };
+       struct berval   attrValue = { 0, NULL };
+
+       LDAPRDN         *newRDN = NULL;
+       LDAPAVA         *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
+       int             num_slots = TMP_AVA_SLOTS;
+
+       char            *str;
+       ber_len_t       stoplen;
+       
+       assert( bv );
+       assert( bv->bv_len );
+       assert( bv->bv_val );
+       assert( rdn || flags & LDAP_DN_SKIP );
+       assert( n );
+
+       str = bv->bv_val;
+       stoplen = bv->bv_len;
+
+       if ( rdn ) {
+               *rdn = NULL;
+       }
+       *n = NULL;
+
+       switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAP:
+       case LDAP_DN_FORMAT_LDAPV3:
+       case LDAP_DN_FORMAT_LDAPV2:
+       case LDAP_DN_FORMAT_DCE:
+               break;
+
+       /* unsupported in str2dn */
+       case LDAP_DN_FORMAT_UFN:
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               return LDAP_PARAM_ERROR;
+
+       case LDAP_DN_FORMAT_LBER:
+       default:
+               return LDAP_PARAM_ERROR;
+       }
+
+       if ( bv->bv_len == 0 ) {
+               return LDAP_SUCCESS;
+
+       }
+
+       if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+               /* value must have embedded NULs */
+               return LDAP_DECODING_ERROR;
+       }
+
+       p = str;
+       for ( ; p[ 0 ] || state == GOTAVA; ) {
+               
+               /*
+                * The parser in principle advances one token a time,
+                * or toggles state if preferable.
+                */
+               switch (state) {
+
+               /*
+                * an AttributeType can be encoded as:
+                * - its string representation; in detail, implementations
+                *   MUST recognize AttributeType string type names listed 
+                *   in section 2.3 of draft-ietf-ldapbis-dn-XX.txt, and
+                *   MAY recognize other names.
+                * - its numeric OID (a dotted decimal string); in detail
+                *   RFC 2253 asserts that ``Implementations MUST allow 
+                *   an oid in the attribute type to be prefixed by one 
+                *   of the character strings "oid." or "OID."''.  As soon
+                *   as draft-ietf-ldapbis-dn-XX.txt obsoletes RFC 2253 
+                *   I'm not sure whether this is required or not any 
+                *   longer; to be liberal, we still implement it.
+                */
+               case B4AVA:
+                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                               if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
+                                       /* error */
+                                       goto parsing_error;
+                               }
+                               p++;
+                       }
+
+                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                               if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+                                       /* error */
+                                       goto parsing_error;
+                               }
+
+                               /* whitespace is allowed (and trimmed) */
+                               p++;
+                               while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                                       p++;
+                               }
+
+                               if ( !p[ 0 ] ) {
+                                       /* error: we expected an AVA */
+                                       goto parsing_error;
+                               }
+                       }
+
+                       /* oid */
+                       if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
+                               state = B4OIDATTRTYPE;
+                               break;
+                       }
+                       
+                       /* else must be alpha */
+                       if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) {
+                               goto parsing_error;
+                       }
+                       
+                       /* LDAPv2 "oid." prefix */
+                       if ( LDAP_DN_LDAPV2( flags ) ) {
+                               /*
+                                * to be overly pedantic, we only accept
+                                * "OID." or "oid."
+                                */
+                               if ( flags & LDAP_DN_PEDANTIC ) {
+                                       if ( !strncmp( p, "OID.", 4 )
+                                               || !strncmp( p, "oid.", 4 ) ) {
+                                               p += 4;
+                                               state = B4OIDATTRTYPE;
+                                               break;
+                                       }
+                               } else {
+                                      if ( !strncasecmp( p, "oid.", 4 ) ) {
+                                              p += 4;
+                                              state = B4OIDATTRTYPE;
+                                              break;
+                                      }
+                               }
+                       }
+
+                       state = B4STRINGATTRTYPE;
+                       break;
+               
+               case B4OIDATTRTYPE: {
+                       int             err = LDAP_SUCCESS;
+                       
+                       attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
+                               LDAP_SCHEMA_SKIP);
+
+                       if ( err != LDAP_SUCCESS ) {
+                               goto parsing_error;
+                       }
+                       attrType.bv_len = p - attrType.bv_val;
+
+                       attrTypeEncoding = LDAP_AVA_BINARY;
+
+                       state = B4AVAEQUALS;
+                       break;
+               }
+
+               case B4STRINGATTRTYPE: {
+                       const char      *startPos, *endPos = NULL;
+                       ber_len_t       len;
+                       
+                       /* 
+                        * the starting char has been found to be
+                        * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
+                        * FIXME: DCE attr types seem to have a more
+                        * restrictive syntax (no '-' ...) 
+                        */
+                       for ( startPos = p++; p[ 0 ]; p++ ) {
+                               if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
+                                       continue;
+                               }
+
+                               if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) {
+                                       
+                                       /*
+                                        * RFC 2253 does not explicitly
+                                        * allow lang extensions to attribute 
+                                        * types in DNs ... 
+                                        */
+                                       if ( flags & LDAP_DN_PEDANTIC ) {
+                                               goto parsing_error;
+                                       }
+
+                                       /*
+                                        * we trim ';' and following lang 
+                                        * and so from attribute types
+                                        */
+                                       endPos = p;
+                                       for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] )
+                                                       || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) {
+                                               /* no op */ ;
+                                       }
+                                       break;
+                               }
+                               break;
+                       }
+
+                       len = ( endPos ? endPos : p ) - startPos;
+                       if ( len == 0 ) {
+                               goto parsing_error;
+                       }
+                       
+                       attrTypeEncoding = LDAP_AVA_STRING;
+
+                       /*
+                        * here we need to decide whether to use it as is 
+                        * or turn it in OID form; as a consequence, we
+                        * need to decide whether to binary encode the value
+                        */
+                       
+                       state = B4AVAEQUALS;
+
+                       if ( flags & LDAP_DN_SKIP ) {
+                               break;
+                       }
+
+                       attrType.bv_val = (char *)startPos;
+                       attrType.bv_len = len;
+
+                       break;
+               }
+                               
+               case B4AVAEQUALS:
+                       /* spaces may not be allowed */
+                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                               if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+                                       goto parsing_error;
+                               }
+                       
+                               /* trim spaces */
+                               for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+                                       /* no op */
+                               }
+                       }
+
+                       /* need equal sign */
+                       if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) {
+                               goto parsing_error;
+                       }
+                       p++;
+
+                       /* spaces may not be allowed */
+                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                               if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+                                       goto parsing_error;
+                               }
+
+                               /* trim spaces */
+                               for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+                                       /* no op */
+                               }
+                       }
+
+                       /*
+                        * octothorpe means a BER encoded value will follow
+                        * FIXME: I don't think DCE will allow it
+                        */
+                       if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) {
+                               p++;
+                               attrValueEncoding = LDAP_AVA_BINARY;
+                               state = B4BINARYVALUE;
+                               break;
+                       }
+
+                       /* STRING value expected */
+
+                       /* 
+                        * if we're pedantic, an attribute type in OID form
+                        * SHOULD imply a BER encoded attribute value; we
+                        * should at least issue a warning
+                        */
+                       if ( ( flags & LDAP_DN_PEDANTIC )
+                               && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) {
+                               /* OID attrType SHOULD use binary encoding */
+                               goto parsing_error;
+                       }
+
+                       attrValueEncoding = LDAP_AVA_STRING;
+
+                       /* 
+                        * LDAPv2 allows the attribute value to be quoted;
+                        * also, IA5 values are expected, in principle
+                        */
+                       if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
+                               if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+                                       p++;
+                                       state = B4IA5VALUEQUOTED;
+                                       break;
+                               }
+
+                               if ( LDAP_DN_LDAPV2( flags ) ) {
+                                       state = B4IA5VALUE;
+                                       break;
+                               }
+                       }
+
+                       /*
+                        * here STRING means RFC 2253 string
+                        * FIXME: what about DCE strings? 
+                        */
+                       if ( !p[ 0 ] ) {
+                               /* empty value */
+                               state = GOTAVA;
+                       } else {
+                               state = B4STRINGVALUE;
+                       }
+                       break;
+
+               case B4BINARYVALUE:
+                       if ( hexstr2binval( p, &attrValue, &p, flags ) ) {
+                               goto parsing_error;
+                       }
+
+                       state = GOTAVA;
+                       break;
+
+               case B4STRINGVALUE:
+                       switch ( LDAP_DN_FORMAT( flags ) ) {
+                       case LDAP_DN_FORMAT_LDAP:
+                       case LDAP_DN_FORMAT_LDAPV3:
+                               if ( str2strval( p, stoplen - ( p - str ),
+                                                       &attrValue, &p, flags, 
+                                                       &attrValueEncoding ) ) {
+                                       goto parsing_error;
+                               }
+                               break;
+
+                       case LDAP_DN_FORMAT_DCE:
+                               if ( DCE2strval( p, &attrValue, &p, flags ) ) {
+                                       goto parsing_error;
+                               }
+                               break;
+
+                       default:
+                               assert( 0 );
+                       }
+
+                       state = GOTAVA;
+                       break;
+
+               case B4IA5VALUE:
+                       if ( IA52strval( p, &attrValue, &p, flags ) ) {
+                               goto parsing_error;
+                       }
+
+                       state = GOTAVA;
+                       break;
+               
+               case B4IA5VALUEQUOTED:
+
+                       /* lead quote already stripped */
+                       if ( quotedIA52strval( p, &attrValue, 
+                                               &p, flags ) ) {
+                               goto parsing_error;
+                       }
+
+                       state = GOTAVA;
+                       break;
+
+               case GOTAVA: {
+                       int     rdnsep = 0;
+
+                       if ( !( flags & LDAP_DN_SKIP ) ) {
+                               LDAPAVA *ava;
+
+                               /*
+                                * we accept empty values
+                                */
+                               ava = ldapava_new( &attrType, &attrValue, 
+                                               attrValueEncoding );
+                               
+                               if ( ava == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       goto parsing_error;
+                               }
+                               tmpRDN[navas++] = ava;
+
+                               attrValue.bv_val = NULL;
+                               attrValue.bv_len = 0;
+
+                               /*
+                                * prepare room for new AVAs if needed
+                                */
+                               if (navas == num_slots) {
+                                       LDAPAVA **tmp;
+                                       
+                                       if ( tmpRDN == tmpRDN_ ) {
+                                               tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPAVA * ) );
+                                               if ( tmp == NULL ) {
+                                                       rc = LDAP_NO_MEMORY;
+                                                       goto parsing_error;
+                                               }
+                                               AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
+
+                                       } else {
+                                               tmp = LDAP_REALLOC( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ) );
+                                               if ( tmp == NULL ) {
+                                                       rc = LDAP_NO_MEMORY;
+                                                       goto parsing_error;
+                                               }
+                                       }
+
+                                       tmpRDN = tmp;
+                                       num_slots *= 2;
+                               }
+                       }
+                       
+                       /* 
+                        * if we got an AVA separator ('+', or ',' for DCE ) 
+                        * we expect a new AVA for this RDN; otherwise 
+                        * we add the RDN to the DN
+                        */
+                       switch ( LDAP_DN_FORMAT( flags ) ) {
+                       case LDAP_DN_FORMAT_LDAP:
+                       case LDAP_DN_FORMAT_LDAPV3:
+                       case LDAP_DN_FORMAT_LDAPV2:
+                               if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
+                                       rdnsep = 1;
+                               }
+                               break;
+
+                       case LDAP_DN_FORMAT_DCE:
+                               if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) {
+                                       rdnsep = 1;
+                               }
+                               break;
+                       }
+
+                       if ( rdnsep ) {
+                               /* 
+                                * the RDN is over, phew
+                                */
+                               *n = p;
+                               if ( !( flags & LDAP_DN_SKIP ) ) {
+                                       newRDN = (LDAPRDN *)LDAP_MALLOC( sizeof(LDAPRDN)
+                                               + sizeof(LDAPAVA *) * (navas+1) );
+                                       if ( newRDN == NULL ) {
+                                               rc = LDAP_NO_MEMORY;
+                                               goto parsing_error;
+                                       } else {
+                                               int i;
+
+                                               newRDN[0] = (LDAPAVA**)(newRDN+1);
+
+                                               for (i=0; i<navas; i++)
+                                                       newRDN[0][i] = tmpRDN[i];
+                                               newRDN[0][i] = NULL;
+                                       }
+
+                               }
+                               rc = LDAP_SUCCESS;
+                               goto return_result;
+                       }
+
+                       /* they should have been used in an AVA */
+                       attrType.bv_val = NULL;
+                       attrValue.bv_val = NULL;
+                       
+                       p++;
+                       state = B4AVA;
+                       break;
+               }
+
+               default:
+                       assert( 0 );
+                       goto parsing_error;
+               }
+       }
+       *n = p;
+       
+parsing_error:;
+       /* They are set to NULL after they're used in an AVA */
+
+       if ( attrValue.bv_val ) {
+               free( attrValue.bv_val );
+       }
+
+       for ( navas-- ; navas >= 0; navas-- ) {
+               ldap_avafree( tmpRDN[navas] );
+       }
+
+return_result:;
+
+       if ( tmpRDN != tmpRDN_ ) {
+               LDAP_FREE( tmpRDN );
+       }
+
+       if ( rdn ) {
+               *rdn = newRDN;
+       }
+       
+       return( rc );
+}
+
+/*
+ * reads in a UTF-8 string value, unescaping stuff:
+ * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c'
+ * '\' + HEXPAIR(p) -> unhex(p)
+ */
+static int
+str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, unsigned *retFlags )
+{
+       const char      *p, *end, *startPos, *endPos = NULL;
+       ber_len_t       len, escapes;
+
+       assert( str );
+       assert( val );
+       assert( next );
+
+       *next = NULL;
+       end = str + stoplen;
+       for ( startPos = p = str, escapes = 0; p < end; p++ ) {
+               if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+                       p++;
+                       if ( p[ 0 ] == '\0' ) {
+                               return( 1 );
+                       }
+                       if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
+                               escapes++;
+                               continue;
+                       }
+
+                       if ( LDAP_DN_HEXPAIR( p ) ) {
+                               char c;
+
+                               hexstr2bin( p, &c );
+                               escapes += 2;
+
+                               if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
+
+                                       /*
+                                        * we assume the string is UTF-8
+                                        */
+                                       *retFlags = LDAP_AVA_NONPRINTABLE;
+                               }
+                               p++;
+
+                               continue;
+                       }
+
+                       if ( LDAP_DN_PEDANTIC & flags ) {
+                               return( 1 );
+                       }
+                       /* 
+                        * we do not allow escaping 
+                        * of chars that don't need 
+                        * to and do not belong to 
+                        * HEXDIGITS
+                        */
+                       return( 1 );
+
+               } else if (!LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
+                       if ( p[ 0 ] == '\0' ) {
+                               return( 1 );
+                       }
+                       *retFlags = LDAP_AVA_NONPRINTABLE;
+
+               } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) 
+                               || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
+                       break;
+
+               } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
+                       /* 
+                        * FIXME: maybe we can add 
+                        * escapes if not pedantic?
+                        */
+                       return( 1 );
+               }
+       }
+
+       /*
+        * we do allow unescaped spaces at the end
+        * of the value only in non-pedantic mode
+        */
+       if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+                       !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+               if ( flags & LDAP_DN_PEDANTIC ) {
+                       return( 1 );
+               }
+
+               /* strip trailing (unescaped) spaces */
+               for ( endPos = p - 1; 
+                               endPos > startPos + 1 && 
+                               LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+                               !LDAP_DN_ESCAPE( endPos[ -2 ] );
+                               endPos-- ) {
+                       /* no op */
+               }
+       }
+
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
+       /*
+        * FIXME: test memory?
+        */
+       len = ( endPos ? endPos : p ) - startPos - escapes;
+       val->bv_len = len;
+
+       if ( escapes == 0 ) {
+               if ( *retFlags == LDAP_AVA_NONPRINTABLE ) {
+                       val->bv_val = LDAP_MALLOC( len + 1 );
+                       AC_MEMCPY( val->bv_val, startPos, len );
+                       val->bv_val[ len ] = '\0';
+               } else {
+                       val->bv_val = LDAP_STRNDUP( startPos, len );
+               }
+
+       } else {
+               ber_len_t       s, d;
+
+               val->bv_val = LDAP_MALLOC( len + 1 );
+               for ( s = 0, d = 0; d < len; ) {
+                       if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+                               s++;
+                               if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
+                                       val->bv_val[ d++ ] = 
+                                               startPos[ s++ ];
+                                       
+                               } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
+                                       char    c;
+
+                                       hexstr2bin( &startPos[ s ], &c );
+                                       val->bv_val[ d++ ] = c;
+                                       s += 2;
+                                       
+                               } else {
+                                       /* we should never get here */
+                                       assert( 0 );
+                               }
+
+                       } else {
+                               val->bv_val[ d++ ] = startPos[ s++ ];
+                       }
+               }
+
+               val->bv_val[ d ] = '\0';
+               assert( d == len );
+       }
+
+       return( 0 );
+}
+
+static int
+DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+       const char      *p, *startPos, *endPos = NULL;
+       ber_len_t       len, escapes;
+
+       assert( str );
+       assert( val );
+       assert( next );
+
+       *next = NULL;
+       
+       for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+               if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) {
+                       p++;
+                       if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+                               escapes++;
+
+                       } else {
+                               return( 1 );
+                       }
+
+               } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+                       break;
+               }
+
+               /*
+                * FIXME: can we accept anything else? I guess we need
+                * to stop if a value is not legal
+                */
+       }
+
+       /* 
+        * (unescaped) trailing spaces are trimmed must be silently ignored;
+        * so we eat them
+        */
+       if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+                       !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+               if ( flags & LDAP_DN_PEDANTIC ) {
+                       return( 1 );
+               }
+
+               /* strip trailing (unescaped) spaces */
+               for ( endPos = p - 1; 
+                               endPos > startPos + 1 && 
+                               LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+                               !LDAP_DN_ESCAPE( endPos[ -2 ] );
+                               endPos-- ) {
+                       /* no op */
+               }
+       }
+
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+       
+       len = ( endPos ? endPos : p ) - startPos - escapes;
+       val->bv_len = len;
+       if ( escapes == 0 ){
+               val->bv_val = LDAP_STRNDUP( startPos, len );
+
+       } else {
+               ber_len_t       s, d;
+
+               val->bv_val = LDAP_MALLOC( len + 1 );
+               for ( s = 0, d = 0; d < len; ) {
+                       /*
+                        * This point is reached only if escapes 
+                        * are properly used, so all we need to
+                        * do is eat them
+                        */
+                       if (  LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) {
+                               s++;
+
+                       }
+                       val->bv_val[ d++ ] = startPos[ s++ ];
+               }
+               val->bv_val[ d ] = '\0';
+               assert( strlen( val->bv_val ) == len );
+       }
+       
+       return( 0 );
+}
+
+static int
+IA52strval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+       const char      *p, *startPos, *endPos = NULL;
+       ber_len_t       len, escapes;
+
+       assert( str );
+       assert( val );
+       assert( next );
+
+       *next = NULL;
+
+       /*
+        * LDAPv2 (RFC 1779)
+        */
+       
+       for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+               if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+                       p++;
+                       if ( p[ 0 ] == '\0' ) {
+                               return( 1 );
+                       }
+
+                       if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] )
+                                       && ( LDAP_DN_PEDANTIC & flags ) ) {
+                               return( 1 );
+                       }
+                       escapes++;
+
+               } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+                       break;
+               }
+
+               /*
+                * FIXME: can we accept anything else? I guess we need
+                * to stop if a value is not legal
+                */
+       }
+
+       /* strip trailing (unescaped) spaces */
+       for ( endPos = p; 
+                       endPos > startPos + 1 && 
+                       LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+                       !LDAP_DN_ESCAPE( endPos[ -2 ] );
+                       endPos-- ) {
+               /* no op */
+       }
+
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
+       len = ( endPos ? endPos : p ) - startPos - escapes;
+       val->bv_len = len;
+       if ( escapes == 0 ) {
+               val->bv_val = LDAP_STRNDUP( startPos, len );
+
+       } else {
+               ber_len_t       s, d;
+               
+               val->bv_val = LDAP_MALLOC( len + 1 );
+               for ( s = 0, d = 0; d < len; ) {
+                       if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+                               s++;
+                       }
+                       val->bv_val[ d++ ] = startPos[ s++ ];
+               }
+               val->bv_val[ d ] = '\0';
+               assert( strlen( val->bv_val ) == len );
+       }
+
+       return( 0 );
+}
+
+static int
+quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+       const char      *p, *startPos, *endPos = NULL;
+       ber_len_t       len;
+       unsigned        escapes = 0;
+
+       assert( str );
+       assert( val );
+       assert( next );
+
+       *next = NULL;
+
+       /* initial quote already eaten */
+       for ( startPos = p = str; p[ 0 ]; p++ ) {
+               /* 
+                * According to RFC 1779, the quoted value can
+                * contain escaped as well as unescaped special values;
+                * as a consequence we tolerate escaped values 
+                * (e.g. '"\,"' -> '\,') and escape unescaped specials
+                * (e.g. '","' -> '\,').
+                */
+               if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+                       if ( p[ 1 ] == '\0' ) {
+                               return( 1 );
+                       }
+                       p++;
+
+                       if ( !LDAP_DN_V2_PAIR( p[ 0 ] )
+                                       && ( LDAP_DN_PEDANTIC & flags ) ) {
+                               /*
+                                * do we allow to escape normal chars?
+                                * LDAPv2 does not allow any mechanism 
+                                * for escaping chars with '\' and hex 
+                                * pair
+                                */
+                               return( 1 );
+                       }
+                       escapes++;
+
+               } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+                       endPos = p;
+                       /* eat closing quotes */
+                       p++;
+                       break;
+               }
+
+               /*
+                * FIXME: can we accept anything else? I guess we need
+                * to stop if a value is not legal
+                */
+       }
+
+       if ( endPos == NULL ) {
+               return( 1 );
+       }
+
+       /* Strip trailing (unescaped) spaces */
+       for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+               /* no op */
+       }
+
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
+       len = endPos - startPos - escapes;
+       assert( len >= 0 );
+       val->bv_len = len;
+       if ( escapes == 0 ) {
+               val->bv_val = LDAP_STRNDUP( startPos, len );
+
+       } else {
+               ber_len_t       s, d;
+               
+               val->bv_val = LDAP_MALLOC( len + 1 );
+               val->bv_len = len;
+
+               for ( s = d = 0; d < len; ) {
+                       if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
+                               s++;
+                       }
+                       val->bv_val[ d++ ] = str[ s++ ];
+               }
+               val->bv_val[ d ] = '\0';
+               assert( strlen( val->bv_val ) == len );
+       }
+
+       return( 0 );
+}
+
+static int
+hexstr2bin( const char *str, char *c )
+{
+       char    c1, c2;
+
+       assert( str );
+       assert( c );
+
+       c1 = str[ 0 ];
+       c2 = str[ 1 ];
+
+       if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
+               *c = c1 - '0';
+
+       } else {
+               if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
+                       *c = c1 - 'A' + 10;
+               } else {
+                       assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
+                       *c = c1 - 'a' + 10;
+               }
+       }
+
+       *c <<= 4;
+
+       if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
+               *c += c2 - '0';
+               
+       } else {
+               if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
+                       *c += c2 - 'A' + 10;
+               } else {
+                       assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
+                       *c += c2 - 'a' + 10;
+               }
+       }
+
+       return( 0 );
+}
+
+static int
+hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+       const char      *p, *startPos, *endPos = NULL;
+       ber_len_t       len;
+       ber_len_t       s, d;
+
+       assert( str );
+       assert( val );
+       assert( next );
+
+       *next = NULL;
+
+       for ( startPos = p = str; p[ 0 ]; p += 2 ) {
+               switch ( LDAP_DN_FORMAT( flags ) ) {
+               case LDAP_DN_FORMAT_LDAPV3:
+                       if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+                               goto end_of_value;
+                       }
+                       break;
+
+               case LDAP_DN_FORMAT_LDAP:
+               case LDAP_DN_FORMAT_LDAPV2:
+                       if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+                               goto end_of_value;
+                       }
+                       break;
+
+               case LDAP_DN_FORMAT_DCE:
+                       if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+                               goto end_of_value;
+                       }
+                       break;
+               }
+
+               if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                       if ( flags & LDAP_DN_PEDANTIC ) {
+                               return( 1 );
+                       }
+                       endPos = p;
+
+                       for ( ; p[ 0 ]; p++ ) {
+                               switch ( LDAP_DN_FORMAT( flags ) ) {
+                               case LDAP_DN_FORMAT_LDAPV3:
+                                       if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+                                               goto end_of_value;
+                                       }
+                                       break;
+
+                               case LDAP_DN_FORMAT_LDAP:
+                               case LDAP_DN_FORMAT_LDAPV2:
+                                       if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+                                               goto end_of_value;
+                                       }
+                                       break;
+
+                               case LDAP_DN_FORMAT_DCE:
+                                       if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+                                               goto end_of_value;
+                                       }
+                                       break;
+                               }
+                       }
+                       break;
+               }
+               
+               if ( !LDAP_DN_HEXPAIR( p ) ) {
+                       return( 1 );
+               }
+       }
+
+end_of_value:;
+
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
+       len = ( ( endPos ? endPos : p ) - startPos ) / 2;
+       /* must be even! */
+       assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
+
+       val->bv_len = len;
+       val->bv_val = LDAP_MALLOC( len + 1 );
+       if ( val->bv_val == NULL ) {
+               return( LDAP_NO_MEMORY );
+       }
+
+       for ( s = 0, d = 0; d < len; s += 2, d++ ) {
+               char    c;
+
+               hexstr2bin( &startPos[ s ], &c );
+
+               val->bv_val[ d ] = c;
+       }
+
+       val->bv_val[ d ] = '\0';
+
+       return( 0 );
+}
+
+/*
+ * convert a byte in a hexadecimal pair
+ */
+static int
+byte2hexpair( const char *val, char *pair )
+{
+       static const char       hexdig[] = "0123456789ABCDEF";
+
+       assert( val );
+       assert( pair );
+
+       /* 
+        * we assume the string has enough room for the hex encoding
+        * of the value
+        */
+
+       pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ];
+       pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ];
+       
+       return( 0 );
+}
+
+/*
+ * convert a binary value in hexadecimal pairs
+ */
+static int
+binval2hexstr( struct berval *val, char *str )
+{
+       ber_len_t       s, d;
+
+       assert( val );
+       assert( str );
+
+       if ( val->bv_len == 0 ) {
+               return( 0 );
+       }
+
+       /* 
+        * we assume the string has enough room for the hex encoding
+        * of the value
+        */
+
+       for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) {
+               byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+       }
+       
+       return( 0 );
+}
+
+/*
+ * Length of the string representation, accounting for escaped hex
+ * of UTF-8 chars
+ */
+static int
+strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       l, cl = 1;
+       char            *p;
+       int             escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
+#ifdef PRETTY_ESCAPE
+       int             escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
+#endif /* PRETTY_ESCAPE */
+       
+       assert( val );
+       assert( len );
+
+       *len = 0;
+       if ( val->bv_len == 0 ) {
+               return( 0 );
+       }
+
+       for ( l = 0, p = val->bv_val; p < val->bv_val + val->bv_len; p += cl ) {
+
+               /* 
+                * escape '%x00' 
+                */
+               if ( p[ 0 ] == '\0' ) {
+                       cl = 1;
+                       l += 3;
+                       continue;
+               }
+
+               cl = LDAP_UTF8_CHARLEN2( p, cl );
+               if ( cl == 0 ) {
+                       /* illegal utf-8 char! */
+                       return( -1 );
+
+               } else if ( cl > 1 ) {
+                       ber_len_t cnt;
+
+                       for ( cnt = 1; cnt < cl; cnt++ ) {
+                               if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
+                                       return( -1 );
+                               }
+                       }
+                       l += escaped_byte_len * cl;
+
+               } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+                               || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+                               || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+#ifdef PRETTY_ESCAPE
+#if 0
+                       if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
+#else
+                       if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
+#endif
+
+                               /* 
+                                * there might be some chars we want 
+                                * to escape in form of a couple 
+                                * of hexdigits for optimization purposes
+                                */
+                               l += 3;
+
+                       } else {
+                               l += escaped_ascii_len;
+                       }
+#else /* ! PRETTY_ESCAPE */
+                       l += 3;
+#endif /* ! PRETTY_ESCAPE */
+
+               } else {
+                       l++;
+               }
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+/*
+ * convert to string representation, escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       s, d, end;
+
+       assert( val );
+       assert( str );
+       assert( len );
+
+       if ( val->bv_len == 0 ) {
+               *len = 0;
+               return( 0 );
+       }
+
+       /* 
+        * we assume the string has enough room for the hex encoding
+        * of the value
+        */
+       for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+               ber_len_t       cl;
+
+               /* 
+                * escape '%x00' 
+                */
+               if ( val->bv_val[ s ] == '\0' ) {
+                       cl = 1;
+                       str[ d++ ] = '\\';
+                       str[ d++ ] = '0';
+                       str[ d++ ] = '0';
+                       s++;
+                       continue;
+               }
+               
+               /*
+                * The length was checked in strval2strlen();
+                * LDAP_UTF8_CHARLEN() should suffice
+                */
+               cl = LDAP_UTF8_CHARLEN2( &val->bv_val[ s ], cl );
+               assert( cl > 0 );
+               
+               /* 
+                * there might be some chars we want to escape in form
+                * of a couple of hexdigits for optimization purposes
+                */
+               if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) ) 
+#ifdef PRETTY_ESCAPE
+#if 0
+                               || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] ) 
+#else
+                               || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] ) 
+#endif
+#else /* ! PRETTY_ESCAPE */
+                               || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+                               || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+                               || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
+
+#endif /* ! PRETTY_ESCAPE */
+                               ) {
+                       for ( ; cl--; ) {
+                               str[ d++ ] = '\\';
+                               byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+                               s++;
+                               d += 2;
+                       }
+
+               } else if ( cl > 1 ) {
+                       for ( ; cl--; ) {
+                               str[ d++ ] = val->bv_val[ s++ ];
+                       }
+
+               } else {
+#ifdef PRETTY_ESCAPE
+                       if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+                                       || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+                                       || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+                               str[ d++ ] = '\\';
+                               if ( !LDAP_DN_IS_PRETTY( flags ) ) {
+                                       byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+                                       s++;
+                                       d += 2;
+                                       continue;
+                               }
+                       }
+#endif /* PRETTY_ESCAPE */
+                       str[ d++ ] = val->bv_val[ s++ ];
+               }
+       }
+
+       *len = d;
+       
+       return( 0 );
+}
+
+/*
+ * Length of the IA5 string representation (no UTF-8 allowed)
+ */
+static int
+strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       l;
+       char            *p;
+
+       assert( val );
+       assert( len );
+
+       *len = 0;
+       if ( val->bv_len == 0 ) {
+               return( 0 );
+       }
+
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
+               /*
+                * Turn value into a binary encoded BER
+                */
+               return( -1 );
+
+       } else {
+               for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+                       if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+                                       || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+                                       || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+                               l += 2;
+
+                       } else {
+                               l++;
+                       }
+               }
+       }
+
+       *len = l;
+       
+       return( 0 );
+}
+
+/*
+ * convert to string representation (np UTF-8)
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       s, d, end;
+
+       assert( val );
+       assert( str );
+       assert( len );
+
+       if ( val->bv_len == 0 ) {
+               *len = 0;
+               return( 0 );
+       }
+
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
+               /*
+                * Turn value into a binary encoded BER
+                */
+               *len = 0;
+               return( -1 );
+
+       } else {
+               /* 
+                * we assume the string has enough room for the hex encoding
+                * of the value
+                */
+
+               for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+                       if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+                                       || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+                                       || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+                               str[ d++ ] = '\\';
+                       }
+                       str[ d++ ] = val->bv_val[ s++ ];
+               }
+       }
+
+       *len = d;
+       
+       return( 0 );
+}
+
+/*
+ * Length of the (supposedly) DCE string representation, 
+ * accounting for escaped hex of UTF-8 chars
+ */
+static int
+strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       l;
+       char            *p;
+
+       assert( val );
+       assert( len );
+
+       *len = 0;
+       if ( val->bv_len == 0 ) {
+               return( 0 );
+       }
+
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
+               /* 
+                * FIXME: Turn the value into a binary encoded BER?
+                */
+               return( -1 );
+               
+       } else {
+               for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+                       if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+                               l += 2;
+
+                       } else {
+                               l++;
+                       }
+               }
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+/*
+ * convert to (supposedly) DCE string representation, 
+ * escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       s, d;
+
+       assert( val );
+       assert( str );
+       assert( len );
+
+       if ( val->bv_len == 0 ) {
+               *len = 0;
+               return( 0 );
+       }
+
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
+               /*
+                * FIXME: Turn the value into a binary encoded BER?
+                */
+               *len = 0;
+               return( -1 );
+               
+       } else {
+
+               /* 
+                * we assume the string has enough room for the hex encoding
+                * of the value
+                */
+
+               for ( s = 0, d = 0; s < val->bv_len; ) {
+                       if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) {
+                               str[ d++ ] = '\\';
+                       }
+                       str[ d++ ] = val->bv_val[ s++ ];
+               }
+       }
+
+       *len = d;
+       
+       return( 0 );
+}
+
+/*
+ * Length of the (supposedly) AD canonical string representation, 
+ * accounting for escaped hex of UTF-8 chars
+ */
+static int
+strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       l;
+       char            *p;
+
+       assert( val );
+       assert( len );
+
+       *len = 0;
+       if ( val->bv_len == 0 ) {
+               return( 0 );
+       }
+
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
+               /* 
+                * FIXME: Turn the value into a binary encoded BER?
+                */
+               return( -1 );
+               
+       } else {
+               for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+                       if ( LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
+                               l += 2;
+
+                       } else {
+                               l++;
+                       }
+               }
+       }
+
+       *len = l;
+       
+       return( 0 );
+}
+
+/*
+ * convert to (supposedly) AD string representation, 
+ * escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+       ber_len_t       s, d;
+
+       assert( val );
+       assert( str );
+       assert( len );
+
+       if ( val->bv_len == 0 ) {
+               *len = 0;
+               return( 0 );
+       }
+
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
+               /*
+                * FIXME: Turn the value into a binary encoded BER?
+                */
+               *len = 0;
+               return( -1 );
+               
+       } else {
+
+               /* 
+                * we assume the string has enough room for the hex encoding
+                * of the value
+                */
+
+               for ( s = 0, d = 0; s < val->bv_len; ) {
+                       if ( LDAP_DN_NEEDESCAPE_AD( val->bv_val[ s ] ) ) {
+                               str[ d++ ] = '\\';
+                       }
+                       str[ d++ ] = val->bv_val[ s++ ];
+               }
+       }
+
+       *len = d;
+       
+       return( 0 );
+}
+
+/*
+ * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
+ * the first part of the AD representation of the DN is written in DNS
+ * form, i.e. dot separated domain name components (as suggested 
+ * by Luke Howard, http://www.padl.com/~lukeh)
+ */
+static int
+dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN )
+{
+       int             i;
+       int             domain = 0, first = 1;
+       ber_len_t       l = 1; /* we move the null also */
+       char            *str;
+
+       /* we are guaranteed there's enough memory in str */
+
+       /* sanity */
+       assert( dn );
+       assert( bv );
+       assert( iRDN );
+       assert( *iRDN >= 0 );
+
+       str = bv->bv_val + pos;
+
+       for ( i = *iRDN; i >= 0; i-- ) {
+               LDAPRDN         *rdn;
+               LDAPAVA         *ava;
+
+               assert( dn[ 0 ][ i ] );
+               rdn = dn[ 0 ][ i ];
+
+               assert( rdn[ 0 ][ 0 ] );
+               ava = rdn[ 0 ][ 0 ];
+
+               if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
+                       break;
+               }
+
+               domain = 1;
+               
+               if ( first ) {
+                       first = 0;
+                       AC_MEMCPY( str, ava->la_value.bv_val, 
+                                       ava->la_value.bv_len + 1);
+                       l += ava->la_value.bv_len;
+
+               } else {
+                       AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
+                       AC_MEMCPY( str, ava->la_value.bv_val, 
+                                       ava->la_value.bv_len );
+                       str[ ava->la_value.bv_len ] = '.';
+                       l += ava->la_value.bv_len + 1;
+               }
+       }
+
+       *iRDN = i;
+       bv->bv_len = pos + l - 1;
+
+       return( domain );
+}
+
+static int
+rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len,
+        int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               /* len(type) + '=' + '+' | ',' */
+               l += ava->la_attr.bv_len + 2;
+
+               if ( ava->la_flags & LDAP_AVA_BINARY ) {
+                       /* octothorpe + twice the length */
+                       l += 1 + 2 * ava->la_value.bv_len;
+
+               } else {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+               }
+       }
+       
+       *len = l;
+       
+       return( 0 );
+}
+
+static int
+rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len,
+       int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, 
+                               ava->la_attr.bv_len );
+               l += ava->la_attr.bv_len;
+
+               str[ l++ ] = '=';
+
+               if ( ava->la_flags & LDAP_AVA_BINARY ) {
+                       str[ l++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+
+               } else {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+               }
+               str[ l++ ] = ( rdn[ 0 ][ iAVA + 1 ] ? '+' : ',' );
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+static int
+rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               /* len(type) + '=' + ',' | '/' */
+               l += ava->la_attr.bv_len + 2;
+
+               switch ( ava->la_flags ) {
+               case LDAP_AVA_BINARY:
+                       /* octothorpe + twice the length */
+                       l += 1 + 2 * ava->la_value.bv_len;
+                       break;
+
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+
+               default:
+                       return( -1 );
+               }
+       }
+       
+       *len = l;
+       
+       return( 0 );
+}
+
+static int
+rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               if ( first ) {
+                       first = 0;
+               } else {
+                       str[ l++ ] = ( iAVA ? ',' : '/' );
+               }
+
+               AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, 
+                               ava->la_attr.bv_len );
+               l += ava->la_attr.bv_len;
+
+               str[ l++ ] = '=';
+
+               switch ( ava->la_flags ) {
+                       case LDAP_AVA_BINARY:
+                       str[ l++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+                       break;
+
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+                                     
+               default:
+                       return( -1 );
+               }
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+static int
+rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       assert( rdn );
+       assert( len );
+
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               /* ' + ' | ', ' */
+               l += ( rdn[ 0 ][ iAVA + 1 ] ? 3 : 2 );
+
+               /* FIXME: are binary values allowed in UFN? */
+               if ( ava->la_flags & LDAP_AVA_BINARY ) {
+                       /* octothorpe + twice the value */
+                       l += 1 + 2 * ava->la_value.bv_len;
+
+               } else {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( strval2strlen( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+               }
+       }
+       
+       *len = l;
+       
+       return( 0 );
+}
+
+static int
+rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               if ( ava->la_flags & LDAP_AVA_BINARY ) {
+                       str[ l++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+                       
+               } else {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+               }
+
+               if ( rdn[ 0 ][ iAVA + 1 ]) {
+                       AC_MEMCPY( &str[ l ], " + ", 3 );
+                       l += 3;
+
+               } else {
+                       AC_MEMCPY( &str[ l ], ", ", 2 );
+                       l += 2;
+               }
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+static int
+rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       assert( rdn );
+       assert( len );
+
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               /* ',' | '/' */
+               l++;
+
+               /* FIXME: are binary values allowed in UFN? */
+               switch ( ava->la_flags ) {
+               case LDAP_AVA_BINARY:
+                       /* octothorpe + twice the value */
+                       l += 1 + 2 * ava->la_value.bv_len;
+                       break;
+
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+
+               default:
+                       return( -1 );
+               }
+       }
+       
+       *len = l;
+       
+       return( 0 );
+}
+
+static int
+rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               if ( first ) {
+                       first = 0;
+               } else {
+                       str[ l++ ] = ( iAVA ? ',' : '/' );
+               }
+
+               switch ( ava->la_flags ) {
+               case LDAP_AVA_BINARY:
+                       str[ l++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+                       break;
+                       
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+
+               default:
+                       return( -1 );
+               }
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+/*
+ * ldap_rdn2str
+ *
+ * Returns in str a string representation of rdn based on flags.
+ * There is some duplication of code between this and ldap_dn2str;
+ * this is wanted to reduce the allocation of temporary buffers.
+ */
+int
+ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags )
+{
+       struct berval bv;
+       int rc;
+
+       assert( str );
+
+       if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+               return LDAP_PARAM_ERROR;
+       }
+
+       rc = ldap_rdn2bv( rdn, &bv, flags );
+       *str = bv.bv_val;
+       return rc;
+}
+
+int
+ldap_rdn2bv( LDAPRDN *rdn, struct berval *bv, unsigned flags )
+{
+       int             rc, back;
+       ber_len_t       l;
+       
+       assert( bv );
+
+       bv->bv_len = 0;
+       bv->bv_val = NULL;
+
+       if ( rdn == NULL ) {
+               bv->bv_val = LDAP_STRDUP( "" );
+               return( LDAP_SUCCESS );
+       }
+
+       /*
+        * This routine wastes "back" bytes at the end of the string
+        */
+
+       switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAPV3:
+               if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_LDAPV2:
+               if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_UFN:
+               if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_DCE:
+               if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               if ( rdn2ADstrlen( rdn, flags, &l ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       default:
+               return( LDAP_PARAM_ERROR );
+       }
+
+       bv->bv_val = LDAP_MALLOC( l + 1 );
+
+       switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAPV3:
+               rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
+               back = 1;
+               break;
+
+       case LDAP_DN_FORMAT_LDAPV2:
+               rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
+               back = 1;
+               break;
+
+       case LDAP_DN_FORMAT_UFN:
+               rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
+               back = 2;
+               break;
+
+       case LDAP_DN_FORMAT_DCE:
+               rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
+               back = 0;
+               break;
+
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
+               back = 0;
+               break;
+
+       default:
+               /* need at least one of the previous */
+               return LDAP_PARAM_ERROR;
+       }
+
+       if ( rc ) {
+               ldap_memfree( bv->bv_val );
+               return rc;
+       }
+
+       bv->bv_len = l - back;
+       bv->bv_val[ bv->bv_len ] = '\0';
+
+       return LDAP_SUCCESS;
+}
+
+/*
+ * Very bulk implementation; many optimizations can be performed
+ *   - a NULL dn results in an empty string ""
+ * 
+ * FIXME: doubts
+ *   a) what do we do if a UTF-8 string must be converted in LDAPv2?
+ *      we must encode it in binary form ('#' + HEXPAIRs)
+ *   b) does DCE/AD support UTF-8?
+ *      no clue; don't think so.
+ *   c) what do we do when binary values must be converted in UTF/DCE/AD?
+ *      use binary encoded BER
+ */ 
+int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags )
+{
+       struct berval bv;
+       int rc;
+
+       assert( str );
+
+       if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+               return LDAP_PARAM_ERROR;
+       }
+       
+       rc = ldap_dn2bv( dn, &bv, flags );
+       *str = bv.bv_val;
+       return rc;
+}
+
+int ldap_dn2bv( LDAPDN *dn, struct berval *bv, unsigned flags )
+{
+       int             iRDN;
+       int             rc = LDAP_ENCODING_ERROR;
+       ber_len_t       len, l;
+
+       /* stringifying helpers for LDAPv3/LDAPv2 */
+       int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
+       int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
+
+       assert( bv );
+       bv->bv_len = 0;
+       bv->bv_val = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2bv(%u)\n%s%s", flags, "", "" );
+
+       /* 
+        * a null dn means an empty dn string 
+        * FIXME: better raise an error?
+        */
+       if ( dn == NULL ) {
+               bv->bv_val = LDAP_STRDUP( "" );
+               return( LDAP_SUCCESS );
+       }
+
+       switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAPV3:
+               sv2l = strval2strlen;
+               sv2s = strval2str;
+
+               if( 0 ) {
+       case LDAP_DN_FORMAT_LDAPV2:
+                       sv2l = strval2IA5strlen;
+                       sv2s = strval2IA5str;
+               }
+
+               for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2strlen( rdn, flags, &rdnl, sv2l ) ) {
+                               goto return_results;
+                       }
+
+                       len += rdnl;
+               }
+
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+                       rc = LDAP_NO_MEMORY;
+                       break;
+               }
+
+               for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2str( rdn, &bv->bv_val[ l ], flags, 
+                                       &rdnl, sv2s ) ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               goto return_results;
+                       }
+                       l += rdnl;
+               }
+
+               assert( l == len );
+
+               /* 
+                * trim the last ',' (the allocated memory 
+                * is one byte longer than required)
+                */
+               bv->bv_len = len - 1;
+               bv->bv_val[ bv->bv_len ] = '\0';
+
+               rc = LDAP_SUCCESS;
+               break;
+
+       case LDAP_DN_FORMAT_UFN: {
+               /*
+                * FIXME: quoting from RFC 1781:
+                *
+   To take a distinguished name, and generate a name of this format with
+   attribute types omitted, the following steps are followed.
+
+    1.  If the first attribute is of type CommonName, the type may be
+       omitted.
+
+    2.  If the last attribute is of type Country, the type may be
+        omitted.
+
+    3.  If the last attribute is of type Country, the last
+        Organisation attribute may have the type omitted.
+
+    4.  All attributes of type OrganisationalUnit may have the type
+        omitted, unless they are after an Organisation attribute or
+        the first attribute is of type OrganisationalUnit.
+
+         * this should be the pedantic implementation.
+                *
+                * Here the standard implementation reflects
+                * the one historically provided by OpenLDAP
+                * (and UMIch, I presume), with the variant
+                * of spaces and plusses (' + ') separating 
+                * rdn components.
+                * 
+                * A non-standard but nice implementation could
+                * be to turn the  final "dc" attributes into a 
+                * dot-separated domain.
+                *
+                * Other improvements could involve the use of
+                * friendly country names and so.
+                */
+#ifdef DC_IN_UFN
+               int     leftmost_dc = -1;
+               int     last_iRDN = -1;
+#endif /* DC_IN_UFN */
+
+               for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2UFNstrlen( rdn, flags, &rdnl ) ) {
+                               goto return_results;
+                       }
+                       len += rdnl;
+
+#ifdef DC_IN_UFN
+                       if ( LDAP_DN_IS_RDN_DC( rdn ) ) {
+                               if ( leftmost_dc == -1 ) {
+                                       leftmost_dc = iRDN;
+                               }
+                       } else {
+                               leftmost_dc = -1;
+                       }
+#endif /* DC_IN_UFN */
+               }
+
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+                       rc = LDAP_NO_MEMORY;
+                       break;
+               }
+
+#ifdef DC_IN_UFN
+               if ( leftmost_dc == -1 ) {
+#endif /* DC_IN_UFN */
+                       for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                               ber_len_t       vl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2UFNstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &vl ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               l += vl;
+                       }
+
+                       /* 
+                        * trim the last ', ' (the allocated memory 
+                        * is two bytes longer than required)
+                        */
+                       bv->bv_len = len - 2;
+                       bv->bv_val[ bv->bv_len ] = '\0';
+#ifdef DC_IN_UFN
+               } else {
+                       last_iRDN = iRDN - 1;
+
+                       for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
+                               ber_len_t       vl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2UFNstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &vl ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               l += vl;
+                       }
+
+                       if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               goto return_results;
+                       }
+
+                       /* the string is correctly terminated by dn2domain */
+               }
+#endif /* DC_IN_UFN */
+               
+               rc = LDAP_SUCCESS;
+
+       } break;
+
+       case LDAP_DN_FORMAT_DCE:
+               for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2DCEstrlen( rdn, flags, &rdnl ) ) {
+                               goto return_results;
+                       }
+
+                       len += rdnl;
+               }
+
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+                       rc = LDAP_NO_MEMORY;
+                       break;
+               }
+
+               for ( l = 0; iRDN--; ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2DCEstr( rdn, &bv->bv_val[ l ], flags, 
+                                       &rdnl, 0 ) ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               goto return_results;
+                       }
+                       l += rdnl;
+               }
+
+               assert( l == len );
+
+               bv->bv_len = len;
+               bv->bv_val[ bv->bv_len ] = '\0';
+
+               rc = LDAP_SUCCESS;
+               break;
+
+       case LDAP_DN_FORMAT_AD_CANONICAL: {
+               /*
+                * Sort of UFN for DCE DNs: a slash ('/') separated
+                * global->local DN with no types; strictly speaking,
+                * the naming context should be a domain, which is
+                * written in DNS-style, e.g. dot-deparated.
+                * 
+                * Example:
+                * 
+                *      "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
+                *
+                * will read
+                * 
+                *      "microsoft.com/People/Bill,Gates"
+                */ 
+               for ( iRDN = 0, len = -1; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2ADstrlen( rdn, flags, &rdnl ) ) {
+                               goto return_results;
+                       }
+
+                       len += rdnl;
+               }
+
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+                       rc = LDAP_NO_MEMORY;
+                       break;
+               }
+
+               iRDN--;
+               if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) ) {
+                       for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
+                               ber_len_t       rdnl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2ADstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &rdnl, 0 ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               l += rdnl;
+                       }
+
+               } else {
+                       int             first = 1;
+
+                       /*
+                        * Strictly speaking, AD canonical requires
+                        * a DN to be in the form "..., dc=smtg",
+                        * i.e. terminated by a domain component
+                        */
+                       if ( flags & LDAP_DN_PEDANTIC ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               rc = LDAP_ENCODING_ERROR;
+                               break;
+                       }
+
+                       for ( l = 0; iRDN >= 0 ; iRDN-- ) {
+                               ber_len_t       rdnl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2ADstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &rdnl, first ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               if ( first ) {
+                                       first = 0;
+                               }
+                               l += rdnl;
+                       }
+               }
+
+               bv->bv_len = len;
+               bv->bv_val[ bv->bv_len ] = '\0';
+
+               rc = LDAP_SUCCESS;
+       } break;
+
+       default:
+               return LDAP_PARAM_ERROR;
+       }
 
-       strcpy( p, s );
+       Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2bv(%s,%u)=%d\n",
+               bv->bv_val, flags, rc );
 
-       return( p );
+return_results:;
+       return( rc );
 }
 
-#endif /* ultrix */
index 395bd6b9505ac165cfb7fd7da18df9613fdf9dad..31110542d14f3149ebf769215c9923a692252f65 100644 (file)
 
 #include <lber.h>
 
-#define        malloc(x)       ber_memalloc(x)
-#define        realloc(x,y)    ber_memrealloc(x,y)
-#define        free(x)         ber_memfree(x)
-
 #include <ldap_utf8.h>
 #include <ldap_pvt_uc.h>
 
+#define        malloc(x)       ber_memalloc(x)
+#define        realloc(x,y)    ber_memrealloc(x,y)
+#define        free(x)         ber_memfree(x)
 
 int ucstrncmp(
        const ldap_unicode_t *u1,
index 6c3b22ee857b04b4f34b74b385891a2a84a66fc9..518238084374f30251ed14a186a81a9f59a43648 100644 (file)
 /* acl.c - routines to parse and check acl's */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
 
 #include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#ifdef sunos5
-#include "regexpr.h"
-#else
-#include "regex.h"
-#endif
+
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
 #include "slap.h"
+#include "sets.h"
+#include "lber_pvt.h"
+
 
-extern Attribute       *attr_find();
-extern char            *re_comp();
-extern struct acl      *global_acl;
-extern int             global_default_access;
-extern char            *access2str();
-extern char            *dn_normalize_case();
+/*
+ * speed up compares
+ */
+static struct berval 
+       aci_bv_entry            = { sizeof("entry") - 1,        "entry" },
+       aci_bv_br_entry         = { sizeof("[entry]") - 1,      "[entry]" },
+       aci_bv_br_all           = { sizeof("[all]") - 1,        "[all]" },
+       aci_bv_access_id        = { sizeof("access-id") - 1,    "access-id" },
+       aci_bv_anonymous        = { sizeof("anonymous") - 1,    "anonymous" },
+       aci_bv_users            = { sizeof("users") - 1,        "users" },
+       aci_bv_self             = { sizeof("self") - 1,         "self" },
+       aci_bv_dnattr           = { sizeof("dnattr") - 1,       "dnattr" },
+       aci_bv_group            = { sizeof("group") - 1,        "group" },
+       aci_bv_role             = { sizeof("role") - 1,         "role" },
+       aci_bv_set              = { sizeof("set") - 1,          "set" },
+       aci_bv_set_ref          = { sizeof("set-ref") - 1,      "set-ref"},
+       aci_bv_grant            = { sizeof("grant") - 1,        "grant" },
+       aci_bv_deny             = { sizeof("deny") - 1,         "deny" };
+
+static AccessControl * acl_get(
+       AccessControl *ac, int *count,
+       Backend *be, Operation *op,
+       Entry *e,
+       AttributeDescription *desc,
+       int nmatches, regmatch_t *matches );
+
+static slap_control_t acl_mask(
+       AccessControl *ac, slap_mask_t *mask,
+       Backend *be, Connection *conn, Operation *op,
+       Entry *e,
+       AttributeDescription *desc,
+       struct berval *val,
+       regmatch_t *matches,
+       int count,
+       AccessControlState *state );
 
-int            acl_access_allowed();
-int            access_allowed();
-struct acl     *acl_get_applicable();
+#ifdef SLAPD_ACI_ENABLED
+static int aci_mask(
+       Backend *be,
+    Connection *conn,
+       Operation *op,
+       Entry *e,
+       AttributeDescription *desc,
+       struct berval *val,
+       struct berval *aci,
+       regmatch_t *matches,
+       slap_access_t *grant,
+       slap_access_t *deny );
+#endif
+
+static int     regex_matches(
+       struct berval *pat, char *str, char *buf, regmatch_t *matches);
+static void    string_expand(
+       struct berval *newbuf, struct berval *pattern,
+       char *match, regmatch_t *matches);
 
-static int     regex_matches();
+typedef        struct AciSetCookie {
+       Backend *be;
+       Entry *e;
+       Connection *conn;
+       Operation *op;
+} AciSetCookie;
 
-extern pthread_mutex_t regex_mutex;
+SLAP_SET_GATHER aci_set_gather;
+static int aci_match_set ( struct berval *subj, Backend *be,
+    Entry *e, Connection *conn, Operation *op, int setref );
 
 /*
- * access_allowed - check whether dn is allowed the requested access
+ * access_allowed - check whether op->o_ndn is allowed the requested access
  * to entry e, attribute attr, value val.  if val is null, access to
- * the whole attribute is assumed (all values).  this routine finds
- * the applicable acl and calls acl_access_allowed() to make the
- * decision.
+ * the whole attribute is assumed (all values).
  *
- * returns     0       access NOT allowed
- *             1       access allowed
+ * This routine loops through all access controls and calls
+ * acl_mask() on each applicable access control.
+ * The loop exits when a definitive answer is reached or
+ * or no more controls remain.
+ *
+ * returns:
+ *             0       access denied
+ *             1       access granted
  */
 
 int
@@ -45,107 +106,413 @@ access_allowed(
     Connection         *conn,
     Operation          *op,
     Entry              *e,
-    char               *attr,
+       AttributeDescription    *desc,
     struct berval      *val,
-    char               *dn,
-    int                        access
-)
+    slap_access_t      access,
+       AccessControlState *state )
 {
-       int             rc;
-       struct acl      *a;
+       int                             ret = 1;
+       int                             count;
+       AccessControl   *a;
+#ifdef LDAP_DEBUG
+       char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif
+       slap_mask_t mask;
+       slap_control_t control;
+       const char *attr;
+       regmatch_t matches[MAXREMATCHES];
 
-       if ( be == NULL ) {
-               return( 0 );
+       assert( e != NULL );
+       assert( desc != NULL );
+       assert( access > ACL_NONE );
+
+       attr = desc->ad_cname.bv_val;
+
+       assert( attr != NULL );
+
+       if( state && state->as_recorded ) { 
+               if( state->as_recorded & ACL_STATE_RECORDED_NV &&
+                       val == NULL )
+               {
+                       return state->as_result;
+
+               } else if ( state->as_recorded & ACL_STATE_RECORDED_VD &&
+                       val != NULL && state->as_vd_acl == NULL )
+               {
+                       return state->as_result;
+               }
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+               "access_allowed: conn %d %s access to \"%s\" \"%s\" requested\n",
+               conn ? conn->c_connid : -1, access2str( access ), e->e_dn, attr ));
+#else
+       Debug( LDAP_DEBUG_ACL,
+               "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
+           access2str( access ), e->e_dn, attr );
+#endif
+
+       if ( op == NULL ) {
+               /* no-op call */
+               goto done;
+       }
+
+       if ( be == NULL ) be = &backends[0];
+       assert( be != NULL );
+
+       /* grant database root access */
+       if ( be != NULL && be_isroot( be, &op->o_ndn ) ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_INFO,
+                      "access_allowed: conn %d root access granted\n",
+                      conn->c_connid));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                   "<= root access granted\n",
+                       0, 0, 0 );
+#endif
+               goto done;
+       }
+
+       /*
+        * no-user-modification operational attributes are ignored
+        * by ACL_WRITE checking as any found here are not provided
+        * by the user
+        */
+       if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
+               && desc != slap_schema.si_ad_entry
+               && desc != slap_schema.si_ad_children )
+       {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                      "access_allowed: conn %d NoUserMod Operational attribute: %s access granted\n",
+                      conn->c_connid, attr ));
+#else
+               Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
+                       " %s access granted\n",
+                       attr, 0, 0 );
+#endif
+               goto done;
+       }
+
+       /* use backend default access if no backend acls */
+       if( be != NULL && be->be_acl == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                      "access_allowed: conn %d backend default %s access %s to \"%s\"\n",
+                      conn->c_connid, access2str( access ),
+                      be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "=> access_allowed: backend default %s access %s to \"%s\"\n",
+                       access2str( access ),
+                       be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val );
+#endif
+               ret = be->be_dfltaccess >= access;
+               goto done;
+
+#ifdef notdef
+       /* be is always non-NULL */
+       /* use global default access if no global acls */
+       } else if ( be == NULL && global_acl == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                      "access_allowed: conn %d global default %s access %s to \"%s\"\n",
+                      conn->c_connid, access2str( access ),
+                      global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "=> access_allowed: global default %s access %s to \"%s\"\n",
+                       access2str( access ),
+                       global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val );
+#endif
+               ret = global_default_access >= access;
+               goto done;
+#endif
+       }
+
+       ret = 0;
+       control = ACL_BREAK;
+
+       if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )) {
+               assert( state->as_vd_acl != NULL );
+
+               a = state->as_vd_acl;
+               mask = state->as_vd_acl_mask;
+               count = state->as_vd_acl_count;
+               AC_MEMCPY( matches, state->as_vd_acl_matches,
+                       sizeof(matches) );
+               goto vd_access;
+
+       } else {
+               a = NULL;
+               ACL_INIT(mask);
+               count = 0;
+               memset(matches, '\0', sizeof(matches));
+       }
+
+       while((a = acl_get( a, &count, be, op, e, desc,
+               MAXREMATCHES, matches )) != NULL)
+       {
+               int i;
+
+               for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                           "access_allowed: conn %d match[%d]:  %d %d ",
+                           conn->c_connid, i,
+                               (int)matches[i].rm_so, (int)matches[i].rm_eo ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
+                           (int)matches[i].rm_so, (int)matches[i].rm_eo );
+#endif
+                       if( matches[i].rm_so <= matches[0].rm_eo ) {
+                               int n;
+                               for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
+                                       Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
+                               }
+                       }
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_ARGS, "\n" ));
+#else
+                       Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
+#endif
+               }
+
+vd_access:
+               control = acl_mask( a, &mask, be, conn, op,
+                       e, desc, val, matches, count, state );
+
+               if ( control != ACL_BREAK ) {
+                       break;
+               }
+
+               memset(matches, '\0', sizeof(matches));
+       }
+
+       if ( ACL_IS_INVALID( mask ) ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                   "access_allowed: conn %d     \"%s\" (%s) invalid!\n",
+                   conn->c_connid, e->e_dn, attr ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "=> access_allowed: \"%s\" (%s) invalid!\n",
+                       e->e_dn, attr, 0 );
+#endif
+               ACL_INIT(mask);
+
+       } else if ( control == ACL_BREAK ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                      "access_allowed: conn %d  no more rules\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "=> access_allowed: no more rules\n", 0, 0, 0);
+#endif
+
+               goto done;
        }
 
-       a = acl_get_applicable( be, op, e, attr );
-       rc = acl_access_allowed( a, be, conn, e, val, op, access );
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+               "access_allowed: conn %d  %s access %s by %s\n",
+               conn->c_connid,
+               access2str( access ),
+               ACL_GRANT( mask, access ) ? "granted" : "denied",
+               accessmask2str( mask, accessmaskbuf ) ));
+#else
+       Debug( LDAP_DEBUG_ACL,
+               "=> access_allowed: %s access %s by %s\n",
+               access2str( access ),
+               ACL_GRANT(mask, access) ? "granted" : "denied",
+               accessmask2str( mask, accessmaskbuf ) );
+#endif
 
-       return( rc );
+       ret = ACL_GRANT(mask, access);
+
+done:
+       if( state != NULL ) {
+               state->as_recorded |= ACL_STATE_RECORDED;
+               state->as_result = ret;
+       }
+       return ret;
 }
 
 /*
- * acl_get_applicable - return the acl applicable to entry e, attribute
+ * acl_get - return the acl applicable to entry e, attribute
  * attr.  the acl returned is suitable for use in subsequent calls to
  * acl_access_allowed().
  */
 
-struct acl *
-acl_get_applicable(
+static AccessControl *
+acl_get(
+       AccessControl *a,
+       int                     *count,
     Backend            *be,
-    Operation          *op,
+    Operation  *op,
     Entry              *e,
-    char               *attr
-)
+       AttributeDescription *desc,
+    int                        nmatch,
+    regmatch_t *matches )
 {
-       int             i;
-       struct acl      *a;
-       char            *edn;
+       const char *attr;
+       int dnlen, patlen;
 
-       Debug( LDAP_DEBUG_ACL, "=> acl_get: entry (%s) attr (%s)\n", e->e_dn,
-           attr, 0 );
+       assert( e != NULL );
+       assert( count != NULL );
+       assert( desc != NULL );
 
-       if ( be_isroot( be, op->o_dn ) ) {
-               Debug( LDAP_DEBUG_ACL,
-                   "<= acl_get: no acl applicable to database root\n", 0, 0,
-                   0 );
-               return( NULL );
-       }
+       attr = desc->ad_cname.bv_val;
 
-       /* check for a backend-specific acl that matches the entry */
-       for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) {
-               if ( a->acl_dnpat != NULL ) {
-                       edn = dn_normalize_case( strdup( e->e_dn ) );
-                       if ( ! regex_matches( a->acl_dnpat, edn ) ) {
-                               free( edn );
-                               continue;
-                       }
-                       free( edn );
-               }
-               if ( a->acl_filter != NULL ) {
-                       if ( test_filter( NULL, NULL, NULL, e, a->acl_filter )
-                           != 0 ) {
-                               continue;
-                       }
-               }
-               if ( attr == NULL || a->acl_attrs == NULL ||
-                   charray_inlist( a->acl_attrs, attr ) ) {
-                       Debug( LDAP_DEBUG_ACL, "<= acl_get: backend acl #%d\n",
-                           i, e->e_dn, attr );
-                       return( a );
+       assert( attr != NULL );
+
+       if( a == NULL ) {
+               if( be == NULL ) {
+                       a = global_acl;
+               } else {
+                       a = be->be_acl;
                }
+
+               assert( a != NULL );
+
+       } else {
+               a = a->acl_next;
        }
 
-       /* check for a global acl that matches the entry */
-       for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
-               if ( a->acl_dnpat != NULL ) {
-                       edn = dn_normalize_case( strdup( e->e_dn ) );
-                       if ( ! regex_matches( a->acl_dnpat, edn ) ) {
-                               free( edn );
-                               continue;
+       dnlen = e->e_nname.bv_len;
+
+       for ( ; a != NULL; a = a->acl_next ) {
+               (*count) ++;
+
+               if (a->acl_dn_pat.bv_len != 0) {
+                       if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                          "acl_get: dnpat [%d] %s nsub: %d\n",
+                                          *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub ));
+#else
+                               Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
+                                       *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
+#endif
+                               if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
+                                       continue;
+
+                       } else {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                          "acl_get: dn [%d] %s\n",
+                                          *count, a->acl_dn_pat.bv_val ));
+#else
+                               Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
+                                       *count, a->acl_dn_pat.bv_val, 0 );
+#endif
+                               patlen = a->acl_dn_pat.bv_len;
+                               if ( dnlen < patlen )
+                                       continue;
+
+                               if ( a->acl_dn_style == ACL_STYLE_BASE ) {
+                                       /* base dn -- entire object DN must match */
+                                       if ( dnlen != patlen )
+                                               continue;
+
+                               } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
+                                       int rdnlen = -1;
+
+                                       if ( dnlen <= patlen )
+                                               continue;
+
+                                       if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+                                               continue;
+
+                                       rdnlen = dn_rdnlen( NULL, &e->e_nname );
+                                       if ( rdnlen != dnlen - patlen - 1 )
+                                               continue;
+
+                               } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
+                                       if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+                                               continue;
+
+                               } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
+                                       if ( dnlen <= patlen )
+                                               continue;
+                                       if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+                                               continue;
+                               }
+
+                               if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
+                                       continue;
                        }
-                       free( edn );
+
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_get: [%d] matched\n",
+                                  *count ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
+                               *count, 0, 0 );
+#endif
                }
+
                if ( a->acl_filter != NULL ) {
-                       if ( test_filter( NULL, NULL, NULL, e, a->acl_filter )
-                           != 0 ) {
+                       ber_int_t rc = test_filter( NULL, NULL, NULL, e, a->acl_filter );
+                       if ( rc != LDAP_COMPARE_TRUE ) {
                                continue;
                        }
                }
-               if ( attr == NULL || a->acl_attrs == NULL || charray_inlist(
-                   a->acl_attrs, attr ) ) {
-                       Debug( LDAP_DEBUG_ACL, "<= acl_get: global acl #%d\n",
-                           i, e->e_dn, attr );
-                       return( a );
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                          "acl_get: [%d] check attr %s\n",
+                          *count, attr ));
+#else
+               Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n",
+                      *count, attr, 0);
+#endif
+               if ( attr == NULL || a->acl_attrs == NULL ||
+                       ad_inlist( desc, a->acl_attrs ) )
+               {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_get:  [%d] acl %s attr: %s\n",
+                                  *count, e->e_dn, attr ));
+#else
+                       Debug( LDAP_DEBUG_ACL,
+                               "<= acl_get: [%d] acl %s attr: %s\n",
+                               *count, e->e_dn, attr );
+#endif
+                       return a;
                }
+               matches[0].rm_so = matches[0].rm_eo = -1;
        }
-       Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
 
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+                  "acl_get: done.\n" ));
+#else
+       Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
+#endif
        return( NULL );
 }
 
 /*
- * acl_access_allowed - check whether the given acl allows dn the
+ * Record value-dependent access control state
+ */
+#define ACL_RECORD_VALUE_STATE do { \
+               if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { \
+                       state->as_recorded |= ACL_STATE_RECORDED_VD; \
+                       state->as_vd_acl = a; \
+                       AC_MEMCPY( state->as_vd_acl_matches, matches, \
+                               sizeof( state->as_vd_acl_matches )) ; \
+                       state->as_vd_acl_count = count; \
+                       state->as_vd_access = b; \
+                       state->as_vd_access_count = i; \
+               } \
+       } while( 0 )
+
+/*
+ * acl_mask - modifies mask based upon the given acl and the
  * requested access to entry e, attribute attr, value val.  if val
  * is null, access to the whole attribute is assumed (all values).
  *
@@ -153,252 +520,1353 @@ acl_get_applicable(
  *             1       access allowed
  */
 
-int
-acl_access_allowed(
-    struct acl         *a,
+static slap_control_t
+acl_mask(
+    AccessControl      *a,
+       slap_mask_t *mask,
     Backend            *be,
-    Connection         *conn,
+    Connection *conn,
+    Operation  *op,
     Entry              *e,
+       AttributeDescription *desc,
     struct berval      *val,
-    Operation          *op,
-    int                        access
-)
+       regmatch_t      *matches,
+       int     count,
+       AccessControlState *state )
 {
-       int             i;
-       char            *edn, *odn;
-       struct access   *b;
-       Attribute       *at;
-       struct berval   bv;
-       int             default_access;
-
-       Debug( LDAP_DEBUG_ACL, "=> acl: %s access to value \"%s\" by \"%s\"\n",
-           access2str( access ), val ? val->bv_val : "any", op->o_dn ?
-           op->o_dn : "" );
-
-       if ( be_isroot( be, op->o_dn ) ) {
-               Debug( LDAP_DEBUG_ACL, "<= acl: granted to database root\n",
-                   0, 0, 0 );
-               return( 1 );
-       }
+       int             i, odnlen, patlen;
+       int             vd_recorded = 0;
+       Access  *b;
+#ifdef LDAP_DEBUG
+       char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif
+       const char *attr;
 
-       default_access = be->be_dfltaccess ? be->be_dfltaccess :
-           global_default_access;
-       if ( a == NULL ) {
-               Debug( LDAP_DEBUG_ACL,
-                   "<= acl: %s by default (no matching to)\n",
-                   default_access >= access ? "granted" : "denied", 0, 0 );
-               return( default_access >= access );
-       }
+       assert( a != NULL );
+       assert( mask != NULL );
+       assert( desc != NULL );
+
+       attr = desc->ad_cname.bv_val;
+
+       assert( attr != NULL );
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+                  "acl_mask: conn %d  access to entry \"%s\", attr \"%s\" requested\n",
+                  conn->c_connid, e->e_dn, attr ));
+
+       LDAP_LOG(( "acl", LDAP_LEVEL_ARGS,
+                  " to %s by \"%s\", (%s) \n",
+                  val ? "value" : "all values",
+                  op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
+                  accessmask2str( *mask, accessmaskbuf ) ));
+#else
+       Debug( LDAP_DEBUG_ACL,
+               "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
+               e->e_dn, attr, 0 );
+
+       Debug( LDAP_DEBUG_ACL,
+               "=> acl_mask: to %s by \"%s\", (%s) \n",
+               val ? "value" : "all values",
+               op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
+               accessmask2str( *mask, accessmaskbuf ) );
+#endif
+
+       if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )
+               && state->as_vd_acl == a )
+       {
+               b = state->as_vd_access;
+               i = state->as_vd_access_count;
 
-       odn = NULL;
-       if ( op->o_dn != NULL ) {
-               odn = dn_normalize_case( strdup( op->o_dn ) );
-               bv.bv_val = odn;
-               bv.bv_len = strlen( odn );
+       } else {
+               b = a->acl_access;
+               i = 1;
        }
-       for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
-               if ( b->a_dnpat != NULL ) {
+
+       for ( ; b != NULL; b = b->a_next, i++ ) {
+               slap_mask_t oldmask, modmask;
+
+               ACL_INVALIDATE( modmask );
+
+               /* AND <who> clauses */
+               if ( b->a_dn_pat.bv_len != 0 ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_dn_pat: %s\n",
+                                  conn->c_connid, b->a_dn_pat.bv_val ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
+                               b->a_dn_pat.bv_val, 0, 0);
+#endif
                        /*
                         * if access applies to the entry itself, and the
                         * user is bound as somebody in the same namespace as
                         * the entry, OR the given dn matches the dn pattern
                         */
-                       if ( strcasecmp( b->a_dnpat, "self" ) == 0 && op->o_dn
-                           != NULL && *(op->o_dn) && e->e_dn != NULL ) {
-                               edn = dn_normalize_case( strdup( e->e_dn ) );
-                               if ( strcasecmp( edn, op->o_dn ) == 0 ) {
-                                       free( edn );
-                                       if ( odn ) free( odn );
-                                       Debug( LDAP_DEBUG_ACL,
-                                   "<= acl: matched by clause #%d access %s\n",
-                                           i, (b->a_access & ~ACL_SELF) >=
-                                           access ? "granted" : "denied", 0 );
-
-                                       return( (b->a_access & ~ACL_SELF)
-                                           >= access );
-                               }
-                               free( edn );
-                       } else {
-                               if ( regex_matches( b->a_dnpat, odn ) ) {
-                                       if ( odn ) free( odn );
-                                       Debug( LDAP_DEBUG_ACL,
-                                   "<= acl: matched by clause #%d access %s\n",
-                                   i, (b->a_access & ~ACL_SELF) >= access ?
-                                           "granted" : "denied", 0 );
-
-                                       return( (b->a_access & ~ACL_SELF)
-                                           >= access );
+                       if ( ber_bvcmp( &b->a_dn_pat, &aci_bv_anonymous ) == 0 ) {
+                               if ( op->o_ndn.bv_len != 0 ) {
+                                       continue;
                                }
-                       }
-               }
-               if ( b->a_addrpat != NULL ) {
-                       if ( regex_matches( b->a_addrpat, conn->c_addr ) ) {
-                               if ( odn ) free( odn );
-                               Debug( LDAP_DEBUG_ACL,
-                                   "<= acl: matched by clause #%d access %s\n",
-                                   i, (b->a_access & ~ACL_SELF) >= access ?
-                                   "granted" : "denied", 0 );
 
-                               return( (b->a_access & ~ACL_SELF) >= access );
-                       }
-               }
-               if ( b->a_domainpat != NULL ) {
-                       if ( regex_matches( b->a_domainpat, conn->c_domain ) ) {
-                               if ( odn ) free( odn );
-                               Debug( LDAP_DEBUG_ACL,
-                                   "<= acl: matched by clause #%d access %s\n",
-                                   i, (b->a_access & ~ACL_SELF) >= access ?
-                                   "granted" : "denied", 0 );
+                       } else if ( ber_bvcmp( &b->a_dn_pat, &aci_bv_users ) == 0 ) {
+                               if ( op->o_ndn.bv_len == 0 ) {
+                                       continue;
+                               }
 
-                               return( (b->a_access & ~ACL_SELF) >= access );
-                       }
-               }
-               if ( b->a_dnattr != NULL && op->o_dn != NULL ) {
-                       /* see if asker is listed in dnattr */
-                       if ( (at = attr_find( e->e_attrs, b->a_dnattr ))
-                           != NULL && value_find( at->a_vals, &bv,
-                           at->a_syntax, 3 ) == 0 )
-                       {
-                               if ( (b->a_access & ACL_SELF) && (val == NULL
-                                   || value_cmp( &bv, val, at->a_syntax,
-                                   2 )) ) {
+                       } else if ( ber_bvcmp( &b->a_dn_pat, &aci_bv_self ) == 0 ) {
+                               if ( op->o_ndn.bv_len == 0 ) {
+                                       continue;
+                               }
+                               
+                               if ( e->e_dn == NULL || !dn_match( &e->e_nname, &op->o_ndn ) ) {
                                        continue;
                                }
 
-                               if ( odn ) free( odn );
-                               Debug( LDAP_DEBUG_ACL,
-                                   "<= acl: matched by clause #%d access %s\n",
-                                   i, (b->a_access & ~ACL_SELF) >= access ?
-                                   "granted" : "denied", 0 );
+                       } else if ( b->a_dn_style == ACL_STYLE_REGEX ) {
+                               if ( ber_bvccmp( &b->a_dn_pat, '*' ) == 0 ) {
+                                       int ret = regex_matches( &b->a_dn_pat,
+                                               op->o_ndn.bv_val, e->e_ndn, matches );
 
-                               return( (b->a_access & ~ACL_SELF) >= access );
-                       }
+                                       if( ret == 0 ) {
+                                               continue;
+                                       }
+                               }
 
-                       /* asker not listed in dnattr - check for self access */
-                       if ( ! (b->a_access & ACL_SELF) || val == NULL ||
-                           value_cmp( &bv, val, at->a_syntax, 2 ) != 0 ) {
-                               continue;
-                       }
+                       } else {
+                               if ( e->e_dn == NULL )
+                                       continue;
 
-                       if ( odn ) free( odn );
-                       Debug( LDAP_DEBUG_ACL,
-                           "<= acl: matched by clause #%d (self) access %s\n",
-                           i, (b->a_access & ~ACL_SELF) >= access ? "granted"
-                           : "denied", 0 );
+                               patlen = b->a_dn_pat.bv_len;
+                               odnlen = op->o_ndn.bv_len;
+                               if ( odnlen < patlen )
+                                       continue;
 
-                       return( (b->a_access & ~ACL_SELF) >= access );
-               }
-       }
+                               if ( b->a_dn_style == ACL_STYLE_BASE ) {
+                                       /* base dn -- entire object DN must match */
+                                       if ( odnlen != patlen )
+                                               continue;
 
-       if ( odn ) free( odn );
-       Debug( LDAP_DEBUG_ACL, "<= acl: %s by default (no matching by)\n",
-           default_access >= access ? "granted" : "denied", 0, 0 );
+                               } else if ( b->a_dn_style == ACL_STYLE_ONE ) {
+                                       int rdnlen = -1;
 
-       return( default_access >= access );
-}
+                                       if ( odnlen <= patlen )
+                                               continue;
 
-/*
- * acl_check_mods - check access control on the given entry to see if
- * it allows the given modifications by the user associated with op.
- * returns     LDAP_SUCCESS    mods allowed ok
- *             anything else   mods not allowed - return is an error
- *                             code indicating the problem
- */
+                                       if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+                                               continue;
 
-int
-acl_check_mods(
-    Backend    *be,
-    Connection *conn,
-    Operation  *op,
-    Entry      *e,
-    LDAPMod    *mods
-)
-{
-       int             i;
-       struct acl      *a;
+                                       rdnlen = dn_rdnlen( NULL, &op->o_ndn );
+                                       if ( rdnlen != odnlen - patlen - 1 )
+                                               continue;
 
-       for ( ; mods != NULL; mods = mods->mod_next ) {
-               if ( strcasecmp( mods->mod_type, "modifiersname" ) == 0 ||
-                   strcasecmp( mods->mod_type, "modifytimestamp" ) == 0 ) {
-                       continue;
+                               } else if ( b->a_dn_style == ACL_STYLE_SUBTREE ) {
+                                       if ( odnlen > patlen && !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+                                               continue;
+
+                               } else if ( b->a_dn_style == ACL_STYLE_CHILDREN ) {
+                                       if ( odnlen <= patlen )
+                                               continue;
+                                       if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+                                               continue;
+                               }
+
+                               if ( strcmp( b->a_dn_pat.bv_val, op->o_ndn.bv_val + odnlen - patlen ) != 0 )
+                                       continue;
+                       }
                }
 
-               a = acl_get_applicable( be, op, e, mods->mod_type );
+               if ( b->a_sockurl_pat.bv_len ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_sockurl_pat: %s\n",
+                                  conn->c_connid, b->a_sockurl_pat.bv_val ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
+                               b->a_sockurl_pat.bv_val, 0, 0 );
+#endif
 
-               switch ( mods->mod_op & ~LDAP_MOD_BVALUES ) {
-               case LDAP_MOD_REPLACE:
-               case LDAP_MOD_ADD:
-                       if ( mods->mod_bvalues == NULL ) {
-                               break;
+                       if ( ber_bvccmp( &b->a_sockurl_pat, '*' ) != 0) {
+                               if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
+                                       if (!regex_matches( &b->a_sockurl_pat, conn->c_listener_url.bv_val,
+                                                       e->e_ndn, matches ) ) 
+                                       {
+                                               continue;
+                                       }
+                               } else {
+                                       if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &conn->c_listener_url ) != 0 )
+                                               continue;
+                               }
                        }
-                       for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
-                               if ( ! acl_access_allowed( a, be, conn, e,
-                                   mods->mod_bvalues[i], op, ACL_WRITE ) ) {
-                                       return( LDAP_INSUFFICIENT_ACCESS );
+               }
+
+               if ( b->a_domain_pat.bv_len ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_domain_pat: %s\n",
+                                  conn->c_connid, b->a_domain_pat.bv_val ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
+                               b->a_domain_pat.bv_val, 0, 0 );
+#endif
+                       if ( ber_bvccmp( &b->a_domain_pat, '*' ) != 0) {
+                               if ( b->a_domain_style == ACL_STYLE_REGEX) {
+                                       if (!regex_matches( &b->a_domain_pat, conn->c_peer_domain.bv_val,
+                                                       e->e_ndn, matches ) ) 
+                                       {
+                                               continue;
+                                       }
+                               } else {
+                                       if ( ber_bvstrcasecmp( &b->a_domain_pat, &conn->c_peer_domain ) != 0 )
+                                               continue;
                                }
                        }
-                       break;
+               }
 
-               case LDAP_MOD_DELETE:
-                       if ( mods->mod_bvalues == NULL ) {
-                               if ( ! acl_access_allowed( a, be, conn, e,
-                                   NULL, op, ACL_WRITE ) ) {
-                                       return( LDAP_INSUFFICIENT_ACCESS );
+               if ( b->a_peername_pat.bv_len ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_perrname_path: %s\n",
+                                  conn->c_connid, b->a_peername_pat.bv_val ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
+                               b->a_peername_pat.bv_val, 0, 0 );
+#endif
+                       if ( ber_bvccmp( &b->a_peername_pat, '*' ) != 0) {
+                               if ( b->a_peername_style == ACL_STYLE_REGEX) {
+                                       if (!regex_matches( &b->a_peername_pat, conn->c_peer_name.bv_val,
+                                                       e->e_ndn, matches ) ) 
+                                       {
+                                               continue;
+                                       }
+                               } else {
+                                       if ( ber_bvstrcasecmp( &b->a_peername_pat, &conn->c_peer_name ) != 0 )
+                                               continue;
                                }
-                               break;
                        }
-                       for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
-                               if ( ! acl_access_allowed( a, be, conn, e,
-                                   mods->mod_bvalues[i], op, ACL_WRITE ) ) {
-                                       return( LDAP_INSUFFICIENT_ACCESS );
+               }
+
+               if ( b->a_sockname_pat.bv_len ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_sockname_path: %s\n",
+                                  conn->c_connid, b->a_sockname_pat.bv_val ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
+                               b->a_sockname_pat.bv_val, 0, 0 );
+#endif
+                       if ( ber_bvccmp( &b->a_sockname_pat, '*' ) != 0) {
+                               if ( b->a_sockname_style == ACL_STYLE_REGEX) {
+                                       if (!regex_matches( &b->a_sockname_pat, conn->c_sock_name.bv_val,
+                                                       e->e_ndn, matches ) ) 
+                                       {
+                                               continue;
+                                       }
+                               } else {
+                                       if ( ber_bvstrcasecmp( &b->a_sockname_pat, &conn->c_sock_name ) != 0 )
+                                               continue;
                                }
                        }
-                       break;
                }
-       }
 
-       return( LDAP_SUCCESS );
-}
+               if ( b->a_dn_at != NULL && op->o_ndn.bv_len != 0 ) {
+                       Attribute       *at;
+                       struct berval   bv;
+                       int rc, match = 0;
+                       const char *text;
+                       const char *attr = b->a_dn_at->ad_cname.bv_val;
 
-#ifdef sunos5
+                       assert( attr != NULL );
 
-static int
-regex_matches( char *pat, char *str )
-{
-       char    *e;
-       int     rc;
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_dn_pat: %s\n",
+                                  conn->c_connid, attr ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
+                               attr, 0, 0);
+#endif
+                       bv = op->o_ndn;
 
-       if ( (e = compile( pat, NULL, NULL )) == NULL ) {
-               Debug( LDAP_DEBUG_ANY,
-                   "compile( \"%s\", \"%s\") failed\n", pat, str, 0 );
-               return( 0 );
-       }
-       rc = step( str ? str : "", e );
-       free( e );
+                       /* see if asker is listed in dnattr */
+                       for( at = attrs_find( e->e_attrs, b->a_dn_at );
+                               at != NULL;
+                               at = attrs_find( at->a_next, b->a_dn_at ) )
+                       {
+                               if( value_find( b->a_dn_at, at->a_vals, &bv ) == 0 ) {
+                                       /* found it */
+                                       match = 1;
+                                       break;
+                               }
+                       }
 
-       return( rc );
-}
+                       if( match ) {
+                               /* have a dnattr match. if this is a self clause then
+                                * the target must also match the op dn.
+                                */
+                               if ( b->a_dn_self ) {
+                                       /* check if the target is an attribute. */
+                                       if ( val == NULL )
+                                               continue;
+                                       /* target is attribute, check if the attribute value
+                                        * is the op dn.
+                                        */
+                                       rc = value_match( &match, b->a_dn_at,
+                                               b->a_dn_at->ad_type->sat_equality, 0,
+                                               val, &bv, &text );
+                                       /* on match error or no match, fail the ACL clause */
+                                       if (rc != LDAP_SUCCESS || match != 0 )
+                                               continue;
+                               }
+                       } else {
+                               /* no dnattr match, check if this is a self clause */
+                               if ( ! b->a_dn_self )
+                                       continue;
 
-#else /* sunos5 */
+                               ACL_RECORD_VALUE_STATE;
+                               
+                               /* this is a self clause, check if the target is an
+                                * attribute.
+                                */
+                               if ( val == NULL )
+                                       continue;
 
-static int
-regex_matches( char *pat, char *str )
-{
-       char    *e;
-       int     rc;
+                               /* target is attribute, check if the attribute value
+                                * is the op dn.
+                                */
+                               rc = value_match( &match, b->a_dn_at,
+                                       b->a_dn_at->ad_type->sat_equality, 0,
+                                       val, &bv, &text );
 
-       pthread_mutex_lock( &regex_mutex );
-       if ( (e = re_comp( pat )) != NULL ) {
-               Debug( LDAP_DEBUG_ANY,
-                   "re_comp( \"%s\", \"%s\") failed because (%s)\n", pat, str,
-                   e );
-               pthread_mutex_unlock( &regex_mutex );
-               return( 0 );
-       }
-       rc = re_exec( str ? str : "" );
-       pthread_mutex_unlock( &regex_mutex );
+                               /* on match error or no match, fail the ACL clause */
+                               if (rc != LDAP_SUCCESS || match != 0 )
+                                       continue;
+                       }
+               }
+
+               if ( b->a_group_pat.bv_len && op->o_ndn.bv_len ) {
+                       char buf[1024];
+                       struct berval bv;
+                       struct berval ndn = { 0, NULL };
+                       int rc;
+
+                       bv.bv_len = sizeof(buf) - 1;
+                       bv.bv_val = buf; 
 
-       return( rc == 1 );
+                       /* b->a_group is an unexpanded entry name, expanded it should be an 
+                        * entry with objectclass group* and we test to see if odn is one of
+                        * the values in the attribute group
+                        */
+                       /* see if asker is listed in dnattr */
+                       if ( b->a_group_style == ACL_STYLE_REGEX ) {
+                               string_expand(&bv, &b->a_group_pat, e->e_ndn, matches);
+                               if ( dnNormalize2(NULL, &bv, &ndn) != LDAP_SUCCESS ) {
+                                       /* did not expand to a valid dn */
+                                       continue;
+                               }
+                               bv = ndn;
+                       } else {
+                               bv = b->a_group_pat;
+                       }
+
+                       rc = backend_group(be, conn, op, e, &bv, &op->o_ndn,
+                               b->a_group_oc, b->a_group_at);
+                       if ( ndn.bv_val )
+                               free( ndn.bv_val );
+                       if ( rc != 0 ) {
+                               continue;
+                       }
+               }
+
+               if ( b->a_set_pat.bv_len != 0 ) {
+                       if (aci_match_set( &b->a_set_pat, be, e, conn, op, 0 ) == 0) {
+                               continue;
+                       }
+               }
+
+               if ( b->a_authz.sai_ssf ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_authz.sai_ssf: ACL %u > OP %u\n",
+                                  conn->c_connid, b->a_authz.sai_ssf, op->o_ssf ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
+                               b->a_authz.sai_ssf, op->o_ssf, 0 );
+#endif
+                       if ( b->a_authz.sai_ssf >  op->o_ssf ) {
+                               continue;
+                       }
+               }
+
+               if ( b->a_authz.sai_transport_ssf ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
+                                  conn->c_connid, b->a_authz.sai_transport_ssf, op->o_transport_ssf ));
+#else
+                       Debug( LDAP_DEBUG_ACL,
+                               "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
+                               b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
+#endif
+                       if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
+                               continue;
+                       }
+               }
+
+               if ( b->a_authz.sai_tls_ssf ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d  check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
+                                  conn->c_connid, b->a_authz.sai_tls_ssf, op->o_tls_ssf ));
+#else
+                       Debug( LDAP_DEBUG_ACL,
+                               "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
+                               b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
+#endif
+                       if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
+                               continue;
+                       }
+               }
+
+               if ( b->a_authz.sai_sasl_ssf ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                                  "acl_mask: conn %d check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
+                                  conn->c_connid, b->a_authz.sai_sasl_ssf, op->o_sasl_ssf ));
+#else
+                       Debug( LDAP_DEBUG_ACL,
+                               "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
+                               b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
+#endif
+                       if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
+                               continue;
+                       }
+               }
+
+#ifdef SLAPD_ACI_ENABLED
+               if ( b->a_aci_at != NULL ) {
+                       Attribute       *at;
+                       slap_access_t grant, deny, tgrant, tdeny;
+
+                       /* this case works different from the others above.
+                        * since aci's themselves give permissions, we need
+                        * to first check b->a_access_mask, the ACL's access level.
+                        */
+
+                       if ( e->e_nname.bv_len == 0 ) {
+                               /* no ACIs in the root DSE */
+                               continue;
+                       }
+
+                       /* first check if the right being requested
+                        * is allowed by the ACL clause.
+                        */
+                       if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
+                               continue;
+                       }
+
+                       /* get the aci attribute */
+                       at = attr_find( e->e_attrs, b->a_aci_at );
+                       if ( at == NULL ) {
+                               continue;
+                       }
+
+                       ACL_RECORD_VALUE_STATE;
+
+                       /* start out with nothing granted, nothing denied */
+                       ACL_INIT(tgrant);
+                       ACL_INIT(tdeny);
+
+                       /* the aci is an multi-valued attribute.  The
+                        * rights are determined by OR'ing the individual
+                        * rights given by the acis.
+                        */
+                       for ( i = 0; at->a_vals[i].bv_val != NULL; i++ ) {
+                               if (aci_mask( be, conn, op,
+                                       e, desc, val, &at->a_vals[i],
+                                       matches, &grant, &deny ) != 0)
+                               {
+                                       tgrant |= grant;
+                                       tdeny |= deny;
+                               }
+                       }
+
+                       /* remove anything that the ACL clause does not allow */
+                       tgrant &= b->a_access_mask & ACL_PRIV_MASK;
+                       tdeny &= ACL_PRIV_MASK;
+
+                       /* see if we have anything to contribute */
+                       if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
+                               continue;
+                       }
+
+                       /* this could be improved by changing acl_mask so that it can deal with
+                        * by clauses that return grant/deny pairs.  Right now, it does either
+                        * additive or subtractive rights, but not both at the same time.  So,
+                        * we need to combine the grant/deny pair into a single rights mask in
+                        * a smart way:  if either grant or deny is "empty", then we use the
+                        * opposite as is, otherwise we remove any denied rights from the grant
+                        * rights mask and construct an additive mask.
+                        */
+                       if (ACL_IS_INVALID(tdeny)) {
+                               modmask = tgrant | ACL_PRIV_ADDITIVE;
+
+                       } else if (ACL_IS_INVALID(tgrant)) {
+                               modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
+
+                       } else {
+                               modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
+                       }
+
+               } else
+#endif
+               {
+                       modmask = b->a_access_mask;
+               }
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_RESULTS,
+                          "acl_mask: conn %d  [%d] applying %s (%s)\n",
+                          conn->c_connid, i, accessmask2str( modmask, accessmaskbuf),
+                          b->a_type == ACL_CONTINUE ? "continue" : b->a_type == ACL_BREAK
+                          ? "break" : "stop" ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "<= acl_mask: [%d] applying %s (%s)\n",
+                       i, accessmask2str( modmask, accessmaskbuf ), 
+                       b->a_type == ACL_CONTINUE
+                               ? "continue"
+                               : b->a_type == ACL_BREAK
+                                       ? "break"
+                                       : "stop" );
+#endif
+               /* save old mask */
+               oldmask = *mask;
+
+               if( ACL_IS_ADDITIVE(modmask) ) {
+                       /* add privs */
+                       ACL_PRIV_SET( *mask, modmask );
+
+                       /* cleanup */
+                       ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
+
+               } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
+                       /* substract privs */
+                       ACL_PRIV_CLR( *mask, modmask );
+
+                       /* cleanup */
+                       ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
+
+               } else {
+                       /* assign privs */
+                       *mask = modmask;
+               }
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+                          "acl_mask: conn %d  [%d] mask: %s\n",
+                          conn->c_connid, i, accessmask2str( *mask, accessmaskbuf) ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "<= acl_mask: [%d] mask: %s\n",
+                       i, accessmask2str(*mask, accessmaskbuf), 0 );
+#endif
+
+               if( b->a_type == ACL_CONTINUE ) {
+                       continue;
+
+               } else if ( b->a_type == ACL_BREAK ) {
+                       return ACL_BREAK;
+
+               } else {
+                       return ACL_STOP;
+               }
+       }
+
+       /* implicit "by * none" clause */
+       ACL_INIT(*mask);
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "acl", LDAP_LEVEL_RESULTS,
+                  "acl_mask: conn %d  no more <who> clauses, returning %d (stop)\n",
+                  conn->c_connid, accessmask2str( *mask, accessmaskbuf) ));
+#else
+       Debug( LDAP_DEBUG_ACL,
+               "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
+               accessmask2str(*mask, accessmaskbuf), 0, 0 );
+#endif
+       return ACL_STOP;
+}
+
+/*
+ * acl_check_modlist - check access control on the given entry to see if
+ * it allows the given modifications by the user associated with op.
+ * returns     1       if mods allowed ok
+ *                     0       mods not allowed
+ */
+
+int
+acl_check_modlist(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    Entry      *e,
+    Modifications      *mlist
+)
+{
+       struct berval *bv;
+
+       assert( be != NULL );
+
+       /* short circuit root database access */
+       if ( be_isroot( be, &op->o_ndn ) ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+                          "acl_check_modlist: conn %d  access granted to root user\n",
+                          conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "<= acl_access_allowed: granted to database root\n",
+                   0, 0, 0 );
+#endif
+               return 1;
+       }
+
+       /* use backend default access if no backend acls */
+       if( be != NULL && be->be_acl == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+                          "acl_check_modlist: conn %d  backend default %s access %s to \"%s\"\n",
+                          conn->c_connid, access2str( ACL_WRITE ),
+                          be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "=> access_allowed: backend default %s access %s to \"%s\"\n",
+                       access2str( ACL_WRITE ),
+                       be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val );
+#endif
+               return be->be_dfltaccess >= ACL_WRITE;
+
+#ifdef notdef
+       /* be is always non-NULL */
+       /* use global default access if no global acls */
+       } else if ( be == NULL && global_acl == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+                          "acl_check_modlist: conn %d  global default %s access %s to \"%s\"\n",
+                          conn->c_connid, access2str( ACL_WRITE ),
+                          global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn ));
+#else
+               Debug( LDAP_DEBUG_ACL,
+                       "=> access_allowed: global default %s access %s to \"%s\"\n",
+                       access2str( ACL_WRITE ),
+                       global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn );
+#endif
+               return global_default_access >= ACL_WRITE;
+#endif
+       }
+
+       for ( ; mlist != NULL; mlist = mlist->sml_next ) {
+               static AccessControlState state_init = ACL_STATE_INIT;
+               AccessControlState state;
+
+               /*
+                * no-user-modification operational attributes are ignored
+                * by ACL_WRITE checking as any found here are not provided
+                * by the user
+                */
+               if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+                                  "acl_check_modlist: conn %d  no-user-mod %s: modify access granted\n",
+                                  conn->c_connid, mlist->sml_desc->ad_cname.bv_val ));
+#else
+                       Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
+                               " modify access granted\n",
+                               mlist->sml_desc->ad_cname.bv_val, 0, 0 );
+#endif
+                       continue;
+               }
+
+               state = state_init;
+
+               switch ( mlist->sml_op ) {
+               case LDAP_MOD_REPLACE:
+                       /*
+                        * We must check both permission to delete the whole
+                        * attribute and permission to add the specific attributes.
+                        * This prevents abuse from selfwriters.
+                        */
+                       if ( ! access_allowed( be, conn, op, e,
+                               mlist->sml_desc, NULL, ACL_WRITE, &state ) )
+                       {
+                               return( 0 );
+                       }
+
+                       if ( mlist->sml_bvalues == NULL ) break;
+
+                       /* fall thru to check value to add */
+
+               case LDAP_MOD_ADD:
+                       assert( mlist->sml_bvalues != NULL );
+
+                       for ( bv = mlist->sml_bvalues; bv->bv_val != NULL; bv++ ) {
+                               if ( ! access_allowed( be, conn, op, e,
+                                       mlist->sml_desc, bv, ACL_WRITE, &state ) )
+                               {
+                                       return( 0 );
+                               }
+                       }
+                       break;
+
+               case LDAP_MOD_DELETE:
+                       if ( mlist->sml_bvalues == NULL ) {
+                               if ( ! access_allowed( be, conn, op, e,
+                                       mlist->sml_desc, NULL, ACL_WRITE, NULL ) )
+                               {
+                                       return( 0 );
+                               }
+                               break;
+                       }
+                       for ( bv = mlist->sml_bvalues; bv->bv_val != NULL; bv++ ) {
+                               if ( ! access_allowed( be, conn, op, e,
+                                       mlist->sml_desc, bv, ACL_WRITE, &state ) )
+                               {
+                                       return( 0 );
+                               }
+                       }
+                       break;
+
+               case SLAP_MOD_SOFTADD:
+                       /* allow adding attribute via modrdn thru */
+                       break;
+
+               default:
+                       assert( 0 );
+                       return( 0 );
+               }
+       }
+
+       return( 1 );
+}
+
+static char *
+aci_bvstrdup( struct berval *bv )
+{
+       char *s;
+
+       s = (char *)ch_malloc(bv->bv_len + 1);
+       if (s != NULL) {
+               AC_MEMCPY(s, bv->bv_val, bv->bv_len);
+               s[bv->bv_len] = 0;
+       }
+       return(s);
+}
+
+static int
+aci_get_part(
+       struct berval *list,
+       int ix,
+       char sep,
+       struct berval *bv )
+{
+       int len;
+       char *p;
+
+       if (bv) {
+               bv->bv_len = 0;
+               bv->bv_val = NULL;
+       }
+       len = list->bv_len;
+       p = list->bv_val;
+       while (len >= 0 && --ix >= 0) {
+               while (--len >= 0 && *p++ != sep) ;
+       }
+       while (len >= 0 && *p == ' ') {
+               len--;
+               p++;
+       }
+       if (len < 0)
+               return(-1);
+
+       if (!bv)
+               return(0);
+
+       bv->bv_val = p;
+       while (--len >= 0 && *p != sep) {
+               bv->bv_len++;
+               p++;
+       }
+       while (bv->bv_len > 0 && *--p == ' ')
+               bv->bv_len--;
+       return(bv->bv_len);
+}
+
+BerVarray
+aci_set_gather (void *cookie, struct berval *name, struct berval *attr)
+{
+       AciSetCookie *cp = cookie;
+       BerVarray bvals = NULL;
+       struct berval ndn;
+
+       /* this routine needs to return the bervals instead of
+        * plain strings, since syntax is not known.  It should
+        * also return the syntax or some "comparison cookie".
+        */
+
+       if (dnNormalize2(NULL, name, &ndn) == LDAP_SUCCESS) {
+               const char *text;
+               AttributeDescription *desc = NULL;
+               if (slap_bv2ad(attr, &desc, &text) == LDAP_SUCCESS) {
+                       backend_attribute(cp->be, NULL, NULL,
+                               cp->e, &ndn, desc, &bvals);
+               }
+               free(ndn.bv_val);
+       }
+       return(bvals);
+}
+
+static int
+aci_match_set (
+       struct berval *subj,
+    Backend *be,
+    Entry *e,
+    Connection *conn,
+    Operation *op,
+    int setref
+)
+{
+       struct berval set = { 0, NULL };
+       int rc = 0;
+       AciSetCookie cookie;
+
+       if (setref == 0) {
+               ber_dupbv( &set, subj );
+       } else {
+               struct berval subjdn, ndn = { 0, NULL };
+               struct berval setat;
+               BerVarray bvals;
+               const char *text;
+               AttributeDescription *desc = NULL;
+
+               /* format of string is "entry/setAttrName" */
+               if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
+                       return(0);
+               }
+
+               if ( aci_get_part(subj, 1, '/', &setat) < 0 ) {
+                       setat.bv_val = SLAPD_ACI_SET_ATTR;
+                       setat.bv_len = sizeof(SLAPD_ACI_SET_ATTR)-1;
+               }
+
+               if ( setat.bv_val != NULL ) {
+                       /*
+                        * NOTE: dnNormalize2 honors the ber_len field
+                        * as the length of the dn to be normalized
+                        */
+                       if ( dnNormalize2(NULL, &subjdn, &ndn) == LDAP_SUCCESS
+                               && slap_bv2ad(&setat, &desc, &text) == LDAP_SUCCESS )
+                       {
+                               backend_attribute(be, NULL, NULL, e,
+                                       &ndn, desc, &bvals);
+                               if ( bvals != NULL ) {
+                                       if ( bvals[0].bv_val != NULL ) {
+                                               int i;
+                                               set = bvals[0];
+                                               bvals[0].bv_val = NULL;
+                                               for (i=1;bvals[i].bv_val;i++);
+                                               bvals[0].bv_val = bvals[i-1].bv_val;
+                                               bvals[i-1].bv_val = NULL;
+                                       }
+                                       ber_bvarray_free(bvals);
+                               }
+                       }
+                       if (ndn.bv_val)
+                               free(ndn.bv_val);
+               }
+       }
+
+       if (set.bv_val != NULL) {
+               cookie.be = be;
+               cookie.e = e;
+               cookie.conn = conn;
+               cookie.op = op;
+               rc = (slap_set_filter(aci_set_gather, &cookie, &set,
+                       &op->o_ndn, &e->e_nname, NULL) > 0);
+               ch_free(set.bv_val);
+       }
+       return(rc);
+}
+
+#ifdef SLAPD_ACI_ENABLED
+static int
+aci_list_map_rights(
+       struct berval *list )
+{
+       struct berval bv;
+       slap_access_t mask;
+       int i;
+
+       ACL_INIT(mask);
+       for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
+               if (bv.bv_len <= 0)
+                       continue;
+               switch (*bv.bv_val) {
+               case 'c':
+                       ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
+                       break;
+               case 's':
+                       /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+                        * the right 's' to mean "set", but in the examples states
+                        * that the right 's' means "search".  The latter definition
+                        * is used here.
+                        */
+                       ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
+                       break;
+               case 'r':
+                       ACL_PRIV_SET(mask, ACL_PRIV_READ);
+                       break;
+               case 'w':
+                       ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
+                       break;
+               case 'x':
+                       /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
+                        * define any equivalent to the AUTH right, so I've just used
+                        * 'x' for now.
+                        */
+                       ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+                       break;
+               default:
+                       break;
+               }
+
+       }
+       return(mask);
+}
+
+static int
+aci_list_has_attr(
+       struct berval *list,
+       const struct berval *attr,
+       struct berval *val )
+{
+       struct berval bv, left, right;
+       int i;
+
+       for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
+               if (aci_get_part(&bv, 0, '=', &left) < 0
+                       || aci_get_part(&bv, 1, '=', &right) < 0)
+               {
+                       if (ber_bvstrcasecmp(attr, &bv) == 0)
+                               return(1);
+               } else if (val == NULL) {
+                       if (ber_bvstrcasecmp(attr, &left) == 0)
+                               return(1);
+               } else {
+                       if (ber_bvstrcasecmp(attr, &left) == 0) {
+                               /* this is experimental code that implements a
+                                * simple (prefix) match of the attribute value.
+                                * the ACI draft does not provide for aci's that
+                                * apply to specific values, but it would be
+                                * nice to have.  If the <attr> part of an aci's
+                                * rights list is of the form <attr>=<value>,
+                                * that means the aci applies only to attrs with
+                                * the given value.  Furthermore, if the attr is
+                                * of the form <attr>=<value>*, then <value> is
+                                * treated as a prefix, and the aci applies to 
+                                * any value with that prefix.
+                                *
+                                * Ideally, this would allow r.e. matches.
+                                */
+                               if (aci_get_part(&right, 0, '*', &left) < 0
+                                       || right.bv_len <= left.bv_len)
+                               {
+                                       if (ber_bvstrcasecmp(val, &right) == 0)
+                                               return(1);
+                               } else if (val->bv_len >= left.bv_len) {
+                                       if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
+                                               return(1);
+                               }
+                       }
+               }
+       }
+       return(0);
+}
+
+static slap_access_t
+aci_list_get_attr_rights(
+       struct berval *list,
+       const struct berval *attr,
+       struct berval *val )
+{
+    struct berval bv;
+    slap_access_t mask;
+    int i;
+
+       /* loop through each rights/attr pair, skip first part (action) */
+       ACL_INIT(mask);
+       for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
+               if (aci_list_has_attr(&bv, attr, val) == 0)
+                       continue;
+               if (aci_get_part(list, i, ';', &bv) < 0)
+                       continue;
+               mask |= aci_list_map_rights(&bv);
+       }
+       return(mask);
+}
+
+static int
+aci_list_get_rights(
+       struct berval *list,
+       const struct berval *attr,
+       struct berval *val,
+       slap_access_t *grant,
+       slap_access_t *deny )
+{
+    struct berval perm, actn;
+    slap_access_t *mask;
+    int i, found;
+
+       if (attr == NULL || attr->bv_len == 0 
+                       || ber_bvstrcasecmp( attr, &aci_bv_entry ) == 0) {
+               attr = &aci_bv_br_entry;
+       }
+
+       found = 0;
+       ACL_INIT(*grant);
+       ACL_INIT(*deny);
+       /* loop through each permissions clause */
+       for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
+               if (aci_get_part(&perm, 0, ';', &actn) < 0)
+                       continue;
+               if (ber_bvstrcasecmp( &aci_bv_grant, &actn ) == 0) {
+                       mask = grant;
+               } else if (ber_bvstrcasecmp( &aci_bv_deny, &actn ) == 0) {
+                       mask = deny;
+               } else {
+                       continue;
+               }
+
+               found = 1;
+               *mask |= aci_list_get_attr_rights(&perm, attr, val);
+               *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
+       }
+       return(found);
+}
+
+static int
+aci_group_member (
+       struct berval *subj,
+       struct berval *defgrpoc,
+       struct berval *defgrpat,
+    Backend            *be,
+    Entry              *e,
+    Connection         *conn,
+    Operation          *op,
+       regmatch_t      *matches
+)
+{
+       struct berval bv;
+       struct berval subjdn;
+       struct berval grpoc;
+       struct berval grpat;
+       ObjectClass *grp_oc = NULL;
+       AttributeDescription *grp_ad = NULL;
+       const char *text;
+       int rc;
+
+       /* format of string is "group/objectClassValue/groupAttrName" */
+       if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
+               return(0);
+       }
+
+       if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
+               grpoc = *defgrpoc;
+       }
+
+       if (aci_get_part(subj, 2, '/', &grpat) < 0) {
+               grpat = *defgrpat;
+       }
+
+       rc = slap_bv2ad( &grpat, &grp_ad, &text );
+       if( rc != LDAP_SUCCESS ) {
+               rc = 0;
+               goto done;
+       }
+       rc = 0;
+
+       grp_oc = oc_bvfind( &grpoc );
+
+       if (grp_oc != NULL && grp_ad != NULL ) {
+               struct berval ndn;
+               bv.bv_val = (char *)ch_malloc(1024);
+               bv.bv_len = 1024;
+               string_expand(&bv, &subjdn, e->e_ndn, matches);
+               if ( dnNormalize2(NULL, &bv, &ndn) == LDAP_SUCCESS ) {
+                       rc = (backend_group(be, conn, op, e, &ndn, &op->o_ndn, grp_oc, grp_ad) == 0);
+                       free( ndn.bv_val );
+               }
+               ch_free(bv.bv_val);
+       }
+
+done:
+       return(rc);
+}
+
+static struct berval GroupClass = {
+       sizeof(SLAPD_GROUP_CLASS)-1, SLAPD_GROUP_CLASS };
+static struct berval GroupAttr = {
+       sizeof(SLAPD_GROUP_ATTR)-1, SLAPD_GROUP_ATTR };
+static struct berval RoleClass = {
+       sizeof(SLAPD_ROLE_CLASS)-1, SLAPD_ROLE_CLASS };
+static struct berval RoleAttr = {
+       sizeof(SLAPD_ROLE_ATTR)-1, SLAPD_ROLE_ATTR };
+
+static int
+aci_mask(
+    Backend                    *be,
+    Connection         *conn,
+    Operation          *op,
+    Entry                      *e,
+       AttributeDescription *desc,
+    struct berval      *val,
+    struct berval      *aci,
+       regmatch_t              *matches,
+       slap_access_t   *grant,
+       slap_access_t   *deny
+)
+{
+    struct berval bv, perms, sdn;
+       int rc;
+               
+
+       assert( desc->ad_cname.bv_val != NULL );
+
+       /* parse an aci of the form:
+               oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
+
+          See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
+          a full description of the format for this attribute.
+
+          For now, this routine only supports scope=entry.
+        */
+
+       /* check that the aci has all 5 components */
+       if (aci_get_part(aci, 4, '#', NULL) < 0)
+               return(0);
+
+       /* check that the aci family is supported */
+       if (aci_get_part(aci, 0, '#', &bv) < 0)
+               return(0);
+
+       /* check that the scope is "entry" */
+       if (aci_get_part(aci, 1, '#', &bv) < 0
+               || ber_bvstrcasecmp( &aci_bv_entry, &bv ) != 0)
+       {
+               return(0);
+       }
+
+       /* get the list of permissions clauses, bail if empty */
+       if (aci_get_part(aci, 2, '#', &perms) <= 0)
+               return(0);
+
+       /* check if any permissions allow desired access */
+       if (aci_list_get_rights(&perms, &desc->ad_cname, val, grant, deny) == 0)
+               return(0);
+
+       /* see if we have a DN match */
+       if (aci_get_part(aci, 3, '#', &bv) < 0)
+               return(0);
+
+       if (aci_get_part(aci, 4, '#', &sdn) < 0)
+               return(0);
+
+       if (ber_bvstrcasecmp( &aci_bv_access_id, &bv ) == 0) {
+               struct berval ndn;
+               rc = 1;
+               if ( dnNormalize2(NULL, &sdn, &ndn) == LDAP_SUCCESS ) {
+                       if (!dn_match( &op->o_ndn, &ndn))
+                               rc = 0;
+                       free(ndn.bv_val);
+               }
+               return(rc);
+       }
+
+       if (ber_bvstrcasecmp( &aci_bv_self, &bv ) == 0) {
+               if (dn_match(&op->o_ndn, &e->e_nname))
+                       return(1);
+
+       } else if (ber_bvstrcasecmp( &aci_bv_dnattr, &bv ) == 0) {
+               Attribute *at;
+               AttributeDescription *ad = NULL;
+               const char *text;
+
+               rc = slap_bv2ad( &sdn, &ad, &text );
+
+               if( rc != LDAP_SUCCESS ) {
+                       return 0;
+               }
+
+               rc = 0;
+
+               bv = op->o_ndn;
+
+               for(at = attrs_find( e->e_attrs, ad );
+                       at != NULL;
+                       at = attrs_find( at->a_next, ad ) )
+               {
+                       if (value_find( ad, at->a_vals, &bv) == 0 ) {
+                               rc = 1;
+                               break;
+                       }
+               }
+
+               return rc;
+
+
+       } else if (ber_bvstrcasecmp( &aci_bv_group, &bv ) == 0) {
+               if (aci_group_member(&sdn, &GroupClass, &GroupAttr, be, e, conn, op, matches))
+                       return(1);
+
+       } else if (ber_bvstrcasecmp( &aci_bv_role, &bv ) == 0) {
+               if (aci_group_member(&sdn, &RoleClass, &RoleAttr, be, e, conn, op, matches))
+                       return(1);
+
+       } else if (ber_bvstrcasecmp( &aci_bv_set, &bv ) == 0) {
+               if (aci_match_set(&sdn, be, e, conn, op, 0))
+                       return(1);
+
+       } else if (ber_bvstrcasecmp( &aci_bv_set_ref, &bv ) == 0) {
+               if (aci_match_set(&sdn, be, e, conn, op, 1))
+                       return(1);
+
+       }
+
+       return(0);
+}
+
+#endif /* SLAPD_ACI_ENABLED */
+
+static void
+string_expand(
+       struct berval *bv,
+       struct berval *pat,
+       char *match,
+       regmatch_t *matches)
+{
+       ber_len_t       size;
+       char   *sp;
+       char   *dp;
+       int     flag;
+
+       size = 0;
+       bv->bv_val[0] = '\0';
+       bv->bv_len--; /* leave space for lone $ */
+
+       flag = 0;
+       for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
+               sp < pat->bv_val + pat->bv_len ; sp++) {
+               /* did we previously see a $ */
+               if (flag) {
+                       if (*sp == '$') {
+                               *dp++ = '$';
+                               size++;
+                       } else if (*sp >= '0' && *sp <= '9' ) {
+                               int     n;
+                               int     i;
+                               int     l;
+
+                               n = *sp - '0';
+                               *dp = '\0';
+                               i = matches[n].rm_so;
+                               l = matches[n].rm_eo; 
+                               for ( ; size < bv->bv_len && i < l; size++, i++ ) {
+                                       *dp++ = match[i];
+                                       size++;
+                               }
+                               *dp = '\0';
+                       }
+                       flag = 0;
+               } else {
+                       if (*sp == '$') {
+                               flag = 1;
+                       } else {
+                               *dp++ = *sp;
+                               size++;
+                       }
+               }
+       }
+
+       if (flag) {
+               /* must have ended with a single $ */
+               *dp++ = '$';
+               size++;
+       }
+
+       *dp = '\0';
+       bv->bv_len = size;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+                  "string_expand:  pattern = %.*s\n", pat->bv_len, pat->bv_val ));
+       LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+                  "string_expand:  expanded = %s\n", bv->bv_val ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %.*s\n", pat->bv_len, pat->bv_val, 0 );
+       Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
+#endif
+}
+
+static int
+regex_matches(
+       struct berval *pat,                     /* pattern to expand and match against */
+       char *str,                              /* string to match against pattern */
+       char *buf,                              /* buffer with $N expansion variables */
+       regmatch_t *matches             /* offsets in buffer for $N expansion variables */
+)
+{
+       regex_t re;
+       char newbuf[512];
+       struct berval bv;
+       int     rc;
+
+       bv.bv_len = sizeof(newbuf);
+       bv.bv_val = newbuf;
+
+       if(str == NULL) str = "";
+
+       string_expand(&bv, pat, buf, matches);
+       if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
+               char error[512];
+               regerror(rc, &re, error, sizeof(error));
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "aci", LDAP_LEVEL_ERR,
+                          "regex_matches: compile( \"%s\", \"%s\") failed %s\n",
+                          pat->bv_val, str, error ));
+#else
+               Debug( LDAP_DEBUG_TRACE,
+                   "compile( \"%s\", \"%s\") failed %s\n",
+                       pat->bv_val, str, error );
+#endif
+               return( 0 );
+       }
+
+       rc = regexec(&re, str, 0, NULL, 0);
+       regfree( &re );
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL2,
+                  "regex_matches: string:   %s\n", str ));
+       LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL2,
+                  "regex_matches: rc:  %d  %s\n",
+                  rc, rc ? "matches" : "no matches" ));
+#else
+       Debug( LDAP_DEBUG_TRACE,
+           "=> regex_matches: string:   %s\n", str, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE,
+           "=> regex_matches: rc: %d %s\n",
+               rc, !rc ? "matches" : "no matches", 0 );
+#endif
+       return( !rc );
 }
 
-#endif /* sunos5 */
diff --git a/servers/slapd/backglue.c b/servers/slapd/backglue.c
new file mode 100644 (file)
index 0000000..4b0b490
--- /dev/null
@@ -0,0 +1,907 @@
+/* backglue.c - backend glue routines */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2001-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+/*
+ * Functions to glue a bunch of other backends into a single tree.
+ * All of the glued backends must share a common suffix. E.g., you
+ * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar.
+ *
+ * This uses the backend structures and routines extensively, but is
+ * not an actual backend of its own. To use it you must add a "subordinate"
+ * keyword to the configuration of other backends. Subordinates will
+ * automatically be connected to their parent backend.
+ *
+ * The purpose of these functions is to allow you to split a single database
+ * into pieces (for load balancing purposes, whatever) but still be able
+ * to treat it as a single database after it's been split. As such, each
+ * of the glued backends should have identical rootdn and rootpw.
+ *
+ * If you need more elaborate configuration, you probably should be using
+ * back-meta instead.
+ *  -- Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#define SLAPD_TOOLS
+#include "slap.h"
+
+typedef struct gluenode {
+       BackendDB *be;
+       struct berval pdn;
+} gluenode;
+
+typedef struct glueinfo {
+       BackendDB *be;
+       int nodes;
+       gluenode n[1];
+} glueinfo;
+
+static int glueMode;
+static BackendDB *glueBack;
+
+/* Just like select_backend, but only for our backends */
+static BackendDB *
+glue_back_select (
+       BackendDB *be,
+       const char *dn
+)
+{
+       glueinfo *gi = (glueinfo *) be->be_private;
+       struct berval bv;
+       int i;
+
+       bv.bv_len = strlen(dn);
+       bv.bv_val = (char *) dn;
+
+       for (i = 0; i<gi->nodes; i++) {
+               if (dnIsSuffix(&bv, gi->n[i].be->be_nsuffix[0])) {
+                       return gi->n[i].be;
+               }
+       }
+       return NULL;
+}
+
+/* This function will only be called in tool mode */
+static int
+glue_back_open (
+       BackendInfo *bi
+)
+{
+       int rc = 0;
+       static int glueOpened = 0;
+
+       if (glueOpened) return 0;
+
+       glueOpened = 1;
+
+       /* If we were invoked in tool mode, open all the underlying backends */
+       if (slapMode & SLAP_TOOL_MODE) {
+               rc = backend_startup (NULL);
+       } /* other case is impossible */
+       return rc;
+}
+
+/* This function will only be called in tool mode */
+static int
+glue_back_close (
+       BackendInfo *bi
+)
+{
+       static int glueClosed = 0;
+       int rc;
+
+       if (glueClosed) return 0;
+
+       glueClosed = 1;
+
+       if (slapMode & SLAP_TOOL_MODE) {
+               rc = backend_shutdown (NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_db_open (
+       BackendDB *be
+)
+{
+       glueinfo *gi = (glueinfo *)be->be_private;
+       static int glueOpened = 0;
+       int rc = 0;
+
+       if (glueOpened) return 0;
+
+       glueOpened = 1;
+
+       gi->be->be_acl = be->be_acl;
+
+       if (gi->be->bd_info->bi_db_open)
+               rc = gi->be->bd_info->bi_db_open(gi->be);
+
+       return rc;
+}
+
+static int
+glue_back_db_close (
+       BackendDB *be
+)
+{
+       glueinfo *gi = (glueinfo *)be->be_private;
+       static int glueClosed = 0;
+
+       if (glueClosed) return 0;
+
+       glueClosed = 1;
+
+       /* Close the master */
+       if (gi->be->bd_info->bi_db_close)
+               gi->be->bd_info->bi_db_close( gi->be );
+
+       return 0;
+}
+
+static int
+glue_back_db_destroy (
+       BackendDB *be
+)
+{
+       glueinfo *gi = (glueinfo *)be->be_private;
+
+       if (gi->be->bd_info->bi_db_destroy)
+               gi->be->bd_info->bi_db_destroy( gi->be );
+       free (gi->be);
+       free (gi);
+       return 0;
+}
+
+typedef struct glue_state {
+       int err;
+       int nentries;
+       int matchlen;
+       char *matched;
+       int nrefs;
+       BerVarray refs;
+       slap_callback *prevcb;
+} glue_state;
+
+static void
+glue_back_response (
+       Connection *conn,
+       Operation *op,
+       ber_tag_t tag,
+       ber_int_t msgid,
+       ber_int_t err,
+       const char *matched,
+       const char *text,
+       BerVarray ref,
+       const char *resoid,
+       struct berval *resdata,
+       struct berval *sasldata,
+       LDAPControl **ctrls
+)
+{
+       glue_state *gs = op->o_callback->sc_private;
+
+       if (err == LDAP_SUCCESS || gs->err != LDAP_SUCCESS)
+               gs->err = err;
+       if (gs->err == LDAP_SUCCESS && gs->matched) {
+               free (gs->matched);
+               gs->matchlen = 0;
+       }
+       if (gs->err != LDAP_SUCCESS && matched) {
+               int len;
+               len = strlen (matched);
+               if (len > gs->matchlen) {
+                       if (gs->matched)
+                               free (gs->matched);
+                       gs->matched = ch_strdup (matched);
+                       gs->matchlen = len;
+               }
+       }
+       if (ref) {
+               int i, j, k;
+               BerVarray new;
+
+               for (i=0; ref[i].bv_val; i++);
+
+               j = gs->nrefs;
+               if (!j) {
+                       new = ch_malloc ((i+1)*sizeof(struct berval));
+               } else {
+                       new = ch_realloc(gs->refs,
+                               (j+i+1)*sizeof(struct berval));
+               }
+               for (k=0; k<i; j++,k++) {
+                       ber_dupbv( &new[j], &ref[k] );
+               }
+               new[j].bv_val = NULL;
+               gs->nrefs = j;
+               gs->refs = new;
+       }
+}
+
+static void
+glue_back_sresult (
+       Connection *c,
+       Operation *op,
+       ber_int_t err,
+       const char *matched,
+       const char *text,
+       BerVarray refs,
+       LDAPControl **ctrls,
+       int nentries
+)
+{
+       glue_state *gs = op->o_callback->sc_private;
+
+       gs->nentries += nentries;
+       glue_back_response (c, op, 0, 0, err, matched, text, refs,
+                           NULL, NULL, NULL, ctrls);
+}
+
+static int
+glue_back_sendentry (
+       BackendDB *be,
+       Connection *c,
+       Operation *op,
+       Entry *e,
+       AttributeName *an,
+       int ao,
+       LDAPControl **ctrls
+)
+{
+       slap_callback *tmp = op->o_callback;
+       glue_state *gs = tmp->sc_private;
+       int rc;
+
+       op->o_callback = gs->prevcb;
+       if (op->o_callback && op->o_callback->sc_sendentry) {
+               rc = op->o_callback->sc_sendentry(be, c, op, e, an, ao, ctrls);
+       } else {
+               rc = send_search_entry(be, c, op, e, an, ao, ctrls);
+       }
+       op->o_callback = tmp;
+       return rc;
+}
+
+static int
+glue_back_search (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn,
+       int scope,
+       int deref,
+       int slimit,
+       int tlimit,
+       Filter *filter,
+       struct berval *filterstr,
+       AttributeName *attrs,
+       int attrsonly
+)
+{
+       glueinfo *gi = (glueinfo *)b0->be_private;
+       BackendDB *be;
+       int i, rc, t2limit = 0, s2limit = 0;
+       long stoptime = 0;
+       struct berval bv;
+       glue_state gs = {0};
+       slap_callback cb;
+
+       cb.sc_response = glue_back_response;
+       cb.sc_sresult = glue_back_sresult;
+       cb.sc_sendentry = glue_back_sendentry;
+       cb.sc_private = &gs;
+
+       gs.prevcb = op->o_callback;
+
+       if (tlimit) {
+               stoptime = slap_get_time () + tlimit;
+       }
+
+       switch (scope) {
+       case LDAP_SCOPE_BASE:
+               be = glue_back_select (b0, ndn->bv_val);
+
+               if (be && be->be_search) {
+                       rc = be->be_search (be, conn, op, dn, ndn, scope,
+                                  deref, slimit, tlimit, filter, filterstr,
+                                           attrs, attrsonly);
+               } else {
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       send_ldap_result (conn, op, rc, NULL,
+                                     "No search target found", NULL, NULL);
+               }
+               return rc;
+
+       case LDAP_SCOPE_ONELEVEL:
+       case LDAP_SCOPE_SUBTREE:
+               op->o_callback = &cb;
+
+               /*
+                * Execute in reverse order, most general first 
+                */
+               for (i = gi->nodes-1; i >= 0; i--) {
+                       if (!gi->n[i].be || !gi->n[i].be->be_search)
+                               continue;
+                       if (tlimit) {
+                               t2limit = stoptime - slap_get_time ();
+                               if (t2limit <= 0)
+                                       break;
+                       }
+                       if (slimit) {
+                               s2limit = slimit - gs.nentries;
+                               if (s2limit <= 0)
+                                       break;
+                       }
+                       /*
+                        * check for abandon 
+                        */
+                       ldap_pvt_thread_mutex_lock (&op->o_abandonmutex);
+                       rc = op->o_abandon;
+                       ldap_pvt_thread_mutex_unlock (&op->o_abandonmutex);
+                       if (rc) {
+                               rc = 0;
+                               goto done;
+                       }
+                       be = gi->n[i].be;
+                       if (scope == LDAP_SCOPE_ONELEVEL && 
+                               dn_match(&gi->n[i].pdn, ndn)) {
+                               rc = be->be_search (be, conn, op,
+                                       be->be_suffix[0], be->be_nsuffix[0],
+                                       LDAP_SCOPE_BASE, deref,
+                                       s2limit, t2limit, filter, filterstr,
+                                       attrs, attrsonly);
+
+                       } else if (scope == LDAP_SCOPE_SUBTREE &&
+                               dnIsSuffix(be->be_nsuffix[0], ndn)) {
+                               rc = be->be_search (be, conn, op,
+                                       be->be_suffix[0], be->be_nsuffix[0],
+                                       scope, deref,
+                                       s2limit, t2limit, filter, filterstr,
+                                       attrs, attrsonly);
+
+                       } else if (dnIsSuffix(&bv, be->be_nsuffix[0])) {
+                               rc = be->be_search (be, conn, op, dn, ndn,
+                                       scope, deref,
+                                       s2limit, t2limit, filter, filterstr,
+                                       attrs, attrsonly);
+                       }
+               }
+               break;
+       }
+       op->o_callback = gs.prevcb;
+
+       send_search_result (conn, op, gs.err, gs.matched, NULL,
+               gs.refs, NULL, gs.nentries);
+
+done:
+       if (gs.matched)
+               free (gs.matched);
+       if (gs.refs)
+               ber_bvarray_free(gs.refs);
+       return rc;
+}
+
+static int
+glue_back_bind (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn,
+       int method,
+       struct berval *cred,
+       struct berval *edn
+)
+{
+       BackendDB *be;
+       int rc;
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_bind) {
+               conn->c_authz_backend = be;
+               rc = be->be_bind (be, conn, op, dn, ndn, method, cred, edn);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               send_ldap_result (conn, op, rc, NULL, "No bind target found",
+                                 NULL, NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_compare (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn,
+       AttributeAssertion *ava
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_compare) {
+               rc = be->be_compare (be, conn, op, dn, ndn, ava);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               send_ldap_result (conn, op, rc, NULL, "No compare target found",
+                       NULL, NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_modify (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn,
+       Modifications *mod
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_modify) {
+               rc = be->be_modify (be, conn, op, dn, ndn, mod);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               send_ldap_result (conn, op, rc, NULL,
+                       "No modify target found", NULL, NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_modrdn (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn,
+       struct berval *newrdn,
+       struct berval *nnewrdn,
+       int del,
+       struct berval *newsup,
+       struct berval *nnewsup
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_modrdn) {
+               rc = be->be_modrdn (be, conn, op, dn, ndn,
+                       newrdn, nnewrdn, del, newsup, nnewsup );
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               send_ldap_result (conn, op, rc, NULL,
+                       "No modrdn target found", NULL, NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_add (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       Entry *e
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, e->e_ndn);
+
+       if (be && be->be_add) {
+               rc = be->be_add (be, conn, op, e);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               send_ldap_result (conn, op, rc, NULL, "No add target found",
+                                 NULL, NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_delete (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_delete) {
+               rc = be->be_delete (be, conn, op, dn, ndn);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               send_ldap_result (conn, op, rc, NULL, "No delete target found",
+                                 NULL, NULL);
+       }
+       return rc;
+}
+
+static int
+glue_back_release_rw (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       Entry *e,
+       int rw
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, e->e_ndn);
+
+       if (be && be->be_release) {
+               rc = be->be_release (be, conn, op, e, rw);
+       } else {
+               entry_free (e);
+               rc = 0;
+       }
+       return rc;
+}
+
+static int
+glue_back_group (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       Entry *target,
+       struct berval *ndn,
+       struct berval *ondn,
+       ObjectClass *oc,
+       AttributeDescription * ad
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_group) {
+               rc = be->be_group (be, conn, op, target, ndn, ondn, oc, ad);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+       }
+       return rc;
+}
+
+static int
+glue_back_attribute (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       Entry *target,
+       struct berval *ndn,
+       AttributeDescription *ad,
+       BerVarray *vals
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_attribute) {
+               rc = be->be_attribute (be, conn, op, target, ndn, ad, vals);
+       } else {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+       }
+       return rc;
+}
+
+static int
+glue_back_referrals (
+       BackendDB *b0,
+       Connection *conn,
+       Operation *op,
+       struct berval *dn,
+       struct berval *ndn,
+       const char **text
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, ndn->bv_val);
+
+       if (be && be->be_chk_referrals) {
+               rc = be->be_chk_referrals (be, conn, op, dn, ndn, text);
+       } else {
+               rc = LDAP_SUCCESS;;
+       }
+       return rc;
+}
+
+static int
+glue_tool_entry_open (
+       BackendDB *b0,
+       int mode
+)
+{
+       /* We don't know which backend to talk to yet, so just
+        * remember the mode and move on...
+        */
+
+       glueMode = mode;
+       glueBack = NULL;
+
+       return 0;
+}
+
+static int
+glue_tool_entry_close (
+       BackendDB *b0
+)
+{
+       int rc = 0;
+
+       if (glueBack) {
+               if (!glueBack->be_entry_close)
+                       return 0;
+               rc = glueBack->be_entry_close (glueBack);
+       }
+       return rc;
+}
+
+static ID
+glue_tool_entry_first (
+       BackendDB *b0
+)
+{
+       glueinfo *gi = (glueinfo *) b0->be_private;
+       int i;
+
+       /* If we're starting from scratch, start at the most general */
+       if (!glueBack) {
+               for (i = gi->nodes-1; i >= 0; i--) {
+                       if (gi->n[i].be->be_entry_open &&
+                           gi->n[i].be->be_entry_first) {
+                               glueBack = gi->n[i].be;
+                               break;
+                       }
+               }
+
+       }
+       if (!glueBack || glueBack->be_entry_open (glueBack, glueMode) != 0)
+               return NOID;
+
+       return glueBack->be_entry_first (glueBack);
+}
+
+static ID
+glue_tool_entry_next (
+       BackendDB *b0
+)
+{
+       glueinfo *gi = (glueinfo *) b0->be_private;
+       int i;
+       ID rc;
+
+       if (!glueBack || !glueBack->be_entry_next)
+               return NOID;
+
+       rc = glueBack->be_entry_next (glueBack);
+
+       /* If we ran out of entries in one database, move on to the next */
+       if (rc == NOID) {
+               glueBack->be_entry_close (glueBack);
+               for (i=0; i<gi->nodes; i++) {
+                       if (gi->n[i].be == glueBack)
+                               break;
+               }
+               if (i == 0) {
+                       glueBack = NULL;
+                       rc = NOID;
+               } else {
+                       glueBack = gi->n[i-1].be;
+                       rc = glue_tool_entry_first (b0);
+               }
+       }
+       return rc;
+}
+
+static Entry *
+glue_tool_entry_get (
+       BackendDB *b0,
+       ID id
+)
+{
+       if (!glueBack || !glueBack->be_entry_get)
+               return NULL;
+
+       return glueBack->be_entry_get (glueBack, id);
+}
+
+static ID
+glue_tool_entry_put (
+       BackendDB *b0,
+       Entry *e,
+       struct berval *text
+)
+{
+       BackendDB *be;
+       int rc;
+
+       be = glue_back_select (b0, e->e_ndn);
+       if (!be->be_entry_put)
+               return NOID;
+
+       if (!glueBack) {
+               rc = be->be_entry_open (be, glueMode);
+               if (rc != 0)
+                       return NOID;
+       } else if (be != glueBack) {
+               /* If this entry belongs in a different branch than the
+                * previous one, close the current database and open the
+                * new one.
+                */
+               glueBack->be_entry_close (glueBack);
+               rc = be->be_entry_open (be, glueMode);
+               if (rc != 0)
+                       return NOID;
+       }
+       glueBack = be;
+       return be->be_entry_put (be, e, text);
+}
+
+static int
+glue_tool_entry_reindex (
+       BackendDB *b0,
+       ID id
+)
+{
+       if (!glueBack || !glueBack->be_entry_reindex)
+               return -1;
+
+       return glueBack->be_entry_reindex (glueBack, id);
+}
+
+static int
+glue_tool_sync (
+       BackendDB *b0
+)
+{
+       glueinfo *gi = (glueinfo *) b0->be_private;
+       int i;
+
+       /* just sync everyone */
+       for (i = 0; i<gi->nodes; i++)
+               if (gi->n[i].be->be_sync)
+                       gi->n[i].be->be_sync (gi->n[i].be);
+       return 0;
+}
+
+int
+glue_sub_init( )
+{
+       int i, j;
+       int cont = num_subordinates;
+       BackendDB *b1, *be;
+       BackendInfo *bi;
+       glueinfo *gi;
+
+       /* While there are subordinate backends, search backwards through the
+        * backends and connect them to their superior.
+        */
+       for (i = nBackendDB - 1, b1=&backendDB[i]; cont && i>=0; b1--,i--) {
+               if (b1->be_flags & SLAP_BFLAG_GLUE_SUBORDINATE) {
+                       /* The last database cannot be a subordinate of noone */
+                       if (i == nBackendDB - 1) {
+                               b1->be_flags ^= SLAP_BFLAG_GLUE_SUBORDINATE;
+                       }
+                       continue;
+               }
+               gi = NULL;
+               for (j = i-1, be=&backendDB[j]; j>=0; be--,j--) {
+                       if (!(be->be_flags & SLAP_BFLAG_GLUE_SUBORDINATE)) {
+                               continue;
+                       }
+                       /* We will only link it once */
+                       if (be->be_flags & SLAP_BFLAG_GLUE_LINKED) {
+                               continue;
+                       }
+                       if (!dnIsSuffix(be->be_nsuffix[0], b1->be_nsuffix[0])) {
+                               continue;
+                       }
+                       cont--;
+                       be->be_flags |= SLAP_BFLAG_GLUE_LINKED;
+                       if (gi == NULL) {
+                               /* We create a copy of the superior's be
+                                * structure, pointing to all of its original
+                                * information. Then we replace elements of
+                                * the superior's info with our own. The copy
+                                * is used whenever we have operations to pass
+                                * down to the real database.
+                                */
+                               b1->be_flags |= SLAP_BFLAG_GLUE_INSTANCE;
+                               gi = (glueinfo *)ch_malloc(sizeof(glueinfo));
+                               gi->be = (BackendDB *)ch_malloc(
+                                       sizeof(BackendDB) + sizeof(BackendInfo));
+                               bi = (BackendInfo *)(gi->be+1);
+                               *gi->be = *b1;
+                               gi->nodes = 0;
+                               *bi = *b1->bd_info;
+                               bi->bi_open = glue_back_open;
+                               bi->bi_close = glue_back_close;
+                               bi->bi_db_open = glue_back_db_open;
+                               bi->bi_db_close = glue_back_db_close;
+                               bi->bi_db_destroy = glue_back_db_destroy;
+
+                               bi->bi_op_bind = glue_back_bind;
+                               bi->bi_op_search = glue_back_search;
+                               bi->bi_op_compare = glue_back_compare;
+                               bi->bi_op_modify = glue_back_modify;
+                               bi->bi_op_modrdn = glue_back_modrdn;
+                               bi->bi_op_add = glue_back_add;
+                               bi->bi_op_delete = glue_back_delete;
+
+                               bi->bi_entry_release_rw = glue_back_release_rw;
+                               bi->bi_acl_group = glue_back_group;
+                               bi->bi_acl_attribute = glue_back_attribute;
+                               bi->bi_chk_referrals = glue_back_referrals;
+
+                               /*
+                                * hooks for slap tools
+                                */
+                               bi->bi_tool_entry_open = glue_tool_entry_open;
+                               bi->bi_tool_entry_close = glue_tool_entry_close;
+                               bi->bi_tool_entry_first = glue_tool_entry_first;
+                               bi->bi_tool_entry_next = glue_tool_entry_next;
+                               bi->bi_tool_entry_get = glue_tool_entry_get;
+                               bi->bi_tool_entry_put = glue_tool_entry_put;
+                               bi->bi_tool_entry_reindex = glue_tool_entry_reindex;
+                               bi->bi_tool_sync = glue_tool_sync;
+                       } else {
+                               gi = (glueinfo *)ch_realloc(gi,
+                                       sizeof(glueinfo) +
+                                       gi->nodes * sizeof(gluenode));
+                       }
+                       gi->n[gi->nodes].be = be;
+                       dnParent( be->be_nsuffix[0], &gi->n[gi->nodes].pdn ); 
+                       gi->nodes++;
+               }
+               if (gi) {
+                       /* One more node for the master */
+                       gi = (glueinfo *)ch_realloc(gi,
+                               sizeof(glueinfo) + gi->nodes * sizeof(gluenode));
+                       gi->n[gi->nodes].be = gi->be;
+                       dnParent( b1->be_nsuffix[0], &gi->n[gi->nodes].pdn );
+                       gi->nodes++;
+                       b1->be_private = gi;
+                       b1->bd_info = bi;
+               }
+       }
+       /* If there are any unresolved subordinates left, something is wrong */
+       return cont;
+}
index d5ee966b570ad152344a50176022a9083e5173df..8094a09e8b841daf31edab5d3af15a1973fb224c 100644 (file)
@@ -1,37 +1,56 @@
 /* entry.c - routines for dealing with entries */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
 
 #include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include "slap.h"
 
-void   entry_free();
-char   *entry2str();
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "ldif.h"
 
-static unsigned char   *ebuf;  /* buf returned by entry2str             */
+static unsigned char   *ebuf;  /* buf returned by entry2str             */
 static unsigned char   *ecur;  /* pointer to end of currently used ebuf */
-static int             emaxsize;/* max size of ebuf                     */
+static int             emaxsize;/* max size of ebuf                     */
+
+/*
+ * Empty root entry
+ */
+const Entry slap_entry_root = { NOID, { 0, "" }, { 0, "" }, NULL, 0, { 0, "" }, NULL };
+
+int entry_destroy(void)
+{
+       if ( ebuf ) free( ebuf );
+       ebuf = NULL;
+       ecur = NULL;
+       emaxsize = 0;
+       return 0;
+}
+
 
 Entry *
-str2entry( char        *s )
+str2entry( char *s )
 {
-       int             i;
+       int rc;
        Entry           *e;
-       Attribute       **a;
        char            *type;
-       char            *value;
-       char            *next;
-       int             vlen, nvals, maxvals;
-       struct berval   bval;
-       struct berval   *vals[2];
-       char            ptype[64];
+       struct berval   vals[2];
+       AttributeDescription *ad;
+       const char *text;
+       char    *next;
 
        /*
-        * In string format, an entry looks like this:
+        * LDIF is used as the string format.
+        * An entry looks like this:
         *
-        *      <id>\n
         *      dn: <dn>\n
         *      [<attr>:[:] <value>\n]
         *      [<tab><continuedvalue>\n]*
@@ -43,134 +62,277 @@ str2entry( char   *s )
         * or newline.
         */
 
-       Debug( LDAP_DEBUG_TRACE, "=> str2entry\n", s, 0, 0 );
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+               "str2entry: \"%s\"\n", s ? s : "NULL" ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "=> str2entry\n",
+               s ? s : "NULL", 0, 0 );
+#endif
 
+       /* initialize reader/writer lock */
        e = (Entry *) ch_calloc( 1, sizeof(Entry) );
 
-       /* check to see if there's an id included */
-       next = s;
-       if ( isdigit( *s ) ) {
-               e->e_id = atoi( s );
-               if ( (s = str_getline( &next )) == NULL ) {
-                       Debug( LDAP_DEBUG_TRACE,
-                           "<= str2entry NULL (missing newline after id)\n",
-                           0, 0, 0 );
-                       return( NULL );
-               }
+       if( e == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                       "str2entry: entry allocation failed.\n" ));
+#else
+               Debug( LDAP_DEBUG_ANY,
+                   "<= str2entry NULL (entry allocation failed)\n",
+                   0, 0, 0 );
+#endif
+               return( NULL );
        }
 
+       /* initialize entry */
+       e->e_id = NOID;
+
        /* dn + attributes */
-       e->e_attrs = NULL;
-       vals[0] = &bval;
-       vals[1] = NULL;
-       ptype[0] = '\0';
-       while ( (s = str_getline( &next )) != NULL ) {
+       vals[1].bv_val = NULL;
+
+       next = s;
+       while ( (s = ldif_getline( &next )) != NULL ) {
                if ( *s == '\n' || *s == '\0' ) {
                        break;
                }
 
-               if ( str_parse_line( s, &type, &value, &vlen ) != 0 ) {
+               if ( ldif_parse_line( s, &type, &vals[0].bv_val, &vals[0].bv_len ) != 0 ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                                  "str2entry:  NULL (parse_line)\n" ));
+#else
                        Debug( LDAP_DEBUG_TRACE,
                            "<= str2entry NULL (parse_line)\n", 0, 0, 0 );
+#endif
                        continue;
                }
 
-               if ( strcasecmp( type, ptype ) != 0 ) {
-                       strncpy( ptype, type, sizeof(ptype) - 1 );
-                       nvals = 0;
-                       maxvals = 0;
-                       a = NULL;
-               }
                if ( strcasecmp( type, "dn" ) == 0 ) {
+                       struct berval *pdn = NULL;
+
+                       free( type );
+
                        if ( e->e_dn != NULL ) {
-                               Debug( LDAP_DEBUG_ANY,
-    "str2entry: entry %d has multiple dns \"%s\" and \"%s\" (second ignored)\n",
-                                   e->e_id, e->e_dn, value );
-                               continue;
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1, "str2entry: "
+                                       "entry %ld has multiple DNs \"%s\" and \"%s\"\n",
+                                       (long) e->e_id, e->e_dn,
+                                       vals[0].bv_val != NULL ? vals[0].bv_val : "" ));
+#else
+                               Debug( LDAP_DEBUG_ANY, "str2entry: "
+                                       "entry %ld has multiple DNs \"%s\" and \"%s\"\n",
+                                   (long) e->e_id, e->e_dn,
+                                       vals[0].bv_val != NULL ? vals[0].bv_val : "" );
+#endif
+                               if( vals[0].bv_val != NULL ) free( vals[0].bv_val );
+                               entry_free( e );
+                               return NULL;
+                       }
+
+                       rc = dnPrettyNormal( NULL, &vals[0], &e->e_name, &e->e_nname );
+                       free( vals[0].bv_val );
+                       if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1, "str2entry: "
+                                       "entry %ld has invalid DN \"%s\"\n",
+                                       (long) e->e_id,
+                                       pdn->bv_val ? pdn->bv_val : "" ));
+#else
+                               Debug( LDAP_DEBUG_ANY, "str2entry: "
+                                       "entry %ld has invalid DN \"%s\"\n",
+                                       (long) e->e_id,
+                                       pdn->bv_val ? pdn->bv_val : "", 0 );
+#endif
+                               entry_free( e );
+                               return NULL;
                        }
-                       e->e_dn = strdup( value );
                        continue;
                }
 
-               bval.bv_val = value;
-               bval.bv_len = vlen;
-               if ( attr_merge_fast( e, type, vals, nvals, 1, &maxvals, &a )
-                   != 0 ) {
-                       Debug( LDAP_DEBUG_TRACE,
+               ad = NULL;
+               rc = slap_str2ad( type, &ad, &text );
+
+               if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                               "str2entry:  str2ad(%s): %s\n", type, text ));
+#else
+                       Debug( slapMode & SLAP_TOOL_MODE
+                               ? LDAP_DEBUG_ANY : LDAP_DEBUG_TRACE,
+                               "<= str2entry: str2ad(%s): %s\n", type, text, 0 );
+#endif
+                       if( slapMode & SLAP_TOOL_MODE ) {
+                               entry_free( e );
+                               free( vals[0].bv_val );
+                               free( type );
+                               return NULL;
+                       }
+
+                       rc = slap_str2undef_ad( type, &ad, &text );
+                       if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                                       "str2entry:  str2undef_ad(%s): %s\n", type, text ));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "<= str2entry: str2undef_ad(%s): %s\n",
+                                               type, text, 0 );
+#endif
+                               entry_free( e );
+                               free( vals[0].bv_val );
+                               free( type );
+                               return NULL;
+                       }
+               }
+
+               if( slapMode & SLAP_TOOL_MODE ) {
+                       struct berval pval;
+                       slap_syntax_validate_func *validate =
+                               ad->ad_type->sat_syntax->ssyn_validate;
+                       slap_syntax_transform_func *pretty =
+                               ad->ad_type->sat_syntax->ssyn_pretty;
+
+                       if( pretty ) {
+                               rc = pretty( ad->ad_type->sat_syntax,
+                                       &vals[0], &pval );
+
+                       } else if( validate ) {
+                               /*
+                                * validate value per syntax
+                                */
+                               rc = validate( ad->ad_type->sat_syntax, &vals[0] );
+
+                       } else {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+                                       "str2entry: no validator for syntax %s\n", 
+                                       ad->ad_type->sat_syntax->ssyn_oid ));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "str2entry: no validator for syntax %s\n",
+                                       ad->ad_type->sat_syntax->ssyn_oid, 0, 0 );
+#endif
+                               entry_free( e );
+                               free( vals[0].bv_val );
+                               free( type );
+                               return NULL;
+                       }
+
+                       if( rc != 0 ) {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                                       "str2entry:  invalid value for syntax %s\n",
+                                       ad->ad_type->sat_syntax->ssyn_oid ));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "str2entry: invalid value for syntax %s\n",
+                                       ad->ad_type->sat_syntax->ssyn_oid, 0, 0 );
+#endif
+                               entry_free( e );
+                               free( vals[0].bv_val );
+                               free( type );
+                               return NULL;
+                       }
+
+                       if( pretty ) {
+                               free( vals[0].bv_val );
+                               vals[0] = pval;
+                       }
+               }
+
+               rc = attr_merge( e, ad, vals );
+               if( rc != 0 ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                               "str2entry:  NULL (attr_merge)\n" ));
+#else
+                       Debug( LDAP_DEBUG_ANY,
                            "<= str2entry NULL (attr_merge)\n", 0, 0, 0 );
+#endif
+                       entry_free( e );
+                       free( vals[0].bv_val );
+                       free( type );
                        return( NULL );
                }
-               nvals++;
+
+               free( type );
+               free( vals[0].bv_val );
        }
 
        /* check to make sure there was a dn: line */
        if ( e->e_dn == NULL ) {
-               Debug( LDAP_DEBUG_ANY, "str2entry: entry %d has no dn\n",
-                   e->e_id, 0, 0 );
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+                       "str2entry:  entry %ld has no dn.\n",
+                       (long) e->e_id ));
+#else
+               Debug( LDAP_DEBUG_ANY, "str2entry: entry %ld has no dn\n",
+                   (long) e->e_id, 0, 0 );
+#endif
                entry_free( e );
                return( NULL );
        }
 
-       Debug( LDAP_DEBUG_TRACE, "<= str2entry 0x%x\n", e, 0, 0 );
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL2,
+               "str2entry(%s) -> 0x%lx\n", e->e_dn, (unsigned long)e ));
+#else
+       Debug(LDAP_DEBUG_TRACE, "<= str2entry(%s) -> 0x%lx\n",
+               e->e_dn, (unsigned long) e, 0 );
+#endif
        return( e );
 }
 
+
 #define GRABSIZE       BUFSIZ
 
 #define MAKE_SPACE( n )        { \
                while ( ecur + (n) > ebuf + emaxsize ) { \
-                       int     offset; \
+                       ptrdiff_t       offset; \
                        offset = (int) (ecur - ebuf); \
                        ebuf = (unsigned char *) ch_realloc( (char *) ebuf, \
                            emaxsize + GRABSIZE ); \
                        emaxsize += GRABSIZE; \
                        ecur = ebuf + offset; \
                } \
-}
+       }
 
 char *
 entry2str(
     Entry      *e,
-    int                *len,
-    int                printid
-)
+    int                *len )
 {
        Attribute       *a;
        struct berval   *bv;
-       int             i, tmplen;
+       int             i;
+       ber_len_t tmplen;
 
        /*
         * In string format, an entry looks like this:
-        *      <id>\n
         *      dn: <dn>\n
         *      [<attr>: <value>\n]*
         */
 
        ecur = ebuf;
 
-       if ( printid ) {
-               /* id + newline */
-               MAKE_SPACE( 10 );
-               sprintf( (char *) ecur, "%ld\n", e->e_id );
-               ecur = (unsigned char *) strchr( (char *) ecur, '\0' );
-       }
-
        /* put the dn */
        if ( e->e_dn != NULL ) {
                /* put "dn: <dn>" */
-               tmplen = strlen( e->e_dn );
+               tmplen = e->e_name.bv_len;
                MAKE_SPACE( LDIF_SIZE_NEEDED( 2, tmplen ));
-               put_type_and_value( (char **) &ecur, "dn", e->e_dn, tmplen );
+               ldif_sput( (char **) &ecur, LDIF_PUT_VALUE, "dn", e->e_dn, tmplen );
        }
 
        /* put the attributes */
        for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
                /* put "<type>:[:] <value>" line for each value */
-               for ( i = 0; a->a_vals[i] != NULL; i++ ) {
-                       bv = a->a_vals[i];
-                       tmplen = strlen( a->a_type );
+               for ( i = 0; a->a_vals[i].bv_val != NULL; i++ ) {
+                       bv = &a->a_vals[i];
+                       tmplen = a->a_desc->ad_cname.bv_len;
                        MAKE_SPACE( LDIF_SIZE_NEEDED( tmplen, bv->bv_len ));
-                       put_type_and_value( (char **) &ecur, a->a_type,
+                       ldif_sput( (char **) &ecur, LDIF_PUT_VALUE,
+                               a->a_desc->ad_cname.bv_val,
                            bv->bv_val, bv->bv_len );
                }
        }
@@ -184,15 +346,311 @@ entry2str(
 void
 entry_free( Entry *e )
 {
-       int             i;
-       Attribute       *a, *next;
+       /* free an entry structure */
+       assert( e != NULL );
+
+       /* e_private must be freed by the caller */
+       assert( e->e_private == NULL );
+       e->e_private = NULL;
 
+       /* free DNs */
        if ( e->e_dn != NULL ) {
                free( e->e_dn );
+               e->e_dn = NULL;
        }
-       for ( a = e->e_attrs; a != NULL; a = next ) {
-               next = a->a_next;
-               attr_free( a );
+       if ( e->e_ndn != NULL ) {
+               free( e->e_ndn );
+               e->e_ndn = NULL;
        }
+
+       if ( e->e_bv.bv_val != NULL ) {
+               free( e->e_bv.bv_val );
+               e->e_bv.bv_val = NULL;
+       }
+
+       /* free attributes */
+       attrs_free( e->e_attrs );
+       e->e_attrs = NULL;
+
        free( e );
 }
+
+/*
+ * These routines are used only by Backend.
+ *
+ * the Entry has three entry points (ways to find things):
+ *
+ *     by entry        e.g., if you already have an entry from the cache
+ *                     and want to delete it. (really by entry ptr)
+ *     by dn           e.g., when looking for the base object of a search
+ *     by id           e.g., for search candidates
+ *
+ * these correspond to three different avl trees that are maintained.
+ */
+
+int
+entry_cmp( Entry *e1, Entry *e2 )
+{
+       return( e1 < e2 ? -1 : (e1 > e2 ? 1 : 0) );
+}
+
+int
+entry_dn_cmp( Entry *e1, Entry *e2 )
+{
+       /* compare their normalized UPPERCASED dn's */
+       int rc = e1->e_nname.bv_len - e2->e_nname.bv_len;
+       if (rc) return rc;
+       return( strcmp( e1->e_ndn, e2->e_ndn ) );
+}
+
+int
+entry_id_cmp( Entry *e1, Entry *e2 )
+{
+       return( e1->e_id < e2->e_id ? -1 : (e1->e_id > e2->e_id ? 1 : 0) );
+}
+
+#ifdef SLAPD_BDB
+
+/* This is like a ber_len */
+static ber_len_t
+entry_lenlen(ber_len_t len)
+{
+       if (len <= 0x7f)
+               return 1;
+       if (len <= 0xff)
+               return 2;
+       if (len <= 0xffff)
+               return 3;
+       if (len <= 0xffffff)
+               return 4;
+       return 5;
+}
+
+static void
+entry_putlen(unsigned char **buf, ber_len_t len)
+{
+       ber_len_t lenlen = entry_lenlen(len);
+
+       if (lenlen == 1) {
+               **buf = (unsigned char) len;
+       } else {
+               int i;
+               **buf = 0x80 | ((unsigned char) lenlen - 1);
+               for (i=lenlen-1; i>0; i--) {
+                       (*buf)[i] = (unsigned char) len;
+                       len >>= 8;
+               }
+       }
+       *buf += lenlen;
+}
+
+static ber_len_t
+entry_getlen(unsigned char **buf)
+{
+       ber_len_t len;
+       int i;
+
+       len = *(*buf)++;
+       if (len <= 0x7f)
+               return len;
+       i = len & 0x7f;
+       len = 0;
+       for (;i > 0; i--) {
+               len <<= 8;
+               len |= *(*buf)++;
+       }
+       return len;
+}
+
+/* Flatten an Entry into a buffer. The buffer is filled with just the
+ * strings/bervals of all the entry components. Each field is preceded
+ * by its length, encoded the way ber_put_len works. Every field is NUL
+ * terminated.  The entire buffer size is precomputed so that a single
+ * malloc can be performed. The entry size is also recorded,
+ * to aid in entry_decode.
+ */
+int entry_encode(Entry *e, struct berval *bv)
+{
+       ber_len_t siz = sizeof(Entry);
+       ber_len_t len, dnlen, ndnlen;
+       int i;
+       Attribute *a;
+       unsigned char *ptr;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+               "entry_encode: id: 0x%08lx  \"%s\"\n",
+               (long) e->e_id, e->e_dn ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "=> entry_encode(0x%08lx): %s\n",
+               (long) e->e_id, e->e_dn, 0 );
+#endif
+       dnlen = e->e_name.bv_len;
+       ndnlen = e->e_nname.bv_len;
+       len = dnlen + ndnlen + 2;       /* two trailing NUL bytes */
+       len += entry_lenlen(dnlen);
+       len += entry_lenlen(ndnlen);
+       for (a=e->e_attrs; a; a=a->a_next) {
+               /* For AttributeDesc, we only store the attr name */
+               siz += sizeof(Attribute);
+               len += a->a_desc->ad_cname.bv_len+1;
+               len += entry_lenlen(a->a_desc->ad_cname.bv_len);
+               for (i=0; a->a_vals[i].bv_val; i++) {
+                       siz += sizeof(struct berval);
+                       len += a->a_vals[i].bv_len + 1;
+                       len += entry_lenlen(a->a_vals[i].bv_len);
+               }
+               len += entry_lenlen(i);
+               siz += sizeof(struct berval);   /* empty berval at end */
+       }
+       len += 1;       /* NUL byte at end */
+       len += entry_lenlen(siz);
+       bv->bv_len = len;
+       bv->bv_val = ch_malloc(len);
+       ptr = (unsigned char *)bv->bv_val;
+       entry_putlen(&ptr, siz);
+       entry_putlen(&ptr, dnlen);
+       AC_MEMCPY(ptr, e->e_dn, dnlen);
+       ptr += dnlen;
+       *ptr++ = '\0';
+       entry_putlen(&ptr, ndnlen);
+       AC_MEMCPY(ptr, e->e_ndn, ndnlen);
+       ptr += ndnlen;
+       *ptr++ = '\0';
+
+       for (a=e->e_attrs; a; a=a->a_next) {
+               entry_putlen(&ptr, a->a_desc->ad_cname.bv_len);
+               AC_MEMCPY(ptr, a->a_desc->ad_cname.bv_val,
+                       a->a_desc->ad_cname.bv_len);
+               ptr += a->a_desc->ad_cname.bv_len;
+               *ptr++ = '\0';
+               if (a->a_vals) {
+                   for (i=0; a->a_vals[i].bv_val; i++);
+                   entry_putlen(&ptr, i);
+                   for (i=0; a->a_vals[i].bv_val; i++) {
+                       entry_putlen(&ptr, a->a_vals[i].bv_len);
+                       memcpy(ptr, a->a_vals[i].bv_val,
+                               a->a_vals[i].bv_len);
+                       ptr += a->a_vals[i].bv_len;
+                       *ptr++ = '\0';
+                   }
+               }
+       }
+       *ptr = '\0';
+       return 0;
+}
+
+/* Retrieve an Entry that was stored using entry_encode above.
+ * We malloc a single block with the size stored above for the Entry
+ * and all if its Attributes. We also must lookup the stored
+ * attribute names to get AttributeDescriptions. To detect if the
+ * attributes of an Entry are later modified, we note that e->e_attr
+ * is always a constant offset from (e).
+ *
+ * Note: everything is stored in a single contiguous block, so
+ * you can not free individual attributes or names from this
+ * structure. Attempting to do so will likely corrupt memory.
+ */
+int entry_decode(struct berval *bv, Entry **e)
+{
+       int i, j;
+       int rc;
+       Attribute *a;
+       Entry *x;
+       const char *text;
+       AttributeDescription *ad;
+       unsigned char *ptr = (unsigned char *)bv->bv_val;
+       BerVarray bptr;
+
+       i = entry_getlen(&ptr);
+       x = ch_calloc(1, i);
+       i = entry_getlen(&ptr);
+       x->e_name.bv_val = ptr;
+       x->e_name.bv_len = i;
+       ptr += i+1;
+       i = entry_getlen(&ptr);
+       x->e_nname.bv_val = ptr;
+       x->e_nname.bv_len = i;
+       ptr += i+1;
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL2,
+               "entry_decode: \"%s\"\n", x->e_dn ));
+#else
+       Debug( LDAP_DEBUG_TRACE,
+           "entry_decode: \"%s\"\n",
+           x->e_dn, 0, 0 );
+#endif
+       x->e_bv = *bv;
+
+       /* A valid entry must have at least one attr, so this
+        * pointer can never be NULL
+        */
+       x->e_attrs = (Attribute *)(x+1);
+       bptr = (BerVarray)x->e_attrs;
+       a = NULL;
+
+       while (i = entry_getlen(&ptr)) {
+               struct berval bv;
+               bv.bv_len = i;
+               bv.bv_val = ptr;
+               if (a) {
+                       a->a_next = (Attribute *)bptr;
+               }
+               a = (Attribute *)bptr;
+               ad = NULL;
+               rc = slap_bv2ad( &bv, &ad, &text );
+
+               if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+                               "entry_decode: str2ad(%s): %s\n", ptr, text ));
+#else
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<= entry_decode: str2ad(%s): %s\n", ptr, text, 0 );
+#endif
+                       rc = slap_bv2undef_ad( &bv, &ad, &text );
+
+                       if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+                                       "entry_decode:  str2undef_ad(%s): %s\n", ptr, text));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "<= entry_decode: str2undef_ad(%s): %s\n",
+                                               ptr, text, 0 );
+#endif
+                               return rc;
+                       }
+               }
+               ptr += i + 1;
+               a->a_desc = ad;
+               bptr = (BerVarray)(a+1);
+               a->a_vals = bptr;
+               a->a_flags = 0;
+               j = entry_getlen(&ptr);
+
+               while (j) {
+                       i = entry_getlen(&ptr);
+                       bptr->bv_len = i;
+                       bptr->bv_val = (char *)ptr;
+                       ptr += i+1;
+                       bptr++;
+                       j--;
+               }
+               bptr->bv_val = NULL;
+               bptr->bv_len = 0;
+               bptr++;
+       }
+       if (a)
+               a->a_next = NULL;
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+               "entry_decode:  %s\n", x->e_dn ));
+#else
+       Debug(LDAP_DEBUG_TRACE, "<= entry_decode(%s)\n",
+               x->e_dn, 0, 0 );
+#endif
+       *e = x;
+       return 0;
+}
+#endif
diff --git a/servers/slapd/sets.c b/servers/slapd/sets.c
new file mode 100644 (file)
index 0000000..9d01b9c
--- /dev/null
@@ -0,0 +1,401 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 2000-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "sets.h"
+
+static BerVarray set_join (BerVarray lset, int op, BerVarray rset);
+static BerVarray set_chase (SLAP_SET_GATHER gatherer,
+       void *cookie, BerVarray set, struct berval *attr, int closure);
+static int set_samedn (char *dn1, char *dn2);
+
+long
+slap_set_size (BerVarray set)
+{
+       int i;
+
+       i = 0;
+       if (set != NULL) {
+               while (set[i].bv_val)
+                       i++;
+       }
+       return(i);
+}
+
+void
+slap_set_dispose (BerVarray set)
+{
+       ber_bvarray_free(set);
+}
+
+static BerVarray
+set_join (BerVarray lset, int op, BerVarray rset)
+{
+       BerVarray set;
+       long i, j, last;
+
+       set = NULL;
+       if (op == '|') {
+               if (lset == NULL || lset->bv_val == NULL) {
+                       if (rset == NULL) {
+                               if (lset == NULL)
+                                       return(ch_calloc(1, sizeof(struct berval)));
+                               return(lset);
+                       }
+                       slap_set_dispose(lset);
+                       return(rset);
+               }
+               if (rset == NULL || rset->bv_val == NULL) {
+                       slap_set_dispose(rset);
+                       return(lset);
+               }
+
+               i = slap_set_size(lset) + slap_set_size(rset) + 1;
+               set = ch_calloc(i, sizeof(struct berval));
+               if (set != NULL) {
+                       /* set_chase() depends on this routine to
+                        * keep the first elements of the result
+                        * set the same (and in the same order)
+                        * as the left-set.
+                        */
+                       for (i = 0; lset[i].bv_val; i++)
+                               set[i] = lset[i];
+                       ch_free(lset);
+                       for (i = 0; rset[i].bv_val; i++) {
+                               for (j = 0; set[j].bv_val; j++) {
+                                       if (set_samedn(rset[i].bv_val, set[j].bv_val)) {
+                                               ch_free(rset[i].bv_val);
+                                               rset[i].bv_val = NULL;
+                                               break;          
+                                       }       
+                               }
+                               if (rset[i].bv_val)
+                                       set[j] = rset[i];
+                       }
+                       ch_free(rset);
+               }
+               return(set);
+       }
+
+       if (op == '&') {
+               if (lset == NULL || lset->bv_val == NULL || rset == NULL || rset->bv_val == NULL) {
+                       set = ch_calloc(1, sizeof(struct berval));
+               } else {
+                       set = lset;
+                       lset = NULL;
+                       last = slap_set_size(set) - 1;
+                       for (i = 0; set[i].bv_val; i++) {
+                               for (j = 0; rset[j].bv_val; j++) {
+                                       if (set_samedn(set[i].bv_val, rset[j].bv_val))
+                                               break;
+                               }
+                               if (rset[j].bv_val == NULL) {
+                                       ch_free(set[i].bv_val);
+                                       set[i] = set[last];
+                                       set[last].bv_val = NULL;
+                                       last--;
+                                       i--;
+                               }
+                       }
+               }
+       }
+
+       slap_set_dispose(lset);
+       slap_set_dispose(rset);
+       return(set);
+}
+
+static BerVarray
+set_chase (SLAP_SET_GATHER gatherer,
+       void *cookie, BerVarray set, struct berval *attr, int closure)
+{
+       BerVarray vals, nset;
+       char attrstr[32];
+       struct berval bv;
+       int i;
+
+       bv.bv_len = attr->bv_len;
+       bv.bv_val = attrstr;
+
+       if (set == NULL)
+               return(ch_calloc(1, sizeof(struct berval)));
+
+       if (set->bv_val == NULL)
+               return(set);
+
+       if (attr->bv_len > (sizeof(attrstr) - 1)) {
+               slap_set_dispose(set);
+               return(NULL);
+       }
+       AC_MEMCPY(attrstr, attr->bv_val, attr->bv_len);
+       attrstr[attr->bv_len] = 0;
+
+       nset = ch_calloc(1, sizeof(struct berval));
+       if (nset == NULL) {
+               slap_set_dispose(set);
+               return(NULL);
+       }
+       for (i = 0; set[i].bv_val; i++) {
+               vals = (gatherer)(cookie, &set[i], &bv);
+               if (vals != NULL)
+                       nset = set_join(nset, '|', vals);
+       }
+       slap_set_dispose(set);
+
+       if (closure) {
+               for (i = 0; nset[i].bv_val; i++) {
+                       vals = (gatherer)(cookie, &nset[i], &bv);
+                       if (vals != NULL) {
+                               nset = set_join(nset, '|', vals);
+                               if (nset == NULL)
+                                       break;
+                       }
+               }
+       }
+       return(nset);
+}
+
+static int
+set_samedn (char *dn1, char *dn2)
+{
+       char c1, c2;
+
+       while (*dn1 == ' ') dn1++;
+       while (*dn2 == ' ') dn2++;
+       while (*dn1 || *dn2) {
+               if (*dn1 != '=' && *dn1 != ','
+                       && *dn2 != '=' && *dn2 != ',')
+               {
+                       c1 = *dn1++;
+                       c2 = *dn2++;
+                       if (c1 >= 'a' && c1 <= 'z')
+                               c1 -= 'a' - 'A';
+                       if (c2 >= 'a' && c2 <= 'z')
+                               c2 -= 'a' - 'A';
+                       if (c1 != c2)
+                               return(0);
+               } else {
+                       while (*dn1 == ' ') dn1++;
+                       while (*dn2 == ' ') dn2++;
+                       if (*dn1++ != *dn2++)
+                               return(0);
+                       while (*dn1 == ' ') dn1++;
+                       while (*dn2 == ' ') dn2++;
+               }
+       }
+       return(1);
+}
+
+int
+slap_set_filter (SLAP_SET_GATHER gatherer,
+       void *cookie, struct berval *fbv,
+       struct berval *user, struct berval *this, BerVarray *results)
+{
+#define IS_SET(x)      ( (long)(x) >= 256 )
+#define IS_OP(x)       ( (long)(x) < 256 )
+#define SF_ERROR(x)    do { rc = -1; goto _error; } while (0)
+#define SF_TOP()       ( (BerVarray)( (stp < 0) ? 0 : stack[stp] ) )
+#define SF_POP()       ( (BerVarray)( (stp < 0) ? 0 : stack[stp--] ) )
+#define SF_PUSH(x)     do { \
+               if (stp >= 63) SF_ERROR(overflow); \
+               stack[++stp] = (BerVarray)(long)(x); \
+       } while (0)
+
+       BerVarray set, lset;
+       BerVarray stack[64];
+       int len, op, rc, stp;
+       char c, *filter = fbv->bv_val;
+
+       if (results)
+               *results = NULL;
+
+       stp = -1;
+       while ((c = *filter++)) {
+               set = NULL;
+               switch (c) {
+               case ' ':
+               case '\t':
+               case '\x0A':
+               case '\x0D':
+                       break;
+
+               case '(':
+                       if (IS_SET(SF_TOP()))
+                               SF_ERROR(syntax);
+                       SF_PUSH(c);
+                       break;
+
+               case ')':
+                       set = SF_POP();
+                       if (IS_OP(set))
+                               SF_ERROR(syntax);
+                       if (SF_TOP() == (void *)'(') {
+                               SF_POP();
+                               SF_PUSH(set);
+                               set = NULL;
+                       } else if (IS_OP(SF_TOP())) {
+                               op = (long)SF_POP();
+                               lset = SF_POP();
+                               SF_POP();
+                               set = set_join(lset, op, set);
+                               if (set == NULL)
+                                       SF_ERROR(memory);
+                               SF_PUSH(set);
+                               set = NULL;
+                       } else {
+                               SF_ERROR(syntax);
+                       }
+                       break;
+
+               case '&':
+               case '|':
+                       set = SF_POP();
+                       if (IS_OP(set))
+                               SF_ERROR(syntax);
+                       if (SF_TOP() == 0 || SF_TOP() == (void *)'(') {
+                               SF_PUSH(set);
+                               set = NULL;
+                       } else if (IS_OP(SF_TOP())) {
+                               op = (long)SF_POP();
+                               lset = SF_POP();
+                               set = set_join(lset, op, set);
+                               if (set == NULL)
+                                       SF_ERROR(memory);
+                               SF_PUSH(set);
+                               set = NULL;
+                       } else {
+                               SF_ERROR(syntax);
+                       }
+                       SF_PUSH(c);
+                       break;
+
+               case '[':
+                       if ((SF_TOP() == (void *)'/') || IS_SET(SF_TOP()))
+                               SF_ERROR(syntax);
+                       for (   len = 0;
+                                       (c = *filter++) && (c != ']');
+                                       len++)
+                       { }
+                       if (c == 0)
+                               SF_ERROR(syntax);
+                       
+                       set = ch_calloc(2, sizeof(struct berval));
+                       if (set == NULL)
+                               SF_ERROR(memory);
+                       set->bv_val = ch_calloc(len + 1, sizeof(char));
+                       if (set->bv_val == NULL)
+                               SF_ERROR(memory);
+                       AC_MEMCPY(set->bv_val, &filter[-len - 1], len);
+                       set->bv_len = len;
+                       SF_PUSH(set);
+                       set = NULL;
+                       break;
+
+               case '-':
+                       c = *filter++;
+                       if (c != '>')
+                               SF_ERROR(syntax);
+                       /* fall through to next case */
+
+               case '/':
+                       if (IS_OP(SF_TOP()))
+                               SF_ERROR(syntax);
+                       SF_PUSH('/');
+                       break;
+
+               default:
+                       if ((c != '_')
+                               && (c < 'A' || c > 'Z')
+                               && (c < 'a' || c > 'z'))
+                       {
+                               SF_ERROR(syntax);
+                       }
+                       filter--;
+                       for (   len = 1;
+                                       (c = filter[len])
+                                               && ((c >= '0' && c <= '9')
+                                                       || (c >= 'A' && c <= 'Z')
+                                                       || (c >= 'a' && c <= 'z'));
+                                       len++)
+                       { }
+                       if (len == 4
+                               && memcmp("this", filter, len) == 0)
+                       {
+                               if ((SF_TOP() == (void *)'/') || IS_SET(SF_TOP()))
+                                       SF_ERROR(syntax);
+                               set = ch_calloc(2, sizeof(struct berval));
+                               if (set == NULL)
+                                       SF_ERROR(memory);
+                               ber_dupbv( set, this );
+                               if (set->bv_val == NULL)
+                                       SF_ERROR(memory);
+                       } else if (len == 4
+                               && memcmp("user", filter, len) == 0) 
+                       {
+                               if ((SF_TOP() == (void *)'/') || IS_SET(SF_TOP()))
+                                       SF_ERROR(syntax);
+                               set = ch_calloc(2, sizeof(struct berval));
+                               if (set == NULL)
+                                       SF_ERROR(memory);
+                               ber_dupbv( set, user );
+                               if (set->bv_val == NULL)
+                                       SF_ERROR(memory);
+                       } else if (SF_TOP() != (void *)'/') {
+                               SF_ERROR(syntax);
+                       } else {
+                               struct berval fb2;
+                               SF_POP();
+                               fb2.bv_val = filter;
+                               fb2.bv_len = len;
+                               set = set_chase(gatherer,
+                                       cookie, SF_POP(), &fb2, c == '*');
+                               if (set == NULL)
+                                       SF_ERROR(memory);
+                               if (c == '*')
+                                       len++;
+                       }
+                       filter += len;
+                       SF_PUSH(set);
+                       set = NULL;
+                       break;
+               }
+       }
+
+       set = SF_POP();
+       if (IS_OP(set))
+               SF_ERROR(syntax);
+       if (SF_TOP() == 0) {
+
+       } else if (IS_OP(SF_TOP())) {
+               op = (long)SF_POP();
+               lset = SF_POP();
+               set = set_join(lset, op, set);
+               if (set == NULL)
+                       SF_ERROR(memory);
+       } else {
+               SF_ERROR(syntax);
+       }
+
+       rc = slap_set_size(set);
+       if (results) {
+               *results = set;
+               set = NULL;
+       }
+
+_error:
+       if (IS_SET(set))
+               slap_set_dispose(set);
+       while ((set = SF_POP())) {
+               if (IS_SET(set))
+                       slap_set_dispose(set);
+       }
+       return(rc);
+}
diff --git a/servers/slapd/tools/slapadd.c b/servers/slapd/tools/slapadd.c
new file mode 100644 (file)
index 0000000..d3ce99b
--- /dev/null
@@ -0,0 +1,185 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lber.h>
+#include <ldif.h>
+
+#include "slapcommon.h"
+
+int
+main( int argc, char **argv )
+{
+       char            *buf = NULL;
+       int         lineno;
+       int         lmax;
+       int                     rc = EXIT_SUCCESS;
+
+       const char *text;
+       char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
+       size_t textlen = sizeof textbuf;
+
+       slap_tool_init( "slapadd", SLAPADD, argc, argv );
+
+       if( !be->be_entry_open ||
+               !be->be_entry_close ||
+               !be->be_entry_put )
+       {
+               fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+                       progname );
+               exit( EXIT_FAILURE );
+       }
+
+       lmax = 0;
+       lineno = 0;
+
+       if( be->be_entry_open( be, 1 ) != 0 ) {
+               fprintf( stderr, "%s: could not open database.\n",
+                       progname );
+               exit( EXIT_FAILURE );
+       }
+
+       while( ldif_read_record( ldiffp, &lineno, &buf, &lmax ) ) {
+               Entry *e = str2entry( buf );
+               struct berval bvtext;
+
+               bvtext.bv_len = textlen;
+               bvtext.bv_val = textbuf;
+
+               if( e == NULL ) {
+                       fprintf( stderr, "%s: could not parse entry (line=%d)\n",
+                               progname, lineno );
+                       rc = EXIT_FAILURE;
+                       if( continuemode ) continue;
+                       break;
+               }
+
+               /* make sure the DN is not empty */
+               if( !e->e_nname.bv_len ) {
+                       fprintf( stderr, "%s: empty dn=\"%s\" (line=%d)\n",
+                               progname, e->e_dn, lineno );
+                       rc = EXIT_FAILURE;
+                       entry_free( e );
+                       if( continuemode ) continue;
+                       break;
+               }
+
+               /* check backend */
+               if( select_backend( &e->e_nname, is_entry_referral(e), nosubordinates )
+                       != be )
+               {
+                       fprintf( stderr, "%s: line %d: "
+                               "database (%s) not configured to hold \"%s\"\n",
+                               progname, lineno,
+                               be ? be->be_suffix[0]->bv_val : "<none>",
+                               e->e_dn );
+                       fprintf( stderr, "%s: line %d: "
+                               "database (%s) not configured to hold \"%s\"\n",
+                               progname, lineno,
+                               be ? be->be_nsuffix[0]->bv_val : "<none>",
+                               e->e_ndn );
+                       rc = EXIT_FAILURE;
+                       entry_free( e );
+                       if( continuemode ) continue;
+                       break;
+               }
+
+               {
+                       Attribute *sc = attr_find( e->e_attrs,
+                               slap_schema.si_ad_structuralObjectClass );
+                       Attribute *oc = attr_find( e->e_attrs,
+                               slap_schema.si_ad_objectClass );
+
+                       if( oc == NULL ) {
+                               fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+                                       progname, e->e_dn, lineno,
+                                       "no objectClass attribute");
+                               rc = EXIT_FAILURE;
+                               entry_free( e );
+                               if( continuemode ) continue;
+                               break;
+                       }
+
+                       if( sc == NULL ) {
+                               struct berval vals[2];
+
+                               int ret = structural_class( oc->a_vals, vals,
+                                       NULL, &text, textbuf, textlen );
+
+                               if( vals[0].bv_len == 0 ) {
+                                       fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+                                       progname, e->e_dn, lineno, text );
+                                       rc = EXIT_FAILURE;
+                                       entry_free( e );
+                                       if( continuemode ) continue;
+                                       break;
+                               }
+
+                               vals[1].bv_val = NULL;
+                               attr_merge( e, slap_schema.si_ad_structuralObjectClass,
+                                       vals );
+                       }
+               }
+
+               if( global_schemacheck ) {
+                       /* check schema */
+
+                       rc = entry_schema_check( be, e, NULL, &text, textbuf, textlen );
+
+                       if( rc != LDAP_SUCCESS ) {
+                               fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+                                       progname, e->e_dn, lineno, text );
+                               rc = EXIT_FAILURE;
+                               entry_free( e );
+                               if( continuemode ) continue;
+                               break;
+                       }
+               }
+
+               if (!dryrun) {
+                       ID id = be->be_entry_put( be, e, &bvtext );
+                       if( id == NOID ) {
+                               fprintf( stderr, "%s: could not add entry dn=\"%s\" (line=%d): %s\n",
+                                       progname, e->e_dn, lineno, bvtext.bv_val );
+                               rc = EXIT_FAILURE;
+                               entry_free( e );
+                               if( continuemode ) continue;
+                               break;
+                       }
+               
+                       if ( verbose ) {
+                               fprintf( stderr, "added: \"%s\" (%08lx)\n",
+                                       e->e_dn, (long) id );
+                       }
+               } else {
+                       if ( verbose ) {
+                               fprintf( stderr, "(dry) added: \"%s\"\n", e->e_dn );
+                       }
+               }
+
+               entry_free( e );
+       }
+
+       ch_free( buf );
+
+       be->be_entry_close( be );
+
+       if( be->be_sync ) {
+               be->be_sync( be );
+       }
+
+       slap_tool_destroy();
+       return rc;
+}