search.c revision 1.3 1 1.1 christos /* $NetBSD: search.c,v 1.3 2025/09/05 21:16:32 christos Exp $ */
2 1.1 christos
3 1.1 christos /* OpenLDAP WiredTiger backend */
4 1.1 christos /* $OpenLDAP$ */
5 1.1 christos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 1.1 christos *
7 1.3 christos * Copyright 2002-2024 The OpenLDAP Foundation.
8 1.1 christos * All rights reserved.
9 1.1 christos *
10 1.1 christos * Redistribution and use in source and binary forms, with or without
11 1.1 christos * modification, are permitted only as authorized by the OpenLDAP
12 1.1 christos * Public License.
13 1.1 christos *
14 1.1 christos * A copy of this license is available in the file LICENSE in the
15 1.1 christos * top-level directory of the distribution or, alternatively, at
16 1.1 christos * <http://www.OpenLDAP.org/license.html>.
17 1.1 christos */
18 1.1 christos /* ACKNOWLEDGEMENTS:
19 1.1 christos * This work was developed by HAMANO Tsukasa <hamano (at) osstech.co.jp>
20 1.1 christos * based on back-bdb for inclusion in OpenLDAP Software.
21 1.1 christos * WiredTiger is a product of MongoDB Inc.
22 1.1 christos */
23 1.1 christos
24 1.1 christos #include <sys/cdefs.h>
25 1.1 christos __RCSID("$NetBSD: search.c,v 1.3 2025/09/05 21:16:32 christos Exp $");
26 1.1 christos
27 1.1 christos #include "portable.h"
28 1.1 christos
29 1.1 christos #include <stdio.h>
30 1.1 christos #include <ac/string.h>
31 1.1 christos
32 1.1 christos #include "back-wt.h"
33 1.1 christos #include "idl.h"
34 1.1 christos
35 1.1 christos static int search_aliases(
36 1.1 christos Operation *op,
37 1.1 christos SlapReply *rs,
38 1.1 christos Entry *e,
39 1.1 christos WT_SESSION *session,
40 1.1 christos ID *ids,
41 1.1 christos ID *scopes,
42 1.1 christos ID *stack )
43 1.1 christos {
44 1.1 christos /* TODO: search_aliases does not implement yet. */
45 1.1 christos WT_IDL_ZERO( ids );
46 1.1 christos return 0;
47 1.1 christos }
48 1.1 christos
49 1.1 christos static int base_candidate(
50 1.1 christos BackendDB *be,
51 1.1 christos Entry *e,
52 1.1 christos ID *ids )
53 1.1 christos {
54 1.1 christos Debug(LDAP_DEBUG_ARGS,
55 1.3 christos "base_candidate: base: \"%s\" (0x%08lx)\n",
56 1.1 christos e->e_nname.bv_val, (long) e->e_id );
57 1.1 christos
58 1.1 christos ids[0] = 1;
59 1.1 christos ids[1] = e->e_id;
60 1.1 christos return 0;
61 1.1 christos }
62 1.1 christos
63 1.1 christos /* Look for "objectClass Present" in this filter.
64 1.1 christos * Also count depth of filter tree while we're at it.
65 1.1 christos */
66 1.1 christos static int oc_filter(
67 1.1 christos Filter *f,
68 1.1 christos int cur,
69 1.1 christos int *max )
70 1.1 christos {
71 1.1 christos int rc = 0;
72 1.1 christos
73 1.1 christos assert( f != NULL );
74 1.1 christos
75 1.1 christos if( cur > *max ) *max = cur;
76 1.1 christos
77 1.1 christos switch( f->f_choice ) {
78 1.1 christos case LDAP_FILTER_PRESENT:
79 1.1 christos if (f->f_desc == slap_schema.si_ad_objectClass) {
80 1.1 christos rc = 1;
81 1.1 christos }
82 1.1 christos break;
83 1.1 christos
84 1.1 christos case LDAP_FILTER_AND:
85 1.1 christos case LDAP_FILTER_OR:
86 1.1 christos cur++;
87 1.1 christos for ( f=f->f_and; f; f=f->f_next ) {
88 1.1 christos (void) oc_filter(f, cur, max);
89 1.1 christos }
90 1.1 christos break;
91 1.1 christos
92 1.1 christos default:
93 1.1 christos break;
94 1.1 christos }
95 1.1 christos return rc;
96 1.1 christos }
97 1.1 christos
98 1.1 christos static void search_stack_free( void *key, void *data )
99 1.1 christos {
100 1.1 christos ber_memfree_x(data, NULL);
101 1.1 christos }
102 1.1 christos
103 1.1 christos static void *search_stack( Operation *op )
104 1.1 christos {
105 1.1 christos struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
106 1.1 christos void *ret = NULL;
107 1.1 christos
108 1.1 christos if ( op->o_threadctx ) {
109 1.1 christos ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
110 1.1 christos &ret, NULL );
111 1.1 christos } else {
112 1.1 christos ret = wi->wi_search_stack;
113 1.1 christos }
114 1.1 christos
115 1.1 christos if ( !ret ) {
116 1.1 christos ret = ch_malloc( wi->wi_search_stack_depth * WT_IDL_UM_SIZE
117 1.1 christos * sizeof( ID ) );
118 1.1 christos if ( op->o_threadctx ) {
119 1.1 christos ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
120 1.1 christos ret, search_stack_free, NULL, NULL );
121 1.1 christos } else {
122 1.1 christos wi->wi_search_stack = ret;
123 1.1 christos }
124 1.1 christos }
125 1.1 christos return ret;
126 1.1 christos }
127 1.1 christos
128 1.1 christos static int search_candidates(
129 1.1 christos Operation *op,
130 1.1 christos SlapReply *rs,
131 1.1 christos Entry *e,
132 1.1 christos wt_ctx *wc,
133 1.1 christos ID *ids,
134 1.1 christos ID *scopes )
135 1.1 christos {
136 1.1 christos struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
137 1.1 christos int rc, depth = 1;
138 1.1 christos Filter f, rf, xf, nf;
139 1.1 christos ID *stack;
140 1.1 christos AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
141 1.1 christos Filter sf;
142 1.1 christos AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
143 1.1 christos
144 1.1 christos Debug(LDAP_DEBUG_TRACE,
145 1.3 christos "wt_search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
146 1.1 christos e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
147 1.1 christos
148 1.1 christos xf.f_or = op->oq_search.rs_filter;
149 1.1 christos xf.f_choice = LDAP_FILTER_OR;
150 1.1 christos xf.f_next = NULL;
151 1.1 christos
152 1.1 christos /* If the user's filter uses objectClass=*,
153 1.1 christos * these clauses are redundant.
154 1.1 christos */
155 1.1 christos if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
156 1.1 christos && !get_subentries_visibility(op)) {
157 1.1 christos if( !get_manageDSAit(op) && !get_domainScope(op) ) {
158 1.1 christos /* match referral objects */
159 1.1 christos struct berval bv_ref = BER_BVC( "referral" );
160 1.1 christos rf.f_choice = LDAP_FILTER_EQUALITY;
161 1.1 christos rf.f_ava = &aa_ref;
162 1.1 christos rf.f_av_desc = slap_schema.si_ad_objectClass;
163 1.1 christos rf.f_av_value = bv_ref;
164 1.1 christos rf.f_next = xf.f_or;
165 1.1 christos xf.f_or = &rf;
166 1.1 christos depth++;
167 1.1 christos }
168 1.1 christos }
169 1.1 christos
170 1.1 christos f.f_next = NULL;
171 1.1 christos f.f_choice = LDAP_FILTER_AND;
172 1.1 christos f.f_and = &nf;
173 1.1 christos /* Dummy; we compute scope separately now */
174 1.1 christos nf.f_choice = SLAPD_FILTER_COMPUTED;
175 1.1 christos nf.f_result = LDAP_SUCCESS;
176 1.1 christos nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
177 1.1 christos ? op->oq_search.rs_filter : &xf ;
178 1.1 christos /* Filter depth increased again, adding dummy clause */
179 1.1 christos depth++;
180 1.1 christos
181 1.1 christos if( get_subentries_visibility( op ) ) {
182 1.1 christos struct berval bv_subentry = BER_BVC( "subentry" );
183 1.1 christos sf.f_choice = LDAP_FILTER_EQUALITY;
184 1.1 christos sf.f_ava = &aa_subentry;
185 1.1 christos sf.f_av_desc = slap_schema.si_ad_objectClass;
186 1.1 christos sf.f_av_value = bv_subentry;
187 1.1 christos sf.f_next = nf.f_next;
188 1.1 christos nf.f_next = &sf;
189 1.1 christos }
190 1.1 christos
191 1.1 christos /* Allocate IDL stack, plus 1 more for former tmp */
192 1.1 christos if ( depth+1 > wi->wi_search_stack_depth ) {
193 1.1 christos stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
194 1.1 christos } else {
195 1.1 christos stack = search_stack( op );
196 1.1 christos }
197 1.1 christos
198 1.1 christos if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
199 1.1 christos rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
200 1.1 christos if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
201 1.3 christos rc = wt_dn2idl( op, wc, &e->e_nname, e, ids, stack );
202 1.1 christos } else {
203 1.3 christos rc = wt_dn2idl(op, wc, &e->e_nname, e, ids, stack );
204 1.1 christos }
205 1.1 christos
206 1.1 christos if ( rc == LDAP_SUCCESS ) {
207 1.1 christos rc = wt_filter_candidates( op, wc, &f, ids,
208 1.1 christos stack, stack+WT_IDL_UM_SIZE );
209 1.1 christos }
210 1.1 christos
211 1.1 christos if ( depth+1 > wi->wi_search_stack_depth ) {
212 1.1 christos ch_free( stack );
213 1.1 christos }
214 1.1 christos
215 1.1 christos if( rc ) {
216 1.1 christos Debug(LDAP_DEBUG_TRACE,
217 1.3 christos "wt_search_candidates: failed (rc=%d)\n", rc );
218 1.1 christos
219 1.1 christos } else {
220 1.1 christos Debug(LDAP_DEBUG_TRACE,
221 1.3 christos "wt_search_candidates: id=%ld first=%ld last=%ld\n",
222 1.1 christos (long) ids[0],
223 1.1 christos (long) WT_IDL_FIRST(ids),
224 1.1 christos (long) WT_IDL_LAST(ids));
225 1.1 christos }
226 1.1 christos return 0;
227 1.1 christos }
228 1.1 christos
229 1.1 christos static int
230 1.1 christos parse_paged_cookie( Operation *op, SlapReply *rs )
231 1.1 christos {
232 1.1 christos int rc = LDAP_SUCCESS;
233 1.1 christos PagedResultsState *ps = op->o_pagedresults_state;
234 1.1 christos
235 1.1 christos /* this function must be invoked only if the pagedResults
236 1.1 christos * control has been detected, parsed and partially checked
237 1.1 christos * by the frontend */
238 1.1 christos assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
239 1.1 christos
240 1.1 christos /* cookie decoding/checks deferred to backend... */
241 1.1 christos if ( ps->ps_cookieval.bv_len ) {
242 1.1 christos PagedResultsCookie reqcookie;
243 1.1 christos if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
244 1.1 christos /* bad cookie */
245 1.1 christos rs->sr_text = "paged results cookie is invalid";
246 1.1 christos rc = LDAP_PROTOCOL_ERROR;
247 1.1 christos goto done;
248 1.1 christos }
249 1.1 christos
250 1.3 christos memcpy( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
251 1.1 christos
252 1.1 christos if ( reqcookie > ps->ps_cookie ) {
253 1.1 christos /* bad cookie */
254 1.1 christos rs->sr_text = "paged results cookie is invalid";
255 1.1 christos rc = LDAP_PROTOCOL_ERROR;
256 1.1 christos goto done;
257 1.1 christos
258 1.1 christos } else if ( reqcookie < ps->ps_cookie ) {
259 1.1 christos rs->sr_text = "paged results cookie is invalid or old";
260 1.1 christos rc = LDAP_UNWILLING_TO_PERFORM;
261 1.1 christos goto done;
262 1.1 christos }
263 1.1 christos
264 1.1 christos } else {
265 1.1 christos /* we're going to use ps_cookie */
266 1.1 christos op->o_conn->c_pagedresults_state.ps_cookie = 0;
267 1.1 christos }
268 1.1 christos
269 1.1 christos done:;
270 1.1 christos
271 1.1 christos return rc;
272 1.1 christos }
273 1.1 christos
274 1.1 christos static void
275 1.1 christos send_paged_response(
276 1.1 christos Operation *op,
277 1.1 christos SlapReply *rs,
278 1.1 christos ID *lastid,
279 1.1 christos int tentries )
280 1.1 christos {
281 1.1 christos LDAPControl *ctrls[2];
282 1.1 christos BerElementBuffer berbuf;
283 1.1 christos BerElement *ber = (BerElement *)&berbuf;
284 1.1 christos PagedResultsCookie respcookie;
285 1.1 christos struct berval cookie;
286 1.1 christos
287 1.1 christos Debug(LDAP_DEBUG_ARGS,
288 1.3 christos "send_paged_response: lastid=0x%08lx nentries=%d\n",
289 1.1 christos lastid ? *lastid : 0, rs->sr_nentries );
290 1.1 christos
291 1.1 christos ctrls[1] = NULL;
292 1.1 christos
293 1.1 christos ber_init2( ber, NULL, LBER_USE_DER );
294 1.1 christos
295 1.1 christos if ( lastid ) {
296 1.1 christos respcookie = ( PagedResultsCookie )(*lastid);
297 1.1 christos cookie.bv_len = sizeof( respcookie );
298 1.1 christos cookie.bv_val = (char *)&respcookie;
299 1.1 christos
300 1.1 christos } else {
301 1.1 christos respcookie = ( PagedResultsCookie )0;
302 1.1 christos BER_BVSTR( &cookie, "" );
303 1.1 christos }
304 1.1 christos
305 1.1 christos op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
306 1.1 christos op->o_conn->c_pagedresults_state.ps_count =
307 1.1 christos ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
308 1.1 christos rs->sr_nentries;
309 1.1 christos
310 1.1 christos /* return size of 0 -- no estimate */
311 1.1 christos ber_printf( ber, "{iO}", 0, &cookie );
312 1.1 christos
313 1.1 christos ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
314 1.1 christos if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
315 1.1 christos goto done;
316 1.1 christos }
317 1.1 christos
318 1.1 christos ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
319 1.1 christos ctrls[0]->ldctl_iscritical = 0;
320 1.1 christos
321 1.1 christos slap_add_ctrls( op, rs, ctrls );
322 1.1 christos rs->sr_err = LDAP_SUCCESS;
323 1.1 christos send_ldap_result( op, rs );
324 1.1 christos
325 1.1 christos done:
326 1.1 christos (void) ber_free_buf( ber );
327 1.1 christos }
328 1.1 christos
329 1.1 christos int
330 1.1 christos wt_search( Operation *op, SlapReply *rs )
331 1.1 christos {
332 1.1 christos struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
333 1.1 christos ID id, cursor;
334 1.1 christos ID lastid = NOID;
335 1.1 christos int manageDSAit;
336 1.1 christos wt_ctx *wc;
337 1.3 christos int rc = LDAP_OTHER;
338 1.1 christos Entry *e = NULL;
339 1.3 christos Entry *ae = NULL;
340 1.1 christos Entry *base = NULL;
341 1.1 christos slap_mask_t mask;
342 1.1 christos time_t stoptime;
343 1.1 christos
344 1.1 christos ID candidates[WT_IDL_UM_SIZE];
345 1.1 christos ID scopes[WT_IDL_DB_SIZE];
346 1.1 christos int tentries = 0;
347 1.1 christos unsigned nentries = 0;
348 1.1 christos
349 1.3 christos Debug( LDAP_DEBUG_ARGS, "==> wt_search: %s\n", op->o_req_dn.bv_val );
350 1.1 christos
351 1.1 christos manageDSAit = get_manageDSAit( op );
352 1.1 christos
353 1.1 christos wc = wt_ctx_get(op, wi);
354 1.1 christos if( !wc ){
355 1.1 christos Debug( LDAP_DEBUG_ANY,
356 1.3 christos "wt_search: wt_ctx_get failed: %d\n", rc );
357 1.1 christos send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
358 1.1 christos return rc;
359 1.1 christos }
360 1.1 christos
361 1.1 christos /* get entry */
362 1.1 christos rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
363 1.1 christos switch( rc ) {
364 1.1 christos case 0:
365 1.1 christos break;
366 1.1 christos case WT_NOTFOUND:
367 1.3 christos rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &ae);
368 1.3 christos break;
369 1.1 christos default:
370 1.1 christos /* TODO: error handling */
371 1.1 christos Debug( LDAP_DEBUG_ANY,
372 1.3 christos "<== wt_search: error at wt_dn2entry() rc=%d\n", rc );
373 1.1 christos send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
374 1.1 christos goto done;
375 1.1 christos }
376 1.1 christos
377 1.1 christos if ( op->ors_deref & LDAP_DEREF_FINDING ) {
378 1.1 christos /* not implement yet */
379 1.1 christos }
380 1.1 christos
381 1.1 christos if ( e == NULL ) {
382 1.3 christos if ( ae ) {
383 1.3 christos struct berval matched_dn = BER_BVNULL;
384 1.3 christos /* found ancestor entry */
385 1.3 christos if ( access_allowed( op, ae,
386 1.3 christos slap_schema.si_ad_entry,
387 1.3 christos NULL, ACL_DISCLOSE, NULL ) ) {
388 1.3 christos BerVarray erefs = NULL;
389 1.3 christos ber_dupbv( &matched_dn, &ae->e_name );
390 1.3 christos erefs = is_entry_referral( ae )
391 1.3 christos ? get_entry_referrals( op, ae )
392 1.3 christos : NULL;
393 1.3 christos rs->sr_err = LDAP_REFERRAL;
394 1.3 christos rs->sr_matched = matched_dn.bv_val;
395 1.3 christos if ( erefs ) {
396 1.3 christos rs->sr_ref = referral_rewrite( erefs, &matched_dn,
397 1.3 christos &op->o_req_dn, op->oq_search.rs_scope );
398 1.3 christos ber_bvarray_free( erefs );
399 1.3 christos }
400 1.3 christos Debug( LDAP_DEBUG_ARGS,
401 1.3 christos "wt_search: ancestor is referral\n");
402 1.3 christos rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
403 1.3 christos send_ldap_result( op, rs );
404 1.3 christos goto done;
405 1.3 christos }
406 1.3 christos }
407 1.3 christos Debug( LDAP_DEBUG_ARGS,
408 1.3 christos "wt_search: no such object %s\n",
409 1.3 christos op->o_req_dn.bv_val);
410 1.3 christos rs->sr_err = LDAP_NO_SUCH_OBJECT;
411 1.3 christos send_ldap_result( op, rs );
412 1.3 christos goto done;
413 1.1 christos }
414 1.1 christos
415 1.1 christos /* NOTE: __NEW__ "search" access is required
416 1.1 christos * on searchBase object */
417 1.1 christos if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
418 1.1 christos NULL, ACL_SEARCH, NULL, &mask ) )
419 1.1 christos {
420 1.1 christos if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
421 1.1 christos rs->sr_err = LDAP_NO_SUCH_OBJECT;
422 1.1 christos } else {
423 1.1 christos rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
424 1.1 christos }
425 1.1 christos
426 1.1 christos send_ldap_result( op, rs );
427 1.1 christos goto done;
428 1.1 christos }
429 1.1 christos
430 1.1 christos if ( !manageDSAit && is_entry_referral( e ) ) {
431 1.3 christos struct berval matched_dn = BER_BVNULL;
432 1.3 christos BerVarray erefs = NULL;
433 1.3 christos ber_dupbv( &matched_dn, &e->e_name );
434 1.3 christos erefs = get_entry_referrals( op, e );
435 1.3 christos rs->sr_err = LDAP_REFERRAL;
436 1.3 christos if ( erefs ) {
437 1.3 christos rs->sr_ref = referral_rewrite( erefs, &matched_dn,
438 1.3 christos &op->o_req_dn, op->oq_search.rs_scope );
439 1.3 christos ber_bvarray_free( erefs );
440 1.3 christos if ( !rs->sr_ref ) {
441 1.3 christos rs->sr_text = "bad_referral object";
442 1.3 christos }
443 1.3 christos }
444 1.3 christos Debug( LDAP_DEBUG_ARGS, "wt_search: entry is referral\n");
445 1.3 christos rs->sr_matched = matched_dn.bv_val;
446 1.3 christos send_ldap_result( op, rs );
447 1.3 christos ber_bvarray_free( rs->sr_ref );
448 1.3 christos rs->sr_ref = NULL;
449 1.3 christos ber_memfree( matched_dn.bv_val );
450 1.3 christos rs->sr_matched = NULL;
451 1.3 christos goto done;
452 1.1 christos }
453 1.1 christos
454 1.1 christos if ( get_assert( op ) &&
455 1.1 christos ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
456 1.1 christos {
457 1.1 christos rs->sr_err = LDAP_ASSERTION_FAILED;
458 1.1 christos send_ldap_result( op, rs );
459 1.1 christos goto done;
460 1.1 christos }
461 1.1 christos
462 1.1 christos /* compute it anyway; root does not use it */
463 1.1 christos stoptime = op->o_time + op->ors_tlimit;
464 1.1 christos
465 1.1 christos base = e;
466 1.1 christos
467 1.1 christos e = NULL;
468 1.1 christos
469 1.1 christos /* select candidates */
470 1.1 christos if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
471 1.1 christos rs->sr_err = base_candidate( op->o_bd, base, candidates );
472 1.1 christos }else{
473 1.1 christos WT_IDL_ZERO( candidates );
474 1.1 christos WT_IDL_ZERO( scopes );
475 1.1 christos rc = search_candidates( op, rs, base,
476 1.1 christos wc, candidates, scopes );
477 1.1 christos switch(rc){
478 1.1 christos case 0:
479 1.1 christos case WT_NOTFOUND:
480 1.1 christos break;
481 1.1 christos default:
482 1.3 christos Debug( LDAP_DEBUG_ANY, "wt_search: error search_candidates\n" );
483 1.1 christos send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
484 1.1 christos goto done;
485 1.1 christos }
486 1.1 christos }
487 1.1 christos
488 1.1 christos /* start cursor at beginning of candidates.
489 1.1 christos */
490 1.1 christos cursor = 0;
491 1.1 christos
492 1.1 christos if ( candidates[0] == 0 ) {
493 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_search: no candidates\n" );
494 1.1 christos goto nochange;
495 1.1 christos }
496 1.1 christos
497 1.1 christos if ( op->ors_limit &&
498 1.1 christos op->ors_limit->lms_s_unchecked != -1 &&
499 1.1 christos WT_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
500 1.1 christos {
501 1.1 christos rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
502 1.1 christos send_ldap_result( op, rs );
503 1.1 christos rs->sr_err = LDAP_SUCCESS;
504 1.1 christos goto done;
505 1.1 christos }
506 1.1 christos
507 1.1 christos if ( op->ors_limit == NULL /* isroot == TRUE */ ||
508 1.1 christos !op->ors_limit->lms_s_pr_hide )
509 1.1 christos {
510 1.1 christos tentries = WT_IDL_N(candidates);
511 1.1 christos }
512 1.1 christos
513 1.1 christos if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
514 1.1 christos /* TODO: pageresult */
515 1.1 christos PagedResultsState *ps = op->o_pagedresults_state;
516 1.1 christos /* deferred cookie parsing */
517 1.1 christos rs->sr_err = parse_paged_cookie( op, rs );
518 1.1 christos if ( rs->sr_err != LDAP_SUCCESS ) {
519 1.1 christos send_ldap_result( op, rs );
520 1.1 christos goto done;
521 1.1 christos }
522 1.1 christos
523 1.1 christos cursor = (ID) ps->ps_cookie;
524 1.1 christos if ( cursor && ps->ps_size == 0 ) {
525 1.1 christos rs->sr_err = LDAP_SUCCESS;
526 1.1 christos rs->sr_text = "search abandoned by pagedResult size=0";
527 1.1 christos send_ldap_result( op, rs );
528 1.1 christos goto done;
529 1.1 christos }
530 1.1 christos id = wt_idl_first( candidates, &cursor );
531 1.1 christos if ( id == NOID ) {
532 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_search: no paged results candidates\n" );
533 1.1 christos send_paged_response( op, rs, &lastid, 0 );
534 1.1 christos
535 1.1 christos rs->sr_err = LDAP_OTHER;
536 1.1 christos goto done;
537 1.1 christos }
538 1.1 christos nentries = ps->ps_count;
539 1.1 christos if ( id == (ID)ps->ps_cookie )
540 1.1 christos id = wt_idl_next( candidates, &cursor );
541 1.1 christos goto loop_begin;
542 1.1 christos }
543 1.1 christos
544 1.1 christos for ( id = wt_idl_first( candidates, &cursor );
545 1.1 christos id != NOID ; id = wt_idl_next( candidates, &cursor ) )
546 1.1 christos {
547 1.1 christos int scopeok;
548 1.1 christos
549 1.1 christos loop_begin:
550 1.1 christos
551 1.1 christos /* check for abandon */
552 1.1 christos if ( op->o_abandon ) {
553 1.1 christos rs->sr_err = SLAPD_ABANDON;
554 1.1 christos send_ldap_result( op, rs );
555 1.1 christos goto done;
556 1.1 christos }
557 1.1 christos
558 1.1 christos /* mostly needed by internal searches,
559 1.1 christos * e.g. related to syncrepl, for whom
560 1.1 christos * abandon does not get set... */
561 1.1 christos if ( slapd_shutdown ) {
562 1.1 christos rs->sr_err = LDAP_UNAVAILABLE;
563 1.1 christos send_ldap_disconnect( op, rs );
564 1.1 christos goto done;
565 1.1 christos }
566 1.1 christos
567 1.1 christos /* check time limit */
568 1.1 christos if ( op->ors_tlimit != SLAP_NO_LIMIT
569 1.1 christos && slap_get_time() > stoptime )
570 1.1 christos {
571 1.1 christos rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
572 1.1 christos rs->sr_ref = rs->sr_v2ref;
573 1.1 christos send_ldap_result( op, rs );
574 1.1 christos rs->sr_err = LDAP_SUCCESS;
575 1.1 christos goto done;
576 1.1 christos }
577 1.1 christos
578 1.1 christos nentries++;
579 1.1 christos
580 1.1 christos fetch_entry_retry:
581 1.1 christos
582 1.3 christos rc = wt_id2entry(op->o_bd, wc, id, &e);
583 1.1 christos /* TODO: error handling */
584 1.1 christos if ( e == NULL ) {
585 1.1 christos /* TODO: */
586 1.1 christos goto loop_continue;
587 1.1 christos }
588 1.1 christos if ( is_entry_subentry( e ) ) {
589 1.1 christos if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
590 1.1 christos if(!get_subentries_visibility( op )) {
591 1.1 christos /* only subentries are visible */
592 1.1 christos goto loop_continue;
593 1.1 christos }
594 1.1 christos
595 1.1 christos } else if ( get_subentries( op ) &&
596 1.1 christos !get_subentries_visibility( op ))
597 1.1 christos {
598 1.1 christos /* only subentries are visible */
599 1.1 christos goto loop_continue;
600 1.1 christos }
601 1.1 christos
602 1.1 christos } else if ( get_subentries_visibility( op )) {
603 1.1 christos /* only subentries are visible */
604 1.1 christos goto loop_continue;
605 1.1 christos }
606 1.1 christos
607 1.1 christos scopeok = 0;
608 1.1 christos switch( op->ors_scope ) {
609 1.1 christos case LDAP_SCOPE_BASE:
610 1.1 christos /* This is always true, yes? */
611 1.1 christos if ( id == base->e_id ) scopeok = 1;
612 1.1 christos break;
613 1.1 christos case LDAP_SCOPE_ONELEVEL:
614 1.3 christos scopeok = dnIsSuffixScope(&e->e_nname, &base->e_nname, LDAP_SCOPE_ONELEVEL);
615 1.1 christos break;
616 1.3 christos case LDAP_SCOPE_CHILDREN:
617 1.3 christos if ( id == base->e_id ) break;
618 1.3 christos /* Fall-thru */
619 1.1 christos case LDAP_SCOPE_SUBTREE:
620 1.3 christos scopeok = dnIsSuffix(&e->e_nname, &base->e_nname);
621 1.1 christos break;
622 1.1 christos }
623 1.1 christos
624 1.1 christos /* aliases were already dereferenced in candidate list */
625 1.1 christos if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
626 1.1 christos /* but if the search base is an alias, and we didn't
627 1.1 christos * deref it when finding, return it.
628 1.1 christos */
629 1.1 christos if ( is_entry_alias(e) &&
630 1.1 christos ((op->ors_deref & LDAP_DEREF_FINDING) ||
631 1.1 christos !bvmatch(&e->e_nname, &op->o_req_ndn)))
632 1.1 christos {
633 1.1 christos goto loop_continue;
634 1.1 christos }
635 1.1 christos /* TODO: alias handling */
636 1.1 christos }
637 1.1 christos
638 1.1 christos /* Not in scope, ignore it */
639 1.1 christos if ( !scopeok )
640 1.1 christos {
641 1.3 christos Debug( LDAP_DEBUG_TRACE, "wt_search: %ld scope not okay\n",
642 1.1 christos (long) id );
643 1.1 christos goto loop_continue;
644 1.1 christos }
645 1.1 christos
646 1.1 christos /*
647 1.1 christos * if it's a referral, add it to the list of referrals. only do
648 1.1 christos * this for non-base searches, and don't check the filter
649 1.1 christos * explicitly here since it's only a candidate anyway.
650 1.1 christos */
651 1.1 christos if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
652 1.1 christos && is_entry_referral( e ) )
653 1.1 christos {
654 1.3 christos BerVarray erefs = get_entry_referrals( op, e );
655 1.3 christos rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
656 1.3 christos op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
657 1.3 christos ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
658 1.3 christos rs->sr_entry = e;
659 1.3 christos send_search_reference( op, rs );
660 1.3 christos rs->sr_entry = NULL;
661 1.3 christos ber_bvarray_free( rs->sr_ref );
662 1.3 christos ber_bvarray_free( erefs );
663 1.3 christos goto loop_continue;
664 1.1 christos }
665 1.1 christos
666 1.1 christos if ( !manageDSAit && is_entry_glue( e )) {
667 1.1 christos goto loop_continue;
668 1.1 christos }
669 1.1 christos
670 1.1 christos /* if it matches the filter and scope, send it */
671 1.1 christos rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
672 1.1 christos if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
673 1.1 christos /* check size limit */
674 1.1 christos if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
675 1.3 christos if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
676 1.3 christos wt_entry_return( e );
677 1.3 christos e = NULL;
678 1.3 christos send_paged_response( op, rs, &lastid, tentries );
679 1.3 christos goto done;
680 1.3 christos }
681 1.3 christos lastid = id;
682 1.1 christos }
683 1.1 christos
684 1.1 christos if (e) {
685 1.1 christos /* safe default */
686 1.1 christos rs->sr_attrs = op->oq_search.rs_attrs;
687 1.1 christos rs->sr_operational_attrs = NULL;
688 1.1 christos rs->sr_ctrls = NULL;
689 1.1 christos rs->sr_entry = e;
690 1.1 christos RS_ASSERT( e->e_private != NULL );
691 1.1 christos rs->sr_flags = REP_ENTRY_MUSTRELEASE;
692 1.1 christos rs->sr_err = LDAP_SUCCESS;
693 1.1 christos rs->sr_err = send_search_entry( op, rs );
694 1.1 christos rs->sr_attrs = NULL;
695 1.1 christos rs->sr_entry = NULL;
696 1.1 christos e = NULL;
697 1.3 christos
698 1.3 christos switch ( rs->sr_err ) {
699 1.3 christos case LDAP_SUCCESS: /* entry sent ok */
700 1.3 christos break;
701 1.3 christos default: /* entry not sent */
702 1.3 christos break;
703 1.3 christos case LDAP_BUSY:
704 1.3 christos send_ldap_result( op, rs );
705 1.3 christos goto done;
706 1.3 christos case LDAP_UNAVAILABLE:
707 1.3 christos rs->sr_err = LDAP_OTHER;
708 1.3 christos goto done;
709 1.3 christos case LDAP_SIZELIMIT_EXCEEDED:
710 1.3 christos rs->sr_ref = rs->sr_v2ref;
711 1.3 christos send_ldap_result( op, rs );
712 1.3 christos rs->sr_err = LDAP_SUCCESS;
713 1.3 christos goto done;
714 1.3 christos }
715 1.1 christos }
716 1.1 christos } else {
717 1.1 christos Debug( LDAP_DEBUG_TRACE,
718 1.3 christos "wt_search: %ld does not match filter\n", (long) id );
719 1.1 christos }
720 1.1 christos
721 1.1 christos loop_continue:
722 1.1 christos if( e ) {
723 1.1 christos wt_entry_return( e );
724 1.1 christos e = NULL;
725 1.1 christos }
726 1.1 christos }
727 1.1 christos
728 1.1 christos nochange:
729 1.1 christos rs->sr_ctrls = NULL;
730 1.1 christos rs->sr_ref = rs->sr_v2ref;
731 1.1 christos rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
732 1.1 christos rs->sr_rspoid = NULL;
733 1.1 christos if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
734 1.3 christos send_paged_response( op, rs, NULL, 0 );
735 1.1 christos } else {
736 1.1 christos send_ldap_result( op, rs );
737 1.1 christos }
738 1.1 christos
739 1.1 christos rs->sr_err = LDAP_SUCCESS;
740 1.1 christos
741 1.1 christos done:
742 1.1 christos
743 1.1 christos if( base ) {
744 1.1 christos wt_entry_return( base );
745 1.1 christos }
746 1.1 christos
747 1.1 christos if( e ) {
748 1.1 christos wt_entry_return( e );
749 1.1 christos }
750 1.1 christos
751 1.3 christos if( ae ) {
752 1.3 christos wt_entry_return( ae );
753 1.3 christos }
754 1.3 christos
755 1.1 christos return rs->sr_err;
756 1.1 christos }
757 1.1 christos
758 1.1 christos /*
759 1.1 christos * Local variables:
760 1.1 christos * indent-tabs-mode: t
761 1.1 christos * tab-width: 4
762 1.1 christos * c-basic-offset: 4
763 1.1 christos * End:
764 1.1 christos */
765