Home | History | Annotate | Line # | Download | only in librewrite
      1 /*	$NetBSD: context.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  * Compares two struct rewrite_context based on the name;
     28  * used by avl stuff
     29  */
     30 static int
     31 rewrite_context_cmp(
     32 		const void *c1,
     33 		const void *c2
     34 )
     35 {
     36 	const struct rewrite_context *lc1, *lc2;
     37 
     38 	lc1 = (const struct rewrite_context *)c1;
     39 	lc2 = (const struct rewrite_context *)c2;
     40 
     41 	assert( c1 != NULL );
     42 	assert( c2 != NULL );
     43 	assert( lc1->lc_name != NULL );
     44 	assert( lc2->lc_name != NULL );
     45 
     46 	return strcasecmp( lc1->lc_name, lc2->lc_name );
     47 }
     48 
     49 /*
     50  * Returns -1 in case a duplicate struct rewrite_context
     51  * has been inserted; used by avl stuff
     52  */
     53 static int
     54 rewrite_context_dup(
     55 		void *c1,
     56 		void *c2
     57 		)
     58 {
     59 	struct rewrite_context *lc1, *lc2;
     60 
     61 	lc1 = (struct rewrite_context *)c1;
     62 	lc2 = (struct rewrite_context *)c2;
     63 
     64 	assert( c1 != NULL );
     65 	assert( c2 != NULL );
     66 	assert( lc1->lc_name != NULL );
     67 	assert( lc2->lc_name != NULL );
     68 
     69 	return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 );
     70 }
     71 
     72 /*
     73  * Finds the context named rewriteContext in the context tree
     74  */
     75 struct rewrite_context *
     76 rewrite_context_find(
     77 		struct rewrite_info *info,
     78 		const char *rewriteContext
     79 )
     80 {
     81 	struct rewrite_context *context, c;
     82 
     83 	assert( info != NULL );
     84 	assert( rewriteContext != NULL );
     85 
     86 	/*
     87 	 * Fetches the required rewrite context
     88 	 */
     89 	c.lc_name = (char *)rewriteContext;
     90 	context = (struct rewrite_context *)ldap_avl_find( info->li_context,
     91 			(caddr_t)&c, rewrite_context_cmp );
     92 	if ( context == NULL ) {
     93 		return NULL;
     94 	}
     95 
     96 	/*
     97 	 * De-aliases the context if required
     98 	 */
     99 	if ( context->lc_alias ) {
    100 		return context->lc_alias;
    101 	}
    102 
    103 	return context;
    104 }
    105 
    106 /*
    107  * Creates a new context called rewriteContext and stores in into the tree
    108  */
    109 struct rewrite_context *
    110 rewrite_context_create(
    111 		struct rewrite_info *info,
    112 		const char *rewriteContext
    113 )
    114 {
    115 	struct rewrite_context *context;
    116 	int rc;
    117 
    118 	assert( info != NULL );
    119 	assert( rewriteContext != NULL );
    120 
    121 	context = calloc( sizeof( struct rewrite_context ), 1 );
    122 	if ( context == NULL ) {
    123 		return NULL;
    124 	}
    125 
    126 	/*
    127 	 * Context name
    128 	 */
    129 	context->lc_name = strdup( rewriteContext );
    130 	if ( context->lc_name == NULL ) {
    131 		free( context );
    132 		return NULL;
    133 	}
    134 
    135 	/*
    136 	 * The first, empty rule
    137 	 */
    138 	context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 );
    139 	if ( context->lc_rule == NULL ) {
    140 		free( context->lc_name );
    141 		free( context );
    142 		return NULL;
    143 	}
    144 	memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
    145 
    146 	/*
    147 	 * Add context to tree
    148 	 */
    149 	rc = ldap_avl_insert( &info->li_context, (caddr_t)context,
    150 			rewrite_context_cmp, rewrite_context_dup );
    151 	if ( rc == -1 ) {
    152 		free( context->lc_rule );
    153 		free( context->lc_name );
    154 		free( context );
    155 		return NULL;
    156 	}
    157 
    158 	return context;
    159 }
    160 
    161 /*
    162  * Finds the next rule according to a goto action statement,
    163  * or null in case of error.
    164  * Helper for rewrite_context_apply.
    165  */
    166 static struct rewrite_rule *
    167 rewrite_action_goto(
    168 		struct rewrite_action *action,
    169 		struct rewrite_rule *rule
    170 )
    171 {
    172 	int n;
    173 
    174 	assert( action != NULL );
    175 	assert( action->la_args != NULL );
    176 	assert( rule != NULL );
    177 
    178 	n = ((int *)action->la_args)[ 0 ];
    179 
    180 	if ( n > 0 ) {
    181 		for ( ; n > 1 && rule != NULL ; n-- ) {
    182 			rule = rule->lr_next;
    183 		}
    184 	} else if ( n <= 0 ) {
    185 		for ( ; n < 1 && rule != NULL ; n++ ) {
    186 			rule = rule->lr_prev;
    187 		}
    188 	}
    189 
    190 	return rule;
    191 }
    192 
    193 /*
    194  * Rewrites string according to context; may return:
    195  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
    196  *      STOP:   fine, rule matched; stop processing following rules
    197  *      UNWILL: rule matched; force 'unwilling to perform'
    198  */
    199 int
    200 rewrite_context_apply(
    201 		struct rewrite_info *info,
    202 		struct rewrite_op *op,
    203 		struct rewrite_context *context,
    204 		const char *string,
    205 		char **result
    206 )
    207 {
    208 	struct rewrite_rule *rule;
    209 	char *s, *res = NULL;
    210 	int return_code = REWRITE_REGEXEC_OK;
    211 
    212 	assert( info != NULL );
    213 	assert( op != NULL );
    214 	assert( context != NULL );
    215 	assert( context->lc_rule != NULL );
    216 	assert( string != NULL );
    217 	assert( result != NULL );
    218 
    219 	op->lo_depth++;
    220 
    221 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
    222 			" [depth=%d] string='%s'\n",
    223 			op->lo_depth, string );
    224 	assert( op->lo_depth > 0 );
    225 
    226 	s = (char *)string;
    227 
    228 	for ( rule = context->lc_rule->lr_next;
    229 			rule != NULL && op->lo_num_passes < info->li_max_passes;
    230 			rule = rule->lr_next, op->lo_num_passes++ ) {
    231 		int rc;
    232 
    233 		/*
    234 		 * Apply a single rule
    235 		 */
    236 		rc = rewrite_rule_apply( info, op, rule, s, &res );
    237 
    238 		/*
    239 		 * A rule may return:
    240 		 * 	OK 		with result != NULL if matched
    241 		 * 	ERR		if anything was wrong
    242 		 * 	UNWILLING	if the server should drop the request
    243 		 * the latter case in honored immediately;
    244 		 * the other two may require some special actions to take
    245 		 * place.
    246 		 */
    247 		switch ( rc ) {
    248 
    249 		case REWRITE_REGEXEC_ERR:
    250 			Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
    251 					" error ...\n" );
    252 
    253 			/*
    254 			 * Checks for special actions to be taken
    255 			 * in case of error ...
    256 			 */
    257 			if ( rule->lr_action != NULL ) {
    258 				struct rewrite_action *action;
    259 				int do_continue = 0;
    260 
    261 				for ( action = rule->lr_action;
    262 						action != NULL;
    263 						action = action->la_next ) {
    264 					switch ( action->la_type ) {
    265 
    266 					/*
    267 					 * This action takes precedence
    268 					 * over the others in case of failure
    269 					 */
    270 					case REWRITE_ACTION_IGNORE_ERR:
    271 						Debug( LDAP_DEBUG_ANY,
    272 					"==> rewrite_context_apply"
    273 					" ignoring error ...\n" );
    274 						do_continue = 1;
    275 						break;
    276 
    277 					/*
    278 					 * Goto is honored only if it comes
    279 					 * after ignore error
    280 					 */
    281 					case REWRITE_ACTION_GOTO:
    282 						if ( do_continue ) {
    283 							rule = rewrite_action_goto( action, rule );
    284 							if ( rule == NULL ) {
    285 								return_code = REWRITE_REGEXEC_ERR;
    286 								goto rc_end_of_context;
    287 							}
    288 						}
    289 						break;
    290 
    291 					/*
    292 					 * Other actions are ignored
    293 					 */
    294 					default:
    295 						break;
    296 					}
    297 				}
    298 
    299 				if ( do_continue ) {
    300 					if ( rule->lr_next == NULL ) {
    301 						res = s;
    302 					}
    303 					goto rc_continue;
    304 				}
    305 			}
    306 
    307 			/*
    308 			 * Default behavior is to bail out ...
    309 			 */
    310 			return_code = REWRITE_REGEXEC_ERR;
    311 			goto rc_end_of_context;
    312 
    313 		/*
    314 		 * OK means there were no errors or special return codes;
    315 		 * if res is defined, it means the rule matched and we
    316 		 * got a successful rewriting
    317 		 */
    318 		case REWRITE_REGEXEC_OK:
    319 
    320 			/*
    321 			 * It matched! Check for actions ...
    322 			 */
    323 			if ( res != NULL ) {
    324 				struct rewrite_action *action;
    325 
    326 				if ( s != string && s != res ) {
    327 					free( s );
    328 				}
    329 				s = res;
    330 
    331 				for ( action = rule->lr_action;
    332 						action != NULL;
    333 						action = action->la_next ) {
    334 
    335 					switch ( action->la_type ) {
    336 
    337 					/*
    338 					 * This ends the rewrite context
    339 					 * successfully
    340 					 */
    341 					case REWRITE_ACTION_STOP:
    342 						goto rc_end_of_context;
    343 
    344 					/*
    345 					 * This instructs the server to return
    346 					 * an `unwilling to perform' error
    347 					 * message
    348 					 */
    349 					case REWRITE_ACTION_UNWILLING:
    350 						return_code = REWRITE_REGEXEC_UNWILLING;
    351 						goto rc_end_of_context;
    352 
    353 					/*
    354 					 * This causes the processing to
    355 					 * jump n rules back and forth
    356 					 */
    357 					case REWRITE_ACTION_GOTO:
    358 						rule = rewrite_action_goto( action, rule );
    359 						if ( rule == NULL ) {
    360 							return_code = REWRITE_REGEXEC_ERR;
    361 							goto rc_end_of_context;
    362 						}
    363 						break;
    364 
    365 					/*
    366 					 * This ends the rewrite context
    367 					 * and returns a user-defined
    368 					 * error code
    369 					 */
    370 					case REWRITE_ACTION_USER:
    371 						return_code = ((int *)action->la_args)[ 0 ];
    372 						goto rc_end_of_context;
    373 
    374 					default:
    375 						/* ... */
    376 						break;
    377 					}
    378 				}
    379 
    380 			/*
    381 			 * If result was OK and string didn't match,
    382 			 * in case of last rule we need to set the
    383 			 * result back to the string
    384 			 */
    385 			} else if ( rule->lr_next == NULL ) {
    386 				res = s;
    387 			}
    388 
    389 			break;
    390 
    391 		/*
    392 		 * A STOP has propagated ...
    393 		 */
    394 		case REWRITE_REGEXEC_STOP:
    395 			goto rc_end_of_context;
    396 
    397 		/*
    398 		 * This will instruct the server to return
    399 		 * an `unwilling to perform' error message
    400 		 */
    401 		case REWRITE_REGEXEC_UNWILLING:
    402 			return_code = REWRITE_REGEXEC_UNWILLING;
    403 			goto rc_end_of_context;
    404 
    405 		/*
    406 		 * A user-defined error code has propagated ...
    407 		 */
    408 		default:
    409 			assert( rc >= REWRITE_REGEXEC_USER );
    410 			goto rc_end_of_context;
    411 
    412 		}
    413 
    414 rc_continue:;	/* sent here by actions that require to continue */
    415 
    416 	}
    417 
    418 rc_end_of_context:;
    419 	*result = res;
    420 
    421 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
    422 			" [depth=%d] res={%d,'%s'}\n",
    423 			op->lo_depth, return_code, ( res ? res : "NULL" ) );
    424 
    425 	assert( op->lo_depth > 0 );
    426 	op->lo_depth--;
    427 
    428 	return return_code;
    429 }
    430 
    431 void
    432 rewrite_context_free(
    433 		void *tmp
    434 )
    435 {
    436 	struct rewrite_context *context = (struct rewrite_context *)tmp;
    437 
    438 	assert( tmp != NULL );
    439 
    440 	rewrite_context_destroy( &context );
    441 }
    442 
    443 int
    444 rewrite_context_destroy(
    445 		struct rewrite_context **pcontext
    446 )
    447 {
    448 	struct rewrite_context *context;
    449 	struct rewrite_rule *r;
    450 
    451 	assert( pcontext != NULL );
    452 	assert( *pcontext != NULL );
    453 
    454 	context = *pcontext;
    455 
    456 	assert( context->lc_rule != NULL );
    457 
    458 	for ( r = context->lc_rule->lr_next; r; ) {
    459 		struct rewrite_rule *cr = r;
    460 
    461 		r = r->lr_next;
    462 		rewrite_rule_destroy( &cr );
    463 	}
    464 
    465 	free( context->lc_rule );
    466 	context->lc_rule = NULL;
    467 
    468 	assert( context->lc_name != NULL );
    469 	free( context->lc_name );
    470 	context->lc_name = NULL;
    471 
    472 	free( context );
    473 	*pcontext = NULL;
    474 
    475 	return 0;
    476 }
    477