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