]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Use e_nname.bv_len
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1999-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * schema.c:  parsing routines used by servers and clients to process
8  *      schema definitions
9  */
10
11 #include "portable.h"
12
13 #include <stdio.h>
14 #include <ac/stdlib.h>
15
16 #include <ac/string.h>
17 #include <ac/time.h>
18
19 #include "ldap-int.h"
20
21 #include <ldap_schema.h>
22
23 static const char *
24 choose_name( char *names[], const char *fallback )
25 {
26         return( (names != NULL && names[0] != NULL) ? names[0] : fallback );
27 }
28
29 LDAP_CONST char *
30 ldap_syntax2name( LDAPSyntax * syn )
31 {
32         return( syn->syn_oid );
33 }
34
35 LDAP_CONST char *
36 ldap_matchingrule2name( LDAPMatchingRule * mr )
37 {
38         return( choose_name( mr->mr_names, mr->mr_oid ) );
39 }
40
41 LDAP_CONST char *
42 ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
43 {
44         return( choose_name( mru->mru_names, mru->mru_oid ) );
45 }
46
47 LDAP_CONST char *
48 ldap_attributetype2name( LDAPAttributeType * at )
49 {
50         return( choose_name( at->at_names, at->at_oid ) );
51 }
52
53 LDAP_CONST char *
54 ldap_objectclass2name( LDAPObjectClass * oc )
55 {
56         return( choose_name( oc->oc_names, oc->oc_oid ) );
57 }
58
59
60 /*
61  * When pretty printing the entities we will be appending to a buffer.
62  * Since checking for overflow, realloc'ing and checking if no error
63  * is extremely boring, we will use a protection layer that will let
64  * us blissfully ignore the error until the end.  This layer is
65  * implemented with the help of the next type.
66  */
67
68 typedef struct safe_string {
69         char * val;
70         ber_len_t size;
71         ber_len_t pos;
72         int at_whsp;
73 } safe_string;
74
75 static safe_string *
76 new_safe_string(int size)
77 {
78         safe_string * ss;
79         
80         ss = LDAP_MALLOC(sizeof(safe_string));
81         if ( !ss )
82                 return(NULL);
83
84         ss->val = LDAP_MALLOC(size);
85         if ( !ss->val ) {
86                 LDAP_FREE(ss);
87                 return(NULL);
88         }
89
90         ss->size = size;
91         ss->pos = 0;
92         ss->at_whsp = 0;
93
94         return ss;
95 }
96
97 static void
98 safe_string_free(safe_string * ss)
99 {
100         if ( !ss )
101                 return;
102         LDAP_FREE(ss->val);
103         LDAP_FREE(ss);
104 }
105
106 static char *
107 safe_string_val(safe_string * ss)
108 {
109         ss->val[ss->pos] = '\0';
110         return(ss->val);
111 }
112
113 static 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( 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( 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( 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( 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(  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 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( LDAP_CONST char * s,
1087         int * code,
1088         LDAP_CONST char ** errp,
1089         LDAP_CONST int flags )
1090 {
1091         int kind;
1092         const char * ss = s;
1093         char * sval;
1094         int seen_name = 0;
1095         int seen_desc = 0;
1096         LDAPSyntax * syn;
1097         char ** ext_vals;
1098
1099         if ( !s ) {
1100                 *code = LDAP_SCHERR_EMPTY;
1101                 *errp = "";
1102                 return NULL;
1103         }
1104
1105         *errp = s;
1106         syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
1107
1108         if ( !syn ) {
1109                 *code = LDAP_SCHERR_OUTOFMEM;
1110                 return NULL;
1111         }
1112
1113         kind = get_token(&ss,&sval);
1114         if ( kind != TK_LEFTPAREN ) {
1115                 LDAP_FREE(sval);
1116                 *code = LDAP_SCHERR_NOLEFTPAREN;
1117                 ldap_syntax_free(syn);
1118                 return NULL;
1119         }
1120
1121         parse_whsp(&ss);
1122         syn->syn_oid = parse_numericoid(&ss,code,0);
1123         if ( !syn->syn_oid ) {
1124                 *errp = ss;
1125                 ldap_syntax_free(syn);
1126                 return NULL;
1127         }
1128         parse_whsp(&ss);
1129
1130         /*
1131          * Beyond this point we will be liberal and accept the items
1132          * in any order.
1133          */
1134         while (1) {
1135                 kind = get_token(&ss,&sval);
1136                 switch (kind) {
1137                 case TK_EOS:
1138                         *code = LDAP_SCHERR_NORIGHTPAREN;
1139                         *errp = ss;
1140                         ldap_syntax_free(syn);
1141                         return NULL;
1142                 case TK_RIGHTPAREN:
1143                         return syn;
1144                 case TK_BAREWORD:
1145                         if ( !strcmp(sval,"NAME") ) {
1146                                 LDAP_FREE(sval);
1147                                 if ( seen_name ) {
1148                                         *code = LDAP_SCHERR_DUPOPT;
1149                                         *errp = ss;
1150                                         ldap_syntax_free(syn);
1151                                         return(NULL);
1152                                 }
1153                                 seen_name = 1;
1154                                 syn->syn_names = parse_qdescrs(&ss,code);
1155                                 if ( !syn->syn_names ) {
1156                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1157                                                 *code = LDAP_SCHERR_BADNAME;
1158                                         *errp = ss;
1159                                         ldap_syntax_free(syn);
1160                                         return NULL;
1161                                 }
1162                         } else if ( !strcmp(sval,"DESC") ) {
1163                                 LDAP_FREE(sval);
1164                                 if ( seen_desc ) {
1165                                         *code = LDAP_SCHERR_DUPOPT;
1166                                         *errp = ss;
1167                                         ldap_syntax_free(syn);
1168                                         return(NULL);
1169                                 }
1170                                 seen_desc = 1;
1171                                 parse_whsp(&ss);
1172                                 kind = get_token(&ss,&sval);
1173                                 if ( kind != TK_QDSTRING ) {
1174                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1175                                         *errp = ss;
1176                                         LDAP_FREE(sval);
1177                                         ldap_syntax_free(syn);
1178                                         return NULL;
1179                                 }
1180                                 syn->syn_desc = sval;
1181                                 parse_whsp(&ss);
1182                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1183                                 /* Should be parse_qdstrings */
1184                                 ext_vals = parse_qdescrs(&ss, code);
1185                                 if ( !ext_vals ) {
1186                                         *errp = ss;
1187                                         ldap_syntax_free(syn);
1188                                         return NULL;
1189                                 }
1190                                 if ( add_extension(&syn->syn_extensions,
1191                                                     sval, ext_vals) ) {
1192                                         *code = LDAP_SCHERR_OUTOFMEM;
1193                                         *errp = ss;
1194                                         LDAP_FREE(sval);
1195                                         ldap_syntax_free(syn);
1196                                         return NULL;
1197                                 }
1198                         } else {
1199                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1200                                 *errp = ss;
1201                                 LDAP_FREE(sval);
1202                                 ldap_syntax_free(syn);
1203                                 return NULL;
1204                         }
1205                         break;
1206                 default:
1207                         *code = LDAP_SCHERR_UNEXPTOKEN;
1208                         *errp = ss;
1209                         LDAP_FREE(sval);
1210                         ldap_syntax_free(syn);
1211                         return NULL;
1212                 }
1213         }
1214 }
1215
1216 void
1217 ldap_matchingrule_free( LDAPMatchingRule * mr )
1218 {
1219         LDAP_FREE(mr->mr_oid);
1220         LDAP_VFREE(mr->mr_names);
1221         LDAP_FREE(mr->mr_desc);
1222         LDAP_FREE(mr->mr_syntax_oid);
1223         free_extensions(mr->mr_extensions);
1224         LDAP_FREE(mr);
1225 }
1226
1227 LDAPMatchingRule *
1228 ldap_str2matchingrule( LDAP_CONST char * s,
1229         int * code,
1230         LDAP_CONST char ** errp,
1231         LDAP_CONST int flags )
1232 {
1233         int kind;
1234         const char * ss = s;
1235         char * sval;
1236         int seen_name = 0;
1237         int seen_desc = 0;
1238         int seen_obsolete = 0;
1239         int seen_syntax = 0;
1240         LDAPMatchingRule * mr;
1241         char ** ext_vals;
1242         const char * savepos;
1243
1244         if ( !s ) {
1245                 *code = LDAP_SCHERR_EMPTY;
1246                 *errp = "";
1247                 return NULL;
1248         }
1249
1250         *errp = s;
1251         mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1252
1253         if ( !mr ) {
1254                 *code = LDAP_SCHERR_OUTOFMEM;
1255                 return NULL;
1256         }
1257
1258         kind = get_token(&ss,&sval);
1259         if ( kind != TK_LEFTPAREN ) {
1260                 *code = LDAP_SCHERR_NOLEFTPAREN;
1261                 LDAP_FREE(sval);
1262                 ldap_matchingrule_free(mr);
1263                 return NULL;
1264         }
1265
1266         parse_whsp(&ss);
1267         savepos = ss;
1268         mr->mr_oid = parse_numericoid(&ss,code,flags);
1269         if ( !mr->mr_oid ) {
1270                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1271                         /* Backtracking */
1272                         ss = savepos;
1273                         kind = get_token(&ss,&sval);
1274                         if ( kind == TK_BAREWORD ) {
1275                                 if ( !strcmp(sval, "NAME") ||
1276                                      !strcmp(sval, "DESC") ||
1277                                      !strcmp(sval, "OBSOLETE") ||
1278                                      !strcmp(sval, "SYNTAX") ||
1279                                      !strncmp(sval, "X-", 2) ) {
1280                                         /* Missing OID, backtrack */
1281                                         ss = savepos;
1282                                 } else {
1283                                         /* Non-numerical OID, ignore */
1284                                 }
1285                         }
1286                         LDAP_FREE(sval);
1287                 } else {
1288                         *errp = ss;
1289                         ldap_matchingrule_free(mr);
1290                         return NULL;
1291                 }
1292         }
1293         parse_whsp(&ss);
1294
1295         /*
1296          * Beyond this point we will be liberal and accept the items
1297          * in any order.
1298          */
1299         while (1) {
1300                 kind = get_token(&ss,&sval);
1301                 switch (kind) {
1302                 case TK_EOS:
1303                         *code = LDAP_SCHERR_NORIGHTPAREN;
1304                         *errp = ss;
1305                         ldap_matchingrule_free(mr);
1306                         return NULL;
1307                 case TK_RIGHTPAREN:
1308                         return mr;
1309                 case TK_BAREWORD:
1310                         if ( !strcmp(sval,"NAME") ) {
1311                                 LDAP_FREE(sval);
1312                                 if ( seen_name ) {
1313                                         *code = LDAP_SCHERR_DUPOPT;
1314                                         *errp = ss;
1315                                         ldap_matchingrule_free(mr);
1316                                         return(NULL);
1317                                 }
1318                                 seen_name = 1;
1319                                 mr->mr_names = parse_qdescrs(&ss,code);
1320                                 if ( !mr->mr_names ) {
1321                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1322                                                 *code = LDAP_SCHERR_BADNAME;
1323                                         *errp = ss;
1324                                         ldap_matchingrule_free(mr);
1325                                         return NULL;
1326                                 }
1327                         } else if ( !strcmp(sval,"DESC") ) {
1328                                 LDAP_FREE(sval);
1329                                 if ( seen_desc ) {
1330                                         *code = LDAP_SCHERR_DUPOPT;
1331                                         *errp = ss;
1332                                         ldap_matchingrule_free(mr);
1333                                         return(NULL);
1334                                 }
1335                                 seen_desc = 1;
1336                                 parse_whsp(&ss);
1337                                 kind = get_token(&ss,&sval);
1338                                 if ( kind != TK_QDSTRING ) {
1339                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1340                                         *errp = ss;
1341                                         LDAP_FREE(sval);
1342                                         ldap_matchingrule_free(mr);
1343                                         return NULL;
1344                                 }
1345                                 mr->mr_desc = sval;
1346                                 parse_whsp(&ss);
1347                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1348                                 LDAP_FREE(sval);
1349                                 if ( seen_obsolete ) {
1350                                         *code = LDAP_SCHERR_DUPOPT;
1351                                         *errp = ss;
1352                                         ldap_matchingrule_free(mr);
1353                                         return(NULL);
1354                                 }
1355                                 seen_obsolete = 1;
1356                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1357                                 parse_whsp(&ss);
1358                         } else if ( !strcmp(sval,"SYNTAX") ) {
1359                                 LDAP_FREE(sval);
1360                                 if ( seen_syntax ) {
1361                                         *code = LDAP_SCHERR_DUPOPT;
1362                                         *errp = ss;
1363                                         ldap_matchingrule_free(mr);
1364                                         return(NULL);
1365                                 }
1366                                 seen_syntax = 1;
1367                                 parse_whsp(&ss);
1368                                 mr->mr_syntax_oid =
1369                                         parse_numericoid(&ss,code,flags);
1370                                 if ( !mr->mr_syntax_oid ) {
1371                                         *errp = ss;
1372                                         ldap_matchingrule_free(mr);
1373                                         return NULL;
1374                                 }
1375                                 parse_whsp(&ss);
1376                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1377                                 /* Should be parse_qdstrings */
1378                                 ext_vals = parse_qdescrs(&ss, code);
1379                                 if ( !ext_vals ) {
1380                                         *errp = ss;
1381                                         ldap_matchingrule_free(mr);
1382                                         return NULL;
1383                                 }
1384                                 if ( add_extension(&mr->mr_extensions,
1385                                                     sval, ext_vals) ) {
1386                                         *code = LDAP_SCHERR_OUTOFMEM;
1387                                         *errp = ss;
1388                                         LDAP_FREE(sval);
1389                                         ldap_matchingrule_free(mr);
1390                                         return NULL;
1391                                 }
1392                         } else {
1393                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1394                                 *errp = ss;
1395                                 LDAP_FREE(sval);
1396                                 ldap_matchingrule_free(mr);
1397                                 return NULL;
1398                         }
1399                         break;
1400                 default:
1401                         *code = LDAP_SCHERR_UNEXPTOKEN;
1402                         *errp = ss;
1403                         LDAP_FREE(sval);
1404                         ldap_matchingrule_free(mr);
1405                         return NULL;
1406                 }
1407         }
1408 }
1409
1410 void
1411 ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
1412 {
1413         LDAP_FREE(mru->mru_oid);
1414         LDAP_VFREE(mru->mru_names);
1415         LDAP_FREE(mru->mru_desc);
1416         LDAP_VFREE(mru->mru_applies_oids);
1417         free_extensions(mru->mru_extensions);
1418         LDAP_FREE(mru);
1419 }
1420
1421 LDAPMatchingRuleUse *
1422 ldap_str2matchingruleuse( LDAP_CONST char * s,
1423         int * code,
1424         LDAP_CONST char ** errp,
1425         LDAP_CONST int flags )
1426 {
1427         int kind;
1428         const char * ss = s;
1429         char * sval;
1430         int seen_name = 0;
1431         int seen_desc = 0;
1432         int seen_obsolete = 0;
1433         int seen_applies = 0;
1434         LDAPMatchingRuleUse * mru;
1435         char ** ext_vals;
1436         const char * savepos;
1437
1438         if ( !s ) {
1439                 *code = LDAP_SCHERR_EMPTY;
1440                 *errp = "";
1441                 return NULL;
1442         }
1443
1444         *errp = s;
1445         mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
1446
1447         if ( !mru ) {
1448                 *code = LDAP_SCHERR_OUTOFMEM;
1449                 return NULL;
1450         }
1451
1452         kind = get_token(&ss,&sval);
1453         if ( kind != TK_LEFTPAREN ) {
1454                 *code = LDAP_SCHERR_NOLEFTPAREN;
1455                 LDAP_FREE(sval);
1456                 ldap_matchingruleuse_free(mru);
1457                 return NULL;
1458         }
1459
1460         parse_whsp(&ss);
1461         savepos = ss;
1462         mru->mru_oid = parse_numericoid(&ss,code,flags);
1463         if ( !mru->mru_oid ) {
1464                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1465                         /* Backtracking */
1466                         ss = savepos;
1467                         kind = get_token(&ss,&sval);
1468                         if ( kind == TK_BAREWORD ) {
1469                                 if ( !strcmp(sval, "NAME") ||
1470                                      !strcmp(sval, "DESC") ||
1471                                      !strcmp(sval, "OBSOLETE") ||
1472                                      !strcmp(sval, "APPLIES") ||
1473                                      !strncmp(sval, "X-", 2) ) {
1474                                         /* Missing OID, backtrack */
1475                                         ss = savepos;
1476                                 } else {
1477                                         /* Non-numerical OID, ignore */
1478                                 }
1479                         }
1480                         LDAP_FREE(sval);
1481                 } else {
1482                         *errp = ss;
1483                         ldap_matchingruleuse_free(mru);
1484                         return NULL;
1485                 }
1486         }
1487         parse_whsp(&ss);
1488
1489         /*
1490          * Beyond this point we will be liberal and accept the items
1491          * in any order.
1492          */
1493         while (1) {
1494                 kind = get_token(&ss,&sval);
1495                 switch (kind) {
1496                 case TK_EOS:
1497                         *code = LDAP_SCHERR_NORIGHTPAREN;
1498                         *errp = ss;
1499                         ldap_matchingruleuse_free(mru);
1500                         return NULL;
1501                 case TK_RIGHTPAREN:
1502                         return mru;
1503                 case TK_BAREWORD:
1504                         if ( !strcmp(sval,"NAME") ) {
1505                                 LDAP_FREE(sval);
1506                                 if ( seen_name ) {
1507                                         *code = LDAP_SCHERR_DUPOPT;
1508                                         *errp = ss;
1509                                         ldap_matchingruleuse_free(mru);
1510                                         return(NULL);
1511                                 }
1512                                 seen_name = 1;
1513                                 mru->mru_names = parse_qdescrs(&ss,code);
1514                                 if ( !mru->mru_names ) {
1515                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1516                                                 *code = LDAP_SCHERR_BADNAME;
1517                                         *errp = ss;
1518                                         ldap_matchingruleuse_free(mru);
1519                                         return NULL;
1520                                 }
1521                         } else if ( !strcmp(sval,"DESC") ) {
1522                                 LDAP_FREE(sval);
1523                                 if ( seen_desc ) {
1524                                         *code = LDAP_SCHERR_DUPOPT;
1525                                         *errp = ss;
1526                                         ldap_matchingruleuse_free(mru);
1527                                         return(NULL);
1528                                 }
1529                                 seen_desc = 1;
1530                                 parse_whsp(&ss);
1531                                 kind = get_token(&ss,&sval);
1532                                 if ( kind != TK_QDSTRING ) {
1533                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1534                                         *errp = ss;
1535                                         LDAP_FREE(sval);
1536                                         ldap_matchingruleuse_free(mru);
1537                                         return NULL;
1538                                 }
1539                                 mru->mru_desc = sval;
1540                                 parse_whsp(&ss);
1541                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1542                                 LDAP_FREE(sval);
1543                                 if ( seen_obsolete ) {
1544                                         *code = LDAP_SCHERR_DUPOPT;
1545                                         *errp = ss;
1546                                         ldap_matchingruleuse_free(mru);
1547                                         return(NULL);
1548                                 }
1549                                 seen_obsolete = 1;
1550                                 mru->mru_obsolete = LDAP_SCHEMA_YES;
1551                                 parse_whsp(&ss);
1552                         } else if ( !strcmp(sval,"APPLIES") ) {
1553                                 LDAP_FREE(sval);
1554                                 if ( seen_applies ) {
1555                                         *code = LDAP_SCHERR_DUPOPT;
1556                                         *errp = ss;
1557                                         ldap_matchingruleuse_free(mru);
1558                                         return(NULL);
1559                                 }
1560                                 seen_applies = 1;
1561                                 mru->mru_applies_oids = parse_oids(&ss,
1562                                                              code,
1563                                                              flags);
1564                                 if ( !mru->mru_applies_oids ) {
1565                                         *errp = ss;
1566                                         ldap_matchingruleuse_free(mru);
1567                                         return NULL;
1568                                 }
1569                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1570                                 /* Should be parse_qdstrings */
1571                                 ext_vals = parse_qdescrs(&ss, code);
1572                                 if ( !ext_vals ) {
1573                                         *errp = ss;
1574                                         ldap_matchingruleuse_free(mru);
1575                                         return NULL;
1576                                 }
1577                                 if ( add_extension(&mru->mru_extensions,
1578                                                     sval, ext_vals) ) {
1579                                         *code = LDAP_SCHERR_OUTOFMEM;
1580                                         *errp = ss;
1581                                         LDAP_FREE(sval);
1582                                         ldap_matchingruleuse_free(mru);
1583                                         return NULL;
1584                                 }
1585                         } else {
1586                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1587                                 *errp = ss;
1588                                 LDAP_FREE(sval);
1589                                 ldap_matchingruleuse_free(mru);
1590                                 return NULL;
1591                         }
1592                         break;
1593                 default:
1594                         *code = LDAP_SCHERR_UNEXPTOKEN;
1595                         *errp = ss;
1596                         LDAP_FREE(sval);
1597                         ldap_matchingruleuse_free(mru);
1598                         return NULL;
1599                 }
1600         }
1601 }
1602
1603 void
1604 ldap_attributetype_free(LDAPAttributeType * at)
1605 {
1606         LDAP_FREE(at->at_oid);
1607         LDAP_VFREE(at->at_names);
1608         LDAP_FREE(at->at_desc);
1609         LDAP_FREE(at->at_sup_oid);
1610         LDAP_FREE(at->at_equality_oid);
1611         LDAP_FREE(at->at_ordering_oid);
1612         LDAP_FREE(at->at_substr_oid);
1613         LDAP_FREE(at->at_syntax_oid);
1614         free_extensions(at->at_extensions);
1615         LDAP_FREE(at);
1616 }
1617
1618 LDAPAttributeType *
1619 ldap_str2attributetype( LDAP_CONST char * s,
1620         int * code,
1621         LDAP_CONST char ** errp,
1622         LDAP_CONST int flags )
1623 {
1624         int kind;
1625         const char * ss = s;
1626         char * sval;
1627         int seen_name = 0;
1628         int seen_desc = 0;
1629         int seen_obsolete = 0;
1630         int seen_sup = 0;
1631         int seen_equality = 0;
1632         int seen_ordering = 0;
1633         int seen_substr = 0;
1634         int seen_syntax = 0;
1635         int seen_usage = 0;
1636         LDAPAttributeType * at;
1637         char ** ext_vals;
1638         const char * savepos;
1639
1640         if ( !s ) {
1641                 *code = LDAP_SCHERR_EMPTY;
1642                 *errp = "";
1643                 return NULL;
1644         }
1645
1646         *errp = s;
1647         at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
1648
1649         if ( !at ) {
1650                 *code = LDAP_SCHERR_OUTOFMEM;
1651                 return NULL;
1652         }
1653
1654         kind = get_token(&ss,&sval);
1655         if ( kind != TK_LEFTPAREN ) {
1656                 *code = LDAP_SCHERR_NOLEFTPAREN;
1657                 LDAP_FREE(sval);
1658                 ldap_attributetype_free(at);
1659                 return NULL;
1660         }
1661
1662         /*
1663          * Definitions MUST begin with an OID in the numericoid format.
1664          * However, this routine is used by clients to parse the response
1665          * from servers and very well known servers will provide an OID
1666          * in the wrong format or even no OID at all.  We do our best to
1667          * extract info from those servers.
1668          */
1669         parse_whsp(&ss);
1670         savepos = ss;
1671         at->at_oid = parse_numericoid(&ss,code,0);
1672         if ( !at->at_oid ) {
1673                 if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
1674                                 | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
1675                             && (ss == savepos) ) {
1676                         /* Backtracking */
1677                         ss = savepos;
1678                         kind = get_token(&ss,&sval);
1679                         if ( kind == TK_BAREWORD ) {
1680                                 if ( !strcmp(sval, "NAME") ||
1681                                      !strcmp(sval, "DESC") ||
1682                                      !strcmp(sval, "OBSOLETE") ||
1683                                      !strcmp(sval, "SUP") ||
1684                                      !strcmp(sval, "EQUALITY") ||
1685                                      !strcmp(sval, "ORDERING") ||
1686                                      !strcmp(sval, "SUBSTR") ||
1687                                      !strcmp(sval, "SYNTAX") ||
1688                                      !strcmp(sval, "SINGLE-VALUE") ||
1689                                      !strcmp(sval, "COLLECTIVE") ||
1690                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1691                                      !strcmp(sval, "USAGE") ||
1692                                      !strncmp(sval, "X-", 2) ) {
1693                                         /* Missing OID, backtrack */
1694                                         ss = savepos;
1695                                 } else if ( flags
1696                                         & LDAP_SCHEMA_ALLOW_OID_MACRO) {
1697                                         /* Non-numerical OID ... */
1698                                         int len = ss-savepos;
1699                                         at->at_oid = LDAP_MALLOC(len+1);
1700                                         strncpy(at->at_oid, savepos, len);
1701                                         at->at_oid[len] = 0;
1702                                 }
1703                         }
1704                         LDAP_FREE(sval);
1705                 } else {
1706                         *errp = ss;
1707                         ldap_attributetype_free(at);
1708                         return NULL;
1709                 }
1710         }
1711         parse_whsp(&ss);
1712
1713         /*
1714          * Beyond this point we will be liberal and accept the items
1715          * in any order.
1716          */
1717         while (1) {
1718                 kind = get_token(&ss,&sval);
1719                 switch (kind) {
1720                 case TK_EOS:
1721                         *code = LDAP_SCHERR_NORIGHTPAREN;
1722                         *errp = ss;
1723                         ldap_attributetype_free(at);
1724                         return NULL;
1725                 case TK_RIGHTPAREN:
1726                         return at;
1727                 case TK_BAREWORD:
1728                         if ( !strcmp(sval,"NAME") ) {
1729                                 LDAP_FREE(sval);
1730                                 if ( seen_name ) {
1731                                         *code = LDAP_SCHERR_DUPOPT;
1732                                         *errp = ss;
1733                                         ldap_attributetype_free(at);
1734                                         return(NULL);
1735                                 }
1736                                 seen_name = 1;
1737                                 at->at_names = parse_qdescrs(&ss,code);
1738                                 if ( !at->at_names ) {
1739                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1740                                                 *code = LDAP_SCHERR_BADNAME;
1741                                         *errp = ss;
1742                                         ldap_attributetype_free(at);
1743                                         return NULL;
1744                                 }
1745                         } else if ( !strcmp(sval,"DESC") ) {
1746                                 LDAP_FREE(sval);
1747                                 if ( seen_desc ) {
1748                                         *code = LDAP_SCHERR_DUPOPT;
1749                                         *errp = ss;
1750                                         ldap_attributetype_free(at);
1751                                         return(NULL);
1752                                 }
1753                                 seen_desc = 1;
1754                                 parse_whsp(&ss);
1755                                 kind = get_token(&ss,&sval);
1756                                 if ( kind != TK_QDSTRING ) {
1757                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1758                                         *errp = ss;
1759                                         LDAP_FREE(sval);
1760                                         ldap_attributetype_free(at);
1761                                         return NULL;
1762                                 }
1763                                 at->at_desc = sval;
1764                                 parse_whsp(&ss);
1765                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1766                                 LDAP_FREE(sval);
1767                                 if ( seen_obsolete ) {
1768                                         *code = LDAP_SCHERR_DUPOPT;
1769                                         *errp = ss;
1770                                         ldap_attributetype_free(at);
1771                                         return(NULL);
1772                                 }
1773                                 seen_obsolete = 1;
1774                                 at->at_obsolete = LDAP_SCHEMA_YES;
1775                                 parse_whsp(&ss);
1776                         } else if ( !strcmp(sval,"SUP") ) {
1777                                 LDAP_FREE(sval);
1778                                 if ( seen_sup ) {
1779                                         *code = LDAP_SCHERR_DUPOPT;
1780                                         *errp = ss;
1781                                         ldap_attributetype_free(at);
1782                                         return(NULL);
1783                                 }
1784                                 seen_sup = 1;
1785                                 at->at_sup_oid = parse_woid(&ss,code);
1786                                 if ( !at->at_sup_oid ) {
1787                                         *errp = ss;
1788                                         ldap_attributetype_free(at);
1789                                         return NULL;
1790                                 }
1791                         } else if ( !strcmp(sval,"EQUALITY") ) {
1792                                 LDAP_FREE(sval);
1793                                 if ( seen_equality ) {
1794                                         *code = LDAP_SCHERR_DUPOPT;
1795                                         *errp = ss;
1796                                         ldap_attributetype_free(at);
1797                                         return(NULL);
1798                                 }
1799                                 seen_equality = 1;
1800                                 at->at_equality_oid = parse_woid(&ss,code);
1801                                 if ( !at->at_equality_oid ) {
1802                                         *errp = ss;
1803                                         ldap_attributetype_free(at);
1804                                         return NULL;
1805                                 }
1806                         } else if ( !strcmp(sval,"ORDERING") ) {
1807                                 LDAP_FREE(sval);
1808                                 if ( seen_ordering ) {
1809                                         *code = LDAP_SCHERR_DUPOPT;
1810                                         *errp = ss;
1811                                         ldap_attributetype_free(at);
1812                                         return(NULL);
1813                                 }
1814                                 seen_ordering = 1;
1815                                 at->at_ordering_oid = parse_woid(&ss,code);
1816                                 if ( !at->at_ordering_oid ) {
1817                                         *errp = ss;
1818                                         ldap_attributetype_free(at);
1819                                         return NULL;
1820                                 }
1821                         } else if ( !strcmp(sval,"SUBSTR") ) {
1822                                 LDAP_FREE(sval);
1823                                 if ( seen_substr ) {
1824                                         *code = LDAP_SCHERR_DUPOPT;
1825                                         *errp = ss;
1826                                         ldap_attributetype_free(at);
1827                                         return(NULL);
1828                                 }
1829                                 seen_substr = 1;
1830                                 at->at_substr_oid = parse_woid(&ss,code);
1831                                 if ( !at->at_substr_oid ) {
1832                                         *errp = ss;
1833                                         ldap_attributetype_free(at);
1834                                         return NULL;
1835                                 }
1836                         } else if ( !strcmp(sval,"SYNTAX") ) {
1837                                 LDAP_FREE(sval);
1838                                 if ( seen_syntax ) {
1839                                         *code = LDAP_SCHERR_DUPOPT;
1840                                         *errp = ss;
1841                                         ldap_attributetype_free(at);
1842                                         return(NULL);
1843                                 }
1844                                 seen_syntax = 1;
1845                                 parse_whsp(&ss);
1846                                 savepos = ss;
1847                                 at->at_syntax_oid =
1848                                         parse_noidlen(&ss,
1849                                                       code,
1850                                                       &at->at_syntax_len,
1851                                                       flags);
1852                                 if ( !at->at_syntax_oid ) {
1853                                     if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
1854                                         kind = get_token(&ss,&sval);
1855                                         if (kind == TK_BAREWORD)
1856                                         {
1857                                             char *sp = strchr(sval, '{');
1858                                             at->at_syntax_oid = sval;
1859                                             if (sp)
1860                                             {
1861                                                 *sp++ = 0;
1862                                                 at->at_syntax_len = atoi(sp);
1863                                                 while ( LDAP_DIGIT(*sp) )
1864                                                         sp++;
1865                                                 if ( *sp != '}' ) {
1866                                                     *code = LDAP_SCHERR_UNEXPTOKEN;
1867                                                     *errp = ss;
1868                                                     ldap_attributetype_free(at);
1869                                                     return NULL;
1870                                                 }
1871                                             }
1872                                         }
1873                                     } else {
1874                                         *errp = ss;
1875                                         ldap_attributetype_free(at);
1876                                         return NULL;
1877                                     }
1878                                 }
1879                                 parse_whsp(&ss);
1880                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1881                                 LDAP_FREE(sval);
1882                                 if ( at->at_single_value ) {
1883                                         *code = LDAP_SCHERR_DUPOPT;
1884                                         *errp = ss;
1885                                         ldap_attributetype_free(at);
1886                                         return(NULL);
1887                                 }
1888                                 at->at_single_value = LDAP_SCHEMA_YES;
1889                                 parse_whsp(&ss);
1890                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1891                                 LDAP_FREE(sval);
1892                                 if ( at->at_collective ) {
1893                                         *code = LDAP_SCHERR_DUPOPT;
1894                                         *errp = ss;
1895                                         ldap_attributetype_free(at);
1896                                         return(NULL);
1897                                 }
1898                                 at->at_collective = LDAP_SCHEMA_YES;
1899                                 parse_whsp(&ss);
1900                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1901                                 LDAP_FREE(sval);
1902                                 if ( at->at_no_user_mod ) {
1903                                         *code = LDAP_SCHERR_DUPOPT;
1904                                         *errp = ss;
1905                                         ldap_attributetype_free(at);
1906                                         return(NULL);
1907                                 }
1908                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1909                                 parse_whsp(&ss);
1910                         } else if ( !strcmp(sval,"USAGE") ) {
1911                                 LDAP_FREE(sval);
1912                                 if ( seen_usage ) {
1913                                         *code = LDAP_SCHERR_DUPOPT;
1914                                         *errp = ss;
1915                                         ldap_attributetype_free(at);
1916                                         return(NULL);
1917                                 }
1918                                 seen_usage = 1;
1919                                 parse_whsp(&ss);
1920                                 kind = get_token(&ss,&sval);
1921                                 if ( kind != TK_BAREWORD ) {
1922                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1923                                         *errp = ss;
1924                                         LDAP_FREE(sval);
1925                                         ldap_attributetype_free(at);
1926                                         return NULL;
1927                                 }
1928                                 if ( !strcasecmp(sval,"userApplications") )
1929                                         at->at_usage =
1930                                             LDAP_SCHEMA_USER_APPLICATIONS;
1931                                 else if ( !strcasecmp(sval,"directoryOperation") )
1932                                         at->at_usage =
1933                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1934                                 else if ( !strcasecmp(sval,"distributedOperation") )
1935                                         at->at_usage =
1936                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1937                                 else if ( !strcasecmp(sval,"dSAOperation") )
1938                                         at->at_usage =
1939                                             LDAP_SCHEMA_DSA_OPERATION;
1940                                 else {
1941                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1942                                         *errp = ss;
1943                                         LDAP_FREE(sval);
1944                                         ldap_attributetype_free(at);
1945                                         return NULL;
1946                                 }
1947                                 LDAP_FREE(sval);
1948                                 parse_whsp(&ss);
1949                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1950                                 /* Should be parse_qdstrings */
1951                                 ext_vals = parse_qdescrs(&ss, code);
1952                                 if ( !ext_vals ) {
1953                                         *errp = ss;
1954                                         ldap_attributetype_free(at);
1955                                         return NULL;
1956                                 }
1957                                 if ( add_extension(&at->at_extensions,
1958                                                     sval, ext_vals) ) {
1959                                         *code = LDAP_SCHERR_OUTOFMEM;
1960                                         *errp = ss;
1961                                         LDAP_FREE(sval);
1962                                         ldap_attributetype_free(at);
1963                                         return NULL;
1964                                 }
1965                         } else {
1966                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1967                                 *errp = ss;
1968                                 LDAP_FREE(sval);
1969                                 ldap_attributetype_free(at);
1970                                 return NULL;
1971                         }
1972                         break;
1973                 default:
1974                         *code = LDAP_SCHERR_UNEXPTOKEN;
1975                         *errp = ss;
1976                         LDAP_FREE(sval);
1977                         ldap_attributetype_free(at);
1978                         return NULL;
1979                 }
1980         }
1981 }
1982
1983 void
1984 ldap_objectclass_free(LDAPObjectClass * oc)
1985 {
1986         LDAP_FREE(oc->oc_oid);
1987         LDAP_VFREE(oc->oc_names);
1988         LDAP_FREE(oc->oc_desc);
1989         LDAP_VFREE(oc->oc_sup_oids);
1990         LDAP_VFREE(oc->oc_at_oids_must);
1991         LDAP_VFREE(oc->oc_at_oids_may);
1992         free_extensions(oc->oc_extensions);
1993         LDAP_FREE(oc);
1994 }
1995
1996 LDAPObjectClass *
1997 ldap_str2objectclass( LDAP_CONST char * s,
1998         int * code,
1999         LDAP_CONST char ** errp,
2000         LDAP_CONST int flags )
2001 {
2002         int kind;
2003         const char * ss = s;
2004         char * sval;
2005         int seen_name = 0;
2006         int seen_desc = 0;
2007         int seen_obsolete = 0;
2008         int seen_sup = 0;
2009         int seen_kind = 0;
2010         int seen_must = 0;
2011         int seen_may = 0;
2012         LDAPObjectClass * oc;
2013         char ** ext_vals;
2014         const char * savepos;
2015
2016         if ( !s ) {
2017                 *code = LDAP_SCHERR_EMPTY;
2018                 *errp = "";
2019                 return NULL;
2020         }
2021
2022         *errp = s;
2023         oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
2024
2025         if ( !oc ) {
2026                 *code = LDAP_SCHERR_OUTOFMEM;
2027                 return NULL;
2028         }
2029         oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2030
2031         kind = get_token(&ss,&sval);
2032         if ( kind != TK_LEFTPAREN ) {
2033                 *code = LDAP_SCHERR_NOLEFTPAREN;
2034                 LDAP_FREE(sval);
2035                 ldap_objectclass_free(oc);
2036                 return NULL;
2037         }
2038
2039         /*
2040          * Definitions MUST begin with an OID in the numericoid format.
2041          * However, this routine is used by clients to parse the response
2042          * from servers and very well known servers will provide an OID
2043          * in the wrong format or even no OID at all.  We do our best to
2044          * extract info from those servers.
2045          */
2046         parse_whsp(&ss);
2047         savepos = ss;
2048         oc->oc_oid = parse_numericoid(&ss,code,0);
2049         if ( !oc->oc_oid ) {
2050                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2051                         /* Backtracking */
2052                         ss = savepos;
2053                         kind = get_token(&ss,&sval);
2054                         if ( kind == TK_BAREWORD ) {
2055                                 if ( !strcmp(sval, "NAME") ||
2056                                      !strcmp(sval, "DESC") ||
2057                                      !strcmp(sval, "OBSOLETE") ||
2058                                      !strcmp(sval, "SUP") ||
2059                                      !strcmp(sval, "ABSTRACT") ||
2060                                      !strcmp(sval, "STRUCTURAL") ||
2061                                      !strcmp(sval, "AUXILIARY") ||
2062                                      !strcmp(sval, "MUST") ||
2063                                      !strncmp(sval, "X-", 2) ) {
2064                                         /* Missing OID, backtrack */
2065                                         ss = savepos;
2066                                 } else if ( flags &
2067                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2068                                         /* Non-numerical OID, ignore */
2069                                         int len = ss-savepos;
2070                                         oc->oc_oid = LDAP_MALLOC(len+1);
2071                                         strncpy(oc->oc_oid, savepos, len);
2072                                         oc->oc_oid[len] = 0;
2073                                 }
2074                         }
2075                         LDAP_FREE(sval);
2076                 } else {
2077                         *errp = ss;
2078                         ldap_objectclass_free(oc);
2079                         return NULL;
2080                 }
2081         }
2082         parse_whsp(&ss);
2083
2084         /*
2085          * Beyond this point we will be liberal an accept the items
2086          * in any order.
2087          */
2088         while (1) {
2089                 kind = get_token(&ss,&sval);
2090                 switch (kind) {
2091                 case TK_EOS:
2092                         *code = LDAP_SCHERR_NORIGHTPAREN;
2093                         *errp = ss;
2094                         ldap_objectclass_free(oc);
2095                         return NULL;
2096                 case TK_RIGHTPAREN:
2097                         return oc;
2098                 case TK_BAREWORD:
2099                         if ( !strcmp(sval,"NAME") ) {
2100                                 LDAP_FREE(sval);
2101                                 if ( seen_name ) {
2102                                         *code = LDAP_SCHERR_DUPOPT;
2103                                         *errp = ss;
2104                                         ldap_objectclass_free(oc);
2105                                         return(NULL);
2106                                 }
2107                                 seen_name = 1;
2108                                 oc->oc_names = parse_qdescrs(&ss,code);
2109                                 if ( !oc->oc_names ) {
2110                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2111                                                 *code = LDAP_SCHERR_BADNAME;
2112                                         *errp = ss;
2113                                         ldap_objectclass_free(oc);
2114                                         return NULL;
2115                                 }
2116                         } else if ( !strcmp(sval,"DESC") ) {
2117                                 LDAP_FREE(sval);
2118                                 if ( seen_desc ) {
2119                                         *code = LDAP_SCHERR_DUPOPT;
2120                                         *errp = ss;
2121                                         ldap_objectclass_free(oc);
2122                                         return(NULL);
2123                                 }
2124                                 seen_desc = 1;
2125                                 parse_whsp(&ss);
2126                                 kind = get_token(&ss,&sval);
2127                                 if ( kind != TK_QDSTRING ) {
2128                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2129                                         *errp = ss;
2130                                         LDAP_FREE(sval);
2131                                         ldap_objectclass_free(oc);
2132                                         return NULL;
2133                                 }
2134                                 oc->oc_desc = sval;
2135                                 parse_whsp(&ss);
2136                         } else if ( !strcmp(sval,"OBSOLETE") ) {
2137                                 LDAP_FREE(sval);
2138                                 if ( seen_obsolete ) {
2139                                         *code = LDAP_SCHERR_DUPOPT;
2140                                         *errp = ss;
2141                                         ldap_objectclass_free(oc);
2142                                         return(NULL);
2143                                 }
2144                                 seen_obsolete = 1;
2145                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
2146                                 parse_whsp(&ss);
2147                         } else if ( !strcmp(sval,"SUP") ) {
2148                                 LDAP_FREE(sval);
2149                                 if ( seen_sup ) {
2150                                         *code = LDAP_SCHERR_DUPOPT;
2151                                         *errp = ss;
2152                                         ldap_objectclass_free(oc);
2153                                         return(NULL);
2154                                 }
2155                                 seen_sup = 1;
2156                                 oc->oc_sup_oids = parse_oids(&ss,
2157                                                              code,
2158                                                              flags);
2159                                 if ( !oc->oc_sup_oids ) {
2160                                         *errp = ss;
2161                                         ldap_objectclass_free(oc);
2162                                         return NULL;
2163                                 }
2164                         } else if ( !strcmp(sval,"ABSTRACT") ) {
2165                                 LDAP_FREE(sval);
2166                                 if ( seen_kind ) {
2167                                         *code = LDAP_SCHERR_DUPOPT;
2168                                         *errp = ss;
2169                                         ldap_objectclass_free(oc);
2170                                         return(NULL);
2171                                 }
2172                                 seen_kind = 1;
2173                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
2174                                 parse_whsp(&ss);
2175                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
2176                                 LDAP_FREE(sval);
2177                                 if ( seen_kind ) {
2178                                         *code = LDAP_SCHERR_DUPOPT;
2179                                         *errp = ss;
2180                                         ldap_objectclass_free(oc);
2181                                         return(NULL);
2182                                 }
2183                                 seen_kind = 1;
2184                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2185                                 parse_whsp(&ss);
2186                         } else if ( !strcmp(sval,"AUXILIARY") ) {
2187                                 LDAP_FREE(sval);
2188                                 if ( seen_kind ) {
2189                                         *code = LDAP_SCHERR_DUPOPT;
2190                                         *errp = ss;
2191                                         ldap_objectclass_free(oc);
2192                                         return(NULL);
2193                                 }
2194                                 seen_kind = 1;
2195                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
2196                                 parse_whsp(&ss);
2197                         } else if ( !strcmp(sval,"MUST") ) {
2198                                 LDAP_FREE(sval);
2199                                 if ( seen_must ) {
2200                                         *code = LDAP_SCHERR_DUPOPT;
2201                                         *errp = ss;
2202                                         ldap_objectclass_free(oc);
2203                                         return(NULL);
2204                                 }
2205                                 seen_must = 1;
2206                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
2207                                 if ( !oc->oc_at_oids_must ) {
2208                                         *errp = ss;
2209                                         ldap_objectclass_free(oc);
2210                                         return NULL;
2211                                 }
2212                                 parse_whsp(&ss);
2213                         } else if ( !strcmp(sval,"MAY") ) {
2214                                 LDAP_FREE(sval);
2215                                 if ( seen_may ) {
2216                                         *code = LDAP_SCHERR_DUPOPT;
2217                                         *errp = ss;
2218                                         ldap_objectclass_free(oc);
2219                                         return(NULL);
2220                                 }
2221                                 seen_may = 1;
2222                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
2223                                 if ( !oc->oc_at_oids_may ) {
2224                                         *errp = ss;
2225                                         ldap_objectclass_free(oc);
2226                                         return NULL;
2227                                 }
2228                                 parse_whsp(&ss);
2229                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2230                                 /* Should be parse_qdstrings */
2231                                 ext_vals = parse_qdescrs(&ss, code);
2232                                 if ( !ext_vals ) {
2233                                         *errp = ss;
2234                                         ldap_objectclass_free(oc);
2235                                         return NULL;
2236                                 }
2237                                 if ( add_extension(&oc->oc_extensions,
2238                                                     sval, ext_vals) ) {
2239                                         *code = LDAP_SCHERR_OUTOFMEM;
2240                                         *errp = ss;
2241                                         LDAP_FREE(sval);
2242                                         ldap_objectclass_free(oc);
2243                                         return NULL;
2244                                 }
2245                         } else {
2246                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2247                                 *errp = ss;
2248                                 LDAP_FREE(sval);
2249                                 ldap_objectclass_free(oc);
2250                                 return NULL;
2251                         }
2252                         break;
2253                 default:
2254                         *code = LDAP_SCHERR_UNEXPTOKEN;
2255                         *errp = ss;
2256                         LDAP_FREE(sval);
2257                         ldap_objectclass_free(oc);
2258                         return NULL;
2259                 }
2260         }
2261 }
2262
2263 static char *const err2text[] = {
2264         "Success",
2265         "Out of memory",
2266         "Unexpected token",
2267         "Missing opening parenthesis",
2268         "Missing closing parenthesis",
2269         "Expecting digit",
2270         "Expecting a name",
2271         "Bad description",
2272         "Bad superiors",
2273         "Duplicate option",
2274         "Unexpected end of data"
2275 };
2276
2277 char *
2278 ldap_scherr2str(int code)
2279 {
2280         if ( code < 0 || code >= (sizeof(err2text)/sizeof(char *)) ) {
2281                 return "Unknown error";
2282         } else {
2283                 return err2text[code];
2284         }
2285 }