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