]> git.sur5r.net Git - openldap/blob - servers/slapd/str2filter.c
More changes to let BDB build without LDBM.
[openldap] / servers / slapd / str2filter.c
1 /* str2filter.c - parse an rfc 1588 string filter */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 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/string.h>
13 #include <ac/ctype.h>
14 #include <ac/socket.h>
15
16 #include "slap.h"
17 #include <ldap_pvt.h>
18
19 static char     *find_matching_paren( const char *s );
20 static Filter   *str2list( const char *str, long unsigned int ftype);
21 static Filter   *str2simple( const char *str);
22 static int      str2subvals( const char *val, Filter *f);
23
24 Filter *
25 str2filter( const char *str )
26 {
27         Filter  *f = NULL;
28         char    *end, *freeme;
29
30 #ifdef NEW_LOGGING
31         LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
32                    "str2filter: \"%s\"\n", str ));
33 #else
34         Debug( LDAP_DEBUG_FILTER, "str2filter \"%s\"\n", str, 0, 0 );
35 #endif
36
37
38         if ( str == NULL || *str == '\0' ) {
39                 return( NULL );
40         }
41
42         str = freeme = ch_strdup( str );
43
44         switch ( *str ) {
45         case '(':
46                 if ( (end = find_matching_paren( str )) == NULL ) {
47                         filter_free( f );
48                         free( freeme );
49                         return( NULL );
50                 }
51                 *end = '\0';
52
53                 str++;
54                 switch ( *str ) {
55                 case '&':
56 #ifdef NEW_LOGGING
57                         LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
58                                    "str2filter:  AND\n" ));
59 #else
60                         Debug( LDAP_DEBUG_FILTER, "str2filter: AND\n",
61                             0, 0, 0 );
62 #endif
63
64
65                         str++;
66                         f = str2list( str, LDAP_FILTER_AND );
67                         break;
68
69                 case '|':
70 #ifdef NEW_LOGGING
71                         LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
72                                    "str2filter:  OR\n" ));
73 #else
74                         Debug( LDAP_DEBUG_FILTER, "put_filter: OR\n",
75                             0, 0, 0 );
76 #endif
77
78
79                         str++;
80                         f = str2list( str, LDAP_FILTER_OR );
81                         break;
82
83                 case '!':
84 #ifdef NEW_LOGGING
85                         LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
86                                    "str2filter:  NOT\n" ));
87 #else
88                         Debug( LDAP_DEBUG_FILTER, "put_filter: NOT\n",
89                             0, 0, 0 );
90 #endif
91
92
93                         str++;
94                         f = str2list( str, LDAP_FILTER_NOT );
95                         break;
96
97                 default:
98 #ifdef NEW_LOGGING
99                         LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
100                                    "str2filter:  simple\n" ));
101 #else
102                         Debug( LDAP_DEBUG_FILTER, "str2filter: simple\n",
103                             0, 0, 0 );
104 #endif
105
106
107                         f = str2simple( str );
108                         break;
109                 }
110                 *end = ')';
111                 break;
112
113         default:        /* assume it's a simple type=value filter */
114 #ifdef NEW_LOGGING
115                 LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1,
116                            "str2filter: default\n" ));
117 #else
118                 Debug( LDAP_DEBUG_FILTER, "str2filter: default\n", 0, 0,
119                     0 );
120 #endif
121
122
123                 f = str2simple( str );
124                 break;
125         }
126
127         free( freeme );
128         return( f );
129 }
130
131 /*
132  * Put a list of filters like this "(filter1)(filter2)..."
133  */
134
135 static Filter *
136 str2list( const char *str, unsigned long ftype )
137 {
138         Filter  *f;
139         Filter  **fp;
140         char    *next;
141         char    save;
142
143 #ifdef NEW_LOGGING
144         LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
145                    "str2list: \"%s\"\n", str ));
146 #else
147         Debug( LDAP_DEBUG_FILTER, "str2list \"%s\"\n", str, 0, 0 );
148 #endif
149
150
151         f = (Filter *) ch_calloc( 1, sizeof(Filter) );
152         f->f_choice = ftype;
153         fp = &f->f_list;
154
155         while ( *str ) {
156                 while ( *str && isspace( (unsigned char) *str ) )
157                         str++;
158                 if ( *str == '\0' )
159                         break;
160
161                 if ( (next = find_matching_paren( str )) == NULL ) {
162                         filter_free( f );
163                         return( NULL );
164                 }
165                 save = *++next;
166                 *next = '\0';
167
168                 /* now we have "(filter)" with str pointing to it */
169                 if ( (*fp = str2filter( str )) == NULL ) {
170                         filter_free( f );
171                         *next = save;
172                         return( NULL );
173                 }
174                 *next = save;
175
176                 str = next;
177                 fp = &(*fp)->f_next;
178         }
179         *fp = NULL;
180
181         return( f );
182 }
183
184 static Filter *
185 str2simple( const char *str )
186 {
187         Filter          *f;
188         char            *s;
189         char            *value, savechar;
190         int                     rc;
191         const char              *text;
192
193 #ifdef NEW_LOGGING
194         LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
195                    "str2simple:  \"%s\"\n", str ));
196 #else
197         Debug( LDAP_DEBUG_FILTER, "str2simple \"%s\"\n", str, 0, 0 );
198 #endif
199
200
201         if ( (s = strchr( str, '=' )) == NULL ) {
202                 return( NULL );
203         }
204         value = &s[1];
205
206         *s-- = '\0';    /* we shouldn't be mucking with str */
207         savechar = *s;
208
209         f = (Filter *) ch_calloc( 1, sizeof(Filter) );
210
211         switch ( *s ) {
212         case '<':
213                 f->f_choice = LDAP_FILTER_LE;
214                 *s = '\0';
215                 break;
216         case '>':
217                 f->f_choice = LDAP_FILTER_GE;
218                 *s = '\0';
219                 break;
220         case '~':
221                 f->f_choice = LDAP_FILTER_APPROX;
222                 *s = '\0';
223                 break;
224         case ':':
225                 f->f_choice = LDAP_FILTER_EXT;
226                 *s = '\0';
227                 return NULL;
228                 break;
229
230         default:
231                 if ( ldap_pvt_find_wildcard( value ) == NULL ) {
232                         f->f_choice = LDAP_FILTER_EQUALITY;
233                 } else if ( strcmp( value, "*" ) == 0 ) {
234                         f->f_choice = LDAP_FILTER_PRESENT;
235                 } else {
236                         f->f_choice = LDAP_FILTER_SUBSTRINGS;
237                         f->f_sub = ch_calloc( 1, sizeof( SubstringsAssertion ) );
238                         rc = slap_str2ad( str, &f->f_sub_desc, &text );
239                         if( rc != LDAP_SUCCESS ) {
240                                 filter_free( f );
241                                 *(value-1) = '=';
242                                 return NULL;
243                         }
244                         if ( str2subvals( value, f ) != 0 ) {
245                                 filter_free( f );
246                                 *(value-1) = '=';
247                                 return( NULL );
248                         }
249                         *(value-1) = '=';
250                         return( f );
251                 }
252                 break;
253         }
254
255         if ( f->f_choice == LDAP_FILTER_PRESENT ) {
256                 rc = slap_str2ad( str, &f->f_desc, &text );
257                 if( rc != LDAP_SUCCESS ) {
258                         filter_free( f );
259                         *(value-1) = '=';
260                         return NULL;
261                 }
262         } else {
263                 char *tmp;
264
265                 f->f_ava = ch_calloc( 1, sizeof( AttributeAssertion ) );
266                 f->f_av_desc = NULL;
267                 rc = slap_str2ad( str, &f->f_av_desc, &text );
268                 if( rc != LDAP_SUCCESS ) {
269                         filter_free( f );
270                         *(value-1) = '=';
271                         return NULL;
272                 }
273
274                 tmp = ch_strdup( value );
275                 ldap_pvt_filter_value_unescape( tmp );
276                 f->f_av_value = ber_bvstr( tmp );
277         }
278
279         *s = savechar;
280         *(value-1) = '=';
281
282         return( f );
283 }
284
285 static int
286 str2subvals( const char *in, Filter *f )
287 {
288         char    *nextstar, *val, *freeme;
289         int     gotstar;
290
291 #ifdef NEW_LOGGING
292         LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY,
293                    "str2subvals: \"%s\"\n", in ));
294 #else
295         Debug( LDAP_DEBUG_FILTER, "str2subvals \"%s\"\n", in, 0, 0 );
296 #endif
297
298
299         if( in == NULL ) return 0;
300
301         val = freeme = ch_strdup( in );
302         gotstar = 0;
303
304         while ( *val ) {
305                 if ( (nextstar = ldap_pvt_find_wildcard( val )) != NULL )
306                         *nextstar++ = '\0';
307
308                 ldap_pvt_filter_value_unescape( val );
309
310                 if ( gotstar == 0 ) {
311                         f->f_sub_initial = ber_bvstrdup( val );
312
313                 } else if ( nextstar == NULL ) {
314                         f->f_sub_final = ber_bvstrdup( val );
315
316                 } else {
317                         charray_add( (char ***) &f->f_sub_any, (char *) ber_bvstrdup( val ) );
318                 }
319
320                 gotstar = 1;
321                 val = nextstar;
322         }
323
324         free( freeme );
325         return( 0 );
326 }
327
328 /*
329  * find_matching_paren - return a pointer to the right paren in s matching
330  * the left paren to which *s currently points
331  */
332
333 static char *
334 find_matching_paren( const char *s )
335 {
336         int     balance, escape;
337
338         balance = 0;
339         escape = 0;
340         for ( ; *s; s++ ) {
341                 if ( escape == 0 ) {
342                         if ( *s == '(' )
343                                 balance++;
344                         else if ( *s == ')' )
345                                 balance--;
346                 }
347                 if ( balance == 0 ) {
348                         return (char *) s;
349                 }
350                 if ( *s == '\\' && ! escape )
351                         escape = 1;
352                 else
353                         escape = 0;
354         }
355
356         return NULL;
357 }