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