Back to index

lightning-sunbird  0.9+nobinonly
search.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 /*
00038  *  Copyright (c) 1990 Regents of the University of Michigan.
00039  *  All rights reserved.
00040  */
00041 /*
00042  *  search.c
00043  */
00044 
00045 #if 0
00046 #ifndef lint 
00047 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
00048 #endif
00049 #endif
00050 
00051 #include "ldap-int.h"
00052 
00053 static int nsldapi_timeval2ldaplimit( struct timeval *timeoutp,
00054        int defaultvalue );
00055 static int nsldapi_search( LDAP *ld, const char *base, int scope,
00056        const char *filter, char **attrs, int attrsonly,
00057        LDAPControl **serverctrls, LDAPControl **clientctrls,
00058        int timelimit, int sizelimit, int *msgidp );
00059 static char *find_right_paren( char *s );
00060 static char *put_complex_filter( BerElement *ber, char *str,
00061        unsigned long tag, int not );
00062 static int put_filter( BerElement *ber, char *str );
00063 static int unescape_filterval( char *str );
00064 static int hexchar2int( char c );
00065 static int is_valid_attr( char *a );
00066 static int put_simple_filter( BerElement *ber, char *str );
00067 static int put_substring_filter( BerElement *ber, char *type,
00068        char *str );
00069 static int put_filter_list( BerElement *ber, char *str );
00070 static int nsldapi_search_s( LDAP *ld, const char *base, int scope, 
00071        const char *filter, char **attrs, int attrsonly,
00072        LDAPControl **serverctrls, LDAPControl **clientctrls,
00073        struct timeval *localtimeoutp, int timelimit, int sizelimit,
00074        LDAPMessage **res );
00075 
00076 /*
00077  * ldap_search - initiate an ldap search operation.  Parameters:
00078  *
00079  *     ld            LDAP descriptor
00080  *     base          DN of the base object
00081  *     scope         the search scope - one of LDAP_SCOPE_BASE,
00082  *                       LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
00083  *     filter        a string containing the search filter
00084  *                   (e.g., "(|(cn=bob)(sn=bob))")
00085  *     attrs         list of attribute types to return for matches
00086  *     attrsonly     1 => attributes only 0 => attributes and values
00087  *
00088  * Example:
00089  *     char   *attrs[] = { "mail", "title", 0 };
00090  *     msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
00091  *         attrs, attrsonly );
00092  */
00093 int
00094 LDAP_CALL
00095 ldap_search(
00096     LDAP      *ld,
00097     const char       *base,
00098     int       scope,
00099     const char       *filter,
00100     char      **attrs,
00101     int       attrsonly
00102 )
00103 {
00104        int           msgid;
00105 
00106        LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
00107 
00108        if ( ldap_search_ext( ld, base, scope, filter, attrs, attrsonly, NULL,
00109            NULL, NULL, -1, &msgid ) == LDAP_SUCCESS ) {
00110               return( msgid );
00111        } else {
00112               return( -1 ); /* error is in ld handle */
00113        }
00114 }
00115 
00116 
00117 /*
00118  * LDAPv3 extended search.
00119  * Returns an LDAP error code.
00120  */
00121 int
00122 LDAP_CALL
00123 ldap_search_ext(
00124     LDAP             *ld,
00125     const char              *base,
00126     int              scope,
00127     const char              *filter,
00128     char             **attrs,
00129     int              attrsonly,
00130     LDAPControl             **serverctrls,
00131     LDAPControl             **clientctrls,
00132     struct timeval   *timeoutp,    /* NULL means use ld->ld_timelimit */
00133     int                     sizelimit,
00134     int                     *msgidp
00135 )
00136 {
00137        /*
00138         * It is an error to pass in a zero'd timeval.
00139         */
00140        if ( timeoutp != NULL && timeoutp->tv_sec == 0 &&
00141            timeoutp->tv_usec == 0 ) {
00142               if ( ld != NULL ) {
00143                      LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
00144               }
00145                 return( LDAP_PARAM_ERROR );
00146         }
00147 
00148        return( nsldapi_search( ld, base, scope, filter, attrs, attrsonly,
00149            serverctrls, clientctrls,
00150            nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, msgidp ));
00151 }
00152 
00153 
00154 /*
00155  * Like ldap_search_ext() except an integer timelimit is passed instead of
00156  * using the overloaded struct timeval *timeoutp.
00157  */
00158 static int
00159 nsldapi_search(
00160     LDAP             *ld,
00161     const char              *base,
00162     int              scope,
00163     const char              *filter,
00164     char             **attrs,
00165     int              attrsonly,
00166     LDAPControl             **serverctrls,
00167     LDAPControl             **clientctrls,
00168     int                     timelimit,    /* -1 means use ld->ld_timelimit */
00169     int                     sizelimit,    /* -1 means use ld->ld_sizelimit */
00170     int                     *msgidp
00171 )
00172 {
00173        BerElement    *ber;
00174        int           rc, rc_key;
00175        unsigned long key;   /* XXXmcs: memcache */
00176 
00177        LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
00178 
00179        if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
00180               return( LDAP_PARAM_ERROR );
00181        }
00182 
00183        if ( base == NULL ) {
00184            base = "";
00185        }
00186 
00187        if ( filter == NULL ) {
00188            filter = "(objectclass=*)";
00189        }
00190 
00191        if ( msgidp == NULL || ( scope != LDAP_SCOPE_BASE
00192            && scope != LDAP_SCOPE_ONELEVEL && scope != LDAP_SCOPE_SUBTREE )
00193               || ( sizelimit < -1 )) {
00194               LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
00195                 return( LDAP_PARAM_ERROR );
00196         }
00197        LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
00198        *msgidp = ++ld->ld_msgid;
00199        LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
00200 
00201        /*
00202         * XXXmcs: should use cache function pointers to hook in memcache
00203         */
00204        if ( ld->ld_memcache == NULL ) {
00205               rc_key = LDAP_NOT_SUPPORTED;
00206        } else if (( rc_key = ldap_memcache_createkey( ld, base, scope, filter,
00207            attrs, attrsonly, serverctrls, clientctrls, &key)) == LDAP_SUCCESS
00208            && ldap_memcache_result( ld, *msgidp, key ) == LDAP_SUCCESS ) {
00209               return LDAP_SUCCESS;
00210        }
00211 
00212        /* check the cache */
00213        if ( ld->ld_cache_on && ld->ld_cache_search != NULL ) {
00214               LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
00215               if ( (rc = (ld->ld_cache_search)( ld, *msgidp, LDAP_REQ_SEARCH,
00216                   base, scope, filter, attrs, attrsonly )) != 0 ) {
00217                      *msgidp = rc;
00218                      LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
00219                      return( LDAP_SUCCESS );
00220               }
00221               LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
00222        }
00223 
00224        /* caching off or did not find it in the cache - check the net */
00225        if (( rc = nsldapi_build_search_req( ld, base, scope, filter, attrs,
00226            attrsonly, serverctrls, clientctrls, timelimit, sizelimit,
00227            *msgidp, &ber )) != LDAP_SUCCESS ) {
00228               return( rc );
00229        }
00230 
00231        /* send the message */
00232        rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_SEARCH,
00233               (char *) base, ber );
00234 
00235        /*
00236         * XXXmcs: should use cache function pointers to hook in memcache
00237         */
00238        if ( (rc_key == LDAP_SUCCESS) && (rc >= 0) ) {
00239               ldap_memcache_new( ld, rc, key, base );
00240        }
00241 
00242        *msgidp = rc;
00243        return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS );
00244 }
00245 
00246 
00247 /*
00248  * Convert a non-NULL timeoutp to a value in seconds that is appropriate to
00249  * send in an LDAP search request.  If timeoutp is NULL, return defaultvalue.
00250  */
00251 static int
00252 nsldapi_timeval2ldaplimit( struct timeval *timeoutp, int defaultvalue )
00253 {
00254        int           timelimit;
00255 
00256        if ( NULL == timeoutp ) {
00257               timelimit = defaultvalue;
00258        } else if ( timeoutp->tv_sec > 0 ) {
00259               timelimit = timeoutp->tv_sec;
00260        } else if ( timeoutp->tv_usec > 0 ) {
00261               timelimit = 1;       /* minimum we can express in LDAP */
00262        } else {
00263               /*
00264                * both tv_sec and tv_usec are less than one (zero?) so
00265                * to maintain compatiblity with our "zero means no limit"
00266                * convention we pass no limit to the server.
00267                */
00268               timelimit = 0;       /* no limit */
00269        }
00270 
00271        return( timelimit );
00272 }
00273 
00274 
00275 /* returns an LDAP error code and also sets it in ld */
00276 int
00277 nsldapi_build_search_req(
00278     LDAP             *ld, 
00279     const char              *base, 
00280     int                     scope, 
00281     const char              *filter,
00282     char             **attrs, 
00283     int                     attrsonly,
00284     LDAPControl             **serverctrls,
00285     LDAPControl             **clientctrls,       /* not used for anything yet */
00286     int                     timelimit,    /* if -1, ld->ld_timelimit is used */
00287     int                     sizelimit,    /* if -1, ld->ld_sizelimit is used */
00288     int                     msgid,
00289     BerElement              **berp
00290 )
00291 {
00292        BerElement    *ber;
00293        int           err;
00294        char          *fdup;
00295 
00296        /*
00297         * Create the search request.  It looks like this:
00298         *     SearchRequest := [APPLICATION 3] SEQUENCE {
00299         *            baseObject    DistinguishedName,
00300         *            scope         ENUMERATED {
00301         *                   baseObject    (0),
00302         *                   singleLevel   (1),
00303         *                   wholeSubtree  (2)
00304         *            },
00305         *            derefAliases  ENUMERATED {
00306         *                   neverDerefaliases    (0),
00307         *                   derefInSearching     (1),
00308         *                   derefFindingBaseObj  (2),
00309         *                   alwaysDerefAliases   (3)
00310         *            },
00311         *            sizelimit     INTEGER (0 .. 65535),
00312         *            timelimit     INTEGER (0 .. 65535),
00313         *            attrsOnly     BOOLEAN,
00314         *            filter        Filter,
00315         *            attributes    SEQUENCE OF AttributeType
00316         *     }
00317         * wrapped in an ldap message.
00318         */
00319 
00320        /* create a message to send */
00321        if (( err = nsldapi_alloc_ber_with_options( ld, &ber ))
00322            != LDAP_SUCCESS ) {
00323               return( err );
00324        }
00325 
00326        if ( base == NULL ) {
00327            base = "";
00328        }
00329 
00330        if ( sizelimit == -1 ) {
00331            sizelimit = ld->ld_sizelimit;
00332        }
00333 
00334        if ( timelimit == -1 ) {
00335            timelimit = ld->ld_timelimit;
00336        }
00337 
00338 #ifdef CLDAP
00339        if ( ld->ld_sbp->sb_naddr > 0 ) {
00340            err = ber_printf( ber, "{ist{seeiib", msgid,
00341               ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
00342               sizelimit, timelimit, attrsonly );
00343        } else {
00344 #endif /* CLDAP */
00345               err = ber_printf( ber, "{it{seeiib", msgid,
00346                   LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
00347                   sizelimit, timelimit, attrsonly );
00348 #ifdef CLDAP
00349        }
00350 #endif /* CLDAP */
00351 
00352        if ( err == -1 ) {
00353               LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
00354               ber_free( ber, 1 );
00355               return( LDAP_ENCODING_ERROR );
00356        }
00357 
00358        fdup = nsldapi_strdup( filter );
00359        err = put_filter( ber, fdup );
00360        NSLDAPI_FREE( fdup );
00361 
00362        if ( err == -1 ) {
00363               LDAP_SET_LDERRNO( ld, LDAP_FILTER_ERROR, NULL, NULL );
00364               ber_free( ber, 1 );
00365               return( LDAP_FILTER_ERROR );
00366        }
00367 
00368        if ( ber_printf( ber, "{v}}", attrs ) == -1 ) {
00369               LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
00370               ber_free( ber, 1 );
00371               return( LDAP_ENCODING_ERROR );
00372        }
00373 
00374        if ( (err = nsldapi_put_controls( ld, serverctrls, 1, ber ))
00375            != LDAP_SUCCESS ) {
00376               ber_free( ber, 1 );
00377               return( err );
00378        }
00379 
00380        *berp = ber;
00381        return( LDAP_SUCCESS );
00382 }
00383 
00384 static char *
00385 find_right_paren( char *s )
00386 {
00387        int    balance, escape;
00388 
00389        balance = 1;
00390        escape = 0;
00391        while ( *s && balance ) {
00392               if ( escape == 0 ) {
00393                      if ( *s == '(' )
00394                             balance++;
00395                      else if ( *s == ')' )
00396                             balance--;
00397               }
00398               if ( *s == '\\' && ! escape )
00399                      escape = 1;
00400               else
00401                      escape = 0;
00402               if ( balance )
00403                      s++;
00404        }
00405 
00406        return( *s ? s : NULL );
00407 }
00408 
00409 static char *
00410 put_complex_filter(
00411     BerElement              *ber, 
00412     char             *str, 
00413     unsigned long    tag, 
00414     int                     not 
00415 )
00416 {
00417        char   *next;
00418 
00419        /*
00420         * We have (x(filter)...) with str sitting on
00421         * the x.  We have to find the paren matching
00422         * the one before the x and put the intervening
00423         * filters by calling put_filter_list().
00424         */
00425 
00426        /* put explicit tag */
00427        if ( ber_printf( ber, "t{", tag ) == -1 )
00428               return( NULL );
00429 
00430        str++;
00431        if ( (next = find_right_paren( str )) == NULL )
00432               return( NULL );
00433 
00434        *next = '\0';
00435        if ( put_filter_list( ber, str ) == -1 )
00436               return( NULL );
00437        *next++ = ')';
00438 
00439        /* flush explicit tagged thang */
00440        if ( ber_printf( ber, "}" ) == -1 )
00441               return( NULL );
00442 
00443        return( next );
00444 }
00445 
00446 static int
00447 put_filter( BerElement *ber, char *str )
00448 {
00449        char   *next;
00450        int    parens, balance, escape;
00451 
00452        /*
00453         * A Filter looks like this:
00454         *      Filter ::= CHOICE {
00455         *              and             [0]     SET OF Filter,
00456         *              or              [1]     SET OF Filter,
00457         *              not             [2]     Filter,
00458         *              equalityMatch   [3]     AttributeValueAssertion,
00459         *              substrings      [4]     SubstringFilter,
00460         *              greaterOrEqual  [5]     AttributeValueAssertion,
00461         *              lessOrEqual     [6]     AttributeValueAssertion,
00462         *              present         [7]     AttributeType,,
00463         *              approxMatch     [8]     AttributeValueAssertion
00464         *      }
00465         *
00466         *      SubstringFilter ::= SEQUENCE {
00467         *              type               AttributeType,
00468         *              SEQUENCE OF CHOICE {
00469         *                      initial          [0] IA5String,
00470         *                      any              [1] IA5String,
00471         *                      final            [2] IA5String
00472         *              }
00473         *      }
00474         * Note: tags in a choice are always explicit
00475         */
00476 
00477        LDAPDebug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
00478 
00479        parens = 0;
00480        while ( *str ) {
00481               switch ( *str ) {
00482               case '(':
00483                      str++;
00484                      parens++;
00485                      switch ( *str ) {
00486                      case '&':
00487                             LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
00488                                 0, 0, 0 );
00489 
00490                             if ( (str = put_complex_filter( ber, str,
00491                                 LDAP_FILTER_AND, 0 )) == NULL )
00492                                    return( -1 );
00493 
00494                             parens--;
00495                             break;
00496 
00497                      case '|':
00498                             LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
00499                                 0, 0, 0 );
00500 
00501                             if ( (str = put_complex_filter( ber, str,
00502                                 LDAP_FILTER_OR, 0 )) == NULL )
00503                                    return( -1 );
00504 
00505                             parens--;
00506                             break;
00507 
00508                      case '!':
00509                             LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
00510                                 0, 0, 0 );
00511 
00512                             if ( (str = put_complex_filter( ber, str,
00513                                 LDAP_FILTER_NOT, 1 )) == NULL )
00514                                    return( -1 );
00515 
00516                             parens--;
00517                             break;
00518 
00519                      default:
00520                             LDAPDebug( LDAP_DEBUG_TRACE,
00521                                 "put_filter: simple\n", 0, 0, 0 );
00522 
00523                             balance = 1;
00524                             escape = 0;
00525                             next = str;
00526                             while ( *next && balance ) {
00527                                    if ( escape == 0 ) {
00528                                           if ( *next == '(' )
00529                                                  balance++;
00530                                           else if ( *next == ')' )
00531                                                  balance--;
00532                                    }
00533                                    if ( *next == '\\' && ! escape )
00534                                           escape = 1;
00535                                    else
00536                                           escape = 0;
00537                                    if ( balance )
00538                                           next++;
00539                             }
00540                             if ( balance != 0 )
00541                                    return( -1 );
00542 
00543                             *next = '\0';
00544                             if ( put_simple_filter( ber, str ) == -1 ) {
00545                                    return( -1 );
00546                             }
00547                             *next++ = ')';
00548                             str = next;
00549                             parens--;
00550                             break;
00551                      }
00552                      break;
00553 
00554               case ')':
00555                      LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
00556                          0 );
00557                      if ( ber_printf( ber, "]" ) == -1 )
00558                             return( -1 );
00559                      str++;
00560                      parens--;
00561                      break;
00562 
00563               case ' ':
00564                      str++;
00565                      break;
00566 
00567               default:      /* assume it's a simple type=value filter */
00568                      LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
00569                          0 );
00570                      next = strchr( str, '\0' );
00571                      if ( put_simple_filter( ber, str ) == -1 ) {
00572                             return( -1 );
00573                      }
00574                      str = next;
00575                      break;
00576               }
00577        }
00578 
00579        return( parens ? -1 : 0 );
00580 }
00581 
00582 
00583 /*
00584  * Put a list of filters like this "(filter1)(filter2)..."
00585  */
00586 
00587 static int
00588 put_filter_list( BerElement *ber, char *str )
00589 {
00590        char   *next;
00591        char   save;
00592 
00593        LDAPDebug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
00594 
00595        while ( *str ) {
00596               while ( *str && isspace( *str ) )
00597                      str++;
00598               if ( *str == '\0' )
00599                      break;
00600 
00601               if ( (next = find_right_paren( str + 1 )) == NULL )
00602                      return( -1 );
00603               save = *++next;
00604 
00605               /* now we have "(filter)" with str pointing to it */
00606               *next = '\0';
00607               if ( put_filter( ber, str ) == -1 )
00608                      return( -1 );
00609               *next = save;
00610 
00611               str = next;
00612        }
00613 
00614        return( 0 );
00615 }
00616 
00617 
00618 /*
00619  * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
00620  * of a filter expression, 0 otherwise.  A valid string may contain only
00621  * letters, numbers, hyphens, semi-colons, colons and periods. examples:
00622  *     cn
00623  *     cn;lang-fr
00624  *     1.2.3.4;binary;dynamic
00625  *     mail;dynamic
00626  *     cn:dn:1.2.3.4
00627  *
00628  * For compatibility with older servers, we also allow underscores in
00629  * attribute types, even through they are not allowed by the LDAPv3 RFCs.
00630  */
00631 static int
00632 is_valid_attr( char *a )
00633 {
00634        for ( ; *a; a++ ) {
00635            if ( !isascii( *a ) ) {
00636               return( 0 );
00637            } else if ( !isalnum( *a ) ) {
00638               switch ( *a ) {
00639                 case '-':
00640                 case '.':
00641                 case ';':
00642                 case ':':
00643                 case '_':
00644                   break; /* valid */
00645                 default:
00646                   return( 0 );
00647               }
00648            }
00649        }
00650 
00651        return( 1 );
00652 }
00653 
00654 static char *
00655 find_star( char *s )
00656 {
00657     for ( ; *s; ++s ) {
00658        switch ( *s ) {
00659          case '*': return s;
00660          case '\\':
00661            ++s;
00662            if ( hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0 ) ++s;
00663          default: break;
00664        }
00665     }
00666     return NULL;
00667 }
00668 
00669 static int
00670 put_simple_filter( BerElement *ber, char *str )
00671 {
00672        char          *s, *s2, *s3, filterop;
00673        char          *value;
00674        unsigned long ftype;
00675        int           rc, len;
00676        char          *oid;  /* for v3 extended filter */
00677        int           dnattr;       /* for v3 extended filter */
00678 
00679        LDAPDebug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
00680 
00681        rc = -1;      /* pessimistic */
00682 
00683        if (( str = nsldapi_strdup( str )) == NULL ) {
00684               return( rc );
00685        }
00686 
00687        if ( (s = strchr( str, '=' )) == NULL ) {
00688               goto free_and_return;
00689        }
00690        value = s + 1;
00691        *s-- = '\0';
00692        filterop = *s;
00693        if ( filterop == '<' || filterop == '>' || filterop == '~' ||
00694            filterop == ':' ) {
00695               *s = '\0';
00696        }
00697 
00698        if ( ! is_valid_attr( str ) ) {
00699               goto free_and_return;
00700        }
00701 
00702        switch ( filterop ) {
00703        case '<':
00704               ftype = LDAP_FILTER_LE;
00705               break;
00706        case '>':
00707               ftype = LDAP_FILTER_GE;
00708               break;
00709        case '~':
00710               ftype = LDAP_FILTER_APPROX;
00711               break;
00712        case ':':     /* extended filter - v3 only */
00713               /*
00714                * extended filter looks like this:
00715                *
00716                *     [type][':dn'][':'oid]':='value
00717                *
00718                * where one of type or :oid is required.
00719                *
00720                */
00721               ftype = LDAP_FILTER_EXTENDED;
00722               s2 = s3 = NULL;
00723               if ( (s2 = strrchr( str, ':' )) == NULL ) {
00724                      goto free_and_return;
00725               }
00726               if ( strcasecmp( s2, ":dn" ) == 0 ) {
00727                      oid = NULL;
00728                      dnattr = 1;
00729                      *s2 = '\0';
00730               } else {
00731                      oid = s2 + 1;
00732                      dnattr = 0;
00733                      *s2 = '\0';
00734                      if ( (s3 = strrchr( str, ':' )) != NULL ) {
00735                             if ( strcasecmp( s3, ":dn" ) == 0 ) {
00736                                    dnattr = 1;
00737                             } else {
00738                                    goto free_and_return;
00739                             }
00740                             *s3 = '\0';
00741                      }
00742               }
00743               if ( (rc = ber_printf( ber, "t{", ftype )) == -1 ) {
00744                      goto free_and_return;
00745               }
00746               if ( oid != NULL ) {
00747                      if ( (rc = ber_printf( ber, "ts", LDAP_TAG_MRA_OID,
00748                          oid )) == -1 ) {
00749                             goto free_and_return;
00750                      }
00751               }
00752               if ( *str != '\0' ) {
00753                      if ( (rc = ber_printf( ber, "ts",
00754                          LDAP_TAG_MRA_TYPE, str )) == -1 ) {
00755                             goto free_and_return;
00756                      }
00757               }
00758               if (( len = unescape_filterval( value )) < 0 ||
00759                   ( rc = ber_printf( ber, "totb}", LDAP_TAG_MRA_VALUE,
00760                   value, len, LDAP_TAG_MRA_DNATTRS, dnattr )) == -1 ) {
00761                      goto free_and_return;
00762               }
00763               rc = 0;
00764               goto free_and_return;
00765               break;
00766        default:
00767               if ( find_star( value ) == NULL ) {
00768                      ftype = LDAP_FILTER_EQUALITY;
00769               } else if ( strcmp( value, "*" ) == 0 ) {
00770                      ftype = LDAP_FILTER_PRESENT;
00771               } else {
00772                      rc = put_substring_filter( ber, str, value );
00773                      goto free_and_return;
00774               }
00775               break;
00776        }
00777 
00778        if ( ftype == LDAP_FILTER_PRESENT ) {
00779               rc = ber_printf( ber, "ts", ftype, str );
00780        } else if (( len = unescape_filterval( value )) >= 0 ) {
00781               rc = ber_printf( ber, "t{so}", ftype, str, value, len );
00782        }
00783        if ( rc != -1 ) {
00784               rc = 0;
00785        }
00786 
00787 free_and_return:
00788        NSLDAPI_FREE( str );
00789        return( rc );
00790 }
00791 
00792 
00793 /*
00794  * Undo in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
00795  * sequences within the null-terminated string 'val'.  The resulting value
00796  * may contain null characters.
00797  *
00798  * If 'val' contains invalid escape sequences we return -1.
00799  * Otherwise the length of the unescaped value is returned.
00800  */
00801 static int
00802 unescape_filterval( char *val )
00803 {
00804        int    escape, firstdigit, ival; 
00805        char   *s, *d;
00806 
00807        escape = 0;
00808        for ( s = d = val; *s; s++ ) {
00809               if ( escape ) {
00810                      /*
00811                       * first try LDAPv3 escape (hexadecimal) sequence
00812                       */
00813                      if (( ival = hexchar2int( *s )) < 0 ) {
00814                             if ( firstdigit ) {
00815                                    /*
00816                                     * LDAPv2 (RFC1960) escape sequence
00817                                     */
00818                                    *d++ = *s;
00819                                    escape = 0;
00820                             } else {
00821                                    return(-1);
00822                             }
00823                      }
00824                      if ( firstdigit ) {
00825                          *d = ( ival<<4 );
00826                          firstdigit = 0;
00827                      } else {
00828                          *d++ |= ival;
00829                          escape = 0;
00830                      }
00831 
00832               } else if ( *s != '\\' ) {
00833                      *d++ = *s;
00834                      escape = 0;
00835 
00836               } else {
00837                      escape = 1;
00838                      firstdigit = 1;
00839               }
00840        }
00841 
00842        return( d - val );
00843 }
00844 
00845 
00846 /*
00847  * convert character 'c' that represents a hexadecimal digit to an integer.
00848  * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
00849  * otherwise the converted value is returned.
00850  */
00851 static int
00852 hexchar2int( char c )
00853 {
00854     if ( c >= '0' && c <= '9' ) {
00855        return( c - '0' );
00856     }
00857     if ( c >= 'A' && c <= 'F' ) {
00858        return( c - 'A' + 10 );
00859     }
00860     if ( c >= 'a' && c <= 'f' ) {
00861        return( c - 'a' + 10 );
00862     }
00863     return( -1 );
00864 }
00865 
00866 static int
00867 put_substring_filter( BerElement *ber, char *type, char *val )
00868 {
00869        char          *nextstar, gotstar = 0;
00870        unsigned long ftype;
00871        int           len;
00872 
00873        LDAPDebug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
00874            val, 0 );
00875 
00876        if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 ) {
00877               return( -1 );
00878        }
00879 
00880        for ( ; val != NULL; val = nextstar ) {
00881               if ( (nextstar = find_star( val )) != NULL ) {
00882                      *nextstar++ = '\0';
00883               }
00884 
00885               if ( gotstar == 0 ) {
00886                      ftype = LDAP_SUBSTRING_INITIAL;
00887               } else if ( nextstar == NULL ) {
00888                      ftype = LDAP_SUBSTRING_FINAL;
00889               } else {
00890                      ftype = LDAP_SUBSTRING_ANY;
00891               }
00892               if ( *val != '\0' ) {
00893                      if (( len = unescape_filterval( val )) < 0 ||
00894                          ber_printf( ber, "to", ftype, val, len ) == -1 ) {
00895                             return( -1 );
00896                      }
00897               }
00898 
00899               gotstar = 1;
00900        }
00901 
00902        if ( ber_printf( ber, "}}" ) == -1 ) {
00903               return( -1 );
00904        }
00905 
00906        return( 0 );
00907 }
00908 
00909 int
00910 LDAP_CALL
00911 ldap_search_st(
00912     LDAP             *ld, 
00913     const char              *base, 
00914     int              scope, 
00915     const char              *filter, 
00916     char             **attrs,
00917     int              attrsonly, 
00918     struct timeval   *timeout, 
00919     LDAPMessage      **res
00920 )
00921 {
00922        return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly,
00923            NULL, NULL, timeout, -1, -1, res ));
00924 }
00925 
00926 int
00927 LDAP_CALL
00928 ldap_search_s(
00929     LDAP      *ld, 
00930     const char       *base, 
00931     int       scope, 
00932     const char       *filter, 
00933     char      **attrs,
00934     int              attrsonly, 
00935     LDAPMessage      **res
00936 )
00937 {
00938        return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly,
00939            NULL, NULL, NULL, -1, -1, res ));
00940 }
00941 
00942 int LDAP_CALL
00943 ldap_search_ext_s(
00944     LDAP             *ld, 
00945     const char              *base, 
00946     int              scope, 
00947     const char              *filter, 
00948     char             **attrs,
00949     int                     attrsonly, 
00950     LDAPControl             **serverctrls,
00951     LDAPControl             **clientctrls,
00952     struct timeval   *timeoutp,
00953     int                     sizelimit,
00954     LDAPMessage             **res
00955 )
00956 {
00957        return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly,
00958            serverctrls, clientctrls, timeoutp,
00959            nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, res ));
00960 }
00961 
00962 
00963 static int 
00964 nsldapi_search_s(
00965     LDAP             *ld, 
00966     const char              *base, 
00967     int              scope, 
00968     const char              *filter, 
00969     char             **attrs,
00970     int                     attrsonly, 
00971     LDAPControl             **serverctrls,
00972     LDAPControl             **clientctrls,
00973     struct timeval   *localtimeoutp,
00974     int                     timelimit,    /* -1 means use ld->ld_timelimit */
00975     int                     sizelimit,    /* -1 means use ld->ld_sizelimit */
00976     LDAPMessage             **res
00977 )
00978 {
00979        int    err, msgid;
00980 
00981        /*
00982         * It is an error to pass in a zero'd timeval.
00983         */
00984        if ( localtimeoutp != NULL && localtimeoutp->tv_sec == 0 &&
00985            localtimeoutp->tv_usec == 0 ) {
00986               if ( ld != NULL ) {
00987                      LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
00988               }
00989               if ( res != NULL ) {
00990                      *res = NULL;
00991               }
00992                 return( LDAP_PARAM_ERROR );
00993         }
00994 
00995        if (( err = nsldapi_search( ld, base, scope, filter, attrs, attrsonly,
00996            serverctrls, clientctrls, timelimit, sizelimit, &msgid ))
00997            != LDAP_SUCCESS ) {
00998               if ( res != NULL ) {
00999                      *res = NULL;
01000               }
01001               return( err );
01002        }
01003 
01004        if ( ldap_result( ld, msgid, 1, localtimeoutp, res ) == -1 ) {
01005               /*
01006                * Error.  ldap_result() sets *res to NULL for us.
01007                */
01008               return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
01009        }
01010 
01011        if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) {
01012               (void) ldap_abandon( ld, msgid );
01013               err = LDAP_TIMEOUT;
01014               LDAP_SET_LDERRNO( ld, err, NULL, NULL );
01015               if ( res != NULL ) {
01016                      *res = NULL;
01017               }
01018               return( err );
01019        }
01020 
01021        return( ldap_result2error( ld, *res, 0 ) );
01022 }