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