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