openldap/libraries/librewrite/context.c
2001-06-23 16:06:34 +00:00

416 lines
8.9 KiB
C

/******************************************************************************
*
* Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
* 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 <portable.h>
#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
)
{
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 );
}
/*
* 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;
}