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