]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
6f7020846923a8ca676eca4f4daa811911f7b24e
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1999-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * schema.c:  parsing routines used by servers and clients to process
8  *      schema definitions
9  */
10
11 #include "portable.h"
12
13 #include <stdio.h>
14 #include <ac/stdlib.h>
15
16 #include <ac/string.h>
17 #include <ac/time.h>
18
19 #include "ldap-int.h"
20
21 #include <ldap_schema.h>
22
23 static const char *
24 choose_name( char *names[], const char *fallback )
25 {
26         return( (names != NULL && names[0] != NULL) ? names[0] : fallback );
27 }
28
29 LDAP_CONST char *
30 ldap_syntax2name( LDAPSyntax * syn )
31 {
32         return( syn->syn_oid );
33 }
34
35 LDAP_CONST char *
36 ldap_matchingrule2name( LDAPMatchingRule * mr )
37 {
38         return( choose_name( mr->mr_names, mr->mr_oid ) );
39 }
40
41 LDAP_CONST char *
42 ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
43 {
44         return( choose_name( mru->mru_names, mru->mru_oid ) );
45 }
46
47 LDAP_CONST char *
48 ldap_attributetype2name( LDAPAttributeType * at )
49 {
50         return( choose_name( at->at_names, at->at_oid ) );
51 }
52
53 LDAP_CONST char *
54 ldap_objectclass2name( LDAPObjectClass * oc )
55 {
56         return( choose_name( oc->oc_names, oc->oc_oid ) );
57 }
58
59
60 /*
61  * When pretty printing the entities we will be appending to a buffer.
62  * Since checking for overflow, realloc'ing and checking if no error
63  * is extremely boring, we will use a protection layer that will let
64  * us blissfully ignore the error until the end.  This layer is
65  * implemented with the help of the next type.
66  */
67
68 typedef struct safe_string {
69         char * val;
70         ber_len_t size;
71         ber_len_t pos;
72         int at_whsp;
73 } safe_string;
74
75 static safe_string *
76 new_safe_string(int size)
77 {
78         safe_string * ss;
79         
80         ss = LDAP_MALLOC(sizeof(safe_string));
81         if ( !ss )
82                 return(NULL);
83
84         ss->val = LDAP_MALLOC(size);
85         if ( !ss->val ) {
86                 LDAP_FREE(ss);
87                 return(NULL);
88         }
89
90         ss->size = size;
91         ss->pos = 0;
92         ss->at_whsp = 0;
93
94         return ss;
95 }
96
97 static void
98 safe_string_free(safe_string * ss)
99 {
100         if ( !ss )
101                 return;
102         LDAP_FREE(ss->val);
103         LDAP_FREE(ss);
104 }
105
106 static char *
107 safe_string_val(safe_string * ss)
108 {
109         ss->val[ss->pos] = '\0';
110         return(ss->val);
111 }
112
113 static char *
114 safe_strdup(safe_string * ss)
115 {
116         char *ret = LDAP_MALLOC(ss->pos+1);
117         if (!ret)
118                 return NULL;
119         AC_MEMCPY(ret, ss->val, ss->pos);
120         ret[ss->pos] = '\0';
121         return ret;
122 }
123
124 static int
125 append_to_safe_string(safe_string * ss, char * s)
126 {
127         int l = strlen(s);
128         char * temp;
129
130         /*
131          * Some runaway process is trying to append to a string that
132          * overflowed and we could not extend.
133          */
134         if ( !ss->val )
135                 return -1;
136
137         /* We always make sure there is at least one position available */
138         if ( ss->pos + l >= ss->size-1 ) {
139                 ss->size *= 2;
140                 if ( ss->pos + l >= ss->size-1 ) {
141                         ss->size = ss->pos + l + 1;
142                 }
143
144                 temp = LDAP_REALLOC(ss->val, ss->size);
145                 if ( !temp ) {
146                         /* Trouble, out of memory */
147                         LDAP_FREE(ss->val);
148                         return -1;
149                 }
150                 ss->val = temp;
151         }
152         strncpy(&ss->val[ss->pos], s, l);
153         ss->pos += l;
154         if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) )
155                 ss->at_whsp = 1;
156         else
157                 ss->at_whsp = 0;
158
159         return 0;
160 }
161
162 static int
163 print_literal(safe_string *ss, char *s)
164 {
165         return(append_to_safe_string(ss,s));
166 }
167
168 static int
169 print_whsp(safe_string *ss)
170 {
171         if ( ss->at_whsp )
172                 return(append_to_safe_string(ss,""));
173         else
174                 return(append_to_safe_string(ss," "));
175 }
176
177 static int
178 print_numericoid(safe_string *ss, char *s)
179 {
180         if ( s )
181                 return(append_to_safe_string(ss,s));
182         else
183                 return(append_to_safe_string(ss,""));
184 }
185
186 /* This one is identical to print_qdescr */
187 static int
188 print_qdstring(safe_string *ss, char *s)
189 {
190         print_whsp(ss);
191         print_literal(ss,"'");
192         append_to_safe_string(ss,s);
193         print_literal(ss,"'");
194         return(print_whsp(ss));
195 }
196
197 static int
198 print_qdescr(safe_string *ss, char *s)
199 {
200         print_whsp(ss);
201         print_literal(ss,"'");
202         append_to_safe_string(ss,s);
203         print_literal(ss,"'");
204         return(print_whsp(ss));
205 }
206
207 static int
208 print_qdescrlist(safe_string *ss, char **sa)
209 {
210         char **sp;
211         int ret = 0;
212         
213         for (sp=sa; *sp; sp++) {
214                 ret = print_qdescr(ss,*sp);
215         }
216         /* If the list was empty, we return zero that is potentially
217          * incorrect, but since we will be still appending things, the
218          * overflow will be detected later.  Maybe FIX.
219          */
220         return(ret);
221 }
222
223 static int
224 print_qdescrs(safe_string *ss, char **sa)
225 {
226         /* The only way to represent an empty list is as a qdescrlist
227          * so, if the list is empty we treat it as a long list.
228          * Really, this is what the syntax mandates.  We should not
229          * be here if the list was empty, but if it happens, a label
230          * has already been output and we cannot undo it.
231          */
232         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
233                 print_whsp(ss);
234                 print_literal(ss,"("/*)*/);
235                 print_qdescrlist(ss,sa);
236                 print_literal(ss,/*(*/")");
237                 return(print_whsp(ss));
238         } else {
239           return(print_qdescr(ss,*sa));
240         }
241 }
242
243 static int
244 print_woid(safe_string *ss, char *s)
245 {
246         print_whsp(ss);
247         append_to_safe_string(ss,s);
248         return print_whsp(ss);
249 }
250
251 static int
252 print_oidlist(safe_string *ss, char **sa)
253 {
254         char **sp;
255
256         for (sp=sa; *(sp+1); sp++) {
257                 print_woid(ss,*sp);
258                 print_literal(ss,"$");
259         }
260         return(print_woid(ss,*sp));
261 }
262
263 static int
264 print_oids(safe_string *ss, char **sa)
265 {
266         if ( sa[0] && sa[1] ) {
267                 print_literal(ss,"("/*)*/);
268                 print_oidlist(ss,sa);
269                 print_whsp(ss);
270                 return(print_literal(ss,/*(*/")"));
271         } else {
272                 return(print_woid(ss,*sa));
273         }
274 }
275
276 static int
277 print_noidlen(safe_string *ss, char *s, int l)
278 {
279         char buf[64];
280         int ret;
281
282         ret = print_numericoid(ss,s);
283         if ( l ) {
284                 sprintf(buf,"{%d}",l);
285                 ret = print_literal(ss,buf);
286         }
287         return(ret);
288 }
289
290 static int
291 print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions)
292 {
293         LDAPSchemaExtensionItem **ext;
294
295         if ( extensions ) {
296                 print_whsp(ss);
297                 for ( ext = extensions; *ext != NULL; ext++ ) {
298                         print_literal(ss, (*ext)->lsei_name);
299                         print_whsp(ss);
300                         /* Should be print_qdstrings */
301                         print_qdescrs(ss, (*ext)->lsei_values);
302                         print_whsp(ss);
303                 }
304         }
305
306         return 0;
307 }
308
309 char *
310 ldap_syntax2str( LDAPSyntax * syn )
311 {
312         struct berval bv;
313         if (ldap_syntax2bv( syn, &bv ))
314                 return(bv.bv_val);
315         else
316                 return NULL;
317 }
318
319 struct berval *
320 ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv )
321 {
322         safe_string * ss;
323         
324         ss = new_safe_string(256);
325         if ( !ss )
326                 return NULL;
327
328         print_literal(ss,"("/*)*/);
329         print_whsp(ss);
330
331         print_numericoid(ss, syn->syn_oid);
332         print_whsp(ss);
333
334         if ( syn->syn_desc ) {
335                 print_literal(ss,"DESC");
336                 print_qdstring(ss,syn->syn_desc);
337         }
338
339         print_whsp(ss);
340
341         print_extensions(ss, syn->syn_extensions);
342
343         print_literal(ss,/*(*/ ")");
344
345         bv->bv_val = safe_strdup(ss);
346         bv->bv_len = ss->pos;
347         safe_string_free(ss);
348         return(bv);
349 }
350
351 char *
352 ldap_matchingrule2str( LDAPMatchingRule * mr )
353 {
354         struct berval bv;
355         if (ldap_matchingrule2bv( mr, &bv ))
356                 return(bv.bv_val);
357         else
358                 return NULL;
359 }
360
361 struct berval *
362 ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv )
363 {
364         safe_string * ss;
365         
366         ss = new_safe_string(256);
367         if ( !ss )
368                 return NULL;
369
370         print_literal(ss,"(" /*)*/);
371         print_whsp(ss);
372
373         print_numericoid(ss, mr->mr_oid);
374         print_whsp(ss);
375
376         if ( mr->mr_names ) {
377                 print_literal(ss,"NAME");
378                 print_qdescrs(ss,mr->mr_names);
379         }
380
381         if ( mr->mr_desc ) {
382                 print_literal(ss,"DESC");
383                 print_qdstring(ss,mr->mr_desc);
384         }
385
386         if ( mr->mr_obsolete == LDAP_SCHEMA_YES ) {
387                 print_literal(ss, "OBSOLETE");
388                 print_whsp(ss);
389         }
390
391         if ( mr->mr_syntax_oid ) {
392                 print_literal(ss,"SYNTAX");
393                 print_whsp(ss);
394                 print_literal(ss, mr->mr_syntax_oid);
395                 print_whsp(ss);
396         }
397
398         print_whsp(ss);
399
400         print_extensions(ss, mr->mr_extensions);
401
402         print_literal(ss,/*(*/")");
403
404         bv->bv_val = safe_strdup(ss);
405         bv->bv_len = ss->pos;
406         safe_string_free(ss);
407         return(bv);
408 }
409
410 char *
411 ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru )
412 {
413         struct berval bv;
414         if (ldap_matchingruleuse2bv( mru, &bv ))
415                 return(bv.bv_val);
416         else
417                 return NULL;
418 }
419
420 struct berval *
421 ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv )
422 {
423         safe_string * ss;
424         
425         ss = new_safe_string(256);
426         if ( !ss )
427                 return NULL;
428
429         print_literal(ss,"(" /*)*/);
430         print_whsp(ss);
431
432         print_numericoid(ss, mru->mru_oid);
433         print_whsp(ss);
434
435         if ( mru->mru_names ) {
436                 print_literal(ss,"NAME");
437                 print_qdescrs(ss,mru->mru_names);
438         }
439
440         if ( mru->mru_desc ) {
441                 print_literal(ss,"DESC");
442                 print_qdstring(ss,mru->mru_desc);
443         }
444
445         if ( mru->mru_obsolete == LDAP_SCHEMA_YES ) {
446                 print_literal(ss, "OBSOLETE");
447                 print_whsp(ss);
448         }
449
450         if ( mru->mru_applies_oids ) {
451                 print_literal(ss,"APPLIES");
452                 print_whsp(ss);
453                 print_oids(ss, mru->mru_applies_oids);
454                 print_whsp(ss);
455         }
456
457         print_whsp(ss);
458
459         print_extensions(ss, mru->mru_extensions);
460
461         print_literal(ss,/*(*/")");
462
463         bv->bv_val = safe_strdup(ss);
464         bv->bv_len = ss->pos;
465         safe_string_free(ss);
466         return(bv);
467 }
468
469 char *
470 ldap_objectclass2str( LDAPObjectClass * oc )
471 {
472         struct berval bv;
473         if (ldap_objectclass2bv( oc, &bv ))
474                 return(bv.bv_val);
475         else
476                 return NULL;
477 }
478
479 struct berval *
480 ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv )
481 {
482         safe_string * ss;
483         
484         ss = new_safe_string(256);
485         if ( !ss )
486                 return NULL;
487
488         print_literal(ss,"("/*)*/);
489         print_whsp(ss);
490
491         print_numericoid(ss, oc->oc_oid);
492         print_whsp(ss);
493
494         if ( oc->oc_names ) {
495                 print_literal(ss,"NAME");
496                 print_qdescrs(ss,oc->oc_names);
497         }
498
499         if ( oc->oc_desc ) {
500                 print_literal(ss,"DESC");
501                 print_qdstring(ss,oc->oc_desc);
502         }
503
504         if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) {
505                 print_literal(ss, "OBSOLETE");
506                 print_whsp(ss);
507         }
508
509         if ( oc->oc_sup_oids ) {
510                 print_literal(ss,"SUP");
511                 print_whsp(ss);
512                 print_oids(ss,oc->oc_sup_oids);
513                 print_whsp(ss);
514         }
515
516         switch (oc->oc_kind) {
517         case LDAP_SCHEMA_ABSTRACT:
518                 print_literal(ss,"ABSTRACT");
519                 break;
520         case LDAP_SCHEMA_STRUCTURAL:
521                 print_literal(ss,"STRUCTURAL");
522                 break;
523         case LDAP_SCHEMA_AUXILIARY:
524                 print_literal(ss,"AUXILIARY");
525                 break;
526         default:
527                 print_literal(ss,"KIND-UNKNOWN");
528                 break;
529         }
530         print_whsp(ss);
531         
532         if ( oc->oc_at_oids_must ) {
533                 print_literal(ss,"MUST");
534                 print_whsp(ss);
535                 print_oids(ss,oc->oc_at_oids_must);
536                 print_whsp(ss);
537         }
538
539         if ( oc->oc_at_oids_may ) {
540                 print_literal(ss,"MAY");
541                 print_whsp(ss);
542                 print_oids(ss,oc->oc_at_oids_may);
543                 print_whsp(ss);
544         }
545
546         print_whsp(ss);
547
548         print_extensions(ss, oc->oc_extensions);
549
550         print_literal(ss, /*(*/")");
551
552         bv->bv_val = safe_strdup(ss);
553         bv->bv_len = ss->pos;
554         safe_string_free(ss);
555         return(bv);
556 }
557
558 char *
559 ldap_attributetype2str( LDAPAttributeType * at )
560 {
561         struct berval bv;
562         if (ldap_attributetype2bv( at, &bv ))
563                 return(bv.bv_val);
564         else
565                 return NULL;
566 }
567
568 struct berval *
569 ldap_attributetype2bv(  LDAPAttributeType * at, struct berval *bv )
570 {
571         safe_string * ss;
572         
573         ss = new_safe_string(256);
574         if ( !ss )
575                 return NULL;
576
577         print_literal(ss,"("/*)*/);
578         print_whsp(ss);
579
580         print_numericoid(ss, at->at_oid);
581         print_whsp(ss);
582
583         if ( at->at_names ) {
584                 print_literal(ss,"NAME");
585                 print_qdescrs(ss,at->at_names);
586         }
587
588         if ( at->at_desc ) {
589                 print_literal(ss,"DESC");
590                 print_qdstring(ss,at->at_desc);
591         }
592
593         if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
594                 print_literal(ss, "OBSOLETE");
595                 print_whsp(ss);
596         }
597
598         if ( at->at_sup_oid ) {
599                 print_literal(ss,"SUP");
600                 print_woid(ss,at->at_sup_oid);
601         }
602
603         if ( at->at_equality_oid ) {
604                 print_literal(ss,"EQUALITY");
605                 print_woid(ss,at->at_equality_oid);
606         }
607
608         if ( at->at_ordering_oid ) {
609                 print_literal(ss,"ORDERING");
610                 print_woid(ss,at->at_ordering_oid);
611         }
612
613         if ( at->at_substr_oid ) {
614                 print_literal(ss,"SUBSTR");
615                 print_woid(ss,at->at_substr_oid);
616         }
617
618         if ( at->at_syntax_oid ) {
619                 print_literal(ss,"SYNTAX");
620                 print_whsp(ss);
621                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
622                 print_whsp(ss);
623         }
624
625         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
626                 print_literal(ss,"SINGLE-VALUE");
627                 print_whsp(ss);
628         }
629
630         if ( at->at_collective == LDAP_SCHEMA_YES ) {
631                 print_literal(ss,"COLLECTIVE");
632                 print_whsp(ss);
633         }
634
635         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
636                 print_literal(ss,"NO-USER-MODIFICATION");
637                 print_whsp(ss);
638         }
639
640         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
641                 print_literal(ss,"USAGE");
642                 print_whsp(ss);
643                 switch (at->at_usage) {
644                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
645                         print_literal(ss,"directoryOperation");
646                         break;
647                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
648                         print_literal(ss,"distributedOperation");
649                         break;
650                 case LDAP_SCHEMA_DSA_OPERATION:
651                         print_literal(ss,"dSAOperation");
652                         break;
653                 default:
654                         print_literal(ss,"UNKNOWN");
655                         break;
656                 }
657         }
658         
659         print_whsp(ss);
660
661         print_extensions(ss, at->at_extensions);
662
663         print_literal(ss,/*(*/")");
664
665         bv->bv_val = safe_strdup(ss);
666         bv->bv_len = ss->pos;
667         safe_string_free(ss);
668         return(bv);
669 }
670
671 /*
672  * Now come the parsers.  There is one parser for each entity type:
673  * objectclasses, attributetypes, etc.
674  *
675  * Each of them is written as a recursive-descent parser, except that
676  * none of them is really recursive.  But the idea is kept: there
677  * is one routine per non-terminal that eithers gobbles lexical tokens
678  * or calls lower-level routines, etc.
679  *
680  * The scanner is implemented in the routine get_token.  Actually,
681  * get_token is more than a scanner and will return tokens that are
682  * in fact non-terminals in the grammar.  So you can see the whole
683  * approach as the combination of a low-level bottom-up recognizer
684  * combined with a scanner and a number of top-down parsers.  Or just
685  * consider that the real grammars recognized by the parsers are not
686  * those of the standards.  As a matter of fact, our parsers are more
687  * liberal than the spec when there is no ambiguity.
688  *
689  * The difference is pretty academic (modulo bugs or incorrect
690  * interpretation of the specs).
691  */
692
693 #define TK_NOENDQUOTE   -2
694 #define TK_OUTOFMEM     -1
695 #define TK_EOS          0
696 #define TK_UNEXPCHAR    1
697 #define TK_BAREWORD     2
698 #define TK_QDSTRING     3
699 #define TK_LEFTPAREN    4
700 #define TK_RIGHTPAREN   5
701 #define TK_DOLLAR       6
702 #define TK_QDESCR       TK_QDSTRING
703
704 struct token {
705         int type;
706         char *sval;
707 };
708
709 static int
710 get_token( const char ** sp, char ** token_val )
711 {
712         int kind;
713         const char * p;
714         const char * q;
715         char * res;
716
717         *token_val = NULL;
718         switch (**sp) {
719         case '\0':
720                 kind = TK_EOS;
721                 (*sp)++;
722                 break;
723         case '(':
724                 kind = TK_LEFTPAREN;
725                 (*sp)++;
726                 break;
727         case ')':
728                 kind = TK_RIGHTPAREN;
729                 (*sp)++;
730                 break;
731         case '$':
732                 kind = TK_DOLLAR;
733                 (*sp)++;
734                 break;
735         case '\'':
736                 kind = TK_QDSTRING;
737                 (*sp)++;
738                 p = *sp;
739                 while ( **sp != '\'' && **sp != '\0' )
740                         (*sp)++;
741                 if ( **sp == '\'' ) {
742                         q = *sp;
743                         res = LDAP_MALLOC(q-p+1);
744                         if ( !res ) {
745                                 kind = TK_OUTOFMEM;
746                         } else {
747                                 strncpy(res,p,q-p);
748                                 res[q-p] = '\0';
749                                 *token_val = res;
750                         }
751                         (*sp)++;
752                 } else {
753                         kind = TK_NOENDQUOTE;
754                 }
755                 break;
756         default:
757                 kind = TK_BAREWORD;
758                 p = *sp;
759                 while ( !LDAP_SPACE(**sp) &&
760                         **sp != '(' &&
761                         **sp != ')' &&
762                         **sp != '$' &&
763                         **sp != '\'' &&
764                         **sp != '\0' )
765                         (*sp)++;
766                 q = *sp;
767                 res = LDAP_MALLOC(q-p+1);
768                 if ( !res ) {
769                         kind = TK_OUTOFMEM;
770                 } else {
771                         strncpy(res,p,q-p);
772                         res[q-p] = '\0';
773                         *token_val = res;
774                 }
775                 break;
776 /*              kind = TK_UNEXPCHAR; */
777 /*              break; */
778         }
779         
780         return kind;
781 }
782
783 /* Gobble optional whitespace */
784 static void
785 parse_whsp(const char **sp)
786 {
787         while (LDAP_SPACE(**sp))
788                 (*sp)++;
789 }
790
791 /* TBC:!!
792  * General note for all parsers: to guarantee the algorithm halts they
793  * must always advance the pointer even when an error is found.  For
794  * this one is not that important since an error here is fatal at the
795  * upper layers, but it is a simple strategy that will not get in
796  * endless loops.
797  */
798
799 /* Parse a sequence of dot-separated decimal strings */
800 char *
801 parse_numericoid(const char **sp, int *code, const int flags)
802 {
803         char * res = NULL;
804         const char * start = *sp;
805         int len;
806         int quoted = 0;
807
808         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
809         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
810                 quoted = 1;
811                 (*sp)++;
812                 start++;
813         }
814         /* Each iteration of this loop gets one decimal string */
815         while (**sp) {
816                 if ( !LDAP_DIGIT(**sp) ) {
817                         /*
818                          * Initial char is not a digit or char after dot is
819                          * not a digit
820                          */
821                         *code = LDAP_SCHERR_NODIGIT;
822                         return NULL;
823                 }
824                 (*sp)++;
825                 while ( LDAP_DIGIT(**sp) )
826                         (*sp)++;
827                 if ( **sp != '.' )
828                         break;
829                 /* Otherwise, gobble the dot and loop again */
830                 (*sp)++;
831         }
832         /* Now *sp points at the char past the numericoid. Perfect. */
833         len = *sp - start;
834         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
835                 if ( **sp == '\'' ) {
836                         (*sp)++;
837                 } else {
838                         *code = LDAP_SCHERR_UNEXPTOKEN;
839                         return NULL;
840                 }
841         }
842         if (flags & LDAP_SCHEMA_SKIP) {
843                 res = start;
844         } else {
845                 res = LDAP_MALLOC(len+1);
846                 if (!res) {
847                         *code = LDAP_SCHERR_OUTOFMEM;
848                         return(NULL);
849                 }
850                 strncpy(res,start,len);
851                 res[len] = '\0';
852         }
853         return(res);
854 }
855
856 /* Parse a qdescr or a list of them enclosed in () */
857 static char **
858 parse_qdescrs(const char **sp, int *code)
859 {
860         char ** res;
861         char ** res1;
862         int kind;
863         char * sval;
864         int size;
865         int pos;
866
867         parse_whsp(sp);
868         kind = get_token(sp,&sval);
869         if ( kind == TK_LEFTPAREN ) {
870                 /* Let's presume there will be at least 2 entries */
871                 size = 3;
872                 res = LDAP_CALLOC(3,sizeof(char *));
873                 if ( !res ) {
874                         *code = LDAP_SCHERR_OUTOFMEM;
875                         return NULL;
876                 }
877                 pos = 0;
878                 while (1) {
879                         parse_whsp(sp);
880                         kind = get_token(sp,&sval);
881                         if ( kind == TK_RIGHTPAREN )
882                                 break;
883                         if ( kind == TK_QDESCR ) {
884                                 if ( pos == size-2 ) {
885                                         size++;
886                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
887                                         if ( !res1 ) {
888                                                 LDAP_VFREE(res);
889                                                 LDAP_FREE(sval);
890                                                 *code = LDAP_SCHERR_OUTOFMEM;
891                                                 return(NULL);
892                                         }
893                                         res = res1;
894                                 }
895                                 res[pos] = sval;
896                                 pos++;
897                                 parse_whsp(sp);
898                         } else {
899                                 LDAP_VFREE(res);
900                                 LDAP_FREE(sval);
901                                 *code = LDAP_SCHERR_UNEXPTOKEN;
902                                 return(NULL);
903                         }
904                 }
905                 res[pos] = NULL;
906                 parse_whsp(sp);
907                 return(res);
908         } else if ( kind == TK_QDESCR ) {
909                 res = LDAP_CALLOC(2,sizeof(char *));
910                 if ( !res ) {
911                         *code = LDAP_SCHERR_OUTOFMEM;
912                         return NULL;
913                 }
914                 res[0] = sval;
915                 res[1] = NULL;
916                 parse_whsp(sp);
917                 return res;
918         } else {
919                 LDAP_FREE(sval);
920                 *code = LDAP_SCHERR_BADNAME;
921                 return NULL;
922         }
923 }
924
925 /* Parse a woid */
926 static char *
927 parse_woid(const char **sp, int *code)
928 {
929         char * sval;
930         int kind;
931
932         parse_whsp(sp);
933         kind = get_token(sp, &sval);
934         if ( kind != TK_BAREWORD ) {
935                 LDAP_FREE(sval);
936                 *code = LDAP_SCHERR_UNEXPTOKEN;
937                 return NULL;
938         }
939         parse_whsp(sp);
940         return sval;
941 }
942
943 /* Parse a noidlen */
944 static char *
945 parse_noidlen(const char **sp, int *code, int *len, int allow_quoted)
946 {
947         char * sval;
948         int quoted = 0;
949
950         *len = 0;
951         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
952         if ( allow_quoted && **sp == '\'' ) {
953                 quoted = 1;
954                 (*sp)++;
955         }
956         sval = parse_numericoid(sp, code, 0);
957         if ( !sval ) {
958                 return NULL;
959         }
960         if ( **sp == '{' /*}*/ ) {
961                 (*sp)++;
962                 *len = atoi(*sp);
963                 while ( LDAP_DIGIT(**sp) )
964                         (*sp)++;
965                 if ( **sp != /*{*/ '}' ) {
966                         *code = LDAP_SCHERR_UNEXPTOKEN;
967                         LDAP_FREE(sval);
968                         return NULL;
969                 }
970                 (*sp)++;
971         }               
972         if ( allow_quoted && quoted ) {
973                 if ( **sp == '\'' ) {
974                         (*sp)++;
975                 } else {
976                         *code = LDAP_SCHERR_UNEXPTOKEN;
977                         LDAP_FREE(sval);
978                         return NULL;
979                 }
980         }
981         return sval;
982 }
983
984 /*
985  * Next routine will accept a qdstring in place of an oid if
986  * allow_quoted is set.  This is necessary to interoperate with
987  * Netscape Directory server that will improperly quote each oid (at
988  * least those of the descr kind) in the SUP clause.
989  */
990
991 /* Parse a woid or a $-separated list of them enclosed in () */
992 static char **
993 parse_oids(const char **sp, int *code, const int allow_quoted)
994 {
995         char ** res;
996         char ** res1;
997         int kind;
998         char * sval;
999         int size;
1000         int pos;
1001
1002         /*
1003          * Strictly speaking, doing this here accepts whsp before the
1004          * ( at the begining of an oidlist, but this is harmless.  Also,
1005          * we are very liberal in what we accept as an OID.  Maybe
1006          * refine later.
1007          */
1008         parse_whsp(sp);
1009         kind = get_token(sp,&sval);
1010         if ( kind == TK_LEFTPAREN ) {
1011                 /* Let's presume there will be at least 2 entries */
1012                 size = 3;
1013                 res = LDAP_CALLOC(3,sizeof(char *));
1014                 if ( !res ) {
1015                         *code = LDAP_SCHERR_OUTOFMEM;
1016                         return NULL;
1017                 }
1018                 pos = 0;
1019                 parse_whsp(sp);
1020                 kind = get_token(sp,&sval);
1021                 if ( kind == TK_BAREWORD ||
1022                      ( allow_quoted && kind == TK_QDSTRING ) ) {
1023                         res[pos] = sval;
1024                         pos++;
1025                 } else {
1026                         *code = LDAP_SCHERR_UNEXPTOKEN;
1027                         LDAP_FREE(sval);
1028                         LDAP_VFREE(res);
1029                         return NULL;
1030                 }
1031                 parse_whsp(sp);
1032                 while (1) {
1033                         kind = get_token(sp,&sval);
1034                         if ( kind == TK_RIGHTPAREN )
1035                                 break;
1036                         if ( kind == TK_DOLLAR ) {
1037                                 parse_whsp(sp);
1038                                 kind = get_token(sp,&sval);
1039                                 if ( kind == TK_BAREWORD ||
1040                                      ( allow_quoted &&
1041                                        kind == TK_QDSTRING ) ) {
1042                                         if ( pos == size-2 ) {
1043                                                 size++;
1044                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
1045                                                 if ( !res1 ) {
1046                                                         LDAP_FREE(sval);
1047                                                         LDAP_VFREE(res);
1048                                                         *code = LDAP_SCHERR_OUTOFMEM;
1049                                                         return(NULL);
1050                                                 }
1051                                                 res = res1;
1052                                         }
1053                                         res[pos] = sval;
1054                                         pos++;
1055                                 } else {
1056                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1057                                         LDAP_FREE(sval);
1058                                         LDAP_VFREE(res);
1059                                         return NULL;
1060                                 }
1061                                 parse_whsp(sp);
1062                         } else {
1063                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1064                                 LDAP_FREE(sval);
1065                                 LDAP_VFREE(res);
1066                                 return NULL;
1067                         }
1068                 }
1069                 res[pos] = NULL;
1070                 parse_whsp(sp);
1071                 return(res);
1072         } else if ( kind == TK_BAREWORD ||
1073                     ( allow_quoted && kind == TK_QDSTRING ) ) {
1074                 res = LDAP_CALLOC(2,sizeof(char *));
1075                 if ( !res ) {
1076                         LDAP_FREE(sval);
1077                         *code = LDAP_SCHERR_OUTOFMEM;
1078                         return NULL;
1079                 }
1080                 res[0] = sval;
1081                 res[1] = NULL;
1082                 parse_whsp(sp);
1083                 return res;
1084         } else {
1085                 LDAP_FREE(sval);
1086                 *code = LDAP_SCHERR_BADNAME;
1087                 return NULL;
1088         }
1089 }
1090
1091 static int
1092 add_extension(LDAPSchemaExtensionItem ***extensions,
1093               char * name, char ** values)
1094 {
1095         int n;
1096         LDAPSchemaExtensionItem **tmp, *ext;
1097
1098         ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
1099         if ( !ext )
1100                 return 1;
1101         ext->lsei_name = name;
1102         ext->lsei_values = values;
1103
1104         if ( !*extensions ) {
1105                 *extensions =
1106                   LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
1107                 if ( !*extensions )
1108                   return 1;
1109                 n = 0;
1110         } else {
1111                 for ( n=0; (*extensions)[n] != NULL; n++ )
1112                         ;
1113                 tmp = LDAP_REALLOC(*extensions,
1114                                    (n+2)*sizeof(LDAPSchemaExtensionItem *));
1115                 if ( !tmp )
1116                         return 1;
1117                 *extensions = tmp;
1118         }
1119         (*extensions)[n] = ext;
1120         (*extensions)[n+1] = NULL;
1121         return 0;
1122 }
1123
1124 static void
1125 free_extensions(LDAPSchemaExtensionItem **extensions)
1126 {
1127         LDAPSchemaExtensionItem **ext;
1128
1129         if ( extensions ) {
1130                 for ( ext = extensions; *ext != NULL; ext++ ) {
1131                         LDAP_FREE((*ext)->lsei_name);
1132                         LDAP_VFREE((*ext)->lsei_values);
1133                         LDAP_FREE(*ext);
1134                 }
1135                 LDAP_FREE(extensions);
1136         }
1137 }
1138
1139 void
1140 ldap_syntax_free( LDAPSyntax * syn )
1141 {
1142         LDAP_FREE(syn->syn_oid);
1143         LDAP_VFREE(syn->syn_names);
1144         LDAP_FREE(syn->syn_desc);
1145         free_extensions(syn->syn_extensions);
1146         LDAP_FREE(syn);
1147 }
1148
1149 LDAPSyntax *
1150 ldap_str2syntax( LDAP_CONST char * s,
1151         int * code,
1152         LDAP_CONST char ** errp,
1153         LDAP_CONST int flags )
1154 {
1155         int kind;
1156         const char * ss = s;
1157         char * sval;
1158         int seen_name = 0;
1159         int seen_desc = 0;
1160         LDAPSyntax * syn;
1161         char ** ext_vals;
1162
1163         if ( !s ) {
1164                 *code = LDAP_SCHERR_EMPTY;
1165                 *errp = "";
1166                 return NULL;
1167         }
1168
1169         *errp = s;
1170         syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
1171
1172         if ( !syn ) {
1173                 *code = LDAP_SCHERR_OUTOFMEM;
1174                 return NULL;
1175         }
1176
1177         kind = get_token(&ss,&sval);
1178         if ( kind != TK_LEFTPAREN ) {
1179                 LDAP_FREE(sval);
1180                 *code = LDAP_SCHERR_NOLEFTPAREN;
1181                 ldap_syntax_free(syn);
1182                 return NULL;
1183         }
1184
1185         parse_whsp(&ss);
1186         syn->syn_oid = parse_numericoid(&ss,code,0);
1187         if ( !syn->syn_oid ) {
1188                 *errp = ss;
1189                 ldap_syntax_free(syn);
1190                 return NULL;
1191         }
1192         parse_whsp(&ss);
1193
1194         /*
1195          * Beyond this point we will be liberal and accept the items
1196          * in any order.
1197          */
1198         while (1) {
1199                 kind = get_token(&ss,&sval);
1200                 switch (kind) {
1201                 case TK_EOS:
1202                         *code = LDAP_SCHERR_NORIGHTPAREN;
1203                         *errp = ss;
1204                         ldap_syntax_free(syn);
1205                         return NULL;
1206                 case TK_RIGHTPAREN:
1207                         return syn;
1208                 case TK_BAREWORD:
1209                         if ( !strcmp(sval,"NAME") ) {
1210                                 LDAP_FREE(sval);
1211                                 if ( seen_name ) {
1212                                         *code = LDAP_SCHERR_DUPOPT;
1213                                         *errp = ss;
1214                                         ldap_syntax_free(syn);
1215                                         return(NULL);
1216                                 }
1217                                 seen_name = 1;
1218                                 syn->syn_names = parse_qdescrs(&ss,code);
1219                                 if ( !syn->syn_names ) {
1220                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1221                                                 *code = LDAP_SCHERR_BADNAME;
1222                                         *errp = ss;
1223                                         ldap_syntax_free(syn);
1224                                         return NULL;
1225                                 }
1226                         } else if ( !strcmp(sval,"DESC") ) {
1227                                 LDAP_FREE(sval);
1228                                 if ( seen_desc ) {
1229                                         *code = LDAP_SCHERR_DUPOPT;
1230                                         *errp = ss;
1231                                         ldap_syntax_free(syn);
1232                                         return(NULL);
1233                                 }
1234                                 seen_desc = 1;
1235                                 parse_whsp(&ss);
1236                                 kind = get_token(&ss,&sval);
1237                                 if ( kind != TK_QDSTRING ) {
1238                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1239                                         *errp = ss;
1240                                         LDAP_FREE(sval);
1241                                         ldap_syntax_free(syn);
1242                                         return NULL;
1243                                 }
1244                                 syn->syn_desc = sval;
1245                                 parse_whsp(&ss);
1246                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1247                                 /* Should be parse_qdstrings */
1248                                 ext_vals = parse_qdescrs(&ss, code);
1249                                 if ( !ext_vals ) {
1250                                         *errp = ss;
1251                                         ldap_syntax_free(syn);
1252                                         return NULL;
1253                                 }
1254                                 if ( add_extension(&syn->syn_extensions,
1255                                                     sval, ext_vals) ) {
1256                                         *code = LDAP_SCHERR_OUTOFMEM;
1257                                         *errp = ss;
1258                                         LDAP_FREE(sval);
1259                                         ldap_syntax_free(syn);
1260                                         return NULL;
1261                                 }
1262                         } else {
1263                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1264                                 *errp = ss;
1265                                 LDAP_FREE(sval);
1266                                 ldap_syntax_free(syn);
1267                                 return NULL;
1268                         }
1269                         break;
1270                 default:
1271                         *code = LDAP_SCHERR_UNEXPTOKEN;
1272                         *errp = ss;
1273                         LDAP_FREE(sval);
1274                         ldap_syntax_free(syn);
1275                         return NULL;
1276                 }
1277         }
1278 }
1279
1280 void
1281 ldap_matchingrule_free( LDAPMatchingRule * mr )
1282 {
1283         LDAP_FREE(mr->mr_oid);
1284         LDAP_VFREE(mr->mr_names);
1285         LDAP_FREE(mr->mr_desc);
1286         LDAP_FREE(mr->mr_syntax_oid);
1287         free_extensions(mr->mr_extensions);
1288         LDAP_FREE(mr);
1289 }
1290
1291 LDAPMatchingRule *
1292 ldap_str2matchingrule( LDAP_CONST char * s,
1293         int * code,
1294         LDAP_CONST char ** errp,
1295         LDAP_CONST int flags )
1296 {
1297         int kind;
1298         const char * ss = s;
1299         char * sval;
1300         int seen_name = 0;
1301         int seen_desc = 0;
1302         int seen_obsolete = 0;
1303         int seen_syntax = 0;
1304         LDAPMatchingRule * mr;
1305         char ** ext_vals;
1306         const char * savepos;
1307
1308         if ( !s ) {
1309                 *code = LDAP_SCHERR_EMPTY;
1310                 *errp = "";
1311                 return NULL;
1312         }
1313
1314         *errp = s;
1315         mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1316
1317         if ( !mr ) {
1318                 *code = LDAP_SCHERR_OUTOFMEM;
1319                 return NULL;
1320         }
1321
1322         kind = get_token(&ss,&sval);
1323         if ( kind != TK_LEFTPAREN ) {
1324                 *code = LDAP_SCHERR_NOLEFTPAREN;
1325                 LDAP_FREE(sval);
1326                 ldap_matchingrule_free(mr);
1327                 return NULL;
1328         }
1329
1330         parse_whsp(&ss);
1331         savepos = ss;
1332         mr->mr_oid = parse_numericoid(&ss,code,flags);
1333         if ( !mr->mr_oid ) {
1334                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1335                         /* Backtracking */
1336                         ss = savepos;
1337                         kind = get_token(&ss,&sval);
1338                         if ( kind == TK_BAREWORD ) {
1339                                 if ( !strcmp(sval, "NAME") ||
1340                                      !strcmp(sval, "DESC") ||
1341                                      !strcmp(sval, "OBSOLETE") ||
1342                                      !strcmp(sval, "SYNTAX") ||
1343                                      !strncmp(sval, "X-", 2) ) {
1344                                         /* Missing OID, backtrack */
1345                                         ss = savepos;
1346                                 } else {
1347                                         /* Non-numerical OID, ignore */
1348                                 }
1349                         }
1350                         LDAP_FREE(sval);
1351                 } else {
1352                         *errp = ss;
1353                         ldap_matchingrule_free(mr);
1354                         return NULL;
1355                 }
1356         }
1357         parse_whsp(&ss);
1358
1359         /*
1360          * Beyond this point we will be liberal and accept the items
1361          * in any order.
1362          */
1363         while (1) {
1364                 kind = get_token(&ss,&sval);
1365                 switch (kind) {
1366                 case TK_EOS:
1367                         *code = LDAP_SCHERR_NORIGHTPAREN;
1368                         *errp = ss;
1369                         ldap_matchingrule_free(mr);
1370                         return NULL;
1371                 case TK_RIGHTPAREN:
1372                         return mr;
1373                 case TK_BAREWORD:
1374                         if ( !strcmp(sval,"NAME") ) {
1375                                 LDAP_FREE(sval);
1376                                 if ( seen_name ) {
1377                                         *code = LDAP_SCHERR_DUPOPT;
1378                                         *errp = ss;
1379                                         ldap_matchingrule_free(mr);
1380                                         return(NULL);
1381                                 }
1382                                 seen_name = 1;
1383                                 mr->mr_names = parse_qdescrs(&ss,code);
1384                                 if ( !mr->mr_names ) {
1385                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1386                                                 *code = LDAP_SCHERR_BADNAME;
1387                                         *errp = ss;
1388                                         ldap_matchingrule_free(mr);
1389                                         return NULL;
1390                                 }
1391                         } else if ( !strcmp(sval,"DESC") ) {
1392                                 LDAP_FREE(sval);
1393                                 if ( seen_desc ) {
1394                                         *code = LDAP_SCHERR_DUPOPT;
1395                                         *errp = ss;
1396                                         ldap_matchingrule_free(mr);
1397                                         return(NULL);
1398                                 }
1399                                 seen_desc = 1;
1400                                 parse_whsp(&ss);
1401                                 kind = get_token(&ss,&sval);
1402                                 if ( kind != TK_QDSTRING ) {
1403                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1404                                         *errp = ss;
1405                                         LDAP_FREE(sval);
1406                                         ldap_matchingrule_free(mr);
1407                                         return NULL;
1408                                 }
1409                                 mr->mr_desc = sval;
1410                                 parse_whsp(&ss);
1411                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1412                                 LDAP_FREE(sval);
1413                                 if ( seen_obsolete ) {
1414                                         *code = LDAP_SCHERR_DUPOPT;
1415                                         *errp = ss;
1416                                         ldap_matchingrule_free(mr);
1417                                         return(NULL);
1418                                 }
1419                                 seen_obsolete = 1;
1420                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1421                                 parse_whsp(&ss);
1422                         } else if ( !strcmp(sval,"SYNTAX") ) {
1423                                 LDAP_FREE(sval);
1424                                 if ( seen_syntax ) {
1425                                         *code = LDAP_SCHERR_DUPOPT;
1426                                         *errp = ss;
1427                                         ldap_matchingrule_free(mr);
1428                                         return(NULL);
1429                                 }
1430                                 seen_syntax = 1;
1431                                 parse_whsp(&ss);
1432                                 mr->mr_syntax_oid =
1433                                         parse_numericoid(&ss,code,flags);
1434                                 if ( !mr->mr_syntax_oid ) {
1435                                         *errp = ss;
1436                                         ldap_matchingrule_free(mr);
1437                                         return NULL;
1438                                 }
1439                                 parse_whsp(&ss);
1440                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1441                                 /* Should be parse_qdstrings */
1442                                 ext_vals = parse_qdescrs(&ss, code);
1443                                 if ( !ext_vals ) {
1444                                         *errp = ss;
1445                                         ldap_matchingrule_free(mr);
1446                                         return NULL;
1447                                 }
1448                                 if ( add_extension(&mr->mr_extensions,
1449                                                     sval, ext_vals) ) {
1450                                         *code = LDAP_SCHERR_OUTOFMEM;
1451                                         *errp = ss;
1452                                         LDAP_FREE(sval);
1453                                         ldap_matchingrule_free(mr);
1454                                         return NULL;
1455                                 }
1456                         } else {
1457                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1458                                 *errp = ss;
1459                                 LDAP_FREE(sval);
1460                                 ldap_matchingrule_free(mr);
1461                                 return NULL;
1462                         }
1463                         break;
1464                 default:
1465                         *code = LDAP_SCHERR_UNEXPTOKEN;
1466                         *errp = ss;
1467                         LDAP_FREE(sval);
1468                         ldap_matchingrule_free(mr);
1469                         return NULL;
1470                 }
1471         }
1472 }
1473
1474 void
1475 ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
1476 {
1477         LDAP_FREE(mru->mru_oid);
1478         LDAP_VFREE(mru->mru_names);
1479         LDAP_FREE(mru->mru_desc);
1480         LDAP_VFREE(mru->mru_applies_oids);
1481         free_extensions(mru->mru_extensions);
1482         LDAP_FREE(mru);
1483 }
1484
1485 LDAPMatchingRuleUse *
1486 ldap_str2matchingruleuse( LDAP_CONST char * s,
1487         int * code,
1488         LDAP_CONST char ** errp,
1489         LDAP_CONST int flags )
1490 {
1491         int kind;
1492         const char * ss = s;
1493         char * sval;
1494         int seen_name = 0;
1495         int seen_desc = 0;
1496         int seen_obsolete = 0;
1497         int seen_applies = 0;
1498         LDAPMatchingRuleUse * mru;
1499         char ** ext_vals;
1500         const char * savepos;
1501
1502         if ( !s ) {
1503                 *code = LDAP_SCHERR_EMPTY;
1504                 *errp = "";
1505                 return NULL;
1506         }
1507
1508         *errp = s;
1509         mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
1510
1511         if ( !mru ) {
1512                 *code = LDAP_SCHERR_OUTOFMEM;
1513                 return NULL;
1514         }
1515
1516         kind = get_token(&ss,&sval);
1517         if ( kind != TK_LEFTPAREN ) {
1518                 *code = LDAP_SCHERR_NOLEFTPAREN;
1519                 LDAP_FREE(sval);
1520                 ldap_matchingruleuse_free(mru);
1521                 return NULL;
1522         }
1523
1524         parse_whsp(&ss);
1525         savepos = ss;
1526         mru->mru_oid = parse_numericoid(&ss,code,flags);
1527         if ( !mru->mru_oid ) {
1528                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1529                         /* Backtracking */
1530                         ss = savepos;
1531                         kind = get_token(&ss,&sval);
1532                         if ( kind == TK_BAREWORD ) {
1533                                 if ( !strcmp(sval, "NAME") ||
1534                                      !strcmp(sval, "DESC") ||
1535                                      !strcmp(sval, "OBSOLETE") ||
1536                                      !strcmp(sval, "APPLIES") ||
1537                                      !strncmp(sval, "X-", 2) ) {
1538                                         /* Missing OID, backtrack */
1539                                         ss = savepos;
1540                                 } else {
1541                                         /* Non-numerical OID, ignore */
1542                                 }
1543                         }
1544                         LDAP_FREE(sval);
1545                 } else {
1546                         *errp = ss;
1547                         ldap_matchingruleuse_free(mru);
1548                         return NULL;
1549                 }
1550         }
1551         parse_whsp(&ss);
1552
1553         /*
1554          * Beyond this point we will be liberal and accept the items
1555          * in any order.
1556          */
1557         while (1) {
1558                 kind = get_token(&ss,&sval);
1559                 switch (kind) {
1560                 case TK_EOS:
1561                         *code = LDAP_SCHERR_NORIGHTPAREN;
1562                         *errp = ss;
1563                         ldap_matchingruleuse_free(mru);
1564                         return NULL;
1565                 case TK_RIGHTPAREN:
1566                         return mru;
1567                 case TK_BAREWORD:
1568                         if ( !strcmp(sval,"NAME") ) {
1569                                 LDAP_FREE(sval);
1570                                 if ( seen_name ) {
1571                                         *code = LDAP_SCHERR_DUPOPT;
1572                                         *errp = ss;
1573                                         ldap_matchingruleuse_free(mru);
1574                                         return(NULL);
1575                                 }
1576                                 seen_name = 1;
1577                                 mru->mru_names = parse_qdescrs(&ss,code);
1578                                 if ( !mru->mru_names ) {
1579                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1580                                                 *code = LDAP_SCHERR_BADNAME;
1581                                         *errp = ss;
1582                                         ldap_matchingruleuse_free(mru);
1583                                         return NULL;
1584                                 }
1585                         } else if ( !strcmp(sval,"DESC") ) {
1586                                 LDAP_FREE(sval);
1587                                 if ( seen_desc ) {
1588                                         *code = LDAP_SCHERR_DUPOPT;
1589                                         *errp = ss;
1590                                         ldap_matchingruleuse_free(mru);
1591                                         return(NULL);
1592                                 }
1593                                 seen_desc = 1;
1594                                 parse_whsp(&ss);
1595                                 kind = get_token(&ss,&sval);
1596                                 if ( kind != TK_QDSTRING ) {
1597                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1598                                         *errp = ss;
1599                                         LDAP_FREE(sval);
1600                                         ldap_matchingruleuse_free(mru);
1601                                         return NULL;
1602                                 }
1603                                 mru->mru_desc = sval;
1604                                 parse_whsp(&ss);
1605                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1606                                 LDAP_FREE(sval);
1607                                 if ( seen_obsolete ) {
1608                                         *code = LDAP_SCHERR_DUPOPT;
1609                                         *errp = ss;
1610                                         ldap_matchingruleuse_free(mru);
1611                                         return(NULL);
1612                                 }
1613                                 seen_obsolete = 1;
1614                                 mru->mru_obsolete = LDAP_SCHEMA_YES;
1615                                 parse_whsp(&ss);
1616                         } else if ( !strcmp(sval,"APPLIES") ) {
1617                                 LDAP_FREE(sval);
1618                                 if ( seen_applies ) {
1619                                         *code = LDAP_SCHERR_DUPOPT;
1620                                         *errp = ss;
1621                                         ldap_matchingruleuse_free(mru);
1622                                         return(NULL);
1623                                 }
1624                                 seen_applies = 1;
1625                                 mru->mru_applies_oids = parse_oids(&ss,
1626                                                              code,
1627                                                              flags);
1628                                 if ( !mru->mru_applies_oids ) {
1629                                         *errp = ss;
1630                                         ldap_matchingruleuse_free(mru);
1631                                         return NULL;
1632                                 }
1633                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1634                                 /* Should be parse_qdstrings */
1635                                 ext_vals = parse_qdescrs(&ss, code);
1636                                 if ( !ext_vals ) {
1637                                         *errp = ss;
1638                                         ldap_matchingruleuse_free(mru);
1639                                         return NULL;
1640                                 }
1641                                 if ( add_extension(&mru->mru_extensions,
1642                                                     sval, ext_vals) ) {
1643                                         *code = LDAP_SCHERR_OUTOFMEM;
1644                                         *errp = ss;
1645                                         LDAP_FREE(sval);
1646                                         ldap_matchingruleuse_free(mru);
1647                                         return NULL;
1648                                 }
1649                         } else {
1650                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1651                                 *errp = ss;
1652                                 LDAP_FREE(sval);
1653                                 ldap_matchingruleuse_free(mru);
1654                                 return NULL;
1655                         }
1656                         break;
1657                 default:
1658                         *code = LDAP_SCHERR_UNEXPTOKEN;
1659                         *errp = ss;
1660                         LDAP_FREE(sval);
1661                         ldap_matchingruleuse_free(mru);
1662                         return NULL;
1663                 }
1664         }
1665 }
1666
1667 void
1668 ldap_attributetype_free(LDAPAttributeType * at)
1669 {
1670         LDAP_FREE(at->at_oid);
1671         LDAP_VFREE(at->at_names);
1672         LDAP_FREE(at->at_desc);
1673         LDAP_FREE(at->at_sup_oid);
1674         LDAP_FREE(at->at_equality_oid);
1675         LDAP_FREE(at->at_ordering_oid);
1676         LDAP_FREE(at->at_substr_oid);
1677         LDAP_FREE(at->at_syntax_oid);
1678         free_extensions(at->at_extensions);
1679         LDAP_FREE(at);
1680 }
1681
1682 LDAPAttributeType *
1683 ldap_str2attributetype( LDAP_CONST char * s,
1684         int * code,
1685         LDAP_CONST char ** errp,
1686         LDAP_CONST int flags )
1687 {
1688         int kind;
1689         const char * ss = s;
1690         char * sval;
1691         int seen_name = 0;
1692         int seen_desc = 0;
1693         int seen_obsolete = 0;
1694         int seen_sup = 0;
1695         int seen_equality = 0;
1696         int seen_ordering = 0;
1697         int seen_substr = 0;
1698         int seen_syntax = 0;
1699         int seen_usage = 0;
1700         LDAPAttributeType * at;
1701         char ** ext_vals;
1702         const char * savepos;
1703
1704         if ( !s ) {
1705                 *code = LDAP_SCHERR_EMPTY;
1706                 *errp = "";
1707                 return NULL;
1708         }
1709
1710         *errp = s;
1711         at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
1712
1713         if ( !at ) {
1714                 *code = LDAP_SCHERR_OUTOFMEM;
1715                 return NULL;
1716         }
1717
1718         kind = get_token(&ss,&sval);
1719         if ( kind != TK_LEFTPAREN ) {
1720                 *code = LDAP_SCHERR_NOLEFTPAREN;
1721                 LDAP_FREE(sval);
1722                 ldap_attributetype_free(at);
1723                 return NULL;
1724         }
1725
1726         /*
1727          * Definitions MUST begin with an OID in the numericoid format.
1728          * However, this routine is used by clients to parse the response
1729          * from servers and very well known servers will provide an OID
1730          * in the wrong format or even no OID at all.  We do our best to
1731          * extract info from those servers.
1732          */
1733         parse_whsp(&ss);
1734         savepos = ss;
1735         at->at_oid = parse_numericoid(&ss,code,0);
1736         if ( !at->at_oid ) {
1737                 if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
1738                                 | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
1739                             && (ss == savepos) ) {
1740                         /* Backtracking */
1741                         ss = savepos;
1742                         kind = get_token(&ss,&sval);
1743                         if ( kind == TK_BAREWORD ) {
1744                                 if ( !strcmp(sval, "NAME") ||
1745                                      !strcmp(sval, "DESC") ||
1746                                      !strcmp(sval, "OBSOLETE") ||
1747                                      !strcmp(sval, "SUP") ||
1748                                      !strcmp(sval, "EQUALITY") ||
1749                                      !strcmp(sval, "ORDERING") ||
1750                                      !strcmp(sval, "SUBSTR") ||
1751                                      !strcmp(sval, "SYNTAX") ||
1752                                      !strcmp(sval, "SINGLE-VALUE") ||
1753                                      !strcmp(sval, "COLLECTIVE") ||
1754                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1755                                      !strcmp(sval, "USAGE") ||
1756                                      !strncmp(sval, "X-", 2) ) {
1757                                         /* Missing OID, backtrack */
1758                                         ss = savepos;
1759                                 } else if ( flags
1760                                         & LDAP_SCHEMA_ALLOW_OID_MACRO) {
1761                                         /* Non-numerical OID ... */
1762                                         int len = ss-savepos;
1763                                         at->at_oid = LDAP_MALLOC(len+1);
1764                                         strncpy(at->at_oid, savepos, len);
1765                                         at->at_oid[len] = 0;
1766                                 }
1767                         }
1768                         LDAP_FREE(sval);
1769                 } else {
1770                         *errp = ss;
1771                         ldap_attributetype_free(at);
1772                         return NULL;
1773                 }
1774         }
1775         parse_whsp(&ss);
1776
1777         /*
1778          * Beyond this point we will be liberal and accept the items
1779          * in any order.
1780          */
1781         while (1) {
1782                 kind = get_token(&ss,&sval);
1783                 switch (kind) {
1784                 case TK_EOS:
1785                         *code = LDAP_SCHERR_NORIGHTPAREN;
1786                         *errp = ss;
1787                         ldap_attributetype_free(at);
1788                         return NULL;
1789                 case TK_RIGHTPAREN:
1790                         return at;
1791                 case TK_BAREWORD:
1792                         if ( !strcmp(sval,"NAME") ) {
1793                                 LDAP_FREE(sval);
1794                                 if ( seen_name ) {
1795                                         *code = LDAP_SCHERR_DUPOPT;
1796                                         *errp = ss;
1797                                         ldap_attributetype_free(at);
1798                                         return(NULL);
1799                                 }
1800                                 seen_name = 1;
1801                                 at->at_names = parse_qdescrs(&ss,code);
1802                                 if ( !at->at_names ) {
1803                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1804                                                 *code = LDAP_SCHERR_BADNAME;
1805                                         *errp = ss;
1806                                         ldap_attributetype_free(at);
1807                                         return NULL;
1808                                 }
1809                         } else if ( !strcmp(sval,"DESC") ) {
1810                                 LDAP_FREE(sval);
1811                                 if ( seen_desc ) {
1812                                         *code = LDAP_SCHERR_DUPOPT;
1813                                         *errp = ss;
1814                                         ldap_attributetype_free(at);
1815                                         return(NULL);
1816                                 }
1817                                 seen_desc = 1;
1818                                 parse_whsp(&ss);
1819                                 kind = get_token(&ss,&sval);
1820                                 if ( kind != TK_QDSTRING ) {
1821                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1822                                         *errp = ss;
1823                                         LDAP_FREE(sval);
1824                                         ldap_attributetype_free(at);
1825                                         return NULL;
1826                                 }
1827                                 at->at_desc = sval;
1828                                 parse_whsp(&ss);
1829                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1830                                 LDAP_FREE(sval);
1831                                 if ( seen_obsolete ) {
1832                                         *code = LDAP_SCHERR_DUPOPT;
1833                                         *errp = ss;
1834                                         ldap_attributetype_free(at);
1835                                         return(NULL);
1836                                 }
1837                                 seen_obsolete = 1;
1838                                 at->at_obsolete = LDAP_SCHEMA_YES;
1839                                 parse_whsp(&ss);
1840                         } else if ( !strcmp(sval,"SUP") ) {
1841                                 LDAP_FREE(sval);
1842                                 if ( seen_sup ) {
1843                                         *code = LDAP_SCHERR_DUPOPT;
1844                                         *errp = ss;
1845                                         ldap_attributetype_free(at);
1846                                         return(NULL);
1847                                 }
1848                                 seen_sup = 1;
1849                                 at->at_sup_oid = parse_woid(&ss,code);
1850                                 if ( !at->at_sup_oid ) {
1851                                         *errp = ss;
1852                                         ldap_attributetype_free(at);
1853                                         return NULL;
1854                                 }
1855                         } else if ( !strcmp(sval,"EQUALITY") ) {
1856                                 LDAP_FREE(sval);
1857                                 if ( seen_equality ) {
1858                                         *code = LDAP_SCHERR_DUPOPT;
1859                                         *errp = ss;
1860                                         ldap_attributetype_free(at);
1861                                         return(NULL);
1862                                 }
1863                                 seen_equality = 1;
1864                                 at->at_equality_oid = parse_woid(&ss,code);
1865                                 if ( !at->at_equality_oid ) {
1866                                         *errp = ss;
1867                                         ldap_attributetype_free(at);
1868                                         return NULL;
1869                                 }
1870                         } else if ( !strcmp(sval,"ORDERING") ) {
1871                                 LDAP_FREE(sval);
1872                                 if ( seen_ordering ) {
1873                                         *code = LDAP_SCHERR_DUPOPT;
1874                                         *errp = ss;
1875                                         ldap_attributetype_free(at);
1876                                         return(NULL);
1877                                 }
1878                                 seen_ordering = 1;
1879                                 at->at_ordering_oid = parse_woid(&ss,code);
1880                                 if ( !at->at_ordering_oid ) {
1881                                         *errp = ss;
1882                                         ldap_attributetype_free(at);
1883                                         return NULL;
1884                                 }
1885                         } else if ( !strcmp(sval,"SUBSTR") ) {
1886                                 LDAP_FREE(sval);
1887                                 if ( seen_substr ) {
1888                                         *code = LDAP_SCHERR_DUPOPT;
1889                                         *errp = ss;
1890                                         ldap_attributetype_free(at);
1891                                         return(NULL);
1892                                 }
1893                                 seen_substr = 1;
1894                                 at->at_substr_oid = parse_woid(&ss,code);
1895                                 if ( !at->at_substr_oid ) {
1896                                         *errp = ss;
1897                                         ldap_attributetype_free(at);
1898                                         return NULL;
1899                                 }
1900                         } else if ( !strcmp(sval,"SYNTAX") ) {
1901                                 LDAP_FREE(sval);
1902                                 if ( seen_syntax ) {
1903                                         *code = LDAP_SCHERR_DUPOPT;
1904                                         *errp = ss;
1905                                         ldap_attributetype_free(at);
1906                                         return(NULL);
1907                                 }
1908                                 seen_syntax = 1;
1909                                 parse_whsp(&ss);
1910                                 savepos = ss;
1911                                 at->at_syntax_oid =
1912                                         parse_noidlen(&ss,
1913                                                       code,
1914                                                       &at->at_syntax_len,
1915                                                       flags);
1916                                 if ( !at->at_syntax_oid ) {
1917                                     if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
1918                                         kind = get_token(&ss,&sval);
1919                                         if (kind == TK_BAREWORD)
1920                                         {
1921                                             char *sp = strchr(sval, '{');
1922                                             at->at_syntax_oid = sval;
1923                                             if (sp)
1924                                             {
1925                                                 *sp++ = 0;
1926                                                 at->at_syntax_len = atoi(sp);
1927                                                 while ( LDAP_DIGIT(*sp) )
1928                                                         sp++;
1929                                                 if ( *sp != '}' ) {
1930                                                     *code = LDAP_SCHERR_UNEXPTOKEN;
1931                                                     *errp = ss;
1932                                                     ldap_attributetype_free(at);
1933                                                     return NULL;
1934                                                 }
1935                                             }
1936                                         }
1937                                     } else {
1938                                         *errp = ss;
1939                                         ldap_attributetype_free(at);
1940                                         return NULL;
1941                                     }
1942                                 }
1943                                 parse_whsp(&ss);
1944                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1945                                 LDAP_FREE(sval);
1946                                 if ( at->at_single_value ) {
1947                                         *code = LDAP_SCHERR_DUPOPT;
1948                                         *errp = ss;
1949                                         ldap_attributetype_free(at);
1950                                         return(NULL);
1951                                 }
1952                                 at->at_single_value = LDAP_SCHEMA_YES;
1953                                 parse_whsp(&ss);
1954                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1955                                 LDAP_FREE(sval);
1956                                 if ( at->at_collective ) {
1957                                         *code = LDAP_SCHERR_DUPOPT;
1958                                         *errp = ss;
1959                                         ldap_attributetype_free(at);
1960                                         return(NULL);
1961                                 }
1962                                 at->at_collective = LDAP_SCHEMA_YES;
1963                                 parse_whsp(&ss);
1964                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1965                                 LDAP_FREE(sval);
1966                                 if ( at->at_no_user_mod ) {
1967                                         *code = LDAP_SCHERR_DUPOPT;
1968                                         *errp = ss;
1969                                         ldap_attributetype_free(at);
1970                                         return(NULL);
1971                                 }
1972                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1973                                 parse_whsp(&ss);
1974                         } else if ( !strcmp(sval,"USAGE") ) {
1975                                 LDAP_FREE(sval);
1976                                 if ( seen_usage ) {
1977                                         *code = LDAP_SCHERR_DUPOPT;
1978                                         *errp = ss;
1979                                         ldap_attributetype_free(at);
1980                                         return(NULL);
1981                                 }
1982                                 seen_usage = 1;
1983                                 parse_whsp(&ss);
1984                                 kind = get_token(&ss,&sval);
1985                                 if ( kind != TK_BAREWORD ) {
1986                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1987                                         *errp = ss;
1988                                         LDAP_FREE(sval);
1989                                         ldap_attributetype_free(at);
1990                                         return NULL;
1991                                 }
1992                                 if ( !strcasecmp(sval,"userApplications") )
1993                                         at->at_usage =
1994                                             LDAP_SCHEMA_USER_APPLICATIONS;
1995                                 else if ( !strcasecmp(sval,"directoryOperation") )
1996                                         at->at_usage =
1997                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1998                                 else if ( !strcasecmp(sval,"distributedOperation") )
1999                                         at->at_usage =
2000                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
2001                                 else if ( !strcasecmp(sval,"dSAOperation") )
2002                                         at->at_usage =
2003                                             LDAP_SCHEMA_DSA_OPERATION;
2004                                 else {
2005                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2006                                         *errp = ss;
2007                                         LDAP_FREE(sval);
2008                                         ldap_attributetype_free(at);
2009                                         return NULL;
2010                                 }
2011                                 LDAP_FREE(sval);
2012                                 parse_whsp(&ss);
2013                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2014                                 /* Should be parse_qdstrings */
2015                                 ext_vals = parse_qdescrs(&ss, code);
2016                                 if ( !ext_vals ) {
2017                                         *errp = ss;
2018                                         ldap_attributetype_free(at);
2019                                         return NULL;
2020                                 }
2021                                 if ( add_extension(&at->at_extensions,
2022                                                     sval, ext_vals) ) {
2023                                         *code = LDAP_SCHERR_OUTOFMEM;
2024                                         *errp = ss;
2025                                         LDAP_FREE(sval);
2026                                         ldap_attributetype_free(at);
2027                                         return NULL;
2028                                 }
2029                         } else {
2030                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2031                                 *errp = ss;
2032                                 LDAP_FREE(sval);
2033                                 ldap_attributetype_free(at);
2034                                 return NULL;
2035                         }
2036                         break;
2037                 default:
2038                         *code = LDAP_SCHERR_UNEXPTOKEN;
2039                         *errp = ss;
2040                         LDAP_FREE(sval);
2041                         ldap_attributetype_free(at);
2042                         return NULL;
2043                 }
2044         }
2045 }
2046
2047 void
2048 ldap_objectclass_free(LDAPObjectClass * oc)
2049 {
2050         LDAP_FREE(oc->oc_oid);
2051         LDAP_VFREE(oc->oc_names);
2052         LDAP_FREE(oc->oc_desc);
2053         LDAP_VFREE(oc->oc_sup_oids);
2054         LDAP_VFREE(oc->oc_at_oids_must);
2055         LDAP_VFREE(oc->oc_at_oids_may);
2056         free_extensions(oc->oc_extensions);
2057         LDAP_FREE(oc);
2058 }
2059
2060 LDAPObjectClass *
2061 ldap_str2objectclass( LDAP_CONST char * s,
2062         int * code,
2063         LDAP_CONST char ** errp,
2064         LDAP_CONST int flags )
2065 {
2066         int kind;
2067         const char * ss = s;
2068         char * sval;
2069         int seen_name = 0;
2070         int seen_desc = 0;
2071         int seen_obsolete = 0;
2072         int seen_sup = 0;
2073         int seen_kind = 0;
2074         int seen_must = 0;
2075         int seen_may = 0;
2076         LDAPObjectClass * oc;
2077         char ** ext_vals;
2078         const char * savepos;
2079
2080         if ( !s ) {
2081                 *code = LDAP_SCHERR_EMPTY;
2082                 *errp = "";
2083                 return NULL;
2084         }
2085
2086         *errp = s;
2087         oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
2088
2089         if ( !oc ) {
2090                 *code = LDAP_SCHERR_OUTOFMEM;
2091                 return NULL;
2092         }
2093         oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2094
2095         kind = get_token(&ss,&sval);
2096         if ( kind != TK_LEFTPAREN ) {
2097                 *code = LDAP_SCHERR_NOLEFTPAREN;
2098                 LDAP_FREE(sval);
2099                 ldap_objectclass_free(oc);
2100                 return NULL;
2101         }
2102
2103         /*
2104          * Definitions MUST begin with an OID in the numericoid format.
2105          * However, this routine is used by clients to parse the response
2106          * from servers and very well known servers will provide an OID
2107          * in the wrong format or even no OID at all.  We do our best to
2108          * extract info from those servers.
2109          */
2110         parse_whsp(&ss);
2111         savepos = ss;
2112         oc->oc_oid = parse_numericoid(&ss,code,0);
2113         if ( !oc->oc_oid ) {
2114                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2115                         /* Backtracking */
2116                         ss = savepos;
2117                         kind = get_token(&ss,&sval);
2118                         if ( kind == TK_BAREWORD ) {
2119                                 if ( !strcmp(sval, "NAME") ||
2120                                      !strcmp(sval, "DESC") ||
2121                                      !strcmp(sval, "OBSOLETE") ||
2122                                      !strcmp(sval, "SUP") ||
2123                                      !strcmp(sval, "ABSTRACT") ||
2124                                      !strcmp(sval, "STRUCTURAL") ||
2125                                      !strcmp(sval, "AUXILIARY") ||
2126                                      !strcmp(sval, "MUST") ||
2127                                      !strncmp(sval, "X-", 2) ) {
2128                                         /* Missing OID, backtrack */
2129                                         ss = savepos;
2130                                 } else if ( flags &
2131                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2132                                         /* Non-numerical OID, ignore */
2133                                         int len = ss-savepos;
2134                                         oc->oc_oid = LDAP_MALLOC(len+1);
2135                                         strncpy(oc->oc_oid, savepos, len);
2136                                         oc->oc_oid[len] = 0;
2137                                 }
2138                         }
2139                         LDAP_FREE(sval);
2140                 } else {
2141                         *errp = ss;
2142                         ldap_objectclass_free(oc);
2143                         return NULL;
2144                 }
2145         }
2146         parse_whsp(&ss);
2147
2148         /*
2149          * Beyond this point we will be liberal an accept the items
2150          * in any order.
2151          */
2152         while (1) {
2153                 kind = get_token(&ss,&sval);
2154                 switch (kind) {
2155                 case TK_EOS:
2156                         *code = LDAP_SCHERR_NORIGHTPAREN;
2157                         *errp = ss;
2158                         ldap_objectclass_free(oc);
2159                         return NULL;
2160                 case TK_RIGHTPAREN:
2161                         return oc;
2162                 case TK_BAREWORD:
2163                         if ( !strcmp(sval,"NAME") ) {
2164                                 LDAP_FREE(sval);
2165                                 if ( seen_name ) {
2166                                         *code = LDAP_SCHERR_DUPOPT;
2167                                         *errp = ss;
2168                                         ldap_objectclass_free(oc);
2169                                         return(NULL);
2170                                 }
2171                                 seen_name = 1;
2172                                 oc->oc_names = parse_qdescrs(&ss,code);
2173                                 if ( !oc->oc_names ) {
2174                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2175                                                 *code = LDAP_SCHERR_BADNAME;
2176                                         *errp = ss;
2177                                         ldap_objectclass_free(oc);
2178                                         return NULL;
2179                                 }
2180                         } else if ( !strcmp(sval,"DESC") ) {
2181                                 LDAP_FREE(sval);
2182                                 if ( seen_desc ) {
2183                                         *code = LDAP_SCHERR_DUPOPT;
2184                                         *errp = ss;
2185                                         ldap_objectclass_free(oc);
2186                                         return(NULL);
2187                                 }
2188                                 seen_desc = 1;
2189                                 parse_whsp(&ss);
2190                                 kind = get_token(&ss,&sval);
2191                                 if ( kind != TK_QDSTRING ) {
2192                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2193                                         *errp = ss;
2194                                         LDAP_FREE(sval);
2195                                         ldap_objectclass_free(oc);
2196                                         return NULL;
2197                                 }
2198                                 oc->oc_desc = sval;
2199                                 parse_whsp(&ss);
2200                         } else if ( !strcmp(sval,"OBSOLETE") ) {
2201                                 LDAP_FREE(sval);
2202                                 if ( seen_obsolete ) {
2203                                         *code = LDAP_SCHERR_DUPOPT;
2204                                         *errp = ss;
2205                                         ldap_objectclass_free(oc);
2206                                         return(NULL);
2207                                 }
2208                                 seen_obsolete = 1;
2209                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
2210                                 parse_whsp(&ss);
2211                         } else if ( !strcmp(sval,"SUP") ) {
2212                                 LDAP_FREE(sval);
2213                                 if ( seen_sup ) {
2214                                         *code = LDAP_SCHERR_DUPOPT;
2215                                         *errp = ss;
2216                                         ldap_objectclass_free(oc);
2217                                         return(NULL);
2218                                 }
2219                                 seen_sup = 1;
2220                                 oc->oc_sup_oids = parse_oids(&ss,
2221                                                              code,
2222                                                              flags);
2223                                 if ( !oc->oc_sup_oids ) {
2224                                         *errp = ss;
2225                                         ldap_objectclass_free(oc);
2226                                         return NULL;
2227                                 }
2228                         } else if ( !strcmp(sval,"ABSTRACT") ) {
2229                                 LDAP_FREE(sval);
2230                                 if ( seen_kind ) {
2231                                         *code = LDAP_SCHERR_DUPOPT;
2232                                         *errp = ss;
2233                                         ldap_objectclass_free(oc);
2234                                         return(NULL);
2235                                 }
2236                                 seen_kind = 1;
2237                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
2238                                 parse_whsp(&ss);
2239                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
2240                                 LDAP_FREE(sval);
2241                                 if ( seen_kind ) {
2242                                         *code = LDAP_SCHERR_DUPOPT;
2243                                         *errp = ss;
2244                                         ldap_objectclass_free(oc);
2245                                         return(NULL);
2246                                 }
2247                                 seen_kind = 1;
2248                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2249                                 parse_whsp(&ss);
2250                         } else if ( !strcmp(sval,"AUXILIARY") ) {
2251                                 LDAP_FREE(sval);
2252                                 if ( seen_kind ) {
2253                                         *code = LDAP_SCHERR_DUPOPT;
2254                                         *errp = ss;
2255                                         ldap_objectclass_free(oc);
2256                                         return(NULL);
2257                                 }
2258                                 seen_kind = 1;
2259                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
2260                                 parse_whsp(&ss);
2261                         } else if ( !strcmp(sval,"MUST") ) {
2262                                 LDAP_FREE(sval);
2263                                 if ( seen_must ) {
2264                                         *code = LDAP_SCHERR_DUPOPT;
2265                                         *errp = ss;
2266                                         ldap_objectclass_free(oc);
2267                                         return(NULL);
2268                                 }
2269                                 seen_must = 1;
2270                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
2271                                 if ( !oc->oc_at_oids_must ) {
2272                                         *errp = ss;
2273                                         ldap_objectclass_free(oc);
2274                                         return NULL;
2275                                 }
2276                                 parse_whsp(&ss);
2277                         } else if ( !strcmp(sval,"MAY") ) {
2278                                 LDAP_FREE(sval);
2279                                 if ( seen_may ) {
2280                                         *code = LDAP_SCHERR_DUPOPT;
2281                                         *errp = ss;
2282                                         ldap_objectclass_free(oc);
2283                                         return(NULL);
2284                                 }
2285                                 seen_may = 1;
2286                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
2287                                 if ( !oc->oc_at_oids_may ) {
2288                                         *errp = ss;
2289                                         ldap_objectclass_free(oc);
2290                                         return NULL;
2291                                 }
2292                                 parse_whsp(&ss);
2293                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2294                                 /* Should be parse_qdstrings */
2295                                 ext_vals = parse_qdescrs(&ss, code);
2296                                 if ( !ext_vals ) {
2297                                         *errp = ss;
2298                                         ldap_objectclass_free(oc);
2299                                         return NULL;
2300                                 }
2301                                 if ( add_extension(&oc->oc_extensions,
2302                                                     sval, ext_vals) ) {
2303                                         *code = LDAP_SCHERR_OUTOFMEM;
2304                                         *errp = ss;
2305                                         LDAP_FREE(sval);
2306                                         ldap_objectclass_free(oc);
2307                                         return NULL;
2308                                 }
2309                         } else {
2310                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2311                                 *errp = ss;
2312                                 LDAP_FREE(sval);
2313                                 ldap_objectclass_free(oc);
2314                                 return NULL;
2315                         }
2316                         break;
2317                 default:
2318                         *code = LDAP_SCHERR_UNEXPTOKEN;
2319                         *errp = ss;
2320                         LDAP_FREE(sval);
2321                         ldap_objectclass_free(oc);
2322                         return NULL;
2323                 }
2324         }
2325 }
2326
2327 static char *const err2text[] = {
2328         "Success",
2329         "Out of memory",
2330         "Unexpected token",
2331         "Missing opening parenthesis",
2332         "Missing closing parenthesis",
2333         "Expecting digit",
2334         "Expecting a name",
2335         "Bad description",
2336         "Bad superiors",
2337         "Duplicate option",
2338         "Unexpected end of data"
2339 };
2340
2341 char *
2342 ldap_scherr2str(int code)
2343 {
2344         if ( code < 0 || code >= (sizeof(err2text)/sizeof(char *)) ) {
2345                 return "Unknown error";
2346         } else {
2347                 return err2text[code];
2348         }
2349 }