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