]> git.sur5r.net Git - openldap/blob - libraries/librewrite/rule.c
Fix previous commit
[openldap] / libraries / librewrite / rule.c
1 /******************************************************************************
2  *
3  * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
4  * All rights reserved.
5  *
6  * Permission is granted to anyone to use this software for any purpose
7  * on any computer system, and to alter it and redistribute it, subject
8  * to the following restrictions:
9  *
10  * 1. The author is not responsible for the consequences of use of this
11  * software, no matter how awful, even if they arise from flaws in it.
12  *
13  * 2. The origin of this software must not be misrepresented, either by
14  * explicit claim or by omission.  Since few users ever read sources,
15  * credits should appear in the documentation.
16  *
17  * 3. Altered versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.  Since few users
19  * ever read sources, credits should appear in the documentation.
20  * 
21  * 4. This notice may not be removed or altered.
22  *
23  ******************************************************************************/
24
25 #include <portable.h>
26
27 #include "rewrite-int.h"
28
29 /*
30  * Appends a rule to the double linked list of rules
31  * Helper for rewrite_rule_compile
32  */
33 static int
34 append_rule(
35                 struct rewrite_context *context,
36                 struct rewrite_rule *rule
37 )
38 {
39         struct rewrite_rule *r;
40
41         assert( context != NULL );
42         assert( context->lc_rule != NULL );
43         assert( rule != NULL );
44
45         for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
46         r->lr_next = rule;
47         rule->lr_prev = r;
48         
49         return REWRITE_SUCCESS;
50 }
51
52 /*
53  * Appends an action to the linked list of actions
54  * Helper for rewrite_rule_compile
55  */
56 static int
57 append_action(
58                 struct rewrite_action *base,
59                 struct rewrite_action *action
60 )
61 {
62         struct rewrite_action *a;
63
64         assert( base != NULL );
65         assert( action != NULL );
66         
67         for ( a = base; a->la_next != NULL; a = a->la_next );
68         a->la_next = action;
69         
70         return REWRITE_SUCCESS;
71 }
72
73 /*
74  * In case of error it returns NULL and does not free all the memory
75  * it allocated; as this is a once only phase, and an error at this stage
76  * would require the server to stop, there is no need to be paranoid
77  * about memory allocation
78  */
79 int
80 rewrite_rule_compile(
81                 struct rewrite_info *info,
82                 struct rewrite_context *context,
83                 const char *pattern,
84                 const char *result,
85                 const char *flagstring
86 )
87 {
88         int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
89         int mode = REWRITE_RECURSE;
90
91         struct rewrite_rule *rule = NULL;
92         struct rewrite_subst *subst = NULL;
93         struct rewrite_action *action = NULL, *first_action = NULL;
94
95         const char *p;
96
97         assert( info != NULL );
98         assert( context != NULL );
99         assert( pattern != NULL );
100         assert( result != NULL );
101
102         /*
103          * A null flagstring should be allowed
104          */
105
106         /*
107          * Take care of substitution string
108          */
109         subst = rewrite_subst_compile( info, result );
110         if ( subst == NULL ) {
111                 return REWRITE_ERR;
112         }
113
114         /*
115          * Take care of flags
116          */
117         for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
118                 switch( p[ 0 ] ) {
119                         
120                 /*
121                  * REGEX flags
122                  */
123                 case REWRITE_FLAG_HONORCASE:            /* 'C' */
124                         /*
125                          * Honor case (default is case insensitive)
126                          */
127                         flags &= ~REWRITE_REGEX_ICASE;
128                         break;
129                         
130                 case REWRITE_FLAG_BASICREGEX:           /* 'R' */
131                         /*
132                          * Use POSIX Basic Regular Expression syntax
133                          * instead of POSIX Extended Regular Expression 
134                          * syntax (default)
135                          */
136                         flags &= ~REWRITE_REGEX_EXTENDED;
137                         break;
138                         
139                 /*
140                  * Execution mode flags
141                  */
142                 case REWRITE_FLAG_EXECONCE:             /* ':' */
143                         /*
144                          * Apply rule once only
145                          */
146                         mode &= ~REWRITE_RECURSE;
147                         mode |= REWRITE_EXEC_ONCE;
148                         break;
149                 
150                 /*
151                  * Special action flags
152                  */
153                 case REWRITE_FLAG_STOP:                 /* '@' */
154                         /*
155                          * Bail out after applying rule
156                          */
157                         action = calloc( sizeof( struct rewrite_action ), 1 );
158                         if ( action == NULL ) {
159                                 /* cleanup ... */
160                                 return REWRITE_ERR;
161                         }
162                         
163                         mode &= ~REWRITE_RECURSE;
164                         mode |= REWRITE_EXEC_ONCE;
165                         action->la_type = REWRITE_ACTION_STOP;
166                         break;
167                         
168                 case REWRITE_FLAG_UNWILLING:            /* '#' */
169                         /*
170                          * Matching objs will be marked as gone!
171                          */
172                         action = calloc( sizeof( struct rewrite_action ), 1 );
173                         if ( action == NULL ) {
174                                 /* cleanup ... */
175                                 return REWRITE_ERR;
176                         }
177                         
178                         mode &= ~REWRITE_RECURSE;
179                         mode |= REWRITE_EXEC_ONCE;
180                         action->la_type = REWRITE_ACTION_UNWILLING;
181                         break;
182
183                 case REWRITE_FLAG_GOTO: {                       /* 'G' */
184                         /*
185                          * After applying rule, jump N rules
186                          */
187
188                         char buf[16], *q;
189                         size_t l;
190                         int *d;
191                         
192                         if ( p[ 1 ] != '{' ) {
193                                 /* XXX Need to free stuff */
194                                 return REWRITE_ERR;
195                         }
196
197                         q = strchr( p + 2, '}' );
198                         if ( q == NULL ) {
199                                 /* XXX Need to free stuff */
200                                 return REWRITE_ERR;
201                         }
202
203                         l = q - p + 2;
204                         if ( l >= sizeof( buf ) ) {
205                                 /* XXX Need to free stuff */
206                                 return REWRITE_ERR;
207                         }
208                         AC_MEMCPY( buf, p + 2, l );
209                         buf[ l ] = '\0';
210
211                         d = malloc( sizeof( int ) );
212                         if ( d == NULL ) {
213                                 /* XXX Need to free stuff */
214                                 return REWRITE_ERR;
215                         }
216                         d[ 0 ] = atoi( buf );
217
218                         action = calloc( sizeof( struct rewrite_action ), 1 );
219                         if ( action == NULL ) {
220                                 /* cleanup ... */       
221                                 return REWRITE_ERR;
222                         }
223                         action->la_type = REWRITE_ACTION_GOTO;
224                         action->la_args = (void *)d;
225
226                         p = q;  /* p is incremented by the for ... */
227                 
228                         break;
229                 }
230
231                 case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
232                         /*
233                          * Ignore errors!
234                          */
235                         action = calloc( sizeof( struct rewrite_action ), 1 );
236                         if ( action == NULL ) {
237                                 /* cleanup ... */
238                                 return REWRITE_ERR;
239                         }
240                         
241                         action->la_type = REWRITE_ACTION_IGNORE_ERR;
242                         break;
243                         
244                 /*
245                  * Other flags ...
246                  */
247                 default:
248                         /*
249                          * Unimplemented feature (complain only)
250                          */
251                         break;
252                 }
253                 
254                 /*
255                  * Stupid way to append to a list ...
256                  */
257                 if ( action != NULL ) {
258                         if ( first_action == NULL ) {
259                                 first_action = action;
260                         } else {
261                                 append_action( first_action, action );
262                         }
263                         action = NULL;
264                 }
265         }
266         
267         /*
268          * Finally, rule allocation
269          */
270         rule = calloc( sizeof( struct rewrite_rule ), 1 );
271         if ( rule == NULL ) {
272                 /* charray_free( res ); */
273                 /*
274                  * XXX need to free the value subst stuff!
275                  */
276                 return REWRITE_ERR;
277         }
278         
279         /*
280          * REGEX compilation (luckily I don't need to take care of this ...)
281          */
282         if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
283                 /* charray_free( res ); */
284                 /*
285                  *XXX need to free the value subst stuff!
286                  */
287                 free( rule );
288                 return REWRITE_ERR;
289         }
290         
291         /*
292          * Just to remember them ...
293          */
294         rule->lr_pattern = strdup( pattern );
295         rule->lr_subststring = strdup( result );
296         rule->lr_flagstring = strdup( flagstring );
297         
298         /*
299          * Load compiled data into rule
300          */
301         rule->lr_subst = subst;
302
303         /*
304          * Set various parameters
305          */
306         rule->lr_flags = flags;         /* don't really need any longer ... */
307         rule->lr_mode = mode;
308         rule->lr_action = first_action;
309         
310         /*
311          * Append rule at the end of the rewrite context
312          */
313         append_rule( context, rule );
314
315         return REWRITE_SUCCESS;
316 }
317
318 /*
319  * Rewrites string according to rule; may return:
320  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
321  *      STOP:   fine, rule matched; stop processing following rules
322  *      UNWILL: rule matched; force 'unwilling to perform'
323  */
324 int
325 rewrite_rule_apply(
326                 struct rewrite_info *info,
327                 struct rewrite_op *op,
328                 struct rewrite_rule *rule,
329                 const char *arg,
330                 char **result
331                 )
332 {
333         size_t nmatch = REWRITE_MAX_MATCH;
334         regmatch_t match[ REWRITE_MAX_MATCH ];
335
336         int rc = REWRITE_SUCCESS;
337
338         char *string;
339         struct berval val;
340
341         assert( info != NULL );
342         assert( op != NULL );
343         assert( rule != NULL );
344         assert( arg != NULL );
345         assert( result != NULL );
346
347         *result = NULL;
348
349         string = strdup( arg );
350         if ( string == NULL ) {
351                 return REWRITE_REGEXEC_ERR;
352         }
353         
354         /*
355          * In case recursive match is required (default)
356          */
357 recurse:;
358
359         Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
360                         " rule='%s' string='%s'\n%s", 
361                         rule->lr_pattern, string, "" );
362         
363         op->lo_num_passes++;
364         if ( regexec( &rule->lr_regex, string, nmatch, match, 0 ) != 0 ) {
365                 if ( *result == NULL ) {
366                         free( string );
367                 }
368
369                 /*
370                  * No match is OK; *result = NULL means no match
371                  */
372                 return REWRITE_REGEXEC_OK;
373         }
374
375         rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
376                         match, &val );
377
378         *result = val.bv_val;
379         free( string );
380
381         if ( rc != REWRITE_REGEXEC_OK ) {
382                 return rc;
383         }
384
385         if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE 
386                         && op->lo_num_passes <= info->li_max_passes ) {
387                 string = *result;
388                 goto recurse;
389         }
390
391         return REWRITE_REGEXEC_OK;
392 }
393