summaryrefslogtreecommitdiffstats
path: root/libraries/librewrite/context.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/librewrite/context.c')
-rw-r--r--libraries/librewrite/context.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/libraries/librewrite/context.c b/libraries/librewrite/context.c
new file mode 100644
index 0000000..cc7854c
--- /dev/null
+++ b/libraries/librewrite/context.c
@@ -0,0 +1,474 @@
+/* $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"
+
+/*
+ * 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;
+ }
+ memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
+
+ /*
+ * 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++;
+
+ Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
+ " [depth=%d] string='%s'\n",
+ op->lo_depth, string, 0 );
+ assert( op->lo_depth > 0 );
+
+ s = (char *)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", 0, 0, 0);
+
+ /*
+ * 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", 0, 0, 0 );
+ 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;
+
+ if ( s != string && s != res ) {
+ 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;
+
+ /*
+ * This ends the rewrite context
+ * and returns a user-defined
+ * error code
+ */
+ case REWRITE_ACTION_USER:
+ return_code = ((int *)action->la_args)[ 0 ];
+ goto rc_end_of_context;
+
+ 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;
+
+ /*
+ * A user-defined error code has propagated ...
+ */
+ default:
+ assert( rc >= REWRITE_REGEXEC_USER );
+ 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;
+}
+
+void
+rewrite_context_free(
+ void *tmp
+)
+{
+ struct rewrite_context *context = (struct rewrite_context *)tmp;
+
+ assert( tmp != NULL );
+
+ rewrite_context_destroy( &context );
+}
+
+int
+rewrite_context_destroy(
+ struct rewrite_context **pcontext
+)
+{
+ struct rewrite_context *context;
+ struct rewrite_rule *r;
+
+ assert( pcontext != NULL );
+ assert( *pcontext != NULL );
+
+ context = *pcontext;
+
+ assert( context->lc_rule != NULL );
+
+ for ( r = context->lc_rule->lr_next; r; ) {
+ struct rewrite_rule *cr = r;
+
+ r = r->lr_next;
+ rewrite_rule_destroy( &cr );
+ }
+
+ free( context->lc_rule );
+ context->lc_rule = NULL;
+
+ assert( context->lc_name != NULL );
+ free( context->lc_name );
+ context->lc_name = NULL;
+
+ free( context );
+ *pcontext = NULL;
+
+ return 0;
+}