]> git.sur5r.net Git - openldap/commitdiff
Matched Values implementation (ITS#1776) based upon submission
authorKurt Zeilenga <kurt@openldap.org>
Thu, 2 May 2002 18:56:56 +0000 (18:56 +0000)
committerKurt Zeilenga <kurt@openldap.org>
Thu, 2 May 2002 18:56:56 +0000 (18:56 +0000)
form Mikhail Sahalaev <M.Sahalayev@pgr.salford.ac.uk>.
Further work needed:
add testxxx-matchedvalues
rework ldapsearch(1) portion of patch to generalize use of options
---
Copyright 2001, Mikhail Sahalaev, All rights reserved.
This software is not subject to any license of University Of
Salford.

Redistribution and use in source and binary forms are permitted
without restriction or fee of any kind as long as this notice
is preserved.

14 files changed:
include/ldap.h
libraries/libldap/filter.c
servers/slapd/Makefile.in
servers/slapd/back-bdb/init.c
servers/slapd/back-dnssrv/init.c
servers/slapd/back-ldbm/init.c
servers/slapd/back-monitor/init.c
servers/slapd/connection.c
servers/slapd/controls.c
servers/slapd/filter.c
servers/slapd/matchedValues.c [new file with mode: 0644]
servers/slapd/proto-slap.h
servers/slapd/result.c
servers/slapd/slap.h

index 0332f88597c847b69395cc4b7d9af25bcd5b3be9..e60b453d5d895221db4e565efc75bf032d359ef6 100644 (file)
@@ -202,6 +202,8 @@ typedef struct ldapcontrol {
 #define LDAP_CONTROL_VLVREQUEST        "2.16.840.1.113730.3.4.9"
 #define LDAP_CONTROL_VLVRESPONSE    "2.16.840.1.113730.3.4.10"
 
+#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.334810.2.3"
+
 /* LDAP Unsolicited Notifications */
 #define        LDAP_NOTICE_OF_DISCONNECTION    "1.3.6.1.4.1.1466.20036"
 #define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION
index fe0fab327b639245fa53e2ee977d9e2b181547d6..7d40d1ceb472588ee886da4c89df09f19c7a18b5 100644 (file)
 
 #include "ldap-int.h"
 
+static int put_simple_vrFilter LDAP_P((
+       BerElement *ber,
+       char *str ));
+
+static int put_vrFilter_list LDAP_P((
+       BerElement *ber,
+       char *str ));
+
 static char *put_complex_filter LDAP_P((
        BerElement *ber,
        char *str,
@@ -799,3 +807,387 @@ put_substring_filter( BerElement *ber, char *type, char *val )
 
        return 0;
 }
+
+int
+ldap_pvt_put_vrFilter( BerElement *ber, const char *str_in )
+{
+       int rc;
+       char    *freeme;
+       char    *str;
+       char    *next;
+       int     parens, balance, escape;
+
+       /*
+        * A ValuesReturnFilter looks like this:
+        *
+        *      ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
+        *      SimpleFilterItem ::= CHOICE {
+        *              equalityMatch   [3]     AttributeValueAssertion,
+        *              substrings      [4]     SubstringFilter,
+        *              greaterOrEqual  [5]     AttributeValueAssertion,
+        *              lessOrEqual     [6]     AttributeValueAssertion,
+        *              present         [7]     AttributeType,
+        *              approxMatch     [8]     AttributeValueAssertion,
+        *              extensibleMatch [9]     SimpleMatchingAssertion -- LDAPv3
+        *      }
+        *
+        *      SubstringFilter ::= SEQUENCE {
+        *              type               AttributeType,
+        *              SEQUENCE OF CHOICE {
+        *                      initial          [0] IA5String,
+        *                      any              [1] IA5String,
+        *                      final            [2] IA5String
+        *              }
+        *      }
+        *
+        *      SimpleMatchingAssertion ::= SEQUENCE {  -- LDAPv3
+        *              matchingRule    [1] MatchingRuleId OPTIONAL,
+        *              type            [2] AttributeDescription OPTIONAL,
+        *              matchValue      [3] AssertionValue }
+        */
+
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, "ldap_pvt_put_vrFilter: \"%s\"\n",
+               str_in ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
+#endif
+
+       freeme = LDAP_STRDUP( str_in );
+       if( freeme == NULL ) return LDAP_NO_MEMORY;
+       str = freeme;
+
+       parens = 0;
+       while ( *str ) {
+               switch ( *str ) {
+               case '(': /*')'*/
+                       str++;
+                       parens++;
+
+                       /* skip spaces */
+                       while( LDAP_SPACE( *str ) ) str++;
+
+                       switch ( *str ) {
+                       case '(':
+                               if ( (next = find_right_paren( str )) == NULL ) {
+                                       rc = -1;
+                                       goto done;
+                               }
+
+                               *next = '\0';
+
+                               if ( put_vrFilter_list( ber, str ) == -1 ) {
+                                       rc = -1;
+                                       goto done;
+                               }
+
+                               /* close the '(' */
+                               *next++ = ')';
+
+                               str = next;
+
+                               parens--;
+                               break;
+
+
+                       default:
+#ifdef NEW_LOGGING
+                               LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
+                                       "ldap_pvt_put_vrFilter: simple\n" ));
+#else
+                               Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
+                                   0, 0, 0 );
+#endif
+
+                               balance = 1;
+                               escape = 0;
+                               next = str;
+
+                               while ( *next && balance ) {
+                                       if ( escape == 0 ) {
+                                               if ( *next == '(' ) {
+                                                       balance++;
+                                               } else if ( *next == ')' ) {
+                                                       balance--;
+                                               }
+                                       }
+
+                                       if ( *next == '\\' && ! escape ) {
+                                               escape = 1;
+                                       } else {
+                                               escape = 0;
+                                       }
+
+                                       if ( balance ) next++;
+                               }
+
+                               if ( balance != 0 ) {
+                                       rc = -1;
+                                       goto done;
+                               }
+
+                               *next = '\0';
+
+                               if ( put_simple_vrFilter( ber, str ) == -1 ) {
+                                       rc = -1;
+                                       goto done;
+                               }
+
+                               *next++ = /*'('*/ ')';
+
+                               str = next;
+                               parens--;
+                               break;
+                       }
+                       break;
+
+               case /*'('*/ ')':
+#ifdef NEW_LOGGING
+                       LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
+                               "ldap_pvt_put_filter: end\n" ));
+#else
+                       Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
+                               0, 0, 0 );
+#endif
+                       if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+                               rc = -1;
+                               goto done;
+                       }
+                       str++;
+                       parens--;
+                       break;
+
+               case ' ':
+                       str++;
+                       break;
+
+               default:        /* assume it's a simple type=value filter */
+#ifdef NEW_LOGGING
+                       LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
+                               "ldap_pvt_put_filter: default\n" ));
+#else
+                       Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
+                               0, 0, 0 );
+#endif
+                       next = strchr( str, '\0' );
+                       if ( put_simple_filter( ber, str ) == -1 ) {
+                               rc = -1;
+                               goto done;
+                       }
+                       str = next;
+                       break;
+               }
+       }
+
+       rc = parens ? -1 : 0;
+
+done:
+       LDAP_FREE( freeme );
+       return rc;
+}
+
+int
+put_vrFilter( BerElement *ber, const char *str_in )
+{
+       int rc =0;
+       
+       if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
+               rc = -1;
+       }
+       
+       rc = ldap_pvt_put_vrFilter( ber, str_in );
+
+       if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+               rc = -1;
+       }
+       
+       return rc;
+}
+
+static int
+put_vrFilter_list( BerElement *ber, char *str )
+{
+       char    *next = NULL;
+       char    save;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
+               "put_vrFilter_list \"%s\"\n", str ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
+               str, 0, 0 );
+#endif
+
+       while ( *str ) {
+               while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
+                       str++;
+               }
+               if ( *str == '\0' ) break;
+
+               if ( (next = find_right_paren( str + 1 )) == NULL ) {
+                       return -1;
+               }
+               save = *++next;
+
+               /* now we have "(filter)" with str pointing to it */
+               *next = '\0';
+               if ( ldap_pvt_put_vrFilter( ber, str ) == -1 ) return -1;
+               *next = save;
+               str = next;
+       }
+
+       return 0;
+}
+
+static int
+put_simple_vrFilter(
+       BerElement *ber,
+       char *str )
+{
+       char            *s;
+       char            *value;
+       ber_tag_t       ftype;
+       int             rc = -1;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
+               "put_simple_vrFilter: \"%s\"\n", str ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
+               str, 0, 0 );
+#endif
+
+       str = LDAP_STRDUP( str );
+       if( str == NULL ) return -1;
+
+       if ( (s = strchr( str, '=' )) == NULL ) {
+               goto done;
+       }
+
+       value = s + 1;
+       *s-- = '\0';
+
+       switch ( *s ) {
+       case '<':
+               ftype = LDAP_FILTER_LE;
+               *s = '\0';
+               break;
+
+       case '>':
+               ftype = LDAP_FILTER_GE;
+               *s = '\0';
+               break;
+
+       case '~':
+               ftype = LDAP_FILTER_APPROX;
+               *s = '\0';
+               break;
+
+       case ':':
+               /* According to ValuesReturnFilter control definition
+                * extensible filters are off the form:
+                *              type [:rule] := value
+                * or   :rule := value          
+                */
+               ftype = LDAP_FILTER_EXT;
+               *s = '\0';
+
+               {
+                       char *rule = strchr( str, ':' );
+                       *rule++ = '\0';
+
+                       if( rule == NULL ) {
+                               /* must have attribute */
+                               if( !ldap_is_desc( str ) ) {
+                                       goto done;
+                               }
+                               rule = "";
+                       
+                       } else {
+                               *rule++ = '\0';
+                       }
+
+
+                       if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
+                               /* must have either type or rule */
+                               goto done;
+                       }
+
+                       if ( *str != '\0' && !ldap_is_desc( str ) ) {
+                               goto done;
+                       }
+
+                       if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
+                               goto done;
+                       }
+
+                       rc = ber_printf( ber, "t{" /*"}"*/, ftype );
+
+                       if( rc != -1 && rule && *rule != '\0' ) {
+                               rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
+                       }
+
+                       if( rc != -1 && *str != '\0' ) {
+                               rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
+                       }
+
+                       if( rc != -1 ) {
+                               ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+                               if( len >= 0 ) {
+                                       rc = ber_printf( ber, "to",
+                                               LDAP_FILTER_EXT_VALUE, value, len );
+                               } else {
+                                       rc = -1;
+                               }
+                       }
+
+                       if( rc != -1 ) { 
+                               rc = ber_printf( ber, /*"{"*/ "N}" );
+                       }
+               }
+               goto done;
+
+       default:
+               if( !ldap_is_desc( str ) ) {
+                       goto done;
+
+               } else {
+                       char *nextstar = ldap_pvt_find_wildcard( value );
+
+                       if ( nextstar == NULL ) {
+                               goto done;
+
+                       } else if ( *nextstar == '\0' ) {
+                               ftype = LDAP_FILTER_EQUALITY;
+
+                       } else if ( strcmp( value, "*" ) == 0 ) {
+                               ftype = LDAP_FILTER_PRESENT;
+
+                       } else {
+                               rc = put_substring_filter( ber, str, value );
+                               goto done;
+                       }
+               } break;
+       }
+
+       if( !ldap_is_desc( str ) ) goto done;
+
+       if ( ftype == LDAP_FILTER_PRESENT ) {
+               rc = ber_printf( ber, "ts", ftype, str );
+
+       } else {
+               ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+               if( len >= 0 ) {
+                       rc = ber_printf( ber, "t{soN}",
+                               ftype, str, value, len );
+               }
+       }
+
+done:
+       if( rc != -1 ) rc = 0;
+       LDAP_FREE( str );
+       return rc;
+}
+
index 2d77b635eeaf34ecab23a4d96c64d2346e86fcc7..936ee9d1062fa091f1ce7984d854f27953872867 100644 (file)
@@ -19,7 +19,7 @@ SRCS  = main.c daemon.c connection.c search.c filter.c add.c charray.c \
                schemaparse.c ad.c at.c mr.c syntax.c oc.c saslauthz.c \
                oidm.c starttls.c index.c sets.c referral.c \
                root_dse.c sasl.c module.c suffixalias.c mra.c mods.c \
-               limits.c backglue.c operational.c \
+               limits.c backglue.c operational.c matchedValues.c \
                $(@PLAT@_SRCS)
 
 OBJS   = main.o daemon.o connection.o search.o filter.o add.o charray.o \
@@ -32,7 +32,7 @@ OBJS  = main.o daemon.o connection.o search.o filter.o add.o charray.o \
                schemaparse.o ad.o at.o mr.o syntax.o oc.o saslauthz.o \
                oidm.o starttls.o index.o sets.o referral.o \
                root_dse.o sasl.o module.o suffixalias.o mra.o mods.o \
-               limits.o backglue.o operational.o \
+               limits.o backglue.o operational.o matchedValues.o \
                $(@PLAT@_OBJS)
 
 LDAP_INCDIR= ../../include
index a1bb111a7b662d120206415eed9dce1553328cb6..0a366fd2b7df8221948824619e4dd7a9b591d235 100644 (file)
@@ -479,6 +479,9 @@ bdb_initialize(
 #endif
 #ifdef LDAP_CONTROL_NOOP
                LDAP_CONTROL_NOOP,
+#endif
+#ifdef LDAP_CONTROL_VALUESRETURNFILTER
+               LDAP_CONTROL_VALUESRETURNFILTER,
 #endif
                NULL
        };
index 9b4059f827c08fb6d738dd7d778aa81d6d3cd3e6..3363644477c17911ff391cf6ed4d4e10298ae658 100644 (file)
@@ -36,6 +36,7 @@ dnssrv_back_initialize(
 {
        static char *controls[] = {
                LDAP_CONTROL_MANAGEDSAIT,
+               LDAP_CONTROL_VALUESRETURNFILTER,
                NULL
        };
 
index ada60432c27dda7052075b41c1d7204e883dfdc3..d3e16e31c95b4b709180b1ca7e4aaf4f20a8e401 100644 (file)
@@ -37,6 +37,7 @@ ldbm_back_initialize(
 {
        static char *controls[] = {
                LDAP_CONTROL_MANAGEDSAIT,
+               LDAP_CONTROL_VALUESRETURNFILTER,
                NULL
        };
 
index 44d81d0529b5c0c5dc4cb3f27c38cdac2ca358cf..b8637dd0d800d6c88ad4c30f6bd7cad7b5b23a26 100644 (file)
@@ -163,6 +163,7 @@ monitor_back_initialize(
 {
        static char *controls[] = {
                LDAP_CONTROL_MANAGEDSAIT,
+               LDAP_CONTROL_VALUESRETURNFILTER,
                NULL
        };
 
index 43844eb97ee233649cd79c2630bc611107151752..6f47c9d1be6d9d7b6c3f84978db3518be8f01f22 100644 (file)
@@ -1423,6 +1423,8 @@ connection_input(
 
        op = slap_op_alloc( ber, msgid, tag, conn->c_n_ops_received++ );
 
+       op->vrFilter = NULL;
+
        op->o_pagedresults_state = conn->c_pagedresults_state;
 
 #ifdef LDAP_CONNECTIONLESS
index b475dd1af44ef3789ebc49bfebbf9bf95934b67d..16ea8cb6ccc87b76c224fdd30728a7a0a3c446a6 100644 (file)
@@ -47,6 +47,7 @@ static SLAP_CTRL_PARSE_FN parseManageDSAit;
 static SLAP_CTRL_PARSE_FN parseSubentries;
 static SLAP_CTRL_PARSE_FN parseNoOp;
 static SLAP_CTRL_PARSE_FN parsePagedResults;
+static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
 
 static struct slap_control {
        char *sc_oid;
@@ -72,6 +73,11 @@ static struct slap_control {
        { LDAP_CONTROL_PAGEDRESULTS_REQUEST,
                SLAP_CTRL_SEARCH, NULL,
                parsePagedResults },
+#endif
+#ifdef LDAP_CONTROL_VALUESRETURNFILTER
+       { LDAP_CONTROL_VALUESRETURNFILTER,
+               SLAP_CTRL_SEARCH, NULL,
+               parseValuesReturnFilter },
 #endif
        { NULL }
 };
@@ -527,3 +533,61 @@ static int parsePagedResults (
        return LDAP_SUCCESS;
 }
 #endif
+
+#ifdef LDAP_CONTROL_VALUESRETURNFILTER
+int parseValuesReturnFilter (
+       Connection *conn,
+       Operation *op,
+       LDAPControl *ctrl,
+       const char **text )
+{
+       int             rc;
+       BerElement      *ber;
+       struct berval   fstr = { 0, NULL };
+       const char *err_msg = "";
+
+       if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) {
+               *text = "valuesreturnfilter control specified multiple times";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber = ber_init( &(ctrl->ldctl_value) );
+       if (ber == NULL) {
+               *text = "internal error";
+               return LDAP_OTHER;
+       }
+       
+       rc = get_vrFilter( conn, ber, &(op->vrFilter), &err_msg);
+
+       if( rc != LDAP_SUCCESS ) {
+               text = &err_msg;
+               if( rc == SLAPD_DISCONNECT ) {
+                       send_ldap_disconnect( conn, op,
+                               LDAP_PROTOCOL_ERROR, *text );
+               } else {
+                       send_ldap_result( conn, op, rc,
+                               NULL, *text, NULL, NULL );
+               }
+               if( fstr.bv_val != NULL) free( fstr.bv_val );
+               if( op->vrFilter != NULL) vrFilter_free( op->vrFilter ); 
+
+       } else {
+               vrFilter2bv( op->vrFilter, &fstr );
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "operation", LDAP_LEVEL_ARGS,
+               "parseValuesReturnFilter: conn %d       vrFilter: %s\n", conn->c_connid,
+               fstr.bv_len ? fstr.bv_val : "empty" ));
+#else
+       Debug( LDAP_DEBUG_ARGS, "       vrFilter: %s\n",
+               fstr.bv_len ? fstr.bv_val : "empty", 0, 0 );
+#endif
+
+       op->o_valuesreturnfilter = ctrl->ldctl_iscritical
+               ? SLAP_CRITICAL_CONTROL
+               : SLAP_NONCRITICAL_CONTROL;
+
+       return LDAP_SUCCESS;
+}
+#endif
index 16124c0c31bc588670946ab4a706ca3aef10a650..105a3fb68cc7116f1d48f3dbec3fcfc1e3db5df4 100644 (file)
@@ -30,6 +30,17 @@ static int filter_escape_value(
        struct berval *in,
        struct berval *out );
 
+static void simple_vrFilter2bv(
+       ValuesReturnFilter *f,
+       struct berval *fstr );
+
+static int     get_simple_vrFilter(
+       Connection *conn,
+       BerElement *ber,
+       ValuesReturnFilter **f,
+       const char **text );
+
+
 int
 get_filter(
        Connection *conn,
@@ -796,3 +807,719 @@ static int filter_escape_value(
        out->bv_val[out->bv_len] = '\0';
        return LDAP_SUCCESS;
 }
+
+static int
+get_simple_vrFilter(
+       Connection *conn,
+       BerElement *ber,
+       ValuesReturnFilter **filt,
+       const char **text )
+{
+       ber_tag_t       tag;
+       ber_len_t       len;
+       int             err;
+       ValuesReturnFilter *f;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, "get_simple_vrFilter: conn %d\n",
+               conn->c_connid ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "begin get_simple_vrFilter\n", 0, 0, 0 );
+#endif
+
+       tag = ber_peek_tag( ber, &len );
+
+       if( tag == LBER_ERROR ) {
+               *text = "error decoding filter";
+               return SLAPD_DISCONNECT;
+       }
+
+       f = (ValuesReturnFilter *) ch_malloc( sizeof(ValuesReturnFilter) );
+       f->f_next = NULL;
+
+       err = LDAP_SUCCESS;
+       f->f_choice = tag; 
+
+       switch ( f->f_choice ) {
+       case LDAP_FILTER_EQUALITY:
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL2,
+                       "get_simple_vrFilter: conn %d  EQUALITY\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "EQUALITY\n", 0, 0, 0 );
+#endif
+               err = get_ava( ber, &f->f_ava, SLAP_MR_EQUALITY, text );
+               if ( err != LDAP_SUCCESS ) {
+                       break;
+               }
+
+               assert( f->f_ava != NULL );
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                       "get_simple_vrFilter: conn %d  SUBSTRINGS\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "SUBSTRINGS\n", 0, 0, 0 );
+#endif
+               err = get_substring_filter( conn, ber, (Filter *)f, text );
+               break;
+
+       case LDAP_FILTER_GE:
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                       "get_simple_vrFilter: conn %d  GE\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "GE\n", 0, 0, 0 );
+#endif
+               err = get_ava( ber, &f->f_ava, SLAP_MR_ORDERING, text );
+               if ( err != LDAP_SUCCESS ) {
+                       break;
+               }
+               break;
+
+       case LDAP_FILTER_LE:
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                       "get_simple_vrFilter: conn %d  LE\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "LE\n", 0, 0, 0 );
+#endif
+               err = get_ava( ber, &f->f_ava, SLAP_MR_ORDERING, text );
+               if ( err != LDAP_SUCCESS ) {
+                       break;
+               }
+               break;
+
+       case LDAP_FILTER_PRESENT: {
+               struct berval type;
+
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                       "get_simple_vrFilter: conn %d PRESENT\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "PRESENT\n", 0, 0, 0 );
+#endif
+               if ( ber_scanf( ber, "m", &type ) == LBER_ERROR ) {
+                       err = SLAPD_DISCONNECT;
+                       *text = "error decoding filter";
+                       break;
+               }
+
+               f->f_desc = NULL;
+               err = slap_bv2ad( &type, &f->f_desc, text );
+
+               if( err != LDAP_SUCCESS ) {
+                       /* unrecognized attribute description or other error */
+                       f->f_choice = SLAPD_FILTER_COMPUTED;
+                       f->f_result = LDAP_COMPARE_FALSE;
+                       err = LDAP_SUCCESS;
+                       break;
+               }
+               } break;
+
+       case LDAP_FILTER_APPROX:
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                       "get_simple_vrFilter: conn %d  APPROX\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "APPROX\n", 0, 0, 0 );
+#endif
+               err = get_ava( ber, &f->f_ava, SLAP_MR_EQUALITY_APPROX, text );
+               if ( err != LDAP_SUCCESS ) {
+                       break;
+               }
+               break;
+
+       case LDAP_FILTER_EXT:
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                       "get_simple_vrFilter: conn %d  EXTENSIBLE\n", conn->c_connid ));
+#else
+               Debug( LDAP_DEBUG_FILTER, "EXTENSIBLE\n", 0, 0, 0 );
+#endif
+
+               err = get_mra( ber, &f->f_mra, text );
+               if ( err != LDAP_SUCCESS ) {
+                       break;
+               }
+
+               assert( f->f_mra != NULL );
+               break;
+
+       default:
+               (void) ber_scanf( ber, "x" ); /* skip the element */
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "filter", LDAP_LEVEL_ERR,
+                       "get_simple_vrFilter: conn %d unknown filter type=%lu\n",
+                       conn->c_connid, f->f_choice ));
+#else
+               Debug( LDAP_DEBUG_ANY, "get_simple_vrFilter: unknown filter type=%lu\n",
+                       f->f_choice, 0, 0 );
+#endif
+               f->f_choice = SLAPD_FILTER_COMPUTED;
+               f->f_result = SLAPD_COMPARE_UNDEFINED;
+               break;
+       }
+
+       if ( err != LDAP_SUCCESS ) {
+               if( err != SLAPD_DISCONNECT ) {
+                       /* ignore error */
+                       f->f_choice = SLAPD_FILTER_COMPUTED;
+                       f->f_result = SLAPD_COMPARE_UNDEFINED;
+                       err = LDAP_SUCCESS;
+                       *filt = f;
+
+               } else {
+                       free(f);
+               }
+
+       } else {
+               *filt = f;
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL2,
+               "get_simple_vrFilter: conn %d exit\n", conn->c_connid ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "end get_simple_vrFilter %d\n", err, 0, 0 );
+#endif
+       return( err );
+}
+
+int
+get_vrFilter( Connection *conn, BerElement *ber,
+       ValuesReturnFilter **f,
+       const char **text )
+{
+       /*
+        * A ValuesReturnFilter looks like this:
+        *
+        *      ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
+        *      SimpleFilterItem ::= CHOICE {
+        *              equalityMatch   [3]     AttributeValueAssertion,
+        *              substrings      [4]     SubstringFilter,
+        *              greaterOrEqual  [5]     AttributeValueAssertion,
+        *              lessOrEqual     [6]     AttributeValueAssertion,
+        *              present         [7]     AttributeType,
+        *              approxMatch     [8]     AttributeValueAssertion,
+        *              extensibleMatch [9]     SimpleMatchingAssertion -- LDAPv3
+        *      }
+        *
+        *      SubstringFilter ::= SEQUENCE {
+        *              type               AttributeType,
+        *              SEQUENCE OF CHOICE {
+        *                      initial          [0] IA5String,
+        *                      any              [1] IA5String,
+        *                      final            [2] IA5String
+        *              }
+        *      }
+        *
+        *      SimpleMatchingAssertion ::= SEQUENCE {  -- LDAPv3
+        *              matchingRule    [1] MatchingRuleId OPTIONAL,
+        *              type            [2] AttributeDescription OPTIONAL,
+        *              matchValue      [3] AssertionValue }
+        */
+
+       ValuesReturnFilter **new;
+       int             err;
+       ber_tag_t       tag;
+       ber_len_t       len;
+       char            *last;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
+               "get_vrFilter: conn %d start\n", conn->c_connid ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "begin get_vrFilter\n", 0, 0, 0 );
+#endif
+
+       tag = ber_peek_tag( ber, &len );
+
+       if( tag == LBER_ERROR ) {
+               *text = "error decoding vrFilter";
+               return SLAPD_DISCONNECT;
+       }
+
+       if( tag != LBER_SEQUENCE ) {
+               *text = "error decoding vrFilter, expect SEQUENCE tag";
+               return SLAPD_DISCONNECT;
+       }
+
+       new = f;
+       for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
+               tag = ber_next_element( ber, &len, last ) )
+       {
+               err = get_simple_vrFilter( conn, ber, new, text );
+               if ( err != LDAP_SUCCESS )
+                       return( err );
+               new = &(*new)->f_next;
+       }
+       *new = NULL;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
+               "get_vrFilter: conn %d exit\n", conn->c_connid ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "end get_vrFilter %d\n", err, 0, 0 );
+#endif
+       return( LDAP_SUCCESS );
+}
+
+void
+vrFilter_free( ValuesReturnFilter *f )
+{
+       ValuesReturnFilter      *p, *next;
+
+       if ( f == NULL ) {
+               return;
+       }
+
+       for ( p = f; p != NULL; p = next ) {
+               next = p->f_next;
+
+               switch ( f->f_choice ) {
+               case LDAP_FILTER_PRESENT:
+                       break;
+
+               case LDAP_FILTER_EQUALITY:
+               case LDAP_FILTER_GE:
+               case LDAP_FILTER_LE:
+               case LDAP_FILTER_APPROX:
+                       ava_free( f->f_ava, 1 );
+                       break;
+
+               case LDAP_FILTER_SUBSTRINGS:
+                       if ( f->f_sub_initial.bv_val != NULL ) {
+                               free( f->f_sub_initial.bv_val );
+                       }
+                       ber_bvarray_free( f->f_sub_any );
+                       if ( f->f_sub_final.bv_val != NULL ) {
+                               free( f->f_sub_final.bv_val );
+                       }
+                       ch_free( f->f_sub );
+                       break;
+
+               case LDAP_FILTER_EXT:
+                       mra_free( f->f_mra, 1 );
+                       break;
+
+               case SLAPD_FILTER_COMPUTED:
+                       break;
+
+               default:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_ERR,
+                               "filter_free: unknown filter type %lu\n", f->f_choice ));
+#else
+                       Debug( LDAP_DEBUG_ANY, "filter_free: unknown filter type=%lu\n",
+                               f->f_choice, 0, 0 );
+#endif
+                       break;
+               }
+
+               free( f );
+       }
+}
+
+
+void
+vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr )
+{
+       int     i;
+       ValuesReturnFilter      *p;
+       struct berval tmp;
+       ber_len_t len;
+
+       if ( f == NULL ) {
+               ber_str2bv( "No filter!", sizeof("No filter!")-1, 1, fstr );
+               return;
+       }
+
+       fstr->bv_len = sizeof("()") - 1;
+       fstr->bv_val = malloc( fstr->bv_len + 128 );
+
+       snprintf( fstr->bv_val, fstr->bv_len + 1, "()");
+
+       for ( p = f; p != NULL; p = p->f_next ) {
+               len = fstr->bv_len;
+
+               simple_vrFilter2bv( p, &tmp );
+                       
+               fstr->bv_len += tmp.bv_len;
+               fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+
+               snprintf( &fstr->bv_val[len-1], tmp.bv_len + 2, 
+                       /*"("*/ "%s)", tmp.bv_val );
+
+               ch_free( tmp.bv_val );
+       }
+}
+
+static void
+simple_vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr )
+{
+       int     i;
+       ValuesReturnFilter      *p;
+       struct berval tmp;
+       ber_len_t len;
+
+       if ( f == NULL ) {
+               ber_str2bv( "No filter!", sizeof("No filter!")-1, 1, fstr );
+               return;
+       }
+
+       switch ( f->f_choice ) {
+       case LDAP_FILTER_EQUALITY:
+               filter_escape_value( &f->f_av_value, &tmp );
+
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len +
+                       tmp.bv_len + ( sizeof("(=)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 1 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+                       f->f_av_desc->ad_cname.bv_val,
+                       tmp.bv_val );
+
+               ber_memfree( tmp.bv_val );
+               break;
+
+       case LDAP_FILTER_GE:
+               filter_escape_value( &f->f_av_value, &tmp );
+
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len +
+                       tmp.bv_len + ( sizeof("(>=)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 1 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+                       f->f_av_desc->ad_cname.bv_val,
+                       tmp.bv_val );
+
+               ber_memfree( tmp.bv_val );
+               break;
+
+       case LDAP_FILTER_LE:
+               filter_escape_value( &f->f_av_value, &tmp );
+
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len +
+                       tmp.bv_len + ( sizeof("(<=)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 1 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+                       f->f_av_desc->ad_cname.bv_val,
+                       tmp.bv_val );
+
+               ber_memfree( tmp.bv_val );
+               break;
+
+       case LDAP_FILTER_APPROX:
+               filter_escape_value( &f->f_av_value, &tmp );
+
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len +
+                       tmp.bv_len + ( sizeof("(~=)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 1 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+                       f->f_av_desc->ad_cname.bv_val,
+                       tmp.bv_val );
+               ber_memfree( tmp.bv_val );
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+               fstr->bv_len = f->f_sub_desc->ad_cname.bv_len +
+                       ( sizeof("(=*)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 128 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+                       f->f_sub_desc->ad_cname.bv_val );
+
+               if ( f->f_sub_initial.bv_val != NULL ) {
+                       len = fstr->bv_len;
+
+                       filter_escape_value( &f->f_sub_initial, &tmp );
+
+                       fstr->bv_len += tmp.bv_len;
+                       fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+
+                       snprintf( &fstr->bv_val[len-2], tmp.bv_len+3,
+                               /* "(attr=" */ "%s*)",
+                               tmp.bv_val );
+
+                       ber_memfree( tmp.bv_val );
+               }
+
+               if ( f->f_sub_any != NULL ) {
+                       for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ ) {
+                               len = fstr->bv_len;
+                               filter_escape_value( &f->f_sub_any[i], &tmp );
+
+                               fstr->bv_len += tmp.bv_len + 1;
+                               fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+
+                               snprintf( &fstr->bv_val[len-1], tmp.bv_len+3,
+                                       /* "(attr=[init]*[any*]" */ "%s*)",
+                                       tmp.bv_val );
+                               ber_memfree( tmp.bv_val );
+                       }
+               }
+
+               if ( f->f_sub_final.bv_val != NULL ) {
+                       len = fstr->bv_len;
+
+                       filter_escape_value( &f->f_sub_final, &tmp );
+
+                       fstr->bv_len += tmp.bv_len;
+                       fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
+
+                       snprintf( &fstr->bv_val[len-1], tmp.bv_len+3,
+                               /* "(attr=[init*][any*]" */ "%s)",
+                               tmp.bv_val );
+
+                       ber_memfree( tmp.bv_val );
+               }
+
+               break;
+
+       case LDAP_FILTER_PRESENT:
+               fstr->bv_len = f->f_desc->ad_cname.bv_len +
+                       ( sizeof("(=*)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 1 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+                       f->f_desc->ad_cname.bv_val );
+               break;
+
+       case LDAP_FILTER_EXT:
+               filter_escape_value( &f->f_mr_value, &tmp );
+
+               fstr->bv_len = f->f_mr_desc->ad_cname.bv_len +
+                       ( f->f_mr_dnattrs ? sizeof(":dn")-1 : 0 ) +
+                       ( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len+1 : 0 ) +
+                       tmp.bv_len + ( sizeof("(:=)") - 1 );
+               fstr->bv_val = malloc( fstr->bv_len + 1 );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+                       f->f_mr_desc->ad_cname.bv_val,
+                       f->f_mr_dnattrs ? ":dn" : "",
+                       f->f_mr_rule_text.bv_len ? ":" : "",
+                       f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "",
+                       tmp.bv_val );
+               ber_memfree( tmp.bv_val );
+               break;
+
+       case SLAPD_FILTER_COMPUTED:
+               ber_str2bv(
+                       f->f_result == LDAP_COMPARE_FALSE ? "(?=false)" :
+                       f->f_result == LDAP_COMPARE_TRUE ? "(?=true)" :
+                       f->f_result == SLAPD_COMPARE_UNDEFINED ? "(?=undefined)" :
+                       "(?=error)",
+                       f->f_result == LDAP_COMPARE_FALSE ? sizeof("(?=false)")-1 :
+                       f->f_result == LDAP_COMPARE_TRUE ? sizeof("(?=true)")-1 :
+                       f->f_result == SLAPD_COMPARE_UNDEFINED ? sizeof("(?=undefined)")-1 :
+                       sizeof("(?=error)")-1,
+                       1, fstr );
+               break;
+
+       default:
+               ber_str2bv( "(?=unknown)", sizeof("(?=unknown)")-1, 1, fstr );
+               break;
+       }
+}
+
+static int
+get_substring_vrFilter(
+       Connection      *conn,
+       BerElement      *ber,
+       ValuesReturnFilter      *f,
+       const char      **text )
+{
+       ber_tag_t       tag;
+       ber_len_t       len;
+       ber_tag_t       rc;
+       struct berval value;
+       char            *last;
+       struct berval bv;
+       *text = "error decoding filter";
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
+               "get_substring_filter: conn %d  begin\n", conn->c_connid ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "begin get_substring_filter\n", 0, 0, 0 );
+#endif
+       if ( ber_scanf( ber, "{m" /*}*/, &bv ) == LBER_ERROR ) {
+               return SLAPD_DISCONNECT;
+       }
+
+       f->f_sub = ch_calloc( 1, sizeof(SubstringsAssertion) );
+       f->f_sub_desc = NULL;
+       rc = slap_bv2ad( &bv, &f->f_sub_desc, text );
+
+       if( rc != LDAP_SUCCESS ) {
+               text = NULL;
+               ch_free( f->f_sub );
+               f->f_choice = SLAPD_FILTER_COMPUTED;
+               f->f_result = SLAPD_COMPARE_UNDEFINED;
+               return LDAP_SUCCESS;
+       }
+
+       f->f_sub_initial.bv_val = NULL;
+       f->f_sub_any = NULL;
+       f->f_sub_final.bv_val = NULL;
+
+       for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
+               tag = ber_next_element( ber, &len, last ) )
+       {
+               unsigned usage;
+
+               rc = ber_scanf( ber, "m", &value );
+               if ( rc == LBER_ERROR ) {
+                       rc = SLAPD_DISCONNECT;
+                       goto return_error;
+               }
+
+               if ( value.bv_val == NULL || value.bv_len == 0 ) {
+                       rc = LDAP_INVALID_SYNTAX;
+                       goto return_error;
+               } 
+
+               switch ( tag ) {
+               case LDAP_SUBSTRING_INITIAL:
+                       usage = SLAP_MR_SUBSTR_INITIAL;
+                       break;
+
+               case LDAP_SUBSTRING_ANY:
+                       usage = SLAP_MR_SUBSTR_ANY;
+                       break;
+
+               case LDAP_SUBSTRING_FINAL:
+                       usage = SLAP_MR_SUBSTR_FINAL;
+                       break;
+
+               default:
+                       rc = LDAP_PROTOCOL_ERROR;
+
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_ERR,
+                               "get_filter_substring: conn %d  unknown substring choice=%ld\n",
+                               conn->c_connid, (long)tag ));
+#else
+                       Debug( LDAP_DEBUG_FILTER,
+                               "  unknown substring choice=%ld\n",
+                               (long) tag, 0, 0 );
+#endif
+                       goto return_error;
+               }
+
+               /* valiate using equality matching rule validator! */
+               rc = value_validate( f->f_sub_desc->ad_type->sat_equality,
+                       &value, text );
+               if( rc != LDAP_SUCCESS ) {
+                       goto return_error;
+               }
+
+               rc = value_normalize( f->f_sub_desc, usage,
+                       &value, &bv, text );
+               if( rc != LDAP_SUCCESS ) {
+                       goto return_error;
+               }
+
+               value = bv;
+
+               rc = LDAP_PROTOCOL_ERROR;
+
+               switch ( tag ) {
+               case LDAP_SUBSTRING_INITIAL:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "get_substring_filter: conn %d  INITIAL\n",
+                               conn->c_connid ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "  INITIAL\n", 0, 0, 0 );
+#endif
+
+                       if ( f->f_sub_initial.bv_val != NULL
+                               || f->f_sub_any != NULL 
+                               || f->f_sub_final.bv_val != NULL )
+                       {
+                               free( value.bv_val );
+                               goto return_error;
+                       }
+
+                       f->f_sub_initial = value;
+                       break;
+
+               case LDAP_SUBSTRING_ANY:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "get_substring_filter: conn %d  ANY\n",
+                               conn->c_connid ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "  ANY\n", 0, 0, 0 );
+#endif
+
+                       if ( f->f_sub_final.bv_val != NULL ) {
+                               free( value.bv_val );
+                               goto return_error;
+                       }
+
+                       ber_bvarray_add( &f->f_sub_any, &value );
+                       break;
+
+               case LDAP_SUBSTRING_FINAL:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "get_substring_filter: conn %d  FINAL\n",
+                               conn->c_connid ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "  FINAL\n", 0, 0, 0 );
+#endif
+
+                       if ( f->f_sub_final.bv_val != NULL ) {
+                               free( value.bv_val );
+                               goto return_error;
+                       }
+
+                       f->f_sub_final = value;
+                       break;
+
+               default:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_INFO,
+                               "get_substring_filter: conn %d  unknown substring type %ld\n",
+                               conn->c_connid, (long)tag ));
+#else
+                       Debug( LDAP_DEBUG_FILTER,
+                               "  unknown substring type=%ld\n",
+                               (long) tag, 0, 0 );
+#endif
+
+                       free( value.bv_val );
+
+return_error:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_INFO,
+                               "get_substring_filter: conn %d  error %ld\n",
+                               conn->c_connid, (long)rc ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "  error=%ld\n",
+                               (long) rc, 0, 0 );
+#endif
+                       free( f->f_sub_initial.bv_val );
+                       ber_bvarray_free( f->f_sub_any );
+                       free( f->f_sub_final.bv_val );
+                       ch_free( f->f_sub );
+                       return rc;
+               }
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
+               "get_substring_filter: conn %d exit\n", conn->c_connid ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "end get_substring_filter\n", 0, 0, 0 );
+#endif
+       return( LDAP_SUCCESS );
+}
+
diff --git a/servers/slapd/matchedValues.c b/servers/slapd/matchedValues.c
new file mode 100644 (file)
index 0000000..7a9b2d2
--- /dev/null
@@ -0,0 +1,423 @@
+/* $OpenLDAP$ */
+/* 
+ * Copyright 1999-2002 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted only
+ * as authorized by the OpenLDAP Public License.  A copy of this
+ * license is available at http://www.OpenLDAP.org/license.html or
+ * in file LICENSE in the top-level directory of the distribution.
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#include "../../libraries/liblber/lber-int.h"
+
+static int test_mra_vrFilter(
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       MatchingRuleAssertion *mra,
+       char            ***e_flags
+);
+
+static int
+test_substrings_vrFilter(
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       ValuesReturnFilter *f,
+       char            ***e_flags
+);
+
+static int
+test_presence_vrFilter(
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       AttributeDescription *desc,
+       char            ***e_flags
+);
+
+static int
+test_ava_vrFilter(
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       AttributeAssertion *ava,
+       int             type,
+       char            ***e_flags
+);
+
+
+int
+filter_matched_values( 
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       char            ***e_flags
+)
+{
+       ValuesReturnFilter *f;
+       Attribute       *a;
+       struct berval   *bv;
+       char            filter_found;
+       int             i, j;
+       int             rc = LDAP_SUCCESS;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
+               "filter_matched_values: begin\n" ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "=> filter_matched_values\n", 0, 0, 0 );
+#endif
+
+       for ( f = op->vrFilter; f != NULL; f = f->f_next ) {
+               switch ( f->f_choice ) {
+               case SLAPD_FILTER_COMPUTED:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "test_vrFilter: COMPUTED %s (%d)\n",
+                               f->f_result == LDAP_COMPARE_FALSE ? "false" :
+                               f->f_result == LDAP_COMPARE_TRUE         ? "true"  :
+                               f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" :
+                               "error",
+                               f->f_result ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "     COMPUTED %s (%d)\n",
+                               f->f_result == LDAP_COMPARE_FALSE ? "false" :
+                               f->f_result == LDAP_COMPARE_TRUE ? "true" :
+                               f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" : "error",
+                               f->f_result, 0 );
+#endif
+                       /*This type of filter does not affect the result */
+                       rc = LDAP_SUCCESS;
+               break;
+
+               case LDAP_FILTER_EQUALITY:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "test_vrFilter: EQUALITY\n" ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "     EQUALITY\n", 0, 0, 0 );
+#endif
+                       rc = test_ava_vrFilter( be, conn, op, e, f->f_ava,
+                               LDAP_FILTER_EQUALITY, e_flags );
+                       if( rc == -1 ) {
+                               return rc;
+                       }
+                       break;
+
+               case LDAP_FILTER_SUBSTRINGS:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "test_vrFilter  SUBSTRINGS\n" ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "     SUBSTRINGS\n", 0, 0, 0 );
+#endif
+
+                       rc = test_substrings_vrFilter( be, conn, op, e,
+                               f, e_flags );
+                       if( rc == -1 ) {
+                               return rc;
+                       }
+                       break;
+
+               case LDAP_FILTER_PRESENT:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "test_vrFilter: PRESENT\n" ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "     PRESENT\n", 0, 0, 0 );
+#endif
+                       rc = test_presence_vrFilter( be, conn, op, e,
+                               f->f_desc, e_flags );
+                       if( rc == -1 ) {
+                               return rc;
+                       }
+                       break;
+
+               case LDAP_FILTER_GE:
+                       rc = test_ava_vrFilter( be, conn, op, e, f->f_ava,
+                               LDAP_FILTER_GE, e_flags );
+                       if( rc == -1 ) {
+                               return rc;
+                       }
+                       break;
+
+               case LDAP_FILTER_LE:
+                       rc = test_ava_vrFilter( be, conn, op, e, f->f_ava,
+                               LDAP_FILTER_LE, e_flags );
+                       if( rc == -1 ) {
+                               return rc;
+                       }
+                       break;
+
+               case LDAP_FILTER_EXT:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
+                               "test_vrFilter: EXT\n" ));
+#else
+                       Debug( LDAP_DEBUG_FILTER, "     EXT\n", 0, 0, 0 );
+#endif
+                       rc = test_mra_vrFilter( be, conn, op, e,
+                               f->f_mra, e_flags );
+                       if( rc == -1 ) {
+                               return rc;
+                       }
+                       break;
+
+               default:
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "filter", LDAP_LEVEL_INFO,
+                               "test_vrFilter:  unknown filter type %lu\n", 
+                               f->f_choice ));
+#else
+                       Debug( LDAP_DEBUG_ANY, "        unknown filter type %lu\n",
+                               f->f_choice, 0, 0 );
+#endif
+                       rc = LDAP_PROTOCOL_ERROR;
+               } 
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
+               "filter_matched_values:  return=%d\n", rc ));
+#else
+       Debug( LDAP_DEBUG_FILTER, "<= filter_matched_values %d\n", rc, 0, 0 );
+#endif
+       return( rc );
+}
+
+static int
+test_ava_vrFilter(
+               Backend         *be,
+       Connection      *conn,
+               Operation       *op,
+               Entry           *e,
+       AttributeAssertion *ava,
+               int             type,
+               char            ***e_flags
+)
+{
+       int             rc;
+       int             i, j;
+       Attribute       *a;
+
+       if ( !access_allowed( be, conn, op, e,
+               ava->aa_desc, &ava->aa_value, ACL_SEARCH, NULL ) )
+       {
+               return LDAP_INSUFFICIENT_ACCESS;
+       }
+
+       for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+
+               MatchingRule *mr;
+               struct berval *bv;
+       
+               if ( !is_ad_subtype( a->a_desc, ava->aa_desc ) ) {
+                       continue;
+               }
+
+               switch ( type ) {
+               case LDAP_FILTER_APPROX:
+                       mr = a->a_desc->ad_type->sat_approx;
+                       if( mr != NULL ) break;
+
+               /* use EQUALITY matching rule if no APPROX rule */
+               case LDAP_FILTER_EQUALITY:
+                       mr = a->a_desc->ad_type->sat_equality;
+                       break;
+               
+               case LDAP_FILTER_GE:
+               case LDAP_FILTER_LE:
+                       mr = a->a_desc->ad_type->sat_ordering;
+                       break;
+
+               default:
+                       mr = NULL;
+               }
+
+               if( mr == NULL ) {
+                       continue;
+
+               }
+
+               for ( bv = a->a_vals, j=0; bv->bv_val != NULL; bv++, j++ ) {
+                       int ret;
+                       int rc;
+                       const char *text;
+
+                       rc = value_match( &ret, a->a_desc, mr, 
+                               SLAP_MR_ASSERTION_SYNTAX_MATCH, bv, &ava->aa_value, &text );
+                       if( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+
+                       switch ( type ) {
+                       case LDAP_FILTER_EQUALITY:
+                       case LDAP_FILTER_APPROX:
+                               if ( ret == 0 ) {
+                                       (*e_flags)[i][j] = 1;
+                               }
+                               break;
+       
+                       case LDAP_FILTER_GE:
+                               if ( ret >= 0 ) {
+                                       (*e_flags)[i][j] = 1;
+                               }
+                               break;
+       
+                       case LDAP_FILTER_LE:
+                               if ( ret <= 0 ) {
+                                       (*e_flags)[i][j] = 1;
+                               }
+                               break;
+                       }
+               }
+       }
+       return( LDAP_SUCCESS );
+}
+
+static int
+test_presence_vrFilter(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               Entry           *e,
+               AttributeDescription *desc,
+               char            ***e_flags
+)
+{
+       int i, j;
+       Attribute       *a;
+
+       if ( !access_allowed( be, conn, op, e, desc, NULL, ACL_SEARCH, NULL ) ) {
+               return LDAP_INSUFFICIENT_ACCESS;
+       }
+
+       for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+               struct berval *bv;
+
+               if ( !is_ad_subtype( a->a_desc, desc ) ) {
+                       continue;
+               }
+
+               for ( bv = a->a_vals, j=0; bv->bv_val != NULL; bv++, j++ );
+               memset( (*e_flags)[i], 1, j);
+       }
+
+       return( LDAP_SUCCESS );
+}
+
+static int
+test_substrings_vrFilter(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               Entry           *e,
+               ValuesReturnFilter *f,
+               char            ***e_flags
+)
+{
+       int i, j;
+       Attribute       *a;
+
+       if ( !access_allowed( be, conn, op, e,
+               f->f_sub_desc, NULL, ACL_SEARCH, NULL ) )
+       {
+               return LDAP_INSUFFICIENT_ACCESS;
+       }
+
+       for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+               MatchingRule *mr = a->a_desc->ad_type->sat_substr;
+               struct berval *bv;
+
+               if ( !is_ad_subtype( a->a_desc, f->f_sub_desc ) ) {
+                       continue;
+               }
+
+               if( mr == NULL ) {
+                       continue;
+               }
+
+               for ( bv = a->a_vals, j = 0; bv->bv_val != NULL; bv++, j++ ) {
+                       int ret;
+                       int rc;
+                       const char *text;
+
+                       rc = value_match( &ret, a->a_desc, mr,
+                               SLAP_MR_ASSERTION_SYNTAX_MATCH,
+                               bv, f->f_sub, &text );
+
+                       if( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+
+                       if ( ret == 0 ) {
+                               (*e_flags)[i][j] = 1;
+                       }
+               }
+       }
+
+       return LDAP_SUCCESS;
+}
+
+static int test_mra_vrFilter(
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       MatchingRuleAssertion *mra,
+       char            ***e_flags
+)
+{
+       int i, j;
+       Attribute       *a;
+
+       if( !access_allowed( be, conn, op, e,
+               mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) )
+       {
+               return LDAP_INSUFFICIENT_ACCESS;
+       }
+
+       for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+               struct berval *bv;
+       
+               if ( !is_ad_subtype( a->a_desc, mra->ma_desc ) ) {
+                       return( LDAP_SUCCESS );
+               }
+
+               for ( bv = a->a_vals, j = 0; bv->bv_val != NULL; bv++, j++ ) {
+                       int ret;
+                       int rc;
+                       const char *text;
+
+                       rc = value_match( &ret, a->a_desc, mra->ma_rule,
+                               SLAP_MR_ASSERTION_SYNTAX_MATCH,
+                               bv, &mra->ma_value,
+                               &text );
+
+                       if( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+
+                       if ( ret ) {
+                               (*e_flags)[i][j] = 1;
+                       }
+               }
+       }
+
+       return LDAP_SUCCESS;
+}
index c3e04319f515c168d26841459a6fc88904d42105..5f469ac84a34aab6a91e000ae86838e5c1d6de3a 100644 (file)
@@ -464,6 +464,14 @@ LDAP_SLAPD_F (int) get_filter LDAP_P((
 LDAP_SLAPD_F (void) filter_free LDAP_P(( Filter *f ));
 LDAP_SLAPD_F (void) filter2bv LDAP_P(( Filter *f, struct berval *bv ));
 
+LDAP_SLAPD_F (int) get_vrFilter( Connection *conn, BerElement *ber,
+       ValuesReturnFilter **f,
+       const char **text );
+
+LDAP_SLAPD_F (void) vrFilter_free( ValuesReturnFilter *f );
+LDAP_SLAPD_F (void) vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr );
+
+
 /*
  * filterentry.c
  */
@@ -511,6 +519,16 @@ LDAP_SLAPD_F (FILE *) lock_fopen LDAP_P(( const char *fname,
        const char *type, FILE **lfp ));
 LDAP_SLAPD_F (int) lock_fclose LDAP_P(( FILE *fp, FILE *lfp ));
 
+/*
+ * matchedValues.c
+ */
+LDAP_SLAPD_F (int) filter_matched_values( 
+       Backend         *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e,
+       char            ***e_flags );
+
 /*
  * modify.c
  */
index 4343f90794cec7729ff25b0d62fa4b9bfbc0f9c8..1f88150e9aa7227ab8e1c14338262273bb17dba1 100644 (file)
@@ -628,7 +628,7 @@ send_search_entry(
        char            berbuf[256];
        BerElement      *ber = (BerElement *)berbuf;
        Attribute       *a, *aa;
-       int             i, rc=-1, bytes;
+       int             i, j, rc=-1, bytes;
        char            *edn;
        int             userattrs;
        int             opattrs;
@@ -637,6 +637,12 @@ send_search_entry(
 
        AttributeDescription *ad_entry = slap_schema.si_ad_entry;
 
+       /* a_flags: array of flags telling if the i-th element will be
+        *          returned or filtered out
+        * e_flags: array of a_flags
+        */
+       char *a_flags, **e_flags;
+
        if (op->o_callback && op->o_callback->sc_sendentry) {
                return op->o_callback->sc_sendentry( be, conn, op, e, attrs,
                        attrsonly, ctrls );
@@ -721,7 +727,52 @@ send_search_entry(
        opattrs = ( attrs == NULL ) ? 0
                : an_find( attrs, &AllOper );
 
-       for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+       /* create an array of arrays of flags. Each flag corresponds
+        * to particular value of attribute an equals 1 if value matches
+        * to ValuesReturnFilter or 0 if not
+        */     
+       for ( a = e->e_attrs; a != NULL; a = a->a_next ) i++;
+       e_flags = ch_malloc ( i * sizeof(a_flags) );
+       
+       for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+               for ( j = 0; a->a_vals[j].bv_val != NULL; j++ );
+
+               a_flags = ch_calloc ( j, sizeof(char) );
+               /* If no ValuesReturnFilter control return everything */
+               if ( op->vrFilter == NULL ){
+                   memset(a_flags, 1, j);
+               }
+               e_flags[i] = a_flags; 
+       }
+
+       if ( op->vrFilter != NULL ){
+
+               rc = filter_matched_values(be, conn, op, e, &e_flags) ; 
+           
+               if ( rc == -1 ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+                               "send_search_entry: conn %d matched values fitering failed\n",
+                               conn ? conn->c_connid : 0 ));
+#else
+               Debug( LDAP_DEBUG_ANY,
+                               "matched values fitering failed\n", 0, 0, 0 );
+#endif
+                       ber_free( ber, 1 );
+
+                       /* free e_flags */
+                       for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+                               free( e_flags[i] );
+                       }
+                       free( e_flags );
+
+                       send_ldap_result( conn, op, LDAP_OTHER,
+                       NULL, "matched values fitering error", NULL, NULL );
+                       goto error_return;
+               }
+       }
+
+       for ( a = e->e_attrs, j = 0; a != NULL; a = a->a_next, j++ ) {
                AttributeDescription *desc = a->a_desc;
 
                if ( attrs == NULL ) {
@@ -771,6 +822,11 @@ send_search_entry(
 #endif
 
                        ber_free_buf( ber );
+                       /* free e_flags */
+                       for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+                               free( e_flags[i] );
+                       }
+                       free( e_flags );
                        send_ldap_result( conn, op, LDAP_OTHER,
                            NULL, "encoding description error", NULL, NULL );
                        goto error_return;
@@ -795,6 +851,10 @@ send_search_entry(
                                        continue;
                                }
 
+                               if ( e_flags[j][i] == 0 ){
+                                       continue;
+                               }
+
                                if (( rc = ber_printf( ber, "O", &a->a_vals[i] )) == -1 ) {
 #ifdef NEW_LOGGING
                                        LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
@@ -806,6 +866,11 @@ send_search_entry(
 #endif
 
                                        ber_free_buf( ber );
+                                       /* free e_flags */
+                                       for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+                                               free( e_flags[i] );
+                                       }
+                                       free( e_flags );
                                        send_ldap_result( conn, op, LDAP_OTHER,
                                                NULL, "encoding values error", NULL, NULL );
                                        goto error_return;
@@ -823,12 +888,23 @@ send_search_entry(
 #endif
 
                        ber_free_buf( ber );
+                       /* free e_flags */
+                       for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+                               free( e_flags[i] );
+                       }
+                       free( e_flags );
                        send_ldap_result( conn, op, LDAP_OTHER,
                            NULL, "encode end error", NULL, NULL );
                        goto error_return;
                }
        }
 
+       /* free e_flags */
+       for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+               free( e_flags[i] );
+       }
+       free( e_flags );
+
        /* eventually will loop through generated operational attributes */
        /* only have subschemaSubentry implemented */
        aa = backend_operational( be, conn, op, e, attrs, opattrs );
index ddd7c9b0485c49e34b03b60860c182bc280c8bfa..6042e3e808f6f6da94a57990f0bcc3091a9da3ae 100644 (file)
@@ -723,6 +723,32 @@ typedef struct slap_filter {
 /* compare routines can return undefined */
 #define SLAPD_COMPARE_UNDEFINED        ((ber_int_t) -1)
 
+typedef struct slap_valuesreturnfilter {
+       ber_tag_t       f_choice;
+
+       union vrf_un_u {
+               /* precomputed result */
+               ber_int_t f_un_result;
+
+               /* DN */
+               char *f_un_dn;
+
+               /* present */
+               AttributeDescription *f_un_desc;
+
+               /* simple value assertion */
+               AttributeAssertion *f_un_ava;
+
+               /* substring assertion */
+               SubstringsAssertion *f_un_ssa;
+
+               /* matching rule assertion */
+               MatchingRuleAssertion *f_un_mra;
+       } f_un;
+
+       struct slap_valuesreturnfilter  *f_next;
+} ValuesReturnFilter;
+
 /*
  * represents an attribute (description + values)
  */
@@ -1453,6 +1479,7 @@ typedef struct slap_op {
        char o_noop;
        char o_subentries;
        char o_subentries_visibility;
+       char o_valuesreturnfilter;
 
        char o_pagedresults;
        ber_int_t o_pagedresults_size;
@@ -1470,6 +1497,7 @@ typedef struct slap_op {
        void    *o_private;     /* anything the backend needs */
 
        LDAP_STAILQ_ENTRY(slap_op)      o_next; /* next operation in list         */
+       ValuesReturnFilter *vrFilter; /* Structure represents ValuesReturnFilter */
 } Operation;
 
 #define get_manageDSAit(op)                            ((int)(op)->o_managedsait)