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