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