]> git.sur5r.net Git - openldap/blob - libraries/librewrite/context.c
return to releng
[openldap] / libraries / librewrite / context.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2000-2004 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%s",
222                         op->lo_depth, string, "" );
223         
224         s = strdup( 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                                 free( s );
325                                 s = res;
326
327                                 for ( action = rule->lr_action;
328                                                 action != NULL;
329                                                 action = action->la_next ) {
330
331                                         switch ( action->la_type ) {
332
333                                         /*
334                                          * This ends the rewrite context
335                                          * successfully
336                                          */
337                                         case REWRITE_ACTION_STOP:
338                                                 goto rc_end_of_context;
339                                         
340                                         /*
341                                          * This instructs the server to return
342                                          * an `unwilling to perform' error
343                                          * message
344                                          */
345                                         case REWRITE_ACTION_UNWILLING:
346                                                 return_code = REWRITE_REGEXEC_UNWILLING;
347                                                 goto rc_end_of_context;
348                                         
349                                         /*
350                                          * This causes the processing to
351                                          * jump n rules back and forth
352                                          */
353                                         case REWRITE_ACTION_GOTO:
354                                                 rule = rewrite_action_goto( action, rule );
355                                                 if ( rule == NULL ) {
356                                                         return_code = REWRITE_REGEXEC_ERR;
357                                                         goto rc_end_of_context;
358                                                 }
359                                                 break;
360                                         
361                                         default:
362                                                 /* ... */
363                                                 break;
364                                         }
365                                 }
366
367                         /*
368                          * If result was OK and string didn't match,
369                          * in case of last rule we need to set the
370                          * result back to the string
371                          */
372                         } else if ( rule->lr_next == NULL ) {
373                                 res = s;
374                         }
375                         
376                         break;
377
378                 /*
379                  * A STOP has propagated ...
380                  */
381                 case REWRITE_REGEXEC_STOP:
382                         goto rc_end_of_context;
383
384                 /*
385                  * This will instruct the server to return
386                  * an `unwilling to perform' error message
387                  */
388                  case REWRITE_REGEXEC_UNWILLING:
389                         return_code = REWRITE_REGEXEC_UNWILLING;
390                         goto rc_end_of_context;
391
392                 }
393                 
394 rc_continue:;   /* sent here by actions that require to continue */
395
396         }
397
398 rc_end_of_context:;
399         *result = res;
400
401         Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
402                         " [depth=%d] res={%d,'%s'}\n",
403                         op->lo_depth, return_code, ( res ? res : "NULL" ) );
404
405         assert( op->lo_depth > 0 );
406         op->lo_depth--;
407
408         return return_code;
409 }
410
411 void
412 rewrite_context_free(
413                 void *tmp
414 )
415 {
416         struct rewrite_context *context = (struct rewrite_context *)tmp;
417
418         assert( tmp );
419
420         rewrite_context_destroy( &context );
421 }
422
423 int
424 rewrite_context_destroy(
425                 struct rewrite_context **pcontext
426 )
427 {
428         struct rewrite_context *context;
429         struct rewrite_rule *r;
430
431         assert( pcontext );
432         assert( *pcontext );
433
434         context = *pcontext;
435
436         assert( context->lc_rule );
437
438         for ( r = context->lc_rule->lr_next; r; ) {
439                 struct rewrite_rule *cr = r;
440
441                 r = r->lr_next;
442                 rewrite_rule_destroy( &cr );
443         }
444
445         free( context->lc_rule );
446         context->lc_rule = NULL;
447
448         assert( context->lc_name );
449         free( context->lc_name );
450         context->lc_name = NULL;
451
452         free( context );
453         *pcontext = NULL;
454         
455         return 0;
456 }