mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
511 lines
9.4 KiB
C
511 lines
9.4 KiB
C
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 2000-2020 The OpenLDAP Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in the file LICENSE in the
|
|
* top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
/* ACKNOWLEDGEMENT:
|
|
* This work was initially developed by Pierangelo Masarati for
|
|
* inclusion in OpenLDAP Software.
|
|
*/
|
|
|
|
#include <portable.h>
|
|
|
|
#include "rewrite-int.h"
|
|
|
|
/*
|
|
* Appends a rule to the double linked list of rules
|
|
* Helper for rewrite_rule_compile
|
|
*/
|
|
static int
|
|
append_rule(
|
|
struct rewrite_context *context,
|
|
struct rewrite_rule *rule
|
|
)
|
|
{
|
|
struct rewrite_rule *r;
|
|
|
|
assert( context != NULL );
|
|
assert( context->lc_rule != NULL );
|
|
assert( rule != NULL );
|
|
|
|
for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
|
|
r->lr_next = rule;
|
|
rule->lr_prev = r;
|
|
|
|
return REWRITE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Appends an action to the linked list of actions
|
|
* Helper for rewrite_rule_compile
|
|
*/
|
|
static int
|
|
append_action(
|
|
struct rewrite_action **pbase,
|
|
struct rewrite_action *action
|
|
)
|
|
{
|
|
struct rewrite_action **pa;
|
|
|
|
assert( pbase != NULL );
|
|
assert( action != NULL );
|
|
|
|
for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
|
|
*pa = action;
|
|
|
|
return REWRITE_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
destroy_action(
|
|
struct rewrite_action **paction
|
|
)
|
|
{
|
|
struct rewrite_action *action;
|
|
|
|
assert( paction != NULL );
|
|
assert( *paction != NULL );
|
|
|
|
action = *paction;
|
|
|
|
/* do something */
|
|
switch ( action->la_type ) {
|
|
case REWRITE_FLAG_GOTO:
|
|
case REWRITE_FLAG_USER: {
|
|
int *pi = (int *)action->la_args;
|
|
|
|
if ( pi ) {
|
|
free( pi );
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
free( action );
|
|
*paction = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
destroy_actions(
|
|
struct rewrite_action *paction
|
|
)
|
|
{
|
|
struct rewrite_action *next;
|
|
|
|
for (; paction; paction = next) {
|
|
next = paction->la_next;
|
|
destroy_action( &paction );
|
|
}
|
|
}
|
|
|
|
/*
|
|
*/
|
|
int
|
|
rewrite_rule_compile(
|
|
struct rewrite_info *info,
|
|
struct rewrite_context *context,
|
|
const char *pattern,
|
|
const char *result,
|
|
const char *flagstring
|
|
)
|
|
{
|
|
int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
|
|
int mode = REWRITE_RECURSE;
|
|
int max_passes;
|
|
|
|
struct rewrite_rule *rule = NULL;
|
|
struct rewrite_subst *subst = NULL;
|
|
struct rewrite_action *action = NULL, *first_action = NULL;
|
|
|
|
const char *p;
|
|
|
|
assert( info != NULL );
|
|
assert( context != NULL );
|
|
assert( pattern != NULL );
|
|
assert( result != NULL );
|
|
/*
|
|
* A null flagstring should be allowed
|
|
*/
|
|
|
|
max_passes = info->li_max_passes_per_rule;
|
|
|
|
/*
|
|
* Take care of substitution string
|
|
*/
|
|
subst = rewrite_subst_compile( info, result );
|
|
if ( subst == NULL ) {
|
|
return REWRITE_ERR;
|
|
}
|
|
|
|
/*
|
|
* Take care of flags
|
|
*/
|
|
for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
|
|
switch( p[ 0 ] ) {
|
|
|
|
/*
|
|
* REGEX flags
|
|
*/
|
|
case REWRITE_FLAG_HONORCASE: /* 'C' */
|
|
/*
|
|
* Honor case (default is case insensitive)
|
|
*/
|
|
flags &= ~REWRITE_REGEX_ICASE;
|
|
break;
|
|
|
|
case REWRITE_FLAG_BASICREGEX: /* 'R' */
|
|
/*
|
|
* Use POSIX Basic Regular Expression syntax
|
|
* instead of POSIX Extended Regular Expression
|
|
* syntax (default)
|
|
*/
|
|
flags &= ~REWRITE_REGEX_EXTENDED;
|
|
break;
|
|
|
|
/*
|
|
* Execution mode flags
|
|
*/
|
|
case REWRITE_FLAG_EXECONCE: /* ':' */
|
|
/*
|
|
* Apply rule once only
|
|
*/
|
|
mode &= ~REWRITE_RECURSE;
|
|
mode |= REWRITE_EXEC_ONCE;
|
|
break;
|
|
|
|
/*
|
|
* Special action flags
|
|
*/
|
|
case REWRITE_FLAG_STOP: /* '@' */
|
|
/*
|
|
* Bail out after applying rule
|
|
*/
|
|
action = calloc( sizeof( struct rewrite_action ), 1 );
|
|
if ( action == NULL ) {
|
|
goto fail;
|
|
}
|
|
|
|
action->la_type = REWRITE_ACTION_STOP;
|
|
break;
|
|
|
|
case REWRITE_FLAG_UNWILLING: /* '#' */
|
|
/*
|
|
* Matching objs will be marked as gone!
|
|
*/
|
|
action = calloc( sizeof( struct rewrite_action ), 1 );
|
|
if ( action == NULL ) {
|
|
goto fail;
|
|
}
|
|
|
|
mode &= ~REWRITE_RECURSE;
|
|
mode |= REWRITE_EXEC_ONCE;
|
|
action->la_type = REWRITE_ACTION_UNWILLING;
|
|
break;
|
|
|
|
case REWRITE_FLAG_GOTO: /* 'G' */
|
|
/*
|
|
* After applying rule, jump N rules
|
|
*/
|
|
|
|
case REWRITE_FLAG_USER: { /* 'U' */
|
|
/*
|
|
* After applying rule, return user-defined
|
|
* error code
|
|
*/
|
|
char *next = NULL;
|
|
int *d;
|
|
|
|
if ( p[ 1 ] != '{' ) {
|
|
goto fail;
|
|
}
|
|
|
|
d = malloc( sizeof( int ) );
|
|
if ( d == NULL ) {
|
|
goto fail;
|
|
}
|
|
|
|
d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
|
|
if ( next == &p[ 2 ] || next[0] != '}' ) {
|
|
free( d );
|
|
goto fail;
|
|
}
|
|
|
|
action = calloc( sizeof( struct rewrite_action ), 1 );
|
|
if ( action == NULL ) {
|
|
free( d );
|
|
goto fail;
|
|
}
|
|
switch ( p[ 0 ] ) {
|
|
case REWRITE_FLAG_GOTO:
|
|
action->la_type = REWRITE_ACTION_GOTO;
|
|
break;
|
|
|
|
case REWRITE_FLAG_USER:
|
|
action->la_type = REWRITE_ACTION_USER;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
action->la_args = (void *)d;
|
|
|
|
p = next; /* p is incremented by the for ... */
|
|
|
|
break;
|
|
}
|
|
|
|
case REWRITE_FLAG_MAX_PASSES: { /* 'U' */
|
|
/*
|
|
* Set the number of max passes per rule
|
|
*/
|
|
char *next = NULL;
|
|
|
|
if ( p[ 1 ] != '{' ) {
|
|
goto fail;
|
|
}
|
|
|
|
max_passes = strtol( &p[ 2 ], &next, 0 );
|
|
if ( next == &p[ 2 ] || next[0] != '}' ) {
|
|
goto fail;
|
|
}
|
|
|
|
if ( max_passes < 1 ) {
|
|
/* FIXME: nonsense ... */
|
|
max_passes = 1;
|
|
}
|
|
|
|
p = next; /* p is incremented by the for ... */
|
|
|
|
break;
|
|
}
|
|
|
|
case REWRITE_FLAG_IGNORE_ERR: /* 'I' */
|
|
/*
|
|
* Ignore errors!
|
|
*/
|
|
action = calloc( sizeof( struct rewrite_action ), 1 );
|
|
if ( action == NULL ) {
|
|
goto fail;
|
|
}
|
|
|
|
action->la_type = REWRITE_ACTION_IGNORE_ERR;
|
|
break;
|
|
|
|
/*
|
|
* Other flags ...
|
|
*/
|
|
default:
|
|
/*
|
|
* Unimplemented feature (complain only)
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Stupid way to append to a list ...
|
|
*/
|
|
if ( action != NULL ) {
|
|
append_action( &first_action, action );
|
|
action = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finally, rule allocation
|
|
*/
|
|
rule = calloc( sizeof( struct rewrite_rule ), 1 );
|
|
if ( rule == NULL ) {
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* REGEX compilation (luckily I don't need to take care of this ...)
|
|
*/
|
|
if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Just to remember them ...
|
|
*/
|
|
rule->lr_pattern = strdup( pattern );
|
|
rule->lr_subststring = strdup( result );
|
|
rule->lr_flagstring = strdup( flagstring );
|
|
if ( rule->lr_pattern == NULL
|
|
|| rule->lr_subststring == NULL
|
|
|| rule->lr_flagstring == NULL )
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Load compiled data into rule
|
|
*/
|
|
rule->lr_subst = subst;
|
|
|
|
/*
|
|
* Set various parameters
|
|
*/
|
|
rule->lr_flags = flags; /* don't really need any longer ... */
|
|
rule->lr_mode = mode;
|
|
rule->lr_max_passes = max_passes;
|
|
rule->lr_action = first_action;
|
|
|
|
/*
|
|
* Append rule at the end of the rewrite context
|
|
*/
|
|
append_rule( context, rule );
|
|
|
|
return REWRITE_SUCCESS;
|
|
|
|
fail:
|
|
if ( rule ) {
|
|
if ( rule->lr_pattern ) free( rule->lr_pattern );
|
|
if ( rule->lr_subststring ) free( rule->lr_subststring );
|
|
if ( rule->lr_flagstring ) free( rule->lr_flagstring );
|
|
free( rule );
|
|
}
|
|
destroy_actions( first_action );
|
|
free( subst );
|
|
return REWRITE_ERR;
|
|
}
|
|
|
|
/*
|
|
* Rewrites string according to rule; 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_rule_apply(
|
|
struct rewrite_info *info,
|
|
struct rewrite_op *op,
|
|
struct rewrite_rule *rule,
|
|
const char *arg,
|
|
char **result
|
|
)
|
|
{
|
|
size_t nmatch = REWRITE_MAX_MATCH;
|
|
regmatch_t match[ REWRITE_MAX_MATCH ];
|
|
|
|
int rc = REWRITE_SUCCESS;
|
|
|
|
char *string;
|
|
int strcnt = 0;
|
|
struct berval val = { 0, NULL };
|
|
|
|
assert( info != NULL );
|
|
assert( op != NULL );
|
|
assert( rule != NULL );
|
|
assert( arg != NULL );
|
|
assert( result != NULL );
|
|
|
|
*result = NULL;
|
|
|
|
string = (char *)arg;
|
|
|
|
/*
|
|
* In case recursive match is required (default)
|
|
*/
|
|
recurse:;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
|
|
" rule='%s' string='%s' [%d pass(es)]\n",
|
|
rule->lr_pattern, string, strcnt + 1 );
|
|
|
|
op->lo_num_passes++;
|
|
|
|
rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
|
|
if ( rc != 0 ) {
|
|
if ( *result == NULL && string != arg ) {
|
|
free( string );
|
|
}
|
|
|
|
/*
|
|
* No match is OK; *result = NULL means no match
|
|
*/
|
|
return REWRITE_REGEXEC_OK;
|
|
}
|
|
|
|
rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
|
|
match, &val );
|
|
|
|
*result = val.bv_val;
|
|
val.bv_val = NULL;
|
|
if ( string != arg ) {
|
|
free( string );
|
|
string = NULL;
|
|
}
|
|
|
|
if ( rc != REWRITE_REGEXEC_OK ) {
|
|
return rc;
|
|
}
|
|
|
|
if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
|
|
&& op->lo_num_passes < info->li_max_passes
|
|
&& ++strcnt < rule->lr_max_passes ) {
|
|
string = *result;
|
|
|
|
goto recurse;
|
|
}
|
|
|
|
return REWRITE_REGEXEC_OK;
|
|
}
|
|
|
|
int
|
|
rewrite_rule_destroy(
|
|
struct rewrite_rule **prule
|
|
)
|
|
{
|
|
struct rewrite_rule *rule;
|
|
|
|
assert( prule != NULL );
|
|
assert( *prule != NULL );
|
|
|
|
rule = *prule;
|
|
|
|
if ( rule->lr_pattern ) {
|
|
free( rule->lr_pattern );
|
|
rule->lr_pattern = NULL;
|
|
}
|
|
|
|
if ( rule->lr_subststring ) {
|
|
free( rule->lr_subststring );
|
|
rule->lr_subststring = NULL;
|
|
}
|
|
|
|
if ( rule->lr_flagstring ) {
|
|
free( rule->lr_flagstring );
|
|
rule->lr_flagstring = NULL;
|
|
}
|
|
|
|
if ( rule->lr_subst ) {
|
|
rewrite_subst_destroy( &rule->lr_subst );
|
|
}
|
|
|
|
regfree( &rule->lr_regex );
|
|
|
|
destroy_actions( rule->lr_action );
|
|
|
|
free( rule );
|
|
*prule = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|