diff options
Diffstat (limited to 'libraries/librewrite/map.c')
-rw-r--r-- | libraries/librewrite/map.c | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c new file mode 100644 index 0000000..43341e3 --- /dev/null +++ b/libraries/librewrite/map.c @@ -0,0 +1,583 @@ +/* $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 <stdio.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#include "rewrite-int.h" +#include "rewrite-map.h" + +static int num_mappers; +static const rewrite_mapper **mappers; +#define MAPPER_ALLOC 8 + +struct rewrite_map * +rewrite_map_parse( + struct rewrite_info *info, + const char *string, + const char **currpos +) +{ + struct rewrite_map *map = NULL; + struct rewrite_subst *subst = NULL; + char *s, *begin = NULL, *end; + const char *p; + int l, cnt, mtx = 0, rc = 0; + + assert( info != NULL ); + assert( string != NULL ); + assert( currpos != NULL ); + + *currpos = NULL; + + /* + * Go to the end of the map invocation (the right closing brace) + */ + for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) { + if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) { + /* + * '%' marks the beginning of a new map + */ + if ( p[ 1 ] == '{' ) { + cnt++; + /* + * '%' followed by a digit may mark the beginning + * of an old map + */ + } else if ( isdigit( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) { + cnt++; + p++; + } + + if ( p[ 1 ] != '\0' ) { + p++; + } + + } else if ( p[ 0 ] == '}' ) { + cnt--; + } + } + if ( cnt != 0 ) { + return NULL; + } + *currpos = p; + + /* + * Copy the map invocation + */ + l = p - string - 1; + s = calloc( sizeof( char ), l + 1 ); + if ( s == NULL ) { + return NULL; + } + AC_MEMCPY( s, string, l ); + s[ l ] = 0; + + /* + * Isolate the map name (except for variable deref) + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + + default: + begin = strchr( s, '(' ); + if ( begin == NULL ) { + rc = -1; + goto cleanup; + } + begin[ 0 ] = '\0'; + begin++; + break; + } + + /* + * Check for special map types + */ + p = s; + switch ( p[ 0 ] ) { + case REWRITE_OPERATOR_SUBCONTEXT: + case REWRITE_OPERATOR_COMMAND: + case REWRITE_OPERATOR_VARIABLE_SET: + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + p++; + break; + } + + /* + * Variable set and get may be repeated to indicate session-wide + * instead of operation-wide variables + */ + switch ( p[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_SET: + case REWRITE_OPERATOR_VARIABLE_GET: + p++; + break; + } + + /* + * Variable get token can be appended to variable set to mean store + * AND rewrite + */ + if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + p++; + } + + /* + * Check the syntax of the variable name + */ + if ( !isalpha( (unsigned char) p[ 0 ] ) ) { + rc = -1; + goto cleanup; + } + for ( p++; p[ 0 ] != '\0'; p++ ) { + if ( !isalnum( (unsigned char) p[ 0 ] ) ) { + rc = -1; + goto cleanup; + } + } + + /* + * Isolate the argument of the map (except for variable deref) + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + + default: + end = strrchr( begin, ')' ); + if ( end == NULL ) { + rc = -1; + goto cleanup; + } + end[ 0 ] = '\0'; + + /* + * Compile the substitution pattern of the map argument + */ + subst = rewrite_subst_compile( info, begin ); + if ( subst == NULL ) { + rc = -1; + goto cleanup; + } + break; + } + + /* + * Create the map + */ + map = calloc( sizeof( struct rewrite_map ), 1 ); + if ( map == NULL ) { + rc = -1; + goto cleanup; + } + memset( map, 0, sizeof( struct rewrite_map ) ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + rc = -1; + goto cleanup; + } + ++mtx; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * No subst for variable deref + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + + default: + map->lm_subst = subst; + break; + } + + /* + * Parses special map types + */ + switch ( s[ 0 ] ) { + + /* + * Subcontext + */ + case REWRITE_OPERATOR_SUBCONTEXT: /* '>' */ + + /* + * Fetch the rewrite context + * it MUST have been defined previously + */ + map->lm_type = REWRITE_MAP_SUBCONTEXT; + map->lm_name = strdup( s + 1 ); + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + map->lm_data = rewrite_context_find( info, s + 1 ); + if ( map->lm_data == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * External command (not implemented yet) + */ + case REWRITE_OPERATOR_COMMAND: /* '|' */ + rc = -1; + goto cleanup; + + /* + * Variable set + */ + case REWRITE_OPERATOR_VARIABLE_SET: /* '&' */ + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) { + if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_SETW_SESN_VAR; + map->lm_name = strdup( s + 3 ); + } else { + map->lm_type = REWRITE_MAP_SET_SESN_VAR; + map->lm_name = strdup( s + 2 ); + } + } else { + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_SETW_OP_VAR; + map->lm_name = strdup( s + 2 ); + } else { + map->lm_type = REWRITE_MAP_SET_OP_VAR; + map->lm_name = strdup( s + 1 ); + } + } + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * Variable dereference + */ + case REWRITE_OPERATOR_VARIABLE_GET: /* '*' */ + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_GET_SESN_VAR; + map->lm_name = strdup( s + 2 ); + } else { + map->lm_type = REWRITE_MAP_GET_OP_VAR; + map->lm_name = strdup( s + 1 ); + } + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * Parameter + */ + case REWRITE_OPERATOR_PARAM_GET: /* '$' */ + map->lm_type = REWRITE_MAP_GET_PARAM; + map->lm_name = strdup( s + 1 ); + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * Built-in map + */ + default: + map->lm_type = REWRITE_MAP_BUILTIN; + map->lm_name = strdup( s ); + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + map->lm_data = rewrite_builtin_map_find( info, s ); + if ( map->lm_data == NULL ) { + rc = -1; + goto cleanup; + } + break; + + } + +cleanup: + free( s ); + if ( rc ) { + if ( subst != NULL ) { + free( subst ); + } + if ( map ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( mtx ) { + ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( map->lm_name ) { + free( map->lm_name ); + map->lm_name = NULL; + } + free( map ); + map = NULL; + } + } + + return map; +} + +/* + * Applies the new map type + */ +int +rewrite_map_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +) +{ + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( op != NULL ); + assert( map != NULL ); + assert( key != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + switch ( map->lm_type ) { + case REWRITE_MAP_SUBCONTEXT: + rc = rewrite_context_apply( info, op, + ( struct rewrite_context * )map->lm_data, + key->bv_val, &val->bv_val ); + if ( val->bv_val != NULL ) { + if ( val->bv_val == key->bv_val ) { + val->bv_len = key->bv_len; + key->bv_val = NULL; + } else { + val->bv_len = strlen( val->bv_val ); + } + } + break; + + case REWRITE_MAP_SET_OP_VAR: + case REWRITE_MAP_SETW_OP_VAR: + rc = rewrite_var_set( &op->lo_vars, map->lm_name, + key->bv_val, 1 ) + ? REWRITE_SUCCESS : REWRITE_ERR; + if ( rc == REWRITE_SUCCESS ) { + if ( map->lm_type == REWRITE_MAP_SET_OP_VAR ) { + val->bv_val = strdup( "" ); + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + } + break; + + case REWRITE_MAP_GET_OP_VAR: { + struct rewrite_var *var; + + var = rewrite_var_find( op->lo_vars, map->lm_name ); + if ( var == NULL ) { + rc = REWRITE_ERR; + } else { + val->bv_val = strdup( var->lv_value.bv_val ); + val->bv_len = var->lv_value.bv_len; + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + } + break; + } + + case REWRITE_MAP_SET_SESN_VAR: + case REWRITE_MAP_SETW_SESN_VAR: + if ( op->lo_cookie == NULL ) { + rc = REWRITE_ERR; + break; + } + rc = rewrite_session_var_set( info, op->lo_cookie, + map->lm_name, key->bv_val ); + if ( rc == REWRITE_SUCCESS ) { + if ( map->lm_type == REWRITE_MAP_SET_SESN_VAR ) { + val->bv_val = strdup( "" ); + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + } + break; + + case REWRITE_MAP_GET_SESN_VAR: + rc = rewrite_session_var_get( info, op->lo_cookie, + map->lm_name, val ); + break; + + case REWRITE_MAP_GET_PARAM: + rc = rewrite_param_get( info, map->lm_name, val ); + break; + + case REWRITE_MAP_BUILTIN: { + struct rewrite_builtin_map *bmap = map->lm_data; + + if ( bmap->lb_mapper && bmap->lb_mapper->rm_apply ) + rc = bmap->lb_mapper->rm_apply( bmap->lb_private, key->bv_val, + val ); + else + rc = REWRITE_ERR; + break; + break; + } + + default: + rc = REWRITE_ERR; + break; + } + + return rc; +} + +void +rewrite_builtin_map_free( + void *tmp +) +{ + struct rewrite_builtin_map *map = ( struct rewrite_builtin_map * )tmp; + + assert( map != NULL ); + + if ( map->lb_mapper && map->lb_mapper->rm_destroy ) + map->lb_mapper->rm_destroy( map->lb_private ); + + free( map->lb_name ); + free( map ); +} + +int +rewrite_map_destroy( + struct rewrite_map **pmap +) +{ + struct rewrite_map *map; + + assert( pmap != NULL ); + assert( *pmap != NULL ); + + map = *pmap; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( map->lm_name ) { + free( map->lm_name ); + map->lm_name = NULL; + } + + if ( map->lm_subst ) { + rewrite_subst_destroy( &map->lm_subst ); + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); + ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + free( map ); + *pmap = NULL; + + return 0; +} + +/* ldapmap.c */ +extern const rewrite_mapper rewrite_ldap_mapper; + +const rewrite_mapper * +rewrite_mapper_find( + const char *name +) +{ + int i; + + if ( !strcasecmp( name, "ldap" )) + return &rewrite_ldap_mapper; + + for (i=0; i<num_mappers; i++) + if ( !strcasecmp( name, mappers[i]->rm_name )) + return mappers[i]; + return NULL; +} + +int +rewrite_mapper_register( + const rewrite_mapper *map +) +{ + if ( num_mappers % MAPPER_ALLOC == 0 ) { + const rewrite_mapper **mnew; + mnew = realloc( mappers, (num_mappers + MAPPER_ALLOC) * + sizeof( rewrite_mapper * )); + if ( mnew ) + mappers = mnew; + else + return -1; + } + mappers[num_mappers++] = map; + return 0; +} + +int +rewrite_mapper_unregister( + const rewrite_mapper *map +) +{ + int i; + + for (i = 0; i<num_mappers; i++) { + if ( mappers[i] == map ) { + num_mappers--; + mappers[i] = mappers[num_mappers]; + mappers[num_mappers] = NULL; + return 0; + } + } + /* not found */ + return -1; +} |