]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
Port ldap.conf patch from -devel.
[openldap] / libraries / libldap / search.c
1 /*
2  *  Copyright (c) 1990 Regents of the University of Michigan.
3  *  All rights reserved.
4  *
5  *  search.c
6  */
7
8 #include "portable.h"
9
10 #ifndef lint 
11 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
12 #endif
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 "lber.h"
23 #include "ldap.h"
24 #include "ldap-int.h"
25
26 static char *find_right_paren LDAP_P(( char *s ));
27 static char *put_complex_filter LDAP_P(( BerElement *ber, char *str,
28         unsigned long tag, int not ));
29 static int put_filter LDAP_P(( BerElement *ber, char *str ));
30 static int put_simple_filter LDAP_P(( BerElement *ber, char *str ));
31 static int put_substring_filter LDAP_P(( BerElement *ber, char *type, char *str ));
32 static int put_filter_list LDAP_P(( BerElement *ber, char *str ));
33
34 /*
35  * ldap_search - initiate an ldap (and X.500) search operation.  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  *      msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
49  *          attrs, attrsonly );
50  */
51 int
52 ldap_search( LDAP *ld, char *base, int scope, char *filter,
53         char **attrs, int attrsonly )
54 {
55         BerElement      *ber;
56
57         Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
58
59         if (( ber = ldap_build_search_req( ld, base, scope, filter, attrs,
60             attrsonly )) == NULLBER ) {
61                 return( -1 );
62         }
63
64 #ifndef LDAP_NOCACHE
65         if ( ld->ld_cache != NULL ) {
66                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
67                         ber_free( ber, 1 );
68                         ld->ld_errno = LDAP_SUCCESS;
69                         return( ld->ld_msgid );
70                 }
71                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
72         }
73 #endif /* LDAP_NOCACHE */
74
75         /* send the message */
76         return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
77 }
78
79
80 BerElement *
81 ldap_build_search_req( LDAP *ld, char *base, int scope, char *filter,
82         char **attrs, int attrsonly )
83 {
84         BerElement      *ber;
85         int             err;
86
87         /*
88          * Create the search request.  It looks like this:
89          *      SearchRequest := [APPLICATION 3] SEQUENCE {
90          *              baseObject      DistinguishedName,
91          *              scope           ENUMERATED {
92          *                      baseObject      (0),
93          *                      singleLevel     (1),
94          *                      wholeSubtree    (2)
95          *              },
96          *              derefAliases    ENUMERATED {
97          *                      neverDerefaliases       (0),
98          *                      derefInSearching        (1),
99          *                      derefFindingBaseObj     (2),
100          *                      alwaysDerefAliases      (3)
101          *              },
102          *              sizelimit       INTEGER (0 .. 65535),
103          *              timelimit       INTEGER (0 .. 65535),
104          *              attrsOnly       BOOLEAN,
105          *              filter          Filter,
106          *              attributes      SEQUENCE OF AttributeType
107          *      }
108          * wrapped in an ldap message.
109          */
110
111         /* create a message to send */
112         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULLBER ) {
113                 return( NULLBER );
114         }
115
116         if ( base == NULL ) {
117                 /* no base provided, use global default base */
118                 base = openldap_ldap_global_options.ldo_defbase;
119         }
120
121         if ( base == NULL ) {
122                 /* no session default base, use top */
123             base = "";
124         }
125
126 #ifdef LDAP_CONNECTIONLESS
127         if ( ld->ld_sb.sb_naddr > 0 ) {
128             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
129                 ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
130                 ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
131         } else {
132 #endif /* LDAP_CONNECTIONLESS */
133                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
134                     LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
135                     ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
136 #ifdef LDAP_CONNECTIONLESS
137         }
138 #endif /* LDAP_CONNECTIONLESS */
139
140         if ( err == -1 ) {
141                 ld->ld_errno = LDAP_ENCODING_ERROR;
142                 ber_free( ber, 1 );
143                 return( NULLBER );
144         }
145
146         filter = ldap_strdup( filter );
147         err = put_filter( ber, filter );
148         free( filter );
149
150         if ( err  == -1 ) {
151                 ld->ld_errno = LDAP_FILTER_ERROR;
152                 ber_free( ber, 1 );
153                 return( NULLBER );
154         }
155
156         if ( ber_printf( ber, "{v}}}", attrs ) == -1 ) {
157                 ld->ld_errno = LDAP_ENCODING_ERROR;
158                 ber_free( ber, 1 );
159                 return( NULLBER );
160         }
161
162         return( ber );
163 }
164
165 static char *
166 find_right_paren( char *s )
167 {
168         int     balance, escape;
169
170         balance = 1;
171         escape = 0;
172         while ( *s && balance ) {
173                 if ( escape == 0 ) {
174                         if ( *s == '(' )
175                                 balance++;
176                         else if ( *s == ')' )
177                                 balance--;
178                 }
179                 if ( *s == '\\' && ! escape )
180                         escape = 1;
181                 else
182                         escape = 0;
183                 if ( balance )
184                         s++;
185         }
186
187         return( *s ? s : NULL );
188 }
189
190 static char *
191 put_complex_filter( BerElement *ber, char *str, unsigned long tag, int not )
192 {
193         char    *next;
194
195         /*
196          * We have (x(filter)...) with str sitting on
197          * the x.  We have to find the paren matching
198          * the one before the x and put the intervening
199          * filters by calling put_filter_list().
200          */
201
202         /* put explicit tag */
203         if ( ber_printf( ber, "t{", tag ) == -1 )
204                 return( NULL );
205 /*
206         if ( !not && ber_printf( ber, "{" ) == -1 )
207                 return( NULL );
208 */
209
210         str++;
211         if ( (next = find_right_paren( str )) == NULL )
212                 return( NULL );
213
214         *next = '\0';
215         if ( put_filter_list( ber, str ) == -1 )
216                 return( NULL );
217         *next++ = ')';
218
219         /* flush explicit tagged thang */
220         if ( ber_printf( ber, "}" ) == -1 )
221                 return( NULL );
222 /*
223         if ( !not && ber_printf( ber, "}" ) == -1 )
224                 return( NULL );
225 */
226
227         return( next );
228 }
229
230 static int
231 put_filter( BerElement *ber, char *str )
232 {
233         char    *next, *tmp, *s, *d;
234         int     parens, balance, escape, gotescape;
235
236         /*
237          * A Filter looks like this:
238          *      Filter ::= CHOICE {
239          *              and             [0]     SET OF Filter,
240          *              or              [1]     SET OF Filter,
241          *              not             [2]     Filter,
242          *              equalityMatch   [3]     AttributeValueAssertion,
243          *              substrings      [4]     SubstringFilter,
244          *              greaterOrEqual  [5]     AttributeValueAssertion,
245          *              lessOrEqual     [6]     AttributeValueAssertion,
246          *              present         [7]     AttributeType,,
247          *              approxMatch     [8]     AttributeValueAssertion
248          *      }
249          *
250          *      SubstringFilter ::= SEQUENCE {
251          *              type               AttributeType,
252          *              SEQUENCE OF CHOICE {
253          *                      initial          [0] IA5String,
254          *                      any              [1] IA5String,
255          *                      final            [2] IA5String
256          *              }
257          *      }
258          * Note: tags in a choice are always explicit
259          */
260
261         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
262
263         gotescape = parens = 0;
264         while ( *str ) {
265                 switch ( *str ) {
266                 case '(':
267                         str++;
268                         parens++;
269                         switch ( *str ) {
270                         case '&':
271                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
272                                     0, 0, 0 );
273
274                                 if ( (str = put_complex_filter( ber, str,
275                                     LDAP_FILTER_AND, 0 )) == NULL )
276                                         return( -1 );
277
278                                 parens--;
279                                 break;
280
281                         case '|':
282                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
283                                     0, 0, 0 );
284
285                                 if ( (str = put_complex_filter( ber, str,
286                                     LDAP_FILTER_OR, 0 )) == NULL )
287                                         return( -1 );
288
289                                 parens--;
290                                 break;
291
292                         case '!':
293                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
294                                     0, 0, 0 );
295
296                                 if ( (str = put_complex_filter( ber, str,
297                                     LDAP_FILTER_NOT, 1 )) == NULL )
298                                         return( -1 );
299
300                                 parens--;
301                                 break;
302
303                         default:
304                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
305                                     0, 0, 0 );
306
307                                 balance = 1;
308                                 escape = 0;
309                                 next = str;
310                                 while ( *next && balance ) {
311                                         if ( escape == 0 ) {
312                                                 if ( *next == '(' )
313                                                         balance++;
314                                                 else if ( *next == ')' )
315                                                         balance--;
316                                         }
317                                         if ( *next == '\\' && ! escape )
318                                                 gotescape = escape = 1;
319                                         else
320                                                 escape = 0;
321                                         if ( balance )
322                                                 next++;
323                                 }
324                                 if ( balance != 0 )
325                                         return( -1 );
326
327                                 *next = '\0';
328                                 tmp = ldap_strdup( str );
329                                 if ( gotescape ) {
330                                         escape = 0;
331                                         for ( s = d = tmp; *s; s++ ) {
332                                                 if ( *s != '\\' || escape ) {
333                                                         *d++ = *s;
334                                                         escape = 0;
335                                                 } else {
336                                                         escape = 1;
337                                                 }
338                                         }
339                                         *d = '\0';
340                                 }
341                                 if ( put_simple_filter( ber, tmp ) == -1 ) {
342                                         free( tmp );
343                                         return( -1 );
344                                 }
345                                 free( tmp );
346                                 *next++ = ')';
347                                 str = next;
348                                 parens--;
349                                 break;
350                         }
351                         break;
352
353                 case ')':
354                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
355                             0 );
356                         if ( ber_printf( ber, "]" ) == -1 )
357                                 return( -1 );
358                         str++;
359                         parens--;
360                         break;
361
362                 case ' ':
363                         str++;
364                         break;
365
366                 default:        /* assume it's a simple type=value filter */
367                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
368                             0 );
369                         next = strchr( str, '\0' );
370                         tmp = ldap_strdup( str );
371                         if ( strchr( tmp, '\\' ) != NULL ) {
372                                 escape = 0;
373                                 for ( s = d = tmp; *s; s++ ) {
374                                         if ( *s != '\\' || escape ) {
375                                                 *d++ = *s;
376                                                 escape = 0;
377                                         } else {
378                                                 escape = 1;
379                                         }
380                                 }
381                                 *d = '\0';
382                         }
383                         if ( put_simple_filter( ber, tmp ) == -1 ) {
384                                 free( tmp );
385                                 return( -1 );
386                         }
387                         free( tmp );
388                         str = next;
389                         break;
390                 }
391         }
392
393         return( parens ? -1 : 0 );
394 }
395
396 /*
397  * Put a list of filters like this "(filter1)(filter2)..."
398  */
399
400 static int
401 put_filter_list( BerElement *ber, char *str )
402 {
403         char    *next;
404         char    save;
405
406         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
407
408         while ( *str ) {
409                 while ( *str && isspace( *str ) )
410                         str++;
411                 if ( *str == '\0' )
412                         break;
413
414                 if ( (next = find_right_paren( str + 1 )) == NULL )
415                         return( -1 );
416                 save = *++next;
417
418                 /* now we have "(filter)" with str pointing to it */
419                 *next = '\0';
420                 if ( put_filter( ber, str ) == -1 )
421                         return( -1 );
422                 *next = save;
423
424                 str = next;
425         }
426
427         return( 0 );
428 }
429
430 static int
431 put_simple_filter( BerElement *ber, char *str )
432 {
433         char            *s;
434         char            *value, savechar;
435         unsigned long   ftype;
436         int             rc;
437
438         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
439
440         if ( (s = strchr( str, '=' )) == NULL )
441                 return( -1 );
442         value = s + 1;
443         *s-- = '\0';
444         savechar = *s;
445
446         switch ( *s ) {
447         case '<':
448                 ftype = LDAP_FILTER_LE;
449                 *s = '\0';
450                 break;
451         case '>':
452                 ftype = LDAP_FILTER_GE;
453                 *s = '\0';
454                 break;
455         case '~':
456                 ftype = LDAP_FILTER_APPROX;
457                 *s = '\0';
458                 break;
459         default:
460                 if ( strchr( value, '*' ) == NULL ) {
461                         ftype = LDAP_FILTER_EQUALITY;
462                 } else if ( strcmp( value, "*" ) == 0 ) {
463                         ftype = LDAP_FILTER_PRESENT;
464                 } else {
465                         rc = put_substring_filter( ber, str, value );
466                         *(value-1) = '=';
467                         return( rc );
468                 }
469                 break;
470         }
471
472         if ( ftype == LDAP_FILTER_PRESENT ) {
473                 rc = ber_printf( ber, "ts", ftype, str );
474         } else {
475                 rc = ber_printf( ber, "t{ss}", ftype, str, value );
476         }
477
478         *s = savechar;
479         *(value-1) = '=';
480         return( rc == -1 ? rc : 0 );
481 }
482
483 static int
484 put_substring_filter( BerElement *ber, char *type, char *val )
485 {
486         char            *nextstar, gotstar = 0;
487         unsigned long   ftype;
488
489         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
490             val, 0 );
491
492         if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
493                 return( -1 );
494
495         while ( val != NULL ) {
496                 if ( (nextstar = strchr( val, '*' )) != NULL )
497                         *nextstar++ = '\0';
498
499                 if ( gotstar == 0 ) {
500                         ftype = LDAP_SUBSTRING_INITIAL;
501                 } else if ( nextstar == NULL ) {
502                         ftype = LDAP_SUBSTRING_FINAL;
503                 } else {
504                         ftype = LDAP_SUBSTRING_ANY;
505                 }
506                 if ( *val != '\0' ) {
507                         if ( ber_printf( ber, "ts", ftype, val ) == -1 )
508                                 return( -1 );
509                 }
510
511                 gotstar = 1;
512                 if ( nextstar != NULL )
513                         *(nextstar-1) = '*';
514                 val = nextstar;
515         }
516
517         if ( ber_printf( ber, "}}" ) == -1 )
518                 return( -1 );
519
520         return( 0 );
521 }
522
523 int
524 ldap_search_st( LDAP *ld, char *base, int scope, char *filter, char **attrs,
525         int attrsonly, struct timeval *timeout, LDAPMessage **res )
526 {
527         int     msgid;
528
529         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
530             == -1 )
531                 return( ld->ld_errno );
532
533         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
534                 return( ld->ld_errno );
535
536         if ( ld->ld_errno == LDAP_TIMEOUT ) {
537                 (void) ldap_abandon( ld, msgid );
538                 ld->ld_errno = LDAP_TIMEOUT;
539                 return( ld->ld_errno );
540         }
541
542         return( ldap_result2error( ld, *res, 0 ) );
543 }
544
545 int
546 ldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs,
547         int attrsonly, LDAPMessage **res )
548 {
549         int     msgid;
550
551         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
552             == -1 )
553                 return( ld->ld_errno );
554
555         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
556                 return( ld->ld_errno );
557
558         return( ldap_result2error( ld, *res, 0 ) );
559 }
560