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