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