2 * Copyright 1999 The OpenLDAP Foundation, All Rights Reserved.
3 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5 * schema.c: parsing routines used by servers and clients to process
15 #include <ac/string.h>
20 #include <ldap_schema.h>
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 pretection 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.
30 typedef struct safe_string {
38 new_safe_string(int size)
42 ss = LDAP_MALLOC(sizeof(safe_string));
47 ss->val = LDAP_MALLOC(size);
57 safe_string_free(safe_string * ss)
61 ldap_memfree(ss->val);
66 safe_string_val(safe_string * ss)
68 ss->val[ss->pos] = '\0';
73 append_to_safe_string(safe_string * ss, char * s)
79 * Some runaway process is trying to append to a string that
80 * overflowed and we could not extend.
85 /* We always make sure there is at least one position available */
86 if ( ss->pos + l >= ss->size-1 ) {
88 temp = LDAP_REALLOC(ss->val, ss->size);
90 /* Trouble, out of memory */
96 strncpy(&ss->val[ss->pos], s, l);
98 if ( ss->pos > 0 && ss->val[ss->pos-1] == ' ' )
107 print_literal(safe_string *ss, char *s)
109 return(append_to_safe_string(ss,s));
113 print_whsp(safe_string *ss)
116 return(append_to_safe_string(ss,""));
118 return(append_to_safe_string(ss," "));
122 print_numericoid(safe_string *ss, char *s)
125 return(append_to_safe_string(ss,s));
128 /* This one is identical to print_qdescr */
130 print_qdstring(safe_string *ss, char *s)
133 print_literal(ss,"'");
134 append_to_safe_string(ss,s);
135 print_literal(ss,"'");
136 return(print_whsp(ss));
140 print_qdescr(safe_string *ss, char *s)
143 print_literal(ss,"'");
144 append_to_safe_string(ss,s);
145 print_literal(ss,"'");
146 return(print_whsp(ss));
150 print_qdescrlist(safe_string *ss, char **sa)
155 for (sp=sa; *sp; sp++) {
156 ret = print_qdescr(ss,*sp);
158 /* If the list was empty, we return zero that is potentially
159 * incorrect, but since we will still appending things, the
160 * overflow will be detected later. Maybe FIX.
166 print_qdescrs(safe_string *ss, char **sa)
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.
172 if ( !sa[0] || ( sa[0] && sa[1] ) ) {
174 print_literal(ss,"(");
175 print_qdescrlist(ss,sa);
176 print_literal(ss,")");
177 return(print_whsp(ss));
179 return(print_qdescr(ss,*sa));
184 print_woid(safe_string *ss, char *s)
187 append_to_safe_string(ss,s);
188 return print_whsp(ss);
192 print_oidlist(safe_string *ss, char **sa)
196 for (sp=sa; *(sp+1); sp++) {
198 print_literal(ss,"$");
200 return(print_woid(ss,*sp));
204 print_oids(safe_string *ss, char **sa)
206 if ( sa[0] && sa[1] ) {
207 print_literal(ss,"(");
208 print_oidlist(ss,sa);
210 return(print_literal(ss,")"));
212 return(print_woid(ss,*sa));
217 print_noidlen(safe_string *ss, char *s, int l)
222 ret = print_numericoid(ss,s);
224 sprintf(buf,"{%d}",l);
225 ret = print_literal(ss,buf);
231 ldap_objectclass2str( LDAP_OBJECT_CLASS * oc )
236 ss = new_safe_string(256);
240 print_literal(ss,"(");
243 print_numericoid(ss, oc->oc_oid);
246 if ( oc->oc_names ) {
247 print_literal(ss,"NAME");
248 print_qdescrs(ss,oc->oc_names);
252 print_literal(ss,"DESC");
253 print_qdstring(ss,oc->oc_desc);
256 if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) {
257 print_literal(ss, "OBSOLETE");
261 if ( oc->oc_sup_oids ) {
262 print_literal(ss,"SUP");
263 print_oids(ss,oc->oc_sup_oids);
266 switch (oc->oc_kind) {
267 case LDAP_SCHEMA_ABSTRACT:
268 print_literal(ss,"ABSTRACT");
270 case LDAP_SCHEMA_STRUCTURAL:
271 print_literal(ss,"STRUCTURAL");
273 case LDAP_SCHEMA_AUXILIARY:
274 print_literal(ss,"AUXILIARY");
277 print_literal(ss,"KIND-UNKNOWN");
282 if ( oc->oc_at_oids_must ) {
283 print_literal(ss,"MUST");
285 print_oids(ss,oc->oc_at_oids_must);
289 if ( oc->oc_at_oids_may ) {
290 print_literal(ss,"MAY");
292 print_oids(ss,oc->oc_at_oids_may);
297 print_literal(ss,")");
299 retstring = strdup(safe_string_val(ss));
300 safe_string_free(ss);
305 ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at )
310 ss = new_safe_string(256);
314 print_literal(ss,"(");
317 print_numericoid(ss, at->at_oid);
320 if ( at->at_names ) {
321 print_literal(ss,"NAME");
322 print_qdescrs(ss,at->at_names);
326 print_literal(ss,"DESC");
327 print_qdstring(ss,at->at_desc);
330 if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
331 print_literal(ss, "OBSOLETE");
335 if ( at->at_sup_oid ) {
336 print_literal(ss,"SUP");
337 print_woid(ss,at->at_sup_oid);
340 if ( at->at_equality_oid ) {
341 print_literal(ss,"EQUALITY");
342 print_woid(ss,at->at_equality_oid);
345 if ( at->at_ordering_oid ) {
346 print_literal(ss,"ORDERING");
347 print_woid(ss,at->at_ordering_oid);
350 if ( at->at_substr_oid ) {
351 print_literal(ss,"SUBSTR");
352 print_woid(ss,at->at_substr_oid);
355 if ( at->at_syntax_oid ) {
356 print_literal(ss,"SYNTAX");
358 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
361 if ( at->at_single_value == LDAP_SCHEMA_YES ) {
362 print_literal(ss,"SINGLE-VALUE");
366 if ( at->at_collective == LDAP_SCHEMA_YES ) {
367 print_literal(ss,"COLLECTIVE");
371 if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
372 print_literal(ss,"NO-USER-MODIFICATION");
376 if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
377 print_literal(ss,"USAGE");
379 switch (at->at_usage) {
380 case LDAP_SCHEMA_DIRECTORY_OPERATION:
381 print_literal(ss,"directoryOperation");
383 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
384 print_literal(ss,"distributedOperation");
386 case LDAP_SCHEMA_DSA_OPERATION:
387 print_literal(ss,"dSAOperation");
390 print_literal(ss,"UNKNOWN");
396 print_literal(ss,")");
398 retstring = strdup(safe_string_val(ss));
399 safe_string_free(ss);
404 * This is ripped from servers/slapd/charray.c that should be promoted
405 * to -lldap or something so that it is used everywhere.
408 charray_free( char **array )
412 if ( array == NULL ) {
416 for ( a = array; *a != NULL; a++ ) {
421 LDAP_FREE( (char *) array );
425 * Now come the parsers. There is one parser for each entity type:
426 * objectclasses, attributetypes, etc.
428 * Each of them is written as a recursive-descent parser, except that
429 * none of them is really recursive. But the idea is kept: there
430 * is one routine per non-terminal that eithers gobbles lexical tokens
431 * or calls lower-level routines, etc.
433 * The scanner is implemented in the routine get_token. Actually,
434 * get_token is more than a scanner and will return tokens that are
435 * in fact non-terminals in the grammar. So you can see the whole
436 * approach as the combination of a low-level bottom-up recognizer
437 * combined with a scanner and a number of top-down parsers. Or just
438 * consider that the real grammars recognized by the parsers are not
439 * those of the standards. As a matter of fact, our parsers are more
440 * liberal than the spec when there is no ambiguity.
442 * The difference is pretty academic (modulo bugs or incorrect
443 * interpretation of the specs).
446 #define TK_NOENDQUOTE -2
447 #define TK_OUTOFMEM -1
449 #define TK_UNEXPCHAR 1
450 #define TK_BAREWORD 2
451 #define TK_QDSTRING 3
452 #define TK_LEFTPAREN 4
453 #define TK_RIGHTPAREN 5
455 #define TK_QDESCR TK_QDSTRING
463 get_token(char ** sp, char ** token_val)
480 kind = TK_RIGHTPAREN;
491 while ( **sp != '\'' && **sp != '\0' )
493 if ( **sp == '\'' ) {
495 res = LDAP_MALLOC(q-p+1);
505 kind = TK_NOENDQUOTE;
511 while ( !isspace(**sp) && **sp != '\0' )
514 res = LDAP_MALLOC(q-p+1);
523 /* kind = TK_UNEXPCHAR; */
530 /* Gobble optional whitespace */
532 parse_whsp(char **sp)
534 while (isspace(**sp))
539 * General note for all parsers: to guarantee the algorithm halts they
540 * must always advance the pointer even when an error is found. For
541 * this one is not that important since an error here is fatal at the
542 * upper layers, but it is a simple strategy that will not get in
546 /* Parse a sequence of dot-separated decimal strings */
548 parse_numericoid(char **sp, int *code)
554 /* Each iteration of this loops gets one decimal string */
556 if ( !isdigit(**sp) ) {
557 /* Initial char is not a digit or char after dot is not a digit */
558 *code = LDAP_SCHERR_NODIGIT;
562 while ( isdigit(**sp) )
566 /* Otherwise, gobble the dot and loop again */
569 /* At this point, *sp points at the char past the numericoid. Perfect. */
571 res = LDAP_MALLOC(len+1);
573 *code = LDAP_SCHERR_OUTOFMEM;
576 strncpy(res,start,len);
581 /* Parse a qdescr or a list of them enclosed in () */
583 parse_qdescrs(char **sp, int *code)
593 kind = get_token(sp,&sval);
594 if ( kind == TK_LEFTPAREN ) {
595 /* Let's presume there will be at least 2 entries */
597 res = LDAP_CALLOC(3,sizeof(char *));
599 *code = LDAP_SCHERR_OUTOFMEM;
605 kind = get_token(sp,&sval);
606 if ( kind == TK_RIGHTPAREN )
608 if ( kind == TK_QDESCR ) {
609 if ( pos == size-2 ) {
611 res1 = LDAP_REALLOC(res,size*sizeof(char *));
614 *code = LDAP_SCHERR_OUTOFMEM;
624 *code = LDAP_SCHERR_UNEXPTOKEN;
631 } else if ( kind == TK_QDESCR ) {
632 res = LDAP_CALLOC(2,sizeof(char *));
634 *code = LDAP_SCHERR_OUTOFMEM;
642 *code = LDAP_SCHERR_BADNAME;
649 parse_woid(char **sp, int *code)
655 kind = get_token(sp, &sval);
656 if ( kind != TK_BAREWORD ) {
657 *code = LDAP_SCHERR_UNEXPTOKEN;
664 /* Parse a noidlen */
666 parse_noidlen(char **sp, int *code, int *len)
672 kind = get_token(sp, &sval);
673 if ( kind != TK_BAREWORD ) {
674 *code = LDAP_SCHERR_UNEXPTOKEN;
680 while ( isdigit(**sp) )
684 *code = LDAP_SCHERR_UNEXPTOKEN;
693 /* Parse a woid or a $-separated list of them enclosed in () */
695 parse_oids(char **sp, int *code)
705 * Strictly speaking, doing this here accepts whsp before the
706 * ( at the begining of an oidlist, but his is harmless. Also,
707 * we are very liberal in what we accept as an OID. Maybe
711 kind = get_token(sp,&sval);
712 if ( kind == TK_LEFTPAREN ) {
713 /* Let's presume there will be at least 2 entries */
715 res = LDAP_CALLOC(3,sizeof(char *));
717 *code = LDAP_SCHERR_OUTOFMEM;
722 kind = get_token(sp,&sval);
723 if ( kind == TK_BAREWORD ) {
727 *code = LDAP_SCHERR_UNEXPTOKEN;
733 kind = get_token(sp,&sval);
734 if ( kind == TK_RIGHTPAREN )
736 if ( kind == TK_DOLLAR ) {
738 kind = get_token(sp,&sval);
739 if ( kind == TK_BAREWORD ) {
740 if ( pos == size-2 ) {
742 res1 = LDAP_REALLOC(res,size*sizeof(char *));
745 *code = LDAP_SCHERR_OUTOFMEM;
753 *code = LDAP_SCHERR_UNEXPTOKEN;
759 *code = LDAP_SCHERR_UNEXPTOKEN;
767 } else if ( kind == TK_BAREWORD ) {
768 res = LDAP_CALLOC(2,sizeof(char *));
770 *code = LDAP_SCHERR_OUTOFMEM;
778 *code = LDAP_SCHERR_BADNAME;
784 free_at(LDAP_ATTRIBUTE_TYPE * at)
786 ldap_memfree(at->at_oid);
787 charray_free(at->at_names);
788 ldap_memfree(at->at_desc);
789 ldap_memfree(at->at_sup_oid);
790 ldap_memfree(at->at_equality_oid);
791 ldap_memfree(at->at_ordering_oid);
792 ldap_memfree(at->at_substr_oid);
793 ldap_memfree(at->at_syntax_oid);
797 LDAP_ATTRIBUTE_TYPE *
798 ldap_str2attributetype( char * s, int * code, char ** errp )
805 int seen_obsolete = 0;
807 int seen_equality = 0;
808 int seen_ordering = 0;
815 LDAP_ATTRIBUTE_TYPE * at;
818 at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
821 *code = LDAP_SCHERR_OUTOFMEM;
825 kind = get_token(&ss,&sval);
826 if ( kind != TK_LEFTPAREN ) {
827 *code = LDAP_SCHERR_NOLEFTPAREN;
833 at->at_oid = parse_numericoid(&ss,code);
842 * Beyond this point we will be liberal an accept the items
846 kind = get_token(&ss,&sval);
849 *code = LDAP_SCHERR_NORIGHTPAREN;
856 if ( !strcmp(sval,"NAME") ) {
858 *code = LDAP_SCHERR_DUPOPT;
864 at->at_names = parse_qdescrs(&ss,code);
865 if ( !at->at_names ) {
866 if ( *code != LDAP_SCHERR_OUTOFMEM )
867 *code = LDAP_SCHERR_BADNAME;
872 } else if ( !strcmp(sval,"DESC") ) {
874 *code = LDAP_SCHERR_DUPOPT;
881 kind = get_token(&ss,&sval);
882 if ( kind != TK_QDSTRING ) {
883 *code = LDAP_SCHERR_UNEXPTOKEN;
890 } else if ( !strcmp(sval,"OBSOLETE") ) {
891 if ( seen_obsolete ) {
892 *code = LDAP_SCHERR_DUPOPT;
898 at->at_obsolete = LDAP_SCHEMA_YES;
900 } else if ( !strcmp(sval,"SUP") ) {
902 *code = LDAP_SCHERR_DUPOPT;
908 at->at_sup_oid = parse_woid(&ss,code);
909 if ( !at->at_sup_oid ) {
914 } else if ( !strcmp(sval,"EQUALITY") ) {
915 if ( seen_equality ) {
916 *code = LDAP_SCHERR_DUPOPT;
922 at->at_equality_oid = parse_woid(&ss,code);
923 if ( !at->at_equality_oid ) {
928 } else if ( !strcmp(sval,"ORDERING") ) {
929 if ( seen_ordering ) {
930 *code = LDAP_SCHERR_DUPOPT;
936 at->at_ordering_oid = parse_woid(&ss,code);
937 if ( !at->at_ordering_oid ) {
942 } else if ( !strcmp(sval,"SUBSTR") ) {
944 *code = LDAP_SCHERR_DUPOPT;
950 at->at_substr_oid = parse_woid(&ss,code);
951 if ( !at->at_substr_oid ) {
956 } else if ( !strcmp(sval,"SYNTAX") ) {
958 *code = LDAP_SCHERR_DUPOPT;
965 at->at_syntax_oid = parse_noidlen(&ss,code,&at->at_syntax_len);
966 if ( !at->at_syntax_oid ) {
971 } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
972 if ( at->at_single_value ) {
973 *code = LDAP_SCHERR_DUPOPT;
978 at->at_single_value = LDAP_SCHEMA_YES;
980 } else if ( !strcmp(sval,"COLLECTIVE") ) {
981 if ( at->at_collective ) {
982 *code = LDAP_SCHERR_DUPOPT;
987 at->at_collective = LDAP_SCHEMA_YES;
989 } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
990 if ( at->at_no_user_mod ) {
991 *code = LDAP_SCHERR_DUPOPT;
996 at->at_no_user_mod = LDAP_SCHEMA_YES;
998 } else if ( !strcmp(sval,"USAGE") ) {
1000 *code = LDAP_SCHERR_DUPOPT;
1007 kind = get_token(&ss,&sval);
1008 if ( kind != TK_BAREWORD ) {
1009 *code = LDAP_SCHERR_UNEXPTOKEN;
1014 if ( !strcasecmp(sval,"userApplications") )
1015 at->at_usage = LDAP_SCHEMA_USER_APPLICATIONS;
1016 else if ( !strcasecmp(sval,"directoryOperation") )
1017 at->at_usage = LDAP_SCHEMA_DIRECTORY_OPERATION;
1018 else if ( !strcasecmp(sval,"distributedOperation") )
1019 at->at_usage = LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1020 else if ( !strcasecmp(sval,"dSAOperation") )
1021 at->at_usage = LDAP_SCHEMA_DSA_OPERATION;
1023 *code = LDAP_SCHERR_UNEXPTOKEN;
1029 *code = LDAP_SCHERR_UNEXPTOKEN;
1036 *code = LDAP_SCHERR_UNEXPTOKEN;
1045 free_oc(LDAP_OBJECT_CLASS * oc)
1047 ldap_memfree(oc->oc_oid);
1048 charray_free(oc->oc_names);
1049 ldap_memfree(oc->oc_desc);
1050 charray_free(oc->oc_sup_oids);
1051 charray_free(oc->oc_at_oids_must);
1052 charray_free(oc->oc_at_oids_may);
1057 ldap_str2objectclass( char * s, int * code, char ** errp )
1064 int seen_obsolete = 0;
1069 LDAP_OBJECT_CLASS * oc;
1072 oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1075 *code = LDAP_SCHERR_OUTOFMEM;
1079 kind = get_token(&ss,&sval);
1080 if ( kind != TK_LEFTPAREN ) {
1081 *code = LDAP_SCHERR_NOLEFTPAREN;
1087 oc->oc_oid = parse_numericoid(&ss,code);
1088 if ( !oc->oc_oid ) {
1096 * Beyond this point we will be liberal an accept the items
1100 kind = get_token(&ss,&sval);
1103 *code = LDAP_SCHERR_NORIGHTPAREN;
1110 if ( !strcmp(sval,"NAME") ) {
1112 *code = LDAP_SCHERR_DUPOPT;
1118 oc->oc_names = parse_qdescrs(&ss,code);
1119 if ( !oc->oc_names ) {
1120 if ( *code != LDAP_SCHERR_OUTOFMEM )
1121 *code = LDAP_SCHERR_BADNAME;
1126 } else if ( !strcmp(sval,"DESC") ) {
1128 *code = LDAP_SCHERR_DUPOPT;
1135 kind = get_token(&ss,&sval);
1136 if ( kind != TK_QDSTRING ) {
1137 *code = LDAP_SCHERR_UNEXPTOKEN;
1144 } else if ( !strcmp(sval,"OBSOLETE") ) {
1145 if ( seen_obsolete ) {
1146 *code = LDAP_SCHERR_DUPOPT;
1152 oc->oc_obsolete = LDAP_SCHEMA_YES;
1154 } else if ( !strcmp(sval,"SUP") ) {
1156 *code = LDAP_SCHERR_DUPOPT;
1162 /* Netscape DS is broken or I have not
1163 understood the syntax. */
1164 /* oc->oc_sup_oids = parse_oids(&ss,code); */
1165 oc->oc_sup_oids = parse_qdescrs(&ss,code);
1166 if ( !oc->oc_sup_oids ) {
1171 } else if ( !strcmp(sval,"ABSTRACT") ) {
1173 *code = LDAP_SCHERR_DUPOPT;
1179 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1181 } else if ( !strcmp(sval,"STRUCTURAL") ) {
1183 *code = LDAP_SCHERR_DUPOPT;
1189 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1191 } else if ( !strcmp(sval,"AUXILIARY") ) {
1193 *code = LDAP_SCHERR_DUPOPT;
1199 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1201 } else if ( !strcmp(sval,"MUST") ) {
1203 *code = LDAP_SCHERR_DUPOPT;
1209 oc->oc_at_oids_must = parse_oids(&ss,code);
1210 if ( !oc->oc_at_oids_must ) {
1216 } else if ( !strcmp(sval,"MAY") ) {
1218 *code = LDAP_SCHERR_DUPOPT;
1224 oc->oc_at_oids_may = parse_oids(&ss,code);
1225 if ( !oc->oc_at_oids_may ) {
1232 *code = LDAP_SCHERR_UNEXPTOKEN;
1239 *code = LDAP_SCHERR_UNEXPTOKEN;
1247 static char *err2text[] = {
1251 "Missing opening parenthesis",
1252 "Missing closing parenthesis",
1261 ldap_scherr2str(int code)
1263 if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1264 return "Unknown error";
1266 return err2text[code];