]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/search.c
6a53be4f112485ab38b91886aafa8f2f438d941c
[openldap] / servers / slapd / back-ldbm / search.c
1 /* search.c - ldbm backend search function */
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include "slap.h"
8 #include "back-ldbm.h"
9
10 extern time_t           currenttime;
11 extern pthread_mutex_t  currenttime_mutex;
12
13 extern ID               dn2id();
14 extern IDList           *idl_alloc();
15 extern Entry            *id2entry();
16 extern Entry            *dn2entry();
17 extern Attribute        *attr_find();
18 extern IDList           *filter_candidates();
19 extern char             *ch_realloc();
20 extern char             *dn_parent();
21
22 static IDList   *base_candidates();
23 static IDList   *onelevel_candidates();
24 static IDList   *subtree_candidates();
25
26 #define GRABSIZE        BUFSIZ
27
28 #define MAKE_SPACE( n ) { \
29         if ( rcur + n > rbuf + rmaxsize ) { \
30                 int     offset = rcur - rbuf; \
31                 rbuf =  ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
32                 rmaxsize += GRABSIZE; \
33                 rcur = rbuf + offset; \
34         } \
35 }
36
37 int
38 ldbm_back_search(
39     Backend     *be,
40     Connection  *conn,
41     Operation   *op,
42     char        *base,
43     int         scope,
44     int         deref,
45     int         slimit,
46     int         tlimit,
47     Filter      *filter,
48     char        *filterstr,
49     char        **attrs,
50     int         attrsonly
51 )
52 {
53         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
54         int             err;
55         time_t          stoptime;
56         IDList          *candidates;
57         ID              id;
58         Entry           *e;
59         Attribute       *ref;
60         char            *matched = NULL;
61         int             rmaxsize, nrefs;
62         char            *rbuf, *rcur, *r;
63         int             nentries = 0;
64
65         if ( tlimit == 0 && be_isroot( be, op->o_dn ) ) {
66                 tlimit = -1;    /* allow root to set no limit */
67         } else {
68                 tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
69                     be->be_timelimit : tlimit;
70                 stoptime = op->o_time + tlimit;
71         }
72         if ( slimit == 0 && be_isroot( be, op->o_dn ) ) {
73                 slimit = -1;    /* allow root to set no limit */
74         } else {
75                 slimit = (slimit > be->be_sizelimit || slimit < 1) ?
76                     be->be_sizelimit : slimit;
77         }
78
79         switch ( scope ) {
80         case LDAP_SCOPE_BASE:
81                 candidates = base_candidates( be, conn, op, base, filter,
82                     attrs, attrsonly, &matched, &err );
83                 break;
84
85         case LDAP_SCOPE_ONELEVEL:
86                 candidates = onelevel_candidates( be, conn, op, base, filter,
87                     attrs, attrsonly, &matched, &err );
88                 break;
89
90         case LDAP_SCOPE_SUBTREE:
91                 candidates = subtree_candidates( be, conn, op, base, filter,
92                     attrs, attrsonly, &matched, NULL, &err, 1 );
93                 break;
94
95         default:
96                 send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, "",
97                     "Bad scope" );
98                 return( -1 );
99         }
100
101         /* null candidates means we could not find the base object */
102         if ( candidates == NULL ) {
103                 send_ldap_result( conn, op, err, matched, "" );
104                 if ( matched != NULL ) {
105                         free( matched );
106                 }
107                 return( -1 );
108         }
109
110         rmaxsize = 0;
111         nrefs = 0;
112         rbuf = rcur = NULL;
113         MAKE_SPACE( sizeof("Referral:") + 1 );
114         strcpy( rbuf, "Referral:" );
115         rcur = strchr( rbuf, '\0' );
116         for ( id = idl_firstid( candidates ); id != NOID;
117             id = idl_nextid( candidates, id ) ) {
118                 /* check for abandon */
119                 pthread_mutex_lock( &op->o_abandonmutex );
120                 if ( op->o_abandon ) {
121                         pthread_mutex_unlock( &op->o_abandonmutex );
122                         idl_free( candidates );
123                         free( rbuf );
124                         return( 0 );
125                 }
126                 pthread_mutex_unlock( &op->o_abandonmutex );
127
128                 /* check time limit */
129                 pthread_mutex_lock( &currenttime_mutex );
130                 time( &currenttime );
131                 if ( tlimit != -1 && currenttime > stoptime ) {
132                         pthread_mutex_unlock( &currenttime_mutex );
133                         send_ldap_search_result( conn, op,
134                             LDAP_TIMELIMIT_EXCEEDED, NULL, nrefs > 0 ? rbuf :
135                             NULL, nentries );
136                         idl_free( candidates );
137                         free( rbuf );
138                         return( 0 );
139                 }
140                 pthread_mutex_unlock( &currenttime_mutex );
141
142                 /* get the entry */
143                 if ( (e = id2entry( be, id )) == NULL ) {
144                         Debug( LDAP_DEBUG_ARGS, "candidate %d not found\n", id,
145                             0, 0 );
146                         continue;
147                 }
148
149                 /*
150                  * if it's a referral, add it to the list of referrals. only do
151                  * this for subtree searches, and don't check the filter explicitly
152                  * here since it's only a candidate anyway.
153                  */
154                 if ( e->e_dn != NULL && strncasecmp( e->e_dn, "ref=", 4 )
155                     == 0 && (ref = attr_find( e->e_attrs, "ref" )) != NULL &&
156                     scope == LDAP_SCOPE_SUBTREE )
157                 {
158                         int     i, len;
159
160                         if ( ref->a_vals == NULL ) {
161                                 Debug( LDAP_DEBUG_ANY, "null ref in (%s)\n", 0,
162                                     0, 0 );
163                         } else {
164                                 for ( i = 0; ref->a_vals[i] != NULL; i++ ) {
165                                         /* referral + newline + null */
166                                         MAKE_SPACE( ref->a_vals[i]->bv_len + 2 );
167                                         *rcur++ = '\n';
168                                         strncpy( rcur, ref->a_vals[i]->bv_val,
169                                           ref->a_vals[i]->bv_len );
170                                         rcur = rcur + ref->a_vals[i]->bv_len;
171                                         *rcur = '\0';
172                                         nrefs++;
173                                 }
174                         }
175
176                 /* otherwise it's an entry - see if it matches the filter */
177                 } else {
178                         /* if it matches the filter and scope, send it */
179                         if ( test_filter( be, conn, op, e, filter ) == 0 ) {
180                                 int             scopeok;
181                                 char    *dn;
182
183                                 /* check scope */
184                                 scopeok = 1;
185                                 if ( scope == LDAP_SCOPE_ONELEVEL ) {
186                                         if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
187                                                 (void) dn_normalize( dn );
188                                                 scopeok = (dn == base) ? 1 : (! strcasecmp( dn, base ));
189                                         } else {
190                                                 scopeok = (base == NULL || *base == '\0');
191                                         }
192                                         free( dn );
193                                 } else if ( scope == LDAP_SCOPE_SUBTREE ) {
194                                         dn = strdup( e->e_dn );
195                                         (void) dn_normalize( dn );
196                                         scopeok = dn_issuffix( dn, base );
197                                         free( dn );
198                                 }
199
200                                 if ( scopeok ) {
201                                         /* check size limit */
202                                         if ( --slimit == -1 ) {
203                                                 cache_return_entry( &li->li_cache, e );
204                                                 send_ldap_search_result( conn, op,
205                                                         LDAP_SIZELIMIT_EXCEEDED, NULL,
206                                                         nrefs > 0 ? rbuf : NULL, nentries );
207                                                 idl_free( candidates );
208                                                 free( rbuf );
209                                                 return( 0 );
210                                         }
211
212                                         switch ( send_search_entry( be, conn, op, e,
213                                                 attrs, attrsonly ) ) {
214                                         case 0:         /* entry sent ok */
215                                                 nentries++;
216                                                 break;
217                                         case 1:         /* entry not sent */
218                                                 break;
219                                         case -1:        /* connection closed */
220                                                 cache_return_entry( &li->li_cache, e );
221                                                 idl_free( candidates );
222                                                 free( rbuf );
223                                                 return( 0 );
224                                         }
225                                 }
226                         }
227                 }
228
229                 cache_return_entry( &li->li_cache, e );
230
231                 pthread_yield();
232         }
233         idl_free( candidates );
234         if ( nrefs > 0 ) {
235                 send_ldap_search_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
236                     rbuf, nentries );
237         } else {
238                 send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL,
239                     nentries );
240         }
241         free( rbuf );
242
243         return( 0 );
244 }
245
246 static IDList *
247 base_candidates(
248     Backend     *be,
249     Connection  *conn,
250     Operation   *op,
251     char        *base,
252     Filter      *filter,
253     char        **attrs,
254     int         attrsonly,
255     char        **matched,
256     int         *err
257 )
258 {
259         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
260         int             rc;
261         ID              id;
262         IDList          *idl;
263         Entry           *e;
264
265         *err = LDAP_SUCCESS;
266         if ( (e = dn2entry( be, base, matched )) == NULL ) {
267                 *err = LDAP_NO_SUCH_OBJECT;
268                 return( NULL );
269         }
270
271         idl = idl_alloc( 1 );
272         idl_insert( &idl, e->e_id, 1 );
273
274         cache_return_entry( &li->li_cache, e );
275
276         return( idl );
277 }
278
279 static IDList *
280 onelevel_candidates(
281     Backend     *be,
282     Connection  *conn,
283     Operation   *op,
284     char        *base,
285     Filter      *filter,
286     char        **attrs,
287     int         attrsonly,
288     char        **matched,
289     int         *err
290 )
291 {
292         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
293         Entry           *e;
294         Filter          *f;
295         char            buf[20];
296         IDList          *candidates;
297
298         *err = LDAP_SUCCESS;
299         e = NULL;
300         /* get the base object */
301         if ( base != NULL && *base != '\0' && (e = dn2entry( be, base,
302             matched )) == NULL ) {
303                 *err = LDAP_NO_SUCH_OBJECT;
304                 return( NULL );
305         }
306
307         /*
308          * modify the filter to be something like this:
309          *
310          *      parent=baseobject & originalfilter
311          */
312
313         f = (Filter *) ch_malloc( sizeof(Filter) );
314         f->f_next = NULL;
315         f->f_choice = LDAP_FILTER_AND;
316         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
317         f->f_and->f_choice = LDAP_FILTER_EQUALITY;
318         f->f_and->f_ava.ava_type = strdup( "id2children" );
319         sprintf( buf, "%d", e != NULL ? e->e_id : 0 );
320         f->f_and->f_ava.ava_value.bv_val = strdup( buf );
321         f->f_and->f_ava.ava_value.bv_len = strlen( buf );
322         f->f_and->f_next = filter;
323
324         /* from here, it's just like subtree_candidates */
325         candidates = subtree_candidates( be, conn, op, base, f, attrs,
326             attrsonly, matched, e, err, 0 );
327
328         /* free up just the filter stuff we allocated above */
329         f->f_and->f_next = NULL;
330         filter_free( f );
331
332         return( candidates );
333 }
334
335 static IDList *
336 subtree_candidates(
337     Backend     *be,
338     Connection  *conn,
339     Operation   *op,
340     char        *base,
341     Filter      *filter,
342     char        **attrs,
343     int         attrsonly,
344     char        **matched,
345     Entry       *e,
346     int         *err,
347     int         lookupbase
348 )
349 {
350         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
351         Filter          *f;
352         IDList          *candidates;
353
354         /*
355          * get the base object - unless we already have it (from one-level).
356          * also, unless this is a one-level search or a subtree search
357          * starting at the very top of our subtree, we need to modify the
358          * filter to be something like this:
359          *
360          *      dn=*baseobjectdn & (originalfilter | ref=*)
361          *
362          * the "objectclass=referral" part is used to select referrals to return
363          */
364
365         *err = LDAP_SUCCESS;
366         f = NULL;
367         if ( lookupbase ) {
368                 if ( base != NULL && *base != '\0' && (e = dn2entry( be, base,
369                     matched )) == NULL ) {
370                         *err = LDAP_NO_SUCH_OBJECT;
371                         return( NULL );
372                 }
373
374                 f = (Filter *) ch_malloc( sizeof(Filter) );
375                 f->f_next = NULL;
376                 f->f_choice = LDAP_FILTER_OR;
377                 f->f_or = (Filter *) ch_malloc( sizeof(Filter) );
378                 f->f_or->f_choice = LDAP_FILTER_EQUALITY;
379                 f->f_or->f_avtype = strdup( "objectclass" );
380                 f->f_or->f_avvalue.bv_val = strdup( "referral" );
381                 f->f_or->f_avvalue.bv_len = strlen( "referral" );
382                 f->f_or->f_next = filter;
383                 filter = f;
384
385                 if ( ! be_issuffix( be, base ) ) {
386                         f = (Filter *) ch_malloc( sizeof(Filter) );
387                         f->f_next = NULL;
388                         f->f_choice = LDAP_FILTER_AND;
389                         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
390                         f->f_and->f_choice = LDAP_FILTER_SUBSTRINGS;
391                         f->f_and->f_sub_type = strdup( "dn" );
392                         f->f_and->f_sub_initial = NULL;
393                         f->f_and->f_sub_any = NULL;
394                         f->f_and->f_sub_final = strdup( base );
395                         value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
396                         f->f_and->f_next = filter;
397                         filter = f;
398                 }
399         }
400
401         candidates = filter_candidates( be, filter );
402
403         /* free up just the parts we allocated above */
404         if ( f != NULL ) {
405                 f->f_and->f_next = NULL;
406                 filter_free( f );
407         }
408
409         if ( e != NULL ) {
410                 cache_return_entry( &li->li_cache, e );
411         }
412
413         return( candidates );
414 }