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