]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/search.c
From ITS#2183, use a long-lived IDL stack per thread.
[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 static void send_pagerequest_response( 
30         Connection      *conn,
31         Operation *op,
32         ID  lastid,
33         int nentries,
34         int tentries );                 
35
36 int
37 bdb_search(
38         BackendDB       *be,
39         Connection      *conn,
40         Operation       *op,
41         struct berval   *base,
42         struct berval   *nbase,
43         int             scope,
44         int             deref,
45         int             slimit,
46         int             tlimit,
47         Filter  *filter,
48         struct berval   *filterstr,
49         AttributeName   *attrs,
50         int             attrsonly )
51 {
52         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
53         int             rc;
54         const char *text = NULL;
55         time_t          stoptime;
56         ID              id, cursor;
57         ID              candidates[BDB_IDL_UM_SIZE];
58         Entry           *e = NULL;
59         BerVarray v2refs = NULL;
60         Entry   *matched = NULL;
61         struct berval   realbase = { 0, NULL };
62         int             nentries = 0;
63         int             manageDSAit;
64         int             pagedresults;
65         int             tentries = 0;
66         ID              lastid = NOID;
67
68 #ifdef LDAP_CLIENT_UPDATE
69         Filter lcupf, csnfnot, csnfeq, csnfand, csnfge;
70         AttributeAssertion aa_ge, aa_eq;
71         int             entry_count = 0;
72         struct berval entrycsn_bv = { 0, NULL };
73         struct berval latest_entrycsn_bv = { 0, NULL };
74 #endif /* LDAP_CLIENT_UPDATE */
75
76         struct slap_limits_set *limit = NULL;
77         int isroot = 0;
78
79         u_int32_t       locker;
80         DB_LOCK         lock;
81
82 #ifdef NEW_LOGGING
83         LDAP_LOG ( OPERATION, ENTRY, "bdb_back_search\n", 0, 0, 0 );
84 #else
85         Debug( LDAP_DEBUG_TRACE, "=> bdb_back_search\n",
86                 0, 0, 0);
87 #endif
88
89 #ifdef LDAP_CLIENT_UPDATE
90         if ( op->o_clientupdate_type & SLAP_LCUP_PERSIST ) {
91                 bdb_add_psearch_spec( be, conn, op, base, base, scope,
92                         deref, slimit, tlimit, filter, filterstr, attrs, attrsonly );
93                 return LDAP_SUCCESS;
94         }
95 #endif
96
97
98         manageDSAit = get_manageDSAit( op );
99         pagedresults = get_pagedresults( op );
100
101         rc = LOCK_ID (bdb->bi_dbenv, &locker );
102         switch(rc) {
103         case 0:
104                 break;
105         default:
106                 send_ldap_result( conn, op, rc=LDAP_OTHER,
107                         NULL, "internal error", NULL, NULL );
108                 return rc;
109         }
110
111         if ( nbase->bv_len == 0 ) {
112                 /* DIT root special case */
113                 e = (Entry *) &slap_entry_root;
114                 rc = 0;
115         } else                                          
116 #ifdef BDB_ALIASES
117         /* get entry with reader lock */
118         if ( deref & LDAP_DEREF_FINDING ) {
119                 e = deref_dn_r( be, nbase-, &err, &matched, &text );
120
121         } else
122 #endif
123         {
124 dn2entry_retry:
125                 rc = bdb_dn2entry_r( be, NULL, nbase, &e, &matched, 0, locker, &lock );
126         }
127
128         switch(rc) {
129         case DB_NOTFOUND:
130         case 0:
131                 break;
132         case LDAP_BUSY:
133                 if (e != NULL) {
134                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
135                 }
136                 if (matched != NULL) {
137                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, matched, &lock);
138                 }
139                 send_ldap_result( conn, op, LDAP_BUSY,
140                         NULL, "ldap server busy", NULL, NULL );
141                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
142                 return LDAP_BUSY;
143         case DB_LOCK_DEADLOCK:
144         case DB_LOCK_NOTGRANTED:
145                 goto dn2entry_retry;
146         default:
147                 if (e != NULL) {
148                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
149                 }
150                 if (matched != NULL) {
151                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, matched, &lock);
152                 }
153                 send_ldap_result( conn, op, rc=LDAP_OTHER,
154                         NULL, "internal error", NULL, NULL );
155                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
156                 return rc;
157         }
158
159         if ( e == NULL ) {
160                 struct berval matched_dn = { 0, NULL };
161                 BerVarray refs = NULL;
162
163                 if ( matched != NULL ) {
164                         BerVarray erefs;
165                         ber_dupbv( &matched_dn, &matched->e_name );
166
167                         erefs = is_entry_referral( matched )
168                                 ? get_entry_referrals( be, conn, op, matched )
169                                 : NULL;
170
171                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, matched, &lock);
172                         matched = NULL;
173
174                         if( erefs ) {
175                                 refs = referral_rewrite( erefs, &matched_dn,
176                                         base, scope );
177                                 ber_bvarray_free( erefs );
178                         }
179
180                 } else {
181                         refs = referral_rewrite( default_referral,
182                                 NULL, base, scope );
183                 }
184
185                 send_ldap_result( conn, op,     rc=LDAP_REFERRAL ,
186                         matched_dn.bv_val, text, refs, NULL );
187
188                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
189                 if ( refs ) ber_bvarray_free( refs );
190                 if ( matched_dn.bv_val ) ber_memfree( matched_dn.bv_val );
191                 return rc;
192         }
193
194         if (!manageDSAit && e != &slap_entry_root && is_entry_referral( e ) ) {
195                 /* entry is a referral, don't allow add */
196                 struct berval matched_dn;
197                 BerVarray erefs, refs;
198                 
199                 ber_dupbv( &matched_dn, &e->e_name );
200                 erefs = get_entry_referrals( be, conn, op, e );
201                 refs = NULL;
202
203                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
204                 e = NULL;
205
206                 if( erefs ) {
207                         refs = referral_rewrite( erefs, &matched_dn,
208                                 base, scope );
209                         ber_bvarray_free( erefs );
210                 }
211
212 #ifdef NEW_LOGGING
213                 LDAP_LOG ( OPERATION, RESULTS, 
214                         "bdb_search: entry is referral\n", 0, 0, 0 );
215 #else
216                 Debug( LDAP_DEBUG_TRACE, "bdb_search: entry is referral\n",
217                         0, 0, 0 );
218 #endif
219
220                 send_ldap_result( conn, op, LDAP_REFERRAL,
221                         matched_dn.bv_val,
222                         refs ? NULL : "bad referral object",
223                         refs, NULL );
224
225                 LOCK_ID_FREE (bdb->bi_dbenv, locker );
226                 ber_bvarray_free( refs );
227                 ber_memfree( matched_dn.bv_val );
228                 return 1;
229         }
230
231         /* if not root, get appropriate limits */
232         if ( be_isroot( be, &op->o_ndn ) ) {
233                 isroot = 1;
234         } else {
235                 ( void ) get_limits( be, &op->o_ndn, &limit );
236         }
237
238         /* The time/size limits come first because they require very little
239          * effort, so there's no chance the candidates are selected and then 
240          * the request is not honored only because of time/size constraints */
241
242         /* if no time limit requested, use soft limit (unless root!) */
243         if ( isroot ) {
244                 if ( tlimit == 0 ) {
245                         tlimit = -1;    /* allow root to set no limit */
246                 }
247
248                 if ( slimit == 0 ) {
249                         slimit = -1;
250                 }
251
252         } else {
253                 /* if no limit is required, use soft limit */
254                 if ( tlimit <= 0 ) {
255                         tlimit = limit->lms_t_soft;
256
257                 /* if requested limit higher than hard limit, abort */
258                 } else if ( tlimit > limit->lms_t_hard ) {
259                         /* no hard limit means use soft instead */
260                         if ( limit->lms_t_hard == 0
261                                         && limit->lms_t_soft > -1
262                                         && tlimit > limit->lms_t_soft ) {
263                                 tlimit = limit->lms_t_soft;
264
265                         /* positive hard limit means abort */
266                         } else if ( limit->lms_t_hard > 0 ) {
267                                 send_search_result( conn, op, 
268                                                 LDAP_ADMINLIMIT_EXCEEDED,
269                                                 NULL, NULL, NULL, NULL, 0 );
270                                 rc = 0;
271                                 goto done;
272                         }
273                 
274                         /* negative hard limit means no limit */
275                 }
276                 
277                 /* if no limit is required, use soft limit */
278                 if ( slimit <= 0 ) {
279                         if ( pagedresults && limit->lms_s_pr != 0 ) {
280                                 slimit = limit->lms_s_pr;
281                         } else {
282                                 slimit = limit->lms_s_soft;
283                         }
284
285                 /* if requested limit higher than hard limit, abort */
286                 } else if ( slimit > limit->lms_s_hard ) {
287                         /* no hard limit means use soft instead */
288                         if ( limit->lms_s_hard == 0
289                                         && limit->lms_s_soft > -1
290                                         && slimit > limit->lms_s_soft ) {
291                                 slimit = limit->lms_s_soft;
292
293                         /* positive hard limit means abort */
294                         } else if ( limit->lms_s_hard > 0 ) {
295                                 send_search_result( conn, op, 
296                                                 LDAP_ADMINLIMIT_EXCEEDED,
297                                                 NULL, NULL, NULL, NULL, 0 );
298                                 rc = 0; 
299                                 goto done;
300                         }
301                         
302                         /* negative hard limit means no limit */
303                 }
304         }
305
306         /* compute it anyway; root does not use it */
307         stoptime = op->o_time + tlimit;
308
309         /* select candidates */
310         if ( scope == LDAP_SCOPE_BASE ) {
311                 rc = base_candidate( be, e, candidates );
312
313         } else {
314                 BDB_IDL_ALL( bdb, candidates );
315                 rc = search_candidates( be, op, e, filter,
316                         scope, deref, candidates );
317         }
318
319         /* need normalized dn below */
320         ber_dupbv( &realbase, &e->e_nname );
321
322         /* start cursor at beginning of candidates.
323          */
324         cursor = 0;
325
326         if ( e != &slap_entry_root ) {
327                 bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
328         }
329         e = NULL;
330
331         if ( candidates[0] == 0 ) {
332 #ifdef NEW_LOGGING
333                 LDAP_LOG ( OPERATION, RESULTS,
334                         "bdb_search: no candidates\n", 0, 0, 0 );
335 #else
336                 Debug( LDAP_DEBUG_TRACE, "bdb_search: no candidates\n",
337                         0, 0, 0 );
338 #endif
339
340                 send_search_result( conn, op,
341                         LDAP_SUCCESS,
342                         NULL, NULL, NULL, NULL, 0 );
343
344                 rc = 1;
345                 goto done;
346         }
347
348         /* if not root and candidates exceed to-be-checked entries, abort */
349         if ( !isroot && limit->lms_s_unchecked != -1 ) {
350                 if ( BDB_IDL_N(candidates) > (unsigned) limit->lms_s_unchecked ) {
351                         send_search_result( conn, op, 
352                                         LDAP_ADMINLIMIT_EXCEEDED,
353                                         NULL, NULL, NULL, NULL, 0 );
354                         rc = 1;
355                         goto done;
356                 }
357         }
358
359         if ( isroot || !limit->lms_s_pr_hide ) {
360                 tentries = BDB_IDL_N(candidates);
361         }
362
363         if ( pagedresults ) {
364                 if ( op->o_pagedresults_state.ps_cookie == 0 ) {
365                         id = 0;
366                 } else {
367                         if ( op->o_pagedresults_size == 0 ) {
368                                 send_search_result( conn, op, LDAP_SUCCESS,
369                                         NULL, "search abandoned by pagedResult size=0",
370                                         NULL, NULL, 0);
371                                 goto done;
372                         }
373                         for ( id = bdb_idl_first( candidates, &cursor );
374                                 id != NOID && id <= (ID)( op->o_pagedresults_state.ps_cookie );
375                                 id = bdb_idl_next( candidates, &cursor ) );
376                 }
377                 if ( cursor == NOID ) {
378 #ifdef NEW_LOGGING
379                         LDAP_LOG ( OPERATION, RESULTS, 
380                                 "bdb_search: no paged results candidates\n", 
381                         0, 0, 0 );
382 #else
383                         Debug( LDAP_DEBUG_TRACE, 
384                                 "bdb_search: no paged results candidates\n",
385                                 0, 0, 0 );
386 #endif
387                         send_pagerequest_response( conn, op, lastid, 0, 0 );
388
389                         rc = 1;
390                         goto done;
391                 }
392                 goto loop_begin;
393         }
394
395 #ifdef LDAP_CLIENT_UPDATE
396         if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
397                 lcupf.f_choice = LDAP_FILTER_AND;
398                 lcupf.f_and = &csnfnot;
399                 lcupf.f_next = NULL;
400
401                 csnfnot.f_choice = LDAP_FILTER_NOT;
402                 csnfnot.f_not = &csnfeq;
403                 csnfnot.f_next = &csnfand;
404
405                 csnfeq.f_choice = LDAP_FILTER_EQUALITY;
406                 csnfeq.f_ava = &aa_eq;
407                 csnfeq.f_av_desc = slap_schema.si_ad_entryCSN;
408                 ber_dupbv( &csnfeq.f_av_value, &op->o_clientupdate_state );
409
410                 csnfand.f_choice = LDAP_FILTER_AND;
411                 csnfand.f_and = &csnfge;
412                 csnfand.f_next = NULL;
413
414                 csnfge.f_choice = LDAP_FILTER_GE;
415                 csnfge.f_ava = &aa_ge;
416                 csnfge.f_av_desc = slap_schema.si_ad_entryCSN;
417                 ber_dupbv( &csnfge.f_av_value, &op->o_clientupdate_state );
418                 csnfge.f_next = filter;
419         }
420 #endif /* LDAP_CLIENT_UPDATE */
421
422         for ( id = bdb_idl_first( candidates, &cursor );
423                 id != NOID;
424                 id = bdb_idl_next( candidates, &cursor ) )
425         {
426
427                 int             scopeok = 0;
428
429 loop_begin:
430                 /* check for abandon */
431                 if ( op->o_abandon ) {
432                         rc = 0;
433                         goto done;
434                 }
435
436                 /* check time limit */
437                 if ( tlimit != -1 && slap_get_time() > stoptime ) {
438                         send_search_result( conn, op, rc = LDAP_TIMELIMIT_EXCEEDED,
439                                 NULL, NULL, v2refs, NULL, nentries );
440                         goto done;
441                 }
442
443 id2entry_retry:
444                 /* get the entry with reader lock */
445                 rc = bdb_id2entry_r( be, NULL, id, &e, locker, &lock );
446
447                 if (rc == LDAP_BUSY) {
448                         send_ldap_result( conn, op, rc=LDAP_BUSY,
449                                 NULL, "ldap server busy", NULL, NULL );
450                         goto done;
451
452                 } else if ( rc == DB_LOCK_DEADLOCK || rc == DB_LOCK_NOTGRANTED ) {
453                         goto id2entry_retry;    
454                 }
455
456                 if ( e == NULL ) {
457                         if( !BDB_IDL_IS_RANGE(candidates) ) {
458                                 /* only complain for non-range IDLs */
459 #ifdef NEW_LOGGING
460                                 LDAP_LOG ( OPERATION, RESULTS,
461                                         "bdb_search: candidate %ld not found\n", (long) id, 0, 0);
462 #else
463                                 Debug( LDAP_DEBUG_TRACE,
464                                         "bdb_search: candidate %ld not found\n",
465                                         (long) id, 0, 0 );
466 #endif
467                         }
468
469                         goto loop_continue;
470                 }
471
472 #ifdef BDB_SUBENTRIES
473                 if ( is_entry_subentry( e ) ) {
474                         if( scope != LDAP_SCOPE_BASE ) {
475                                 if(!get_subentries_visibility( op )) {
476                                         /* only subentries are visible */
477                                         goto loop_continue;
478                                 }
479
480                         } else if ( get_subentries( op ) &&
481                                 !get_subentries_visibility( op ))
482                         {
483                                 /* only subentries are visible */
484                                 goto loop_continue;
485                         }
486
487                 } else if ( get_subentries_visibility( op )) {
488                         /* only subentries are visible */
489                         goto loop_continue;
490                 }
491 #endif
492
493 #ifdef BDB_ALIASES
494                 if ( deref & LDAP_DEREF_SEARCHING && is_entry_alias( e ) ) {
495                         Entry *matched;
496                         int err;
497                         const char *text;
498                         
499                         e = deref_entry_r( be, e, &err, &matched, &text );
500
501                         if( e == NULL ) {
502                                 e = matched;
503                                 goto loop_continue;
504                         }
505
506                         if( e->e_id == id ) {
507                                 /* circular loop */
508                                 goto loop_continue;
509                         }
510
511                         /* need to skip alias which deref into scope */
512                         if( scope & LDAP_SCOPE_ONELEVEL ) {
513                                 struct berval   pdn;
514                                 
515                                 dnParent( &e->e_nname, &pdn ):
516                                 if ( ber_bvcmp( pdn, &realbase ) ) {
517                                         goto loop_continue;
518                                 }
519
520                         } else if ( dnIsSuffix( &e->e_nname, &realbase ) ) {
521                                 /* alias is within scope */
522 #ifdef NEW_LOGGING
523                                 LDAP_LOG ( OPERATION, RESULTS,
524                                         "bdb_search: \"%s\" in subtree\n", e->edn, 0, 0);
525 #else
526                                 Debug( LDAP_DEBUG_TRACE,
527                                         "bdb_search: \"%s\" in subtree\n",
528                                         e->e_dn, 0, 0 );
529 #endif
530                                 goto loop_continue;
531                         }
532
533                         scopeok = 1;
534                 }
535 #endif
536
537                 /*
538                  * if it's a referral, add it to the list of referrals. only do
539                  * this for non-base searches, and don't check the filter
540                  * explicitly here since it's only a candidate anyway.
541                  */
542                 if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
543                         is_entry_referral( e ) )
544                 {
545                         struct berval   dn;
546
547                         /* check scope */
548                         if ( !scopeok && scope == LDAP_SCOPE_ONELEVEL ) {
549                                 if ( !be_issuffix( be, &e->e_nname ) ) {
550                                         dnParent( &e->e_nname, &dn );
551                                         scopeok = dn_match( &dn, &realbase );
552                                 } else {
553                                         scopeok = (realbase.bv_len == 0);
554                                 }
555
556                         } else if ( !scopeok && scope == LDAP_SCOPE_SUBTREE ) {
557                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
558
559                         } else {
560                                 scopeok = 1;
561                         }
562
563                         if( scopeok ) {
564                                 BerVarray erefs = get_entry_referrals(
565                                         be, conn, op, e );
566                                 BerVarray refs = referral_rewrite( erefs,
567                                         &e->e_name, NULL,
568                                         scope == LDAP_SCOPE_SUBTREE
569                                                 ? LDAP_SCOPE_SUBTREE
570                                                 : LDAP_SCOPE_BASE );
571
572                                 send_search_reference( be, conn, op,
573                                         e, refs, NULL, &v2refs );
574
575                                 ber_bvarray_free( refs );
576
577                         } else {
578 #ifdef NEW_LOGGING
579                                 LDAP_LOG(OPERATION, DETAIL2, 
580                                         "bdb_search: candidate referral %ld scope not okay\n",
581                                         id, 0, 0 );
582 #else
583                                 Debug( LDAP_DEBUG_TRACE,
584                                         "bdb_search: candidate referral %ld scope not okay\n",
585                                         id, 0, 0 );
586 #endif
587                         }
588
589                         goto loop_continue;
590                 }
591
592                 /* if it matches the filter and scope, send it */
593 #ifdef LDAP_CLIENT_UPDATE
594                 if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
595                         rc = test_filter( be, conn, op, e, &lcupf );
596                 } else
597 #endif /* LDAP_CLIENT_UPDATE */
598                 {
599                         rc = test_filter( be, conn, op, e, filter );
600                 }
601
602                 if ( rc == LDAP_COMPARE_TRUE ) {
603                         struct berval   dn;
604
605                         /* check scope */
606                         if ( !scopeok && scope == LDAP_SCOPE_ONELEVEL ) {
607                                 if ( be_issuffix( be, &e->e_nname ) ) {
608                                         scopeok = (realbase.bv_len == 0);
609                                 } else {
610                                         dnParent( &e->e_nname, &dn );
611                                         scopeok = dn_match( &dn, &realbase );
612                                 }
613
614                         } else if ( !scopeok && scope == LDAP_SCOPE_SUBTREE ) {
615                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
616
617                         } else {
618                                 scopeok = 1;
619                         }
620
621                         if ( scopeok ) {
622                                 /* check size limit */
623                                 if ( --slimit == -1 ) {
624                                         bdb_cache_return_entry_r( bdb->bi_dbenv,
625                                                 &bdb->bi_cache, e, &lock );
626                                         e = NULL;
627                                         send_search_result( conn, op,
628                                                 rc = LDAP_SIZELIMIT_EXCEEDED, NULL, NULL,
629                                                 v2refs, NULL, nentries );
630                                         goto done;
631                                 }
632
633                                 if ( pagedresults ) {
634                                         if ( nentries >= op->o_pagedresults_size ) {
635                                                 send_pagerequest_response( conn, op, lastid, nentries, tentries );
636                                                 goto done;
637                                         }
638                                         lastid = id;
639                                 }
640
641                                 if (e) {
642                                         int result;
643                                         
644 #if 0   /* noop is masked SLAP_CTRL_UPDATE */
645                                         if( op->o_noop ) {
646                                                 result = 0;
647                                         } else
648 #endif
649                                         {
650 #ifdef LDAP_CLIENT_UPDATE
651                                                 if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
652                                                         Attribute* a;
653                                                         int ret;
654                                                         int res;
655                                                         const char *text = NULL;
656                                                         LDAPControl *ctrls[2];
657                                                         struct berval *bv;
658
659                                                         BerElement *ber = ber_alloc_t( LBER_USE_DER );
660
661                                                         if ( ber == NULL ) {
662 #ifdef NEW_LOGGING
663                                                                 LDAP_LOG ( OPERATION, RESULTS, 
664                                                                         "bdb_search: ber_alloc_t failed\n",
665                                                                         0, 0, 0 );
666 #else
667                                                                 Debug( LDAP_DEBUG_TRACE,
668                                                                         "bdb_search: ber_alloc_t failed\n",
669                                                                         0, 0, 0 );
670 #endif
671                                                                 send_ldap_result( conn, op, rc=LDAP_OTHER,
672                                                                         NULL, "internal error", NULL, NULL );
673                                                                 goto done;
674                                                         }
675
676                                                         entry_count++;
677
678                                                         ctrls[0] = ch_malloc ( sizeof ( LDAPControl ) );
679                                                         ctrls[1] = NULL;
680
681                                                         if ( entry_count % op->o_clientupdate_interval == 0 ) {
682                                                                 /* Send cookie */
683                                                                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
684                                                                         AttributeDescription *desc = a->a_desc;
685                                                                         if ( desc == slap_schema.si_ad_entryCSN ) {
686                                                                                 ber_dupbv( &entrycsn_bv, &a->a_vals[0] );
687                                                                                 if ( latest_entrycsn_bv.bv_val == NULL ) {
688                                                                                         ber_dupbv( &latest_entrycsn_bv, &entrycsn_bv );
689                                                                                 } else {
690                                                                                         res = value_match( &ret, desc,
691                                                                                                 desc->ad_type->sat_ordering,
692                                                                                                 SLAP_MR_ASSERTION_SYNTAX_MATCH,
693                                                                                                 &entrycsn_bv, &latest_entrycsn_bv, &text );
694                                                                                         if ( res != LDAP_SUCCESS ) {
695                                                                                                 ret = 0;
696 #ifdef NEW_LOGGING
697                                                                                                 LDAP_LOG ( OPERATION, RESULTS, 
698                                                                                                         "bdb_search: value_match failed\n",
699                                                                                                         0, 0, 0 );
700 #else
701                                                                                                 Debug( LDAP_DEBUG_TRACE,
702                                                                                                         "bdb_search: value_match failed\n",
703                                                                                                         0, 0, 0 );
704 #endif
705                                                                                         }
706
707                                                                                         if ( ret > 0 ) {
708                                                                                                 ch_free( latest_entrycsn_bv.bv_val );
709                                                                                                 latest_entrycsn_bv.bv_val = NULL;
710                                                                                                 ber_dupbv( &latest_entrycsn_bv,
711                                                                                                         &entrycsn_bv );
712                                                                                         }
713                                                                                 }
714                                                                         }
715                                                                 }
716
717                                                                 ber_printf( ber,
718                                                                         "{bb{sON}N}",
719                                                                         SLAP_LCUP_STATE_UPDATE_FALSE,
720                                                                         SLAP_LCUP_ENTRY_DELETED_FALSE,
721                                                                         LCUP_COOKIE_OID, &entrycsn_bv );
722
723                                                                 ch_free( entrycsn_bv.bv_val );
724                                                                 entrycsn_bv.bv_val = NULL;
725
726                                                         } else {
727                                                                 /* Do not send cookie */
728                                                                 ber_printf( ber,
729                                                                         "{bbN}",
730                                                                         SLAP_LCUP_STATE_UPDATE_FALSE,
731                                                                         SLAP_LCUP_ENTRY_DELETED_FALSE );
732                                                         }
733
734                                                         ctrls[0]->ldctl_oid = LDAP_CONTROL_ENTRY_UPDATE;
735                                                         ctrls[0]->ldctl_iscritical = op->o_clientupdate;
736                                                         ret = ber_flatten( ber, &bv );
737
738                                                         if ( ret < 0 ) {
739 #ifdef NEW_LOGGING
740                                                                 LDAP_LOG ( OPERATION, RESULTS, 
741                                                                         "bdb_search: ber_flatten failed\n",
742                                                                         0, 0, 0 );
743 #else
744                                                                 Debug( LDAP_DEBUG_TRACE,
745                                                                         "bdb_search: ber_flatten failed\n",
746                                                                         0, 0, 0 );
747 #endif
748                                                                 send_ldap_result( conn, op, rc=LDAP_OTHER,
749                                                                         NULL, "internal error", NULL, NULL );
750                                                                 goto done;
751                                                         }
752
753                                                         ber_dupbv( &ctrls[0]->ldctl_value, bv );
754                                                         
755                                                         result = send_search_entry( be, conn, op,
756                                                                 e, attrs, attrsonly, ctrls);
757
758                                                         ch_free( ctrls[0]->ldctl_value.bv_val );
759                                                         ch_free( ctrls[0] );
760                                                         ber_free( ber, 1 );
761                                                         ber_bvfree( bv );
762                                                 } else
763 #endif /* LDAP_CLIENT_UPDATE */
764                                                 {
765                                                         result = send_search_entry( be, conn, op,
766                                                                 e, attrs, attrsonly, NULL);
767                                                 }
768                                         }
769
770                                         switch (result) {
771                                         case 0:         /* entry sent ok */
772                                                 nentries++;
773                                                 break;
774                                         case 1:         /* entry not sent */
775                                                 break;
776                                         case -1:        /* connection closed */
777                                                 bdb_cache_return_entry_r(bdb->bi_dbenv,
778                                                         &bdb->bi_cache, e, &lock);
779                                                 e = NULL;
780                                                 rc = LDAP_OTHER;
781                                                 goto done;
782                                         }
783                                 }
784                         } else {
785 #ifdef NEW_LOGGING
786                                 LDAP_LOG ( OPERATION, RESULTS,
787                                         "bdb_search: %ld scope not okay\n", (long) id, 0, 0);
788 #else
789                                 Debug( LDAP_DEBUG_TRACE,
790                                         "bdb_search: %ld scope not okay\n",
791                                         (long) id, 0, 0 );
792 #endif
793                         }
794                 } else {
795 #ifdef NEW_LOGGING
796                         LDAP_LOG ( OPERATION, RESULTS,
797                                 "bdb_search: %ld does match filter\n", (long) id, 0, 0);
798 #else
799                         Debug( LDAP_DEBUG_TRACE,
800                                 "bdb_search: %ld does match filter\n",
801                                 (long) id, 0, 0 );
802 #endif
803                 }
804
805 loop_continue:
806                 if( e != NULL ) {
807                         /* free reader lock */
808                         bdb_cache_return_entry_r( bdb->bi_dbenv,
809                                 &bdb->bi_cache, e , &lock);
810                         e = NULL;
811                 }
812
813                 ldap_pvt_thread_yield();
814         }
815
816 #ifdef LDAP_CLIENT_UPDATE
817         if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
818                 int ret;
819                 LDAPControl *ctrls[2];
820                 BerElement *ber = ber_alloc_t( LBER_USE_DER );
821                 struct berval *bv;
822
823                 if ( ber == NULL ) {
824 #ifdef NEW_LOGGING
825                         LDAP_LOG ( OPERATION, RESULTS, 
826                                 "bdb_search: ber_alloc_t failed\n", 0, 0, 0 );
827 #else
828                         Debug( LDAP_DEBUG_TRACE, "bdb_search: ber_alloc_t failed\n",
829                                 0, 0, 0 );
830 #endif
831                         send_ldap_result( conn, op, rc=LDAP_OTHER,
832                                 NULL, "internal error", NULL, NULL );
833                         goto done;
834                 }
835
836                 ctrls[0] = ch_malloc ( sizeof ( LDAPControl ) );
837                 ctrls[1] = NULL;
838
839                 ber_printf( ber, "{sO", LCUP_COOKIE_OID, &latest_entrycsn_bv );
840                 ber_printf( ber, "N}" );
841
842                 ctrls[0]->ldctl_oid = LDAP_CONTROL_CLIENT_UPDATE_DONE;
843                 ctrls[0]->ldctl_iscritical = op->o_clientupdate;
844                 ret = ber_flatten( ber, &bv );
845
846                 if ( ret < 0 ) {
847 #ifdef NEW_LOGGING
848                         LDAP_LOG ( OPERATION, RESULTS, 
849                                 "bdb_search: ber_flatten failed\n", 0, 0, 0 );
850 #else
851                         Debug( LDAP_DEBUG_TRACE, "bdb_search: ber_flatten failed\n",
852                                 0, 0, 0 );
853 #endif
854                         send_ldap_result( conn, op, rc=LDAP_OTHER,
855                                 NULL, "internal error", NULL, NULL );
856                         goto done;
857                 }
858
859                 ber_dupbv( &ctrls[0]->ldctl_value, bv );
860
861                 send_search_result( conn, op,
862                         v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
863                         NULL, NULL, v2refs, ctrls, nentries );
864
865                 ch_free( latest_entrycsn_bv.bv_val );
866                 latest_entrycsn_bv.bv_val = NULL;
867                 ch_free( ctrls[0]->ldctl_value.bv_val );
868                 ch_free( ctrls[0] );
869                 ber_free( ber, 1 );
870                 ber_bvfree( bv );
871         } else
872 #endif /* LDAP_CLIENT_UPDATE */
873         {
874                 send_search_result( conn, op,
875                         v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
876                         NULL, NULL, v2refs, NULL, nentries );
877         }
878
879         rc = 0;
880
881 done:
882         if( e != NULL ) {
883                 /* free reader lock */
884                 bdb_cache_return_entry_r ( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
885         }
886
887 #ifdef LDAP_CLIENT_UPDATE
888         if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
889                 if ( csnfeq.f_ava != NULL && csnfeq.f_av_value.bv_val != NULL ) {
890                         ch_free( csnfeq.f_av_value.bv_val );
891                 }
892         
893                 if ( csnfge.f_ava != NULL && csnfge.f_av_value.bv_val != NULL ) {
894                         ch_free( csnfge.f_av_value.bv_val );
895                 }
896         }
897 #endif /* LDAP_CLIENT_UPDATE */
898
899         LOCK_ID_FREE (bdb->bi_dbenv, locker );
900
901         if( v2refs ) ber_bvarray_free( v2refs );
902         if( realbase.bv_val ) ch_free( realbase.bv_val );
903
904         return rc;
905 }
906
907
908 static int base_candidate(
909         BackendDB       *be,
910         Entry   *e,
911         ID              *ids )
912 {
913 #ifdef NEW_LOGGING
914         LDAP_LOG ( OPERATION, ENTRY,
915                 "base_candidate: base: \"%s\" (0x%08lx)\n", e->e_dn, (long) e->e_id, 0);
916 #else
917         Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
918                 e->e_dn, (long) e->e_id, 0);
919 #endif
920
921         ids[0] = 1;
922         ids[1] = e->e_id;
923         return 0;
924 }
925
926 /* Look for "objectClass Present" in this filter.
927  * Also count depth of filter tree while we're at it.
928  */
929 static int oc_filter(
930         Filter *f,
931         int cur,
932         int *max
933 )
934 {
935         int rc = 0;
936
937         if( cur > *max ) *max = cur;
938
939         switch(f->f_choice) {
940         case LDAP_FILTER_PRESENT:
941                 if (f->f_desc == slap_schema.si_ad_objectClass) {
942                         rc = 1;
943                 }
944                 break;
945
946         case LDAP_FILTER_AND:
947         case LDAP_FILTER_OR:
948                 cur++;
949                 for (f=f->f_and; f; f=f->f_next) {
950                         (void) oc_filter(f, cur, max);
951                 }
952                 break;
953
954         default:
955                 break;
956         }
957         return rc;
958 }
959
960 #define SRCH_STACK_SIZE 16
961
962 static void search_stack_free( void *key, void *data)
963 {
964         ch_free(data);
965 }
966
967 static void *search_stack(
968         BackendDB *be,
969         Operation *op
970 )
971 {
972         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
973         void *ret = NULL;
974
975         if ( op->o_threadctx ) {
976                 ldap_pvt_thread_pool_getkey( op->o_threadctx, search_stack,
977                         &ret, NULL );
978         } else {
979                 ret = bdb->bi_search_stack;
980         }
981
982         if ( !ret ) {
983                 ret = ch_malloc( SRCH_STACK_SIZE * BDB_IDL_UM_SIZE * sizeof( ID ) );
984                 if ( op->o_threadctx ) {
985                         ldap_pvt_thread_pool_setkey( op->o_threadctx, search_stack,
986                                 ret, search_stack_free );
987                 } else {
988                         bdb->bi_search_stack = ret;
989                 }
990         }
991         return ret;
992 }
993
994 static int search_candidates(
995         BackendDB *be,
996         Operation *op,
997         Entry *e,
998         Filter *filter,
999         int scope,
1000         int deref,
1001         ID      *ids )
1002 {
1003         int rc, depth = 1;
1004         Filter          f, scopef, rf, xf;
1005         ID              *stack;
1006         AttributeAssertion aa_ref;
1007 #ifdef BDB_SUBENTRIES
1008         Filter  sf;
1009         AttributeAssertion aa_subentry;
1010 #endif
1011 #ifdef BDB_ALIASES
1012         Filter  af;
1013         AttributeAssertion aa_alias;
1014 #endif
1015
1016         /*
1017          * This routine takes as input a filter (user-filter)
1018          * and rewrites it as follows:
1019          *      (&(scope=DN)[(objectClass=subentry)]
1020          *              (|[(objectClass=referral)(objectClass=alias)](user-filter))
1021          */
1022
1023 #ifdef NEW_LOGGING
1024         LDAP_LOG ( OPERATION, ENTRY,
1025                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n", 
1026                 e->e_dn, (long) e->e_id, scope);
1027 #else
1028         Debug(LDAP_DEBUG_TRACE,
1029                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
1030                 e->e_dn, (long) e->e_id, scope );
1031 #endif
1032
1033         xf.f_or = filter;
1034         xf.f_choice = LDAP_FILTER_OR;
1035         xf.f_next = NULL;
1036
1037         /* If the user's filter uses objectClass=*,
1038          * these clauses are redundant.
1039          */
1040         if (!oc_filter(filter, 1, &depth) && !get_subentries_visibility(op) ) {
1041                 if( !get_manageDSAit(op) ) { /* match referrals */
1042                         struct berval bv_ref = { sizeof("referral")-1, "referral" };
1043                         rf.f_choice = LDAP_FILTER_EQUALITY;
1044                         rf.f_ava = &aa_ref;
1045                         rf.f_av_desc = slap_schema.si_ad_objectClass;
1046                         rf.f_av_value = bv_ref;
1047                         rf.f_next = xf.f_or;
1048                         xf.f_or = &rf;
1049                 }
1050
1051 #ifdef BDB_ALIASES
1052                 if( deref & LDAP_DEREF_SEARCHING ) { /* match aliases */
1053                         struct berval bv_alias = { sizeof("alias")-1, "alias" };
1054                         af.f_choice = LDAP_FILTER_EQUALITY;
1055                         af.f_ava = &aa_alias;
1056                         af.f_av_desc = slap_schema.si_ad_objectClass;
1057                         af.f_av_value = bv_alias;
1058                         af.f_next = xf.f_or;
1059                         xf.f_or = &af;
1060                 }
1061 #endif
1062                 /* We added one of these clauses, filter depth increased */
1063                 if( xf.f_or != filter ) depth++;
1064         }
1065
1066         f.f_next = NULL;
1067         f.f_choice = LDAP_FILTER_AND;
1068         f.f_and = &scopef;
1069         scopef.f_choice = scope == LDAP_SCOPE_SUBTREE
1070                 ? SLAPD_FILTER_DN_SUBTREE
1071                 : SLAPD_FILTER_DN_ONE;
1072         scopef.f_dn = &e->e_nname;
1073         scopef.f_next = xf.f_or == filter ? filter : &xf ;
1074         /* Filter depth increased again, adding scope clause */
1075         depth++;
1076
1077 #ifdef BDB_SUBENTRIES
1078         if( get_subentries_visibility( op ) ) {
1079                 struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
1080                 sf.f_choice = LDAP_FILTER_EQUALITY;
1081                 sf.f_ava = &aa_subentry;
1082                 sf.f_av_desc = slap_schema.si_ad_objectClass;
1083                 sf.f_av_value = bv_subentry;
1084                 sf.f_next = scopef.f_next;
1085                 scopef.f_next = &sf;
1086         }
1087 #endif
1088
1089         /* Allocate IDL stack, plus 1 more for former tmp */
1090         if ( depth+1 > SRCH_STACK_SIZE ) {
1091                 stack = ch_malloc( (depth + 1) * BDB_IDL_UM_SIZE * sizeof( ID ) );
1092         } else {
1093                 stack = search_stack( be, op );
1094         }
1095
1096         rc = bdb_filter_candidates( be, &f, ids, stack, stack+BDB_IDL_UM_SIZE );
1097
1098         if ( depth+1 > SRCH_STACK_SIZE ) {
1099                 ch_free( stack );
1100         }
1101
1102         if( rc ) {
1103 #ifdef NEW_LOGGING
1104                 LDAP_LOG ( OPERATION, DETAIL1,
1105                         "bdb_search_candidates: failed (rc=%d)\n", rc, 0, 0  );
1106 #else
1107                 Debug(LDAP_DEBUG_TRACE,
1108                         "bdb_search_candidates: failed (rc=%d)\n",
1109                         rc, NULL, NULL );
1110 #endif
1111
1112         } else {
1113 #ifdef NEW_LOGGING
1114                 LDAP_LOG ( OPERATION, DETAIL1,
1115                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
1116                         (long) ids[0], (long) BDB_IDL_FIRST(ids), 
1117                         (long) BDB_IDL_LAST(ids));
1118 #else
1119                 Debug(LDAP_DEBUG_TRACE,
1120                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
1121                         (long) ids[0],
1122                         (long) BDB_IDL_FIRST(ids),
1123                         (long) BDB_IDL_LAST(ids) );
1124 #endif
1125         }
1126
1127         return rc;
1128 }
1129
1130 static void
1131 send_pagerequest_response( 
1132         Connection      *conn,
1133         Operation       *op,
1134         ID              lastid,
1135         int             nentries,
1136         int             tentries )
1137 {
1138         LDAPControl     ctrl, *ctrls[2];
1139         BerElement      *ber;
1140         struct berval   *bvalp, cookie = { 0, NULL };
1141         PagedResultsCookie respcookie;
1142
1143 #ifdef NEW_LOGGING
1144         LDAP_LOG ( OPERATION, ENTRY,
1145                 "send_pagerequest_response: lastid: (0x%08lx) "
1146                 "nentries: (0x%081x)\n", 
1147                 lastid, nentries, NULL );
1148 #else
1149         Debug(LDAP_DEBUG_ARGS, "send_pagerequest_response: lastid: (0x%08lx) "
1150                         "nentries: (0x%081x)\n", lastid, nentries, NULL );
1151 #endif
1152
1153         ctrl.ldctl_value.bv_val = NULL;
1154         ctrls[0] = &ctrl;
1155         ctrls[1] = NULL;
1156
1157         if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1158                 goto done;
1159         }
1160
1161         respcookie = ( PagedResultsCookie )lastid;
1162         conn->c_pagedresults_state.ps_cookie = respcookie;
1163         cookie.bv_len = sizeof( respcookie );
1164         cookie.bv_val = (char *)&respcookie;
1165
1166         /*
1167          * FIXME: we should consider sending an estimate of the entries
1168          * left, after appropriate security check is done
1169          */
1170         ber_printf( ber, "{iO}", tentries, &cookie ); 
1171
1172         if ( ber_flatten( ber, &bvalp ) == LBER_ERROR ) {
1173                 goto done;
1174         }
1175
1176         ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
1177         ctrls[0]->ldctl_value = ( *bvalp );
1178         ctrls[0]->ldctl_iscritical = 0;
1179
1180         send_search_result( conn, op,
1181                 LDAP_SUCCESS,
1182                 NULL, NULL, NULL, ctrls, nentries );
1183
1184 done:
1185         (void) ber_free( ber, 1 );
1186         if ( ctrls[0]->ldctl_value.bv_val ) {
1187                 ch_free( ctrls[0]->ldctl_value.bv_val );
1188         }
1189 }                       
1190