]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/search.c
e98e41fb910d0160864be23e3ae2fc8b334f5c2a
[openldap] / servers / slapd / back-bdb / search.c
1 /* search.c - search operation */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 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         Operation *stackop,     /* op with the current threadctx/slab cache */
23         Operation *sop,         /* search op */
24         SlapReply *rs,
25         Entry *e,
26         u_int32_t locker,
27         ID      *ids,
28         ID      *scopes );
29 static void send_pagerequest_response( 
30         Operation *op,
31         SlapReply *rs,
32         ID  lastid,
33         int tentries );
34
35 /* Dereference aliases for a single alias entry. Return the final
36  * dereferenced entry on success, NULL on any failure.
37  */
38 static Entry * deref_base (
39         Operation *op,
40         SlapReply *rs,
41         Entry *e,
42         Entry **matched,
43         u_int32_t locker,
44         DB_LOCK *lock,
45         ID      *tmp,
46         ID      *visited )
47 {
48         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
49         struct berval ndn;
50         EntryInfo *ei;
51         DB_LOCK lockr;
52
53         rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
54         rs->sr_text = "maximum deref depth exceeded";
55
56         while (BDB_IDL_N(tmp) < op->o_bd->be_max_deref_depth) {
57
58                 /* Remember the last entry we looked at, so we can
59                  * report broken links
60                  */
61                 *matched = e;
62
63                 /* If this is part of a subtree or onelevel search,
64                  * have we seen this ID before? If so, quit.
65                  */
66                 if ( visited && bdb_idl_insert( visited, e->e_id ) ) {
67                         e = NULL;
68                         break;
69                 }
70
71                 /* If we've seen this ID during this deref iteration,
72                  * we've hit a loop.
73                  */
74                 if ( bdb_idl_insert( tmp, e->e_id ) ) {
75                         rs->sr_err = LDAP_ALIAS_PROBLEM;
76                         rs->sr_text = "circular alias";
77                         e = NULL;
78                         break;
79                 }
80
81                 /* If there was a problem getting the aliasedObjectName,
82                  * get_alias_dn will have set the error status.
83                  */
84                 if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
85                         e = NULL;
86                         break;
87                 }
88
89                 rs->sr_err = bdb_dn2entry( op, NULL, &ndn, &ei,
90                         0, locker, &lockr );
91
92                 if ( ei ) e = ei->bei_e;
93                 else    e = NULL;
94
95                 if (!e) {
96                         rs->sr_err = LDAP_ALIAS_PROBLEM;
97                         rs->sr_text = "aliasedObject not found";
98                         break;
99                 }
100
101                 /* Free the previous entry, continue to work with the
102                  * one we just retrieved.
103                  */
104                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache,
105                         *matched, lock);
106                 *lock = lockr;
107
108                 /* We found a regular entry. Return this to the caller. The
109                  * entry is still locked for Read.
110                  */
111                 if (!is_entry_alias(e)) {
112                         rs->sr_err = LDAP_SUCCESS;
113                         rs->sr_text = NULL;
114                         break;
115                 }
116         }
117         return e;
118 }
119
120 /* Look for and dereference all aliases within the search scope. Adds
121  * the dereferenced entries to the "ids" list. Requires "stack" to be
122  * able to hold 8 levels of DB_SIZE IDLs. Of course we're hardcoded to
123  * require a minimum of 8 UM_SIZE IDLs so this is never a problem.
124  */
125 static int search_aliases(
126         Operation *op,
127         SlapReply *rs,
128         Entry *e,
129         u_int32_t locker,
130         ID *ids,
131         ID *scopes,
132         ID *stack
133 )
134 {
135         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
136         ID *aliases, *curscop, *subscop, *visited, *newsubs, *oldsubs, *tmp;
137         ID cursora, ida, cursoro, ido, *subscop2;
138         Entry *matched, *a;
139         EntryInfo *ei;
140         struct berval bv_alias = { sizeof("alias")-1, "alias" };
141         AttributeAssertion aa_alias;
142         Filter  af;
143         DB_LOCK locka, lockr;
144         int first = 1;
145
146         aliases = stack;        /* IDL of all aliases in the database */
147         curscop = aliases + BDB_IDL_DB_SIZE;    /* Aliases in the current scope */
148         subscop = curscop + BDB_IDL_DB_SIZE;    /* The current scope */
149         visited = subscop + BDB_IDL_DB_SIZE;    /* IDs we've seen in this search */
150         newsubs = visited + BDB_IDL_DB_SIZE;    /* New subtrees we've added */
151         oldsubs = newsubs + BDB_IDL_DB_SIZE;    /* Subtrees added previously */
152         tmp = oldsubs + BDB_IDL_DB_SIZE;        /* Scratch space for deref_base() */
153
154         /* A copy of subscop, because subscop gets clobbered by
155          * the bdb_idl_union/intersection routines
156          */
157         subscop2 = tmp + BDB_IDL_DB_SIZE;
158
159         af.f_choice = LDAP_FILTER_EQUALITY;
160         af.f_ava = &aa_alias;
161         af.f_av_desc = slap_schema.si_ad_objectClass;
162         af.f_av_value = bv_alias;
163         af.f_next = NULL;
164
165         /* Find all aliases in database */
166         BDB_IDL_ZERO( aliases );
167         rs->sr_err = bdb_filter_candidates( op, &af, aliases,
168                 curscop, visited );
169         if (rs->sr_err != LDAP_SUCCESS) {
170                 return rs->sr_err;
171         }
172         oldsubs[0] = 1;
173         oldsubs[1] = e->e_id;
174
175         BDB_IDL_ZERO( ids );
176         BDB_IDL_ZERO( visited );
177         BDB_IDL_ZERO( newsubs );
178
179         cursoro = 0;
180         ido = bdb_idl_first( oldsubs, &cursoro );
181
182         for (;;) {
183                 /* Set curscop to only the aliases in the current scope. Start with
184                  * all the aliases, obtain the IDL for the current scope, and then
185                  * get the intersection of these two IDLs. Add the current scope
186                  * to the cumulative list of candidates.
187                  */
188                 BDB_IDL_CPY( curscop, aliases );
189                 rs->sr_err = bdb_dn2idl( op, e, subscop,
190                                         subscop2+BDB_IDL_DB_SIZE );
191                 if (first) {
192                         first = 0;
193                 } else {
194                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, e, &locka);
195                 }
196                 BDB_IDL_CPY(subscop2, subscop);
197                 rs->sr_err = bdb_idl_intersection(curscop, subscop);
198                 bdb_idl_union( ids, subscop2 );
199
200                 /* Dereference all of the aliases in the current scope. */
201                 cursora = 0;
202                 for (ida = bdb_idl_first(curscop, &cursora); ida != NOID;
203                         ida = bdb_idl_next(curscop, &cursora))
204                 {
205                         ei = NULL;
206                         rs->sr_err = bdb_cache_find_id(op, NULL,
207                                 ida, &ei, 0, locker, &lockr );
208                         if (rs->sr_err != LDAP_SUCCESS) {
209                                 continue;
210                         }
211                         a = ei->bei_e;
212
213                         /* This should only happen if the curscop IDL has maxed out and
214                          * turned into a range that spans IDs indiscriminately
215                          */
216                         if (!is_entry_alias(a)) {
217                                 bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache,
218                                         a, &lockr);
219                                 continue;
220                         }
221
222                         /* Actually dereference the alias */
223                         BDB_IDL_ZERO(tmp);
224                         a = deref_base( op, rs, a, &matched, locker, &lockr,
225                                 tmp, visited );
226                         if (a) {
227                                 /* If the target was not already in our current candidates,
228                                  * make note of it in the newsubs list. Also
229                                  * set it in the scopes list so that bdb_search
230                                  * can check it.
231                                  */
232                                 if (bdb_idl_insert(ids, a->e_id) == 0) {
233                                         bdb_idl_insert(newsubs, a->e_id);
234                                         bdb_idl_insert(scopes, a->e_id);
235                                 }
236                                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache,
237                                         a, &lockr);
238
239                         } else if (matched) {
240                                 /* Alias could not be dereferenced, or it deref'd to
241                                  * an ID we've already seen. Ignore it.
242                                  */
243                                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache,
244                                         matched, &lockr );
245                                 rs->sr_text = NULL;
246                         }
247                 }
248                 /* If this is a OneLevel search, we're done; oldsubs only had one
249                  * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
250                  */
251                 if (op->ors_scope != LDAP_SCOPE_SUBTREE) break;
252 nextido:
253                 ido = bdb_idl_next( oldsubs, &cursoro );
254                 
255                 /* If we're done processing the old scopes, did we add any new
256                  * scopes in this iteration? If so, go back and do those now.
257                  */
258                 if (ido == NOID) {
259                         if (BDB_IDL_IS_ZERO(newsubs)) break;
260                         BDB_IDL_CPY(oldsubs, newsubs);
261                         BDB_IDL_ZERO(newsubs);
262                         cursoro = 0;
263                         ido = bdb_idl_first( oldsubs, &cursoro );
264                 }
265
266                 /* Find the entry corresponding to the next scope. If it can't
267                  * be found, ignore it and move on. This should never happen;
268                  * we should never see the ID of an entry that doesn't exist.
269                  * Set the name so that the scope's IDL can be retrieved.
270                  */
271                 ei = NULL;
272                 rs->sr_err = bdb_cache_find_id(op, NULL, ido, &ei,
273                         0, locker, &locka );
274                 if (rs->sr_err != LDAP_SUCCESS) goto nextido;
275                 e = ei->bei_e;
276         }
277         return rs->sr_err;
278 }
279
280 static
281 int is_sync_protocol( Operation *op )
282 {
283         if ( op->o_sync_mode & SLAP_SYNC_REFRESH_AND_PERSIST )
284                 return 1;
285         return 0;
286 }
287         
288 #define IS_BDB_REPLACE(type) (( type == LDAP_PSEARCH_BY_DELETE ) || \
289         ( type == LDAP_PSEARCH_BY_SCOPEOUT ))
290 #define IS_PSEARCH (op != sop)
291
292 int
293 bdb_abandon( Operation *op, SlapReply *rs )
294 {
295         Operation       *ps_list;
296         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
297
298         LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
299                 if ( ps_list->o_connid == op->o_connid ) {
300                         if ( ps_list->o_msgid == op->oq_abandon.rs_msgid ) {
301                                 ps_list->o_abandon = 1;
302                                 LDAP_LIST_REMOVE( ps_list, o_ps_link );
303                                 slap_op_free ( ps_list );
304                                 return LDAP_SUCCESS;
305                         }
306                 }
307         }
308         return LDAP_UNAVAILABLE;
309 }
310
311 int
312 bdb_cancel( Operation *op, SlapReply *rs )
313 {
314         Operation       *ps_list;
315         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
316
317         LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
318                 if ( ps_list->o_connid == op->o_connid ) {
319                         if ( ps_list->o_msgid == op->oq_cancel.rs_msgid ) {
320                                 ps_list->o_cancel = SLAP_CANCEL_DONE;
321                                 LDAP_LIST_REMOVE( ps_list, o_ps_link );
322
323 #if 0
324                                 bdb_build_sync_done_ctrl( conn, ps_list, ps_list->ctrls,
325                                         1, &latest_entrycsn_bv );
326                                 send_ldap_result( conn, ps_list, LDAP_CANCELLED,
327                                         NULL, NULL, NULL, ps_list->ctrls, ps_list->nentries);
328 #endif
329                                 rs->sr_err = LDAP_CANCELLED;
330                                 send_ldap_result( ps_list, rs );
331
332                                 if ( ps_list->o_tmpmemctx ) {
333                                         sl_mem_destroy( NULL, ps_list->o_tmpmemctx );
334                                 }
335
336                                 slap_op_free ( ps_list );
337                                 return LDAP_SUCCESS;
338                         }
339                 }
340         }
341         return LDAP_UNAVAILABLE;
342 }
343
344 int bdb_search( Operation *op, SlapReply *rs )
345 {
346         return bdb_do_search( op, rs, op, NULL, 0 );
347 }
348
349 /* For persistent searches, op is the currently executing operation,
350  * sop is the persistent search. For regular searches, sop = op.
351  */
352 int
353 bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
354         Entry *ps_e, int ps_type )
355 {
356         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
357         time_t          stoptime;
358         ID              id, cursor;
359         ID              candidates[BDB_IDL_UM_SIZE];
360         ID              scopes[BDB_IDL_DB_SIZE];
361         Entry           *e = NULL, base;
362         Entry   *matched = NULL;
363         EntryInfo       *ei;
364         struct berval   realbase = { 0, NULL };
365         int             manageDSAit;
366         int             tentries = 0;
367         ID              lastid = NOID;
368         AttributeName   *attrs;
369
370         Filter          contextcsnand, contextcsnle, cookief, csnfnot, csnfeq, csnfand, csnfge;
371         Filter          omitcsnf, omitcsnfle;
372         AttributeAssertion aa_ge, aa_eq, aa_le;
373         int             entry_count = 0;
374         struct berval *search_context_csn = NULL;
375         struct berval ctxcsn_rdn = { 0, NULL };
376         struct berval ctxcsn_ndn = { 0, NULL };
377         EntryInfo       *ctxcsn_ei;
378         Entry           *ctxcsn_e;
379         DB_LOCK         ctxcsn_lock;
380         Attribute       *csn_a;
381 #if 0
382         struct berval   entrycsn_bv = { 0, NULL };
383 #endif
384         LDAPControl     *ctrls[SLAP_SEARCH_MAX_CTRLS];
385         int             num_ctrls = 0;
386         AttributeName   uuid_attr[2];
387         int             rc_sync = 0;
388         int             entry_sync_state = -1;
389         AttributeName   null_attr;
390         int             no_sync_state_change = 0;
391         struct slap_limits_set *limit = NULL;
392         int isroot = 0;
393
394         u_int32_t       locker = 0;
395         DB_LOCK         lock;
396
397 #ifdef NEW_LOGGING
398         LDAP_LOG( OPERATION, ENTRY, "bdb_back_search\n", 0, 0, 0 );
399 #else
400         Debug( LDAP_DEBUG_TRACE, "=> bdb_back_search\n",
401                 0, 0, 0);
402 #endif
403         attrs = sop->oq_search.rs_attrs;
404
405         /* psearch needs to be registered before refresh begins */
406         /* psearch and refresh transmission is serialized in send_ldap_ber() */
407         if ( !IS_PSEARCH && sop->o_sync_mode & SLAP_SYNC_PERSIST ) {
408                 LDAP_LIST_INSERT_HEAD( &bdb->bi_psearch_list, sop, o_ps_link );
409         }
410         null_attr.an_desc = NULL;
411         null_attr.an_oc = NULL;
412         null_attr.an_name.bv_len = 0;
413         null_attr.an_name.bv_val = NULL;
414
415         for ( num_ctrls = 0; num_ctrls < SLAP_SEARCH_MAX_CTRLS; num_ctrls++ ) {
416                 ctrls[num_ctrls] = NULL;
417         }
418         num_ctrls = 0;
419
420         if ( IS_PSEARCH && IS_BDB_REPLACE(ps_type)) {
421                 attrs = uuid_attr;
422                 attrs[0].an_desc = NULL;
423                 attrs[0].an_oc = NULL;
424                 attrs[0].an_name.bv_len = 0;
425                 attrs[0].an_name.bv_val = NULL;
426         }
427
428         manageDSAit = get_manageDSAit( sop );
429
430         /* Sync control overrides manageDSAit */
431
432         if ( !IS_PSEARCH && sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
433                 if ( manageDSAit == SLAP_NO_CONTROL )
434                         manageDSAit = SLAP_CRITICAL_CONTROL;
435         } else if ( IS_PSEARCH ) {
436                 if ( manageDSAit == SLAP_NO_CONTROL )
437                         manageDSAit = SLAP_CRITICAL_CONTROL;
438         }
439
440         rs->sr_err = LOCK_ID (bdb->bi_dbenv, &locker );
441
442         switch(rs->sr_err) {
443         case 0:
444                 break;
445         default:
446                 send_ldap_error( sop, rs, LDAP_OTHER, "internal error" );
447                 return rs->sr_err;
448         }
449
450         if ( sop->o_req_ndn.bv_len == 0 ) {
451                 /* DIT root special case */
452                 e = (Entry *) &slap_entry_root;
453                 rs->sr_err = LDAP_SUCCESS;
454         } else {
455 dn2entry_retry:
456                 /* get entry with reader lock */
457                 rs->sr_err = bdb_dn2entry( op, NULL, &sop->o_req_ndn, &ei,
458                         1, locker, &lock );
459         }
460
461         switch(rs->sr_err) {
462         case DB_NOTFOUND:
463                 matched = ei->bei_e; break;
464         case 0:
465                 e = ei->bei_e; break;
466         case LDAP_BUSY:
467                 send_ldap_error( sop, rs, LDAP_BUSY, "ldap server busy" );
468                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
469                 return LDAP_BUSY;
470         case DB_LOCK_DEADLOCK:
471         case DB_LOCK_NOTGRANTED:
472                 goto dn2entry_retry;
473         default:
474                 send_ldap_error( sop, rs, LDAP_OTHER, "internal error" );
475                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
476                 return rs->sr_err;
477         }
478
479         if ( e && (op->ors_deref & LDAP_DEREF_FINDING) && is_entry_alias(e) ) {
480                 BDB_IDL_ZERO(candidates);
481                 e = deref_base( op, rs, e, &matched, locker, &lock,
482                         candidates, NULL );
483         }
484
485         if ( e == NULL ) {
486                 struct berval matched_dn = { 0, NULL };
487
488                 if ( matched != NULL ) {
489                         BerVarray erefs;
490                         ber_dupbv( &matched_dn, &matched->e_name );
491
492                         erefs = is_entry_referral( matched )
493                                 ? get_entry_referrals( op, matched )
494                                 : NULL;
495
496                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache,
497                                 matched, &lock);
498                         matched = NULL;
499
500                         if( erefs ) {
501                                 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
502                                         &sop->o_req_dn, sop->oq_search.rs_scope );
503                                 ber_bvarray_free( erefs );
504                         }
505
506                 } else {
507                         rs->sr_ref = referral_rewrite( default_referral,
508                                 NULL, &sop->o_req_dn, sop->oq_search.rs_scope );
509                 }
510
511                 rs->sr_err = LDAP_REFERRAL;
512                 rs->sr_matched = matched_dn.bv_val;
513                 send_ldap_result( sop, rs );
514
515                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
516                 if ( rs->sr_ref ) {
517                         ber_bvarray_free( rs->sr_ref );
518                         rs->sr_ref = NULL;
519                 }
520                 if ( matched_dn.bv_val ) {
521                         ber_memfree( matched_dn.bv_val );
522                         rs->sr_matched = NULL;
523                 }
524                 return rs->sr_err;
525         }
526
527         if (!manageDSAit && e != &slap_entry_root && is_entry_referral( e ) ) {
528                 /* entry is a referral, don't allow add */
529                 struct berval matched_dn;
530                 BerVarray erefs;
531                 
532                 ber_dupbv( &matched_dn, &e->e_name );
533                 erefs = get_entry_referrals( op, e );
534
535                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
536                 e = NULL;
537
538                 if( erefs ) {
539                         rs->sr_ref = referral_rewrite( erefs, &matched_dn,
540                                 &sop->o_req_dn, sop->oq_search.rs_scope );
541                         ber_bvarray_free( erefs );
542                 }
543
544 #ifdef NEW_LOGGING
545                 LDAP_LOG ( OPERATION, RESULTS, 
546                         "bdb_search: entry is referral\n", 0, 0, 0 );
547 #else
548                 Debug( LDAP_DEBUG_TRACE, "bdb_search: entry is referral\n",
549                         0, 0, 0 );
550 #endif
551
552                 if (!rs->sr_ref) rs->sr_text = "bad_referral object";
553                 rs->sr_err = LDAP_REFERRAL;
554                 rs->sr_matched = matched_dn.bv_val;
555                 send_ldap_result( sop, rs );
556
557                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
558                 ber_bvarray_free( rs->sr_ref );
559                 rs->sr_ref = NULL;
560                 ber_memfree( matched_dn.bv_val );
561                 rs->sr_matched = NULL;
562                 return 1;
563         }
564
565         if ( get_assert( op ) &&
566                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
567         {
568                 rs->sr_err = LDAP_ASSERTION_FAILED;
569                 send_ldap_result( sop, rs );
570                 return 1;
571         }
572
573         /* if not root, get appropriate limits */
574         if ( be_isroot( op->o_bd, &sop->o_ndn ) ) {
575                 isroot = 1;
576         } else {
577                 ( void ) get_limits( op->o_bd, &sop->o_ndn, &limit );
578         }
579
580         /* The time/size limits come first because they require very little
581          * effort, so there's no chance the candidates are selected and then 
582          * the request is not honored only because of time/size constraints */
583
584         /* if no time limit requested, use soft limit (unless root!) */
585         if ( isroot ) {
586                 if ( sop->oq_search.rs_tlimit == 0 ) {
587                         sop->oq_search.rs_tlimit = -1;  /* allow root to set no limit */
588                 }
589
590                 if ( sop->oq_search.rs_slimit == 0 ) {
591                         sop->oq_search.rs_slimit = -1;
592                 }
593
594         } else {
595                 /* if no limit is required, use soft limit */
596                 if ( sop->oq_search.rs_tlimit <= 0 ) {
597                         sop->oq_search.rs_tlimit = limit->lms_t_soft;
598
599                 /* if requested limit higher than hard limit, abort */
600                 } else if ( sop->oq_search.rs_tlimit > limit->lms_t_hard ) {
601                         /* no hard limit means use soft instead */
602                         if ( limit->lms_t_hard == 0
603                                         && limit->lms_t_soft > -1
604                                         && sop->oq_search.rs_tlimit > limit->lms_t_soft ) {
605                                 sop->oq_search.rs_tlimit = limit->lms_t_soft;
606
607                         /* positive hard limit means abort */
608                         } else if ( limit->lms_t_hard > 0 ) {
609                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
610                                 send_ldap_result( sop, rs );
611                                 rs->sr_err = LDAP_SUCCESS;
612                                 goto done;
613                         }
614                 
615                         /* negative hard limit means no limit */
616                 }
617                 
618                 /* if no limit is required, use soft limit */
619                 if ( sop->oq_search.rs_slimit <= 0 ) {
620                         if ( get_pagedresults(sop) && limit->lms_s_pr != 0 ) {
621                                 sop->oq_search.rs_slimit = limit->lms_s_pr;
622                         } else {
623                                 sop->oq_search.rs_slimit = limit->lms_s_soft;
624                         }
625
626                 /* if requested limit higher than hard limit, abort */
627                 } else if ( sop->oq_search.rs_slimit > limit->lms_s_hard ) {
628                         /* no hard limit means use soft instead */
629                         if ( limit->lms_s_hard == 0
630                                         && limit->lms_s_soft > -1
631                                         && sop->oq_search.rs_slimit > limit->lms_s_soft ) {
632                                 sop->oq_search.rs_slimit = limit->lms_s_soft;
633
634                         /* positive hard limit means abort */
635                         } else if ( limit->lms_s_hard > 0 ) {
636                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
637                                 send_ldap_result( sop, rs );
638                                 rs->sr_err = LDAP_SUCCESS;      
639                                 goto done;
640                         }
641                         
642                         /* negative hard limit means no limit */
643                 }
644         }
645
646         /* compute it anyway; root does not use it */
647         stoptime = op->o_time + sop->oq_search.rs_tlimit;
648
649         /* need normalized dn below */
650         ber_dupbv( &realbase, &e->e_nname );
651
652         /* Copy info to base, must free entry before accessing the database
653          * in search_candidates, to avoid deadlocks.
654          */
655         base.e_private = e->e_private;
656         base.e_nname = realbase;
657         base.e_id = e->e_id;
658
659         if ( e != &slap_entry_root ) {
660                 bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
661         }
662         e = NULL;
663
664         if ( sop->o_sync_mode != SLAP_SYNC_NONE ) {
665                 if ( sop->o_bd->syncinfo ) {
666                         char substr[67];
667                         sprintf( substr, "cn=syncrepl%d", sop->o_bd->syncinfo->id );
668                         ber_str2bv( substr, strlen( substr ), 0, &ctxcsn_rdn );
669                         build_new_dn( &ctxcsn_ndn, &op->o_bd->be_nsuffix[0], &ctxcsn_rdn );
670                 } else {
671                         ber_str2bv( "cn=ldapsync", strlen("cn=ldapsync"), 0, &ctxcsn_rdn );
672                         build_new_dn( &ctxcsn_ndn, &op->o_bd->be_nsuffix[0], &ctxcsn_rdn );
673                 }
674
675 ctxcsn_retry :
676                 rs->sr_err = bdb_dn2entry( op, NULL, &ctxcsn_ndn, &ctxcsn_ei, 0, locker, &ctxcsn_lock );
677
678                 switch(rs->sr_err) {
679                 case 0:
680                         e = ei->bei_e; break;
681                 case LDAP_BUSY:
682                         send_ldap_error( sop, rs, LDAP_BUSY, "ldap server busy" );
683                         LOCK_ID_FREE (bdb->bi_dbenv, locker );
684                         return LDAP_BUSY;
685                 case DB_LOCK_DEADLOCK:
686                 case DB_LOCK_NOTGRANTED:
687                         goto ctxcsn_retry;
688                 case DB_NOTFOUND:
689                         send_ldap_error( sop, rs, LDAP_OTHER, "context csn entry not present" );
690                         LOCK_ID_FREE( bdb->bi_dbenv, locker );
691                         return rs->sr_err;
692                 default:
693                         send_ldap_error( sop, rs, LDAP_OTHER, "internal error" );
694                         LOCK_ID_FREE (bdb->bi_dbenv, locker );
695                         return rs->sr_err;
696                 }
697
698                 if ( ctxcsn_ei ) {
699                         ctxcsn_e = ctxcsn_ei->bei_e;
700                 }
701
702                 if ( ctxcsn_e ) {
703                         if ( sop->o_bd->syncinfo ) {
704                                 csn_a = attr_find( ctxcsn_e->e_attrs, slap_schema.si_ad_syncreplCookie );
705                         } else {
706                                 csn_a = attr_find( ctxcsn_e->e_attrs, slap_schema.si_ad_contextCSN );
707                         }
708                         if ( csn_a ) {
709                                 search_context_csn = ber_dupbv( NULL, &csn_a->a_vals[0] );
710                         } else {
711                                 search_context_csn = NULL;
712                         }
713                 } else {
714                         search_context_csn = NULL;
715                 }
716         }
717
718         /* select candidates */
719         if ( sop->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
720                 rs->sr_err = base_candidate( op->o_bd, &base, candidates );
721
722         } else {
723                 BDB_IDL_ZERO( candidates );
724                 BDB_IDL_ZERO( scopes );
725                 rs->sr_err = search_candidates( op, sop, rs, &base, locker, candidates, scopes );
726         }
727
728         if ( sop->o_sync_mode != SLAP_SYNC_NONE ) {
729                 bdb_cache_entry_db_unlock( bdb->bi_dbenv, &ctxcsn_lock );
730         }
731
732         /* start cursor at beginning of candidates.
733          */
734         cursor = 0;
735         if (IS_PSEARCH) {
736                 if ( !BDB_IDL_IS_RANGE( candidates ) ) {
737                         cursor = bdb_idl_search( candidates, ps_e->e_id );
738                         if ( candidates[cursor] != ps_e->e_id ) {
739                                 rs->sr_err = LDAP_SUCCESS;
740                                 goto done;
741                         }
742                 } else {
743                         if ( ps_e->e_id < BDB_IDL_RANGE_FIRST(candidates)
744                            || ps_e->e_id > BDB_IDL_RANGE_LAST(candidates)){
745                                 rs->sr_err = LDAP_SUCCESS;
746                                 goto done;
747                         }
748                 }
749                 candidates[0] = 1;
750                 candidates[1] = ps_e->e_id;
751         }
752
753         if ( candidates[0] == 0 ) {
754 #ifdef NEW_LOGGING
755                 LDAP_LOG ( OPERATION, RESULTS,
756                         "bdb_search: no candidates\n", 0, 0, 0 );
757 #else
758                 Debug( LDAP_DEBUG_TRACE, "bdb_search: no candidates\n",
759                         0, 0, 0 );
760 #endif
761
762                 rs->sr_err = LDAP_SUCCESS;
763                 rs->sr_entry = NULL;
764                 send_ldap_result( sop, rs );
765                 goto done;
766         }
767
768         /* if not root and candidates exceed to-be-checked entries, abort */
769         if ( !isroot && limit->lms_s_unchecked != -1 ) {
770                 if ( BDB_IDL_N(candidates) > (unsigned) limit->lms_s_unchecked ) {
771                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
772                         send_ldap_result( sop, rs );
773                         rs->sr_err = LDAP_SUCCESS;
774                         goto done;
775                 }
776         }
777
778         if ( isroot || !limit->lms_s_pr_hide ) {
779                 tentries = BDB_IDL_N(candidates);
780         }
781
782 #ifdef LDAP_CONTROL_PAGEDRESULTS
783         if ( get_pagedresults(sop) ) {
784                 if ( sop->o_pagedresults_state.ps_cookie == 0 ) {
785                         id = 0;
786                 } else {
787                         if ( sop->o_pagedresults_size == 0 ) {
788                                 rs->sr_err = LDAP_SUCCESS;
789                                 rs->sr_text = "search abandoned by pagedResult size=0";
790                                 send_ldap_result( sop, rs );
791                                 goto done;
792                         }
793                         for ( id = bdb_idl_first( candidates, &cursor );
794                                 id != NOID && id <= (ID)( sop->o_pagedresults_state.ps_cookie );
795                                 id = bdb_idl_next( candidates, &cursor ) );
796                 }
797                 if ( cursor == NOID ) {
798 #ifdef NEW_LOGGING
799                         LDAP_LOG ( OPERATION, RESULTS, 
800                                 "bdb_search: no paged results candidates\n", 
801                         0, 0, 0 );
802 #else
803                         Debug( LDAP_DEBUG_TRACE, 
804                                 "bdb_search: no paged results candidates\n",
805                                 0, 0, 0 );
806 #endif
807                         send_pagerequest_response( sop, rs, lastid, 0 );
808
809                         rs->sr_err = LDAP_OTHER;
810                         goto done;
811                 }
812                 goto loop_begin;
813         }
814 #endif
815
816         if ( (sop->o_sync_mode & SLAP_SYNC_REFRESH) || IS_PSEARCH )
817         {
818                 MatchingRule    *mr;
819                 const char              *text;
820                 int                             match;
821
822                 cookief.f_choice = LDAP_FILTER_AND;
823                 cookief.f_and = &csnfnot;
824                 cookief.f_next = NULL;
825
826                 csnfnot.f_choice = LDAP_FILTER_NOT;
827                 csnfnot.f_not = &csnfeq;
828                 csnfnot.f_next = &csnfand;
829
830                 csnfeq.f_choice = LDAP_FILTER_EQUALITY;
831                 csnfeq.f_ava = &aa_eq;
832                 csnfeq.f_av_desc = slap_schema.si_ad_entryCSN;
833                 csnfeq.f_av_value = sop->o_sync_state;
834
835                 csnfand.f_choice = LDAP_FILTER_AND;
836                 csnfand.f_and = &csnfge;
837                 csnfand.f_next = NULL;
838
839                 csnfge.f_choice = LDAP_FILTER_GE;
840                 csnfge.f_ava = &aa_ge;
841                 csnfge.f_av_desc = slap_schema.si_ad_entryCSN;
842                 csnfge.f_av_value = sop->o_sync_state;
843
844                 if ( search_context_csn && !IS_PSEARCH ) {
845                         csnfge.f_next = &contextcsnand;
846
847                         contextcsnand.f_choice = LDAP_FILTER_AND;
848                         contextcsnand.f_and = &contextcsnle;
849                         contextcsnand.f_next = NULL;
850         
851                         contextcsnle.f_choice = LDAP_FILTER_LE;
852                         contextcsnle.f_ava = &aa_le;
853                         contextcsnle.f_av_desc = slap_schema.si_ad_entryCSN;
854                         contextcsnle.f_av_value = *search_context_csn;
855                         contextcsnle.f_next = sop->oq_search.rs_filter;
856
857                         mr = slap_schema.si_ad_entryCSN->ad_type->sat_ordering;
858                         if ( sop->o_sync_state.bv_len != 0 ) {
859                                 value_match( &match, slap_schema.si_ad_entryCSN, mr,
860                                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
861                                                         &sop->o_sync_state, search_context_csn, &text );
862                         } else {
863                                 match = -1;
864                         }
865                         no_sync_state_change = !match;
866                 } else {
867                         csnfge.f_next = sop->oq_search.rs_filter;
868                 }
869         }
870
871         for ( id = bdb_idl_first( candidates, &cursor );
872                 id != NOID;
873                 id = bdb_idl_next( candidates, &cursor ) )
874         {
875                 int             scopeok = 0;
876
877 loop_begin:
878                 /* check for abandon */
879                 if ( sop->o_abandon ) {
880                         rs->sr_err = LDAP_SUCCESS;
881                         goto done;
882                 }
883
884 #ifdef LDAP_EXOP_X_CANCEL
885                 if ( sop->o_cancel ) {
886                         assert( sop->o_cancel == SLAP_CANCEL_REQ );
887                         rs->sr_err = LDAP_CANCELLED;
888                         send_ldap_result( sop, rs );
889                         sop->o_cancel = SLAP_CANCEL_ACK;
890                         rs->sr_err = LDAP_SUCCESS;
891                         goto done;
892                 }
893 #endif
894
895                 /* check time limit */
896                 if ( sop->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
897                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
898                         rs->sr_ref = rs->sr_v2ref;
899                         send_ldap_result( sop, rs );
900                         rs->sr_err = LDAP_SUCCESS;
901                         goto done;
902                 }
903
904
905                 if (!IS_PSEARCH) {
906 id2entry_retry:
907                         /* get the entry with reader lock */
908                         ei = NULL;
909                         rs->sr_err = bdb_cache_find_id( op, NULL,
910                                 id, &ei, 0, locker, &lock );
911
912                         if (rs->sr_err == LDAP_BUSY) {
913                                 rs->sr_text = "ldap server busy";
914                                 send_ldap_result( sop, rs );
915                                 goto done;
916
917                         } else if ( rs->sr_err == DB_LOCK_DEADLOCK
918                                 || rs->sr_err == DB_LOCK_NOTGRANTED )
919                         {
920                                 goto id2entry_retry;    
921                         }
922
923                         if ( ei && rs->sr_err == LDAP_SUCCESS ) {
924                                 e = ei->bei_e;
925                         } else {
926                                 e = NULL;
927                         }
928
929                         if ( e == NULL ) {
930                                 if( !BDB_IDL_IS_RANGE(candidates) ) {
931                                         /* only complain for non-range IDLs */
932 #ifdef NEW_LOGGING
933                                         LDAP_LOG ( OPERATION, RESULTS,
934                                                 "bdb_search: candidate %ld not found\n",
935                                                 (long) id, 0, 0);
936 #else
937                                         Debug( LDAP_DEBUG_TRACE,
938                                                 "bdb_search: candidate %ld not found\n",
939                                                 (long) id, 0, 0 );
940 #endif
941                                 }
942
943                                 goto loop_continue;
944                         }
945                 } else {
946                         e = ps_e;
947                 }
948
949                 rs->sr_entry = e;
950 #ifdef BDB_SUBENTRIES
951                 /* FIXME: send all but syncrepl
952                 if ( !is_sync_protocol( sop ) ) {
953                 */
954                         if ( is_entry_subentry( e ) ) {
955                                 if( sop->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
956                                         if(!get_subentries_visibility( sop )) {
957                                                 /* only subentries are visible */
958                                                 goto loop_continue;
959                                         }
960
961                                 } else if ( get_subentries( sop ) &&
962                                         !get_subentries_visibility( sop ))
963                                 {
964                                         /* only subentries are visible */
965                                         goto loop_continue;
966                                 }
967
968                         } else if ( get_subentries_visibility( sop )) {
969                                 /* only subentries are visible */
970                                 goto loop_continue;
971                         }
972                 /*
973                 }
974                 */
975 #endif
976
977                 /* Does this candidate actually satisfy the search scope?
978                  *
979                  * Note that we don't lock access to the bei_parent pointer.
980                  * Since only leaf nodes can be deleted, the parent of any
981                  * node will always be a valid node. Also since we have
982                  * a Read lock on the data, it cannot be renamed out of the
983                  * scope while we are looking at it, and unless we're using
984                  * BDB_HIER, its parents cannot be moved either.
985                  */
986                 switch( sop->ors_scope ) {
987                 case LDAP_SCOPE_BASE:
988                         /* This is always true, yes? */
989                         if ( id == base.e_id )
990                                 scopeok = 1;
991                         break;
992                 case LDAP_SCOPE_ONELEVEL:
993                         if ( ei->bei_parent->bei_id == base.e_id )
994                                 scopeok = 1;
995                         break;
996                 case LDAP_SCOPE_SUBTREE:
997                         { EntryInfo *tmp;
998                         for ( tmp = BEI(e); tmp->bei_parent;
999                                 tmp = tmp->bei_parent ) {
1000                                 if ( tmp->bei_id == base.e_id ) {
1001                                         scopeok = 1;
1002                                         break;
1003                                 }
1004                         } }
1005                         break;
1006                 }
1007
1008 #ifdef BDB_ALIASES
1009                 /* aliases were already dereferenced in candidate list */
1010                 if ( sop->ors_deref & LDAP_DEREF_SEARCHING ) {
1011                         /* but if the search base is an alias, and we didn't
1012                          * deref it when finding, return it.
1013                          */
1014                         if ( is_entry_alias(e) &&
1015                                 ((sop->ors_deref & LDAP_DEREF_FINDING)
1016                                   || !bvmatch(&e->e_nname, &op->o_req_ndn)))
1017                         {
1018                                 goto loop_continue;
1019                         }
1020
1021                         /* scopes is only non-empty for onelevel or subtree */
1022                         if ( !scopeok && BDB_IDL_N(scopes) ) {
1023                                 unsigned x;
1024                                 if ( sop->ors_scope == LDAP_SCOPE_ONELEVEL ) {
1025                                         x = bdb_idl_search( scopes,
1026                                                 e->e_id );
1027                                         if ( scopes[x] == e->e_id )
1028                                                 scopeok = 1;
1029                                 } else {
1030                                 /* subtree, walk up the tree */
1031                                         EntryInfo *tmp = BEI(e);
1032                                         for (;tmp->bei_parent;
1033                                                 tmp=tmp->bei_parent) {
1034                                                 x = bdb_idl_search(
1035                                                         scopes, tmp->bei_id );
1036                                                 if ( scopes[x] == tmp->bei_id ) {
1037                                                         scopeok = 1;
1038                                                         break;
1039                                                 }
1040                                         }
1041                                 }
1042                         }
1043                 }
1044 #endif
1045
1046                 /* Not in scope, ignore it */
1047                 if ( !scopeok ) {
1048 #ifdef NEW_LOGGING
1049                         LDAP_LOG ( OPERATION, RESULTS,
1050                                 "bdb_search: %ld scope not okay\n",
1051                                 (long) id, 0, 0);
1052 #else
1053                         Debug( LDAP_DEBUG_TRACE,
1054                                 "bdb_search: %ld scope not okay\n",
1055                                 (long) id, 0, 0 );
1056 #endif
1057                         goto loop_continue;
1058                 }
1059
1060                 /*
1061                  * if it's a referral, add it to the list of referrals. only do
1062                  * this for non-base searches, and don't check the filter
1063                  * explicitly here since it's only a candidate anyway.
1064                  */
1065                 if ( !manageDSAit && sop->oq_search.rs_scope != LDAP_SCOPE_BASE
1066                         && is_entry_referral( e ) )
1067                 {
1068                         BerVarray erefs = get_entry_referrals( sop, e );
1069                         rs->sr_ref = referral_rewrite( erefs,
1070                                 &e->e_name, NULL,
1071                                 sop->oq_search.rs_scope == LDAP_SCOPE_SUBTREE
1072                                         ? LDAP_SCOPE_SUBTREE
1073                                         : LDAP_SCOPE_BASE );
1074
1075                         send_search_reference( sop, rs );
1076
1077                         ber_bvarray_free( rs->sr_ref );
1078                         ber_bvarray_free( erefs );
1079                         rs->sr_ref = NULL;
1080
1081                         goto loop_continue;
1082                 }
1083
1084                 if ( !manageDSAit && is_entry_glue( e )) {
1085                         goto loop_continue;
1086                 }
1087
1088                 /* if it matches the filter and scope, send it */
1089                 if (IS_PSEARCH) {
1090                         if (ps_type != LDAP_PSEARCH_BY_SCOPEOUT) {
1091                                 rs->sr_err = test_filter( sop, rs->sr_entry, &cookief );
1092                         } else {
1093                                 rs->sr_err = LDAP_COMPARE_TRUE;
1094                         }
1095                 } else {
1096                         if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
1097                                 rc_sync = test_filter( sop, rs->sr_entry, &cookief );
1098                                 rs->sr_err = test_filter( sop,
1099                                         rs->sr_entry, &contextcsnand );
1100                                 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
1101                                         if ( rc_sync == LDAP_COMPARE_TRUE ) {
1102                                                 if ( no_sync_state_change ) {
1103 #ifdef NEW_LOGGING
1104                                                         LDAP_LOG ( OPERATION, RESULTS,
1105                                                                 "bdb_search: error in context csn management\n",
1106                                                                 0, 0, 0 );
1107 #else
1108                                                         Debug( LDAP_DEBUG_TRACE,
1109                                                                 "bdb_search: error in context csn management\n",
1110                                                                 0, 0, 0 );
1111 #endif
1112                                                 }
1113                                                 entry_sync_state = LDAP_SYNC_ADD;
1114                                         } else {
1115                                                 if ( no_sync_state_change ) {
1116                                                         goto loop_continue;
1117                                                 }
1118                                                 entry_sync_state = LDAP_SYNC_PRESENT;
1119                                         }
1120                                 }
1121                         } else {
1122                                 rs->sr_err = test_filter( sop,
1123                                         rs->sr_entry, sop->oq_search.rs_filter );
1124                         }
1125                 }
1126
1127                 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
1128                         /* check size limit */
1129                         if ( --sop->oq_search.rs_slimit == -1 ) {
1130                                 if (!IS_PSEARCH) {
1131                                         bdb_cache_return_entry_r( bdb->bi_dbenv,
1132                                                 &bdb->bi_cache, e, &lock );
1133                                 }
1134                                 e = NULL;
1135                                 rs->sr_entry = NULL;
1136                                 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1137                                 rs->sr_ref = rs->sr_v2ref;
1138                                 send_ldap_result( sop, rs );
1139                                 rs->sr_err = LDAP_SUCCESS;
1140                                 goto done;
1141                         }
1142
1143 #ifdef LDAP_CONTROL_PAGEDRESULTS
1144                         if ( get_pagedresults(sop) ) {
1145                                 if ( rs->sr_nentries >= sop->o_pagedresults_size ) {
1146                                         send_pagerequest_response( sop, rs,
1147                                                 lastid, tentries );
1148                                         goto done;
1149                                 }
1150                                 lastid = id;
1151                         }
1152 #endif
1153
1154                         if (e) {
1155                                 /* safe default */
1156                                 int result = -1;
1157                                 
1158 #if 0   /* noop is masked SLAP_CTRL_UPDATE */
1159                                 if( op->o_noop ) {
1160                                         result = 0;
1161                                 } else
1162 #endif
1163                                 if (IS_PSEARCH) {
1164                                         int premodify_found = 0;
1165                                         int entry_sync_state;
1166
1167                                         if ( ps_type == LDAP_PSEARCH_BY_ADD ||
1168                                                  ps_type == LDAP_PSEARCH_BY_DELETE ||
1169                                                  ps_type == LDAP_PSEARCH_BY_MODIFY ||
1170                                                  ps_type == LDAP_PSEARCH_BY_SCOPEOUT )
1171                                         {
1172                                                 if ( ps_type == LDAP_PSEARCH_BY_MODIFY ) {
1173                                                         struct psid_entry* psid_e;
1174                                                         LDAP_LIST_FOREACH( psid_e,
1175                                                                 &op->o_pm_list, ps_link)
1176                                                         {
1177                                                                 if( psid_e->ps_op == sop ) {
1178                                                                         premodify_found = 1;
1179                                                                         LDAP_LIST_REMOVE(psid_e, ps_link);
1180                                                                         break;
1181                                                                 }
1182                                                         }
1183                                                         if (psid_e != NULL) free (psid_e);
1184                                                 }
1185                                                 if ( ps_type == LDAP_PSEARCH_BY_ADD ) {
1186                                                         entry_sync_state = LDAP_SYNC_ADD;
1187                                                 } else if ( ps_type == LDAP_PSEARCH_BY_DELETE ) {
1188                                                         entry_sync_state = LDAP_SYNC_DELETE;
1189                                                 } else if ( ps_type == LDAP_PSEARCH_BY_MODIFY ) {
1190                                                         if ( premodify_found ) {
1191                                                                 entry_sync_state = LDAP_SYNC_MODIFY;
1192                                                         } else {
1193                                                                 entry_sync_state = LDAP_SYNC_ADD;
1194                                                         }
1195                                                 } else if ( ps_type == LDAP_PSEARCH_BY_SCOPEOUT )
1196                                                         entry_sync_state = LDAP_SYNC_DELETE;
1197                                                 else {
1198                                                         rs->sr_err = LDAP_OTHER;
1199                                                         goto done;
1200                                                 }
1201                                                 rs->sr_err = bdb_build_sync_state_ctrl( sop,
1202                                                         rs, e, entry_sync_state, ctrls,
1203                                                         num_ctrls++, 1, search_context_csn );
1204                                                 if ( rs->sr_err != LDAP_SUCCESS ) goto done;
1205                                                 rs->sr_attrs = attrs;
1206                                                 rs->sr_ctrls = ctrls;
1207                                                 result = send_search_entry( sop, rs );
1208                                                 ch_free( ctrls[num_ctrls-1]->ldctl_value.bv_val );
1209                                                 ch_free( ctrls[--num_ctrls] );
1210                                                 ctrls[num_ctrls] = NULL;
1211                                                 rs->sr_ctrls = NULL;
1212
1213                                         } else if ( ps_type == LDAP_PSEARCH_BY_PREMODIFY ) {
1214                                                 struct psid_entry* psid_e;
1215                                                 psid_e = (struct psid_entry *) calloc (1,
1216                                                         sizeof(struct psid_entry));
1217                                                 psid_e->ps_op = sop;
1218                                                 LDAP_LIST_INSERT_HEAD( &op->o_pm_list,
1219                                                         psid_e, ps_link );
1220
1221                                         } else {
1222                                                 printf("Error !\n");
1223                                         }
1224                                 } else {
1225                                         if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
1226                                                 rs->sr_err = bdb_build_sync_state_ctrl( sop,
1227                                                         rs, e, entry_sync_state, ctrls,
1228                                                         num_ctrls++, 0, search_context_csn );
1229                                                 if ( rs->sr_err != LDAP_SUCCESS ) goto done;
1230
1231                                                 rs->sr_ctrls = ctrls;
1232                                                 if ( rc_sync == LDAP_COMPARE_TRUE ) { /* ADD */
1233                                                         rs->sr_attrs = sop->oq_search.rs_attrs;
1234                                                 } else { /* PRESENT */
1235                                                         rs->sr_attrs = &null_attr;
1236                                                 }
1237                                                 result = send_search_entry( sop, rs );
1238                                                 ch_free( ctrls[num_ctrls-1]->ldctl_value.bv_val );
1239                                                 ch_free( ctrls[--num_ctrls] );
1240                                                 ctrls[num_ctrls] = NULL;
1241                                                 rs->sr_ctrls = NULL;
1242                                         } else {
1243                                                 rs->sr_attrs = sop->oq_search.rs_attrs;
1244                                                 rs->sr_ctrls = NULL;
1245                                                 result = send_search_entry( sop, rs );
1246                                         }
1247                                 }
1248
1249                                 switch (result) {
1250                                 case 0:         /* entry sent ok */
1251                                         break;
1252                                 case 1:         /* entry not sent */
1253                                         break;
1254                                 case -1:        /* connection closed */
1255                                         if (!IS_PSEARCH)
1256                                         bdb_cache_return_entry_r(bdb->bi_dbenv,
1257                                                 &bdb->bi_cache, e, &lock);
1258                                         e = NULL;
1259                                         rs->sr_entry = NULL;
1260                                         rs->sr_err = LDAP_OTHER;
1261                                         goto done;
1262                                 }
1263                         }
1264                 } else {
1265 #ifdef NEW_LOGGING
1266                         LDAP_LOG ( OPERATION, RESULTS,
1267                                 "bdb_search: %ld does not match filter\n", (long) id, 0, 0);
1268 #else
1269                         Debug( LDAP_DEBUG_TRACE,
1270                                 "bdb_search: %ld does not match filter\n",
1271                                 (long) id, 0, 0 );
1272 #endif
1273                 }
1274
1275 loop_continue:
1276                 if( e != NULL ) {
1277                         /* free reader lock */
1278                         if (!IS_PSEARCH) {
1279                                 bdb_cache_return_entry_r( bdb->bi_dbenv,
1280                                         &bdb->bi_cache, e , &lock);
1281                         }
1282                         e = NULL;
1283                         rs->sr_entry = NULL;
1284                 }
1285
1286                 ldap_pvt_thread_yield();
1287         }
1288
1289         if (!IS_PSEARCH) {
1290                 if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
1291                         rs->sr_err = LDAP_SUCCESS;
1292                         rs->sr_rspoid = LDAP_SYNC_INFO;
1293                         rs->sr_ctrls = NULL;
1294                         bdb_send_ldap_intermediate( sop, rs,
1295                                 LDAP_SYNC_STATE_MODE_DONE, search_context_csn );
1296
1297                         /* If changelog is supported, this is where to process it */
1298         
1299                         if ( sop->o_sync_mode & SLAP_SYNC_PERSIST ) {
1300                                 /* refreshAndPersist mode */
1301                                 bdb_send_ldap_intermediate( sop, rs,
1302                                         LDAP_SYNC_LOG_MODE_DONE, search_context_csn );
1303                         } else {
1304                                 /* refreshOnly mode */
1305                                 bdb_build_sync_done_ctrl( sop, rs, ctrls,
1306                                         num_ctrls++, 1, search_context_csn );
1307                                 rs->sr_ctrls = ctrls;
1308                                 rs->sr_ref = rs->sr_v2ref;
1309                                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
1310                                 send_ldap_result( sop, rs );
1311                                 if ( ctrls[num_ctrls-1]->ldctl_value.bv_val != NULL ) {
1312                                         ch_free( ctrls[num_ctrls-1]->ldctl_value.bv_val );
1313                                 }
1314                                 ch_free( ctrls[--num_ctrls] );
1315                                 ctrls[num_ctrls] = NULL;
1316                         }
1317                 } else {
1318                         rs->sr_ctrls = NULL;
1319                         rs->sr_ref = rs->sr_v2ref;
1320                         rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
1321                         send_ldap_result( sop, rs );
1322                 }
1323         }
1324
1325         rs->sr_err = LDAP_SUCCESS;
1326
1327 done:
1328         if( !IS_PSEARCH && e != NULL ) {
1329                 /* free reader lock */
1330                 bdb_cache_return_entry_r ( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
1331         }
1332
1333         LOCK_ID_FREE (bdb->bi_dbenv, locker );
1334
1335         if( rs->sr_v2ref ) {
1336                 ber_bvarray_free( rs->sr_v2ref );
1337                 rs->sr_v2ref = NULL;
1338         }
1339         if( realbase.bv_val ) ch_free( realbase.bv_val );
1340
1341         return rs->sr_err;
1342 }
1343
1344
1345 static int base_candidate(
1346         BackendDB       *be,
1347         Entry   *e,
1348         ID              *ids )
1349 {
1350 #ifdef NEW_LOGGING
1351         LDAP_LOG ( OPERATION, ENTRY,
1352                 "base_candidate: base: \"%s\" (0x%08lx)\n",
1353                 e->e_nname.bv_val, (long) e->e_id, 0);
1354 #else
1355         Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
1356                 e->e_nname.bv_val, (long) e->e_id, 0);
1357 #endif
1358
1359         ids[0] = 1;
1360         ids[1] = e->e_id;
1361         return 0;
1362 }
1363
1364 /* Look for "objectClass Present" in this filter.
1365  * Also count depth of filter tree while we're at it.
1366  */
1367 static int oc_filter(
1368         Filter *f,
1369         int cur,
1370         int *max
1371 )
1372 {
1373         int rc = 0;
1374
1375         if( cur > *max ) *max = cur;
1376
1377         switch(f->f_choice) {
1378         case LDAP_FILTER_PRESENT:
1379                 if (f->f_desc == slap_schema.si_ad_objectClass) {
1380                         rc = 1;
1381                 }
1382                 break;
1383
1384         case LDAP_FILTER_AND:
1385         case LDAP_FILTER_OR:
1386                 cur++;
1387                 for (f=f->f_and; f; f=f->f_next) {
1388                         (void) oc_filter(f, cur, max);
1389                 }
1390                 break;
1391
1392         default:
1393                 break;
1394         }
1395         return rc;
1396 }
1397
1398 static void search_stack_free( void *key, void *data )
1399 {
1400         ber_memfree_x(data, NULL);
1401 }
1402
1403 static void *search_stack(
1404         Operation *op
1405 )
1406 {
1407         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
1408         void *ret = NULL;
1409
1410         if ( op->o_threadctx ) {
1411                 ldap_pvt_thread_pool_getkey( op->o_threadctx, search_stack,
1412                         &ret, NULL );
1413         } else {
1414                 ret = bdb->bi_search_stack;
1415         }
1416
1417         if ( !ret ) {
1418                 ret = ch_malloc( bdb->bi_search_stack_depth * BDB_IDL_UM_SIZE
1419                         * sizeof( ID ) );
1420                 if ( op->o_threadctx ) {
1421                         ldap_pvt_thread_pool_setkey( op->o_threadctx, search_stack,
1422                                 ret, search_stack_free );
1423                 } else {
1424                         bdb->bi_search_stack = ret;
1425                 }
1426         }
1427         return ret;
1428 }
1429
1430 static int search_candidates(
1431         Operation *stackop,
1432         Operation *op,
1433         SlapReply *rs,
1434         Entry *e,
1435         u_int32_t locker,
1436         ID      *ids,
1437         ID      *scopes )
1438 {
1439         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
1440         int rc, depth = 1;
1441         Filter          f, rf, xf, nf;
1442         ID              *stack;
1443         AttributeAssertion aa_ref;
1444 #ifdef BDB_SUBENTRIES
1445         Filter  sf;
1446         AttributeAssertion aa_subentry;
1447 #endif
1448
1449         /*
1450          * This routine takes as input a filter (user-filter)
1451          * and rewrites it as follows:
1452          *      (&(scope=DN)[(objectClass=subentry)]
1453          *              (|[(objectClass=referral)(objectClass=alias)](user-filter))
1454          */
1455
1456 #ifdef NEW_LOGGING
1457         LDAP_LOG ( OPERATION, ENTRY,
1458                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n", 
1459                 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope);
1460 #else
1461         Debug(LDAP_DEBUG_TRACE,
1462                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
1463                 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
1464 #endif
1465
1466         xf.f_or = op->oq_search.rs_filter;
1467         xf.f_choice = LDAP_FILTER_OR;
1468         xf.f_next = NULL;
1469
1470         /* If the user's filter uses objectClass=*,
1471          * these clauses are redundant.
1472          */
1473         if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
1474                 && !get_subentries_visibility(op)
1475                 && !is_sync_protocol(op) )
1476         {
1477                 if( !get_manageDSAit(op) && !get_domainScope(op) ) {
1478                         /* match referral objects */
1479                         struct berval bv_ref = { sizeof("referral")-1, "referral" };
1480                         rf.f_choice = LDAP_FILTER_EQUALITY;
1481                         rf.f_ava = &aa_ref;
1482                         rf.f_av_desc = slap_schema.si_ad_objectClass;
1483                         rf.f_av_value = bv_ref;
1484                         rf.f_next = xf.f_or;
1485                         xf.f_or = &rf;
1486                         depth++;
1487                 }
1488         }
1489
1490         f.f_next = NULL;
1491         f.f_choice = LDAP_FILTER_AND;
1492         f.f_and = &nf;
1493         /* Dummy; we compute scope separately now */
1494         nf.f_choice = SLAPD_FILTER_COMPUTED;
1495         nf.f_result = LDAP_SUCCESS;
1496         nf.f_next = xf.f_or == op->oq_search.rs_filter
1497                 ? op->oq_search.rs_filter : &xf ;
1498         /* Filter depth increased again, adding dummy clause */
1499         depth++;
1500
1501 #ifdef BDB_SUBENTRIES
1502         if( get_subentries_visibility( op ) ) {
1503                 struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
1504                 sf.f_choice = LDAP_FILTER_EQUALITY;
1505                 sf.f_ava = &aa_subentry;
1506                 sf.f_av_desc = slap_schema.si_ad_objectClass;
1507                 sf.f_av_value = bv_subentry;
1508                 sf.f_next = nf.f_next;
1509                 nf.f_next = &sf;
1510         }
1511 #endif
1512
1513         /* Allocate IDL stack, plus 1 more for former tmp */
1514         if ( depth+1 > bdb->bi_search_stack_depth ) {
1515                 stack = ch_malloc( (depth + 1) * BDB_IDL_UM_SIZE * sizeof( ID ) );
1516         } else {
1517                 stack = search_stack( stackop );
1518         }
1519
1520         if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
1521                 rc = search_aliases( op, rs, e, locker, ids, scopes, stack );
1522         } else {
1523                 rc = bdb_dn2idl( op, e, ids, stack );
1524         }
1525
1526         if ( rc == LDAP_SUCCESS ) {
1527                 rc = bdb_filter_candidates( op, &f, ids,
1528                         stack, stack+BDB_IDL_UM_SIZE );
1529         }
1530
1531         if ( depth+1 > bdb->bi_search_stack_depth ) {
1532                 ch_free( stack );
1533         }
1534
1535         if( rc ) {
1536 #ifdef NEW_LOGGING
1537                 LDAP_LOG ( OPERATION, DETAIL1,
1538                         "bdb_search_candidates: failed (rc=%d)\n", rc, 0, 0  );
1539 #else
1540                 Debug(LDAP_DEBUG_TRACE,
1541                         "bdb_search_candidates: failed (rc=%d)\n",
1542                         rc, NULL, NULL );
1543 #endif
1544
1545         } else {
1546 #ifdef NEW_LOGGING
1547                 LDAP_LOG ( OPERATION, DETAIL1,
1548                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
1549                         (long) ids[0], (long) BDB_IDL_FIRST(ids), 
1550                         (long) BDB_IDL_LAST(ids));
1551 #else
1552                 Debug(LDAP_DEBUG_TRACE,
1553                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
1554                         (long) ids[0],
1555                         (long) BDB_IDL_FIRST(ids),
1556                         (long) BDB_IDL_LAST(ids) );
1557 #endif
1558         }
1559
1560         return rc;
1561 }
1562
1563 #ifdef LDAP_CONTROL_PAGEDRESULTS
1564 static void
1565 send_pagerequest_response( 
1566         Operation       *op,
1567         SlapReply       *rs,
1568         ID              lastid,
1569         int             tentries )
1570 {
1571         LDAPControl     ctrl, *ctrls[2];
1572         char berbuf[LBER_ELEMENT_SIZEOF];
1573         BerElement      *ber = (BerElement *)berbuf;
1574         struct berval   cookie = { 0, NULL };
1575         PagedResultsCookie respcookie;
1576
1577 #ifdef NEW_LOGGING
1578         LDAP_LOG ( OPERATION, ENTRY,
1579                 "send_pagerequest_response: lastid: (0x%08lx) "
1580                 "nentries: (0x%081x)\n", 
1581                 lastid, rs->sr_nentries, NULL );
1582 #else
1583         Debug(LDAP_DEBUG_ARGS, "send_pagerequest_response: lastid: (0x%08lx) "
1584                         "nentries: (0x%081x)\n", lastid, rs->sr_nentries, NULL );
1585 #endif
1586
1587         ctrl.ldctl_value.bv_val = NULL;
1588         ctrls[0] = &ctrl;
1589         ctrls[1] = NULL;
1590
1591         ber_init2( ber, NULL, LBER_USE_DER );
1592
1593         respcookie = ( PagedResultsCookie )lastid;
1594         op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
1595         cookie.bv_len = sizeof( respcookie );
1596         cookie.bv_val = (char *)&respcookie;
1597
1598         /*
1599          * FIXME: we should consider sending an estimate of the entries
1600          * left, after appropriate security check is done
1601          */
1602         ber_printf( ber, "{iO}", tentries, &cookie ); 
1603
1604         if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
1605                 goto done;
1606         }
1607
1608         ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
1609         ctrls[0]->ldctl_iscritical = 0;
1610
1611         rs->sr_ctrls = ctrls;
1612         rs->sr_err = LDAP_SUCCESS;
1613         send_ldap_result( op, rs );
1614
1615 done:
1616         (void) ber_free_buf( ber );
1617 }                       
1618 #endif
1619
1620 int
1621 bdb_build_sync_state_ctrl(
1622         Operation       *op,
1623         SlapReply       *rs,
1624         Entry           *e,
1625         int                     entry_sync_state,
1626         LDAPControl     **ctrls,
1627         int                     num_ctrls,
1628         int                     send_cookie,
1629         struct berval   *csn)
1630 {
1631         Attribute* a;
1632         int ret;
1633         int res;
1634         const char *text = NULL;
1635
1636         char berbuf[LBER_ELEMENT_SIZEOF];
1637         BerElement *ber = (BerElement *)berbuf;
1638
1639         struct berval entryuuid_bv      = { 0, NULL };
1640
1641         ber_init2( ber, 0, LBER_USE_DER );
1642
1643         ctrls[num_ctrls] = ch_malloc ( sizeof ( LDAPControl ) );
1644
1645         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
1646                 AttributeDescription *desc = a->a_desc;
1647                 if ( desc == slap_schema.si_ad_entryUUID ) {
1648                         ber_dupbv( &entryuuid_bv, &a->a_vals[0] );
1649                 }
1650         }
1651
1652         if ( send_cookie && csn ) {
1653                 ber_printf( ber, "{eOON}",
1654                         entry_sync_state, &entryuuid_bv, csn );
1655         } else {
1656                 ber_printf( ber, "{eON}",
1657                         entry_sync_state, &entryuuid_bv );
1658         }
1659
1660         ch_free( entryuuid_bv.bv_val );
1661         entryuuid_bv.bv_val = NULL;
1662
1663         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
1664         ctrls[num_ctrls]->ldctl_iscritical = op->o_sync;
1665         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
1666
1667         ber_free_buf( ber );
1668
1669         if ( ret < 0 ) {
1670 #ifdef NEW_LOGGING
1671                 LDAP_LOG ( OPERATION, RESULTS, 
1672                         "bdb_build_sync_ctrl: ber_flatten2 failed\n",
1673                         0, 0, 0 );
1674 #else
1675                 Debug( LDAP_DEBUG_TRACE,
1676                         "bdb_build_sync_ctrl: ber_flatten2 failed\n",
1677                         0, 0, 0 );
1678 #endif
1679                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
1680                 return ret;
1681         }
1682
1683         return LDAP_SUCCESS;
1684 }
1685
1686 int
1687 bdb_build_sync_done_ctrl(
1688         Operation       *op,
1689         SlapReply       *rs,
1690         LDAPControl     **ctrls,
1691         int             num_ctrls,
1692         int             send_cookie,
1693         struct berval   *csn )
1694 {
1695         int ret;
1696         char berbuf[LBER_ELEMENT_SIZEOF];
1697         BerElement *ber = (BerElement *)berbuf;
1698
1699         ber_init2( ber, NULL, LBER_USE_DER );
1700
1701         ctrls[num_ctrls] = ch_malloc ( sizeof ( LDAPControl ) );
1702
1703         if ( send_cookie && csn ) {
1704                 ber_printf( ber, "{ON}", csn );
1705         } else {
1706                 ber_printf( ber, "{N}" );
1707         }
1708
1709         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
1710         ctrls[num_ctrls]->ldctl_iscritical = op->o_sync;
1711         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
1712
1713         ber_free_buf( ber );
1714
1715         if ( ret < 0 ) {
1716 #ifdef NEW_LOGGING
1717                 LDAP_LOG ( OPERATION, RESULTS, 
1718                         "bdb_build_sync_done_ctrl: ber_flatten2 failed\n",
1719                         0, 0, 0 );
1720 #else
1721                 Debug( LDAP_DEBUG_TRACE,
1722                         "bdb_build_sync_done_ctrl: ber_flatten2 failed\n",
1723                         0, 0, 0 );
1724 #endif
1725                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
1726                 return ret;
1727         }
1728
1729         return LDAP_SUCCESS;
1730 }
1731
1732 int
1733 bdb_send_ldap_intermediate(
1734         Operation   *op,
1735         SlapReply   *rs,
1736         int         state,
1737         struct berval *cookie )
1738 {
1739         char berbuf[LBER_ELEMENT_SIZEOF];
1740         BerElement *ber = (BerElement *)berbuf;
1741         struct berval rspdata;
1742
1743         int ret;
1744
1745         ber_init2( ber, NULL, LBER_USE_DER );
1746
1747         if ( cookie == NULL ) {
1748                 ber_printf( ber, "{eN}", state );
1749         } else {
1750                 ber_printf( ber, "{eON}", state, cookie );
1751         }
1752
1753         ret = ber_flatten2( ber, &rspdata, 0 );
1754
1755         if ( ret < 0 ) {
1756 #ifdef NEW_LOGGING
1757                 LDAP_LOG ( OPERATION, RESULTS, 
1758                         "bdb_send_ldap_intermediate: ber_flatten2 failed\n",
1759                         0, 0, 0 );
1760 #else
1761                 Debug( LDAP_DEBUG_TRACE,
1762                         "bdb_send_ldap_intermediate: ber_flatten2 failed\n",
1763                         0, 0, 0 );
1764 #endif
1765                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
1766                 return ret;
1767         }
1768
1769         rs->sr_rspdata = &rspdata;
1770         send_ldap_intermediate( op, rs );
1771         rs->sr_rspdata = NULL;
1772         ber_free_buf( ber );
1773
1774         return LDAP_SUCCESS;
1775 }