]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
s/<stdlib.h>/<ac/stdlib.h>/
[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          *      }
389          *
390          *      SubstringFilter ::= SEQUENCE {
391          *              type               AttributeType,
392          *              SEQUENCE OF CHOICE {
393          *                      initial          [0] IA5String,
394          *                      any              [1] IA5String,
395          *                      final            [2] IA5String
396          *              }
397          *      }
398          * Note: tags in a choice are always explicit
399          */
400
401         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
402
403         gotescape = parens = 0;
404         while ( *str ) {
405                 switch ( *str ) {
406                 case '(':
407                         str++;
408                         parens++;
409                         switch ( *str ) {
410                         case '&':
411                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
412                                     0, 0, 0 );
413
414                                 if ( (str = put_complex_filter( ber, str,
415                                     LDAP_FILTER_AND, 0 )) == NULL )
416                                         return( -1 );
417
418                                 parens--;
419                                 break;
420
421                         case '|':
422                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
423                                     0, 0, 0 );
424
425                                 if ( (str = put_complex_filter( ber, str,
426                                     LDAP_FILTER_OR, 0 )) == NULL )
427                                         return( -1 );
428
429                                 parens--;
430                                 break;
431
432                         case '!':
433                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
434                                     0, 0, 0 );
435
436                                 if ( (str = put_complex_filter( ber, str,
437                                     LDAP_FILTER_NOT, 1 )) == NULL )
438                                         return( -1 );
439
440                                 parens--;
441                                 break;
442
443                         default:
444                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
445                                     0, 0, 0 );
446
447                                 balance = 1;
448                                 escape = 0;
449                                 next = str;
450                                 while ( *next && balance ) {
451                                         if ( escape == 0 ) {
452                                                 if ( *next == '(' )
453                                                         balance++;
454                                                 else if ( *next == ')' )
455                                                         balance--;
456                                         }
457                                         if ( *next == '\\' && ! escape )
458                                                 gotescape = escape = 1;
459                                         else
460                                                 escape = 0;
461                                         if ( balance )
462                                                 next++;
463                                 }
464                                 if ( balance != 0 )
465                                         return( -1 );
466
467                                 *next = '\0';
468                                 tmp = LDAP_STRDUP( str );
469                                 if ( gotescape ) {
470                                         escape = 0;
471                                         for ( s = d = tmp; *s; s++ ) {
472                                                 if ( *s != '\\' || escape ) {
473                                                         *d++ = *s;
474                                                         escape = 0;
475                                                 } else {
476                                                         escape = 1;
477                                                 }
478                                         }
479                                         *d = '\0';
480                                 }
481                                 if ( put_simple_filter( ber, tmp ) == -1 ) {
482                                         LDAP_FREE( tmp );
483                                         return( -1 );
484                                 }
485                                 LDAP_FREE( tmp );
486                                 *next++ = ')';
487                                 str = next;
488                                 parens--;
489                                 break;
490                         }
491                         break;
492
493                 case ')':
494                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
495                             0 );
496                         if ( ber_printf( ber, "]" ) == -1 )
497                                 return( -1 );
498                         str++;
499                         parens--;
500                         break;
501
502                 case ' ':
503                         str++;
504                         break;
505
506                 default:        /* assume it's a simple type=value filter */
507                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
508                             0 );
509                         next = strchr( str, '\0' );
510                         tmp = LDAP_STRDUP( str );
511                         if ( strchr( tmp, '\\' ) != NULL ) {
512                                 escape = 0;
513                                 for ( s = d = tmp; *s; s++ ) {
514                                         if ( *s != '\\' || escape ) {
515                                                 *d++ = *s;
516                                                 escape = 0;
517                                         } else {
518                                                 escape = 1;
519                                         }
520                                 }
521                                 *d = '\0';
522                         }
523                         if ( put_simple_filter( ber, tmp ) == -1 ) {
524                                 LDAP_FREE( tmp );
525                                 return( -1 );
526                         }
527                         LDAP_FREE( tmp );
528                         str = next;
529                         break;
530                 }
531         }
532
533         return( parens ? -1 : 0 );
534 }
535
536 /*
537  * Put a list of filters like this "(filter1)(filter2)..."
538  */
539
540 static int
541 put_filter_list( BerElement *ber, char *str )
542 {
543         char    *next;
544         char    save;
545
546         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
547
548         while ( *str ) {
549                 while ( *str && isspace( (unsigned char) *str ) )
550                         str++;
551                 if ( *str == '\0' )
552                         break;
553
554                 if ( (next = find_right_paren( str + 1 )) == NULL )
555                         return( -1 );
556                 save = *++next;
557
558                 /* now we have "(filter)" with str pointing to it */
559                 *next = '\0';
560                 if ( put_filter( ber, str ) == -1 )
561                         return( -1 );
562                 *next = save;
563
564                 str = next;
565         }
566
567         return( 0 );
568 }
569
570 static int
571 put_simple_filter( BerElement *ber, char *str )
572 {
573         char            *s;
574         char            *value, savechar;
575         unsigned long   ftype;
576         int             rc;
577
578         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
579
580         if ( (s = strchr( str, '=' )) == NULL )
581                 return( -1 );
582         value = s + 1;
583         *s-- = '\0';
584         savechar = *s;
585
586         switch ( *s ) {
587         case '<':
588                 ftype = LDAP_FILTER_LE;
589                 *s = '\0';
590                 break;
591         case '>':
592                 ftype = LDAP_FILTER_GE;
593                 *s = '\0';
594                 break;
595         case '~':
596                 ftype = LDAP_FILTER_APPROX;
597                 *s = '\0';
598                 break;
599         default:
600                 if ( strchr( value, '*' ) == NULL ) {
601                         ftype = LDAP_FILTER_EQUALITY;
602                 } else if ( strcmp( value, "*" ) == 0 ) {
603                         ftype = LDAP_FILTER_PRESENT;
604                 } else {
605                         rc = put_substring_filter( ber, str, value );
606                         *(value-1) = '=';
607                         return( rc );
608                 }
609                 break;
610         }
611
612         if ( ftype == LDAP_FILTER_PRESENT ) {
613                 rc = ber_printf( ber, "ts", ftype, str );
614         } else {
615                 rc = ber_printf( ber, "t{ss}", ftype, str, value );
616         }
617
618         *s = savechar;
619         *(value-1) = '=';
620         return( rc == -1 ? rc : 0 );
621 }
622
623 static int
624 put_substring_filter( BerElement *ber, char *type, char *val )
625 {
626         char            *nextstar, gotstar = 0;
627         unsigned long   ftype;
628
629         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
630             val, 0 );
631
632         if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
633                 return( -1 );
634
635         while ( val != NULL ) {
636                 if ( (nextstar = strchr( val, '*' )) != NULL )
637                         *nextstar++ = '\0';
638
639                 if ( gotstar == 0 ) {
640                         ftype = LDAP_SUBSTRING_INITIAL;
641                 } else if ( nextstar == NULL ) {
642                         ftype = LDAP_SUBSTRING_FINAL;
643                 } else {
644                         ftype = LDAP_SUBSTRING_ANY;
645                 }
646                 if ( *val != '\0' ) {
647                         if ( ber_printf( ber, "ts", ftype, val ) == -1 )
648                                 return( -1 );
649                 }
650
651                 gotstar = 1;
652                 if ( nextstar != NULL )
653                         *(nextstar-1) = '*';
654                 val = nextstar;
655         }
656
657         if ( ber_printf( ber, "}}" ) == -1 )
658                 return( -1 );
659
660         return( 0 );
661 }
662
663 int
664 ldap_search_st(
665         LDAP *ld, LDAP_CONST char *base, int scope,
666         LDAP_CONST char *filter, char **attrs,
667         int attrsonly, struct timeval *timeout, LDAPMessage **res )
668 {
669         int     msgid;
670
671         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
672             == -1 )
673                 return( ld->ld_errno );
674
675         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
676                 return( ld->ld_errno );
677
678         if ( ld->ld_errno == LDAP_TIMEOUT ) {
679                 (void) ldap_abandon( ld, msgid );
680                 ld->ld_errno = LDAP_TIMEOUT;
681                 return( ld->ld_errno );
682         }
683
684         return( ldap_result2error( ld, *res, 0 ) );
685 }
686
687 int
688 ldap_search_s(
689         LDAP *ld,
690         LDAP_CONST char *base,
691         int scope,
692         LDAP_CONST char *filter,
693         char **attrs,
694         int attrsonly,
695         LDAPMessage **res )
696 {
697         int     msgid;
698
699         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
700             == -1 )
701                 return( ld->ld_errno );
702
703         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
704                 return( ld->ld_errno );
705
706         return( ldap_result2error( ld, *res, 0 ) );
707 }
708