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