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