]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/search.c
d9fadde8290872cc2df52c9b801d1924c0efa3ec
[openldap] / servers / slapd / back-bdb / search.c
1 /* search.c - search operation */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12
13 #include "back-bdb.h"
14 #include "idl.h"
15 #include "external.h"
16
17 static int base_candidate(
18         BackendDB       *be,
19         Entry   *e,
20         ID              *ids );
21 static int search_candidates(
22         BackendDB *be,
23         Operation *op,
24         Entry *e,
25         Filter *filter,
26         int scope,
27         int deref,
28         ID      *ids );
29
30 int
31 bdb_search(
32         BackendDB       *be,
33         Connection      *conn,
34         Operation       *op,
35         struct berval   *base,
36         struct berval   *nbase,
37         int             scope,
38         int             deref,
39         int             slimit,
40         int             tlimit,
41         Filter  *filter,
42         struct berval   *filterstr,
43         AttributeName   *attrs,
44         int             attrsonly )
45 {
46         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
47         int             rc;
48         const char *text = NULL;
49         time_t          stoptime;
50         ID              id, cursor;
51         ID              candidates[BDB_IDL_UM_SIZE];
52         Entry           *e = NULL;
53         BerVarray v2refs = NULL;
54         Entry   *matched = NULL;
55         struct berval   realbase = { 0, NULL };
56         int             nentries = 0;
57         int             manageDSAit;
58
59 #ifdef LDAP_CLIENT_UPDATE
60         Filter lcupf, csnfnot, csnfeq, csnfand, csnfge;
61         AttributeAssertion aa_ge, aa_eq;
62         LDAPControl ctrl;
63         int             entry_count = 0;
64 #endif /* LDAP_CLIENT_UPDATE */
65
66         struct slap_limits_set *limit = NULL;
67         int isroot = 0;
68
69 #ifdef SLAP_X_FILTER_HASSUBORDINATES
70         int             filter_hasSubordinates = 0;
71 #endif /* SLAP_X_FILTER_HASSUBORDINATES */
72
73         u_int32_t       locker;
74         DB_LOCK         lock;
75
76 #ifdef NEW_LOGGING
77         LDAP_LOG ( OPERATION, ENTRY, "bdb_back_search\n", 0, 0, 0 );
78 #else
79         Debug( LDAP_DEBUG_TRACE, "=> bdb_back_search\n",
80                 0, 0, 0);
81 #endif
82
83         manageDSAit = get_manageDSAit( op );
84
85         rc = LOCK_ID (bdb->bi_dbenv, &locker );
86         switch(rc) {
87         case 0:
88                 break;
89         default:
90                 send_ldap_result( conn, op, rc=LDAP_OTHER,
91                         NULL, "internal error", NULL, NULL );
92                 return rc;
93         }
94
95         if ( nbase->bv_len == 0 ) {
96                 /* DIT root special case */
97                 e = (Entry *) &slap_entry_root;
98                 rc = 0;
99         } else                                          
100 #ifdef BDB_ALIASES
101         /* get entry with reader lock */
102         if ( deref & LDAP_DEREF_FINDING ) {
103                 e = deref_dn_r( be, nbase-, &err, &matched, &text );
104
105         } else
106 #endif
107         {
108 dn2entry_retry:
109                 rc = bdb_dn2entry_r( be, NULL, nbase, &e, &matched, 0, locker, &lock );
110         }
111
112         switch(rc) {
113         case DB_NOTFOUND:
114         case 0:
115                 break;
116         case LDAP_BUSY:
117                 if (e != NULL) {
118                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
119                 }
120                 if (matched != NULL) {
121                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, matched, &lock);
122                 }
123                 send_ldap_result( conn, op, LDAP_BUSY,
124                         NULL, "ldap server busy", NULL, NULL );
125                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
126                 return LDAP_BUSY;
127         case DB_LOCK_DEADLOCK:
128         case DB_LOCK_NOTGRANTED:
129                 goto dn2entry_retry;
130         default:
131                 if (e != NULL) {
132                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
133                 }
134                 if (matched != NULL) {
135                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, matched, &lock);
136                 }
137                 send_ldap_result( conn, op, rc=LDAP_OTHER,
138                         NULL, "internal error", NULL, NULL );
139                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
140                 return rc;
141         }
142
143         if ( e == NULL ) {
144                 struct berval matched_dn = { 0, NULL };
145                 BerVarray refs = NULL;
146
147                 if ( matched != NULL ) {
148                         BerVarray erefs;
149                         ber_dupbv( &matched_dn, &matched->e_name );
150
151                         erefs = is_entry_referral( matched )
152                                 ? get_entry_referrals( be, conn, op, matched )
153                                 : NULL;
154
155                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, matched, &lock);
156                         matched = NULL;
157
158                         if( erefs ) {
159                                 refs = referral_rewrite( erefs, &matched_dn,
160                                         base, scope );
161                                 ber_bvarray_free( erefs );
162                         }
163
164                 } else {
165                         refs = referral_rewrite( default_referral,
166                                 NULL, base, scope );
167                 }
168
169                 send_ldap_result( conn, op,     rc=LDAP_REFERRAL ,
170                         matched_dn.bv_val, text, refs, NULL );
171
172                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
173                 if ( refs ) ber_bvarray_free( refs );
174                 if ( matched_dn.bv_val ) ber_memfree( matched_dn.bv_val );
175                 return rc;
176         }
177
178         if (!manageDSAit && e != &slap_entry_root && is_entry_referral( e ) ) {
179                 /* entry is a referral, don't allow add */
180                 struct berval matched_dn;
181                 BerVarray erefs, refs;
182                 
183                 ber_dupbv( &matched_dn, &e->e_name );
184                 erefs = get_entry_referrals( be, conn, op, e );
185                 refs = NULL;
186
187                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
188                 e = NULL;
189
190                 if( erefs ) {
191                         refs = referral_rewrite( erefs, &matched_dn,
192                                 base, scope );
193                         ber_bvarray_free( erefs );
194                 }
195
196 #ifdef NEW_LOGGING
197                 LDAP_LOG ( OPERATION, RESULTS, 
198                         "bdb_search: entry is referral\n", 0, 0, 0 );
199 #else
200                 Debug( LDAP_DEBUG_TRACE, "bdb_search: entry is referral\n",
201                         0, 0, 0 );
202 #endif
203
204                 send_ldap_result( conn, op, LDAP_REFERRAL,
205                         matched_dn.bv_val,
206                         refs ? NULL : "bad referral object",
207                         refs, NULL );
208
209                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
210                 ber_bvarray_free( refs );
211                 ber_memfree( matched_dn.bv_val );
212                 return 1;
213         }
214
215         /* if not root, get appropriate limits */
216         if ( be_isroot( be, &op->o_ndn ) ) {
217                 isroot = 1;
218         } else {
219                 ( void ) get_limits( be, &op->o_ndn, &limit );
220         }
221
222         /* The time/size limits come first because they require very little
223          * effort, so there's no chance the candidates are selected and then 
224          * the request is not honored only because of time/size constraints */
225
226         /* if no time limit requested, use soft limit (unless root!) */
227         if ( isroot ) {
228                 if ( tlimit == 0 ) {
229                         tlimit = -1;    /* allow root to set no limit */
230                 }
231
232                 if ( slimit == 0 ) {
233                         slimit = -1;
234                 }
235
236         } else {
237                 /* if no limit is required, use soft limit */
238                 if ( tlimit <= 0 ) {
239                         tlimit = limit->lms_t_soft;
240
241                 /* if requested limit higher than hard limit, abort */
242                 } else if ( tlimit > limit->lms_t_hard ) {
243                         /* no hard limit means use soft instead */
244                         if ( limit->lms_t_hard == 0 && tlimit > limit->lms_t_soft ) {
245                                 tlimit = limit->lms_t_soft;
246
247                         /* positive hard limit means abort */
248                         } else if ( limit->lms_t_hard > 0 ) {
249                                 send_search_result( conn, op, 
250                                                 LDAP_UNWILLING_TO_PERFORM,
251                                                 NULL, NULL, NULL, NULL, 0 );
252                                 rc = 0;
253                                 goto done;
254                         }
255                 
256                         /* negative hard limit means no limit */
257                 }
258                 
259                 /* if no limit is required, use soft limit */
260                 if ( slimit <= 0 ) {
261                         slimit = limit->lms_s_soft;
262
263                 /* if requested limit higher than hard limit, abort */
264                 } else if ( slimit > limit->lms_s_hard ) {
265                         /* no hard limit means use soft instead */
266                         if ( limit->lms_s_hard == 0 && slimit > limit->lms_s_soft ) {
267                                 slimit = limit->lms_s_soft;
268
269                         /* positive hard limit means abort */
270                         } else if ( limit->lms_s_hard > 0 ) {
271                                 send_search_result( conn, op, 
272                                                 LDAP_UNWILLING_TO_PERFORM,
273                                                 NULL, NULL, NULL, NULL, 0 );
274                                 rc = 0; 
275                                 goto done;
276                         }
277                         
278                         /* negative hard limit means no limit */
279                 }
280         }
281
282         /* compute it anyway; root does not use it */
283         stoptime = op->o_time + tlimit;
284
285         /* select candidates */
286         if ( scope == LDAP_SCOPE_BASE ) {
287                 rc = base_candidate( be, e, candidates );
288
289         } else {
290                 BDB_IDL_ALL( bdb, candidates );
291                 rc = search_candidates( be, op, e, filter,
292                         scope, deref, candidates );
293         }
294
295         /* need normalized dn below */
296         ber_dupbv( &realbase, &e->e_nname );
297
298         /* start cursor at base entry's id 
299          * FIXME: hack to make "" base work
300          * FIXME: moddn needs to assign new ID for this to work
301          */
302         cursor = e->e_id == NOID ? 1 : e->e_id;
303
304         if ( e != &slap_entry_root ) {
305                 bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
306         }
307         e = NULL;
308
309         if ( candidates[0] == 0 ) {
310 #ifdef NEW_LOGGING
311         LDAP_LOG ( OPERATION, RESULTS, "bdb_search: no candidates\n", 0, 0, 0 );
312 #else
313                 Debug( LDAP_DEBUG_TRACE, "bdb_search: no candidates\n",
314                         0, 0, 0 );
315 #endif
316
317                 send_search_result( conn, op,
318                         LDAP_SUCCESS,
319                         NULL, NULL, NULL, NULL, 0 );
320
321                 rc = 1;
322                 goto done;
323         }
324
325         /* if not root and candidates exceed to-be-checked entries, abort */
326         if ( !isroot && limit->lms_s_unchecked != -1 ) {
327                 if ( BDB_IDL_N(candidates) > (unsigned) limit->lms_s_unchecked ) {
328                         send_search_result( conn, op, 
329                                         LDAP_ADMINLIMIT_EXCEEDED,
330                                         NULL, NULL, NULL, NULL, 0 );
331                         rc = 1;
332                         goto done;
333                 }
334         }
335
336 #ifdef SLAP_X_FILTER_HASSUBORDINATES
337         /*
338          * is hasSubordinates used in the filter ?
339          * FIXME: we may compute this directly when parsing the filter
340          */
341         filter_hasSubordinates = filter_has_subordinates( filter );
342 #endif /* SLAP_X_FILTER_HASSUBORDINATES */
343
344 #ifdef LDAP_CLIENT_UPDATE
345         if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
346                 lcupf.f_choice = LDAP_FILTER_AND;
347                 lcupf.f_and = &csnfnot;
348                 lcupf.f_next = NULL;
349
350                 csnfnot.f_choice = LDAP_FILTER_NOT;
351                 csnfnot.f_not = &csnfeq;
352                 csnfnot.f_next = &csnfand;
353
354                 csnfeq.f_choice = LDAP_FILTER_EQUALITY;
355                 csnfeq.f_ava = &aa_eq;
356                 csnfeq.f_av_desc = slap_schema.si_ad_entryCSN;
357                 ber_dupbv(&csnfeq.f_av_value, op->o_clientupdate_state);
358
359                 csnfand.f_choice = LDAP_FILTER_AND;
360                 csnfand.f_and = &csnfge;
361                 csnfand.f_next = NULL;
362
363                 csnfge.f_choice = LDAP_FILTER_GE;
364                 csnfge.f_ava = &aa_ge;
365                 csnfge.f_av_desc = slap_schema.si_ad_entryCSN;
366                 ber_dupbv(&csnfge.f_av_value, op->o_clientupdate_state);
367                 csnfge.f_next = filter;
368         }
369 #endif /* LDAP_CLIENT_UPDATE */
370
371         for ( id = bdb_idl_first( candidates, &cursor );
372                 id != NOID;
373                 id = bdb_idl_next( candidates, &cursor ) )
374         {
375                 int             scopeok = 0;
376 #ifdef SLAP_X_FILTER_HASSUBORDINATES
377                 Attribute       *hasSubordinates = NULL;
378 #endif /* SLAP_X_FILTER_HASSUBORDINATES */
379
380                 /* check for abandon */
381                 if ( op->o_abandon ) {
382                         rc = 0;
383                         goto done;
384                 }
385
386                 /* check time limit */
387                 if ( tlimit != -1 && slap_get_time() > stoptime ) {
388                         send_search_result( conn, op, rc = LDAP_TIMELIMIT_EXCEEDED,
389                                 NULL, NULL, v2refs, NULL, nentries );
390                         goto done;
391                 }
392
393 id2entry_retry:
394                 /* get the entry with reader lock */
395                 rc = bdb_id2entry_r( be, NULL, id, &e, locker, &lock );
396
397                 if (rc == LDAP_BUSY) {
398                         send_ldap_result( conn, op, rc=LDAP_BUSY,
399                                 NULL, "ldap server busy", NULL, NULL );
400                         goto done;
401
402                 } else if ( rc == DB_LOCK_DEADLOCK || rc == DB_LOCK_NOTGRANTED ) {
403                         goto id2entry_retry;    
404                 }
405
406                 if ( e == NULL ) {
407                         if( !BDB_IDL_IS_RANGE(candidates) ) {
408                                 /* only complain for non-range IDLs */
409 #ifdef NEW_LOGGING
410                                 LDAP_LOG ( OPERATION, RESULTS,
411                                         "bdb_search: candidate %ld not found\n", (long) id, 0, 0);
412 #else
413                                 Debug( LDAP_DEBUG_TRACE,
414                                         "bdb_search: candidate %ld not found\n",
415                                         (long) id, 0, 0 );
416 #endif
417                         }
418
419                         goto loop_continue;
420                 }
421
422 #ifdef BDB_SUBENTRIES
423                 if ( is_entry_subentry( e ) ) {
424                         if( scope != LDAP_SCOPE_BASE ) {
425                                 if(!get_subentries_visibility( op )) {
426                                         /* only subentries are visible */
427                                         goto loop_continue;
428                                 }
429
430                         } else if ( get_subentries( op ) &&
431                                 !get_subentries_visibility( op ))
432                         {
433                                 /* only subentries are visible */
434                                 goto loop_continue;
435                         }
436
437                 } else if ( get_subentries_visibility( op )) {
438                         /* only subentries are visible */
439                         goto loop_continue;
440                 }
441 #endif
442
443 #ifdef BDB_ALIASES
444                 if ( deref & LDAP_DEREF_SEARCHING && is_entry_alias( e ) ) {
445                         Entry *matched;
446                         int err;
447                         const char *text;
448                         
449                         e = deref_entry_r( be, e, &err, &matched, &text );
450
451                         if( e == NULL ) {
452                                 e = matched;
453                                 goto loop_continue;
454                         }
455
456                         if( e->e_id == id ) {
457                                 /* circular loop */
458                                 goto loop_continue;
459                         }
460
461                         /* need to skip alias which deref into scope */
462                         if( scope & LDAP_SCOPE_ONELEVEL ) {
463                                 struct berval   pdn;
464                                 
465                                 dnParent( &e->e_nname, &pdn ):
466                                 if ( ber_bvcmp( pdn, &realbase ) ) {
467                                         goto loop_continue;
468                                 }
469
470                         } else if ( dnIsSuffix( &e->e_nname, &realbase ) ) {
471                                 /* alias is within scope */
472 #ifdef NEW_LOGGING
473                                 LDAP_LOG ( OPERATION, RESULTS,
474                                         "bdb_search: \"%s\" in subtree\n", e->edn, 0, 0);
475 #else
476                                 Debug( LDAP_DEBUG_TRACE,
477                                         "bdb_search: \"%s\" in subtree\n",
478                                         e->e_dn, 0, 0 );
479 #endif
480                                 goto loop_continue;
481                         }
482
483                         scopeok = 1;
484                 }
485 #endif
486
487                 /*
488                  * if it's a referral, add it to the list of referrals. only do
489                  * this for non-base searches, and don't check the filter
490                  * explicitly here since it's only a candidate anyway.
491                  */
492                 if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
493                         is_entry_referral( e ) )
494                 {
495                         struct berval   dn;
496
497                         /* check scope */
498                         if ( !scopeok && scope == LDAP_SCOPE_ONELEVEL ) {
499                                 if ( !be_issuffix( be, &e->e_nname ) ) {
500                                         dnParent( &e->e_nname, &dn );
501                                         scopeok = dn_match( &dn, &realbase );
502                                 } else {
503                                         scopeok = (realbase.bv_len == 0);
504                                 }
505
506                         } else if ( !scopeok && scope == LDAP_SCOPE_SUBTREE ) {
507                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
508
509                         } else {
510                                 scopeok = 1;
511                         }
512
513                         if( scopeok ) {
514                                 BerVarray erefs = get_entry_referrals(
515                                         be, conn, op, e );
516                                 BerVarray refs = referral_rewrite( erefs,
517                                         &e->e_name, NULL,
518                                         scope == LDAP_SCOPE_SUBTREE
519                                                 ? LDAP_SCOPE_SUBTREE
520                                                 : LDAP_SCOPE_BASE );
521
522                                 send_search_reference( be, conn, op,
523                                         e, refs, NULL, &v2refs );
524
525                                 ber_bvarray_free( refs );
526
527                         } else {
528 #ifdef NEW_LOGGING
529                                 LDAP_LOG(OPERATION, DETAIL2, 
530                                         "bdb_search: candidate referral %ld scope not okay\n",
531                                         id, 0, 0 );
532 #else
533                                 Debug( LDAP_DEBUG_TRACE,
534                                         "bdb_search: candidate referral %ld scope not okay\n",
535                                         id, 0, 0 );
536 #endif
537                         }
538
539                         goto loop_continue;
540                 }
541
542 #ifdef SLAP_X_FILTER_HASSUBORDINATES
543                 /*
544                  * if hasSubordinates is used in the filter,
545                  * append it to the entry's attributes
546                  */
547                 if ( filter_hasSubordinates ) {
548                         int     hs;
549
550                         rc = bdb_hasSubordinates( be, conn, op, e, &hs);
551                         if ( rc != LDAP_SUCCESS ) {
552                                 goto loop_continue;
553                         }
554
555                         hasSubordinates = slap_operational_hasSubordinate(
556                                 hs == LDAP_COMPARE_TRUE );
557
558                         if ( hasSubordinates == NULL ) {
559                                 goto loop_continue;
560                         }
561
562                         hasSubordinates->a_next = e->e_attrs;
563                         e->e_attrs = hasSubordinates;
564                 }
565 #endif /* SLAP_X_FILTER_HASSUBORDINATES */
566
567                 /* if it matches the filter and scope, send it */
568 #ifdef LDAP_CLIENT_UPDATE
569                 if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
570                         rc = test_filter( be, conn, op, e, &lcupf );
571                 } else
572 #endif /* LDAP_CLIENT_UPDATE */
573                 {
574                         rc = test_filter( be, conn, op, e, filter );
575                 }
576
577 #ifdef SLAP_X_FILTER_HASSUBORDINATES
578                 if ( hasSubordinates ) {
579                         /*
580                          * FIXME: this is fairly inefficient, because 
581                          * if hasSubordinates is among the required
582                          * attrs, it will be added again later;
583                          * maybe we should leave it and check
584                          * check later if it's already present,
585                          * if required
586                          */
587                         e->e_attrs = e->e_attrs->a_next;
588                         attr_free( hasSubordinates );
589                 }
590 #endif /* SLAP_X_FILTER_HASSUBORDINATES */
591
592                 if ( rc == LDAP_COMPARE_TRUE ) {
593                         struct berval   dn;
594
595                         /* check scope */
596                         if ( !scopeok && scope == LDAP_SCOPE_ONELEVEL ) {
597                                 if ( be_issuffix( be, &e->e_nname ) ) {
598                                         scopeok = (realbase.bv_len == 0);
599                                 } else {
600                                         dnParent( &e->e_nname, &dn );
601                                         scopeok = dn_match( &dn, &realbase );
602                                 }
603
604                         } else if ( !scopeok && scope == LDAP_SCOPE_SUBTREE ) {
605                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
606
607                         } else {
608                                 scopeok = 1;
609                         }
610
611                         if ( scopeok ) {
612                                 /* check size limit */
613                                 if ( --slimit == -1 ) {
614                                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
615                                         e = NULL;
616                                         send_search_result( conn, op,
617                                                 rc = LDAP_SIZELIMIT_EXCEEDED, NULL, NULL,
618                                                 v2refs, NULL, nentries );
619                                         goto done;
620                                 }
621
622                                 if (e) {
623                                         int result;
624                                         
625 #if 0   /* noop is masked SLAP_CTRL_UPDATE */
626                                         if( op->o_noop ) {
627                                                 result = 0;
628                                         } else {
629 #endif
630                                                 result = send_search_entry( be, conn, op,
631                                                         e, attrs, attrsonly, NULL);
632
633 #if 0
634                                         }
635 #endif
636
637                                         switch (result) {
638                                         case 0:         /* entry sent ok */
639                                                 nentries++;
640                                                 break;
641                                         case 1:         /* entry not sent */
642                                                 break;
643                                         case -1:        /* connection closed */
644                                                 bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
645                                                 e = NULL;
646                                                 rc = LDAP_OTHER;
647                                                 goto done;
648                                         }
649                                 }
650                         } else {
651 #ifdef NEW_LOGGING
652                                 LDAP_LOG ( OPERATION, RESULTS,
653                                         "bdb_search: %ld scope not okay\n", (long) id, 0, 0);
654 #else
655                                 Debug( LDAP_DEBUG_TRACE,
656                                         "bdb_search: %ld scope not okay\n",
657                                         (long) id, 0, 0 );
658 #endif
659                         }
660                 } else {
661 #ifdef NEW_LOGGING
662                         LDAP_LOG ( OPERATION, RESULTS,
663                                 "bdb_search: %ld does match filter\n", (long) id, 0, 0);
664 #else
665                         Debug( LDAP_DEBUG_TRACE,
666                                 "bdb_search: %ld does match filter\n",
667                                 (long) id, 0, 0 );
668 #endif
669                 }
670
671 loop_continue:
672                 if( e != NULL ) {
673                         /* free reader lock */
674                         bdb_cache_return_entry_r( bdb->bi_dbenv,
675                                 &bdb->bi_cache, e , &lock);
676                         e = NULL;
677                 }
678
679                 ldap_pvt_thread_yield();
680         }
681
682         send_search_result( conn, op,
683                 v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
684                 NULL, NULL, v2refs, NULL, nentries );
685
686         rc = 0;
687
688 done:
689         if( e != NULL ) {
690                 /* free reader lock */
691                 bdb_cache_return_entry_r ( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
692         }
693
694         LOCK_ID_FREE (bdb->bi_dbenv, locker );
695
696         if( v2refs ) ber_bvarray_free( v2refs );
697         if( realbase.bv_val ) ch_free( realbase.bv_val );
698
699         return rc;
700 }
701
702
703 static int base_candidate(
704         BackendDB       *be,
705         Entry   *e,
706         ID              *ids )
707 {
708 #ifdef NEW_LOGGING
709         LDAP_LOG ( OPERATION, ENTRY,
710                 "base_candidate: base: \"%s\" (0x%08lx)\n", e->e_dn, (long) e->e_id, 0);
711 #else
712         Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
713                 e->e_dn, (long) e->e_id, 0);
714 #endif
715
716         ids[0] = 1;
717         ids[1] = e->e_id;
718         return 0;
719 }
720
721 /* Look for "objectClass Present" in this filter.
722  * Also count depth of filter tree while we're at it.
723  */
724 static int oc_filter(
725         Filter *f,
726         int cur,
727         int *max
728 )
729 {
730         int rc = 0;
731
732         if( cur > *max ) *max = cur;
733
734         switch(f->f_choice) {
735         case LDAP_FILTER_PRESENT:
736                 if (f->f_desc == slap_schema.si_ad_objectClass)
737                         rc = 1;
738                 break;
739
740         case LDAP_FILTER_AND:
741         case LDAP_FILTER_OR:
742                 cur++;
743                 for (f=f->f_and; f; f=f->f_next)
744                         rc |= oc_filter(f, cur, max);
745                 break;
746         default:
747                 break;
748         }
749         return rc;
750 }
751
752 static int search_candidates(
753         BackendDB *be,
754         Operation *op,
755         Entry *e,
756         Filter *filter,
757         int scope,
758         int deref,
759         ID      *ids )
760 {
761         int rc, depth = 1;
762         Filter          f, scopef, rf, xf;
763         ID              *stack;
764         AttributeAssertion aa_ref;
765 #ifdef BDB_SUBENTRIES
766         Filter  sf;
767         AttributeAssertion aa_subentry;
768 #endif
769 #ifdef BDB_ALIASES
770         Filter  af;
771         AttributeAssertion aa_alias;
772 #endif
773
774         /*
775          * This routine takes as input a filter (user-filter)
776          * and rewrites it as follows:
777          *      (&(scope=DN)[(objectClass=subentry)]
778          *              (|[(objectClass=referral)(objectClass=alias)](user-filter))
779          */
780
781 #ifdef NEW_LOGGING
782         LDAP_LOG ( OPERATION, ENTRY,
783                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n", 
784                 e->e_dn, (long) e->e_id, scope);
785 #else
786         Debug(LDAP_DEBUG_TRACE,
787                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
788                 e->e_dn, (long) e->e_id, scope );
789 #endif
790
791         xf.f_or = filter;
792         xf.f_choice = LDAP_FILTER_OR;
793         xf.f_next = NULL;
794
795         /* If the user's filter uses objectClass=*,
796          * these clauses are redundant.
797          */
798         if (!oc_filter(filter, 1, &depth) && !get_subentries_visibility(op) ) {
799                 if( !get_manageDSAit(op) ) { /* match referrals */
800                         struct berval bv_ref = { sizeof("REFERRAL")-1, "REFERRAL" };
801                         rf.f_choice = LDAP_FILTER_EQUALITY;
802                         rf.f_ava = &aa_ref;
803                         rf.f_av_desc = slap_schema.si_ad_objectClass;
804                         rf.f_av_value = bv_ref;
805                         rf.f_next = xf.f_or;
806                         xf.f_or = &rf;
807                 }
808
809 #ifdef BDB_ALIASES
810                 if( deref & LDAP_DEREF_SEARCHING ) { /* match aliases */
811                         struct berval bv_alias = { sizeof("ALIAS")-1, "ALIAS" };
812                         af.f_choice = LDAP_FILTER_EQUALITY;
813                         af.f_ava = &aa_alias;
814                         af.f_av_desc = slap_schema.si_ad_objectClass;
815                         af.f_av_value = bv_alias;
816                         af.f_next = xf.f_or;
817                         xf.f_or = &af;
818                 }
819 #endif
820                 /* We added one of these clauses, filter depth increased */
821                 if( xf.f_or != filter ) depth++;
822         }
823
824         f.f_next = NULL;
825         f.f_choice = LDAP_FILTER_AND;
826         f.f_and = &scopef;
827         scopef.f_choice = scope == LDAP_SCOPE_SUBTREE
828                 ? SLAPD_FILTER_DN_SUBTREE
829                 : SLAPD_FILTER_DN_ONE;
830         scopef.f_dn = &e->e_nname;
831         scopef.f_next = xf.f_or == filter ? filter : &xf ;
832         /* Filter depth increased again, adding scope clause */
833         depth++;
834
835 #ifdef BDB_SUBENTRIES
836         if( get_subentries_visibility( op ) ) {
837                 struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
838                 sf.f_choice = LDAP_FILTER_EQUALITY;
839                 sf.f_ava = &aa_subentry;
840                 sf.f_av_desc = slap_schema.si_ad_objectClass;
841                 sf.f_av_value = bv_subentry;
842                 sf.f_next = scopef.f_next;
843                 scopef.f_next = &sf;
844         }
845 #endif
846
847         /* Allocate IDL stack, plus 1 more for former tmp */
848         stack = ch_malloc( (depth + 1) * BDB_IDL_UM_SIZE * sizeof( ID ) );
849
850         rc = bdb_filter_candidates( be, &f, ids, stack, stack+BDB_IDL_UM_SIZE );
851
852         ch_free( stack );
853
854         if( rc ) {
855 #ifdef NEW_LOGGING
856                 LDAP_LOG ( OPERATION, DETAIL1,
857                         "bdb_search_candidates: failed (rc=%d)\n", rc, 0, 0  );
858 #else
859                 Debug(LDAP_DEBUG_TRACE,
860                         "bdb_search_candidates: failed (rc=%d)\n",
861                         rc, NULL, NULL );
862 #endif
863
864         } else {
865 #ifdef NEW_LOGGING
866                 LDAP_LOG ( OPERATION, DETAIL1,
867                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
868                         (long) ids[0], (long) BDB_IDL_FIRST(ids), 
869                         (long) BDB_IDL_LAST(ids));
870 #else
871                 Debug(LDAP_DEBUG_TRACE,
872                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
873                         (long) ids[0],
874                         (long) BDB_IDL_FIRST(ids),
875                         (long) BDB_IDL_LAST(ids) );
876 #endif
877         }
878
879         return rc;
880 }
881