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