xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/variant/variant.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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