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