xref: /netbsd-src/external/bsd/openldap/dist/libraries/librewrite/context.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /* $OpenLDAP: pkg/ldap/libraries/librewrite/context.c,v 1.15.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  * Compares two struct rewrite_context based on the name;
26  * used by avl stuff
27  */
28 static int
29 rewrite_context_cmp(
30 		const void *c1,
31 		const void *c2
32 )
33 {
34 	const struct rewrite_context *lc1, *lc2;
35 
36 	lc1 = (const struct rewrite_context *)c1;
37 	lc2 = (const struct rewrite_context *)c2;
38 
39 	assert( c1 != NULL );
40 	assert( c2 != NULL );
41 	assert( lc1->lc_name != NULL );
42 	assert( lc2->lc_name != NULL );
43 
44 	return strcasecmp( lc1->lc_name, lc2->lc_name );
45 }
46 
47 /*
48  * Returns -1 in case a duplicate struct rewrite_context
49  * has been inserted; used by avl stuff
50  */
51 static int
52 rewrite_context_dup(
53 		void *c1,
54 		void *c2
55 		)
56 {
57 	struct rewrite_context *lc1, *lc2;
58 
59 	lc1 = (struct rewrite_context *)c1;
60 	lc2 = (struct rewrite_context *)c2;
61 
62 	assert( c1 != NULL );
63 	assert( c2 != NULL );
64 	assert( lc1->lc_name != NULL );
65 	assert( lc2->lc_name != NULL );
66 
67 	return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 );
68 }
69 
70 /*
71  * Finds the context named rewriteContext in the context tree
72  */
73 struct rewrite_context *
74 rewrite_context_find(
75 		struct rewrite_info *info,
76 		const char *rewriteContext
77 )
78 {
79 	struct rewrite_context *context, c;
80 
81 	assert( info != NULL );
82 	assert( rewriteContext != NULL );
83 
84 	/*
85 	 * Fetches the required rewrite context
86 	 */
87 	c.lc_name = (char *)rewriteContext;
88 	context = (struct rewrite_context *)avl_find( info->li_context,
89 			(caddr_t)&c, rewrite_context_cmp );
90 	if ( context == NULL ) {
91 		return NULL;
92 	}
93 
94 	/*
95 	 * De-aliases the context if required
96 	 */
97 	if ( context->lc_alias ) {
98 		return context->lc_alias;
99 	}
100 
101 	return context;
102 }
103 
104 /*
105  * Creates a new context called rewriteContext and stores in into the tree
106  */
107 struct rewrite_context *
108 rewrite_context_create(
109 		struct rewrite_info *info,
110 		const char *rewriteContext
111 )
112 {
113 	struct rewrite_context *context;
114 	int rc;
115 
116 	assert( info != NULL );
117 	assert( rewriteContext != NULL );
118 
119 	context = calloc( sizeof( struct rewrite_context ), 1 );
120 	if ( context == NULL ) {
121 		return NULL;
122 	}
123 
124 	/*
125 	 * Context name
126 	 */
127 	context->lc_name = strdup( rewriteContext );
128 	if ( context->lc_name == NULL ) {
129 		free( context );
130 		return NULL;
131 	}
132 
133 	/*
134 	 * The first, empty rule
135 	 */
136 	context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 );
137 	if ( context->lc_rule == NULL ) {
138 		free( context->lc_name );
139 		free( context );
140 		return NULL;
141 	}
142 	memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
143 
144 	/*
145 	 * Add context to tree
146 	 */
147 	rc = avl_insert( &info->li_context, (caddr_t)context,
148 			rewrite_context_cmp, rewrite_context_dup );
149 	if ( rc == -1 ) {
150 		free( context->lc_rule );
151 		free( context->lc_name );
152 		free( context );
153 		return NULL;
154 	}
155 
156 	return context;
157 }
158 
159 /*
160  * Finds the next rule according to a goto action statement,
161  * or null in case of error.
162  * Helper for rewrite_context_apply.
163  */
164 static struct rewrite_rule *
165 rewrite_action_goto(
166 		struct rewrite_action *action,
167 		struct rewrite_rule *rule
168 )
169 {
170 	int n;
171 
172 	assert( action != NULL );
173 	assert( action->la_args != NULL );
174 	assert( rule != NULL );
175 
176 	n = ((int *)action->la_args)[ 0 ];
177 
178 	if ( n > 0 ) {
179 		for ( ; n > 1 && rule != NULL ; n-- ) {
180 			rule = rule->lr_next;
181 		}
182 	} else if ( n <= 0 ) {
183 		for ( ; n < 1 && rule != NULL ; n++ ) {
184 			rule = rule->lr_prev;
185 		}
186 	}
187 
188 	return rule;
189 }
190 
191 /*
192  * Rewrites string according to context; may return:
193  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
194  *      STOP:   fine, rule matched; stop processing following rules
195  *      UNWILL: rule matched; force 'unwilling to perform'
196  */
197 int
198 rewrite_context_apply(
199 		struct rewrite_info *info,
200 		struct rewrite_op *op,
201 		struct rewrite_context *context,
202 		const char *string,
203 		char **result
204 )
205 {
206 	struct rewrite_rule *rule;
207 	char *s, *res = NULL;
208 	int return_code = REWRITE_REGEXEC_OK;
209 
210 	assert( info != NULL );
211 	assert( op != NULL );
212 	assert( context != NULL );
213 	assert( context->lc_rule != NULL );
214 	assert( string != NULL );
215 	assert( result != NULL );
216 
217 	op->lo_depth++;
218 	assert( op->lo_depth > 0 );
219 
220 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
221 			" [depth=%d] string='%s'\n",
222 			op->lo_depth, string, 0 );
223 
224 	s = (char *)string;
225 
226 	for ( rule = context->lc_rule->lr_next;
227 			rule != NULL && op->lo_num_passes < info->li_max_passes;
228 			rule = rule->lr_next, op->lo_num_passes++ ) {
229 		int rc;
230 
231 		/*
232 		 * Apply a single rule
233 		 */
234 		rc = rewrite_rule_apply( info, op, rule, s, &res );
235 
236 		/*
237 		 * A rule may return:
238 		 * 	OK 		with result != NULL if matched
239 		 * 	ERR		if anything was wrong
240 		 * 	UNWILLING	if the server should drop the request
241 		 * the latter case in honored immediately;
242 		 * the other two may require some special actions to take
243 		 * place.
244 		 */
245 		switch ( rc ) {
246 
247 		case REWRITE_REGEXEC_ERR:
248 			Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
249 					" error ...\n", 0, 0, 0);
250 
251 			/*
252 			 * Checks for special actions to be taken
253 			 * in case of error ...
254 			 */
255 			if ( rule->lr_action != NULL ) {
256 				struct rewrite_action *action;
257 				int do_continue = 0;
258 
259 				for ( action = rule->lr_action;
260 						action != NULL;
261 						action = action->la_next ) {
262 					switch ( action->la_type ) {
263 
264 					/*
265 					 * This action takes precedence
266 					 * over the others in case of failure
267 					 */
268 					case REWRITE_ACTION_IGNORE_ERR:
269 						Debug( LDAP_DEBUG_ANY,
270 					"==> rewrite_context_apply"
271 					" ignoring error ...\n", 0, 0, 0 );
272 						do_continue = 1;
273 						break;
274 
275 					/*
276 					 * Goto is honored only if it comes
277 					 * after ignore error
278 					 */
279 					case REWRITE_ACTION_GOTO:
280 						if ( do_continue ) {
281 							rule = rewrite_action_goto( action, rule );
282 							if ( rule == NULL ) {
283 								return_code = REWRITE_REGEXEC_ERR;
284 								goto rc_end_of_context;
285 							}
286 						}
287 						break;
288 
289 					/*
290 					 * Other actions are ignored
291 					 */
292 					default:
293 						break;
294 					}
295 				}
296 
297 				if ( do_continue ) {
298 					if ( rule->lr_next == NULL ) {
299 						res = s;
300 					}
301 					goto rc_continue;
302 				}
303 			}
304 
305 			/*
306 			 * Default behavior is to bail out ...
307 			 */
308 			return_code = REWRITE_REGEXEC_ERR;
309 			goto rc_end_of_context;
310 
311 		/*
312 		 * OK means there were no errors or special return codes;
313 		 * if res is defined, it means the rule matched and we
314 		 * got a sucessful rewriting
315 		 */
316 		case REWRITE_REGEXEC_OK:
317 
318 			/*
319 			 * It matched! Check for actions ...
320 			 */
321 			if ( res != NULL ) {
322 				struct rewrite_action *action;
323 
324 				if ( s != string && s != res ) {
325 					free( s );
326 				}
327 				s = res;
328 
329 				for ( action = rule->lr_action;
330 						action != NULL;
331 						action = action->la_next ) {
332 
333 					switch ( action->la_type ) {
334 
335 					/*
336 					 * This ends the rewrite context
337 					 * successfully
338 					 */
339 					case REWRITE_ACTION_STOP:
340 						goto rc_end_of_context;
341 
342 					/*
343 					 * This instructs the server to return
344 					 * an `unwilling to perform' error
345 					 * message
346 					 */
347 					case REWRITE_ACTION_UNWILLING:
348 						return_code = REWRITE_REGEXEC_UNWILLING;
349 						goto rc_end_of_context;
350 
351 					/*
352 					 * This causes the processing to
353 					 * jump n rules back and forth
354 					 */
355 					case REWRITE_ACTION_GOTO:
356 						rule = rewrite_action_goto( action, rule );
357 						if ( rule == NULL ) {
358 							return_code = REWRITE_REGEXEC_ERR;
359 							goto rc_end_of_context;
360 						}
361 						break;
362 
363 					/*
364 					 * This ends the rewrite context
365 					 * and returns a user-defined
366 					 * error code
367 					 */
368 					case REWRITE_ACTION_USER:
369 						return_code = ((int *)action->la_args)[ 0 ];
370 						goto rc_end_of_context;
371 
372 					default:
373 						/* ... */
374 						break;
375 					}
376 				}
377 
378 			/*
379 			 * If result was OK and string didn't match,
380 			 * in case of last rule we need to set the
381 			 * result back to the string
382 			 */
383 			} else if ( rule->lr_next == NULL ) {
384 				res = s;
385 			}
386 
387 			break;
388 
389 		/*
390 		 * A STOP has propagated ...
391 		 */
392 		case REWRITE_REGEXEC_STOP:
393 			goto rc_end_of_context;
394 
395 		/*
396 		 * This will instruct the server to return
397 		 * an `unwilling to perform' error message
398 		 */
399 		case REWRITE_REGEXEC_UNWILLING:
400 			return_code = REWRITE_REGEXEC_UNWILLING;
401 			goto rc_end_of_context;
402 
403 		/*
404 		 * A user-defined error code has propagated ...
405 		 */
406 		default:
407 			assert( rc >= REWRITE_REGEXEC_USER );
408 			goto rc_end_of_context;
409 
410 		}
411 
412 rc_continue:;	/* sent here by actions that require to continue */
413 
414 	}
415 
416 rc_end_of_context:;
417 	*result = res;
418 
419 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
420 			" [depth=%d] res={%d,'%s'}\n",
421 			op->lo_depth, return_code, ( res ? res : "NULL" ) );
422 
423 	assert( op->lo_depth > 0 );
424 	op->lo_depth--;
425 
426 	return return_code;
427 }
428 
429 void
430 rewrite_context_free(
431 		void *tmp
432 )
433 {
434 	struct rewrite_context *context = (struct rewrite_context *)tmp;
435 
436 	assert( tmp != NULL );
437 
438 	rewrite_context_destroy( &context );
439 }
440 
441 int
442 rewrite_context_destroy(
443 		struct rewrite_context **pcontext
444 )
445 {
446 	struct rewrite_context *context;
447 	struct rewrite_rule *r;
448 
449 	assert( pcontext != NULL );
450 	assert( *pcontext != NULL );
451 
452 	context = *pcontext;
453 
454 	assert( context->lc_rule != NULL );
455 
456 	for ( r = context->lc_rule->lr_next; r; ) {
457 		struct rewrite_rule *cr = r;
458 
459 		r = r->lr_next;
460 		rewrite_rule_destroy( &cr );
461 	}
462 
463 	free( context->lc_rule );
464 	context->lc_rule = NULL;
465 
466 	assert( context->lc_name != NULL );
467 	free( context->lc_name );
468 	context->lc_name = NULL;
469 
470 	free( context );
471 	*pcontext = NULL;
472 
473 	return 0;
474 }
475