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