]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
50fd9c4d391deca4b78c33e5a6bda0e28e86866b
[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_syntax2str( LDAP_SYNTAX * syn )
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, syn->syn_oid);
246         print_whsp(ss);
247
248         if ( syn->syn_desc ) {
249                 print_literal(ss,"DESC");
250                 print_qdstring(ss,syn->syn_desc);
251         }
252
253         print_whsp(ss);
254         print_literal(ss,")");
255
256         retstring = strdup(safe_string_val(ss));
257         safe_string_free(ss);
258         return(retstring);
259 }
260
261 char *
262 ldap_objectclass2str( LDAP_OBJECT_CLASS * oc )
263 {
264         safe_string * ss;
265         char * retstring;
266         
267         ss = new_safe_string(256);
268         if ( !ss )
269                 return NULL;
270
271         print_literal(ss,"(");
272         print_whsp(ss);
273
274         print_numericoid(ss, oc->oc_oid);
275         print_whsp(ss);
276
277         if ( oc->oc_names ) {
278                 print_literal(ss,"NAME");
279                 print_qdescrs(ss,oc->oc_names);
280         }
281
282         if ( oc->oc_desc ) {
283                 print_literal(ss,"DESC");
284                 print_qdstring(ss,oc->oc_desc);
285         }
286
287         if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) {
288                 print_literal(ss, "OBSOLETE");
289                 print_whsp(ss);
290         }
291
292         if ( oc->oc_sup_oids ) {
293                 print_literal(ss,"SUP");
294                 print_oids(ss,oc->oc_sup_oids);
295         }
296
297         switch (oc->oc_kind) {
298         case LDAP_SCHEMA_ABSTRACT:
299                 print_literal(ss,"ABSTRACT");
300                 break;
301         case LDAP_SCHEMA_STRUCTURAL:
302                 print_literal(ss,"STRUCTURAL");
303                 break;
304         case LDAP_SCHEMA_AUXILIARY:
305                 print_literal(ss,"AUXILIARY");
306                 break;
307         default:
308                 print_literal(ss,"KIND-UNKNOWN");
309                 break;
310         }
311         print_whsp(ss);
312         
313         if ( oc->oc_at_oids_must ) {
314                 print_literal(ss,"MUST");
315                 print_whsp(ss);
316                 print_oids(ss,oc->oc_at_oids_must);
317                 print_whsp(ss);
318         }
319
320         if ( oc->oc_at_oids_may ) {
321                 print_literal(ss,"MAY");
322                 print_whsp(ss);
323                 print_oids(ss,oc->oc_at_oids_may);
324                 print_whsp(ss);
325         }
326
327         print_whsp(ss);
328         print_literal(ss,")");
329
330         retstring = strdup(safe_string_val(ss));
331         safe_string_free(ss);
332         return(retstring);
333 }
334
335 char *
336 ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at )
337 {
338         safe_string * ss;
339         char * retstring;
340         
341         ss = new_safe_string(256);
342         if ( !ss )
343                 return NULL;
344
345         print_literal(ss,"(");
346         print_whsp(ss);
347
348         print_numericoid(ss, at->at_oid);
349         print_whsp(ss);
350
351         if ( at->at_names ) {
352                 print_literal(ss,"NAME");
353                 print_qdescrs(ss,at->at_names);
354         }
355
356         if ( at->at_desc ) {
357                 print_literal(ss,"DESC");
358                 print_qdstring(ss,at->at_desc);
359         }
360
361         if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
362                 print_literal(ss, "OBSOLETE");
363                 print_whsp(ss);
364         }
365
366         if ( at->at_sup_oid ) {
367                 print_literal(ss,"SUP");
368                 print_woid(ss,at->at_sup_oid);
369         }
370
371         if ( at->at_equality_oid ) {
372                 print_literal(ss,"EQUALITY");
373                 print_woid(ss,at->at_equality_oid);
374         }
375
376         if ( at->at_ordering_oid ) {
377                 print_literal(ss,"ORDERING");
378                 print_woid(ss,at->at_ordering_oid);
379         }
380
381         if ( at->at_substr_oid ) {
382                 print_literal(ss,"SUBSTR");
383                 print_woid(ss,at->at_substr_oid);
384         }
385
386         if ( at->at_syntax_oid ) {
387                 print_literal(ss,"SYNTAX");
388                 print_whsp(ss);
389                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
390         }
391
392         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
393                 print_literal(ss,"SINGLE-VALUE");
394                 print_whsp(ss);
395         }
396
397         if ( at->at_collective == LDAP_SCHEMA_YES ) {
398                 print_literal(ss,"COLLECTIVE");
399                 print_whsp(ss);
400         }
401
402         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
403                 print_literal(ss,"NO-USER-MODIFICATION");
404                 print_whsp(ss);
405         }
406
407         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
408                 print_literal(ss,"USAGE");
409                 print_whsp(ss);
410                 switch (at->at_usage) {
411                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
412                         print_literal(ss,"directoryOperation");
413                         break;
414                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
415                         print_literal(ss,"distributedOperation");
416                         break;
417                 case LDAP_SCHEMA_DSA_OPERATION:
418                         print_literal(ss,"dSAOperation");
419                         break;
420                 default:
421                         print_literal(ss,"UNKNOWN");
422                         break;
423                 }
424         }
425         
426         print_whsp(ss);
427         print_literal(ss,")");
428
429         retstring = strdup(safe_string_val(ss));
430         safe_string_free(ss);
431         return(retstring);
432 }
433
434 /*
435  * This is ripped from servers/slapd/charray.c that should be promoted
436  * to -lldap or something so that it is used everywhere.
437  */
438 static void
439 charray_free( char **array )
440 {
441         char    **a;
442
443         if ( array == NULL ) {
444                 return;
445         }
446
447         for ( a = array; *a != NULL; a++ ) {
448                 if ( *a != NULL ) {
449                         LDAP_FREE( *a );
450                 }
451         }
452         LDAP_FREE( (char *) array );
453 }
454
455 /*
456  * Now come the parsers.  There is one parser for each entity type:
457  * objectclasses, attributetypes, etc.
458  *
459  * Each of them is written as a recursive-descent parser, except that
460  * none of them is really recursive.  But the idea is kept: there
461  * is one routine per non-terminal that eithers gobbles lexical tokens
462  * or calls lower-level routines, etc.
463  *
464  * The scanner is implemented in the routine get_token.  Actually,
465  * get_token is more than a scanner and will return tokens that are
466  * in fact non-terminals in the grammar.  So you can see the whole
467  * approach as the combination of a low-level bottom-up recognizer
468  * combined with a scanner and a number of top-down parsers.  Or just
469  * consider that the real grammars recognized by the parsers are not
470  * those of the standards.  As a matter of fact, our parsers are more
471  * liberal than the spec when there is no ambiguity.
472  *
473  * The difference is pretty academic (modulo bugs or incorrect
474  * interpretation of the specs).
475  */
476
477 #define TK_NOENDQUOTE   -2
478 #define TK_OUTOFMEM     -1
479 #define TK_EOS          0
480 #define TK_UNEXPCHAR    1
481 #define TK_BAREWORD     2
482 #define TK_QDSTRING     3
483 #define TK_LEFTPAREN    4
484 #define TK_RIGHTPAREN   5
485 #define TK_DOLLAR       6
486 #define TK_QDESCR       TK_QDSTRING
487
488 struct token {
489         int type;
490         char *sval;
491 };
492
493 static int
494 get_token(char ** sp, char ** token_val)
495 {
496         int kind;
497         char * p;
498         char * q;
499         char * res;
500
501         switch (**sp) {
502         case '\0':
503                 kind = TK_EOS;
504                 (*sp)++;
505                 break;
506         case '(':
507                 kind = TK_LEFTPAREN;
508                 (*sp)++;
509                 break;
510         case ')':
511                 kind = TK_RIGHTPAREN;
512                 (*sp)++;
513                 break;
514         case '$':
515                 kind = TK_DOLLAR;
516                 (*sp)++;
517                 break;
518         case '\'':
519                 kind = TK_QDSTRING;
520                 (*sp)++;
521                 p = *sp;
522                 while ( **sp != '\'' && **sp != '\0' )
523                         (*sp)++;
524                 if ( **sp == '\'' ) {
525                         q = *sp;
526                         res = LDAP_MALLOC(q-p+1);
527                         if ( !res ) {
528                                 kind = TK_OUTOFMEM;
529                         } else {
530                                 strncpy(res,p,q-p);
531                                 res[q-p] = '\0';
532                                 *token_val = res;
533                         }
534                         (*sp)++;
535                 } else {
536                         kind = TK_NOENDQUOTE;
537                 }
538                 break;
539         default:
540                 kind = TK_BAREWORD;
541                 p = *sp;
542                 while ( !isspace(**sp) && **sp != '\0' )
543                         (*sp)++;
544                 q = *sp;
545                 res = LDAP_MALLOC(q-p+1);
546                 if ( !res ) {
547                         kind = TK_OUTOFMEM;
548                 } else {
549                         strncpy(res,p,q-p);
550                         res[q-p] = '\0';
551                         *token_val = res;
552                 }
553                 break;
554 /*              kind = TK_UNEXPCHAR; */
555 /*              break; */
556         }
557         
558         return kind;
559 }
560
561 /* Gobble optional whitespace */
562 static void
563 parse_whsp(char **sp)
564 {
565         while (isspace(**sp))
566                 (*sp)++;
567 }
568
569 /* TBC:!!
570  * General note for all parsers: to guarantee the algorithm halts they
571  * must always advance the pointer even when an error is found.  For
572  * this one is not that important since an error here is fatal at the
573  * upper layers, but it is a simple strategy that will not get in
574  * endless loops.
575  */
576
577 /* Parse a sequence of dot-separated decimal strings */
578 static char *
579 parse_numericoid(char **sp, int *code)
580 {
581         char * res;
582         char * start = *sp;
583         int len;
584
585         /* Each iteration of this loops gets one decimal string */
586         while (**sp) {
587                 if ( !isdigit(**sp) ) {
588                         /*
589                          * Initial char is not a digit or char after dot is
590                          * not a digit
591                          */
592                         *code = LDAP_SCHERR_NODIGIT;
593                         return NULL;
594                 }
595                 (*sp)++;
596                 while ( isdigit(**sp) )
597                         (*sp)++;
598                 if ( **sp != '.' )
599                         break;
600                 /* Otherwise, gobble the dot and loop again */
601                 (*sp)++;
602         }
603         /* Now *sp points at the char past the numericoid. Perfect. */
604         len = *sp - start;
605         res = LDAP_MALLOC(len+1);
606         if (!res) {
607           *code = LDAP_SCHERR_OUTOFMEM;
608           return(NULL);
609         }
610         strncpy(res,start,len);
611         res[len] = '\0';
612         return(res);
613 }
614
615 /* Parse a qdescr or a list of them enclosed in () */
616 static char **
617 parse_qdescrs(char **sp, int *code)
618 {
619         char ** res;
620         char ** res1;
621         int kind;
622         char * sval;
623         int size;
624         int pos;
625
626         parse_whsp(sp);
627         kind = get_token(sp,&sval);
628         if ( kind == TK_LEFTPAREN ) {
629                 /* Let's presume there will be at least 2 entries */
630                 size = 3;
631                 res = LDAP_CALLOC(3,sizeof(char *));
632                 if ( !res ) {
633                         *code = LDAP_SCHERR_OUTOFMEM;
634                         return NULL;
635                 }
636                 pos = 0;
637                 while (1) {
638                         parse_whsp(sp);
639                         kind = get_token(sp,&sval);
640                         if ( kind == TK_RIGHTPAREN )
641                                 break;
642                         if ( kind == TK_QDESCR ) {
643                                 if ( pos == size-2 ) {
644                                         size++;
645                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
646                                         if ( !res1 ) {
647                                                 charray_free(res);
648                                                 *code = LDAP_SCHERR_OUTOFMEM;
649                                                 return(NULL);
650                                         }
651                                         res = res1;
652                                 }
653                                 res[pos] = sval;
654                                 pos++;
655                                 parse_whsp(sp);
656                         } else {
657                                 charray_free(res);
658                                 *code = LDAP_SCHERR_UNEXPTOKEN;
659                                 return(NULL);
660                         }
661                 }
662                 res[pos] = NULL;
663                 parse_whsp(sp);
664                 return(res);
665         } else if ( kind == TK_QDESCR ) {
666                 res = LDAP_CALLOC(2,sizeof(char *));
667                 if ( !res ) {
668                         *code = LDAP_SCHERR_OUTOFMEM;
669                         return NULL;
670                 }
671                 res[0] = sval;
672                 res[1] = NULL;
673                 parse_whsp(sp);
674                 return res;
675         } else {
676                 *code = LDAP_SCHERR_BADNAME;
677                 return NULL;
678         }
679 }
680
681 /* Parse a woid */
682 static char *
683 parse_woid(char **sp, int *code)
684 {
685         char * sval;
686         int kind;
687
688         parse_whsp(sp);
689         kind = get_token(sp, &sval);
690         if ( kind != TK_BAREWORD ) {
691                 *code = LDAP_SCHERR_UNEXPTOKEN;
692                 return NULL;
693         }
694         parse_whsp(sp);
695         return sval;
696 }
697
698 /* Parse a noidlen */
699 static char *
700 parse_noidlen(char **sp, int *code, int *len)
701 {
702         char * sval;
703         int kind;
704
705         *len = 0;
706         kind = get_token(sp, &sval);
707         if ( kind != TK_BAREWORD ) {
708                 *code = LDAP_SCHERR_UNEXPTOKEN;
709                 return NULL;
710         }
711         if ( **sp == '{' ) {
712                 (*sp)++;
713                 *len = atoi(*sp);
714                 while ( isdigit(**sp) )
715                         (*sp)++;
716                 (*sp)++;
717                 if ( **sp != '}' ) {
718                         *code = LDAP_SCHERR_UNEXPTOKEN;
719                         ldap_memfree(sval);
720                         return NULL;
721                 }
722                 (*sp)++;
723         }               
724         return sval;
725 }
726
727 /*
728  * Next routine will accept a qdstring in place of an oid.  This is
729  * necessary to interoperate with Netscape Directory server that
730  * will improperly quote each oid (at least those of the descr kind)
731  * in the SUP clause.
732  */
733
734 /* Parse a woid or a $-separated list of them enclosed in () */
735 static char **
736 parse_oids(char **sp, int *code)
737 {
738         char ** res;
739         char ** res1;
740         int kind;
741         char * sval;
742         int size;
743         int pos;
744
745         /*
746          * Strictly speaking, doing this here accepts whsp before the
747          * ( at the begining of an oidlist, but his is harmless.  Also,
748          * we are very liberal in what we accept as an OID.  Maybe
749          * refine later.
750          */
751         parse_whsp(sp);
752         kind = get_token(sp,&sval);
753         if ( kind == TK_LEFTPAREN ) {
754                 /* Let's presume there will be at least 2 entries */
755                 size = 3;
756                 res = LDAP_CALLOC(3,sizeof(char *));
757                 if ( !res ) {
758                         *code = LDAP_SCHERR_OUTOFMEM;
759                         return NULL;
760                 }
761                 pos = 0;
762                 parse_whsp(sp);
763                 kind = get_token(sp,&sval);
764                 if ( kind == TK_BAREWORD || kind == TK_QDSTRING ) {
765                         res[pos] = sval;
766                         pos++;
767                 } else {
768                         *code = LDAP_SCHERR_UNEXPTOKEN;
769                         charray_free(res);
770                         return NULL;
771                 }
772                 parse_whsp(sp);
773                 while (1) {
774                         kind = get_token(sp,&sval);
775                         if ( kind == TK_RIGHTPAREN )
776                                 break;
777                         if ( kind == TK_DOLLAR ) {
778                                 parse_whsp(sp);
779                                 kind = get_token(sp,&sval);
780                                 if ( kind == TK_BAREWORD ||
781                                      kind == TK_QDSTRING ) {
782                                         if ( pos == size-2 ) {
783                                                 size++;
784                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
785                                                 if ( !res1 ) {
786                                                   charray_free(res);
787                                                   *code = LDAP_SCHERR_OUTOFMEM;
788                                                   return(NULL);
789                                                 }
790                                                 res = res1;
791                                         }
792                                         res[pos] = sval;
793                                         pos++;
794                                 } else {
795                                         *code = LDAP_SCHERR_UNEXPTOKEN;
796                                         charray_free(res);
797                                         return NULL;
798                                 }
799                                 parse_whsp(sp);
800                         } else {
801                                 *code = LDAP_SCHERR_UNEXPTOKEN;
802                                 charray_free(res);
803                                 return NULL;
804                         }
805                 }
806                 res[pos] = NULL;
807                 parse_whsp(sp);
808                 return(res);
809         } else if ( kind == TK_BAREWORD || kind == TK_QDSTRING ) {
810                 res = LDAP_CALLOC(2,sizeof(char *));
811                 if ( !res ) {
812                         *code = LDAP_SCHERR_OUTOFMEM;
813                         return NULL;
814                 }
815                 res[0] = sval;
816                 res[1] = NULL;
817                 parse_whsp(sp);
818                 return res;
819         } else {
820                 *code = LDAP_SCHERR_BADNAME;
821                 return NULL;
822         }
823 }
824
825 static void
826 free_syn(LDAP_SYNTAX * syn)
827 {
828         ldap_memfree(syn->syn_oid);
829         ldap_memfree(syn->syn_desc);
830         ldap_memfree(syn);
831 }
832
833 LDAP_SYNTAX *
834 ldap_str2syntax( char * s, int * code, char ** errp )
835 {
836         int kind;
837         char * ss = s;
838         char * sval;
839         int seen_desc = 0;
840         LDAP_SYNTAX * syn;
841         char ** ssdummy;
842
843         if ( !s ) {
844                 *code = LDAP_SCHERR_EMPTY;
845                 *errp = "";
846                 return NULL;
847         }
848
849         *errp = s;
850         syn = LDAP_CALLOC(1,sizeof(LDAP_SYNTAX));
851
852         if ( !syn ) {
853                 *code = LDAP_SCHERR_OUTOFMEM;
854                 return NULL;
855         }
856
857         kind = get_token(&ss,&sval);
858         if ( kind != TK_LEFTPAREN ) {
859                 *code = LDAP_SCHERR_NOLEFTPAREN;
860                 free_syn(syn);
861                 return NULL;
862         }
863
864         parse_whsp(&ss);
865         syn->syn_oid = parse_numericoid(&ss,code);
866         if ( !syn->syn_oid ) {
867                 *errp = ss;
868                 free_syn(syn);
869                 return NULL;
870         }
871         parse_whsp(&ss);
872
873         /*
874          * Beyond this point we will be liberal and accept the items
875          * in any order.
876          */
877         while (1) {
878                 kind = get_token(&ss,&sval);
879                 switch (kind) {
880                 case TK_EOS:
881                         *code = LDAP_SCHERR_NORIGHTPAREN;
882                         *errp = ss;
883                         free_syn(syn);
884                         return NULL;
885                 case TK_RIGHTPAREN:
886                         return syn;
887                 case TK_BAREWORD:
888                         if ( !strcmp(sval,"DESC") ) {
889                                 if ( seen_desc ) {
890                                         *code = LDAP_SCHERR_DUPOPT;
891                                         *errp = ss;
892                                         free_syn(syn);
893                                         return(NULL);
894                                 }
895                                 seen_desc = 1;
896                                 parse_whsp(&ss);
897                                 kind = get_token(&ss,&sval);
898                                 if ( kind != TK_QDSTRING ) {
899                                         *code = LDAP_SCHERR_UNEXPTOKEN;
900                                         *errp = ss;
901                                         free_syn(syn);
902                                         return NULL;
903                                 }
904                                 syn->syn_desc = sval;
905                                 parse_whsp(&ss);
906                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
907                                 /* Should be parse_qdstrings */
908                                 ssdummy = parse_qdescrs(&ss, code);
909                                 if ( !ssdummy ) {
910                                         *errp = ss;
911                                         free_syn(syn);
912                                         return NULL;
913                                 }
914                         } else {
915                                 *code = LDAP_SCHERR_UNEXPTOKEN;
916                                 *errp = ss;
917                                 free_syn(syn);
918                                 return NULL;
919                         }
920                         break;
921                 default:
922                         *code = LDAP_SCHERR_UNEXPTOKEN;
923                         *errp = ss;
924                         free_syn(syn);
925                         return NULL;
926                 }
927         }
928 }
929
930 static void
931 free_at(LDAP_ATTRIBUTE_TYPE * at)
932 {
933         ldap_memfree(at->at_oid);
934         charray_free(at->at_names);
935         ldap_memfree(at->at_desc);
936         ldap_memfree(at->at_sup_oid);
937         ldap_memfree(at->at_equality_oid);
938         ldap_memfree(at->at_ordering_oid);
939         ldap_memfree(at->at_substr_oid);
940         ldap_memfree(at->at_syntax_oid);
941         ldap_memfree(at);
942 }
943
944 LDAP_ATTRIBUTE_TYPE *
945 ldap_str2attributetype( char * s, int * code, char ** errp )
946 {
947         int kind;
948         char * ss = s;
949         char * sval;
950         int seen_name = 0;
951         int seen_desc = 0;
952         int seen_obsolete = 0;
953         int seen_sup = 0;
954         int seen_equality = 0;
955         int seen_ordering = 0;
956         int seen_substr = 0;
957         int seen_syntax = 0;
958         int seen_usage = 0;
959         int seen_kind = 0;
960         int seen_must = 0;
961         int seen_may = 0;
962         LDAP_ATTRIBUTE_TYPE * at;
963         char ** ssdummy;
964
965         if ( !s ) {
966                 *code = LDAP_SCHERR_EMPTY;
967                 *errp = "";
968                 return NULL;
969         }
970
971         *errp = s;
972         at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
973
974         if ( !at ) {
975                 *code = LDAP_SCHERR_OUTOFMEM;
976                 return NULL;
977         }
978
979         kind = get_token(&ss,&sval);
980         if ( kind != TK_LEFTPAREN ) {
981                 *code = LDAP_SCHERR_NOLEFTPAREN;
982                 free_at(at);
983                 return NULL;
984         }
985
986         parse_whsp(&ss);
987         at->at_oid = parse_numericoid(&ss,code);
988         if ( !at->at_oid ) {
989                 *errp = ss;
990                 free_at(at);
991                 return NULL;
992         }
993         parse_whsp(&ss);
994
995         /*
996          * Beyond this point we will be liberal and accept the items
997          * in any order.
998          */
999         while (1) {
1000                 kind = get_token(&ss,&sval);
1001                 switch (kind) {
1002                 case TK_EOS:
1003                         *code = LDAP_SCHERR_NORIGHTPAREN;
1004                         *errp = ss;
1005                         free_at(at);
1006                         return NULL;
1007                 case TK_RIGHTPAREN:
1008                         return at;
1009                 case TK_BAREWORD:
1010                         if ( !strcmp(sval,"NAME") ) {
1011                                 if ( seen_name ) {
1012                                         *code = LDAP_SCHERR_DUPOPT;
1013                                         *errp = ss;
1014                                         free_at(at);
1015                                         return(NULL);
1016                                 }
1017                                 seen_name = 1;
1018                                 at->at_names = parse_qdescrs(&ss,code);
1019                                 if ( !at->at_names ) {
1020                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1021                                                 *code = LDAP_SCHERR_BADNAME;
1022                                         *errp = ss;
1023                                         free_at(at);
1024                                         return NULL;
1025                                 }
1026                         } else if ( !strcmp(sval,"DESC") ) {
1027                                 if ( seen_desc ) {
1028                                         *code = LDAP_SCHERR_DUPOPT;
1029                                         *errp = ss;
1030                                         free_at(at);
1031                                         return(NULL);
1032                                 }
1033                                 seen_desc = 1;
1034                                 parse_whsp(&ss);
1035                                 kind = get_token(&ss,&sval);
1036                                 if ( kind != TK_QDSTRING ) {
1037                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1038                                         *errp = ss;
1039                                         free_at(at);
1040                                         return NULL;
1041                                 }
1042                                 at->at_desc = sval;
1043                                 parse_whsp(&ss);
1044                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1045                                 if ( seen_obsolete ) {
1046                                         *code = LDAP_SCHERR_DUPOPT;
1047                                         *errp = ss;
1048                                         free_at(at);
1049                                         return(NULL);
1050                                 }
1051                                 seen_obsolete = 1;
1052                                 at->at_obsolete = LDAP_SCHEMA_YES;
1053                                 parse_whsp(&ss);
1054                         } else if ( !strcmp(sval,"SUP") ) {
1055                                 if ( seen_sup ) {
1056                                         *code = LDAP_SCHERR_DUPOPT;
1057                                         *errp = ss;
1058                                         free_at(at);
1059                                         return(NULL);
1060                                 }
1061                                 seen_sup = 1;
1062                                 at->at_sup_oid = parse_woid(&ss,code);
1063                                 if ( !at->at_sup_oid ) {
1064                                         *errp = ss;
1065                                         free_at(at);
1066                                         return NULL;
1067                                 }
1068                         } else if ( !strcmp(sval,"EQUALITY") ) {
1069                                 if ( seen_equality ) {
1070                                         *code = LDAP_SCHERR_DUPOPT;
1071                                         *errp = ss;
1072                                         free_at(at);
1073                                         return(NULL);
1074                                 }
1075                                 seen_equality = 1;
1076                                 at->at_equality_oid = parse_woid(&ss,code);
1077                                 if ( !at->at_equality_oid ) {
1078                                         *errp = ss;
1079                                         free_at(at);
1080                                         return NULL;
1081                                 }
1082                         } else if ( !strcmp(sval,"ORDERING") ) {
1083                                 if ( seen_ordering ) {
1084                                         *code = LDAP_SCHERR_DUPOPT;
1085                                         *errp = ss;
1086                                         free_at(at);
1087                                         return(NULL);
1088                                 }
1089                                 seen_ordering = 1;
1090                                 at->at_ordering_oid = parse_woid(&ss,code);
1091                                 if ( !at->at_ordering_oid ) {
1092                                         *errp = ss;
1093                                         free_at(at);
1094                                         return NULL;
1095                                 }
1096                         } else if ( !strcmp(sval,"SUBSTR") ) {
1097                                 if ( seen_substr ) {
1098                                         *code = LDAP_SCHERR_DUPOPT;
1099                                         *errp = ss;
1100                                         free_at(at);
1101                                         return(NULL);
1102                                 }
1103                                 seen_substr = 1;
1104                                 at->at_substr_oid = parse_woid(&ss,code);
1105                                 if ( !at->at_substr_oid ) {
1106                                         *errp = ss;
1107                                         free_at(at);
1108                                         return NULL;
1109                                 }
1110                         } else if ( !strcmp(sval,"SYNTAX") ) {
1111                                 if ( seen_syntax ) {
1112                                         *code = LDAP_SCHERR_DUPOPT;
1113                                         *errp = ss;
1114                                         free_at(at);
1115                                         return(NULL);
1116                                 }
1117                                 seen_syntax = 1;
1118                                 parse_whsp(&ss);
1119                                 at->at_syntax_oid = parse_noidlen(&ss,code,&at->at_syntax_len);
1120                                 if ( !at->at_syntax_oid ) {
1121                                         *errp = ss;
1122                                         free_at(at);
1123                                         return NULL;
1124                                 }
1125                                 parse_whsp(&ss);
1126                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1127                                 if ( at->at_single_value ) {
1128                                         *code = LDAP_SCHERR_DUPOPT;
1129                                         *errp = ss;
1130                                         free_at(at);
1131                                         return(NULL);
1132                                 }
1133                                 at->at_single_value = LDAP_SCHEMA_YES;
1134                                 parse_whsp(&ss);
1135                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1136                                 if ( at->at_collective ) {
1137                                         *code = LDAP_SCHERR_DUPOPT;
1138                                         *errp = ss;
1139                                         free_at(at);
1140                                         return(NULL);
1141                                 }
1142                                 at->at_collective = LDAP_SCHEMA_YES;
1143                                 parse_whsp(&ss);
1144                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1145                                 if ( at->at_no_user_mod ) {
1146                                         *code = LDAP_SCHERR_DUPOPT;
1147                                         *errp = ss;
1148                                         free_at(at);
1149                                         return(NULL);
1150                                 }
1151                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1152                                 parse_whsp(&ss);
1153                         } else if ( !strcmp(sval,"USAGE") ) {
1154                                 if ( seen_usage ) {
1155                                         *code = LDAP_SCHERR_DUPOPT;
1156                                         *errp = ss;
1157                                         free_at(at);
1158                                         return(NULL);
1159                                 }
1160                                 seen_usage = 1;
1161                                 parse_whsp(&ss);
1162                                 kind = get_token(&ss,&sval);
1163                                 if ( kind != TK_BAREWORD ) {
1164                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1165                                         *errp = ss;
1166                                         free_at(at);
1167                                         return NULL;
1168                                 }
1169                                 if ( !strcasecmp(sval,"userApplications") )
1170                                         at->at_usage =
1171                                             LDAP_SCHEMA_USER_APPLICATIONS;
1172                                 else if ( !strcasecmp(sval,"directoryOperation") )
1173                                         at->at_usage =
1174                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1175                                 else if ( !strcasecmp(sval,"distributedOperation") )
1176                                         at->at_usage =
1177                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1178                                 else if ( !strcasecmp(sval,"dSAOperation") )
1179                                         at->at_usage =
1180                                             LDAP_SCHEMA_DSA_OPERATION;
1181                                 else {
1182                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1183                                         *errp = ss;
1184                                         free_at(at);
1185                                         return NULL;
1186                                 }
1187                                 parse_whsp(&ss);
1188                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1189                                 /* Should be parse_qdstrings */
1190                                 ssdummy = parse_qdescrs(&ss, code);
1191                                 if ( !ssdummy ) {
1192                                         *errp = ss;
1193                                         free_at(at);
1194                                         return NULL;
1195                                 }
1196                         } else {
1197                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1198                                 *errp = ss;
1199                                 free_at(at);
1200                                 return NULL;
1201                         }
1202                         break;
1203                 default:
1204                         *code = LDAP_SCHERR_UNEXPTOKEN;
1205                         *errp = ss;
1206                         free_at(at);
1207                         return NULL;
1208                 }
1209         }
1210 }
1211
1212 static void
1213 free_oc(LDAP_OBJECT_CLASS * oc)
1214 {
1215         ldap_memfree(oc->oc_oid);
1216         charray_free(oc->oc_names);
1217         ldap_memfree(oc->oc_desc);
1218         charray_free(oc->oc_sup_oids);
1219         charray_free(oc->oc_at_oids_must);
1220         charray_free(oc->oc_at_oids_may);
1221         ldap_memfree(oc);
1222 }
1223
1224 LDAP_OBJECT_CLASS *
1225 ldap_str2objectclass( char * s, int * code, char ** errp )
1226 {
1227         int kind;
1228         char * ss = s;
1229         char * sval;
1230         int seen_name = 0;
1231         int seen_desc = 0;
1232         int seen_obsolete = 0;
1233         int seen_sup = 0;
1234         int seen_kind = 0;
1235         int seen_must = 0;
1236         int seen_may = 0;
1237         LDAP_OBJECT_CLASS * oc;
1238         char ** ssdummy;
1239
1240         if ( !s ) {
1241                 *code = LDAP_SCHERR_EMPTY;
1242                 *errp = "";
1243                 return NULL;
1244         }
1245
1246         *errp = s;
1247         oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1248
1249         if ( !oc ) {
1250                 *code = LDAP_SCHERR_OUTOFMEM;
1251                 return NULL;
1252         }
1253
1254         kind = get_token(&ss,&sval);
1255         if ( kind != TK_LEFTPAREN ) {
1256                 *code = LDAP_SCHERR_NOLEFTPAREN;
1257                 free_oc(oc);
1258                 return NULL;
1259         }
1260
1261         parse_whsp(&ss);
1262         oc->oc_oid = parse_numericoid(&ss,code);
1263         if ( !oc->oc_oid ) {
1264                 *errp = ss;
1265                 free_oc(oc);
1266                 return NULL;
1267         }
1268         parse_whsp(&ss);
1269
1270         /*
1271          * Beyond this point we will be liberal an accept the items
1272          * in any order.
1273          */
1274         while (1) {
1275                 kind = get_token(&ss,&sval);
1276                 switch (kind) {
1277                 case TK_EOS:
1278                         *code = LDAP_SCHERR_NORIGHTPAREN;
1279                         *errp = ss;
1280                         free_oc(oc);
1281                         return NULL;
1282                 case TK_RIGHTPAREN:
1283                         return oc;
1284                 case TK_BAREWORD:
1285                         if ( !strcmp(sval,"NAME") ) {
1286                                 if ( seen_name ) {
1287                                         *code = LDAP_SCHERR_DUPOPT;
1288                                         *errp = ss;
1289                                         free_oc(oc);
1290                                         return(NULL);
1291                                 }
1292                                 seen_name = 1;
1293                                 oc->oc_names = parse_qdescrs(&ss,code);
1294                                 if ( !oc->oc_names ) {
1295                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1296                                                 *code = LDAP_SCHERR_BADNAME;
1297                                         *errp = ss;
1298                                         free_oc(oc);
1299                                         return NULL;
1300                                 }
1301                         } else if ( !strcmp(sval,"DESC") ) {
1302                                 if ( seen_desc ) {
1303                                         *code = LDAP_SCHERR_DUPOPT;
1304                                         *errp = ss;
1305                                         free_oc(oc);
1306                                         return(NULL);
1307                                 }
1308                                 seen_desc = 1;
1309                                 parse_whsp(&ss);
1310                                 kind = get_token(&ss,&sval);
1311                                 if ( kind != TK_QDSTRING ) {
1312                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1313                                         *errp = ss;
1314                                         free_oc(oc);
1315                                         return NULL;
1316                                 }
1317                                 oc->oc_desc = sval;
1318                                 parse_whsp(&ss);
1319                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1320                                 if ( seen_obsolete ) {
1321                                         *code = LDAP_SCHERR_DUPOPT;
1322                                         *errp = ss;
1323                                         free_oc(oc);
1324                                         return(NULL);
1325                                 }
1326                                 seen_obsolete = 1;
1327                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1328                                 parse_whsp(&ss);
1329                         } else if ( !strcmp(sval,"SUP") ) {
1330                                 if ( seen_sup ) {
1331                                         *code = LDAP_SCHERR_DUPOPT;
1332                                         *errp = ss;
1333                                         free_oc(oc);
1334                                         return(NULL);
1335                                 }
1336                                 seen_sup = 1;
1337                                 oc->oc_sup_oids = parse_oids(&ss,code);
1338                                 if ( !oc->oc_sup_oids ) {
1339                                         *errp = ss;
1340                                         free_oc(oc);
1341                                         return NULL;
1342                                 }
1343                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1344                                 if ( seen_kind ) {
1345                                         *code = LDAP_SCHERR_DUPOPT;
1346                                         *errp = ss;
1347                                         free_oc(oc);
1348                                         return(NULL);
1349                                 }
1350                                 seen_kind = 1;
1351                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1352                                 parse_whsp(&ss);
1353                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1354                                 if ( seen_kind ) {
1355                                         *code = LDAP_SCHERR_DUPOPT;
1356                                         *errp = ss;
1357                                         free_oc(oc);
1358                                         return(NULL);
1359                                 }
1360                                 seen_kind = 1;
1361                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1362                                 parse_whsp(&ss);
1363                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1364                                 if ( seen_kind ) {
1365                                         *code = LDAP_SCHERR_DUPOPT;
1366                                         *errp = ss;
1367                                         free_oc(oc);
1368                                         return(NULL);
1369                                 }
1370                                 seen_kind = 1;
1371                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1372                                 parse_whsp(&ss);
1373                         } else if ( !strcmp(sval,"MUST") ) {
1374                                 if ( seen_must ) {
1375                                         *code = LDAP_SCHERR_DUPOPT;
1376                                         *errp = ss;
1377                                         free_oc(oc);
1378                                         return(NULL);
1379                                 }
1380                                 seen_must = 1;
1381                                 oc->oc_at_oids_must = parse_oids(&ss,code);
1382                                 if ( !oc->oc_at_oids_must ) {
1383                                         *errp = ss;
1384                                         free_oc(oc);
1385                                         return NULL;
1386                                 }
1387                                 parse_whsp(&ss);
1388                         } else if ( !strcmp(sval,"MAY") ) {
1389                                 if ( seen_may ) {
1390                                         *code = LDAP_SCHERR_DUPOPT;
1391                                         *errp = ss;
1392                                         free_oc(oc);
1393                                         return(NULL);
1394                                 }
1395                                 seen_may = 1;
1396                                 oc->oc_at_oids_may = parse_oids(&ss,code);
1397                                 if ( !oc->oc_at_oids_may ) {
1398                                         *errp = ss;
1399                                         free_oc(oc);
1400                                         return NULL;
1401                                 }
1402                                 parse_whsp(&ss);
1403                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1404                                 /* Should be parse_qdstrings */
1405                                 ssdummy = parse_qdescrs(&ss, code);
1406                                 if ( !ssdummy ) {
1407                                         *errp = ss;
1408                                         free_oc(oc);
1409                                         return NULL;
1410                                 }
1411                         } else {
1412                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1413                                 *errp = ss;
1414                                 free_oc(oc);
1415                                 return NULL;
1416                         }
1417                         break;
1418                 default:
1419                         *code = LDAP_SCHERR_UNEXPTOKEN;
1420                         *errp = ss;
1421                         free_oc(oc);
1422                         return NULL;
1423                 }
1424         }
1425 }
1426
1427 static char *err2text[] = {
1428         "",
1429         "Out of memory",
1430         "Unexpected token",
1431         "Missing opening parenthesis",
1432         "Missing closing parenthesis",
1433         "Expecting digit",
1434         "Expecting a name",
1435         "Bad description",
1436         "Bad superiors",
1437         "Duplicate option",
1438         "Unexpected end of data"
1439 };
1440
1441 char *
1442 ldap_scherr2str(int code)
1443 {
1444         if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1445                 return "Unknown error";
1446         } else {
1447                 return err2text[code];
1448         }
1449 }