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