1/* cr.c - content rule routines */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2011 The OpenLDAP Foundation.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
10 * Public License.
11 *
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
15 */
16
17#include "portable.h"
18
19#include <stdio.h>
20
21#include <ac/ctype.h>
22#include <ac/string.h>
23#include <ac/socket.h>
24
25#include "slap.h"
26
27struct cindexrec {
28	struct berval	cir_name;
29	ContentRule	*cir_cr;
30};
31
32static Avlnode	*cr_index = NULL;
33static LDAP_STAILQ_HEAD(CRList, ContentRule) cr_list
34	= LDAP_STAILQ_HEAD_INITIALIZER(cr_list);
35
36static int
37cr_index_cmp(
38    const void	*v_cir1,
39    const void	*v_cir2 )
40{
41	const struct cindexrec	*cir1 = v_cir1;
42	const struct cindexrec	*cir2 = v_cir2;
43	int i = cir1->cir_name.bv_len - cir2->cir_name.bv_len;
44	if (i) return i;
45	return strcasecmp( cir1->cir_name.bv_val, cir2->cir_name.bv_val );
46}
47
48static int
49cr_index_name_cmp(
50    const void	*v_name,
51    const void	*v_cir )
52{
53	const struct berval    *name = v_name;
54	const struct cindexrec *cir  = v_cir;
55	int i = name->bv_len - cir->cir_name.bv_len;
56	if (i) return i;
57	return strncasecmp( name->bv_val, cir->cir_name.bv_val, name->bv_len );
58}
59
60ContentRule *
61cr_find( const char *crname )
62{
63	struct berval bv;
64
65	bv.bv_val = (char *)crname;
66	bv.bv_len = strlen( crname );
67
68	return( cr_bvfind( &bv ) );
69}
70
71ContentRule *
72cr_bvfind( struct berval *crname )
73{
74	struct cindexrec	*cir;
75
76	cir = avl_find( cr_index, crname, cr_index_name_cmp );
77
78	if ( cir != NULL ) {
79		return( cir->cir_cr );
80	}
81
82	return( NULL );
83}
84
85static int
86cr_destroy_one( ContentRule *c )
87{
88	assert( c != NULL );
89
90	if (c->scr_auxiliaries) ldap_memfree(c->scr_auxiliaries);
91	if (c->scr_required) ldap_memfree(c->scr_required);
92	if (c->scr_allowed) ldap_memfree(c->scr_allowed);
93	if (c->scr_precluded) ldap_memfree(c->scr_precluded);
94	ldap_contentrule_free((LDAPContentRule *)c);
95
96	return 0;
97}
98
99void
100cr_destroy( void )
101{
102	ContentRule *c;
103
104	avl_free(cr_index, ldap_memfree);
105
106	while( !LDAP_STAILQ_EMPTY(&cr_list) ) {
107		c = LDAP_STAILQ_FIRST(&cr_list);
108		LDAP_STAILQ_REMOVE_HEAD(&cr_list, scr_next);
109
110		cr_destroy_one( c );
111	}
112}
113
114static int
115cr_insert(
116    ContentRule		*scr,
117    const char		**err
118)
119{
120	struct cindexrec	*cir;
121	char			**names;
122
123	assert( scr != NULL );
124
125	if ( scr->scr_oid ) {
126		cir = (struct cindexrec *)
127			ch_calloc( 1, sizeof(struct cindexrec) );
128		cir->cir_name.bv_val = scr->scr_oid;
129		cir->cir_name.bv_len = strlen( scr->scr_oid );
130		cir->cir_cr = scr;
131
132		if ( avl_insert( &cr_index, (caddr_t) cir,
133		                 cr_index_cmp, avl_dup_error ) )
134		{
135			*err = scr->scr_oid;
136			ldap_memfree(cir);
137			return SLAP_SCHERR_CR_DUP;
138		}
139
140		/* FIX: temporal consistency check */
141		assert( cr_bvfind(&cir->cir_name) != NULL );
142	}
143
144	if ( (names = scr->scr_names) ) {
145		while ( *names ) {
146			cir = (struct cindexrec *)
147				ch_calloc( 1, sizeof(struct cindexrec) );
148			cir->cir_name.bv_val = *names;
149			cir->cir_name.bv_len = strlen( *names );
150			cir->cir_cr = scr;
151
152			if ( avl_insert( &cr_index, (caddr_t) cir,
153			                 cr_index_cmp, avl_dup_error ) )
154			{
155				*err = *names;
156				ldap_memfree(cir);
157				return SLAP_SCHERR_CR_DUP;
158			}
159
160			/* FIX: temporal consistency check */
161			assert( cr_bvfind(&cir->cir_name) != NULL );
162
163			names++;
164		}
165	}
166
167	LDAP_STAILQ_INSERT_TAIL(&cr_list, scr, scr_next);
168
169	return 0;
170}
171
172static int
173cr_add_auxiliaries(
174    ContentRule		*scr,
175	int			*op,
176    const char		**err )
177{
178	int naux;
179
180	if( scr->scr_oc_oids_aux == NULL ) return 0;
181
182	for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
183		/* count them */ ;
184	}
185
186	scr->scr_auxiliaries = ch_calloc( naux+1, sizeof(ObjectClass *));
187
188	for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
189		ObjectClass *soc = scr->scr_auxiliaries[naux]
190			= oc_find(scr->scr_oc_oids_aux[naux]);
191		if ( !soc ) {
192			*err = scr->scr_oc_oids_aux[naux];
193			return SLAP_SCHERR_CLASS_NOT_FOUND;
194		}
195
196		if( soc->soc_flags & SLAP_OC_OPERATIONAL &&
197			soc != slap_schema.si_oc_extensibleObject )
198		{
199			(*op)++;
200		}
201
202		if( soc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
203			*err = scr->scr_oc_oids_aux[naux];
204			return SLAP_SCHERR_CR_BAD_AUX;
205		}
206	}
207
208	scr->scr_auxiliaries[naux] = NULL;
209	return 0;
210}
211
212static int
213cr_create_required(
214    ContentRule		*scr,
215	int			*op,
216    const char		**err )
217{
218    char		**attrs = scr->scr_at_oids_must;
219	char		**attrs1;
220	AttributeType	*sat;
221
222	if ( attrs ) {
223		attrs1 = attrs;
224		while ( *attrs1 ) {
225			sat = at_find(*attrs1);
226			if ( !sat ) {
227				*err = *attrs1;
228				return SLAP_SCHERR_ATTR_NOT_FOUND;
229			}
230
231			if( is_at_operational( sat )) (*op)++;
232
233			if ( at_find_in_list(sat, scr->scr_required) < 0) {
234				if ( at_append_to_list(sat, &scr->scr_required) ) {
235					*err = *attrs1;
236					return SLAP_SCHERR_OUTOFMEM;
237				}
238			} else {
239				*err = *attrs1;
240				return SLAP_SCHERR_CR_BAD_AT;
241			}
242			attrs1++;
243		}
244	}
245	return 0;
246}
247
248static int
249cr_create_allowed(
250    ContentRule		*scr,
251	int			*op,
252    const char		**err )
253{
254    char		**attrs = scr->scr_at_oids_may;
255	char		**attrs1;
256	AttributeType	*sat;
257
258	if ( attrs ) {
259		attrs1 = attrs;
260		while ( *attrs1 ) {
261			sat = at_find(*attrs1);
262			if ( !sat ) {
263				*err = *attrs1;
264				return SLAP_SCHERR_ATTR_NOT_FOUND;
265			}
266
267			if( is_at_operational( sat )) (*op)++;
268
269			if ( at_find_in_list(sat, scr->scr_required) < 0 &&
270				at_find_in_list(sat, scr->scr_allowed) < 0 )
271			{
272				if ( at_append_to_list(sat, &scr->scr_allowed) ) {
273					*err = *attrs1;
274					return SLAP_SCHERR_OUTOFMEM;
275				}
276			} else {
277				*err = *attrs1;
278				return SLAP_SCHERR_CR_BAD_AT;
279			}
280			attrs1++;
281		}
282	}
283	return 0;
284}
285
286static int
287cr_create_precluded(
288    ContentRule		*scr,
289	int			*op,
290    const char		**err )
291{
292    char		**attrs = scr->scr_at_oids_not;
293	char		**attrs1;
294	AttributeType	*sat;
295
296	if ( attrs ) {
297		attrs1 = attrs;
298		while ( *attrs1 ) {
299			sat = at_find(*attrs1);
300			if ( !sat ) {
301				*err = *attrs1;
302				return SLAP_SCHERR_ATTR_NOT_FOUND;
303			}
304
305			if( is_at_operational( sat )) (*op)++;
306
307			/* FIXME: should also make sure attribute type is not
308				a required attribute of the structural class or
309				any auxiliary class */
310			if ( at_find_in_list(sat, scr->scr_required) < 0 &&
311				at_find_in_list(sat, scr->scr_allowed) < 0 &&
312				at_find_in_list(sat, scr->scr_precluded) < 0 )
313			{
314				if ( at_append_to_list(sat, &scr->scr_precluded) ) {
315					*err = *attrs1;
316					return SLAP_SCHERR_OUTOFMEM;
317				}
318			} else {
319				*err = *attrs1;
320				return SLAP_SCHERR_CR_BAD_AT;
321			}
322			attrs1++;
323		}
324	}
325	return 0;
326}
327
328int
329cr_add(
330    LDAPContentRule	*cr,
331	int user,
332	ContentRule **rscr,
333    const char		**err
334)
335{
336	ContentRule	*scr;
337	int		code;
338	int		op = 0;
339	char	*oidm = NULL;
340
341	if ( cr->cr_names != NULL ) {
342		int i;
343
344		for( i=0; cr->cr_names[i]; i++ ) {
345			if( !slap_valid_descr( cr->cr_names[i] ) ) {
346				return SLAP_SCHERR_BAD_DESCR;
347			}
348		}
349	}
350
351	if ( !OID_LEADCHAR( cr->cr_oid[0] )) {
352		/* Expand OID macros */
353		char *oid = oidm_find( cr->cr_oid );
354		if ( !oid ) {
355			*err = cr->cr_oid;
356			return SLAP_SCHERR_OIDM;
357		}
358		if ( oid != cr->cr_oid ) {
359			oidm = cr->cr_oid;
360			cr->cr_oid = oid;
361		}
362	}
363
364	scr = (ContentRule *) ch_calloc( 1, sizeof(ContentRule) );
365	AC_MEMCPY( &scr->scr_crule, cr, sizeof(LDAPContentRule) );
366
367	scr->scr_oidmacro = oidm;
368	scr->scr_sclass = oc_find(cr->cr_oid);
369	if ( !scr->scr_sclass ) {
370		*err = cr->cr_oid;
371		code = SLAP_SCHERR_CLASS_NOT_FOUND;
372		goto fail;
373	}
374
375	/* check object class usage */
376	if( scr->scr_sclass->soc_kind != LDAP_SCHEMA_STRUCTURAL )
377	{
378		*err = cr->cr_oid;
379		code = SLAP_SCHERR_CR_BAD_STRUCT;
380		goto fail;
381	}
382
383	if( scr->scr_sclass->soc_flags & SLAP_OC_OPERATIONAL ) op++;
384
385	code = cr_add_auxiliaries( scr, &op, err );
386	if ( code != 0 ) goto fail;
387
388	code = cr_create_required( scr, &op, err );
389	if ( code != 0 ) goto fail;
390
391	code = cr_create_allowed( scr, &op, err );
392	if ( code != 0 ) goto fail;
393
394	code = cr_create_precluded( scr, &op, err );
395	if ( code != 0 ) goto fail;
396
397	if( user && op ) {
398		code = SLAP_SCHERR_CR_BAD_AUX;
399		goto fail;
400	}
401
402	code = cr_insert(scr,err);
403	if ( code == 0 && rscr )
404		*rscr = scr;
405	return code;
406fail:
407	ch_free( scr );
408	return code;
409}
410
411void
412cr_unparse( BerVarray *res, ContentRule *start, ContentRule *end, int sys )
413{
414	ContentRule *cr;
415	int i, num;
416	struct berval bv, *bva = NULL, idx;
417	char ibuf[32];
418
419	if ( !start )
420		start = LDAP_STAILQ_FIRST( &cr_list );
421
422	/* count the result size */
423	i = 0;
424	for ( cr=start; cr; cr=LDAP_STAILQ_NEXT(cr, scr_next)) {
425		if ( sys && !(cr->scr_flags & SLAP_CR_HARDCODE)) continue;
426		i++;
427		if ( cr == end ) break;
428	}
429	if (!i) return;
430
431	num = i;
432	bva = ch_malloc( (num+1) * sizeof(struct berval) );
433	BER_BVZERO( bva );
434	idx.bv_val = ibuf;
435	if ( sys ) {
436		idx.bv_len = 0;
437		ibuf[0] = '\0';
438	}
439	i = 0;
440	for ( cr=start; cr; cr=LDAP_STAILQ_NEXT(cr, scr_next)) {
441		LDAPContentRule lcr, *lcrp;
442		if ( sys && !(cr->scr_flags & SLAP_CR_HARDCODE)) continue;
443		if ( cr->scr_oidmacro ) {
444			lcr = cr->scr_crule;
445			lcr.cr_oid = cr->scr_oidmacro;
446			lcrp = &lcr;
447		} else {
448			lcrp = &cr->scr_crule;
449		}
450		if ( ldap_contentrule2bv( lcrp, &bv ) == NULL ) {
451			ber_bvarray_free( bva );
452		}
453		if ( !sys ) {
454			idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
455		}
456		bva[i].bv_len = idx.bv_len + bv.bv_len;
457		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
458		strcpy( bva[i].bv_val, ibuf );
459		strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
460		i++;
461		bva[i].bv_val = NULL;
462		ldap_memfree( bv.bv_val );
463		if ( cr == end ) break;
464	}
465	*res = bva;
466}
467
468int
469cr_schema_info( Entry *e )
470{
471	AttributeDescription *ad_ditContentRules
472		= slap_schema.si_ad_ditContentRules;
473	ContentRule	*cr;
474
475	struct berval	val;
476	struct berval	nval;
477
478	LDAP_STAILQ_FOREACH(cr, &cr_list, scr_next) {
479		if ( ldap_contentrule2bv( &cr->scr_crule, &val ) == NULL ) {
480			return -1;
481		}
482
483#if 0
484		if( cr->scr_flags & SLAP_CR_HIDE ) continue;
485#endif
486#if 0
487		Debug( LDAP_DEBUG_TRACE, "Merging cr [%ld] %s\n",
488	       (long) val.bv_len, val.bv_val, 0 );
489#endif
490
491		nval.bv_val = cr->scr_oid;
492		nval.bv_len = strlen(cr->scr_oid);
493
494		if( attr_merge_one( e, ad_ditContentRules, &val, &nval ) )
495		{
496			return -1;
497		}
498		ldap_memfree( val.bv_val );
499	}
500	return 0;
501}
502