1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "str-sanitize.h"
6
7 #include "sieve-common.h"
8 #include "sieve-script.h"
9 #include "sieve-storage.h"
10 #include "sieve-ast.h"
11 #include "sieve-code.h"
12 #include "sieve-extensions.h"
13 #include "sieve-commands.h"
14 #include "sieve-validator.h"
15 #include "sieve-binary.h"
16 #include "sieve-generator.h"
17 #include "sieve-interpreter.h"
18 #include "sieve-dump.h"
19
20 #include "ext-include-common.h"
21 #include "ext-include-binary.h"
22
23 /*
24 * Include command
25 *
26 * Syntax:
27 * include [LOCATION] [":once"] [":optional"] <value: string>
28 *
29 * [LOCATION]:
30 * ":personal" / ":global"
31 */
32
33 static bool cmd_include_registered
34 (struct sieve_validator *valdtr, const struct sieve_extension *ext,
35 struct sieve_command_registration *cmd_reg);
36 static bool cmd_include_pre_validate
37 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd);
38 static bool cmd_include_validate
39 (struct sieve_validator *valdtr, struct sieve_command *cmd);
40 static bool cmd_include_generate
41 (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
42
43 const struct sieve_command_def cmd_include = {
44 .identifier = "include",
45 .type = SCT_COMMAND,
46 .positional_args = 1,
47 .subtests = 0,
48 .block_allowed = FALSE,
49 .block_required = FALSE,
50 .registered = cmd_include_registered,
51 .pre_validate = cmd_include_pre_validate,
52 .validate = cmd_include_validate,
53 .generate = cmd_include_generate
54 };
55
56 /*
57 * Include operation
58 */
59
60 static bool opc_include_dump
61 (const struct sieve_dumptime_env *denv, sieve_size_t *address);
62 static int opc_include_execute
63 (const struct sieve_runtime_env *renv, sieve_size_t *address);
64
65 const struct sieve_operation_def include_operation = {
66 .mnemonic = "include",
67 .ext_def = &include_extension,
68 .code = EXT_INCLUDE_OPERATION_INCLUDE,
69 .dump = opc_include_dump,
70 .execute = opc_include_execute
71 };
72
73 /*
74 * Context structures
75 */
76
77 struct cmd_include_context_data {
78 enum ext_include_script_location location;
79
80 struct sieve_script *script;
81 enum ext_include_flags flags;
82
83 bool location_assigned:1;
84 };
85
86 /*
87 * Tagged arguments
88 */
89
90 static bool cmd_include_validate_location_tag
91 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
92 struct sieve_command *cmd);
93
94 static const struct sieve_argument_def include_personal_tag = {
95 .identifier = "personal",
96 .validate = cmd_include_validate_location_tag
97 };
98
99 static const struct sieve_argument_def include_global_tag = {
100 .identifier = "global",
101 .validate = cmd_include_validate_location_tag
102 };
103
104 static bool cmd_include_validate_boolean_tag
105 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
106 struct sieve_command *cmd);
107
108 static const struct sieve_argument_def include_once_tag = {
109 .identifier = "once",
110 .validate = cmd_include_validate_boolean_tag
111 };
112
113 static const struct sieve_argument_def include_optional_tag = {
114 .identifier = "optional",
115 .validate = cmd_include_validate_boolean_tag
116 };
117
118 /*
119 * Tag validation
120 */
121
cmd_include_validate_location_tag(struct sieve_validator * valdtr,struct sieve_ast_argument ** arg,struct sieve_command * cmd)122 static bool cmd_include_validate_location_tag
123 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
124 struct sieve_command *cmd)
125 {
126 struct cmd_include_context_data *ctx_data =
127 (struct cmd_include_context_data *) cmd->data;
128
129 if ( ctx_data->location_assigned) {
130 sieve_argument_validate_error(valdtr, *arg,
131 "include: cannot use location tags ':personal' and ':global' "
132 "multiple times");
133 return FALSE;
134 }
135
136 if ( sieve_argument_is(*arg, include_personal_tag) )
137 ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
138 else if ( sieve_argument_is(*arg, include_global_tag) )
139 ctx_data->location = EXT_INCLUDE_LOCATION_GLOBAL;
140 else
141 return FALSE;
142
143 ctx_data->location_assigned = TRUE;
144
145 /* Delete this tag (for now) */
146 *arg = sieve_ast_arguments_detach(*arg, 1);
147
148 return TRUE;
149 }
150
cmd_include_validate_boolean_tag(struct sieve_validator * valdtr ATTR_UNUSED,struct sieve_ast_argument ** arg,struct sieve_command * cmd)151 static bool cmd_include_validate_boolean_tag
152 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
153 struct sieve_command *cmd)
154 {
155 struct cmd_include_context_data *ctx_data =
156 (struct cmd_include_context_data *) cmd->data;
157
158 if ( sieve_argument_is(*arg, include_once_tag) )
159 ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE;
160 else
161 ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL;
162
163 /* Delete this tag (for now) */
164 *arg = sieve_ast_arguments_detach(*arg, 1);
165
166 return TRUE;
167 }
168
169 /*
170 * Command registration
171 */
172
cmd_include_registered(struct sieve_validator * valdtr,const struct sieve_extension * ext,struct sieve_command_registration * cmd_reg)173 static bool cmd_include_registered
174 (struct sieve_validator *valdtr, const struct sieve_extension *ext,
175 struct sieve_command_registration *cmd_reg)
176 {
177 sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0);
178 sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0);
179 sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0);
180 sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0);
181
182 return TRUE;
183 }
184
185 /*
186 * Command validation
187 */
188
cmd_include_pre_validate(struct sieve_validator * valdtr ATTR_UNUSED,struct sieve_command * cmd)189 static bool cmd_include_pre_validate
190 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd)
191 {
192 struct cmd_include_context_data *ctx_data;
193
194 /* Assign context */
195 ctx_data = p_new(sieve_command_pool(cmd), struct cmd_include_context_data, 1);
196 ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
197 cmd->data = ctx_data;
198
199 return TRUE;
200 }
201
cmd_include_validate(struct sieve_validator * valdtr,struct sieve_command * cmd)202 static bool cmd_include_validate
203 (struct sieve_validator *valdtr, struct sieve_command *cmd)
204 {
205 const struct sieve_extension *this_ext = cmd->ext;
206 struct sieve_ast_argument *arg = cmd->first_positional;
207 struct cmd_include_context_data *ctx_data =
208 (struct cmd_include_context_data *) cmd->data;
209 struct sieve_storage *storage;
210 struct sieve_script *script;
211 const char *script_name;
212 enum sieve_error error = SIEVE_ERROR_NONE;
213 int ret;
214
215 /* Check argument */
216 if ( !sieve_validate_positional_argument
217 (valdtr, cmd, arg, "value", 1, SAAT_STRING) ) {
218 return FALSE;
219 }
220
221 if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
222 return FALSE;
223
224 /*
225 * Variables are not allowed.
226 */
227 if ( !sieve_argument_is_string_literal(arg) ) {
228 sieve_argument_validate_error(valdtr, arg,
229 "the include command requires a constant string for its value argument");
230 return FALSE;
231 }
232
233 /* Find the script */
234
235 script_name = sieve_ast_argument_strc(arg);
236
237 if ( !sieve_script_name_is_valid(script_name) ) {
238 sieve_argument_validate_error(valdtr, arg,
239 "include: invalid script name '%s'",
240 str_sanitize(script_name, 80));
241 return FALSE;
242 }
243
244 storage = ext_include_get_script_storage
245 (this_ext, ctx_data->location, script_name, &error);
246 if ( storage == NULL ) {
247 // FIXME: handle ':optional' in this case
248 if (error == SIEVE_ERROR_NOT_FOUND) {
249 sieve_argument_validate_error(valdtr, arg,
250 "include: %s location for included script `%s' is unavailable "
251 "(contact system administrator for more information)",
252 ext_include_script_location_name(ctx_data->location),
253 str_sanitize(script_name, 80));
254 } else {
255 sieve_argument_validate_error(valdtr, arg,
256 "include: failed to access %s location for included script `%s' "
257 "(contact system administrator for more information)",
258 ext_include_script_location_name(ctx_data->location),
259 str_sanitize(script_name, 80));
260 }
261 return FALSE;
262 }
263
264 /* Create script object */
265 script = sieve_storage_get_script
266 (storage, script_name, &error);
267 if ( script == NULL )
268 return FALSE;
269
270 ret = sieve_script_open(script, &error);
271 if ( ret < 0 ) {
272 if ( error != SIEVE_ERROR_NOT_FOUND ) {
273 sieve_argument_validate_error(valdtr, arg,
274 "failed to access included %s script '%s': %s",
275 ext_include_script_location_name(ctx_data->location),
276 str_sanitize(script_name, 80),
277 sieve_script_get_last_error_lcase(script));
278 sieve_script_unref(&script);
279 return FALSE;
280
281 /* Not found */
282 } else {
283 enum sieve_compile_flags cpflags =
284 sieve_validator_compile_flags(valdtr);
285
286 if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) {
287 /* :optional */
288
289 } else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) {
290 /* Script is being uploaded */
291 sieve_argument_validate_warning(valdtr, arg,
292 "included %s script '%s' does not exist (ignored during upload)",
293 ext_include_script_location_name(ctx_data->location),
294 str_sanitize(script_name, 80));
295 ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD;
296
297 } else {
298 /* Should have existed */
299 sieve_argument_validate_error(valdtr, arg,
300 "included %s script '%s' does not exist",
301 ext_include_script_location_name(ctx_data->location),
302 str_sanitize(script_name, 80));
303 sieve_script_unref(&script);
304 return FALSE;
305 }
306 }
307 }
308
309 ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script);
310 ctx_data->script = script;
311
312 (void)sieve_ast_arguments_detach(arg, 1);
313 return TRUE;
314 }
315
316 /*
317 * Code Generation
318 */
319
cmd_include_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)320 static bool cmd_include_generate
321 (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
322 {
323 struct cmd_include_context_data *ctx_data =
324 (struct cmd_include_context_data *) cmd->data;
325 const struct ext_include_script_info *included;
326 int ret;
327
328 /* Compile (if necessary) and include the script into the binary.
329 * This yields the id of the binary block containing the compiled byte code.
330 */
331 if ( (ret=ext_include_generate_include
332 (cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script,
333 &included)) < 0 )
334 return FALSE;
335
336 if ( ret > 0 ) {
337 (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation);
338 (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id);
339 (void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags);
340 }
341
342 return TRUE;
343 }
344
345 /*
346 * Code dump
347 */
348
opc_include_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)349 static bool opc_include_dump
350 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
351 {
352 const struct ext_include_script_info *included;
353 struct ext_include_binary_context *binctx;
354 unsigned int include_id, flags;
355
356 sieve_code_dumpf(denv, "INCLUDE:");
357
358 sieve_code_mark(denv);
359 if ( !sieve_binary_read_unsigned(denv->sblock, address, &include_id) )
360 return FALSE;
361
362 if ( !sieve_binary_read_byte(denv->sblock, address, &flags) )
363 return FALSE;
364
365 binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin);
366 included = ext_include_binary_script_get_included(binctx, include_id);
367 if ( included == NULL )
368 return FALSE;
369
370 sieve_code_descend(denv);
371 sieve_code_dumpf(denv, "script: `%s' from %s %s%s[ID: %d, BLOCK: %d]",
372 sieve_script_name(included->script), sieve_script_location(included->script),
373 ((flags & EXT_INCLUDE_FLAG_ONCE) != 0 ? "(once) " : ""),
374 ((flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ? "(optional) " : ""),
375 include_id, sieve_binary_block_get_id(included->block));
376
377 return TRUE;
378 }
379
380 /*
381 * Execution
382 */
383
opc_include_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)384 static int opc_include_execute
385 (const struct sieve_runtime_env *renv, sieve_size_t *address)
386 {
387 unsigned int include_id, flags;
388
389 if ( !sieve_binary_read_unsigned(renv->sblock, address, &include_id) ) {
390 sieve_runtime_trace_error(renv, "invalid include-id operand");
391 return SIEVE_EXEC_BIN_CORRUPT;
392 }
393
394 if ( !sieve_binary_read_unsigned(renv->sblock, address, &flags) ) {
395 sieve_runtime_trace_error(renv, "invalid flags operand");
396 return SIEVE_EXEC_BIN_CORRUPT;
397 }
398
399 return ext_include_execute_include
400 (renv, include_id, (enum ext_include_flags)flags);
401 }
402
403
404
405
406
407