]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
s/slapentry/slapadd/
[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_in,
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    *base;
238         char    *filter;
239
240         /*
241          * Create the search request.  It looks like this:
242          *      SearchRequest := [APPLICATION 3] SEQUENCE {
243          *              baseObject      DistinguishedName,
244          *              scope           ENUMERATED {
245          *                      baseObject      (0),
246          *                      singleLevel     (1),
247          *                      wholeSubtree    (2)
248          *              },
249          *              derefAliases    ENUMERATED {
250          *                      neverDerefaliases       (0),
251          *                      derefInSearching        (1),
252          *                      derefFindingBaseObj     (2),
253          *                      alwaysDerefAliases      (3)
254          *              },
255          *              sizelimit       INTEGER (0 .. 65535),
256          *              timelimit       INTEGER (0 .. 65535),
257          *              attrsOnly       BOOLEAN,
258          *              filter          Filter,
259          *              attributes      SEQUENCE OF AttributeType
260          *      }
261          * wrapped in an ldap message.
262          */
263
264         /* create a message to send */
265         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
266                 return( NULL );
267         }
268
269         if ( base_in == NULL ) {
270                 /* no base provided, use session default base */
271                 base = ld->ld_options.ldo_defbase;
272         } else {
273                 base = (char *) base_in;
274         }
275
276         if ( base == NULL ) {
277                 /* no session default base, use top */
278             base = "";
279         }
280
281 #ifdef LDAP_CONNECTIONLESS
282         if ( ld->ld_cldapnaddr > 0 ) {
283             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
284                         ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
285                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
286                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
287                         attrsonly );
288         } else {
289 #endif /* LDAP_CONNECTIONLESS */
290                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
291                     LDAP_REQ_SEARCH, base, (ber_int_t) scope, ld->ld_deref,
292                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
293                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
294                     attrsonly );
295 #ifdef LDAP_CONNECTIONLESS
296         }
297 #endif /* LDAP_CONNECTIONLESS */
298
299         if ( err == -1 ) {
300                 ld->ld_errno = LDAP_ENCODING_ERROR;
301                 ber_free( ber, 1 );
302                 return( NULL );
303         }
304
305         filter = LDAP_STRDUP( filter_in );
306         err = put_filter( ber, filter );
307         LDAP_FREE( filter );
308
309         if ( err  == -1 ) {
310                 ld->ld_errno = LDAP_FILTER_ERROR;
311                 ber_free( ber, 1 );
312                 return( NULL );
313         }
314
315         if ( ber_printf( ber, /*{*/ "{v}}", attrs ) == -1 ) {
316                 ld->ld_errno = LDAP_ENCODING_ERROR;
317                 ber_free( ber, 1 );
318                 return( NULL );
319         }
320
321         /* Put Server Controls */
322         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
323                 ber_free( ber, 1 );
324                 return( NULL );
325         }
326
327         if ( ber_printf( ber, /*{*/ "}", attrs ) == -1 ) {
328                 ld->ld_errno = LDAP_ENCODING_ERROR;
329                 ber_free( ber, 1 );
330                 return( NULL );
331         }
332
333         return( ber );
334 }
335
336 static int ldap_is_attr_oid LDAP_P(( const char *attr ))
337 {
338         int i, c, digit=0;
339
340         for( i=0 ; c = attr[i] ; i++ ) {
341                 if( c >= '0' && c <= '9' ) {
342                         digit=1;
343
344                 } else if ( c != '.' ) {
345                         /* not digit nor '.' */
346                         return 0;
347
348                 } else if ( !digit ) {
349                         /* '.' but prev not digit */
350                         return 0;
351
352                 } else {
353                         /* '.' */
354                         digit = 0;
355                 }
356         }
357
358         return digit;
359
360 }
361
362 static int ldap_is_attr_desc LDAP_P(( const char *attr ))
363 {
364         /* cheap attribute description check */
365         int i, c;
366
367         for( i=0; c = attr[i]; i++ ) {
368                 if (( c >= '0' && c <= '9' )
369                         || ( c >= 'A' && c <= 'Z' )
370                         || ( c >= 'a' && c <= 'z' )
371                         || ( c == '.' || c == '-' )
372                         || ( c == ';' )) continue;
373
374                 return 0;
375         }
376
377         return i > 0;
378 }
379
380 static char *
381 find_right_paren( char *s )
382 {
383         int     balance, escape;
384
385         balance = 1;
386         escape = 0;
387         while ( *s && balance ) {
388                 if ( escape == 0 ) {
389                         if ( *s == '(' )
390                                 balance++;
391                         else if ( *s == ')' )
392                                 balance--;
393                 }
394                 if ( *s == '\\' && ! escape )
395                         escape = 1;
396                 else
397                         escape = 0;
398                 if ( balance )
399                         s++;
400         }
401
402         return( *s ? s : NULL );
403 }
404
405 static int hex2value( int c )
406 {
407         if( c >= '0' && c <= '9' ) {
408                 return c - '0';
409         }
410
411         if( c >= 'A' && c <= 'F' ) {
412                 return c + (10 - (int) 'A');
413         }
414
415         if( c >= 'a' && c <= 'f' ) {
416                 return c + (10 - (int) 'a');
417         }
418
419         return -1;
420 }
421
422 static char *
423 find_wildcard( char *s )
424 {
425         for( ; *s != '\0' ; s++ ) {
426                 switch( *s ) {
427                 case '*':       /* found wildcard */
428                         return s;
429
430                 case '\\':
431                         s++; /* skip over escape */
432                         if( hex2value( s[0] ) >= 0 && hex2value( s[1] ) >= 0 ) {
433                                 /* skip over lead digit of two hex digit code */
434                                 s++;
435                         }
436                 }
437         }
438
439         return NULL;
440 }
441
442 /* unescape filter value */
443 /* support both LDAP v2 and v3 escapes */
444 /* output can include nul characters */
445 static ber_slen_t
446 filter_value_unescape( char *fval )
447 {
448         ber_slen_t r, v;
449         int v1, v2;
450
451         for( r=v=0; fval[v] != '\0'; v++ ) {
452                 switch( fval[v] ) {
453                 case '\\':
454                         /* escape */
455                         v++;
456
457                         if ( fval[v] == '\0' ) {
458                                 /* escape at end of string */
459                                 return -1;
460
461                         } else if (( v1 = hex2value( fval[v] )) < 0 )  {
462                                 /* LDAPv3 escape */
463
464                                 if (( v2 = hex2value( fval[v+1] )) < 0 ) {
465                                         /* must be two digit code */
466                                         return -1;
467                                 }
468
469                                 fval[r++] = v1 * 16 + v2;
470                                 v++;
471
472                         } else {
473                                 /* LDAPv2 escape */
474                                 fval[r++] = fval[v];
475                         }
476
477                         break;
478
479                 default:
480                         fval[r++] = fval[v];
481                 }
482         }
483
484         fval[r] = '\0';
485         return r;
486 }
487
488 static char *
489 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
490 {
491         char    *next;
492
493         /*
494          * We have (x(filter)...) with str sitting on
495          * the x.  We have to find the paren matching
496          * the one before the x and put the intervening
497          * filters by calling put_filter_list().
498          */
499
500         /* put explicit tag */
501         if ( ber_printf( ber, "t{" /*}*/, tag ) == -1 )
502                 return( NULL );
503
504         str++;
505         if ( (next = find_right_paren( str )) == NULL )
506                 return( NULL );
507
508         *next = '\0';
509         if ( put_filter_list( ber, str ) == -1 )
510                 return( NULL );
511         *next++ = ')';
512
513         /* flush explicit tagged thang */
514         if ( ber_printf( ber, /*{*/ "}" ) == -1 )
515                 return( NULL );
516
517         return( next );
518 }
519
520 static int
521 put_filter( BerElement *ber, char *str )
522 {
523         char    *next;
524         int     parens, balance, escape;
525
526         /*
527          * A Filter looks like this:
528          *      Filter ::= CHOICE {
529          *              and             [0]     SET OF Filter,
530          *              or              [1]     SET OF Filter,
531          *              not             [2]     Filter,
532          *              equalityMatch   [3]     AttributeValueAssertion,
533          *              substrings      [4]     SubstringFilter,
534          *              greaterOrEqual  [5]     AttributeValueAssertion,
535          *              lessOrEqual     [6]     AttributeValueAssertion,
536          *              present         [7]     AttributeType,
537          *              approxMatch     [8]     AttributeValueAssertion,
538          *                              extensibleMatch [9]             MatchingRuleAssertion -- LDAPv3
539          *      }
540          *
541          *      SubstringFilter ::= SEQUENCE {
542          *              type               AttributeType,
543          *              SEQUENCE OF CHOICE {
544          *                      initial          [0] IA5String,
545          *                      any              [1] IA5String,
546          *                      final            [2] IA5String
547          *              }
548          *      }
549          *
550          *              MatchingRuleAssertion ::= SEQUENCE {    -- LDAPv3
551          *                      matchingRule    [1] MatchingRuleId OPTIONAL,
552          *                      type            [2] AttributeDescription OPTIONAL,
553          *                      matchValue      [3] AssertionValue,
554          *                      dnAttributes    [4] BOOLEAN DEFAULT FALSE }
555          *
556          * Note: tags in a choice are always explicit
557          */
558
559         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
560
561         parens = 0;
562         while ( *str ) {
563                 switch ( *str ) {
564                 case '(':
565                         str++;
566                         parens++;
567                         switch ( *str ) {
568                         case '&':
569                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
570                                     0, 0, 0 );
571
572                                 if ( (str = put_complex_filter( ber, str,
573                                     LDAP_FILTER_AND, 0 )) == NULL )
574                                         return( -1 );
575
576                                 parens--;
577                                 break;
578
579                         case '|':
580                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
581                                     0, 0, 0 );
582
583                                 if ( (str = put_complex_filter( ber, str,
584                                     LDAP_FILTER_OR, 0 )) == NULL )
585                                         return( -1 );
586
587                                 parens--;
588                                 break;
589
590                         case '!':
591                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
592                                     0, 0, 0 );
593
594                                 if ( (str = put_complex_filter( ber, str,
595                                     LDAP_FILTER_NOT, 1 )) == NULL )
596                                         return( -1 );
597
598                                 parens--;
599                                 break;
600
601                         default:
602                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
603                                     0, 0, 0 );
604
605                                 balance = 1;
606                                 escape = 0;
607                                 next = str;
608                                 while ( *next && balance ) {
609                                         if ( escape == 0 ) {
610                                                 if ( *next == '(' )
611                                                         balance++;
612                                                 else if ( *next == ')' )
613                                                         balance--;
614                                         }
615                                         if ( *next == '\\' && ! escape )
616                                                 escape = 1;
617                                         else
618                                                 escape = 0;
619                                         if ( balance )
620                                                 next++;
621                                 }
622                                 if ( balance != 0 )
623                                         return( -1 );
624
625                                 *next = '\0';
626                                 if ( put_simple_filter( ber, str ) == -1 ) {
627                                         return( -1 );
628                                 }
629                                 *next++ = ')';
630                                 str = next;
631                                 parens--;
632                                 break;
633                         }
634                         break;
635
636                 case ')':
637                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
638                             0 );
639                         if ( ber_printf( ber, /*[*/ "]" ) == -1 )
640                                 return( -1 );
641                         str++;
642                         parens--;
643                         break;
644
645                 case ' ':
646                         str++;
647                         break;
648
649                 default:        /* assume it's a simple type=value filter */
650                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
651                             0 );
652                         next = strchr( str, '\0' );
653                         if ( put_simple_filter( ber, str ) == -1 ) {
654                                 return( -1 );
655                         }
656                         str = next;
657                         break;
658                 }
659         }
660
661         return( parens ? -1 : 0 );
662 }
663
664 /*
665  * Put a list of filters like this "(filter1)(filter2)..."
666  */
667
668 static int
669 put_filter_list( BerElement *ber, char *str )
670 {
671         char    *next;
672         char    save;
673
674         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
675
676         while ( *str ) {
677                 while ( *str && isspace( (unsigned char) *str ) )
678                         str++;
679                 if ( *str == '\0' )
680                         break;
681
682                 if ( (next = find_right_paren( str + 1 )) == NULL )
683                         return( -1 );
684                 save = *++next;
685
686                 /* now we have "(filter)" with str pointing to it */
687                 *next = '\0';
688                 if ( put_filter( ber, str ) == -1 )
689                         return( -1 );
690                 *next = save;
691
692                 str = next;
693         }
694
695         return( 0 );
696 }
697
698 static int
699 put_simple_filter(
700         BerElement *ber,
701         char *str )
702 {
703         char            *s;
704         char            *value;
705         ber_tag_t       ftype;
706         int             rc = -1;
707
708         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
709
710         str = LDAP_STRDUP( str );
711         if( str == NULL ) return -1;
712
713         if ( (s = strchr( str, '=' )) == NULL ) {
714                 goto done;
715         }
716
717         value = s + 1;
718         *s-- = '\0';
719
720         switch ( *s ) {
721         case '<':
722                 ftype = LDAP_FILTER_LE;
723                 *s = '\0';
724                 if(! ldap_is_attr_desc( str ) ) goto done;
725                 break;
726
727         case '>':
728                 ftype = LDAP_FILTER_GE;
729                 *s = '\0';
730                 if(! ldap_is_attr_desc( str ) ) goto done;
731                 break;
732
733         case '~':
734                 ftype = LDAP_FILTER_APPROX;
735                 *s = '\0';
736                 if(! ldap_is_attr_desc( str ) ) goto done;
737                 break;
738
739         case ':':
740                 /* RFC2254 extensible filters are off the form:
741                  *              type [:dn] [:rule] := value
742                  * or   [:dn]:rule := value             
743                  */
744                 ftype = LDAP_FILTER_EXT;
745                 *s = '\0';
746
747                 {
748                         char *dn = strchr( str, ':' );
749                         char *rule = NULL;
750
751                         if( dn == NULL ) {
752                                 if(! ldap_is_attr_desc( str ) ) goto done;
753                                 break;
754                         }
755
756                         *dn++ = '\0';
757                         rule = strchr( dn, ':' );
758
759                         if( rule == NULL ) {
760                                 /* one colon */
761                                 if ( strcmp(dn, "dn") == 0 ) {
762                                         /* must have attribute */
763                                         if( !ldap_is_attr_desc( str ) ) {
764                                                 goto done;
765                                         }
766
767                                         rule = "";
768
769                                 } else {
770                                         rule = dn;
771                                         dn = NULL;
772                                 }
773                                 
774                         } else {
775                                 /* two colons */
776                                 *rule++ = '\0';
777
778                                 if ( strcmp(dn, "dn") != 0 ) {
779                                         /* must have "dn" */
780                                         goto done;
781                                 }
782                         }
783
784                         if ( *str == '\0' && *rule == '\0' ) {
785                                 /* must have either type or rule */
786                                 goto done;
787                         }
788
789                         if ( *str != '\0' && !ldap_is_attr_desc( str ) ) {
790                                 goto done;
791                         }
792
793                         if ( *rule != '\0' && !ldap_is_attr_oid( rule ) ) {
794                                 goto done;
795                         }
796
797                         rc = ber_printf( ber, "t{" /*}*/, ftype );
798
799                         if( rc != -1 && *rule != '\0' ) {
800                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
801                         }
802                         if( rc != -1 && *str != '\0' ) {
803                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
804                         }
805
806                         if( rc != -1 ) {
807                                 ber_slen_t len = filter_value_unescape( value );
808
809                                 if( len >= 0 ) {
810                                         rc = ber_printf( ber, "totb}",
811                                                 LDAP_FILTER_EXT_VALUE, value, len,
812                                                 LDAP_FILTER_EXT_DNATTRS, dn != NULL);
813                                 } else {
814                                         rc = -1;
815                                 }
816                         }
817                 }
818                 break;
819
820         default:
821                 if ( find_wildcard( value ) == NULL ) {
822                         ftype = LDAP_FILTER_EQUALITY;
823                 } else if ( strcmp( value, "*" ) == 0 ) {
824                         ftype = LDAP_FILTER_PRESENT;
825                 } else {
826                         rc = put_substring_filter( ber, str, value );
827                         goto done;
828                 }
829                 break;
830         }
831
832         if ( ftype == LDAP_FILTER_PRESENT ) {
833                 rc = ber_printf( ber, "ts", ftype, str );
834
835         } else {
836                 ber_slen_t len = filter_value_unescape( value );
837
838                 if( len >= 0 ) {
839                         rc = ber_printf( ber, "t{so}",
840                                 ftype, str, value, len );
841                 }
842         }
843
844         if( rc != -1 ) rc = 0;
845
846 done:
847         LDAP_FREE( str );
848         return rc;
849 }
850
851 static int
852 put_substring_filter( BerElement *ber, char *type, char *val )
853 {
854         char            *nextstar, gotstar = 0;
855         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
856
857         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
858             val, 0 );
859
860         if ( ber_printf( ber, "t{s{", ftype, type ) == -1 )
861                 return( -1 );
862
863         for( ; val != NULL; val=nextstar ) {
864                 if ( (nextstar = find_wildcard( val )) != NULL )
865                         *nextstar++ = '\0';
866
867                 if ( gotstar == 0 ) {
868                         ftype = LDAP_SUBSTRING_INITIAL;
869                 } else if ( nextstar == NULL ) {
870                         ftype = LDAP_SUBSTRING_FINAL;
871                 } else {
872                         ftype = LDAP_SUBSTRING_ANY;
873                 }
874
875                 if ( *val != '\0' ) {
876                         ber_slen_t len = filter_value_unescape( val );
877
878                         if ( len < 0  ) {
879                                 return -1;
880                         }
881
882                         if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
883                                 return( -1 );
884                         }
885                 }
886
887                 gotstar = 1;
888         }
889
890         if ( ber_printf( ber, /* {{ */ "}}" ) == -1 )
891                 return( -1 );
892
893         return( 0 );
894 }
895
896 int
897 ldap_search_st(
898         LDAP *ld, LDAP_CONST char *base, int scope,
899         LDAP_CONST char *filter, char **attrs,
900         int attrsonly, struct timeval *timeout, LDAPMessage **res )
901 {
902         int     msgid;
903
904         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
905             == -1 )
906                 return( ld->ld_errno );
907
908         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
909                 return( ld->ld_errno );
910
911         if ( ld->ld_errno == LDAP_TIMEOUT ) {
912                 (void) ldap_abandon( ld, msgid );
913                 ld->ld_errno = LDAP_TIMEOUT;
914                 return( ld->ld_errno );
915         }
916
917         return( ldap_result2error( ld, *res, 0 ) );
918 }
919
920 int
921 ldap_search_s(
922         LDAP *ld,
923         LDAP_CONST char *base,
924         int scope,
925         LDAP_CONST char *filter,
926         char **attrs,
927         int attrsonly,
928         LDAPMessage **res )
929 {
930         int     msgid;
931
932         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
933             == -1 )
934                 return( ld->ld_errno );
935
936         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
937                 return( ld->ld_errno );
938
939         return( ldap_result2error( ld, *res, 0 ) );
940 }
941