]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb2/search.c
Add OpenLDAP RCSid to *.[ch] in clients, libraries, and servers.
[openldap] / servers / slapd / back-bdb2 / search.c
1 /* search.c - bdb2 backend search function */
2 /* $OpenLDAP$ */
3
4 #include "portable.h"
5
6 #include <stdio.h>
7
8 #include <ac/string.h>
9 #include <ac/socket.h>
10 #include <ac/time.h>
11
12 #include "slap.h"
13 #include "back-bdb2.h"
14 #include "proto-back-bdb2.h"
15
16 static ID_BLOCK *base_candidate(
17         Backend *be, Entry *e );
18
19 static ID_BLOCK *search_candidates(
20         Backend *be, Entry *e, Filter *filter,
21         int scope, int deref, int manageDSAit );
22
23 static int
24 bdb2i_back_search_internal(
25     BackendDB   *be,
26     Connection  *conn,
27     Operation   *op,
28     char        *base,
29     int         scope,
30     int         deref,
31     int         slimit,
32     int         tlimit,
33     Filter      *filter,
34     char        *filterstr,
35     char        **attrs,
36     int         attrsonly
37 )
38 {
39         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
40         int             rc, err;
41         char *text;
42         time_t          stoptime;
43         ID_BLOCK                *candidates;
44         ID              id, cursor;
45         Entry           *e;
46         struct berval **v2refs = NULL;
47         Entry   *matched = NULL;
48         char    *realbase = NULL;
49         int             nentries = 0;
50         int             manageDSAit = get_manageDSAit( op );
51
52         Debug(LDAP_DEBUG_TRACE, "=> bdb2_back_search\n", 0, 0, 0);
53
54         /* get entry with reader lock */
55         if ( deref & LDAP_DEREF_FINDING ) {
56                 e = deref_dn_r( be, base, &err, &matched, &text );
57
58         } else {
59                 e = bdb2i_dn2entry_r( be, base, &matched );
60                 err = e != NULL ? LDAP_SUCCESS : LDAP_REFERRAL;
61                 text = NULL;
62         }
63
64         if ( e == NULL ) {
65                 char *matched_dn = NULL;
66                 struct berval **refs = NULL;
67
68                 if ( matched != NULL ) {
69                         matched_dn = ch_strdup( matched->e_dn );
70
71                         refs = is_entry_referral( matched )
72                                 ? get_entry_referrals( be, conn, op, matched )
73                                 : NULL;
74
75                         bdb2i_cache_return_entry_r( &li->li_cache, matched );
76                 } else {
77                         refs = default_referral;
78                 }
79
80                 send_ldap_result( conn, op, err,
81                         matched_dn, text, refs, NULL );
82
83                 if( matched != NULL ) {
84                         ber_bvecfree( refs );
85                         free( matched_dn );
86                 }
87
88                 return 1;
89         }
90
91         if (!manageDSAit && is_entry_referral( e ) ) {
92                 /* entry is a referral, don't allow add */
93                 char *matched_dn = ch_strdup( e->e_dn );
94                 struct berval **refs = get_entry_referrals( be,
95                         conn, op, e );
96
97                 bdb2i_cache_return_entry_r( &li->li_cache, e );
98
99                 Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
100                     0, 0 );
101
102                 send_ldap_result( conn, op, LDAP_REFERRAL,
103                     matched_dn, NULL, refs, NULL );
104
105                 ber_bvecfree( refs );
106                 free( matched_dn );
107
108                 return 1;
109         }
110
111         if ( tlimit == 0 && be_isroot( be, op->o_ndn ) ) {
112                 tlimit = -1;    /* allow root to set no limit */
113         } else {
114                 tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
115                     be->be_timelimit : tlimit;
116                 stoptime = op->o_time + tlimit;
117         }
118
119         if ( slimit == 0 && be_isroot( be, op->o_ndn ) ) {
120                 slimit = -1;    /* allow root to set no limit */
121         } else {
122                 slimit = (slimit > be->be_sizelimit || slimit < 1) ?
123                     be->be_sizelimit : slimit;
124         }
125
126         if ( scope == LDAP_SCOPE_BASE) {
127                 candidates = base_candidate( be, e );
128
129         } else {
130                 candidates = search_candidates( be, e, filter,
131                     scope, deref, manageDSAit );
132         }
133
134         /* need normalized dn below */
135         realbase = ch_strdup( e->e_ndn );
136         bdb2i_cache_return_entry_r( &li->li_cache, e );
137
138         if ( candidates == NULL ) {
139                 /* no candidates */
140                 Debug( LDAP_DEBUG_TRACE, "no candidates\n", 0,
141                     0, 0 );
142
143                 send_search_result( conn, op,
144                         LDAP_SUCCESS,
145                         NULL, NULL, NULL, NULL, 0 );
146
147                 rc = 1;
148                 goto done;
149         }
150
151         for ( id = bdb2i_idl_firstid( candidates, &cursor ); id != NOID;
152             id = bdb2i_idl_nextid( candidates, &cursor ) )
153         {
154                 int             scopeok = 0;
155
156                 /* check for abandon */
157                 ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
158
159                 if ( op->o_abandon ) {
160                         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
161                         rc = 0;
162                         goto done;
163                 }
164
165                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
166
167                 /* check time limit */
168                 if ( tlimit != -1 && slap_get_time() > stoptime ) {
169                         send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
170                                 NULL, NULL, v2refs, NULL, nentries );
171                         rc = 0;
172                         goto done;
173                 }
174
175                 /* get the entry with reader lock */
176                 e = bdb2i_id2entry_r( be, id );
177
178                 if ( e == NULL ) {
179                         Debug( LDAP_DEBUG_ARGS, "search: candidate %ld not found\n",
180                                 id, 0, 0 );
181
182                         goto loop_continue;
183                 }
184
185                 if ( deref & LDAP_DEREF_SEARCHING && is_entry_alias( e ) ) {
186                         Entry *matched;
187                         int err;
188                         char *text;
189                         
190                         e = deref_entry_r( be, e, &err, &matched, &text );
191
192                         if( e == NULL ) {
193                                 e = matched;
194                                 goto loop_continue;
195                         }
196
197                         if( e->e_id == id ) {
198                                 /* circular loop */
199                                 goto loop_continue;
200                         }
201
202                         /* need to skip alias which deref into scope */
203                         if( scope & LDAP_SCOPE_ONELEVEL ) {
204                                 char *pdn = dn_parent( NULL, e->e_ndn );
205                                 if ( pdn != NULL ) {
206                                         if( strcmp( pdn, realbase ) ) {
207                                                 free( pdn );
208                                                 goto loop_continue;
209                                         }
210                                         free(pdn);
211                                 }
212
213                         } else if ( dn_issuffix( e->e_ndn, realbase ) ) {
214                                 /* alias is within scope */
215                                 Debug( LDAP_DEBUG_ARGS, "search: \"%s\" in subtree\n",
216                                         e->e_dn, 0, 0 );
217                                 goto loop_continue;
218                         }
219
220                         scopeok = 1;
221                 }
222
223                 /*
224                  * if it's a referral, add it to the list of referrals. only do
225                  * this for non-base searches, and don't check the filter
226                  * explicitly here since it's only a candidate anyway.
227                  */
228                 if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
229                         is_entry_referral( e ) )
230                 {
231                         struct berval **refs = get_entry_referrals(
232                                 be, conn, op, e );
233
234                         send_search_reference( be, conn, op,
235                                 e, refs, scope, NULL, &v2refs );
236
237                         ber_bvecfree( refs );
238
239                         goto loop_continue;
240                 }
241
242                 /* if it matches the filter and scope, send it */
243                 if ( test_filter( be, conn, op, e, filter ) == 0 ) {
244                         char    *dn;
245
246                         /* check scope */
247                         if ( !scopeok && scope == LDAP_SCOPE_ONELEVEL ) {
248                                 if ( (dn = dn_parent( be, e->e_ndn )) != NULL ) {
249                                         (void) dn_normalize_case( dn );
250                                         scopeok = (dn == realbase)
251                                                 ? 1
252                                                 : (strcmp( dn, realbase ) ? 0 : 1 );
253                                         free( dn );
254
255                                 } else {
256                                         scopeok = (realbase == NULL || *realbase == '\0');
257                                 }
258
259                         } else if ( !scopeok && scope == LDAP_SCOPE_SUBTREE ) {
260                                 dn = ch_strdup( e->e_ndn );
261                                 scopeok = dn_issuffix( dn, realbase );
262                                 free( dn );
263
264                         } else {
265                                 scopeok = 1;
266                         }
267
268                         if ( scopeok ) {
269                                 /* check size limit */
270                                 if ( --slimit == -1 ) {
271                                         bdb2i_cache_return_entry_r( &li->li_cache, e );
272                                         send_search_result( conn, op,
273                                                 LDAP_SIZELIMIT_EXCEEDED, NULL, NULL,
274                                                 v2refs, NULL, nentries );
275                                         rc = 0;
276                                         goto done;
277                                 }
278
279                                 if (e) {
280                                         switch ( send_search_entry( be, conn, op, e,
281                                                 attrs, attrsonly, NULL ) ) {
282                                         case 0:         /* entry sent ok */
283                                                 nentries++;
284                                                 break;
285                                         case 1:         /* entry not sent */
286                                                 break;
287                                         case -1:        /* connection closed */
288                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
289                                                 rc = 0;
290                                                 goto done;
291                                         }
292                                 }
293                         } else {
294                                 Debug( LDAP_DEBUG_TRACE, "candidate %ld scope not okay\n",
295                                         id, 0, 0 );
296                         }
297                 } else {
298                         Debug( LDAP_DEBUG_TRACE, "candidate %ld does match filter\n",
299                                 id, 0, 0 );
300                 }
301
302 loop_continue:
303                 if( e != NULL ) {
304                         /* free reader lock */
305                         bdb2i_cache_return_entry_r( &li->li_cache, e );
306                 }
307
308                 ldap_pvt_thread_yield();
309         }
310         send_search_result( conn, op,
311                 v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
312                 NULL, NULL, v2refs, NULL, nentries );
313
314         rc = 0;
315
316 done:
317         bdb2i_idl_free( candidates );
318
319         ber_bvecfree( v2refs );
320         if( realbase ) free( realbase );
321
322         return rc;
323 }
324
325
326 int
327 bdb2_back_search(
328     BackendDB   *be,
329     Connection  *conn,
330     Operation   *op,
331     char        *base,
332     int         scope,
333     int         deref,
334     int         slimit,
335     int         tlimit,
336     Filter      *filter,
337     char        *filterstr,
338     char        **attrs,
339     int         attrsonly
340 )
341 {
342         DB_LOCK         lock;
343         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
344         struct timeval  time1;
345         int             ret;
346
347         bdb2i_start_timing( be->bd_info, &time1 );
348
349         if ( bdb2i_enter_backend_r( &lock ) != 0 ) {
350                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
351                         NULL, NULL, NULL, NULL );
352                 return( -1 );
353
354         }
355
356         ret = bdb2i_back_search_internal( be, conn, op, base, scope, deref,
357                                         slimit, tlimit, filter, filterstr, attrs, attrsonly );
358
359         (void) bdb2i_leave_backend_r( lock );
360         bdb2i_stop_timing( be->bd_info, time1, "SRCH", conn, op );
361
362         return( ret );
363 }
364
365
366 static ID_BLOCK *
367 base_candidate(
368     Backend     *be,
369         Entry   *e
370 )
371 {
372         ID_BLOCK                *idl;
373
374         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n",
375                 e->e_dn, 0, 0);
376
377         idl = bdb2i_idl_alloc( 1 );
378         bdb2i_idl_insert( &idl, e->e_id, 1 );
379
380         return( idl );
381 }
382
383 static ID_BLOCK *
384 search_candidates(
385     Backend     *be,
386     Entry       *e,
387     Filter      *filter,
388     int         scope,
389         int             deref,
390         int             manageDSAit
391 )
392 {
393         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
394         ID_BLOCK                *candidates;
395         Filter          *f, *rf, *af, *lf;
396
397         Debug(LDAP_DEBUG_TRACE, "search_candidates: base=\"%s\" s=%d d=%d\n",
398                 e->e_ndn, scope, deref );
399
400         f = NULL;
401
402         if( !manageDSAit ) {
403                 /* match referrals */
404                 rf = (Filter *) ch_malloc( sizeof(Filter) );
405                 rf->f_next = NULL;
406                 rf->f_choice = LDAP_FILTER_OR;
407                 rf->f_or = (Filter *) ch_malloc( sizeof(Filter) );
408                 rf->f_or->f_choice = LDAP_FILTER_EQUALITY;
409                 rf->f_or->f_avtype = ch_strdup( "objectclass" );
410                 rf->f_or->f_avvalue.bv_val = ch_strdup( "REFERRAL" );
411                 rf->f_or->f_avvalue.bv_len = sizeof("REFERRAL")-1;
412                 rf->f_or->f_next = filter;
413                 f = rf;
414         } else {
415                 rf = NULL;
416                 f = filter;
417         }
418
419         if( deref & LDAP_DEREF_SEARCHING ) {
420                 /* match aliases */
421                 af = (Filter *) ch_malloc( sizeof(Filter) );
422                 af->f_next = NULL;
423                 af->f_choice = LDAP_FILTER_OR;
424                 af->f_or = (Filter *) ch_malloc( sizeof(Filter) );
425                 af->f_or->f_choice = LDAP_FILTER_EQUALITY;
426                 af->f_or->f_avtype = ch_strdup( "objectclass" );
427                 af->f_or->f_avvalue.bv_val = ch_strdup( "ALIAS" );
428                 af->f_or->f_avvalue.bv_len = sizeof("ALIAS")-1;
429                 af->f_or->f_next = f;
430                 f = af;
431         } else {
432                 af = NULL;
433         }
434
435         if ( scope == LDAP_SCOPE_SUBTREE ) {
436                 lf = (Filter *) ch_malloc( sizeof(Filter) );
437                 lf->f_next = NULL;
438                 lf->f_choice = LDAP_FILTER_AND;
439                 lf->f_and = (Filter *) ch_malloc( sizeof(Filter) );
440
441                 lf->f_and->f_choice = SLAPD_FILTER_DN_SUBTREE;
442                 lf->f_and->f_dn = e->e_ndn;
443
444                 lf->f_and->f_next = f;
445                 f = lf;
446
447         } else if ( scope == LDAP_SCOPE_ONELEVEL ) {
448                 lf = (Filter *) ch_malloc( sizeof(Filter) );
449                 lf->f_next = NULL;
450                 lf->f_choice = LDAP_FILTER_AND;
451                 lf->f_and = (Filter *) ch_malloc( sizeof(Filter) );
452
453                 lf->f_and->f_choice = SLAPD_FILTER_DN_ONE;
454                 lf->f_and->f_dn = e->e_ndn;
455
456                 lf->f_and->f_next = f;
457                 f = lf;
458
459         } else {
460                 lf = NULL;
461         }
462
463         candidates = bdb2i_filter_candidates( be, f );
464
465         /* free up filter additions we allocated above */
466         if( lf != NULL ) {
467                 free( lf->f_and );
468                 free( lf );
469         }
470
471         if( af != NULL ) {
472                 af->f_or->f_next = NULL;
473                 filter_free( af );
474         }
475
476         if( rf != NULL ) {
477                 rf->f_or->f_next = NULL;
478                 filter_free( rf );
479         }
480
481         return( candidates );
482 }