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 *
rewrite_subst_compile(struct rewrite_info * info,const char * str)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
submatch_copy(struct rewrite_submatch * submatch,const char * string,const regmatch_t * match,struct berval * val)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
rewrite_subst_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_subst * subst,const char * string,const regmatch_t * match,struct berval * val)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
rewrite_subst_destroy(struct rewrite_subst ** psubst)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