]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
ITS#837: fix server down abandon bug
[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         BerElement      *ber;
95         int timelimit;
96
97         Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
98
99         /*
100          * if timeout is provided, both tv_sec and tv_usec must
101          * be non-zero
102          */
103         if( timeout != NULL ) {
104                 if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
105                         return LDAP_PARAM_ERROR;
106                 }
107
108                 /* timelimit must be non-zero if timeout is provided */
109                 timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
110
111         } else {
112                 /* no timeout, no timelimit */
113                 timelimit = -1;
114         }
115
116         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
117             attrsonly, sctrls, cctrls, timelimit, sizelimit ); 
118
119         if ( ber == NULL ) {
120                 return ld->ld_errno;
121         }
122
123 #ifndef LDAP_NOCACHE
124         if ( ld->ld_cache != NULL ) {
125                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
126                         ber_free( ber, 1 );
127                         ld->ld_errno = LDAP_SUCCESS;
128                         *msgidp = ld->ld_msgid;
129                         return ld->ld_errno;
130                 }
131                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
132         }
133 #endif /* LDAP_NOCACHE */
134
135         /* send the message */
136         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber );
137
138         if( *msgidp < 0 )
139                 return ld->ld_errno;
140
141         return LDAP_SUCCESS;
142 }
143
144 int
145 ldap_search_ext_s(
146         LDAP *ld,
147         LDAP_CONST char *base,
148         int scope,
149         LDAP_CONST char *filter,
150         char **attrs,
151         int attrsonly,
152         LDAPControl **sctrls,
153         LDAPControl **cctrls,
154         struct timeval *timeout,
155         int sizelimit,
156         LDAPMessage **res )
157 {
158         int rc;
159         int     msgid;
160
161         rc = ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,
162                 sctrls, cctrls, timeout, sizelimit, &msgid );
163
164         if ( rc != LDAP_SUCCESS ) {
165                 return( rc );
166         }
167
168         rc = ldap_result( ld, msgid, 1, timeout, res );
169
170         if( rc <= 0 ) {
171                 /* error(-1) or timeout(0) */
172                 return( ld->ld_errno );
173         }
174
175         if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_EXTENDED_PARTIAL ) {
176                 return( ld->ld_errno );
177         }
178
179         return( ldap_result2error( ld, *res, 0 ) );
180 }
181
182 /*
183  * ldap_search - initiate an ldap search operation.
184  *
185  * Parameters:
186  *
187  *      ld              LDAP descriptor
188  *      base            DN of the base object
189  *      scope           the search scope - one of LDAP_SCOPE_BASE,
190  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
191  *      filter          a string containing the search filter
192  *                      (e.g., "(|(cn=bob)(sn=bob))")
193  *      attrs           list of attribute types to return for matches
194  *      attrsonly       1 => attributes only 0 => attributes and values
195  *
196  * Example:
197  *      char    *attrs[] = { "mail", "title", 0 };
198  *      msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
199  *          attrs, attrsonly );
200  */
201 int
202 ldap_search(
203         LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
204         char **attrs, int attrsonly )
205 {
206         BerElement      *ber;
207
208         Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
209
210         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
211             attrsonly, NULL, NULL, -1, -1 ); 
212
213         if ( ber == NULL ) {
214                 return( -1 );
215         }
216
217 #ifndef LDAP_NOCACHE
218         if ( ld->ld_cache != NULL ) {
219                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
220                         ber_free( ber, 1 );
221                         ld->ld_errno = LDAP_SUCCESS;
222                         return( ld->ld_msgid );
223                 }
224                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
225         }
226 #endif /* LDAP_NOCACHE */
227
228         /* send the message */
229         return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
230 }
231
232
233 BerElement *
234 ldap_build_search_req(
235         LDAP *ld,
236         LDAP_CONST char *base,
237         ber_int_t scope,
238         LDAP_CONST char *filter_in,
239         char **attrs,
240         ber_int_t attrsonly,
241         LDAPControl **sctrls,
242         LDAPControl **cctrls,
243         ber_int_t timelimit,
244         ber_int_t sizelimit )
245 {
246         BerElement      *ber;
247         int             err;
248         char    *filter;
249
250         /*
251          * Create the search request.  It looks like this:
252          *      SearchRequest := [APPLICATION 3] SEQUENCE {
253          *              baseObject      DistinguishedName,
254          *              scope           ENUMERATED {
255          *                      baseObject      (0),
256          *                      singleLevel     (1),
257          *                      wholeSubtree    (2)
258          *              },
259          *              derefAliases    ENUMERATED {
260          *                      neverDerefaliases       (0),
261          *                      derefInSearching        (1),
262          *                      derefFindingBaseObj     (2),
263          *                      alwaysDerefAliases      (3)
264          *              },
265          *              sizelimit       INTEGER (0 .. 65535),
266          *              timelimit       INTEGER (0 .. 65535),
267          *              attrsOnly       BOOLEAN,
268          *              filter          Filter,
269          *              attributes      SEQUENCE OF AttributeType
270          *      }
271          * wrapped in an ldap message.
272          */
273
274         /* create a message to send */
275         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
276                 return( NULL );
277         }
278
279         if ( base == NULL ) {
280                 /* no base provided, use session default base */
281                 base = ld->ld_options.ldo_defbase;
282
283                 if ( base == NULL ) {
284                         /* no session default base, use top */
285                         base = "";
286                 }
287         }
288
289         err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
290                 LDAP_REQ_SEARCH, base, (ber_int_t) scope, ld->ld_deref,
291                 (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
292                 (timelimit < 0) ? ld->ld_timelimit : timelimit,
293                 attrsonly );
294
295         if ( err == -1 ) {
296                 ld->ld_errno = LDAP_ENCODING_ERROR;
297                 ber_free( ber, 1 );
298                 return( NULL );
299         }
300
301         if( filter_in != NULL ) {
302                 filter = LDAP_STRDUP( filter_in );
303         } else {
304                 filter = LDAP_STRDUP( "(objectclass=*)" );
305         }
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}N}", 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, /*{*/ "N}" ) == -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 ( const char *attr )
337 {
338         int i, c, digit=0;
339
340         for( i = 0; (c = attr[i]) != 0; 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 static int ldap_is_attr_desc ( const char *attr )
362 {
363         /* cheap attribute description check */
364         int i, c;
365
366         for( i = 0; (c = attr[i]) != 0; i++ ) {
367                 if (( c >= '0' && c <= '9' )
368                         || ( c >= 'A' && c <= 'Z' )
369                         || ( c >= 'a' && c <= 'z' )
370                         || ( c == '.' || c == '-' )
371                         || ( c == ';' )) continue;
372
373                 return 0;
374         }
375
376         return i > 0;
377 }
378
379 static char *
380 find_right_paren( char *s )
381 {
382         int     balance, escape;
383
384         balance = 1;
385         escape = 0;
386         while ( *s && balance ) {
387                 if ( escape == 0 ) {
388                         if ( *s == '(' )
389                                 balance++;
390                         else if ( *s == ')' )
391                                 balance--;
392                 }
393                 if ( *s == '\\' && ! escape )
394                         escape = 1;
395                 else
396                         escape = 0;
397                 if ( balance )
398                         s++;
399         }
400
401         return( *s ? s : NULL );
402 }
403
404 static int hex2value( int c )
405 {
406         if( c >= '0' && c <= '9' ) {
407                 return c - '0';
408         }
409
410         if( c >= 'A' && c <= 'F' ) {
411                 return c + (10 - (int) 'A');
412         }
413
414         if( c >= 'a' && c <= 'f' ) {
415                 return c + (10 - (int) 'a');
416         }
417
418         return -1;
419 }
420
421 char *
422 ldap_pvt_find_wildcard( const char *s )
423 {
424         for( ; *s != '\0' ; s++ ) {
425                 switch( *s ) {
426                 case '*':       /* found wildcard */
427                         return (char *) s;
428
429                 case '\\':
430                         s++; /* skip over escape */
431                         if ( *s == '\0' )
432                                 return NULL;    /* escape at end of string */
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 ber_slen_t
443 ldap_pvt_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, /*{*/ "N}" ) == -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
567                         /* skip spaces */
568                         while( LDAP_SPACE( *str ) ) str++;
569
570                         switch ( *str ) {
571                         case '&':
572                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
573                                     0, 0, 0 );
574
575                                 if ( (str = put_complex_filter( ber, str,
576                                     LDAP_FILTER_AND, 0 )) == NULL )
577                                         return( -1 );
578
579                                 parens--;
580                                 break;
581
582                         case '|':
583                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
584                                     0, 0, 0 );
585
586                                 if ( (str = put_complex_filter( ber, str,
587                                     LDAP_FILTER_OR, 0 )) == NULL )
588                                         return( -1 );
589
590                                 parens--;
591                                 break;
592
593                         case '!':
594                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
595                                     0, 0, 0 );
596
597                                 if ( (str = put_complex_filter( ber, str,
598                                     LDAP_FILTER_NOT, 1 )) == NULL )
599                                         return( -1 );
600
601                                 parens--;
602                                 break;
603
604                         default:
605                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
606                                     0, 0, 0 );
607
608                                 balance = 1;
609                                 escape = 0;
610                                 next = str;
611                                 while ( *next && balance ) {
612                                         if ( escape == 0 ) {
613                                                 if ( *next == '(' )
614                                                         balance++;
615                                                 else if ( *next == ')' )
616                                                         balance--;
617                                         }
618                                         if ( *next == '\\' && ! escape )
619                                                 escape = 1;
620                                         else
621                                                 escape = 0;
622                                         if ( balance )
623                                                 next++;
624                                 }
625                                 if ( balance != 0 )
626                                         return( -1 );
627
628                                 *next = '\0';
629                                 if ( put_simple_filter( ber, str ) == -1 ) {
630                                         return( -1 );
631                                 }
632                                 *next++ = ')';
633                                 str = next;
634                                 parens--;
635                                 break;
636                         }
637                         break;
638
639                 case ')':
640                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
641                             0 );
642                         if ( ber_printf( ber, /*[*/ "]" ) == -1 )
643                                 return( -1 );
644                         str++;
645                         parens--;
646                         break;
647
648                 case ' ':
649                         str++;
650                         break;
651
652                 default:        /* assume it's a simple type=value filter */
653                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
654                             0 );
655                         next = strchr( str, '\0' );
656                         if ( put_simple_filter( ber, str ) == -1 ) {
657                                 return( -1 );
658                         }
659                         str = next;
660                         break;
661                 }
662         }
663
664         return( parens ? -1 : 0 );
665 }
666
667 /*
668  * Put a list of filters like this "(filter1)(filter2)..."
669  */
670
671 static int
672 put_filter_list( BerElement *ber, char *str )
673 {
674         char    *next;
675         char    save;
676
677         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
678
679         while ( *str ) {
680                 while ( *str && LDAP_SPACE( (unsigned char) *str ) )
681                         str++;
682                 if ( *str == '\0' )
683                         break;
684
685                 if ( (next = find_right_paren( str + 1 )) == NULL )
686                         return( -1 );
687                 save = *++next;
688
689                 /* now we have "(filter)" with str pointing to it */
690                 *next = '\0';
691                 if ( put_filter( ber, str ) == -1 )
692                         return( -1 );
693                 *next = save;
694
695                 str = next;
696         }
697
698         return( 0 );
699 }
700
701 static int
702 put_simple_filter(
703         BerElement *ber,
704         char *str )
705 {
706         char            *s;
707         char            *value;
708         ber_tag_t       ftype;
709         int             rc = -1;
710
711         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
712
713         str = LDAP_STRDUP( str );
714         if( str == NULL ) return -1;
715
716         if ( (s = strchr( str, '=' )) == NULL ) {
717                 goto done;
718         }
719
720         value = s + 1;
721         *s-- = '\0';
722
723         switch ( *s ) {
724         case '<':
725                 ftype = LDAP_FILTER_LE;
726                 *s = '\0';
727                 if(! ldap_is_attr_desc( str ) ) goto done;
728                 break;
729
730         case '>':
731                 ftype = LDAP_FILTER_GE;
732                 *s = '\0';
733                 if(! ldap_is_attr_desc( str ) ) goto done;
734                 break;
735
736         case '~':
737                 ftype = LDAP_FILTER_APPROX;
738                 *s = '\0';
739                 if(! ldap_is_attr_desc( str ) ) goto done;
740                 break;
741
742         case ':':
743                 /* RFC2254 extensible filters are off the form:
744                  *              type [:dn] [:rule] := value
745                  * or   [:dn]:rule := value             
746                  */
747                 ftype = LDAP_FILTER_EXT;
748                 *s = '\0';
749
750                 {
751                         char *dn = strchr( str, ':' );
752                         char *rule = NULL;
753
754                         if( dn == NULL ) {
755                                 if(! ldap_is_attr_desc( str ) ) goto done;
756                                 break;
757                         }
758
759                         *dn++ = '\0';
760                         rule = strchr( dn, ':' );
761
762                         if( rule == NULL ) {
763                                 /* one colon */
764                                 if ( strcmp(dn, "dn") == 0 ) {
765                                         /* must have attribute */
766                                         if( !ldap_is_attr_desc( str ) ) {
767                                                 goto done;
768                                         }
769
770                                         rule = "";
771
772                                 } else {
773                                         rule = dn;
774                                         dn = NULL;
775                                 }
776                                 
777                         } else {
778                                 /* two colons */
779                                 *rule++ = '\0';
780
781                                 if ( strcmp(dn, "dn") != 0 ) {
782                                         /* must have "dn" */
783                                         goto done;
784                                 }
785                         }
786
787                         if ( *str == '\0' && *rule == '\0' ) {
788                                 /* must have either type or rule */
789                                 goto done;
790                         }
791
792                         if ( *str != '\0' && !ldap_is_attr_desc( str ) ) {
793                                 goto done;
794                         }
795
796                         if ( *rule != '\0' && !ldap_is_attr_oid( rule ) ) {
797                                 goto done;
798                         }
799
800                         rc = ber_printf( ber, "t{" /*}*/, ftype );
801
802                         if( rc != -1 && *rule != '\0' ) {
803                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
804                         }
805                         if( rc != -1 && *str != '\0' ) {
806                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
807                         }
808
809                         if( rc != -1 ) {
810                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
811
812                                 if( len >= 0 ) {
813                                         rc = ber_printf( ber, "totbN}",
814                                                 LDAP_FILTER_EXT_VALUE, value, len,
815                                                 LDAP_FILTER_EXT_DNATTRS, dn != NULL);
816                                 } else {
817                                         rc = -1;
818                                 }
819                         }
820                 }
821                 break;
822
823         default:
824                 if ( ldap_pvt_find_wildcard( value ) == NULL ) {
825                         ftype = LDAP_FILTER_EQUALITY;
826                 } else if ( strcmp( value, "*" ) == 0 ) {
827                         ftype = LDAP_FILTER_PRESENT;
828                 } else {
829                         rc = put_substring_filter( ber, str, value );
830                         goto done;
831                 }
832                 break;
833         }
834
835         if ( ftype == LDAP_FILTER_PRESENT ) {
836                 rc = ber_printf( ber, "ts", ftype, str );
837
838         } else {
839                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
840
841                 if( len >= 0 ) {
842                         rc = ber_printf( ber, "t{soN}",
843                                 ftype, str, value, len );
844                 }
845         }
846
847         if( rc != -1 ) rc = 0;
848
849 done:
850         LDAP_FREE( str );
851         return rc;
852 }
853
854 static int
855 put_substring_filter( BerElement *ber, char *type, char *val )
856 {
857         char            *nextstar, gotstar = 0;
858         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
859
860         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
861             val, 0 );
862
863         if ( ber_printf( ber, "t{s{", ftype, type ) == -1 )
864                 return( -1 );
865
866         for( ; val != NULL; val=nextstar ) {
867                 if ( (nextstar = ldap_pvt_find_wildcard( val )) != NULL )
868                         *nextstar++ = '\0';
869
870                 if ( gotstar == 0 ) {
871                         ftype = LDAP_SUBSTRING_INITIAL;
872                 } else if ( nextstar == NULL ) {
873                         ftype = LDAP_SUBSTRING_FINAL;
874                 } else {
875                         ftype = LDAP_SUBSTRING_ANY;
876                 }
877
878                 if ( *val != '\0' ) {
879                         ber_slen_t len = ldap_pvt_filter_value_unescape( val );
880
881                         if ( len < 0  ) {
882                                 return -1;
883                         }
884
885                         if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
886                                 return( -1 );
887                         }
888                 }
889
890                 gotstar = 1;
891         }
892
893         if ( ber_printf( ber, /* {{ */ "N}N}" ) == -1 )
894                 return( -1 );
895
896         return( 0 );
897 }
898
899 int
900 ldap_search_st(
901         LDAP *ld, LDAP_CONST char *base, int scope,
902         LDAP_CONST char *filter, char **attrs,
903         int attrsonly, struct timeval *timeout, LDAPMessage **res )
904 {
905         int     msgid;
906
907         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
908             == -1 )
909                 return( ld->ld_errno );
910
911         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
912                 return( ld->ld_errno );
913
914         if ( ld->ld_errno == LDAP_TIMEOUT ) {
915                 (void) ldap_abandon( ld, msgid );
916                 ld->ld_errno = LDAP_TIMEOUT;
917                 return( ld->ld_errno );
918         }
919
920         return( ldap_result2error( ld, *res, 0 ) );
921 }
922
923 int
924 ldap_search_s(
925         LDAP *ld,
926         LDAP_CONST char *base,
927         int scope,
928         LDAP_CONST char *filter,
929         char **attrs,
930         int attrsonly,
931         LDAPMessage **res )
932 {
933         int     msgid;
934
935         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
936             == -1 )
937                 return( ld->ld_errno );
938
939         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
940                 return( ld->ld_errno );
941
942         return( ldap_result2error( ld, *res, 0 ) );
943 }
944