1/*	$NetBSD: context.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2000-2021 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 */
30static int
31rewrite_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 */
53static int
54rewrite_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 */
75struct rewrite_context *
76rewrite_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 *)ldap_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 */
109struct rewrite_context *
110rewrite_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 = ldap_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 */
166static struct rewrite_rule *
167rewrite_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 */
199int
200rewrite_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
221	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
222			" [depth=%d] string='%s'\n",
223			op->lo_depth, string );
224	assert( op->lo_depth > 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" );
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" );
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 successful 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
414rc_continue:;	/* sent here by actions that require to continue */
415
416	}
417
418rc_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
431void
432rewrite_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
443int
444rewrite_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