X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Fschema.c;h=cf7a9923c47c807cfeae7a0a51dde41c51c3f5b0;hb=c1a257a83f3d8b9565238b5f9b8cad39a6194f63;hp=6ff131316e70796a7a72fd7a56597ea777c77734;hpb=b2b3581bc1b5dfcd973c06d0e66848840f07b73d;p=openldap diff --git a/libraries/libldap/schema.c b/libraries/libldap/schema.c index 6ff131316e..cf7a9923c4 100644 --- a/libraries/libldap/schema.c +++ b/libraries/libldap/schema.c @@ -1,35 +1,74 @@ +/* $OpenLDAP$ */ /* - * Copyright 1999 The OpenLDAP Foundation, All Rights Reserved. + * Copyright 1999-2000 The OpenLDAP Foundation, All Rights Reserved. * COPYING RESTRICTIONS APPLY, see COPYRIGHT file - * + */ +/* * schema.c: parsing routines used by servers and clients to process * schema definitions */ #include "portable.h" -#include +#include +#include + #include -#ifdef HAVE_MALLOC_H -#include -#endif -#include -#include +#include + +#include "ldap-int.h" + #include -#include + +static LDAP_CONST char * +choose_name( char *names[], LDAP_CONST char *fallback ) +{ + return( (names != NULL && names[0] != NULL) ? names[0] : fallback ); +} + +LDAP_CONST char * +ldap_syntax2name( LDAPSyntax * syn ) +{ + return( syn->syn_oid ); +} + +LDAP_CONST char * +ldap_matchingrule2name( LDAPMatchingRule * mr ) +{ + return( choose_name( mr->mr_names, mr->mr_oid ) ); +} + +LDAP_CONST char * +ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru ) +{ + return( choose_name( mru->mru_names, mru->mru_oid ) ); +} + +LDAP_CONST char * +ldap_attributetype2name( LDAPAttributeType * at ) +{ + return( choose_name( at->at_names, at->at_oid ) ); +} + +LDAP_CONST char * +ldap_objectclass2name( LDAPObjectClass * oc ) +{ + return( choose_name( oc->oc_names, oc->oc_oid ) ); +} + /* * When pretty printing the entities we will be appending to a buffer. * Since checking for overflow, realloc'ing and checking if no error - * is extremely boring, we will use a pretection layer that will let + * is extremely boring, we will use a protection layer that will let * us blissfully ignore the error until the end. This layer is * implemented with the help of the next type. */ typedef struct safe_string { char * val; - int size; - int pos; + ber_len_t size; + ber_len_t pos; int at_whsp; } safe_string; @@ -38,32 +77,36 @@ new_safe_string(int size) { safe_string * ss; - ss = malloc(sizeof(safe_string)); + ss = LDAP_MALLOC(sizeof(safe_string)); if ( !ss ) return(NULL); - ss->size = size; - ss->pos = 0; - ss->val = malloc(size); - ss->at_whsp = 0; + + ss->val = LDAP_MALLOC(size); if ( !ss->val ) { - free(ss); + LDAP_FREE(ss); return(NULL); } + + ss->size = size; + ss->pos = 0; + ss->at_whsp = 0; + return ss; } -void +static void safe_string_free(safe_string * ss) { if ( !ss ) return; - ldap_memfree(ss->val); - ldap_memfree(ss); + LDAP_FREE(ss->val); + LDAP_FREE(ss); } static char * safe_string_val(safe_string * ss) { + ss->val[ss->pos] = '\0'; return(ss->val); } @@ -83,17 +126,21 @@ append_to_safe_string(safe_string * ss, char * s) /* We always make sure there is at least one position available */ if ( ss->pos + l >= ss->size-1 ) { ss->size *= 2; - temp = realloc(ss->val, ss->size); + if ( ss->pos + l >= ss->size-1 ) { + ss->size = ss->pos + l + 1; + } + + temp = LDAP_REALLOC(ss->val, ss->size); if ( !temp ) { /* Trouble, out of memory */ - free(ss->val); + LDAP_FREE(ss->val); return -1; } ss->val = temp; } strncpy(&ss->val[ss->pos], s, l); ss->pos += l; - if ( ss->pos > 0 && ss->val[ss->pos-1] == ' ' ) + if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) ) ss->at_whsp = 1; else ss->at_whsp = 0; @@ -119,7 +166,10 @@ print_whsp(safe_string *ss) static int print_numericoid(safe_string *ss, char *s) { - return(append_to_safe_string(ss,s)); + if ( s ) + return(append_to_safe_string(ss,s)); + else + return(append_to_safe_string(ss,"")); } /* This one is identical to print_qdescr */ @@ -153,7 +203,7 @@ print_qdescrlist(safe_string *ss, char **sa) ret = print_qdescr(ss,*sp); } /* If the list was empty, we return zero that is potentially - * incorrect, but since we will still appending things, the + * incorrect, but since we will be still appending things, the * overflow will be detected later. Maybe FIX. */ return(ret); @@ -164,13 +214,15 @@ print_qdescrs(safe_string *ss, char **sa) { /* The only way to represent an empty list is as a qdescrlist * so, if the list is empty we treat it as a long list. - * Really, this is what the syntax mandates. + * Really, this is what the syntax mandates. We should not + * be here if the list was empty, but if it happens, a label + * has already been output and we cannot undo it. */ if ( !sa[0] || ( sa[0] && sa[1] ) ) { print_whsp(ss); - print_literal(ss,"("); + print_literal(ss,"("/*)*/); print_qdescrlist(ss,sa); - print_literal(ss,")"); + print_literal(ss,/*(*/")"); return(print_whsp(ss)); } else { return(print_qdescr(ss,*sa)); @@ -201,10 +253,10 @@ static int print_oids(safe_string *ss, char **sa) { if ( sa[0] && sa[1] ) { - print_literal(ss,"("); + print_literal(ss,"("/*)*/); print_oidlist(ss,sa); print_whsp(ss); - return(print_literal(ss,")")); + return(print_literal(ss,/*(*/")")); } else { return(print_woid(ss,*sa)); } @@ -224,8 +276,157 @@ print_noidlen(safe_string *ss, char *s, int l) return(ret); } +static int +print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions) +{ + LDAPSchemaExtensionItem **ext; + + if ( extensions ) { + print_whsp(ss); + for ( ext = extensions; *ext != NULL; ext++ ) { + print_literal(ss, (*ext)->lsei_name); + print_whsp(ss); + /* Should be print_qdstrings */ + print_qdescrs(ss, (*ext)->lsei_values); + print_whsp(ss); + } + } + + return 0; +} + +char * +ldap_syntax2str( const LDAPSyntax * syn ) +{ + safe_string * ss; + char * retstring; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, syn->syn_oid); + print_whsp(ss); + + if ( syn->syn_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,syn->syn_desc); + } + + print_whsp(ss); + + print_extensions(ss, syn->syn_extensions); + + print_literal(ss,/*(*/ ")"); + + retstring = LDAP_STRDUP(safe_string_val(ss)); + safe_string_free(ss); + return(retstring); +} + +char * +ldap_matchingrule2str( const LDAPMatchingRule * mr ) +{ + safe_string * ss; + char * retstring; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"(" /*)*/); + print_whsp(ss); + + print_numericoid(ss, mr->mr_oid); + print_whsp(ss); + + if ( mr->mr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,mr->mr_names); + } + + if ( mr->mr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,mr->mr_desc); + } + + if ( mr->mr_obsolete == LDAP_SCHEMA_YES ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( mr->mr_syntax_oid ) { + print_literal(ss,"SYNTAX"); + print_whsp(ss); + print_literal(ss, mr->mr_syntax_oid); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, mr->mr_extensions); + + print_literal(ss,/*(*/")"); + + retstring = LDAP_STRDUP(safe_string_val(ss)); + safe_string_free(ss); + return(retstring); +} + +char * +ldap_matchingruleuse2str( const LDAPMatchingRuleUse * mru ) +{ + safe_string * ss; + char * retstring; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"(" /*)*/); + print_whsp(ss); + + print_numericoid(ss, mru->mru_oid); + print_whsp(ss); + + if ( mru->mru_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,mru->mru_names); + } + + if ( mru->mru_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,mru->mru_desc); + } + + if ( mru->mru_obsolete == LDAP_SCHEMA_YES ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( mru->mru_applies_oids ) { + print_literal(ss,"APPLIES"); + print_whsp(ss); + print_oids(ss, mru->mru_applies_oids); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, mru->mru_extensions); + + print_literal(ss,/*(*/")"); + + retstring = LDAP_STRDUP(safe_string_val(ss)); + safe_string_free(ss); + return(retstring); +} + char * -ldap_objectclass2str( LDAP_OBJECT_CLASS * oc ) +ldap_objectclass2str( const LDAPObjectClass * oc ) { safe_string * ss; char * retstring; @@ -234,7 +435,7 @@ ldap_objectclass2str( LDAP_OBJECT_CLASS * oc ) if ( !ss ) return NULL; - print_literal(ss,"("); + print_literal(ss,"("/*)*/); print_whsp(ss); print_numericoid(ss, oc->oc_oid); @@ -250,24 +451,26 @@ ldap_objectclass2str( LDAP_OBJECT_CLASS * oc ) print_qdstring(ss,oc->oc_desc); } - if ( oc->oc_obsolete ) { + if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) { print_literal(ss, "OBSOLETE"); print_whsp(ss); } if ( oc->oc_sup_oids ) { print_literal(ss,"SUP"); + print_whsp(ss); print_oids(ss,oc->oc_sup_oids); + print_whsp(ss); } switch (oc->oc_kind) { - case 0: + case LDAP_SCHEMA_ABSTRACT: print_literal(ss,"ABSTRACT"); break; - case 1: + case LDAP_SCHEMA_STRUCTURAL: print_literal(ss,"STRUCTURAL"); break; - case 2: + case LDAP_SCHEMA_AUXILIARY: print_literal(ss,"AUXILIARY"); break; default: @@ -291,15 +494,18 @@ ldap_objectclass2str( LDAP_OBJECT_CLASS * oc ) } print_whsp(ss); - print_literal(ss,")"); - retstring = safe_string_val(ss); + print_extensions(ss, oc->oc_extensions); + + print_literal(ss, /*(*/")"); + + retstring = LDAP_STRDUP(safe_string_val(ss)); safe_string_free(ss); return(retstring); } char * -ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at ) +ldap_attributetype2str( const LDAPAttributeType * at ) { safe_string * ss; char * retstring; @@ -308,7 +514,7 @@ ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at ) if ( !ss ) return NULL; - print_literal(ss,"("); + print_literal(ss,"("/*)*/); print_whsp(ss); print_numericoid(ss, at->at_oid); @@ -324,7 +530,7 @@ ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at ) print_qdstring(ss,at->at_desc); } - if ( at->at_obsolete ) { + if ( at->at_obsolete == LDAP_SCHEMA_YES ) { print_literal(ss, "OBSOLETE"); print_whsp(ss); } @@ -351,35 +557,37 @@ ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at ) if ( at->at_syntax_oid ) { print_literal(ss,"SYNTAX"); + print_whsp(ss); print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len); + print_whsp(ss); } - if ( at->at_single_value ) { + if ( at->at_single_value == LDAP_SCHEMA_YES ) { print_literal(ss,"SINGLE-VALUE"); print_whsp(ss); } - if ( at->at_collective ) { + if ( at->at_collective == LDAP_SCHEMA_YES ) { print_literal(ss,"COLLECTIVE"); print_whsp(ss); } - if ( at->at_no_user_mod ) { + if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) { print_literal(ss,"NO-USER-MODIFICATION"); print_whsp(ss); } - if ( at->at_usage ) { + if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) { print_literal(ss,"USAGE"); print_whsp(ss); switch (at->at_usage) { - case 1: + case LDAP_SCHEMA_DIRECTORY_OPERATION: print_literal(ss,"directoryOperation"); break; - case 2: + case LDAP_SCHEMA_DISTRIBUTED_OPERATION: print_literal(ss,"distributedOperation"); break; - case 3: + case LDAP_SCHEMA_DSA_OPERATION: print_literal(ss,"dSAOperation"); break; default: @@ -389,33 +597,37 @@ ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at ) } print_whsp(ss); - print_literal(ss,")"); - retstring = safe_string_val(ss); + print_extensions(ss, at->at_extensions); + + print_literal(ss,/*(*/")"); + + retstring = LDAP_STRDUP(safe_string_val(ss)); safe_string_free(ss); return(retstring); } /* - * This is ripped from servers/slapd/charray.c that should be promoted - * to -lldap or something so that it is used everywhere. + * Now come the parsers. There is one parser for each entity type: + * objectclasses, attributetypes, etc. + * + * Each of them is written as a recursive-descent parser, except that + * none of them is really recursive. But the idea is kept: there + * is one routine per non-terminal that eithers gobbles lexical tokens + * or calls lower-level routines, etc. + * + * The scanner is implemented in the routine get_token. Actually, + * get_token is more than a scanner and will return tokens that are + * in fact non-terminals in the grammar. So you can see the whole + * approach as the combination of a low-level bottom-up recognizer + * combined with a scanner and a number of top-down parsers. Or just + * consider that the real grammars recognized by the parsers are not + * those of the standards. As a matter of fact, our parsers are more + * liberal than the spec when there is no ambiguity. + * + * The difference is pretty academic (modulo bugs or incorrect + * interpretation of the specs). */ -static void -charray_free( char **array ) -{ - char **a; - - if ( array == NULL ) { - return; - } - - for ( a = array; *a != NULL; a++ ) { - if ( *a != NULL ) { - free( *a ); - } - } - free( (char *) array ); -} #define TK_NOENDQUOTE -2 #define TK_OUTOFMEM -1 @@ -429,18 +641,19 @@ charray_free( char **array ) #define TK_QDESCR TK_QDSTRING struct token { - int type; - char *sval; + int type; + char *sval; }; static int -get_token(char ** sp, char ** token_val) +get_token(const char ** sp, char ** token_val) { int kind; - char * p; - char * q; + const char * p; + const char * q; char * res; + *token_val = NULL; switch (**sp) { case '\0': kind = TK_EOS; @@ -466,7 +679,7 @@ get_token(char ** sp, char ** token_val) (*sp)++; if ( **sp == '\'' ) { q = *sp; - res = malloc(q-p+1); + res = LDAP_MALLOC(q-p+1); if ( !res ) { kind = TK_OUTOFMEM; } else { @@ -482,10 +695,15 @@ get_token(char ** sp, char ** token_val) default: kind = TK_BAREWORD; p = *sp; - while ( !isspace(**sp) && **sp != '\0' ) + while ( !LDAP_SPACE(**sp) && + **sp != '(' && + **sp != ')' && + **sp != '$' && + **sp != '\'' && + **sp != '\0' ) (*sp)++; q = *sp; - res = malloc(q-p+1); + res = LDAP_MALLOC(q-p+1); if ( !res ) { kind = TK_OUTOFMEM; } else { @@ -503,9 +721,9 @@ get_token(char ** sp, char ** token_val) /* Gobble optional whitespace */ static void -parse_whsp(char **sp) +parse_whsp(const char **sp) { - while (isspace(**sp)) + while (LDAP_SPACE(**sp)) (*sp)++; } @@ -518,43 +736,62 @@ parse_whsp(char **sp) */ /* Parse a sequence of dot-separated decimal strings */ -static char * -parse_numericoid(char **sp, int *code) +char * +parse_numericoid(const char **sp, int *code, const int flags) { char * res; - char * start = *sp; + const char * start = *sp; int len; + int quoted = 0; - /* Each iteration of this loops gets one decimal string */ + /* Netscape puts the SYNTAX value in quotes (incorrectly) */ + if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) { + quoted = 1; + (*sp)++; + start++; + } + /* Each iteration of this loop gets one decimal string */ while (**sp) { - if ( !isdigit(**sp) ) { - /* Initial char is not a digit or char after dot is not a digit */ - *code = SCHEMA_ERR_NODIGIT; + if ( !LDAP_DIGIT(**sp) ) { + /* + * Initial char is not a digit or char after dot is + * not a digit + */ + *code = LDAP_SCHERR_NODIGIT; return NULL; } (*sp)++; - while ( isdigit(**sp) ) + while ( LDAP_DIGIT(**sp) ) (*sp)++; if ( **sp != '.' ) break; /* Otherwise, gobble the dot and loop again */ (*sp)++; } - /* At this point, *sp points at the char past the numericoid. Perfect. */ + /* Now *sp points at the char past the numericoid. Perfect. */ len = *sp - start; - res = malloc(len+1); + res = LDAP_MALLOC(len+1); if (!res) { - *code = SCHEMA_ERR_OUTOFMEM; - return(NULL); + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); } strncpy(res,start,len); res[len] = '\0'; + if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) { + if ( **sp == '\'' ) { + (*sp)++; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(res); + return NULL; + } + } return(res); } /* Parse a qdescr or a list of them enclosed in () */ static char ** -parse_qdescrs(char **sp, int *code) +parse_qdescrs(const char **sp, int *code) { char ** res; char ** res1; @@ -568,9 +805,9 @@ parse_qdescrs(char **sp, int *code) if ( kind == TK_LEFTPAREN ) { /* Let's presume there will be at least 2 entries */ size = 3; - res = calloc(3,sizeof(char *)); + res = LDAP_CALLOC(3,sizeof(char *)); if ( !res ) { - *code = SCHEMA_ERR_OUTOFMEM; + *code = LDAP_SCHERR_OUTOFMEM; return NULL; } pos = 0; @@ -582,10 +819,11 @@ parse_qdescrs(char **sp, int *code) if ( kind == TK_QDESCR ) { if ( pos == size-2 ) { size++; - res1 = realloc(res,size*sizeof(char *)); + res1 = LDAP_REALLOC(res,size*sizeof(char *)); if ( !res1 ) { - charray_free(res); - *code = SCHEMA_ERR_OUTOFMEM; + LDAP_VFREE(res); + LDAP_FREE(sval); + *code = LDAP_SCHERR_OUTOFMEM; return(NULL); } res = res1; @@ -594,8 +832,9 @@ parse_qdescrs(char **sp, int *code) pos++; parse_whsp(sp); } else { - charray_free(res); - *code = SCHEMA_ERR_UNEXPTOKEN; + LDAP_VFREE(res); + LDAP_FREE(sval); + *code = LDAP_SCHERR_UNEXPTOKEN; return(NULL); } } @@ -603,9 +842,9 @@ parse_qdescrs(char **sp, int *code) parse_whsp(sp); return(res); } else if ( kind == TK_QDESCR ) { - res = calloc(2,sizeof(char *)); + res = LDAP_CALLOC(2,sizeof(char *)); if ( !res ) { - *code = SCHEMA_ERR_OUTOFMEM; + *code = LDAP_SCHERR_OUTOFMEM; return NULL; } res[0] = sval; @@ -613,14 +852,81 @@ parse_qdescrs(char **sp, int *code) parse_whsp(sp); return res; } else { - *code = SCHEMA_ERR_BADNAME; + LDAP_FREE(sval); + *code = LDAP_SCHERR_BADNAME; + return NULL; + } +} + +/* Parse a woid */ +static char * +parse_woid(const char **sp, int *code) +{ + char * sval; + int kind; + + parse_whsp(sp); + kind = get_token(sp, &sval); + if ( kind != TK_BAREWORD ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_UNEXPTOKEN; + return NULL; + } + parse_whsp(sp); + return sval; +} + +/* Parse a noidlen */ +static char * +parse_noidlen(const char **sp, int *code, int *len, int allow_quoted) +{ + char * sval; + int quoted = 0; + + *len = 0; + /* Netscape puts the SYNTAX value in quotes (incorrectly) */ + if ( allow_quoted && **sp == '\'' ) { + quoted = 1; + (*sp)++; + } + sval = parse_numericoid(sp, code, 0); + if ( !sval ) { return NULL; } + if ( **sp == '{' /*}*/ ) { + (*sp)++; + *len = atoi(*sp); + while ( LDAP_DIGIT(**sp) ) + (*sp)++; + if ( **sp != /*{*/ '}' ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + return NULL; + } + (*sp)++; + } + if ( allow_quoted && quoted ) { + if ( **sp == '\'' ) { + (*sp)++; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + return NULL; + } + } + return sval; } +/* + * Next routine will accept a qdstring in place of an oid if + * allow_quoted is set. This is necessary to interoperate with + * Netscape Directory server that will improperly quote each oid (at + * least those of the descr kind) in the SUP clause. + */ + /* Parse a woid or a $-separated list of them enclosed in () */ static char ** -parse_oids(char **sp, int *code) +parse_oids(const char **sp, int *code, const int allow_quoted) { char ** res; char ** res1; @@ -631,7 +937,7 @@ parse_oids(char **sp, int *code) /* * Strictly speaking, doing this here accepts whsp before the - * ( at the begining of an oidlist, but his is harmless. Also, + * ( at the begining of an oidlist, but this is harmless. Also, * we are very liberal in what we accept as an OID. Maybe * refine later. */ @@ -640,20 +946,22 @@ parse_oids(char **sp, int *code) if ( kind == TK_LEFTPAREN ) { /* Let's presume there will be at least 2 entries */ size = 3; - res = calloc(3,sizeof(char *)); + res = LDAP_CALLOC(3,sizeof(char *)); if ( !res ) { - *code = SCHEMA_ERR_OUTOFMEM; + *code = LDAP_SCHERR_OUTOFMEM; return NULL; } pos = 0; parse_whsp(sp); kind = get_token(sp,&sval); - if ( kind == TK_BAREWORD ) { + if ( kind == TK_BAREWORD || + ( allow_quoted && kind == TK_QDSTRING ) ) { res[pos] = sval; pos++; } else { - *code = SCHEMA_ERR_UNEXPTOKEN; - charray_free(res); + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); return NULL; } parse_whsp(sp); @@ -664,38 +972,45 @@ parse_oids(char **sp, int *code) if ( kind == TK_DOLLAR ) { parse_whsp(sp); kind = get_token(sp,&sval); - if ( kind == TK_BAREWORD ) { + if ( kind == TK_BAREWORD || + ( allow_quoted && + kind == TK_QDSTRING ) ) { if ( pos == size-2 ) { size++; - res1 = realloc(res,size*sizeof(char *)); + res1 = LDAP_REALLOC(res,size*sizeof(char *)); if ( !res1 ) { - charray_free(res); - *code = SCHEMA_ERR_OUTOFMEM; - return(NULL); + LDAP_FREE(sval); + LDAP_VFREE(res); + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); } res = res1; } res[pos] = sval; pos++; } else { - *code = SCHEMA_ERR_UNEXPTOKEN; - charray_free(res); + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); return NULL; } parse_whsp(sp); } else { - *code = SCHEMA_ERR_UNEXPTOKEN; - charray_free(res); + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); return NULL; } } res[pos] = NULL; parse_whsp(sp); return(res); - } else if ( kind == TK_BAREWORD ) { - res = calloc(2,sizeof(char *)); + } else if ( kind == TK_BAREWORD || + ( allow_quoted && kind == TK_QDSTRING ) ) { + res = LDAP_CALLOC(2,sizeof(char *)); if ( !res ) { - *code = SCHEMA_ERR_OUTOFMEM; + LDAP_FREE(sval); + *code = LDAP_SCHERR_OUTOFMEM; return NULL; } res[0] = sval; @@ -703,212 +1018,1253 @@ parse_oids(char **sp, int *code) parse_whsp(sp); return res; } else { - *code = SCHEMA_ERR_BADNAME; + LDAP_FREE(sval); + *code = LDAP_SCHERR_BADNAME; return NULL; } } +static int +add_extension(LDAPSchemaExtensionItem ***extensions, + char * name, char ** values) +{ + int n; + LDAPSchemaExtensionItem **tmp, *ext; + + ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem)); + if ( !ext ) + return 1; + ext->lsei_name = name; + ext->lsei_values = values; + + if ( !*extensions ) { + *extensions = + LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *)); + if ( !*extensions ) + return 1; + n = 0; + } else { + for ( n=0; (*extensions)[n] != NULL; n++ ) + ; + tmp = LDAP_REALLOC(*extensions, + (n+2)*sizeof(LDAPSchemaExtensionItem *)); + if ( !tmp ) + return 1; + *extensions = tmp; + } + (*extensions)[n] = ext; + (*extensions)[n+1] = NULL; + return 0; +} + static void -free_oc(LDAP_OBJECT_CLASS * oc) +free_extensions(LDAPSchemaExtensionItem **extensions) +{ + LDAPSchemaExtensionItem **ext; + + if ( extensions ) { + for ( ext = extensions; *ext != NULL; ext++ ) { + LDAP_FREE((*ext)->lsei_name); + LDAP_VFREE((*ext)->lsei_values); + LDAP_FREE(*ext); + } + LDAP_FREE(extensions); + } +} + +void +ldap_syntax_free( LDAPSyntax * syn ) { - ldap_memfree(oc->oc_oid); - charray_free(oc->oc_names); - ldap_memfree(oc->oc_desc); - charray_free(oc->oc_sup_oids); - charray_free(oc->oc_at_oids_must); - charray_free(oc->oc_at_oids_may); - ldap_memfree(oc); + LDAP_FREE(syn->syn_oid); + LDAP_VFREE(syn->syn_names); + LDAP_FREE(syn->syn_desc); + free_extensions(syn->syn_extensions); + LDAP_FREE(syn); } -LDAP_OBJECT_CLASS * -ldap_str2objectclass( char * s, int * code, char ** errp ) +LDAPSyntax * +ldap_str2syntax( const char * s, int * code, const char ** errp, const int flags ) { int kind; - char * ss = s; + const char * ss = s; char * sval; int seen_name = 0; int seen_desc = 0; - int seen_obsolete = 0; - int seen_sup = 0; - int seen_kind = 0; - int seen_must = 0; - int seen_may = 0; - LDAP_OBJECT_CLASS * oc; + LDAPSyntax * syn; + char ** ext_vals; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } *errp = s; - oc = calloc(1,sizeof(LDAP_OBJECT_CLASS)); + syn = LDAP_CALLOC(1,sizeof(LDAPSyntax)); - if ( !oc ) { - *code = SCHEMA_ERR_OUTOFMEM; + if ( !syn ) { + *code = LDAP_SCHERR_OUTOFMEM; return NULL; } kind = get_token(&ss,&sval); if ( kind != TK_LEFTPAREN ) { - *code = SCHEMA_ERR_NOLEFTPAREN; - free_oc(oc); + LDAP_FREE(sval); + *code = LDAP_SCHERR_NOLEFTPAREN; + ldap_syntax_free(syn); return NULL; } parse_whsp(&ss); - oc->oc_oid = parse_numericoid(&ss,code); - if ( !oc->oc_oid ) { + syn->syn_oid = parse_numericoid(&ss,code,0); + if ( !syn->syn_oid ) { *errp = ss; - free_oc(oc); + ldap_syntax_free(syn); return NULL; } parse_whsp(&ss); /* - * Beyond this point we will be liberal an accept the items + * Beyond this point we will be liberal and accept the items * in any order. */ while (1) { kind = get_token(&ss,&sval); switch (kind) { case TK_EOS: - *code = SCHEMA_ERR_NORIGHTPAREN; + *code = LDAP_SCHERR_NORIGHTPAREN; *errp = ss; - free_oc(oc); + ldap_syntax_free(syn); return NULL; case TK_RIGHTPAREN: - return oc; + return syn; case TK_BAREWORD: if ( !strcmp(sval,"NAME") ) { + LDAP_FREE(sval); if ( seen_name ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_syntax_free(syn); return(NULL); } seen_name = 1; - oc->oc_names = parse_qdescrs(&ss,code); - if ( !oc->oc_names ) { - if ( *code != SCHEMA_ERR_OUTOFMEM ) - *code = SCHEMA_ERR_BADNAME; + syn->syn_names = parse_qdescrs(&ss,code); + if ( !syn->syn_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; *errp = ss; - free_oc(oc); + ldap_syntax_free(syn); return NULL; } } else if ( !strcmp(sval,"DESC") ) { + LDAP_FREE(sval); if ( seen_desc ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_syntax_free(syn); return(NULL); } seen_desc = 1; parse_whsp(&ss); kind = get_token(&ss,&sval); if ( kind != TK_QDSTRING ) { - *code = SCHEMA_ERR_UNEXPTOKEN; + *code = LDAP_SCHERR_UNEXPTOKEN; *errp = ss; - free(oc); + LDAP_FREE(sval); + ldap_syntax_free(syn); return NULL; } - oc->oc_desc = sval; + syn->syn_desc = sval; parse_whsp(&ss); - } else if ( !strcmp(sval,"OBSOLETE") ) { - if ( seen_obsolete ) { - *code = SCHEMA_ERR_DUPOPT; + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { *errp = ss; - free_oc(oc); - return(NULL); + ldap_syntax_free(syn); + return NULL; } - seen_obsolete = 1; - oc->oc_obsolete = 1; - parse_whsp(&ss); - } else if ( !strcmp(sval,"SUP") ) { - if ( seen_sup ) { - *code = SCHEMA_ERR_DUPOPT; + if ( add_extension(&syn->syn_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; *errp = ss; - free_oc(oc); - return(NULL); + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; } - seen_sup = 1; - /* Netscape DS is broken or I have not - understood the syntax. */ - /* oc->oc_sup_oids = parse_oids(&ss,code); */ - oc->oc_sup_oids = parse_qdescrs(&ss,code); + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + } +} + +void +ldap_matchingrule_free( LDAPMatchingRule * mr ) +{ + LDAP_FREE(mr->mr_oid); + LDAP_VFREE(mr->mr_names); + LDAP_FREE(mr->mr_desc); + LDAP_FREE(mr->mr_syntax_oid); + free_extensions(mr->mr_extensions); + LDAP_FREE(mr); +} + +LDAPMatchingRule * +ldap_str2matchingrule( const char * s, int * code, const char ** errp, const int flags ) +{ + int kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_syntax = 0; + LDAPMatchingRule * mr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule)); + + if ( !mr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + + parse_whsp(&ss); + savepos = ss; + mr->mr_oid = parse_numericoid(&ss,code,flags); + if ( !mr->mr_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcmp(sval, "NAME") || + !strcmp(sval, "DESC") || + !strcmp(sval, "OBSOLETE") || + !strcmp(sval, "SYNTAX") || + !strncmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else { + /* Non-numerical OID, ignore */ + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + case TK_RIGHTPAREN: + return mr; + case TK_BAREWORD: + if ( !strcmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_name = 1; + mr->mr_names = parse_qdescrs(&ss,code); + if ( !mr->mr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + } else if ( !strcmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + mr->mr_desc = sval; + parse_whsp(&ss); + } else if ( !strcmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_obsolete = 1; + mr->mr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"SYNTAX") ) { + LDAP_FREE(sval); + if ( seen_syntax ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_syntax = 1; + parse_whsp(&ss); + mr->mr_syntax_oid = + parse_numericoid(&ss,code,flags); + if ( !mr->mr_syntax_oid ) { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + if ( add_extension(&mr->mr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + } +} + +void +ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru ) +{ + LDAP_FREE(mru->mru_oid); + LDAP_VFREE(mru->mru_names); + LDAP_FREE(mru->mru_desc); + LDAP_VFREE(mru->mru_applies_oids); + free_extensions(mru->mru_extensions); + LDAP_FREE(mru); +} + +LDAPMatchingRuleUse * +ldap_str2matchingruleuse( const char * s, int * code, const char ** errp, const int flags ) +{ + int kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_applies = 0; + LDAPMatchingRuleUse * mru; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse)); + + if ( !mru ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + + parse_whsp(&ss); + savepos = ss; + mru->mru_oid = parse_numericoid(&ss,code,flags); + if ( !mru->mru_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcmp(sval, "NAME") || + !strcmp(sval, "DESC") || + !strcmp(sval, "OBSOLETE") || + !strcmp(sval, "APPLIES") || + !strncmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else { + /* Non-numerical OID, ignore */ + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + case TK_RIGHTPAREN: + return mru; + case TK_BAREWORD: + if ( !strcmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_name = 1; + mru->mru_names = parse_qdescrs(&ss,code); + if ( !mru->mru_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } else if ( !strcmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + mru->mru_desc = sval; + parse_whsp(&ss); + } else if ( !strcmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_obsolete = 1; + mru->mru_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"APPLIES") ) { + LDAP_FREE(sval); + if ( seen_applies ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_applies = 1; + mru->mru_applies_oids = parse_oids(&ss, + code, + flags); + if ( !mru->mru_applies_oids ) { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + if ( add_extension(&mru->mru_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + } +} + +void +ldap_attributetype_free(LDAPAttributeType * at) +{ + LDAP_FREE(at->at_oid); + LDAP_VFREE(at->at_names); + LDAP_FREE(at->at_desc); + LDAP_FREE(at->at_sup_oid); + LDAP_FREE(at->at_equality_oid); + LDAP_FREE(at->at_ordering_oid); + LDAP_FREE(at->at_substr_oid); + LDAP_FREE(at->at_syntax_oid); + free_extensions(at->at_extensions); + LDAP_FREE(at); +} + +LDAPAttributeType * +ldap_str2attributetype( const char * s, int * code, const char ** errp, const int flags ) +{ + int kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_sup = 0; + int seen_equality = 0; + int seen_ordering = 0; + int seen_substr = 0; + int seen_syntax = 0; + int seen_usage = 0; + LDAPAttributeType * at; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + at = LDAP_CALLOC(1,sizeof(LDAPAttributeType)); + + if ( !at ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + at->at_oid = parse_numericoid(&ss,code,0); + if ( !at->at_oid ) { + if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID + | LDAP_SCHEMA_ALLOW_OID_MACRO ) ) + && (ss == savepos) ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcmp(sval, "NAME") || + !strcmp(sval, "DESC") || + !strcmp(sval, "OBSOLETE") || + !strcmp(sval, "SUP") || + !strcmp(sval, "EQUALITY") || + !strcmp(sval, "ORDERING") || + !strcmp(sval, "SUBSTR") || + !strcmp(sval, "SYNTAX") || + !strcmp(sval, "SINGLE-VALUE") || + !strcmp(sval, "COLLECTIVE") || + !strcmp(sval, "NO-USER-MODIFICATION") || + !strcmp(sval, "USAGE") || + !strncmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags + & LDAP_SCHEMA_ALLOW_OID_MACRO) { + /* Non-numerical OID ... */ + int len = ss-savepos; + at->at_oid = LDAP_MALLOC(len+1); + strncpy(at->at_oid, savepos, len); + at->at_oid[len] = 0; + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + case TK_RIGHTPAREN: + return at; + case TK_BAREWORD: + if ( !strcmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_name = 1; + at->at_names = parse_qdescrs(&ss,code); + if ( !at->at_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + at->at_desc = sval; + parse_whsp(&ss); + } else if ( !strcmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_obsolete = 1; + at->at_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"SUP") ) { + LDAP_FREE(sval); + if ( seen_sup ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_sup = 1; + at->at_sup_oid = parse_woid(&ss,code); + if ( !at->at_sup_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcmp(sval,"EQUALITY") ) { + LDAP_FREE(sval); + if ( seen_equality ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_equality = 1; + at->at_equality_oid = parse_woid(&ss,code); + if ( !at->at_equality_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcmp(sval,"ORDERING") ) { + LDAP_FREE(sval); + if ( seen_ordering ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_ordering = 1; + at->at_ordering_oid = parse_woid(&ss,code); + if ( !at->at_ordering_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcmp(sval,"SUBSTR") ) { + LDAP_FREE(sval); + if ( seen_substr ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_substr = 1; + at->at_substr_oid = parse_woid(&ss,code); + if ( !at->at_substr_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcmp(sval,"SYNTAX") ) { + LDAP_FREE(sval); + if ( seen_syntax ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_syntax = 1; + parse_whsp(&ss); + savepos = ss; + at->at_syntax_oid = + parse_noidlen(&ss, + code, + &at->at_syntax_len, + flags); + if ( !at->at_syntax_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) { + kind = get_token(&ss,&sval); + if (kind == TK_BAREWORD) + { + char *sp = strchr(sval, '{'); + at->at_syntax_oid = sval; + if (sp) + { + *sp++ = 0; + at->at_syntax_len = atoi(sp); + while ( LDAP_DIGIT(*sp) ) + sp++; + if ( *sp != '}' ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + } + } else { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + parse_whsp(&ss); + } else if ( !strcmp(sval,"SINGLE-VALUE") ) { + LDAP_FREE(sval); + if ( at->at_single_value ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_single_value = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"COLLECTIVE") ) { + LDAP_FREE(sval); + if ( at->at_collective ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_collective = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) { + LDAP_FREE(sval); + if ( at->at_no_user_mod ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_no_user_mod = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"USAGE") ) { + LDAP_FREE(sval); + if ( seen_usage ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_usage = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_BAREWORD ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + if ( !strcasecmp(sval,"userApplications") ) + at->at_usage = + LDAP_SCHEMA_USER_APPLICATIONS; + else if ( !strcasecmp(sval,"directoryOperation") ) + at->at_usage = + LDAP_SCHEMA_DIRECTORY_OPERATION; + else if ( !strcasecmp(sval,"distributedOperation") ) + at->at_usage = + LDAP_SCHEMA_DISTRIBUTED_OPERATION; + else if ( !strcasecmp(sval,"dSAOperation") ) + at->at_usage = + LDAP_SCHEMA_DSA_OPERATION; + else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + LDAP_FREE(sval); + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + if ( add_extension(&at->at_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + } +} + +void +ldap_objectclass_free(LDAPObjectClass * oc) +{ + LDAP_FREE(oc->oc_oid); + LDAP_VFREE(oc->oc_names); + LDAP_FREE(oc->oc_desc); + LDAP_VFREE(oc->oc_sup_oids); + LDAP_VFREE(oc->oc_at_oids_must); + LDAP_VFREE(oc->oc_at_oids_may); + free_extensions(oc->oc_extensions); + LDAP_FREE(oc); +} + +LDAPObjectClass * +ldap_str2objectclass( const char * s, int * code, const char ** errp, const int flags ) +{ + int kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_sup = 0; + int seen_kind = 0; + int seen_must = 0; + int seen_may = 0; + LDAPObjectClass * oc; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass)); + + if ( !oc ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + oc->oc_oid = parse_numericoid(&ss,code,0); + if ( !oc->oc_oid ) { + if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcmp(sval, "NAME") || + !strcmp(sval, "DESC") || + !strcmp(sval, "OBSOLETE") || + !strcmp(sval, "SUP") || + !strcmp(sval, "ABSTRACT") || + !strcmp(sval, "STRUCTURAL") || + !strcmp(sval, "AUXILIARY") || + !strcmp(sval, "MUST") || + !strncmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags & + LDAP_SCHEMA_ALLOW_OID_MACRO ) { + /* Non-numerical OID, ignore */ + int len = ss-savepos; + oc->oc_oid = LDAP_MALLOC(len+1); + strncpy(oc->oc_oid, savepos, len); + oc->oc_oid[len] = 0; + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + case TK_RIGHTPAREN: + return oc; + case TK_BAREWORD: + if ( !strcmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_name = 1; + oc->oc_names = parse_qdescrs(&ss,code); + if ( !oc->oc_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + } else if ( !strcmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + oc->oc_desc = sval; + parse_whsp(&ss); + } else if ( !strcmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_obsolete = 1; + oc->oc_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcmp(sval,"SUP") ) { + LDAP_FREE(sval); + if ( seen_sup ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_sup = 1; + oc->oc_sup_oids = parse_oids(&ss, + code, + flags); if ( !oc->oc_sup_oids ) { *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return NULL; } } else if ( !strcmp(sval,"ABSTRACT") ) { + LDAP_FREE(sval); if ( seen_kind ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return(NULL); } seen_kind = 1; - oc->oc_kind = 0; + oc->oc_kind = LDAP_SCHEMA_ABSTRACT; parse_whsp(&ss); } else if ( !strcmp(sval,"STRUCTURAL") ) { + LDAP_FREE(sval); if ( seen_kind ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return(NULL); } seen_kind = 1; - oc->oc_kind = 1; + oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; parse_whsp(&ss); } else if ( !strcmp(sval,"AUXILIARY") ) { + LDAP_FREE(sval); if ( seen_kind ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return(NULL); } seen_kind = 1; - oc->oc_kind = 2; + oc->oc_kind = LDAP_SCHEMA_AUXILIARY; parse_whsp(&ss); } else if ( !strcmp(sval,"MUST") ) { + LDAP_FREE(sval); if ( seen_must ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return(NULL); } seen_must = 1; - oc->oc_at_oids_must = parse_oids(&ss,code); + oc->oc_at_oids_must = parse_oids(&ss,code,0); if ( !oc->oc_at_oids_must ) { *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return NULL; } parse_whsp(&ss); } else if ( !strcmp(sval,"MAY") ) { + LDAP_FREE(sval); if ( seen_may ) { - *code = SCHEMA_ERR_DUPOPT; + *code = LDAP_SCHERR_DUPOPT; *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return(NULL); } seen_may = 1; - oc->oc_at_oids_may = parse_oids(&ss,code); + oc->oc_at_oids_may = parse_oids(&ss,code,0); if ( !oc->oc_at_oids_may ) { *errp = ss; - free_oc(oc); + ldap_objectclass_free(oc); return NULL; } parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + if ( add_extension(&oc->oc_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } } else { - *code = SCHEMA_ERR_UNEXPTOKEN; + *code = LDAP_SCHERR_UNEXPTOKEN; *errp = ss; - free_oc(oc); + LDAP_FREE(sval); + ldap_objectclass_free(oc); return NULL; } break; default: - *code = SCHEMA_ERR_UNEXPTOKEN; + *code = LDAP_SCHERR_UNEXPTOKEN; *errp = ss; - free_oc(oc); + LDAP_FREE(sval); + ldap_objectclass_free(oc); return NULL; } } } +static char *const err2text[] = { + "Success", + "Out of memory", + "Unexpected token", + "Missing opening parenthesis", + "Missing closing parenthesis", + "Expecting digit", + "Expecting a name", + "Bad description", + "Bad superiors", + "Duplicate option", + "Unexpected end of data" +}; +char * +ldap_scherr2str(int code) +{ + if ( code < 0 || code >= (sizeof(err2text)/sizeof(char *)) ) { + return "Unknown error"; + } else { + return err2text[code]; + } +}