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#include "rewrite-map.h"
24
25/*
26 * Parses a plugin map
27 */
28static int
29rewrite_parse_builtin_map(
30		struct rewrite_info *info,
31		const char *fname,
32		int lineno,
33		int argc,
34		char **argv
35);
36
37/*
38 * Parses a config line and takes actions to fit content in rewrite structure;
39 * lines handled are of the form:
40 *
41 *      rewriteEngine 		{on|off}
42 *      rewriteMaxPasses        numPasses [numPassesPerRule]
43 *      rewriteContext 		contextName [alias aliasedContextName]
44 *      rewriteRule 		pattern substPattern [ruleFlags]
45 *      rewriteMap 		mapType mapName [mapArgs]
46 *      rewriteParam		paramName paramValue
47 */
48int
49rewrite_parse(
50		struct rewrite_info *info,
51		const char *fname,
52		int lineno,
53		int argc,
54		char **argv
55)
56{
57	int rc = -1;
58
59	assert( info != NULL );
60	assert( fname != NULL );
61	assert( argv != NULL );
62	assert( argc > 0 );
63
64	/*
65	 * Switch on the rewrite engine
66	 */
67	if ( strcasecmp( argv[ 0 ], "rewriteEngine" ) == 0 ) {
68		if ( argc < 2 ) {
69			Debug( LDAP_DEBUG_ANY,
70					"[%s:%d] rewriteEngine needs 'state'\n%s",
71					fname, lineno, "" );
72			return -1;
73
74		} else if ( argc > 2 ) {
75			Debug( LDAP_DEBUG_ANY,
76					"[%s:%d] extra fields in rewriteEngine"
77					" will be discarded\n%s",
78					fname, lineno, "" );
79		}
80
81		if ( strcasecmp( argv[ 1 ], "on" ) == 0 ) {
82			info->li_state = REWRITE_ON;
83
84		} else if ( strcasecmp( argv[ 1 ], "off" ) == 0 ) {
85			info->li_state = REWRITE_OFF;
86
87		} else {
88			Debug( LDAP_DEBUG_ANY,
89					"[%s:%d] unknown 'state' in rewriteEngine;"
90					" assuming 'on'\n%s",
91					fname, lineno, "" );
92			info->li_state = REWRITE_ON;
93		}
94		rc = REWRITE_SUCCESS;
95
96	/*
97	 * Alter max passes
98	 */
99	} else if ( strcasecmp( argv[ 0 ], "rewriteMaxPasses" ) == 0 ) {
100		if ( argc < 2 ) {
101			Debug( LDAP_DEBUG_ANY,
102					"[%s:%d] rewriteMaxPasses needs 'value'\n%s",
103					fname, lineno, "" );
104			return -1;
105		}
106
107		if ( lutil_atoi( &info->li_max_passes, argv[ 1 ] ) != 0 ) {
108			Debug( LDAP_DEBUG_ANY,
109					"[%s:%d] unable to parse rewriteMaxPasses=\"%s\"\n",
110					fname, lineno, argv[ 1 ] );
111			return -1;
112		}
113
114		if ( info->li_max_passes <= 0 ) {
115			Debug( LDAP_DEBUG_ANY,
116					"[%s:%d] negative or null rewriteMaxPasses\n",
117					fname, lineno, 0 );
118			return -1;
119		}
120
121		if ( argc > 2 ) {
122			if ( lutil_atoi( &info->li_max_passes_per_rule, argv[ 2 ] ) != 0 ) {
123				Debug( LDAP_DEBUG_ANY,
124						"[%s:%d] unable to parse rewriteMaxPassesPerRule=\"%s\"\n",
125						fname, lineno, argv[ 2 ] );
126				return -1;
127			}
128
129			if ( info->li_max_passes_per_rule <= 0 ) {
130				Debug( LDAP_DEBUG_ANY,
131						"[%s:%d] negative or null rewriteMaxPassesPerRule\n",
132						fname, lineno, 0 );
133				return -1;
134			}
135
136		} else {
137			info->li_max_passes_per_rule = info->li_max_passes;
138		}
139		rc = REWRITE_SUCCESS;
140
141	/*
142	 * Start a new rewrite context and set current context
143	 */
144	} else if ( strcasecmp( argv[ 0 ], "rewriteContext" ) == 0 ) {
145		if ( argc < 2 ) {
146			Debug( LDAP_DEBUG_ANY,
147					"[%s:%d] rewriteContext needs 'name'\n%s",
148					fname, lineno, "" );
149			return -1;
150		}
151
152		/*
153		 * Checks for existence (lots of contexts should be
154		 * available by default ...)
155		 */
156		 rewrite_int_curr_context = rewrite_context_find( info, argv[ 1 ] );
157		 if ( rewrite_int_curr_context == NULL ) {
158			 rewrite_int_curr_context = rewrite_context_create( info,
159					 argv[ 1 ] );
160		 }
161		 if ( rewrite_int_curr_context == NULL ) {
162			 return -1;
163		 }
164
165		 if ( argc > 2 ) {
166
167			 /*
168			  * A context can alias another (e.g., the `builtin'
169			  * contexts for backend operations, if not defined,
170			  * alias the `default' rewrite context (with the
171			  * notable exception of the searchResult context,
172			  * which can be undefined)
173			  */
174			 if ( strcasecmp( argv[ 2 ], "alias" ) == 0 ) {
175				 struct rewrite_context *aliased;
176
177				 if ( argc == 3 ) {
178					 Debug( LDAP_DEBUG_ANY,
179							 "[%s:%d] rewriteContext"
180							 " needs 'name' after"
181							 " 'alias'\n%s",
182							 fname, lineno, "" );
183					 return -1;
184
185				 } else if ( argc > 4 ) {
186					 Debug( LDAP_DEBUG_ANY,
187							 "[%s:%d] extra fields in"
188							 " rewriteContext"
189							 " after aliased name"
190							 " will be"
191							 " discarded\n%s",
192							 fname, lineno, "" );
193				 }
194
195				 aliased = rewrite_context_find( info,
196						 argv[ 3 ] );
197				 if ( aliased == NULL ) {
198					 Debug( LDAP_DEBUG_ANY,
199							 "[%s:%d] aliased"
200							 " rewriteContext '%s'"
201							 " does not exists\n",
202							 fname, lineno,
203							 argv[ 3 ] );
204					 return -1;
205				 }
206
207				 rewrite_int_curr_context->lc_alias = aliased;
208				 rewrite_int_curr_context = aliased;
209
210			 } else {
211				 Debug( LDAP_DEBUG_ANY,
212						 "[%s:%d] extra fields"
213						 " in rewriteContext"
214						 " will be discarded\n%s",
215						 fname, lineno, "" );
216			 }
217		 }
218		 rc = REWRITE_SUCCESS;
219
220	/*
221	 * Compile a rule in current context
222	 */
223	} else if ( strcasecmp( argv[ 0 ], "rewriteRule" ) == 0 ) {
224		if ( argc < 3 ) {
225			Debug( LDAP_DEBUG_ANY,
226					"[%s:%d] rewriteRule needs 'pattern'"
227					" 'subst' ['flags']\n%s",
228					fname, lineno, "" );
229			return -1;
230
231		} else if ( argc > 4 ) {
232			Debug( LDAP_DEBUG_ANY,
233					"[%s:%d] extra fields in rewriteRule"
234					" will be discarded\n%s",
235					fname, lineno, "" );
236		}
237
238		if ( rewrite_int_curr_context == NULL ) {
239			Debug( LDAP_DEBUG_ANY,
240					"[%s:%d] rewriteRule outside a"
241					" context; will add to default\n%s",
242					fname, lineno, "" );
243			rewrite_int_curr_context = rewrite_context_find( info,
244					REWRITE_DEFAULT_CONTEXT );
245
246			/*
247			 * Default context MUST exist in a properly initialized
248			 * struct rewrite_info
249			 */
250			assert( rewrite_int_curr_context != NULL );
251		}
252
253		rc = rewrite_rule_compile( info, rewrite_int_curr_context, argv[ 1 ],
254				argv[ 2 ], ( argc == 4 ? argv[ 3 ] : "" ) );
255
256	/*
257	 * Add a plugin map to the map tree
258	 */
259	} else if ( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ) {
260		if ( argc < 3 ) {
261			Debug( LDAP_DEBUG_ANY,
262					"[%s:%d] rewriteMap needs at least 'type'"
263					" and 'name' ['args']\n%s",
264					fname, lineno, "" );
265			return -1;
266		}
267
268		rc = rewrite_parse_builtin_map( info, fname, lineno,
269				argc, argv );
270
271	/*
272	 * Set the value of a global scope parameter
273	 */
274	} else if ( strcasecmp( argv[ 0 ], "rewriteParam" ) == 0 ) {
275		if ( argc < 3 ) {
276			Debug( LDAP_DEBUG_ANY,
277					"[%s:%d] rewriteParam needs 'name'"
278					" and 'value'\n%s",
279					fname, lineno, "" );
280			return -1;
281		}
282
283		rc = rewrite_param_set( info, argv[ 1 ], argv[ 2 ] );
284
285	/*
286	 * Error
287	 */
288	} else {
289		Debug( LDAP_DEBUG_ANY,
290				"[%s:%d] unknown command '%s'\n",
291				fname, lineno, "" );
292		return -1;
293	}
294
295	return rc;
296}
297
298/*
299 * Compares two maps
300 */
301static int
302rewrite_builtin_map_cmp(
303		const void *c1,
304                const void *c2
305)
306{
307	const struct rewrite_builtin_map *m1, *m2;
308
309        m1 = ( const struct rewrite_builtin_map * )c1;
310        m2 = ( const struct rewrite_builtin_map * )c2;
311
312        assert( m1 != NULL );
313        assert( m2 != NULL );
314        assert( m1->lb_name != NULL );
315        assert( m2->lb_name != NULL );
316
317        return strcasecmp( m1->lb_name, m2->lb_name );
318}
319
320/*
321 * Duplicate map ?
322 */
323static int
324rewrite_builtin_map_dup(
325	                void *c1,
326	                void *c2
327)
328{
329        struct rewrite_builtin_map *m1, *m2;
330
331        m1 = ( struct rewrite_builtin_map * )c1;
332        m2 = ( struct rewrite_builtin_map * )c2;
333
334        assert( m1 != NULL );
335        assert( m2 != NULL );
336        assert( m1->lb_name != NULL );
337        assert( m2->lb_name != NULL );
338
339        return ( strcasecmp( m1->lb_name, m2->lb_name ) == 0 ? -1 : 0 );
340}
341
342/*
343 * Adds a map to the info map tree
344 */
345static int
346rewrite_builtin_map_insert(
347		struct rewrite_info *info,
348		struct rewrite_builtin_map *map
349)
350{
351	/*
352	 * May need a mutex?
353	 */
354	return avl_insert( &info->li_maps, ( caddr_t )map,
355			rewrite_builtin_map_cmp,
356		       	rewrite_builtin_map_dup );
357}
358
359/*
360 * Retrieves a map
361 */
362struct rewrite_builtin_map *
363rewrite_builtin_map_find(
364		struct rewrite_info *info,
365		const char *name
366)
367{
368	struct rewrite_builtin_map tmp;
369
370	assert( info != NULL );
371	assert( name != NULL );
372
373	tmp.lb_name = ( char * )name;
374
375	return ( struct rewrite_builtin_map * )avl_find( info->li_maps,
376			( caddr_t )&tmp, rewrite_builtin_map_cmp );
377}
378
379/*
380 * Parses a plugin map
381 */
382static int
383rewrite_parse_builtin_map(
384		struct rewrite_info *info,
385		const char *fname,
386		int lineno,
387		int argc,
388		char **argv
389)
390{
391	struct rewrite_builtin_map *map;
392
393#define MAP_TYPE	1
394#define MAP_NAME	2
395
396	assert( info != NULL );
397	assert( fname != NULL );
398	assert( argc > 2 );
399	assert( argv != NULL );
400	assert( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 );
401
402	map = calloc( sizeof( struct rewrite_builtin_map ), 1 );
403	if ( map == NULL ) {
404		return REWRITE_ERR;
405	}
406
407	map->lb_name = strdup( argv[ MAP_NAME ] );
408	if ( map->lb_name == NULL ) {
409		free( map );
410		return REWRITE_ERR;
411	}
412
413	/*
414	 * Built-in ldap map
415	 */
416	if (( map->lb_mapper = rewrite_mapper_find( argv[ MAP_TYPE ] ))) {
417		map->lb_type = REWRITE_BUILTIN_MAP;
418
419#ifdef USE_REWRITE_LDAP_PVT_THREADS
420		if ( ldap_pvt_thread_mutex_init( & map->lb_mutex ) ) {
421			free( map->lb_name );
422			free( map );
423			return REWRITE_ERR;
424		}
425#endif /* USE_REWRITE_LDAP_PVT_THREADS */
426
427		map->lb_private = map->lb_mapper->rm_config( fname, lineno,
428				argc - 3, argv + 3 );
429
430	/*
431	 * Error
432	 */
433	} else {
434		free( map );
435		Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n%s",
436				fname, lineno, "" );
437		return -1;
438	}
439
440	return rewrite_builtin_map_insert( info, map );
441}
442