subst.c revision 1.2 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