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