1 /* $NetBSD: variant.c,v 1.2 2021/08/14 16:14:54 christos Exp $ */
2
3 /* variant.c - variant overlay */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2016-2021 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 developed in 2016-2017 by Ondřej Kuzník for Symas Corp.
19 */
20
21 #include <sys/cdefs.h>
22 __RCSID("$NetBSD: variant.c,v 1.2 2021/08/14 16:14:54 christos Exp $");
23
24 #include "portable.h"
25
26 #ifdef SLAPD_OVER_VARIANT
27
28 #include "slap.h"
29 #include "slap-config.h"
30 #include "ldap_queue.h"
31
32 typedef enum variant_type_t {
33 VARIANT_INFO_PLAIN = 1 << 0,
34 VARIANT_INFO_REGEX = 1 << 1,
35
36 VARIANT_INFO_ALL = ~0
37 } variant_type_t;
38
39 typedef struct variant_info_t {
40 int passReplication;
41 LDAP_STAILQ_HEAD(variant_list, variantEntry_info) variants, regex_variants;
42 } variant_info_t;
43
44 typedef struct variantEntry_info {
45 variant_info_t *ov;
46 struct berval dn;
47 variant_type_t type;
48 regex_t *regex;
49 LDAP_SLIST_HEAD(attribute_list, variantAttr_info) attributes;
50 LDAP_STAILQ_ENTRY(variantEntry_info) next;
51 } variantEntry_info;
52
53 typedef struct variantAttr_info {
54 variantEntry_info *variant;
55 struct berval dn;
56 AttributeDescription *attr, *alternative;
57 LDAP_SLIST_ENTRY(variantAttr_info) next;
58 } variantAttr_info;
59
60 static int
variant_build_dn(Operation * op,variantAttr_info * vai,int nmatch,regmatch_t * pmatch,struct berval * out)61 variant_build_dn(
62 Operation *op,
63 variantAttr_info *vai,
64 int nmatch,
65 regmatch_t *pmatch,
66 struct berval *out )
67 {
68 struct berval dn, *ndn = &op->o_req_ndn;
69 char *dest, *p, *prev, *end = vai->dn.bv_val + vai->dn.bv_len;
70 size_t len = vai->dn.bv_len;
71 int rc;
72
73 p = vai->dn.bv_val;
74 while ( (p = memchr( p, '$', end - p )) != NULL ) {
75 len -= 1;
76 p += 1;
77
78 if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
79 int i = *p - '0';
80
81 len += ( pmatch[i].rm_eo - pmatch[i].rm_so );
82 } else if ( *p != '$' ) {
83 /* Should have been checked at configuration time */
84 assert(0);
85 }
86 len -= 1;
87 p += 1;
88 }
89
90 dest = dn.bv_val = ch_realloc( out->bv_val, len + 1 );
91 dn.bv_len = len;
92
93 prev = vai->dn.bv_val;
94 while ( (p = memchr( prev, '$', end - prev )) != NULL ) {
95 len = p - prev;
96 AC_MEMCPY( dest, prev, len );
97 dest += len;
98 p += 1;
99
100 if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
101 int i = *p - '0';
102 len = pmatch[i].rm_eo - pmatch[i].rm_so;
103
104 AC_MEMCPY( dest, ndn->bv_val + pmatch[i].rm_so, len );
105 dest += len;
106 } else if ( *p == '$' ) {
107 *dest++ = *p;
108 }
109 prev = p + 1;
110 }
111 len = end - prev;
112 AC_MEMCPY( dest, prev, len );
113 dest += len;
114 *dest = '\0';
115
116 rc = dnNormalize( 0, NULL, NULL, &dn, out, NULL );
117 ch_free( dn.bv_val );
118
119 return rc;
120 }
121
122 static int
variant_build_entry(Operation * op,variantEntry_info * vei,struct berval * dn,Entry ** ep,int nmatch,regmatch_t * pmatch)123 variant_build_entry(
124 Operation *op,
125 variantEntry_info *vei,
126 struct berval *dn,
127 Entry **ep,
128 int nmatch,
129 regmatch_t *pmatch )
130 {
131 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
132 BackendDB *be_orig = op->o_bd, *db;
133 struct berval ndn = BER_BVNULL;
134 variantAttr_info *vai;
135 Attribute *a;
136 BerVarray nvals;
137 Entry *e;
138 unsigned int i;
139 int rc;
140
141 assert( ep );
142 assert( !*ep );
143
144 rc = overlay_entry_get_ov( op, dn, NULL, NULL, 0, &e, on );
145 if ( rc == LDAP_SUCCESS && is_entry_referral( e ) ) {
146 overlay_entry_release_ov( op, e, 0, on );
147 rc = LDAP_REFERRAL;
148 }
149
150 if ( rc != LDAP_SUCCESS ) {
151 goto done;
152 }
153
154 *ep = entry_dup( e );
155 overlay_entry_release_ov( op, e, 0, on );
156
157 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
158 if ( vei->type == VARIANT_INFO_REGEX ) {
159 rc = variant_build_dn( op, vai, nmatch, pmatch, &ndn );
160 if ( rc != LDAP_SUCCESS ) {
161 goto done;
162 }
163 } else {
164 ndn = vai->dn;
165 }
166
167 (void)attr_delete( &(*ep)->e_attrs, vai->attr );
168 op->o_bd = be_orig;
169
170 /* only select backend if not served by ours, would retrace all
171 * overlays again */
172 db = select_backend( &ndn, 0 );
173 if ( db && db != be_orig->bd_self ) {
174 op->o_bd = db;
175 rc = be_entry_get_rw( op, &ndn, NULL, vai->alternative, 0, &e );
176 } else {
177 rc = overlay_entry_get_ov(
178 op, &ndn, NULL, vai->alternative, 0, &e, on );
179 }
180
181 switch ( rc ) {
182 case LDAP_SUCCESS:
183 break;
184 case LDAP_INSUFFICIENT_ACCESS:
185 case LDAP_NO_SUCH_ATTRIBUTE:
186 case LDAP_NO_SUCH_OBJECT:
187 rc = LDAP_SUCCESS;
188 continue;
189 break;
190 default:
191 goto done;
192 break;
193 }
194
195 a = attr_find( e->e_attrs, vai->alternative );
196
197 /* back-ldif doesn't check the attribute exists in the entry before
198 * returning it */
199 if ( a ) {
200 if ( a->a_nvals ) {
201 nvals = a->a_nvals;
202 } else {
203 nvals = a->a_vals;
204 }
205
206 for ( i = 0; i < a->a_numvals; i++ ) {
207 if ( backend_access( op, e, &ndn, vai->alternative, &nvals[i],
208 ACL_READ, NULL ) != LDAP_SUCCESS ) {
209 continue;
210 }
211
212 rc = attr_merge_one( *ep, vai->attr, &a->a_vals[i], &nvals[i] );
213 if ( rc != LDAP_SUCCESS ) {
214 break;
215 }
216 }
217 }
218
219 if ( db && db != be_orig->bd_self ) {
220 be_entry_release_rw( op, e, 0 );
221 } else {
222 overlay_entry_release_ov( op, e, 0, on );
223 }
224 if ( rc != LDAP_SUCCESS ) {
225 goto done;
226 }
227 }
228
229 done:
230 op->o_bd = be_orig;
231 if ( rc != LDAP_SUCCESS && *ep ) {
232 entry_free( *ep );
233 *ep = NULL;
234 }
235 if ( vei->type == VARIANT_INFO_REGEX ) {
236 ch_free( ndn.bv_val );
237 }
238
239 return rc;
240 }
241
242 static int
variant_find_config(Operation * op,variant_info_t * ov,struct berval * ndn,int which,variantEntry_info ** veip,size_t nmatch,regmatch_t * pmatch)243 variant_find_config(
244 Operation *op,
245 variant_info_t *ov,
246 struct berval *ndn,
247 int which,
248 variantEntry_info **veip,
249 size_t nmatch,
250 regmatch_t *pmatch )
251 {
252 variantEntry_info *vei;
253
254 assert( veip );
255
256 if ( which & VARIANT_INFO_PLAIN ) {
257 int diff;
258
259 LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
260 dnMatch( &diff, 0, NULL, NULL, ndn, &vei->dn );
261 if ( diff ) continue;
262
263 *veip = vei;
264 return LDAP_SUCCESS;
265 }
266 }
267
268 if ( which & VARIANT_INFO_REGEX ) {
269 LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
270 if ( regexec( vei->regex, ndn->bv_val, nmatch, pmatch, 0 ) ) {
271 continue;
272 }
273
274 *veip = vei;
275 return LDAP_SUCCESS;
276 }
277 }
278
279 return SLAP_CB_CONTINUE;
280 }
281
282 static int
variant_op_add(Operation * op,SlapReply * rs)283 variant_op_add( Operation *op, SlapReply *rs )
284 {
285 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
286 variant_info_t *ov = on->on_bi.bi_private;
287 variantEntry_info *vei;
288 int rc;
289
290 /* Replication always uses the rootdn */
291 if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
292 be_isroot( op ) ) {
293 return SLAP_CB_CONTINUE;
294 }
295
296 Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
297 "dn=%s\n", op->o_req_ndn.bv_val );
298
299 rc = variant_find_config(
300 op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, 0, NULL );
301 if ( rc == LDAP_SUCCESS ) {
302 variantAttr_info *vai;
303
304 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
305 Attribute *a;
306 for ( a = op->ora_e->e_attrs; a; a = a->a_next ) {
307 if ( a->a_desc == vai->attr ) {
308 rc = LDAP_CONSTRAINT_VIOLATION;
309 send_ldap_error( op, rs, rc,
310 "variant: trying to add variant attributes" );
311 goto done;
312 }
313 }
314 }
315 }
316 rc = SLAP_CB_CONTINUE;
317
318 done:
319 Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
320 "finished with %d\n",
321 rc );
322 return rc;
323 }
324
325 static int
variant_op_compare(Operation * op,SlapReply * rs)326 variant_op_compare( Operation *op, SlapReply *rs )
327 {
328 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
329 variant_info_t *ov = on->on_bi.bi_private;
330 variantEntry_info *vei;
331 regmatch_t pmatch[10];
332 int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
333
334 Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
335 "dn=%s\n", op->o_req_ndn.bv_val );
336
337 rc = variant_find_config(
338 op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
339 if ( rc == LDAP_SUCCESS ) {
340 Entry *e = NULL;
341
342 rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
343 /* in case of error, just let the backend deal with the mod and the
344 * client should get a meaningful error back */
345 if ( rc != LDAP_SUCCESS ) {
346 rc = SLAP_CB_CONTINUE;
347 } else {
348 rc = slap_compare_entry( op, e, op->orc_ava );
349
350 entry_free( e );
351 e = NULL;
352 }
353 }
354
355 if ( rc != SLAP_CB_CONTINUE ) {
356 rs->sr_err = rc;
357 send_ldap_result( op, rs );
358 }
359
360 Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
361 "finished with %d\n", rc );
362 return rc;
363 }
364
365 static int
variant_cmp_op(const void * l,const void * r)366 variant_cmp_op( const void *l, const void *r )
367 {
368 const Operation *left = l, *right = r;
369 int diff;
370
371 dnMatch( &diff, 0, NULL, NULL, (struct berval *)&left->o_req_ndn,
372 (void *)&right->o_req_ndn );
373
374 return diff;
375 }
376
377 static int
variant_run_mod(void * nop,void * arg)378 variant_run_mod( void *nop, void *arg )
379 {
380 SlapReply nrs = { REP_RESULT };
381 slap_callback cb = { 0 };
382 Operation *op = nop;
383 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
384 int *rc = arg;
385
386 cb.sc_response = slap_null_cb;
387 op->o_callback = &cb;
388
389 Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
390 "running mod on dn=%s\n",
391 op->o_req_ndn.bv_val );
392 *rc = on->on_info->oi_orig->bi_op_modify( op, &nrs );
393 Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
394 "finished with %d\n", *rc );
395
396 return ( *rc != LDAP_SUCCESS );
397 }
398
399 /** Move the Modifications back to the original Op so that they can be disposed
400 * of by the original creator
401 */
402 static int
variant_reassign_mods(void * nop,void * arg)403 variant_reassign_mods( void *nop, void *arg )
404 {
405 Operation *op = nop, *orig_op = arg;
406 Modifications *mod;
407
408 assert( op->orm_modlist );
409
410 for ( mod = op->orm_modlist; mod->sml_next; mod = mod->sml_next )
411 /* get the tail mod */;
412
413 mod->sml_next = orig_op->orm_modlist;
414 orig_op->orm_modlist = op->orm_modlist;
415
416 return LDAP_SUCCESS;
417 }
418
419 void
variant_free_op(void * op)420 variant_free_op( void *op )
421 {
422 ch_free( ((Operation *)op)->o_req_ndn.bv_val );
423 ch_free( op );
424 }
425
426 static int
variant_op_mod(Operation * op,SlapReply * rs)427 variant_op_mod( Operation *op, SlapReply *rs )
428 {
429 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
430 variant_info_t *ov = on->on_bi.bi_private;
431 variantEntry_info *vei;
432 variantAttr_info *vai;
433 Avlnode *ops = NULL;
434 Entry *e = NULL;
435 Modifications *mod, *nextmod;
436 regmatch_t pmatch[10];
437 int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
438
439 /* Replication always uses the rootdn */
440 if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
441 be_isroot( op ) ) {
442 return SLAP_CB_CONTINUE;
443 }
444
445 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
446 "dn=%s\n", op->o_req_ndn.bv_val );
447
448 rc = variant_find_config(
449 op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
450 if ( rc != LDAP_SUCCESS ) {
451 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
452 "not a variant\n" );
453 rc = SLAP_CB_CONTINUE;
454 goto done;
455 }
456
457 rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
458 /* in case of error, just let the backend deal with the mod and the client
459 * should get a meaningful error back */
460 if ( rc != LDAP_SUCCESS ) {
461 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
462 "failed to retrieve entry\n" );
463 rc = SLAP_CB_CONTINUE;
464 goto done;
465 }
466
467 rc = acl_check_modlist( op, e, op->orm_modlist );
468 entry_free( e );
469
470 if ( !rc ) {
471 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
472 send_ldap_error( op, rs, rc, "" );
473 return rc;
474 }
475
476 for ( mod = op->orm_modlist; mod; mod = nextmod ) {
477 Operation needle = { .o_req_ndn = BER_BVNULL }, *nop;
478
479 nextmod = mod->sml_next;
480
481 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
482 if ( vai->attr == mod->sml_desc ) {
483 break;
484 }
485 }
486
487 if ( vai ) {
488 if ( vei->type == VARIANT_INFO_REGEX ) {
489 rc = variant_build_dn(
490 op, vai, nmatch, pmatch, &needle.o_req_ndn );
491 if ( rc != LDAP_SUCCESS ) {
492 continue;
493 }
494 } else {
495 needle.o_req_ndn = vai->dn;
496 }
497
498 nop = ldap_avl_find( ops, &needle, variant_cmp_op );
499 if ( nop == NULL ) {
500 nop = ch_calloc( 1, sizeof(Operation) );
501 *nop = *op;
502
503 ber_dupbv( &nop->o_req_ndn, &needle.o_req_ndn );
504 nop->o_req_dn = nop->o_req_ndn;
505 nop->orm_modlist = NULL;
506
507 rc = ldap_avl_insert( &ops, nop, variant_cmp_op, ldap_avl_dup_error );
508 assert( rc == 0 );
509 }
510 mod->sml_desc = vai->alternative;
511
512 op->orm_modlist = nextmod;
513 mod->sml_next = nop->orm_modlist;
514 nop->orm_modlist = mod;
515
516 if ( vei->type == VARIANT_INFO_REGEX ) {
517 ch_free( needle.o_req_ndn.bv_val );
518 }
519 }
520 }
521
522 if ( !ops ) {
523 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
524 "no variant attributes in mod\n" );
525 return SLAP_CB_CONTINUE;
526 }
527
528 /*
529 * First run original Operation
530 * This will take care of making sure the entry exists as well.
531 *
532 * FIXME?
533 * Since we cannot make the subsequent Ops atomic wrt. this one, we just
534 * let it send the response as well. After all, the changes on the main DN
535 * have finished by then
536 */
537 rc = on->on_info->oi_orig->bi_op_modify( op, rs );
538 if ( rc == LDAP_SUCCESS ) {
539 /* FIXME: if a mod fails, should we attempt to apply the rest? */
540 ldap_avl_apply( ops, variant_run_mod, &rc, -1, AVL_INORDER );
541 }
542
543 ldap_avl_apply( ops, variant_reassign_mods, op, -1, AVL_INORDER );
544 ldap_avl_free( ops, variant_free_op );
545
546 done:
547 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
548 "finished with %d\n", rc );
549 return rc;
550 }
551
552 static int
variant_search_response(Operation * op,SlapReply * rs)553 variant_search_response( Operation *op, SlapReply *rs )
554 {
555 slap_overinst *on = op->o_callback->sc_private;
556 variant_info_t *ov = on->on_bi.bi_private;
557 variantEntry_info *vei;
558 int rc;
559
560 if ( rs->sr_type == REP_RESULT ) {
561 ch_free( op->o_callback );
562 op->o_callback = NULL;
563 }
564
565 if ( rs->sr_type != REP_SEARCH ) {
566 return SLAP_CB_CONTINUE;
567 }
568
569 rc = variant_find_config(
570 op, ov, &rs->sr_entry->e_nname, VARIANT_INFO_PLAIN, &vei, 0, NULL );
571 if ( rc == LDAP_SUCCESS ) {
572 rs->sr_nentries--;
573 return rc;
574 }
575
576 return SLAP_CB_CONTINUE;
577 }
578
579 static int
variant_op_search(Operation * op,SlapReply * rs)580 variant_op_search( Operation *op, SlapReply *rs )
581 {
582 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
583 variant_info_t *ov = on->on_bi.bi_private;
584 variantEntry_info *vei;
585 slap_callback *cb;
586 Entry *e = NULL;
587 regmatch_t pmatch[10];
588 int variantInScope = 0, rc = SLAP_CB_CONTINUE,
589 nmatch = sizeof(pmatch) / sizeof(regmatch_t);
590
591 if ( ov->passReplication && ( op->o_sync > SLAP_CONTROL_IGNORED ) ) {
592 return SLAP_CB_CONTINUE;
593 }
594
595 Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
596 "dn=%s, scope=%d\n",
597 op->o_req_ndn.bv_val, op->ors_scope );
598
599 LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
600 if ( !dnIsSuffixScope( &vei->dn, &op->o_req_ndn, op->ors_scope ) )
601 continue;
602
603 variantInScope = 1;
604
605 rc = variant_build_entry( op, vei, &vei->dn, &e, 0, NULL );
606 if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
607 rc = SLAP_CB_CONTINUE;
608 continue;
609 } else if ( rc != LDAP_SUCCESS ) {
610 Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
611 "failed to retrieve entry: dn=%s\n",
612 vei->dn.bv_val );
613 goto done;
614 }
615
616 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
617 Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
618 "entry matched: dn=%s\n",
619 vei->dn.bv_val );
620 rs->sr_entry = e;
621 rs->sr_attrs = op->ors_attrs;
622 rc = send_search_entry( op, rs );
623 }
624 entry_free( e );
625 e = NULL;
626 }
627
628 /* Three options:
629 * - the entry has been handled above, in that case vei->type is VARIANT_INFO_PLAIN
630 * - the entry matches a regex, use the first one and we're finished
631 * - no configuration matches entry - do nothing
632 */
633 if ( op->ors_scope == LDAP_SCOPE_BASE &&
634 variant_find_config( op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei,
635 nmatch, pmatch ) == LDAP_SUCCESS &&
636 vei->type == VARIANT_INFO_REGEX ) {
637 rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
638 if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
639 rc = SLAP_CB_CONTINUE;
640 } else if ( rc != LDAP_SUCCESS ) {
641 Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
642 "failed to retrieve entry: dn=%s\n",
643 vei->dn.bv_val );
644 goto done;
645 } else {
646 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
647 Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
648 "entry matched: dn=%s\n",
649 vei->dn.bv_val );
650 rs->sr_entry = e;
651 rs->sr_attrs = op->ors_attrs;
652 rc = send_search_entry( op, rs );
653 }
654 entry_free( e );
655 e = NULL;
656 goto done;
657 }
658 }
659 rc = SLAP_CB_CONTINUE;
660
661 if ( variantInScope ) {
662 cb = ch_calloc( 1, sizeof(slap_callback) );
663 cb->sc_private = on;
664 cb->sc_response = variant_search_response;
665 cb->sc_next = op->o_callback;
666
667 op->o_callback = cb;
668 }
669
670 done:
671 if ( rc != SLAP_CB_CONTINUE ) {
672 rs->sr_err = (rc == LDAP_SUCCESS) ? rc : LDAP_OTHER;
673 send_ldap_result( op, rs );
674 }
675 Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
676 "finished with %d\n", rc );
677 return rc;
678 }
679
680 /* Configuration */
681
682 static ConfigLDAPadd variant_ldadd;
683 static ConfigLDAPadd variant_regex_ldadd;
684 static ConfigLDAPadd variant_attr_ldadd;
685
686 static ConfigDriver variant_set_dn;
687 static ConfigDriver variant_set_regex;
688 static ConfigDriver variant_set_alt_dn;
689 static ConfigDriver variant_set_alt_pattern;
690 static ConfigDriver variant_set_attribute;
691 static ConfigDriver variant_add_alt_attr;
692 static ConfigDriver variant_add_alt_attr_regex;
693
694 static ConfigCfAdd variant_cfadd;
695
696 enum
697 {
698 VARIANT_ATTR = 1,
699 VARIANT_ATTR_ALT,
700
701 VARIANT_LAST,
702 };
703
704 static ConfigTable variant_cfg[] = {
705 { "passReplication", "on|off", 2, 2, 0,
706 ARG_ON_OFF|ARG_OFFSET,
707 (void *)offsetof( variant_info_t, passReplication ),
708 "( OLcfgOvAt:FIXME.1 NAME 'olcVariantPassReplication' "
709 "DESC 'Whether to let searches with replication control "
710 "pass unmodified' "
711 "SYNTAX OMsBoolean "
712 "SINGLE-VALUE )",
713 NULL, NULL
714 },
715 { "variantDN", "dn", 2, 2, 0,
716 ARG_DN|ARG_QUOTE|ARG_MAGIC,
717 variant_set_dn,
718 "( OLcfgOvAt:FIXME.2 NAME 'olcVariantEntry' "
719 "DESC 'DN of the variant entry' "
720 "EQUALITY distinguishedNameMatch "
721 "SYNTAX OMsDN "
722 "SINGLE-VALUE )",
723 NULL, NULL
724 },
725 { "variantRegex", "regex", 2, 2, 0,
726 ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
727 variant_set_regex,
728 "( OLcfgOvAt:FIXME.6 NAME 'olcVariantEntryRegex' "
729 "DESC 'Pattern for the variant entry' "
730 "EQUALITY caseExactMatch "
731 "SYNTAX OMsDirectoryString "
732 "SINGLE-VALUE )",
733 NULL, NULL
734 },
735 /* These have no equivalent in slapd.conf */
736 { "", NULL, 2, 2, 0,
737 ARG_STRING|ARG_MAGIC|VARIANT_ATTR,
738 variant_set_attribute,
739 "( OLcfgOvAt:FIXME.3 NAME 'olcVariantVariantAttribute' "
740 "DESC 'Attribute to fill in the entry' "
741 "EQUALITY caseIgnoreMatch "
742 "SYNTAX OMsDirectoryString "
743 "SINGLE-VALUE )",
744 NULL, NULL
745 },
746 { "", NULL, 2, 2, 0,
747 ARG_STRING|ARG_MAGIC|VARIANT_ATTR_ALT,
748 variant_set_attribute,
749 "( OLcfgOvAt:FIXME.4 NAME 'olcVariantAlternativeAttribute' "
750 "DESC 'Attribute to take from the alternative entry' "
751 "EQUALITY caseIgnoreMatch "
752 "SYNTAX OMsDirectoryString "
753 "SINGLE-VALUE )",
754 NULL, NULL
755 },
756 { "", NULL, 2, 2, 0,
757 ARG_DN|ARG_QUOTE|ARG_MAGIC,
758 variant_set_alt_dn,
759 "( OLcfgOvAt:FIXME.5 NAME 'olcVariantAlternativeEntry' "
760 "DESC 'DN of the alternative entry' "
761 "EQUALITY distinguishedNameMatch "
762 "SYNTAX OMsDN "
763 "SINGLE-VALUE )",
764 NULL, NULL
765 },
766 { "", NULL, 2, 2, 0,
767 ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
768 variant_set_alt_pattern,
769 "( OLcfgOvAt:FIXME.7 NAME 'olcVariantAlternativeEntryPattern' "
770 "DESC 'Replacement pattern to locate the alternative entry' "
771 "EQUALITY caseExactMatch "
772 "SYNTAX OMsDirectoryString "
773 "SINGLE-VALUE )",
774 NULL, NULL
775 },
776 /* slapd.conf alternatives for the four above */
777 { "variantSpec", "attr attr2 dn", 4, 4, 0,
778 ARG_QUOTE|ARG_MAGIC,
779 variant_add_alt_attr,
780 NULL, NULL, NULL
781 },
782 { "variantRegexSpec", "attr attr2 pattern", 4, 4, 0,
783 ARG_QUOTE|ARG_MAGIC,
784 variant_add_alt_attr_regex,
785 NULL, NULL, NULL
786 },
787
788 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
789 };
790
791 static ConfigOCs variant_ocs[] = {
792 { "( OLcfgOvOc:FIXME.1 "
793 "NAME 'olcVariantConfig' "
794 "DESC 'Variant overlay configuration' "
795 "SUP olcOverlayConfig "
796 "MAY ( olcVariantPassReplication ) )",
797 Cft_Overlay, variant_cfg, NULL, variant_cfadd },
798 { "( OLcfgOvOc:FIXME.2 "
799 "NAME 'olcVariantVariant' "
800 "DESC 'Variant configuration' "
801 "MUST ( olcVariantEntry ) "
802 "MAY ( name ) "
803 "SUP top "
804 "STRUCTURAL )",
805 Cft_Misc, variant_cfg, variant_ldadd },
806 { "( OLcfgOvOc:FIXME.3 "
807 "NAME 'olcVariantAttribute' "
808 "DESC 'Variant attribute description' "
809 "MUST ( olcVariantVariantAttribute $ "
810 "olcVariantAlternativeAttribute $ "
811 "olcVariantAlternativeEntry "
812 ") "
813 "MAY name "
814 "SUP top "
815 "STRUCTURAL )",
816 Cft_Misc, variant_cfg, variant_attr_ldadd },
817 { "( OLcfgOvOc:FIXME.4 "
818 "NAME 'olcVariantRegex' "
819 "DESC 'Variant configuration' "
820 "MUST ( olcVariantEntryRegex ) "
821 "MAY ( name ) "
822 "SUP top "
823 "STRUCTURAL )",
824 Cft_Misc, variant_cfg, variant_regex_ldadd },
825 { "( OLcfgOvOc:FIXME.5 "
826 "NAME 'olcVariantAttributePattern' "
827 "DESC 'Variant attribute description' "
828 "MUST ( olcVariantVariantAttribute $ "
829 "olcVariantAlternativeAttribute $ "
830 "olcVariantAlternativeEntryPattern "
831 ") "
832 "MAY name "
833 "SUP top "
834 "STRUCTURAL )",
835 Cft_Misc, variant_cfg, variant_attr_ldadd },
836
837 { NULL, 0, NULL }
838 };
839
840 static int
variant_set_dn(ConfigArgs * ca)841 variant_set_dn( ConfigArgs *ca )
842 {
843 variantEntry_info *vei2, *vei = ca->ca_private;
844 slap_overinst *on = (slap_overinst *)ca->bi;
845 variant_info_t *ov = on->on_bi.bi_private;
846 int diff;
847
848 if ( ca->op == SLAP_CONFIG_EMIT ) {
849 value_add_one( &ca->rvalue_vals, &vei->dn );
850 return LDAP_SUCCESS;
851 } else if ( ca->op == LDAP_MOD_DELETE ) {
852 ber_memfree( vei->dn.bv_val );
853 BER_BVZERO( &vei->dn );
854 return LDAP_SUCCESS;
855 }
856
857 if ( !vei ) {
858 vei = ch_calloc( 1, sizeof(variantEntry_info) );
859 vei->ov = ov;
860 vei->type = VARIANT_INFO_PLAIN;
861 LDAP_SLIST_INIT(&vei->attributes);
862 LDAP_STAILQ_ENTRY_INIT(vei, next);
863 LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
864
865 ca->ca_private = vei;
866 }
867 vei->dn = ca->value_ndn;
868 ber_memfree( ca->value_dn.bv_val );
869
870 /* Each DN should only be listed once */
871 LDAP_STAILQ_FOREACH( vei2, &vei->ov->variants, next ) {
872 if ( vei == vei2 ) continue;
873
874 dnMatch( &diff, 0, NULL, NULL, &vei->dn, &vei2->dn );
875 if ( !diff ) {
876 ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
877 return ca->reply.err;
878 }
879 }
880
881 return LDAP_SUCCESS;
882 }
883
884 static int
variant_set_regex(ConfigArgs * ca)885 variant_set_regex( ConfigArgs *ca )
886 {
887 variantEntry_info *vei2, *vei = ca->ca_private;
888 slap_overinst *on = (slap_overinst *)ca->bi;
889 variant_info_t *ov = on->on_bi.bi_private;
890
891 if ( ca->op == SLAP_CONFIG_EMIT ) {
892 ca->value_bv = vei->dn;
893 return LDAP_SUCCESS;
894 } else if ( ca->op == LDAP_MOD_DELETE ) {
895 ber_memfree( vei->dn.bv_val );
896 BER_BVZERO( &vei->dn );
897 regfree( vei->regex );
898 return LDAP_SUCCESS;
899 }
900
901 if ( !vei ) {
902 vei = ch_calloc( 1, sizeof(variantEntry_info) );
903 vei->ov = ov;
904 vei->type = VARIANT_INFO_REGEX;
905 LDAP_SLIST_INIT(&vei->attributes);
906 LDAP_STAILQ_ENTRY_INIT(vei, next);
907 LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
908
909 ca->ca_private = vei;
910 }
911 vei->dn = ca->value_bv;
912
913 /* Each regex should only be listed once */
914 LDAP_STAILQ_FOREACH( vei2, &vei->ov->regex_variants, next ) {
915 if ( vei == vei2 ) continue;
916
917 if ( !ber_bvcmp( &ca->value_bv, &vei2->dn ) ) {
918 ch_free( vei );
919 ca->ca_private = NULL;
920 ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
921 return ca->reply.err;
922 }
923 }
924
925 vei->regex = ch_calloc( 1, sizeof(regex_t) );
926 if ( regcomp( vei->regex, vei->dn.bv_val, REG_EXTENDED ) ) {
927 ch_free( vei->regex );
928 ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
929 return ca->reply.err;
930 }
931
932 return LDAP_SUCCESS;
933 }
934
935 static int
variant_set_alt_dn(ConfigArgs * ca)936 variant_set_alt_dn( ConfigArgs *ca )
937 {
938 variantAttr_info *vai = ca->ca_private;
939
940 if ( ca->op == SLAP_CONFIG_EMIT ) {
941 value_add_one( &ca->rvalue_vals, &vai->dn );
942 return LDAP_SUCCESS;
943 } else if ( ca->op == LDAP_MOD_DELETE ) {
944 ber_memfree( vai->dn.bv_val );
945 BER_BVZERO( &vai->dn );
946 return LDAP_SUCCESS;
947 }
948
949 vai->dn = ca->value_ndn;
950 ber_memfree( ca->value_dn.bv_val );
951
952 return LDAP_SUCCESS;
953 }
954
955 static int
variant_set_alt_pattern(ConfigArgs * ca)956 variant_set_alt_pattern( ConfigArgs *ca )
957 {
958 variantAttr_info *vai = ca->ca_private;
959 char *p = ca->value_bv.bv_val,
960 *end = ca->value_bv.bv_val + ca->value_bv.bv_len;
961
962 if ( ca->op == SLAP_CONFIG_EMIT ) {
963 ca->value_bv = vai->dn;
964 return LDAP_SUCCESS;
965 } else if ( ca->op == LDAP_MOD_DELETE ) {
966 ber_memfree( vai->dn.bv_val );
967 BER_BVZERO( &vai->dn );
968 return LDAP_SUCCESS;
969 }
970
971 while ( (p = memchr( p, '$', end - p )) != NULL ) {
972 p += 1;
973
974 if ( ( ( *p >= '0' ) && ( *p <= '9' ) ) || ( *p == '$' ) ) {
975 p += 1;
976 } else {
977 Debug( LDAP_DEBUG_ANY, "variant_set_alt_pattern: "
978 "invalid replacement pattern supplied '%s'\n",
979 ca->value_bv.bv_val );
980 ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
981 return ca->reply.err;
982 }
983 }
984
985 vai->dn = ca->value_bv;
986
987 return LDAP_SUCCESS;
988 }
989
990 static int
variant_set_attribute(ConfigArgs * ca)991 variant_set_attribute( ConfigArgs *ca )
992 {
993 variantAttr_info *vai2, *vai = ca->ca_private;
994 char *s = ca->value_string;
995 const char *text;
996 AttributeDescription **ad;
997 int rc;
998
999 if ( ca->type == VARIANT_ATTR ) {
1000 ad = &vai->attr;
1001 } else {
1002 ad = &vai->alternative;
1003 }
1004
1005 if ( ca->op == SLAP_CONFIG_EMIT ) {
1006 ca->value_string = ch_strdup( (*ad)->ad_cname.bv_val );
1007 return LDAP_SUCCESS;
1008 } else if ( ca->op == LDAP_MOD_DELETE ) {
1009 *ad = NULL;
1010 return LDAP_SUCCESS;
1011 }
1012
1013 if ( *s == '{' ) {
1014 s = strchr( s, '}' );
1015 if ( !s ) {
1016 ca->reply.err = LDAP_UNDEFINED_TYPE;
1017 return ca->reply.err;
1018 }
1019 s += 1;
1020 }
1021
1022 rc = slap_str2ad( s, ad, &text );
1023 ber_memfree( ca->value_string );
1024 if ( rc ) {
1025 return rc;
1026 }
1027
1028 /* Both attributes have to share the same syntax */
1029 if ( vai->attr && vai->alternative &&
1030 vai->attr->ad_type->sat_syntax !=
1031 vai->alternative->ad_type->sat_syntax ) {
1032 ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
1033 return ca->reply.err;
1034 }
1035
1036 if ( ca->type == VARIANT_ATTR ) {
1037 /* Each attribute should only be listed once */
1038 LDAP_SLIST_FOREACH( vai2, &vai->variant->attributes, next ) {
1039 if ( vai == vai2 ) continue;
1040 if ( vai->attr == vai2->attr ) {
1041 ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
1042 return ca->reply.err;
1043 }
1044 }
1045 }
1046
1047 return LDAP_SUCCESS;
1048 }
1049
1050 static int
variant_add_alt_attr(ConfigArgs * ca)1051 variant_add_alt_attr( ConfigArgs *ca )
1052 {
1053 slap_overinst *on = (slap_overinst *)ca->bi;
1054 variant_info_t *ov = on->on_bi.bi_private;
1055 variantEntry_info *vei =
1056 LDAP_STAILQ_LAST( &ov->variants, variantEntry_info, next );
1057 variantAttr_info *vai;
1058 struct berval dn, ndn;
1059 int rc;
1060
1061 vai = ch_calloc( 1, sizeof(variantAttr_info) );
1062 vai->variant = vei;
1063 LDAP_SLIST_ENTRY_INIT( vai, next );
1064 ca->ca_private = vai;
1065
1066 ca->value_string = ch_strdup( ca->argv[1] );
1067 ca->type = VARIANT_ATTR;
1068 rc = variant_set_attribute( ca );
1069 if ( rc != LDAP_SUCCESS ) {
1070 goto done;
1071 }
1072
1073 ca->value_string = ch_strdup( ca->argv[2] );
1074 ca->type = VARIANT_ATTR_ALT;
1075 rc = variant_set_attribute( ca );
1076 if ( rc != LDAP_SUCCESS ) {
1077 goto done;
1078 }
1079
1080 dn.bv_val = ca->argv[3];
1081 dn.bv_len = strlen( dn.bv_val );
1082 rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
1083 if ( rc != LDAP_SUCCESS ) {
1084 goto done;
1085 }
1086
1087 ca->type = 0;
1088 BER_BVZERO( &ca->value_dn );
1089 ca->value_ndn = ndn;
1090 rc = variant_set_alt_dn( ca );
1091 if ( rc != LDAP_SUCCESS ) {
1092 ch_free( ndn.bv_val );
1093 goto done;
1094 }
1095
1096 done:
1097 if ( rc == LDAP_SUCCESS ) {
1098 LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
1099 } else {
1100 ca->reply.err = rc;
1101 }
1102
1103 return rc;
1104 }
1105
1106 static int
variant_add_alt_attr_regex(ConfigArgs * ca)1107 variant_add_alt_attr_regex( ConfigArgs *ca )
1108 {
1109 slap_overinst *on = (slap_overinst *)ca->bi;
1110 variant_info_t *ov = on->on_bi.bi_private;
1111 variantEntry_info *vei =
1112 LDAP_STAILQ_LAST( &ov->regex_variants, variantEntry_info, next );
1113 variantAttr_info *vai;
1114 int rc;
1115
1116 vai = ch_calloc( 1, sizeof(variantAttr_info) );
1117 vai->variant = vei;
1118 LDAP_SLIST_ENTRY_INIT( vai, next );
1119 ca->ca_private = vai;
1120
1121 ca->value_string = ch_strdup( ca->argv[1] );
1122 ca->type = VARIANT_ATTR;
1123 rc = variant_set_attribute( ca );
1124 if ( rc != LDAP_SUCCESS ) {
1125 goto done;
1126 }
1127
1128 ca->value_string = ch_strdup( ca->argv[2] );
1129 ca->type = VARIANT_ATTR_ALT;
1130 rc = variant_set_attribute( ca );
1131 if ( rc != LDAP_SUCCESS ) {
1132 goto done;
1133 }
1134
1135 ca->type = 0;
1136 ber_str2bv( ca->argv[3], 0, 1, &ca->value_bv );
1137 rc = variant_set_alt_pattern( ca );
1138 if ( rc != LDAP_SUCCESS ) {
1139 goto done;
1140 }
1141
1142 done:
1143 if ( rc == LDAP_SUCCESS ) {
1144 LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
1145 } else {
1146 ca->reply.err = rc;
1147 }
1148
1149 return rc;
1150 }
1151
1152 static int
variant_ldadd_cleanup(ConfigArgs * ca)1153 variant_ldadd_cleanup( ConfigArgs *ca )
1154 {
1155 variantEntry_info *vei = ca->ca_private;
1156 slap_overinst *on = (slap_overinst *)ca->bi;
1157 variant_info_t *ov = on->on_bi.bi_private;
1158
1159 if ( ca->reply.err != LDAP_SUCCESS ) {
1160 assert( LDAP_SLIST_EMPTY(&vei->attributes) );
1161 ch_free( vei );
1162 return LDAP_SUCCESS;
1163 }
1164
1165 if ( vei->type == VARIANT_INFO_PLAIN ) {
1166 LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
1167 } else {
1168 LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
1169 }
1170
1171 return LDAP_SUCCESS;
1172 }
1173
1174 static int
variant_ldadd(CfEntryInfo * cei,Entry * e,ConfigArgs * ca)1175 variant_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
1176 {
1177 slap_overinst *on;
1178 variant_info_t *ov;
1179 variantEntry_info *vei;
1180
1181 if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
1182 cei->ce_bi->bi_cf_ocs != variant_ocs )
1183 return LDAP_CONSTRAINT_VIOLATION;
1184
1185 on = (slap_overinst *)cei->ce_bi;
1186 ov = on->on_bi.bi_private;
1187
1188 vei = ch_calloc( 1, sizeof(variantEntry_info) );
1189 vei->ov = ov;
1190 vei->type = VARIANT_INFO_PLAIN;
1191 LDAP_SLIST_INIT(&vei->attributes);
1192 LDAP_STAILQ_ENTRY_INIT(vei, next);
1193
1194 ca->bi = cei->ce_bi;
1195 ca->ca_private = vei;
1196 config_push_cleanup( ca, variant_ldadd_cleanup );
1197 /* config_push_cleanup is only run in the case of online config but we use it to
1198 * save the new config when done with the entry */
1199 ca->lineno = 0;
1200
1201 return LDAP_SUCCESS;
1202 }
1203
1204 static int
variant_regex_ldadd(CfEntryInfo * cei,Entry * e,ConfigArgs * ca)1205 variant_regex_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
1206 {
1207 slap_overinst *on;
1208 variant_info_t *ov;
1209 variantEntry_info *vei;
1210
1211 if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
1212 cei->ce_bi->bi_cf_ocs != variant_ocs )
1213 return LDAP_CONSTRAINT_VIOLATION;
1214
1215 on = (slap_overinst *)cei->ce_bi;
1216 ov = on->on_bi.bi_private;
1217
1218 vei = ch_calloc( 1, sizeof(variantEntry_info) );
1219 vei->ov = ov;
1220 vei->type = VARIANT_INFO_REGEX;
1221 LDAP_SLIST_INIT(&vei->attributes);
1222 LDAP_STAILQ_ENTRY_INIT(vei, next);
1223
1224 ca->bi = cei->ce_bi;
1225 ca->ca_private = vei;
1226 config_push_cleanup( ca, variant_ldadd_cleanup );
1227 /* config_push_cleanup is only run in the case of online config but we use it to
1228 * save the new config when done with the entry */
1229 ca->lineno = 0;
1230
1231 return LDAP_SUCCESS;
1232 }
1233
1234 static int
variant_attr_ldadd_cleanup(ConfigArgs * ca)1235 variant_attr_ldadd_cleanup( ConfigArgs *ca )
1236 {
1237 variantAttr_info *vai = ca->ca_private;
1238 variantEntry_info *vei = vai->variant;
1239
1240 if ( ca->reply.err != LDAP_SUCCESS ) {
1241 ch_free( vai );
1242 return LDAP_SUCCESS;
1243 }
1244
1245 LDAP_SLIST_INSERT_HEAD(&vei->attributes, vai, next);
1246
1247 return LDAP_SUCCESS;
1248 }
1249
1250 static int
variant_attr_ldadd(CfEntryInfo * cei,Entry * e,ConfigArgs * ca)1251 variant_attr_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
1252 {
1253 variantEntry_info *vei;
1254 variantAttr_info *vai;
1255 CfEntryInfo *parent = cei->ce_parent;
1256
1257 if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
1258 parent->ce_bi->bi_cf_ocs != variant_ocs )
1259 return LDAP_CONSTRAINT_VIOLATION;
1260
1261 vei = (variantEntry_info *)cei->ce_private;
1262
1263 vai = ch_calloc( 1, sizeof(variantAttr_info) );
1264 vai->variant = vei;
1265 LDAP_SLIST_ENTRY_INIT(vai, next);
1266
1267 ca->ca_private = vai;
1268 config_push_cleanup( ca, variant_attr_ldadd_cleanup );
1269 /* config_push_cleanup is only run in the case of online config but we use it to
1270 * save the new config when done with the entry */
1271 ca->lineno = 0;
1272
1273 return LDAP_SUCCESS;
1274 }
1275
1276 static int
variant_cfadd(Operation * op,SlapReply * rs,Entry * p,ConfigArgs * ca)1277 variant_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1278 {
1279 slap_overinst *on = (slap_overinst *)ca->bi;
1280 variant_info_t *ov = on->on_bi.bi_private;
1281 variantEntry_info *vei;
1282 variantAttr_info *vai;
1283 Entry *e;
1284 struct berval rdn;
1285 int i = 0;
1286
1287 LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
1288 int j = 0;
1289 rdn.bv_len = snprintf(
1290 ca->cr_msg, sizeof(ca->cr_msg), "name={%d}variant", i++ );
1291 rdn.bv_val = ca->cr_msg;
1292
1293 ca->ca_private = vei;
1294 e = config_build_entry(
1295 op, rs, p->e_private, ca, &rdn, &variant_ocs[1], NULL );
1296 assert( e );
1297
1298 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
1299 rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
1300 "olcVariantVariantAttribute={%d}%s", j++,
1301 vai->attr->ad_cname.bv_val );
1302 rdn.bv_val = ca->cr_msg;
1303
1304 ca->ca_private = vai;
1305 config_build_entry(
1306 op, rs, e->e_private, ca, &rdn, &variant_ocs[2], NULL );
1307 }
1308 }
1309
1310 LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
1311 int j = 0;
1312 rdn.bv_len = snprintf(
1313 ca->cr_msg, sizeof(ca->cr_msg), "name={%d}regex", i++ );
1314 rdn.bv_val = ca->cr_msg;
1315
1316 ca->ca_private = vei;
1317 e = config_build_entry(
1318 op, rs, p->e_private, ca, &rdn, &variant_ocs[3], NULL );
1319 assert( e );
1320
1321 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
1322 rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
1323 "olcVariantVariantAttribute={%d}%s", j++,
1324 vai->attr->ad_cname.bv_val );
1325 rdn.bv_val = ca->cr_msg;
1326
1327 ca->ca_private = vai;
1328 config_build_entry(
1329 op, rs, e->e_private, ca, &rdn, &variant_ocs[4], NULL );
1330 }
1331 }
1332 return LDAP_SUCCESS;
1333 }
1334
1335 static slap_overinst variant;
1336
1337 static int
variant_db_init(BackendDB * be,ConfigReply * cr)1338 variant_db_init( BackendDB *be, ConfigReply *cr )
1339 {
1340 slap_overinst *on = (slap_overinst *)be->bd_info;
1341 variant_info_t *ov;
1342
1343 if ( SLAP_ISGLOBALOVERLAY(be) ) {
1344 Debug( LDAP_DEBUG_ANY, "variant overlay must be instantiated within "
1345 "a database.\n" );
1346 return 1;
1347 }
1348
1349 ov = ch_calloc( 1, sizeof(variant_info_t) );
1350 LDAP_STAILQ_INIT(&ov->variants);
1351 LDAP_STAILQ_INIT(&ov->regex_variants);
1352
1353 on->on_bi.bi_private = ov;
1354
1355 return LDAP_SUCCESS;
1356 }
1357
1358 static int
variant_db_destroy(BackendDB * be,ConfigReply * cr)1359 variant_db_destroy( BackendDB *be, ConfigReply *cr )
1360 {
1361 slap_overinst *on = (slap_overinst *)be->bd_info;
1362 variant_info_t *ov = on->on_bi.bi_private;
1363
1364 if ( ov ) {
1365 while ( !LDAP_STAILQ_EMPTY( &ov->variants ) ) {
1366 variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->variants );
1367 LDAP_STAILQ_REMOVE_HEAD( &ov->variants, next );
1368
1369 while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
1370 variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
1371 LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
1372
1373 ber_memfree( vai->dn.bv_val );
1374 ch_free( vai );
1375 }
1376 ber_memfree( vei->dn.bv_val );
1377 ch_free( vei );
1378 }
1379 while ( !LDAP_STAILQ_EMPTY( &ov->regex_variants ) ) {
1380 variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->regex_variants );
1381 LDAP_STAILQ_REMOVE_HEAD( &ov->regex_variants, next );
1382
1383 while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
1384 variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
1385 LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
1386
1387 ber_memfree( vai->dn.bv_val );
1388 ch_free( vai );
1389 }
1390 ber_memfree( vei->dn.bv_val );
1391 ch_free( vei );
1392 }
1393 ch_free( ov );
1394 }
1395
1396 return LDAP_SUCCESS;
1397 }
1398
1399 int
variant_initialize()1400 variant_initialize()
1401 {
1402 int rc;
1403
1404 variant.on_bi.bi_type = "variant";
1405 variant.on_bi.bi_db_init = variant_db_init;
1406 variant.on_bi.bi_db_destroy = variant_db_destroy;
1407
1408 variant.on_bi.bi_op_add = variant_op_add;
1409 variant.on_bi.bi_op_compare = variant_op_compare;
1410 variant.on_bi.bi_op_modify = variant_op_mod;
1411 variant.on_bi.bi_op_search = variant_op_search;
1412
1413 variant.on_bi.bi_cf_ocs = variant_ocs;
1414
1415 rc = config_register_schema( variant_cfg, variant_ocs );
1416 if ( rc ) return rc;
1417
1418 return overlay_register( &variant );
1419 }
1420
1421 #if SLAPD_OVER_VARIANT == SLAPD_MOD_DYNAMIC
1422 int
init_module(int argc,char * argv[])1423 init_module( int argc, char *argv[] )
1424 {
1425 return variant_initialize();
1426 }
1427 #endif
1428
1429 #endif /* SLAPD_OVER_VARIANT */
1430