]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/search.c
19dafe4c017680d060d5da3af5f0a0d04d1c0c77
[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 #include "proto-back-ldbm.h"
10
11 extern time_t           currenttime;
12 extern pthread_mutex_t  currenttime_mutex;
13
14 extern IDList           *idl_alloc();
15 extern Attribute        *attr_find();
16 extern IDList           *filter_candidates();
17 extern char             *ch_realloc();
18 extern char             *dn_parent();
19
20 static IDList   *base_candidates();
21 static IDList   *onelevel_candidates();
22 static IDList   *subtree_candidates();
23
24 #define GRABSIZE        BUFSIZ
25
26 #define MAKE_SPACE( n ) { \
27         if ( rcur + n > rbuf + rmaxsize ) { \
28                 int     offset = rcur - rbuf; \
29                 rbuf =  ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
30                 rmaxsize += GRABSIZE; \
31                 rcur = rbuf + offset; \
32         } \
33 }
34
35 int
36 ldbm_back_search(
37     Backend     *be,
38     Connection  *conn,
39     Operation   *op,
40     char        *base,
41     int         scope,
42     int         deref,
43     int         slimit,
44     int         tlimit,
45     Filter      *filter,
46     char        *filterstr,
47     char        **attrs,
48     int         attrsonly
49 )
50 {
51         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
52         int             err;
53         time_t          stoptime;
54         IDList          *candidates;
55         ID              id;
56         Entry           *e;
57         Attribute       *ref;
58         char            *matched = NULL;
59         int             rmaxsize, nrefs;
60         char            *rbuf, *rcur, *r;
61         int             nentries = 0;
62
63         Debug(LDAP_DEBUG_ARGS, "=> ldbm_back_search\n", 0, 0, 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 with reader lock */
143                 if ( (e = id2entry_r( be, id )) == NULL ) {
144                         Debug( LDAP_DEBUG_ARGS, "candidate %d not found\n", id, 0, 0 );
145                         continue;
146                 }
147
148                 /*
149                  * if it's a referral, add it to the list of referrals. only do
150                  * this for subtree searches, and don't check the filter explicitly
151                  * here since it's only a candidate anyway.
152                  */
153                 if ( e->e_dn != NULL && strncasecmp( e->e_dn, "ref=", 4 )
154                         == 0 && (ref = attr_find( e->e_attrs, "ref" )) != NULL &&
155                         scope == LDAP_SCOPE_SUBTREE )
156                 {
157                         int     i, len;
158
159                         if ( ref->a_vals == NULL ) {
160                                 Debug( LDAP_DEBUG_ANY, "null ref in (%s)\n", 0,
161                                         0, 0 );
162                         } else {
163                                 for ( i = 0; ref->a_vals[i] != NULL; i++ ) {
164                                         /* referral + newline + null */
165                                         MAKE_SPACE( ref->a_vals[i]->bv_len + 2 );
166                                         *rcur++ = '\n';
167                                         strncpy( rcur, ref->a_vals[i]->bv_val,
168                                           ref->a_vals[i]->bv_len );
169                                         rcur = rcur + ref->a_vals[i]->bv_len;
170                                         *rcur = '\0';
171                                         nrefs++;
172                                 }
173                         }
174
175                 /* otherwise it's an entry - see if it matches the filter */
176                 } else {
177                         /* if it matches the filter and scope, send it */
178                         if ( test_filter( be, conn, op, e, filter ) == 0 ) {
179                                 int             scopeok;
180                                 char    *dn;
181
182                                 /* check scope */
183                                 scopeok = 1;
184                                 if ( scope == LDAP_SCOPE_ONELEVEL ) {
185                                         if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
186                                                 (void) dn_normalize( dn );
187                                                 scopeok = (dn == base) ? 1 : (! strcasecmp( dn, base ));
188                                         } else {
189                                                 scopeok = (base == NULL || *base == '\0');
190                                         }
191                                         free( dn );
192                                 } else if ( scope == LDAP_SCOPE_SUBTREE ) {
193                                         dn = strdup( e->e_dn );
194                                         (void) dn_normalize( dn );
195                                         scopeok = dn_issuffix( dn, base );
196                                         free( dn );
197                                 }
198
199                                 if ( scopeok ) {
200                                         /* check size limit */
201                                         if ( --slimit == -1 ) {
202                                                 cache_return_entry_r( &li->li_cache, e );
203                                                 send_ldap_search_result( conn, op,
204                                                         LDAP_SIZELIMIT_EXCEEDED, NULL,
205                                                         nrefs > 0 ? rbuf : NULL, nentries );
206                                                 idl_free( candidates );
207                                                 free( rbuf );
208                                                 return( 0 );
209                                         }
210
211                                         switch ( send_search_entry( be, conn, op, e,
212                                                 attrs, attrsonly ) ) {
213                                         case 0:         /* entry sent ok */
214                                                 nentries++;
215                                                 break;
216                                         case 1:         /* entry not sent */
217                                                 break;
218                                         case -1:        /* connection closed */
219                                                 cache_return_entry_r( &li->li_cache, e );
220                                                 idl_free( candidates );
221                                                 free( rbuf );
222                                                 return( 0 );
223                                         }
224                                 }
225                         }
226                 }
227
228                 if( e == NULL ) {
229                         /* free reader lock */
230                         cache_return_entry_r( &li->li_cache, e );
231                 }
232
233                 pthread_yield();
234         }
235         idl_free( candidates );
236         if ( nrefs > 0 ) {
237                 send_ldap_search_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
238                     rbuf, nentries );
239         } else {
240                 send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL,
241                     nentries );
242         }
243         free( rbuf );
244
245         return( 0 );
246 }
247
248 static IDList *
249 base_candidates(
250     Backend     *be,
251     Connection  *conn,
252     Operation   *op,
253     char        *base,
254     Filter      *filter,
255     char        **attrs,
256     int         attrsonly,
257     char        **matched,
258     int         *err
259 )
260 {
261         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
262         int             rc;
263         ID              id;
264         IDList          *idl;
265         Entry           *e;
266
267         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: %s\n", base, 0, 0);
268
269         *err = LDAP_SUCCESS;
270
271         /* get entry with reader lock */
272         if ( (e = dn2entry_r( be, base, matched )) == NULL ) {
273                 *err = LDAP_NO_SUCH_OBJECT;
274                 return( NULL );
275         }
276
277         /* check for deleted */
278
279         idl = idl_alloc( 1 );
280         idl_insert( &idl, e->e_id, 1 );
281
282
283         /* free reader lock */
284         cache_return_entry_r( &li->li_cache, e );
285
286         return( idl );
287 }
288
289 static IDList *
290 onelevel_candidates(
291     Backend     *be,
292     Connection  *conn,
293     Operation   *op,
294     char        *base,
295     Filter      *filter,
296     char        **attrs,
297     int         attrsonly,
298     char        **matched,
299     int         *err
300 )
301 {
302         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
303         Entry           *e;
304         Filter          *f;
305         char            buf[20];
306         IDList          *candidates;
307
308         Debug(LDAP_DEBUG_TRACE, "onelevel_candidates: base: %s\n", base, 0, 0);
309
310         *err = LDAP_SUCCESS;
311         e = NULL;
312         /* get the base object with reader lock */
313         if ( base != NULL && *base != '\0' &&
314                 (e = dn2entry_r( be, base, matched )) == NULL )
315         {
316                 *err = LDAP_NO_SUCH_OBJECT;
317                 return( NULL );
318         }
319
320         /*
321          * modify the filter to be something like this:
322          *
323          *      parent=baseobject & originalfilter
324          */
325
326         f = (Filter *) ch_malloc( sizeof(Filter) );
327         f->f_next = NULL;
328         f->f_choice = LDAP_FILTER_AND;
329         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
330         f->f_and->f_choice = LDAP_FILTER_EQUALITY;
331         f->f_and->f_ava.ava_type = strdup( "id2children" );
332         sprintf( buf, "%d", e != NULL ? e->e_id : 0 );
333         f->f_and->f_ava.ava_value.bv_val = strdup( buf );
334         f->f_and->f_ava.ava_value.bv_len = strlen( buf );
335         f->f_and->f_next = filter;
336
337         /* from here, it's just like subtree_candidates */
338         candidates = subtree_candidates( be, conn, op, base, f, attrs,
339             attrsonly, matched, e, err, 0 );
340
341         /* free up just the filter stuff we allocated above */
342         f->f_and->f_next = NULL;
343         filter_free( f );
344
345         /* free entry and reader lock */
346         cache_return_entry_r( &li->li_cache, e );
347         return( candidates );
348 }
349
350 static IDList *
351 subtree_candidates(
352     Backend     *be,
353     Connection  *conn,
354     Operation   *op,
355     char        *base,
356     Filter      *filter,
357     char        **attrs,
358     int         attrsonly,
359     char        **matched,
360     Entry       *e,
361     int         *err,
362     int         lookupbase
363 )
364 {
365         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
366         Filter          *f;
367         IDList          *candidates;
368
369         Debug(LDAP_DEBUG_TRACE, "subtree_candidates: base: %s\n",
370                 base ? base : "NULL", 0, 0);
371
372         /*
373          * get the base object - unless we already have it (from one-level).
374          * also, unless this is a one-level search or a subtree search
375          * starting at the very top of our subtree, we need to modify the
376          * filter to be something like this:
377          *
378          *      dn=*baseobjectdn & (originalfilter | ref=*)
379          *
380          * the "objectclass=referral" part is used to select referrals to return
381          */
382
383         *err = LDAP_SUCCESS;
384         f = NULL;
385         if ( lookupbase ) {
386                 if ( base != NULL && *base != '\0' &&
387                         (e = dn2entry_r( be, base, matched )) == NULL )
388                 {
389                         *err = LDAP_NO_SUCH_OBJECT;
390                         return( NULL );
391                 }
392
393                 if (e) {
394                         cache_return_entry_r( &li->li_cache, e );
395                 }
396
397                 f = (Filter *) ch_malloc( sizeof(Filter) );
398                 f->f_next = NULL;
399                 f->f_choice = LDAP_FILTER_OR;
400                 f->f_or = (Filter *) ch_malloc( sizeof(Filter) );
401                 f->f_or->f_choice = LDAP_FILTER_EQUALITY;
402                 f->f_or->f_avtype = strdup( "objectclass" );
403                 /* Patch to use normalized uppercase */
404                 f->f_or->f_avvalue.bv_val = strdup( "REFERRAL" );
405                 f->f_or->f_avvalue.bv_len = strlen( "REFERRAL" );
406                 f->f_or->f_next = filter;
407                 filter = f;
408
409                 if ( ! be_issuffix( be, base ) ) {
410                         f = (Filter *) ch_malloc( sizeof(Filter) );
411                         f->f_next = NULL;
412                         f->f_choice = LDAP_FILTER_AND;
413                         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
414                         f->f_and->f_choice = LDAP_FILTER_SUBSTRINGS;
415                         f->f_and->f_sub_type = strdup( "dn" );
416                         f->f_and->f_sub_initial = NULL;
417                         f->f_and->f_sub_any = NULL;
418                         f->f_and->f_sub_final = strdup( base );
419                         value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
420                         f->f_and->f_next = filter;
421                         filter = f;
422                 }
423         }
424
425         candidates = filter_candidates( be, filter );
426
427         /* free up just the parts we allocated above */
428         if ( f != NULL ) {
429                 f->f_and->f_next = NULL;
430                 filter_free( f );
431         }
432
433         return( candidates );
434 }