Back to index

openldap  2.4.31
filter.c
Go to the documentation of this file.
00001 /* search.c */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
00017  * All rights reserved.
00018  */
00019 
00020 #include "portable.h"
00021 
00022 #include <stdio.h>
00023 
00024 #include <ac/stdlib.h>
00025 
00026 #include <ac/socket.h>
00027 #include <ac/string.h>
00028 #include <ac/time.h>
00029 
00030 #include "ldap-int.h"
00031 
00032 static int put_simple_vrFilter LDAP_P((
00033        BerElement *ber,
00034        char *str ));
00035 
00036 static int put_vrFilter_list LDAP_P((
00037        BerElement *ber,
00038        char *str ));
00039 
00040 static char *put_complex_filter LDAP_P((
00041        BerElement *ber,
00042        char *str,
00043        ber_tag_t tag,
00044        int not ));
00045 
00046 static int put_simple_filter LDAP_P((
00047        BerElement *ber,
00048        char *str ));
00049 
00050 static int put_substring_filter LDAP_P((
00051        BerElement *ber,
00052        char *type,
00053        char *str,
00054        char *nextstar ));
00055 
00056 static int put_filter_list LDAP_P((
00057        BerElement *ber,
00058        char *str,
00059        ber_tag_t tag ));
00060 
00061 static int ldap_is_oid ( const char *str )
00062 {
00063        int i;
00064 
00065        if( LDAP_ALPHA( str[0] )) {
00066               for( i=1; str[i]; i++ ) {
00067                      if( !LDAP_LDH( str[i] )) {
00068                             return 0;
00069                      }
00070               }
00071               return 1;
00072 
00073        } else if LDAP_DIGIT( str[0] ) {
00074               int dot=0;
00075               for( i=1; str[i]; i++ ) {
00076                      if( LDAP_DIGIT( str[i] )) {
00077                             dot=0;
00078 
00079                      } else if ( str[i] == '.' ) {
00080                             if( dot ) return 0;
00081                             if( ++dot > 1 ) return 0;
00082 
00083                      } else {
00084                             return 0;
00085                      }
00086               }
00087               return !dot;
00088        }
00089 
00090        return 0;
00091 }
00092 
00093 static int ldap_is_desc ( const char *str )
00094 {
00095        int i;
00096 
00097        if( LDAP_ALPHA( str[0] )) {
00098               for( i=1; str[i]; i++ ) {
00099                      if( str[i] == ';' ) {
00100                             str = &str[i+1];
00101                             goto options;
00102                      }
00103 
00104                      if( !LDAP_LDH( str[i] )) {
00105                             return 0;
00106                      }
00107               }
00108               return 1;
00109 
00110        } else if LDAP_DIGIT( str[0] ) {
00111               int dot=0;
00112               for( i=1; str[i]; i++ ) {
00113                      if( str[i] == ';' ) {
00114                             if( dot ) return 0;
00115                             str = &str[i+1];
00116                             goto options;
00117                      }
00118 
00119                      if( LDAP_DIGIT( str[i] )) {
00120                             dot=0;
00121 
00122                      } else if ( str[i] == '.' ) {
00123                             if( dot ) return 0;
00124                             if( ++dot > 1 ) return 0;
00125 
00126                      } else {
00127                             return 0;
00128                      }
00129               }
00130               return !dot;
00131        }
00132 
00133        return 0;
00134 
00135 options:
00136        if( !LDAP_LDH( str[0] )) {
00137               return 0;
00138        }
00139        for( i=1; str[i]; i++ ) {
00140               if( str[i] == ';' ) {
00141                      str = &str[i+1];
00142                      goto options;
00143               }
00144               if( !LDAP_LDH( str[i] )) {
00145                      return 0;
00146               }
00147        }
00148        return 1;
00149 }
00150 
00151 static char *
00152 find_right_paren( char *s )
00153 {
00154        int    balance, escape;
00155 
00156        balance = 1;
00157        escape = 0;
00158        while ( *s && balance ) {
00159               if ( !escape ) {
00160                      if ( *s == '(' ) {
00161                             balance++;
00162                      } else if ( *s == ')' ) {
00163                             balance--;
00164                      }
00165               }
00166 
00167               escape = ( *s == '\\' && !escape );
00168 
00169               if ( balance ) s++;
00170        }
00171 
00172        return *s ? s : NULL;
00173 }
00174 
00175 static int hex2value( int c )
00176 {
00177        if( c >= '0' && c <= '9' ) {
00178               return c - '0';
00179        }
00180 
00181        if( c >= 'A' && c <= 'F' ) {
00182               return c + (10 - (int) 'A');
00183        }
00184 
00185        if( c >= 'a' && c <= 'f' ) {
00186               return c + (10 - (int) 'a');
00187        }
00188 
00189        return -1;
00190 }
00191 
00192 char *
00193 ldap_pvt_find_wildcard( const char *s )
00194 {
00195        for( ; *s; s++ ) {
00196               switch( *s ) {
00197               case '*':     /* found wildcard */
00198                      return (char *) s;
00199 
00200               case '(':
00201               case ')':
00202                      return NULL;
00203 
00204               case '\\':
00205                      if( s[1] == '\0' ) return NULL;
00206 
00207                      if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) {
00208                             s+=2;
00209 
00210                      } else switch( s[1] ) {
00211                      default:
00212                             return NULL;
00213 
00214                      /* allow RFC 1960 escapes */
00215                      case '*':
00216                      case '(':
00217                      case ')':
00218                      case '\\':
00219                             s++;
00220                      }
00221               }
00222        }
00223 
00224        return (char *) s;
00225 }
00226 
00227 /* unescape filter value */
00228 /* support both LDAP v2 and v3 escapes */
00229 /* output can include nul characters! */
00230 ber_slen_t
00231 ldap_pvt_filter_value_unescape( char *fval )
00232 {
00233        ber_slen_t r, v;
00234        int v1, v2;
00235 
00236        for( r=v=0; fval[v] != '\0'; v++ ) {
00237               switch( fval[v] ) {
00238               case '(':
00239               case ')':
00240               case '*':
00241                      return -1;
00242 
00243               case '\\':
00244                      /* escape */
00245                      v++;
00246 
00247                      if ( fval[v] == '\0' ) {
00248                             /* escape at end of string */
00249                             return -1;
00250                      }
00251 
00252                      if (( v1 = hex2value( fval[v] )) >= 0 ) {
00253                             /* LDAPv3 escape */
00254                             if (( v2 = hex2value( fval[v+1] )) < 0 ) {
00255                                    /* must be two digit code */
00256                                    return -1;
00257                             }
00258 
00259                             fval[r++] = v1 * 16 + v2;
00260                             v++;
00261 
00262                      } else {
00263                             /* LDAPv2 escape */
00264                             switch( fval[v] ) {
00265                             case '(':
00266                             case ')':
00267                             case '*':
00268                             case '\\':
00269                                    fval[r++] = fval[v];
00270                                    break;
00271                             default:
00272                                    /* illegal escape */
00273                                    return -1;
00274                             }
00275                      }
00276                      break;
00277 
00278               default:
00279                      fval[r++] = fval[v];
00280               }
00281        }
00282 
00283        fval[r] = '\0';
00284        return r;
00285 }
00286 
00287 static char *
00288 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
00289 {
00290        char   *next;
00291 
00292        /*
00293         * We have (x(filter)...) with str sitting on
00294         * the x.  We have to find the paren matching
00295         * the one before the x and put the intervening
00296         * filters by calling put_filter_list().
00297         */
00298 
00299        /* put explicit tag */
00300        if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) {
00301               return NULL;
00302        }
00303 
00304        str++;
00305        if ( (next = find_right_paren( str )) == NULL ) {
00306               return NULL;
00307        }
00308 
00309        *next = '\0';
00310        if ( put_filter_list( ber, str, tag ) == -1 ) {
00311               return NULL;
00312        }
00313 
00314        /* close the '(' */
00315        *next++ = ')';
00316 
00317        /* flush explicit tagged thang */
00318        if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
00319               return NULL;
00320        }
00321 
00322        return next;
00323 }
00324 
00325 int
00326 ldap_pvt_put_filter( BerElement *ber, const char *str_in )
00327 {
00328        int rc;
00329        char   *freeme;
00330        char   *str;
00331        char   *next;
00332        int    parens, balance, escape;
00333 
00334        /*
00335         * A Filter looks like this (RFC 4511 as extended by RFC 4526):
00336         *     Filter ::= CHOICE {
00337         *         and             [0]     SET SIZE (0..MAX) OF filter Filter,
00338         *         or              [1]     SET SIZE (0..MAX) OF filter Filter,
00339         *         not             [2]     Filter,
00340         *         equalityMatch   [3]     AttributeValueAssertion,
00341         *         substrings      [4]     SubstringFilter,
00342         *         greaterOrEqual  [5]     AttributeValueAssertion,
00343         *         lessOrEqual     [6]     AttributeValueAssertion,
00344         *         present         [7]     AttributeDescription,
00345         *         approxMatch     [8]     AttributeValueAssertion,
00346         *         extensibleMatch [9]     MatchingRuleAssertion,
00347         *         ... }
00348         *
00349         *     SubstringFilter ::= SEQUENCE {
00350         *         type         AttributeDescription,
00351         *         substrings   SEQUENCE SIZE (1..MAX) OF substring CHOICE {
00352         *             initial          [0] AssertionValue, -- only once
00353         *             any              [1] AssertionValue,
00354         *             final            [2] AssertionValue  -- only once
00355         *             }
00356         *         }
00357         *
00358         *        MatchingRuleAssertion ::= SEQUENCE {
00359         *         matchingRule    [1] MatchingRuleId OPTIONAL,
00360         *         type            [2] AttributeDescription OPTIONAL,
00361         *         matchValue      [3] AssertionValue,
00362         *         dnAttributes    [4] BOOLEAN DEFAULT FALSE }
00363         *
00364         * Note: tags in a CHOICE are always explicit
00365         */
00366 
00367        Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
00368 
00369        freeme = LDAP_STRDUP( str_in );
00370        if( freeme == NULL ) return LDAP_NO_MEMORY;
00371        str = freeme;
00372 
00373        parens = 0;
00374        while ( *str ) {
00375               switch ( *str ) {
00376               case '(': /*')'*/
00377                      str++;
00378                      parens++;
00379 
00380                      /* skip spaces */
00381                      while( LDAP_SPACE( *str ) ) str++;
00382 
00383                      switch ( *str ) {
00384                      case '&':
00385                             Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
00386                                 0, 0, 0 );
00387 
00388                             str = put_complex_filter( ber, str,
00389                                 LDAP_FILTER_AND, 0 );
00390                             if( str == NULL ) {
00391                                    rc = -1;
00392                                    goto done;
00393                             }
00394 
00395                             parens--;
00396                             break;
00397 
00398                      case '|':
00399                             Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
00400                                 0, 0, 0 );
00401 
00402                             str = put_complex_filter( ber, str,
00403                                 LDAP_FILTER_OR, 0 );
00404                             if( str == NULL ) {
00405                                    rc = -1;
00406                                    goto done;
00407                             }
00408 
00409                             parens--;
00410                             break;
00411 
00412                      case '!':
00413                             Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
00414                                 0, 0, 0 );
00415 
00416                             str = put_complex_filter( ber, str,
00417                                 LDAP_FILTER_NOT, 0 );
00418                             if( str == NULL ) {
00419                                    rc = -1;
00420                                    goto done;
00421                             }
00422 
00423                             parens--;
00424                             break;
00425 
00426                      case '(':
00427                             rc = -1;
00428                             goto done;
00429 
00430                      default:
00431                             Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
00432                                 0, 0, 0 );
00433 
00434                             balance = 1;
00435                             escape = 0;
00436                             next = str;
00437 
00438                             while ( *next && balance ) {
00439                                    if ( escape == 0 ) {
00440                                           if ( *next == '(' ) {
00441                                                  balance++;
00442                                           } else if ( *next == ')' ) {
00443                                                  balance--;
00444                                           }
00445                                    }
00446 
00447                                    if ( *next == '\\' && ! escape ) {
00448                                           escape = 1;
00449                                    } else {
00450                                           escape = 0;
00451                                    }
00452 
00453                                    if ( balance ) next++;
00454                             }
00455 
00456                             if ( balance != 0 ) {
00457                                    rc = -1;
00458                                    goto done;
00459                             }
00460 
00461                             *next = '\0';
00462 
00463                             if ( put_simple_filter( ber, str ) == -1 ) {
00464                                    rc = -1;
00465                                    goto done;
00466                             }
00467 
00468                             *next++ = /*'('*/ ')';
00469 
00470                             str = next;
00471                             parens--;
00472                             break;
00473                      }
00474                      break;
00475 
00476               case /*'('*/ ')':
00477                      Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
00478                             0, 0, 0 );
00479                      if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
00480                             rc = -1;
00481                             goto done;
00482                      }
00483                      str++;
00484                      parens--;
00485                      break;
00486 
00487               case ' ':
00488                      str++;
00489                      break;
00490 
00491               default:      /* assume it's a simple type=value filter */
00492                      Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
00493                             0, 0, 0 );
00494                      next = strchr( str, '\0' );
00495                      if ( put_simple_filter( ber, str ) == -1 ) {
00496                             rc = -1;
00497                             goto done;
00498                      }
00499                      str = next;
00500                      break;
00501               }
00502               if ( !parens )
00503                      break;
00504        }
00505 
00506        rc = ( parens || *str ) ? -1 : 0;
00507 
00508 done:
00509        LDAP_FREE( freeme );
00510        return rc;
00511 }
00512 
00513 /*
00514  * Put a list of filters like this "(filter1)(filter2)..."
00515  */
00516 
00517 static int
00518 put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
00519 {
00520        char   *next = NULL;
00521        char   save;
00522 
00523        Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
00524               str, 0, 0 );
00525 
00526        while ( *str ) {
00527               while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
00528                      str++;
00529               }
00530               if ( *str == '\0' ) break;
00531 
00532               if ( (next = find_right_paren( str + 1 )) == NULL ) {
00533                      return -1;
00534               }
00535               save = *++next;
00536 
00537               /* now we have "(filter)" with str pointing to it */
00538               *next = '\0';
00539               if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
00540               *next = save;
00541               str = next;
00542 
00543               if( tag == LDAP_FILTER_NOT ) break;
00544        }
00545 
00546        if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
00547               return -1;
00548        }
00549 
00550        return 0;
00551 }
00552 
00553 static int
00554 put_simple_filter(
00555        BerElement *ber,
00556        char *str )
00557 {
00558        char          *s;
00559        char          *value;
00560        ber_tag_t     ftype;
00561        int           rc = -1;
00562 
00563        Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
00564               str, 0, 0 );
00565 
00566        str = LDAP_STRDUP( str );
00567        if( str == NULL ) return -1;
00568 
00569        if ( (s = strchr( str, '=' )) == NULL ) {
00570               goto done;
00571        }
00572 
00573        value = s + 1;
00574        *s-- = '\0';
00575 
00576        switch ( *s ) {
00577        case '<':
00578               ftype = LDAP_FILTER_LE;
00579               *s = '\0';
00580               break;
00581 
00582        case '>':
00583               ftype = LDAP_FILTER_GE;
00584               *s = '\0';
00585               break;
00586 
00587        case '~':
00588               ftype = LDAP_FILTER_APPROX;
00589               *s = '\0';
00590               break;
00591 
00592        case ':':
00593               /* RFC 4515 extensible filters are off the form:
00594                *            type [:dn] [:rule] := value
00595                * or  [:dn]:rule := value         
00596                */
00597               ftype = LDAP_FILTER_EXT;
00598               *s = '\0';
00599 
00600               {
00601                      char *dn = strchr( str, ':' );
00602                      char *rule = NULL;
00603 
00604                      if( dn != NULL ) {
00605                             *dn++ = '\0';
00606                             rule = strchr( dn, ':' );
00607 
00608                             if( rule == NULL ) {
00609                                    /* one colon */
00610                                    if ( strcasecmp(dn, "dn") == 0 ) {
00611                                           /* must have attribute */
00612                                           if( !ldap_is_desc( str ) ) {
00613                                                  goto done;
00614                                           }
00615 
00616                                           rule = "";
00617 
00618                                    } else {
00619                                      rule = dn;
00620                                      dn = NULL;
00621                                    }
00622                             
00623                             } else {
00624                                    /* two colons */
00625                                    *rule++ = '\0';
00626 
00627                                    if ( strcasecmp(dn, "dn") != 0 ) {
00628                                           /* must have "dn" */
00629                                           goto done;
00630                                    }
00631                             }
00632 
00633                      }
00634 
00635                      if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
00636                             /* must have either type or rule */
00637                             goto done;
00638                      }
00639 
00640                      if ( *str != '\0' && !ldap_is_desc( str ) ) {
00641                             goto done;
00642                      }
00643 
00644                      if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
00645                             goto done;
00646                      }
00647 
00648                      rc = ber_printf( ber, "t{" /*"}"*/, ftype );
00649 
00650                      if( rc != -1 && rule && *rule != '\0' ) {
00651                             rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
00652                      }
00653 
00654                      if( rc != -1 && *str != '\0' ) {
00655                             rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
00656                      }
00657 
00658                      if( rc != -1 ) {
00659                             ber_slen_t len = ldap_pvt_filter_value_unescape( value );
00660 
00661                             if( len >= 0 ) {
00662                                    rc = ber_printf( ber, "to",
00663                                           LDAP_FILTER_EXT_VALUE, value, len );
00664                             } else {
00665                                    rc = -1;
00666                             }
00667                      }
00668 
00669                      if( rc != -1 && dn ) {
00670                             rc = ber_printf( ber, "tb",
00671                                    LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
00672                      }
00673 
00674                      if( rc != -1 ) { 
00675                             rc = ber_printf( ber, /*"{"*/ "N}" );
00676                      }
00677               }
00678               goto done;
00679 
00680        default:
00681               if( !ldap_is_desc( str ) ) {
00682                      goto done;
00683 
00684               } else {
00685                      char *nextstar = ldap_pvt_find_wildcard( value );
00686 
00687                      if ( nextstar == NULL ) {
00688                             goto done;
00689 
00690                      } else if ( *nextstar == '\0' ) {
00691                             ftype = LDAP_FILTER_EQUALITY;
00692 
00693                      } else if ( strcmp( value, "*" ) == 0 ) {
00694                             ftype = LDAP_FILTER_PRESENT;
00695 
00696                      } else {
00697                             rc = put_substring_filter( ber, str, value, nextstar );
00698                             goto done;
00699                      }
00700               } break;
00701        }
00702 
00703        if( !ldap_is_desc( str ) ) goto done;
00704 
00705        if ( ftype == LDAP_FILTER_PRESENT ) {
00706               rc = ber_printf( ber, "ts", ftype, str );
00707 
00708        } else {
00709               ber_slen_t len = ldap_pvt_filter_value_unescape( value );
00710 
00711               if( len >= 0 ) {
00712                      rc = ber_printf( ber, "t{soN}",
00713                             ftype, str, value, len );
00714               }
00715        }
00716 
00717 done:
00718        if( rc != -1 ) rc = 0;
00719        LDAP_FREE( str );
00720        return rc;
00721 }
00722 
00723 static int
00724 put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar )
00725 {
00726        int gotstar = 0;
00727        ber_tag_t     ftype = LDAP_FILTER_SUBSTRINGS;
00728 
00729        Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
00730               type, val, 0 );
00731 
00732        if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
00733               return -1;
00734        }
00735 
00736        for( ; *val; val=nextstar ) {
00737               if ( gotstar )
00738                      nextstar = ldap_pvt_find_wildcard( val );
00739 
00740               if ( nextstar == NULL ) {
00741                      return -1;
00742               }
00743 
00744               if ( *nextstar == '\0' ) {
00745                      ftype = LDAP_SUBSTRING_FINAL;
00746               } else {
00747                      *nextstar++ = '\0';
00748                      if ( gotstar++ == 0 ) {
00749                             ftype = LDAP_SUBSTRING_INITIAL;
00750                      } else {
00751                             ftype = LDAP_SUBSTRING_ANY;
00752                      }
00753               }
00754 
00755               if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
00756                      ber_slen_t len = ldap_pvt_filter_value_unescape( val );
00757 
00758                      if ( len <= 0  ) {
00759                             return -1;
00760                      }
00761 
00762                      if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
00763                             return -1;
00764                      }
00765               }
00766        }
00767 
00768        if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
00769               return -1;
00770        }
00771 
00772        return 0;
00773 }
00774 
00775 static int
00776 put_vrFilter( BerElement *ber, const char *str_in )
00777 {
00778        int rc;
00779        char   *freeme;
00780        char   *str;
00781        char   *next;
00782        int    parens, balance, escape;
00783 
00784        /*
00785         * A ValuesReturnFilter looks like this:
00786         *
00787         *     ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
00788         *      SimpleFilterItem ::= CHOICE {
00789         *              equalityMatch   [3]     AttributeValueAssertion,
00790         *              substrings      [4]     SubstringFilter,
00791         *              greaterOrEqual  [5]     AttributeValueAssertion,
00792         *              lessOrEqual     [6]     AttributeValueAssertion,
00793         *              present         [7]     AttributeType,
00794         *              approxMatch     [8]     AttributeValueAssertion,
00795         *            extensibleMatch [9]  SimpleMatchingAssertion -- LDAPv3
00796         *      }
00797         *
00798         *      SubstringFilter ::= SEQUENCE {
00799         *              type               AttributeType,
00800         *              SEQUENCE OF CHOICE {
00801         *                      initial          [0] IA5String,
00802         *                      any              [1] IA5String,
00803         *                      final            [2] IA5String
00804         *              }
00805         *      }
00806         *
00807         *     SimpleMatchingAssertion ::= SEQUENCE {    -- LDAPv3
00808         *            matchingRule    [1] MatchingRuleId OPTIONAL,
00809         *            type            [2] AttributeDescription OPTIONAL,
00810         *            matchValue      [3] AssertionValue }
00811         *
00812         * (Source: RFC 3876)
00813         */
00814 
00815        Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
00816 
00817        freeme = LDAP_STRDUP( str_in );
00818        if( freeme == NULL ) return LDAP_NO_MEMORY;
00819        str = freeme;
00820 
00821        parens = 0;
00822        while ( *str ) {
00823               switch ( *str ) {
00824               case '(': /*')'*/
00825                      str++;
00826                      parens++;
00827 
00828                      /* skip spaces */
00829                      while( LDAP_SPACE( *str ) ) str++;
00830 
00831                      switch ( *str ) {
00832                      case '(':
00833                             if ( (next = find_right_paren( str )) == NULL ) {
00834                                    rc = -1;
00835                                    goto done;
00836                             }
00837 
00838                             *next = '\0';
00839 
00840                             if ( put_vrFilter_list( ber, str ) == -1 ) {
00841                                    rc = -1;
00842                                    goto done;
00843                             }
00844 
00845                             /* close the '(' */
00846                             *next++ = ')';
00847 
00848                             str = next;
00849 
00850                             parens--;
00851                             break;
00852 
00853 
00854                      default:
00855                             Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
00856                                 0, 0, 0 );
00857 
00858                             balance = 1;
00859                             escape = 0;
00860                             next = str;
00861 
00862                             while ( *next && balance ) {
00863                                    if ( escape == 0 ) {
00864                                           if ( *next == '(' ) {
00865                                                  balance++;
00866                                           } else if ( *next == ')' ) {
00867                                                  balance--;
00868                                           }
00869                                    }
00870 
00871                                    if ( *next == '\\' && ! escape ) {
00872                                           escape = 1;
00873                                    } else {
00874                                           escape = 0;
00875                                    }
00876 
00877                                    if ( balance ) next++;
00878                             }
00879 
00880                             if ( balance != 0 ) {
00881                                    rc = -1;
00882                                    goto done;
00883                             }
00884 
00885                             *next = '\0';
00886 
00887                             if ( put_simple_vrFilter( ber, str ) == -1 ) {
00888                                    rc = -1;
00889                                    goto done;
00890                             }
00891 
00892                             *next++ = /*'('*/ ')';
00893 
00894                             str = next;
00895                             parens--;
00896                             break;
00897                      }
00898                      break;
00899 
00900               case /*'('*/ ')':
00901                      Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n",
00902                             0, 0, 0 );
00903                      if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
00904                             rc = -1;
00905                             goto done;
00906                      }
00907                      str++;
00908                      parens--;
00909                      break;
00910 
00911               case ' ':
00912                      str++;
00913                      break;
00914 
00915               default:      /* assume it's a simple type=value filter */
00916                      Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n",
00917                             0, 0, 0 );
00918                      next = strchr( str, '\0' );
00919                      if ( put_simple_vrFilter( ber, str ) == -1 ) {
00920                             rc = -1;
00921                             goto done;
00922                      }
00923                      str = next;
00924                      break;
00925               }
00926        }
00927 
00928        rc = parens ? -1 : 0;
00929 
00930 done:
00931        LDAP_FREE( freeme );
00932        return rc;
00933 }
00934 
00935 int
00936 ldap_put_vrFilter( BerElement *ber, const char *str_in )
00937 {
00938        int rc =0;
00939        
00940        if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
00941               rc = -1;
00942        }
00943        
00944        rc = put_vrFilter( ber, str_in );
00945 
00946        if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
00947               rc = -1;
00948        }
00949        
00950        return rc;
00951 }
00952 
00953 static int
00954 put_vrFilter_list( BerElement *ber, char *str )
00955 {
00956        char   *next = NULL;
00957        char   save;
00958 
00959        Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
00960               str, 0, 0 );
00961 
00962        while ( *str ) {
00963               while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
00964                      str++;
00965               }
00966               if ( *str == '\0' ) break;
00967 
00968               if ( (next = find_right_paren( str + 1 )) == NULL ) {
00969                      return -1;
00970               }
00971               save = *++next;
00972 
00973               /* now we have "(filter)" with str pointing to it */
00974               *next = '\0';
00975               if ( put_vrFilter( ber, str ) == -1 ) return -1;
00976               *next = save;
00977               str = next;
00978        }
00979 
00980        return 0;
00981 }
00982 
00983 static int
00984 put_simple_vrFilter(
00985        BerElement *ber,
00986        char *str )
00987 {
00988        char          *s;
00989        char          *value;
00990        ber_tag_t     ftype;
00991        int           rc = -1;
00992 
00993        Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
00994               str, 0, 0 );
00995 
00996        str = LDAP_STRDUP( str );
00997        if( str == NULL ) return -1;
00998 
00999        if ( (s = strchr( str, '=' )) == NULL ) {
01000               goto done;
01001        }
01002 
01003        value = s + 1;
01004        *s-- = '\0';
01005 
01006        switch ( *s ) {
01007        case '<':
01008               ftype = LDAP_FILTER_LE;
01009               *s = '\0';
01010               break;
01011 
01012        case '>':
01013               ftype = LDAP_FILTER_GE;
01014               *s = '\0';
01015               break;
01016 
01017        case '~':
01018               ftype = LDAP_FILTER_APPROX;
01019               *s = '\0';
01020               break;
01021 
01022        case ':':
01023               /* According to ValuesReturnFilter control definition
01024                * extensible filters are off the form:
01025                *            type [:rule] := value
01026                * or  :rule := value              
01027                */
01028               ftype = LDAP_FILTER_EXT;
01029               *s = '\0';
01030 
01031               {
01032                      char *rule = strchr( str, ':' );
01033 
01034                      if( rule == NULL ) {
01035                             /* must have attribute */
01036                             if( !ldap_is_desc( str ) ) {
01037                                    goto done;
01038                             }
01039                             rule = "";
01040                      } else {
01041                             *rule++ = '\0';
01042                      }
01043 
01044                      if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
01045                             /* must have either type or rule */
01046                             goto done;
01047                      }
01048 
01049                      if ( *str != '\0' && !ldap_is_desc( str ) ) {
01050                             goto done;
01051                      }
01052 
01053                      if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
01054                             goto done;
01055                      }
01056 
01057                      rc = ber_printf( ber, "t{" /*"}"*/, ftype );
01058 
01059                      if( rc != -1 && rule && *rule != '\0' ) {
01060                             rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
01061                      }
01062 
01063                      if( rc != -1 && *str != '\0' ) {
01064                             rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
01065                      }
01066 
01067                      if( rc != -1 ) {
01068                             ber_slen_t len = ldap_pvt_filter_value_unescape( value );
01069 
01070                             if( len >= 0 ) {
01071                                    rc = ber_printf( ber, "to",
01072                                           LDAP_FILTER_EXT_VALUE, value, len );
01073                             } else {
01074                                    rc = -1;
01075                             }
01076                      }
01077 
01078                      if( rc != -1 ) { 
01079                             rc = ber_printf( ber, /*"{"*/ "N}" );
01080                      }
01081               }
01082               goto done;
01083 
01084        default:
01085               if( !ldap_is_desc( str ) ) {
01086                      goto done;
01087 
01088               } else {
01089                      char *nextstar = ldap_pvt_find_wildcard( value );
01090 
01091                      if ( nextstar == NULL ) {
01092                             goto done;
01093 
01094                      } else if ( *nextstar == '\0' ) {
01095                             ftype = LDAP_FILTER_EQUALITY;
01096 
01097                      } else if ( strcmp( value, "*" ) == 0 ) {
01098                             ftype = LDAP_FILTER_PRESENT;
01099 
01100                      } else {
01101                             rc = put_substring_filter( ber, str, value, nextstar );
01102                             goto done;
01103                      }
01104               } break;
01105        }
01106 
01107        if( !ldap_is_desc( str ) ) goto done;
01108 
01109        if ( ftype == LDAP_FILTER_PRESENT ) {
01110               rc = ber_printf( ber, "ts", ftype, str );
01111 
01112        } else {
01113               ber_slen_t len = ldap_pvt_filter_value_unescape( value );
01114 
01115               if( len >= 0 ) {
01116                      rc = ber_printf( ber, "t{soN}",
01117                             ftype, str, value, len );
01118               }
01119        }
01120 
01121 done:
01122        if( rc != -1 ) rc = 0;
01123        LDAP_FREE( str );
01124        return rc;
01125 }
01126