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