]> git.sur5r.net Git - openldap/blob - libraries/librewrite/rule.c
Sync with HEAD
[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 static int
74 destroy_action(
75                 struct rewrite_action **paction
76 )
77 {
78         struct rewrite_action   *action;
79
80         assert( paction );
81         assert( *paction );
82
83         action = *paction;
84
85         /* do something */
86         switch ( action->la_type ) {
87         case REWRITE_FLAG_GOTO: {
88                 int *pi = (int *)action->la_args;
89
90                 if ( pi ) {
91                         free( pi );
92                 }
93                 break;
94         }
95
96         default:
97                 break;
98         }
99         
100         free( action );
101         *paction = NULL;
102         
103         return 0;
104 }
105
106 /*
107  * In case of error it returns NULL and does not free all the memory
108  * it allocated; as this is a once only phase, and an error at this stage
109  * would require the server to stop, there is no need to be paranoid
110  * about memory allocation
111  */
112 int
113 rewrite_rule_compile(
114                 struct rewrite_info *info,
115                 struct rewrite_context *context,
116                 const char *pattern,
117                 const char *result,
118                 const char *flagstring
119 )
120 {
121         int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
122         int mode = REWRITE_RECURSE;
123
124         struct rewrite_rule *rule = NULL;
125         struct rewrite_subst *subst = NULL;
126         struct rewrite_action *action = NULL, *first_action = NULL;
127
128         const char *p;
129
130         assert( info != NULL );
131         assert( context != NULL );
132         assert( pattern != NULL );
133         assert( result != NULL );
134
135         /*
136          * A null flagstring should be allowed
137          */
138
139         /*
140          * Take care of substitution string
141          */
142         subst = rewrite_subst_compile( info, result );
143         if ( subst == NULL ) {
144                 return REWRITE_ERR;
145         }
146
147         /*
148          * Take care of flags
149          */
150         for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
151                 switch( p[ 0 ] ) {
152                         
153                 /*
154                  * REGEX flags
155                  */
156                 case REWRITE_FLAG_HONORCASE:            /* 'C' */
157                         /*
158                          * Honor case (default is case insensitive)
159                          */
160                         flags &= ~REWRITE_REGEX_ICASE;
161                         break;
162                         
163                 case REWRITE_FLAG_BASICREGEX:           /* 'R' */
164                         /*
165                          * Use POSIX Basic Regular Expression syntax
166                          * instead of POSIX Extended Regular Expression 
167                          * syntax (default)
168                          */
169                         flags &= ~REWRITE_REGEX_EXTENDED;
170                         break;
171                         
172                 /*
173                  * Execution mode flags
174                  */
175                 case REWRITE_FLAG_EXECONCE:             /* ':' */
176                         /*
177                          * Apply rule once only
178                          */
179                         mode &= ~REWRITE_RECURSE;
180                         mode |= REWRITE_EXEC_ONCE;
181                         break;
182                 
183                 /*
184                  * Special action flags
185                  */
186                 case REWRITE_FLAG_STOP:                 /* '@' */
187                         /*
188                          * Bail out after applying rule
189                          */
190                         action = calloc( sizeof( struct rewrite_action ), 1 );
191                         if ( action == NULL ) {
192                                 /* cleanup ... */
193                                 return REWRITE_ERR;
194                         }
195                         
196                         mode &= ~REWRITE_RECURSE;
197                         mode |= REWRITE_EXEC_ONCE;
198                         action->la_type = REWRITE_ACTION_STOP;
199                         break;
200                         
201                 case REWRITE_FLAG_UNWILLING:            /* '#' */
202                         /*
203                          * Matching objs will be marked as gone!
204                          */
205                         action = calloc( sizeof( struct rewrite_action ), 1 );
206                         if ( action == NULL ) {
207                                 /* cleanup ... */
208                                 return REWRITE_ERR;
209                         }
210                         
211                         mode &= ~REWRITE_RECURSE;
212                         mode |= REWRITE_EXEC_ONCE;
213                         action->la_type = REWRITE_ACTION_UNWILLING;
214                         break;
215
216                 case REWRITE_FLAG_GOTO: {                       /* 'G' */
217                         /*
218                          * After applying rule, jump N rules
219                          */
220
221                         char buf[16], *q;
222                         size_t l;
223                         int *d;
224                         
225                         if ( p[ 1 ] != '{' ) {
226                                 /* XXX Need to free stuff */
227                                 return REWRITE_ERR;
228                         }
229
230                         q = strchr( p + 2, '}' );
231                         if ( q == NULL ) {
232                                 /* XXX Need to free stuff */
233                                 return REWRITE_ERR;
234                         }
235
236                         l = q - p + 2;
237                         if ( l >= sizeof( buf ) ) {
238                                 /* XXX Need to free stuff */
239                                 return REWRITE_ERR;
240                         }
241                         AC_MEMCPY( buf, p + 2, l );
242                         buf[ l ] = '\0';
243
244                         d = malloc( sizeof( int ) );
245                         if ( d == NULL ) {
246                                 /* XXX Need to free stuff */
247                                 return REWRITE_ERR;
248                         }
249                         d[ 0 ] = atoi( buf );
250
251                         action = calloc( sizeof( struct rewrite_action ), 1 );
252                         if ( action == NULL ) {
253                                 /* cleanup ... */       
254                                 return REWRITE_ERR;
255                         }
256                         action->la_type = REWRITE_ACTION_GOTO;
257                         action->la_args = (void *)d;
258
259                         p = q;  /* p is incremented by the for ... */
260                 
261                         break;
262                 }
263
264                 case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
265                         /*
266                          * Ignore errors!
267                          */
268                         action = calloc( sizeof( struct rewrite_action ), 1 );
269                         if ( action == NULL ) {
270                                 /* cleanup ... */
271                                 return REWRITE_ERR;
272                         }
273                         
274                         action->la_type = REWRITE_ACTION_IGNORE_ERR;
275                         break;
276                         
277                 /*
278                  * Other flags ...
279                  */
280                 default:
281                         /*
282                          * Unimplemented feature (complain only)
283                          */
284                         break;
285                 }
286                 
287                 /*
288                  * Stupid way to append to a list ...
289                  */
290                 if ( action != NULL ) {
291                         if ( first_action == NULL ) {
292                                 first_action = action;
293                         } else {
294                                 append_action( first_action, action );
295                         }
296                         action = NULL;
297                 }
298         }
299         
300         /*
301          * Finally, rule allocation
302          */
303         rule = calloc( sizeof( struct rewrite_rule ), 1 );
304         if ( rule == NULL ) {
305                 /* charray_free( res ); */
306                 /*
307                  * XXX need to free the value subst stuff!
308                  */
309                 return REWRITE_ERR;
310         }
311         
312         /*
313          * REGEX compilation (luckily I don't need to take care of this ...)
314          */
315         if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
316                 /* charray_free( res ); */
317                 /*
318                  *XXX need to free the value subst stuff!
319                  */
320                 free( rule );
321                 return REWRITE_ERR;
322         }
323         
324         /*
325          * Just to remember them ...
326          */
327         rule->lr_pattern = strdup( pattern );
328         rule->lr_subststring = strdup( result );
329         rule->lr_flagstring = strdup( flagstring );
330         
331         /*
332          * Load compiled data into rule
333          */
334         rule->lr_subst = subst;
335
336         /*
337          * Set various parameters
338          */
339         rule->lr_flags = flags;         /* don't really need any longer ... */
340         rule->lr_mode = mode;
341         rule->lr_action = first_action;
342         
343         /*
344          * Append rule at the end of the rewrite context
345          */
346         append_rule( context, rule );
347
348         return REWRITE_SUCCESS;
349 }
350
351 /*
352  * Rewrites string according to rule; may return:
353  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
354  *      STOP:   fine, rule matched; stop processing following rules
355  *      UNWILL: rule matched; force 'unwilling to perform'
356  */
357 int
358 rewrite_rule_apply(
359                 struct rewrite_info *info,
360                 struct rewrite_op *op,
361                 struct rewrite_rule *rule,
362                 const char *arg,
363                 char **result
364                 )
365 {
366         size_t nmatch = REWRITE_MAX_MATCH;
367         regmatch_t match[ REWRITE_MAX_MATCH ];
368
369         int rc = REWRITE_SUCCESS;
370
371         char *string;
372         int strcnt = 0;
373         struct berval val = { 0, NULL };
374
375         assert( info != NULL );
376         assert( op != NULL );
377         assert( rule != NULL );
378         assert( arg != NULL );
379         assert( result != NULL );
380
381         *result = NULL;
382
383         string = (char *)arg;
384         
385         /*
386          * In case recursive match is required (default)
387          */
388 recurse:;
389
390         Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
391                         " rule='%s' string='%s'\n", 
392                         rule->lr_pattern, string, 0 );
393         
394         op->lo_num_passes++;
395         if ( regexec( &rule->lr_regex, string, nmatch, match, 0 ) != 0 ) {
396                 if ( *result == NULL && strcnt > 0 ) {
397                         free( string );
398                         string = NULL;
399                 }
400
401                 /*
402                  * No match is OK; *result = NULL means no match
403                  */
404                 return REWRITE_REGEXEC_OK;
405         }
406
407         rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
408                         match, &val );
409
410         *result = val.bv_val;
411         val.bv_val = NULL;
412         if ( strcnt > 0 ) {
413                 free( string );
414                 string = NULL;
415         }
416
417         if ( rc != REWRITE_REGEXEC_OK ) {
418                 return rc;
419         }
420
421         if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE 
422                         && op->lo_num_passes <= info->li_max_passes ) {
423                 string = *result;
424                 strcnt++;
425
426                 goto recurse;
427         }
428
429         return REWRITE_REGEXEC_OK;
430 }
431
432 int
433 rewrite_rule_destroy(
434                 struct rewrite_rule **prule
435                 )
436 {
437         struct rewrite_rule *rule;
438         struct rewrite_action *action;
439
440         assert( prule );
441         assert( *prule );
442
443         rule = *prule;
444
445         if ( rule->lr_pattern ) {
446                 free( rule->lr_pattern );
447                 rule->lr_pattern = NULL;
448         }
449
450         if ( rule->lr_subststring ) {
451                 free( rule->lr_subststring );
452                 rule->lr_subststring = NULL;
453         }
454
455         if ( rule->lr_flagstring ) {
456                 free( rule->lr_flagstring );
457                 rule->lr_flagstring = NULL;
458         }
459
460         if ( rule->lr_subst ) {
461                 rewrite_subst_destroy( &rule->lr_subst );
462         }
463
464         regfree( &rule->lr_regex );
465
466         for ( action = rule->lr_action; action; ) {
467                 struct rewrite_action *curraction = action;
468
469                 action = action->la_next;
470                 destroy_action( &curraction );
471         }
472
473         free( rule );
474         *prule = NULL;
475
476         return 0;
477 }
478