237
243
* Extension initialization
240
static bool ext_spamvirustest_config_load
241
(struct ext_spamvirustest_data *ext_data, const char *ext_name,
242
const char *status_header, const char *max_header,
243
const char *status_type, const char *max_value)
246
bool ext_spamvirustest_load
247
(const struct sieve_extension *ext, void **context)
249
struct ext_spamvirustest_data *ext_data =
250
(struct ext_spamvirustest_data *) *context;
251
struct sieve_instance *svinst = ext->svinst;
252
const char *ext_name, *status_header, *max_header, *status_type,
254
enum ext_spamvirustest_status_type type;
245
255
const char *error;
247
if ( !ext_spamvirustest_header_spec_parse
248
(&ext_data->status_header, ext_data->pool, status_header, &error) ) {
249
sieve_sys_error("%s: invalid status header specification "
250
"'%s': %s", ext_name, status_header, error);
254
if ( max_header != NULL && !ext_spamvirustest_header_spec_parse
255
(&ext_data->max_header, ext_data->pool, max_header, &error) ) {
256
sieve_sys_error("%s: invalid max header specification "
257
"'%s': %s", ext_name, max_header, error);
261
if ( status_type == NULL || strcmp(status_type, "value") == 0 ) {
262
ext_data->status_type = EXT_SPAMVIRUSTEST_STATUS_TYPE_VALUE;
263
} else if ( strcmp(status_type, "strlen") == 0 ) {
264
ext_data->status_type = EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN;
265
} else if ( strcmp(status_type, "yesno") == 0 ) {
266
ext_data->status_type = EXT_SPAMVIRUSTEST_STATUS_TYPE_YESNO;
268
sieve_sys_error("%s: invalid status type '%s'", ext_name, status_type);
272
if ( max_value != NULL ) {
273
switch ( ext_data->status_type ) {
274
case EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN:
275
case EXT_SPAMVIRUSTEST_STATUS_TYPE_VALUE:
276
if ( !ext_spamvirustest_parse_decimal_value
277
(max_value, &ext_data->max_value, &error) ) {
278
sieve_sys_error("%s: invalid max value specification "
279
"'%s': %s", ext_name, max_value, error);
283
case EXT_SPAMVIRUSTEST_STATUS_TYPE_YESNO:
284
ext_data->yes_string = p_strdup(ext_data->pool, max_value);
285
ext_data->max_value = 1;
293
static void ext_spamvirustest_config_free(struct ext_spamvirustest_data *ext_data)
295
ext_spamvirustest_header_spec_free(&ext_data->status_header);
296
ext_spamvirustest_header_spec_free(&ext_data->max_header);
299
bool ext_spamvirustest_load(const struct sieve_extension *ext, void **context)
301
struct sieve_instance *svinst = ext->svinst;
302
struct ext_spamvirustest_data *ext_data;
303
const char *status_header, *max_header, *status_type, *max_value;
304
const char *ext_name;
307
260
if ( *context != NULL ) {
261
reload = ext_data->reload + 1;
308
262
ext_spamvirustest_unload(ext);
313
* Prevent loading of both spamtest and spamtestplus: let these share
267
* Prevent loading of both spamtest and spamtestplus: let these share
317
if ( sieve_extension_is(ext, spamtest_extension) ||
271
if ( sieve_extension_is(ext, spamtest_extension) ||
318
272
sieve_extension_is(ext, spamtestplus_extension) ) {
319
273
ext_name = spamtest_extension.name;
326
280
status_header = sieve_setting_get
327
281
(svinst, t_strconcat("sieve_", ext_name, "_status_header", NULL));
282
status_type = sieve_setting_get
283
(svinst, t_strconcat("sieve_", ext_name, "_status_type", NULL));
328
284
max_header = sieve_setting_get
329
285
(svinst, t_strconcat("sieve_", ext_name, "_max_header", NULL));
330
status_type = sieve_setting_get
331
(svinst, t_strconcat("sieve_", ext_name, "_status_type", NULL));
332
286
max_value = sieve_setting_get
333
287
(svinst, t_strconcat("sieve_", ext_name, "_max_value", NULL));
289
/* Base configuration */
291
if ( status_header == NULL ) {
295
if ( status_type == NULL || strcmp(status_type, "score") == 0 ) {
296
type = EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE;
297
} else if ( strcmp(status_type, "strlen") == 0 ) {
298
type = EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN;
299
} else if ( strcmp(status_type, "text") == 0 ) {
300
type = EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT;
302
sieve_sys_error("%s: invalid status type '%s'", ext_name, status_type);
335
306
/* Verify settings */
337
if ( status_header == NULL ) {
341
if ( max_header != NULL && max_value != NULL ) {
342
sieve_sys_error("%s: sieve_%s_max_header and sieve_%s_max_value "
343
"cannot both be configured", ext_name, ext_name, ext_name);
347
if ( max_header == NULL && max_value == NULL ) {
348
sieve_sys_error("%s: none of sieve_%s_max_header or sieve_%s_max_value "
349
"is configured", ext_name, ext_name, ext_name);
353
/* Pre-process configuration */
308
if ( type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) {
310
if ( max_header != NULL && max_value != NULL ) {
311
sieve_sys_error("%s: sieve_%s_max_header and sieve_%s_max_value "
312
"cannot both be configured", ext_name, ext_name, ext_name);
316
if ( max_header == NULL && max_value == NULL ) {
317
sieve_sys_error("%s: none of sieve_%s_max_header or sieve_%s_max_value "
318
"is configured", ext_name, ext_name, ext_name);
322
if ( max_header != NULL ) {
323
sieve_sys_warning("%s: setting sieve_%s_max_header has no meaning "
324
"for sieve_%s_status_type=text", ext_name, ext_name, ext_name);
327
if ( max_value != NULL ) {
328
sieve_sys_warning("%s: setting sieve_%s_max_value has no meaning "
329
"for sieve_%s_status_type=text", ext_name, ext_name, ext_name);
355
333
pool = pool_alloconly_create("spamvirustest_data", 512);
356
334
ext_data = p_new(pool, struct ext_spamvirustest_data, 1);
357
335
ext_data->pool = pool;
359
if ( !ext_spamvirustest_config_load
360
(ext_data, ext_name, status_header, max_header, status_type, max_value) ) {
336
ext_data->reload = reload;
337
ext_data->status_type = type;
339
if ( !ext_spamvirustest_header_spec_parse
340
(&ext_data->status_header, ext_data->pool, status_header, &error) ) {
341
sieve_sys_error("%s: invalid status header specification "
342
"'%s': %s", ext_name, status_header, error);
347
if ( type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) {
348
/* Parse max header */
350
if ( max_header != NULL && !ext_spamvirustest_header_spec_parse
351
(&ext_data->max_header, ext_data->pool, max_header, &error) ) {
352
sieve_sys_error("%s: invalid max header specification "
353
"'%s': %s", ext_name, max_header, error);
357
/* Parse max value */
359
if ( result && max_value != NULL ) {
360
if ( !ext_spamvirustest_parse_decimal_value
361
(max_value, &ext_data->max_value, &error) ) {
362
sieve_sys_error("%s: invalid max value specification "
363
"'%s': %s", ext_name, max_value, error);
369
unsigned int i, max_text;
371
max_text = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 );
373
/* Get text values */
374
for ( i = 0; i <= max_text; i++ ) {
375
const char *value = sieve_setting_get
376
(svinst, t_strdup_printf("sieve_%s_text_value%d", ext_name, i));
378
if ( value != NULL && *value != '\0' )
379
ext_data->text_values[i] = p_strdup(ext_data->pool, value);
382
ext_data->max_value = 1;
387
*context = (void *) ext_data;
361
389
sieve_sys_warning("%s: extension not configured, "
362
390
"tests will always match against \"0\"", ext_name);
363
ext_spamvirustest_config_free(ext_data);
391
ext_spamvirustest_unload(ext);
367
*context = (void *) ext_data;
373
398
void ext_spamvirustest_unload(const struct sieve_extension *ext)
375
struct ext_spamvirustest_data *ext_data =
400
struct ext_spamvirustest_data *ext_data =
376
401
(struct ext_spamvirustest_data *) ext->context;
378
403
if ( ext_data != NULL ) {
379
404
ext_spamvirustest_header_spec_free(&ext_data->status_header);
380
405
ext_spamvirustest_header_spec_free(&ext_data->max_header);
382
406
pool_unref(&ext_data->pool);
414
struct ext_spamvirustest_message_context {
419
static const char *ext_spamvirustest_get_score
420
(const struct sieve_extension *ext, float score_ratio, bool percent)
424
if ( score_ratio < 0 )
427
if ( score_ratio > 1 )
431
score = score_ratio * 100 + 0.001;
432
else if ( sieve_extension_is(ext, virustest_extension) )
433
score = score_ratio * 4 + 1.001;
435
score = score_ratio * 9 + 1.001;
437
return t_strdup_printf("%d", score);
390
440
const char *ext_spamvirustest_get_value
391
441
(const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
394
static const char *VALUE_FAILED = "0";
395
struct ext_spamvirustest_data *ext_data =
396
(struct ext_spamvirustest_data *) ext->context;
444
struct ext_spamvirustest_data *ext_data =
445
(struct ext_spamvirustest_data *) ext->context;
397
446
struct ext_spamvirustest_header_spec *status_header, *max_header;
398
447
const struct sieve_message_data *msgdata = renv->msgdata;
448
struct sieve_message_context *msgctx = renv->msgctx;
449
struct ext_spamvirustest_message_context *mctx;
399
450
const char *ext_name = sieve_extension_name(ext);
400
451
regmatch_t match_values[2];
401
452
const char *header_value, *error;
402
const char *status = NULL, *max = NULL, *yes = NULL;
453
const char *status = NULL, *max = NULL;
403
454
float status_value, max_value;
455
unsigned int i, max_text;
456
pool_t pool = sieve_interpreter_pool(renv->interp);
407
* Check whether extension is properly configured
459
* Check whether extension is properly configured
409
461
if ( ext_data == NULL ) {
410
462
sieve_runtime_trace(renv, "%s: extension not configured", ext_name);
467
* Check wether a cached result is available
470
mctx = (struct ext_spamvirustest_message_context *)
471
sieve_message_context_extension_get(msgctx, ext);
473
if ( mctx == NULL ) {
474
mctx = p_new(pool, struct ext_spamvirustest_message_context, 1);
475
sieve_message_context_extension_set(msgctx, ext, (void *)mctx);
476
} else if ( mctx->reload == ext_data->reload ) {
477
return ext_spamvirustest_get_score(ext, mctx->score_ratio, percent);
480
mctx->reload = ext_data->reload;
483
* Get max status value
414
486
status_header = &ext_data->status_header;
415
487
max_header = &ext_data->max_header;
418
* Get max status value
421
if ( max_header->header_name != NULL ) {
422
/* Get header from message */
423
if ( mail_get_first_header_utf8
424
(msgdata->mail, max_header->header_name, &header_value) < 0 ||
425
header_value == NULL ) {
426
sieve_runtime_trace(renv, "%s: header '%s' not found in message",
427
ext_name, max_header->header_name);
431
if ( max_header->regexp_match ) {
433
if ( regexec(&max_header->regexp, header_value, 2, match_values, 0)
435
sieve_runtime_trace(renv, "%s: regexp for header '%s' did not match "
436
"on value '%s'", ext_name, max_header->header_name, header_value);
440
max = _regexp_match_get_value(header_value, 1, match_values, 2);
442
sieve_runtime_trace(renv, "%s: regexp did not return match value "
443
"for string '%s'", ext_name, header_value);
450
switch ( ext_data->status_type ) {
451
case EXT_SPAMVIRUSTEST_STATUS_TYPE_VALUE:
452
case EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN:
489
if ( ext_data->status_type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) {
490
if ( max_header->header_name != NULL ) {
491
/* Get header from message */
492
if ( mail_get_first_header_utf8
493
(msgdata->mail, max_header->header_name, &header_value) < 0 ||
494
header_value == NULL ) {
495
sieve_runtime_trace(renv, "%s: header '%s' not found in message",
496
ext_name, max_header->header_name);
500
if ( max_header->regexp_match ) {
502
if ( regexec(&max_header->regexp, header_value, 2, match_values, 0)
504
sieve_runtime_trace(renv, "%s: regexp for header '%s' did not match "
505
"on value '%s'", ext_name, max_header->header_name, header_value);
509
max = _regexp_match_get_value(header_value, 1, match_values, 2);
511
sieve_runtime_trace(renv, "%s: regexp did not return match value "
512
"for string '%s'", ext_name, header_value);
453
519
if ( !ext_spamvirustest_parse_decimal_value(max, &max_value, &error) ) {
454
sieve_runtime_trace(renv, "%s: failed to parse maximum value: %s",
520
sieve_runtime_trace(renv, "%s: failed to parse maximum value: %s",
455
521
ext_name, error);
459
case EXT_SPAMVIRUSTEST_STATUS_TYPE_YESNO:
525
max_value = ext_data->max_value;
528
if ( max_value == 0 ) {
529
sieve_runtime_trace(renv, "%s: max value is 0", ext_name);
465
yes = ext_data->yes_string;
466
max_value = ext_data->max_value;
469
if ( max_value == 0 ) {
470
sieve_runtime_trace(renv, "%s: max value is 0", ext_name);
533
max_value = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 );
479
541
if ( mail_get_first_header_utf8
480
542
(msgdata->mail, status_header->header_name, &header_value) < 0 ||
481
543
header_value == NULL ) {
482
sieve_runtime_trace(renv, "%s: header '%s' not found in message",
544
sieve_runtime_trace(renv, "%s: header '%s' not found in message",
483
545
ext_name, status_header->header_name);
487
549
/* Execute regex */
488
550
if ( status_header->regexp_match ) {
489
if ( regexec(&status_header->regexp, header_value, 2, match_values, 0)
551
if ( regexec(&status_header->regexp, header_value, 2, match_values, 0)
491
553
sieve_runtime_trace(renv, "%s: regexp for header '%s' did not match "
492
554
"on value '%s'", ext_name, status_header->header_name, header_value);
496
558
status = _regexp_match_get_value(header_value, 1, match_values, 2);
497
559
if ( status == NULL ) {
498
560
sieve_runtime_trace(renv, "%s: regexp did not return match value "
499
561
"for string '%s'", ext_name, header_value);
503
565
status = header_value;
506
568
switch ( ext_data->status_type ) {
507
case EXT_SPAMVIRUSTEST_STATUS_TYPE_VALUE:
508
if ( !ext_spamvirustest_parse_decimal_value(status, &status_value, &error)
510
sieve_runtime_trace(renv, "%s: failed to parse status value '%s': %s",
569
case EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE:
570
if ( !ext_spamvirustest_parse_decimal_value
571
(status, &status_value, &error) ) {
572
sieve_runtime_trace(renv, "%s: failed to parse status value '%s': %s",
511
573
ext_name, status, error);
515
577
case EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN:
516
if ( !ext_spamvirustest_parse_strlen_value(status, &status_value, &error)
518
sieve_runtime_trace(renv, "%s: failed to parse status value '%s': %s",
578
if ( !ext_spamvirustest_parse_strlen_value
579
(status, &status_value, &error) ) {
580
sieve_runtime_trace(renv, "%s: failed to parse status value '%s': %s",
519
581
ext_name, status, error);
523
case EXT_SPAMVIRUSTEST_STATUS_TYPE_YESNO:
524
if ( strcmp(status, yes) == 0 )
585
case EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT:
586
max_text = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 );
590
while ( i <= max_text ) {
591
if ( ext_data->text_values[i] != NULL &&
592
strcmp(status, ext_data->text_values[i]) == 0 ) {
593
status_value = (float) i;
599
if ( i > max_text ) {
600
sieve_runtime_trace(renv, "%s: failed to match textstatus value '%s'",
531
610
/* Calculate value */
532
if ( status_value < 0 ) {
534
} else if ( status_value > max_value ) {
541
value = (status_value / max_value) * 99 + 1;
543
value = (status_value / max_value) * 9 + 1;
546
return t_strdup_printf("%d", value);;
611
if ( status_value < 0 )
612
mctx->score_ratio = 0;
613
else if ( status_value > max_value )
614
mctx->score_ratio = 1;
616
mctx->score_ratio = (status_value / max_value);
618
return ext_spamvirustest_get_score(ext, mctx->score_ratio, percent);
621
mctx->score_ratio = -1;