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