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