1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5
6 #include "sieve-common.h"
7 #include "sieve-extensions.h"
8 #include "sieve-commands.h"
9 #include "sieve-stringlist.h"
10 #include "sieve-code.h"
11 #include "sieve-comparators.h"
12 #include "sieve-match-types.h"
13 #include "sieve-address-parts.h"
14 #include "sieve-validator.h"
15 #include "sieve-generator.h"
16 #include "sieve-interpreter.h"
17 #include "sieve-dump.h"
18 #include "sieve-match.h"
19
20 #include "ext-spamvirustest-common.h"
21
22 /*
23 * Tests
24 */
25
26 static bool tst_spamvirustest_validate
27 (struct sieve_validator *valdtr, struct sieve_command *tst);
28 static bool tst_spamvirustest_generate
29 (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
30 static bool tst_spamvirustest_registered
31 (struct sieve_validator *valdtr, const struct sieve_extension *ext,
32 struct sieve_command_registration *cmd_reg);
33
34 /* Spamtest test
35 *
36 * Syntax:
37 * spamtest [":percent"] [COMPARATOR] [MATCH-TYPE] <value: string>
38 */
39
40 const struct sieve_command_def spamtest_test = {
41 .identifier = "spamtest",
42 .type = SCT_TEST,
43 .positional_args = 1,
44 .subtests = 0,
45 .block_allowed = FALSE,
46 .block_required = FALSE,
47 .registered = tst_spamvirustest_registered,
48 .validate = tst_spamvirustest_validate,
49 .generate = tst_spamvirustest_generate
50 };
51
52 /* Virustest test
53 *
54 * Syntax:
55 * virustest [COMPARATOR] [MATCH-TYPE] <value: string>
56 */
57
58 const struct sieve_command_def virustest_test = {
59 .identifier = "virustest",
60 .type = SCT_TEST,
61 .positional_args = 1,
62 .subtests = 0,
63 .block_allowed = FALSE,
64 .block_required = FALSE,
65 .registered = tst_spamvirustest_registered,
66 .validate = tst_spamvirustest_validate,
67 .generate = tst_spamvirustest_generate
68 };
69
70 /*
71 * Tagged arguments
72 */
73
74 static bool tst_spamtest_validate_percent_tag
75 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
76 struct sieve_command *tst);
77
78 static const struct sieve_argument_def spamtest_percent_tag = {
79 .identifier = "percent",
80 .validate = tst_spamtest_validate_percent_tag
81 };
82
83 /*
84 * Spamtest and virustest operations
85 */
86
87 static bool tst_spamvirustest_operation_dump
88 (const struct sieve_dumptime_env *denv, sieve_size_t *address);
89 static int tst_spamvirustest_operation_execute
90 (const struct sieve_runtime_env *renv, sieve_size_t *address);
91
92 const struct sieve_operation_def spamtest_operation = {
93 .mnemonic = "SPAMTEST",
94 .ext_def = &spamtest_extension,
95 .dump = tst_spamvirustest_operation_dump,
96 .execute = tst_spamvirustest_operation_execute
97 };
98
99 const struct sieve_operation_def virustest_operation = {
100 .mnemonic = "VIRUSTEST",
101 .ext_def = &virustest_extension,
102 .dump = tst_spamvirustest_operation_dump,
103 .execute = tst_spamvirustest_operation_execute
104 };
105
106 /*
107 * Optional operands
108 */
109
110 enum tst_spamvirustest_optional {
111 OPT_SPAMTEST_PERCENT = SIEVE_MATCH_OPT_LAST,
112 OPT_SPAMTEST_LAST
113 };
114
115 /*
116 * Test registration
117 */
118
tst_spamvirustest_registered(struct sieve_validator * valdtr,const struct sieve_extension * ext,struct sieve_command_registration * cmd_reg)119 static bool tst_spamvirustest_registered
120 (struct sieve_validator *valdtr, const struct sieve_extension *ext,
121 struct sieve_command_registration *cmd_reg)
122 {
123 sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
124 sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
125
126 if ( sieve_extension_is(ext, spamtestplus_extension) ||
127 sieve_extension_is(ext, spamtest_extension) ) {
128 sieve_validator_register_tag
129 (valdtr, cmd_reg, ext, &spamtest_percent_tag, OPT_SPAMTEST_PERCENT);
130 }
131
132 return TRUE;
133 }
134
135 /*
136 * Validation
137 */
138
tst_spamtest_validate_percent_tag(struct sieve_validator * valdtr,struct sieve_ast_argument ** arg,struct sieve_command * tst)139 static bool tst_spamtest_validate_percent_tag
140 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
141 struct sieve_command *tst)
142 {
143 if ( !sieve_extension_is(tst->ext, spamtestplus_extension) ) {
144 sieve_argument_validate_error(valdtr, *arg,
145 "the spamtest test only accepts the :percent argument when "
146 "the spamtestplus extension is active");
147 return FALSE;
148 }
149
150 /* Skip tag */
151 *arg = sieve_ast_argument_next(*arg);
152
153 return TRUE;
154 }
155
tst_spamvirustest_validate(struct sieve_validator * valdtr,struct sieve_command * tst)156 static bool tst_spamvirustest_validate
157 (struct sieve_validator *valdtr, struct sieve_command *tst)
158 {
159 struct sieve_ast_argument *arg = tst->first_positional;
160 const struct sieve_match_type mcht_default =
161 SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
162 const struct sieve_comparator cmp_default =
163 SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
164
165 /* Check value */
166
167 if ( !sieve_validate_positional_argument
168 (valdtr, tst, arg, "value", 1, SAAT_STRING) ) {
169 return FALSE;
170 }
171
172 if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
173 return FALSE;
174
175 /* Validate the key argument to a specified match type */
176 return sieve_match_type_validate
177 (valdtr, tst, arg, &mcht_default, &cmp_default);
178 }
179
180 /*
181 * Code generation
182 */
183
tst_spamvirustest_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * tst)184 static bool tst_spamvirustest_generate
185 (const struct sieve_codegen_env *cgenv, struct sieve_command *tst)
186 {
187 if ( sieve_command_is(tst, spamtest_test) )
188 sieve_operation_emit(cgenv->sblock, tst->ext, &spamtest_operation);
189 else if ( sieve_command_is(tst, virustest_test) )
190 sieve_operation_emit(cgenv->sblock, tst->ext, &virustest_operation);
191 else
192 i_unreached();
193
194 /* Generate arguments */
195 return sieve_generate_arguments(cgenv, tst, NULL);
196 }
197
198 /*
199 * Code dump
200 */
201
tst_spamvirustest_operation_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)202 static bool tst_spamvirustest_operation_dump
203 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
204 {
205 int opt_code = 0;
206 const struct sieve_operation *op = denv->oprtn;
207
208 sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
209 sieve_code_descend(denv);
210
211 /* Optional operands */
212 for (;;) {
213 int opt;
214
215 if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 )
216 return FALSE;
217
218 if ( opt == 0 ) break;
219
220 switch ( opt_code ) {
221 case OPT_SPAMTEST_PERCENT:
222 sieve_code_dumpf(denv, "percent");
223 break;
224 default:
225 return FALSE;
226 }
227 }
228
229 return
230 sieve_opr_string_dump(denv, address, "value");
231 }
232
233 /*
234 * Code execution
235 */
236
tst_spamvirustest_operation_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)237 static int tst_spamvirustest_operation_execute
238 (const struct sieve_runtime_env *renv, sieve_size_t *address)
239 {
240 const struct sieve_operation *op = renv->oprtn;
241 const struct sieve_extension *this_ext = op->ext;
242 int opt_code = 0;
243 struct sieve_match_type mcht =
244 SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
245 struct sieve_comparator cmp =
246 SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
247 bool percent = FALSE;
248 struct sieve_stringlist *value_list, *key_list;
249 const char *score_value;
250 int match, ret;
251
252 /* Read optional operands */
253 for (;;) {
254 int opt;
255
256 if ( (opt=sieve_match_opr_optional_read
257 (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 )
258 return ret;
259
260 if ( opt == 0 ) break;
261
262 switch ( opt_code ) {
263 case OPT_SPAMTEST_PERCENT:
264 percent = TRUE;
265 break;
266 default:
267 sieve_runtime_trace_error(renv, "unknown optional operand");
268 return SIEVE_EXEC_BIN_CORRUPT;
269 }
270 }
271
272 /* Read value part */
273 if ( (ret=sieve_opr_stringlist_read(renv, address, "value", &key_list)) <= 0 )
274 return ret;
275
276 /* Perform test */
277
278 if ( sieve_operation_is(op, spamtest_operation) ) {
279 sieve_runtime_trace
280 (renv, SIEVE_TRLVL_TESTS, "spamtest test [percent=%s]",
281 ( percent ? "true" : "false" ));
282 } else {
283 sieve_runtime_trace
284 (renv, SIEVE_TRLVL_TESTS, "virustest test");
285 }
286
287 /* Get score value */
288 sieve_runtime_trace_descend(renv);
289 if ( (ret=ext_spamvirustest_get_value
290 (renv, this_ext, percent, &score_value)) <= 0 )
291 return ret;
292 sieve_runtime_trace_ascend(renv);
293
294 /* Construct value list */
295 value_list = sieve_single_stringlist_create_cstr(renv, score_value, TRUE);
296
297 /* Perform match */
298 if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 )
299 return ret;
300
301 /* Set test result for subsequent conditional jump */
302 sieve_interpreter_set_test_result(renv->interp, match > 0);
303 return SIEVE_EXEC_OK;
304 }
305