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