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