1/* oc.c - object class 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
27int is_object_subclass(
28	ObjectClass *sup,
29	ObjectClass *sub )
30{
31	int i;
32
33	if( sub == NULL || sup == NULL ) return 0;
34
35#if 0
36	Debug( LDAP_DEBUG_TRACE, "is_object_subclass(%s,%s) %d\n",
37		sup->soc_oid, sub->soc_oid, sup == sub );
38#endif
39
40	if ( sup == sub ) {
41		return 1;
42	}
43
44	if ( sub->soc_sups == NULL ) {
45		return 0;
46	}
47
48	for ( i = 0; sub->soc_sups[i] != NULL; i++ ) {
49		if ( is_object_subclass( sup, sub->soc_sups[i] ) ) {
50			return 1;
51		}
52	}
53
54	return 0;
55}
56
57int is_entry_objectclass(
58	Entry*	e,
59	ObjectClass *oc,
60	unsigned flags )
61{
62	/*
63	 * set_flags should only be true if oc is one of operational
64	 * object classes which we support objectClass flags for
65	 * (e.g., referral, alias, ...).  See <slap.h>.
66	 */
67
68	Attribute *attr;
69	struct berval *bv;
70
71	assert( !( e == NULL || oc == NULL ) );
72	assert( ( flags & SLAP_OCF_MASK ) != SLAP_OCF_MASK );
73
74	if ( e == NULL || oc == NULL ) {
75		return 0;
76	}
77
78	if ( flags == SLAP_OCF_SET_FLAGS && ( e->e_ocflags & SLAP_OC__END ) )
79	{
80		/* flags are set, use them */
81		return (e->e_ocflags & oc->soc_flags & SLAP_OC__MASK) != 0;
82	}
83
84	/*
85	 * find objectClass attribute
86	 */
87	attr = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
88	if ( attr == NULL ) {
89		/* no objectClass attribute */
90		Debug( LDAP_DEBUG_ANY, "is_entry_objectclass(\"%s\", \"%s\") "
91			"no objectClass attribute\n",
92			e->e_dn == NULL ? "" : e->e_dn,
93			oc->soc_oclass.oc_oid, 0 );
94
95		/* mark flags as set */
96		e->e_ocflags |= SLAP_OC__END;
97
98		return 0;
99	}
100
101	for ( bv = attr->a_vals; bv->bv_val; bv++ ) {
102		ObjectClass *objectClass = oc_bvfind( bv );
103
104		if ( objectClass == NULL ) {
105			/* FIXME: is this acceptable? */
106			continue;
107		}
108
109		if ( !( flags & SLAP_OCF_SET_FLAGS ) ) {
110			if ( objectClass == oc ) {
111				return 1;
112			}
113
114			if ( ( flags & SLAP_OCF_CHECK_SUP )
115				&& is_object_subclass( oc, objectClass ) )
116			{
117				return 1;
118			}
119		}
120
121		e->e_ocflags |= objectClass->soc_flags;
122	}
123
124	/* mark flags as set */
125	e->e_ocflags |= SLAP_OC__END;
126
127	return ( e->e_ocflags & oc->soc_flags & SLAP_OC__MASK ) != 0;
128}
129
130
131struct oindexrec {
132	struct berval oir_name;
133	ObjectClass	*oir_oc;
134};
135
136static Avlnode	*oc_index = NULL;
137static Avlnode	*oc_cache = NULL;
138static LDAP_STAILQ_HEAD(OCList, ObjectClass) oc_list
139	= LDAP_STAILQ_HEAD_INITIALIZER(oc_list);
140
141ObjectClass *oc_sys_tail;
142
143static int
144oc_index_cmp(
145	const void *v_oir1,
146	const void *v_oir2 )
147{
148	const struct oindexrec *oir1 = v_oir1, *oir2 = v_oir2;
149	int i = oir1->oir_name.bv_len - oir2->oir_name.bv_len;
150	if (i) return i;
151	return strcasecmp( oir1->oir_name.bv_val, oir2->oir_name.bv_val );
152}
153
154static int
155oc_index_name_cmp(
156	const void *v_name,
157	const void *v_oir )
158{
159	const struct berval    *name = v_name;
160	const struct oindexrec *oir  = v_oir;
161	int i = name->bv_len - oir->oir_name.bv_len;
162	if (i) return i;
163	return strncasecmp( name->bv_val, oir->oir_name.bv_val, name->bv_len );
164}
165
166ObjectClass *
167oc_find( const char *ocname )
168{
169	struct berval bv;
170
171	bv.bv_val = (char *)ocname;
172	bv.bv_len = strlen( ocname );
173
174	return( oc_bvfind( &bv ) );
175}
176
177ObjectClass *
178oc_bvfind( struct berval *ocname )
179{
180	struct oindexrec	*oir;
181
182	if ( oc_cache ) {
183		oir = avl_find( oc_cache, ocname, oc_index_name_cmp );
184		if ( oir ) return oir->oir_oc;
185	}
186	oir = avl_find( oc_index, ocname, oc_index_name_cmp );
187
188	if ( oir != NULL ) {
189		if ( at_oc_cache ) {
190			avl_insert( &oc_cache, (caddr_t) oir,
191				oc_index_cmp, avl_dup_error );
192		}
193		return( oir->oir_oc );
194	}
195
196	return( NULL );
197}
198
199static LDAP_STAILQ_HEAD(OCUList, ObjectClass) oc_undef_list
200	= LDAP_STAILQ_HEAD_INITIALIZER(oc_undef_list);
201
202ObjectClass *
203oc_bvfind_undef( struct berval *ocname )
204{
205	ObjectClass	*oc = oc_bvfind( ocname );
206
207	if ( oc ) {
208		return oc;
209	}
210
211	LDAP_STAILQ_FOREACH( oc, &oc_undef_list, soc_next ) {
212		int	d = oc->soc_cname.bv_len - ocname->bv_len;
213
214		if ( d ) {
215			continue;
216		}
217
218		if ( strcasecmp( oc->soc_cname.bv_val, ocname->bv_val ) == 0 ) {
219			break;
220		}
221	}
222
223	if ( oc ) {
224		return oc;
225	}
226
227	oc = ch_malloc( sizeof( ObjectClass ) + ocname->bv_len + 1 );
228	memset( oc, 0, sizeof( ObjectClass ) );
229
230	oc->soc_cname.bv_len = ocname->bv_len;
231	oc->soc_cname.bv_val = (char *)&oc[ 1 ];
232	AC_MEMCPY( oc->soc_cname.bv_val, ocname->bv_val, ocname->bv_len );
233	oc->soc_cname.bv_val[ oc->soc_cname.bv_len ] = '\0';
234
235	/* canonical to upper case */
236	ldap_pvt_str2upper( oc->soc_cname.bv_val );
237
238	LDAP_STAILQ_NEXT( oc, soc_next ) = NULL;
239	ldap_pvt_thread_mutex_lock( &oc_undef_mutex );
240	LDAP_STAILQ_INSERT_HEAD( &oc_undef_list, oc, soc_next );
241	ldap_pvt_thread_mutex_unlock( &oc_undef_mutex );
242
243	return oc;
244}
245
246static int
247oc_create_required(
248	ObjectClass		*soc,
249	char			**attrs,
250	int			*op,
251	const char		**err )
252{
253	char		**attrs1;
254	AttributeType	*sat;
255	AttributeType	**satp;
256	int		i;
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, soc->soc_required) < 0) {
270				if ( at_append_to_list(sat, &soc->soc_required) ) {
271					*err = *attrs1;
272					return SLAP_SCHERR_OUTOFMEM;
273				}
274			}
275			attrs1++;
276		}
277		/* Now delete duplicates from the allowed list */
278		for ( satp = soc->soc_required; *satp; satp++ ) {
279			i = at_find_in_list(*satp, soc->soc_allowed);
280			if ( i >= 0 ) {
281				at_delete_from_list(i, &soc->soc_allowed);
282			}
283		}
284	}
285	return 0;
286}
287
288static int
289oc_create_allowed(
290    ObjectClass		*soc,
291    char		**attrs,
292	int			*op,
293    const char		**err )
294{
295	char		**attrs1;
296	AttributeType	*sat;
297
298	if ( attrs ) {
299		attrs1 = attrs;
300		while ( *attrs1 ) {
301			sat = at_find(*attrs1);
302			if ( !sat ) {
303				*err = *attrs1;
304				return SLAP_SCHERR_ATTR_NOT_FOUND;
305			}
306
307			if( is_at_operational( sat )) (*op)++;
308
309			if ( at_find_in_list(sat, soc->soc_required) < 0 &&
310			     at_find_in_list(sat, soc->soc_allowed) < 0 ) {
311				if ( at_append_to_list(sat, &soc->soc_allowed) ) {
312					*err = *attrs1;
313					return SLAP_SCHERR_OUTOFMEM;
314				}
315			}
316			attrs1++;
317		}
318	}
319	return 0;
320}
321
322static int
323oc_add_sups(
324	ObjectClass		*soc,
325	char			**sups,
326	int			*op,
327	const char		**err )
328{
329	int		code;
330	ObjectClass	*soc1;
331	int		nsups;
332	char	**sups1;
333	int		add_sups = 0;
334
335	if ( sups ) {
336		if ( !soc->soc_sups ) {
337			/* We are at the first recursive level */
338			add_sups = 1;
339			nsups = 1;
340			sups1 = sups;
341			while ( *sups1 ) {
342				nsups++;
343				sups1++;
344			}
345			soc->soc_sups = (ObjectClass **)ch_calloc(nsups,
346					  sizeof(ObjectClass *));
347		}
348
349		nsups = 0;
350		sups1 = sups;
351		while ( *sups1 ) {
352			soc1 = oc_find(*sups1);
353			if ( !soc1 ) {
354				*err = *sups1;
355				return SLAP_SCHERR_CLASS_NOT_FOUND;
356			}
357
358			/* check object class usage
359			 * abstract classes can only sup abstract classes
360			 * structural classes can not sup auxiliary classes
361			 * auxiliary classes can not sup structural classes
362			 */
363			if( soc->soc_kind != soc1->soc_kind
364				&& soc1->soc_kind != LDAP_SCHEMA_ABSTRACT )
365			{
366				*err = *sups1;
367				return SLAP_SCHERR_CLASS_BAD_SUP;
368			}
369
370			if( soc1->soc_obsolete && !soc->soc_obsolete ) {
371				*err = *sups1;
372				return SLAP_SCHERR_CLASS_BAD_SUP;
373			}
374
375			if( soc->soc_flags & SLAP_OC_OPERATIONAL ) (*op)++;
376
377			if ( add_sups ) {
378				soc->soc_sups[nsups] = soc1;
379			}
380
381			code = oc_add_sups( soc, soc1->soc_sup_oids, op, err );
382			if ( code ) return code;
383
384			code = oc_create_required( soc, soc1->soc_at_oids_must, op, err );
385			if ( code ) return code;
386
387			code = oc_create_allowed( soc, soc1->soc_at_oids_may, op, err );
388			if ( code ) return code;
389
390			nsups++;
391			sups1++;
392		}
393	}
394
395	return 0;
396}
397
398static void
399oc_delete_names( ObjectClass *oc )
400{
401	char			**names = oc->soc_names;
402
403	while (*names) {
404		struct oindexrec	tmpoir, *oir;
405
406		ber_str2bv( *names, 0, 0, &tmpoir.oir_name );
407		tmpoir.oir_oc = oc;
408		oir = (struct oindexrec *)avl_delete( &oc_index,
409			(caddr_t)&tmpoir, oc_index_cmp );
410		assert( oir != NULL );
411		ldap_memfree( oir );
412		names++;
413	}
414}
415
416/* Mark the ObjectClass as deleted, remove from list, and remove all its
417 * names from the AVL tree. Leave the OID in the tree.
418 */
419void
420oc_delete( ObjectClass *oc )
421{
422	oc->soc_flags |= SLAP_OC_DELETED;
423
424	LDAP_STAILQ_REMOVE(&oc_list, oc, ObjectClass, soc_next);
425
426	oc_delete_names( oc );
427}
428
429static void
430oc_clean( ObjectClass *o )
431{
432	if (o->soc_sups) {
433		ldap_memfree(o->soc_sups);
434		o->soc_sups = NULL;
435	}
436	if (o->soc_required) {
437		ldap_memfree(o->soc_required);
438		o->soc_required = NULL;
439	}
440	if (o->soc_allowed) {
441		ldap_memfree(o->soc_allowed);
442		o->soc_allowed = NULL;
443	}
444	if (o->soc_oidmacro) {
445		ldap_memfree(o->soc_oidmacro);
446		o->soc_oidmacro = NULL;
447	}
448}
449
450static void
451oc_destroy_one( void *v )
452{
453	struct oindexrec *oir = v;
454	ObjectClass *o = oir->oir_oc;
455
456	oc_clean( o );
457	ldap_objectclass_free((LDAPObjectClass *)o);
458	ldap_memfree(oir);
459}
460
461void
462oc_destroy( void )
463{
464	ObjectClass *o;
465
466	while( !LDAP_STAILQ_EMPTY(&oc_list) ) {
467		o = LDAP_STAILQ_FIRST(&oc_list);
468		LDAP_STAILQ_REMOVE_HEAD(&oc_list, soc_next);
469
470		oc_delete_names( o );
471	}
472
473	avl_free( oc_index, oc_destroy_one );
474
475	while( !LDAP_STAILQ_EMPTY(&oc_undef_list) ) {
476		o = LDAP_STAILQ_FIRST(&oc_undef_list);
477		LDAP_STAILQ_REMOVE_HEAD(&oc_undef_list, soc_next);
478
479		ch_free( (ObjectClass *)o );
480	}
481}
482
483int
484oc_start( ObjectClass **oc )
485{
486	assert( oc != NULL );
487
488	*oc = LDAP_STAILQ_FIRST(&oc_list);
489
490	return (*oc != NULL);
491}
492
493int
494oc_next( ObjectClass **oc )
495{
496	assert( oc != NULL );
497
498#if 0	/* pedantic check: breaks when deleting an oc, don't use it. */
499	{
500		ObjectClass *tmp = NULL;
501
502		LDAP_STAILQ_FOREACH(tmp,&oc_list,soc_next) {
503			if ( tmp == *oc ) {
504				break;
505			}
506		}
507
508		assert( tmp != NULL );
509	}
510#endif
511
512	if ( *oc == NULL ) {
513		return 0;
514	}
515
516	*oc = LDAP_STAILQ_NEXT(*oc,soc_next);
517
518	return (*oc != NULL);
519}
520
521/*
522 * check whether the two ObjectClasses actually __are__ identical,
523 * or rather inconsistent
524 */
525static int
526oc_check_dup(
527	ObjectClass	*soc,
528	ObjectClass	*new_soc )
529{
530	if ( new_soc->soc_oid != NULL ) {
531		if ( soc->soc_oid == NULL ) {
532			return SLAP_SCHERR_CLASS_INCONSISTENT;
533		}
534
535		if ( strcmp( soc->soc_oid, new_soc->soc_oid ) != 0 ) {
536			return SLAP_SCHERR_CLASS_INCONSISTENT;
537		}
538
539	} else {
540		if ( soc->soc_oid != NULL ) {
541			return SLAP_SCHERR_CLASS_INCONSISTENT;
542		}
543	}
544
545	if ( new_soc->soc_names ) {
546		int	i;
547
548		if ( soc->soc_names == NULL ) {
549			return SLAP_SCHERR_CLASS_INCONSISTENT;
550		}
551
552		for ( i = 0; new_soc->soc_names[ i ]; i++ ) {
553			if ( soc->soc_names[ i ] == NULL ) {
554				return SLAP_SCHERR_CLASS_INCONSISTENT;
555			}
556
557			if ( strcasecmp( soc->soc_names[ i ],
558					new_soc->soc_names[ i ] ) != 0 )
559			{
560				return SLAP_SCHERR_CLASS_INCONSISTENT;
561			}
562		}
563	} else {
564		if ( soc->soc_names != NULL ) {
565			return SLAP_SCHERR_CLASS_INCONSISTENT;
566		}
567	}
568
569	return SLAP_SCHERR_CLASS_DUP;
570}
571
572static struct oindexrec *oir_old;
573
574static int
575oc_dup_error( void *left, void *right )
576{
577	oir_old = left;
578	return -1;
579}
580
581static int
582oc_insert(
583    ObjectClass		**roc,
584	ObjectClass		*prev,
585    const char		**err )
586{
587	struct oindexrec	*oir;
588	char			**names;
589	ObjectClass		*soc = *roc;
590
591	if ( soc->soc_oid ) {
592		oir = (struct oindexrec *)
593			ch_calloc( 1, sizeof(struct oindexrec) );
594		ber_str2bv( soc->soc_oid, 0, 0, &oir->oir_name );
595		oir->oir_oc = soc;
596		oir_old = NULL;
597
598		if ( avl_insert( &oc_index, (caddr_t) oir,
599			oc_index_cmp, oc_dup_error ) )
600		{
601			ObjectClass	*old_soc;
602			int		rc;
603
604			*err = soc->soc_oid;
605
606			assert( oir_old != NULL );
607			old_soc = oir_old->oir_oc;
608
609			/* replacing a deleted definition? */
610			if ( old_soc->soc_flags & SLAP_OC_DELETED ) {
611				ObjectClass tmp;
612
613				/* Keep old oid, free new oid;
614				 * Keep new everything else, free old
615				 */
616				tmp = *old_soc;
617				*old_soc = *soc;
618				old_soc->soc_oid = tmp.soc_oid;
619				tmp.soc_oid = soc->soc_oid;
620				*soc = tmp;
621
622				oc_clean( soc );
623				oc_destroy_one( oir );
624
625				oir = oir_old;
626				soc = old_soc;
627				*roc = soc;
628			} else {
629				rc = oc_check_dup( old_soc, soc );
630
631				ldap_memfree( oir );
632				return rc;
633			}
634		}
635
636		/* FIX: temporal consistency check */
637		assert( oc_bvfind( &oir->oir_name ) != NULL );
638	}
639
640	assert( soc != NULL );
641
642	if ( (names = soc->soc_names) ) {
643		while ( *names ) {
644			oir = (struct oindexrec *)
645				ch_calloc( 1, sizeof(struct oindexrec) );
646			oir->oir_name.bv_val = *names;
647			oir->oir_name.bv_len = strlen( *names );
648			oir->oir_oc = soc;
649
650			if ( avl_insert( &oc_index, (caddr_t) oir,
651				oc_index_cmp, avl_dup_error ) )
652			{
653				ObjectClass	*old_soc;
654				int		rc;
655
656				*err = *names;
657
658				old_soc = oc_bvfind( &oir->oir_name );
659				assert( old_soc != NULL );
660				rc = oc_check_dup( old_soc, soc );
661
662				ldap_memfree( oir );
663
664				while ( names > soc->soc_names ) {
665					struct oindexrec	tmpoir;
666
667					names--;
668					ber_str2bv( *names, 0, 0, &tmpoir.oir_name );
669					tmpoir.oir_oc = soc;
670					oir = (struct oindexrec *)avl_delete( &oc_index,
671						(caddr_t)&tmpoir, oc_index_cmp );
672					assert( oir != NULL );
673					ldap_memfree( oir );
674				}
675
676				if ( soc->soc_oid ) {
677					struct oindexrec	tmpoir;
678
679					ber_str2bv( soc->soc_oid, 0, 0, &tmpoir.oir_name );
680					tmpoir.oir_oc = soc;
681					oir = (struct oindexrec *)avl_delete( &oc_index,
682						(caddr_t)&tmpoir, oc_index_cmp );
683					assert( oir != NULL );
684					ldap_memfree( oir );
685				}
686
687				return rc;
688			}
689
690			/* FIX: temporal consistency check */
691			assert( oc_bvfind(&oir->oir_name) != NULL );
692
693			names++;
694		}
695	}
696	if ( soc->soc_flags & SLAP_OC_HARDCODE ) {
697		prev = oc_sys_tail;
698		oc_sys_tail = soc;
699	}
700	if ( prev ) {
701		LDAP_STAILQ_INSERT_AFTER( &oc_list, prev, soc, soc_next );
702	} else {
703		LDAP_STAILQ_INSERT_TAIL( &oc_list, soc, soc_next );
704	}
705
706	return 0;
707}
708
709int
710oc_add(
711    LDAPObjectClass	*oc,
712	int user,
713	ObjectClass		**rsoc,
714	ObjectClass		*prev,
715    const char		**err )
716{
717	ObjectClass	*soc;
718	int		code;
719	int		op = 0;
720	char	*oidm = NULL;
721
722	if ( oc->oc_names != NULL ) {
723		int i;
724
725		for( i=0; oc->oc_names[i]; i++ ) {
726			if( !slap_valid_descr( oc->oc_names[i] ) ) {
727				return SLAP_SCHERR_BAD_DESCR;
728			}
729		}
730	}
731
732	if ( !OID_LEADCHAR( oc->oc_oid[0] )) {
733		/* Expand OID macros */
734		char *oid = oidm_find( oc->oc_oid );
735		if ( !oid ) {
736			*err = oc->oc_oid;
737			return SLAP_SCHERR_OIDM;
738		}
739		if ( oid != oc->oc_oid ) {
740			oidm = oc->oc_oid;
741			oc->oc_oid = oid;
742		}
743	}
744
745	soc = (ObjectClass *) ch_calloc( 1, sizeof(ObjectClass) );
746	AC_MEMCPY( &soc->soc_oclass, oc, sizeof(LDAPObjectClass) );
747
748	soc->soc_oidmacro = oidm;
749	if( oc->oc_names != NULL ) {
750		soc->soc_cname.bv_val = soc->soc_names[0];
751	} else {
752		soc->soc_cname.bv_val = soc->soc_oid;
753	}
754	soc->soc_cname.bv_len = strlen( soc->soc_cname.bv_val );
755
756	if( soc->soc_sup_oids == NULL &&
757		soc->soc_kind == LDAP_SCHEMA_STRUCTURAL )
758	{
759		/* structural object classes implicitly inherit from 'top' */
760		static char *top_oids[] = { SLAPD_TOP_OID, NULL };
761		code = oc_add_sups( soc, top_oids, &op, err );
762	} else {
763		code = oc_add_sups( soc, soc->soc_sup_oids, &op, err );
764	}
765
766	if ( code != 0 ) {
767		goto done;
768	}
769
770	if ( user && op ) {
771		code = SLAP_SCHERR_CLASS_BAD_SUP;
772		goto done;
773	}
774
775	code = oc_create_required( soc, soc->soc_at_oids_must, &op, err );
776	if ( code != 0 ) {
777		goto done;
778	}
779
780	code = oc_create_allowed( soc, soc->soc_at_oids_may, &op, err );
781	if ( code != 0 ) {
782		goto done;
783	}
784
785	if ( user && op ) {
786		code = SLAP_SCHERR_CLASS_BAD_USAGE;
787		goto done;
788	}
789
790	if ( !user ) {
791		soc->soc_flags |= SLAP_OC_HARDCODE;
792	}
793
794	code = oc_insert(&soc,prev,err);
795done:;
796	if ( code != 0 ) {
797		if ( soc->soc_sups ) {
798			ch_free( soc->soc_sups );
799		}
800
801		if ( soc->soc_required ) {
802			ch_free( soc->soc_required );
803		}
804
805		if ( soc->soc_allowed ) {
806			ch_free( soc->soc_allowed );
807		}
808
809		if ( soc->soc_oidmacro ) {
810			ch_free( soc->soc_oidmacro );
811		}
812
813		ch_free( soc );
814
815	} else if ( rsoc ) {
816		*rsoc = soc;
817	}
818	return code;
819}
820
821void
822oc_unparse( BerVarray *res, ObjectClass *start, ObjectClass *end, int sys )
823{
824	ObjectClass *oc;
825	int i, num;
826	struct berval bv, *bva = NULL, idx;
827	char ibuf[32];
828
829	if ( !start )
830		start = LDAP_STAILQ_FIRST( &oc_list );
831
832	/* count the result size */
833	i = 0;
834	for ( oc=start; oc; oc=LDAP_STAILQ_NEXT(oc, soc_next)) {
835		if ( sys && !(oc->soc_flags & SLAP_OC_HARDCODE)) break;
836		i++;
837		if ( oc == end ) break;
838	}
839	if (!i) return;
840
841	num = i;
842	bva = ch_malloc( (num+1) * sizeof(struct berval) );
843	BER_BVZERO( bva );
844	idx.bv_val = ibuf;
845	if ( sys ) {
846		idx.bv_len = 0;
847		ibuf[0] = '\0';
848	}
849	i = 0;
850	for ( oc=start; oc; oc=LDAP_STAILQ_NEXT(oc, soc_next)) {
851		LDAPObjectClass loc, *locp;
852		if ( sys && !(oc->soc_flags & SLAP_OC_HARDCODE)) break;
853		if ( oc->soc_oidmacro ) {
854			loc = oc->soc_oclass;
855			loc.oc_oid = oc->soc_oidmacro;
856			locp = &loc;
857		} else {
858			locp = &oc->soc_oclass;
859		}
860		if ( ldap_objectclass2bv( locp, &bv ) == NULL ) {
861			ber_bvarray_free( bva );
862		}
863		if ( !sys ) {
864			idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
865		}
866		bva[i].bv_len = idx.bv_len + bv.bv_len;
867		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
868		strcpy( bva[i].bv_val, ibuf );
869		strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
870		i++;
871		bva[i].bv_val = NULL;
872		ldap_memfree( bv.bv_val );
873		if ( oc == end ) break;
874	}
875	*res = bva;
876}
877
878int
879oc_schema_info( Entry *e )
880{
881	AttributeDescription *ad_objectClasses = slap_schema.si_ad_objectClasses;
882	ObjectClass	*oc;
883	struct berval	val;
884	struct berval	nval;
885
886	LDAP_STAILQ_FOREACH( oc, &oc_list, soc_next ) {
887		if( oc->soc_flags & SLAP_OC_HIDE ) continue;
888
889		if ( ldap_objectclass2bv( &oc->soc_oclass, &val ) == NULL ) {
890			return -1;
891		}
892
893		nval = oc->soc_cname;
894
895#if 0
896		Debug( LDAP_DEBUG_TRACE, "Merging oc [%ld] %s (%s)\n",
897	       (long) val.bv_len, val.bv_val, nval.bv_val );
898#endif
899
900		if( attr_merge_one( e, ad_objectClasses, &val, &nval ) ) {
901			return -1;
902		}
903		ldap_memfree( val.bv_val );
904	}
905	return 0;
906}
907
908int
909register_oc( const char *def, ObjectClass **soc, int dupok )
910{
911	LDAPObjectClass *oc;
912	int code;
913	const char *err;
914
915	oc = ldap_str2objectclass( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
916	if ( !oc ) {
917		Debug( LDAP_DEBUG_ANY,
918			"register_oc: objectclass \"%s\": %s, %s\n",
919			def, ldap_scherr2str(code), err );
920		return code;
921	}
922	code = oc_add(oc,0,NULL,NULL,&err);
923	if ( code && ( code != SLAP_SCHERR_CLASS_DUP || !dupok )) {
924		Debug( LDAP_DEBUG_ANY,
925			"register_oc: objectclass \"%s\": %s, %s\n",
926			def, scherr2str(code), err );
927		ldap_objectclass_free(oc);
928		return code;
929	}
930	if ( soc )
931		*soc = oc_find(oc->oc_names[0]);
932	if ( code ) {
933		ldap_objectclass_free(oc);
934	} else {
935		ldap_memfree(oc);
936	}
937	return 0;
938}
939