xref: /netbsd-src/external/bsd/openldap/dist/libraries/librewrite/subst.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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