1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 2000-2011 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 */
28static int
29rewrite_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 */
51static int
52rewrite_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 */
73struct rewrite_context *
74rewrite_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 */
107struct rewrite_context *
108rewrite_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 */
164static struct rewrite_rule *
165rewrite_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 */
197int
198rewrite_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
219	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
220			" [depth=%d] string='%s'\n",
221			op->lo_depth, string, 0 );
222	assert( op->lo_depth > 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
412rc_continue:;	/* sent here by actions that require to continue */
413
414	}
415
416rc_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
429void
430rewrite_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
441int
442rewrite_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