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