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