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
12 #include <ac/string.h>
18 #include <ldap_schema.h>
22 * When pretty printing the entities we will be appending to a buffer.
23 * Since checking for overflow, realloc'ing and checking if no error
24 * is extremely boring, we will use a pretection layer that will let
25 * us blissfully ignore the error until the end. This layer is
26 * implemented with the help of the next type.
29 typedef struct safe_string {
37 new_safe_string(int size)
41 ss = malloc(sizeof(safe_string));
46 ss->val = malloc(size);
56 safe_string_free(safe_string * ss)
60 ldap_memfree(ss->val);
65 safe_string_val(safe_string * ss)
67 ss->val[ss->pos] = '\0';
72 append_to_safe_string(safe_string * ss, char * s)
78 * Some runaway process is trying to append to a string that
79 * overflowed and we could not extend.
84 /* We always make sure there is at least one position available */
85 if ( ss->pos + l >= ss->size-1 ) {
87 temp = realloc(ss->val, ss->size);
89 /* Trouble, out of memory */
95 strncpy(&ss->val[ss->pos], s, l);
97 if ( ss->pos > 0 && ss->val[ss->pos-1] == ' ' )
106 print_literal(safe_string *ss, char *s)
108 return(append_to_safe_string(ss,s));
112 print_whsp(safe_string *ss)
115 return(append_to_safe_string(ss,""));
117 return(append_to_safe_string(ss," "));
121 print_numericoid(safe_string *ss, char *s)
124 return(append_to_safe_string(ss,s));
127 /* This one is identical to print_qdescr */
129 print_qdstring(safe_string *ss, char *s)
132 print_literal(ss,"'");
133 append_to_safe_string(ss,s);
134 print_literal(ss,"'");
135 return(print_whsp(ss));
139 print_qdescr(safe_string *ss, char *s)
142 print_literal(ss,"'");
143 append_to_safe_string(ss,s);
144 print_literal(ss,"'");
145 return(print_whsp(ss));
149 print_qdescrlist(safe_string *ss, char **sa)
154 for (sp=sa; *sp; sp++) {
155 ret = print_qdescr(ss,*sp);
157 /* If the list was empty, we return zero that is potentially
158 * incorrect, but since we will still appending things, the
159 * overflow will be detected later. Maybe FIX.
165 print_qdescrs(safe_string *ss, char **sa)
167 /* The only way to represent an empty list is as a qdescrlist
168 * so, if the list is empty we treat it as a long list.
169 * Really, this is what the syntax mandates.
171 if ( !sa[0] || ( sa[0] && sa[1] ) ) {
173 print_literal(ss,"(");
174 print_qdescrlist(ss,sa);
175 print_literal(ss,")");
176 return(print_whsp(ss));
178 return(print_qdescr(ss,*sa));
183 print_woid(safe_string *ss, char *s)
186 append_to_safe_string(ss,s);
187 return print_whsp(ss);
191 print_oidlist(safe_string *ss, char **sa)
195 for (sp=sa; *(sp+1); sp++) {
197 print_literal(ss,"$");
199 return(print_woid(ss,*sp));
203 print_oids(safe_string *ss, char **sa)
205 if ( sa[0] && sa[1] ) {
206 print_literal(ss,"(");
207 print_oidlist(ss,sa);
209 return(print_literal(ss,")"));
211 return(print_woid(ss,*sa));
216 print_noidlen(safe_string *ss, char *s, int l)
221 ret = print_numericoid(ss,s);
223 sprintf(buf,"{%d}",l);
224 ret = print_literal(ss,buf);
230 ldap_objectclass2str( LDAP_OBJECT_CLASS * oc )
235 ss = new_safe_string(256);
239 print_literal(ss,"(");
242 print_numericoid(ss, oc->oc_oid);
245 if ( oc->oc_names ) {
246 print_literal(ss,"NAME");
247 print_qdescrs(ss,oc->oc_names);
251 print_literal(ss,"DESC");
252 print_qdstring(ss,oc->oc_desc);
255 if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) {
256 print_literal(ss, "OBSOLETE");
260 if ( oc->oc_sup_oids ) {
261 print_literal(ss,"SUP");
262 print_oids(ss,oc->oc_sup_oids);
265 switch (oc->oc_kind) {
266 case LDAP_SCHEMA_ABSTRACT:
267 print_literal(ss,"ABSTRACT");
269 case LDAP_SCHEMA_STRUCTURAL:
270 print_literal(ss,"STRUCTURAL");
272 case LDAP_SCHEMA_AUXILIARY:
273 print_literal(ss,"AUXILIARY");
276 print_literal(ss,"KIND-UNKNOWN");
281 if ( oc->oc_at_oids_must ) {
282 print_literal(ss,"MUST");
284 print_oids(ss,oc->oc_at_oids_must);
288 if ( oc->oc_at_oids_may ) {
289 print_literal(ss,"MAY");
291 print_oids(ss,oc->oc_at_oids_may);
296 print_literal(ss,")");
298 retstring = safe_string_val(ss);
299 safe_string_free(ss);
304 ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at )
309 ss = new_safe_string(256);
313 print_literal(ss,"(");
316 print_numericoid(ss, at->at_oid);
319 if ( at->at_names ) {
320 print_literal(ss,"NAME");
321 print_qdescrs(ss,at->at_names);
325 print_literal(ss,"DESC");
326 print_qdstring(ss,at->at_desc);
329 if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
330 print_literal(ss, "OBSOLETE");
334 if ( at->at_sup_oid ) {
335 print_literal(ss,"SUP");
336 print_woid(ss,at->at_sup_oid);
339 if ( at->at_equality_oid ) {
340 print_literal(ss,"EQUALITY");
341 print_woid(ss,at->at_equality_oid);
344 if ( at->at_ordering_oid ) {
345 print_literal(ss,"ORDERING");
346 print_woid(ss,at->at_ordering_oid);
349 if ( at->at_substr_oid ) {
350 print_literal(ss,"SUBSTR");
351 print_woid(ss,at->at_substr_oid);
354 if ( at->at_syntax_oid ) {
355 print_literal(ss,"SYNTAX");
357 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
360 if ( at->at_single_value == LDAP_SCHEMA_YES ) {
361 print_literal(ss,"SINGLE-VALUE");
365 if ( at->at_collective == LDAP_SCHEMA_YES ) {
366 print_literal(ss,"COLLECTIVE");
370 if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
371 print_literal(ss,"NO-USER-MODIFICATION");
375 if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
376 print_literal(ss,"USAGE");
378 switch (at->at_usage) {
379 case LDAP_SCHEMA_DIRECTORY_OPERATION:
380 print_literal(ss,"directoryOperation");
382 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
383 print_literal(ss,"distributedOperation");
385 case LDAP_SCHEMA_DSA_OPERATION:
386 print_literal(ss,"dSAOperation");
389 print_literal(ss,"UNKNOWN");
395 print_literal(ss,")");
397 retstring = safe_string_val(ss);
398 safe_string_free(ss);
403 * This is ripped from servers/slapd/charray.c that should be promoted
404 * to -lldap or something so that it is used everywhere.
407 charray_free( char **array )
411 if ( array == NULL ) {
415 for ( a = array; *a != NULL; a++ ) {
420 free( (char *) array );
424 * Now come the parsers. There is one parser for each entity type:
425 * objectclasses, attributetypes, etc.
427 * Each of them is written as a recursive-descent parser, except that
428 * none of them is really recursive. But the idea is kept: there
429 * is one routine per non-terminal that eithers gobbles lexical tokens
430 * or calls lower-level routines, etc.
432 * The scanner is implemented in the routine get_token. Actually,
433 * get_token is more than a scanner and will return tokens that are
434 * in fact non-terminals in the grammar. So you can see the whole
435 * approach as the combination of a low-level bottom-up recognizer
436 * combined with a scanner and a number of top-down parsers. Or just
437 * consider that the real grammars recognized by the parsers are not
438 * those of the standards. As a matter of fact, our parsers are more
439 * liberal than the spec when there is no ambiguity.
441 * The difference is pretty academic (modulo bugs or incorrect
442 * interpretation of the specs).
445 #define TK_NOENDQUOTE -2
446 #define TK_OUTOFMEM -1
448 #define TK_UNEXPCHAR 1
449 #define TK_BAREWORD 2
450 #define TK_QDSTRING 3
451 #define TK_LEFTPAREN 4
452 #define TK_RIGHTPAREN 5
454 #define TK_QDESCR TK_QDSTRING
462 get_token(char ** sp, char ** token_val)
479 kind = TK_RIGHTPAREN;
490 while ( **sp != '\'' && **sp != '\0' )
492 if ( **sp == '\'' ) {
504 kind = TK_NOENDQUOTE;
510 while ( !isspace(**sp) && **sp != '\0' )
522 /* kind = TK_UNEXPCHAR; */
529 /* Gobble optional whitespace */
531 parse_whsp(char **sp)
533 while (isspace(**sp))
538 * General note for all parsers: to guarantee the algorithm halts they
539 * must always advance the pointer even when an error is found. For
540 * this one is not that important since an error here is fatal at the
541 * upper layers, but it is a simple strategy that will not get in
545 /* Parse a sequence of dot-separated decimal strings */
547 parse_numericoid(char **sp, int *code)
553 /* Each iteration of this loops gets one decimal string */
555 if ( !isdigit(**sp) ) {
556 /* Initial char is not a digit or char after dot is not a digit */
557 *code = LDAP_SCHERR_NODIGIT;
561 while ( isdigit(**sp) )
565 /* Otherwise, gobble the dot and loop again */
568 /* At this point, *sp points at the char past the numericoid. Perfect. */
572 *code = LDAP_SCHERR_OUTOFMEM;
575 strncpy(res,start,len);
580 /* Parse a qdescr or a list of them enclosed in () */
582 parse_qdescrs(char **sp, int *code)
592 kind = get_token(sp,&sval);
593 if ( kind == TK_LEFTPAREN ) {
594 /* Let's presume there will be at least 2 entries */
596 res = calloc(3,sizeof(char *));
598 *code = LDAP_SCHERR_OUTOFMEM;
604 kind = get_token(sp,&sval);
605 if ( kind == TK_RIGHTPAREN )
607 if ( kind == TK_QDESCR ) {
608 if ( pos == size-2 ) {
610 res1 = realloc(res,size*sizeof(char *));
613 *code = LDAP_SCHERR_OUTOFMEM;
623 *code = LDAP_SCHERR_UNEXPTOKEN;
630 } else if ( kind == TK_QDESCR ) {
631 res = calloc(2,sizeof(char *));
633 *code = LDAP_SCHERR_OUTOFMEM;
641 *code = LDAP_SCHERR_BADNAME;
648 parse_woid(char **sp, int *code)
654 kind = get_token(sp, &sval);
655 if ( kind != TK_BAREWORD ) {
656 *code = LDAP_SCHERR_UNEXPTOKEN;
663 /* Parse a noidlen */
665 parse_noidlen(char **sp, int *code, int *len)
671 kind = get_token(sp, &sval);
672 if ( kind != TK_BAREWORD ) {
673 *code = LDAP_SCHERR_UNEXPTOKEN;
679 while ( isdigit(**sp) )
683 *code = LDAP_SCHERR_UNEXPTOKEN;
692 /* Parse a woid or a $-separated list of them enclosed in () */
694 parse_oids(char **sp, int *code)
704 * Strictly speaking, doing this here accepts whsp before the
705 * ( at the begining of an oidlist, but his is harmless. Also,
706 * we are very liberal in what we accept as an OID. Maybe
710 kind = get_token(sp,&sval);
711 if ( kind == TK_LEFTPAREN ) {
712 /* Let's presume there will be at least 2 entries */
714 res = calloc(3,sizeof(char *));
716 *code = LDAP_SCHERR_OUTOFMEM;
721 kind = get_token(sp,&sval);
722 if ( kind == TK_BAREWORD ) {
726 *code = LDAP_SCHERR_UNEXPTOKEN;
732 kind = get_token(sp,&sval);
733 if ( kind == TK_RIGHTPAREN )
735 if ( kind == TK_DOLLAR ) {
737 kind = get_token(sp,&sval);
738 if ( kind == TK_BAREWORD ) {
739 if ( pos == size-2 ) {
741 res1 = realloc(res,size*sizeof(char *));
744 *code = LDAP_SCHERR_OUTOFMEM;
752 *code = LDAP_SCHERR_UNEXPTOKEN;
758 *code = LDAP_SCHERR_UNEXPTOKEN;
766 } else if ( kind == TK_BAREWORD ) {
767 res = calloc(2,sizeof(char *));
769 *code = LDAP_SCHERR_OUTOFMEM;
777 *code = LDAP_SCHERR_BADNAME;
783 free_at(LDAP_ATTRIBUTE_TYPE * at)
785 ldap_memfree(at->at_oid);
786 charray_free(at->at_names);
787 ldap_memfree(at->at_desc);
788 ldap_memfree(at->at_sup_oid);
789 ldap_memfree(at->at_equality_oid);
790 ldap_memfree(at->at_ordering_oid);
791 ldap_memfree(at->at_substr_oid);
792 ldap_memfree(at->at_syntax_oid);
796 LDAP_ATTRIBUTE_TYPE *
797 ldap_str2attributetype( char * s, int * code, char ** errp )
804 int seen_obsolete = 0;
806 int seen_equality = 0;
807 int seen_ordering = 0;
814 LDAP_ATTRIBUTE_TYPE * at;
817 at = calloc(1,sizeof(LDAP_ATTRIBUTE_TYPE));
820 *code = LDAP_SCHERR_OUTOFMEM;
824 kind = get_token(&ss,&sval);
825 if ( kind != TK_LEFTPAREN ) {
826 *code = LDAP_SCHERR_NOLEFTPAREN;
832 at->at_oid = parse_numericoid(&ss,code);
841 * Beyond this point we will be liberal an accept the items
845 kind = get_token(&ss,&sval);
848 *code = LDAP_SCHERR_NORIGHTPAREN;
855 if ( !strcmp(sval,"NAME") ) {
857 *code = LDAP_SCHERR_DUPOPT;
863 at->at_names = parse_qdescrs(&ss,code);
864 if ( !at->at_names ) {
865 if ( *code != LDAP_SCHERR_OUTOFMEM )
866 *code = LDAP_SCHERR_BADNAME;
871 } else if ( !strcmp(sval,"DESC") ) {
873 *code = LDAP_SCHERR_DUPOPT;
880 kind = get_token(&ss,&sval);
881 if ( kind != TK_QDSTRING ) {
882 *code = LDAP_SCHERR_UNEXPTOKEN;
889 } else if ( !strcmp(sval,"OBSOLETE") ) {
890 if ( seen_obsolete ) {
891 *code = LDAP_SCHERR_DUPOPT;
897 at->at_obsolete = LDAP_SCHEMA_YES;
899 } else if ( !strcmp(sval,"SUP") ) {
901 *code = LDAP_SCHERR_DUPOPT;
907 at->at_sup_oid = parse_woid(&ss,code);
908 if ( !at->at_sup_oid ) {
913 } else if ( !strcmp(sval,"EQUALITY") ) {
914 if ( seen_equality ) {
915 *code = LDAP_SCHERR_DUPOPT;
921 at->at_equality_oid = parse_woid(&ss,code);
922 if ( !at->at_equality_oid ) {
927 } else if ( !strcmp(sval,"ORDERING") ) {
928 if ( seen_ordering ) {
929 *code = LDAP_SCHERR_DUPOPT;
935 at->at_ordering_oid = parse_woid(&ss,code);
936 if ( !at->at_ordering_oid ) {
941 } else if ( !strcmp(sval,"SUBSTR") ) {
943 *code = LDAP_SCHERR_DUPOPT;
949 at->at_substr_oid = parse_woid(&ss,code);
950 if ( !at->at_substr_oid ) {
955 } else if ( !strcmp(sval,"SYNTAX") ) {
957 *code = LDAP_SCHERR_DUPOPT;
964 at->at_syntax_oid = parse_noidlen(&ss,code,&at->at_syntax_len);
965 if ( !at->at_syntax_oid ) {
970 } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
971 if ( at->at_single_value ) {
972 *code = LDAP_SCHERR_DUPOPT;
977 at->at_single_value = LDAP_SCHEMA_YES;
979 } else if ( !strcmp(sval,"COLLECTIVE") ) {
980 if ( at->at_collective ) {
981 *code = LDAP_SCHERR_DUPOPT;
986 at->at_collective = LDAP_SCHEMA_YES;
988 } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
989 if ( at->at_no_user_mod ) {
990 *code = LDAP_SCHERR_DUPOPT;
995 at->at_no_user_mod = LDAP_SCHEMA_YES;
997 } else if ( !strcmp(sval,"USAGE") ) {
999 *code = LDAP_SCHERR_DUPOPT;
1006 kind = get_token(&ss,&sval);
1007 if ( kind != TK_BAREWORD ) {
1008 *code = LDAP_SCHERR_UNEXPTOKEN;
1013 if ( !strcasecmp(sval,"userApplications") )
1014 at->at_usage = LDAP_SCHEMA_USER_APPLICATIONS;
1015 else if ( !strcasecmp(sval,"directoryOperation") )
1016 at->at_usage = LDAP_SCHEMA_DIRECTORY_OPERATION;
1017 else if ( !strcasecmp(sval,"distributedOperation") )
1018 at->at_usage = LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1019 else if ( !strcasecmp(sval,"dSAOperation") )
1020 at->at_usage = LDAP_SCHEMA_DSA_OPERATION;
1022 *code = LDAP_SCHERR_UNEXPTOKEN;
1028 *code = LDAP_SCHERR_UNEXPTOKEN;
1035 *code = LDAP_SCHERR_UNEXPTOKEN;
1044 free_oc(LDAP_OBJECT_CLASS * oc)
1046 ldap_memfree(oc->oc_oid);
1047 charray_free(oc->oc_names);
1048 ldap_memfree(oc->oc_desc);
1049 charray_free(oc->oc_sup_oids);
1050 charray_free(oc->oc_at_oids_must);
1051 charray_free(oc->oc_at_oids_may);
1056 ldap_str2objectclass( char * s, int * code, char ** errp )
1063 int seen_obsolete = 0;
1068 LDAP_OBJECT_CLASS * oc;
1071 oc = calloc(1,sizeof(LDAP_OBJECT_CLASS));
1074 *code = LDAP_SCHERR_OUTOFMEM;
1078 kind = get_token(&ss,&sval);
1079 if ( kind != TK_LEFTPAREN ) {
1080 *code = LDAP_SCHERR_NOLEFTPAREN;
1086 oc->oc_oid = parse_numericoid(&ss,code);
1087 if ( !oc->oc_oid ) {
1095 * Beyond this point we will be liberal an accept the items
1099 kind = get_token(&ss,&sval);
1102 *code = LDAP_SCHERR_NORIGHTPAREN;
1109 if ( !strcmp(sval,"NAME") ) {
1111 *code = LDAP_SCHERR_DUPOPT;
1117 oc->oc_names = parse_qdescrs(&ss,code);
1118 if ( !oc->oc_names ) {
1119 if ( *code != LDAP_SCHERR_OUTOFMEM )
1120 *code = LDAP_SCHERR_BADNAME;
1125 } else if ( !strcmp(sval,"DESC") ) {
1127 *code = LDAP_SCHERR_DUPOPT;
1134 kind = get_token(&ss,&sval);
1135 if ( kind != TK_QDSTRING ) {
1136 *code = LDAP_SCHERR_UNEXPTOKEN;
1143 } else if ( !strcmp(sval,"OBSOLETE") ) {
1144 if ( seen_obsolete ) {
1145 *code = LDAP_SCHERR_DUPOPT;
1151 oc->oc_obsolete = LDAP_SCHEMA_YES;
1153 } else if ( !strcmp(sval,"SUP") ) {
1155 *code = LDAP_SCHERR_DUPOPT;
1161 /* Netscape DS is broken or I have not
1162 understood the syntax. */
1163 /* oc->oc_sup_oids = parse_oids(&ss,code); */
1164 oc->oc_sup_oids = parse_qdescrs(&ss,code);
1165 if ( !oc->oc_sup_oids ) {
1170 } else if ( !strcmp(sval,"ABSTRACT") ) {
1172 *code = LDAP_SCHERR_DUPOPT;
1178 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1180 } else if ( !strcmp(sval,"STRUCTURAL") ) {
1182 *code = LDAP_SCHERR_DUPOPT;
1188 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1190 } else if ( !strcmp(sval,"AUXILIARY") ) {
1192 *code = LDAP_SCHERR_DUPOPT;
1198 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1200 } else if ( !strcmp(sval,"MUST") ) {
1202 *code = LDAP_SCHERR_DUPOPT;
1208 oc->oc_at_oids_must = parse_oids(&ss,code);
1209 if ( !oc->oc_at_oids_must ) {
1215 } else if ( !strcmp(sval,"MAY") ) {
1217 *code = LDAP_SCHERR_DUPOPT;
1223 oc->oc_at_oids_may = parse_oids(&ss,code);
1224 if ( !oc->oc_at_oids_may ) {
1231 *code = LDAP_SCHERR_UNEXPTOKEN;
1238 *code = LDAP_SCHERR_UNEXPTOKEN;
1246 static char *err2text[] = {
1250 "Missing opening parenthesis",
1251 "Missing closing parenthesis",
1260 ldap_scherr2str(int code)
1262 if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1263 return "Unknown error";
1265 return err2text[code];