]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/unique.c
8ee76343218d0a651c9346f80a9ebc43d9e92b51
[openldap] / servers / slapd / overlays / unique.c
1 /* unique.c - attribute uniqueness module */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004-2006 The OpenLDAP Foundation.
6  * Portions Copyright 2004 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Symas Corp. for inclusion in
19  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_UNIQUE
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/socket.h>
30
31 #include "slap.h"
32 #include "config.h"
33
34 static slap_overinst unique;
35
36 typedef struct unique_attrs_s {
37         struct unique_attrs_s *next;            /* list of attrs */
38         AttributeDescription *attr;
39 } unique_attrs;
40
41 typedef struct unique_data_s {
42         const char *message;                    /* breadcrumbs */
43         struct unique_attrs_s *attrs;           /* list of known attrs */
44         struct unique_attrs_s *ignore;          /* list of ignored attrs */
45         BerValue dn;                            /* base of "unique tree" */
46         char strict;                            /* null considered unique too */
47 } unique_data;
48
49 typedef struct unique_counter_s {
50         struct berval *ndn;
51         int count;
52 } unique_counter;
53
54 enum {
55         UNIQUE_BASE = 1,
56         UNIQUE_IGNORE,
57         UNIQUE_ATTR,
58         UNIQUE_STRICT
59 };
60
61 static ConfigDriver unique_cf_gen;
62
63 static ConfigTable uniquecfg[] = {
64         { "unique_base", "basedn", 2, 2, 0, ARG_DN|ARG_MAGIC|UNIQUE_BASE,
65           unique_cf_gen, "( OLcfgOvAt:10.1 NAME 'olcUniqueBase' "
66           "DESC 'Subtree for uniqueness searches' "
67           "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
68         { "unique_ignore", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_IGNORE,
69           unique_cf_gen, "( OLcfgOvAt:10.2 NAME 'olcUniqueIgnore' "
70           "DESC 'Attributes for which uniqueness shall not be enforced' "
71           "SYNTAX OMsDirectoryString )", NULL, NULL },
72         { "unique_attributes", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_ATTR,
73           unique_cf_gen, "( OLcfgOvAt:10.3 NAME 'olcUniqueAttribute' "
74           "DESC 'Attributes for which uniqueness shall be enforced' "
75           "SYNTAX OMsDirectoryString )", NULL, NULL },
76         { "unique_strict", "on|off", 1, 2, 0,
77           ARG_ON_OFF|ARG_OFFSET|UNIQUE_STRICT,
78           (void *)offsetof(unique_data, strict),
79           "( OLcfgOvAt:10.4 NAME 'olcUniqueStrict' "
80           "DESC 'Enforce uniqueness of null values' "
81           "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
82         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
83 };
84
85 static ConfigOCs uniqueocs[] = {
86         { "( OLcfgOvOc:10.1 "
87           "NAME 'olcUniqueConfig' "
88           "DESC 'Attribute value uniqueness configuration' "
89           "SUP olcOverlayConfig "
90           "MAY ( olcUniqueBase $ olcUniqueIgnore $ "
91           "olcUniqueAttribute $ olcUniqueStrict ) )",
92           Cft_Overlay, uniquecfg },
93         { NULL, 0, NULL }
94 };
95
96 static int
97 unique_cf_gen( ConfigArgs *c )
98 {
99         slap_overinst *on = (slap_overinst *)c->bi;
100         unique_data *ud = (unique_data *)on->on_bi.bi_private;
101         BackendDB *be = (BackendDB *)c->be;
102         unique_attrs *up, *pup, **pupp = NULL;
103         AttributeDescription *ad;
104         const char *text;
105         int rc = ARG_BAD_CONF;
106         int i;
107
108         switch ( c->op ) {
109         case SLAP_CONFIG_EMIT:
110                 switch ( c->type ) {
111                 case UNIQUE_BASE:
112                         if ( !BER_BVISEMPTY( &ud->dn )) {
113                                 rc = value_add_one( &c->rvalue_vals, &ud->dn );
114                                 if ( rc ) return rc;
115                                 rc = value_add_one( &c->rvalue_nvals, &ud->dn );
116                                 return rc;
117                         }
118                         break;
119                 case UNIQUE_IGNORE:
120                         /* fallthrough to UNIQUE_ATTR */
121                 case UNIQUE_ATTR:
122                         if ( c->type == UNIQUE_IGNORE ) up = ud->ignore;
123                         else up = ud->attrs;
124                         while ( up ) {
125                                 value_add_one( &c->rvalue_vals,
126                                                &up->attr->ad_cname );
127                                 up = up->next;
128                         }
129                         rc = 0;
130                         break;
131                 case UNIQUE_STRICT:
132                         /* handled via ARG_OFFSET */
133                         /* fallthrough to default */
134                 default:
135                         abort ();
136                 }
137                 break;
138         case LDAP_MOD_DELETE:
139                 switch ( c->type ) {
140                 case UNIQUE_BASE:
141                         /* default to the base of our configured database */
142                         if ( ud->dn.bv_val ) ber_memfree ( ud->dn.bv_val );
143                         ber_dupbv( &ud->dn, &be->be_nsuffix[0] );
144                         rc = 0;
145                         break;
146                 case UNIQUE_IGNORE:
147                         /* fallthrough to UNIQUE_ATTR */
148                 case UNIQUE_ATTR:
149                         if ( c->type == UNIQUE_IGNORE ) pupp = &ud->ignore;
150                         else pupp = &ud->attrs;
151
152                         if ( c->valx < 0 ) {
153                                 up = *pupp;
154                                 *pupp = NULL;
155                                 while ( up ) {
156                                         pup = up;
157                                         up = up->next;
158                                         ch_free ( pup );
159                                 }
160
161                         } else {
162
163                                 /* delete from linked list */
164                                 for ( i=0; i < c->valx; ++i ) {
165                                         pupp = &(*pupp)->next;
166                                 }
167                                 up = *pupp;
168                                 *pupp = (*pupp)->next;
169
170                                 /* AttributeDescriptions are global so
171                                  * shouldn't be freed here... */
172                                 ch_free ( up );
173                         }
174                         rc = 0;
175                         break;
176                 case UNIQUE_STRICT:
177                         /* handled via ARG_OFFSET */
178                         /* fallthrough to default */
179                 default:
180                         abort ();
181                 }
182                 break;
183         case SLAP_CONFIG_ADD:
184                 /* fallthrough to LDAP_MOD_ADD */
185         case LDAP_MOD_ADD:
186                 switch ( c->type ) {
187                 case UNIQUE_BASE:
188                         if ( !dnIsSuffix ( &c->value_ndn,
189                                            &be->be_nsuffix[0] ) ) {
190                                 sprintf ( c->msg, "dn is not a suffix of backend base" );
191                                 Debug ( LDAP_DEBUG_CONFIG, "unique add: %s\n",
192                                         c->msg, NULL, NULL );
193                                 rc = ARG_BAD_CONF;
194                         }
195                         if ( ud->dn.bv_val ) ber_memfree ( ud->dn.bv_val );
196                         ud->dn = c->value_ndn;
197                         rc = 0;
198                         break;
199                 case UNIQUE_IGNORE:
200                         /* fallthrough to UNIQUE_ATTR */
201                 case UNIQUE_ATTR:
202                         rc = 0;
203                         for ( i=1; i < c->argc; ++i ) {
204                                 ad = NULL;
205                                 if ( slap_str2ad ( c->argv[i], &ad, &text )
206                                      == LDAP_SUCCESS) {
207
208                                         up = ch_malloc (
209                                                 sizeof ( unique_attrs ) );
210                                         up->attr = ad;
211                                         if ( c->type == UNIQUE_IGNORE ) {
212                                                 up->next = ud->ignore;
213                                                 ud->ignore = up;
214                                         } else {
215                                                 up->next = ud->attrs;
216                                                 ud->attrs = up;
217                                         }
218                                 } else {
219                                         Debug ( LDAP_DEBUG_CONFIG,
220                                                 "unique add: <%s>: %s\n",
221                                                 c->argv[i], text, NULL );
222                                         strncpy ( c->msg,
223                                                   text,
224                                                   SLAP_TEXT_BUFLEN-1 );
225                                         c->msg[SLAP_TEXT_BUFLEN-1] = '\0';
226                                         rc = ARG_BAD_CONF;
227                                 }
228                         }
229                         break;
230                 case UNIQUE_STRICT:
231                         /* handled via ARG_OFFSET */
232                         /* fallthrough to default */
233                 default:
234                         abort ();
235                 }
236                 break;
237         default:
238                 abort ();
239         }
240
241         return rc;
242 }
243
244 /*
245 ** allocate new unique_data;
246 ** initialize, copy basedn;
247 ** store in on_bi.bi_private;
248 **
249 */
250
251 static int unique_db_init(
252         BackendDB       *be
253 )
254 {
255         slap_overinst *on = (slap_overinst *)be->bd_info;
256         unique_data *ud   = ch_malloc(sizeof(unique_data));
257
258         /* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */
259
260         ud->message     = "_init";
261         ud->attrs       = NULL;
262         ud->ignore      = NULL;
263         ud->strict      = 0;
264
265         /* default to the base of our configured database */
266         ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
267         on->on_bi.bi_private = ud;
268
269         return 0;
270 }
271
272 static int unique_db_destroy(
273         BackendDB       *be
274 )
275 {
276         slap_overinst *on = (slap_overinst *)be->bd_info;
277
278         if ( on->on_bi.bi_private ) {
279                 ch_free( on->on_bi.bi_private );
280                 on->on_bi.bi_private = NULL;
281         }
282         return 0;
283 }
284
285 /*
286 ** mostly, just print the init message;
287 **
288 */
289
290 static int
291 unique_open(
292         BackendDB *be
293 )
294 {
295         slap_overinst *on       = (slap_overinst *)be->bd_info;
296         unique_data *ud         = on->on_bi.bi_private;
297         ud->message             = "_open";
298
299         Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
300
301         return(0);
302 }
303
304
305 /*
306 ** foreach configured attribute:
307 **      free it;
308 ** free our basedn;
309 **
310 */
311
312 static int
313 unique_close(
314         BackendDB *be
315 )
316 {
317         slap_overinst *on       = (slap_overinst *) be->bd_info;
318         unique_data *ud         = on->on_bi.bi_private;
319         unique_attrs *ii, *ij;
320         ud->message             = "_close";
321
322         Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
323
324         for(ii = ud->attrs; ii; ii = ij) {
325                 ij = ii->next;
326                 ch_free(ii);
327         }
328         ud->attrs = NULL;
329
330         for(ii = ud->ignore; ii; ii = ij) {
331                 ij = ii->next;
332                 ch_free(ii);
333         }
334         ud->ignore = NULL;
335
336         ch_free(ud->dn.bv_val);
337         BER_BVZERO( &ud->dn );
338
339         ud->strict = 0;
340
341         return(0);
342 }
343
344
345 /*
346 ** search callback
347 **      if this is a REP_SEARCH, count++;
348 **
349 */
350
351 static int count_attr_cb(
352         Operation *op,
353         SlapReply *rs
354 )
355 {
356         unique_counter *uc;
357
358         /* because you never know */
359         if(!op || !rs) return(0);
360
361         /* Only search entries are interesting */
362         if(rs->sr_type != REP_SEARCH) return(0);
363
364         uc = op->o_callback->sc_private;
365
366         /* Ignore the current entry */
367         if ( dn_match( uc->ndn, &rs->sr_entry->e_nname )) return(0);
368
369         Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
370                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
371
372         uc->count++;
373
374         return(0);
375 }
376
377 static int count_filter_len(
378         unique_data *ud,
379         AttributeDescription *ad,
380         BerVarray b,
381         int ks
382 )
383 {
384         unique_attrs *up;
385         int i;
386
387         while ( !is_at_operational( ad->ad_type ) ) {
388                 if ( ud->ignore ) {
389                         for ( up = ud->ignore; up; up = up->next ) {
390                                 if (ad == up->attr ) {
391                                         break;
392                                 }
393                         }
394                         if ( up ) {
395                                 break;
396                         }
397                 }
398                 if ( ud->attrs ) {
399                         for ( up = ud->attrs; up; up = up->next ) {
400                                 if ( ad == up->attr ) {
401                                         break;
402                                 }
403                         }
404                         if ( !up ) {
405                                 break;
406                         }
407                 }
408                 if ( b && b[0].bv_val ) {
409                         for (i = 0; b[i].bv_val; i++ ) {
410                                 /* note: make room for filter escaping... */
411                                 ks += ( 3 * b[i].bv_len ) + ad->ad_cname.bv_len + STRLENOF( "(=)" );
412                         }
413                 } else if ( ud->strict ) {
414                         ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" ); /* (attr=*) */
415                 }
416                 break;
417         }
418         return ks;
419 }
420
421 static char *build_filter(
422         unique_data *ud,
423         AttributeDescription *ad,
424         BerVarray b,
425         char *kp,
426         void *ctx
427 )
428 {
429         unique_attrs *up;
430         int i;
431
432         while ( !is_at_operational( ad->ad_type ) ) {
433                 if ( ud->ignore ) {
434                         for ( up = ud->ignore; up; up = up->next ) {
435                                 if ( ad == up->attr ) {
436                                         break;
437                                 }
438                         }
439                         if ( up ) {
440                                 break;
441                         }
442                 }
443                 if ( ud->attrs ) {
444                         for ( up = ud->attrs; up; up = up->next ) {
445                                 if ( ad == up->attr ) {
446                                         break;
447                                 }
448                         }
449                         if ( !up ) {
450                                 break;
451                         }
452                 }
453                 if ( b && b[0].bv_val ) {
454                         for ( i = 0; b[i].bv_val; i++ ) {
455                                 struct berval   bv;
456
457                                 ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx );
458                                 kp += sprintf( kp, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val );
459                                 if ( bv.bv_val != b[i].bv_val ) {
460                                         ber_memfree_x( bv.bv_val, ctx );
461                                 }
462                         }
463                 } else if ( ud->strict ) {
464                         kp += sprintf( kp, "(%s=*)", ad->ad_cname.bv_val );
465                 }
466                 break;
467         }
468         return kp;
469 }
470
471 static int unique_search(
472         Operation *op,
473         Operation *nop,
474         SlapReply *rs,
475         char *key
476 )
477 {
478         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
479         unique_data *ud = on->on_bi.bi_private;
480         SlapReply nrs = { REP_RESULT };
481         slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
482         unique_counter uq = { NULL, 0 };
483         int rc;
484
485         nop->ors_filter = str2filter_x(nop, key);
486         ber_str2bv(key, 0, 0, &nop->ors_filterstr);
487
488         cb.sc_response  = (slap_response*)count_attr_cb;
489         cb.sc_private   = &uq;
490         nop->o_callback = &cb;
491         nop->o_tag      = LDAP_REQ_SEARCH;
492         nop->ors_scope  = LDAP_SCOPE_SUBTREE;
493         nop->ors_deref  = LDAP_DEREF_NEVER;
494         nop->ors_limit  = NULL;
495         nop->ors_slimit = SLAP_NO_LIMIT;
496         nop->ors_tlimit = SLAP_NO_LIMIT;
497         nop->ors_attrs  = slap_anlist_no_attrs;
498         nop->ors_attrsonly = 1;
499
500         uq.ndn = &op->o_req_ndn;
501
502         nop->o_req_ndn  = ud->dn;
503         nop->o_ndn = op->o_bd->be_rootndn;
504
505         nop->o_bd = on->on_info->oi_origdb;
506         rc = nop->o_bd->be_search(nop, &nrs);
507         filter_free_x(nop, nop->ors_filter);
508         op->o_tmpfree( key, op->o_tmpmemctx );
509
510         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
511                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
512                 send_ldap_error(op, rs, rc, "unique_search failed");
513                 return(rs->sr_err);
514         }
515
516         Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count, 0, 0);
517
518         if(uq.count) {
519                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
520                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
521                         "some attributes not unique");
522                 return(rs->sr_err);
523         }
524
525         return(SLAP_CB_CONTINUE);
526 }
527
528 #define ALLOC_EXTRA     16      /* extra slop */
529
530 static int unique_add(
531         Operation *op,
532         SlapReply *rs
533 )
534 {
535         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
536         unique_data *ud = on->on_bi.bi_private;
537         Operation nop = *op;
538
539         Attribute *a;
540         char *key, *kp;
541         int ks = 0;
542
543         Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);
544
545         if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
546                 return SLAP_CB_CONTINUE;
547
548 /*
549 ** count everything first;
550 ** allocate some memory;
551 ** write the search key;
552 **
553 */
554
555         if(!(a = op->ora_e->e_attrs)) {
556                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
557                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
558                         "unique_add() got null op.ora_e.e_attrs");
559                 return(rs->sr_err);
560         } else for(; a; a = a->a_next) {
561                 ks = count_filter_len(ud, a->a_desc, a->a_vals, ks);
562         }
563
564         if ( !ks )
565                 return SLAP_CB_CONTINUE;
566
567         ks += ALLOC_EXTRA;
568         key = op->o_tmpalloc(ks, op->o_tmpmemctx);
569
570         kp = key + sprintf(key, "(|");
571
572         for(a = op->ora_e->e_attrs; a; a = a->a_next) {
573                 kp = build_filter(ud, a->a_desc, a->a_vals, kp, op->o_tmpmemctx);
574         }
575
576         sprintf(kp, ")");
577
578         Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);
579
580         return unique_search(op, &nop, rs, key);
581 }
582
583
584 static int unique_modify(
585         Operation *op,
586         SlapReply *rs
587 )
588 {
589         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
590         unique_data *ud = on->on_bi.bi_private;
591         Operation nop = *op;
592
593         Modifications *m;
594         char *key, *kp;
595         int ks = 0;
596
597         Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
598
599         if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
600                 return SLAP_CB_CONTINUE;
601
602 /*
603 ** count everything first;
604 ** allocate some memory;
605 ** write the search key;
606 **
607 */
608
609         if(!(m = op->orm_modlist)) {
610                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
611                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
612                         "unique_modify() got null op.orm_modlist");
613                 return(rs->sr_err);
614         } else for(; m; m = m->sml_next) {
615                 if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
616                 ks = count_filter_len(ud, m->sml_desc, m->sml_values, ks);
617         }
618
619         if ( !ks )
620                 return SLAP_CB_CONTINUE;
621
622         ks += ALLOC_EXTRA;
623         key = op->o_tmpalloc(ks, op->o_tmpmemctx);
624
625         kp = key + sprintf(key, "(|");
626
627         for(m = op->orm_modlist; m; m = m->sml_next) {
628                 if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
629                 kp = build_filter(ud, m->sml_desc, m->sml_values, kp, op->o_tmpmemctx);
630         }
631
632         sprintf(kp, ")");
633
634         Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
635
636         return unique_search(op, &nop, rs, key);
637 }
638
639
640 static int unique_modrdn(
641         Operation *op,
642         SlapReply *rs
643 )
644 {
645         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
646         unique_data *ud = on->on_bi.bi_private;
647         Operation nop = *op;
648
649         char *key, *kp;
650         int i, ks = 0;
651         LDAPRDN newrdn;
652         struct berval bv[2];
653
654         Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
655                 op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
656
657         if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ) && 
658                 (!op->orr_nnewSup || !dnIsSuffix( op->orr_nnewSup, &ud->dn )))
659                 return SLAP_CB_CONTINUE;
660
661         if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
662                 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
663                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
664                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
665                         "unknown type(s) used in RDN");
666                 return(rs->sr_err);
667         }
668         for(i = 0; newrdn[i]; i++) {
669                 AttributeDescription *ad = NULL;
670                 if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
671                         ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
672                         rs->sr_err = LDAP_INVALID_SYNTAX;
673                         send_ldap_result( op, rs );
674                         return(rs->sr_err);
675                 }
676                 newrdn[i]->la_private = ad;
677         }
678
679         bv[1].bv_val = NULL;
680         bv[1].bv_len = 0;
681
682         for(i = 0; newrdn[i]; i++) {
683                 bv[0] = newrdn[i]->la_value;
684                 ks = count_filter_len(ud, newrdn[i]->la_private, bv, ks);
685         }
686
687         if ( !ks )
688                 return SLAP_CB_CONTINUE;
689
690         ks += ALLOC_EXTRA;
691         key = op->o_tmpalloc(ks, op->o_tmpmemctx);
692         kp = key + sprintf(key, "(|");
693
694         for(i = 0; newrdn[i]; i++) {
695                 bv[0] = newrdn[i]->la_value;
696                 kp = build_filter(ud, newrdn[i]->la_private, bv, kp, op->o_tmpmemctx);
697         }
698
699         sprintf(kp, ")");
700
701         Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);
702
703         return unique_search(op, &nop, rs, key);
704 }
705
706 /*
707 ** init_module is last so the symbols resolve "for free" --
708 ** it expects to be called automagically during dynamic module initialization
709 */
710
711 int unique_initialize() {
712         int rc;
713
714         /* statically declared just after the #includes at top */
715         unique.on_bi.bi_type = "unique";
716         unique.on_bi.bi_db_init = unique_db_init;
717         unique.on_bi.bi_db_destroy = unique_db_destroy;
718         unique.on_bi.bi_db_open = unique_open;
719         unique.on_bi.bi_db_close = unique_close;
720         unique.on_bi.bi_op_add = unique_add;
721         unique.on_bi.bi_op_modify = unique_modify;
722         unique.on_bi.bi_op_modrdn = unique_modrdn;
723         unique.on_bi.bi_op_delete = NULL;
724
725         unique.on_bi.bi_cf_ocs = uniqueocs;
726         rc = config_register_schema( uniquecfg, uniqueocs );
727         if ( rc ) return rc;
728
729         return(overlay_register(&unique));
730 }
731
732 #if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
733 int init_module(int argc, char *argv[]) {
734         return unique_initialize();
735 }
736 #endif
737
738 #endif /* SLAPD_OVER_UNIQUE */