diff options
Diffstat (limited to 'libraries/librewrite/rule.c')
-rw-r--r-- | libraries/librewrite/rule.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c new file mode 100644 index 0000000..c9eb2b4 --- /dev/null +++ b/libraries/librewrite/rule.c @@ -0,0 +1,510 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 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; +} + |