xref: /netbsd-src/external/bsd/openldap/dist/libraries/librewrite/subst.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: subst.c,v 1.1.1.4 2014/05/28 09:58:45 tron Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2000-2014 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 				goto cleanup;
161 			}
162 			submatch = tmpsm;
163 			submatch[ nsub ].ls_type =
164 				REWRITE_SUBMATCH_MAP_W_ARG;
165 			submatch[ nsub ].ls_map = map;
166 
167 		/*
168 		 * Escape '%' ...
169 		 */
170 		} else if ( p[ 1 ] == '%' ) {
171 			AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) );
172 			continue;
173 
174 		} else {
175 			goto cleanup;
176 		}
177 
178 		nsub++;
179 	}
180 
181 	/*
182 	 * Last part of string
183 	 */
184 	tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) );
185 	if ( tmps == NULL ) {
186 		/*
187 		 * XXX need to free the value subst stuff!
188 		 */
189 		free( subs );
190 		goto cleanup;
191 	}
192 	subs = tmps;
193 	l = p - begin;
194 	if ( l > 0 ) {
195 		subs_len += l;
196 		subs[ nsub ].bv_len = l;
197 		subs[ nsub ].bv_val = malloc( l + 1 );
198 		if ( subs[ nsub ].bv_val == NULL ) {
199 			free( subs );
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 
219 cleanup:;
220 	free( result );
221 
222 	return s;
223 }
224 
225 /*
226  * Copies the match referred to by submatch and fetched in string by match.
227  * Helper for rewrite_rule_apply.
228  */
229 static int
230 submatch_copy(
231 		struct rewrite_submatch *submatch,
232 		const char *string,
233 		const regmatch_t *match,
234 		struct berval *val
235 )
236 {
237 	int		c, l;
238 	const char	*s;
239 
240 	assert( submatch != NULL );
241 	assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
242 			|| submatch->ls_type == REWRITE_SUBMATCH_XMAP );
243 	assert( string != NULL );
244 	assert( match != NULL );
245 	assert( val != NULL );
246 	assert( val->bv_val == NULL );
247 
248 	c = submatch->ls_submatch;
249 	s = string + match[ c ].rm_so;
250 	l = match[ c ].rm_eo - match[ c ].rm_so;
251 
252 	val->bv_len = l;
253 	val->bv_val = malloc( l + 1 );
254 	if ( val->bv_val == NULL ) {
255 		return REWRITE_ERR;
256 	}
257 
258 	AC_MEMCPY( val->bv_val, s, l );
259 	val->bv_val[ l ] = '\0';
260 
261 	return REWRITE_SUCCESS;
262 }
263 
264 /*
265  * Substitutes a portion of rewritten string according to substitution
266  * pattern using submatches
267  */
268 int
269 rewrite_subst_apply(
270 		struct rewrite_info *info,
271 		struct rewrite_op *op,
272 		struct rewrite_subst *subst,
273 		const char *string,
274 		const regmatch_t *match,
275 		struct berval *val
276 )
277 {
278 	struct berval *submatch = NULL;
279 	char *res = NULL;
280 	int n = 0, l, cl;
281 	int rc = REWRITE_REGEXEC_OK;
282 
283 	assert( info != NULL );
284 	assert( op != NULL );
285 	assert( subst != NULL );
286 	assert( string != NULL );
287 	assert( match != NULL );
288 	assert( val != NULL );
289 
290 	assert( val->bv_val == NULL );
291 
292 	val->bv_val = NULL;
293 	val->bv_len = 0;
294 
295 	/*
296 	 * Prepare room for submatch expansion
297 	 */
298 	if ( subst->lt_num_submatch > 0 ) {
299 		submatch = calloc( sizeof( struct berval ),
300 				subst->lt_num_submatch );
301 		if ( submatch == NULL ) {
302 			return REWRITE_REGEXEC_ERR;
303 		}
304 	}
305 
306 	/*
307 	 * Resolve submatches (simple subst, map expansion and so).
308 	 */
309 	for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
310 		struct berval	key = { 0, NULL };
311 
312 		submatch[ n ].bv_val = NULL;
313 
314 		/*
315 		 * Get key
316 		 */
317 		switch ( subst->lt_submatch[ n ].ls_type ) {
318 		case REWRITE_SUBMATCH_ASIS:
319 		case REWRITE_SUBMATCH_XMAP:
320 			rc = submatch_copy( &subst->lt_submatch[ n ],
321 					string, match, &key );
322 			if ( rc != REWRITE_SUCCESS ) {
323 				rc = REWRITE_REGEXEC_ERR;
324 				goto cleanup;
325 			}
326 			break;
327 
328 		case REWRITE_SUBMATCH_MAP_W_ARG:
329 			switch ( subst->lt_submatch[ n ].ls_map->lm_type ) {
330 			case REWRITE_MAP_GET_OP_VAR:
331 			case REWRITE_MAP_GET_SESN_VAR:
332 			case REWRITE_MAP_GET_PARAM:
333 				rc = REWRITE_SUCCESS;
334 				break;
335 
336 			default:
337 				rc = rewrite_subst_apply( info, op,
338 					subst->lt_submatch[ n ].ls_map->lm_subst,
339 					string, match, &key);
340 			}
341 
342 			if ( rc != REWRITE_SUCCESS ) {
343 				goto cleanup;
344 			}
345 			break;
346 
347 		default:
348 			Debug( LDAP_DEBUG_ANY, "Not Implemented\n", 0, 0, 0 );
349 			rc = REWRITE_ERR;
350 			break;
351 		}
352 
353 		if ( rc != REWRITE_SUCCESS ) {
354 			rc = REWRITE_REGEXEC_ERR;
355 			goto cleanup;
356 		}
357 
358 		/*
359 		 * Resolve key
360 		 */
361 		switch ( subst->lt_submatch[ n ].ls_type ) {
362 		case REWRITE_SUBMATCH_ASIS:
363 			submatch[ n ] = key;
364 			rc = REWRITE_SUCCESS;
365 			break;
366 
367 		case REWRITE_SUBMATCH_XMAP:
368 			rc = rewrite_xmap_apply( info, op,
369 					subst->lt_submatch[ n ].ls_map,
370 					&key, &submatch[ n ] );
371 			free( key.bv_val );
372 			key.bv_val = NULL;
373 			break;
374 
375 		case REWRITE_SUBMATCH_MAP_W_ARG:
376 			rc = rewrite_map_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 		default:
384 			/*
385 			 * When implemented, this might return the
386                          * exit status of a rewrite context,
387                          * which may include a stop, or an
388                          * unwilling to perform
389                          */
390 			rc = REWRITE_ERR;
391 			break;
392 		}
393 
394 		if ( rc != REWRITE_SUCCESS ) {
395 			rc = REWRITE_REGEXEC_ERR;
396 			goto cleanup;
397 		}
398 
399 		/*
400                  * Increment the length of the resulting string
401                  */
402 		l += submatch[ n ].bv_len;
403 	}
404 
405 	/*
406          * Alloc result buffer
407          */
408 	l += subst->lt_subs_len;
409 	res = malloc( l + 1 );
410 	if ( res == NULL ) {
411 		rc = REWRITE_REGEXEC_ERR;
412 		goto cleanup;
413 	}
414 
415 	/*
416 	 * Apply submatches (possibly resolved thru maps)
417 	 */
418         for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
419 		if ( subst->lt_subs[ n ].bv_val != NULL ) {
420                 	AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
421 					subst->lt_subs[ n ].bv_len );
422 			cl += subst->lt_subs[ n ].bv_len;
423 		}
424 		AC_MEMCPY( res + cl, submatch[ n ].bv_val,
425 				submatch[ n ].bv_len );
426 		cl += submatch[ n ].bv_len;
427 	}
428 	if ( subst->lt_subs[ n ].bv_val != NULL ) {
429 		AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
430 				subst->lt_subs[ n ].bv_len );
431 		cl += subst->lt_subs[ n ].bv_len;
432 	}
433 	res[ cl ] = '\0';
434 
435 	val->bv_val = res;
436 	val->bv_len = l;
437 
438 cleanup:;
439 	if ( submatch ) {
440         	for ( ; --n >= 0; ) {
441 			if ( submatch[ n ].bv_val ) {
442 				free( submatch[ n ].bv_val );
443 			}
444 		}
445 		free( submatch );
446 	}
447 
448 	return rc;
449 }
450 
451 /*
452  * frees data
453  */
454 int
455 rewrite_subst_destroy(
456 		struct rewrite_subst **psubst
457 )
458 {
459 	int			n;
460 	struct rewrite_subst	*subst;
461 
462 	assert( psubst != NULL );
463 	assert( *psubst != NULL );
464 
465 	subst = *psubst;
466 
467 	for ( n = 0; n < subst->lt_num_submatch; n++ ) {
468 		if ( subst->lt_subs[ n ].bv_val ) {
469 			free( subst->lt_subs[ n ].bv_val );
470 			subst->lt_subs[ n ].bv_val = NULL;
471 		}
472 
473 		switch ( subst->lt_submatch[ n ].ls_type ) {
474 		case REWRITE_SUBMATCH_ASIS:
475 			break;
476 
477 		case REWRITE_SUBMATCH_XMAP:
478 			rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
479 			break;
480 
481 		case REWRITE_SUBMATCH_MAP_W_ARG:
482 			rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
483 			break;
484 
485 		default:
486 			break;
487 		}
488 	}
489 
490 	free( subst->lt_submatch );
491 	subst->lt_submatch = NULL;
492 
493 	/* last one */
494 	if ( subst->lt_subs[ n ].bv_val ) {
495 		free( subst->lt_subs[ n ].bv_val );
496 		subst->lt_subs[ n ].bv_val = NULL;
497 	}
498 
499 	free( subst->lt_subs );
500 	subst->lt_subs = NULL;
501 
502 	free( subst );
503 	*psubst = NULL;
504 
505 	return 0;
506 }
507 
508