]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
More files that didn't get merged properly.
[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             base = "";
118         }
119
120 #ifdef LDAP_CONNECTIONLESS
121         if ( ld->ld_sb.sb_naddr > 0 ) {
122             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
123                 ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
124                 ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
125         } else {
126 #endif /* LDAP_CONNECTIONLESS */
127                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
128                     LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
129                     ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
130 #ifdef LDAP_CONNECTIONLESS
131         }
132 #endif /* LDAP_CONNECTIONLESS */
133
134         if ( err == -1 ) {
135                 ld->ld_errno = LDAP_ENCODING_ERROR;
136                 ber_free( ber, 1 );
137                 return( NULLBER );
138         }
139
140         filter = strdup( filter );
141         err = put_filter( ber, filter );
142         free( filter );
143
144         if ( err  == -1 ) {
145                 ld->ld_errno = LDAP_FILTER_ERROR;
146                 ber_free( ber, 1 );
147                 return( NULLBER );
148         }
149
150         if ( ber_printf( ber, "{v}}}", attrs ) == -1 ) {
151                 ld->ld_errno = LDAP_ENCODING_ERROR;
152                 ber_free( ber, 1 );
153                 return( NULLBER );
154         }
155
156         return( ber );
157 }
158
159 static char *
160 find_right_paren( char *s )
161 {
162         int     balance, escape;
163
164         balance = 1;
165         escape = 0;
166         while ( *s && balance ) {
167                 if ( escape == 0 ) {
168                         if ( *s == '(' )
169                                 balance++;
170                         else if ( *s == ')' )
171                                 balance--;
172                 }
173                 if ( *s == '\\' && ! escape )
174                         escape = 1;
175                 else
176                         escape = 0;
177                 if ( balance )
178                         s++;
179         }
180
181         return( *s ? s : NULL );
182 }
183
184 static char *
185 put_complex_filter( BerElement *ber, char *str, unsigned long tag, int not )
186 {
187         char    *next;
188
189         /*
190          * We have (x(filter)...) with str sitting on
191          * the x.  We have to find the paren matching
192          * the one before the x and put the intervening
193          * filters by calling put_filter_list().
194          */
195
196         /* put explicit tag */
197         if ( ber_printf( ber, "t{", tag ) == -1 )
198                 return( NULL );
199 /*
200         if ( !not && ber_printf( ber, "{" ) == -1 )
201                 return( NULL );
202 */
203
204         str++;
205         if ( (next = find_right_paren( str )) == NULL )
206                 return( NULL );
207
208         *next = '\0';
209         if ( put_filter_list( ber, str ) == -1 )
210                 return( NULL );
211         *next++ = ')';
212
213         /* flush explicit tagged thang */
214         if ( ber_printf( ber, "}" ) == -1 )
215                 return( NULL );
216 /*
217         if ( !not && ber_printf( ber, "}" ) == -1 )
218                 return( NULL );
219 */
220
221         return( next );
222 }
223
224 static int
225 put_filter( BerElement *ber, char *str )
226 {
227         char    *next, *tmp, *s, *d;
228         int     parens, balance, escape, gotescape;
229
230         /*
231          * A Filter looks like this:
232          *      Filter ::= CHOICE {
233          *              and             [0]     SET OF Filter,
234          *              or              [1]     SET OF Filter,
235          *              not             [2]     Filter,
236          *              equalityMatch   [3]     AttributeValueAssertion,
237          *              substrings      [4]     SubstringFilter,
238          *              greaterOrEqual  [5]     AttributeValueAssertion,
239          *              lessOrEqual     [6]     AttributeValueAssertion,
240          *              present         [7]     AttributeType,,
241          *              approxMatch     [8]     AttributeValueAssertion
242          *      }
243          *
244          *      SubstringFilter ::= SEQUENCE {
245          *              type               AttributeType,
246          *              SEQUENCE OF CHOICE {
247          *                      initial          [0] IA5String,
248          *                      any              [1] IA5String,
249          *                      final            [2] IA5String
250          *              }
251          *      }
252          * Note: tags in a choice are always explicit
253          */
254
255         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
256
257         gotescape = parens = 0;
258         while ( *str ) {
259                 switch ( *str ) {
260                 case '(':
261                         str++;
262                         parens++;
263                         switch ( *str ) {
264                         case '&':
265                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
266                                     0, 0, 0 );
267
268                                 if ( (str = put_complex_filter( ber, str,
269                                     LDAP_FILTER_AND, 0 )) == NULL )
270                                         return( -1 );
271
272                                 parens--;
273                                 break;
274
275                         case '|':
276                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
277                                     0, 0, 0 );
278
279                                 if ( (str = put_complex_filter( ber, str,
280                                     LDAP_FILTER_OR, 0 )) == NULL )
281                                         return( -1 );
282
283                                 parens--;
284                                 break;
285
286                         case '!':
287                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
288                                     0, 0, 0 );
289
290                                 if ( (str = put_complex_filter( ber, str,
291                                     LDAP_FILTER_NOT, 1 )) == NULL )
292                                         return( -1 );
293
294                                 parens--;
295                                 break;
296
297                         default:
298                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
299                                     0, 0, 0 );
300
301                                 balance = 1;
302                                 escape = 0;
303                                 next = str;
304                                 while ( *next && balance ) {
305                                         if ( escape == 0 ) {
306                                                 if ( *next == '(' )
307                                                         balance++;
308                                                 else if ( *next == ')' )
309                                                         balance--;
310                                         }
311                                         if ( *next == '\\' && ! escape )
312                                                 gotescape = escape = 1;
313                                         else
314                                                 escape = 0;
315                                         if ( balance )
316                                                 next++;
317                                 }
318                                 if ( balance != 0 )
319                                         return( -1 );
320
321                                 *next = '\0';
322                                 tmp = strdup( str );
323                                 if ( gotescape ) {
324                                         escape = 0;
325                                         for ( s = d = tmp; *s; s++ ) {
326                                                 if ( *s != '\\' || escape ) {
327                                                         *d++ = *s;
328                                                         escape = 0;
329                                                 } else {
330                                                         escape = 1;
331                                                 }
332                                         }
333                                         *d = '\0';
334                                 }
335                                 if ( put_simple_filter( ber, tmp ) == -1 ) {
336                                         free( tmp );
337                                         return( -1 );
338                                 }
339                                 free( tmp );
340                                 *next++ = ')';
341                                 str = next;
342                                 parens--;
343                                 break;
344                         }
345                         break;
346
347                 case ')':
348                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
349                             0 );
350                         if ( ber_printf( ber, "]" ) == -1 )
351                                 return( -1 );
352                         str++;
353                         parens--;
354                         break;
355
356                 case ' ':
357                         str++;
358                         break;
359
360                 default:        /* assume it's a simple type=value filter */
361                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
362                             0 );
363                         next = strchr( str, '\0' );
364                         tmp = strdup( str );
365                         if ( strchr( tmp, '\\' ) != NULL ) {
366                                 escape = 0;
367                                 for ( s = d = tmp; *s; s++ ) {
368                                         if ( *s != '\\' || escape ) {
369                                                 *d++ = *s;
370                                                 escape = 0;
371                                         } else {
372                                                 escape = 1;
373                                         }
374                                 }
375                                 *d = '\0';
376                         }
377                         if ( put_simple_filter( ber, tmp ) == -1 ) {
378                                 free( tmp );
379                                 return( -1 );
380                         }
381                         free( tmp );
382                         str = next;
383                         break;
384                 }
385         }
386
387         return( parens ? -1 : 0 );
388 }
389
390 /*
391  * Put a list of filters like this "(filter1)(filter2)..."
392  */
393
394 static int
395 put_filter_list( BerElement *ber, char *str )
396 {
397         char    *next;
398         char    save;
399
400         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
401
402         while ( *str ) {
403                 while ( *str && isspace( *str ) )
404                         str++;
405                 if ( *str == '\0' )
406                         break;
407
408                 if ( (next = find_right_paren( str + 1 )) == NULL )
409                         return( -1 );
410                 save = *++next;
411
412                 /* now we have "(filter)" with str pointing to it */
413                 *next = '\0';
414                 if ( put_filter( ber, str ) == -1 )
415                         return( -1 );
416                 *next = save;
417
418                 str = next;
419         }
420
421         return( 0 );
422 }
423
424 static int
425 put_simple_filter( BerElement *ber, char *str )
426 {
427         char            *s;
428         char            *value, savechar;
429         unsigned long   ftype;
430         int             rc;
431
432         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
433
434         if ( (s = strchr( str, '=' )) == NULL )
435                 return( -1 );
436         value = s + 1;
437         *s-- = '\0';
438         savechar = *s;
439
440         switch ( *s ) {
441         case '<':
442                 ftype = LDAP_FILTER_LE;
443                 *s = '\0';
444                 break;
445         case '>':
446                 ftype = LDAP_FILTER_GE;
447                 *s = '\0';
448                 break;
449         case '~':
450                 ftype = LDAP_FILTER_APPROX;
451                 *s = '\0';
452                 break;
453         default:
454                 if ( strchr( value, '*' ) == NULL ) {
455                         ftype = LDAP_FILTER_EQUALITY;
456                 } else if ( strcmp( value, "*" ) == 0 ) {
457                         ftype = LDAP_FILTER_PRESENT;
458                 } else {
459                         rc = put_substring_filter( ber, str, value );
460                         *(value-1) = '=';
461                         return( rc );
462                 }
463                 break;
464         }
465
466         if ( ftype == LDAP_FILTER_PRESENT ) {
467                 rc = ber_printf( ber, "ts", ftype, str );
468         } else {
469                 rc = ber_printf( ber, "t{ss}", ftype, str, value );
470         }
471
472         *s = savechar;
473         *(value-1) = '=';
474         return( rc == -1 ? rc : 0 );
475 }
476
477 static int
478 put_substring_filter( BerElement *ber, char *type, char *val )
479 {
480         char            *nextstar, gotstar = 0;
481         unsigned long   ftype;
482
483         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
484             val, 0 );
485
486         if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
487                 return( -1 );
488
489         while ( val != NULL ) {
490                 if ( (nextstar = strchr( val, '*' )) != NULL )
491                         *nextstar++ = '\0';
492
493                 if ( gotstar == 0 ) {
494                         ftype = LDAP_SUBSTRING_INITIAL;
495                 } else if ( nextstar == NULL ) {
496                         ftype = LDAP_SUBSTRING_FINAL;
497                 } else {
498                         ftype = LDAP_SUBSTRING_ANY;
499                 }
500                 if ( *val != '\0' ) {
501                         if ( ber_printf( ber, "ts", ftype, val ) == -1 )
502                                 return( -1 );
503                 }
504
505                 gotstar = 1;
506                 if ( nextstar != NULL )
507                         *(nextstar-1) = '*';
508                 val = nextstar;
509         }
510
511         if ( ber_printf( ber, "}}" ) == -1 )
512                 return( -1 );
513
514         return( 0 );
515 }
516
517 int
518 ldap_search_st( LDAP *ld, char *base, int scope, char *filter, char **attrs,
519         int attrsonly, struct timeval *timeout, LDAPMessage **res )
520 {
521         int     msgid;
522
523         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
524             == -1 )
525                 return( ld->ld_errno );
526
527         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
528                 return( ld->ld_errno );
529
530         if ( ld->ld_errno == LDAP_TIMEOUT ) {
531                 (void) ldap_abandon( ld, msgid );
532                 ld->ld_errno = LDAP_TIMEOUT;
533                 return( ld->ld_errno );
534         }
535
536         return( ldap_result2error( ld, *res, 0 ) );
537 }
538
539 int
540 ldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs,
541         int attrsonly, LDAPMessage **res )
542 {
543         int     msgid;
544
545         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
546             == -1 )
547                 return( ld->ld_errno );
548
549         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
550                 return( ld->ld_errno );
551
552         return( ldap_result2error( ld, *res, 0 ) );
553 }
554