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