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