]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
Add ber_bvstr and ber_bvstrdup string to berval allocators.
[openldap] / libraries / libldap / search.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1990 Regents of the University of Michigan.
8  *  All rights reserved.
9  *
10  *  search.c
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18
19 #include <ac/ctype.h>
20 #include <ac/socket.h>
21 #include <ac/string.h>
22 #include <ac/time.h>
23
24 #include "ldap-int.h"
25
26 static int ldap_is_attr_oid LDAP_P((
27         const char *attr ));
28
29 static int ldap_is_attr_desc LDAP_P((
30         const char *attr ));
31
32 static int hex2value LDAP_P((
33         int c ));
34
35 static char *find_right_paren LDAP_P((
36         char *s ));
37
38 static char *put_complex_filter LDAP_P((
39         BerElement *ber,
40         char *str,
41         ber_tag_t tag,
42         int not ));
43
44 static int put_filter LDAP_P((
45         BerElement *ber,
46         char *str ));
47
48 static int put_simple_filter LDAP_P((
49         BerElement *ber,
50         char *str ));
51
52 static int put_substring_filter LDAP_P((
53         BerElement *ber,
54         char *type,
55         char *str ));
56
57 static int put_filter_list LDAP_P((
58         BerElement *ber,
59         char *str ));
60
61 /*
62  * ldap_search_ext - initiate an ldap search operation.
63  *
64  * Parameters:
65  *
66  *      ld              LDAP descriptor
67  *      base            DN of the base object
68  *      scope           the search scope - one of LDAP_SCOPE_BASE,
69  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
70  *      filter          a string containing the search filter
71  *                      (e.g., "(|(cn=bob)(sn=bob))")
72  *      attrs           list of attribute types to return for matches
73  *      attrsonly       1 => attributes only 0 => attributes and values
74  *
75  * Example:
76  *      char    *attrs[] = { "mail", "title", 0 };
77  *      ldap_search_ext( ld, "c=us,o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
78  *          attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
79  *              &msgid );
80  */
81 int
82 ldap_search_ext(
83         LDAP *ld,
84         LDAP_CONST char *base,
85         int scope,
86         LDAP_CONST char *filter,
87         char **attrs,
88         int attrsonly,
89         LDAPControl **sctrls,
90         LDAPControl **cctrls,
91         struct timeval *timeout,
92         int sizelimit,
93         int *msgidp )
94 {
95         BerElement      *ber;
96         int timelimit;
97
98         Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
99
100         /*
101          * if timeout is provided, both tv_sec and tv_usec must
102          * be non-zero
103          */
104         if( timeout != NULL ) {
105                 if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
106                         return LDAP_PARAM_ERROR;
107                 }
108
109                 /* timelimit must be non-zero if timeout is provided */
110                 timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
111
112         } else {
113                 /* no timeout, no timelimit */
114                 timelimit = -1;
115         }
116
117         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
118             attrsonly, sctrls, cctrls, timelimit, sizelimit ); 
119
120         if ( ber == NULL ) {
121                 return ld->ld_errno;
122         }
123
124 #ifndef LDAP_NOCACHE
125         if ( ld->ld_cache != NULL ) {
126                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
127                         ber_free( ber, 1 );
128                         ld->ld_errno = LDAP_SUCCESS;
129                         *msgidp = ld->ld_msgid;
130                         return ld->ld_errno;
131                 }
132                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
133         }
134 #endif /* LDAP_NOCACHE */
135
136         /* send the message */
137         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber );
138
139         if( *msgidp < 0 )
140                 return ld->ld_errno;
141
142         return LDAP_SUCCESS;
143 }
144
145 int
146 ldap_search_ext_s(
147         LDAP *ld,
148         LDAP_CONST char *base,
149         int scope,
150         LDAP_CONST char *filter,
151         char **attrs,
152         int attrsonly,
153         LDAPControl **sctrls,
154         LDAPControl **cctrls,
155         struct timeval *timeout,
156         int sizelimit,
157         LDAPMessage **res )
158 {
159         int rc;
160         int     msgid;
161
162         rc = ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,
163                 sctrls, cctrls, timeout, sizelimit, &msgid );
164
165         if ( rc != LDAP_SUCCESS ) {
166                 return( rc );
167         }
168
169         rc = ldap_result( ld, msgid, 1, timeout, res );
170
171         if( rc <= 0 ) {
172                 /* error(-1) or timeout(0) */
173                 return( ld->ld_errno );
174         }
175
176         return( ldap_result2error( ld, *res, 0 ) );
177 }
178
179 /*
180  * ldap_search - initiate an ldap search operation.
181  *
182  * Parameters:
183  *
184  *      ld              LDAP descriptor
185  *      base            DN of the base object
186  *      scope           the search scope - one of LDAP_SCOPE_BASE,
187  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
188  *      filter          a string containing the search filter
189  *                      (e.g., "(|(cn=bob)(sn=bob))")
190  *      attrs           list of attribute types to return for matches
191  *      attrsonly       1 => attributes only 0 => attributes and values
192  *
193  * Example:
194  *      char    *attrs[] = { "mail", "title", 0 };
195  *      msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
196  *          attrs, attrsonly );
197  */
198 int
199 ldap_search(
200         LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
201         char **attrs, int attrsonly )
202 {
203         BerElement      *ber;
204
205         Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
206
207         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
208             attrsonly, NULL, NULL, -1, -1 ); 
209
210         if ( ber == NULL ) {
211                 return( -1 );
212         }
213
214 #ifndef LDAP_NOCACHE
215         if ( ld->ld_cache != NULL ) {
216                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
217                         ber_free( ber, 1 );
218                         ld->ld_errno = LDAP_SUCCESS;
219                         return( ld->ld_msgid );
220                 }
221                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
222         }
223 #endif /* LDAP_NOCACHE */
224
225         /* send the message */
226         return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
227 }
228
229
230 BerElement *
231 ldap_build_search_req(
232         LDAP *ld,
233         LDAP_CONST char *base,
234         ber_int_t scope,
235         LDAP_CONST char *filter_in,
236         char **attrs,
237         ber_int_t attrsonly,
238         LDAPControl **sctrls,
239         LDAPControl **cctrls,
240         ber_int_t timelimit,
241         ber_int_t sizelimit )
242 {
243         BerElement      *ber;
244         int             err;
245         char    *filter;
246
247         /*
248          * Create the search request.  It looks like this:
249          *      SearchRequest := [APPLICATION 3] SEQUENCE {
250          *              baseObject      DistinguishedName,
251          *              scope           ENUMERATED {
252          *                      baseObject      (0),
253          *                      singleLevel     (1),
254          *                      wholeSubtree    (2)
255          *              },
256          *              derefAliases    ENUMERATED {
257          *                      neverDerefaliases       (0),
258          *                      derefInSearching        (1),
259          *                      derefFindingBaseObj     (2),
260          *                      alwaysDerefAliases      (3)
261          *              },
262          *              sizelimit       INTEGER (0 .. 65535),
263          *              timelimit       INTEGER (0 .. 65535),
264          *              attrsOnly       BOOLEAN,
265          *              filter          Filter,
266          *              attributes      SEQUENCE OF AttributeType
267          *      }
268          * wrapped in an ldap message.
269          */
270
271         /* create a message to send */
272         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
273                 return( NULL );
274         }
275
276         if ( base == NULL ) {
277                 /* no base provided, use session default base */
278                 base = ld->ld_options.ldo_defbase;
279
280                 if ( base == NULL ) {
281                         /* no session default base, use top */
282                         base = "";
283                 }
284         }
285
286 #ifdef LDAP_CONNECTIONLESS
287         if ( ld->ld_cldapnaddr > 0 ) {
288             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
289                         ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
290                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
291                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
292                         attrsonly );
293         } else {
294 #endif /* LDAP_CONNECTIONLESS */
295                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
296                     LDAP_REQ_SEARCH, base, (ber_int_t) scope, ld->ld_deref,
297                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
298                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
299                     attrsonly );
300 #ifdef LDAP_CONNECTIONLESS
301         }
302 #endif /* LDAP_CONNECTIONLESS */
303
304         if ( err == -1 ) {
305                 ld->ld_errno = LDAP_ENCODING_ERROR;
306                 ber_free( ber, 1 );
307                 return( NULL );
308         }
309
310         filter = LDAP_STRDUP( filter_in );
311         err = put_filter( ber, filter );
312         LDAP_FREE( filter );
313
314         if ( err  == -1 ) {
315                 ld->ld_errno = LDAP_FILTER_ERROR;
316                 ber_free( ber, 1 );
317                 return( NULL );
318         }
319
320         if ( ber_printf( ber, /*{*/ "{v}}", attrs ) == -1 ) {
321                 ld->ld_errno = LDAP_ENCODING_ERROR;
322                 ber_free( ber, 1 );
323                 return( NULL );
324         }
325
326         /* Put Server Controls */
327         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
328                 ber_free( ber, 1 );
329                 return( NULL );
330         }
331
332         if ( ber_printf( ber, /*{*/ "}", attrs ) == -1 ) {
333                 ld->ld_errno = LDAP_ENCODING_ERROR;
334                 ber_free( ber, 1 );
335                 return( NULL );
336         }
337
338         return( ber );
339 }
340
341 static int ldap_is_attr_oid ( const char *attr )
342 {
343         int i, c, digit=0;
344
345         for( i = 0; (c = attr[i]) != 0; i++ ) {
346                 if( c >= '0' && c <= '9' ) {
347                         digit=1;
348
349                 } else if ( c != '.' ) {
350                         /* not digit nor '.' */
351                         return 0;
352
353                 } else if ( !digit ) {
354                         /* '.' but prev not digit */
355                         return 0;
356
357                 } else {
358                         /* '.' */
359                         digit = 0;
360                 }
361         }
362
363         return digit;
364 }
365
366 static int ldap_is_attr_desc ( const char *attr )
367 {
368         /* cheap attribute description check */
369         int i, c;
370
371         for( i = 0; (c = attr[i]) != 0; i++ ) {
372                 if (( c >= '0' && c <= '9' )
373                         || ( c >= 'A' && c <= 'Z' )
374                         || ( c >= 'a' && c <= 'z' )
375                         || ( c == '.' || c == '-' )
376                         || ( c == ';' )) continue;
377
378                 return 0;
379         }
380
381         return i > 0;
382 }
383
384 static char *
385 find_right_paren( char *s )
386 {
387         int     balance, escape;
388
389         balance = 1;
390         escape = 0;
391         while ( *s && balance ) {
392                 if ( escape == 0 ) {
393                         if ( *s == '(' )
394                                 balance++;
395                         else if ( *s == ')' )
396                                 balance--;
397                 }
398                 if ( *s == '\\' && ! escape )
399                         escape = 1;
400                 else
401                         escape = 0;
402                 if ( balance )
403                         s++;
404         }
405
406         return( *s ? s : NULL );
407 }
408
409 static int hex2value( int c )
410 {
411         if( c >= '0' && c <= '9' ) {
412                 return c - '0';
413         }
414
415         if( c >= 'A' && c <= 'F' ) {
416                 return c + (10 - (int) 'A');
417         }
418
419         if( c >= 'a' && c <= 'f' ) {
420                 return c + (10 - (int) 'a');
421         }
422
423         return -1;
424 }
425
426 char *
427 ldap_pvt_find_wildcard( char *s )
428 {
429         for( ; *s != '\0' ; s++ ) {
430                 switch( *s ) {
431                 case '*':       /* found wildcard */
432                         return s;
433
434                 case '\\':
435                         s++; /* skip over escape */
436                         if ( *s == '\0' )
437                                 return NULL;    /* escape at end of string */
438                 }
439         }
440
441         return NULL;
442 }
443
444 /* unescape filter value */
445 /* support both LDAP v2 and v3 escapes */
446 /* output can include nul characters */
447 ber_slen_t
448 ldap_pvt_filter_value_unescape( char *fval )
449 {
450         ber_slen_t r, v;
451         int v1, v2;
452
453         for( r=v=0; fval[v] != '\0'; v++ ) {
454                 switch( fval[v] ) {
455                 case '\\':
456                         /* escape */
457                         v++;
458
459                         if ( fval[v] == '\0' ) {
460                                 /* escape at end of string */
461                                 return -1;
462
463                         }
464
465                         if (( v1 = hex2value( fval[v] )) >= 0 ) {
466                                 /* LDAPv3 escape */
467
468                                 if (( v2 = hex2value( fval[v+1] )) < 0 ) {
469                                         /* must be two digit code */
470                                         return -1;
471                                 }
472
473                                 fval[r++] = v1 * 16 + v2;
474                                 v++;
475
476                         } else {
477                                 /* LDAPv2 escape */
478                                 fval[r++] = fval[v];
479                         }
480
481                         break;
482
483                 default:
484                         fval[r++] = fval[v];
485                 }
486         }
487
488         fval[r] = '\0';
489         return r;
490 }
491
492 static char *
493 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
494 {
495         char    *next;
496
497         /*
498          * We have (x(filter)...) with str sitting on
499          * the x.  We have to find the paren matching
500          * the one before the x and put the intervening
501          * filters by calling put_filter_list().
502          */
503
504         /* put explicit tag */
505         if ( ber_printf( ber, "t{" /*}*/, tag ) == -1 )
506                 return( NULL );
507
508         str++;
509         if ( (next = find_right_paren( str )) == NULL )
510                 return( NULL );
511
512         *next = '\0';
513         if ( put_filter_list( ber, str ) == -1 )
514                 return( NULL );
515         *next++ = ')';
516
517         /* flush explicit tagged thang */
518         if ( ber_printf( ber, /*{*/ "}" ) == -1 )
519                 return( NULL );
520
521         return( next );
522 }
523
524 static int
525 put_filter( BerElement *ber, char *str )
526 {
527         char    *next;
528         int     parens, balance, escape;
529
530         /*
531          * A Filter looks like this:
532          *      Filter ::= CHOICE {
533          *              and             [0]     SET OF Filter,
534          *              or              [1]     SET OF Filter,
535          *              not             [2]     Filter,
536          *              equalityMatch   [3]     AttributeValueAssertion,
537          *              substrings      [4]     SubstringFilter,
538          *              greaterOrEqual  [5]     AttributeValueAssertion,
539          *              lessOrEqual     [6]     AttributeValueAssertion,
540          *              present         [7]     AttributeType,
541          *              approxMatch     [8]     AttributeValueAssertion,
542          *                              extensibleMatch [9]             MatchingRuleAssertion -- LDAPv3
543          *      }
544          *
545          *      SubstringFilter ::= SEQUENCE {
546          *              type               AttributeType,
547          *              SEQUENCE OF CHOICE {
548          *                      initial          [0] IA5String,
549          *                      any              [1] IA5String,
550          *                      final            [2] IA5String
551          *              }
552          *      }
553          *
554          *              MatchingRuleAssertion ::= SEQUENCE {    -- LDAPv3
555          *                      matchingRule    [1] MatchingRuleId OPTIONAL,
556          *                      type            [2] AttributeDescription OPTIONAL,
557          *                      matchValue      [3] AssertionValue,
558          *                      dnAttributes    [4] BOOLEAN DEFAULT FALSE }
559          *
560          * Note: tags in a choice are always explicit
561          */
562
563         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
564
565         parens = 0;
566         while ( *str ) {
567                 switch ( *str ) {
568                 case '(':
569                         str++;
570                         parens++;
571                         switch ( *str ) {
572                         case '&':
573                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
574                                     0, 0, 0 );
575
576                                 if ( (str = put_complex_filter( ber, str,
577                                     LDAP_FILTER_AND, 0 )) == NULL )
578                                         return( -1 );
579
580                                 parens--;
581                                 break;
582
583                         case '|':
584                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
585                                     0, 0, 0 );
586
587                                 if ( (str = put_complex_filter( ber, str,
588                                     LDAP_FILTER_OR, 0 )) == NULL )
589                                         return( -1 );
590
591                                 parens--;
592                                 break;
593
594                         case '!':
595                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
596                                     0, 0, 0 );
597
598                                 if ( (str = put_complex_filter( ber, str,
599                                     LDAP_FILTER_NOT, 1 )) == NULL )
600                                         return( -1 );
601
602                                 parens--;
603                                 break;
604
605                         default:
606                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
607                                     0, 0, 0 );
608
609                                 balance = 1;
610                                 escape = 0;
611                                 next = str;
612                                 while ( *next && balance ) {
613                                         if ( escape == 0 ) {
614                                                 if ( *next == '(' )
615                                                         balance++;
616                                                 else if ( *next == ')' )
617                                                         balance--;
618                                         }
619                                         if ( *next == '\\' && ! escape )
620                                                 escape = 1;
621                                         else
622                                                 escape = 0;
623                                         if ( balance )
624                                                 next++;
625                                 }
626                                 if ( balance != 0 )
627                                         return( -1 );
628
629                                 *next = '\0';
630                                 if ( put_simple_filter( ber, str ) == -1 ) {
631                                         return( -1 );
632                                 }
633                                 *next++ = ')';
634                                 str = next;
635                                 parens--;
636                                 break;
637                         }
638                         break;
639
640                 case ')':
641                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
642                             0 );
643                         if ( ber_printf( ber, /*[*/ "]" ) == -1 )
644                                 return( -1 );
645                         str++;
646                         parens--;
647                         break;
648
649                 case ' ':
650                         str++;
651                         break;
652
653                 default:        /* assume it's a simple type=value filter */
654                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
655                             0 );
656                         next = strchr( str, '\0' );
657                         if ( put_simple_filter( ber, str ) == -1 ) {
658                                 return( -1 );
659                         }
660                         str = next;
661                         break;
662                 }
663         }
664
665         return( parens ? -1 : 0 );
666 }
667
668 /*
669  * Put a list of filters like this "(filter1)(filter2)..."
670  */
671
672 static int
673 put_filter_list( BerElement *ber, char *str )
674 {
675         char    *next;
676         char    save;
677
678         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
679
680         while ( *str ) {
681                 while ( *str && isspace( (unsigned char) *str ) )
682                         str++;
683                 if ( *str == '\0' )
684                         break;
685
686                 if ( (next = find_right_paren( str + 1 )) == NULL )
687                         return( -1 );
688                 save = *++next;
689
690                 /* now we have "(filter)" with str pointing to it */
691                 *next = '\0';
692                 if ( put_filter( ber, str ) == -1 )
693                         return( -1 );
694                 *next = save;
695
696                 str = next;
697         }
698
699         return( 0 );
700 }
701
702 static int
703 put_simple_filter(
704         BerElement *ber,
705         char *str )
706 {
707         char            *s;
708         char            *value;
709         ber_tag_t       ftype;
710         int             rc = -1;
711
712         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
713
714         str = LDAP_STRDUP( str );
715         if( str == NULL ) return -1;
716
717         if ( (s = strchr( str, '=' )) == NULL ) {
718                 goto done;
719         }
720
721         value = s + 1;
722         *s-- = '\0';
723
724         switch ( *s ) {
725         case '<':
726                 ftype = LDAP_FILTER_LE;
727                 *s = '\0';
728                 if(! ldap_is_attr_desc( str ) ) goto done;
729                 break;
730
731         case '>':
732                 ftype = LDAP_FILTER_GE;
733                 *s = '\0';
734                 if(! ldap_is_attr_desc( str ) ) goto done;
735                 break;
736
737         case '~':
738                 ftype = LDAP_FILTER_APPROX;
739                 *s = '\0';
740                 if(! ldap_is_attr_desc( str ) ) goto done;
741                 break;
742
743         case ':':
744                 /* RFC2254 extensible filters are off the form:
745                  *              type [:dn] [:rule] := value
746                  * or   [:dn]:rule := value             
747                  */
748                 ftype = LDAP_FILTER_EXT;
749                 *s = '\0';
750
751                 {
752                         char *dn = strchr( str, ':' );
753                         char *rule = NULL;
754
755                         if( dn == NULL ) {
756                                 if(! ldap_is_attr_desc( str ) ) goto done;
757                                 break;
758                         }
759
760                         *dn++ = '\0';
761                         rule = strchr( dn, ':' );
762
763                         if( rule == NULL ) {
764                                 /* one colon */
765                                 if ( strcmp(dn, "dn") == 0 ) {
766                                         /* must have attribute */
767                                         if( !ldap_is_attr_desc( str ) ) {
768                                                 goto done;
769                                         }
770
771                                         rule = "";
772
773                                 } else {
774                                         rule = dn;
775                                         dn = NULL;
776                                 }
777                                 
778                         } else {
779                                 /* two colons */
780                                 *rule++ = '\0';
781
782                                 if ( strcmp(dn, "dn") != 0 ) {
783                                         /* must have "dn" */
784                                         goto done;
785                                 }
786                         }
787
788                         if ( *str == '\0' && *rule == '\0' ) {
789                                 /* must have either type or rule */
790                                 goto done;
791                         }
792
793                         if ( *str != '\0' && !ldap_is_attr_desc( str ) ) {
794                                 goto done;
795                         }
796
797                         if ( *rule != '\0' && !ldap_is_attr_oid( rule ) ) {
798                                 goto done;
799                         }
800
801                         rc = ber_printf( ber, "t{" /*}*/, ftype );
802
803                         if( rc != -1 && *rule != '\0' ) {
804                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
805                         }
806                         if( rc != -1 && *str != '\0' ) {
807                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
808                         }
809
810                         if( rc != -1 ) {
811                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
812
813                                 if( len >= 0 ) {
814                                         rc = ber_printf( ber, "totb}",
815                                                 LDAP_FILTER_EXT_VALUE, value, len,
816                                                 LDAP_FILTER_EXT_DNATTRS, dn != NULL);
817                                 } else {
818                                         rc = -1;
819                                 }
820                         }
821                 }
822                 break;
823
824         default:
825                 if ( ldap_pvt_find_wildcard( value ) == NULL ) {
826                         ftype = LDAP_FILTER_EQUALITY;
827                 } else if ( strcmp( value, "*" ) == 0 ) {
828                         ftype = LDAP_FILTER_PRESENT;
829                 } else {
830                         rc = put_substring_filter( ber, str, value );
831                         goto done;
832                 }
833                 break;
834         }
835
836         if ( ftype == LDAP_FILTER_PRESENT ) {
837                 rc = ber_printf( ber, "ts", ftype, str );
838
839         } else {
840                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
841
842                 if( len >= 0 ) {
843                         rc = ber_printf( ber, "t{so}",
844                                 ftype, str, value, len );
845                 }
846         }
847
848         if( rc != -1 ) rc = 0;
849
850 done:
851         LDAP_FREE( str );
852         return rc;
853 }
854
855 static int
856 put_substring_filter( BerElement *ber, char *type, char *val )
857 {
858         char            *nextstar, gotstar = 0;
859         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
860
861         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
862             val, 0 );
863
864         if ( ber_printf( ber, "t{s{", ftype, type ) == -1 )
865                 return( -1 );
866
867         for( ; val != NULL; val=nextstar ) {
868                 if ( (nextstar = ldap_pvt_find_wildcard( val )) != NULL )
869                         *nextstar++ = '\0';
870
871                 if ( gotstar == 0 ) {
872                         ftype = LDAP_SUBSTRING_INITIAL;
873                 } else if ( nextstar == NULL ) {
874                         ftype = LDAP_SUBSTRING_FINAL;
875                 } else {
876                         ftype = LDAP_SUBSTRING_ANY;
877                 }
878
879                 if ( *val != '\0' ) {
880                         ber_slen_t len = ldap_pvt_filter_value_unescape( val );
881
882                         if ( len < 0  ) {
883                                 return -1;
884                         }
885
886                         if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
887                                 return( -1 );
888                         }
889                 }
890
891                 gotstar = 1;
892         }
893
894         if ( ber_printf( ber, /* {{ */ "}}" ) == -1 )
895                 return( -1 );
896
897         return( 0 );
898 }
899
900 int
901 ldap_search_st(
902         LDAP *ld, LDAP_CONST char *base, int scope,
903         LDAP_CONST char *filter, char **attrs,
904         int attrsonly, struct timeval *timeout, LDAPMessage **res )
905 {
906         int     msgid;
907
908         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
909             == -1 )
910                 return( ld->ld_errno );
911
912         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
913                 return( ld->ld_errno );
914
915         if ( ld->ld_errno == LDAP_TIMEOUT ) {
916                 (void) ldap_abandon( ld, msgid );
917                 ld->ld_errno = LDAP_TIMEOUT;
918                 return( ld->ld_errno );
919         }
920
921         return( ldap_result2error( ld, *res, 0 ) );
922 }
923
924 int
925 ldap_search_s(
926         LDAP *ld,
927         LDAP_CONST char *base,
928         int scope,
929         LDAP_CONST char *filter,
930         char **attrs,
931         int attrsonly,
932         LDAPMessage **res )
933 {
934         int     msgid;
935
936         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
937             == -1 )
938                 return( ld->ld_errno );
939
940         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
941                 return( ld->ld_errno );
942
943         return( ldap_result2error( ld, *res, 0 ) );
944 }
945