]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/filter.c
ITS#3208: fix matchedValues parsing
[openldap] / libraries / libldap / filter.c
index ca00c60683e911a6dafdf33e1a7a621d3d27b441..b1468d047b842f70988f7a9b4045049302e4a626 100644 (file)
@@ -1,13 +1,23 @@
+/* search.c */
 /* $OpenLDAP$ */
-/*
- * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
- * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
- */
-/*  Portions
- *  Copyright (c) 1990 Regents of the University of Michigan.
- *  All rights reserved.
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2004 The OpenLDAP Foundation.
+ * All rights reserved.
  *
- *  search.c
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Portions Copyright (C) The Internet Society (1997).
+ * ASN.1 fragments are from RFC 2251; see RFC for full legal notices.
  */
 
 #include "portable.h"
 
 #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,
@@ -221,6 +239,7 @@ ldap_pvt_filter_value_unescape( char *fval )
                switch( fval[v] ) {
                case '(':
                case ')':
+               case '*':
                        return -1;
 
                case '\\':
@@ -306,7 +325,7 @@ put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
 }
 
 int
-ldap_int_put_filter( BerElement *ber, const char *str_in )
+ldap_pvt_put_filter( BerElement *ber, const char *str_in )
 {
        int rc;
        char    *freeme;
@@ -326,7 +345,7 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
         *              lessOrEqual     [6]     AttributeValueAssertion,
         *              present         [7]     AttributeType,
         *              approxMatch     [8]     AttributeValueAssertion,
-        *                              extensibleMatch [9]             MatchingRuleAssertion -- LDAPv3
+        *              extensibleMatch [9]     MatchingRuleAssertion -- LDAPv3
         *      }
         *
         *      SubstringFilter ::= SEQUENCE {
@@ -338,16 +357,20 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
         *              }
         *      }
         *
-        *              MatchingRuleAssertion ::= SEQUENCE {    -- LDAPv3
-        *                      matchingRule    [1] MatchingRuleId OPTIONAL,
-        *                      type            [2] AttributeDescription OPTIONAL,
-        *                      matchValue      [3] AssertionValue,
-        *                      dnAttributes    [4] BOOLEAN DEFAULT FALSE }
+        *      MatchingRuleAssertion ::= SEQUENCE {    -- LDAPv3
+        *              matchingRule    [1] MatchingRuleId OPTIONAL,
+        *              type            [2] AttributeDescription OPTIONAL,
+        *              matchValue      [3] AssertionValue,
+        *              dnAttributes    [4] BOOLEAN DEFAULT FALSE }
         *
         * Note: tags in a choice are always explicit
         */
 
+#ifdef NEW_LOGGING
+       LDAP_LOG ( FILTER, ARGS, "ldap_pvt_put_filter: \"%s\"\n", str_in,0,0 );
+#else
        Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
+#endif
 
        freeme = LDAP_STRDUP( str_in );
        if( freeme == NULL ) return LDAP_NO_MEMORY;
@@ -365,8 +388,12 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
 
                        switch ( *str ) {
                        case '&':
+#ifdef NEW_LOGGING
+                               LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: AND\n", 0,0,0 );
+#else
                                Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
                                    0, 0, 0 );
+#endif
 
                                str = put_complex_filter( ber, str,
                                    LDAP_FILTER_AND, 0 );
@@ -379,8 +406,12 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
                                break;
 
                        case '|':
+#ifdef NEW_LOGGING
+                               LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: OR\n", 0,0,0 );
+#else
                                Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
                                    0, 0, 0 );
+#endif
 
                                str = put_complex_filter( ber, str,
                                    LDAP_FILTER_OR, 0 );
@@ -393,8 +424,12 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
                                break;
 
                        case '!':
+#ifdef NEW_LOGGING
+                               LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: NOT\n", 0,0,0 );
+#else
                                Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
                                    0, 0, 0 );
+#endif
 
                                str = put_complex_filter( ber, str,
                                    LDAP_FILTER_NOT, 0 );
@@ -407,8 +442,12 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
                                break;
 
                        default:
+#ifdef NEW_LOGGING
+                               LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: simple\n", 0,0,0);
+#else
                                Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
                                    0, 0, 0 );
+#endif
 
                                balance = 1;
                                escape = 0;
@@ -453,8 +492,12 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
                        break;
 
                case /*'('*/ ')':
+#ifdef NEW_LOGGING
+                       LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: end\n", 0,0,0 );
+#else
                        Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
                                0, 0, 0 );
+#endif
                        if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
                                rc = -1;
                                goto done;
@@ -468,8 +511,12 @@ ldap_int_put_filter( BerElement *ber, const char *str_in )
                        break;
 
                default:        /* assume it's a simple type=value filter */
+#ifdef NEW_LOGGING
+                       LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: default\n", 0,0,0 );
+#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;
@@ -497,8 +544,12 @@ put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
        char    *next = NULL;
        char    save;
 
+#ifdef NEW_LOGGING
+       LDAP_LOG ( FILTER, ARGS, "put_filter_list \"%s\"\n", str,0,0 );
+#else
        Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
                str, 0, 0 );
+#endif
 
        while ( *str ) {
                while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
@@ -513,7 +564,7 @@ put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
 
                /* now we have "(filter)" with str pointing to it */
                *next = '\0';
-               if ( ldap_int_put_filter( ber, str ) == -1 ) return -1;
+               if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
                *next = save;
                str = next;
 
@@ -537,8 +588,12 @@ put_simple_filter(
        ber_tag_t       ftype;
        int             rc = -1;
 
+#ifdef NEW_LOGGING
+       LDAP_LOG ( FILTER, ARGS, "put_simple_filter: \"%s\"\n", str,0,0 );
+#else
        Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
                str, 0, 0 );
+#endif
 
        str = LDAP_STRDUP( str );
        if( str == NULL ) return -1;
@@ -704,8 +759,12 @@ put_substring_filter( BerElement *ber, char *type, char *val )
        int gotstar = 0;
        ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
 
+#ifdef NEW_LOGGING
+       LDAP_LOG ( FILTER, ARGS, "put_substring_filter \"%s=%s\"\n", type, val, 0 );
+#else
        Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
                type, val, 0 );
+#endif
 
        if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
                return -1;
@@ -720,14 +779,15 @@ put_substring_filter( BerElement *ber, char *type, char *val )
                
                if ( *nextstar == '\0' ) {
                        ftype = LDAP_SUBSTRING_FINAL;
-               } else if ( gotstar++ == 0 ) {
-                       ftype = LDAP_SUBSTRING_INITIAL;
                } else {
-                       ftype = LDAP_SUBSTRING_ANY;
+                       *nextstar++ = '\0';
+                       if ( gotstar++ == 0 ) {
+                               ftype = LDAP_SUBSTRING_INITIAL;
+                       } else {
+                               ftype = LDAP_SUBSTRING_ANY;
+                       }
                }
 
-               *nextstar++ = '\0';
-
                if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
                        ber_slen_t len = ldap_pvt_filter_value_unescape( val );
 
@@ -747,3 +807,379 @@ put_substring_filter( BerElement *ber, char *type, char *val )
 
        return 0;
 }
+
+static int
+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, ARGS, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
+#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, DETAIL1, 
+                                       "put_vrFilter: simple\n", 0, 0, 0 );
+#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, DETAIL1, "put_vrFilter: end\n", 0, 0, 0 );
+#else
+                       Debug( LDAP_DEBUG_TRACE, "put_vrFilter: 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, DETAIL1, "put_vrFilter: default\n", 
+                               0, 0, 0 );
+#else
+                       Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n",
+                               0, 0, 0 );
+#endif
+                       next = strchr( str, '\0' );
+                       if ( put_simple_vrFilter( ber, str ) == -1 ) {
+                               rc = -1;
+                               goto done;
+                       }
+                       str = next;
+                       break;
+               }
+       }
+
+       rc = parens ? -1 : 0;
+
+done:
+       LDAP_FREE( freeme );
+       return rc;
+}
+
+int
+ldap_put_vrFilter( BerElement *ber, const char *str_in )
+{
+       int rc =0;
+       
+       if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
+               rc = -1;
+       }
+       
+       rc = 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, ARGS, "put_vrFilter_list \"%s\"\n", str, 0, 0 );
+#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 ( 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, ARGS, "put_simple_vrFilter: \"%s\"\n", str, 0, 0 );
+#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 = "";
+                       }
+
+                       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;
+}
+