--- /dev/null
+.TH SLAPO-REFINT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-refint \- Referential Integrity overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Referential Integrity overlay can be used with a backend database such as
+.BR slapd-bdb (5)
+to maintain the cohesiveness of a schema which utilizes reference attributes.
+.LP
+Integrity is maintained by updating database records which contain the named
+attributes to match the results of a
+.B modrdn
+or
+.B delete
+operation. For example, if the integrity attribute were configured as
+.B manager ,
+deletion of the record "uid=robert,ou=people,o=openldap.org" would trigger a
+search for all other records which have a
+.B manager
+attribute containing that DN. Entries matching that search would have their
+.B manager
+attribute removed.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Referential Integrity overlay.
+They should appear after the
+.B overlay
+directive and before any subsequent
+.B database
+directive.
+.TP
+.B refint_attributes <attribute...>
+Specify one or more attributes which for which integrity will be maintained
+as described above.
+.TP
+.B refint_nothing <string>
+Specify an arbitrary value to be used as a placeholder when the last value
+would otherwise be deleted from an attribute. This can be useful in cases
+where the schema requires the existence of an attribute for which referential
+integrity is enforced. The attempted deletion of a required attribute will
+otherwise result in an Object Class Violation, causing the request to fail.
+.B
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
--- /dev/null
+.TH SLAPO-UNIQUE 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-unique \- Attribute Uniqueness overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Attribute Uniqueness overlay can be used with a backend database such as
+.BR slapd-bdb (5)
+to enforce the uniqueness of some or all attributes within a subtree. This
+subtree defaults to the base DN of the database for which the Uniqueness
+overlay is configured.
+.LP
+Uniqueness is enforced by searching the subtree to ensure that the values of
+all attributes presented with an
+.B add ,
+.B modify
+or
+.B modrdn
+operation are unique within the subtree.
+For example, if uniquness were enforced for the
+.B uid
+attribute, the subtree would be searched for any other records which also
+have a
+.B uid
+attribute containing the same value. If any are found, the request is
+rejected.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Attribute Uniqueness overlay.
+They should appear after the
+.B overlay
+directive and before any subsequent
+.B database
+directive.
+.TP
+.B unique_base <basedn>
+Configure the subtree against which uniqueness searches will be invoked.
+The
+.B basedn
+defaults to the base DN of the database for which uniqueness is configured.
+.TP
+.B unique_ignore <attribute...>
+Configure one or more attributes for which uniqueness will not be enforced.
+If not configured, all non-operational (eg, system) attributes must be
+unique. Note that the
+.B unique_ignore
+list should generally contain the
+.B objectClass ,
+.B dc ,
+.B ou
+and
+.B o
+attributes, as these will generally not be unique, nor are they operational
+attributes.
+.TP
+.B unique_attributes <attribute...>
+Specify one or more attributes which for which uniqueness will be enforced.
+If not specified, all attributes which are not operational (eg, system
+attributes such as
+.B entryUUID )
+or specified via the
+.B unique_ignore
+directive above must be unique within the subtree.
+.TP
+.B unique_strict
+By default, uniqueness is not enforced for null values. Enabling
+.B unique_strict
+mode extends the concept of uniqueness to include null values, such that
+only one attribute within a subtree will be allowed to have a null value.
+.SH CAVEATS
+.LP
+The search key is generated with attributes that are non-operational, not
+on the
+.B unique_ignore
+list, and included in the
+.B unique_attributes
+list, in that order. This makes it possible to create interesting and
+unusable configurations.
+.LP
+Typical attributes for the
+.B unique_ignore
+directive are intentionally not hardcoded into the overlay to allow for
+maximum flexibility in meeting site-specific requirements.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
dyngroup.c \
lastmod.c \
pcache.c \
+ refint.c \
+ unique.c \
rwm.c rwmconf.c rwmdn.c rwmmap.c
OBJS = overlays.lo \
chain.lo \
dyngroup.lo \
lastmod.lo \
pcache.lo \
+ refint.lo \
+ unique.lo \
rwm.lo rwmconf.lo rwmdn.lo rwmmap.lo
LDAP_INCDIR= ../../../include
pcache.la : pcache.lo $(@PLAT@_LINK_LIBS)
$(LTLINK_MOD) -module -o $@ pcache.lo version.lo $(LINK_LIBS)
+refint.la : refint.lo $(@PLAT@_LINK_LIBS)
+ $(LTLINK_MOD) -module -o $@ refint.lo version.lo $(LINK_LIBS)
+
+unique.la : unique.lo $(@PLAT@_LINK_LIBS)
+ $(LTLINK_MOD) -module -o $@ unique.lo version.lo $(LINK_LIBS)
+
rwm.la : rwm.lo $(@PLAT@_LINK_LIBS)
$(LTLINK_MOD) -module -o $@ rwm.lo rwmconf.lo rwmdn.lo rwmmap.lo version.lo $(LINK_LIBS)
--- /dev/null
+/* refint.c - referential integrity module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Symas Corp. for inclusion in
+ * OpenLDAP Software. This work was sponsored by Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+/* This module maintains referential integrity for a set of
+ * DN-valued attributes by searching for all references to a given
+ * DN whenever the DN is changed or its entry is deleted, and making
+ * the appropriate update.
+ *
+ * Updates are performed using the database rootdn, but the ModifiersName
+ * is always set to refint_dn.
+ */
+
+#ifdef SLAPD_OVER_REFINT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+static slap_overinst refint;
+
+/* The DN to use in the ModifiersName for all refint updates */
+static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
+
+typedef struct refint_attrs_s {
+ struct refint_attrs_s *next;
+ AttributeDescription *attr;
+} refint_attrs;
+
+typedef struct dependents_s {
+ struct dependents_s *next;
+ BerValue dn; /* target dn */
+ Modifications *mm;
+} dependent_data;
+
+typedef struct refint_data_s {
+ const char *message; /* breadcrumbs */
+ struct refint_attrs_s *attrs; /* list of known attrs */
+ struct dependents_s *mods; /* modifications returned from callback */
+ BerValue dn; /* basedn in parent, searchdn in call */
+ BerValue newdn; /* replacement value for modrdn callback */
+ BerValue nnewdn; /* normalized replacement value */
+ BerValue nothing; /* the nothing value, if needed */
+ BerValue nnothing; /* normalized nothingness */
+} refint_data;
+
+/*
+** allocate new refint_data;
+** initialize, copy basedn;
+** store in on_bi.bi_private;
+**
+*/
+
+static int
+refint_db_init(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ refint_data *id = ch_malloc(sizeof(refint_data));
+ refint_attrs *ip;
+ id->message = "_init";
+ id->attrs = NULL;
+ id->newdn.bv_val = NULL;
+ id->nothing.bv_val = NULL;
+ id->nnothing.bv_val = NULL;
+ ber_dupbv( &id->dn, &be->be_nsuffix[0] );
+ on->on_bi.bi_private = id;
+ return(0);
+}
+
+
+/*
+** if command = attributes:
+** foreach argument:
+** convert to attribute;
+** add to configured attribute list;
+** elseif command = basedn:
+** set our basedn to argument;
+**
+*/
+
+static int
+refint_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+ refint_attrs *ip;
+ const char *text;
+ AttributeDescription *ad;
+ BerValue dn;
+ int i;
+
+ if(!strcasecmp(*argv, "refint_attributes")) {
+ for(i = 1; i < argc; i++) {
+ for(ip = id->attrs; ip; ip = ip->next)
+ if(!strcmp(argv[i], ip->attr->ad_cname.bv_val)) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: duplicate attribute <s>, ignored\n",
+ fname, lineno, argv[i]);
+ continue;
+ }
+ ad = NULL;
+ if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: bad attribute <%s>, ignored\n",
+ fname, lineno, text);
+ continue; /* XXX */
+ } else if(ad->ad_next) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: multiple attributes match <%s>, ignored\n",
+ fname, lineno, argv[i]);
+ continue;
+ }
+ ip = ch_malloc(sizeof(refint_attrs));
+ ip->attr = ad;
+ ip->next = id->attrs;
+ id->attrs = ip;
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n",
+ fname, lineno, argv[i]);
+ }
+ } else if(!strcasecmp(*argv, "refint_base")) {
+ /* XXX only one basedn (yet) - need validate argument! */
+ if(id->dn.bv_val) ch_free(id->dn.bv_val);
+ ber_str2bv( argv[1], 0, 0, &dn );
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: new baseDN <%s>\n",
+ fname, lineno, argv[1]);
+ if(dnNormalize(0, NULL, NULL, &dn, &id->dn, NULL)) {
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: bad baseDN!\n", fname, lineno, 0);
+ return(1);
+ }
+ } else if(!strcasecmp(*argv, "refint_nothing")) {
+ if(id->nothing.bv_val) ch_free(id->nothing.bv_val);
+ if(id->nnothing.bv_val) ch_free(id->nnothing.bv_val);
+ ber_str2bv( argv[1], 0, 1, &id->nothing );
+ if(dnNormalize(0, NULL, NULL, &id->nothing, &id->nnothing, NULL)) {
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: bad nothingDN!\n", fname, lineno, 0);
+ return(1);
+ }
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: new nothingDN<%s>\n",
+ fname, lineno, argv[1]);
+ } else {
+ return(SLAP_CONF_UNKNOWN);
+ }
+
+ id->message = "_config";
+ return(0);
+}
+
+
+/*
+** nothing really happens here;
+**
+*/
+
+static int
+refint_open(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+ id->message = "_open";
+ return(0);
+}
+
+
+/*
+** foreach configured attribute:
+** free it;
+** free our basedn;
+** (do not) free id->message;
+** reset on_bi.bi_private;
+** free our config data;
+**
+*/
+
+static int
+refint_close(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+ refint_attrs *ii, *ij;
+ id->message = "_close";
+
+ for(ii = id->attrs; ii; ii = ij) {
+ ij = ii->next;
+ ch_free(ii);
+ }
+
+ ch_free(id->dn.bv_val);
+ ch_free(id->nothing.bv_val);
+ ch_free(id->nnothing.bv_val);
+
+ on->on_bi.bi_private = NULL; /* XXX */
+
+ ch_free(id);
+
+ return(0);
+}
+
+/*
+** delete callback
+** generates a list of Modification* from search results
+*/
+
+static int
+refint_delete_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Attribute *a;
+ BerVarray b = NULL;
+ refint_data *id, *dd = op->o_callback->sc_private;
+ refint_attrs *ia, *da = dd->attrs;
+ dependent_data *ip, *dp = NULL;
+ Modifications *mp, *ma;
+ int i;
+
+ Debug(LDAP_DEBUG_TRACE, "refint_delete_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
+
+ if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
+ dd->message = "_delete_cb";
+
+ /*
+ ** foreach configured attribute type:
+ ** if this attr exists in the search result,
+ ** and it has a value matching the target:
+ ** allocate a Modification;
+ ** allocate its array of 2 BerValues;
+ ** if only one value, and we have a configured Nothing:
+ ** allocate additional Modification
+ ** type = MOD_ADD
+ ** BerValues[] = { Nothing, NULL };
+ ** add to list
+ ** type = MOD_DELETE
+ ** BerValues[] = { our target dn, NULL };
+ ** add this mod to the list of mods;
+ **
+ */
+
+ ip = ch_malloc(sizeof(dependent_data));
+ ip->dn.bv_val = NULL;
+ ip->next = NULL;
+ ip->mm = NULL;
+ ma = NULL;
+ for(ia = da; ia; ia = ia->next) {
+ if(a = attr_find(rs->sr_entry->e_attrs, ia->attr))
+ for(i = 0, b = a->a_nvals; b[i].bv_val; i++)
+ if(bvmatch(&dd->dn, &b[i])) {
+ if(!ip->dn.bv_val) ber_dupbv(&ip->dn, &rs->sr_entry->e_nname);
+ if(!b[1].bv_val && dd->nothing.bv_val) {
+ mp = ch_malloc(sizeof(Modifications));
+ mp->sml_desc = ia->attr; /* XXX */
+ mp->sml_type = a->a_desc->ad_cname;
+ mp->sml_values = ch_malloc(2 * sizeof(BerValue));
+ mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
+ mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
+ mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
+
+ mp->sml_op = LDAP_MOD_ADD;
+ ber_dupbv(&mp->sml_values[0], &dd->nothing);
+ ber_dupbv(&mp->sml_nvalues[0], &dd->nnothing);
+ mp->sml_next = ma;
+ ma = mp;
+ }
+ /* this might violate the object class */
+ mp = ch_malloc(sizeof(Modifications));
+ mp->sml_desc = ia->attr; /* XXX */
+ mp->sml_type = a->a_desc->ad_cname;
+ mp->sml_values = ch_malloc(2 * sizeof(BerValue));
+ mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
+ mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
+ mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
+ mp->sml_op = LDAP_MOD_DELETE;
+ ber_dupbv(&mp->sml_values[0], &dd->dn);
+ ber_dupbv(&mp->sml_nvalues[0], &mp->sml_values[0]);
+ mp->sml_next = ma;
+ ma = mp;
+ Debug(LDAP_DEBUG_TRACE, "refint_delete_cb: %s: %s\n",
+ a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0);
+ break;
+ }
+ }
+ ip->mm = ma;
+ ip->next = dd->mods;
+ dd->mods = ip;
+
+ return(0);
+}
+
+/*
+** null callback
+** does nothing
+*/
+
+static int
+refint_null_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ ((refint_data *)op->o_callback->sc_private)->message = "_null_cb";
+ return(LDAP_SUCCESS);
+}
+
+/*
+** modrdn callback
+** generates a list of Modification* from search results
+*/
+
+static int
+refint_modrdn_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Attribute *a;
+ BerVarray b = NULL;
+ refint_data *id, *dd = op->o_callback->sc_private;
+ refint_attrs *ia, *da = dd->attrs;
+ dependent_data *ip = NULL, *dp = NULL;
+ Modifications *mp;
+ int i, j, fix;
+
+ Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
+
+ if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
+ dd->message = "_modrdn_cb";
+
+ /*
+ ** foreach configured attribute type:
+ ** if this attr exists in the search result,
+ ** and it has a value matching the target:
+ ** allocate a pair of Modifications;
+ ** make it MOD_ADD the new value and MOD_DELETE the old;
+ ** allocate its array of BerValues;
+ ** foreach value in the search result:
+ ** if it matches our target value, replace it;
+ ** otherwise, copy from the search result;
+ ** terminate the array of BerValues;
+ ** add these mods to the list of mods;
+ **
+ */
+
+ for(ia = da; ia; ia = ia->next) {
+ if(a = attr_find(rs->sr_entry->e_attrs, ia->attr)) {
+ for(fix = 0, i = 0, b = a->a_nvals; b[i].bv_val; i++)
+ if(bvmatch(&dd->dn, &b[i])) { fix++; break; }
+ if(fix) {
+ if (!ip) {
+ ip = ch_malloc(sizeof(dependent_data));
+ ip->next = NULL;
+ ip->mm = NULL;
+ ber_dupbv(&ip->dn, &rs->sr_entry->e_nname);
+ }
+ mp = ch_malloc(sizeof(Modifications));
+ mp->sml_op = LDAP_MOD_ADD;
+ mp->sml_desc = ia->attr; /* XXX */
+ mp->sml_type = ia->attr->ad_cname;
+ mp->sml_values = ch_malloc(2 * sizeof(BerValue));
+ mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
+ ber_dupbv(&mp->sml_values[0], &dd->newdn);
+ ber_dupbv(&mp->sml_nvalues[0], &dd->nnewdn);
+ mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
+ mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
+ mp->sml_next = ip->mm;
+ ip->mm = mp;
+ mp = ch_malloc(sizeof(Modifications));
+ mp->sml_op = LDAP_MOD_DELETE;
+ mp->sml_desc = ia->attr; /* XXX */
+ mp->sml_type = ia->attr->ad_cname;
+ mp->sml_values = ch_malloc(2 * sizeof(BerValue));
+ mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
+ ber_dupbv(&mp->sml_values[0], &dd->dn);
+ ber_dupbv(&mp->sml_nvalues[0], &dd->dn);
+ mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
+ mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
+ mp->sml_next = ip->mm;
+ ip->mm = mp;
+ Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb: %s: %s\n",
+ a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0);
+ }
+ }
+ }
+ if (ip) {
+ ip->next = dd->mods;
+ dd->mods = ip;
+ }
+
+ return(0);
+}
+
+
+/*
+** refint_response
+** search for matching records and modify them
+*/
+
+static int
+refint_response(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Operation nop = *op;
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { NULL, NULL, NULL, NULL };
+ slap_callback cb2 = { NULL, slap_replog_cb, NULL, NULL };
+ slap_callback *cbo, *cbp;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+ refint_data dd = *id;
+ refint_attrs *ip;
+ dependent_data *dp;
+ char *fstr, *key, *kp, **dnpp, **ndnpp, *cp;
+ BerValue ndn, moddn, pdn;
+ BerVarray b = NULL;
+ int rc, ac, i, j, ksize;
+
+ id->message = "_refint_response";
+
+ /* If the main op failed or is not a Delete or ModRdn, ignore it */
+ if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
+ rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ /*
+ ** validate (and count) the list of attrs;
+ **
+ */
+
+ for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
+ if(!ac) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "refint_response called without any attributes";
+ return SLAP_CB_CONTINUE;
+ }
+
+ /*
+ ** find the backend that matches our configured basedn;
+ ** make sure it exists and has search and modify methods;
+ **
+ */
+
+ nop.o_bd = select_backend(&id->dn, 0, 1);
+
+ if(nop.o_bd) {
+ if (!nop.o_bd->be_search || !nop.o_bd->be_modify) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "backend missing search and/or modify";
+ return SLAP_CB_CONTINUE;
+ }
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "no known backend? this shouldn't be happening!";
+ return SLAP_CB_CONTINUE;
+ }
+
+ cb2.sc_next = &cb;
+
+ /*
+ ** if delete: set delete callback;
+ ** else modrdn: create a newdn, set modify callback;
+ **
+ */
+
+ if(op->o_tag == LDAP_REQ_DELETE) {
+ cb.sc_response = &refint_delete_cb;
+ dd.newdn.bv_val = NULL;
+ dd.nnewdn.bv_val = NULL;
+ } else {
+ cb.sc_response = &refint_modrdn_cb;
+ if ( op->oq_modrdn.rs_newSup ) {
+ pdn = *op->oq_modrdn.rs_newSup;
+ } else {
+ dnParent( &op->o_req_dn, &pdn );
+ }
+ build_new_dn( &dd.newdn, &pdn, &op->orr_newrdn, NULL );
+ if ( op->oq_modrdn.rs_nnewSup ) {
+ pdn = *op->oq_modrdn.rs_nnewSup;
+ } else {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+ build_new_dn( &dd.nnewdn, &pdn, &op->orr_nnewrdn, NULL );
+ }
+
+ /*
+ ** calculate the search key size and allocate it;
+ ** build a search filter for all configured attributes;
+ ** populate our Operation;
+ ** pass our data (attr list, dn) to backend via sc_private;
+ ** call the backend search function;
+ ** nb: (|(one=thing)) is valid, but do smart formatting anyway;
+ ** nb: 16 is arbitrarily a dozen or so extra bytes;
+ **
+ */
+
+ for(ksize = 16, ip = id->attrs; ip; ip = ip->next)
+ ksize += ip->attr->ad_cname.bv_len + op->o_req_dn.bv_len + 3;
+ kp = key = ch_malloc(ksize);
+ if(--ac) kp += sprintf(key, "(|");
+ for(ip = id->attrs; ip; ip = ip->next)
+ kp += sprintf(kp, "(%s=%s)",
+ ip->attr->ad_cname.bv_val, op->o_req_dn.bv_val);
+ if(ac) *kp++ = ')';
+ *kp = 0;
+
+ nop.ors_filter = str2filter_x(&nop, key);
+ ber_str2bv(key, 0, 0, &nop.ors_filterstr);
+
+ /* callback gets the searched dn instead */
+ dd.dn = op->o_req_ndn;
+ dd.message = "_dependent_search";
+ dd.mods = NULL;
+ cb.sc_private = ⅆ
+ nop.o_callback = &cb;
+ nop.o_tag = LDAP_REQ_SEARCH;
+ nop.ors_scope = LDAP_SCOPE_SUBTREE;
+ nop.ors_deref = LDAP_DEREF_NEVER;
+ nop.ors_slimit = -1;
+ nop.ors_tlimit = -1;
+ nop.o_req_ndn = id->dn;
+ nop.o_req_dn = id->dn;
+
+ /* search */
+ rc = nop.o_bd->be_search(&nop, &nrs);
+
+ filter_free_x(&nop, nop.ors_filter);
+ ch_free(key);
+ ch_free(dd.nnewdn.bv_val);
+ ch_free(dd.newdn.bv_val);
+ dd.newdn.bv_val = NULL;
+ dd.nnewdn.bv_val = NULL;
+
+ if(rc != LDAP_SUCCESS) {
+ rs->sr_err = nrs.sr_err;
+ rs->sr_text = "refint_response search failed";
+ goto done;
+ }
+
+ /* safety? paranoid just in case */
+ if(!cb.sc_private) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "whoa! refint_response callback wiped out sc_private?!";
+ goto done;
+ }
+
+ /* presto! now it's a modify request with null callback */
+ cb.sc_response = &refint_null_cb;
+ nop.o_tag = LDAP_REQ_MODIFY;
+ dd.message = "_dependent_modify";
+
+ /* See if the parent operation is going into the replog */
+ cbo = NULL;
+ for (cbp = op->o_callback->sc_next; cbp; cbo=cbp,cbp=cbp->sc_next) {
+ if (cbp->sc_response == slap_replog_cb) {
+ /* Invoke replog now, arrange for our
+ * dependent mods to also be logged
+ */
+ cbo->sc_next = cbp->sc_next;
+ replog( op );
+ nop.o_callback = &cb2;
+ break;
+ }
+ }
+
+ /*
+ ** [our search callback builds a list of mods]
+ ** foreach mod:
+ ** make sure its dn has a backend;
+ ** connect Modification* chain to our op;
+ ** call the backend modify function;
+ ** pass any errors upstream;
+ **
+ */
+
+ for(dp = dd.mods; dp; dp = dp->next) {
+ Modifications **tail, *m;
+
+ for(m = dp->mm; m && m->sml_next; m = m->sml_next);
+ tail = &m->sml_next;
+ nop.o_req_dn = dp->dn;
+ nop.o_req_ndn = dp->dn;
+ nop.o_bd = select_backend(&dp->dn, 0, 1);
+ if(!nop.o_bd) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "this should never happen either!";
+ goto done;
+ }
+ nrs.sr_type = REP_RESULT;
+ nop.orm_modlist = dp->mm; /* callback did all the work */
+ nop.o_dn = refint_dn;
+ nop.o_ndn = refint_dn;
+ rs->sr_err = slap_mods_opattrs( &nop, nop.orm_modlist,
+ tail, &rs->sr_text, NULL, 0 );
+ nop.o_dn = nop.o_bd->be_rootdn;
+ nop.o_ndn = nop.o_bd->be_rootndn;
+ if(rs->sr_err != LDAP_SUCCESS) goto done;
+ if((rc = nop.o_bd->be_modify(&nop, &nrs)) != LDAP_SUCCESS) {
+ rs->sr_err = nrs.sr_err;
+ rs->sr_text = "dependent modify failed";
+ goto done;
+ }
+ }
+
+done:
+ for(dp = dd.mods; dp; dp = dd.mods) {
+ dd.mods = dp->next;
+ ch_free(dp->dn.bv_val);
+ slap_mods_free(dp->mm);
+ }
+ dd.mods = NULL;
+
+ return(SLAP_CB_CONTINUE);
+}
+
+/*
+** init_module is last so the symbols resolve "for free" --
+** it expects to be called automagically during dynamic module initialization
+*/
+
+int refint_init() {
+
+ /* statically declared just after the #includes at top */
+ refint.on_bi.bi_type = "refint";
+ refint.on_bi.bi_db_init = refint_db_init;
+ refint.on_bi.bi_db_config = refint_config;
+ refint.on_bi.bi_db_open = refint_open;
+ refint.on_bi.bi_db_close = refint_close;
+ refint.on_response = refint_response;
+
+ return(overlay_register(&refint));
+}
+
+#if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
+int init_module(int argc, char *argv[]) {
+ return refint_init();
+}
+#endif
+
+#endif /* SLAPD_OVER_REFINT */
--- /dev/null
+/* unique.c - attribute uniqueness module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Symas Corp. for inclusion in
+ * OpenLDAP Software. This work was sponsored by Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_UNIQUE
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+static slap_overinst unique;
+
+typedef struct unique_attrs_s {
+ struct unique_attrs_s *next; /* list of attrs */
+ AttributeDescription *attr;
+} unique_attrs;
+
+typedef struct unique_data_s {
+ const char *message; /* breadcrumbs */
+ struct unique_attrs_s *attrs; /* list of known attrs */
+ struct unique_attrs_s *ignore; /* list of ignored attrs */
+ BerValue dn; /* base of "unique tree" */
+ char strict; /* null considered unique too */
+} unique_data;
+
+typedef struct unique_counter_s {
+ int count;
+} unique_counter;
+
+/*
+** allocate new unique_data;
+** initialize, copy basedn;
+** store in on_bi.bi_private;
+**
+*/
+
+static int unique_db_init(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ unique_data *ud = ch_malloc(sizeof(unique_data));
+ unique_attrs *up;
+
+ /* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */
+
+ ud->message = "_init";
+ ud->attrs = NULL;
+ ud->ignore = NULL;
+ ud->strict = 0;
+
+ /* default to the base of our configured database */
+ ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
+ on->on_bi.bi_private = ud;
+}
+
+
+/*
+** if command = attributes:
+** foreach argument:
+** convert to attribute;
+** add to configured attribute list;
+** elseif command = base:
+** set our basedn to argument;
+** else complain about invalid directive;
+**
+*/
+
+static int unique_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ unique_data *ud = on->on_bi.bi_private;
+ unique_attrs *up;
+ const char *text;
+ AttributeDescription *ad;
+ int i;
+
+ ud->message = "_config";
+ Debug(LDAP_DEBUG_TRACE, "==> unique_config\n", 0, 0, 0);
+
+ if(!strcasecmp(*argv, "unique_attributes") ||
+ !strcasecmp(*argv, "unique_ignore")) {
+ for(i = 1; i < argc; i++) {
+ for(up = ud->attrs; up; up = up->next)
+ if(!strcmp(argv[i], up->attr->ad_cname.bv_val)) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: duplicate attribute <s>, ignored\n",
+ fname, lineno, argv[i]);
+ continue;
+ }
+ ad = NULL;
+ if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: bad attribute <%s>, ignored\n",
+ fname, lineno, text);
+ continue; /* XXX */
+ } else if(ad->ad_next) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: multiple attributes match <%s>, ignored\n",
+ fname, lineno, argv[i]);
+ continue;
+ }
+ up = ch_malloc(sizeof(unique_attrs));
+ up->attr = ad;
+ if(!strcasecmp(*argv, "unique_ignore")) {
+ up->next = ud->ignore;
+ ud->ignore = up;
+ } else {
+ up->next = ud->attrs;
+ ud->attrs = up;
+ }
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n",
+ fname, lineno, argv[i]);
+ }
+ } else if(!strcasecmp(*argv, "unique_strict")) {
+ ud->strict = 1;
+ } else if(!strcasecmp(*argv, "unique_base")) {
+ struct berval bv;
+ ber_str2bv( argv[1], 0, 0, &bv );
+ ch_free(ud->dn.bv_val);
+ dnNormalize(0, NULL, NULL, &bv, &ud->dn, NULL);
+ Debug(LDAP_DEBUG_ANY, "%s: line %d: new base dn <%s>\n",
+ fname, lineno, argv[1]);
+ } else {
+ return(SLAP_CONF_UNKNOWN);
+ }
+
+ return(0);
+}
+
+
+/*
+** mostly, just print the init message;
+**
+*/
+
+static int
+unique_open(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ unique_data *ud = on->on_bi.bi_private;
+ ud->message = "_open";
+
+ Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
+
+ return(0);
+}
+
+
+/*
+** foreach configured attribute:
+** free it;
+** free our basedn;
+** (do not) free ud->message;
+** reset on_bi.bi_private;
+** free our config data;
+**
+*/
+
+static int
+unique_close(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ unique_data *ud = on->on_bi.bi_private;
+ unique_attrs *ii, *ij;
+ ud->message = "_close";
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
+
+ for(ii = ud->attrs; ii; ii = ij) {
+ ij = ii->next;
+ ch_free(ii);
+ }
+
+ for(ii = ud->ignore; ii; ii = ij) {
+ ij = ii->next;
+ ch_free(ii);
+ }
+
+ ch_free(ud->dn.bv_val);
+
+ on->on_bi.bi_private = NULL; /* XXX */
+
+ ch_free(ud);
+
+ return(0);
+}
+
+
+/*
+** search callback
+** if this is a REP_SEARCH, count++;
+**
+*/
+
+static int count_attr_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ /* because you never know */
+ if(!op || !rs) return(0);
+
+ /* Only search entries are interesting */
+ if(rs->sr_type != REP_SEARCH) return(0);
+
+ Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
+
+ ((unique_counter*)op->o_callback->sc_private)->count++;
+
+ return(0);
+}
+
+/* XXX extraneous (slap_response*) to avoid compiler warning */
+
+static int unique_add(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Operation nop = *op;
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ Attribute *a;
+ AttributeDescription *st;
+ BerVarray b = NULL;
+ char *fstr, *key, *kp;
+ const char *why;
+ int i, rc, ks = 16;
+ unique_attrs *up;
+ unique_counter uq = { 0 };
+ unique_data *ud = on->on_bi.bi_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);
+
+ /* validate backend. Should have already been done, but whatever */
+ nop.o_bd = select_backend(&ud->dn, 0, 1);
+ if(nop.o_bd) {
+ if (!nop.o_bd->be_search) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "backend missing search function");
+ return(rs->sr_err);
+ }
+ } else {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_OTHER,
+ "no known backend? this shouldn't be happening!");
+ return(rs->sr_err);
+ }
+
+/*
+** count everything first;
+** allocate some memory;
+** write the search key;
+**
+*/
+
+ if(!(a = op->ora_e->e_attrs)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "unique_add() got null op.ora_e.e_attrs");
+ return(rs->sr_err);
+ } else for(; a; a = a->a_next) {
+ if(is_at_operational(a->a_desc->ad_type)) continue;
+ if(ud->ignore) {
+ for(up = ud->ignore; up; up = up->next)
+ if(a->a_desc == up->attr) break;
+ if(up) continue;
+ }
+ if(ud->attrs) {
+ for(up = ud->attrs; up; up = up->next)
+ if(a->a_desc == up->attr) break;
+ if(!up) continue;
+ }
+ if((b = a->a_vals) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
+ ks += b[i].bv_len + a->a_desc->ad_cname.bv_len + 3;
+ else if(ud->strict)
+ ks += a->a_desc->ad_cname.bv_len + 4; /* (attr=*) */
+ }
+
+ key = ch_malloc(ks);
+
+ kp = key + sprintf(key, "(|");
+
+ for(a = op->ora_e->e_attrs; a; a = a->a_next) {
+ if(is_at_operational(a->a_desc->ad_type)) continue;
+ if(ud->ignore) {
+ for(up = ud->ignore; up; up = up->next)
+ if(a->a_desc == up->attr) break;
+ if(up) continue;
+ }
+ if(ud->attrs) {
+ for(up = ud->attrs; up; up = up->next)
+ if(a->a_desc == up->attr) break;
+ if(!up) continue;
+ }
+ if((b = a->a_vals) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
+ kp += sprintf(kp, "(%s=%s)", a->a_desc->ad_cname.bv_val, b[i].bv_val);
+ else if(ud->strict)
+ kp += sprintf(kp, "(%s=*)", a->a_desc->ad_cname.bv_val);
+ }
+
+ kp += sprintf(kp, ")");
+
+ Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);
+
+ nop.ors_filter = str2filter_x(&nop, key);
+ ber_str2bv(key, 0, 0, &nop.ors_filterstr);
+
+ cb.sc_response = (slap_response*)count_attr_cb;
+ cb.sc_private = &uq;
+ nop.o_callback = &cb;
+ nop.o_tag = LDAP_REQ_SEARCH;
+ nop.ors_scope = LDAP_SCOPE_SUBTREE;
+ nop.ors_deref = LDAP_DEREF_NEVER;
+ nop.ors_slimit = -1;
+ nop.ors_tlimit = -1;
+ nop.o_req_ndn = ud->dn;
+ nop.o_ndn = op->o_bd->be_rootndn;
+
+ rc = nop.o_bd->be_search(&nop, &nrs);
+ filter_free_x(&nop, nop.ors_filter);
+ ch_free( key );
+
+ if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, rc, "unique_add search failed");
+ return(rs->sr_err);
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "=> unique_add found %d records\n", uq.count, 0, 0);
+
+ if(uq.count) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "some attributes not unique");
+ return(rs->sr_err);
+ }
+
+ return(SLAP_CB_CONTINUE);
+}
+
+
+static int unique_modify(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Operation nop = *op;
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ Attribute *a;
+ AttributeDescription *st;
+ BerVarray b = NULL;
+ Modifications *m;
+ char *fstr, *key, *kp;
+ const char *why;
+ int i, rc, ks = 16; /* a handful of extra bytes */
+ unique_attrs *up;
+ unique_counter uq = { 0 };
+ unique_data *ud = on->on_bi.bi_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
+
+ nop.o_bd = select_backend(&ud->dn, 0, 1);
+ if(nop.o_bd) {
+ if (!nop.o_bd->be_search) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "backend missing search function");
+ return(rs->sr_err);
+ }
+ } else {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_OTHER,
+ "no known backend? this shouldn't be happening!");
+ return(rs->sr_err);
+ }
+
+/*
+** count everything first;
+** allocate some memory;
+** write the search key;
+**
+*/
+
+ if(!(m = op->orm_modlist)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "unique_modify() got null op.orm_modlist");
+ return(rs->sr_err);
+ } else for(; m; m = m->sml_next) {
+ if(is_at_operational(m->sml_desc->ad_type) ||
+ ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
+ if(ud->ignore) {
+ for(up = ud->ignore; up; up = up->next)
+ if(m->sml_desc == up->attr) break;
+ if(up) continue;
+ }
+ if(ud->attrs) {
+ for(up = ud->attrs; up; up = up->next)
+ if(m->sml_desc == up->attr) break;
+ if(!up) continue;
+ }
+ if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
+ ks += b[i].bv_len + m->sml_desc->ad_cname.bv_len + 3;
+ else if(ud->strict)
+ ks += m->sml_desc->ad_cname.bv_len + 4; /* (attr=*) */
+ }
+
+ key = ch_malloc(ks);
+
+ kp = key + sprintf(key, "(|");
+
+ for(m = op->orm_modlist; m; m = m->sml_next) {
+ if(is_at_operational(m->sml_desc->ad_type) ||
+ ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
+ if(ud->ignore) {
+ for(up = ud->ignore; up; up = up->next)
+ if(m->sml_desc == up->attr) break;
+ if(up) continue;
+ }
+ if(ud->attrs) {
+ for(up = ud->attrs; up; up = up->next)
+ if(m->sml_desc == up->attr) break;
+ if(!up) continue;
+ }
+ if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
+ kp += sprintf(kp, "(%s=%s)", m->sml_desc->ad_cname.bv_val, b[i].bv_val);
+ else if(ud->strict)
+ kp += sprintf(kp, "(%s=*)", m->sml_desc->ad_cname.bv_val);
+ }
+
+ kp += sprintf(kp, ")");
+
+ Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
+
+ nop.ors_filter = str2filter_x(&nop, key);
+ ber_str2bv(key, 0, 0, &nop.ors_filterstr);
+
+ cb.sc_response = (slap_response*)count_attr_cb;
+ cb.sc_private = &uq;
+ nop.o_callback = &cb;
+ nop.o_tag = LDAP_REQ_SEARCH;
+ nop.ors_scope = LDAP_SCOPE_SUBTREE;
+ nop.ors_deref = LDAP_DEREF_NEVER;
+ nop.ors_slimit = -1;
+ nop.ors_tlimit = -1;
+ nop.o_req_ndn = ud->dn;
+ nop.o_ndn = op->o_bd->be_rootndn;
+
+ rc = nop.o_bd->be_search(&nop, &nrs);
+ ch_free( key );
+
+ if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, rc, "unique_modify search failed");
+ return(rs->sr_err);
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "=> unique_modify found %d records\n", uq.count, 0, 0);
+
+ if(uq.count) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "some attributes not unique");
+ return(rs->sr_err);
+ }
+
+ return(SLAP_CB_CONTINUE);
+
+}
+
+
+static int unique_modrdn(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Operation nop = *op;
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ char *fstr, *key, *kp;
+ const char *why;
+ int i, rc, ks = 16; /* a handful of extra bytes */
+ unique_attrs *up;
+ unique_counter uq = { 0 };
+ unique_data *ud = on->on_bi.bi_private;
+ LDAPRDN newrdn;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
+ op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
+
+ nop.o_bd = select_backend(&ud->dn, 0, 1);
+ if(nop.o_bd) {
+ if (!nop.o_bd->be_search) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "backend missing search function");
+ return(rs->sr_err);
+ }
+ } else {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_OTHER,
+ "no known backend? this shouldn't be happening!");
+ return(rs->sr_err);
+ }
+
+ if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
+ (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "unknown type(s) used in RDN");
+ return(rs->sr_err);
+ }
+ for(i = 0; newrdn[i]; i++) {
+ AttributeDescription *ad = NULL;
+ if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
+ ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
+ rs->sr_err = LDAP_INVALID_SYNTAX;
+ send_ldap_result( op, rs );
+ return(rs->sr_err);
+ }
+ newrdn[i]->la_private = ad;
+ }
+
+ for(i = 0; newrdn[i]; i++) {
+ AttributeDescription *ad = newrdn[i]->la_private;
+ if(ud->ignore) {
+ for(up = ud->ignore; up; up = up->next)
+ if(ad == up->attr) break;
+ if(up) continue;
+ }
+ if(ud->attrs) {
+ for(up = ud->attrs; up; up = up->next)
+ if(ad == up->attr) break;
+ if(!up) continue;
+ }
+ ks += newrdn[i]->la_value.bv_len + ad->ad_cname.bv_len + 3;
+ }
+
+ key = ch_malloc(ks);
+ kp = key + sprintf(key, "(|");
+
+ for(i = 0; newrdn[i]; i++) {
+ AttributeDescription *ad = newrdn[i]->la_private;
+ if(ud->ignore) {
+ for(up = ud->ignore; up; up = up->next)
+ if(ad == up->attr) break;
+ if(up) continue;
+ }
+ if(ud->attrs) {
+ for(up = ud->attrs; up; up = up->next)
+ if(ad == up->attr) break;
+ if(!up) continue;
+ }
+ kp += sprintf(kp, "(%s=%s)", ad->ad_cname.bv_val,
+ newrdn[i]->la_value.bv_val);
+ }
+
+ kp += sprintf(kp, ")");
+
+
+ Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);
+
+ nop.ors_filter = str2filter_x(&nop, key);
+ ber_str2bv(key, 0, 0, &nop.ors_filterstr);
+
+ cb.sc_response = (slap_response*)count_attr_cb;
+ cb.sc_private = &uq;
+ nop.o_callback = &cb;
+ nop.o_tag = LDAP_REQ_SEARCH;
+ nop.ors_scope = LDAP_SCOPE_SUBTREE;
+ nop.ors_deref = LDAP_DEREF_NEVER;
+ nop.ors_slimit = -1;
+ nop.ors_tlimit = -1;
+ nop.o_req_ndn = ud->dn;
+ nop.o_ndn = op->o_bd->be_rootndn;
+
+ rc = nop.o_bd->be_search(&nop, &nrs);
+ ch_free( key );
+ ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
+
+ if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, rc, "unique_modrdn search failed");
+ return(rs->sr_err);
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn found %d records\n", uq.count, 0, 0);
+
+ if(uq.count) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "some attributes not unique");
+ return(rs->sr_err);
+ }
+
+ return(SLAP_CB_CONTINUE);
+}
+
+/*
+** init_module is last so the symbols resolve "for free" --
+** it expects to be called automagically during dynamic module initialization
+*/
+
+int unique_init() {
+
+ /* statically declared just after the #includes at top */
+ unique.on_bi.bi_type = "unique";
+ unique.on_bi.bi_db_init = unique_db_init;
+ unique.on_bi.bi_db_config = unique_config;
+ unique.on_bi.bi_db_open = unique_open;
+ unique.on_bi.bi_db_close = unique_close;
+ unique.on_bi.bi_op_add = unique_add;
+ unique.on_bi.bi_op_modify = unique_modify;
+ unique.on_bi.bi_op_modrdn = unique_modrdn;
+ unique.on_bi.bi_op_delete = NULL;
+
+ return(overlay_register(&unique));
+}
+
+#if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
+int init_module(int argc, char *argv[]) {
+ return unique_init();
+}
+#endif
+
+#endif /* SLAPD_OVER_UNIQUE */