]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/overlays/constraint.c
handle softadd/softdel (internal modifications; ITS#7773)
[openldap] / servers / slapd / overlays / constraint.c
index 711790080f4799b22fc7a864720f057ef75e7f77..4e8b13dd6cd93a6539defe8816eecf6e4a9a2914 100644 (file)
 typedef struct constraint {
        struct constraint *ap_next;
        AttributeDescription **ap;
+
+       LDAPURLDesc *restrict_lud;
+       struct berval restrict_ndn;
+       Filter *restrict_filter;
+       struct berval restrict_val;
+
        regex_t *re;
        LDAPURLDesc *lud;
        int set;
@@ -74,10 +80,10 @@ enum {
 static ConfigDriver constraint_cf_gen;
 
 static ConfigTable constraintcfg[] = {
-       { "constraint_attribute", "attribute> (regex|uri) <value",
-         4, 4, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen,
+       { "constraint_attribute", "attribute[list]> (regex|uri|set|size|count) <value> [<restrict URI>]",
+         4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen,
          "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' "
-         "DESC 'regular expression constraint for attribute' "
+         "DESC 'constraint for list of attributes' "
          "EQUALITY caseIgnoreMatch "
          "SYNTAX OMsDirectoryString )", NULL, NULL },
        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
@@ -94,8 +100,16 @@ static ConfigOCs constraintocs[] = {
 };
 
 static void
-constraint_free( constraint *cp )
+constraint_free( constraint *cp, int freeme )
 {
+       if (cp->restrict_lud)
+               ldap_free_urldesc(cp->restrict_lud);
+       if (!BER_BVISNULL(&cp->restrict_ndn))
+               ch_free(cp->restrict_ndn.bv_val);
+       if (cp->restrict_filter != NULL && cp->restrict_filter != slap_filter_objectClass_pres)
+               filter_free(cp->restrict_filter);
+       if (!BER_BVISNULL(&cp->restrict_val))
+               ch_free(cp->restrict_val.bv_val);
        if (cp->re) {
                regfree(cp->re);
                ch_free(cp->re);
@@ -108,7 +122,8 @@ constraint_free( constraint *cp )
                ch_free(cp->attrs);
        if (cp->ap)
                ch_free(cp->ap);
-       ch_free(cp);
+       if (freeme)
+               ch_free(cp);
 }
 
 static int
@@ -118,7 +133,7 @@ constraint_cf_gen( ConfigArgs *c )
        constraint *cn = on->on_bi.bi_private, *cp;
        struct berval bv;
        int i, rc = 0;
-       constraint ap = { NULL, NULL, NULL      }, *a2 = NULL;
+       constraint ap = { NULL };
        const char *text = NULL;
        
        switch ( c->op ) {
@@ -130,6 +145,8 @@ constraint_cf_gen( ConfigArgs *c )
                                char *tstr = NULL;
                                int quotes = 0;
                                int j;
+                               size_t val;
+                               char val_buf[SLAP_TEXT_BUFLEN] = { '\0' };
 
                                bv.bv_len = STRLENOF("  ");
                                for (j = 0; cp->ap[j]; j++) {
@@ -141,20 +158,37 @@ constraint_cf_gen( ConfigArgs *c )
 
                                if (cp->re) {
                                        tstr = REGEX_STR;
+                                       quotes = 1;
                                } else if (cp->lud) {
                                        tstr = URI_STR;
+                                       quotes = 1;
                                } else if (cp->set) {
                                        tstr = SET_STR;
                                        quotes = 1;
                                } else if (cp->size) {
                                        tstr = SIZE_STR;
+                                       val = cp->size;
                                } else if (cp->count) {
                                        tstr = COUNT_STR;
+                                       val = cp->count;
                                }
 
                                bv.bv_len += strlen(tstr);
                                bv.bv_len += cp->val.bv_len + 2*quotes;
 
+                               if (cp->restrict_lud != NULL) {
+                                       bv.bv_len += cp->restrict_val.bv_len + STRLENOF(" restrict=\"\"");
+                               }
+
+                               if (cp->count || cp->size) {
+                                       int len = snprintf(val_buf, sizeof(val_buf), "%zd", val);
+                                       if (len <= 0) {
+                                               /* error */
+                                               return -1;
+                                       }
+                                       bv.bv_len += len;
+                               }
+
                                s = bv.bv_val = ch_malloc(bv.bv_len + 1);
 
                                s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len );
@@ -165,9 +199,18 @@ constraint_cf_gen( ConfigArgs *c )
                                *s++ = ' ';
                                s = lutil_strcopy( s, tstr );
                                *s++ = ' ';
-                               if ( quotes ) *s++ = '"';
-                               s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len );
-                               if ( quotes ) *s++ = '"';
+                               if (cp->count || cp->size) {
+                                       s = lutil_strcopy( s, val_buf );
+                               } else {
+                                       if ( quotes ) *s++ = '"';
+                                       s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len );
+                                       if ( quotes ) *s++ = '"';
+                               }
+                               if (cp->restrict_lud != NULL) {
+                                       s = lutil_strcopy( s, " restrict=\"" );
+                                       s = lutil_strncopy( s, cp->restrict_val.bv_val, cp->restrict_val.bv_len );
+                                       *s++ = '"';
+                               }
                                *s = '\0';
 
                                rc = value_add_one( &c->rvalue_vals, &bv );
@@ -191,7 +234,7 @@ constraint_cf_gen( ConfigArgs *c )
                                /* zap all constraints */
                                while (cn) {
                                        cp = cn->ap_next;
-                                       constraint_free( cn );
+                                       constraint_free( cn, 1 );
                                        cn = cp;
                                }
                                                
@@ -207,7 +250,7 @@ constraint_cf_gen( ConfigArgs *c )
                                if (cp) {
                                        /* zap cp, and join cpp to cp->ap_next */
                                        *cpp = cp->ap_next;
-                                       constraint_free( cp );
+                                       constraint_free( cp, 1 );
                                }
                                on->on_bi.bi_private = cn;
                        }
@@ -232,13 +275,10 @@ constraint_cf_gen( ConfigArgs *c )
                                if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) {
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                                "%s <%s>: %s\n", c->argv[0], attrs[j], text );
-                                       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
-                                                  "%s: %s\n", c->log, c->cr_msg, 0 );
-                                       ldap_memvfree((void**)attrs);
-                                       return( ARG_BAD_CONF );
+                                       rc = ARG_BAD_CONF;
+                                       goto done;
                                }
                        }
-                       ldap_memvfree((void**)attrs);
 
                        if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) {
                                int err;
@@ -251,12 +291,11 @@ constraint_cf_gen( ConfigArgs *c )
                                        regerror( err, ap.re, errmsg, sizeof(errmsg) );
                                        ch_free(ap.re);
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                          "%s %s: Illegal regular expression \"%s\": Error %s",
-                                          c->argv[0], c->argv[1], c->argv[3], errmsg);
-                                       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
-                                               "%s: %s\n", c->log, c->cr_msg, 0 );
+                                               "%s %s: Illegal regular expression \"%s\": Error %s",
+                                               c->argv[0], c->argv[1], c->argv[3], errmsg);
                                        ap.re = NULL;
-                                       return( ARG_BAD_CONF );
+                                       rc = ARG_BAD_CONF;
+                                       goto done;
                                }
                                ber_str2bv( c->argv[3], 0, 1, &ap.val );
                        } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) {
@@ -277,21 +316,17 @@ constraint_cf_gen( ConfigArgs *c )
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                                "%s %s: Invalid URI \"%s\"",
                                                c->argv[0], c->argv[1], c->argv[3]);
-                                       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
-                                               "%s: %s\n", c->log, c->cr_msg, 0 );
-                                       return( ARG_BAD_CONF );
+                                       rc = ARG_BAD_CONF;
+                                       goto done;
                                }
 
                                if (ap.lud->lud_host != NULL) {
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                                "%s %s: unsupported hostname in URI \"%s\"",
                                                c->argv[0], c->argv[1], c->argv[3]);
-                                       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
-                                               "%s: %s\n", c->log, c->cr_msg, 0 );
-
                                        ldap_free_urldesc(ap.lud);
-
-                                       return( ARG_BAD_CONF );
+                                       rc = ARG_BAD_CONF;
+                                       goto done;
                                }
 
                                for ( i=0; ap.lud->lud_attrs[i]; i++);
@@ -304,23 +339,43 @@ constraint_cf_gen( ConfigArgs *c )
                                                        ch_free( ap.attrs );
                                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                                                "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text );
-                                                       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
-                                                                  "%s: %s\n", c->log, c->cr_msg, 0 );
-                                                       return( ARG_BAD_CONF );
+                                                       rc = ARG_BAD_CONF;
+                                                       goto done;
                                                }
                                        }
                                        ap.attrs[i] = NULL;
                                }
 
-                               if (ap.lud->lud_dn == NULL)
+                               if (ap.lud->lud_dn == NULL) {
                                        ap.lud->lud_dn = ch_strdup("");
+                               } else {
+                                       struct berval dn, ndn;
+
+                                       ber_str2bv( ap.lud->lud_dn, 0, 0, &dn );
+                                       if (dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) ) {
+                                               /* cleanup */
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "%s %s: URI %s DN normalization failed",
+                                                       c->argv[0], c->argv[1], c->argv[3] );
+                                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+                                                          "%s: %s\n", c->log, c->cr_msg, 0 );
+                                               rc = ARG_BAD_CONF;
+                                               goto done;
+                                       }
+                                       ldap_memfree( ap.lud->lud_dn );
+                                       ap.lud->lud_dn = ndn.bv_val;
+                               }
 
                                if (ap.lud->lud_filter == NULL) {
                                        ap.lud->lud_filter = ch_strdup("objectClass=*");
                                } else if ( ap.lud->lud_filter[0] == '(' ) {
                                        ber_len_t len = strlen( ap.lud->lud_filter );
                                        if ( ap.lud->lud_filter[len - 1] != ')' ) {
-                                                       return( ARG_BAD_CONF );
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "%s %s: invalid URI filter: %s",
+                                                       c->argv[0], c->argv[1], ap.lud->lud_filter );
+                                               rc = ARG_BAD_CONF;
+                                               goto done;
                                        }
                                        AC_MEMCPY( &ap.lud->lud_filter[0], &ap.lud->lud_filter[1], len - 2 );
                                        ap.lud->lud_filter[len - 2] = '\0';
@@ -334,28 +389,148 @@ constraint_cf_gen( ConfigArgs *c )
 
                        } else {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                  "%s %s: Unknown constraint type: %s",
-                                  c->argv[0], c->argv[1], c->argv[2] );
-                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
-                                  "%s: %s\n", c->log, c->cr_msg, 0 );
-                               return ( ARG_BAD_CONF );
+                                       "%s %s: Unknown constraint type: %s",
+                                       c->argv[0], c->argv[1], c->argv[2] );
+                               rc = ARG_BAD_CONF;
+                               goto done;
                        }
 
-                       a2 = ch_calloc( sizeof(constraint), 1 );
-                       a2->ap_next = on->on_bi.bi_private;
-                       a2->ap = ap.ap;
-                       a2->re = ap.re;
-                       a2->val = ap.val;
-                       a2->lud = ap.lud;
-                       a2->set = ap.set;
-                       a2->size = ap.size;
-                       a2->count = ap.count;
-                       if ( a2->lud ) {
-                               ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
-                               ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
+                       if ( c->argc > 4 ) {
+                               int argidx;
+
+                               for ( argidx = 4; argidx < c->argc; argidx++ ) {
+                                       if ( strncasecmp( c->argv[argidx], "restrict=", STRLENOF("restrict=") ) == 0 ) {
+                                               int err;
+                                               char *arg = c->argv[argidx] + STRLENOF("restrict=");
+
+                                               err = ldap_url_parse(arg, &ap.restrict_lud);
+                                               if ( err != LDAP_URL_SUCCESS ) {
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                               "%s %s: Invalid restrict URI \"%s\"",
+                                                               c->argv[0], c->argv[1], arg);
+                                                       rc = ARG_BAD_CONF;
+                                                       goto done;
+                                               }
+
+                                               if (ap.restrict_lud->lud_host != NULL) {
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                               "%s %s: unsupported hostname in restrict URI \"%s\"",
+                                                               c->argv[0], c->argv[1], arg);
+                                                       rc = ARG_BAD_CONF;
+                                                       goto done;
+                                               }
+
+                                               if ( ap.restrict_lud->lud_attrs != NULL ) {
+                                                       if ( ap.restrict_lud->lud_attrs[0] != '\0' ) {
+                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                       "%s %s: attrs not allowed in restrict URI %s\n",
+                                                                       c->argv[0], c->argv[1], arg);
+                                                               rc = ARG_BAD_CONF;
+                                                               goto done;
+                                                       }
+                                                       ldap_memvfree((void *)ap.restrict_lud->lud_attrs);
+                                                       ap.restrict_lud->lud_attrs = NULL;
+                                               }
+
+                                               if (ap.restrict_lud->lud_dn != NULL) {
+                                                       if (ap.restrict_lud->lud_dn[0] == '\0') {
+                                                               ldap_memfree(ap.restrict_lud->lud_dn);
+                                                               ap.restrict_lud->lud_dn = NULL;
+
+                                                       } else {
+                                                               struct berval dn, ndn;
+                                                               int j;
+
+                                                               ber_str2bv(ap.restrict_lud->lud_dn, 0, 0, &dn);
+                                                               if (dnNormalize(0, NULL, NULL, &dn, &ndn, NULL)) {
+                                                                       /* cleanup */
+                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                               "%s %s: restrict URI %s DN normalization failed",
+                                                                               c->argv[0], c->argv[1], arg );
+                                                                       rc = ARG_BAD_CONF;
+                                                                       goto done;
+                                                               }
+
+                                                               assert(c->be != NULL);
+                                                               if (c->be->be_nsuffix == NULL) {
+                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                               "%s %s: restrict URI requires suffix",
+                                                                               c->argv[0], c->argv[1] );
+                                                                       rc = ARG_BAD_CONF;
+                                                                       goto done;
+                                                               }
+
+                                                               for ( j = 0; !BER_BVISNULL(&c->be->be_nsuffix[j]); j++) {
+                                                                       if (dnIsSuffix(&ndn, &c->be->be_nsuffix[j])) break;
+                                                               }
+
+                                                               if (BER_BVISNULL(&c->be->be_nsuffix[j])) {
+                                                                       /* error */
+                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                               "%s %s: restrict URI DN %s not within database naming context(s)",
+                                                                               c->argv[0], c->argv[1], dn.bv_val );
+                                                                       rc = ARG_BAD_CONF;
+                                                                       goto done;
+                                                               }
+
+                                                               ap.restrict_ndn = ndn;
+                                                       }
+                                               }
+
+                                               if (ap.restrict_lud->lud_filter != NULL) {
+                                                       ap.restrict_filter = str2filter(ap.restrict_lud->lud_filter);
+                                                       if (ap.restrict_filter == NULL) {
+                                                               /* error */
+                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                       "%s %s: restrict URI filter %s invalid",
+                                                                       c->argv[0], c->argv[1], ap.restrict_lud->lud_filter );
+                                                               rc = ARG_BAD_CONF;
+                                                               goto done;
+                                                       }
+                                               }
+
+                                               ber_str2bv(c->argv[argidx] + STRLENOF("restrict="), 0, 1, &ap.restrict_val);
+
+                                       } else {
+                                               /* cleanup */
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "%s %s: unrecognized arg #%d (%s)",
+                                                       c->argv[0], c->argv[1], argidx, c->argv[argidx] );
+                                               rc = ARG_BAD_CONF;
+                                               goto done;
+                                       }
+                               }
+                       }
+
+done:;
+                       if ( rc == LDAP_SUCCESS ) {
+                               constraint *a2 = ch_calloc( sizeof(constraint), 1 );
+                               a2->ap_next = on->on_bi.bi_private;
+                               a2->ap = ap.ap;
+                               a2->re = ap.re;
+                               a2->val = ap.val;
+                               a2->lud = ap.lud;
+                               a2->set = ap.set;
+                               a2->size = ap.size;
+                               a2->count = ap.count;
+                               if ( a2->lud ) {
+                                       ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
+                                       ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
+                               }
+                               a2->attrs = ap.attrs;
+                               a2->restrict_lud = ap.restrict_lud;
+                               a2->restrict_ndn = ap.restrict_ndn;
+                               a2->restrict_filter = ap.restrict_filter;
+                               a2->restrict_val = ap.restrict_val;
+                               on->on_bi.bi_private = a2;
+
+                       } else {
+                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+                                          "%s: %s\n", c->log, c->cr_msg, 0 );
+                               constraint_free( &ap, 0 );
                        }
-                       a2->attrs = ap.attrs;
-                       on->on_bi.bi_private = a2;
+
+                       ldap_memvfree((void**)attrs);
                        } break;
                default:
                        abort();
@@ -384,7 +559,7 @@ constraint_uri_cb( Operation *op, SlapReply *rs )
 }
 
 static int
-constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply *rs)
+constraint_violation( constraint *c, struct berval *bv, Operation *op )
 {
        if ((!c) || (!bv)) return LDAP_SUCCESS;
        
@@ -399,19 +574,13 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply
                Operation nop = *op;
                slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
                slap_callback cb;
-               SlapReply nrs = { REP_RESULT };
                int i;
-               int found;
+               int found = 0;
                int rc;
                size_t len;
                struct berval filterstr;
                char *ptr;
 
-               found = 0;
-
-               nrs.sr_entry = NULL;
-               nrs.sr_nentries = 0;
-
                cb.sc_next = NULL;
                cb.sc_response = constraint_uri_cb;
                cb.sc_cleanup = NULL;
@@ -485,8 +654,11 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply
                        Debug( LDAP_DEBUG_ANY,
                                "%s constraint_violation uri filter=\"%s\" invalid\n",
                                op->o_log_prefix, filterstr.bv_val, 0 );
+                       rc = LDAP_OTHER;
 
                } else {
+                       SlapReply nrs = { REP_RESULT };
+
                        Debug(LDAP_DEBUG_TRACE, 
                                "==> constraint_violation uri filter = %s\n",
                                filterstr.bv_val, 0, 0);
@@ -499,13 +671,12 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply
                }
                op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
 
-               if((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
+               if ((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
                        return rc; /* unexpected error */
                }
 
                if (!found)
                        return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */
-                       
        }
 
        return LDAP_SUCCESS;
@@ -533,6 +704,61 @@ constraint_count_attr(Entry *e, AttributeDescription *ad)
        return 0;
 }
 
+static int
+constraint_check_restrict( Operation *op, constraint *c, Entry *e )
+{
+       assert( c->restrict_lud != NULL );
+
+       if ( c->restrict_lud->lud_dn != NULL ) {
+               int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len;
+
+               if ( diff < 0 ) {
+                       return 0;
+               }
+
+               if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) {
+                       return bvmatch( &e->e_nname, &c->restrict_ndn );
+               }
+
+               if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) {
+                       return 0;
+               }
+
+               if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) {
+                       struct berval pdn;
+
+                       if ( diff == 0 ) {
+                               return 0;
+                       }
+
+                       dnParent( &e->e_nname, &pdn );
+
+                       if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL
+                               && pdn.bv_len != c->restrict_ndn.bv_len )
+                       {
+                               return 0;
+                       }
+               }
+       }
+
+       if ( c->restrict_filter != NULL ) {
+               int rc;
+               struct berval save_dn = op->o_dn, save_ndn = op->o_ndn;
+
+               op->o_dn = op->o_bd->be_rootdn;
+               op->o_ndn = op->o_bd->be_rootndn;
+               rc = test_filter( op, e, c->restrict_filter );
+               op->o_dn = save_dn;
+               op->o_ndn = save_ndn;
+
+               if ( rc != LDAP_COMPARE_TRUE ) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 static int
 constraint_add( Operation *op, SlapReply *rs )
 {
@@ -545,6 +771,10 @@ constraint_add( Operation *op, SlapReply *rs )
        int rc;
        char *msg = NULL;
 
+       if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
+               return SLAP_CB_CONTINUE;
+       }
+
        if ((a = op->ora_e->e_attrs) == NULL) {
                op->o_bd->bd_info = (BackendInfo *)(on->on_info);
                send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
@@ -563,11 +793,15 @@ constraint_add( Operation *op, SlapReply *rs )
                        }
                        if (cp->ap[j] == NULL) continue;
                        if ((b = a->a_vals) == NULL) continue;
-                               
+
+                       if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) {
+                               continue;
+                       }
+
                        Debug(LDAP_DEBUG_TRACE, 
                                "==> constraint_add, "
-                               "a->a_numvals = %d, cp->count = %d\n",
-                               a->a_numvals, cp->count, 0);
+                               "a->a_numvals = %u, cp->count = %lu\n",
+                               a->a_numvals, (unsigned long) cp->count, 0);
 
                        if ((cp->count != 0) && (a->a_numvals > cp->count)) {
                                rc = LDAP_CONSTRAINT_VIOLATION;
@@ -575,7 +809,7 @@ constraint_add( Operation *op, SlapReply *rs )
                        }
 
                        for ( i = 0; b[i].bv_val; i++ ) {
-                               rc = constraint_violation( cp, &b[i], op, rs );
+                               rc = constraint_violation( cp, &b[i], op );
                                if ( rc ) {
                                        goto add_violation;
                                }
@@ -604,50 +838,144 @@ add_violation:
 
 
 static int
-constraint_modify( Operation *op, SlapReply *rs )
+constraint_check_count_violation( Modifications *m, Entry *target_entry, constraint *cp )
+{
+       BerVarray b = NULL;
+       unsigned ce = 0;
+       unsigned ca;
+       int j;
+
+       for ( j = 0; cp->ap[j]; j++ ) {
+               /* Get this attribute count */
+               if ( target_entry )
+                       ce = constraint_count_attr( target_entry, cp->ap[j] );
+
+               for( ; m; m = m->sml_next ) {
+                       if ( cp->ap[j] == m->sml_desc ) {
+                               ca = m->sml_numvals;
+                               switch ( m->sml_op ) {
+                               case LDAP_MOD_DELETE:
+                               case SLAP_MOD_SOFTDEL:
+                                       if ( !ca || ca > ce ) {
+                                               ce = 0;
+                                       } else {
+                                               /* No need to check for values' validity. Invalid values
+                                                * cause the whole transaction to die anyway. */
+                                               ce -= ca;
+                                       }
+                                       break;
+
+                               case LDAP_MOD_ADD:
+                               case SLAP_MOD_SOFTADD:
+                                       ce += ca;
+                                       break;
+
+                               case LDAP_MOD_REPLACE:
+                                       ce = ca;
+                                       break;
+
+#if 0
+                               /* TODO */
+                               case handle SLAP_MOD_ADD_IF_NOT_PRESENT:
+#endif
+
+                               default:
+                                       /* impossible! assert? */
+                                       return 1;
+                               }
+
+                               Debug(LDAP_DEBUG_TRACE,
+                                       "==> constraint_check_count_violation ce = %u, "
+                                       "ca = %u, cp->count = %lu\n",
+                                       ce, ca, (unsigned long) cp->count);
+                       }
+               }
+       }
+
+       return ( ce > cp->count );
+}
+
+static int
+constraint_update( Operation *op, SlapReply *rs )
 {
        slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
        Backend *be = op->o_bd;
        constraint *c = on->on_bi.bi_private, *cp;
        Entry *target_entry = NULL, *target_entry_copy = NULL;
-       Modifications *m;
+       Modifications *modlist, *m;
        BerVarray b = NULL;
        int i;
        struct berval rsv = BER_BVC("modify breaks constraint");
        int rc;
        char *msg = NULL;
+       int is_v;
+
+       if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
+               return SLAP_CB_CONTINUE;
+       }
+
+       switch ( op->o_tag ) {
+       case LDAP_REQ_MODIFY:
+               modlist = op->orm_modlist;
+               break;
+
+       case LDAP_REQ_MODRDN:
+               modlist = op->orr_modlist;
+               break;
+
+       default:
+               /* impossible! assert? */
+               return LDAP_OTHER;
+       }
        
-       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_modify()\n", 0,0,0);
-       if ((m = op->orm_modlist) == NULL) {
+       Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n", 0,0,0);
+       if ((m = modlist) == NULL) {
                op->o_bd->bd_info = (BackendInfo *)(on->on_info);
                send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
-                                               "constraint_modify() got null orm_modlist");
+                                               "constraint_update() got null modlist");
                return(rs->sr_err);
        }
 
+       op->o_bd = on->on_info->oi_origdb;
+       rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
+       op->o_bd = be;
+
+       /* let the backend send the error */
+       if ( target_entry == NULL )
+               return SLAP_CB_CONTINUE;
+
        /* Do we need to count attributes? */
        for(cp = c; cp; cp = cp->ap_next) {
-               if (cp->count != 0 || cp->set) {
-                       op->o_bd = on->on_info->oi_origdb;
-                       rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
-                       op->o_bd = be;
-
+               if (cp->count != 0) {
                        if (rc != 0 || target_entry == NULL) {
                                Debug(LDAP_DEBUG_TRACE, 
-                                       "==> constraint_modify rc = %d DN=\"%s\"%s\n",
+                                       "==> constraint_update rc = %d DN=\"%s\"%s\n",
                                        rc, op->o_req_ndn.bv_val,
                                        target_entry ? "" : " not found" );
                                if ( rc == 0 ) 
                                        rc = LDAP_CONSTRAINT_VIOLATION;
                                goto mod_violation;
                        }
-                       break;
+
+                       if (cp->restrict_lud && constraint_check_restrict(op, cp, target_entry) == 0) {
+                               continue;
+                       }
+
+                       is_v = constraint_check_count_violation(m, target_entry, cp);
+
+                       Debug(LDAP_DEBUG_TRACE,
+                               "==> constraint_update is_v: %d\n", is_v, 0, 0);
+
+                       if (is_v) {
+                               rc = LDAP_CONSTRAINT_VIOLATION;
+                               goto mod_violation;
+                       }
                }
        }
 
        rc = LDAP_CONSTRAINT_VIOLATION;
        for(;m; m = m->sml_next) {
-               int ce = 0;
+               unsigned ce = 0;
 
                if (is_at_operational( m->sml_desc->ad_type )) continue;
 
@@ -660,10 +988,6 @@ constraint_modify( Operation *op, SlapReply *rs )
                if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
                        continue;
 
-               /* Get this attribute count, if needed */
-               if (target_entry)
-                       ce = constraint_count_attr(target_entry, m->sml_desc);
-
                for(cp = c; cp; cp = cp->ap_next) {
                        int j;
                        for (j = 0; cp->ap[j]; j++) {
@@ -672,41 +996,17 @@ constraint_modify( Operation *op, SlapReply *rs )
                                }
                        }
                        if (cp->ap[j] == NULL) continue;
-                       
-                       if (cp->count != 0) {
-                               int ca;
-
-                               if (m->sml_op == LDAP_MOD_DELETE)
-                                       ce = 0;
 
-                               for (ca = 0; b[ca].bv_val; ++ca);
-
-                               Debug(LDAP_DEBUG_TRACE, 
-                                       "==> constraint_modify ce = %d, "
-                                       "ca = %d, cp->count = %d\n",
-                                       ce, ca, cp->count);
-
-                               if (m->sml_op == LDAP_MOD_ADD) {
-                                       if (ca + ce > cp->count) {
-                                               rc = LDAP_CONSTRAINT_VIOLATION;
-                                               goto mod_violation;
-                                       }
-                               }
-                               if (m->sml_op == LDAP_MOD_REPLACE) {
-                                       if (ca > cp->count) {
-                                               rc = LDAP_CONSTRAINT_VIOLATION;
-                                               goto mod_violation;
-                                       }
-                                       ce = ca;
-                               }
-                       } 
+                       if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) {
+                               continue;
+                       }
 
                        /* DELETE are to be ignored beyond this point */
                        if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE)
                                continue;
 
                        for ( i = 0; b[i].bv_val; i++ ) {
-                               rc = constraint_violation( cp, &b[i], op, rs );
+                               rc = constraint_violation( cp, &b[i], op );
                                if ( rc ) {
                                        goto mod_violation;
                                }
@@ -718,11 +1018,30 @@ constraint_modify( Operation *op, SlapReply *rs )
 
                                        target_entry_copy = entry_dup(target_entry);
 
+                                       /* if rename, set the new entry's name
+                                        * (in normalized form only) */
+                                       if ( op->o_tag == LDAP_REQ_MODRDN ) {
+                                               struct berval pdn, ndn = BER_BVNULL;
+
+                                               if ( op->orr_nnewSup ) {
+                                                       pdn = *op->orr_nnewSup;
+
+                                               } else {
+                                                       dnParent( &target_entry_copy->e_nname, &pdn );
+                                               }
+
+                                               build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL ); 
+
+                                               ber_memfree( target_entry_copy->e_nname.bv_val );
+                                               target_entry_copy->e_nname = ndn;
+                                               ber_bvreplace( &target_entry_copy->e_name, &ndn );
+                                       }
+
                                        /* apply modifications, in an attempt
                                         * to estimate what the entry would
                                         * look like in case all modifications
                                         * pass */
-                                       for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+                                       for ( ml = modlist; ml; ml = ml->sml_next ) {
                                                Modification *mod = &ml->sml_mod;
                                                const char *text;
                                                char textbuf[SLAP_TEXT_BUFLEN];
@@ -765,6 +1084,29 @@ constraint_modify( Operation *op, SlapReply *rs )
                                                        }
                                                        break;
 
+                                               case SLAP_MOD_SOFTDEL:
+                                                       mod->sm_op = LDAP_MOD_ADD;
+                                                       err = modify_delete_values( target_entry_copy,
+                                                               mod, get_permissiveModify(op),
+                                                               &text, textbuf, textlen );
+                                                       mod->sm_op = SLAP_MOD_SOFTDEL;
+                                                       if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+                                                               err = LDAP_SUCCESS;
+                                                       }
+                                                       break;
+
+                                               case SLAP_MOD_ADD_IF_NOT_PRESENT:
+                                                       if ( attr_find( target_entry_copy->e_attrs, mod->sm_desc ) ) {
+                                                               err = LDAP_SUCCESS;
+                                                               break;
+                                                       }
+                                                       mod->sm_op = LDAP_MOD_ADD;
+                                                       err = modify_add_values( target_entry_copy,
+                                                               mod, get_permissiveModify(op),
+                                                               &text, textbuf, textlen );
+                                                       mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+                                                       break;
+
                                                default:
                                                        err = LDAP_OTHER;
                                                        break;
@@ -777,7 +1119,6 @@ constraint_modify( Operation *op, SlapReply *rs )
                                        }
                                }
 
-               
                                if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) {
                                        rc = LDAP_CONSTRAINT_VIOLATION;
                                        goto mod_violation;
@@ -829,7 +1170,7 @@ constraint_close(
 
        for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
                a2 = ap->ap_next;
-               constraint_free( ap );
+               constraint_free( ap, 1 );
        }
 
        return 0;
@@ -847,7 +1188,8 @@ constraint_initialize( void ) {
        constraint_ovl.on_bi.bi_type = "constraint";
        constraint_ovl.on_bi.bi_db_close = constraint_close;
        constraint_ovl.on_bi.bi_op_add = constraint_add;
-       constraint_ovl.on_bi.bi_op_modify = constraint_modify;
+       constraint_ovl.on_bi.bi_op_modify = constraint_update;
+       constraint_ovl.on_bi.bi_op_modrdn = constraint_update;
 
        constraint_ovl.on_bi.bi_private = NULL;