]> git.sur5r.net Git - openldap/commitdiff
More clean experimental removal/#ifdef'ing
authorKurt Zeilenga <kurt@openldap.org>
Mon, 18 Feb 2002 20:40:38 +0000 (20:40 +0000)
committerKurt Zeilenga <kurt@openldap.org>
Mon, 18 Feb 2002 20:40:38 +0000 (20:40 +0000)
servers/slapd/modify.c
servers/slapd/schema_check.c [new file with mode: 0644]
servers/slapd/schema_prep.c
servers/slapd/slap.h

index 6575fe36adbd7a434c772d6ab328acd9fc51ad77..dc5ff605d7ac1736c2208de36baaafb45e1edca6 100644 (file)
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1995 Regents of the University of Michigan.
  * All rights reserved.
  * is provided ``as is'' without express or implied warranty.
  */
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include "slap.h"
 
-extern Backend *select_backend();
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
 
-extern char            *default_referral;
-extern time_t          currenttime;
-extern pthread_mutex_t currenttime_mutex;
-extern int             global_lastmod;
+#include "lutil.h"
 
-static void    modlist_free();
-static void    add_lastmods();
+#include "ldap_pvt.h"
+#include "slap.h"
 
-void
+int
 do_modify(
     Connection *conn,
-    Operation  *op
-)
+    Operation  *op )
 {
-       char            *dn, *odn;
+       struct berval dn = { 0, NULL };
+       struct berval pdn = { 0, NULL };
+       struct berval ndn = { 0, NULL };
        char            *last;
-       unsigned long   tag, len;
-       LDAPMod         *mods, *tmp;
-       LDAPMod         **modtail;
+       ber_tag_t       tag;
+       ber_len_t       len;
+       Modifications   *modlist = NULL;
+       Modifications   **modtail = &modlist;
+#ifdef LDAP_DEBUG
+       Modifications *tmp;
+#endif
        Backend         *be;
+       int rc;
+       const char      *text;
+       int manageDSAit;
 
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_ENTRY,
+               "do_modify: enter\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
+#endif
 
        /*
         * Parse the modify request.  It looks like this:
@@ -62,198 +75,615 @@ do_modify(
         *      }
         */
 
-       if ( ber_scanf( op->o_ber, "{a", &dn ) == LBER_ERROR ) {
-               Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
-               send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL, "" );
-               return;
+       if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                       "do_modify: ber_scanf failed\n" ));
+#else
+               Debug( LDAP_DEBUG_ANY, "do_modify: ber_scanf failed\n", 0, 0, 0 );
+#endif
+
+               send_ldap_disconnect( conn, op,
+                       LDAP_PROTOCOL_ERROR, "decoding error" );
+               return SLAPD_DISCONNECT;
        }
-       odn = strdup( dn );
-       dn_normalize( dn );
 
-       Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn, 0, 0 );
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_ARGS,
+                  "do_modify: dn (%s)\n", dn.bv_val ));
+#else
+       Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
+#endif
+
 
        /* collect modifications & save for later */
-       mods = NULL;
-       modtail = &mods;
+
        for ( tag = ber_first_element( op->o_ber, &len, &last );
            tag != LBER_DEFAULT;
            tag = ber_next_element( op->o_ber, &len, last ) )
        {
-               (*modtail) = (LDAPMod *) ch_calloc( 1, sizeof(LDAPMod) );
+               ber_int_t mop;
+               Modifications tmp, *mod;
+
 
-               if ( ber_scanf( op->o_ber, "{i{a[V]}}", &(*modtail)->mod_op,
-                   &(*modtail)->mod_type, &(*modtail)->mod_bvalues )
+               if ( ber_scanf( op->o_ber, "{i{m[W]}}", &mop,
+                   &tmp.sml_type, &tmp.sml_bvalues )
                    == LBER_ERROR )
                {
-                       send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
-                           "decoding error" );
-                       free( dn );
-                       free( odn );
-                       free( *modtail );
-                       modlist_free( mods );
-                       return;
+                       send_ldap_disconnect( conn, op,
+                               LDAP_PROTOCOL_ERROR, "decoding modlist error" );
+                       rc = SLAPD_DISCONNECT;
+                       goto cleanup;
                }
 
-               if ( (*modtail)->mod_op != LDAP_MOD_ADD &&
-                   (*modtail)->mod_op != LDAP_MOD_DELETE &&
-                   (*modtail)->mod_op != LDAP_MOD_REPLACE )
-               {
-                       send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
-                           "unrecognized modify operation" );
-                       free( dn );
-                       free( odn );
-                       modlist_free( mods );
-                       return;
-               }
+               mod = (Modifications *) ch_malloc( sizeof(Modifications) );
+               mod->sml_op = mop;
+               mod->sml_type = tmp.sml_type;
+               mod->sml_bvalues = tmp.sml_bvalues;
+               mod->sml_desc = NULL;
+               mod->sml_next =NULL;
+               *modtail = mod;
 
-               if ( (*modtail)->mod_bvalues == NULL && (*modtail)->mod_op
-                 != LDAP_MOD_DELETE ) {
-                       send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
-                           "no values given" );
-                       free( dn );
-                       free( odn );
-                       modlist_free( mods );
-                       return;
+               switch( mop ) {
+               case LDAP_MOD_ADD:
+                       if ( mod->sml_bvalues == NULL ) {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                                       "do_modify: modify/add operation (%ld) requires values\n",
+                                       (long)mop ));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "do_modify: modify/add operation (%ld) requires values\n",
+                                       (long) mop, 0, 0 );
+#endif
+
+                               send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
+                                       NULL, "modify/add operation requires values",
+                                       NULL, NULL );
+                               rc = LDAP_PROTOCOL_ERROR;
+                               goto cleanup;
+                       }
+
+                       /* fall through */
+
+               case LDAP_MOD_DELETE:
+               case LDAP_MOD_REPLACE:
+                       break;
+
+               default: {
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                                       "do_modify: invalid modify operation (%ld)\n",
+                                       (long)mop ));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "do_modify: invalid modify operation (%ld)\n",
+                                       (long) mop, 0, 0 );
+#endif
+
+                               send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
+                                       NULL, "unrecognized modify operation", NULL, NULL );
+                               rc = LDAP_PROTOCOL_ERROR;
+                               goto cleanup;
+                       }
                }
-               attr_normalize( (*modtail)->mod_type );
 
-               modtail = &(*modtail)->mod_next;
+               modtail = &mod->sml_next;
        }
        *modtail = NULL;
 
+       if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                       "do_modify: get_ctrls failed\n" ));
+#else
+               Debug( LDAP_DEBUG_ANY, "do_modify: get_ctrls failed\n", 0, 0, 0 );
+#endif
+
+               goto cleanup;
+       }
+
+       rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn );
+       if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+                       "do_modify: conn %d  invalid dn (%s)\n",
+                       conn->c_connid, dn.bv_val ));
+#else
+               Debug( LDAP_DEBUG_ANY,
+                       "do_modify: invalid dn (%s)\n", dn.bv_val, 0, 0 );
+#endif
+               send_ldap_result( conn, op, rc = LDAP_INVALID_DN_SYNTAX, NULL,
+                   "invalid DN", NULL, NULL );
+               goto cleanup;
+       }
+
+       if( ndn.bv_len == 0 ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                       "do_modify: attempt to modify root DSE.\n" ));
+#else
+               Debug( LDAP_DEBUG_ANY, "do_modify: root dse!\n", 0, 0, 0 );
+#endif
+
+               send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
+                       NULL, "modify upon the root DSE not supported", NULL, NULL );
+               goto cleanup;
+
+#if defined( SLAPD_SCHEMA_DN )
+       } else if ( strcasecmp( ndn.bv_val, SLAPD_SCHEMA_DN ) == 0 ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                       "do_modify: attempt to modify subschema subentry.\n" ));
+#else
+               Debug( LDAP_DEBUG_ANY, "do_modify: subschema subentry!\n", 0, 0, 0 );
+#endif
+
+               send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
+                       NULL, "modification of subschema subentry not supported",
+                       NULL, NULL );
+               goto cleanup;
+#endif
+       }
+
 #ifdef LDAP_DEBUG
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+               "do_modify: modifications:\n" ));
+#else
        Debug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
-       for ( tmp = mods; tmp != NULL; tmp = tmp->mod_next ) {
-               Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n", tmp->mod_op
-                   == LDAP_MOD_ADD ? "add" : (tmp->mod_op == LDAP_MOD_DELETE ?
-                   "delete" : "replace"), tmp->mod_type, 0 );
+#endif
+
+       for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                       "\t%s:  %s\n", tmp->sml_op == LDAP_MOD_ADD ?
+                               "add" : (tmp->sml_op == LDAP_MOD_DELETE ?
+                                       "delete" : "replace"), tmp->sml_type.bv_val ));
+
+               if ( tmp->sml_bvalues == NULL ) {
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                          "\t\tno values" ));
+               } else if ( tmp->sml_bvalues[0].bv_val == NULL ) {
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                          "\t\tzero values" ));
+               } else if ( tmp->sml_bvalues[1].bv_val == NULL ) {
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                          "\t\tone value" ));
+               } else {
+                       LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+                          "\t\tmultiple values" ));
+               }
+
+#else
+               Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
+                       tmp->sml_op == LDAP_MOD_ADD
+                               ? "add" : (tmp->sml_op == LDAP_MOD_DELETE
+                                       ? "delete" : "replace"), tmp->sml_type.bv_val, 0 );
+
+               if ( tmp->sml_bvalues == NULL ) {
+                       Debug( LDAP_DEBUG_ARGS, "%s\n",
+                          "\t\tno values", NULL, NULL );
+               } else if ( tmp->sml_bvalues[0].bv_val == NULL ) {
+                       Debug( LDAP_DEBUG_ARGS, "%s\n",
+                          "\t\tzero values", NULL, NULL );
+               } else if ( tmp->sml_bvalues[1].bv_val == NULL ) {
+                       Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
+                          "\t\tone value", (long) tmp->sml_bvalues[0].bv_len, NULL );
+               } else {
+                       Debug( LDAP_DEBUG_ARGS, "%s\n",
+                          "\t\tmultiple values", NULL, NULL );
+               }
+#endif
        }
 #endif
 
-       Statslog( LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\"\n",
-           conn->c_connid, op->o_opid, dn, 0, 0 );
+       Statslog( LDAP_DEBUG_STATS, "conn=%ld op=%d MOD dn=\"%s\"\n",
+           op->o_connid, op->o_opid, dn.bv_val, 0, 0 );
+
+       manageDSAit = get_manageDSAit( op );
 
        /*
         * We could be serving multiple database backends.  Select the
         * appropriate one, or send a referral to our "referral server"
         * if we don't hold it.
         */
-       if ( (be = select_backend( dn )) == NULL ) {
-               free( dn );
-               free( odn );
-               modlist_free( mods );
-               send_ldap_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
-                   default_referral );
-               return;
+       if ( (be = select_backend( &ndn, manageDSAit, 0 )) == NULL ) {
+               BerVarray ref = referral_rewrite( default_referral,
+                       NULL, &pdn, LDAP_SCOPE_DEFAULT );
+
+               send_ldap_result( conn, op, rc = LDAP_REFERRAL,
+                       NULL, NULL, ref ? ref : default_referral, NULL );
+
+               ber_bvarray_free( ref );
+               goto cleanup;
+       }
+
+       /* check restrictions */
+       rc = backend_check_restrictions( be, conn, op, NULL, &text ) ;
+       if( rc != LDAP_SUCCESS ) {
+               send_ldap_result( conn, op, rc,
+                       NULL, text, NULL, NULL );
+               goto cleanup;
        }
 
+       /* check for referrals */
+       rc = backend_check_referrals( be, conn, op, &pdn, &ndn );
+       if ( rc != LDAP_SUCCESS ) {
+               goto cleanup;
+       }
+
+       /* deref suffix alias if appropriate */
+       suffix_alias( be, &ndn );
+
        /*
         * do the modify if 1 && (2 || 3)
         * 1) there is a modify function implemented in this backend;
         * 2) this backend is master for what it holds;
-        * 3) it's a replica and the dn supplied is the updatedn.
+        * 3) it's a replica and the dn supplied is the update_ndn.
         */
-       if ( be->be_modify != NULL ) {
+       if ( be->be_modify ) {
                /* do the update here */
-               if ( be->be_updatedn == NULL || strcasecmp( be->be_updatedn,
-                   op->o_dn ) == 0 ) {
-                       if ( (be->be_lastmod == ON || be->be_lastmod == 0 &&
-                           global_lastmod == ON) && be->be_updatedn == NULL ) {
-                               add_lastmods( op, &mods );
+               int repl_user = be_isupdate( be, &op->o_ndn );
+#ifndef SLAPD_MULTIMASTER
+               /* Multimaster slapd does not have to check for replicator dn
+                * because it accepts each modify request
+                */
+               if ( !be->be_update_ndn.bv_len || repl_user )
+#endif
+               {
+                       int update = be->be_update_ndn.bv_len;
+                       const char *text;
+                       char textbuf[SLAP_TEXT_BUFLEN];
+                       size_t textlen = sizeof textbuf;
+
+                       rc = slap_mods_check( modlist, update, &text,
+                               textbuf, textlen );
+
+                       if( rc != LDAP_SUCCESS ) {
+                               send_ldap_result( conn, op, rc,
+                                       NULL, text, NULL, NULL );
+                               goto cleanup;
                        }
-                       if ( (*be->be_modify)( be, conn, op, odn, mods )
-                           == 0 ) {
-                               replog( be, LDAP_REQ_MODIFY, dn, mods, 0 );
+
+                       if ( SLAP_LASTMOD(be) && !repl_user ) {
+                               for( modtail = &modlist;
+                                       *modtail != NULL;
+                                       modtail = &(*modtail)->sml_next )
+                               {
+                                       /* empty */
+                               }
+
+                               rc = slap_mods_opattrs( op, modlist, modtail, &text,
+                                       textbuf, textlen );
+                               if( rc != LDAP_SUCCESS ) {
+                                       send_ldap_result( conn, op, rc,
+                                               NULL, text,
+                                               NULL, NULL );
+                                       goto cleanup;
+                               }
+                       }
+
+                       if ( (*be->be_modify)( be, conn, op, &pdn, &ndn, modlist ) == 0
+#ifdef SLAPD_MULTIMASTER
+                               && !repl_user
+#endif
+                       ) {
+                               /* but we log only the ones not from a replicator user */
+                               replog( be, op, &pdn, &ndn, modlist );
                        }
 
+#ifndef SLAPD_MULTIMASTER
                /* send a referral */
                } else {
-                       send_ldap_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
-                           default_referral );
+                       BerVarray defref = be->be_update_refs
+                               ? be->be_update_refs : default_referral;
+                       BerVarray ref = referral_rewrite( defref,
+                               NULL, &pdn, LDAP_SCOPE_DEFAULT );
+
+                       send_ldap_result( conn, op, rc = LDAP_REFERRAL, NULL, NULL,
+                               ref ? ref : defref, NULL );
+
+                       ber_bvarray_free( ref );
+#endif
                }
        } else {
-               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, NULL,
-                   "Function not implemented" );
+               send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
+                   NULL, "operation not supported within namingContext",
+                       NULL, NULL );
        }
 
-       free( dn );
-       free( odn );
-       modlist_free( mods );
+cleanup:
+       free( pdn.bv_val );
+       free( ndn.bv_val );
+       if ( modlist != NULL )
+               slap_mods_free( modlist );
+       return rc;
 }
 
-static void
-modlist_free(
-    LDAPMod    *mods
-)
+/*
+ * Do basic attribute type checking and syntax validation.
+ */
+int slap_mods_check(
+       Modifications *ml,
+       int update,
+       const char **text,
+       char *textbuf,
+       size_t textlen )
 {
-       LDAPMod *next;
-
-       for ( ; mods != NULL; mods = next ) {
-               next = mods->mod_next;
-               free( mods->mod_type );
-               if ( mods->mod_bvalues != NULL )
-                       ber_bvecfree( mods->mod_bvalues );
-               free( mods );
-       }
-}
+       int rc;
 
-static void
-add_lastmods( Operation *op, LDAPMod **mods )
-{
-       char            buf[20];
-       struct berval   bv;
-       struct berval   *bvals[2];
-       LDAPMod         **m;
-       LDAPMod         *tmp;
-       struct tm       *ltm;
-
-       Debug( LDAP_DEBUG_TRACE, "add_lastmods\n", 0, 0, 0 );
-
-       bvals[0] = &bv;
-       bvals[1] = NULL;
-
-       /* remove any attempts by the user to modify these attrs */
-       for ( m = mods; *m != NULL; m = &(*m)->mod_next ) {
-               if ( strcasecmp( (*m)->mod_type, "modifytimestamp" ) == 0
-                   || strcasecmp( (*m)->mod_type, "modifiersname" ) == 0 ) {
-                       tmp = *m;
-                       *m = (*m)->mod_next;
-                       free( tmp->mod_type );
-                       if ( tmp->mod_bvalues != NULL ) {
-                               ber_bvecfree( tmp->mod_bvalues );
+       for( ; ml != NULL; ml = ml->sml_next ) {
+               AttributeDescription *ad = NULL;
+
+               /* convert to attribute description */
+               rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
+
+               if( rc != LDAP_SUCCESS ) {
+                       snprintf( textbuf, textlen, "%s: %s",
+                               ml->sml_type.bv_val, *text );
+                       *text = textbuf;
+                       return rc;
+               }
+
+               ad = ml->sml_desc;
+
+               if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
+                       && !slap_ad_is_binary( ad ))
+               {
+                       /* attribute requires binary transfer */
+                       snprintf( textbuf, textlen,
+                               "%s: requires ;binary transfer",
+                               ml->sml_type.bv_val );
+                       *text = textbuf;
+                       return LDAP_UNDEFINED_TYPE;
+               }
+
+               if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
+                       && slap_ad_is_binary( ad ))
+               {
+                       /* attribute requires binary transfer */
+                       snprintf( textbuf, textlen,
+                               "%s: disallows ;binary transfer",
+                               ml->sml_type.bv_val );
+                       *text = textbuf;
+                       return LDAP_UNDEFINED_TYPE;
+               }
+
+               if( slap_ad_is_lang_range( ad )) {
+                       /* attribute requires binary transfer */
+                       snprintf( textbuf, textlen,
+                               "%s: inappropriate use of language range option",
+                               ml->sml_type.bv_val );
+                       *text = textbuf;
+                       return LDAP_UNDEFINED_TYPE;
+               }
+
+               if (!update && is_at_no_user_mod( ad->ad_type )) {
+                       /* user modification disallowed */
+                       snprintf( textbuf, textlen,
+                               "%s: no user modification allowed",
+                               ml->sml_type.bv_val );
+                       *text = textbuf;
+                       return LDAP_CONSTRAINT_VIOLATION;
+               }
+
+               if ( is_at_obsolete( ad->ad_type ) &&
+                       ( ml->sml_op == LDAP_MOD_ADD || ml->sml_bvalues != NULL ) )
+               {
+                       /*
+                        * attribute is obsolete,
+                        * only allow replace/delete with no values
+                        */
+                       snprintf( textbuf, textlen,
+                               "%s: attribute is obsolete",
+                               ml->sml_type.bv_val );
+                       *text = textbuf;
+                       return LDAP_CONSTRAINT_VIOLATION;
+               }
+
+               /*
+                * check values
+                */
+               if( ml->sml_bvalues != NULL ) {
+                       ber_len_t nvals;
+                       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 && !validate ) {
+                               *text = "no validator for syntax";
+                               snprintf( textbuf, textlen,
+                                       "%s: no validator for syntax %s",
+                                       ml->sml_type.bv_val,
+                                       ad->ad_type->sat_syntax->ssyn_oid );
+                               *text = textbuf;
+                               return LDAP_INVALID_SYNTAX;
+                       }
+
+                       /*
+                        * check that each value is valid per syntax
+                        *      and pretty if appropriate
+                        */
+                       for( nvals = 0; ml->sml_bvalues[nvals].bv_val; nvals++ ) {
+                               struct berval pval;
+                               if( pretty ) {
+                                       rc = pretty( ad->ad_type->sat_syntax,
+                                               &ml->sml_bvalues[nvals], &pval );
+                               } else {
+                                       rc = validate( ad->ad_type->sat_syntax,
+                                               &ml->sml_bvalues[nvals] );
+                               }
+
+                               if( rc != 0 ) {
+                                       snprintf( textbuf, textlen,
+                                               "%s: value #%ld invalid per syntax",
+                                               ml->sml_type.bv_val, (long) nvals );
+                                       *text = textbuf;
+                                       return LDAP_INVALID_SYNTAX;
+                               }
+
+                               if( pretty ) {
+                                       ber_memfree( ml->sml_bvalues[nvals].bv_val );
+                                       ml->sml_bvalues[nvals] = pval;
+                               }
+                       }
+
+                       /*
+                        * a rough single value check... an additional check is needed
+                        * to catch add of single value to existing single valued attribute
+                        */
+                       if( ( ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE )
+                               && nvals > 1 && is_at_single_value( ad->ad_type ))
+                       {
+                               snprintf( textbuf, textlen,
+                                       "%s: multiple value provided",
+                                       ml->sml_type.bv_val );
+                               *text = textbuf;
+                               return LDAP_CONSTRAINT_VIOLATION;
                        }
-                       free( tmp );
                }
        }
 
-       if ( op->o_dn == NULL || op->o_dn[0] == '\0' ) {
-               bv.bv_val = "NULLDN";
-               bv.bv_len = strlen( bv.bv_val );
+       return LDAP_SUCCESS;
+}
+
+int slap_mods_opattrs(
+       Operation *op,
+       Modifications *mods,
+       Modifications **modtail,
+       const char **text,
+       char *textbuf, size_t textlen )
+{
+       struct berval name, timestamp, csn;
+       time_t now = slap_get_time();
+       char timebuf[22];
+       char csnbuf[64];
+       struct tm *ltm;
+       Modifications *mod;
+
+       int mop = op->o_tag == LDAP_REQ_ADD
+               ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
+
+       assert( modtail != NULL );
+       assert( *modtail == NULL );
+
+       ldap_pvt_thread_mutex_lock( &gmtime_mutex );
+       ltm = gmtime( &now );
+       strftime( timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", ltm );
+
+       csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
+       ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
+       csn.bv_val = csnbuf;
+
+       timestamp.bv_val = timebuf;
+       timestamp.bv_len = strlen(timebuf);
+
+       if( op->o_dn.bv_len == 0 ) {
+               name.bv_val = SLAPD_ANONYMOUS;
+               name.bv_len = sizeof(SLAPD_ANONYMOUS)-1;
        } else {
-               bv.bv_val = op->o_dn;
-               bv.bv_len = strlen( bv.bv_val );
+               name = op->o_dn;
        }
-       tmp = (LDAPMod *) ch_calloc( 1, sizeof(LDAPMod) );
-       tmp->mod_type = strdup( "modifiersname" );
-       tmp->mod_op = LDAP_MOD_REPLACE;
-       tmp->mod_bvalues = (struct berval **) ch_calloc( 1,
-           2 * sizeof(struct berval *) );
-       tmp->mod_bvalues[0] = ber_bvdup( &bv );
-       tmp->mod_next = *mods;
-       *mods = tmp;
-
-       pthread_mutex_lock( &currenttime_mutex );
-        ltm = localtime( &currenttime );
-        strftime( buf, sizeof(buf), "%y%m%d%H%M%SZ", ltm );
-       pthread_mutex_unlock( &currenttime_mutex );
-       bv.bv_val = buf;
-       bv.bv_len = strlen( bv.bv_val );
-       tmp = (LDAPMod *) ch_calloc( 1, sizeof(LDAPMod) );
-       tmp->mod_type = strdup( "modifytimestamp" );
-       tmp->mod_op = LDAP_MOD_REPLACE;
-       tmp->mod_bvalues = (struct berval **) ch_calloc( 1,
-           2 * sizeof(struct berval *) );
-       tmp->mod_bvalues[0] = ber_bvdup( &bv );
-       tmp->mod_next = *mods;
-       *mods = tmp;
+
+       if( op->o_tag == LDAP_REQ_ADD ) {
+               struct berval tmpval;
+               char uuidbuf[40];
+               int rc;
+
+               rc = mods_structural_class( mods, &tmpval, text, textbuf, textlen );
+               if( rc != LDAP_SUCCESS ) {
+                       return rc;
+               }
+               if ( tmpval.bv_len ) {
+                       mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+                       mod->sml_op = mop;
+                       mod->sml_type.bv_val = NULL;
+                       mod->sml_desc = slap_schema.si_ad_structuralObjectClass;
+                       mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+                       ber_dupbv( &mod->sml_bvalues[0], &tmpval );
+                       mod->sml_bvalues[1].bv_val = NULL;
+                       assert( mod->sml_bvalues[0].bv_val );
+                       *modtail = mod;
+                       modtail = &mod->sml_next;
+               }
+
+               tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
+               tmpval.bv_val = uuidbuf;
+               
+#if 0
+               mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+               mod->sml_op = mop;
+               mod->sml_type.bv_val = NULL;
+               mod->sml_desc = slap_schema.si_ad_entryUUID;
+               mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+               ber_dupbv( &mod->sml_bvalues[0], &tmpval );
+               mod->sml_bvalues[1].bv_val = NULL;
+               assert( mod->sml_bvalues[0].bv_val );
+               *modtail = mod;
+               modtail = &mod->sml_next;
+#endif
+
+               mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+               mod->sml_op = mop;
+               mod->sml_type.bv_val = NULL;
+               mod->sml_desc = slap_schema.si_ad_creatorsName;
+               mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+               ber_dupbv( &mod->sml_bvalues[0], &name );
+               mod->sml_bvalues[1].bv_val = NULL;
+               assert( mod->sml_bvalues[0].bv_val );
+               *modtail = mod;
+               modtail = &mod->sml_next;
+
+               mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+               mod->sml_op = mop;
+               mod->sml_type.bv_val = NULL;
+               mod->sml_desc = slap_schema.si_ad_createTimestamp;
+               mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+               ber_dupbv( &mod->sml_bvalues[0], &timestamp );
+               mod->sml_bvalues[1].bv_val = NULL;
+               assert( mod->sml_bvalues[0].bv_val );
+               *modtail = mod;
+               modtail = &mod->sml_next;
+       }
+
+#if 0
+       mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+       mod->sml_op = mop;
+       mod->sml_type.bv_val = NULL;
+       mod->sml_desc = slap_schema.si_ad_entryCSN;
+       mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+       ber_dupbv( &mod->sml_bvalues[0], &csn );
+       mod->sml_bvalues[1].bv_val = NULL;
+       assert( mod->sml_bvalues[0].bv_val );
+       *modtail = mod;
+       modtail = &mod->sml_next;
+#endif
+
+       mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+       mod->sml_op = mop;
+       mod->sml_type.bv_val = NULL;
+       mod->sml_desc = slap_schema.si_ad_modifiersName;
+       mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+       ber_dupbv( &mod->sml_bvalues[0], &name );
+       mod->sml_bvalues[1].bv_val = NULL;
+       assert( mod->sml_bvalues[0].bv_val );
+       *modtail = mod;
+       modtail = &mod->sml_next;
+
+       mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+       mod->sml_op = mop;
+       mod->sml_type.bv_val = NULL;
+       mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
+       mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+       ber_dupbv( &mod->sml_bvalues[0], &timestamp );
+       mod->sml_bvalues[1].bv_val = NULL;
+       assert( mod->sml_bvalues[0].bv_val );
+       *modtail = mod;
+       modtail = &mod->sml_next;
+
+       *modtail = NULL;
+
+       return LDAP_SUCCESS;
 }
diff --git a/servers/slapd/schema_check.c b/servers/slapd/schema_check.c
new file mode 100644 (file)
index 0000000..bedbb14
--- /dev/null
@@ -0,0 +1,598 @@
+/* schema_check.c - routines to enforce schema definitions */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "ldap_pvt.h"
+
+static char * oc_check_required(
+       Entry *e,
+       ObjectClass *oc,
+       struct berval *ocname );
+
+/*
+ * entry_schema_check - check that entry e conforms to the schema required
+ * by its object class(es).
+ *
+ * returns 0 if so, non-zero otherwise.
+ */
+
+int
+entry_schema_check( 
+       Backend *be,
+       Entry *e,
+       Attribute *oldattrs,
+       const char** text,
+       char *textbuf, size_t textlen )
+{
+       Attribute       *a, *asc, *aoc;
+       ObjectClass *sc, *oc;
+       int     rc, i;
+       struct berval nsc;
+       AttributeDescription *ad_structuralObjectClass
+               = slap_schema.si_ad_structuralObjectClass;
+       AttributeDescription *ad_objectClass
+               = slap_schema.si_ad_objectClass;
+       int extensible = 0;
+       int subentry = is_entry_subentry( e );
+       int collectiveSubentry = 0;
+
+#if 0
+       if( subentry) collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
+#endif
+
+       *text = textbuf;
+
+       /* misc attribute checks */
+       for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+               const char *type = a->a_desc->ad_cname.bv_val;
+
+               /* there should be at least one value */
+               assert( a->a_vals );
+               assert( a->a_vals[0].bv_val != NULL ); 
+
+               if( a->a_desc->ad_type->sat_check ) {
+                       int rc = (a->a_desc->ad_type->sat_check)(
+                               be, e, a, text, textbuf, textlen );
+                       if( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+               }
+
+               if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
+                       snprintf( textbuf, textlen,
+                               "'%s' can only appear in collectiveAttributeSubentry",
+                               type );
+                       return LDAP_OBJECT_CLASS_VIOLATION;
+               }
+
+               /* if single value type, check for multiple values */
+               if( is_at_single_value( a->a_desc->ad_type ) &&
+                       a->a_vals[1].bv_val != NULL )
+               {
+                       snprintf( textbuf, textlen, 
+                               "attribute '%s' cannot have multiple values",
+                               type );
+
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                               "entry_schema_check: dn=\"%s\" %s\n",
+                               e->e_dn, textbuf ));
+#else
+                       Debug( LDAP_DEBUG_ANY,
+                           "Entry (%s), %s\n",
+                           e->e_dn, textbuf, 0 );
+#endif
+
+                       return LDAP_CONSTRAINT_VIOLATION;
+               }
+       }
+
+       /* it's a REALLY bad idea to disable schema checks */
+       if( !global_schemacheck ) return LDAP_SUCCESS;
+
+       /* find the object class attribute - could error out here */
+       asc = attr_find( e->e_attrs, ad_structuralObjectClass );
+       if ( asc == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "schema", LDAP_LEVEL_INFO, "entry_schema_check: "
+                       "No structuralObjectClass for entry (%s)\n",
+                       e->e_dn ));
+#else
+               Debug( LDAP_DEBUG_ANY,
+                       "No structuralObjectClass for entry (%s)\n",
+                   e->e_dn, 0, 0 );
+#endif
+
+               *text = "no structuralObjectClass operational attribute";
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       assert( asc->a_vals != NULL );
+       assert( asc->a_vals[0].bv_val != NULL );
+       assert( asc->a_vals[1].bv_val == NULL );
+
+       sc = oc_bvfind( &asc->a_vals[0] );
+       if( sc == NULL ) {
+               snprintf( textbuf, textlen, 
+                       "unrecognized structuralObjectClass '%s'",
+                       asc->a_vals[0].bv_val );
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                       "entry_schema_check: dn (%s), %s\n",
+                       e->e_dn, textbuf ));
+#else
+               Debug( LDAP_DEBUG_ANY,
+                       "entry_check_schema(%s): %s\n",
+                       e->e_dn, textbuf, 0 );
+#endif
+
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
+               snprintf( textbuf, textlen, 
+                       "structuralObjectClass '%s' is not STRUCTURAL",
+                       asc->a_vals[0].bv_val );
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                       "entry_schema_check: dn (%s), %s\n",
+                       e->e_dn, textbuf ));
+#else
+               Debug( LDAP_DEBUG_ANY,
+                       "entry_check_schema(%s): %s\n",
+                       e->e_dn, textbuf, 0 );
+#endif
+
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       /* find the object class attribute */
+       aoc = attr_find( e->e_attrs, ad_objectClass );
+       if ( aoc == NULL ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                       "entry_schema_check: No objectClass for entry (%s).\n"
+                       e->e_dn ));
+#else
+               Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
+                   e->e_dn, 0, 0 );
+#endif
+
+               *text = "no objectClass attribute";
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       assert( aoc->a_vals != NULL );
+       assert( aoc->a_vals[0].bv_val != NULL );
+
+       rc = structural_class( aoc->a_vals, &nsc, &oc, text, textbuf, textlen );
+       if( rc != LDAP_SUCCESS ) {
+               return rc;
+       } else if ( nsc.bv_len == 0 ) {
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       *text = textbuf;
+
+       if ( oc == NULL ) {
+               snprintf( textbuf, textlen, 
+                       "unrecognized objectClass '%s'",
+                       aoc->a_vals[0].bv_val );
+               return LDAP_OBJECT_CLASS_VIOLATION;
+
+       } else if ( sc != oc ) {
+               snprintf( textbuf, textlen, 
+                       "structuralObjectClass modification from '%s' to '%s' not allowed",
+                       asc->a_vals[0].bv_val, nsc.bv_val );
+               return LDAP_NO_OBJECT_CLASS_MODS;
+       }
+
+       /* check that the entry has required attrs for each oc */
+       for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
+               if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
+                       snprintf( textbuf, textlen, 
+                               "unrecognized objectClass '%s'",
+                               aoc->a_vals[i].bv_val );
+
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                               "entry_schema_check: dn (%s), %s\n",
+                               e->e_dn, textbuf ));
+#else
+                       Debug( LDAP_DEBUG_ANY,
+                               "entry_check_schema(%s): %s\n",
+                               e->e_dn, textbuf, 0 );
+#endif
+
+                       return LDAP_OBJECT_CLASS_VIOLATION;
+               }
+
+               if ( oc->soc_check ) {
+                       int rc = (oc->soc_check)( be, e, oc,
+                               text, textbuf, textlen );
+                       if( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+               }
+
+               if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
+                       /* object class is abstract */
+                       if ( oc != slap_schema.si_oc_top &&
+                               !is_object_subclass( oc, sc ))
+                       {
+                               int j;
+                               ObjectClass *xc = NULL;
+                               for( j=0; aoc->a_vals[j].bv_val; j++ ) {
+                                       if( i != j ) {
+                                               xc = oc_bvfind( &aoc->a_vals[i] );
+                                               if( xc == NULL ) {
+                                                       snprintf( textbuf, textlen, 
+                                                               "unrecognized objectClass '%s'",
+                                                               aoc->a_vals[i].bv_val );
+
+#ifdef NEW_LOGGING
+                                                       LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                                                               "entry_schema_check: dn (%s), %s\n",
+                                                               e->e_dn, textbuf ));
+#else
+                                                       Debug( LDAP_DEBUG_ANY,
+                                                               "entry_check_schema(%s): %s\n",
+                                                               e->e_dn, textbuf, 0 );
+#endif
+
+                                                       return LDAP_OBJECT_CLASS_VIOLATION;
+                                               }
+
+                                               /* since we previous check against the
+                                                * structural object of this entry, the
+                                                * abstract class must be a (direct or indirect)
+                                                * superclass of one of the auxiliary classes of
+                                                * the entry.
+                                                */
+                                               if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
+                                                       is_object_subclass( oc, xc ) )
+                                               {
+                                                       break;;
+                                               }
+
+                                               xc = NULL;
+                                       }
+                               }
+
+                               if( xc == NULL ) {
+                                       snprintf( textbuf, textlen, "instanstantiation of "
+                                               "abstract objectClass '%s' not allowed",
+                                               aoc->a_vals[i].bv_val );
+
+#ifdef NEW_LOGGING
+                                       LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                                               "entry_schema_check: dn (%s), %s\n",
+                                               e->e_dn, textbuf ));
+#else
+                                       Debug( LDAP_DEBUG_ANY,
+                                               "entry_check_schema(%s): %s\n",
+                                               e->e_dn, textbuf, 0 );
+#endif
+
+                                       return LDAP_OBJECT_CLASS_VIOLATION;
+                               }
+                       }
+
+               } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
+                       char *s = oc_check_required( e, oc, &aoc->a_vals[i] );
+
+                       if (s != NULL) {
+                               snprintf( textbuf, textlen, 
+                                       "object class '%s' requires attribute '%s'",
+                                       aoc->a_vals[i].bv_val, s );
+
+#ifdef NEW_LOGGING
+                               LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                                       "entry_schema_check: dn=\"%s\" %s",
+                                       e->e_dn, textbuf ));
+#else
+                               Debug( LDAP_DEBUG_ANY,
+                                       "Entry (%s): %s\n",
+                                       e->e_dn, textbuf, 0 );
+#endif
+
+                               return LDAP_OBJECT_CLASS_VIOLATION;
+                       }
+
+                       if( oc == slap_schema.si_oc_extensibleObject ) {
+                               extensible=1;
+                       }
+               }
+       }
+
+       if( extensible ) {
+               return LDAP_SUCCESS;
+       }
+
+       /* check that each attr in the entry is allowed by some oc */
+       for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+               int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
+               if ( ret != LDAP_SUCCESS ) {
+                       char *type = a->a_desc->ad_cname.bv_val;
+
+                       snprintf( textbuf, textlen, 
+                               "attribute '%s' not allowed",
+                               type );
+
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
+                               "entry_schema_check: dn=\"%s\" %s\n",
+                               e->e_dn, textbuf ));
+#else
+                       Debug( LDAP_DEBUG_ANY,
+                           "Entry (%s), %s\n",
+                           e->e_dn, textbuf, 0 );
+#endif
+
+                       return ret;
+               }
+       }
+
+       return LDAP_SUCCESS;
+}
+
+static char *
+oc_check_required(
+       Entry *e,
+       ObjectClass *oc,
+       struct berval *ocname )
+{
+       AttributeType   *at;
+       int             i;
+       Attribute       *a;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
+               "oc_check_required: dn (%s), objectClass \"%s\"\n",
+       e->e_dn, ocname->bv_val ));
+#else
+       Debug( LDAP_DEBUG_TRACE,
+               "oc_check_required entry (%s), objectClass \"%s\"\n",
+               e->e_dn, ocname->bv_val, 0 );
+#endif
+
+
+       /* check for empty oc_required */
+       if(oc->soc_required == NULL) {
+               return NULL;
+       }
+
+       /* for each required attribute */
+       for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
+               at = oc->soc_required[i];
+               /* see if it's in the entry */
+               for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+                       if( a->a_desc->ad_type == at ) {
+                               break;
+                       }
+               }
+               /* not there => schema violation */
+               if ( a == NULL ) {
+                       return at->sat_cname.bv_val;
+               }
+       }
+
+       return( NULL );
+}
+
+int oc_check_allowed(
+       AttributeType *at,
+       BerVarray ocl,
+       ObjectClass *sc )
+{
+       int             i, j;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
+               "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val ));
+#else
+       Debug( LDAP_DEBUG_TRACE,
+               "oc_check_allowed type \"%s\"\n",
+               at->sat_cname.bv_val, 0, 0 );
+#endif
+
+       /* always allow objectClass attribute */
+       if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
+               return LDAP_SUCCESS;
+       }
+
+       /*
+        * All operational attributions are allowed by schema rules.
+        */
+       if( is_at_operational(at) ) {
+               return LDAP_SUCCESS;
+       }
+
+       /* check to see if its allowed by the structuralObjectClass */
+       if( sc ) {
+               /* does it require the type? */
+               for ( j = 0; sc->soc_required != NULL && 
+                       sc->soc_required[j] != NULL; j++ )
+               {
+                       if( at == sc->soc_required[j] ) {
+                               return LDAP_SUCCESS;
+                       }
+               }
+
+               /* does it allow the type? */
+               for ( j = 0; sc->soc_allowed != NULL && 
+                       sc->soc_allowed[j] != NULL; j++ )
+               {
+                       if( at == sc->soc_allowed[j] ) {
+                               return LDAP_SUCCESS;
+                       }
+               }
+       }
+
+       /* check that the type appears as req or opt in at least one oc */
+       for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
+               /* if we know about the oc */
+               ObjectClass     *oc = oc_bvfind( &ocl[i] );
+               if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
+                       ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
+               {
+                       /* does it require the type? */
+                       for ( j = 0; oc->soc_required != NULL && 
+                               oc->soc_required[j] != NULL; j++ )
+                       {
+                               if( at == oc->soc_required[j] ) {
+                                       return LDAP_SUCCESS;
+                               }
+                       }
+                       /* does it allow the type? */
+                       for ( j = 0; oc->soc_allowed != NULL && 
+                               oc->soc_allowed[j] != NULL; j++ )
+                       {
+                               if( at == oc->soc_allowed[j] ) {
+                                       return LDAP_SUCCESS;
+                               }
+                       }
+               }
+       }
+
+       /* not allowed by any oc */
+       return LDAP_OBJECT_CLASS_VIOLATION;
+}
+
+/*
+ * Determine the structural object class from a set of OIDs
+ */
+int structural_class(
+       BerVarray ocs,
+       struct berval *scbv,
+       ObjectClass **scp,
+       const char **text,
+       char *textbuf, size_t textlen )
+{
+       int i;
+       ObjectClass *oc;
+       ObjectClass *sc = NULL;
+       int scn = -1;
+
+       *text = "structural_class: internal error";
+       scbv->bv_len = 0;
+
+       for( i=0; ocs[i].bv_val; i++ ) {
+               oc = oc_bvfind( &ocs[i] );
+
+               if( oc == NULL ) {
+                       snprintf( textbuf, textlen,
+                               "unrecognized objectClass '%s'",
+                               ocs[i].bv_val );
+                       *text = textbuf;
+                       return LDAP_OBJECT_CLASS_VIOLATION;
+               }
+
+               if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
+                       if( sc == NULL || is_object_subclass( sc, oc ) ) {
+                               sc = oc;
+                               scn = i;
+
+                       } else if ( !is_object_subclass( oc, sc ) ) {
+                               int j;
+                               ObjectClass *xc = NULL;
+
+                               /* find common superior */
+                               for( j=i+1; ocs[j].bv_val; j++ ) {
+                                       xc = oc_bvfind( &ocs[j] );
+
+                                       if( xc == NULL ) {
+                                               snprintf( textbuf, textlen,
+                                                       "unrecognized objectClass '%s'",
+                                                       ocs[i].bv_val );
+                                               *text = textbuf;
+                                               return LDAP_OBJECT_CLASS_VIOLATION;
+                                       }
+
+                                       if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
+                                               xc = NULL;
+                                               continue;
+                                       }
+
+                                       if( is_object_subclass( sc, xc ) &&
+                                               is_object_subclass( oc, xc ) )
+                                       {
+                                               /* found common subclass */
+                                               break;
+                                       }
+
+                                       xc = NULL;
+                               }
+
+                               if( xc == NULL ) {
+                                       /* no common subclass */
+                                       snprintf( textbuf, textlen,
+                                               "invalid structural object class chain (%s/%s)",
+                                               ocs[scn].bv_val, ocs[i].bv_val );
+                                       *text = textbuf;
+                                       return LDAP_OBJECT_CLASS_VIOLATION;
+                               }
+                       }
+               }
+       }
+
+       if( scp )
+               *scp = sc;
+
+       if( sc == NULL ) {
+               *text = "no structural object classes provided";
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       *scbv = ocs[scn];
+       return LDAP_SUCCESS;
+}
+
+/*
+ * Return structural object class from list of modifications
+ */
+int mods_structural_class(
+       Modifications *mods,
+       struct berval *sc,
+       const char **text,
+       char *textbuf, size_t textlen )
+{
+       Modifications *ocmod = NULL;
+
+       for( ; mods != NULL; mods = mods->sml_next ) {
+               if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
+                       if( ocmod != NULL ) {
+                               *text = "entry has multiple objectClass attributes";
+                               return LDAP_OBJECT_CLASS_VIOLATION;
+                       }
+                       ocmod = mods;
+               }
+       }
+
+       if( ocmod == NULL ) {
+               *text = "entry has no objectClass attribute";
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
+               *text = "objectClass attribute has no values";
+               return LDAP_OBJECT_CLASS_VIOLATION;
+       }
+
+       return structural_class( ocmod->sml_bvalues, sc, NULL,
+               text, textbuf, textlen );
+}
index 16262445b2346930d9948df8091768f5bbfcc26c..78293d9a8fadebb8b5108d8eef51267bdb98c1be 100644 (file)
@@ -448,20 +448,6 @@ static struct slap_schema_ad_map {
                offsetof(struct slap_internal_schema, si_ad_aci) },
 #endif
 
-       { "entryTtl", "( 1.3.6.1.4.1.1466.101.119.3 NAME 'entryTtl' "
-                       "DESC 'RFC2589: entry time-to-live' "
-                       "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE "
-                       "NO-USER-MODIFICATION USAGE dSAOperation )",
-               dynamicAttribute, 0, NULL, NULL, NULL,
-               offsetof(struct slap_internal_schema, si_ad_entryTtl) },
-       { "dynamicSubtrees", "( 1.3.6.1.4.1.1466.101.119.4 "
-                       "NAME 'dynamicSubtrees' "
-                       "DESC 'RFC2589: dynamic subtrees' "
-                       "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION "
-                       "USAGE dSAOperation )",
-               rootDseAttribute, 0, NULL, NULL, NULL,
-               offsetof(struct slap_internal_schema, si_ad_dynamicSubtrees) },
-
        /* userApplication attributes (which system schema depends upon) */
        { "distinguishedName", "( 2.5.4.49 NAME 'distinguishedName' "
                        "DESC 'RFC2256: common supertype of DN attributes' "
@@ -953,29 +939,3 @@ static int administrativeRoleAttribute (
                attr->a_desc->ad_cname.bv_val );
        return LDAP_OBJECT_CLASS_VIOLATION;
 }
-
-static int dynamicAttribute (
-       Backend *be,
-       Entry *e,
-       Attribute *attr,
-       const char** text,
-       char *textbuf, size_t textlen )
-{
-       *text = textbuf;
-
-       if( !SLAP_DYNAMIC(be) ) {
-               snprintf( textbuf, textlen,
-                       "attribute \"%s\" not supported in context",
-                       attr->a_desc->ad_cname.bv_val );
-               return LDAP_OBJECT_CLASS_VIOLATION;
-       }
-
-       if( !is_entry_dynamicObject( e ) ) {
-               snprintf( textbuf, textlen,
-                       "attribute \"%s\" only allowed in dynamic object",
-                       attr->a_desc->ad_cname.bv_val );
-               return LDAP_OBJECT_CLASS_VIOLATION;
-       }
-
-       return LDAP_SUCCESS;
-}
index 25f911049fae36b21154fd063b3b7014481ef9aa..6ce21c742698a5bd0a8b207a24596f754d1a9f6a 100644 (file)
@@ -557,8 +557,6 @@ struct slap_internal_schema {
        ObjectClass *si_oc_subentry;
        ObjectClass *si_oc_subschema;
        ObjectClass *si_oc_monitor;
-       ObjectClass *si_oc_collectiveAttributeSubentry;
-       ObjectClass *si_oc_dynamicObject;
 
        /* objectClass attribute descriptions */
        AttributeDescription *si_ad_objectClass;
@@ -571,10 +569,6 @@ struct slap_internal_schema {
        AttributeDescription *si_ad_modifyTimestamp;
        AttributeDescription *si_ad_hasSubordinates;
        AttributeDescription *si_ad_subschemaSubentry;
-       AttributeDescription *si_ad_collectiveSubentries;
-       AttributeDescription *si_ad_collectiveExclusions;
-       AttributeDescription *si_ad_entryUUID;
-       AttributeDescription *si_ad_entryCSN;
 
        /* root DSE attribute descriptions */
        AttributeDescription *si_ad_altServer;
@@ -612,10 +606,6 @@ struct slap_internal_schema {
        AttributeDescription *si_ad_aci;
 #endif
 
-       /* dynamic entries */
-       AttributeDescription *si_ad_entryTtl;
-       AttributeDescription *si_ad_dynamicSubtrees;
-
        /* Other attributes descriptions */
        AttributeDescription *si_ad_distinguishedName;
        AttributeDescription *si_ad_name;