/* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * 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 * . */ /* ACKNOWLEDGEMENT: * This work was initially developed by Pierangelo Masarati for * inclusion in OpenLDAP Software. */ #include #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; }