]> git.sur5r.net Git - openldap/blob - servers/slapd/filterentry.c
dd6da92f64142f76849be1a3e248402e353e6352
[openldap] / servers / slapd / filterentry.c
1 /* filterentry.c - apply a filter to an entry */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 #include "portable.h"
8
9 #include <stdio.h>
10
11 #include <ac/socket.h>
12 #include <ac/string.h>
13 #include <ac/regex.h>
14
15 #include "slap.h"
16
17 static int      test_filter_list(Backend *be, Connection *conn, Operation *op, Entry *e, Filter *flist, int ftype);
18 static int      test_substring_filter(Backend *be, Connection *conn, Operation *op, Entry *e, Filter *f);
19 static int      test_ava_filter(Backend *be, Connection *conn, Operation *op, Entry *e, Ava *ava, int type);
20 static int      test_approx_filter(Backend *be, Connection *conn, Operation *op, Entry *e, Ava *ava);
21 static int      test_presence_filter(Backend *be, Connection *conn, Operation *op, Entry *e, char *type);
22
23 /*
24  * test_filter - test a filter against a single entry.
25  * returns      0       filter matched
26  *              -1      filter did not match
27  *              >0      an ldap error code
28  */
29
30 int
31 test_filter(
32     Backend     *be,
33     Connection  *conn,
34     Operation   *op,
35     Entry       *e,
36     Filter      *f
37 )
38 {
39         int     rc;
40
41         Debug( LDAP_DEBUG_FILTER, "=> test_filter\n", 0, 0, 0 );
42
43         switch ( f->f_choice ) {
44         case LDAP_FILTER_EQUALITY:
45                 Debug( LDAP_DEBUG_FILTER, "    EQUALITY\n", 0, 0, 0 );
46                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
47                     LDAP_FILTER_EQUALITY );
48                 break;
49
50         case LDAP_FILTER_SUBSTRINGS:
51                 Debug( LDAP_DEBUG_FILTER, "    SUBSTRINGS\n", 0, 0, 0 );
52                 rc = test_substring_filter( be, conn, op, e, f );
53                 break;
54
55         case LDAP_FILTER_GE:
56                 Debug( LDAP_DEBUG_FILTER, "    GE\n", 0, 0, 0 );
57                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
58                     LDAP_FILTER_GE );
59                 break;
60
61         case LDAP_FILTER_LE:
62                 Debug( LDAP_DEBUG_FILTER, "    LE\n", 0, 0, 0 );
63                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
64                     LDAP_FILTER_LE );
65                 break;
66
67         case LDAP_FILTER_PRESENT:
68                 Debug( LDAP_DEBUG_FILTER, "    PRESENT\n", 0, 0, 0 );
69                 rc = test_presence_filter( be, conn, op, e, f->f_type );
70                 break;
71
72         case LDAP_FILTER_APPROX:
73                 Debug( LDAP_DEBUG_FILTER, "    APPROX\n", 0, 0, 0 );
74                 rc = test_approx_filter( be, conn, op, e, &f->f_ava );
75                 break;
76
77         case LDAP_FILTER_AND:
78                 Debug( LDAP_DEBUG_FILTER, "    AND\n", 0, 0, 0 );
79                 rc = test_filter_list( be, conn, op, e, f->f_and,
80                     LDAP_FILTER_AND );
81                 break;
82
83         case LDAP_FILTER_OR:
84                 Debug( LDAP_DEBUG_FILTER, "    OR\n", 0, 0, 0 );
85                 rc = test_filter_list( be, conn, op, e, f->f_or,
86                     LDAP_FILTER_OR );
87                 break;
88
89         case LDAP_FILTER_NOT:
90                 Debug( LDAP_DEBUG_FILTER, "    NOT\n", 0, 0, 0 );
91                 rc = (! test_filter( be, conn, op, e, f->f_not ) );
92                 break;
93
94         default:
95                 Debug( LDAP_DEBUG_ANY, "    unknown filter type %lu\n",
96                     f->f_choice, 0, 0 );
97                 rc = -1;
98         }
99
100         Debug( LDAP_DEBUG_FILTER, "<= test_filter %d\n", rc, 0, 0 );
101         return( rc );
102 }
103
104 static int
105 test_ava_filter(
106     Backend     *be,
107     Connection  *conn,
108     Operation   *op,
109     Entry       *e,
110     Ava         *ava,
111     int         type
112 )
113 {
114         int             i, rc;
115         Attribute       *a;
116
117         if ( be != NULL && ! access_allowed( be, conn, op, e,
118                 ava->ava_type, &ava->ava_value, ACL_SEARCH ) )
119         {
120                 return( -2 );
121         }
122
123         if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
124                 return( -1 );
125         }
126
127         if ( a->a_syntax == 0 ) {
128                 a->a_syntax = attr_syntax( ava->ava_type );
129         }
130         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
131                 rc = value_cmp( a->a_vals[i], &ava->ava_value, a->a_syntax,
132                     3 );
133
134                 switch ( type ) {
135                 case LDAP_FILTER_EQUALITY:
136                         if ( rc == 0 ) {
137                                 return( 0 );
138                         }
139                         break;
140
141                 case LDAP_FILTER_GE:
142                         if ( rc >= 0 ) {
143                                 return( 0 );
144                         }
145                         break;
146
147                 case LDAP_FILTER_LE:
148                         if ( rc <= 0 ) {
149                                 return( 0 );
150                         }
151                         break;
152                 }
153         }
154
155         return( 1 );
156 }
157
158 static int
159 test_presence_filter(
160     Backend     *be,
161     Connection  *conn,
162     Operation   *op,
163     Entry       *e,
164     char        *type
165 )
166 {
167         if ( be != NULL && ! access_allowed( be, conn, op, e,
168                 type, NULL, ACL_SEARCH ) )
169         {
170                 return( -2 );
171         }
172
173         return( attr_find( e->e_attrs, type ) != NULL ? 0 : -1 );
174 }
175
176 static int
177 test_approx_filter(
178     Backend     *be,
179     Connection  *conn,
180     Operation   *op,
181     Entry       *e,
182     Ava         *ava
183 )
184 {
185         char            *w1, *w2, *c1, *c2;
186         int             i;
187         Attribute       *a;
188
189         if ( be != NULL && ! access_allowed( be, conn, op, e,
190                 ava->ava_type, NULL, ACL_SEARCH ) )
191         {
192                 return( -2 );
193         }
194
195         if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
196                 return( -1 );
197         }
198
199         /* for each value in the attribute */
200         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
201                 /*
202                  * try to match words in the filter value in order
203                  * in the attribute value.
204                  */
205
206                 w2 = a->a_vals[i]->bv_val;
207                 /* for each word in the filter value */
208                 for ( w1 = first_word( ava->ava_value.bv_val ); w1 != NULL;
209                     w1 = next_word( w1 ) ) {
210                         if ( (c1 = phonetic( w1 )) == NULL ) {
211                                 break;
212                         }
213
214                         /*
215                          * for each word in the attribute value from
216                          * where we left off...
217                          */
218                         for ( w2 = first_word( w2 ); w2 != NULL;
219                             w2 = next_word( w2 ) ) {
220                                 c2 = phonetic( w2 );
221                                 if ( strcmp( c1, c2 ) == 0 ) {
222                                         free( c2 );
223                                         break;
224                                 }
225                                 free( c2 );
226                         }
227                         free( c1 );
228
229                         /*
230                          * if we stopped because we ran out of words
231                          * before making a match, go on to the next
232                          * value.  otherwise try to keep matching
233                          * words in this value from where we left off.
234                          */
235                         if ( w2 == NULL ) {
236                                 break;
237                         } else {
238                                 w2 = next_word( w2 );
239                         }
240                 }
241                 /*
242                  * if we stopped because we ran out of words we
243                  * have a match.
244                  */
245                 if ( w1 == NULL ) {
246                         return( 0 );
247                 }
248         }
249
250         return( 1 );
251 }
252
253 static int
254 test_filter_list(
255     Backend     *be,
256     Connection  *conn,
257     Operation   *op,
258     Entry       *e,
259     Filter      *flist,
260     int         ftype
261 )
262 {
263         int     nomatch;
264         Filter  *f;
265
266         Debug( LDAP_DEBUG_FILTER, "=> test_filter_list\n", 0, 0, 0 );
267
268         nomatch = 1;
269         for ( f = flist; f != NULL; f = f->f_next ) {
270                 if ( test_filter( be, conn, op, e, f ) != 0 ) {
271                         if ( ftype == LDAP_FILTER_AND ) {
272                                 Debug( LDAP_DEBUG_FILTER,
273                                     "<= test_filter_list 1\n", 0, 0, 0 );
274                                 return( 1 );
275                         }
276                 } else {
277                         nomatch = 0;
278                 }
279         }
280
281         Debug( LDAP_DEBUG_FILTER, "<= test_filter_list %d\n", nomatch, 0, 0 );
282         return( nomatch );
283 }
284
285 static void
286 strcpy_special( char *d, char *s )
287 {
288         for ( ; *s; s++ ) {
289                 switch ( *s ) {
290                 case '.':
291                 case '\\':
292                 case '[':
293                 case ']':
294                 case '*':
295                 case '+':
296                 case '^':
297                 case '$':
298                         *d++ = '\\';
299                         /* FALL */
300                 default:
301                         *d++ = *s;
302                 }
303         }
304         *d = '\0';
305 }
306
307 static int
308 test_substring_filter(
309     Backend     *be,
310     Connection  *conn,
311     Operation   *op,
312     Entry       *e,
313     Filter      *f
314 )
315 {
316         Attribute       *a;
317         int             i, rc;
318         char            *p, *end, *realval, *tmp;
319         char            pat[BUFSIZ];
320         char            buf[BUFSIZ];
321         struct berval   *val;
322         regex_t         re;
323
324         Debug( LDAP_DEBUG_FILTER, "begin test_substring_filter\n", 0, 0, 0 );
325
326         if ( be != NULL && ! access_allowed( be, conn, op, e,
327                 f->f_sub_type, NULL, ACL_SEARCH ) )
328         {
329                 return( -2 );
330         }
331
332         if ( (a = attr_find( e->e_attrs, f->f_sub_type )) == NULL ) {
333                 return( -1 );
334         }
335
336         if ( a->a_syntax & SYNTAX_BIN ) {
337                 Debug( LDAP_DEBUG_FILTER, "test_substring_filter bin attr\n",
338                     0, 0, 0 );
339                 return( -1 );
340         }
341
342         /*
343          * construct a regular expression corresponding to the
344          * filter and let regex do the work
345          */
346
347         pat[0] = '\0';
348         p = pat;
349         end = pat + sizeof(pat) - 2;    /* leave room for null */
350         if ( f->f_sub_initial != NULL ) {
351                 strcpy( p, "^" );
352                 p = strchr( p, '\0' );
353                 /* 2 * in case every char is special */
354                 if ( p + 2 * strlen( f->f_sub_initial ) > end ) {
355                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
356                             0, 0, 0 );
357                         return( -1 );
358                 }
359                 strcpy_special( p, f->f_sub_initial );
360                 p = strchr( p, '\0' );
361         }
362         if ( f->f_sub_any != NULL ) {
363                 for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
364                         /* ".*" + value */
365                         if ( p + 2 * strlen( f->f_sub_any[i] ) + 2 > end ) {
366                                 Debug( LDAP_DEBUG_ANY,
367                                     "not enough pattern space\n", 0, 0, 0 );
368                                 return( -1 );
369                         }
370                         strcpy( p, ".*" );
371                         p = strchr( p, '\0' );
372                         strcpy_special( p, f->f_sub_any[i] );
373                         p = strchr( p, '\0' );
374                 }
375         }
376         if ( f->f_sub_final != NULL ) {
377                 /* ".*" + value */
378                 if ( p + 2 * strlen( f->f_sub_final ) + 2 > end ) {
379                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
380                             0, 0, 0 );
381                         return( -1 );
382                 }
383                 strcpy( p, ".*" );
384                 p = strchr( p, '\0' );
385                 strcpy_special( p, f->f_sub_final );
386                 p = strchr( p, '\0' );
387                 strcpy( p, "$" );
388         }
389
390         /* compile the regex */
391         Debug( LDAP_DEBUG_FILTER, "test_substring_filter: regcomp pat: %s\n",
392                 pat, 0, 0 );
393         if ((rc = regcomp(&re, pat, 0))) {
394                 char error[512];
395
396                 regerror(rc, &re, error, sizeof(error));
397                 Debug( LDAP_DEBUG_ANY, "regcomp failed (%s) %s\n",
398                         p, error, 0 );
399                 return( -1 );
400         }
401
402         /* for each value in the attribute see if regex matches */
403         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
404                 val = a->a_vals[i];
405                 tmp = NULL;
406                 if ( val->bv_len < sizeof(buf) ) {
407                         strcpy( buf, val->bv_val );
408                         realval = buf;
409                 } else {
410                         tmp = (char *) ch_malloc( val->bv_len + 1 );
411                         strcpy( tmp, val->bv_val );
412                         realval = tmp;
413                 }
414                 value_normalize( realval, a->a_syntax );
415
416                 rc = !regexec(&re, realval, 0, NULL, 0);
417
418                 if ( tmp != NULL ) {
419                         free( tmp );
420                 }
421                 if ( rc == 1 ) {
422                         regfree(&re);
423                         return( 0 );
424                 }
425         }
426
427         regfree(&re);
428
429         Debug( LDAP_DEBUG_FILTER, "end test_substring_filter 1\n", 0, 0, 0 );
430         return( 1 );
431 }