xref: /netbsd-src/external/bsd/openldap/dist/libraries/librewrite/rule.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: rule.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2000-2021 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  * Appends a rule to the double linked list of rules
28  * Helper for rewrite_rule_compile
29  */
30 static int
append_rule(struct rewrite_context * context,struct rewrite_rule * rule)31 append_rule(
32 		struct rewrite_context *context,
33 		struct rewrite_rule *rule
34 )
35 {
36 	struct rewrite_rule *r;
37 
38 	assert( context != NULL );
39 	assert( context->lc_rule != NULL );
40 	assert( rule != NULL );
41 
42 	for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
43 	r->lr_next = rule;
44 	rule->lr_prev = r;
45 
46 	return REWRITE_SUCCESS;
47 }
48 
49 /*
50  * Appends an action to the linked list of actions
51  * Helper for rewrite_rule_compile
52  */
53 static int
append_action(struct rewrite_action ** pbase,struct rewrite_action * action)54 append_action(
55 		struct rewrite_action **pbase,
56 		struct rewrite_action *action
57 )
58 {
59 	struct rewrite_action **pa;
60 
61 	assert( pbase != NULL );
62 	assert( action != NULL );
63 
64 	for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
65 	*pa = action;
66 
67 	return REWRITE_SUCCESS;
68 }
69 
70 static int
destroy_action(struct rewrite_action ** paction)71 destroy_action(
72 		struct rewrite_action **paction
73 )
74 {
75 	struct rewrite_action	*action;
76 
77 	assert( paction != NULL );
78 	assert( *paction != NULL );
79 
80 	action = *paction;
81 
82 	/* do something */
83 	switch ( action->la_type ) {
84 	case REWRITE_FLAG_GOTO:
85 	case REWRITE_FLAG_USER: {
86 		int *pi = (int *)action->la_args;
87 
88 		if ( pi ) {
89 			free( pi );
90 		}
91 		break;
92 	}
93 
94 	default:
95 		break;
96 	}
97 
98 	free( action );
99 	*paction = NULL;
100 
101 	return 0;
102 }
103 
104 static void
destroy_actions(struct rewrite_action * paction)105 destroy_actions(
106 	struct rewrite_action *paction
107 )
108 {
109 	struct rewrite_action *next;
110 
111 	for (; paction; paction = next) {
112 		next = paction->la_next;
113 		destroy_action( &paction );
114 	}
115 }
116 
117 /*
118  */
119 int
rewrite_rule_compile(struct rewrite_info * info,struct rewrite_context * context,const char * pattern,const char * result,const char * flagstring)120 rewrite_rule_compile(
121 		struct rewrite_info *info,
122 		struct rewrite_context *context,
123 		const char *pattern,
124 		const char *result,
125 		const char *flagstring
126 )
127 {
128 	int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
129 	int mode = REWRITE_RECURSE;
130 	int max_passes;
131 
132 	struct rewrite_rule *rule = NULL;
133 	struct rewrite_subst *subst = NULL;
134 	struct rewrite_action *action = NULL, *first_action = NULL;
135 
136 	const char *p;
137 
138 	assert( info != NULL );
139 	assert( context != NULL );
140 	assert( pattern != NULL );
141 	assert( result != NULL );
142 	/*
143 	 * A null flagstring should be allowed
144 	 */
145 
146 	max_passes = info->li_max_passes_per_rule;
147 
148 	/*
149 	 * Take care of substitution string
150 	 */
151 	subst = rewrite_subst_compile( info, result );
152 	if ( subst == NULL ) {
153 		return REWRITE_ERR;
154 	}
155 
156 	/*
157 	 * Take care of flags
158 	 */
159 	for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
160 		switch( p[ 0 ] ) {
161 
162 		/*
163 		 * REGEX flags
164 		 */
165 		case REWRITE_FLAG_HONORCASE: 		/* 'C' */
166 			/*
167 			 * Honor case (default is case insensitive)
168 			 */
169 			flags &= ~REWRITE_REGEX_ICASE;
170 			break;
171 
172 		case REWRITE_FLAG_BASICREGEX: 		/* 'R' */
173 			/*
174 			 * Use POSIX Basic Regular Expression syntax
175 			 * instead of POSIX Extended Regular Expression
176 			 * syntax (default)
177 			 */
178 			flags &= ~REWRITE_REGEX_EXTENDED;
179 			break;
180 
181 		/*
182 		 * Execution mode flags
183 		 */
184 		case REWRITE_FLAG_EXECONCE: 		/* ':' */
185 			/*
186 			 * Apply rule once only
187 			 */
188 			mode &= ~REWRITE_RECURSE;
189 			mode |= REWRITE_EXEC_ONCE;
190 			break;
191 
192 		/*
193 		 * Special action flags
194 		 */
195 		case REWRITE_FLAG_STOP:	 		/* '@' */
196 			/*
197 			 * Bail out after applying rule
198 			 */
199 			action = calloc( sizeof( struct rewrite_action ), 1 );
200 			if ( action == NULL ) {
201 				goto fail;
202 			}
203 
204 			action->la_type = REWRITE_ACTION_STOP;
205 			break;
206 
207 		case REWRITE_FLAG_UNWILLING: 		/* '#' */
208 			/*
209 			 * Matching objs will be marked as gone!
210 			 */
211 			action = calloc( sizeof( struct rewrite_action ), 1 );
212 			if ( action == NULL ) {
213 				goto fail;
214 			}
215 
216 			mode &= ~REWRITE_RECURSE;
217 			mode |= REWRITE_EXEC_ONCE;
218 			action->la_type = REWRITE_ACTION_UNWILLING;
219 			break;
220 
221 		case REWRITE_FLAG_GOTO:				/* 'G' */
222 			/*
223 			 * After applying rule, jump N rules
224 			 */
225 
226 		case REWRITE_FLAG_USER: {			/* 'U' */
227 			/*
228 			 * After applying rule, return user-defined
229 			 * error code
230 			 */
231 			char *next = NULL;
232 			int *d;
233 
234 			if ( p[ 1 ] != '{' ) {
235 				goto fail;
236 			}
237 
238 			d = malloc( sizeof( int ) );
239 			if ( d == NULL ) {
240 				goto fail;
241 			}
242 
243 			d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
244 			if ( next == &p[ 2 ] || next[0] != '}' ) {
245 				free( d );
246 				goto fail;
247 			}
248 
249 			action = calloc( sizeof( struct rewrite_action ), 1 );
250 			if ( action == NULL ) {
251 				free( d );
252 				goto fail;
253 			}
254 			switch ( p[ 0 ] ) {
255 			case REWRITE_FLAG_GOTO:
256 				action->la_type = REWRITE_ACTION_GOTO;
257 				break;
258 
259 			case REWRITE_FLAG_USER:
260 				action->la_type = REWRITE_ACTION_USER;
261 				break;
262 
263 			default:
264 				assert(0);
265 			}
266 
267 			action->la_args = (void *)d;
268 
269 			p = next;	/* p is incremented by the for ... */
270 
271 			break;
272 		}
273 
274 		case REWRITE_FLAG_MAX_PASSES: {			/* 'U' */
275 			/*
276 			 * Set the number of max passes per rule
277 			 */
278 			char *next = NULL;
279 
280 			if ( p[ 1 ] != '{' ) {
281 				goto fail;
282 			}
283 
284 			max_passes = strtol( &p[ 2 ], &next, 0 );
285 			if ( next == &p[ 2 ] || next[0] != '}' ) {
286 				goto fail;
287 			}
288 
289 			if ( max_passes < 1 ) {
290 				/* FIXME: nonsense ... */
291 				max_passes = 1;
292 			}
293 
294 			p = next;	/* p is incremented by the for ... */
295 
296 			break;
297 		}
298 
299 		case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
300 			/*
301 			 * Ignore errors!
302 			 */
303 			action = calloc( sizeof( struct rewrite_action ), 1 );
304 			if ( action == NULL ) {
305 				goto fail;
306 			}
307 
308 			action->la_type = REWRITE_ACTION_IGNORE_ERR;
309 			break;
310 
311 		/*
312 		 * Other flags ...
313 		 */
314 		default:
315 			/*
316 			 * Unimplemented feature (complain only)
317 			 */
318 			break;
319 		}
320 
321 		/*
322 		 * Stupid way to append to a list ...
323 		 */
324 		if ( action != NULL ) {
325 			append_action( &first_action, action );
326 			action = NULL;
327 		}
328 	}
329 
330 	/*
331 	 * Finally, rule allocation
332 	 */
333 	rule = calloc( sizeof( struct rewrite_rule ), 1 );
334 	if ( rule == NULL ) {
335 		goto fail;
336 	}
337 
338 	/*
339 	 * REGEX compilation (luckily I don't need to take care of this ...)
340 	 */
341 	if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
342 		goto fail;
343 	}
344 
345 	/*
346 	 * Just to remember them ...
347 	 */
348 	rule->lr_pattern = strdup( pattern );
349 	rule->lr_subststring = strdup( result );
350 	rule->lr_flagstring = strdup( flagstring );
351 	if ( rule->lr_pattern == NULL
352 		|| rule->lr_subststring == NULL
353 		|| rule->lr_flagstring == NULL )
354 	{
355 		goto fail;
356 	}
357 
358 	/*
359 	 * Load compiled data into rule
360 	 */
361 	rule->lr_subst = subst;
362 
363 	/*
364 	 * Set various parameters
365 	 */
366 	rule->lr_flags = flags;		/* don't really need any longer ... */
367 	rule->lr_mode = mode;
368 	rule->lr_max_passes = max_passes;
369 	rule->lr_action = first_action;
370 
371 	/*
372 	 * Append rule at the end of the rewrite context
373 	 */
374 	append_rule( context, rule );
375 
376 	return REWRITE_SUCCESS;
377 
378 fail:
379 	if ( rule ) {
380 		if ( rule->lr_pattern ) free( rule->lr_pattern );
381 		if ( rule->lr_subststring ) free( rule->lr_subststring );
382 		if ( rule->lr_flagstring ) free( rule->lr_flagstring );
383 		free( rule );
384 	}
385 	destroy_actions( first_action );
386 	free( subst );
387 	return REWRITE_ERR;
388 }
389 
390 /*
391  * Rewrites string according to rule; may return:
392  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
393  *      STOP:   fine, rule matched; stop processing following rules
394  *      UNWILL: rule matched; force 'unwilling to perform'
395  */
396 int
rewrite_rule_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_rule * rule,const char * arg,char ** result)397 rewrite_rule_apply(
398 		struct rewrite_info *info,
399 		struct rewrite_op *op,
400 		struct rewrite_rule *rule,
401 		const char *arg,
402 		char **result
403 		)
404 {
405 	size_t nmatch = REWRITE_MAX_MATCH;
406 	regmatch_t match[ REWRITE_MAX_MATCH ];
407 
408 	int rc = REWRITE_SUCCESS;
409 
410 	char *string;
411 	int strcnt = 0;
412 	struct berval val = { 0, NULL };
413 
414 	assert( info != NULL );
415 	assert( op != NULL );
416 	assert( rule != NULL );
417 	assert( arg != NULL );
418 	assert( result != NULL );
419 
420 	*result = NULL;
421 
422 	string = (char *)arg;
423 
424 	/*
425 	 * In case recursive match is required (default)
426 	 */
427 recurse:;
428 
429 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
430 			" rule='%s' string='%s' [%d pass(es)]\n",
431 			rule->lr_pattern, string, strcnt + 1 );
432 
433 	op->lo_num_passes++;
434 
435 	rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
436 	if ( rc != 0 ) {
437 		if ( *result == NULL && string != arg ) {
438 			free( string );
439 		}
440 
441 		/*
442 		 * No match is OK; *result = NULL means no match
443 		 */
444 		return REWRITE_REGEXEC_OK;
445 	}
446 
447 	rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
448 			match, &val );
449 
450 	*result = val.bv_val;
451 	val.bv_val = NULL;
452 	if ( string != arg ) {
453 		free( string );
454 		string = NULL;
455 	}
456 
457 	if ( rc != REWRITE_REGEXEC_OK ) {
458 		return rc;
459 	}
460 
461 	if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
462 			&& op->lo_num_passes < info->li_max_passes
463 			&& ++strcnt < rule->lr_max_passes ) {
464 		string = *result;
465 
466 		goto recurse;
467 	}
468 
469 	return REWRITE_REGEXEC_OK;
470 }
471 
472 int
rewrite_rule_destroy(struct rewrite_rule ** prule)473 rewrite_rule_destroy(
474 		struct rewrite_rule **prule
475 		)
476 {
477 	struct rewrite_rule *rule;
478 
479 	assert( prule != NULL );
480 	assert( *prule != NULL );
481 
482 	rule = *prule;
483 
484 	if ( rule->lr_pattern ) {
485 		free( rule->lr_pattern );
486 		rule->lr_pattern = NULL;
487 	}
488 
489 	if ( rule->lr_subststring ) {
490 		free( rule->lr_subststring );
491 		rule->lr_subststring = NULL;
492 	}
493 
494 	if ( rule->lr_flagstring ) {
495 		free( rule->lr_flagstring );
496 		rule->lr_flagstring = NULL;
497 	}
498 
499 	if ( rule->lr_subst ) {
500 		rewrite_subst_destroy( &rule->lr_subst );
501 	}
502 
503 	regfree( &rule->lr_regex );
504 
505 	destroy_actions( rule->lr_action );
506 
507 	free( rule );
508 	*prule = NULL;
509 
510 	return 0;
511 }
512 
513