]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Netscape DS quotes the SYNTAX values, we propagate the be_liberal
[openldap] / libraries / libldap / schema.c
1 /*
2  * Copyright 1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  *
5  * schema.c:  parsing routines used by servers and clients to process
6  *      schema definitions
7  */
8
9 #include "portable.h"
10
11 #include <stdio.h>
12 #include <ac/stdlib.h>
13
14 #include <ac/ctype.h>
15 #include <ac/string.h>
16 #include <ac/time.h>
17
18 #include "ldap-int.h"
19
20 #include <ldap_schema.h>
21
22 /*
23  * When pretty printing the entities we will be appending to a buffer.
24  * Since checking for overflow, realloc'ing and checking if no error
25  * is extremely boring, we will use a protection layer that will let
26  * us blissfully ignore the error until the end.  This layer is
27  * implemented with the help of the next type.
28  */
29
30 typedef struct safe_string {
31         char * val;
32         int size;
33         int pos;
34         int at_whsp;
35 } safe_string;
36
37 static safe_string *
38 new_safe_string(int size)
39 {
40         safe_string * ss;
41         
42         ss = LDAP_MALLOC(sizeof(safe_string));
43         if ( !ss )
44                 return(NULL);
45
46         ss->val = LDAP_MALLOC(size);
47         if ( !ss->val ) {
48                 LDAP_FREE(ss);
49                 return(NULL);
50         }
51
52         ss->size = size;
53         ss->pos = 0;
54         ss->at_whsp = 0;
55
56         return ss;
57 }
58
59 static void
60 safe_string_free(safe_string * ss)
61 {
62         if ( !ss )
63                 return;
64         LDAP_FREE(ss->val);
65         LDAP_FREE(ss);
66 }
67
68 static char *
69 safe_string_val(safe_string * ss)
70 {
71         ss->val[ss->pos] = '\0';
72         return(ss->val);
73 }
74
75 static int
76 append_to_safe_string(safe_string * ss, char * s)
77 {
78         int l = strlen(s);
79         char * temp;
80
81         /*
82          * Some runaway process is trying to append to a string that
83          * overflowed and we could not extend.
84          */
85         if ( !ss->val )
86                 return -1;
87
88         /* We always make sure there is at least one position available */
89         if ( ss->pos + l >= ss->size-1 ) {
90                 ss->size *= 2;
91                 temp = LDAP_REALLOC(ss->val, ss->size);
92                 if ( !temp ) {
93                         /* Trouble, out of memory */
94                         LDAP_FREE(ss->val);
95                         return -1;
96                 }
97                 ss->val = temp;
98         }
99         strncpy(&ss->val[ss->pos], s, l);
100         ss->pos += l;
101         if ( ss->pos > 0 && isspace(ss->val[ss->pos-1]) )
102                 ss->at_whsp = 1;
103         else
104                 ss->at_whsp = 0;
105
106         return 0;
107 }
108
109 static int
110 print_literal(safe_string *ss, char *s)
111 {
112         return(append_to_safe_string(ss,s));
113 }
114
115 static int
116 print_whsp(safe_string *ss)
117 {
118         if ( ss->at_whsp )
119                 return(append_to_safe_string(ss,""));
120         else
121                 return(append_to_safe_string(ss," "));
122 }
123
124 static int
125 print_numericoid(safe_string *ss, char *s)
126 {
127         if ( s )
128                 return(append_to_safe_string(ss,s));
129 }
130
131 /* This one is identical to print_qdescr */
132 static int
133 print_qdstring(safe_string *ss, char *s)
134 {
135         print_whsp(ss);
136         print_literal(ss,"'");
137         append_to_safe_string(ss,s);
138         print_literal(ss,"'");
139         return(print_whsp(ss));
140 }
141
142 static int
143 print_qdescr(safe_string *ss, char *s)
144 {
145         print_whsp(ss);
146         print_literal(ss,"'");
147         append_to_safe_string(ss,s);
148         print_literal(ss,"'");
149         return(print_whsp(ss));
150 }
151
152 static int
153 print_qdescrlist(safe_string *ss, char **sa)
154 {
155         char **sp;
156         int ret = 0;
157         
158         for (sp=sa; *sp; sp++) {
159                 ret = print_qdescr(ss,*sp);
160         }
161         /* If the list was empty, we return zero that is potentially
162          * incorrect, but since we will be still appending things, the
163          * overflow will be detected later.  Maybe FIX.
164          */
165         return(ret);
166 }
167
168 static int
169 print_qdescrs(safe_string *ss, char **sa)
170 {
171         /* The only way to represent an empty list is as a qdescrlist
172          * so, if the list is empty we treat it as a long list.
173          * Really, this is what the syntax mandates.  We should not
174          * be here if the list was empty, but if it happens, a label
175          * has already been output and we cannot undo it.
176          */
177         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
178                 print_whsp(ss);
179                 print_literal(ss,"(");
180                 print_qdescrlist(ss,sa);
181                 print_literal(ss,")");
182                 return(print_whsp(ss));
183         } else {
184           return(print_qdescr(ss,*sa));
185         }
186 }
187
188 static int
189 print_woid(safe_string *ss, char *s)
190 {
191         print_whsp(ss);
192         append_to_safe_string(ss,s);
193         return print_whsp(ss);
194 }
195
196 static int
197 print_oidlist(safe_string *ss, char **sa)
198 {
199         char **sp;
200
201         for (sp=sa; *(sp+1); sp++) {
202                 print_woid(ss,*sp);
203                 print_literal(ss,"$");
204         }
205         return(print_woid(ss,*sp));
206 }
207
208 static int
209 print_oids(safe_string *ss, char **sa)
210 {
211         if ( sa[0] && sa[1] ) {
212                 print_literal(ss,"(");
213                 print_oidlist(ss,sa);
214                 print_whsp(ss);
215                 return(print_literal(ss,")"));
216         } else {
217                 return(print_woid(ss,*sa));
218         }
219 }
220
221 static int
222 print_noidlen(safe_string *ss, char *s, int l)
223 {
224         char buf[64];
225         int ret;
226
227         ret = print_numericoid(ss,s);
228         if ( l ) {
229                 sprintf(buf,"{%d}",l);
230                 ret = print_literal(ss,buf);
231         }
232         return(ret);
233 }
234
235 char *
236 ldap_syntax2str( LDAP_SYNTAX * syn )
237 {
238         safe_string * ss;
239         char * retstring;
240         
241         ss = new_safe_string(256);
242         if ( !ss )
243                 return NULL;
244
245         print_literal(ss,"(");
246         print_whsp(ss);
247
248         print_numericoid(ss, syn->syn_oid);
249         print_whsp(ss);
250
251         if ( syn->syn_desc ) {
252                 print_literal(ss,"DESC");
253                 print_qdstring(ss,syn->syn_desc);
254         }
255
256         print_whsp(ss);
257         print_literal(ss,")");
258
259         retstring = LDAP_STRDUP(safe_string_val(ss));
260         safe_string_free(ss);
261         return(retstring);
262 }
263
264 char *
265 ldap_objectclass2str( LDAP_OBJECT_CLASS * oc )
266 {
267         safe_string * ss;
268         char * retstring;
269         
270         ss = new_safe_string(256);
271         if ( !ss )
272                 return NULL;
273
274         print_literal(ss,"(");
275         print_whsp(ss);
276
277         print_numericoid(ss, oc->oc_oid);
278         print_whsp(ss);
279
280         if ( oc->oc_names ) {
281                 print_literal(ss,"NAME");
282                 print_qdescrs(ss,oc->oc_names);
283         }
284
285         if ( oc->oc_desc ) {
286                 print_literal(ss,"DESC");
287                 print_qdstring(ss,oc->oc_desc);
288         }
289
290         if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) {
291                 print_literal(ss, "OBSOLETE");
292                 print_whsp(ss);
293         }
294
295         if ( oc->oc_sup_oids ) {
296                 print_literal(ss,"SUP");
297                 print_whsp(ss);
298                 print_oids(ss,oc->oc_sup_oids);
299                 print_whsp(ss);
300         }
301
302         switch (oc->oc_kind) {
303         case LDAP_SCHEMA_ABSTRACT:
304                 print_literal(ss,"ABSTRACT");
305                 break;
306         case LDAP_SCHEMA_STRUCTURAL:
307                 print_literal(ss,"STRUCTURAL");
308                 break;
309         case LDAP_SCHEMA_AUXILIARY:
310                 print_literal(ss,"AUXILIARY");
311                 break;
312         default:
313                 print_literal(ss,"KIND-UNKNOWN");
314                 break;
315         }
316         print_whsp(ss);
317         
318         if ( oc->oc_at_oids_must ) {
319                 print_literal(ss,"MUST");
320                 print_whsp(ss);
321                 print_oids(ss,oc->oc_at_oids_must);
322                 print_whsp(ss);
323         }
324
325         if ( oc->oc_at_oids_may ) {
326                 print_literal(ss,"MAY");
327                 print_whsp(ss);
328                 print_oids(ss,oc->oc_at_oids_may);
329                 print_whsp(ss);
330         }
331
332         print_whsp(ss);
333         print_literal(ss,")");
334
335         retstring = LDAP_STRDUP(safe_string_val(ss));
336         safe_string_free(ss);
337         return(retstring);
338 }
339
340 char *
341 ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at )
342 {
343         safe_string * ss;
344         char * retstring;
345         
346         ss = new_safe_string(256);
347         if ( !ss )
348                 return NULL;
349
350         print_literal(ss,"(");
351         print_whsp(ss);
352
353         print_numericoid(ss, at->at_oid);
354         print_whsp(ss);
355
356         if ( at->at_names ) {
357                 print_literal(ss,"NAME");
358                 print_qdescrs(ss,at->at_names);
359         }
360
361         if ( at->at_desc ) {
362                 print_literal(ss,"DESC");
363                 print_qdstring(ss,at->at_desc);
364         }
365
366         if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
367                 print_literal(ss, "OBSOLETE");
368                 print_whsp(ss);
369         }
370
371         if ( at->at_sup_oid ) {
372                 print_literal(ss,"SUP");
373                 print_woid(ss,at->at_sup_oid);
374         }
375
376         if ( at->at_equality_oid ) {
377                 print_literal(ss,"EQUALITY");
378                 print_woid(ss,at->at_equality_oid);
379         }
380
381         if ( at->at_ordering_oid ) {
382                 print_literal(ss,"ORDERING");
383                 print_woid(ss,at->at_ordering_oid);
384         }
385
386         if ( at->at_substr_oid ) {
387                 print_literal(ss,"SUBSTR");
388                 print_woid(ss,at->at_substr_oid);
389         }
390
391         if ( at->at_syntax_oid ) {
392                 print_literal(ss,"SYNTAX");
393                 print_whsp(ss);
394                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
395                 print_whsp(ss);
396         }
397
398         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
399                 print_literal(ss,"SINGLE-VALUE");
400                 print_whsp(ss);
401         }
402
403         if ( at->at_collective == LDAP_SCHEMA_YES ) {
404                 print_literal(ss,"COLLECTIVE");
405                 print_whsp(ss);
406         }
407
408         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
409                 print_literal(ss,"NO-USER-MODIFICATION");
410                 print_whsp(ss);
411         }
412
413         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
414                 print_literal(ss,"USAGE");
415                 print_whsp(ss);
416                 switch (at->at_usage) {
417                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
418                         print_literal(ss,"directoryOperation");
419                         break;
420                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
421                         print_literal(ss,"distributedOperation");
422                         break;
423                 case LDAP_SCHEMA_DSA_OPERATION:
424                         print_literal(ss,"dSAOperation");
425                         break;
426                 default:
427                         print_literal(ss,"UNKNOWN");
428                         break;
429                 }
430         }
431         
432         print_whsp(ss);
433         print_literal(ss,")");
434
435         retstring = LDAP_STRDUP(safe_string_val(ss));
436         safe_string_free(ss);
437         return(retstring);
438 }
439
440 /*
441  * Now come the parsers.  There is one parser for each entity type:
442  * objectclasses, attributetypes, etc.
443  *
444  * Each of them is written as a recursive-descent parser, except that
445  * none of them is really recursive.  But the idea is kept: there
446  * is one routine per non-terminal that eithers gobbles lexical tokens
447  * or calls lower-level routines, etc.
448  *
449  * The scanner is implemented in the routine get_token.  Actually,
450  * get_token is more than a scanner and will return tokens that are
451  * in fact non-terminals in the grammar.  So you can see the whole
452  * approach as the combination of a low-level bottom-up recognizer
453  * combined with a scanner and a number of top-down parsers.  Or just
454  * consider that the real grammars recognized by the parsers are not
455  * those of the standards.  As a matter of fact, our parsers are more
456  * liberal than the spec when there is no ambiguity.
457  *
458  * The difference is pretty academic (modulo bugs or incorrect
459  * interpretation of the specs).
460  */
461
462 #define TK_NOENDQUOTE   -2
463 #define TK_OUTOFMEM     -1
464 #define TK_EOS          0
465 #define TK_UNEXPCHAR    1
466 #define TK_BAREWORD     2
467 #define TK_QDSTRING     3
468 #define TK_LEFTPAREN    4
469 #define TK_RIGHTPAREN   5
470 #define TK_DOLLAR       6
471 #define TK_QDESCR       TK_QDSTRING
472
473 struct token {
474         int type;
475         char *sval;
476 };
477
478 static int
479 get_token(char ** sp, char ** token_val)
480 {
481         int kind;
482         char * p;
483         char * q;
484         char * res;
485
486         switch (**sp) {
487         case '\0':
488                 kind = TK_EOS;
489                 (*sp)++;
490                 break;
491         case '(':
492                 kind = TK_LEFTPAREN;
493                 (*sp)++;
494                 break;
495         case ')':
496                 kind = TK_RIGHTPAREN;
497                 (*sp)++;
498                 break;
499         case '$':
500                 kind = TK_DOLLAR;
501                 (*sp)++;
502                 break;
503         case '\'':
504                 kind = TK_QDSTRING;
505                 (*sp)++;
506                 p = *sp;
507                 while ( **sp != '\'' && **sp != '\0' )
508                         (*sp)++;
509                 if ( **sp == '\'' ) {
510                         q = *sp;
511                         res = LDAP_MALLOC(q-p+1);
512                         if ( !res ) {
513                                 kind = TK_OUTOFMEM;
514                         } else {
515                                 strncpy(res,p,q-p);
516                                 res[q-p] = '\0';
517                                 *token_val = res;
518                         }
519                         (*sp)++;
520                 } else {
521                         kind = TK_NOENDQUOTE;
522                 }
523                 break;
524         default:
525                 kind = TK_BAREWORD;
526                 p = *sp;
527                 while ( !isspace(**sp) && **sp != '\0' )
528                         (*sp)++;
529                 q = *sp;
530                 res = LDAP_MALLOC(q-p+1);
531                 if ( !res ) {
532                         kind = TK_OUTOFMEM;
533                 } else {
534                         strncpy(res,p,q-p);
535                         res[q-p] = '\0';
536                         *token_val = res;
537                 }
538                 break;
539 /*              kind = TK_UNEXPCHAR; */
540 /*              break; */
541         }
542         
543         return kind;
544 }
545
546 /* Gobble optional whitespace */
547 static void
548 parse_whsp(char **sp)
549 {
550         while (isspace(**sp))
551                 (*sp)++;
552 }
553
554 /* TBC:!!
555  * General note for all parsers: to guarantee the algorithm halts they
556  * must always advance the pointer even when an error is found.  For
557  * this one is not that important since an error here is fatal at the
558  * upper layers, but it is a simple strategy that will not get in
559  * endless loops.
560  */
561
562 /* Parse a sequence of dot-separated decimal strings */
563 static char *
564 parse_numericoid(char **sp, int *code)
565 {
566         char * res;
567         char * start = *sp;
568         int len;
569
570         /* Each iteration of this loop gets one decimal string */
571         while (**sp) {
572                 if ( !isdigit(**sp) ) {
573                         /*
574                          * Initial char is not a digit or char after dot is
575                          * not a digit
576                          */
577                         *code = LDAP_SCHERR_NODIGIT;
578                         return NULL;
579                 }
580                 (*sp)++;
581                 while ( isdigit(**sp) )
582                         (*sp)++;
583                 if ( **sp != '.' )
584                         break;
585                 /* Otherwise, gobble the dot and loop again */
586                 (*sp)++;
587         }
588         /* Now *sp points at the char past the numericoid. Perfect. */
589         len = *sp - start;
590         res = LDAP_MALLOC(len+1);
591         if (!res) {
592                 *code = LDAP_SCHERR_OUTOFMEM;
593                 return(NULL);
594         }
595         strncpy(res,start,len);
596         res[len] = '\0';
597         return(res);
598 }
599
600 /* Parse a qdescr or a list of them enclosed in () */
601 static char **
602 parse_qdescrs(char **sp, int *code)
603 {
604         char ** res;
605         char ** res1;
606         int kind;
607         char * sval;
608         int size;
609         int pos;
610
611         parse_whsp(sp);
612         kind = get_token(sp,&sval);
613         if ( kind == TK_LEFTPAREN ) {
614                 /* Let's presume there will be at least 2 entries */
615                 size = 3;
616                 res = LDAP_CALLOC(3,sizeof(char *));
617                 if ( !res ) {
618                         *code = LDAP_SCHERR_OUTOFMEM;
619                         return NULL;
620                 }
621                 pos = 0;
622                 while (1) {
623                         parse_whsp(sp);
624                         kind = get_token(sp,&sval);
625                         if ( kind == TK_RIGHTPAREN )
626                                 break;
627                         if ( kind == TK_QDESCR ) {
628                                 if ( pos == size-2 ) {
629                                         size++;
630                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
631                                         if ( !res1 ) {
632                                                 LDAP_VFREE(res);
633                                                 *code = LDAP_SCHERR_OUTOFMEM;
634                                                 return(NULL);
635                                         }
636                                         res = res1;
637                                 }
638                                 res[pos] = sval;
639                                 pos++;
640                                 parse_whsp(sp);
641                         } else {
642                                 LDAP_VFREE(res);
643                                 *code = LDAP_SCHERR_UNEXPTOKEN;
644                                 return(NULL);
645                         }
646                 }
647                 res[pos] = NULL;
648                 parse_whsp(sp);
649                 return(res);
650         } else if ( kind == TK_QDESCR ) {
651                 res = LDAP_CALLOC(2,sizeof(char *));
652                 if ( !res ) {
653                         *code = LDAP_SCHERR_OUTOFMEM;
654                         return NULL;
655                 }
656                 res[0] = sval;
657                 res[1] = NULL;
658                 parse_whsp(sp);
659                 return res;
660         } else {
661                 *code = LDAP_SCHERR_BADNAME;
662                 return NULL;
663         }
664 }
665
666 /* Parse a woid */
667 static char *
668 parse_woid(char **sp, int *code)
669 {
670         char * sval;
671         int kind;
672
673         parse_whsp(sp);
674         kind = get_token(sp, &sval);
675         if ( kind != TK_BAREWORD ) {
676                 *code = LDAP_SCHERR_UNEXPTOKEN;
677                 return NULL;
678         }
679         parse_whsp(sp);
680         return sval;
681 }
682
683 /* Parse a noidlen */
684 static char *
685 parse_noidlen(char **sp, int *code, int *len, int be_liberal)
686 {
687         char * sval;
688         int kind;
689         int quoted = 0;
690
691         *len = 0;
692         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
693         if ( be_liberal && **sp == '\'' ) {
694                 quoted = 1;
695                 (*sp)++;
696         }
697         sval = parse_numericoid(sp, code);
698         if ( !sval ) {
699                 return NULL;
700         }
701         if ( **sp == '{' ) {
702                 (*sp)++;
703                 *len = atoi(*sp);
704                 while ( isdigit(**sp) )
705                         (*sp)++;
706                 if ( **sp != '}' ) {
707                         *code = LDAP_SCHERR_UNEXPTOKEN;
708                         LDAP_FREE(sval);
709                         return NULL;
710                 }
711                 (*sp)++;
712         }               
713         if ( be_liberal && quoted ) {
714                 if ( **sp == '\'' ) {
715                         (*sp)++;
716                 } else {
717                         *code = LDAP_SCHERR_UNEXPTOKEN;
718                         LDAP_FREE(sval);
719                         return NULL;
720                 }
721         }
722         return sval;
723 }
724
725 /*
726  * Next routine will accept a qdstring in place of an oid if
727  * be_liberal is set.  This is necessary to interoperate with Netscape
728  * Directory server that will improperly quote each oid (at least
729  * those of the descr kind) in the SUP clause.
730  */
731
732 /* Parse a woid or a $-separated list of them enclosed in () */
733 static char **
734 parse_oids(char **sp, int *code, int be_liberal)
735 {
736         char ** res;
737         char ** res1;
738         int kind;
739         char * sval;
740         int size;
741         int pos;
742
743         /*
744          * Strictly speaking, doing this here accepts whsp before the
745          * ( at the begining of an oidlist, but this is harmless.  Also,
746          * we are very liberal in what we accept as an OID.  Maybe
747          * refine later.
748          */
749         parse_whsp(sp);
750         kind = get_token(sp,&sval);
751         if ( kind == TK_LEFTPAREN ) {
752                 /* Let's presume there will be at least 2 entries */
753                 size = 3;
754                 res = LDAP_CALLOC(3,sizeof(char *));
755                 if ( !res ) {
756                         *code = LDAP_SCHERR_OUTOFMEM;
757                         return NULL;
758                 }
759                 pos = 0;
760                 parse_whsp(sp);
761                 kind = get_token(sp,&sval);
762                 if ( kind == TK_BAREWORD ||
763                      ( be_liberal && kind == TK_QDSTRING ) ) {
764                         res[pos] = sval;
765                         pos++;
766                 } else {
767                         *code = LDAP_SCHERR_UNEXPTOKEN;
768                         LDAP_VFREE(res);
769                         return NULL;
770                 }
771                 parse_whsp(sp);
772                 while (1) {
773                         kind = get_token(sp,&sval);
774                         if ( kind == TK_RIGHTPAREN )
775                                 break;
776                         if ( kind == TK_DOLLAR ) {
777                                 parse_whsp(sp);
778                                 kind = get_token(sp,&sval);
779                                 if ( kind == TK_BAREWORD ||
780                                      ( be_liberal && kind == TK_QDSTRING ) ) {
781                                         if ( pos == size-2 ) {
782                                                 size++;
783                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
784                                                 if ( !res1 ) {
785                                                   LDAP_VFREE(res);
786                                                   *code = LDAP_SCHERR_OUTOFMEM;
787                                                   return(NULL);
788                                                 }
789                                                 res = res1;
790                                         }
791                                         res[pos] = sval;
792                                         pos++;
793                                 } else {
794                                         *code = LDAP_SCHERR_UNEXPTOKEN;
795                                         LDAP_VFREE(res);
796                                         return NULL;
797                                 }
798                                 parse_whsp(sp);
799                         } else {
800                                 *code = LDAP_SCHERR_UNEXPTOKEN;
801                                 LDAP_VFREE(res);
802                                 return NULL;
803                         }
804                 }
805                 res[pos] = NULL;
806                 parse_whsp(sp);
807                 return(res);
808         } else if ( kind == TK_BAREWORD ||
809                     ( be_liberal && kind == TK_QDSTRING ) ) {
810                 res = LDAP_CALLOC(2,sizeof(char *));
811                 if ( !res ) {
812                         *code = LDAP_SCHERR_OUTOFMEM;
813                         return NULL;
814                 }
815                 res[0] = sval;
816                 res[1] = NULL;
817                 parse_whsp(sp);
818                 return res;
819         } else {
820                 *code = LDAP_SCHERR_BADNAME;
821                 return NULL;
822         }
823 }
824
825 void
826 ldap_syntax_free(LDAP_SYNTAX * syn)
827 {
828         LDAP_FREE(syn->syn_oid);
829         LDAP_FREE(syn->syn_desc);
830         LDAP_FREE(syn);
831 }
832
833 LDAP_SYNTAX *
834 ldap_str2syntax( char * s, int * code, char ** errp )
835 {
836         int kind;
837         char * ss = s;
838         char * sval;
839         int seen_desc = 0;
840         LDAP_SYNTAX * syn;
841         char ** ssdummy;
842
843         if ( !s ) {
844                 *code = LDAP_SCHERR_EMPTY;
845                 *errp = "";
846                 return NULL;
847         }
848
849         *errp = s;
850         syn = LDAP_CALLOC(1,sizeof(LDAP_SYNTAX));
851
852         if ( !syn ) {
853                 *code = LDAP_SCHERR_OUTOFMEM;
854                 return NULL;
855         }
856
857         kind = get_token(&ss,&sval);
858         if ( kind != TK_LEFTPAREN ) {
859                 *code = LDAP_SCHERR_NOLEFTPAREN;
860                 ldap_syntax_free(syn);
861                 return NULL;
862         }
863
864         parse_whsp(&ss);
865         syn->syn_oid = parse_numericoid(&ss,code);
866         if ( !syn->syn_oid ) {
867                 *errp = ss;
868                 ldap_syntax_free(syn);
869                 return NULL;
870         }
871         parse_whsp(&ss);
872
873         /*
874          * Beyond this point we will be liberal and accept the items
875          * in any order.
876          */
877         while (1) {
878                 kind = get_token(&ss,&sval);
879                 switch (kind) {
880                 case TK_EOS:
881                         *code = LDAP_SCHERR_NORIGHTPAREN;
882                         *errp = ss;
883                         ldap_syntax_free(syn);
884                         return NULL;
885                 case TK_RIGHTPAREN:
886                         return syn;
887                 case TK_BAREWORD:
888                         if ( !strcmp(sval,"DESC") ) {
889                                 if ( seen_desc ) {
890                                         *code = LDAP_SCHERR_DUPOPT;
891                                         *errp = ss;
892                                         ldap_syntax_free(syn);
893                                         return(NULL);
894                                 }
895                                 seen_desc = 1;
896                                 parse_whsp(&ss);
897                                 kind = get_token(&ss,&sval);
898                                 if ( kind != TK_QDSTRING ) {
899                                         *code = LDAP_SCHERR_UNEXPTOKEN;
900                                         *errp = ss;
901                                         ldap_syntax_free(syn);
902                                         return NULL;
903                                 }
904                                 syn->syn_desc = sval;
905                                 parse_whsp(&ss);
906                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
907                                 /* Should be parse_qdstrings */
908                                 ssdummy = parse_qdescrs(&ss, code);
909                                 if ( !ssdummy ) {
910                                         *errp = ss;
911                                         ldap_syntax_free(syn);
912                                         return NULL;
913                                 }
914                         } else {
915                                 *code = LDAP_SCHERR_UNEXPTOKEN;
916                                 *errp = ss;
917                                 ldap_syntax_free(syn);
918                                 return NULL;
919                         }
920                         break;
921                 default:
922                         *code = LDAP_SCHERR_UNEXPTOKEN;
923                         *errp = ss;
924                         ldap_syntax_free(syn);
925                         return NULL;
926                 }
927         }
928 }
929
930 void
931 ldap_attributetype_free(LDAP_ATTRIBUTE_TYPE * at)
932 {
933         LDAP_FREE(at->at_oid);
934         LDAP_VFREE(at->at_names);
935         LDAP_FREE(at->at_desc);
936         LDAP_FREE(at->at_sup_oid);
937         LDAP_FREE(at->at_equality_oid);
938         LDAP_FREE(at->at_ordering_oid);
939         LDAP_FREE(at->at_substr_oid);
940         LDAP_FREE(at->at_syntax_oid);
941         LDAP_FREE(at);
942 }
943
944 LDAP_ATTRIBUTE_TYPE *
945 ldap_str2attributetype( char * s, int * code, char ** errp )
946 {
947         int kind;
948         char * ss = s;
949         char * sval;
950         int be_liberal = 1;     /* Future additional argument */
951         int seen_name = 0;
952         int seen_desc = 0;
953         int seen_obsolete = 0;
954         int seen_sup = 0;
955         int seen_equality = 0;
956         int seen_ordering = 0;
957         int seen_substr = 0;
958         int seen_syntax = 0;
959         int seen_usage = 0;
960         int seen_kind = 0;
961         int seen_must = 0;
962         int seen_may = 0;
963         LDAP_ATTRIBUTE_TYPE * at;
964         char ** ssdummy;
965         char * savepos;
966
967         if ( !s ) {
968                 *code = LDAP_SCHERR_EMPTY;
969                 *errp = "";
970                 return NULL;
971         }
972
973         *errp = s;
974         at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
975
976         if ( !at ) {
977                 *code = LDAP_SCHERR_OUTOFMEM;
978                 return NULL;
979         }
980
981         kind = get_token(&ss,&sval);
982         if ( kind != TK_LEFTPAREN ) {
983                 *code = LDAP_SCHERR_NOLEFTPAREN;
984                 ldap_attributetype_free(at);
985                 return NULL;
986         }
987
988         /*
989          * Definitions MUST begin with an OID in the numericoid format.
990          * However, this routine is used by clients to parse the response
991          * from servers and very well known servers will provide an OID
992          * in the wrong format or even no OID at all.  We do our best to
993          * extract info from those servers.
994          */
995         parse_whsp(&ss);
996         savepos = ss;
997         at->at_oid = parse_numericoid(&ss,code);
998         if ( !at->at_oid ) {
999                 if ( be_liberal ) {
1000                         /* Backtracking */
1001                         ss = savepos;
1002                         kind = get_token(&ss,&sval);
1003                         if ( kind == TK_BAREWORD ) {
1004                                 if ( !strcmp(sval, "NAME") ||
1005                                      !strcmp(sval, "DESC") ||
1006                                      !strcmp(sval, "OBSOLETE") ||
1007                                      !strcmp(sval, "SUP") ||
1008                                      !strcmp(sval, "EQUALITY") ||
1009                                      !strcmp(sval, "ORDERING") ||
1010                                      !strcmp(sval, "SUBSTR") ||
1011                                      !strcmp(sval, "SYNTAX") ||
1012                                      !strcmp(sval, "SINGLE-VALUE") ||
1013                                      !strcmp(sval, "COLLECTIVE") ||
1014                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1015                                      !strcmp(sval, "USAGE") ||
1016                                      !strncmp(sval, "X-", 2) ) {
1017                                         /* Missing OID, backtrack */
1018                                         ss = savepos;
1019                                 } else {
1020                                         /* Non-numerical OID, ignore */
1021                                 }
1022                           }
1023                 } else {
1024                         *errp = ss;
1025                         ldap_attributetype_free(at);
1026                         return NULL;
1027                 }
1028         }
1029         parse_whsp(&ss);
1030
1031         /*
1032          * Beyond this point we will be liberal and accept the items
1033          * in any order.
1034          */
1035         while (1) {
1036                 kind = get_token(&ss,&sval);
1037                 switch (kind) {
1038                 case TK_EOS:
1039                         *code = LDAP_SCHERR_NORIGHTPAREN;
1040                         *errp = ss;
1041                         ldap_attributetype_free(at);
1042                         return NULL;
1043                 case TK_RIGHTPAREN:
1044                         return at;
1045                 case TK_BAREWORD:
1046                         if ( !strcmp(sval,"NAME") ) {
1047                                 if ( seen_name ) {
1048                                         *code = LDAP_SCHERR_DUPOPT;
1049                                         *errp = ss;
1050                                         ldap_attributetype_free(at);
1051                                         return(NULL);
1052                                 }
1053                                 seen_name = 1;
1054                                 at->at_names = parse_qdescrs(&ss,code);
1055                                 if ( !at->at_names ) {
1056                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1057                                                 *code = LDAP_SCHERR_BADNAME;
1058                                         *errp = ss;
1059                                         ldap_attributetype_free(at);
1060                                         return NULL;
1061                                 }
1062                         } else if ( !strcmp(sval,"DESC") ) {
1063                                 if ( seen_desc ) {
1064                                         *code = LDAP_SCHERR_DUPOPT;
1065                                         *errp = ss;
1066                                         ldap_attributetype_free(at);
1067                                         return(NULL);
1068                                 }
1069                                 seen_desc = 1;
1070                                 parse_whsp(&ss);
1071                                 kind = get_token(&ss,&sval);
1072                                 if ( kind != TK_QDSTRING ) {
1073                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1074                                         *errp = ss;
1075                                         ldap_attributetype_free(at);
1076                                         return NULL;
1077                                 }
1078                                 at->at_desc = sval;
1079                                 parse_whsp(&ss);
1080                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1081                                 if ( seen_obsolete ) {
1082                                         *code = LDAP_SCHERR_DUPOPT;
1083                                         *errp = ss;
1084                                         ldap_attributetype_free(at);
1085                                         return(NULL);
1086                                 }
1087                                 seen_obsolete = 1;
1088                                 at->at_obsolete = LDAP_SCHEMA_YES;
1089                                 parse_whsp(&ss);
1090                         } else if ( !strcmp(sval,"SUP") ) {
1091                                 if ( seen_sup ) {
1092                                         *code = LDAP_SCHERR_DUPOPT;
1093                                         *errp = ss;
1094                                         ldap_attributetype_free(at);
1095                                         return(NULL);
1096                                 }
1097                                 seen_sup = 1;
1098                                 at->at_sup_oid = parse_woid(&ss,code);
1099                                 if ( !at->at_sup_oid ) {
1100                                         *errp = ss;
1101                                         ldap_attributetype_free(at);
1102                                         return NULL;
1103                                 }
1104                         } else if ( !strcmp(sval,"EQUALITY") ) {
1105                                 if ( seen_equality ) {
1106                                         *code = LDAP_SCHERR_DUPOPT;
1107                                         *errp = ss;
1108                                         ldap_attributetype_free(at);
1109                                         return(NULL);
1110                                 }
1111                                 seen_equality = 1;
1112                                 at->at_equality_oid = parse_woid(&ss,code);
1113                                 if ( !at->at_equality_oid ) {
1114                                         *errp = ss;
1115                                         ldap_attributetype_free(at);
1116                                         return NULL;
1117                                 }
1118                         } else if ( !strcmp(sval,"ORDERING") ) {
1119                                 if ( seen_ordering ) {
1120                                         *code = LDAP_SCHERR_DUPOPT;
1121                                         *errp = ss;
1122                                         ldap_attributetype_free(at);
1123                                         return(NULL);
1124                                 }
1125                                 seen_ordering = 1;
1126                                 at->at_ordering_oid = parse_woid(&ss,code);
1127                                 if ( !at->at_ordering_oid ) {
1128                                         *errp = ss;
1129                                         ldap_attributetype_free(at);
1130                                         return NULL;
1131                                 }
1132                         } else if ( !strcmp(sval,"SUBSTR") ) {
1133                                 if ( seen_substr ) {
1134                                         *code = LDAP_SCHERR_DUPOPT;
1135                                         *errp = ss;
1136                                         ldap_attributetype_free(at);
1137                                         return(NULL);
1138                                 }
1139                                 seen_substr = 1;
1140                                 at->at_substr_oid = parse_woid(&ss,code);
1141                                 if ( !at->at_substr_oid ) {
1142                                         *errp = ss;
1143                                         ldap_attributetype_free(at);
1144                                         return NULL;
1145                                 }
1146                         } else if ( !strcmp(sval,"SYNTAX") ) {
1147                                 if ( seen_syntax ) {
1148                                         *code = LDAP_SCHERR_DUPOPT;
1149                                         *errp = ss;
1150                                         ldap_attributetype_free(at);
1151                                         return(NULL);
1152                                 }
1153                                 seen_syntax = 1;
1154                                 parse_whsp(&ss);
1155                                 at->at_syntax_oid = parse_noidlen(&ss,code,&at->at_syntax_len,be_liberal);
1156                                 if ( !at->at_syntax_oid ) {
1157                                         *errp = ss;
1158                                         ldap_attributetype_free(at);
1159                                         return NULL;
1160                                 }
1161                                 parse_whsp(&ss);
1162                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1163                                 if ( at->at_single_value ) {
1164                                         *code = LDAP_SCHERR_DUPOPT;
1165                                         *errp = ss;
1166                                         ldap_attributetype_free(at);
1167                                         return(NULL);
1168                                 }
1169                                 at->at_single_value = LDAP_SCHEMA_YES;
1170                                 parse_whsp(&ss);
1171                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1172                                 if ( at->at_collective ) {
1173                                         *code = LDAP_SCHERR_DUPOPT;
1174                                         *errp = ss;
1175                                         ldap_attributetype_free(at);
1176                                         return(NULL);
1177                                 }
1178                                 at->at_collective = LDAP_SCHEMA_YES;
1179                                 parse_whsp(&ss);
1180                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1181                                 if ( at->at_no_user_mod ) {
1182                                         *code = LDAP_SCHERR_DUPOPT;
1183                                         *errp = ss;
1184                                         ldap_attributetype_free(at);
1185                                         return(NULL);
1186                                 }
1187                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1188                                 parse_whsp(&ss);
1189                         } else if ( !strcmp(sval,"USAGE") ) {
1190                                 if ( seen_usage ) {
1191                                         *code = LDAP_SCHERR_DUPOPT;
1192                                         *errp = ss;
1193                                         ldap_attributetype_free(at);
1194                                         return(NULL);
1195                                 }
1196                                 seen_usage = 1;
1197                                 parse_whsp(&ss);
1198                                 kind = get_token(&ss,&sval);
1199                                 if ( kind != TK_BAREWORD ) {
1200                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1201                                         *errp = ss;
1202                                         ldap_attributetype_free(at);
1203                                         return NULL;
1204                                 }
1205                                 if ( !strcasecmp(sval,"userApplications") )
1206                                         at->at_usage =
1207                                             LDAP_SCHEMA_USER_APPLICATIONS;
1208                                 else if ( !strcasecmp(sval,"directoryOperation") )
1209                                         at->at_usage =
1210                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1211                                 else if ( !strcasecmp(sval,"distributedOperation") )
1212                                         at->at_usage =
1213                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1214                                 else if ( !strcasecmp(sval,"dSAOperation") )
1215                                         at->at_usage =
1216                                             LDAP_SCHEMA_DSA_OPERATION;
1217                                 else {
1218                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1219                                         *errp = ss;
1220                                         ldap_attributetype_free(at);
1221                                         return NULL;
1222                                 }
1223                                 parse_whsp(&ss);
1224                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1225                                 /* Should be parse_qdstrings */
1226                                 ssdummy = parse_qdescrs(&ss, code);
1227                                 if ( !ssdummy ) {
1228                                         *errp = ss;
1229                                         ldap_attributetype_free(at);
1230                                         return NULL;
1231                                 }
1232                         } else {
1233                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1234                                 *errp = ss;
1235                                 ldap_attributetype_free(at);
1236                                 return NULL;
1237                         }
1238                         break;
1239                 default:
1240                         *code = LDAP_SCHERR_UNEXPTOKEN;
1241                         *errp = ss;
1242                         ldap_attributetype_free(at);
1243                         return NULL;
1244                 }
1245         }
1246 }
1247
1248 void
1249 ldap_objectclass_free(LDAP_OBJECT_CLASS * oc)
1250 {
1251         LDAP_FREE(oc->oc_oid);
1252         LDAP_VFREE(oc->oc_names);
1253         LDAP_FREE(oc->oc_desc);
1254         LDAP_VFREE(oc->oc_sup_oids);
1255         LDAP_VFREE(oc->oc_at_oids_must);
1256         LDAP_VFREE(oc->oc_at_oids_may);
1257         LDAP_FREE(oc);
1258 }
1259
1260 LDAP_OBJECT_CLASS *
1261 ldap_str2objectclass( char * s, int * code, char ** errp )
1262 {
1263         int kind;
1264         char * ss = s;
1265         char * sval;
1266         int be_liberal = 1;     /* Future additional argument */
1267         int seen_name = 0;
1268         int seen_desc = 0;
1269         int seen_obsolete = 0;
1270         int seen_sup = 0;
1271         int seen_kind = 0;
1272         int seen_must = 0;
1273         int seen_may = 0;
1274         LDAP_OBJECT_CLASS * oc;
1275         char ** ssdummy;
1276         char * savepos;
1277
1278         if ( !s ) {
1279                 *code = LDAP_SCHERR_EMPTY;
1280                 *errp = "";
1281                 return NULL;
1282         }
1283
1284         *errp = s;
1285         oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1286
1287         if ( !oc ) {
1288                 *code = LDAP_SCHERR_OUTOFMEM;
1289                 return NULL;
1290         }
1291
1292         kind = get_token(&ss,&sval);
1293         if ( kind != TK_LEFTPAREN ) {
1294                 *code = LDAP_SCHERR_NOLEFTPAREN;
1295                 ldap_objectclass_free(oc);
1296                 return NULL;
1297         }
1298
1299         /*
1300          * Definitions MUST begin with an OID in the numericoid format.
1301          * However, this routine is used by clients to parse the response
1302          * from servers and very well known servers will provide an OID
1303          * in the wrong format or even no OID at all.  We do our best to
1304          * extract info from those servers.
1305          */
1306         parse_whsp(&ss);
1307         savepos = ss;
1308         oc->oc_oid = parse_numericoid(&ss,code);
1309         if ( !oc->oc_oid ) {
1310                 if ( be_liberal ) {
1311                         /* Backtracking */
1312                         ss = savepos;
1313                         kind = get_token(&ss,&sval);
1314                         if ( kind == TK_BAREWORD ) {
1315                                 if ( !strcmp(sval, "NAME") ||
1316                                      !strcmp(sval, "DESC") ||
1317                                      !strcmp(sval, "OBSOLETE") ||
1318                                      !strcmp(sval, "SUP") ||
1319                                      !strcmp(sval, "ABSTRACT") ||
1320                                      !strcmp(sval, "STRUCTURAL") ||
1321                                      !strcmp(sval, "AUXILIARY") ||
1322                                      !strcmp(sval, "MUST") ||
1323                                      !strncmp(sval, "X-", 2) ) {
1324                                         /* Missing OID, backtrack */
1325                                         ss = savepos;
1326                                 } else {
1327                                         /* Non-numerical OID, ignore */
1328                                 }
1329                           }
1330                 } else {
1331                         *errp = ss;
1332                         ldap_objectclass_free(oc);
1333                         return NULL;
1334                 }
1335         }
1336         parse_whsp(&ss);
1337
1338         /*
1339          * Beyond this point we will be liberal an accept the items
1340          * in any order.
1341          */
1342         while (1) {
1343                 kind = get_token(&ss,&sval);
1344                 switch (kind) {
1345                 case TK_EOS:
1346                         *code = LDAP_SCHERR_NORIGHTPAREN;
1347                         *errp = ss;
1348                         ldap_objectclass_free(oc);
1349                         return NULL;
1350                 case TK_RIGHTPAREN:
1351                         return oc;
1352                 case TK_BAREWORD:
1353                         if ( !strcmp(sval,"NAME") ) {
1354                                 if ( seen_name ) {
1355                                         *code = LDAP_SCHERR_DUPOPT;
1356                                         *errp = ss;
1357                                         ldap_objectclass_free(oc);
1358                                         return(NULL);
1359                                 }
1360                                 seen_name = 1;
1361                                 oc->oc_names = parse_qdescrs(&ss,code);
1362                                 if ( !oc->oc_names ) {
1363                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1364                                                 *code = LDAP_SCHERR_BADNAME;
1365                                         *errp = ss;
1366                                         ldap_objectclass_free(oc);
1367                                         return NULL;
1368                                 }
1369                         } else if ( !strcmp(sval,"DESC") ) {
1370                                 if ( seen_desc ) {
1371                                         *code = LDAP_SCHERR_DUPOPT;
1372                                         *errp = ss;
1373                                         ldap_objectclass_free(oc);
1374                                         return(NULL);
1375                                 }
1376                                 seen_desc = 1;
1377                                 parse_whsp(&ss);
1378                                 kind = get_token(&ss,&sval);
1379                                 if ( kind != TK_QDSTRING ) {
1380                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1381                                         *errp = ss;
1382                                         ldap_objectclass_free(oc);
1383                                         return NULL;
1384                                 }
1385                                 oc->oc_desc = sval;
1386                                 parse_whsp(&ss);
1387                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1388                                 if ( seen_obsolete ) {
1389                                         *code = LDAP_SCHERR_DUPOPT;
1390                                         *errp = ss;
1391                                         ldap_objectclass_free(oc);
1392                                         return(NULL);
1393                                 }
1394                                 seen_obsolete = 1;
1395                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1396                                 parse_whsp(&ss);
1397                         } else if ( !strcmp(sval,"SUP") ) {
1398                                 if ( seen_sup ) {
1399                                         *code = LDAP_SCHERR_DUPOPT;
1400                                         *errp = ss;
1401                                         ldap_objectclass_free(oc);
1402                                         return(NULL);
1403                                 }
1404                                 seen_sup = 1;
1405                                 oc->oc_sup_oids = parse_oids(&ss,code,be_liberal);
1406                                 if ( !oc->oc_sup_oids ) {
1407                                         *errp = ss;
1408                                         ldap_objectclass_free(oc);
1409                                         return NULL;
1410                                 }
1411                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1412                                 if ( seen_kind ) {
1413                                         *code = LDAP_SCHERR_DUPOPT;
1414                                         *errp = ss;
1415                                         ldap_objectclass_free(oc);
1416                                         return(NULL);
1417                                 }
1418                                 seen_kind = 1;
1419                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1420                                 parse_whsp(&ss);
1421                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1422                                 if ( seen_kind ) {
1423                                         *code = LDAP_SCHERR_DUPOPT;
1424                                         *errp = ss;
1425                                         ldap_objectclass_free(oc);
1426                                         return(NULL);
1427                                 }
1428                                 seen_kind = 1;
1429                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1430                                 parse_whsp(&ss);
1431                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1432                                 if ( seen_kind ) {
1433                                         *code = LDAP_SCHERR_DUPOPT;
1434                                         *errp = ss;
1435                                         ldap_objectclass_free(oc);
1436                                         return(NULL);
1437                                 }
1438                                 seen_kind = 1;
1439                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1440                                 parse_whsp(&ss);
1441                         } else if ( !strcmp(sval,"MUST") ) {
1442                                 if ( seen_must ) {
1443                                         *code = LDAP_SCHERR_DUPOPT;
1444                                         *errp = ss;
1445                                         ldap_objectclass_free(oc);
1446                                         return(NULL);
1447                                 }
1448                                 seen_must = 1;
1449                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
1450                                 if ( !oc->oc_at_oids_must ) {
1451                                         *errp = ss;
1452                                         ldap_objectclass_free(oc);
1453                                         return NULL;
1454                                 }
1455                                 parse_whsp(&ss);
1456                         } else if ( !strcmp(sval,"MAY") ) {
1457                                 if ( seen_may ) {
1458                                         *code = LDAP_SCHERR_DUPOPT;
1459                                         *errp = ss;
1460                                         ldap_objectclass_free(oc);
1461                                         return(NULL);
1462                                 }
1463                                 seen_may = 1;
1464                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
1465                                 if ( !oc->oc_at_oids_may ) {
1466                                         *errp = ss;
1467                                         ldap_objectclass_free(oc);
1468                                         return NULL;
1469                                 }
1470                                 parse_whsp(&ss);
1471                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1472                                 /* Should be parse_qdstrings */
1473                                 ssdummy = parse_qdescrs(&ss, code);
1474                                 if ( !ssdummy ) {
1475                                         *errp = ss;
1476                                         ldap_objectclass_free(oc);
1477                                         return NULL;
1478                                 }
1479                         } else {
1480                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1481                                 *errp = ss;
1482                                 ldap_objectclass_free(oc);
1483                                 return NULL;
1484                         }
1485                         break;
1486                 default:
1487                         *code = LDAP_SCHERR_UNEXPTOKEN;
1488                         *errp = ss;
1489                         ldap_objectclass_free(oc);
1490                         return NULL;
1491                 }
1492         }
1493 }
1494
1495 static char *err2text[] = {
1496         "",
1497         "Out of memory",
1498         "Unexpected token",
1499         "Missing opening parenthesis",
1500         "Missing closing parenthesis",
1501         "Expecting digit",
1502         "Expecting a name",
1503         "Bad description",
1504         "Bad superiors",
1505         "Duplicate option",
1506         "Unexpected end of data"
1507 };
1508
1509 char *
1510 ldap_scherr2str(int code)
1511 {
1512         if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1513                 return "Unknown error";
1514         } else {
1515                 return err2text[code];
1516         }
1517 }