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