+
+/*
+ * Determine the structural object class from a set of OIDs
+ */
+int structural_class(
+ BerVarray ocs,
+ ObjectClass **scp,
+ ObjectClass ***socsp,
+ const char **text,
+ char *textbuf, size_t textlen,
+ void *ctx )
+{
+ int i, nocs;
+ ObjectClass *oc, **socs;
+ ObjectClass *sc = NULL;
+ int scn = -1;
+
+ *text = "structural_class: internal error";
+
+ /* count them */
+ for( i=0; ocs[i].bv_val; i++ ) ;
+ nocs = i;
+
+ socs = slap_sl_malloc( (nocs+1) * sizeof(ObjectClass *), ctx );
+
+ for( i=0; ocs[i].bv_val; i++ ) {
+ socs[i] = oc_bvfind( &ocs[i] );
+
+ if( socs[i] == NULL ) {
+ snprintf( textbuf, textlen,
+ "unrecognized objectClass '%s'",
+ ocs[i].bv_val );
+ *text = textbuf;
+ goto fail;
+ }
+ }
+ socs[i] = NULL;
+
+ for( i=0; ocs[i].bv_val; i++ ) {
+ oc = socs[i];
+ 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 = socs[j];
+
+ if( xc == NULL ) {
+ snprintf( textbuf, textlen,
+ "unrecognized objectClass '%s'",
+ ocs[j].bv_val );
+ *text = textbuf;
+ goto fail;
+ }
+
+ 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;
+ goto fail;
+ }
+ }
+ }
+ }
+
+ if( scp ) {
+ *scp = sc;
+ }
+
+ if( sc == NULL ) {
+ *text = "no structural object class provided";
+ goto fail;
+ }
+
+ if( scn < 0 ) {
+ *text = "invalid structural object class";
+ goto fail;
+ }
+
+ if ( socsp ) {
+ *socsp = socs;
+ } else {
+ slap_sl_free( socs, ctx );
+ }
+ *text = NULL;
+
+ return LDAP_SUCCESS;
+
+fail:
+ slap_sl_free( socs, ctx );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+}
+
+/*
+ * 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, void *ctx )
+{
+ Modifications *ocmod = NULL;
+ ObjectClass *ssc;
+ int rc;
+
+ 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_values == NULL || ocmod->sml_values[0].bv_val == NULL ) {
+ *text = "objectClass attribute has no values";
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ rc = structural_class( ocmod->sml_values, &ssc, NULL,
+ text, textbuf, textlen, ctx );
+ if ( rc == LDAP_SUCCESS )
+ *sc = ssc->soc_cname;
+ return rc;
+}
+
+
+static int
+entry_naming_check(
+ Entry *e,
+ int manage,
+ int add_naming,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ /* naming check */
+ LDAPRDN rdn = NULL;
+ const char *p = NULL;
+ ber_len_t cnt;
+ int rc = LDAP_SUCCESS;
+
+ if ( BER_BVISEMPTY( &e->e_name )) {
+ return LDAP_SUCCESS;
+ }
+
+ /*
+ * Get attribute type(s) and attribute value(s) of our RDN
+ */
+ if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
+ LDAP_DN_FORMAT_LDAP ) )
+ {
+ *text = "unrecognized attribute type(s) in RDN";
+ return LDAP_INVALID_DN_SYNTAX;
+ }
+
+ /* Check that each AVA of the RDN is present in the entry */
+ /* FIXME: Should also check that each AVA lists a distinct type */
+ for ( cnt = 0; rdn[cnt]; cnt++ ) {
+ LDAPAVA *ava = rdn[cnt];
+ AttributeDescription *desc = NULL;
+ Attribute *attr;
+ const char *errtext;
+ int add = 0;
+
+ if( ava->la_flags & LDAP_AVA_BINARY ) {
+ snprintf( textbuf, textlen,
+ "value of naming attribute '%s' in unsupported BER form",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ }
+
+ rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( textbuf, textlen, "%s (in RDN)", errtext );
+ break;
+ }
+
+ if( desc->ad_type->sat_usage ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is operational",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( desc->ad_type->sat_collective ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is collective",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( !manage && desc->ad_type->sat_obsolete ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is obsolete",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( !desc->ad_type->sat_equality ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' has no equality matching rule",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( !desc->ad_type->sat_equality->smr_match ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' has unsupported equality matching rule",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ /* find the naming attribute */
+ attr = attr_find( e->e_attrs, desc );
+ if ( attr == NULL ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is not present in entry",
+ ava->la_attr.bv_val );
+ if ( add_naming ) {
+ add = 1;
+
+ } else {
+ rc = LDAP_NAMING_VIOLATION;
+ }
+
+ } else {
+ rc = attr_valfind( attr, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX|
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &ava->la_value, NULL, NULL );
+
+ if ( rc != 0 ) {
+ switch( rc ) {
+ case LDAP_INAPPROPRIATE_MATCHING:
+ snprintf( textbuf, textlen,
+ "inappropriate matching for naming attribute '%s'",
+ ava->la_attr.bv_val );
+ break;
+ case LDAP_INVALID_SYNTAX:
+ snprintf( textbuf, textlen,
+ "value of naming attribute '%s' is invalid",
+ ava->la_attr.bv_val );
+ break;
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ if ( add_naming ) {
+ if ( is_at_single_value( desc->ad_type ) ) {
+ snprintf( textbuf, textlen,
+ "value of single-valued naming attribute '%s' conflicts with value present in entry",
+ ava->la_attr.bv_val );
+
+ } else {
+ add = 1;
+ rc = LDAP_SUCCESS;
+ }
+
+ } else {
+ snprintf( textbuf, textlen,
+ "value of naming attribute '%s' is not present in entry",
+ ava->la_attr.bv_val );
+ }
+ break;
+ default:
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is inappropriate",
+ ava->la_attr.bv_val );
+ }
+
+ if ( !add ) {
+ rc = LDAP_NAMING_VIOLATION;
+ }
+ }
+ }
+
+ if ( add ) {
+ attr_merge_normalize_one( e, desc, &ava->la_value, NULL );
+
+ } else if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ ldap_rdnfree( rdn );
+ return rc;
+}
+