]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/search.c
Update with latest from -devel.
[openldap] / servers / slapd / back-ldbm / search.c
1 /* search.c - ldbm backend search function */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/string.h>
8 #include <ac/socket.h>
9
10 #include "slap.h"
11 #include "back-ldbm.h"
12 #include "proto-back-ldbm.h"
13
14 extern time_t           currenttime;
15 extern pthread_mutex_t  currenttime_mutex;
16
17 extern IDList           *idl_alloc();
18 extern Attribute        *attr_find();
19 extern IDList           *filter_candidates();
20 extern char             *ch_realloc();
21 extern char             *dn_parent();
22
23 static IDList   *base_candidates();
24 static IDList   *onelevel_candidates();
25 static IDList   *subtree_candidates();
26
27 #define GRABSIZE        BUFSIZ
28
29 #define MAKE_SPACE( n ) { \
30         if ( rcur + n > rbuf + rmaxsize ) { \
31                 int     offset = rcur - rbuf; \
32                 rbuf =  ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
33                 rmaxsize += GRABSIZE; \
34                 rcur = rbuf + offset; \
35         } \
36 }
37
38 int
39 ldbm_back_search(
40     Backend     *be,
41     Connection  *conn,
42     Operation   *op,
43     char        *base,
44     int         scope,
45     int         deref,
46     int         slimit,
47     int         tlimit,
48     Filter      *filter,
49     char        *filterstr,
50     char        **attrs,
51     int         attrsonly
52 )
53 {
54         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
55         int             err;
56         time_t          stoptime;
57         IDList          *candidates;
58         ID              id;
59         Entry           *e;
60         Attribute       *ref;
61         char            *matched = NULL;
62         int             rmaxsize, nrefs;
63         char            *rbuf, *rcur, *r;
64         int             nentries = 0;
65         char            *realBase;
66
67         Debug(LDAP_DEBUG_ARGS, "=> ldbm_back_search\n", 0, 0, 0);
68
69         if ( tlimit == 0 && be_isroot( be, op->o_dn ) ) {
70                 tlimit = -1;    /* allow root to set no limit */
71         } else {
72                 tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
73                     be->be_timelimit : tlimit;
74                 stoptime = op->o_time + tlimit;
75         }
76         if ( slimit == 0 && be_isroot( be, op->o_dn ) ) {
77                 slimit = -1;    /* allow root to set no limit */
78         } else {
79                 slimit = (slimit > be->be_sizelimit || slimit < 1) ?
80                     be->be_sizelimit : slimit;
81         }
82
83         /*
84          * check and apply aliasing where the dereferencing applies to
85          * the subordinates of the base
86          */
87         realBase = strdup (base);
88         switch ( deref ) {
89         case LDAP_DEREF_FINDING:
90         case LDAP_DEREF_ALWAYS:
91                 free (realBase);
92                 realBase = derefDN ( be, conn, op, base );
93                 break;
94         }
95
96         (void) dn_normalize (realBase);
97
98         Debug( LDAP_DEBUG_TRACE, "using base %s\n",
99                 realBase, 0, 0 );
100
101         switch ( scope ) {
102         case LDAP_SCOPE_BASE:
103                 candidates = base_candidates( be, conn, op, realBase, filter,
104                     attrs, attrsonly, &matched, &err );
105                 break;
106
107         case LDAP_SCOPE_ONELEVEL:
108                 candidates = onelevel_candidates( be, conn, op, realBase, filter,
109                     attrs, attrsonly, &matched, &err );
110                 break;
111
112         case LDAP_SCOPE_SUBTREE:
113                 candidates = subtree_candidates( be, conn, op, realBase, filter,
114                     attrs, attrsonly, &matched, NULL, &err, 1 );
115                 break;
116
117         default:
118                 send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, "",
119                     "Bad scope" );
120                 return( -1 );
121         }
122
123         /* null candidates means we could not find the base object */
124         if ( candidates == NULL ) {
125                 send_ldap_result( conn, op, err, matched, "" );
126                 if ( matched != NULL ) {
127                         free( matched );
128                 }
129                 return( -1 );
130         }
131
132         rmaxsize = 0;
133         nrefs = 0;
134         rbuf = rcur = NULL;
135         MAKE_SPACE( sizeof("Referral:") + 1 );
136         strcpy( rbuf, "Referral:" );
137         rcur = strchr( rbuf, '\0' );
138         for ( id = idl_firstid( candidates ); id != NOID;
139             id = idl_nextid( candidates, id ) ) {
140                 /* check for abandon */
141                 pthread_mutex_lock( &op->o_abandonmutex );
142                 if ( op->o_abandon ) {
143                         pthread_mutex_unlock( &op->o_abandonmutex );
144                         idl_free( candidates );
145                         free( rbuf );
146                         return( 0 );
147                 }
148                 pthread_mutex_unlock( &op->o_abandonmutex );
149
150                 /* check time limit */
151                 pthread_mutex_lock( &currenttime_mutex );
152                 time( &currenttime );
153                 if ( tlimit != -1 && currenttime > stoptime ) {
154                         pthread_mutex_unlock( &currenttime_mutex );
155                         send_ldap_search_result( conn, op,
156                             LDAP_TIMELIMIT_EXCEEDED, NULL, nrefs > 0 ? rbuf :
157                             NULL, nentries );
158                         idl_free( candidates );
159                         free( rbuf );
160                         return( 0 );
161                 }
162                 pthread_mutex_unlock( &currenttime_mutex );
163
164                 /* get the entry with reader lock */
165                 if ( (e = id2entry_r( be, id )) == NULL ) {
166                         Debug( LDAP_DEBUG_ARGS, "candidate %d not found\n", id, 0, 0 );
167                         continue;
168                 }
169
170                 /*
171                  * if it's a referral, add it to the list of referrals. only do
172                  * this for subtree searches, and don't check the filter explicitly
173                  * here since it's only a candidate anyway.
174                  */
175                 if ( e->e_dn != NULL &&
176                         strncasecmp( e->e_dn, "ref=", 4 ) == 0 &&
177                         (ref = attr_find( e->e_attrs, "ref" )) != NULL &&
178                         scope == LDAP_SCOPE_SUBTREE )
179                 {
180                         int     i, len;
181
182                         if ( ref->a_vals == NULL ) {
183                                 Debug( LDAP_DEBUG_ANY, "null ref in (%s)\n", 
184                                         e->e_dn, 0, 0 );
185                         } else {
186                                 for ( i = 0; ref->a_vals[i] != NULL; i++ ) {
187                                         /* referral + newline + null */
188                                         MAKE_SPACE( ref->a_vals[i]->bv_len + 2 );
189                                         *rcur++ = '\n';
190                                         strncpy( rcur, ref->a_vals[i]->bv_val,
191                                                 ref->a_vals[i]->bv_len );
192                                         rcur = rcur + ref->a_vals[i]->bv_len;
193                                         *rcur = '\0';
194                                         nrefs++;
195                                 }
196                         }
197
198                 /* otherwise it's an entry - see if it matches the filter */
199                 } else {
200                         /* if it matches the filter and scope, send it */
201                         if ( test_filter( be, conn, op, e, filter ) == 0 ) {
202                                 int             scopeok;
203                                 char    *dn;
204
205                                 /* check scope */
206                                 scopeok = 1;
207                                 if ( scope == LDAP_SCOPE_ONELEVEL ) {
208                                         if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
209                                                 (void) dn_normalize( dn );
210                                                 scopeok = (dn == realBase) ? 1 : (! strcasecmp( dn, realBase ));
211                                         } else {
212                                                 scopeok = (realBase == NULL || *realBase == '\0');
213                                         }
214                                         free( dn );
215                                 } else if ( scope == LDAP_SCOPE_SUBTREE ) {
216                                         dn = strdup( e->e_dn );
217                                         (void) dn_normalize( dn );
218                                         scopeok = dn_issuffix( dn, realBase );
219                                         free( dn );
220                                 }
221
222                                 if ( scopeok ) {
223                                         /* check size limit */
224                                         if ( --slimit == -1 ) {
225                                                 cache_return_entry_r( &li->li_cache, e );
226                                                 send_ldap_search_result( conn, op,
227                                                         LDAP_SIZELIMIT_EXCEEDED, NULL,
228                                                         nrefs > 0 ? rbuf : NULL, nentries );
229                                                 idl_free( candidates );
230                                                 free( rbuf );
231                                                 return( 0 );
232                                         }
233
234                                         /*
235                                          * check and apply aliasing where the dereferencing applies to
236                                          * the subordinates of the base
237                                          */
238                                         switch ( deref ) {
239                                         case LDAP_DEREF_SEARCHING:
240                                         case LDAP_DEREF_ALWAYS:
241                                                 {
242                                                         Entry *newe = derefAlias_r( be, conn, op, e );
243                                                         cache_return_entry_r( &li->li_cache, e );
244                                                         e = newe;
245                                                 }
246                                                 break;
247                                         }
248
249                                         switch ( send_search_entry( be, conn, op, e,
250                                                 attrs, attrsonly ) ) {
251                                         case 0:         /* entry sent ok */
252                                                 nentries++;
253                                                 break;
254                                         case 1:         /* entry not sent */
255                                                 break;
256                                         case -1:        /* connection closed */
257                                                 cache_return_entry_r( &li->li_cache, e );
258                                                 idl_free( candidates );
259                                                 free( rbuf );
260                                                 return( 0 );
261                                         }
262                                 }
263                         }
264                 }
265
266                 if( e != NULL ) {
267                         /* free reader lock */
268                         cache_return_entry_r( &li->li_cache, e );
269                 }
270
271                 pthread_yield();
272         }
273         idl_free( candidates );
274         if ( nrefs > 0 ) {
275                 send_ldap_search_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
276                     rbuf, nentries );
277         } else {
278                 send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL,
279                     nentries );
280         }
281         free( rbuf );
282
283         return( 0 );
284 }
285
286 static IDList *
287 base_candidates(
288     Backend     *be,
289     Connection  *conn,
290     Operation   *op,
291     char        *base,
292     Filter      *filter,
293     char        **attrs,
294     int         attrsonly,
295     char        **matched,
296     int         *err
297 )
298 {
299         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
300         int             rc;
301         ID              id;
302         IDList          *idl;
303         Entry           *e;
304
305         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: %s\n", base, 0, 0);
306
307         *err = LDAP_SUCCESS;
308
309         /* get entry with reader lock */
310         if ( (e = dn2entry_r( be, base, matched )) == NULL ) {
311                 *err = LDAP_NO_SUCH_OBJECT;
312                 return( NULL );
313         }
314
315         /* check for deleted */
316
317         idl = idl_alloc( 1 );
318         idl_insert( &idl, e->e_id, 1 );
319
320
321         /* free reader lock */
322         cache_return_entry_r( &li->li_cache, e );
323
324         return( idl );
325 }
326
327 static IDList *
328 onelevel_candidates(
329     Backend     *be,
330     Connection  *conn,
331     Operation   *op,
332     char        *base,
333     Filter      *filter,
334     char        **attrs,
335     int         attrsonly,
336     char        **matched,
337     int         *err
338 )
339 {
340         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
341         Entry           *e;
342         Filter          *f;
343         char            buf[20];
344         IDList          *candidates;
345
346         Debug(LDAP_DEBUG_TRACE, "onelevel_candidates: base: %s\n", base, 0, 0);
347
348         *err = LDAP_SUCCESS;
349         e = NULL;
350         /* get the base object with reader lock */
351         if ( base != NULL && *base != '\0' &&
352                 (e = dn2entry_r( be, base, matched )) == NULL )
353         {
354                 *err = LDAP_NO_SUCH_OBJECT;
355                 return( NULL );
356         }
357
358         /*
359          * modify the filter to be something like this:
360          *
361          *      parent=baseobject & originalfilter
362          */
363
364         f = (Filter *) ch_malloc( sizeof(Filter) );
365         f->f_next = NULL;
366         f->f_choice = LDAP_FILTER_AND;
367         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
368         f->f_and->f_choice = LDAP_FILTER_EQUALITY;
369         f->f_and->f_ava.ava_type = strdup( "id2children" );
370         sprintf( buf, "%ld", e != NULL ? e->e_id : 0 );
371         f->f_and->f_ava.ava_value.bv_val = strdup( buf );
372         f->f_and->f_ava.ava_value.bv_len = strlen( buf );
373         f->f_and->f_next = filter;
374
375         /* from here, it's just like subtree_candidates */
376         candidates = subtree_candidates( be, conn, op, base, f, attrs,
377             attrsonly, matched, e, err, 0 );
378
379         /* free up just the filter stuff we allocated above */
380         f->f_and->f_next = NULL;
381         filter_free( f );
382
383         /* free entry and reader lock */
384         cache_return_entry_r( &li->li_cache, e );
385         return( candidates );
386 }
387
388 static IDList *
389 subtree_candidates(
390     Backend     *be,
391     Connection  *conn,
392     Operation   *op,
393     char        *base,
394     Filter      *filter,
395     char        **attrs,
396     int         attrsonly,
397     char        **matched,
398     Entry       *e,
399     int         *err,
400     int         lookupbase
401 )
402 {
403         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
404         Filter          *f;
405         IDList          *candidates;
406
407         Debug(LDAP_DEBUG_TRACE, "subtree_candidates: base: %s\n",
408                 base ? base : "NULL", 0, 0);
409
410         /*
411          * get the base object - unless we already have it (from one-level).
412          * also, unless this is a one-level search or a subtree search
413          * starting at the very top of our subtree, we need to modify the
414          * filter to be something like this:
415          *
416          *      dn=*baseobjectdn & (originalfilter | ref=*)
417          *
418          * the "objectclass=referral" part is used to select referrals to return
419          */
420
421         *err = LDAP_SUCCESS;
422         f = NULL;
423         if ( lookupbase ) {
424                 if ( base != NULL && *base != '\0' &&
425                         (e = dn2entry_r( be, base, matched )) == NULL )
426                 {
427                         *err = LDAP_NO_SUCH_OBJECT;
428                         return( NULL );
429                 }
430
431                 if (e) {
432                         cache_return_entry_r( &li->li_cache, e );
433                 }
434
435                 f = (Filter *) ch_malloc( sizeof(Filter) );
436                 f->f_next = NULL;
437                 f->f_choice = LDAP_FILTER_OR;
438                 f->f_or = (Filter *) ch_malloc( sizeof(Filter) );
439                 f->f_or->f_choice = LDAP_FILTER_EQUALITY;
440                 f->f_or->f_avtype = strdup( "objectclass" );
441                 /* Patch to use normalized uppercase */
442                 f->f_or->f_avvalue.bv_val = strdup( "REFERRAL" );
443                 f->f_or->f_avvalue.bv_len = strlen( "REFERRAL" );
444                 f->f_or->f_next = filter;
445                 filter = f;
446
447                 if ( ! be_issuffix( be, base ) ) {
448                         f = (Filter *) ch_malloc( sizeof(Filter) );
449                         f->f_next = NULL;
450                         f->f_choice = LDAP_FILTER_AND;
451                         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
452                         f->f_and->f_choice = LDAP_FILTER_SUBSTRINGS;
453                         f->f_and->f_sub_type = strdup( "dn" );
454                         f->f_and->f_sub_initial = NULL;
455                         f->f_and->f_sub_any = NULL;
456                         f->f_and->f_sub_final = strdup( base );
457                         value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
458                         f->f_and->f_next = filter;
459                         filter = f;
460                 }
461         }
462
463         candidates = filter_candidates( be, filter );
464
465         /* free up just the parts we allocated above */
466         if ( f != NULL ) {
467                 f->f_and->f_next = NULL;
468                 filter_free( f );
469         }
470
471         return( candidates );
472 }