]> git.sur5r.net Git - openldap/blob - libraries/librewrite/context.c
4758c8fff7b0ebb07c503a4a473bc1cd3b89a100
[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%s%s%s", "", "",  "");
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%s%s%s",
277                                                         "", "", "" );
278                                                 do_continue = 1;
279                                                 break;
280
281                                         /*
282                                          * Goto is honored only if it comes
283                                          * after ignore error
284                                          */
285                                         case REWRITE_ACTION_GOTO:
286                                                 if ( do_continue ) {
287                                                         rule = rewrite_action_goto( action, rule );
288                                                         if ( rule == NULL ) {
289                                                                 return_code = REWRITE_REGEXEC_ERR;
290                                                                 goto rc_end_of_context;
291                                                         }
292                                                 }
293                                                 break;
294
295                                         /*
296                                          * Other actions are ignored
297                                          */
298                                         default:
299                                                 break;
300                                         }
301                                 }
302
303                                 if ( do_continue ) {
304                                         if ( rule->lr_next == NULL ) {
305                                                 res = s;
306                                         }
307                                         goto rc_continue;
308                                 }
309                         }
310
311                         /* 
312                          * Default behavior is to bail out ...
313                          */
314                         return_code = REWRITE_REGEXEC_ERR;
315                         goto rc_end_of_context;
316                 
317                 /*
318                  * OK means there were no errors or special return codes;
319                  * if res is defined, it means the rule matched and we
320                  * got a sucessful rewriting
321                  */
322                 case REWRITE_REGEXEC_OK:
323
324                         /*
325                          * It matched! Check for actions ...
326                          */
327                         if ( res != NULL ) {
328                                 struct rewrite_action *action;
329                                 
330                                 free( s );
331                                 s = res;
332
333                                 for ( action = rule->lr_action;
334                                                 action != NULL;
335                                                 action = action->la_next ) {
336
337                                         switch ( action->la_type ) {
338
339                                         /*
340                                          * This ends the rewrite context
341                                          * successfully
342                                          */
343                                         case REWRITE_ACTION_STOP:
344                                                 goto rc_end_of_context;
345                                         
346                                         /*
347                                          * This instructs the server to return
348                                          * an `unwilling to perform' error
349                                          * message
350                                          */
351                                         case REWRITE_ACTION_UNWILLING:
352                                                 return_code = REWRITE_REGEXEC_UNWILLING;
353                                                 goto rc_end_of_context;
354                                         
355                                         /*
356                                          * This causes the processing to
357                                          * jump n rules back and forth
358                                          */
359                                         case REWRITE_ACTION_GOTO:
360                                                 rule = rewrite_action_goto( action, rule );
361                                                 if ( rule == NULL ) {
362                                                         return_code = REWRITE_REGEXEC_ERR;
363                                                         goto rc_end_of_context;
364                                                 }
365                                                 break;
366                                         
367                                         default:
368                                                 /* ... */
369                                                 break;
370                                         }
371                                 }
372
373                         /*
374                          * If result was OK and string didn't match,
375                          * in case of last rule we need to set the
376                          * result back to the string
377                          */
378                         } else if ( rule->lr_next == NULL ) {
379                                 res = s;
380                         }
381                         
382                         break;
383
384                 /*
385                  * A STOP has propagated ...
386                  */
387                 case REWRITE_REGEXEC_STOP:
388                         goto rc_end_of_context;
389
390                 /*
391                  * This will instruct the server to return
392                  * an `unwilling to perform' error message
393                  */
394                  case REWRITE_REGEXEC_UNWILLING:
395                         return_code = REWRITE_REGEXEC_UNWILLING;
396                         goto rc_end_of_context;
397
398                 }
399                 
400 rc_continue:;   /* sent here by actions that require to continue */
401
402         }
403
404 rc_end_of_context:;
405         *result = res;
406
407         Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
408                         " [depth=%d] res={%d,'%s'}\n",
409                         op->lo_depth, return_code, ( res ? res : "NULL" ) );
410
411         assert( op->lo_depth > 0 );
412         op->lo_depth--;
413
414         return return_code;
415 }
416
417 void
418 rewrite_context_free(
419                 void *tmp
420 )
421 {
422         struct rewrite_context *context = (struct rewrite_context *)tmp;
423
424         assert( tmp );
425
426         rewrite_context_destroy( &context );
427 }
428
429 int
430 rewrite_context_destroy(
431                 struct rewrite_context **pcontext
432 )
433 {
434         struct rewrite_context *context;
435         struct rewrite_rule *r;
436
437         assert( pcontext );
438         assert( *pcontext );
439
440         context = *pcontext;
441
442         assert( context->lc_rule );
443
444         for ( r = context->lc_rule->lr_next; r; ) {
445                 struct rewrite_rule *cr = r;
446
447                 r = r->lr_next;
448                 rewrite_rule_destroy( &cr );
449         }
450
451         free( context->lc_rule );
452         context->lc_rule = NULL;
453
454         assert( context->lc_name );
455         free( context->lc_name );
456         context->lc_name = NULL;
457
458         free( context );
459         *pcontext = NULL;
460         
461         return 0;
462 }