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