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