1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "array.h"
6
7 #include "sieve-common.h"
8 #include "sieve-stringlist.h"
9 #include "sieve-extensions.h"
10 #include "sieve-code.h"
11 #include "sieve-commands.h"
12 #include "sieve-validator.h"
13 #include "sieve-generator.h"
14 #include "sieve-interpreter.h"
15
16 #include "ext-ihave-common.h"
17
18 /*
19 * Ihave test
20 *
21 * Syntax:
22 * ihave <capabilities: string-list>
23 */
24
25 static bool
26 tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst);
27 static bool
28 tst_ihave_validate_const(struct sieve_validator *valdtr,
29 struct sieve_command *tst, int *const_current,
30 int const_next);
31 static bool
32 tst_ihave_generate(const struct sieve_codegen_env *cgenv,
33 struct sieve_command *tst);
34
35 const struct sieve_command_def ihave_test = {
36 .identifier = "ihave",
37 .type = SCT_TEST,
38 .positional_args = 1,
39 .subtests = 0,
40 .block_allowed = FALSE,
41 .block_required = FALSE,
42 .validate = tst_ihave_validate,
43 .validate_const = tst_ihave_validate_const,
44 .generate = tst_ihave_generate
45 };
46
47 /*
48 * Ihave operation
49 */
50
51 static bool
52 tst_ihave_operation_dump(const struct sieve_dumptime_env *denv,
53 sieve_size_t *address);
54 static int
55 tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
56 sieve_size_t *address);
57
58 const struct sieve_operation_def tst_ihave_operation = {
59 .mnemonic = "IHAVE",
60 .ext_def = &ihave_extension,
61 .code = EXT_IHAVE_OPERATION_IHAVE,
62 .dump = tst_ihave_operation_dump,
63 .execute = tst_ihave_operation_execute
64 };
65
66 /*
67 * Code validation
68 */
69
70 static bool
tst_ihave_validate(struct sieve_validator * valdtr,struct sieve_command * tst)71 tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
72 {
73 struct _capability {
74 const struct sieve_extension *ext;
75 struct sieve_ast_argument *arg;
76 };
77 struct sieve_ast_argument *arg = tst->first_positional;
78 struct sieve_ast_argument *stritem;
79 enum sieve_compile_flags cpflags =
80 sieve_validator_compile_flags(valdtr);
81 bool no_global = ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0);
82 ARRAY(struct _capability) capabilities;
83 struct _capability capability;
84 const struct _capability *caps;
85 unsigned int i, count;
86 bool all_known = TRUE;
87
88 t_array_init(&capabilities, 64);
89
90 tst->data = (void *)FALSE;
91
92 /* Check stringlist argument */
93 if (!sieve_validate_positional_argument(valdtr, tst, arg,
94 "capabilities", 1,
95 SAAT_STRING_LIST))
96 return FALSE;
97
98 switch (sieve_ast_argument_type(arg)) {
99 case SAAT_STRING:
100 /* Single string */
101 capability.arg = arg;
102 capability.ext = sieve_extension_get_by_name(
103 tst->ext->svinst, sieve_ast_argument_strc(arg));
104
105 if (capability.ext == NULL ||
106 (no_global && capability.ext->global)) {
107 all_known = FALSE;
108
109 ext_ihave_ast_add_missing_extension(
110 tst->ext, tst->ast_node->ast,
111 sieve_ast_argument_strc(arg));
112 } else {
113 array_append(&capabilities, &capability, 1);
114 }
115
116 break;
117
118 case SAAT_STRING_LIST:
119 /* String list */
120 stritem = sieve_ast_strlist_first(arg);
121
122 while (stritem != NULL) {
123 capability.arg = stritem;
124 capability.ext = sieve_extension_get_by_name(
125 tst->ext->svinst,
126 sieve_ast_argument_strc(stritem));
127
128 if (capability.ext == NULL ||
129 (no_global && capability.ext->global)) {
130 all_known = FALSE;
131
132 ext_ihave_ast_add_missing_extension(
133 tst->ext, tst->ast_node->ast,
134 sieve_ast_argument_strc(stritem));
135 } else {
136 array_append(&capabilities, &capability, 1);
137 }
138 stritem = sieve_ast_strlist_next(stritem);
139 }
140
141 break;
142 default:
143 i_unreached();
144 }
145
146 if (!all_known)
147 return TRUE;
148
149 /* RFC 5463, Section 4, page 4:
150
151 The "ihave" extension is designed to be used with other extensions
152 that add tests, actions, comparators, or arguments. Implementations
153 MUST NOT allow it to be used with extensions that change the
154 underlying Sieve grammar, or extensions like encoded-character
155 [RFC5228], or variables [RFC5229] that change how the content of
156 Sieve scripts are interpreted. The test MUST fail and the extension
157 MUST NOT be enabled if such usage is attempted.
158
159 FIXME: current implementation of this restriction is hardcoded and
160 therefore highly inflexible
161 */
162 caps = array_get(&capabilities, &count);
163 for (i = 0; i < count; i++) {
164 if (sieve_extension_name_is(caps[i].ext, "variables") ||
165 sieve_extension_name_is(caps[i].ext, "encoded-character"))
166 return TRUE;
167 }
168
169 /* Load all extensions */
170 caps = array_get(&capabilities, &count);
171 for (i = 0; i < count; i++) {
172 if (!sieve_validator_extension_load(valdtr, tst, caps[i].arg,
173 caps[i].ext, FALSE))
174 return FALSE;
175 }
176
177 if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
178 return FALSE;
179
180 tst->data = (void *)TRUE;
181 return TRUE;
182 }
183
184 static bool
tst_ihave_validate_const(struct sieve_validator * valdtr ATTR_UNUSED,struct sieve_command * tst,int * const_current,int const_next ATTR_UNUSED)185 tst_ihave_validate_const(struct sieve_validator *valdtr ATTR_UNUSED,
186 struct sieve_command *tst, int *const_current,
187 int const_next ATTR_UNUSED)
188 {
189 if ((bool)tst->data == TRUE)
190 *const_current = -1;
191 else
192 *const_current = 0;
193 return TRUE;
194 }
195
196 /*
197 * Code generation
198 */
199
tst_ihave_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * tst)200 bool tst_ihave_generate(const struct sieve_codegen_env *cgenv,
201 struct sieve_command *tst)
202 {
203 /* Emit opcode */
204 sieve_operation_emit(cgenv->sblock, tst->ext, &tst_ihave_operation);
205
206 /* Generate arguments */
207 return sieve_generate_arguments(cgenv, tst, NULL);
208 }
209
210 /*
211 * Code dump
212 */
213
214 static bool
tst_ihave_operation_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)215 tst_ihave_operation_dump(const struct sieve_dumptime_env *denv,
216 sieve_size_t *address)
217 {
218 sieve_code_dumpf(denv, "IHAVE");
219 sieve_code_descend(denv);
220
221 return sieve_opr_stringlist_dump(denv, address, "capabilities");
222 }
223
224 /*
225 * Code execution
226 */
227
228 static int
tst_ihave_operation_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)229 tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
230 sieve_size_t *address)
231 {
232 const struct sieve_execute_env *eenv = renv->exec_env;
233 struct sieve_instance *svinst = eenv->svinst;
234 struct sieve_stringlist *capabilities;
235 string_t *cap_item;
236 bool matched;
237 int ret;
238
239 /*
240 * Read operands
241 */
242
243 /* Read capabilities */
244 if ((ret = sieve_opr_stringlist_read(renv, address, "capabilities",
245 &capabilities)) <= 0)
246 return ret;
247
248 /*
249 * Perform test
250 */
251
252 /* Perform the test */
253 sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "ihave test");
254 sieve_runtime_trace_descend(renv);
255
256 cap_item = NULL;
257 matched = TRUE;
258 while (matched &&
259 (ret = sieve_stringlist_next_item(capabilities,
260 &cap_item)) > 0) {
261 const struct sieve_extension *ext;
262 int sret;
263
264 ext = sieve_extension_get_by_name(svinst, str_c(cap_item));
265 if (ext == NULL) {
266 sieve_runtime_trace_error(
267 renv, "ihave: invalid extension name");
268 return SIEVE_EXEC_BIN_CORRUPT;
269 }
270 sret = sieve_interpreter_extension_start(renv->interp, ext);
271 if (sret == SIEVE_EXEC_FAILURE) {
272 sieve_runtime_trace(
273 renv, SIEVE_TRLVL_TESTS,
274 "extension `%s' not available",
275 sieve_extension_name(ext));
276 matched = FALSE;
277 } else if (sret == SIEVE_EXEC_OK) {
278 sieve_runtime_trace(
279 renv, SIEVE_TRLVL_TESTS,
280 "extension `%s' available",
281 sieve_extension_name(ext));
282 } else {
283 return sret;
284 }
285 }
286 if (ret < 0) {
287 sieve_runtime_trace_error(renv, "invalid capabilities item");
288 return SIEVE_EXEC_BIN_CORRUPT;
289 }
290
291 /* Set test result for subsequent conditional jump */
292 sieve_interpreter_set_test_result(renv->interp, matched);
293 return SIEVE_EXEC_OK;
294 }
295