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