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