+ } else if( in->bv_len == 5 ) {
+ if( !memcmp( in->bv_val, "FALSE", 5 ) ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+static int
+booleanMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ /* simplistic matching allowed by rigid validation */
+ struct berval *asserted = (struct berval *) assertedValue;
+ *matchp = value->bv_len != asserted->bv_len;
+ return LDAP_SUCCESS;
+}
+
+/*-------------------------------------------------------------------
+LDAP/X.500 string syntax / matching rules have a few oddities. This
+comment attempts to detail how slapd(8) treats them.
+
+Summary:
+ StringSyntax X.500 LDAP Matching
+ DirectoryString CHOICE UTF8 i/e + ignore insignificant spaces
+ PrintableString subset subset i/e + ignore insignificant spaces
+ NumericString subset subset ignore all spaces
+ IA5String ASCII ASCII i/e + ignore insignificant spaces
+ TeletexString T.61 T.61 i/e + ignore insignificant spaces
+
+ TelephoneNumber subset subset i + ignore all spaces and "-"
+
+ See draft-ietf-ldapbis-strpro for details (once published).
+
+
+Directory String -
+ In X.500(93), a directory string can be either a PrintableString,
+ a bmpString, or a UniversalString (e.g., UCS (a subset of Unicode)).
+ In later versions, more CHOICEs were added. In all cases the string
+ must be non-empty.
+
+ In LDPAv3, a directory string is a UTF-8 encoded UCS string.
+
+ For matching, there are both case ignore and exact rules. Both
+ also require that "insignificant" spaces be ignored.
+ spaces before the first non-space are ignored;
+ spaces after the last non-space are ignored;
+ spaces after a space are ignored.
+ Note: by these rules (and as clarified in X.520), a string of only
+ spaces is to be treated as if held one space, not empty (which
+ would be a syntax error).
+
+NumericString
+ In ASN.1, numeric string is just a string of digits and spaces
+ and could be empty. However, in X.500, all attribute values of
+ numeric string carry a non-empty constraint. For example:
+
+ internationalISDNNumber ATTRIBUTE ::= {
+ WITH SYNTAX InternationalISDNNumber
+ EQUALITY MATCHING RULE numericStringMatch
+ SUBSTRINGS MATCHING RULE numericStringSubstringsMatch
+ ID id-at-internationalISDNNumber }
+ InternationalISDNNumber ::=
+ NumericString (SIZE(1..ub-international-isdn-number))
+
+ Unforunately, some assertion values are don't carry the same
+ constraint (but its unclear how such an assertion could ever
+ be true). In LDAP, there is one syntax (numericString) not two
+ (numericString with constraint, numericString without constraint).
+ This should be treated as numericString with non-empty constraint.
+ Note that while someone may have no ISDN number, there are no ISDN
+ numbers which are zero length.
+
+ In matching, spaces are ignored.
+
+PrintableString
+ In ASN.1, Printable string is just a string of printable characters
+ and can be empty. In X.500, semantics much like NumericString (see
+ serialNumber for a like example) excepting uses insignificant space
+ handling instead of ignore all spaces.
+
+IA5String
+ Basically same as PrintableString. There are no examples in X.500,
+ but same logic applies. So we require them to be non-empty as
+ well.
+
+-------------------------------------------------------------------*/
+
+static int
+UTF8StringValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ ber_len_t count;
+ int len;
+ unsigned char *u = in->bv_val;
+
+ if( !in->bv_len ) return LDAP_INVALID_SYNTAX;
+
+ for( count = in->bv_len; count > 0; count-=len, u+=len ) {
+ /* get the length indicated by the first byte */
+ len = LDAP_UTF8_CHARLEN2( u, len );
+
+ /* very basic checks */
+ switch( len ) {
+ case 6:
+ if( (u[5] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 5:
+ if( (u[4] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 4:
+ if( (u[3] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 3:
+ if( (u[2] & 0xC0 )!= 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 2:
+ if( (u[1] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 1:
+ /* CHARLEN already validated it */
+ break;
+ default:
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* make sure len corresponds with the offset
+ to the next character */
+ if( LDAP_UTF8_OFFSET( u ) != len ) return LDAP_INVALID_SYNTAX;
+ }
+
+ if( count != 0 ) return LDAP_INVALID_SYNTAX;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+UTF8StringNormalize(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *normalized )
+{
+ char *p, *q, *s, *e;
+ int len = 0;
+
+ /* validator should have refused an empty string */
+ assert( val->bv_len );
+
+ p = val->bv_val;
+
+ /* Ignore initial whitespace */
+ /* All space is ASCII. All ASCII is 1 byte */
+ for ( ; p < val->bv_val + val->bv_len && ASCII_SPACE( p[ 0 ] ); p++ );
+
+ normalized->bv_len = val->bv_len - (p - val->bv_val);
+
+ if( !normalized->bv_len ) {
+ ber_mem2bv( " ", 1, 1, normalized );
+ return LDAP_SUCCESS;
+ }
+
+ ber_mem2bv( p, normalized->bv_len, 1, normalized );
+ e = normalized->bv_val + normalized->bv_len;
+
+ assert( normalized->bv_val );
+
+ p = q = normalized->bv_val;
+ s = NULL;
+
+ while ( p < e ) {
+ q += len;
+ if ( ASCII_SPACE( *p ) ) {
+ s = q - len;
+ len = 1;
+ *q = *p++;
+
+ /* Ignore the extra whitespace */
+ while ( ASCII_SPACE( *p ) ) {
+ p++;
+ }
+ } else {
+ len = LDAP_UTF8_COPY(q,p);
+ s=NULL;
+ p+=len;
+ }
+ }
+
+ assert( normalized->bv_val <= p );
+ assert( q+len <= p );
+
+ /* cannot start with a space */
+ assert( !ASCII_SPACE( normalized->bv_val[0] ) );
+
+ /*
+ * If the string ended in space, backup the pointer one
+ * position. One is enough because the above loop collapsed
+ * all whitespace to a single space.
+ */
+
+ if ( s != NULL ) {
+ len = q - s;
+ q = s;
+ }
+
+ /* cannot end with a space */
+ assert( !ASCII_SPACE( *q ) );
+
+ q += len;
+
+ /* null terminate */
+ *q = '\0';
+
+ normalized->bv_len = q - normalized->bv_val;
+
+ return LDAP_SUCCESS;
+}
+
+/* Returns Unicode canonically normalized copy of a substring assertion
+ * Skipping attribute description */
+static SubstringsAssertion *
+UTF8SubstringsassertionNormalize(
+ SubstringsAssertion *sa,
+ unsigned casefold )
+{
+ SubstringsAssertion *nsa;
+ int i;
+
+ nsa = (SubstringsAssertion *)ch_calloc( 1, sizeof(SubstringsAssertion) );
+ if( nsa == NULL ) {
+ return NULL;
+ }
+
+ if( sa->sa_initial.bv_val != NULL ) {
+ UTF8bvnormalize( &sa->sa_initial, &nsa->sa_initial, casefold );
+ if( nsa->sa_initial.bv_val == NULL ) {
+ goto err;
+ }
+ }
+
+ if( sa->sa_any != NULL ) {
+ for( i=0; sa->sa_any[i].bv_val != NULL; i++ ) {
+ /* empty */
+ }
+ nsa->sa_any = (struct berval *)
+ ch_malloc( (i + 1) * sizeof(struct berval) );
+
+ for( i=0; sa->sa_any[i].bv_val != NULL; i++ ) {
+ UTF8bvnormalize( &sa->sa_any[i], &nsa->sa_any[i],
+ casefold );
+ if( nsa->sa_any[i].bv_val == NULL ) {
+ goto err;
+ }
+ }
+ nsa->sa_any[i].bv_val = NULL;
+ }
+
+ if( sa->sa_final.bv_val != NULL ) {
+ UTF8bvnormalize( &sa->sa_final, &nsa->sa_final, casefold );
+ if( nsa->sa_final.bv_val == NULL ) {
+ goto err;
+ }
+ }
+
+ return nsa;
+
+err:
+ if ( nsa->sa_final.bv_val ) free( nsa->sa_final.bv_val );
+ if ( nsa->sa_any ) ber_bvarray_free( nsa->sa_any );
+ if ( nsa->sa_initial.bv_val ) free( nsa->sa_initial.bv_val );
+ ch_free( nsa );
+ return NULL;
+}
+
+#ifndef SLAPD_APPROX_OLDSINGLESTRING
+
+#if defined(SLAPD_APPROX_INITIALS)
+#define SLAPD_APPROX_DELIMITER "._ "
+#define SLAPD_APPROX_WORDLEN 2
+#else
+#define SLAPD_APPROX_DELIMITER " "
+#define SLAPD_APPROX_WORDLEN 1
+#endif
+
+static int
+approxMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *nval, *assertv;
+ char *val, **values, **words, *c;
+ int i, count, len, nextchunk=0, nextavail=0;
+
+ /* Yes, this is necessary */
+ nval = UTF8bvnormalize( value, NULL, LDAP_UTF8_APPROX );
+ if( nval == NULL ) {
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* Yes, this is necessary */
+ assertv = UTF8bvnormalize( ((struct berval *)assertedValue),
+ NULL, LDAP_UTF8_APPROX );
+ if( assertv == NULL ) {
+ ber_bvfree( nval );
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* Isolate how many words there are */
+ for ( c = nval->bv_val, count = 1; *c; c++ ) {
+ c = strpbrk( c, SLAPD_APPROX_DELIMITER );
+ if ( c == NULL ) break;
+ *c = '\0';
+ count++;
+ }
+
+ /* Get a phonetic copy of each word */
+ words = (char **)ch_malloc( count * sizeof(char *) );
+ values = (char **)ch_malloc( count * sizeof(char *) );
+ for ( c = nval->bv_val, i = 0; i < count; i++, c += strlen(c) + 1 ) {
+ words[i] = c;
+ values[i] = phonetic(c);
+ }
+
+ /* Work through the asserted value's words, to see if at least some
+ of the words are there, in the same order. */
+ len = 0;
+ while ( (ber_len_t) nextchunk < assertv->bv_len ) {
+ len = strcspn( assertv->bv_val + nextchunk, SLAPD_APPROX_DELIMITER);
+ if( len == 0 ) {
+ nextchunk++;
+ continue;
+ }
+#if defined(SLAPD_APPROX_INITIALS)
+ else if( len == 1 ) {
+ /* Single letter words need to at least match one word's initial */
+ for( i=nextavail; i<count; i++ )
+ if( !strncasecmp( assertv->bv_val + nextchunk, words[i], 1 )) {
+ nextavail=i+1;
+ break;
+ }
+ }
+#endif
+ else {
+ /* Isolate the next word in the asserted value and phonetic it */
+ assertv->bv_val[nextchunk+len] = '\0';
+ val = phonetic( assertv->bv_val + nextchunk );
+
+ /* See if this phonetic chunk is in the remaining words of *value */
+ for( i=nextavail; i<count; i++ ){
+ if( !strcmp( val, values[i] ) ){
+ nextavail = i+1;
+ break;
+ }
+ }
+ ch_free( val );
+ }
+
+ /* This chunk in the asserted value was NOT within the *value. */
+ if( i >= count ) {
+ nextavail=-1;
+ break;
+ }
+
+ /* Go on to the next word in the asserted value */
+ nextchunk += len+1;
+ }
+
+ /* If some of the words were seen, call it a match */
+ if( nextavail > 0 ) {
+ *matchp = 0;
+ }
+ else {
+ *matchp = 1;
+ }
+
+ /* Cleanup allocs */
+ ber_bvfree( assertv );
+ for( i=0; i<count; i++ ) {
+ ch_free( values[i] );
+ }
+ ch_free( values );
+ ch_free( words );
+ ber_bvfree( nval );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+approxIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp )
+{
+ char *c;
+ int i,j, len, wordcount, keycount=0;
+ struct berval *newkeys;
+ BerVarray keys=NULL;
+
+ for( j=0; values[j].bv_val != NULL; j++ ) {
+ struct berval val = { 0, NULL };
+ /* Yes, this is necessary */
+ UTF8bvnormalize( &values[j], &val, LDAP_UTF8_APPROX );
+ assert( val.bv_val != NULL );
+
+ /* Isolate how many words there are. There will be a key for each */
+ for( wordcount = 0, c = val.bv_val; *c; c++) {
+ len = strcspn(c, SLAPD_APPROX_DELIMITER);
+ if( len >= SLAPD_APPROX_WORDLEN ) wordcount++;
+ c+= len;
+ if (*c == '\0') break;
+ *c = '\0';
+ }
+
+ /* Allocate/increase storage to account for new keys */
+ newkeys = (struct berval *)ch_malloc( (keycount + wordcount + 1)
+ * sizeof(struct berval) );
+ AC_MEMCPY( newkeys, keys, keycount * sizeof(struct berval) );
+ if( keys ) ch_free( keys );
+ keys = newkeys;
+
+ /* Get a phonetic copy of each word */
+ for( c = val.bv_val, i = 0; i < wordcount; c += len + 1 ) {
+ len = strlen( c );
+ if( len < SLAPD_APPROX_WORDLEN ) continue;
+ ber_str2bv( phonetic( c ), 0, 0, &keys[keycount] );
+ keycount++;
+ i++;
+ }
+
+ ber_memfree( val.bv_val );
+ }
+ keys[keycount].bv_val = NULL;
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+approxFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertValue,
+ BerVarray *keysp )
+{
+ char *c;
+ int i, count, len;
+ struct berval *val;
+ BerVarray keys;
+
+ /* Yes, this is necessary */
+ val = UTF8bvnormalize( ((struct berval *)assertValue),
+ NULL, LDAP_UTF8_APPROX );
+ if( val == NULL || val->bv_val == NULL ) {
+ keys = (struct berval *)ch_malloc( sizeof(struct berval) );
+ keys[0].bv_val = NULL;
+ *keysp = keys;
+ ber_bvfree( val );
+ return LDAP_SUCCESS;
+ }
+
+ /* Isolate how many words there are. There will be a key for each */
+ for( count = 0,c = val->bv_val; *c; c++) {
+ len = strcspn(c, SLAPD_APPROX_DELIMITER);
+ if( len >= SLAPD_APPROX_WORDLEN ) count++;
+ c+= len;
+ if (*c == '\0') break;
+ *c = '\0';
+ }
+
+ /* Allocate storage for new keys */
+ keys = (struct berval *)ch_malloc( (count + 1) * sizeof(struct berval) );
+
+ /* Get a phonetic copy of each word */
+ for( c = val->bv_val, i = 0; i < count; c += len + 1 ) {
+ len = strlen(c);
+ if( len < SLAPD_APPROX_WORDLEN ) continue;
+ ber_str2bv( phonetic( c ), 0, 0, &keys[i] );
+ i++;
+ }
+
+ ber_bvfree( val );
+
+ keys[count].bv_val = NULL;
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+
+#else
+/* No other form of Approximate Matching is defined */
+
+static int
+approxMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ char *vapprox, *avapprox;
+ char *s, *t;
+
+ /* Yes, this is necessary */
+ s = UTF8normalize( value, UTF8_NOCASEFOLD );
+ if( s == NULL ) {
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* Yes, this is necessary */
+ t = UTF8normalize( ((struct berval *)assertedValue),
+ UTF8_NOCASEFOLD );
+ if( t == NULL ) {
+ free( s );
+ *matchp = -1;
+ return LDAP_SUCCESS;
+ }
+
+ vapprox = phonetic( strip8bitChars( s ) );
+ avapprox = phonetic( strip8bitChars( t ) );
+
+ free( s );
+ free( t );
+
+ *matchp = strcmp( vapprox, avapprox );
+
+ ch_free( vapprox );
+ ch_free( avapprox );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+approxIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp )
+{
+ int i;
+ BerVarray *keys;
+ char *s;
+
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ /* empty - just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = (struct berval *)ch_malloc( sizeof( struct berval ) * (i+1) );
+
+ /* Copy each value and run it through phonetic() */
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ /* Yes, this is necessary */
+ s = UTF8normalize( &values[i], UTF8_NOCASEFOLD );
+
+ /* strip 8-bit chars and run through phonetic() */
+ ber_str2bv( phonetic( strip8bitChars( s ) ), 0, 0, &keys[i] );
+ free( s );
+ }
+ keys[i].bv_val = NULL;
+
+ *keysp = keys;
+ return LDAP_SUCCESS;
+}
+
+
+static int
+approxFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertValue,
+ BerVarray *keysp )
+{
+ BerVarray keys;
+ char *s;
+
+ keys = (struct berval *)ch_malloc( sizeof( struct berval * ) * 2 );
+
+ /* Yes, this is necessary */
+ s = UTF8normalize( ((struct berval *)assertValue),
+ UTF8_NOCASEFOLD );
+ if( s == NULL ) {
+ keys[0] = NULL;
+ } else {
+ /* strip 8-bit chars and run through phonetic() */
+ keys[0] = ber_bvstr( phonetic( strip8bitChars( s ) ) );
+ free( s );
+ keys[1] = NULL;
+ }
+
+ *keysp = keys;
+ return LDAP_SUCCESS;
+}
+#endif
+
+
+static int
+caseExactMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ *matchp = UTF8bvnormcmp( value,
+ (struct berval *) assertedValue,
+ LDAP_UTF8_NOCASEFOLD );
+ return LDAP_SUCCESS;
+}
+
+static int
+caseExactIgnoreSubstringsMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match = 0;
+ SubstringsAssertion *sub = NULL;
+ struct berval left = { 0, NULL };
+ int i;
+ ber_len_t inlen=0;
+ char *nav = NULL;
+ unsigned casefold;
+
+ casefold = ( mr != caseExactSubstringsMatchingRule )
+ ? LDAP_UTF8_CASEFOLD : LDAP_UTF8_NOCASEFOLD;
+
+ if ( UTF8bvnormalize( value, &left, casefold ) == NULL ) {
+ match = 1;
+ goto done;
+ }
+ nav = left.bv_val;
+
+ sub = UTF8SubstringsassertionNormalize( assertedValue, casefold );
+ if( sub == NULL ) {
+ match = -1;
+ goto done;
+ }
+
+ /* Add up asserted input length */
+ if( sub->sa_initial.bv_val ) {
+ inlen += sub->sa_initial.bv_len;
+ }
+ if( sub->sa_any ) {
+ for(i=0; sub->sa_any[i].bv_val != NULL; i++) {
+ inlen += sub->sa_any[i].bv_len;
+ }
+ }
+ if( sub->sa_final.bv_val ) {
+ inlen += sub->sa_final.bv_len;
+ }
+
+ if( sub->sa_initial.bv_val ) {
+ if( inlen > left.bv_len ) {
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( sub->sa_initial.bv_val, left.bv_val,
+ sub->sa_initial.bv_len );
+
+ if( match != 0 ) {
+ goto done;
+ }
+
+ left.bv_val += sub->sa_initial.bv_len;
+ left.bv_len -= sub->sa_initial.bv_len;
+ inlen -= sub->sa_initial.bv_len;
+ }
+
+ if( sub->sa_final.bv_val ) {
+ if( inlen > left.bv_len ) {
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( sub->sa_final.bv_val,
+ &left.bv_val[left.bv_len - sub->sa_final.bv_len],
+ sub->sa_final.bv_len );
+
+ if( match != 0 ) {
+ goto done;
+ }
+
+ left.bv_len -= sub->sa_final.bv_len;
+ inlen -= sub->sa_final.bv_len;
+ }
+
+ if( sub->sa_any ) {
+ for(i=0; sub->sa_any[i].bv_val; i++) {
+ ber_len_t idx;
+ char *p;
+
+retry:
+ if( inlen > left.bv_len ) {
+ /* not enough length */
+ match = 1;
+ goto done;
+ }
+
+ if( sub->sa_any[i].bv_len == 0 ) {
+ continue;
+ }
+
+ p = ber_bvchr( &left, *sub->sa_any[i].bv_val );
+ if ( p == NULL ) {
+ match = 1;
+ goto done;
+ }
+
+ idx = p - left.bv_val;
+
+ if( idx >= left.bv_len ) {
+ /* this shouldn't happen */
+ free( nav );
+ if ( sub->sa_final.bv_val )
+ ch_free( sub->sa_final.bv_val );
+ if ( sub->sa_any )
+ ber_bvarray_free( sub->sa_any );
+ if ( sub->sa_initial.bv_val )
+ ch_free( sub->sa_initial.bv_val );
+ ch_free( sub );
+ return LDAP_OTHER;
+ }
+
+ left.bv_val = p;
+ left.bv_len -= idx;
+
+ if( sub->sa_any[i].bv_len > left.bv_len ) {
+ /* not enough left */
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( left.bv_val,
+ sub->sa_any[i].bv_val,
+ sub->sa_any[i].bv_len );
+
+ if( match != 0 ) {
+ left.bv_val++;
+ left.bv_len--;
+ goto retry;
+ }
+
+ left.bv_val += sub->sa_any[i].bv_len;
+ left.bv_len -= sub->sa_any[i].bv_len;
+ inlen -= sub->sa_any[i].bv_len;
+ }
+ }
+
+done:
+ free( nav );
+ if( sub != NULL ) {
+ if ( sub->sa_final.bv_val ) free( sub->sa_final.bv_val );
+ if ( sub->sa_any ) ber_bvarray_free( sub->sa_any );
+ if ( sub->sa_initial.bv_val ) free( sub->sa_initial.bv_val );
+ ch_free( sub );
+ }
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function */
+static int caseExactIgnoreIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp )
+{
+ int i;
+ unsigned casefold;
+ size_t slen, mlen;
+ BerVarray keys;
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval digest;
+ digest.bv_val = HASHdigest;
+ digest.bv_len = sizeof(HASHdigest);
+
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ /* empty - just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = ch_malloc( sizeof( struct berval ) * (i+1) );
+
+ slen = syntax->ssyn_oidlen;
+ mlen = mr->smr_oidlen;
+
+ casefold = ( mr != caseExactMatchingRule )
+ ? LDAP_UTF8_CASEFOLD : LDAP_UTF8_NOCASEFOLD;
+
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ struct berval value;
+ UTF8bvnormalize( &values[i], &value, casefold );
+
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ value.bv_val, value.bv_len );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ free( value.bv_val );
+
+ ber_dupbv( &keys[i], &digest );
+ }
+
+ keys[i].bv_val = NULL;
+ *keysp = keys;
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function */
+static int caseExactIgnoreFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertValue,
+ BerVarray *keysp )
+{
+ unsigned casefold;
+ size_t slen, mlen;
+ BerVarray keys;
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval value = { 0, NULL };
+ struct berval digest;
+
+ digest.bv_val = HASHdigest;
+ digest.bv_len = sizeof(HASHdigest);
+
+ slen = syntax->ssyn_oidlen;
+ mlen = mr->smr_oidlen;
+
+ casefold = ( mr != caseExactMatchingRule )
+ ? LDAP_UTF8_CASEFOLD : LDAP_UTF8_NOCASEFOLD;
+
+ UTF8bvnormalize( (struct berval *) assertValue, &value, casefold );
+ /* This usually happens if filter contains bad UTF8 */
+ if( value.bv_val == NULL ) {
+ keys = ch_malloc( sizeof( struct berval ) );
+ keys[0].bv_val = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ keys = ch_malloc( sizeof( struct berval ) * 2 );
+
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ value.bv_val, value.bv_len );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( keys, &digest );
+ keys[1].bv_val = NULL;
+
+ free( value.bv_val );
+
+ *keysp = keys;
+ return LDAP_SUCCESS;
+}
+
+/* Substrings Index generation function */
+static int caseExactIgnoreSubstringsIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp )
+{
+ unsigned casefold;
+ ber_len_t i, nkeys;
+ size_t slen, mlen;
+ BerVarray keys;
+ BerVarray nvalues;
+
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval digest;
+ digest.bv_val = HASHdigest;
+ digest.bv_len = sizeof(HASHdigest);
+
+ nkeys=0;
+
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ /* empty - just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ casefold = ( mr != caseExactSubstringsMatchingRule )
+ ? LDAP_UTF8_CASEFOLD : LDAP_UTF8_NOCASEFOLD;
+
+ nvalues = ch_malloc( sizeof( struct berval ) * (i+1) );
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ UTF8bvnormalize( &values[i], &nvalues[i], casefold );
+ }
+ nvalues[i].bv_val = NULL;
+ values = nvalues;
+
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ /* count number of indices to generate */
+ if( values[i].bv_len < SLAP_INDEX_SUBSTR_MINLEN ) {
+ continue;
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
+ if( values[i].bv_len >= SLAP_INDEX_SUBSTR_MAXLEN ) {
+ nkeys += SLAP_INDEX_SUBSTR_MAXLEN -
+ ( SLAP_INDEX_SUBSTR_MINLEN - 1);
+ } else {
+ nkeys += values[i].bv_len - ( SLAP_INDEX_SUBSTR_MINLEN - 1 );
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_ANY ) {
+ if( values[i].bv_len >= SLAP_INDEX_SUBSTR_MAXLEN ) {
+ nkeys += values[i].bv_len - ( SLAP_INDEX_SUBSTR_MAXLEN - 1 );
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
+ if( values[i].bv_len >= SLAP_INDEX_SUBSTR_MAXLEN ) {
+ nkeys += SLAP_INDEX_SUBSTR_MAXLEN -
+ ( SLAP_INDEX_SUBSTR_MINLEN - 1);
+ } else {
+ nkeys += values[i].bv_len - ( SLAP_INDEX_SUBSTR_MINLEN - 1 );
+ }
+ }
+ }
+
+ if( nkeys == 0 ) {
+ /* no keys to generate */
+ *keysp = NULL;
+ ber_bvarray_free( nvalues );
+ return LDAP_SUCCESS;
+ }
+
+ keys = ch_malloc( sizeof( struct berval ) * (nkeys+1) );
+
+ slen = syntax->ssyn_oidlen;
+ mlen = mr->smr_oidlen;
+
+ nkeys=0;
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ ber_len_t j,max;
+
+ if( values[i].bv_len < SLAP_INDEX_SUBSTR_MINLEN ) continue;
+
+ if( ( flags & SLAP_INDEX_SUBSTR_ANY ) &&
+ ( values[i].bv_len >= SLAP_INDEX_SUBSTR_MAXLEN ) )
+ {
+ char pre = SLAP_INDEX_SUBSTR_PREFIX;
+ max = values[i].bv_len - ( SLAP_INDEX_SUBSTR_MAXLEN - 1);
+
+ for( j=0; j<max; j++ ) {
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+
+ HASH_Update( &HASHcontext,
+ &pre, sizeof( pre ) );
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ &values[i].bv_val[j],
+ SLAP_INDEX_SUBSTR_MAXLEN );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( &keys[nkeys++], &digest );
+ }
+ }
+
+ max = SLAP_INDEX_SUBSTR_MAXLEN < values[i].bv_len
+ ? SLAP_INDEX_SUBSTR_MAXLEN : values[i].bv_len;
+
+ for( j=SLAP_INDEX_SUBSTR_MINLEN; j<=max; j++ ) {
+ char pre;
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
+ pre = SLAP_INDEX_SUBSTR_INITIAL_PREFIX;
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ &pre, sizeof( pre ) );
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ values[i].bv_val, j );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( &keys[nkeys++], &digest );
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
+ pre = SLAP_INDEX_SUBSTR_FINAL_PREFIX;
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ &pre, sizeof( pre ) );
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ &values[i].bv_val[values[i].bv_len-j], j );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( &keys[nkeys++], &digest );
+ }
+
+ }
+
+ }
+
+ if( nkeys > 0 ) {
+ keys[nkeys].bv_val = NULL;
+ *keysp = keys;
+ } else {
+ ch_free( keys );
+ *keysp = NULL;
+ }
+
+ ber_bvarray_free( nvalues );
+
+ return LDAP_SUCCESS;
+}
+
+static int caseExactIgnoreSubstringsFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertValue,
+ BerVarray *keysp )
+{
+ SubstringsAssertion *sa;
+ char pre;
+ unsigned casefold;
+ ber_len_t nkeys = 0;
+ size_t slen, mlen, klen;
+ BerVarray keys;
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval *value;
+ struct berval digest;
+
+ casefold = ( mr != caseExactSubstringsMatchingRule )
+ ? LDAP_UTF8_CASEFOLD : LDAP_UTF8_NOCASEFOLD;
+
+ sa = UTF8SubstringsassertionNormalize( assertValue, casefold );
+ if( sa == NULL ) {
+ *keysp = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL && sa->sa_initial.bv_val != NULL &&
+ sa->sa_initial.bv_len >= SLAP_INDEX_SUBSTR_MINLEN )
+ {
+ nkeys++;
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
+ ber_len_t i;
+ for( i=0; sa->sa_any[i].bv_val != NULL; i++ ) {
+ if( sa->sa_any[i].bv_len >= SLAP_INDEX_SUBSTR_MAXLEN ) {
+ /* don't bother accounting for stepping */
+ nkeys += sa->sa_any[i].bv_len -
+ ( SLAP_INDEX_SUBSTR_MAXLEN - 1 );
+ }
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL && sa->sa_final.bv_val != NULL &&
+ sa->sa_final.bv_len >= SLAP_INDEX_SUBSTR_MINLEN )
+ {
+ nkeys++;
+ }
+
+ if( nkeys == 0 ) {
+ if ( sa->sa_final.bv_val ) free( sa->sa_final.bv_val );
+ if ( sa->sa_any ) ber_bvarray_free( sa->sa_any );
+ if ( sa->sa_initial.bv_val ) free( sa->sa_initial.bv_val );
+ ch_free( sa );
+ *keysp = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ digest.bv_val = HASHdigest;
+ digest.bv_len = sizeof(HASHdigest);
+
+ slen = syntax->ssyn_oidlen;
+ mlen = mr->smr_oidlen;
+
+ keys = ch_malloc( sizeof( struct berval ) * (nkeys+1) );
+ nkeys = 0;
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL && sa->sa_initial.bv_val != NULL &&
+ sa->sa_initial.bv_len >= SLAP_INDEX_SUBSTR_MINLEN )
+ {
+ pre = SLAP_INDEX_SUBSTR_INITIAL_PREFIX;
+ value = &sa->sa_initial;
+
+ klen = SLAP_INDEX_SUBSTR_MAXLEN < value->bv_len
+ ? SLAP_INDEX_SUBSTR_MAXLEN : value->bv_len;
+
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ &pre, sizeof( pre ) );
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ value->bv_val, klen );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( &keys[nkeys++], &digest );
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
+ ber_len_t i, j;
+ pre = SLAP_INDEX_SUBSTR_PREFIX;
+ klen = SLAP_INDEX_SUBSTR_MAXLEN;
+
+ for( i=0; sa->sa_any[i].bv_val != NULL; i++ ) {
+ if( sa->sa_any[i].bv_len < SLAP_INDEX_SUBSTR_MAXLEN ) {
+ continue;
+ }
+
+ value = &sa->sa_any[i];
+
+ for(j=0;
+ j <= value->bv_len - SLAP_INDEX_SUBSTR_MAXLEN;
+ j += SLAP_INDEX_SUBSTR_STEP )
+ {
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ &pre, sizeof( pre ) );
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ &value->bv_val[j], klen );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( &keys[nkeys++], &digest );
+ }
+
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL && sa->sa_final.bv_val != NULL &&
+ sa->sa_final.bv_len >= SLAP_INDEX_SUBSTR_MINLEN )
+ {
+ pre = SLAP_INDEX_SUBSTR_FINAL_PREFIX;
+ value = &sa->sa_final;
+
+ klen = SLAP_INDEX_SUBSTR_MAXLEN < value->bv_len
+ ? SLAP_INDEX_SUBSTR_MAXLEN : value->bv_len;
+
+ HASH_Init( &HASHcontext );
+ if( prefix != NULL && prefix->bv_len > 0 ) {
+ HASH_Update( &HASHcontext,
+ prefix->bv_val, prefix->bv_len );
+ }
+ HASH_Update( &HASHcontext,
+ &pre, sizeof( pre ) );
+ HASH_Update( &HASHcontext,
+ syntax->ssyn_oid, slen );
+ HASH_Update( &HASHcontext,
+ mr->smr_oid, mlen );
+ HASH_Update( &HASHcontext,
+ &value->bv_val[value->bv_len-klen], klen );
+ HASH_Final( HASHdigest, &HASHcontext );
+
+ ber_dupbv( &keys[nkeys++], &digest );
+ }
+
+ if( nkeys > 0 ) {
+ keys[nkeys].bv_val = NULL;
+ *keysp = keys;
+ } else {
+ ch_free( keys );
+ *keysp = NULL;
+ }
+ if ( sa->sa_final.bv_val ) free( sa->sa_final.bv_val );
+ if ( sa->sa_any ) ber_bvarray_free( sa->sa_any );
+ if ( sa->sa_initial.bv_val ) free( sa->sa_initial.bv_val );
+ ch_free( sa );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+caseIgnoreMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ *matchp = UTF8bvnormcmp( value,
+ (struct berval *) assertedValue,
+ LDAP_UTF8_CASEFOLD );
+ return LDAP_SUCCESS;
+}
+
+/* Remove all spaces and '-' characters */
+static int
+telephoneNumberNormalize(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *normalized )
+{
+ char *p, *q;
+
+ /* validator should have refused an empty string */
+ assert( val->bv_len );
+
+ q = normalized->bv_val = ch_malloc( val->bv_len + 1 );
+
+ for( p = val->bv_val; *p; p++ ) {
+ if ( ! ( ASCII_SPACE( *p ) || *p == '-' )) {
+ *q++ = *p;
+ }
+ }
+ *q = '\0';
+
+ normalized->bv_len = q - normalized->bv_val;
+
+ if( normalized->bv_len == 0 ) {
+ free( normalized->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+oidValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ ber_len_t i;
+
+ if( val->bv_len == 0 ) {
+ /* disallow empty strings */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( OID_LEADCHAR(val->bv_val[0]) ) {
+ int dot = 0;
+ for(i=1; i < val->bv_len; i++) {
+ if( OID_SEPARATOR( val->bv_val[i] ) ) {
+ if( dot++ ) return 1;
+ } else if ( OID_CHAR( val->bv_val[i] ) ) {
+ dot = 0;
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return !dot ? LDAP_SUCCESS : LDAP_INVALID_SYNTAX;
+
+ } else if( DESC_LEADCHAR(val->bv_val[0]) ) {
+ for(i=1; i < val->bv_len; i++) {
+ if( !DESC_CHAR(val->bv_val[i] ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }