]> git.sur5r.net Git - openldap/blob - libraries/librewrite/context.c
More contrib cleanout
[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         struct rewrite_context *lc1, *lc2;
40         
41         lc1 = (struct rewrite_context *)c1;
42         lc2 = (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         
148         /*
149          * Add context to tree
150          */
151         rc = avl_insert( &info->li_context, (caddr_t)context,
152                         rewrite_context_cmp, rewrite_context_dup );
153         if ( rc == -1 ) {
154                 free( context->lc_rule );
155                 free( context->lc_name );
156                 free( context );
157                 return NULL;
158         }
159
160         return context;
161 }
162
163 /*
164  * Finds the next rule according to a goto action statement,
165  * or null in case of error.
166  * Helper for rewrite_context_apply.
167  */
168 static struct rewrite_rule *
169 rewrite_action_goto(
170                 struct rewrite_action *action,
171                 struct rewrite_rule *rule
172 )
173 {
174         int n;
175         
176         assert( action != NULL );
177         assert( action->la_args != NULL );
178         assert( rule != NULL );
179         
180         n = ((int *)action->la_args)[ 0 ];
181         
182         if ( n > 0 ) {
183                 for ( ; n > 1 && rule != NULL ; n-- ) {
184                         rule = rule->lr_next;
185                 }
186         } else if ( n <= 0 ) {
187                 for ( ; n < 1 && rule != NULL ; n++ ) {
188                         rule = rule->lr_prev;
189                 }
190         }
191
192         return rule;
193 }
194
195 /*
196  * Rewrites string according to context; may return:
197  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
198  *      STOP:   fine, rule matched; stop processing following rules
199  *      UNWILL: rule matched; force 'unwilling to perform'
200  */
201 int
202 rewrite_context_apply(
203                 struct rewrite_info *info,
204                 struct rewrite_op *op,
205                 struct rewrite_context *context,
206                 const char *string,
207                 char **result
208 )
209 {
210         struct rewrite_rule *rule;
211         char *s, *res = NULL;
212         int return_code = REWRITE_REGEXEC_OK;
213         
214         assert( info != NULL );
215         assert( op != NULL );
216         assert( context != NULL );
217         assert( context->lc_rule != NULL );
218         assert( string != NULL );
219         assert( result != NULL );
220
221         op->lo_depth++;
222         assert( op->lo_depth > 0 );
223
224         Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
225                         " [depth=%d] string='%s'\n%s",
226                         op->lo_depth, string, "" );
227         
228         s = strdup( string );
229         
230         for ( rule = context->lc_rule->lr_next;
231                         rule != NULL && op->lo_num_passes < info->li_max_passes;
232                         rule = rule->lr_next, op->lo_num_passes++ ) {
233                 int rc;
234                 
235                 /*
236                  * Apply a single rule
237                  */
238                 rc = rewrite_rule_apply( info, op, rule, s, &res );
239                 
240                 /*
241                  * A rule may return:
242                  *      OK              with result != NULL if matched
243                  *      ERR             if anything was wrong
244                  *      UNWILLING       if the server should drop the request
245                  * the latter case in honored immediately;
246                  * the other two may require some special actions to take
247                  * place.
248                  */
249                 switch ( rc ) {
250                         
251                 case REWRITE_REGEXEC_ERR:
252                         Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
253                                         " error ...\n%s%s%s", "", "",  "");
254
255                         /*
256                          * Checks for special actions to be taken
257                          * in case of error ...
258                          */
259                         if ( rule->lr_action != NULL ) {
260                                 struct rewrite_action *action;
261                                 int do_continue = 0;
262                                 
263                                 for ( action = rule->lr_action;
264                                                 action != NULL;
265                                                 action = action->la_next ) {
266                                         switch ( action->la_type ) {
267                                         
268                                         /*
269                                          * This action takes precedence
270                                          * over the others in case of failure
271                                          */
272                                         case REWRITE_ACTION_IGNORE_ERR:
273                                                 Debug( LDAP_DEBUG_ANY,
274                                         "==> rewrite_context_apply"
275                                         " ignoring error ...\n%s%s%s",
276                                                         "", "", "" );
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