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