1/*	$NetBSD: schema.c,v 1.1.1.3 2010/12/12 15:21:36 adam Exp $	*/
2
3/* OpenLDAP: pkg/ldap/libraries/libldap/schema.c,v 1.77.2.6 2010/04/13 20:23:00 kurt Exp */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2010 The OpenLDAP Foundation.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17
18/*
19 * schema.c:  parsing routines used by servers and clients to process
20 *	schema definitions
21 */
22
23#include "portable.h"
24
25#include <stdio.h>
26#include <ac/stdlib.h>
27
28#include <ac/string.h>
29#include <ac/time.h>
30
31#include "ldap-int.h"
32
33#include <ldap_schema.h>
34
35static const char EndOfInput[] = "end of input";
36
37static const char *
38choose_name( char *names[], const char *fallback )
39{
40	return (names != NULL && names[0] != NULL) ? names[0] : fallback;
41}
42
43LDAP_CONST char *
44ldap_syntax2name( LDAPSyntax * syn )
45{
46	return( syn->syn_oid );
47}
48
49LDAP_CONST char *
50ldap_matchingrule2name( LDAPMatchingRule * mr )
51{
52	return( choose_name( mr->mr_names, mr->mr_oid ) );
53}
54
55LDAP_CONST char *
56ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
57{
58	return( choose_name( mru->mru_names, mru->mru_oid ) );
59}
60
61LDAP_CONST char *
62ldap_attributetype2name( LDAPAttributeType * at )
63{
64	return( choose_name( at->at_names, at->at_oid ) );
65}
66
67LDAP_CONST char *
68ldap_objectclass2name( LDAPObjectClass * oc )
69{
70	return( choose_name( oc->oc_names, oc->oc_oid ) );
71}
72
73LDAP_CONST char *
74ldap_contentrule2name( LDAPContentRule * cr )
75{
76	return( choose_name( cr->cr_names, cr->cr_oid ) );
77}
78
79LDAP_CONST char *
80ldap_nameform2name( LDAPNameForm * nf )
81{
82	return( choose_name( nf->nf_names, nf->nf_oid ) );
83}
84
85LDAP_CONST char *
86ldap_structurerule2name( LDAPStructureRule * sr )
87{
88	return( choose_name( sr->sr_names, NULL ) );
89}
90
91/*
92 * When pretty printing the entities we will be appending to a buffer.
93 * Since checking for overflow, realloc'ing and checking if no error
94 * is extremely boring, we will use a protection layer that will let
95 * us blissfully ignore the error until the end.  This layer is
96 * implemented with the help of the next type.
97 */
98
99typedef struct safe_string {
100	char * val;
101	ber_len_t size;
102	ber_len_t pos;
103	int at_whsp;
104} safe_string;
105
106static safe_string *
107new_safe_string(int size)
108{
109	safe_string * ss;
110
111	ss = LDAP_MALLOC(sizeof(safe_string));
112	if ( !ss )
113		return(NULL);
114
115	ss->val = LDAP_MALLOC(size);
116	if ( !ss->val ) {
117		LDAP_FREE(ss);
118		return(NULL);
119	}
120
121	ss->size = size;
122	ss->pos = 0;
123	ss->at_whsp = 0;
124
125	return ss;
126}
127
128static void
129safe_string_free(safe_string * ss)
130{
131	if ( !ss )
132		return;
133	LDAP_FREE(ss->val);
134	LDAP_FREE(ss);
135}
136
137#if 0	/* unused */
138static char *
139safe_string_val(safe_string * ss)
140{
141	ss->val[ss->pos] = '\0';
142	return(ss->val);
143}
144#endif
145
146static char *
147safe_strdup(safe_string * ss)
148{
149	char *ret = LDAP_MALLOC(ss->pos+1);
150	if (!ret)
151		return NULL;
152	AC_MEMCPY(ret, ss->val, ss->pos);
153	ret[ss->pos] = '\0';
154	return ret;
155}
156
157static int
158append_to_safe_string(safe_string * ss, char * s)
159{
160	int l = strlen(s);
161	char * temp;
162
163	/*
164	 * Some runaway process is trying to append to a string that
165	 * overflowed and we could not extend.
166	 */
167	if ( !ss->val )
168		return -1;
169
170	/* We always make sure there is at least one position available */
171	if ( ss->pos + l >= ss->size-1 ) {
172		ss->size *= 2;
173		if ( ss->pos + l >= ss->size-1 ) {
174			ss->size = ss->pos + l + 1;
175		}
176
177		temp = LDAP_REALLOC(ss->val, ss->size);
178		if ( !temp ) {
179			/* Trouble, out of memory */
180			LDAP_FREE(ss->val);
181			return -1;
182		}
183		ss->val = temp;
184	}
185	strncpy(&ss->val[ss->pos], s, l);
186	ss->pos += l;
187	if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) )
188		ss->at_whsp = 1;
189	else
190		ss->at_whsp = 0;
191
192	return 0;
193}
194
195static int
196print_literal(safe_string *ss, char *s)
197{
198	return(append_to_safe_string(ss,s));
199}
200
201static int
202print_whsp(safe_string *ss)
203{
204	if ( ss->at_whsp )
205		return(append_to_safe_string(ss,""));
206	else
207		return(append_to_safe_string(ss," "));
208}
209
210static int
211print_numericoid(safe_string *ss, char *s)
212{
213	if ( s )
214		return(append_to_safe_string(ss,s));
215	else
216		return(append_to_safe_string(ss,""));
217}
218
219/* This one is identical to print_qdescr */
220static int
221print_qdstring(safe_string *ss, char *s)
222{
223	print_whsp(ss);
224	print_literal(ss,"'");
225	append_to_safe_string(ss,s);
226	print_literal(ss,"'");
227	return(print_whsp(ss));
228}
229
230static int
231print_qdescr(safe_string *ss, char *s)
232{
233	print_whsp(ss);
234	print_literal(ss,"'");
235	append_to_safe_string(ss,s);
236	print_literal(ss,"'");
237	return(print_whsp(ss));
238}
239
240static int
241print_qdescrlist(safe_string *ss, char **sa)
242{
243	char **sp;
244	int ret = 0;
245
246	for (sp=sa; *sp; sp++) {
247		ret = print_qdescr(ss,*sp);
248	}
249	/* If the list was empty, we return zero that is potentially
250	 * incorrect, but since we will be still appending things, the
251	 * overflow will be detected later.  Maybe FIX.
252	 */
253	return(ret);
254}
255
256static int
257print_qdescrs(safe_string *ss, char **sa)
258{
259	/* The only way to represent an empty list is as a qdescrlist
260	 * so, if the list is empty we treat it as a long list.
261	 * Really, this is what the syntax mandates.  We should not
262	 * be here if the list was empty, but if it happens, a label
263	 * has already been output and we cannot undo it.
264	 */
265	if ( !sa[0] || ( sa[0] && sa[1] ) ) {
266		print_whsp(ss);
267		print_literal(ss,"("/*)*/);
268		print_qdescrlist(ss,sa);
269		print_literal(ss,/*(*/")");
270		return(print_whsp(ss));
271	} else {
272	  return(print_qdescr(ss,*sa));
273	}
274}
275
276static int
277print_woid(safe_string *ss, char *s)
278{
279	print_whsp(ss);
280	append_to_safe_string(ss,s);
281	return print_whsp(ss);
282}
283
284static int
285print_oidlist(safe_string *ss, char **sa)
286{
287	char **sp;
288
289	for (sp=sa; *(sp+1); sp++) {
290		print_woid(ss,*sp);
291		print_literal(ss,"$");
292	}
293	return(print_woid(ss,*sp));
294}
295
296static int
297print_oids(safe_string *ss, char **sa)
298{
299	if ( sa[0] && sa[1] ) {
300		print_literal(ss,"("/*)*/);
301		print_oidlist(ss,sa);
302		print_whsp(ss);
303		return(print_literal(ss,/*(*/")"));
304	} else {
305		return(print_woid(ss,*sa));
306	}
307}
308
309static int
310print_noidlen(safe_string *ss, char *s, int l)
311{
312	char buf[64];
313	int ret;
314
315	ret = print_numericoid(ss,s);
316	if ( l ) {
317		snprintf(buf, sizeof buf, "{%d}",l);
318		ret = print_literal(ss,buf);
319	}
320	return(ret);
321}
322
323static int
324print_ruleid(safe_string *ss, int rid)
325{
326	char buf[64];
327	snprintf(buf, sizeof buf, "%d", rid);
328	return print_literal(ss,buf);
329}
330
331static int
332print_ruleids(safe_string *ss, int n, int *rids)
333{
334	int i;
335
336	if( n == 1 ) {
337		print_ruleid(ss,rids[0]);
338		return print_whsp(ss);
339	} else {
340		print_literal(ss,"("/*)*/);
341		for( i=0; i<n; i++ ) {
342			print_whsp(ss);
343			print_ruleid(ss,rids[i]);
344		}
345		print_whsp(ss);
346		return print_literal(ss,/*(*/")");
347	}
348}
349
350
351static int
352print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions)
353{
354	LDAPSchemaExtensionItem **ext;
355
356	if ( extensions ) {
357		print_whsp(ss);
358		for ( ext = extensions; *ext != NULL; ext++ ) {
359			print_literal(ss, (*ext)->lsei_name);
360			print_whsp(ss);
361			/* Should be print_qdstrings */
362			print_qdescrs(ss, (*ext)->lsei_values);
363			print_whsp(ss);
364		}
365	}
366
367	return 0;
368}
369
370char *
371ldap_syntax2str( LDAPSyntax * syn )
372{
373	struct berval bv;
374	if (ldap_syntax2bv( syn, &bv ))
375		return(bv.bv_val);
376	else
377		return NULL;
378}
379
380struct berval *
381ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv )
382{
383	safe_string * ss;
384
385	ss = new_safe_string(256);
386	if ( !ss )
387		return NULL;
388
389	print_literal(ss,"("/*)*/);
390	print_whsp(ss);
391
392	print_numericoid(ss, syn->syn_oid);
393	print_whsp(ss);
394
395	if ( syn->syn_desc ) {
396		print_literal(ss,"DESC");
397		print_qdstring(ss,syn->syn_desc);
398	}
399
400	print_whsp(ss);
401
402	print_extensions(ss, syn->syn_extensions);
403
404	print_literal(ss,/*(*/ ")");
405
406	bv->bv_val = safe_strdup(ss);
407	bv->bv_len = ss->pos;
408	safe_string_free(ss);
409	return(bv);
410}
411
412char *
413ldap_matchingrule2str( LDAPMatchingRule * mr )
414{
415	struct berval bv;
416	if (ldap_matchingrule2bv( mr, &bv ))
417		return(bv.bv_val);
418	else
419		return NULL;
420}
421
422struct berval *
423ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv )
424{
425	safe_string * ss;
426
427	ss = new_safe_string(256);
428	if ( !ss )
429		return NULL;
430
431	print_literal(ss,"(" /*)*/);
432	print_whsp(ss);
433
434	print_numericoid(ss, mr->mr_oid);
435	print_whsp(ss);
436
437	if ( mr->mr_names ) {
438		print_literal(ss,"NAME");
439		print_qdescrs(ss,mr->mr_names);
440	}
441
442	if ( mr->mr_desc ) {
443		print_literal(ss,"DESC");
444		print_qdstring(ss,mr->mr_desc);
445	}
446
447	if ( mr->mr_obsolete ) {
448		print_literal(ss, "OBSOLETE");
449		print_whsp(ss);
450	}
451
452	if ( mr->mr_syntax_oid ) {
453		print_literal(ss,"SYNTAX");
454		print_whsp(ss);
455		print_literal(ss, mr->mr_syntax_oid);
456		print_whsp(ss);
457	}
458
459	print_whsp(ss);
460
461	print_extensions(ss, mr->mr_extensions);
462
463	print_literal(ss,/*(*/")");
464
465	bv->bv_val = safe_strdup(ss);
466	bv->bv_len = ss->pos;
467	safe_string_free(ss);
468	return(bv);
469}
470
471char *
472ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru )
473{
474	struct berval bv;
475	if (ldap_matchingruleuse2bv( mru, &bv ))
476		return(bv.bv_val);
477	else
478		return NULL;
479}
480
481struct berval *
482ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv )
483{
484	safe_string * ss;
485
486	ss = new_safe_string(256);
487	if ( !ss )
488		return NULL;
489
490	print_literal(ss,"(" /*)*/);
491	print_whsp(ss);
492
493	print_numericoid(ss, mru->mru_oid);
494	print_whsp(ss);
495
496	if ( mru->mru_names ) {
497		print_literal(ss,"NAME");
498		print_qdescrs(ss,mru->mru_names);
499	}
500
501	if ( mru->mru_desc ) {
502		print_literal(ss,"DESC");
503		print_qdstring(ss,mru->mru_desc);
504	}
505
506	if ( mru->mru_obsolete ) {
507		print_literal(ss, "OBSOLETE");
508		print_whsp(ss);
509	}
510
511	if ( mru->mru_applies_oids ) {
512		print_literal(ss,"APPLIES");
513		print_whsp(ss);
514		print_oids(ss, mru->mru_applies_oids);
515		print_whsp(ss);
516	}
517
518	print_whsp(ss);
519
520	print_extensions(ss, mru->mru_extensions);
521
522	print_literal(ss,/*(*/")");
523
524	bv->bv_val = safe_strdup(ss);
525	bv->bv_len = ss->pos;
526	safe_string_free(ss);
527	return(bv);
528}
529
530char *
531ldap_objectclass2str( LDAPObjectClass * oc )
532{
533	struct berval bv;
534	if (ldap_objectclass2bv( oc, &bv ))
535		return(bv.bv_val);
536	else
537		return NULL;
538}
539
540struct berval *
541ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv )
542{
543	safe_string * ss;
544
545	ss = new_safe_string(256);
546	if ( !ss )
547		return NULL;
548
549	print_literal(ss,"("/*)*/);
550	print_whsp(ss);
551
552	print_numericoid(ss, oc->oc_oid);
553	print_whsp(ss);
554
555	if ( oc->oc_names ) {
556		print_literal(ss,"NAME");
557		print_qdescrs(ss,oc->oc_names);
558	}
559
560	if ( oc->oc_desc ) {
561		print_literal(ss,"DESC");
562		print_qdstring(ss,oc->oc_desc);
563	}
564
565	if ( oc->oc_obsolete ) {
566		print_literal(ss, "OBSOLETE");
567		print_whsp(ss);
568	}
569
570	if ( oc->oc_sup_oids ) {
571		print_literal(ss,"SUP");
572		print_whsp(ss);
573		print_oids(ss,oc->oc_sup_oids);
574		print_whsp(ss);
575	}
576
577	switch (oc->oc_kind) {
578	case LDAP_SCHEMA_ABSTRACT:
579		print_literal(ss,"ABSTRACT");
580		break;
581	case LDAP_SCHEMA_STRUCTURAL:
582		print_literal(ss,"STRUCTURAL");
583		break;
584	case LDAP_SCHEMA_AUXILIARY:
585		print_literal(ss,"AUXILIARY");
586		break;
587	default:
588		print_literal(ss,"KIND-UNKNOWN");
589		break;
590	}
591	print_whsp(ss);
592
593	if ( oc->oc_at_oids_must ) {
594		print_literal(ss,"MUST");
595		print_whsp(ss);
596		print_oids(ss,oc->oc_at_oids_must);
597		print_whsp(ss);
598	}
599
600	if ( oc->oc_at_oids_may ) {
601		print_literal(ss,"MAY");
602		print_whsp(ss);
603		print_oids(ss,oc->oc_at_oids_may);
604		print_whsp(ss);
605	}
606
607	print_whsp(ss);
608
609	print_extensions(ss, oc->oc_extensions);
610
611	print_literal(ss, /*(*/")");
612
613	bv->bv_val = safe_strdup(ss);
614	bv->bv_len = ss->pos;
615	safe_string_free(ss);
616	return(bv);
617}
618
619char *
620ldap_contentrule2str( LDAPContentRule * cr )
621{
622	struct berval bv;
623	if (ldap_contentrule2bv( cr, &bv ))
624		return(bv.bv_val);
625	else
626		return NULL;
627}
628
629struct berval *
630ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv )
631{
632	safe_string * ss;
633
634	ss = new_safe_string(256);
635	if ( !ss )
636		return NULL;
637
638	print_literal(ss,"("/*)*/);
639	print_whsp(ss);
640
641	print_numericoid(ss, cr->cr_oid);
642	print_whsp(ss);
643
644	if ( cr->cr_names ) {
645		print_literal(ss,"NAME");
646		print_qdescrs(ss,cr->cr_names);
647	}
648
649	if ( cr->cr_desc ) {
650		print_literal(ss,"DESC");
651		print_qdstring(ss,cr->cr_desc);
652	}
653
654	if ( cr->cr_obsolete ) {
655		print_literal(ss, "OBSOLETE");
656		print_whsp(ss);
657	}
658
659	if ( cr->cr_oc_oids_aux ) {
660		print_literal(ss,"AUX");
661		print_whsp(ss);
662		print_oids(ss,cr->cr_oc_oids_aux);
663		print_whsp(ss);
664	}
665
666	if ( cr->cr_at_oids_must ) {
667		print_literal(ss,"MUST");
668		print_whsp(ss);
669		print_oids(ss,cr->cr_at_oids_must);
670		print_whsp(ss);
671	}
672
673	if ( cr->cr_at_oids_may ) {
674		print_literal(ss,"MAY");
675		print_whsp(ss);
676		print_oids(ss,cr->cr_at_oids_may);
677		print_whsp(ss);
678	}
679
680	if ( cr->cr_at_oids_not ) {
681		print_literal(ss,"NOT");
682		print_whsp(ss);
683		print_oids(ss,cr->cr_at_oids_not);
684		print_whsp(ss);
685	}
686
687	print_whsp(ss);
688	print_extensions(ss, cr->cr_extensions);
689
690	print_literal(ss, /*(*/")");
691
692	bv->bv_val = safe_strdup(ss);
693	bv->bv_len = ss->pos;
694	safe_string_free(ss);
695	return(bv);
696}
697
698char *
699ldap_structurerule2str( LDAPStructureRule * sr )
700{
701	struct berval bv;
702	if (ldap_structurerule2bv( sr, &bv ))
703		return(bv.bv_val);
704	else
705		return NULL;
706}
707
708struct berval *
709ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv )
710{
711	safe_string * ss;
712
713	ss = new_safe_string(256);
714	if ( !ss )
715		return NULL;
716
717	print_literal(ss,"("/*)*/);
718	print_whsp(ss);
719
720	print_ruleid(ss, sr->sr_ruleid);
721	print_whsp(ss);
722
723	if ( sr->sr_names ) {
724		print_literal(ss,"NAME");
725		print_qdescrs(ss,sr->sr_names);
726	}
727
728	if ( sr->sr_desc ) {
729		print_literal(ss,"DESC");
730		print_qdstring(ss,sr->sr_desc);
731	}
732
733	if ( sr->sr_obsolete ) {
734		print_literal(ss, "OBSOLETE");
735		print_whsp(ss);
736	}
737
738	print_literal(ss,"FORM");
739	print_whsp(ss);
740	print_woid(ss,sr->sr_nameform);
741	print_whsp(ss);
742
743	if ( sr->sr_nsup_ruleids ) {
744		print_literal(ss,"SUP");
745		print_whsp(ss);
746		print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids);
747		print_whsp(ss);
748	}
749
750	print_whsp(ss);
751	print_extensions(ss, sr->sr_extensions);
752
753	print_literal(ss, /*(*/")");
754
755	bv->bv_val = safe_strdup(ss);
756	bv->bv_len = ss->pos;
757	safe_string_free(ss);
758	return(bv);
759}
760
761
762char *
763ldap_nameform2str( LDAPNameForm * nf )
764{
765	struct berval bv;
766	if (ldap_nameform2bv( nf, &bv ))
767		return(bv.bv_val);
768	else
769		return NULL;
770}
771
772struct berval *
773ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv )
774{
775	safe_string * ss;
776
777	ss = new_safe_string(256);
778	if ( !ss )
779		return NULL;
780
781	print_literal(ss,"("/*)*/);
782	print_whsp(ss);
783
784	print_numericoid(ss, nf->nf_oid);
785	print_whsp(ss);
786
787	if ( nf->nf_names ) {
788		print_literal(ss,"NAME");
789		print_qdescrs(ss,nf->nf_names);
790	}
791
792	if ( nf->nf_desc ) {
793		print_literal(ss,"DESC");
794		print_qdstring(ss,nf->nf_desc);
795	}
796
797	if ( nf->nf_obsolete ) {
798		print_literal(ss, "OBSOLETE");
799		print_whsp(ss);
800	}
801
802	print_literal(ss,"OC");
803	print_whsp(ss);
804	print_woid(ss,nf->nf_objectclass);
805	print_whsp(ss);
806
807	print_literal(ss,"MUST");
808	print_whsp(ss);
809	print_oids(ss,nf->nf_at_oids_must);
810	print_whsp(ss);
811
812
813	if ( nf->nf_at_oids_may ) {
814		print_literal(ss,"MAY");
815		print_whsp(ss);
816		print_oids(ss,nf->nf_at_oids_may);
817		print_whsp(ss);
818	}
819
820	print_whsp(ss);
821	print_extensions(ss, nf->nf_extensions);
822
823	print_literal(ss, /*(*/")");
824
825	bv->bv_val = safe_strdup(ss);
826	bv->bv_len = ss->pos;
827	safe_string_free(ss);
828	return(bv);
829}
830
831char *
832ldap_attributetype2str( LDAPAttributeType * at )
833{
834	struct berval bv;
835	if (ldap_attributetype2bv( at, &bv ))
836		return(bv.bv_val);
837	else
838		return NULL;
839}
840
841struct berval *
842ldap_attributetype2bv(  LDAPAttributeType * at, struct berval *bv )
843{
844	safe_string * ss;
845
846	ss = new_safe_string(256);
847	if ( !ss )
848		return NULL;
849
850	print_literal(ss,"("/*)*/);
851	print_whsp(ss);
852
853	print_numericoid(ss, at->at_oid);
854	print_whsp(ss);
855
856	if ( at->at_names ) {
857		print_literal(ss,"NAME");
858		print_qdescrs(ss,at->at_names);
859	}
860
861	if ( at->at_desc ) {
862		print_literal(ss,"DESC");
863		print_qdstring(ss,at->at_desc);
864	}
865
866	if ( at->at_obsolete ) {
867		print_literal(ss, "OBSOLETE");
868		print_whsp(ss);
869	}
870
871	if ( at->at_sup_oid ) {
872		print_literal(ss,"SUP");
873		print_woid(ss,at->at_sup_oid);
874	}
875
876	if ( at->at_equality_oid ) {
877		print_literal(ss,"EQUALITY");
878		print_woid(ss,at->at_equality_oid);
879	}
880
881	if ( at->at_ordering_oid ) {
882		print_literal(ss,"ORDERING");
883		print_woid(ss,at->at_ordering_oid);
884	}
885
886	if ( at->at_substr_oid ) {
887		print_literal(ss,"SUBSTR");
888		print_woid(ss,at->at_substr_oid);
889	}
890
891	if ( at->at_syntax_oid ) {
892		print_literal(ss,"SYNTAX");
893		print_whsp(ss);
894		print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
895		print_whsp(ss);
896	}
897
898	if ( at->at_single_value == LDAP_SCHEMA_YES ) {
899		print_literal(ss,"SINGLE-VALUE");
900		print_whsp(ss);
901	}
902
903	if ( at->at_collective == LDAP_SCHEMA_YES ) {
904		print_literal(ss,"COLLECTIVE");
905		print_whsp(ss);
906	}
907
908	if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
909		print_literal(ss,"NO-USER-MODIFICATION");
910		print_whsp(ss);
911	}
912
913	if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
914		print_literal(ss,"USAGE");
915		print_whsp(ss);
916		switch (at->at_usage) {
917		case LDAP_SCHEMA_DIRECTORY_OPERATION:
918			print_literal(ss,"directoryOperation");
919			break;
920		case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
921			print_literal(ss,"distributedOperation");
922			break;
923		case LDAP_SCHEMA_DSA_OPERATION:
924			print_literal(ss,"dSAOperation");
925			break;
926		default:
927			print_literal(ss,"UNKNOWN");
928			break;
929		}
930	}
931
932	print_whsp(ss);
933
934	print_extensions(ss, at->at_extensions);
935
936	print_literal(ss,/*(*/")");
937
938	bv->bv_val = safe_strdup(ss);
939	bv->bv_len = ss->pos;
940	safe_string_free(ss);
941	return(bv);
942}
943
944/*
945 * Now come the parsers.  There is one parser for each entity type:
946 * objectclasses, attributetypes, etc.
947 *
948 * Each of them is written as a recursive-descent parser, except that
949 * none of them is really recursive.  But the idea is kept: there
950 * is one routine per non-terminal that eithers gobbles lexical tokens
951 * or calls lower-level routines, etc.
952 *
953 * The scanner is implemented in the routine get_token.  Actually,
954 * get_token is more than a scanner and will return tokens that are
955 * in fact non-terminals in the grammar.  So you can see the whole
956 * approach as the combination of a low-level bottom-up recognizer
957 * combined with a scanner and a number of top-down parsers.  Or just
958 * consider that the real grammars recognized by the parsers are not
959 * those of the standards.  As a matter of fact, our parsers are more
960 * liberal than the spec when there is no ambiguity.
961 *
962 * The difference is pretty academic (modulo bugs or incorrect
963 * interpretation of the specs).
964 */
965
966typedef enum tk_t {
967	TK_NOENDQUOTE	= -2,
968	TK_OUTOFMEM	= -1,
969	TK_EOS		= 0,
970	TK_UNEXPCHAR	= 1,
971	TK_BAREWORD	= 2,
972	TK_QDSTRING	= 3,
973	TK_LEFTPAREN	= 4,
974	TK_RIGHTPAREN	= 5,
975	TK_DOLLAR	= 6,
976	TK_QDESCR	= TK_QDSTRING
977} tk_t;
978
979static tk_t
980get_token( const char ** sp, char ** token_val )
981{
982	tk_t kind;
983	const char * p;
984	const char * q;
985	char * res;
986
987	*token_val = NULL;
988	switch (**sp) {
989	case '\0':
990		kind = TK_EOS;
991		(*sp)++;
992		break;
993	case '(':
994		kind = TK_LEFTPAREN;
995		(*sp)++;
996		break;
997	case ')':
998		kind = TK_RIGHTPAREN;
999		(*sp)++;
1000		break;
1001	case '$':
1002		kind = TK_DOLLAR;
1003		(*sp)++;
1004		break;
1005	case '\'':
1006		kind = TK_QDSTRING;
1007		(*sp)++;
1008		p = *sp;
1009		while ( **sp != '\'' && **sp != '\0' )
1010			(*sp)++;
1011		if ( **sp == '\'' ) {
1012			q = *sp;
1013			res = LDAP_MALLOC(q-p+1);
1014			if ( !res ) {
1015				kind = TK_OUTOFMEM;
1016			} else {
1017				strncpy(res,p,q-p);
1018				res[q-p] = '\0';
1019				*token_val = res;
1020			}
1021			(*sp)++;
1022		} else {
1023			kind = TK_NOENDQUOTE;
1024		}
1025		break;
1026	default:
1027		kind = TK_BAREWORD;
1028		p = *sp;
1029		while ( !LDAP_SPACE(**sp) &&
1030			**sp != '(' &&
1031			**sp != ')' &&
1032			**sp != '$' &&
1033			**sp != '\'' &&
1034			/* for suggested minimum upper bound on the number
1035			 * of characters (RFC 4517) */
1036			**sp != '{' &&
1037			**sp != '\0' )
1038			(*sp)++;
1039		q = *sp;
1040		res = LDAP_MALLOC(q-p+1);
1041		if ( !res ) {
1042			kind = TK_OUTOFMEM;
1043		} else {
1044			strncpy(res,p,q-p);
1045			res[q-p] = '\0';
1046			*token_val = res;
1047		}
1048		break;
1049/*  		kind = TK_UNEXPCHAR; */
1050/*  		break; */
1051	}
1052
1053	return kind;
1054}
1055
1056/* Gobble optional whitespace */
1057static void
1058parse_whsp(const char **sp)
1059{
1060	while (LDAP_SPACE(**sp))
1061		(*sp)++;
1062}
1063
1064/* TBC:!!
1065 * General note for all parsers: to guarantee the algorithm halts they
1066 * must always advance the pointer even when an error is found.  For
1067 * this one is not that important since an error here is fatal at the
1068 * upper layers, but it is a simple strategy that will not get in
1069 * endless loops.
1070 */
1071
1072/* Parse a sequence of dot-separated decimal strings */
1073char *
1074ldap_int_parse_numericoid(const char **sp, int *code, const int flags)
1075{
1076	char * res = NULL;
1077	const char * start = *sp;
1078	int len;
1079	int quoted = 0;
1080
1081	/* Netscape puts the SYNTAX value in quotes (incorrectly) */
1082	if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
1083		quoted = 1;
1084		(*sp)++;
1085		start++;
1086	}
1087	/* Each iteration of this loop gets one decimal string */
1088	while (**sp) {
1089		if ( !LDAP_DIGIT(**sp) ) {
1090			/*
1091			 * Initial char is not a digit or char after dot is
1092			 * not a digit
1093			 */
1094			*code = LDAP_SCHERR_NODIGIT;
1095			return NULL;
1096		}
1097		(*sp)++;
1098		while ( LDAP_DIGIT(**sp) )
1099			(*sp)++;
1100		if ( **sp != '.' )
1101			break;
1102		/* Otherwise, gobble the dot and loop again */
1103		(*sp)++;
1104	}
1105	/* Now *sp points at the char past the numericoid. Perfect. */
1106	len = *sp - start;
1107	if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
1108		if ( **sp == '\'' ) {
1109			(*sp)++;
1110		} else {
1111			*code = LDAP_SCHERR_UNEXPTOKEN;
1112			return NULL;
1113		}
1114	}
1115	if (flags & LDAP_SCHEMA_SKIP) {
1116		res = (char *)start;
1117	} else {
1118		res = LDAP_MALLOC(len+1);
1119		if (!res) {
1120			*code = LDAP_SCHERR_OUTOFMEM;
1121			return(NULL);
1122		}
1123		strncpy(res,start,len);
1124		res[len] = '\0';
1125	}
1126	return(res);
1127}
1128
1129/* Parse a sequence of dot-separated decimal strings */
1130int
1131ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid)
1132{
1133	*ruleid=0;
1134
1135	if ( !LDAP_DIGIT(**sp) ) {
1136		*code = LDAP_SCHERR_NODIGIT;
1137		return -1;
1138	}
1139	*ruleid = (**sp) - '0';
1140	(*sp)++;
1141
1142	while ( LDAP_DIGIT(**sp) ) {
1143		*ruleid *= 10;
1144		*ruleid += (**sp) - '0';
1145		(*sp)++;
1146	}
1147
1148	return 0;
1149}
1150
1151/* Parse a qdescr or a list of them enclosed in () */
1152static char **
1153parse_qdescrs(const char **sp, int *code)
1154{
1155	char ** res;
1156	char ** res1;
1157	tk_t kind;
1158	char * sval;
1159	int size;
1160	int pos;
1161
1162	parse_whsp(sp);
1163	kind = get_token(sp,&sval);
1164	if ( kind == TK_LEFTPAREN ) {
1165		/* Let's presume there will be at least 2 entries */
1166		size = 3;
1167		res = LDAP_CALLOC(3,sizeof(char *));
1168		if ( !res ) {
1169			*code = LDAP_SCHERR_OUTOFMEM;
1170			return NULL;
1171		}
1172		pos = 0;
1173		while (1) {
1174			parse_whsp(sp);
1175			kind = get_token(sp,&sval);
1176			if ( kind == TK_RIGHTPAREN )
1177				break;
1178			if ( kind == TK_QDESCR ) {
1179				if ( pos == size-2 ) {
1180					size++;
1181					res1 = LDAP_REALLOC(res,size*sizeof(char *));
1182					if ( !res1 ) {
1183						LDAP_VFREE(res);
1184						LDAP_FREE(sval);
1185						*code = LDAP_SCHERR_OUTOFMEM;
1186						return(NULL);
1187					}
1188					res = res1;
1189				}
1190				res[pos++] = sval;
1191				res[pos] = NULL;
1192				parse_whsp(sp);
1193			} else {
1194				LDAP_VFREE(res);
1195				LDAP_FREE(sval);
1196				*code = LDAP_SCHERR_UNEXPTOKEN;
1197				return(NULL);
1198			}
1199		}
1200		parse_whsp(sp);
1201		return(res);
1202	} else if ( kind == TK_QDESCR ) {
1203		res = LDAP_CALLOC(2,sizeof(char *));
1204		if ( !res ) {
1205			*code = LDAP_SCHERR_OUTOFMEM;
1206			return NULL;
1207		}
1208		res[0] = sval;
1209		res[1] = NULL;
1210		parse_whsp(sp);
1211		return res;
1212	} else {
1213		LDAP_FREE(sval);
1214		*code = LDAP_SCHERR_BADNAME;
1215		return NULL;
1216	}
1217}
1218
1219/* Parse a woid */
1220static char *
1221parse_woid(const char **sp, int *code)
1222{
1223	char * sval;
1224	tk_t kind;
1225
1226	parse_whsp(sp);
1227	kind = get_token(sp, &sval);
1228	if ( kind != TK_BAREWORD ) {
1229		LDAP_FREE(sval);
1230		*code = LDAP_SCHERR_UNEXPTOKEN;
1231		return NULL;
1232	}
1233	parse_whsp(sp);
1234	return sval;
1235}
1236
1237/* Parse a noidlen */
1238static char *
1239parse_noidlen(const char **sp, int *code, int *len, int flags)
1240{
1241	char * sval;
1242	const char *savepos;
1243	int quoted = 0;
1244	int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED );
1245	int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO );
1246
1247	*len = 0;
1248	/* Netscape puts the SYNTAX value in quotes (incorrectly) */
1249	if ( allow_quoted && **sp == '\'' ) {
1250		quoted = 1;
1251		(*sp)++;
1252	}
1253	savepos = *sp;
1254	sval = ldap_int_parse_numericoid(sp, code, 0);
1255	if ( !sval ) {
1256		if ( allow_oidmacro
1257			&& *sp == savepos
1258			&& *code == LDAP_SCHERR_NODIGIT )
1259		{
1260			if ( get_token(sp, &sval) != TK_BAREWORD ) {
1261				if ( sval != NULL ) {
1262					LDAP_FREE(sval);
1263				}
1264				return NULL;
1265			}
1266		} else {
1267			return NULL;
1268		}
1269	}
1270	if ( **sp == '{' /*}*/ ) {
1271		(*sp)++;
1272		*len = atoi(*sp);
1273		while ( LDAP_DIGIT(**sp) )
1274			(*sp)++;
1275		if ( **sp != /*{*/ '}' ) {
1276			*code = LDAP_SCHERR_UNEXPTOKEN;
1277			LDAP_FREE(sval);
1278			return NULL;
1279		}
1280		(*sp)++;
1281	}
1282	if ( allow_quoted && quoted ) {
1283		if ( **sp == '\'' ) {
1284			(*sp)++;
1285		} else {
1286			*code = LDAP_SCHERR_UNEXPTOKEN;
1287			LDAP_FREE(sval);
1288			return NULL;
1289		}
1290	}
1291	return sval;
1292}
1293
1294/*
1295 * Next routine will accept a qdstring in place of an oid if
1296 * allow_quoted is set.  This is necessary to interoperate with
1297 * Netscape Directory server that will improperly quote each oid (at
1298 * least those of the descr kind) in the SUP clause.
1299 */
1300
1301/* Parse a woid or a $-separated list of them enclosed in () */
1302static char **
1303parse_oids(const char **sp, int *code, const int allow_quoted)
1304{
1305	char ** res;
1306	char ** res1;
1307	tk_t kind;
1308	char * sval;
1309	int size;
1310	int pos;
1311
1312	/*
1313	 * Strictly speaking, doing this here accepts whsp before the
1314	 * ( at the begining of an oidlist, but this is harmless.  Also,
1315	 * we are very liberal in what we accept as an OID.  Maybe
1316	 * refine later.
1317	 */
1318	parse_whsp(sp);
1319	kind = get_token(sp,&sval);
1320	if ( kind == TK_LEFTPAREN ) {
1321		/* Let's presume there will be at least 2 entries */
1322		size = 3;
1323		res = LDAP_CALLOC(3,sizeof(char *));
1324		if ( !res ) {
1325			*code = LDAP_SCHERR_OUTOFMEM;
1326			return NULL;
1327		}
1328		pos = 0;
1329		parse_whsp(sp);
1330		kind = get_token(sp,&sval);
1331		if ( kind == TK_BAREWORD ||
1332		     ( allow_quoted && kind == TK_QDSTRING ) ) {
1333			res[pos++] = sval;
1334			res[pos] = NULL;
1335		} else if ( kind == TK_RIGHTPAREN ) {
1336			/* FIXME: be liberal in what we accept... */
1337			parse_whsp(sp);
1338			LDAP_FREE(res);
1339			return NULL;
1340		} else {
1341			*code = LDAP_SCHERR_UNEXPTOKEN;
1342			LDAP_FREE(sval);
1343			LDAP_VFREE(res);
1344			return NULL;
1345		}
1346		parse_whsp(sp);
1347		while (1) {
1348			kind = get_token(sp,&sval);
1349			if ( kind == TK_RIGHTPAREN )
1350				break;
1351			if ( kind == TK_DOLLAR ) {
1352				parse_whsp(sp);
1353				kind = get_token(sp,&sval);
1354				if ( kind == TK_BAREWORD ||
1355				     ( allow_quoted &&
1356				       kind == TK_QDSTRING ) ) {
1357					if ( pos == size-2 ) {
1358						size++;
1359						res1 = LDAP_REALLOC(res,size*sizeof(char *));
1360						if ( !res1 ) {
1361							LDAP_FREE(sval);
1362							LDAP_VFREE(res);
1363							*code = LDAP_SCHERR_OUTOFMEM;
1364							return(NULL);
1365						}
1366						res = res1;
1367					}
1368					res[pos++] = sval;
1369					res[pos] = NULL;
1370				} else {
1371					*code = LDAP_SCHERR_UNEXPTOKEN;
1372					LDAP_FREE(sval);
1373					LDAP_VFREE(res);
1374					return NULL;
1375				}
1376				parse_whsp(sp);
1377			} else {
1378				*code = LDAP_SCHERR_UNEXPTOKEN;
1379				LDAP_FREE(sval);
1380				LDAP_VFREE(res);
1381				return NULL;
1382			}
1383		}
1384		parse_whsp(sp);
1385		return(res);
1386	} else if ( kind == TK_BAREWORD ||
1387		    ( allow_quoted && kind == TK_QDSTRING ) ) {
1388		res = LDAP_CALLOC(2,sizeof(char *));
1389		if ( !res ) {
1390			LDAP_FREE(sval);
1391			*code = LDAP_SCHERR_OUTOFMEM;
1392			return NULL;
1393		}
1394		res[0] = sval;
1395		res[1] = NULL;
1396		parse_whsp(sp);
1397		return res;
1398	} else {
1399		LDAP_FREE(sval);
1400		*code = LDAP_SCHERR_BADNAME;
1401		return NULL;
1402	}
1403}
1404
1405static int
1406add_extension(LDAPSchemaExtensionItem ***extensions,
1407	      char * name, char ** values)
1408{
1409	int n;
1410	LDAPSchemaExtensionItem **tmp, *ext;
1411
1412	ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
1413	if ( !ext )
1414		return 1;
1415	ext->lsei_name = name;
1416	ext->lsei_values = values;
1417
1418	if ( !*extensions ) {
1419		*extensions =
1420		  LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
1421		if ( !*extensions ) {
1422			LDAP_FREE( ext );
1423			return 1;
1424		}
1425		n = 0;
1426	} else {
1427		for ( n=0; (*extensions)[n] != NULL; n++ )
1428	  		;
1429		tmp = LDAP_REALLOC(*extensions,
1430				   (n+2)*sizeof(LDAPSchemaExtensionItem *));
1431		if ( !tmp ) {
1432			LDAP_FREE( ext );
1433			return 1;
1434		}
1435		*extensions = tmp;
1436	}
1437	(*extensions)[n] = ext;
1438	(*extensions)[n+1] = NULL;
1439	return 0;
1440}
1441
1442static void
1443free_extensions(LDAPSchemaExtensionItem **extensions)
1444{
1445	LDAPSchemaExtensionItem **ext;
1446
1447	if ( extensions ) {
1448		for ( ext = extensions; *ext != NULL; ext++ ) {
1449			LDAP_FREE((*ext)->lsei_name);
1450			LDAP_VFREE((*ext)->lsei_values);
1451			LDAP_FREE(*ext);
1452		}
1453		LDAP_FREE(extensions);
1454	}
1455}
1456
1457void
1458ldap_syntax_free( LDAPSyntax * syn )
1459{
1460	LDAP_FREE(syn->syn_oid);
1461	if (syn->syn_names) LDAP_VFREE(syn->syn_names);
1462	if (syn->syn_desc) LDAP_FREE(syn->syn_desc);
1463	free_extensions(syn->syn_extensions);
1464	LDAP_FREE(syn);
1465}
1466
1467LDAPSyntax *
1468ldap_str2syntax( LDAP_CONST char * s,
1469	int * code,
1470	LDAP_CONST char ** errp,
1471	LDAP_CONST unsigned flags )
1472{
1473	tk_t kind;
1474	const char * ss = s;
1475	char * sval;
1476	int seen_name = 0;
1477	int seen_desc = 0;
1478	LDAPSyntax * syn;
1479	char ** ext_vals;
1480
1481	if ( !s ) {
1482		*code = LDAP_SCHERR_EMPTY;
1483		*errp = "";
1484		return NULL;
1485	}
1486
1487	*errp = s;
1488	syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
1489
1490	if ( !syn ) {
1491		*code = LDAP_SCHERR_OUTOFMEM;
1492		return NULL;
1493	}
1494
1495	kind = get_token(&ss,&sval);
1496	if ( kind != TK_LEFTPAREN ) {
1497		LDAP_FREE(sval);
1498		*code = LDAP_SCHERR_NOLEFTPAREN;
1499		ldap_syntax_free(syn);
1500		return NULL;
1501	}
1502
1503	parse_whsp(&ss);
1504	syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0);
1505	if ( !syn->syn_oid ) {
1506		*errp = ss;
1507		ldap_syntax_free(syn);
1508		return NULL;
1509	}
1510	parse_whsp(&ss);
1511
1512	/*
1513	 * Beyond this point we will be liberal and accept the items
1514	 * in any order.
1515	 */
1516	while (1) {
1517		kind = get_token(&ss,&sval);
1518		switch (kind) {
1519		case TK_EOS:
1520			*code = LDAP_SCHERR_NORIGHTPAREN;
1521			*errp = EndOfInput;
1522			ldap_syntax_free(syn);
1523			return NULL;
1524		case TK_RIGHTPAREN:
1525			return syn;
1526		case TK_BAREWORD:
1527			if ( !strcasecmp(sval,"NAME") ) {
1528				LDAP_FREE(sval);
1529				if ( seen_name ) {
1530					*code = LDAP_SCHERR_DUPOPT;
1531					*errp = ss;
1532					ldap_syntax_free(syn);
1533					return(NULL);
1534				}
1535				seen_name = 1;
1536				syn->syn_names = parse_qdescrs(&ss,code);
1537				if ( !syn->syn_names ) {
1538					if ( *code != LDAP_SCHERR_OUTOFMEM )
1539						*code = LDAP_SCHERR_BADNAME;
1540					*errp = ss;
1541					ldap_syntax_free(syn);
1542					return NULL;
1543				}
1544			} else if ( !strcasecmp(sval,"DESC") ) {
1545				LDAP_FREE(sval);
1546				if ( seen_desc ) {
1547					*code = LDAP_SCHERR_DUPOPT;
1548					*errp = ss;
1549					ldap_syntax_free(syn);
1550					return(NULL);
1551				}
1552				seen_desc = 1;
1553				parse_whsp(&ss);
1554				kind = get_token(&ss,&sval);
1555				if ( kind != TK_QDSTRING ) {
1556					*code = LDAP_SCHERR_UNEXPTOKEN;
1557					*errp = ss;
1558					LDAP_FREE(sval);
1559					ldap_syntax_free(syn);
1560					return NULL;
1561				}
1562				syn->syn_desc = sval;
1563				parse_whsp(&ss);
1564			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
1565				/* Should be parse_qdstrings */
1566				ext_vals = parse_qdescrs(&ss, code);
1567				if ( !ext_vals ) {
1568					*errp = ss;
1569					ldap_syntax_free(syn);
1570					return NULL;
1571				}
1572				if ( add_extension(&syn->syn_extensions,
1573						    sval, ext_vals) ) {
1574					*code = LDAP_SCHERR_OUTOFMEM;
1575					*errp = ss;
1576					LDAP_FREE(sval);
1577					ldap_syntax_free(syn);
1578					return NULL;
1579				}
1580			} else {
1581				*code = LDAP_SCHERR_UNEXPTOKEN;
1582				*errp = ss;
1583				LDAP_FREE(sval);
1584				ldap_syntax_free(syn);
1585				return NULL;
1586			}
1587			break;
1588		default:
1589			*code = LDAP_SCHERR_UNEXPTOKEN;
1590			*errp = ss;
1591			LDAP_FREE(sval);
1592			ldap_syntax_free(syn);
1593			return NULL;
1594		}
1595	}
1596}
1597
1598void
1599ldap_matchingrule_free( LDAPMatchingRule * mr )
1600{
1601	LDAP_FREE(mr->mr_oid);
1602	if (mr->mr_names) LDAP_VFREE(mr->mr_names);
1603	if (mr->mr_desc) LDAP_FREE(mr->mr_desc);
1604	if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid);
1605	free_extensions(mr->mr_extensions);
1606	LDAP_FREE(mr);
1607}
1608
1609LDAPMatchingRule *
1610ldap_str2matchingrule( LDAP_CONST char * s,
1611	int * code,
1612	LDAP_CONST char ** errp,
1613	LDAP_CONST unsigned flags )
1614{
1615	tk_t kind;
1616	const char * ss = s;
1617	char * sval;
1618	int seen_name = 0;
1619	int seen_desc = 0;
1620	int seen_obsolete = 0;
1621	int seen_syntax = 0;
1622	LDAPMatchingRule * mr;
1623	char ** ext_vals;
1624	const char * savepos;
1625
1626	if ( !s ) {
1627		*code = LDAP_SCHERR_EMPTY;
1628		*errp = "";
1629		return NULL;
1630	}
1631
1632	*errp = s;
1633	mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1634
1635	if ( !mr ) {
1636		*code = LDAP_SCHERR_OUTOFMEM;
1637		return NULL;
1638	}
1639
1640	kind = get_token(&ss,&sval);
1641	if ( kind != TK_LEFTPAREN ) {
1642		*code = LDAP_SCHERR_NOLEFTPAREN;
1643		LDAP_FREE(sval);
1644		ldap_matchingrule_free(mr);
1645		return NULL;
1646	}
1647
1648	parse_whsp(&ss);
1649	savepos = ss;
1650	mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags);
1651	if ( !mr->mr_oid ) {
1652		if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1653			/* Backtracking */
1654			ss = savepos;
1655			kind = get_token(&ss,&sval);
1656			if ( kind == TK_BAREWORD ) {
1657				if ( !strcasecmp(sval, "NAME") ||
1658				     !strcasecmp(sval, "DESC") ||
1659				     !strcasecmp(sval, "OBSOLETE") ||
1660				     !strcasecmp(sval, "SYNTAX") ||
1661				     !strncasecmp(sval, "X-", 2) ) {
1662					/* Missing OID, backtrack */
1663					ss = savepos;
1664				} else {
1665					/* Non-numerical OID, ignore */
1666				}
1667			}
1668			LDAP_FREE(sval);
1669		} else {
1670			*errp = ss;
1671			ldap_matchingrule_free(mr);
1672			return NULL;
1673		}
1674	}
1675	parse_whsp(&ss);
1676
1677	/*
1678	 * Beyond this point we will be liberal and accept the items
1679	 * in any order.
1680	 */
1681	while (1) {
1682		kind = get_token(&ss,&sval);
1683		switch (kind) {
1684		case TK_EOS:
1685			*code = LDAP_SCHERR_NORIGHTPAREN;
1686			*errp = EndOfInput;
1687			ldap_matchingrule_free(mr);
1688			return NULL;
1689		case TK_RIGHTPAREN:
1690			if( !seen_syntax ) {
1691				*code = LDAP_SCHERR_MISSING;
1692				ldap_matchingrule_free(mr);
1693				return NULL;
1694			}
1695			return mr;
1696		case TK_BAREWORD:
1697			if ( !strcasecmp(sval,"NAME") ) {
1698				LDAP_FREE(sval);
1699				if ( seen_name ) {
1700					*code = LDAP_SCHERR_DUPOPT;
1701					*errp = ss;
1702					ldap_matchingrule_free(mr);
1703					return(NULL);
1704				}
1705				seen_name = 1;
1706				mr->mr_names = parse_qdescrs(&ss,code);
1707				if ( !mr->mr_names ) {
1708					if ( *code != LDAP_SCHERR_OUTOFMEM )
1709						*code = LDAP_SCHERR_BADNAME;
1710					*errp = ss;
1711					ldap_matchingrule_free(mr);
1712					return NULL;
1713				}
1714			} else if ( !strcasecmp(sval,"DESC") ) {
1715				LDAP_FREE(sval);
1716				if ( seen_desc ) {
1717					*code = LDAP_SCHERR_DUPOPT;
1718					*errp = ss;
1719					ldap_matchingrule_free(mr);
1720					return(NULL);
1721				}
1722				seen_desc = 1;
1723				parse_whsp(&ss);
1724				kind = get_token(&ss,&sval);
1725				if ( kind != TK_QDSTRING ) {
1726					*code = LDAP_SCHERR_UNEXPTOKEN;
1727					*errp = ss;
1728					LDAP_FREE(sval);
1729					ldap_matchingrule_free(mr);
1730					return NULL;
1731				}
1732				mr->mr_desc = sval;
1733				parse_whsp(&ss);
1734			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
1735				LDAP_FREE(sval);
1736				if ( seen_obsolete ) {
1737					*code = LDAP_SCHERR_DUPOPT;
1738					*errp = ss;
1739					ldap_matchingrule_free(mr);
1740					return(NULL);
1741				}
1742				seen_obsolete = 1;
1743				mr->mr_obsolete = LDAP_SCHEMA_YES;
1744				parse_whsp(&ss);
1745			} else if ( !strcasecmp(sval,"SYNTAX") ) {
1746				LDAP_FREE(sval);
1747				if ( seen_syntax ) {
1748					*code = LDAP_SCHERR_DUPOPT;
1749					*errp = ss;
1750					ldap_matchingrule_free(mr);
1751					return(NULL);
1752				}
1753				seen_syntax = 1;
1754				parse_whsp(&ss);
1755				mr->mr_syntax_oid =
1756					ldap_int_parse_numericoid(&ss,code,flags);
1757				if ( !mr->mr_syntax_oid ) {
1758					*errp = ss;
1759					ldap_matchingrule_free(mr);
1760					return NULL;
1761				}
1762				parse_whsp(&ss);
1763			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
1764				/* Should be parse_qdstrings */
1765				ext_vals = parse_qdescrs(&ss, code);
1766				if ( !ext_vals ) {
1767					*errp = ss;
1768					ldap_matchingrule_free(mr);
1769					return NULL;
1770				}
1771				if ( add_extension(&mr->mr_extensions,
1772						    sval, ext_vals) ) {
1773					*code = LDAP_SCHERR_OUTOFMEM;
1774					*errp = ss;
1775					LDAP_FREE(sval);
1776					ldap_matchingrule_free(mr);
1777					return NULL;
1778				}
1779			} else {
1780				*code = LDAP_SCHERR_UNEXPTOKEN;
1781				*errp = ss;
1782				LDAP_FREE(sval);
1783				ldap_matchingrule_free(mr);
1784				return NULL;
1785			}
1786			break;
1787		default:
1788			*code = LDAP_SCHERR_UNEXPTOKEN;
1789			*errp = ss;
1790			LDAP_FREE(sval);
1791			ldap_matchingrule_free(mr);
1792			return NULL;
1793		}
1794	}
1795}
1796
1797void
1798ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
1799{
1800	LDAP_FREE(mru->mru_oid);
1801	if (mru->mru_names) LDAP_VFREE(mru->mru_names);
1802	if (mru->mru_desc) LDAP_FREE(mru->mru_desc);
1803	if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids);
1804	free_extensions(mru->mru_extensions);
1805	LDAP_FREE(mru);
1806}
1807
1808LDAPMatchingRuleUse *
1809ldap_str2matchingruleuse( LDAP_CONST char * s,
1810	int * code,
1811	LDAP_CONST char ** errp,
1812	LDAP_CONST unsigned flags )
1813{
1814	tk_t kind;
1815	const char * ss = s;
1816	char * sval;
1817	int seen_name = 0;
1818	int seen_desc = 0;
1819	int seen_obsolete = 0;
1820	int seen_applies = 0;
1821	LDAPMatchingRuleUse * mru;
1822	char ** ext_vals;
1823	const char * savepos;
1824
1825	if ( !s ) {
1826		*code = LDAP_SCHERR_EMPTY;
1827		*errp = "";
1828		return NULL;
1829	}
1830
1831	*errp = s;
1832	mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
1833
1834	if ( !mru ) {
1835		*code = LDAP_SCHERR_OUTOFMEM;
1836		return NULL;
1837	}
1838
1839	kind = get_token(&ss,&sval);
1840	if ( kind != TK_LEFTPAREN ) {
1841		*code = LDAP_SCHERR_NOLEFTPAREN;
1842		LDAP_FREE(sval);
1843		ldap_matchingruleuse_free(mru);
1844		return NULL;
1845	}
1846
1847	parse_whsp(&ss);
1848	savepos = ss;
1849	mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags);
1850	if ( !mru->mru_oid ) {
1851		if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1852			/* Backtracking */
1853			ss = savepos;
1854			kind = get_token(&ss,&sval);
1855			if ( kind == TK_BAREWORD ) {
1856				if ( !strcasecmp(sval, "NAME") ||
1857				     !strcasecmp(sval, "DESC") ||
1858				     !strcasecmp(sval, "OBSOLETE") ||
1859				     !strcasecmp(sval, "APPLIES") ||
1860				     !strncasecmp(sval, "X-", 2) ) {
1861					/* Missing OID, backtrack */
1862					ss = savepos;
1863				} else {
1864					/* Non-numerical OID, ignore */
1865				}
1866			}
1867			LDAP_FREE(sval);
1868		} else {
1869			*errp = ss;
1870			ldap_matchingruleuse_free(mru);
1871			return NULL;
1872		}
1873	}
1874	parse_whsp(&ss);
1875
1876	/*
1877	 * Beyond this point we will be liberal and accept the items
1878	 * in any order.
1879	 */
1880	while (1) {
1881		kind = get_token(&ss,&sval);
1882		switch (kind) {
1883		case TK_EOS:
1884			*code = LDAP_SCHERR_NORIGHTPAREN;
1885			*errp = EndOfInput;
1886			ldap_matchingruleuse_free(mru);
1887			return NULL;
1888		case TK_RIGHTPAREN:
1889			if( !seen_applies ) {
1890				*code = LDAP_SCHERR_MISSING;
1891				ldap_matchingruleuse_free(mru);
1892				return NULL;
1893			}
1894			return mru;
1895		case TK_BAREWORD:
1896			if ( !strcasecmp(sval,"NAME") ) {
1897				LDAP_FREE(sval);
1898				if ( seen_name ) {
1899					*code = LDAP_SCHERR_DUPOPT;
1900					*errp = ss;
1901					ldap_matchingruleuse_free(mru);
1902					return(NULL);
1903				}
1904				seen_name = 1;
1905				mru->mru_names = parse_qdescrs(&ss,code);
1906				if ( !mru->mru_names ) {
1907					if ( *code != LDAP_SCHERR_OUTOFMEM )
1908						*code = LDAP_SCHERR_BADNAME;
1909					*errp = ss;
1910					ldap_matchingruleuse_free(mru);
1911					return NULL;
1912				}
1913			} else if ( !strcasecmp(sval,"DESC") ) {
1914				LDAP_FREE(sval);
1915				if ( seen_desc ) {
1916					*code = LDAP_SCHERR_DUPOPT;
1917					*errp = ss;
1918					ldap_matchingruleuse_free(mru);
1919					return(NULL);
1920				}
1921				seen_desc = 1;
1922				parse_whsp(&ss);
1923				kind = get_token(&ss,&sval);
1924				if ( kind != TK_QDSTRING ) {
1925					*code = LDAP_SCHERR_UNEXPTOKEN;
1926					*errp = ss;
1927					LDAP_FREE(sval);
1928					ldap_matchingruleuse_free(mru);
1929					return NULL;
1930				}
1931				mru->mru_desc = sval;
1932				parse_whsp(&ss);
1933			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
1934				LDAP_FREE(sval);
1935				if ( seen_obsolete ) {
1936					*code = LDAP_SCHERR_DUPOPT;
1937					*errp = ss;
1938					ldap_matchingruleuse_free(mru);
1939					return(NULL);
1940				}
1941				seen_obsolete = 1;
1942				mru->mru_obsolete = LDAP_SCHEMA_YES;
1943				parse_whsp(&ss);
1944			} else if ( !strcasecmp(sval,"APPLIES") ) {
1945				LDAP_FREE(sval);
1946				if ( seen_applies ) {
1947					*code = LDAP_SCHERR_DUPOPT;
1948					*errp = ss;
1949					ldap_matchingruleuse_free(mru);
1950					return(NULL);
1951				}
1952				seen_applies = 1;
1953				mru->mru_applies_oids = parse_oids(&ss,
1954							     code,
1955							     flags);
1956				if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) {
1957					*errp = ss;
1958					ldap_matchingruleuse_free(mru);
1959					return NULL;
1960				}
1961			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
1962				/* Should be parse_qdstrings */
1963				ext_vals = parse_qdescrs(&ss, code);
1964				if ( !ext_vals ) {
1965					*errp = ss;
1966					ldap_matchingruleuse_free(mru);
1967					return NULL;
1968				}
1969				if ( add_extension(&mru->mru_extensions,
1970						    sval, ext_vals) ) {
1971					*code = LDAP_SCHERR_OUTOFMEM;
1972					*errp = ss;
1973					LDAP_FREE(sval);
1974					ldap_matchingruleuse_free(mru);
1975					return NULL;
1976				}
1977			} else {
1978				*code = LDAP_SCHERR_UNEXPTOKEN;
1979				*errp = ss;
1980				LDAP_FREE(sval);
1981				ldap_matchingruleuse_free(mru);
1982				return NULL;
1983			}
1984			break;
1985		default:
1986			*code = LDAP_SCHERR_UNEXPTOKEN;
1987			*errp = ss;
1988			LDAP_FREE(sval);
1989			ldap_matchingruleuse_free(mru);
1990			return NULL;
1991		}
1992	}
1993}
1994
1995void
1996ldap_attributetype_free(LDAPAttributeType * at)
1997{
1998	LDAP_FREE(at->at_oid);
1999	if (at->at_names) LDAP_VFREE(at->at_names);
2000	if (at->at_desc) LDAP_FREE(at->at_desc);
2001	if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid);
2002	if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid);
2003	if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid);
2004	if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid);
2005	if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid);
2006	free_extensions(at->at_extensions);
2007	LDAP_FREE(at);
2008}
2009
2010LDAPAttributeType *
2011ldap_str2attributetype( LDAP_CONST char * s,
2012	int * code,
2013	LDAP_CONST char ** errp,
2014	LDAP_CONST unsigned flags )
2015{
2016	tk_t kind;
2017	const char * ss = s;
2018	char * sval;
2019	int seen_name = 0;
2020	int seen_desc = 0;
2021	int seen_obsolete = 0;
2022	int seen_sup = 0;
2023	int seen_equality = 0;
2024	int seen_ordering = 0;
2025	int seen_substr = 0;
2026	int seen_syntax = 0;
2027	int seen_usage = 0;
2028	LDAPAttributeType * at;
2029	char ** ext_vals;
2030	const char * savepos;
2031
2032	if ( !s ) {
2033		*code = LDAP_SCHERR_EMPTY;
2034		*errp = "";
2035		return NULL;
2036	}
2037
2038	*errp = s;
2039	at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
2040
2041	if ( !at ) {
2042		*code = LDAP_SCHERR_OUTOFMEM;
2043		return NULL;
2044	}
2045
2046	kind = get_token(&ss,&sval);
2047	if ( kind != TK_LEFTPAREN ) {
2048		*code = LDAP_SCHERR_NOLEFTPAREN;
2049		LDAP_FREE(sval);
2050		ldap_attributetype_free(at);
2051		return NULL;
2052	}
2053
2054	/*
2055	 * Definitions MUST begin with an OID in the numericoid format.
2056	 * However, this routine is used by clients to parse the response
2057	 * from servers and very well known servers will provide an OID
2058	 * in the wrong format or even no OID at all.  We do our best to
2059	 * extract info from those servers.
2060	 */
2061	parse_whsp(&ss);
2062	savepos = ss;
2063	at->at_oid = ldap_int_parse_numericoid(&ss,code,0);
2064	if ( !at->at_oid ) {
2065		if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
2066				| LDAP_SCHEMA_ALLOW_OID_MACRO ) )
2067			    && (ss == savepos) )
2068		{
2069			/* Backtracking */
2070			ss = savepos;
2071			kind = get_token(&ss,&sval);
2072			if ( kind == TK_BAREWORD ) {
2073				if ( !strcasecmp(sval, "NAME") ||
2074				     !strcasecmp(sval, "DESC") ||
2075				     !strcasecmp(sval, "OBSOLETE") ||
2076				     !strcasecmp(sval, "SUP") ||
2077				     !strcasecmp(sval, "EQUALITY") ||
2078				     !strcasecmp(sval, "ORDERING") ||
2079				     !strcasecmp(sval, "SUBSTR") ||
2080				     !strcasecmp(sval, "SYNTAX") ||
2081				     !strcasecmp(sval, "SINGLE-VALUE") ||
2082				     !strcasecmp(sval, "COLLECTIVE") ||
2083				     !strcasecmp(sval, "NO-USER-MODIFICATION") ||
2084				     !strcasecmp(sval, "USAGE") ||
2085				     !strncasecmp(sval, "X-", 2) )
2086				{
2087					/* Missing OID, backtrack */
2088					ss = savepos;
2089				} else if ( flags
2090					& LDAP_SCHEMA_ALLOW_OID_MACRO)
2091				{
2092					/* Non-numerical OID ... */
2093					int len = ss-savepos;
2094					at->at_oid = LDAP_MALLOC(len+1);
2095					strncpy(at->at_oid, savepos, len);
2096					at->at_oid[len] = 0;
2097				}
2098			}
2099			LDAP_FREE(sval);
2100		} else {
2101			*errp = ss;
2102			ldap_attributetype_free(at);
2103			return NULL;
2104		}
2105	}
2106	parse_whsp(&ss);
2107
2108	/*
2109	 * Beyond this point we will be liberal and accept the items
2110	 * in any order.
2111	 */
2112	while (1) {
2113		kind = get_token(&ss,&sval);
2114		switch (kind) {
2115		case TK_EOS:
2116			*code = LDAP_SCHERR_NORIGHTPAREN;
2117			*errp = EndOfInput;
2118			ldap_attributetype_free(at);
2119			return NULL;
2120		case TK_RIGHTPAREN:
2121			return at;
2122		case TK_BAREWORD:
2123			if ( !strcasecmp(sval,"NAME") ) {
2124				LDAP_FREE(sval);
2125				if ( seen_name ) {
2126					*code = LDAP_SCHERR_DUPOPT;
2127					*errp = ss;
2128					ldap_attributetype_free(at);
2129					return(NULL);
2130				}
2131				seen_name = 1;
2132				at->at_names = parse_qdescrs(&ss,code);
2133				if ( !at->at_names ) {
2134					if ( *code != LDAP_SCHERR_OUTOFMEM )
2135						*code = LDAP_SCHERR_BADNAME;
2136					*errp = ss;
2137					ldap_attributetype_free(at);
2138					return NULL;
2139				}
2140			} else if ( !strcasecmp(sval,"DESC") ) {
2141				LDAP_FREE(sval);
2142				if ( seen_desc ) {
2143					*code = LDAP_SCHERR_DUPOPT;
2144					*errp = ss;
2145					ldap_attributetype_free(at);
2146					return(NULL);
2147				}
2148				seen_desc = 1;
2149				parse_whsp(&ss);
2150				kind = get_token(&ss,&sval);
2151				if ( kind != TK_QDSTRING ) {
2152					*code = LDAP_SCHERR_UNEXPTOKEN;
2153					*errp = ss;
2154					LDAP_FREE(sval);
2155					ldap_attributetype_free(at);
2156					return NULL;
2157				}
2158				at->at_desc = sval;
2159				parse_whsp(&ss);
2160			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
2161				LDAP_FREE(sval);
2162				if ( seen_obsolete ) {
2163					*code = LDAP_SCHERR_DUPOPT;
2164					*errp = ss;
2165					ldap_attributetype_free(at);
2166					return(NULL);
2167				}
2168				seen_obsolete = 1;
2169				at->at_obsolete = LDAP_SCHEMA_YES;
2170				parse_whsp(&ss);
2171			} else if ( !strcasecmp(sval,"SUP") ) {
2172				LDAP_FREE(sval);
2173				if ( seen_sup ) {
2174					*code = LDAP_SCHERR_DUPOPT;
2175					*errp = ss;
2176					ldap_attributetype_free(at);
2177					return(NULL);
2178				}
2179				seen_sup = 1;
2180				at->at_sup_oid = parse_woid(&ss,code);
2181				if ( !at->at_sup_oid ) {
2182					*errp = ss;
2183					ldap_attributetype_free(at);
2184					return NULL;
2185				}
2186			} else if ( !strcasecmp(sval,"EQUALITY") ) {
2187				LDAP_FREE(sval);
2188				if ( seen_equality ) {
2189					*code = LDAP_SCHERR_DUPOPT;
2190					*errp = ss;
2191					ldap_attributetype_free(at);
2192					return(NULL);
2193				}
2194				seen_equality = 1;
2195				at->at_equality_oid = parse_woid(&ss,code);
2196				if ( !at->at_equality_oid ) {
2197					*errp = ss;
2198					ldap_attributetype_free(at);
2199					return NULL;
2200				}
2201			} else if ( !strcasecmp(sval,"ORDERING") ) {
2202				LDAP_FREE(sval);
2203				if ( seen_ordering ) {
2204					*code = LDAP_SCHERR_DUPOPT;
2205					*errp = ss;
2206					ldap_attributetype_free(at);
2207					return(NULL);
2208				}
2209				seen_ordering = 1;
2210				at->at_ordering_oid = parse_woid(&ss,code);
2211				if ( !at->at_ordering_oid ) {
2212					*errp = ss;
2213					ldap_attributetype_free(at);
2214					return NULL;
2215				}
2216			} else if ( !strcasecmp(sval,"SUBSTR") ) {
2217				LDAP_FREE(sval);
2218				if ( seen_substr ) {
2219					*code = LDAP_SCHERR_DUPOPT;
2220					*errp = ss;
2221					ldap_attributetype_free(at);
2222					return(NULL);
2223				}
2224				seen_substr = 1;
2225				at->at_substr_oid = parse_woid(&ss,code);
2226				if ( !at->at_substr_oid ) {
2227					*errp = ss;
2228					ldap_attributetype_free(at);
2229					return NULL;
2230				}
2231			} else if ( !strcasecmp(sval,"SYNTAX") ) {
2232				LDAP_FREE(sval);
2233				if ( seen_syntax ) {
2234					*code = LDAP_SCHERR_DUPOPT;
2235					*errp = ss;
2236					ldap_attributetype_free(at);
2237					return(NULL);
2238				}
2239				seen_syntax = 1;
2240				parse_whsp(&ss);
2241				savepos = ss;
2242				at->at_syntax_oid =
2243					parse_noidlen(&ss,
2244						      code,
2245						      &at->at_syntax_len,
2246						      flags);
2247				if ( !at->at_syntax_oid ) {
2248				    if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2249					kind = get_token(&ss,&sval);
2250					if (kind == TK_BAREWORD)
2251					{
2252					    char *sp = strchr(sval, '{');
2253					    at->at_syntax_oid = sval;
2254					    if (sp)
2255					    {
2256						*sp++ = 0;
2257					    	at->at_syntax_len = atoi(sp);
2258						while ( LDAP_DIGIT(*sp) )
2259							sp++;
2260						if ( *sp != '}' ) {
2261						    *code = LDAP_SCHERR_UNEXPTOKEN;
2262						    *errp = ss;
2263						    ldap_attributetype_free(at);
2264						    return NULL;
2265						}
2266					    }
2267					}
2268				    } else {
2269					*errp = ss;
2270					ldap_attributetype_free(at);
2271					return NULL;
2272				    }
2273				}
2274				parse_whsp(&ss);
2275			} else if ( !strcasecmp(sval,"SINGLE-VALUE") ) {
2276				LDAP_FREE(sval);
2277				if ( at->at_single_value ) {
2278					*code = LDAP_SCHERR_DUPOPT;
2279					*errp = ss;
2280					ldap_attributetype_free(at);
2281					return(NULL);
2282				}
2283				at->at_single_value = LDAP_SCHEMA_YES;
2284				parse_whsp(&ss);
2285			} else if ( !strcasecmp(sval,"COLLECTIVE") ) {
2286				LDAP_FREE(sval);
2287				if ( at->at_collective ) {
2288					*code = LDAP_SCHERR_DUPOPT;
2289					*errp = ss;
2290					ldap_attributetype_free(at);
2291					return(NULL);
2292				}
2293				at->at_collective = LDAP_SCHEMA_YES;
2294				parse_whsp(&ss);
2295			} else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) {
2296				LDAP_FREE(sval);
2297				if ( at->at_no_user_mod ) {
2298					*code = LDAP_SCHERR_DUPOPT;
2299					*errp = ss;
2300					ldap_attributetype_free(at);
2301					return(NULL);
2302				}
2303				at->at_no_user_mod = LDAP_SCHEMA_YES;
2304				parse_whsp(&ss);
2305			} else if ( !strcasecmp(sval,"USAGE") ) {
2306				LDAP_FREE(sval);
2307				if ( seen_usage ) {
2308					*code = LDAP_SCHERR_DUPOPT;
2309					*errp = ss;
2310					ldap_attributetype_free(at);
2311					return(NULL);
2312				}
2313				seen_usage = 1;
2314				parse_whsp(&ss);
2315				kind = get_token(&ss,&sval);
2316				if ( kind != TK_BAREWORD ) {
2317					*code = LDAP_SCHERR_UNEXPTOKEN;
2318					*errp = ss;
2319					LDAP_FREE(sval);
2320					ldap_attributetype_free(at);
2321					return NULL;
2322				}
2323				if ( !strcasecmp(sval,"userApplications") )
2324					at->at_usage =
2325					    LDAP_SCHEMA_USER_APPLICATIONS;
2326				else if ( !strcasecmp(sval,"directoryOperation") )
2327					at->at_usage =
2328					    LDAP_SCHEMA_DIRECTORY_OPERATION;
2329				else if ( !strcasecmp(sval,"distributedOperation") )
2330					at->at_usage =
2331					    LDAP_SCHEMA_DISTRIBUTED_OPERATION;
2332				else if ( !strcasecmp(sval,"dSAOperation") )
2333					at->at_usage =
2334					    LDAP_SCHEMA_DSA_OPERATION;
2335				else {
2336					*code = LDAP_SCHERR_UNEXPTOKEN;
2337					*errp = ss;
2338					LDAP_FREE(sval);
2339					ldap_attributetype_free(at);
2340					return NULL;
2341				}
2342				LDAP_FREE(sval);
2343				parse_whsp(&ss);
2344			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
2345				/* Should be parse_qdstrings */
2346				ext_vals = parse_qdescrs(&ss, code);
2347				if ( !ext_vals ) {
2348					*errp = ss;
2349					ldap_attributetype_free(at);
2350					return NULL;
2351				}
2352				if ( add_extension(&at->at_extensions,
2353						    sval, ext_vals) ) {
2354					*code = LDAP_SCHERR_OUTOFMEM;
2355					*errp = ss;
2356					LDAP_FREE(sval);
2357					ldap_attributetype_free(at);
2358					return NULL;
2359				}
2360			} else {
2361				*code = LDAP_SCHERR_UNEXPTOKEN;
2362				*errp = ss;
2363				LDAP_FREE(sval);
2364				ldap_attributetype_free(at);
2365				return NULL;
2366			}
2367			break;
2368		default:
2369			*code = LDAP_SCHERR_UNEXPTOKEN;
2370			*errp = ss;
2371			LDAP_FREE(sval);
2372			ldap_attributetype_free(at);
2373			return NULL;
2374		}
2375	}
2376}
2377
2378void
2379ldap_objectclass_free(LDAPObjectClass * oc)
2380{
2381	LDAP_FREE(oc->oc_oid);
2382	if (oc->oc_names) LDAP_VFREE(oc->oc_names);
2383	if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
2384	if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
2385	if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
2386	if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
2387	free_extensions(oc->oc_extensions);
2388	LDAP_FREE(oc);
2389}
2390
2391LDAPObjectClass *
2392ldap_str2objectclass( LDAP_CONST char * s,
2393	int * code,
2394	LDAP_CONST char ** errp,
2395	LDAP_CONST unsigned flags )
2396{
2397	tk_t kind;
2398	const char * ss = s;
2399	char * sval;
2400	int seen_name = 0;
2401	int seen_desc = 0;
2402	int seen_obsolete = 0;
2403	int seen_sup = 0;
2404	int seen_kind = 0;
2405	int seen_must = 0;
2406	int seen_may = 0;
2407	LDAPObjectClass * oc;
2408	char ** ext_vals;
2409	const char * savepos;
2410
2411	if ( !s ) {
2412		*code = LDAP_SCHERR_EMPTY;
2413		*errp = "";
2414		return NULL;
2415	}
2416
2417	*errp = s;
2418	oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
2419
2420	if ( !oc ) {
2421		*code = LDAP_SCHERR_OUTOFMEM;
2422		return NULL;
2423	}
2424	oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2425
2426	kind = get_token(&ss,&sval);
2427	if ( kind != TK_LEFTPAREN ) {
2428		*code = LDAP_SCHERR_NOLEFTPAREN;
2429		LDAP_FREE(sval);
2430		ldap_objectclass_free(oc);
2431		return NULL;
2432	}
2433
2434	/*
2435	 * Definitions MUST begin with an OID in the numericoid format.
2436	 * However, this routine is used by clients to parse the response
2437	 * from servers and very well known servers will provide an OID
2438	 * in the wrong format or even no OID at all.  We do our best to
2439	 * extract info from those servers.
2440	 */
2441	parse_whsp(&ss);
2442	savepos = ss;
2443	oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
2444	if ( !oc->oc_oid ) {
2445		if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2446			/* Backtracking */
2447			ss = savepos;
2448			kind = get_token(&ss,&sval);
2449			if ( kind == TK_BAREWORD ) {
2450				if ( !strcasecmp(sval, "NAME") ||
2451				     !strcasecmp(sval, "DESC") ||
2452				     !strcasecmp(sval, "OBSOLETE") ||
2453				     !strcasecmp(sval, "SUP") ||
2454				     !strcasecmp(sval, "ABSTRACT") ||
2455				     !strcasecmp(sval, "STRUCTURAL") ||
2456				     !strcasecmp(sval, "AUXILIARY") ||
2457				     !strcasecmp(sval, "MUST") ||
2458				     !strcasecmp(sval, "MAY") ||
2459				     !strncasecmp(sval, "X-", 2) ) {
2460					/* Missing OID, backtrack */
2461					ss = savepos;
2462				} else if ( flags &
2463					LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2464					/* Non-numerical OID, ignore */
2465					int len = ss-savepos;
2466					oc->oc_oid = LDAP_MALLOC(len+1);
2467					strncpy(oc->oc_oid, savepos, len);
2468					oc->oc_oid[len] = 0;
2469				}
2470			}
2471			LDAP_FREE(sval);
2472			*code = 0;
2473		} else {
2474			*errp = ss;
2475			ldap_objectclass_free(oc);
2476			return NULL;
2477		}
2478	}
2479	parse_whsp(&ss);
2480
2481	/*
2482	 * Beyond this point we will be liberal an accept the items
2483	 * in any order.
2484	 */
2485	while (1) {
2486		kind = get_token(&ss,&sval);
2487		switch (kind) {
2488		case TK_EOS:
2489			*code = LDAP_SCHERR_NORIGHTPAREN;
2490			*errp = EndOfInput;
2491			ldap_objectclass_free(oc);
2492			return NULL;
2493		case TK_RIGHTPAREN:
2494			return oc;
2495		case TK_BAREWORD:
2496			if ( !strcasecmp(sval,"NAME") ) {
2497				LDAP_FREE(sval);
2498				if ( seen_name ) {
2499					*code = LDAP_SCHERR_DUPOPT;
2500					*errp = ss;
2501					ldap_objectclass_free(oc);
2502					return(NULL);
2503				}
2504				seen_name = 1;
2505				oc->oc_names = parse_qdescrs(&ss,code);
2506				if ( !oc->oc_names ) {
2507					if ( *code != LDAP_SCHERR_OUTOFMEM )
2508						*code = LDAP_SCHERR_BADNAME;
2509					*errp = ss;
2510					ldap_objectclass_free(oc);
2511					return NULL;
2512				}
2513			} else if ( !strcasecmp(sval,"DESC") ) {
2514				LDAP_FREE(sval);
2515				if ( seen_desc ) {
2516					*code = LDAP_SCHERR_DUPOPT;
2517					*errp = ss;
2518					ldap_objectclass_free(oc);
2519					return(NULL);
2520				}
2521				seen_desc = 1;
2522				parse_whsp(&ss);
2523				kind = get_token(&ss,&sval);
2524				if ( kind != TK_QDSTRING ) {
2525					*code = LDAP_SCHERR_UNEXPTOKEN;
2526					*errp = ss;
2527					LDAP_FREE(sval);
2528					ldap_objectclass_free(oc);
2529					return NULL;
2530				}
2531				oc->oc_desc = sval;
2532				parse_whsp(&ss);
2533			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
2534				LDAP_FREE(sval);
2535				if ( seen_obsolete ) {
2536					*code = LDAP_SCHERR_DUPOPT;
2537					*errp = ss;
2538					ldap_objectclass_free(oc);
2539					return(NULL);
2540				}
2541				seen_obsolete = 1;
2542				oc->oc_obsolete = LDAP_SCHEMA_YES;
2543				parse_whsp(&ss);
2544			} else if ( !strcasecmp(sval,"SUP") ) {
2545				LDAP_FREE(sval);
2546				if ( seen_sup ) {
2547					*code = LDAP_SCHERR_DUPOPT;
2548					*errp = ss;
2549					ldap_objectclass_free(oc);
2550					return(NULL);
2551				}
2552				seen_sup = 1;
2553				oc->oc_sup_oids = parse_oids(&ss,
2554							     code,
2555							     flags);
2556				if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) {
2557					*errp = ss;
2558					ldap_objectclass_free(oc);
2559					return NULL;
2560				}
2561				*code = 0;
2562			} else if ( !strcasecmp(sval,"ABSTRACT") ) {
2563				LDAP_FREE(sval);
2564				if ( seen_kind ) {
2565					*code = LDAP_SCHERR_DUPOPT;
2566					*errp = ss;
2567					ldap_objectclass_free(oc);
2568					return(NULL);
2569				}
2570				seen_kind = 1;
2571				oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
2572				parse_whsp(&ss);
2573			} else if ( !strcasecmp(sval,"STRUCTURAL") ) {
2574				LDAP_FREE(sval);
2575				if ( seen_kind ) {
2576					*code = LDAP_SCHERR_DUPOPT;
2577					*errp = ss;
2578					ldap_objectclass_free(oc);
2579					return(NULL);
2580				}
2581				seen_kind = 1;
2582				oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2583				parse_whsp(&ss);
2584			} else if ( !strcasecmp(sval,"AUXILIARY") ) {
2585				LDAP_FREE(sval);
2586				if ( seen_kind ) {
2587					*code = LDAP_SCHERR_DUPOPT;
2588					*errp = ss;
2589					ldap_objectclass_free(oc);
2590					return(NULL);
2591				}
2592				seen_kind = 1;
2593				oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
2594				parse_whsp(&ss);
2595			} else if ( !strcasecmp(sval,"MUST") ) {
2596				LDAP_FREE(sval);
2597				if ( seen_must ) {
2598					*code = LDAP_SCHERR_DUPOPT;
2599					*errp = ss;
2600					ldap_objectclass_free(oc);
2601					return(NULL);
2602				}
2603				seen_must = 1;
2604				oc->oc_at_oids_must = parse_oids(&ss,code,0);
2605				if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) {
2606					*errp = ss;
2607					ldap_objectclass_free(oc);
2608					return NULL;
2609				}
2610				*code = 0;
2611				parse_whsp(&ss);
2612			} else if ( !strcasecmp(sval,"MAY") ) {
2613				LDAP_FREE(sval);
2614				if ( seen_may ) {
2615					*code = LDAP_SCHERR_DUPOPT;
2616					*errp = ss;
2617					ldap_objectclass_free(oc);
2618					return(NULL);
2619				}
2620				seen_may = 1;
2621				oc->oc_at_oids_may = parse_oids(&ss,code,0);
2622				if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) {
2623					*errp = ss;
2624					ldap_objectclass_free(oc);
2625					return NULL;
2626				}
2627				*code = 0;
2628				parse_whsp(&ss);
2629			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
2630				/* Should be parse_qdstrings */
2631				ext_vals = parse_qdescrs(&ss, code);
2632				*code = 0;
2633				if ( !ext_vals ) {
2634					*errp = ss;
2635					ldap_objectclass_free(oc);
2636					return NULL;
2637				}
2638				if ( add_extension(&oc->oc_extensions,
2639						    sval, ext_vals) ) {
2640					*code = LDAP_SCHERR_OUTOFMEM;
2641					*errp = ss;
2642					LDAP_FREE(sval);
2643					ldap_objectclass_free(oc);
2644					return NULL;
2645				}
2646			} else {
2647				*code = LDAP_SCHERR_UNEXPTOKEN;
2648				*errp = ss;
2649				LDAP_FREE(sval);
2650				ldap_objectclass_free(oc);
2651				return NULL;
2652			}
2653			break;
2654		default:
2655			*code = LDAP_SCHERR_UNEXPTOKEN;
2656			*errp = ss;
2657			LDAP_FREE(sval);
2658			ldap_objectclass_free(oc);
2659			return NULL;
2660		}
2661	}
2662}
2663
2664void
2665ldap_contentrule_free(LDAPContentRule * cr)
2666{
2667	LDAP_FREE(cr->cr_oid);
2668	if (cr->cr_names) LDAP_VFREE(cr->cr_names);
2669	if (cr->cr_desc) LDAP_FREE(cr->cr_desc);
2670	if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux);
2671	if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must);
2672	if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may);
2673	if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not);
2674	free_extensions(cr->cr_extensions);
2675	LDAP_FREE(cr);
2676}
2677
2678LDAPContentRule *
2679ldap_str2contentrule( LDAP_CONST char * s,
2680	int * code,
2681	LDAP_CONST char ** errp,
2682	LDAP_CONST unsigned flags )
2683{
2684	tk_t kind;
2685	const char * ss = s;
2686	char * sval;
2687	int seen_name = 0;
2688	int seen_desc = 0;
2689	int seen_obsolete = 0;
2690	int seen_aux = 0;
2691	int seen_must = 0;
2692	int seen_may = 0;
2693	int seen_not = 0;
2694	LDAPContentRule * cr;
2695	char ** ext_vals;
2696	const char * savepos;
2697
2698	if ( !s ) {
2699		*code = LDAP_SCHERR_EMPTY;
2700		*errp = "";
2701		return NULL;
2702	}
2703
2704	*errp = s;
2705	cr = LDAP_CALLOC(1,sizeof(LDAPContentRule));
2706
2707	if ( !cr ) {
2708		*code = LDAP_SCHERR_OUTOFMEM;
2709		return NULL;
2710	}
2711
2712	kind = get_token(&ss,&sval);
2713	if ( kind != TK_LEFTPAREN ) {
2714		*code = LDAP_SCHERR_NOLEFTPAREN;
2715		LDAP_FREE(sval);
2716		ldap_contentrule_free(cr);
2717		return NULL;
2718	}
2719
2720	/*
2721	 * Definitions MUST begin with an OID in the numericoid format.
2722	 */
2723	parse_whsp(&ss);
2724	savepos = ss;
2725	cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0);
2726	if ( !cr->cr_oid ) {
2727		if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2728			/* Backtracking */
2729			ss = savepos;
2730			kind = get_token(&ss,&sval);
2731			if ( kind == TK_BAREWORD ) {
2732				if ( !strcasecmp(sval, "NAME") ||
2733				     !strcasecmp(sval, "DESC") ||
2734				     !strcasecmp(sval, "OBSOLETE") ||
2735				     !strcasecmp(sval, "AUX") ||
2736				     !strcasecmp(sval, "MUST") ||
2737				     !strcasecmp(sval, "MAY") ||
2738				     !strcasecmp(sval, "NOT") ||
2739				     !strncasecmp(sval, "X-", 2) ) {
2740					/* Missing OID, backtrack */
2741					ss = savepos;
2742				} else if ( flags &
2743					LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2744					/* Non-numerical OID, ignore */
2745					int len = ss-savepos;
2746					cr->cr_oid = LDAP_MALLOC(len+1);
2747					strncpy(cr->cr_oid, savepos, len);
2748					cr->cr_oid[len] = 0;
2749				}
2750			}
2751			LDAP_FREE(sval);
2752		} else {
2753			*errp = ss;
2754			ldap_contentrule_free(cr);
2755			return NULL;
2756		}
2757	}
2758	parse_whsp(&ss);
2759
2760	/*
2761	 * Beyond this point we will be liberal an accept the items
2762	 * in any order.
2763	 */
2764	while (1) {
2765		kind = get_token(&ss,&sval);
2766		switch (kind) {
2767		case TK_EOS:
2768			*code = LDAP_SCHERR_NORIGHTPAREN;
2769			*errp = EndOfInput;
2770			ldap_contentrule_free(cr);
2771			return NULL;
2772		case TK_RIGHTPAREN:
2773			return cr;
2774		case TK_BAREWORD:
2775			if ( !strcasecmp(sval,"NAME") ) {
2776				LDAP_FREE(sval);
2777				if ( seen_name ) {
2778					*code = LDAP_SCHERR_DUPOPT;
2779					*errp = ss;
2780					ldap_contentrule_free(cr);
2781					return(NULL);
2782				}
2783				seen_name = 1;
2784				cr->cr_names = parse_qdescrs(&ss,code);
2785				if ( !cr->cr_names ) {
2786					if ( *code != LDAP_SCHERR_OUTOFMEM )
2787						*code = LDAP_SCHERR_BADNAME;
2788					*errp = ss;
2789					ldap_contentrule_free(cr);
2790					return NULL;
2791				}
2792			} else if ( !strcasecmp(sval,"DESC") ) {
2793				LDAP_FREE(sval);
2794				if ( seen_desc ) {
2795					*code = LDAP_SCHERR_DUPOPT;
2796					*errp = ss;
2797					ldap_contentrule_free(cr);
2798					return(NULL);
2799				}
2800				seen_desc = 1;
2801				parse_whsp(&ss);
2802				kind = get_token(&ss,&sval);
2803				if ( kind != TK_QDSTRING ) {
2804					*code = LDAP_SCHERR_UNEXPTOKEN;
2805					*errp = ss;
2806					LDAP_FREE(sval);
2807					ldap_contentrule_free(cr);
2808					return NULL;
2809				}
2810				cr->cr_desc = sval;
2811				parse_whsp(&ss);
2812			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
2813				LDAP_FREE(sval);
2814				if ( seen_obsolete ) {
2815					*code = LDAP_SCHERR_DUPOPT;
2816					*errp = ss;
2817					ldap_contentrule_free(cr);
2818					return(NULL);
2819				}
2820				seen_obsolete = 1;
2821				cr->cr_obsolete = LDAP_SCHEMA_YES;
2822				parse_whsp(&ss);
2823			} else if ( !strcasecmp(sval,"AUX") ) {
2824				LDAP_FREE(sval);
2825				if ( seen_aux ) {
2826					*code = LDAP_SCHERR_DUPOPT;
2827					*errp = ss;
2828					ldap_contentrule_free(cr);
2829					return(NULL);
2830				}
2831				seen_aux = 1;
2832				cr->cr_oc_oids_aux = parse_oids(&ss,code,0);
2833				if ( !cr->cr_oc_oids_aux ) {
2834					*errp = ss;
2835					ldap_contentrule_free(cr);
2836					return NULL;
2837				}
2838				parse_whsp(&ss);
2839			} else if ( !strcasecmp(sval,"MUST") ) {
2840				LDAP_FREE(sval);
2841				if ( seen_must ) {
2842					*code = LDAP_SCHERR_DUPOPT;
2843					*errp = ss;
2844					ldap_contentrule_free(cr);
2845					return(NULL);
2846				}
2847				seen_must = 1;
2848				cr->cr_at_oids_must = parse_oids(&ss,code,0);
2849				if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) {
2850					*errp = ss;
2851					ldap_contentrule_free(cr);
2852					return NULL;
2853				}
2854				parse_whsp(&ss);
2855			} else if ( !strcasecmp(sval,"MAY") ) {
2856				LDAP_FREE(sval);
2857				if ( seen_may ) {
2858					*code = LDAP_SCHERR_DUPOPT;
2859					*errp = ss;
2860					ldap_contentrule_free(cr);
2861					return(NULL);
2862				}
2863				seen_may = 1;
2864				cr->cr_at_oids_may = parse_oids(&ss,code,0);
2865				if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) {
2866					*errp = ss;
2867					ldap_contentrule_free(cr);
2868					return NULL;
2869				}
2870				parse_whsp(&ss);
2871			} else if ( !strcasecmp(sval,"NOT") ) {
2872				LDAP_FREE(sval);
2873				if ( seen_not ) {
2874					*code = LDAP_SCHERR_DUPOPT;
2875					*errp = ss;
2876					ldap_contentrule_free(cr);
2877					return(NULL);
2878				}
2879				seen_not = 1;
2880				cr->cr_at_oids_not = parse_oids(&ss,code,0);
2881				if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) {
2882					*errp = ss;
2883					ldap_contentrule_free(cr);
2884					return NULL;
2885				}
2886				parse_whsp(&ss);
2887			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
2888				/* Should be parse_qdstrings */
2889				ext_vals = parse_qdescrs(&ss, code);
2890				if ( !ext_vals ) {
2891					*errp = ss;
2892					ldap_contentrule_free(cr);
2893					return NULL;
2894				}
2895				if ( add_extension(&cr->cr_extensions,
2896						    sval, ext_vals) ) {
2897					*code = LDAP_SCHERR_OUTOFMEM;
2898					*errp = ss;
2899					LDAP_FREE(sval);
2900					ldap_contentrule_free(cr);
2901					return NULL;
2902				}
2903			} else {
2904				*code = LDAP_SCHERR_UNEXPTOKEN;
2905				*errp = ss;
2906				LDAP_FREE(sval);
2907				ldap_contentrule_free(cr);
2908				return NULL;
2909			}
2910			break;
2911		default:
2912			*code = LDAP_SCHERR_UNEXPTOKEN;
2913			*errp = ss;
2914			LDAP_FREE(sval);
2915			ldap_contentrule_free(cr);
2916			return NULL;
2917		}
2918	}
2919}
2920
2921void
2922ldap_structurerule_free(LDAPStructureRule * sr)
2923{
2924	if (sr->sr_names) LDAP_VFREE(sr->sr_names);
2925	if (sr->sr_desc) LDAP_FREE(sr->sr_desc);
2926	if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform);
2927	if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids);
2928	free_extensions(sr->sr_extensions);
2929	LDAP_FREE(sr);
2930}
2931
2932LDAPStructureRule *
2933ldap_str2structurerule( LDAP_CONST char * s,
2934	int * code,
2935	LDAP_CONST char ** errp,
2936	LDAP_CONST unsigned flags )
2937{
2938	tk_t kind;
2939	int ret;
2940	const char * ss = s;
2941	char * sval;
2942	int seen_name = 0;
2943	int seen_desc = 0;
2944	int seen_obsolete = 0;
2945	int seen_nameform = 0;
2946	LDAPStructureRule * sr;
2947	char ** ext_vals;
2948	const char * savepos;
2949
2950	if ( !s ) {
2951		*code = LDAP_SCHERR_EMPTY;
2952		*errp = "";
2953		return NULL;
2954	}
2955
2956	*errp = s;
2957	sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule));
2958
2959	if ( !sr ) {
2960		*code = LDAP_SCHERR_OUTOFMEM;
2961		return NULL;
2962	}
2963
2964	kind = get_token(&ss,&sval);
2965	if ( kind != TK_LEFTPAREN ) {
2966		*code = LDAP_SCHERR_NOLEFTPAREN;
2967		LDAP_FREE(sval);
2968		ldap_structurerule_free(sr);
2969		return NULL;
2970	}
2971
2972	/*
2973	 * Definitions MUST begin with a ruleid.
2974	 */
2975	parse_whsp(&ss);
2976	savepos = ss;
2977	ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid);
2978	if ( ret ) {
2979		*errp = ss;
2980		ldap_structurerule_free(sr);
2981		return NULL;
2982	}
2983	parse_whsp(&ss);
2984
2985	/*
2986	 * Beyond this point we will be liberal an accept the items
2987	 * in any order.
2988	 */
2989	while (1) {
2990		kind = get_token(&ss,&sval);
2991		switch (kind) {
2992		case TK_EOS:
2993			*code = LDAP_SCHERR_NORIGHTPAREN;
2994			*errp = EndOfInput;
2995			ldap_structurerule_free(sr);
2996			return NULL;
2997		case TK_RIGHTPAREN:
2998			if( !seen_nameform ) {
2999				*code = LDAP_SCHERR_MISSING;
3000				ldap_structurerule_free(sr);
3001				return NULL;
3002			}
3003			return sr;
3004		case TK_BAREWORD:
3005			if ( !strcasecmp(sval,"NAME") ) {
3006				LDAP_FREE(sval);
3007				if ( seen_name ) {
3008					*code = LDAP_SCHERR_DUPOPT;
3009					*errp = ss;
3010					ldap_structurerule_free(sr);
3011					return(NULL);
3012				}
3013				seen_name = 1;
3014				sr->sr_names = parse_qdescrs(&ss,code);
3015				if ( !sr->sr_names ) {
3016					if ( *code != LDAP_SCHERR_OUTOFMEM )
3017						*code = LDAP_SCHERR_BADNAME;
3018					*errp = ss;
3019					ldap_structurerule_free(sr);
3020					return NULL;
3021				}
3022			} else if ( !strcasecmp(sval,"DESC") ) {
3023				LDAP_FREE(sval);
3024				if ( seen_desc ) {
3025					*code = LDAP_SCHERR_DUPOPT;
3026					*errp = ss;
3027					ldap_structurerule_free(sr);
3028					return(NULL);
3029				}
3030				seen_desc = 1;
3031				parse_whsp(&ss);
3032				kind = get_token(&ss,&sval);
3033				if ( kind != TK_QDSTRING ) {
3034					*code = LDAP_SCHERR_UNEXPTOKEN;
3035					*errp = ss;
3036					LDAP_FREE(sval);
3037					ldap_structurerule_free(sr);
3038					return NULL;
3039				}
3040				sr->sr_desc = sval;
3041				parse_whsp(&ss);
3042			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
3043				LDAP_FREE(sval);
3044				if ( seen_obsolete ) {
3045					*code = LDAP_SCHERR_DUPOPT;
3046					*errp = ss;
3047					ldap_structurerule_free(sr);
3048					return(NULL);
3049				}
3050				seen_obsolete = 1;
3051				sr->sr_obsolete = LDAP_SCHEMA_YES;
3052				parse_whsp(&ss);
3053			} else if ( !strcasecmp(sval,"FORM") ) {
3054				LDAP_FREE(sval);
3055				if ( seen_nameform ) {
3056					*code = LDAP_SCHERR_DUPOPT;
3057					*errp = ss;
3058					ldap_structurerule_free(sr);
3059					return(NULL);
3060				}
3061				seen_nameform = 1;
3062				sr->sr_nameform = parse_woid(&ss,code);
3063				if ( !sr->sr_nameform ) {
3064					*errp = ss;
3065					ldap_structurerule_free(sr);
3066					return NULL;
3067				}
3068				parse_whsp(&ss);
3069			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
3070				/* Should be parse_qdstrings */
3071				ext_vals = parse_qdescrs(&ss, code);
3072				if ( !ext_vals ) {
3073					*errp = ss;
3074					ldap_structurerule_free(sr);
3075					return NULL;
3076				}
3077				if ( add_extension(&sr->sr_extensions,
3078						    sval, ext_vals) ) {
3079					*code = LDAP_SCHERR_OUTOFMEM;
3080					*errp = ss;
3081					LDAP_FREE(sval);
3082					ldap_structurerule_free(sr);
3083					return NULL;
3084				}
3085			} else {
3086				*code = LDAP_SCHERR_UNEXPTOKEN;
3087				*errp = ss;
3088				LDAP_FREE(sval);
3089				ldap_structurerule_free(sr);
3090				return NULL;
3091			}
3092			break;
3093		default:
3094			*code = LDAP_SCHERR_UNEXPTOKEN;
3095			*errp = ss;
3096			LDAP_FREE(sval);
3097			ldap_structurerule_free(sr);
3098			return NULL;
3099		}
3100	}
3101}
3102
3103void
3104ldap_nameform_free(LDAPNameForm * nf)
3105{
3106	LDAP_FREE(nf->nf_oid);
3107	if (nf->nf_names) LDAP_VFREE(nf->nf_names);
3108	if (nf->nf_desc) LDAP_FREE(nf->nf_desc);
3109	if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass);
3110	if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must);
3111	if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may);
3112	free_extensions(nf->nf_extensions);
3113	LDAP_FREE(nf);
3114}
3115
3116LDAPNameForm *
3117ldap_str2nameform( LDAP_CONST char * s,
3118	int * code,
3119	LDAP_CONST char ** errp,
3120	LDAP_CONST unsigned flags )
3121{
3122	tk_t kind;
3123	const char * ss = s;
3124	char * sval;
3125	int seen_name = 0;
3126	int seen_desc = 0;
3127	int seen_obsolete = 0;
3128	int seen_class = 0;
3129	int seen_must = 0;
3130	int seen_may = 0;
3131	LDAPNameForm * nf;
3132	char ** ext_vals;
3133	const char * savepos;
3134
3135	if ( !s ) {
3136		*code = LDAP_SCHERR_EMPTY;
3137		*errp = "";
3138		return NULL;
3139	}
3140
3141	*errp = s;
3142	nf = LDAP_CALLOC(1,sizeof(LDAPNameForm));
3143
3144	if ( !nf ) {
3145		*code = LDAP_SCHERR_OUTOFMEM;
3146		return NULL;
3147	}
3148
3149	kind = get_token(&ss,&sval);
3150	if ( kind != TK_LEFTPAREN ) {
3151		*code = LDAP_SCHERR_NOLEFTPAREN;
3152		LDAP_FREE(sval);
3153		ldap_nameform_free(nf);
3154		return NULL;
3155	}
3156
3157	/*
3158	 * Definitions MUST begin with an OID in the numericoid format.
3159	 * However, this routine is used by clients to parse the response
3160	 * from servers and very well known servers will provide an OID
3161	 * in the wrong format or even no OID at all.  We do our best to
3162	 * extract info from those servers.
3163	 */
3164	parse_whsp(&ss);
3165	savepos = ss;
3166	nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0);
3167	if ( !nf->nf_oid ) {
3168		*errp = ss;
3169		ldap_nameform_free(nf);
3170		return NULL;
3171	}
3172	parse_whsp(&ss);
3173
3174	/*
3175	 * Beyond this point we will be liberal an accept the items
3176	 * in any order.
3177	 */
3178	while (1) {
3179		kind = get_token(&ss,&sval);
3180		switch (kind) {
3181		case TK_EOS:
3182			*code = LDAP_SCHERR_NORIGHTPAREN;
3183			*errp = EndOfInput;
3184			ldap_nameform_free(nf);
3185			return NULL;
3186		case TK_RIGHTPAREN:
3187			if( !seen_class || !seen_must ) {
3188				*code = LDAP_SCHERR_MISSING;
3189				ldap_nameform_free(nf);
3190				return NULL;
3191			}
3192			return nf;
3193		case TK_BAREWORD:
3194			if ( !strcasecmp(sval,"NAME") ) {
3195				LDAP_FREE(sval);
3196				if ( seen_name ) {
3197					*code = LDAP_SCHERR_DUPOPT;
3198					*errp = ss;
3199					ldap_nameform_free(nf);
3200					return(NULL);
3201				}
3202				seen_name = 1;
3203				nf->nf_names = parse_qdescrs(&ss,code);
3204				if ( !nf->nf_names ) {
3205					if ( *code != LDAP_SCHERR_OUTOFMEM )
3206						*code = LDAP_SCHERR_BADNAME;
3207					*errp = ss;
3208					ldap_nameform_free(nf);
3209					return NULL;
3210				}
3211			} else if ( !strcasecmp(sval,"DESC") ) {
3212				LDAP_FREE(sval);
3213				if ( seen_desc ) {
3214					*code = LDAP_SCHERR_DUPOPT;
3215					*errp = ss;
3216					ldap_nameform_free(nf);
3217					return(NULL);
3218				}
3219				seen_desc = 1;
3220				parse_whsp(&ss);
3221				kind = get_token(&ss,&sval);
3222				if ( kind != TK_QDSTRING ) {
3223					*code = LDAP_SCHERR_UNEXPTOKEN;
3224					*errp = ss;
3225					LDAP_FREE(sval);
3226					ldap_nameform_free(nf);
3227					return NULL;
3228				}
3229				nf->nf_desc = sval;
3230				parse_whsp(&ss);
3231			} else if ( !strcasecmp(sval,"OBSOLETE") ) {
3232				LDAP_FREE(sval);
3233				if ( seen_obsolete ) {
3234					*code = LDAP_SCHERR_DUPOPT;
3235					*errp = ss;
3236					ldap_nameform_free(nf);
3237					return(NULL);
3238				}
3239				seen_obsolete = 1;
3240				nf->nf_obsolete = LDAP_SCHEMA_YES;
3241				parse_whsp(&ss);
3242			} else if ( !strcasecmp(sval,"OC") ) {
3243				LDAP_FREE(sval);
3244				if ( seen_class ) {
3245					*code = LDAP_SCHERR_DUPOPT;
3246					*errp = ss;
3247					ldap_nameform_free(nf);
3248					return(NULL);
3249				}
3250				seen_class = 1;
3251				nf->nf_objectclass = parse_woid(&ss,code);
3252				if ( !nf->nf_objectclass ) {
3253					*errp = ss;
3254					ldap_nameform_free(nf);
3255					return NULL;
3256				}
3257			} else if ( !strcasecmp(sval,"MUST") ) {
3258				LDAP_FREE(sval);
3259				if ( seen_must ) {
3260					*code = LDAP_SCHERR_DUPOPT;
3261					*errp = ss;
3262					ldap_nameform_free(nf);
3263					return(NULL);
3264				}
3265				seen_must = 1;
3266				nf->nf_at_oids_must = parse_oids(&ss,code,0);
3267				if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) {
3268					*errp = ss;
3269					ldap_nameform_free(nf);
3270					return NULL;
3271				}
3272				parse_whsp(&ss);
3273			} else if ( !strcasecmp(sval,"MAY") ) {
3274				LDAP_FREE(sval);
3275				if ( seen_may ) {
3276					*code = LDAP_SCHERR_DUPOPT;
3277					*errp = ss;
3278					ldap_nameform_free(nf);
3279					return(NULL);
3280				}
3281				seen_may = 1;
3282				nf->nf_at_oids_may = parse_oids(&ss,code,0);
3283				if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) {
3284					*errp = ss;
3285					ldap_nameform_free(nf);
3286					return NULL;
3287				}
3288				parse_whsp(&ss);
3289			} else if ( sval[0] == 'X' && sval[1] == '-' ) {
3290				/* Should be parse_qdstrings */
3291				ext_vals = parse_qdescrs(&ss, code);
3292				if ( !ext_vals ) {
3293					*errp = ss;
3294					ldap_nameform_free(nf);
3295					return NULL;
3296				}
3297				if ( add_extension(&nf->nf_extensions,
3298						    sval, ext_vals) ) {
3299					*code = LDAP_SCHERR_OUTOFMEM;
3300					*errp = ss;
3301					LDAP_FREE(sval);
3302					ldap_nameform_free(nf);
3303					return NULL;
3304				}
3305			} else {
3306				*code = LDAP_SCHERR_UNEXPTOKEN;
3307				*errp = ss;
3308				LDAP_FREE(sval);
3309				ldap_nameform_free(nf);
3310				return NULL;
3311			}
3312			break;
3313		default:
3314			*code = LDAP_SCHERR_UNEXPTOKEN;
3315			*errp = ss;
3316			LDAP_FREE(sval);
3317			ldap_nameform_free(nf);
3318			return NULL;
3319		}
3320	}
3321}
3322
3323static char *const err2text[] = {
3324	N_("Success"),
3325	N_("Out of memory"),
3326	N_("Unexpected token"),
3327	N_("Missing opening parenthesis"),
3328	N_("Missing closing parenthesis"),
3329	N_("Expecting digit"),
3330	N_("Expecting a name"),
3331	N_("Bad description"),
3332	N_("Bad superiors"),
3333	N_("Duplicate option"),
3334	N_("Unexpected end of data"),
3335	N_("Missing required field"),
3336	N_("Out of order field")
3337};
3338
3339char *
3340ldap_scherr2str(int code)
3341{
3342	if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
3343		return _("Unknown error");
3344	} else {
3345		return _(err2text[code]);
3346	}
3347}
3348