]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
50c46cfa20543dd6d864708842f75e3f8d310ca1
[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( hex2value( s[0] ) >= 0 && hex2value( s[1] ) >= 0 ) {
430                                 /* skip over lead digit of two hex digit code */
431                                 s++;
432                         }
433                 }
434         }
435
436         return NULL;
437 }
438
439 /* unescape filter value */
440 /* support both LDAP v2 and v3 escapes */
441 /* output can include nul characters */
442 static ber_slen_t
443 filter_value_unescape( char *fval )
444 {
445         ber_slen_t r, v;
446         int v1, v2;
447
448         for( r=v=0; fval[v] != '\0'; v++ ) {
449                 switch( fval[v] ) {
450                 case '\\':
451                         /* escape */
452                         v++;
453
454                         if ( fval[v] == '\0' ) {
455                                 /* escape at end of string */
456                                 return -1;
457
458                         }
459
460                         if (( v1 = hex2value( fval[v] )) >= 0 ) {
461                                 /* LDAPv3 escape */
462
463                                 if (( v2 = hex2value( fval[v+1] )) < 0 ) {
464                                         /* must be two digit code */
465                                         return -1;
466                                 }
467
468                                 fval[r++] = v1 * 16 + v2;
469                                 v++;
470
471                         } else {
472                                 /* LDAPv2 escape */
473                                 fval[r++] = fval[v];
474                         }
475
476                         break;
477
478                 default:
479                         fval[r++] = fval[v];
480                 }
481         }
482
483         fval[r] = '\0';
484         return r;
485 }
486
487 static char *
488 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
489 {
490         char    *next;
491
492         /*
493          * We have (x(filter)...) with str sitting on
494          * the x.  We have to find the paren matching
495          * the one before the x and put the intervening
496          * filters by calling put_filter_list().
497          */
498
499         /* put explicit tag */
500         if ( ber_printf( ber, "t{" /*}*/, tag ) == -1 )
501                 return( NULL );
502
503         str++;
504         if ( (next = find_right_paren( str )) == NULL )
505                 return( NULL );
506
507         *next = '\0';
508         if ( put_filter_list( ber, str ) == -1 )
509                 return( NULL );
510         *next++ = ')';
511
512         /* flush explicit tagged thang */
513         if ( ber_printf( ber, /*{*/ "}" ) == -1 )
514                 return( NULL );
515
516         return( next );
517 }
518
519 static int
520 put_filter( BerElement *ber, char *str )
521 {
522         char    *next;
523         int     parens, balance, escape;
524
525         /*
526          * A Filter looks like this:
527          *      Filter ::= CHOICE {
528          *              and             [0]     SET OF Filter,
529          *              or              [1]     SET OF Filter,
530          *              not             [2]     Filter,
531          *              equalityMatch   [3]     AttributeValueAssertion,
532          *              substrings      [4]     SubstringFilter,
533          *              greaterOrEqual  [5]     AttributeValueAssertion,
534          *              lessOrEqual     [6]     AttributeValueAssertion,
535          *              present         [7]     AttributeType,
536          *              approxMatch     [8]     AttributeValueAssertion,
537          *                              extensibleMatch [9]             MatchingRuleAssertion -- LDAPv3
538          *      }
539          *
540          *      SubstringFilter ::= SEQUENCE {
541          *              type               AttributeType,
542          *              SEQUENCE OF CHOICE {
543          *                      initial          [0] IA5String,
544          *                      any              [1] IA5String,
545          *                      final            [2] IA5String
546          *              }
547          *      }
548          *
549          *              MatchingRuleAssertion ::= SEQUENCE {    -- LDAPv3
550          *                      matchingRule    [1] MatchingRuleId OPTIONAL,
551          *                      type            [2] AttributeDescription OPTIONAL,
552          *                      matchValue      [3] AssertionValue,
553          *                      dnAttributes    [4] BOOLEAN DEFAULT FALSE }
554          *
555          * Note: tags in a choice are always explicit
556          */
557
558         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
559
560         parens = 0;
561         while ( *str ) {
562                 switch ( *str ) {
563                 case '(':
564                         str++;
565                         parens++;
566                         switch ( *str ) {
567                         case '&':
568                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
569                                     0, 0, 0 );
570
571                                 if ( (str = put_complex_filter( ber, str,
572                                     LDAP_FILTER_AND, 0 )) == NULL )
573                                         return( -1 );
574
575                                 parens--;
576                                 break;
577
578                         case '|':
579                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
580                                     0, 0, 0 );
581
582                                 if ( (str = put_complex_filter( ber, str,
583                                     LDAP_FILTER_OR, 0 )) == NULL )
584                                         return( -1 );
585
586                                 parens--;
587                                 break;
588
589                         case '!':
590                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
591                                     0, 0, 0 );
592
593                                 if ( (str = put_complex_filter( ber, str,
594                                     LDAP_FILTER_NOT, 1 )) == NULL )
595                                         return( -1 );
596
597                                 parens--;
598                                 break;
599
600                         default:
601                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
602                                     0, 0, 0 );
603
604                                 balance = 1;
605                                 escape = 0;
606                                 next = str;
607                                 while ( *next && balance ) {
608                                         if ( escape == 0 ) {
609                                                 if ( *next == '(' )
610                                                         balance++;
611                                                 else if ( *next == ')' )
612                                                         balance--;
613                                         }
614                                         if ( *next == '\\' && ! escape )
615                                                 escape = 1;
616                                         else
617                                                 escape = 0;
618                                         if ( balance )
619                                                 next++;
620                                 }
621                                 if ( balance != 0 )
622                                         return( -1 );
623
624                                 *next = '\0';
625                                 if ( put_simple_filter( ber, str ) == -1 ) {
626                                         return( -1 );
627                                 }
628                                 *next++ = ')';
629                                 str = next;
630                                 parens--;
631                                 break;
632                         }
633                         break;
634
635                 case ')':
636                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
637                             0 );
638                         if ( ber_printf( ber, /*[*/ "]" ) == -1 )
639                                 return( -1 );
640                         str++;
641                         parens--;
642                         break;
643
644                 case ' ':
645                         str++;
646                         break;
647
648                 default:        /* assume it's a simple type=value filter */
649                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
650                             0 );
651                         next = strchr( str, '\0' );
652                         if ( put_simple_filter( ber, str ) == -1 ) {
653                                 return( -1 );
654                         }
655                         str = next;
656                         break;
657                 }
658         }
659
660         return( parens ? -1 : 0 );
661 }
662
663 /*
664  * Put a list of filters like this "(filter1)(filter2)..."
665  */
666
667 static int
668 put_filter_list( BerElement *ber, char *str )
669 {
670         char    *next;
671         char    save;
672
673         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
674
675         while ( *str ) {
676                 while ( *str && isspace( (unsigned char) *str ) )
677                         str++;
678                 if ( *str == '\0' )
679                         break;
680
681                 if ( (next = find_right_paren( str + 1 )) == NULL )
682                         return( -1 );
683                 save = *++next;
684
685                 /* now we have "(filter)" with str pointing to it */
686                 *next = '\0';
687                 if ( put_filter( ber, str ) == -1 )
688                         return( -1 );
689                 *next = save;
690
691                 str = next;
692         }
693
694         return( 0 );
695 }
696
697 static int
698 put_simple_filter(
699         BerElement *ber,
700         char *str )
701 {
702         char            *s;
703         char            *value;
704         ber_tag_t       ftype;
705         int             rc = -1;
706
707         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
708
709         str = LDAP_STRDUP( str );
710         if( str == NULL ) return -1;
711
712         if ( (s = strchr( str, '=' )) == NULL ) {
713                 goto done;
714         }
715
716         value = s + 1;
717         *s-- = '\0';
718
719         switch ( *s ) {
720         case '<':
721                 ftype = LDAP_FILTER_LE;
722                 *s = '\0';
723                 if(! ldap_is_attr_desc( str ) ) goto done;
724                 break;
725
726         case '>':
727                 ftype = LDAP_FILTER_GE;
728                 *s = '\0';
729                 if(! ldap_is_attr_desc( str ) ) goto done;
730                 break;
731
732         case '~':
733                 ftype = LDAP_FILTER_APPROX;
734                 *s = '\0';
735                 if(! ldap_is_attr_desc( str ) ) goto done;
736                 break;
737
738         case ':':
739                 /* RFC2254 extensible filters are off the form:
740                  *              type [:dn] [:rule] := value
741                  * or   [:dn]:rule := value             
742                  */
743                 ftype = LDAP_FILTER_EXT;
744                 *s = '\0';
745
746                 {
747                         char *dn = strchr( str, ':' );
748                         char *rule = NULL;
749
750                         if( dn == NULL ) {
751                                 if(! ldap_is_attr_desc( str ) ) goto done;
752                                 break;
753                         }
754
755                         *dn++ = '\0';
756                         rule = strchr( dn, ':' );
757
758                         if( rule == NULL ) {
759                                 /* one colon */
760                                 if ( strcmp(dn, "dn") == 0 ) {
761                                         /* must have attribute */
762                                         if( !ldap_is_attr_desc( str ) ) {
763                                                 goto done;
764                                         }
765
766                                         rule = "";
767
768                                 } else {
769                                         rule = dn;
770                                         dn = NULL;
771                                 }
772                                 
773                         } else {
774                                 /* two colons */
775                                 *rule++ = '\0';
776
777                                 if ( strcmp(dn, "dn") != 0 ) {
778                                         /* must have "dn" */
779                                         goto done;
780                                 }
781                         }
782
783                         if ( *str == '\0' && *rule == '\0' ) {
784                                 /* must have either type or rule */
785                                 goto done;
786                         }
787
788                         if ( *str != '\0' && !ldap_is_attr_desc( str ) ) {
789                                 goto done;
790                         }
791
792                         if ( *rule != '\0' && !ldap_is_attr_oid( rule ) ) {
793                                 goto done;
794                         }
795
796                         rc = ber_printf( ber, "t{" /*}*/, ftype );
797
798                         if( rc != -1 && *rule != '\0' ) {
799                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
800                         }
801                         if( rc != -1 && *str != '\0' ) {
802                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
803                         }
804
805                         if( rc != -1 ) {
806                                 ber_slen_t len = filter_value_unescape( value );
807
808                                 if( len >= 0 ) {
809                                         rc = ber_printf( ber, "totb}",
810                                                 LDAP_FILTER_EXT_VALUE, value, len,
811                                                 LDAP_FILTER_EXT_DNATTRS, dn != NULL);
812                                 } else {
813                                         rc = -1;
814                                 }
815                         }
816                 }
817                 break;
818
819         default:
820                 if ( find_wildcard( value ) == NULL ) {
821                         ftype = LDAP_FILTER_EQUALITY;
822                 } else if ( strcmp( value, "*" ) == 0 ) {
823                         ftype = LDAP_FILTER_PRESENT;
824                 } else {
825                         rc = put_substring_filter( ber, str, value );
826                         goto done;
827                 }
828                 break;
829         }
830
831         if ( ftype == LDAP_FILTER_PRESENT ) {
832                 rc = ber_printf( ber, "ts", ftype, str );
833
834         } else {
835                 ber_slen_t len = filter_value_unescape( value );
836
837                 if( len >= 0 ) {
838                         rc = ber_printf( ber, "t{so}",
839                                 ftype, str, value, len );
840                 }
841         }
842
843         if( rc != -1 ) rc = 0;
844
845 done:
846         LDAP_FREE( str );
847         return rc;
848 }
849
850 static int
851 put_substring_filter( BerElement *ber, char *type, char *val )
852 {
853         char            *nextstar, gotstar = 0;
854         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
855
856         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
857             val, 0 );
858
859         if ( ber_printf( ber, "t{s{", ftype, type ) == -1 )
860                 return( -1 );
861
862         for( ; val != NULL; val=nextstar ) {
863                 if ( (nextstar = find_wildcard( val )) != NULL )
864                         *nextstar++ = '\0';
865
866                 if ( gotstar == 0 ) {
867                         ftype = LDAP_SUBSTRING_INITIAL;
868                 } else if ( nextstar == NULL ) {
869                         ftype = LDAP_SUBSTRING_FINAL;
870                 } else {
871                         ftype = LDAP_SUBSTRING_ANY;
872                 }
873
874                 if ( *val != '\0' ) {
875                         ber_slen_t len = filter_value_unescape( val );
876
877                         if ( len < 0  ) {
878                                 return -1;
879                         }
880
881                         if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
882                                 return( -1 );
883                         }
884                 }
885
886                 gotstar = 1;
887         }
888
889         if ( ber_printf( ber, /* {{ */ "}}" ) == -1 )
890                 return( -1 );
891
892         return( 0 );
893 }
894
895 int
896 ldap_search_st(
897         LDAP *ld, LDAP_CONST char *base, int scope,
898         LDAP_CONST char *filter, char **attrs,
899         int attrsonly, struct timeval *timeout, LDAPMessage **res )
900 {
901         int     msgid;
902
903         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
904             == -1 )
905                 return( ld->ld_errno );
906
907         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
908                 return( ld->ld_errno );
909
910         if ( ld->ld_errno == LDAP_TIMEOUT ) {
911                 (void) ldap_abandon( ld, msgid );
912                 ld->ld_errno = LDAP_TIMEOUT;
913                 return( ld->ld_errno );
914         }
915
916         return( ldap_result2error( ld, *res, 0 ) );
917 }
918
919 int
920 ldap_search_s(
921         LDAP *ld,
922         LDAP_CONST char *base,
923         int scope,
924         LDAP_CONST char *filter,
925         char **attrs,
926         int attrsonly,
927         LDAPMessage **res )
928 {
929         int     msgid;
930
931         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
932             == -1 )
933                 return( ld->ld_errno );
934
935         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
936                 return( ld->ld_errno );
937
938         return( ldap_result2error( ld, *res, 0 ) );
939 }
940