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