1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
13
#include "sieve-common.h"
14
#include "sieve-limits.h"
15
#include "sieve-ast.h"
16
#include "sieve-commands.h"
17
#include "sieve-validator.h"
18
#include "sieve-comparators.h"
19
#include "sieve-match-types.h"
20
#include "sieve-match.h"
22
#include "ext-regex-common.h"
24
#include <sys/types.h>
32
#define MCHT_REGEX_MAX_SUBSTITUTIONS SIEVE_MAX_MATCH_VALUES
38
bool mcht_regex_validate_context
39
(struct sieve_validator *validator, struct sieve_ast_argument *arg,
40
struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg);
42
static void mcht_regex_match_init(struct sieve_match_context *mctx);
43
static int mcht_regex_match
44
(struct sieve_match_context *mctx, const char *val, size_t val_size,
45
const char *key, size_t key_size, int key_index);
46
static int mcht_regex_match_deinit(struct sieve_match_context *mctx);
48
const struct sieve_match_type regex_match_type = {
49
SIEVE_OBJECT("regex", ®ex_match_type_operand, 0),
52
mcht_regex_validate_context,
53
mcht_regex_match_init,
55
mcht_regex_match_deinit
59
* Match type validation
62
/* Wrapper around the regerror function for easy access */
63
static const char *_regexp_error(regex_t *regexp, int errorcode)
65
size_t errsize = regerror(errorcode, regexp, NULL, 0);
71
buffer_create_dynamic(pool_datastack_create(), errsize);
72
errbuf = buffer_get_space_unsafe(error_buf, 0, errsize);
74
errsize = regerror(errorcode, regexp, errbuf, errsize);
76
/* We don't want the error to start with a capital letter */
77
errbuf[0] = i_tolower(errbuf[0]);
79
buffer_append_space_unsafe(error_buf, errsize);
81
return str_c(error_buf);
87
static int mcht_regex_validate_regexp
88
(struct sieve_validator *validator,
89
struct sieve_match_type_context *ctx ATTR_UNUSED,
90
struct sieve_ast_argument *key, int cflags)
95
if ( (ret=regcomp(®exp, sieve_ast_argument_strc(key), cflags)) != 0 ) {
96
sieve_argument_validate_error(validator, key,
97
"invalid regular expression for regex match: %s",
98
_regexp_error(®exp, ret));
108
struct _regex_key_context {
109
struct sieve_validator *valdtr;
110
struct sieve_match_type_context *mctx;
114
static int mcht_regex_validate_key_argument
115
(void *context, struct sieve_ast_argument *key)
117
struct _regex_key_context *keyctx = (struct _regex_key_context *) context;
119
/* FIXME: We can currently only handle string literal argument, so
120
* variables are not allowed.
122
if ( !sieve_argument_is_string_literal(key) ) {
123
sieve_argument_validate_error(keyctx->valdtr, key,
124
"this Sieve implementation currently only accepts a literal string "
125
"for a regular expression");
129
return mcht_regex_validate_regexp
130
(keyctx->valdtr, keyctx->mctx, key, keyctx->cflags);
133
bool mcht_regex_validate_context
134
(struct sieve_validator *validator, struct sieve_ast_argument *arg ATTR_UNUSED,
135
struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg)
137
const struct sieve_comparator *cmp = ctx->comparator;
138
int cflags = REG_EXTENDED | REG_NOSUB;
139
struct _regex_key_context keyctx;
140
struct sieve_ast_argument *kitem;
143
if ( cmp == &i_ascii_casemap_comparator )
144
cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE;
145
else if ( cmp == &i_octet_comparator )
146
cflags = REG_EXTENDED | REG_NOSUB;
148
sieve_argument_validate_error(validator, ctx->match_type_arg,
149
"regex match type only supports "
150
"i;octet and i;ascii-casemap comparators" );
155
/* Validate regular expression keys */
157
keyctx.valdtr = validator;
159
keyctx.cflags = cflags;
162
if ( !sieve_ast_stringlist_map(&kitem, (void *) &keyctx,
163
mcht_regex_validate_key_argument) )
170
* Match type implementation
173
struct mcht_regex_context {
174
ARRAY_DEFINE(reg_expressions, regex_t);
180
static void mcht_regex_match_init
181
(struct sieve_match_context *mctx)
183
pool_t pool = mctx->pool;
184
struct mcht_regex_context *ctx;
187
ctx = p_new(pool, struct mcht_regex_context, 1);
188
p_array_init(&ctx->reg_expressions, pool, 4);
189
ctx->value_index = -1;
191
/* Create storage for match values if match values are requested */
192
if ( sieve_match_values_are_enabled(mctx->interp) ) {
193
ctx->pmatch = p_new(pool, regmatch_t, MCHT_REGEX_MAX_SUBSTITUTIONS);
194
ctx->nmatch = MCHT_REGEX_MAX_SUBSTITUTIONS;
201
mctx->data = (void *) ctx;
204
static regex_t *mcht_regex_get
205
(struct mcht_regex_context *ctx,
206
const struct sieve_comparator *cmp,
207
const char *key, unsigned int key_index)
213
/* If this is the first matched value, the regexes are not compiled
216
if ( ctx->value_index <= 0 ) {
218
array_idx_clear(&ctx->reg_expressions, key_index);
219
regexp = array_idx_modifiable(&ctx->reg_expressions, key_index);
221
/* Configure case-sensitivity according to comparator */
222
if ( cmp == &i_octet_comparator )
223
cflags = REG_EXTENDED;
224
else if ( cmp == &i_ascii_casemap_comparator )
225
cflags = REG_EXTENDED | REG_ICASE;
227
return NULL; /* Not supported */
229
/* Indicate whether match values need to be produced */
230
if ( ctx->nmatch == 0 ) cflags |= REG_NOSUB;
232
/* Compile regular expression */
233
if ( (ret=regcomp(regexp, key, cflags)) != 0 ) {
234
/* FIXME: Do something useful, i.e. report error somewhere */
238
/* Get compiled regex from cache */
239
regexp = array_idx_modifiable(&ctx->reg_expressions, key_index);
245
static int mcht_regex_match
246
(struct sieve_match_context *mctx,
247
const char *val, size_t val_size ATTR_UNUSED,
248
const char *key, size_t key_size ATTR_UNUSED, int key_index)
250
struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
258
if ( key_index < 0 ) return FALSE;
260
if ( key_index == 0 ) ctx->value_index++;
262
/* Get compiled regex */
263
if ( (regexp=mcht_regex_get(ctx, mctx->comparator, key, key_index)) == NULL )
267
if ( regexec(regexp, val, ctx->nmatch, ctx->pmatch, 0) == 0 ) {
269
/* Handle match values if necessary */
270
if ( ctx->nmatch > 0 ) {
271
struct sieve_match_values *mvalues;
274
string_t *subst = t_str_new(32);
276
/* Start new list of match values */
277
mvalues = sieve_match_values_start(mctx->interp);
279
i_assert( mvalues != NULL );
281
/* Add match values from regular expression */
282
for ( i = 0; i < ctx->nmatch; i++ ) {
283
str_truncate(subst, 0);
285
if ( ctx->pmatch[i].rm_so != -1 ) {
287
sieve_match_values_skip(mvalues, skipped);
291
str_append_n(subst, val + ctx->pmatch[i].rm_so,
292
ctx->pmatch[i].rm_eo - ctx->pmatch[i].rm_so);
293
sieve_match_values_add(mvalues, subst);
298
/* Substitute the new match values */
299
sieve_match_values_commit(mctx->interp, &mvalues);
308
int mcht_regex_match_deinit
309
(struct sieve_match_context *mctx)
311
struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
313
unsigned int count, i;
315
/* Clean up compiled regular expressions */
316
regexps = array_get_modifiable(&ctx->reg_expressions, &count);
317
for ( i = 0; i < count; i++ ) {
318
regfree(®exps[i]);