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