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