/* Define an attribute option. */ int ad_define_option( const char *name, const char *fname, int lineno ) { int i; unsigned int optlen; if ( options == &lang_option ) { options = NULL; option_count = 0; } if ( name == NULL ) return 0; optlen = 0; do { if ( !DESC_CHAR( name[optlen] ) ) { /* allow trailing '=', same as '-' */ if ( name[optlen] == '=' && !name[optlen+1] ) { msad_range_hack = 1; continue; } Debug( LDAP_DEBUG_ANY, "%s: line %d: illegal option name \"%s\"\n", fname, lineno, name ); return 1; } } while ( name[++optlen] ); options = ch_realloc( options, (option_count+1) * sizeof(Attr_option) ); if ( strcasecmp( name, "binary" ) == 0 || ad_find_option_definition( name, optlen ) ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: option \"%s\" is already defined\n", fname, lineno, name ); return 1; } for ( i = option_count; i; --i ) { if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 ) break; options[i] = options[i-1]; } options[i].name.bv_val = ch_strdup( name ); options[i].name.bv_len = optlen; options[i].prefix = (name[optlen-1] == '-') || (name[optlen-1] == '='); if ( i != option_count && options[i].prefix && optlen < options[i+1].name.bv_len && strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: option \"%s\" overrides previous option\n", fname, lineno, name ); return 1; } option_count++; return 0; }
int slap_bv2ad( struct berval *bv, AttributeDescription **ad, const char **text ) { int rtn = LDAP_UNDEFINED_TYPE; AttributeDescription desc, *d2; char *name, *options, *optn; char *opt, *next; int ntags; int tagslen; /* hardcoded limits for speed */ #define MAX_TAGGING_OPTIONS 128 struct berval tags[MAX_TAGGING_OPTIONS+1]; #define MAX_TAGS_LEN 1024 char tagbuf[MAX_TAGS_LEN]; assert( ad != NULL ); assert( *ad == NULL ); /* temporary */ if( bv == NULL || BER_BVISNULL( bv ) || BER_BVISEMPTY( bv ) ) { *text = "empty AttributeDescription"; return rtn; } /* make sure description is IA5 */ if( ad_keystring( bv ) ) { *text = "AttributeDescription contains inappropriate characters"; return rtn; } /* find valid base attribute type; parse in place */ desc.ad_cname = *bv; desc.ad_flags = 0; BER_BVZERO( &desc.ad_tags ); name = bv->bv_val; options = ber_bvchr( bv, ';' ); if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) { /* don't go past the end of the berval! */ desc.ad_cname.bv_len = options - name; } else { options = NULL; } desc.ad_type = at_bvfind( &desc.ad_cname ); if( desc.ad_type == NULL ) { *text = "attribute type undefined"; return rtn; } if( is_at_operational( desc.ad_type ) && options != NULL ) { *text = "operational attribute with options undefined"; return rtn; } /* * parse options in place */ ntags = 0; tagslen = 0; optn = bv->bv_val + bv->bv_len; for( opt=options; opt != NULL; opt=next ) { int optlen; opt++; next = strchrlen( opt, optn, ';', &optlen ); if( optlen == 0 ) { *text = "zero length option is invalid"; return rtn; } else if ( optlen == STRLENOF("binary") && strncasecmp( opt, "binary", STRLENOF("binary") ) == 0 ) { /* binary option */ if( slap_ad_is_binary( &desc ) ) { *text = "option \"binary\" specified multiple times"; return rtn; } if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) { /* not stored in binary, disallow option */ *text = "option \"binary\" not supported with type"; return rtn; } desc.ad_flags |= SLAP_DESC_BINARY; continue; } else if ( ad_find_option_definition( opt, optlen ) ) { int i; if( opt[optlen-1] == '-' || ( opt[optlen-1] == '=' && msad_range_hack )) { desc.ad_flags |= SLAP_DESC_TAG_RANGE; } if( ntags >= MAX_TAGGING_OPTIONS ) { *text = "too many tagging options"; return rtn; } /* * tags should be presented in sorted order, * so run the array in reverse. */ for( i=ntags-1; i>=0; i-- ) { int rc; rc = strncasecmp( opt, tags[i].bv_val, (unsigned) optlen < tags[i].bv_len ? (unsigned) optlen : tags[i].bv_len ); if( rc == 0 && (unsigned)optlen == tags[i].bv_len ) { /* duplicate (ignore) */ goto done; } else if ( rc > 0 || ( rc == 0 && (unsigned)optlen > tags[i].bv_len )) { AC_MEMCPY( &tags[i+2], &tags[i+1], (ntags-i-1)*sizeof(struct berval) ); tags[i+1].bv_val = opt; tags[i+1].bv_len = optlen; goto done; } } if( ntags ) { AC_MEMCPY( &tags[1], &tags[0], ntags*sizeof(struct berval) ); } tags[0].bv_val = opt; tags[0].bv_len = optlen; done:; tagslen += optlen + 1; ntags++; } else { *text = "unrecognized option"; return rtn; } } if( ntags > 0 ) { int i; if( tagslen > MAX_TAGS_LEN ) { *text = "tagging options too long"; return rtn; } desc.ad_tags.bv_val = tagbuf; tagslen = 0; for( i=0; i<ntags; i++ ) { AC_MEMCPY( &desc.ad_tags.bv_val[tagslen], tags[i].bv_val, tags[i].bv_len ); tagslen += tags[i].bv_len; desc.ad_tags.bv_val[tagslen++] = ';'; } desc.ad_tags.bv_val[--tagslen] = '\0'; desc.ad_tags.bv_len = tagslen; } /* see if a matching description is already cached */ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { if( d2->ad_flags != desc.ad_flags ) { continue; } if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) { continue; } if( d2->ad_tags.bv_len == 0 ) { break; } if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val, desc.ad_tags.bv_len ) == 0 ) { break; } } /* Not found, add new one */ while (d2 == NULL) { size_t dlen = 0; ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex ); /* check again now that we've locked */ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { if (d2->ad_flags != desc.ad_flags) continue; if (d2->ad_tags.bv_len != desc.ad_tags.bv_len) continue; if (d2->ad_tags.bv_len == 0) break; if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val, desc.ad_tags.bv_len) == 0) break; } if (d2) { ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); break; } /* Allocate a single contiguous block. If there are no * options, we just need space for the AttrDesc structure. * Otherwise, we need to tack on the full name length + * options length, + maybe tagging options length again. */ if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) { dlen = desc.ad_type->sat_cname.bv_len + 1; if (desc.ad_tags.bv_len) { dlen += 1 + desc.ad_tags.bv_len; } if ( slap_ad_is_binary( &desc ) ) { dlen += 1 + STRLENOF(";binary") + desc.ad_tags.bv_len; } } d2 = ch_malloc(sizeof(AttributeDescription) + dlen); d2->ad_next = NULL; d2->ad_type = desc.ad_type; d2->ad_flags = desc.ad_flags; d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len; d2->ad_tags.bv_len = desc.ad_tags.bv_len; ldap_pvt_thread_mutex_lock( &ad_index_mutex ); d2->ad_index = ++ad_count; ldap_pvt_thread_mutex_unlock( &ad_index_mutex ); if (dlen == 0) { d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val; d2->ad_tags.bv_val = NULL; } else { char *cp, *op, *lp; int j; d2->ad_cname.bv_val = (char *)(d2+1); strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val); cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len; if( slap_ad_is_binary( &desc ) ) { op = cp; lp = NULL; if( desc.ad_tags.bv_len ) { lp = desc.ad_tags.bv_val; while( strncasecmp(lp, "binary", STRLENOF("binary")) < 0 && (lp = strchr( lp, ';' )) != NULL ) ++lp; if( lp != desc.ad_tags.bv_val ) { *cp++ = ';'; j = (lp ? (unsigned) (lp - desc.ad_tags.bv_val - 1) : strlen( desc.ad_tags.bv_val )); cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j); } } cp = lutil_strcopy(cp, ";binary"); if( lp != NULL ) { *cp++ = ';'; cp = lutil_strcopy(cp, lp); } d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val; if( desc.ad_tags.bv_len ) ldap_pvt_str2lower(op); j = 1; } else { j = 0; } if( desc.ad_tags.bv_len ) { lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j; if ( j == 0 ) *lp++ = ';'; d2->ad_tags.bv_val = lp; strcpy(lp, desc.ad_tags.bv_val); ldap_pvt_str2lower(lp); if( j == 0 ) d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len; } } /* Add new desc to list. We always want the bare Desc with * no options to stay at the head of the list, assuming * that one will be used most frequently. */ if (desc.ad_type->sat_ad == NULL || dlen == 0) { d2->ad_next = desc.ad_type->sat_ad; desc.ad_type->sat_ad = d2; } else { d2->ad_next = desc.ad_type->sat_ad->ad_next; desc.ad_type->sat_ad->ad_next = d2; } ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); } if( *ad == NULL ) { *ad = d2; } else { **ad = *d2; } return LDAP_SUCCESS; }