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