Skip to content
Snippets Groups Projects
Commit 9a193393 authored by Stephan Bosch's avatar Stephan Bosch
Browse files

lib-sieve: Implemented the Sieve index extension (RFC 5260).

parent 842ebbec
No related branches found
No related tags found
No related merge requests found
......@@ -140,6 +140,8 @@ test_cases = \
tests/extensions/date/basic.svtest \
tests/extensions/date/date-parts.svtest \
tests/extensions/date/zones.svtest \
tests/extensions/index/basic.svtest \
tests/extensions/index/errors.svtest \
tests/extensions/spamvirustest/spamtest.svtest \
tests/extensions/spamvirustest/virustest.svtest \
tests/extensions/spamvirustest/spamtestplus.svtest \
......
......@@ -203,6 +203,7 @@ src/lib-sieve/plugins/ihave/Makefile
src/lib-sieve/plugins/editheader/Makefile
src/lib-sieve/plugins/metadata/Makefile
src/lib-sieve/plugins/duplicate/Makefile
src/lib-sieve/plugins/index/Makefile
src/lib-sieve/plugins/vnd.dovecot/Makefile
src/lib-sieve/plugins/vnd.dovecot/debug/Makefile
src/lib-sieve-tool/Makefile
......
......@@ -76,6 +76,7 @@ plugins = \
$(extdir)/ihave/libsieve_ext_ihave.la \
$(extdir)/editheader/libsieve_ext_editheader.la \
$(extdir)/duplicate/libsieve_ext_duplicate.la \
$(extdir)/index/libsieve_ext_index.la \
$(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \
$(unfinished_plugins)
......
......@@ -22,6 +22,7 @@ SUBDIRS = \
ihave \
editheader \
duplicate \
index \
vnd.dovecot \
$(UNFINISHED)
......
noinst_LTLIBRARIES = libsieve_ext_index.la
AM_CPPFLAGS = \
-I$(srcdir)/../.. \
$(LIBDOVECOT_INCLUDE)
libsieve_ext_index_la_SOURCES = \
ext-index-common.c \
ext-index.c \
tag-index.c
noinst_HEADERS = \
ext-index-common.h
/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
*/
#include "lib.h"
#include "utc-offset.h"
#include "str.h"
#include "sieve-common.h"
#include "sieve-stringlist.h"
#include "sieve-code.h"
#include "sieve-interpreter.h"
#include "sieve-message.h"
#include "ext-index-common.h"
/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
*/
#ifndef __EXT_INDEX_COMMON_H
#define __EXT_INDEX_COMMON_H
#include "sieve-common.h"
#include <time.h>
#define SIEVE_EXT_INDEX_HDR_OVERRIDE_SEQUENCE 100
/*
* Tagged arguments
*/
extern const struct sieve_argument_def index_tag;
extern const struct sieve_argument_def last_tag;
/*
* Operands
*/
extern const struct sieve_operand_def index_operand;
/*
* Extension
*/
extern const struct sieve_extension_def index_extension;
#endif /* __EXT_INDEX_COMMON_H */
/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
*/
/* Extension index
* ------------------
*
* Authors: Stephan Bosch
* Specification: RFC 5260
* Implementation: full
* Status: testing
*
*/
#include "lib.h"
#include "array.h"
#include "sieve-common.h"
#include "sieve-message.h"
#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-binary.h"
#include "sieve-interpreter.h"
#include "sieve-dump.h"
#include "ext-index-common.h"
/*
* Extension
*/
static bool ext_index_validator_load
(const struct sieve_extension *ext, struct sieve_validator *validator);
const struct sieve_extension_def index_extension = {
.name = "index",
.validator_load = ext_index_validator_load,
SIEVE_EXT_DEFINE_OPERAND(index_operand)
};
static bool ext_index_validator_load
(const struct sieve_extension *ext, struct sieve_validator *valdtr)
{
/* Register :index and :last tags with header, address and date test commands
* and we don't care whether these command are registered or even whether
* these will be registered at all. The validator handles either situation
* gracefully.
*/
sieve_validator_register_external_tag
(valdtr, "header", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
sieve_validator_register_external_tag
(valdtr, "header", ext, &last_tag, 0);
sieve_validator_register_external_tag
(valdtr, "address", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
sieve_validator_register_external_tag
(valdtr, "address", ext, &last_tag, 0);
sieve_validator_register_external_tag
(valdtr, "date", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
sieve_validator_register_external_tag
(valdtr, "date", ext, &last_tag, 0);
return TRUE;
}
/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
*/
#include "lib.h"
#include "mail-storage.h"
#include "mail-namespace.h"
#include "sieve-common.h"
#include "sieve-stringlist.h"
#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-binary.h"
#include "sieve-code.h"
#include "sieve-message.h"
#include "sieve-result.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "ext-index-common.h"
/*
* Tagged argument
*/
static bool tag_index_validate
(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
struct sieve_command *cmd);
static bool tag_index_generate
(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
struct sieve_command *context);
const struct sieve_argument_def index_tag = {
"index",
NULL,
tag_index_validate,
NULL, NULL,
tag_index_generate
};
static bool tag_last_validate
(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
struct sieve_command *cmd);
const struct sieve_argument_def last_tag = {
"last",
NULL,
tag_last_validate,
NULL, NULL, NULL
};
/*
* Header override
*/
static bool svmo_index_dump_context
(const struct sieve_message_override *svmo,
const struct sieve_dumptime_env *denv, sieve_size_t *address);
static int svmo_index_read_context
(const struct sieve_message_override *svmo,
const struct sieve_runtime_env *renv, sieve_size_t *address,
void **ho_context);
static int svmo_index_header_override
(const struct sieve_message_override *svmo,
const struct sieve_runtime_env *renv,
struct sieve_stringlist **headers);
const struct sieve_message_override_def index_header_override = {
SIEVE_OBJECT("index", &index_operand, 0),
.sequence = SIEVE_EXT_INDEX_HDR_OVERRIDE_SEQUENCE,
.dump_context = svmo_index_dump_context,
.read_context = svmo_index_read_context,
.header_override = svmo_index_header_override
};
/*
* Operand
*/
static const struct sieve_extension_objects ext_header_overrides =
SIEVE_EXT_DEFINE_MESSAGE_OVERRIDE(index_header_override);
const struct sieve_operand_def index_operand = {
"index operand",
&index_extension,
0,
&sieve_message_override_operand_class,
&ext_header_overrides
};
/*
* Tag data
*/
struct tag_index_data {
sieve_number_t fieldno;
unsigned int last:1;
};
/*
* Tag validation
*/
static bool tag_index_validate
(struct sieve_validator *valdtr ATTR_UNUSED,
struct sieve_ast_argument **arg, struct sieve_command *cmd)
{
struct sieve_ast_argument *tag = *arg;
struct tag_index_data *data;
/* Skip the tag itself */
*arg = sieve_ast_argument_next(*arg);
/* Check syntax:
* ":index" <fieldno: number>
*/
if ( !sieve_validate_tag_parameter
(valdtr, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) ) {
return FALSE;
}
if (tag->argument->data == NULL) {
data = p_new(sieve_command_pool(cmd), struct tag_index_data, 1);
tag->argument->data = (void *)data;
} else {
data = (struct tag_index_data *)tag->argument->data;
}
data->fieldno = sieve_ast_argument_number(*arg);
/* Detach parameter */
*arg = sieve_ast_arguments_detach(*arg,1);
return TRUE;
}
static bool tag_last_validate
(struct sieve_validator *valdtr ATTR_UNUSED,
struct sieve_ast_argument **arg, struct sieve_command *cmd)
{
struct sieve_ast_argument *index_arg;
struct tag_index_data *data;
index_arg = sieve_command_find_argument(cmd, &index_tag);
if (index_arg == NULL) {
sieve_argument_validate_error(valdtr, *arg,
"the :last tag for the %s %s cannot be specified "
"without the :index tag",
sieve_command_identifier(cmd), sieve_command_type_name(cmd));
return FALSE;
}
/* Set :last flag */
if (index_arg->argument->data == NULL) {
data = p_new(sieve_command_pool(cmd), struct tag_index_data, 1);
index_arg->argument->data = (void*)data;
} else {
data = (struct tag_index_data *)index_arg->argument->data;
}
data->last = TRUE;
/* Detach */
*arg = sieve_ast_arguments_detach(*arg,1);
return TRUE;
}
/*
* Code generation
*/
static bool tag_index_generate
(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
struct sieve_command *cmd ATTR_UNUSED)
{
struct tag_index_data *data =
(struct tag_index_data *)arg->argument->data;
if ( sieve_ast_argument_type(arg) != SAAT_TAG )
return FALSE;
sieve_opr_message_override_emit
(cgenv->sblock, arg->argument->ext, &index_header_override);
(void)sieve_binary_emit_integer
(cgenv->sblock, data->fieldno);
(void)sieve_binary_emit_byte
(cgenv->sblock, ( data->last ? 1 : 0 ));
return TRUE;
}
/*
* Header override implementation
*/
/* Context data */
struct svmo_index_context {
unsigned int fieldno;
unsigned int last:1;
};
/* Context coding */
static bool svmo_index_dump_context
(const struct sieve_message_override *svmo ATTR_UNUSED,
const struct sieve_dumptime_env *denv, sieve_size_t *address)
{
sieve_number_t fieldno = 0;
unsigned int last;
if ( !sieve_binary_read_integer(denv->sblock, address, &fieldno) )
return FALSE;
sieve_code_dumpf(denv, "fieldno: %llu",
(unsigned long long) fieldno);
if ( !sieve_binary_read_byte(denv->sblock, address, &last) )
return FALSE;
if (last)
sieve_code_dumpf(denv, "last");
return TRUE;
}
static int svmo_index_read_context
(const struct sieve_message_override *svmo ATTR_UNUSED,
const struct sieve_runtime_env *renv, sieve_size_t *address,
void **ho_context)
{
pool_t pool = sieve_result_pool(renv->result);
struct svmo_index_context *ctx;
sieve_number_t fieldno;
unsigned int last = 0;
if ( !sieve_binary_read_integer(renv->sblock, address, &fieldno) ) {
sieve_runtime_trace_error(renv, "fieldno: invalid number");
return SIEVE_EXEC_BIN_CORRUPT;
}
if ( !sieve_binary_read_byte(renv->sblock, address, &last) ) {
sieve_runtime_trace_error(renv, "last: invalid byte");
return SIEVE_EXEC_BIN_CORRUPT;
}
ctx = p_new(pool, struct svmo_index_context, 1);
ctx->fieldno = fieldno;
ctx->last = (last == 0 ? FALSE : TRUE);
*ho_context = (void *) ctx;
return SIEVE_EXEC_OK;
}
/* Override */
static int svmo_index_header_override
(const struct sieve_message_override *svmo,
const struct sieve_runtime_env *renv,
struct sieve_stringlist **headers)
{
struct svmo_index_context *ctx =
(struct svmo_index_context *)svmo->context;
sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
"header index override: only returning index %d%s",
ctx->fieldno, ( ctx->last ? " (from last)" : "" ));
*headers = sieve_index_stringlist_create(renv, *headers,
(int)ctx->fieldno * ( ctx->last ? -1 : 1 ));
return SIEVE_EXEC_OK;
}
......@@ -103,6 +103,7 @@ extern const struct sieve_extension_def enotify_extension;
extern const struct sieve_extension_def environment_extension;
extern const struct sieve_extension_def mailbox_extension;
extern const struct sieve_extension_def date_extension;
extern const struct sieve_extension_def index_extension;
extern const struct sieve_extension_def ihave_extension;
extern const struct sieve_extension_def duplicate_extension;
......@@ -117,7 +118,8 @@ const struct sieve_extension_def *sieve_core_extensions[] = {
&relational_extension, &regex_extension, &imap4flags_extension,
&copy_extension, &include_extension, &body_extension,
&variables_extension, &enotify_extension, &environment_extension,
&mailbox_extension, &date_extension, &ihave_extension, &duplicate_extension
&mailbox_extension, &date_extension, &index_extension, &ihave_extension,
&duplicate_extension
};
const unsigned int sieve_core_extensions_count =
......
require "vnd.dovecot.testsuite";
require "index";
require "date";
require "variables";
require "subaddress";
test_set "message" text:
To: first@friep.example.com
X-A: First
Received: from mx.example.com (127.0.0.13) by mx.example.org
(127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4;
Wed, 12 Nov 2014 18:18:31 +0100
To: second@friep.example.com
From: stephan@example.org
Received: from mx.example.com (127.0.0.13) by mx.example.org
(127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4;
Wed, 12 Nov 2014 18:18:30 +0100
X-A: Second
To: third@friep.example.com
X-A: Third
Received: from mx.example.com (127.0.0.13) by mx.example.org
(127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4;
Wed, 12 Nov 2014 18:18:29 +0100
Subject: Frop!
X-A: Fourth
To: fourth@friep.example.com
Received: from mx.example.com (127.0.0.13) by mx.example.org
(127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4;
Wed, 12 Nov 2014 18:18:28 +0100
Frop
.
;
test "Header :index" {
if not header :index 3 "x-a" "Third" {
test_fail "wrong header retrieved";
}
if header :index 3 "x-a" ["First", "Second", "Fourth"] {
test_fail "other header retrieved";
}
}
test "Header :index :last" {
if not header :index 3 :last "x-a" "Second" {
test_fail "wrong header retrieved";
}
if header :index 3 :last "x-a" ["First", "Third", "Fourth"] {
test_fail "other header retrieved";
}
}
test "Address :index" {
if not address :localpart :index 2 "to" "second" {
test_fail "wrong header retrieved";
}
if address :localpart :index 2 "to" ["first", "third", "fourth"] {
test_fail "other header retrieved";
}
}
test "Address :index :last" {
if not address :localpart :index 2 :last "to" "third" {
test_fail "wrong header retrieved";
}
if address :localpart :index 2 :last "to" ["first", "second", "fourth"] {
test_fail "other header retrieved";
}
}
test "Date :index" {
if not date :index 1 "received" "second" "31" {
test_fail "wrong header retrieved";
}
if date :index 1 "received" "second" ["30", "29", "28"] {
test_fail "other header retrieved";
}
}
test "Date :index :last" {
if not date :index 1 :last "received" "second" "28"{
test_fail "wrong header retrieved";
}
if date :index 1 :last "received" "second" ["31", "30", "29"] {
test_fail "other header retrieved";
}
}
require "vnd.dovecot.testsuite";
require "relational";
require "comparator-i;ascii-numeric";
/*
* Invalid syntax
*/
test "Invalid Syntax" {
if test_script_compile "errors/syntax.sieve" {
test_fail "compile should have failed";
}
if not test_error :count "eq" :comparator "i;ascii-numeric" "5" {
test_fail "wrong number of errors reported";
}
}
require "date";
require "index";
# Not an error
if header :last :index 2 "to" "ok" { }
# Not an error
if header :index 444 :last "to" "ok" { }
# 1: missing argument
if header :index "to" "ok" {}
# 2: missing argument
if header :index :last "to" "ok" {}
# 3: erroneous string argument
if header :index "frop" "to" "ok" {}
# 4: last without index
if header :last "to" "ok" {}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment

Consent

On this website, we use the web analytics service Matomo to analyze and review the use of our website. Through the collected statistics, we can improve our offerings and make them more appealing for you. Here, you can decide whether to allow us to process your data and set corresponding cookies for these purposes, in addition to technically necessary cookies. Further information on data protection—especially regarding "cookies" and "Matomo"—can be found in our privacy policy. You can withdraw your consent at any time.