]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
strlen returns size_t not int.
[openldap] / libraries / libldap / search.c
1 /*
2  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*  Portions
6  *  Copyright (c) 1990 Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  search.c
10  */
11
12 #include "portable.h"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include <ac/ctype.h>
18 #include <ac/socket.h>
19 #include <ac/string.h>
20 #include <ac/time.h>
21
22 #include "ldap-int.h"
23
24 static char *find_right_paren LDAP_P(( char *s ));
25 static char *put_complex_filter LDAP_P(( BerElement *ber, char *str,
26         unsigned long tag, int not ));
27 static int put_filter LDAP_P(( BerElement *ber, char *str ));
28 static int put_simple_filter LDAP_P(( BerElement *ber, char *str ));
29 static int put_substring_filter LDAP_P(( BerElement *ber, char *type, char *str ));
30 static int put_filter_list LDAP_P(( BerElement *ber, char *str ));
31
32 /*
33  * ldap_search_ext - initiate an ldap search operation.
34  *
35  * Parameters:
36  *
37  *      ld              LDAP descriptor
38  *      base            DN of the base object
39  *      scope           the search scope - one of LDAP_SCOPE_BASE,
40  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
41  *      filter          a string containing the search filter
42  *                      (e.g., "(|(cn=bob)(sn=bob))")
43  *      attrs           list of attribute types to return for matches
44  *      attrsonly       1 => attributes only 0 => attributes and values
45  *
46  * Example:
47  *      char    *attrs[] = { "mail", "title", 0 };
48  *      ldap_search_ext( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
49  *          attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
50  *              &msgid );
51  */
52 int
53 ldap_search_ext(
54         LDAP *ld,
55         LDAP_CONST char *base,
56         int scope,
57         LDAP_CONST char *filter,
58         char **attrs,
59         int attrsonly,
60         LDAPControl **sctrls,
61         LDAPControl **cctrls,
62         struct timeval *timeout,
63         int sizelimit,
64         int *msgidp )
65 {
66         BerElement      *ber;
67         int timelimit;
68
69         Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
70
71         /*
72          * if timeout is provided, use only tv_sec as timelimit.
73          * otherwise, use default.
74          */
75         timelimit = (timeout != NULL)
76                         ?  timelimit = timeout->tv_sec
77                         : -1;
78
79         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
80             attrsonly, sctrls, cctrls, timelimit, sizelimit ); 
81
82         if ( ber == NULLBER ) {
83                 return ld->ld_errno;
84         }
85
86 #ifndef LDAP_NOCACHE
87         if ( ld->ld_cache != NULL ) {
88                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
89                         ber_free( ber, 1 );
90                         ld->ld_errno = LDAP_SUCCESS;
91                         *msgidp = ld->ld_msgid;
92                         return ld->ld_errno;
93                 }
94                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
95         }
96 #endif /* LDAP_NOCACHE */
97
98         /* send the message */
99         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber );
100
101         if( *msgidp < 0 )
102                 return ld->ld_errno;
103
104         return LDAP_SUCCESS;
105 }
106
107 int
108 ldap_search_ext_s(
109         LDAP *ld,
110         LDAP_CONST char *base,
111         int scope,
112         LDAP_CONST char *filter,
113         char **attrs,
114         int attrsonly,
115         LDAPControl **sctrls,
116         LDAPControl **cctrls,
117         struct timeval *timeout,
118         int sizelimit,
119         LDAPMessage **res )
120 {
121         int rc;
122         int     msgid;
123
124         rc = ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,
125                 sctrls, cctrls, timeout, sizelimit, &msgid );
126
127         if ( rc != LDAP_SUCCESS ) {
128                 return( rc );
129         }
130
131         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
132                 return( ld->ld_errno );
133
134         return( ldap_result2error( ld, *res, 0 ) );
135 }
136
137 /*
138  * ldap_search - initiate an ldap search operation.
139  *
140  * Parameters:
141  *
142  *      ld              LDAP descriptor
143  *      base            DN of the base object
144  *      scope           the search scope - one of LDAP_SCOPE_BASE,
145  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
146  *      filter          a string containing the search filter
147  *                      (e.g., "(|(cn=bob)(sn=bob))")
148  *      attrs           list of attribute types to return for matches
149  *      attrsonly       1 => attributes only 0 => attributes and values
150  *
151  * Example:
152  *      char    *attrs[] = { "mail", "title", 0 };
153  *      msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
154  *          attrs, attrsonly );
155  */
156 int
157 ldap_search(
158         LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
159         char **attrs, int attrsonly )
160 {
161         BerElement      *ber;
162
163         Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
164
165         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
166             attrsonly, NULL, NULL, -1, -1 ); 
167
168         if ( ber == NULLBER ) {
169                 return( -1 );
170         }
171
172 #ifndef LDAP_NOCACHE
173         if ( ld->ld_cache != NULL ) {
174                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
175                         ber_free( ber, 1 );
176                         ld->ld_errno = LDAP_SUCCESS;
177                         return( ld->ld_msgid );
178                 }
179                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
180         }
181 #endif /* LDAP_NOCACHE */
182
183         /* send the message */
184         return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
185 }
186
187
188 BerElement *
189 ldap_build_search_req(
190         LDAP *ld,
191         LDAP_CONST char *base_in,
192         int scope,
193         LDAP_CONST char *filter_in,
194         char **attrs,
195         int attrsonly,
196         LDAPControl **sctrls,
197         LDAPControl **cctrls,
198         int timelimit,
199         int sizelimit )
200 {
201         BerElement      *ber;
202         int             err;
203         char    *base;
204         char    *filter;
205
206         /*
207          * Create the search request.  It looks like this:
208          *      SearchRequest := [APPLICATION 3] SEQUENCE {
209          *              baseObject      DistinguishedName,
210          *              scope           ENUMERATED {
211          *                      baseObject      (0),
212          *                      singleLevel     (1),
213          *                      wholeSubtree    (2)
214          *              },
215          *              derefAliases    ENUMERATED {
216          *                      neverDerefaliases       (0),
217          *                      derefInSearching        (1),
218          *                      derefFindingBaseObj     (2),
219          *                      alwaysDerefAliases      (3)
220          *              },
221          *              sizelimit       INTEGER (0 .. 65535),
222          *              timelimit       INTEGER (0 .. 65535),
223          *              attrsOnly       BOOLEAN,
224          *              filter          Filter,
225          *              attributes      SEQUENCE OF AttributeType
226          *      }
227          * wrapped in an ldap message.
228          */
229
230         /* create a message to send */
231         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULLBER ) {
232                 return( NULLBER );
233         }
234
235         if ( base_in == NULL ) {
236                 /* no base provided, use session default base */
237                 base = ld->ld_options.ldo_defbase;
238         } else {
239                 base = (char *) base_in;
240         }
241
242         if ( base == NULL ) {
243                 /* no session default base, use top */
244             base = "";
245         }
246
247 #ifdef LDAP_CONNECTIONLESS
248         if ( ld->ld_cldapnaddr > 0 ) {
249             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
250                         ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
251                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
252                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
253                         attrsonly );
254         } else {
255 #endif /* LDAP_CONNECTIONLESS */
256                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
257                     LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
258                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
259                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
260                     attrsonly );
261 #ifdef LDAP_CONNECTIONLESS
262         }
263 #endif /* LDAP_CONNECTIONLESS */
264
265         if ( err == -1 ) {
266                 ld->ld_errno = LDAP_ENCODING_ERROR;
267                 ber_free( ber, 1 );
268                 return( NULLBER );
269         }
270
271         filter = strdup( filter_in );
272         err = put_filter( ber, filter );
273         free( filter );
274
275         if ( err  == -1 ) {
276                 ld->ld_errno = LDAP_FILTER_ERROR;
277                 ber_free( ber, 1 );
278                 return( NULLBER );
279         }
280
281         if ( ber_printf( ber, "{v}}", attrs ) == -1 ) {
282                 ld->ld_errno = LDAP_ENCODING_ERROR;
283                 ber_free( ber, 1 );
284                 return( NULLBER );
285         }
286
287         /* Put Server Controls */
288         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
289                 ber_free( ber, 1 );
290                 return( NULLBER );
291         }
292
293         if ( ber_printf( ber, "}", attrs ) == -1 ) {
294                 ld->ld_errno = LDAP_ENCODING_ERROR;
295                 ber_free( ber, 1 );
296                 return( NULLBER );
297         }
298
299         return( ber );
300 }
301
302 static char *
303 find_right_paren( char *s )
304 {
305         int     balance, escape;
306
307         balance = 1;
308         escape = 0;
309         while ( *s && balance ) {
310                 if ( escape == 0 ) {
311                         if ( *s == '(' )
312                                 balance++;
313                         else if ( *s == ')' )
314                                 balance--;
315                 }
316                 if ( *s == '\\' && ! escape )
317                         escape = 1;
318                 else
319                         escape = 0;
320                 if ( balance )
321                         s++;
322         }
323
324         return( *s ? s : NULL );
325 }
326
327 static char *
328 put_complex_filter( BerElement *ber, char *str, unsigned long tag, int not )
329 {
330         char    *next;
331
332         /*
333          * We have (x(filter)...) with str sitting on
334          * the x.  We have to find the paren matching
335          * the one before the x and put the intervening
336          * filters by calling put_filter_list().
337          */
338
339         /* put explicit tag */
340         if ( ber_printf( ber, "t{", tag ) == -1 )
341                 return( NULL );
342
343 #if 0
344         if ( !not && ber_printf( ber, "{" ) == -1 )
345                 return( NULL );
346 #endif
347
348         str++;
349         if ( (next = find_right_paren( str )) == NULL )
350                 return( NULL );
351
352         *next = '\0';
353         if ( put_filter_list( ber, str ) == -1 )
354                 return( NULL );
355         *next++ = ')';
356
357         /* flush explicit tagged thang */
358         if ( ber_printf( ber, "}" ) == -1 )
359                 return( NULL );
360
361 #if 0
362         if ( !not && ber_printf( ber, "}" ) == -1 )
363                 return( NULL );
364 #endif
365
366         return( next );
367 }
368
369 static int
370 put_filter( BerElement *ber, char *str )
371 {
372         char    *next, *tmp, *s, *d;
373         int     parens, balance, escape, gotescape;
374
375         /*
376          * A Filter looks like this:
377          *      Filter ::= CHOICE {
378          *              and             [0]     SET OF Filter,
379          *              or              [1]     SET OF Filter,
380          *              not             [2]     Filter,
381          *              equalityMatch   [3]     AttributeValueAssertion,
382          *              substrings      [4]     SubstringFilter,
383          *              greaterOrEqual  [5]     AttributeValueAssertion,
384          *              lessOrEqual     [6]     AttributeValueAssertion,
385          *              present         [7]     AttributeType,,
386          *              approxMatch     [8]     AttributeValueAssertion
387          *      }
388          *
389          *      SubstringFilter ::= SEQUENCE {
390          *              type               AttributeType,
391          *              SEQUENCE OF CHOICE {
392          *                      initial          [0] IA5String,
393          *                      any              [1] IA5String,
394          *                      final            [2] IA5String
395          *              }
396          *      }
397          * Note: tags in a choice are always explicit
398          */
399
400         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
401
402         gotescape = parens = 0;
403         while ( *str ) {
404                 switch ( *str ) {
405                 case '(':
406                         str++;
407                         parens++;
408                         switch ( *str ) {
409                         case '&':
410                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
411                                     0, 0, 0 );
412
413                                 if ( (str = put_complex_filter( ber, str,
414                                     LDAP_FILTER_AND, 0 )) == NULL )
415                                         return( -1 );
416
417                                 parens--;
418                                 break;
419
420                         case '|':
421                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
422                                     0, 0, 0 );
423
424                                 if ( (str = put_complex_filter( ber, str,
425                                     LDAP_FILTER_OR, 0 )) == NULL )
426                                         return( -1 );
427
428                                 parens--;
429                                 break;
430
431                         case '!':
432                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
433                                     0, 0, 0 );
434
435                                 if ( (str = put_complex_filter( ber, str,
436                                     LDAP_FILTER_NOT, 1 )) == NULL )
437                                         return( -1 );
438
439                                 parens--;
440                                 break;
441
442                         default:
443                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
444                                     0, 0, 0 );
445
446                                 balance = 1;
447                                 escape = 0;
448                                 next = str;
449                                 while ( *next && balance ) {
450                                         if ( escape == 0 ) {
451                                                 if ( *next == '(' )
452                                                         balance++;
453                                                 else if ( *next == ')' )
454                                                         balance--;
455                                         }
456                                         if ( *next == '\\' && ! escape )
457                                                 gotescape = escape = 1;
458                                         else
459                                                 escape = 0;
460                                         if ( balance )
461                                                 next++;
462                                 }
463                                 if ( balance != 0 )
464                                         return( -1 );
465
466                                 *next = '\0';
467                                 tmp = strdup( str );
468                                 if ( gotescape ) {
469                                         escape = 0;
470                                         for ( s = d = tmp; *s; s++ ) {
471                                                 if ( *s != '\\' || escape ) {
472                                                         *d++ = *s;
473                                                         escape = 0;
474                                                 } else {
475                                                         escape = 1;
476                                                 }
477                                         }
478                                         *d = '\0';
479                                 }
480                                 if ( put_simple_filter( ber, tmp ) == -1 ) {
481                                         free( tmp );
482                                         return( -1 );
483                                 }
484                                 free( tmp );
485                                 *next++ = ')';
486                                 str = next;
487                                 parens--;
488                                 break;
489                         }
490                         break;
491
492                 case ')':
493                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
494                             0 );
495                         if ( ber_printf( ber, "]" ) == -1 )
496                                 return( -1 );
497                         str++;
498                         parens--;
499                         break;
500
501                 case ' ':
502                         str++;
503                         break;
504
505                 default:        /* assume it's a simple type=value filter */
506                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
507                             0 );
508                         next = strchr( str, '\0' );
509                         tmp = strdup( str );
510                         if ( strchr( tmp, '\\' ) != NULL ) {
511                                 escape = 0;
512                                 for ( s = d = tmp; *s; s++ ) {
513                                         if ( *s != '\\' || escape ) {
514                                                 *d++ = *s;
515                                                 escape = 0;
516                                         } else {
517                                                 escape = 1;
518                                         }
519                                 }
520                                 *d = '\0';
521                         }
522                         if ( put_simple_filter( ber, tmp ) == -1 ) {
523                                 free( tmp );
524                                 return( -1 );
525                         }
526                         free( tmp );
527                         str = next;
528                         break;
529                 }
530         }
531
532         return( parens ? -1 : 0 );
533 }
534
535 /*
536  * Put a list of filters like this "(filter1)(filter2)..."
537  */
538
539 static int
540 put_filter_list( BerElement *ber, char *str )
541 {
542         char    *next;
543         char    save;
544
545         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
546
547         while ( *str ) {
548                 while ( *str && isspace( (unsigned char) *str ) )
549                         str++;
550                 if ( *str == '\0' )
551                         break;
552
553                 if ( (next = find_right_paren( str + 1 )) == NULL )
554                         return( -1 );
555                 save = *++next;
556
557                 /* now we have "(filter)" with str pointing to it */
558                 *next = '\0';
559                 if ( put_filter( ber, str ) == -1 )
560                         return( -1 );
561                 *next = save;
562
563                 str = next;
564         }
565
566         return( 0 );
567 }
568
569 static int
570 put_simple_filter( BerElement *ber, char *str )
571 {
572         char            *s;
573         char            *value, savechar;
574         unsigned long   ftype;
575         int             rc;
576
577         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
578
579         if ( (s = strchr( str, '=' )) == NULL )
580                 return( -1 );
581         value = s + 1;
582         *s-- = '\0';
583         savechar = *s;
584
585         switch ( *s ) {
586         case '<':
587                 ftype = LDAP_FILTER_LE;
588                 *s = '\0';
589                 break;
590         case '>':
591                 ftype = LDAP_FILTER_GE;
592                 *s = '\0';
593                 break;
594         case '~':
595                 ftype = LDAP_FILTER_APPROX;
596                 *s = '\0';
597                 break;
598         default:
599                 if ( strchr( value, '*' ) == NULL ) {
600                         ftype = LDAP_FILTER_EQUALITY;
601                 } else if ( strcmp( value, "*" ) == 0 ) {
602                         ftype = LDAP_FILTER_PRESENT;
603                 } else {
604                         rc = put_substring_filter( ber, str, value );
605                         *(value-1) = '=';
606                         return( rc );
607                 }
608                 break;
609         }
610
611         if ( ftype == LDAP_FILTER_PRESENT ) {
612                 rc = ber_printf( ber, "ts", ftype, str );
613         } else {
614                 rc = ber_printf( ber, "t{ss}", ftype, str, value );
615         }
616
617         *s = savechar;
618         *(value-1) = '=';
619         return( rc == -1 ? rc : 0 );
620 }
621
622 static int
623 put_substring_filter( BerElement *ber, char *type, char *val )
624 {
625         char            *nextstar, gotstar = 0;
626         unsigned long   ftype;
627
628         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
629             val, 0 );
630
631         if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
632                 return( -1 );
633
634         while ( val != NULL ) {
635                 if ( (nextstar = strchr( val, '*' )) != NULL )
636                         *nextstar++ = '\0';
637
638                 if ( gotstar == 0 ) {
639                         ftype = LDAP_SUBSTRING_INITIAL;
640                 } else if ( nextstar == NULL ) {
641                         ftype = LDAP_SUBSTRING_FINAL;
642                 } else {
643                         ftype = LDAP_SUBSTRING_ANY;
644                 }
645                 if ( *val != '\0' ) {
646                         if ( ber_printf( ber, "ts", ftype, val ) == -1 )
647                                 return( -1 );
648                 }
649
650                 gotstar = 1;
651                 if ( nextstar != NULL )
652                         *(nextstar-1) = '*';
653                 val = nextstar;
654         }
655
656         if ( ber_printf( ber, "}}" ) == -1 )
657                 return( -1 );
658
659         return( 0 );
660 }
661
662 int
663 ldap_search_st(
664         LDAP *ld, LDAP_CONST char *base, int scope,
665         LDAP_CONST char *filter, char **attrs,
666         int attrsonly, struct timeval *timeout, LDAPMessage **res )
667 {
668         int     msgid;
669
670         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
671             == -1 )
672                 return( ld->ld_errno );
673
674         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
675                 return( ld->ld_errno );
676
677         if ( ld->ld_errno == LDAP_TIMEOUT ) {
678                 (void) ldap_abandon( ld, msgid );
679                 ld->ld_errno = LDAP_TIMEOUT;
680                 return( ld->ld_errno );
681         }
682
683         return( ldap_result2error( ld, *res, 0 ) );
684 }
685
686 int
687 ldap_search_s(
688         LDAP *ld,
689         LDAP_CONST char *base,
690         int scope,
691         LDAP_CONST char *filter,
692         char **attrs,
693         int attrsonly,
694         LDAPMessage **res )
695 {
696         int     msgid;
697
698         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
699             == -1 )
700                 return( ld->ld_errno );
701
702         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
703                 return( ld->ld_errno );
704
705         return( ldap_result2error( ld, *res, 0 ) );
706 }
707