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