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