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