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