/****************************************************************************** * * Copyright (C) 2000 Pierangelo Masarati, * All rights reserved. * * Permission is granted to anyone to use this software for any purpose * on any computer system, and to alter it and redistribute it, subject * to the following restrictions: * * 1. The author is not responsible for the consequences of use of this * software, no matter how awful, even if they arise from flaws in it. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Since few users ever read sources, * credits should appear in the documentation. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. Since few users * ever read sources, credits should appear in the documentation. * * 4. This notice may not be removed or altered. * ******************************************************************************/ #include #include "rewrite-int.h" /* * Compares two struct rewrite_context based on the name; * used by avl stuff */ static int rewrite_context_cmp( const void *c1, const void *c2 ) { const struct rewrite_context *lc1, *lc2; lc1 = (const struct rewrite_context *)c1; lc2 = (const struct rewrite_context *)c2; assert( c1 != NULL ); assert( c2 != NULL ); assert( lc1->lc_name != NULL ); assert( lc2->lc_name != NULL ); return strcasecmp( lc1->lc_name, lc2->lc_name ); } /* * Returns -1 in case a duplicate struct rewrite_context * has been inserted; used by avl stuff */ static int rewrite_context_dup( void *c1, void *c2 ) { struct rewrite_context *lc1, *lc2; lc1 = (struct rewrite_context *)c1; lc2 = (struct rewrite_context *)c2; assert( c1 != NULL ); assert( c2 != NULL ); assert( lc1->lc_name != NULL ); assert( lc2->lc_name != NULL ); return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 ); } /* * Finds the context named rewriteContext in the context tree */ struct rewrite_context * rewrite_context_find( struct rewrite_info *info, const char *rewriteContext ) { struct rewrite_context *context, c; assert( info != NULL ); assert( rewriteContext != NULL ); /* * Fetches the required rewrite context */ c.lc_name = (char *)rewriteContext; context = (struct rewrite_context *)avl_find( info->li_context, (caddr_t)&c, rewrite_context_cmp ); if ( context == NULL ) { return NULL; } /* * De-aliases the context if required */ if ( context->lc_alias ) { return context->lc_alias; } return context; } /* * Creates a new context called rewriteContext and stores in into the tree */ struct rewrite_context * rewrite_context_create( struct rewrite_info *info, const char *rewriteContext ) { struct rewrite_context *context; int rc; assert( info != NULL ); assert( rewriteContext != NULL ); context = calloc( sizeof( struct rewrite_context ), 1 ); if ( context == NULL ) { return NULL; } /* * Context name */ context->lc_name = strdup( rewriteContext ); if ( context->lc_name == NULL ) { free( context ); return NULL; } /* * The first, empty rule */ context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 ); if ( context->lc_rule == NULL ) { free( context->lc_name ); free( context ); return NULL; } /* * Add context to tree */ rc = avl_insert( &info->li_context, (caddr_t)context, rewrite_context_cmp, rewrite_context_dup ); if ( rc == -1 ) { free( context->lc_rule ); free( context->lc_name ); free( context ); return NULL; } return context; } /* * Finds the next rule according to a goto action statement, * or null in case of error. * Helper for rewrite_context_apply. */ static struct rewrite_rule * rewrite_action_goto( struct rewrite_action *action, struct rewrite_rule *rule ) { int n; assert( action != NULL ); assert( action->la_args != NULL ); assert( rule != NULL ); n = ((int *)action->la_args)[ 0 ]; if ( n > 0 ) { for ( ; n > 1 && rule != NULL ; n-- ) { rule = rule->lr_next; } } else if ( n <= 0 ) { for ( ; n < 1 && rule != NULL ; n++ ) { rule = rule->lr_prev; } } return rule; } /* * Rewrites string according to context; may return: * OK: fine; if *result != NULL rule matched and rewrite succeeded. * STOP: fine, rule matched; stop processing following rules * UNWILL: rule matched; force 'unwilling to perform' */ int rewrite_context_apply( struct rewrite_info *info, struct rewrite_op *op, struct rewrite_context *context, const char *string, char **result ) { struct rewrite_rule *rule; char *s, *res = NULL; int return_code = REWRITE_REGEXEC_OK; assert( info != NULL ); assert( op != NULL ); assert( context != NULL ); assert( context->lc_rule != NULL ); assert( string != NULL ); assert( result != NULL ); op->lo_depth++; assert( op->lo_depth > 0 ); Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" " [depth=%d] string='%s'\n%s", op->lo_depth, string, "" ); s = strdup( string ); for ( rule = context->lc_rule->lr_next; rule != NULL && op->lo_num_passes < info->li_max_passes; rule = rule->lr_next, op->lo_num_passes++ ) { int rc; /* * Apply a single rule */ rc = rewrite_rule_apply( info, op, rule, s, &res ); /* * A rule may return: * OK with result != NULL if matched * ERR if anything was wrong * UNWILLING if the server should drop the request * the latter case in honored immediately; * the other two may require some special actions to take * place. */ switch ( rc ) { case REWRITE_REGEXEC_ERR: Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply" " error ...\n%s%s%s", "", "", ""); /* * Checks for special actions to be taken * in case of error ... */ if ( rule->lr_action != NULL ) { struct rewrite_action *action; int do_continue = 0; for ( action = rule->lr_action; action != NULL; action = action->la_next ) { switch ( action->la_type ) { /* * This action takes precedence * over the others in case of failure */ case REWRITE_ACTION_IGNORE_ERR: Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply" " ignoring error ...\n%s%s%s", "", "", "" ); do_continue = 1; break; /* * Goto is honored only if it comes * after ignore error */ case REWRITE_ACTION_GOTO: if ( do_continue ) { rule = rewrite_action_goto( action, rule ); if ( rule == NULL ) { return_code = REWRITE_REGEXEC_ERR; goto rc_end_of_context; } } break; /* * Other actions are ignored */ default: break; } } if ( do_continue ) { if ( rule->lr_next == NULL ) { res = s; } goto rc_continue; } } /* * Default behavior is to bail out ... */ return_code = REWRITE_REGEXEC_ERR; goto rc_end_of_context; /* * OK means there were no errors or special return codes; * if res is defined, it means the rule matched and we * got a sucessful rewriting */ case REWRITE_REGEXEC_OK: /* * It matched! Check for actions ... */ if ( res != NULL ) { struct rewrite_action *action; free( s ); s = res; for ( action = rule->lr_action; action != NULL; action = action->la_next ) { switch ( action->la_type ) { /* * This ends the rewrite context * successfully */ case REWRITE_ACTION_STOP: goto rc_end_of_context; /* * This instructs the server to return * an `unwilling to perform' error * message */ case REWRITE_ACTION_UNWILLING: return_code = REWRITE_REGEXEC_UNWILLING; goto rc_end_of_context; /* * This causes the processing to * jump n rules back and forth */ case REWRITE_ACTION_GOTO: rule = rewrite_action_goto( action, rule ); if ( rule == NULL ) { return_code = REWRITE_REGEXEC_ERR; goto rc_end_of_context; } break; default: /* ... */ break; } } /* * If result was OK and string didn't match, * in case of last rule we need to set the * result back to the string */ } else if ( rule->lr_next == NULL ) { res = s; } break; /* * A STOP has propagated ... */ case REWRITE_REGEXEC_STOP: goto rc_end_of_context; /* * This will instruct the server to return * an `unwilling to perform' error message */ case REWRITE_REGEXEC_UNWILLING: return_code = REWRITE_REGEXEC_UNWILLING; goto rc_end_of_context; } rc_continue:; /* sent here by actions that require to continue */ } rc_end_of_context:; *result = res; Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" " [depth=%d] res={%d,'%s'}\n", op->lo_depth, return_code, ( res ? res : "NULL" ) ); assert( op->lo_depth > 0 ); op->lo_depth--; return return_code; }