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