1 /* $NetBSD: subst.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 2000-2021 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 /* ACKNOWLEDGEMENT: 18 * This work was initially developed by Pierangelo Masarati for 19 * inclusion in OpenLDAP Software. 20 */ 21 22 #include <portable.h> 23 24 #include "rewrite-int.h" 25 26 /* 27 * Compiles a substitution pattern 28 */ 29 struct rewrite_subst * 30 rewrite_subst_compile( 31 struct rewrite_info *info, 32 const char *str 33 ) 34 { 35 size_t subs_len; 36 struct berval *subs = NULL, *tmps; 37 struct rewrite_submatch *submatch = NULL, *tmpsm; 38 39 struct rewrite_subst *s = NULL; 40 41 char *result, *begin, *p; 42 int nsub = 0, l; 43 44 assert( info != NULL ); 45 assert( str != NULL ); 46 47 result = strdup( str ); 48 if ( result == NULL ) { 49 return NULL; 50 } 51 52 /* 53 * Take care of substitution string 54 */ 55 for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) { 56 57 /* 58 * Keep only single escapes '%' 59 */ 60 if ( !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) { 61 continue; 62 } 63 64 if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) { 65 /* Pull &p[1] over p, including the trailing '\0' */ 66 AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) ); 67 continue; 68 } 69 70 tmps = ( struct berval * )realloc( subs, 71 sizeof( struct berval )*( nsub + 1 ) ); 72 if ( tmps == NULL ) { 73 goto cleanup; 74 } 75 subs = tmps; 76 subs[ nsub ].bv_val = NULL; 77 78 tmpsm = ( struct rewrite_submatch * )realloc( submatch, 79 sizeof( struct rewrite_submatch )*( nsub + 1 ) ); 80 if ( tmpsm == NULL ) { 81 goto cleanup; 82 } 83 submatch = tmpsm; 84 submatch[ nsub ].ls_map = NULL; 85 86 /* 87 * I think an `if l > 0' at runtime is better outside than 88 * inside a function call ... 89 */ 90 l = p - begin; 91 if ( l > 0 ) { 92 subs_len += l; 93 subs[ nsub ].bv_len = l; 94 subs[ nsub ].bv_val = malloc( l + 1 ); 95 if ( subs[ nsub ].bv_val == NULL ) { 96 goto cleanup; 97 } 98 AC_MEMCPY( subs[ nsub ].bv_val, begin, l ); 99 subs[ nsub ].bv_val[ l ] = '\0'; 100 } else { 101 subs[ nsub ].bv_val = NULL; 102 subs[ nsub ].bv_len = 0; 103 } 104 105 /* 106 * Substitution pattern 107 */ 108 if ( isdigit( (unsigned char) p[ 1 ] ) ) { 109 int d = p[ 1 ] - '0'; 110 111 /* 112 * Add a new value substitution scheme 113 */ 114 115 submatch[ nsub ].ls_submatch = d; 116 117 /* 118 * If there is no argument, use default 119 * (substitute substring as is) 120 */ 121 if ( p[ 2 ] != '{' ) { 122 submatch[ nsub ].ls_type = 123 REWRITE_SUBMATCH_ASIS; 124 submatch[ nsub ].ls_map = NULL; 125 begin = ++p + 1; 126 127 } else { 128 struct rewrite_map *map; 129 130 submatch[ nsub ].ls_type = 131 REWRITE_SUBMATCH_XMAP; 132 133 map = rewrite_xmap_parse( info, 134 p + 3, (const char **)&begin ); 135 if ( map == NULL ) { 136 goto cleanup; 137 } 138 submatch[ nsub ].ls_map = map; 139 p = begin - 1; 140 } 141 142 /* 143 * Map with args ... 144 */ 145 } else if ( p[ 1 ] == '{' ) { 146 struct rewrite_map *map; 147 148 map = rewrite_map_parse( info, p + 2, 149 (const char **)&begin ); 150 if ( map == NULL ) { 151 goto cleanup; 152 } 153 p = begin - 1; 154 155 /* 156 * Add a new value substitution scheme 157 */ 158 submatch[ nsub ].ls_type = 159 REWRITE_SUBMATCH_MAP_W_ARG; 160 submatch[ nsub ].ls_map = map; 161 162 /* 163 * Escape '%' ... 164 */ 165 } else if ( p[ 1 ] == '%' ) { 166 AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) ); 167 continue; 168 169 } else { 170 goto cleanup; 171 } 172 173 nsub++; 174 } 175 176 /* 177 * Last part of string 178 */ 179 tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) ); 180 if ( tmps == NULL ) { 181 /* 182 * XXX need to free the value subst stuff! 183 */ 184 free( subs ); 185 goto cleanup; 186 } 187 subs = tmps; 188 l = p - begin; 189 if ( l > 0 ) { 190 subs_len += l; 191 subs[ nsub ].bv_len = l; 192 subs[ nsub ].bv_val = malloc( l + 1 ); 193 if ( subs[ nsub ].bv_val == NULL ) { 194 goto cleanup; 195 } 196 AC_MEMCPY( subs[ nsub ].bv_val, begin, l ); 197 subs[ nsub ].bv_val[ l ] = '\0'; 198 } else { 199 subs[ nsub ].bv_val = NULL; 200 subs[ nsub ].bv_len = 0; 201 } 202 203 s = calloc( sizeof( struct rewrite_subst ), 1 ); 204 if ( s == NULL ) { 205 goto cleanup; 206 } 207 208 s->lt_subs_len = subs_len; 209 s->lt_subs = subs; 210 s->lt_num_submatch = nsub; 211 s->lt_submatch = submatch; 212 subs = NULL; 213 submatch = NULL; 214 215 cleanup:; 216 if ( subs ) { 217 for ( l=0; l<nsub; l++ ) { 218 free( subs[nsub].bv_val ); 219 } 220 free( subs ); 221 } 222 if ( submatch ) { 223 for ( l=0; l<nsub; l++ ) { 224 free( submatch[nsub].ls_map ); 225 } 226 free( submatch ); 227 } 228 free( result ); 229 230 return s; 231 } 232 233 /* 234 * Copies the match referred to by submatch and fetched in string by match. 235 * Helper for rewrite_rule_apply. 236 */ 237 static int 238 submatch_copy( 239 struct rewrite_submatch *submatch, 240 const char *string, 241 const regmatch_t *match, 242 struct berval *val 243 ) 244 { 245 int c, l; 246 const char *s; 247 248 assert( submatch != NULL ); 249 assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS 250 || submatch->ls_type == REWRITE_SUBMATCH_XMAP ); 251 assert( string != NULL ); 252 assert( match != NULL ); 253 assert( val != NULL ); 254 assert( val->bv_val == NULL ); 255 256 c = submatch->ls_submatch; 257 s = string + match[ c ].rm_so; 258 l = match[ c ].rm_eo - match[ c ].rm_so; 259 260 val->bv_len = l; 261 val->bv_val = malloc( l + 1 ); 262 if ( val->bv_val == NULL ) { 263 return REWRITE_ERR; 264 } 265 266 AC_MEMCPY( val->bv_val, s, l ); 267 val->bv_val[ l ] = '\0'; 268 269 return REWRITE_SUCCESS; 270 } 271 272 /* 273 * Substitutes a portion of rewritten string according to substitution 274 * pattern using submatches 275 */ 276 int 277 rewrite_subst_apply( 278 struct rewrite_info *info, 279 struct rewrite_op *op, 280 struct rewrite_subst *subst, 281 const char *string, 282 const regmatch_t *match, 283 struct berval *val 284 ) 285 { 286 struct berval *submatch = NULL; 287 char *res = NULL; 288 int n = 0, l, cl; 289 int rc = REWRITE_REGEXEC_OK; 290 291 assert( info != NULL ); 292 assert( op != NULL ); 293 assert( subst != NULL ); 294 assert( string != NULL ); 295 assert( match != NULL ); 296 assert( val != NULL ); 297 298 assert( val->bv_val == NULL ); 299 300 val->bv_val = NULL; 301 val->bv_len = 0; 302 303 /* 304 * Prepare room for submatch expansion 305 */ 306 if ( subst->lt_num_submatch > 0 ) { 307 submatch = calloc( sizeof( struct berval ), 308 subst->lt_num_submatch ); 309 if ( submatch == NULL ) { 310 return REWRITE_REGEXEC_ERR; 311 } 312 } 313 314 /* 315 * Resolve submatches (simple subst, map expansion and so). 316 */ 317 for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) { 318 struct berval key = { 0, NULL }; 319 320 submatch[ n ].bv_val = NULL; 321 322 /* 323 * Get key 324 */ 325 switch ( subst->lt_submatch[ n ].ls_type ) { 326 case REWRITE_SUBMATCH_ASIS: 327 case REWRITE_SUBMATCH_XMAP: 328 rc = submatch_copy( &subst->lt_submatch[ n ], 329 string, match, &key ); 330 if ( rc != REWRITE_SUCCESS ) { 331 rc = REWRITE_REGEXEC_ERR; 332 goto cleanup; 333 } 334 break; 335 336 case REWRITE_SUBMATCH_MAP_W_ARG: 337 switch ( subst->lt_submatch[ n ].ls_map->lm_type ) { 338 case REWRITE_MAP_GET_OP_VAR: 339 case REWRITE_MAP_GET_SESN_VAR: 340 case REWRITE_MAP_GET_PARAM: 341 rc = REWRITE_SUCCESS; 342 break; 343 344 default: 345 rc = rewrite_subst_apply( info, op, 346 subst->lt_submatch[ n ].ls_map->lm_subst, 347 string, match, &key); 348 } 349 350 if ( rc != REWRITE_SUCCESS ) { 351 goto cleanup; 352 } 353 break; 354 355 default: 356 Debug( LDAP_DEBUG_ANY, "Not Implemented\n" ); 357 rc = REWRITE_ERR; 358 break; 359 } 360 361 if ( rc != REWRITE_SUCCESS ) { 362 rc = REWRITE_REGEXEC_ERR; 363 goto cleanup; 364 } 365 366 /* 367 * Resolve key 368 */ 369 switch ( subst->lt_submatch[ n ].ls_type ) { 370 case REWRITE_SUBMATCH_ASIS: 371 submatch[ n ] = key; 372 rc = REWRITE_SUCCESS; 373 break; 374 375 case REWRITE_SUBMATCH_XMAP: 376 rc = rewrite_xmap_apply( info, op, 377 subst->lt_submatch[ n ].ls_map, 378 &key, &submatch[ n ] ); 379 free( key.bv_val ); 380 key.bv_val = NULL; 381 break; 382 383 case REWRITE_SUBMATCH_MAP_W_ARG: 384 rc = rewrite_map_apply( info, op, 385 subst->lt_submatch[ n ].ls_map, 386 &key, &submatch[ n ] ); 387 free( key.bv_val ); 388 key.bv_val = NULL; 389 break; 390 391 default: 392 /* 393 * When implemented, this might return the 394 * exit status of a rewrite context, 395 * which may include a stop, or an 396 * unwilling to perform 397 */ 398 rc = REWRITE_ERR; 399 break; 400 } 401 402 if ( rc != REWRITE_SUCCESS ) { 403 rc = REWRITE_REGEXEC_ERR; 404 goto cleanup; 405 } 406 407 /* 408 * Increment the length of the resulting string 409 */ 410 l += submatch[ n ].bv_len; 411 } 412 413 /* 414 * Alloc result buffer 415 */ 416 l += subst->lt_subs_len; 417 res = malloc( l + 1 ); 418 if ( res == NULL ) { 419 rc = REWRITE_REGEXEC_ERR; 420 goto cleanup; 421 } 422 423 /* 424 * Apply submatches (possibly resolved thru maps) 425 */ 426 for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) { 427 if ( subst->lt_subs[ n ].bv_val != NULL ) { 428 AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val, 429 subst->lt_subs[ n ].bv_len ); 430 cl += subst->lt_subs[ n ].bv_len; 431 } 432 AC_MEMCPY( res + cl, submatch[ n ].bv_val, 433 submatch[ n ].bv_len ); 434 cl += submatch[ n ].bv_len; 435 } 436 if ( subst->lt_subs[ n ].bv_val != NULL ) { 437 AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val, 438 subst->lt_subs[ n ].bv_len ); 439 cl += subst->lt_subs[ n ].bv_len; 440 } 441 res[ cl ] = '\0'; 442 443 val->bv_val = res; 444 val->bv_len = l; 445 446 cleanup:; 447 if ( submatch ) { 448 for ( ; --n >= 0; ) { 449 if ( submatch[ n ].bv_val ) { 450 free( submatch[ n ].bv_val ); 451 } 452 } 453 free( submatch ); 454 } 455 456 return rc; 457 } 458 459 /* 460 * frees data 461 */ 462 int 463 rewrite_subst_destroy( 464 struct rewrite_subst **psubst 465 ) 466 { 467 int n; 468 struct rewrite_subst *subst; 469 470 assert( psubst != NULL ); 471 assert( *psubst != NULL ); 472 473 subst = *psubst; 474 475 for ( n = 0; n < subst->lt_num_submatch; n++ ) { 476 if ( subst->lt_subs[ n ].bv_val ) { 477 free( subst->lt_subs[ n ].bv_val ); 478 subst->lt_subs[ n ].bv_val = NULL; 479 } 480 481 switch ( subst->lt_submatch[ n ].ls_type ) { 482 case REWRITE_SUBMATCH_ASIS: 483 break; 484 485 case REWRITE_SUBMATCH_XMAP: 486 rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map ); 487 break; 488 489 case REWRITE_SUBMATCH_MAP_W_ARG: 490 rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map ); 491 break; 492 493 default: 494 break; 495 } 496 } 497 498 free( subst->lt_submatch ); 499 subst->lt_submatch = NULL; 500 501 /* last one */ 502 if ( subst->lt_subs[ n ].bv_val ) { 503 free( subst->lt_subs[ n ].bv_val ); 504 subst->lt_subs[ n ].bv_val = NULL; 505 } 506 507 free( subst->lt_subs ); 508 subst->lt_subs = NULL; 509 510 free( subst ); 511 *psubst = NULL; 512 513 return 0; 514 } 515 516