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