summaryrefslogtreecommitdiffstats
path: root/libraries/librewrite/rule.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/librewrite/rule.c')
-rw-r--r--libraries/librewrite/rule.c510
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..b4fe405
--- /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-2022 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;
+}
+