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