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