1*cf1d77f7Schristos /* $NetBSD: rule.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */
24e6df137Slukem
333197c6aStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem *
6*cf1d77f7Schristos * Copyright 2000-2021 The OpenLDAP Foundation.
72de962bdSlukem * All rights reserved.
82de962bdSlukem *
92de962bdSlukem * Redistribution and use in source and binary forms, with or without
102de962bdSlukem * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem * Public License.
122de962bdSlukem *
132de962bdSlukem * A copy of this license is available in the file LICENSE in the
142de962bdSlukem * top-level directory of the distribution or, alternatively, at
152de962bdSlukem * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem */
172de962bdSlukem /* ACKNOWLEDGEMENT:
182de962bdSlukem * This work was initially developed by Pierangelo Masarati for
192de962bdSlukem * inclusion in OpenLDAP Software.
202de962bdSlukem */
212de962bdSlukem
222de962bdSlukem #include <portable.h>
232de962bdSlukem
242de962bdSlukem #include "rewrite-int.h"
252de962bdSlukem
262de962bdSlukem /*
272de962bdSlukem * Appends a rule to the double linked list of rules
282de962bdSlukem * Helper for rewrite_rule_compile
292de962bdSlukem */
302de962bdSlukem static int
append_rule(struct rewrite_context * context,struct rewrite_rule * rule)312de962bdSlukem append_rule(
322de962bdSlukem struct rewrite_context *context,
332de962bdSlukem struct rewrite_rule *rule
342de962bdSlukem )
352de962bdSlukem {
362de962bdSlukem struct rewrite_rule *r;
372de962bdSlukem
382de962bdSlukem assert( context != NULL );
392de962bdSlukem assert( context->lc_rule != NULL );
402de962bdSlukem assert( rule != NULL );
412de962bdSlukem
422de962bdSlukem for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
432de962bdSlukem r->lr_next = rule;
442de962bdSlukem rule->lr_prev = r;
452de962bdSlukem
462de962bdSlukem return REWRITE_SUCCESS;
472de962bdSlukem }
482de962bdSlukem
492de962bdSlukem /*
502de962bdSlukem * Appends an action to the linked list of actions
512de962bdSlukem * Helper for rewrite_rule_compile
522de962bdSlukem */
532de962bdSlukem static int
append_action(struct rewrite_action ** pbase,struct rewrite_action * action)542de962bdSlukem append_action(
552de962bdSlukem struct rewrite_action **pbase,
562de962bdSlukem struct rewrite_action *action
572de962bdSlukem )
582de962bdSlukem {
592de962bdSlukem struct rewrite_action **pa;
602de962bdSlukem
612de962bdSlukem assert( pbase != NULL );
622de962bdSlukem assert( action != NULL );
632de962bdSlukem
642de962bdSlukem for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
652de962bdSlukem *pa = action;
662de962bdSlukem
672de962bdSlukem return REWRITE_SUCCESS;
682de962bdSlukem }
692de962bdSlukem
702de962bdSlukem static int
destroy_action(struct rewrite_action ** paction)712de962bdSlukem destroy_action(
722de962bdSlukem struct rewrite_action **paction
732de962bdSlukem )
742de962bdSlukem {
752de962bdSlukem struct rewrite_action *action;
762de962bdSlukem
772de962bdSlukem assert( paction != NULL );
782de962bdSlukem assert( *paction != NULL );
792de962bdSlukem
802de962bdSlukem action = *paction;
812de962bdSlukem
822de962bdSlukem /* do something */
832de962bdSlukem switch ( action->la_type ) {
842de962bdSlukem case REWRITE_FLAG_GOTO:
852de962bdSlukem case REWRITE_FLAG_USER: {
862de962bdSlukem int *pi = (int *)action->la_args;
872de962bdSlukem
882de962bdSlukem if ( pi ) {
892de962bdSlukem free( pi );
902de962bdSlukem }
912de962bdSlukem break;
922de962bdSlukem }
932de962bdSlukem
942de962bdSlukem default:
952de962bdSlukem break;
962de962bdSlukem }
972de962bdSlukem
982de962bdSlukem free( action );
992de962bdSlukem *paction = NULL;
1002de962bdSlukem
1012de962bdSlukem return 0;
1022de962bdSlukem }
1032de962bdSlukem
1042de962bdSlukem static void
destroy_actions(struct rewrite_action * paction)1052de962bdSlukem destroy_actions(
1062de962bdSlukem struct rewrite_action *paction
1072de962bdSlukem )
1082de962bdSlukem {
1092de962bdSlukem struct rewrite_action *next;
1102de962bdSlukem
1112de962bdSlukem for (; paction; paction = next) {
1122de962bdSlukem next = paction->la_next;
1132de962bdSlukem destroy_action( &paction );
1142de962bdSlukem }
1152de962bdSlukem }
1162de962bdSlukem
1172de962bdSlukem /*
1182de962bdSlukem */
1192de962bdSlukem int
rewrite_rule_compile(struct rewrite_info * info,struct rewrite_context * context,const char * pattern,const char * result,const char * flagstring)1202de962bdSlukem rewrite_rule_compile(
1212de962bdSlukem struct rewrite_info *info,
1222de962bdSlukem struct rewrite_context *context,
1232de962bdSlukem const char *pattern,
1242de962bdSlukem const char *result,
1252de962bdSlukem const char *flagstring
1262de962bdSlukem )
1272de962bdSlukem {
1282de962bdSlukem int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
1292de962bdSlukem int mode = REWRITE_RECURSE;
13033197c6aStron int max_passes;
1312de962bdSlukem
1322de962bdSlukem struct rewrite_rule *rule = NULL;
1332de962bdSlukem struct rewrite_subst *subst = NULL;
1342de962bdSlukem struct rewrite_action *action = NULL, *first_action = NULL;
1352de962bdSlukem
1362de962bdSlukem const char *p;
1372de962bdSlukem
1382de962bdSlukem assert( info != NULL );
1392de962bdSlukem assert( context != NULL );
1402de962bdSlukem assert( pattern != NULL );
1412de962bdSlukem assert( result != NULL );
1422de962bdSlukem /*
1432de962bdSlukem * A null flagstring should be allowed
1442de962bdSlukem */
1452de962bdSlukem
14633197c6aStron max_passes = info->li_max_passes_per_rule;
14733197c6aStron
1482de962bdSlukem /*
1492de962bdSlukem * Take care of substitution string
1502de962bdSlukem */
1512de962bdSlukem subst = rewrite_subst_compile( info, result );
1522de962bdSlukem if ( subst == NULL ) {
1532de962bdSlukem return REWRITE_ERR;
1542de962bdSlukem }
1552de962bdSlukem
1562de962bdSlukem /*
1572de962bdSlukem * Take care of flags
1582de962bdSlukem */
1592de962bdSlukem for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
1602de962bdSlukem switch( p[ 0 ] ) {
1612de962bdSlukem
1622de962bdSlukem /*
1632de962bdSlukem * REGEX flags
1642de962bdSlukem */
1652de962bdSlukem case REWRITE_FLAG_HONORCASE: /* 'C' */
1662de962bdSlukem /*
1672de962bdSlukem * Honor case (default is case insensitive)
1682de962bdSlukem */
1692de962bdSlukem flags &= ~REWRITE_REGEX_ICASE;
1702de962bdSlukem break;
1712de962bdSlukem
1722de962bdSlukem case REWRITE_FLAG_BASICREGEX: /* 'R' */
1732de962bdSlukem /*
1742de962bdSlukem * Use POSIX Basic Regular Expression syntax
1752de962bdSlukem * instead of POSIX Extended Regular Expression
1762de962bdSlukem * syntax (default)
1772de962bdSlukem */
1782de962bdSlukem flags &= ~REWRITE_REGEX_EXTENDED;
1792de962bdSlukem break;
1802de962bdSlukem
1812de962bdSlukem /*
1822de962bdSlukem * Execution mode flags
1832de962bdSlukem */
1842de962bdSlukem case REWRITE_FLAG_EXECONCE: /* ':' */
1852de962bdSlukem /*
1862de962bdSlukem * Apply rule once only
1872de962bdSlukem */
1882de962bdSlukem mode &= ~REWRITE_RECURSE;
1892de962bdSlukem mode |= REWRITE_EXEC_ONCE;
1902de962bdSlukem break;
1912de962bdSlukem
1922de962bdSlukem /*
1932de962bdSlukem * Special action flags
1942de962bdSlukem */
1952de962bdSlukem case REWRITE_FLAG_STOP: /* '@' */
1962de962bdSlukem /*
1972de962bdSlukem * Bail out after applying rule
1982de962bdSlukem */
1992de962bdSlukem action = calloc( sizeof( struct rewrite_action ), 1 );
2002de962bdSlukem if ( action == NULL ) {
2012de962bdSlukem goto fail;
2022de962bdSlukem }
2032de962bdSlukem
2042de962bdSlukem action->la_type = REWRITE_ACTION_STOP;
2052de962bdSlukem break;
2062de962bdSlukem
2072de962bdSlukem case REWRITE_FLAG_UNWILLING: /* '#' */
2082de962bdSlukem /*
2092de962bdSlukem * Matching objs will be marked as gone!
2102de962bdSlukem */
2112de962bdSlukem action = calloc( sizeof( struct rewrite_action ), 1 );
2122de962bdSlukem if ( action == NULL ) {
2132de962bdSlukem goto fail;
2142de962bdSlukem }
2152de962bdSlukem
2162de962bdSlukem mode &= ~REWRITE_RECURSE;
2172de962bdSlukem mode |= REWRITE_EXEC_ONCE;
2182de962bdSlukem action->la_type = REWRITE_ACTION_UNWILLING;
2192de962bdSlukem break;
2202de962bdSlukem
2212de962bdSlukem case REWRITE_FLAG_GOTO: /* 'G' */
2222de962bdSlukem /*
2232de962bdSlukem * After applying rule, jump N rules
2242de962bdSlukem */
2252de962bdSlukem
2262de962bdSlukem case REWRITE_FLAG_USER: { /* 'U' */
2272de962bdSlukem /*
2282de962bdSlukem * After applying rule, return user-defined
2292de962bdSlukem * error code
2302de962bdSlukem */
2312de962bdSlukem char *next = NULL;
2322de962bdSlukem int *d;
2332de962bdSlukem
2342de962bdSlukem if ( p[ 1 ] != '{' ) {
2352de962bdSlukem goto fail;
2362de962bdSlukem }
2372de962bdSlukem
2382de962bdSlukem d = malloc( sizeof( int ) );
2392de962bdSlukem if ( d == NULL ) {
2402de962bdSlukem goto fail;
2412de962bdSlukem }
2422de962bdSlukem
2432de962bdSlukem d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
2442de962bdSlukem if ( next == &p[ 2 ] || next[0] != '}' ) {
2452de962bdSlukem free( d );
2462de962bdSlukem goto fail;
2472de962bdSlukem }
2482de962bdSlukem
2492de962bdSlukem action = calloc( sizeof( struct rewrite_action ), 1 );
2502de962bdSlukem if ( action == NULL ) {
2512de962bdSlukem free( d );
2522de962bdSlukem goto fail;
2532de962bdSlukem }
2542de962bdSlukem switch ( p[ 0 ] ) {
2552de962bdSlukem case REWRITE_FLAG_GOTO:
2562de962bdSlukem action->la_type = REWRITE_ACTION_GOTO;
2572de962bdSlukem break;
2582de962bdSlukem
2592de962bdSlukem case REWRITE_FLAG_USER:
2602de962bdSlukem action->la_type = REWRITE_ACTION_USER;
2612de962bdSlukem break;
2622de962bdSlukem
2632de962bdSlukem default:
2642de962bdSlukem assert(0);
2652de962bdSlukem }
2662de962bdSlukem
2672de962bdSlukem action->la_args = (void *)d;
2682de962bdSlukem
2692de962bdSlukem p = next; /* p is incremented by the for ... */
2702de962bdSlukem
2712de962bdSlukem break;
2722de962bdSlukem }
2732de962bdSlukem
2742de962bdSlukem case REWRITE_FLAG_MAX_PASSES: { /* 'U' */
2752de962bdSlukem /*
2762de962bdSlukem * Set the number of max passes per rule
2772de962bdSlukem */
2782de962bdSlukem char *next = NULL;
2792de962bdSlukem
2802de962bdSlukem if ( p[ 1 ] != '{' ) {
2812de962bdSlukem goto fail;
2822de962bdSlukem }
2832de962bdSlukem
2842de962bdSlukem max_passes = strtol( &p[ 2 ], &next, 0 );
2852de962bdSlukem if ( next == &p[ 2 ] || next[0] != '}' ) {
2862de962bdSlukem goto fail;
2872de962bdSlukem }
2882de962bdSlukem
2892de962bdSlukem if ( max_passes < 1 ) {
2902de962bdSlukem /* FIXME: nonsense ... */
2912de962bdSlukem max_passes = 1;
2922de962bdSlukem }
2932de962bdSlukem
2942de962bdSlukem p = next; /* p is incremented by the for ... */
2952de962bdSlukem
2962de962bdSlukem break;
2972de962bdSlukem }
2982de962bdSlukem
2992de962bdSlukem case REWRITE_FLAG_IGNORE_ERR: /* 'I' */
3002de962bdSlukem /*
3012de962bdSlukem * Ignore errors!
3022de962bdSlukem */
3032de962bdSlukem action = calloc( sizeof( struct rewrite_action ), 1 );
3042de962bdSlukem if ( action == NULL ) {
3052de962bdSlukem goto fail;
3062de962bdSlukem }
3072de962bdSlukem
3082de962bdSlukem action->la_type = REWRITE_ACTION_IGNORE_ERR;
3092de962bdSlukem break;
3102de962bdSlukem
3112de962bdSlukem /*
3122de962bdSlukem * Other flags ...
3132de962bdSlukem */
3142de962bdSlukem default:
3152de962bdSlukem /*
3162de962bdSlukem * Unimplemented feature (complain only)
3172de962bdSlukem */
3182de962bdSlukem break;
3192de962bdSlukem }
3202de962bdSlukem
3212de962bdSlukem /*
3222de962bdSlukem * Stupid way to append to a list ...
3232de962bdSlukem */
3242de962bdSlukem if ( action != NULL ) {
3252de962bdSlukem append_action( &first_action, action );
3262de962bdSlukem action = NULL;
3272de962bdSlukem }
3282de962bdSlukem }
3292de962bdSlukem
3302de962bdSlukem /*
3312de962bdSlukem * Finally, rule allocation
3322de962bdSlukem */
3332de962bdSlukem rule = calloc( sizeof( struct rewrite_rule ), 1 );
3342de962bdSlukem if ( rule == NULL ) {
3352de962bdSlukem goto fail;
3362de962bdSlukem }
3372de962bdSlukem
3382de962bdSlukem /*
3392de962bdSlukem * REGEX compilation (luckily I don't need to take care of this ...)
3402de962bdSlukem */
3412de962bdSlukem if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
3422de962bdSlukem goto fail;
3432de962bdSlukem }
3442de962bdSlukem
3452de962bdSlukem /*
3462de962bdSlukem * Just to remember them ...
3472de962bdSlukem */
3482de962bdSlukem rule->lr_pattern = strdup( pattern );
3492de962bdSlukem rule->lr_subststring = strdup( result );
3502de962bdSlukem rule->lr_flagstring = strdup( flagstring );
3514e6df137Slukem if ( rule->lr_pattern == NULL
3524e6df137Slukem || rule->lr_subststring == NULL
3534e6df137Slukem || rule->lr_flagstring == NULL )
3544e6df137Slukem {
3554e6df137Slukem goto fail;
3564e6df137Slukem }
3572de962bdSlukem
3582de962bdSlukem /*
3592de962bdSlukem * Load compiled data into rule
3602de962bdSlukem */
3612de962bdSlukem rule->lr_subst = subst;
3622de962bdSlukem
3632de962bdSlukem /*
3642de962bdSlukem * Set various parameters
3652de962bdSlukem */
3662de962bdSlukem rule->lr_flags = flags; /* don't really need any longer ... */
3672de962bdSlukem rule->lr_mode = mode;
3682de962bdSlukem rule->lr_max_passes = max_passes;
3692de962bdSlukem rule->lr_action = first_action;
3702de962bdSlukem
3712de962bdSlukem /*
3722de962bdSlukem * Append rule at the end of the rewrite context
3732de962bdSlukem */
3742de962bdSlukem append_rule( context, rule );
3752de962bdSlukem
3762de962bdSlukem return REWRITE_SUCCESS;
3772de962bdSlukem
3782de962bdSlukem fail:
3794e6df137Slukem if ( rule ) {
3804e6df137Slukem if ( rule->lr_pattern ) free( rule->lr_pattern );
3814e6df137Slukem if ( rule->lr_subststring ) free( rule->lr_subststring );
3824e6df137Slukem if ( rule->lr_flagstring ) free( rule->lr_flagstring );
3834e6df137Slukem free( rule );
3844e6df137Slukem }
3852de962bdSlukem destroy_actions( first_action );
3862de962bdSlukem free( subst );
3872de962bdSlukem return REWRITE_ERR;
3882de962bdSlukem }
3892de962bdSlukem
3902de962bdSlukem /*
3912de962bdSlukem * Rewrites string according to rule; may return:
3922de962bdSlukem * OK: fine; if *result != NULL rule matched and rewrite succeeded.
3932de962bdSlukem * STOP: fine, rule matched; stop processing following rules
3942de962bdSlukem * UNWILL: rule matched; force 'unwilling to perform'
3952de962bdSlukem */
3962de962bdSlukem int
rewrite_rule_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_rule * rule,const char * arg,char ** result)3972de962bdSlukem rewrite_rule_apply(
3982de962bdSlukem struct rewrite_info *info,
3992de962bdSlukem struct rewrite_op *op,
4002de962bdSlukem struct rewrite_rule *rule,
4012de962bdSlukem const char *arg,
4022de962bdSlukem char **result
4032de962bdSlukem )
4042de962bdSlukem {
4052de962bdSlukem size_t nmatch = REWRITE_MAX_MATCH;
4062de962bdSlukem regmatch_t match[ REWRITE_MAX_MATCH ];
4072de962bdSlukem
4082de962bdSlukem int rc = REWRITE_SUCCESS;
4092de962bdSlukem
4102de962bdSlukem char *string;
4112de962bdSlukem int strcnt = 0;
4122de962bdSlukem struct berval val = { 0, NULL };
4132de962bdSlukem
4142de962bdSlukem assert( info != NULL );
4152de962bdSlukem assert( op != NULL );
4162de962bdSlukem assert( rule != NULL );
4172de962bdSlukem assert( arg != NULL );
4182de962bdSlukem assert( result != NULL );
4192de962bdSlukem
4202de962bdSlukem *result = NULL;
4212de962bdSlukem
4222de962bdSlukem string = (char *)arg;
4232de962bdSlukem
4242de962bdSlukem /*
4252de962bdSlukem * In case recursive match is required (default)
4262de962bdSlukem */
4272de962bdSlukem recurse:;
4282de962bdSlukem
4292de962bdSlukem Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
4302de962bdSlukem " rule='%s' string='%s' [%d pass(es)]\n",
4312de962bdSlukem rule->lr_pattern, string, strcnt + 1 );
4322de962bdSlukem
4332de962bdSlukem op->lo_num_passes++;
4342de962bdSlukem
4352de962bdSlukem rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
4362de962bdSlukem if ( rc != 0 ) {
4372de962bdSlukem if ( *result == NULL && string != arg ) {
4382de962bdSlukem free( string );
4392de962bdSlukem }
4402de962bdSlukem
4412de962bdSlukem /*
4422de962bdSlukem * No match is OK; *result = NULL means no match
4432de962bdSlukem */
4442de962bdSlukem return REWRITE_REGEXEC_OK;
4452de962bdSlukem }
4462de962bdSlukem
4472de962bdSlukem rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
4482de962bdSlukem match, &val );
4492de962bdSlukem
4502de962bdSlukem *result = val.bv_val;
4512de962bdSlukem val.bv_val = NULL;
4522de962bdSlukem if ( string != arg ) {
4532de962bdSlukem free( string );
4542de962bdSlukem string = NULL;
4552de962bdSlukem }
4562de962bdSlukem
4572de962bdSlukem if ( rc != REWRITE_REGEXEC_OK ) {
4582de962bdSlukem return rc;
4592de962bdSlukem }
4602de962bdSlukem
4612de962bdSlukem if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
4622de962bdSlukem && op->lo_num_passes < info->li_max_passes
4632de962bdSlukem && ++strcnt < rule->lr_max_passes ) {
4642de962bdSlukem string = *result;
4652de962bdSlukem
4662de962bdSlukem goto recurse;
4672de962bdSlukem }
4682de962bdSlukem
4692de962bdSlukem return REWRITE_REGEXEC_OK;
4702de962bdSlukem }
4712de962bdSlukem
4722de962bdSlukem int
rewrite_rule_destroy(struct rewrite_rule ** prule)4732de962bdSlukem rewrite_rule_destroy(
4742de962bdSlukem struct rewrite_rule **prule
4752de962bdSlukem )
4762de962bdSlukem {
4772de962bdSlukem struct rewrite_rule *rule;
4782de962bdSlukem
4792de962bdSlukem assert( prule != NULL );
4802de962bdSlukem assert( *prule != NULL );
4812de962bdSlukem
4822de962bdSlukem rule = *prule;
4832de962bdSlukem
4842de962bdSlukem if ( rule->lr_pattern ) {
4852de962bdSlukem free( rule->lr_pattern );
4862de962bdSlukem rule->lr_pattern = NULL;
4872de962bdSlukem }
4882de962bdSlukem
4892de962bdSlukem if ( rule->lr_subststring ) {
4902de962bdSlukem free( rule->lr_subststring );
4912de962bdSlukem rule->lr_subststring = NULL;
4922de962bdSlukem }
4932de962bdSlukem
4942de962bdSlukem if ( rule->lr_flagstring ) {
4952de962bdSlukem free( rule->lr_flagstring );
4962de962bdSlukem rule->lr_flagstring = NULL;
4972de962bdSlukem }
4982de962bdSlukem
4992de962bdSlukem if ( rule->lr_subst ) {
5002de962bdSlukem rewrite_subst_destroy( &rule->lr_subst );
5012de962bdSlukem }
5022de962bdSlukem
5032de962bdSlukem regfree( &rule->lr_regex );
5042de962bdSlukem
5052de962bdSlukem destroy_actions( rule->lr_action );
5062de962bdSlukem
5072de962bdSlukem free( rule );
5082de962bdSlukem *prule = NULL;
5092de962bdSlukem
5102de962bdSlukem return 0;
5112de962bdSlukem }
5122de962bdSlukem
513