]> git.sur5r.net Git - openldap/blob - libraries/librewrite/rule.c
Pick up last remaining changes
[openldap] / libraries / librewrite / rule.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2000-2005 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  * Appends a rule to the double linked list of rules
26  * Helper for rewrite_rule_compile
27  */
28 static int
29 append_rule(
30                 struct rewrite_context *context,
31                 struct rewrite_rule *rule
32 )
33 {
34         struct rewrite_rule *r;
35
36         assert( context != NULL );
37         assert( context->lc_rule != NULL );
38         assert( rule != NULL );
39
40         for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
41         r->lr_next = rule;
42         rule->lr_prev = r;
43         
44         return REWRITE_SUCCESS;
45 }
46
47 /*
48  * Appends an action to the linked list of actions
49  * Helper for rewrite_rule_compile
50  */
51 static int
52 append_action(
53                 struct rewrite_action **pbase,
54                 struct rewrite_action *action
55 )
56 {
57         struct rewrite_action **pa;
58
59         assert( pbase != NULL );
60         assert( action != NULL );
61         
62         for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
63         *pa = action;
64         
65         return REWRITE_SUCCESS;
66 }
67
68 static int
69 destroy_action(
70                 struct rewrite_action **paction
71 )
72 {
73         struct rewrite_action   *action;
74
75         assert( paction != NULL );
76         assert( *paction != NULL );
77
78         action = *paction;
79
80         /* do something */
81         switch ( action->la_type ) {
82         case REWRITE_FLAG_GOTO:
83         case REWRITE_FLAG_USER: {
84                 int *pi = (int *)action->la_args;
85
86                 if ( pi ) {
87                         free( pi );
88                 }
89                 break;
90         }
91
92         default:
93                 break;
94         }
95         
96         free( action );
97         *paction = NULL;
98         
99         return 0;
100 }
101
102 /*
103  * In case of error it returns NULL and does not free all the memory
104  * it allocated; as this is a once only phase, and an error at this stage
105  * would require the server to stop, there is no need to be paranoid
106  * about memory allocation
107  */
108 int
109 rewrite_rule_compile(
110                 struct rewrite_info *info,
111                 struct rewrite_context *context,
112                 const char *pattern,
113                 const char *result,
114                 const char *flagstring
115 )
116 {
117         int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
118         int mode = REWRITE_RECURSE;
119         int max_passes = info->li_max_passes_per_rule;
120
121         struct rewrite_rule *rule = NULL;
122         struct rewrite_subst *subst = NULL;
123         struct rewrite_action *action = NULL, *first_action = NULL;
124
125         const char *p;
126
127         assert( info != NULL );
128         assert( context != NULL );
129         assert( pattern != NULL );
130         assert( result != NULL );
131
132         /*
133          * A null flagstring should be allowed
134          */
135
136         /*
137          * Take care of substitution string
138          */
139         subst = rewrite_subst_compile( info, result );
140         if ( subst == NULL ) {
141                 return REWRITE_ERR;
142         }
143
144         /*
145          * Take care of flags
146          */
147         for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
148                 switch( p[ 0 ] ) {
149                         
150                 /*
151                  * REGEX flags
152                  */
153                 case REWRITE_FLAG_HONORCASE:            /* 'C' */
154                         /*
155                          * Honor case (default is case insensitive)
156                          */
157                         flags &= ~REWRITE_REGEX_ICASE;
158                         break;
159                         
160                 case REWRITE_FLAG_BASICREGEX:           /* 'R' */
161                         /*
162                          * Use POSIX Basic Regular Expression syntax
163                          * instead of POSIX Extended Regular Expression 
164                          * syntax (default)
165                          */
166                         flags &= ~REWRITE_REGEX_EXTENDED;
167                         break;
168                         
169                 /*
170                  * Execution mode flags
171                  */
172                 case REWRITE_FLAG_EXECONCE:             /* ':' */
173                         /*
174                          * Apply rule once only
175                          */
176                         mode &= ~REWRITE_RECURSE;
177                         mode |= REWRITE_EXEC_ONCE;
178                         break;
179                 
180                 /*
181                  * Special action flags
182                  */
183                 case REWRITE_FLAG_STOP:                 /* '@' */
184                         /*
185                          * Bail out after applying rule
186                          */
187                         action = calloc( sizeof( struct rewrite_action ), 1 );
188                         if ( action == NULL ) {
189                                 /* cleanup ... */
190                                 return REWRITE_ERR;
191                         }
192
193                         action->la_type = REWRITE_ACTION_STOP;
194                         break;
195                         
196                 case REWRITE_FLAG_UNWILLING:            /* '#' */
197                         /*
198                          * Matching objs will be marked as gone!
199                          */
200                         action = calloc( sizeof( struct rewrite_action ), 1 );
201                         if ( action == NULL ) {
202                                 /* cleanup ... */
203                                 return REWRITE_ERR;
204                         }
205                         
206                         mode &= ~REWRITE_RECURSE;
207                         mode |= REWRITE_EXEC_ONCE;
208                         action->la_type = REWRITE_ACTION_UNWILLING;
209                         break;
210
211                 case REWRITE_FLAG_GOTO:                         /* 'G' */
212                         /*
213                          * After applying rule, jump N rules
214                          */
215
216                 case REWRITE_FLAG_USER: {                       /* 'U' */
217                         /*
218                          * After applying rule, return user-defined
219                          * error code
220                          */
221                         char *next = NULL;
222                         int *d;
223                         
224                         if ( p[ 1 ] != '{' ) {
225                                 /* XXX Need to free stuff */
226                                 return REWRITE_ERR;
227                         }
228
229                         d = malloc( sizeof( int ) );
230                         if ( d == NULL ) {
231                                 /* XXX Need to free stuff */
232                                 return REWRITE_ERR;
233                         }
234
235                         d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
236                         if ( next == NULL || next == &p[ 2 ] || next[0] != '}' ) {
237                                 /* XXX Need to free stuff */
238                                 return REWRITE_ERR;
239                         }
240
241                         action = calloc( sizeof( struct rewrite_action ), 1 );
242                         if ( action == NULL ) {
243                                 /* cleanup ... */       
244                                 return REWRITE_ERR;
245                         }
246                         switch ( p[ 0 ] ) {
247                         case REWRITE_FLAG_GOTO:
248                                 action->la_type = REWRITE_ACTION_GOTO;
249                                 break;
250
251                         case REWRITE_FLAG_USER:
252                                 action->la_type = REWRITE_ACTION_USER;
253                                 break;
254
255                         default:
256                                 assert(0);
257                         }
258
259                         action->la_args = (void *)d;
260
261                         p = next;       /* p is incremented by the for ... */
262                 
263                         break;
264                 }
265
266                 case REWRITE_FLAG_MAX_PASSES: {                 /* 'U' */
267                         /*
268                          * Set the number of max passes per rule
269                          */
270                         char *next = NULL;
271                         
272                         if ( p[ 1 ] != '{' ) {
273                                 /* XXX Need to free stuff */
274                                 return REWRITE_ERR;
275                         }
276
277                         max_passes = strtol( &p[ 2 ], &next, 0 );
278                         if ( next == NULL || next == &p[ 2 ] || next[0] != '}' ) {
279                                 /* XXX Need to free stuff */
280                                 return REWRITE_ERR;
281                         }
282
283                         if ( max_passes < 1 ) {
284                                 /* FIXME: nonsense ... */
285                                 max_passes = 1;
286                         }
287
288                         p = next;       /* p is incremented by the for ... */
289                 
290                         break;
291                 }
292
293                 case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
294                         /*
295                          * Ignore errors!
296                          */
297                         action = calloc( sizeof( struct rewrite_action ), 1 );
298                         if ( action == NULL ) {
299                                 /* cleanup ... */
300                                 return REWRITE_ERR;
301                         }
302                         
303                         action->la_type = REWRITE_ACTION_IGNORE_ERR;
304                         break;
305                         
306                 /*
307                  * Other flags ...
308                  */
309                 default:
310                         /*
311                          * Unimplemented feature (complain only)
312                          */
313                         break;
314                 }
315                 
316                 /*
317                  * Stupid way to append to a list ...
318                  */
319                 if ( action != NULL ) {
320                         append_action( &first_action, action );
321                         action = NULL;
322                 }
323         }
324         
325         /*
326          * Finally, rule allocation
327          */
328         rule = calloc( sizeof( struct rewrite_rule ), 1 );
329         if ( rule == NULL ) {
330                 /* charray_free( res ); */
331                 /*
332                  * XXX need to free the value subst stuff!
333                  */
334                 return REWRITE_ERR;
335         }
336         
337         /*
338          * REGEX compilation (luckily I don't need to take care of this ...)
339          */
340         if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
341                 /* charray_free( res ); */
342                 /*
343                  *XXX need to free the value subst stuff!
344                  */
345                 free( rule );
346                 return REWRITE_ERR;
347         }
348         
349 #ifdef USE_REWRITE_LDAP_PVT_THREADS
350         if ( ldap_pvt_thread_mutex_init( &rule->lr_mutex ) ) {
351                 regfree( &rule->lr_regex );
352                 free( rule );
353                 return REWRITE_ERR;
354         }
355 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
356
357         /*
358          * Just to remember them ...
359          */
360         rule->lr_pattern = strdup( pattern );
361         rule->lr_subststring = strdup( result );
362         rule->lr_flagstring = strdup( flagstring );
363         
364         /*
365          * Load compiled data into rule
366          */
367         rule->lr_subst = subst;
368
369         /*
370          * Set various parameters
371          */
372         rule->lr_flags = flags;         /* don't really need any longer ... */
373         rule->lr_mode = mode;
374         rule->lr_max_passes = max_passes;
375         rule->lr_action = first_action;
376         
377         /*
378          * Append rule at the end of the rewrite context
379          */
380         append_rule( context, rule );
381
382         return REWRITE_SUCCESS;
383 }
384
385 /*
386  * Rewrites string according to rule; may return:
387  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
388  *      STOP:   fine, rule matched; stop processing following rules
389  *      UNWILL: rule matched; force 'unwilling to perform'
390  */
391 int
392 rewrite_rule_apply(
393                 struct rewrite_info *info,
394                 struct rewrite_op *op,
395                 struct rewrite_rule *rule,
396                 const char *arg,
397                 char **result
398                 )
399 {
400         size_t nmatch = REWRITE_MAX_MATCH;
401         regmatch_t match[ REWRITE_MAX_MATCH ];
402
403         int rc = REWRITE_SUCCESS;
404
405         char *string;
406         int strcnt = 0;
407         struct berval val = { 0, NULL };
408
409         assert( info != NULL );
410         assert( op != NULL );
411         assert( rule != NULL );
412         assert( arg != NULL );
413         assert( result != NULL );
414
415         *result = NULL;
416
417         string = (char *)arg;
418         
419         /*
420          * In case recursive match is required (default)
421          */
422 recurse:;
423
424         Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
425                         " rule='%s' string='%s' [%d pass(es)]\n", 
426                         rule->lr_pattern, string, strcnt + 1 );
427         
428         op->lo_num_passes++;
429
430 #ifdef USE_REWRITE_LDAP_PVT_THREADS
431         ldap_pvt_thread_mutex_lock( &rule->lr_mutex );
432 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
433         rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
434 #ifdef USE_REWRITE_LDAP_PVT_THREADS
435         ldap_pvt_thread_mutex_unlock( &rule->lr_mutex );
436 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
437         if ( rc != 0 ) {
438                 if ( *result == NULL && string != arg ) {
439                         free( string );
440                 }
441
442                 /*
443                  * No match is OK; *result = NULL means no match
444                  */
445                 return REWRITE_REGEXEC_OK;
446         }
447
448         rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
449                         match, &val );
450
451         *result = val.bv_val;
452         val.bv_val = NULL;
453         if ( string != arg ) {
454                 free( string );
455                 string = NULL;
456         }
457
458         if ( rc != REWRITE_REGEXEC_OK ) {
459                 return rc;
460         }
461
462         if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE 
463                         && op->lo_num_passes < info->li_max_passes
464                         && ++strcnt < rule->lr_max_passes ) {
465                 string = *result;
466
467                 goto recurse;
468         }
469
470         return REWRITE_REGEXEC_OK;
471 }
472
473 int
474 rewrite_rule_destroy(
475                 struct rewrite_rule **prule
476                 )
477 {
478         struct rewrite_rule *rule;
479         struct rewrite_action *action;
480
481         assert( prule != NULL );
482         assert( *prule != NULL );
483
484         rule = *prule;
485
486         if ( rule->lr_pattern ) {
487                 free( rule->lr_pattern );
488                 rule->lr_pattern = NULL;
489         }
490
491         if ( rule->lr_subststring ) {
492                 free( rule->lr_subststring );
493                 rule->lr_subststring = NULL;
494         }
495
496         if ( rule->lr_flagstring ) {
497                 free( rule->lr_flagstring );
498                 rule->lr_flagstring = NULL;
499         }
500
501         if ( rule->lr_subst ) {
502                 rewrite_subst_destroy( &rule->lr_subst );
503         }
504
505         regfree( &rule->lr_regex );
506 #ifdef USE_REWRITE_LDAP_PVT_THREADS
507         ldap_pvt_thread_mutex_destroy( &rule->lr_mutex );
508 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
509
510         for ( action = rule->lr_action; action; ) {
511                 struct rewrite_action *curraction = action;
512
513                 action = action->la_next;
514                 destroy_action( &curraction );
515         }
516
517         free( rule );
518         *prule = NULL;
519
520         return 0;
521 }
522