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