]> git.sur5r.net Git - openldap/blob - servers/slapd/filterentry.c
import localtime -> gmtime change from -devel
[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 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <regex.h>
10 #include "slap.h"
11
12 extern Attribute        *attr_find();
13 extern char             *first_word();
14 extern char             *next_word();
15 extern char             *phonetic();
16
17 static int      test_filter_list();
18 static int      test_substring_filter();
19 static int      test_ava_filter();
20 static int      test_approx_filter();
21 static int      test_presence_filter();
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 %d\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, ava->ava_type,
118             &ava->ava_value, op->o_dn, ACL_SEARCH ) ) {
119                 return( -2 );
120         }
121
122         if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
123                 return( -1 );
124         }
125
126         if ( a->a_syntax == 0 ) {
127                 a->a_syntax = attr_syntax( ava->ava_type );
128         }
129         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
130                 rc = value_cmp( a->a_vals[i], &ava->ava_value, a->a_syntax,
131                     3 );
132
133                 switch ( type ) {
134                 case LDAP_FILTER_EQUALITY:
135                         if ( rc == 0 ) {
136                                 return( 0 );
137                         }
138                         break;
139
140                 case LDAP_FILTER_GE:
141                         if ( rc > 0 ) {
142                                 return( 0 );
143                         }
144                         break;
145
146                 case LDAP_FILTER_LE:
147                         if ( rc < 0 ) {
148                                 return( 0 );
149                         }
150                         break;
151                 }
152         }
153
154         return( 1 );
155 }
156
157 static int
158 test_presence_filter(
159     Backend     *be,
160     Connection  *conn,
161     Operation   *op,
162     Entry       *e,
163     char        *type
164 )
165 {
166         if ( be != NULL && ! access_allowed( be, conn, op, e, type, NULL,
167             op->o_dn, ACL_SEARCH ) ) {
168                 return( -2 );
169         }
170
171         return( attr_find( e->e_attrs, type ) != NULL ? 0 : -1 );
172 }
173
174 static int
175 test_approx_filter(
176     Backend     *be,
177     Connection  *conn,
178     Operation   *op,
179     Entry       *e,
180     Ava         *ava
181 )
182 {
183         char            *w1, *w2, *c1, *c2;
184         int             i, rc, match;
185         Attribute       *a;
186
187         if ( be != NULL && ! access_allowed( be, conn, op, e, ava->ava_type,
188             NULL, op->o_dn, ACL_SEARCH ) ) {
189                 return( -2 );
190         }
191
192         if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
193                 return( -1 );
194         }
195
196         /* for each value in the attribute */
197         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
198                 /*
199                  * try to match words in the filter value in order
200                  * in the attribute value.
201                  */
202
203                 w2 = a->a_vals[i]->bv_val;
204                 /* for each word in the filter value */
205                 for ( w1 = first_word( ava->ava_value.bv_val ); w1 != NULL;
206                     w1 = next_word( w1 ) ) {
207                         if ( (c1 = phonetic( w1 )) == NULL ) {
208                                 break;
209                         }
210
211                         /*
212                          * for each word in the attribute value from
213                          * where we left off...
214                          */
215                         for ( w2 = first_word( w2 ); w2 != NULL;
216                             w2 = next_word( w2 ) ) {
217                                 c2 = phonetic( w2 );
218                                 if ( strcmp( c1, c2 ) == 0 ) {
219                                         free( c2 );
220                                         break;
221                                 }
222                                 free( c2 );
223                         }
224                         free( c1 );
225
226                         /*
227                          * if we stopped because we ran out of words
228                          * before making a match, go on to the next
229                          * value.  otherwise try to keep matching
230                          * words in this value from where we left off.
231                          */
232                         if ( w2 == NULL ) {
233                                 break;
234                         } else {
235                                 w2 = next_word( w2 );
236                         }
237                 }
238                 /*
239                  * if we stopped because we ran out of words we
240                  * have a match.
241                  */
242                 if ( w1 == NULL ) {
243                         return( 0 );
244                 }
245         }
246
247         return( 1 );
248 }
249
250 static int
251 test_filter_list(
252     Backend     *be,
253     Connection  *conn,
254     Operation   *op,
255     Entry       *e,
256     Filter      *flist,
257     int         ftype
258 )
259 {
260         int     rc, nomatch;
261         Filter  *f;
262
263         Debug( LDAP_DEBUG_FILTER, "=> test_filter_list\n", 0, 0, 0 );
264
265         nomatch = 1;
266         for ( f = flist; f != NULL; f = f->f_next ) {
267                 if ( test_filter( be, conn, op, e, f ) != 0 ) {
268                         if ( ftype == LDAP_FILTER_AND ) {
269                                 Debug( LDAP_DEBUG_FILTER,
270                                     "<= test_filter_list 1\n", 0, 0, 0 );
271                                 return( 1 );
272                         }
273                 } else {
274                         nomatch = 0;
275                 }
276         }
277
278         Debug( LDAP_DEBUG_FILTER, "<= test_filter_list %d\n", nomatch, 0, 0 );
279         return( nomatch );
280 }
281
282 static void
283 strcpy_special( char *d, char *s )
284 {
285         for ( ; *s; s++ ) {
286                 switch ( *s ) {
287                 case '.':
288                 case '\\':
289                 case '[':
290                 case ']':
291                 case '*':
292                 case '+':
293                 case '^':
294                 case '$':
295                         *d++ = '\\';
296                         /* FALL */
297                 default:
298                         *d++ = *s;
299                 }
300         }
301         *d = '\0';
302 }
303
304 static int
305 test_substring_filter(
306     Backend     *be,
307     Connection  *conn,
308     Operation   *op,
309     Entry       *e,
310     Filter      *f
311 )
312 {
313         Attribute       *a;
314         int             i, rc;
315         char            *p, *end, *realval, *tmp;
316         char            pat[BUFSIZ];
317         char            buf[BUFSIZ];
318         struct berval   *val;
319         regex_t         re;
320
321         Debug( LDAP_DEBUG_FILTER, "begin test_substring_filter\n", 0, 0, 0 );
322
323         if ( be != NULL && ! access_allowed( be, conn, op, e, f->f_sub_type,
324             NULL, op->o_dn, ACL_SEARCH ) ) {
325                 return( -2 );
326         }
327
328         if ( (a = attr_find( e->e_attrs, f->f_sub_type )) == NULL ) {
329                 return( -1 );
330         }
331
332         if ( a->a_syntax & SYNTAX_BIN ) {
333                 Debug( LDAP_DEBUG_FILTER, "test_substring_filter bin attr\n",
334                     0, 0, 0 );
335                 return( -1 );
336         }
337
338         /*
339          * construct a regular expression corresponding to the
340          * filter and let regex do the work
341          */
342
343         pat[0] = '\0';
344         p = pat;
345         end = pat + sizeof(pat) - 2;    /* leave room for null */
346         if ( f->f_sub_initial != NULL ) {
347                 strcpy( p, "^" );
348                 p = strchr( p, '\0' );
349                 /* 2 * in case every char is special */
350                 if ( p + 2 * strlen( f->f_sub_initial ) > end ) {
351                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
352                             0, 0, 0 );
353                         return( -1 );
354                 }
355                 strcpy_special( p, f->f_sub_initial );
356                 p = strchr( p, '\0' );
357         }
358         if ( f->f_sub_any != NULL ) {
359                 for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
360                         /* ".*" + value */
361                         if ( p + 2 * strlen( f->f_sub_any[i] ) + 2 > end ) {
362                                 Debug( LDAP_DEBUG_ANY,
363                                     "not enough pattern space\n", 0, 0, 0 );
364                                 return( -1 );
365                         }
366                         strcpy( p, ".*" );
367                         p = strchr( p, '\0' );
368                         strcpy_special( p, f->f_sub_any[i] );
369                         p = strchr( p, '\0' );
370                 }
371         }
372         if ( f->f_sub_final != NULL ) {
373                 /* ".*" + value */
374                 if ( p + 2 * strlen( f->f_sub_final ) + 2 > end ) {
375                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
376                             0, 0, 0 );
377                         return( -1 );
378                 }
379                 strcpy( p, ".*" );
380                 p = strchr( p, '\0' );
381                 strcpy_special( p, f->f_sub_final );
382                 p = strchr( p, '\0' );
383                 strcpy( p, "$" );
384         }
385
386         /* compile the regex */
387         Debug( LDAP_DEBUG_FILTER, "test_substring_filter: regcomp pat: %s\n",
388                 pat, 0, 0 );
389         if ((rc = regcomp(&re, pat, 0))) {
390                 char error[512];
391
392                 regerror(rc, &re, error, sizeof(error));
393                 Debug( LDAP_DEBUG_ANY, "regcomp failed (%s) %s\n",
394                         p, error, 0 );
395                 return( -1 );
396         }
397
398         /* for each value in the attribute see if regex matches */
399         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
400                 val = a->a_vals[i];
401                 tmp = NULL;
402                 if ( val->bv_len < sizeof(buf) ) {
403                         strcpy( buf, val->bv_val );
404                         realval = buf;
405                 } else {
406                         tmp = (char *) ch_malloc( val->bv_len + 1 );
407                         strcpy( tmp, val->bv_val );
408                         realval = tmp;
409                 }
410                 value_normalize( realval, a->a_syntax );
411
412                 rc = !regexec(&re, realval, 0, NULL, 0);
413
414                 if ( tmp != NULL ) {
415                         free( tmp );
416                 }
417                 if ( rc == 1 ) {
418                         regfree(&re);
419                         return( 0 );
420                 }
421         }
422
423         regfree(&re);
424
425         Debug( LDAP_DEBUG_FILTER, "end test_substring_filter 1\n", 0, 0, 0 );
426         return( 1 );
427 }