1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "array.h"
6 #include "buffer.h"
7 #include "str.h"
8 #include "str-sanitize.h"
9 #include "istream.h"
10 #include "ostream.h"
11
12 #include "sieve-common.h"
13 #include "sieve-stringlist.h"
14 #include "sieve-binary.h"
15 #include "sieve-code.h"
16 #include "sieve-message.h"
17 #include "sieve-extensions.h"
18 #include "sieve-commands.h"
19 #include "sieve-actions.h"
20 #include "sieve-validator.h"
21 #include "sieve-generator.h"
22 #include "sieve-interpreter.h"
23 #include "sieve-dump.h"
24 #include "sieve-result.h"
25
26 #include "sieve-ext-variables.h"
27
28 #include "sieve-extprograms-common.h"
29
30 /* Execute command
31 *
32 * Syntax:
33 * "execute" [":input" <input-data: string> / ":pipe"]
34 * [":output" <varname: string>]
35 * <program-name: string> [<arguments: string-list>]
36 *
37 */
38
39 static bool
40 cmd_execute_registered(struct sieve_validator *valdtr,
41 const struct sieve_extension *ext,
42 struct sieve_command_registration *cmd_reg);
43 static bool
44 cmd_execute_generate(const struct sieve_codegen_env *cgenv,
45 struct sieve_command *ctx);
46
47 const struct sieve_command_def sieve_cmd_execute = {
48 .identifier = "execute",
49 .type = SCT_HYBRID,
50 .positional_args = -1, /* We check positional arguments ourselves */
51 .subtests = 0,
52 .block_allowed = FALSE,
53 .block_required = FALSE,
54 .registered = cmd_execute_registered,
55 .validate = sieve_extprogram_command_validate,
56 .generate = cmd_execute_generate,
57 };
58
59 /*
60 * Tagged arguments
61 */
62
63 static bool
64 cmd_execute_validate_input_tag(struct sieve_validator *valdtr,
65 struct sieve_ast_argument **arg,
66 struct sieve_command *cmd);
67 static bool
68 cmd_execute_generate_input_tag(const struct sieve_codegen_env *cgenv,
69 struct sieve_ast_argument *arg,
70 struct sieve_command *cmd);
71
72 static bool
73 cmd_execute_validate_output_tag(struct sieve_validator *valdtr,
74 struct sieve_ast_argument **arg,
75 struct sieve_command *cmd);
76
77 static const struct sieve_argument_def execute_input_tag = {
78 .identifier = "input",
79 .validate = cmd_execute_validate_input_tag,
80 .generate = cmd_execute_generate_input_tag
81 };
82
83 static const struct sieve_argument_def execute_pipe_tag = {
84 .identifier = "pipe",
85 .validate = cmd_execute_validate_input_tag,
86 .generate = cmd_execute_generate_input_tag
87 };
88
89 static const struct sieve_argument_def execute_output_tag = {
90 .identifier = "output",
91 .validate = cmd_execute_validate_output_tag,
92 };
93
94
95 /*
96 * Execute operation
97 */
98
99 static bool
100 cmd_execute_operation_dump(const struct sieve_dumptime_env *denv,
101 sieve_size_t *address);
102 static int
103 cmd_execute_operation_execute(const struct sieve_runtime_env *renv,
104 sieve_size_t *address);
105
106 const struct sieve_operation_def sieve_opr_execute = {
107 .mnemonic = "EXECUTE",
108 .ext_def = &sieve_ext_vnd_execute,
109 .dump = cmd_execute_operation_dump,
110 .execute = cmd_execute_operation_execute
111 };
112
113 /* Codes for optional operands */
114
115 enum cmd_execute_optional {
116 OPT_END,
117 OPT_INPUT,
118 OPT_OUTPUT
119 };
120
121 /*
122 * Tag validation
123 */
124
125 static bool
cmd_execute_validate_input_tag(struct sieve_validator * valdtr,struct sieve_ast_argument ** arg,struct sieve_command * cmd)126 cmd_execute_validate_input_tag(struct sieve_validator *valdtr,
127 struct sieve_ast_argument **arg,
128 struct sieve_command *cmd)
129 {
130 struct sieve_ast_argument *tag = *arg;
131
132 if ((bool)cmd->data) {
133 sieve_argument_validate_error(
134 valdtr, *arg,
135 "multiple :input or :pipe arguments specified for the %s %s",
136 sieve_command_identifier(cmd),
137 sieve_command_type_name(cmd));
138 return FALSE;
139 }
140
141 cmd->data = (void *)TRUE;
142
143 /* Skip tag */
144 *arg = sieve_ast_argument_next(*arg);
145
146 if (sieve_argument_is(tag, execute_input_tag)) {
147 /* Check syntax:
148 * :input <input-data: string>
149 */
150 if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL,
151 0, SAAT_STRING, FALSE))
152 return FALSE;
153
154 /* Assign tag parameters */
155 tag->parameters = *arg;
156 *arg = sieve_ast_arguments_detach(*arg,1);
157 }
158
159 return TRUE;
160 }
161
162 static bool
cmd_execute_validate_output_tag(struct sieve_validator * valdtr,struct sieve_ast_argument ** arg,struct sieve_command * cmd)163 cmd_execute_validate_output_tag(struct sieve_validator *valdtr,
164 struct sieve_ast_argument **arg,
165 struct sieve_command *cmd)
166 {
167 struct sieve_ast_argument *tag = *arg;
168 struct sieve_extprograms_config *ext_config =
169 (struct sieve_extprograms_config *)cmd->ext->context;
170
171 if (ext_config == NULL || ext_config->var_ext == NULL ||
172 !sieve_ext_variables_is_active(ext_config->var_ext, valdtr)) {
173 sieve_argument_validate_error(
174 valdtr,*arg,
175 "the %s %s only allows for the specification of an "
176 ":output argument when the variables extension is active",
177 sieve_command_identifier(cmd),
178 sieve_command_type_name(cmd));
179 return FALSE;
180 }
181
182 /* Detach the tag itself */
183 *arg = sieve_ast_arguments_detach(*arg, 1);
184
185 if (!sieve_variable_argument_activate(ext_config->var_ext,
186 ext_config->var_ext, valdtr,
187 cmd, *arg, TRUE))
188 return FALSE;
189
190 (*arg)->argument->id_code = tag->argument->id_code;
191
192 /* Skip parameter */
193 *arg = sieve_ast_argument_next(*arg);
194
195 return TRUE;
196 }
197
198 /*
199 * Command registration
200 */
201
202 static bool
cmd_execute_registered(struct sieve_validator * valdtr,const struct sieve_extension * ext,struct sieve_command_registration * cmd_reg)203 cmd_execute_registered(struct sieve_validator *valdtr,
204 const struct sieve_extension *ext,
205 struct sieve_command_registration *cmd_reg)
206 {
207 sieve_validator_register_tag(valdtr, cmd_reg, ext,
208 &execute_input_tag, OPT_INPUT);
209 sieve_validator_register_tag(valdtr, cmd_reg, ext,
210 &execute_pipe_tag, OPT_INPUT);
211 sieve_validator_register_tag(valdtr, cmd_reg, ext,
212 &execute_output_tag, OPT_OUTPUT);
213 return TRUE;
214 }
215
216 /*
217 * Code generation
218 */
219
220 static bool
cmd_execute_generate_input_tag(const struct sieve_codegen_env * cgenv,struct sieve_ast_argument * arg,struct sieve_command * cmd)221 cmd_execute_generate_input_tag(const struct sieve_codegen_env *cgenv,
222 struct sieve_ast_argument *arg,
223 struct sieve_command *cmd)
224 {
225 if (arg->parameters == NULL) {
226 sieve_opr_omitted_emit(cgenv->sblock);
227 return TRUE;
228 }
229
230 return sieve_generate_argument_parameters(cgenv, cmd, arg);
231 }
232
233 static bool
cmd_execute_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)234 cmd_execute_generate(const struct sieve_codegen_env *cgenv,
235 struct sieve_command *cmd)
236 {
237 sieve_operation_emit(cgenv->sblock, cmd->ext, &sieve_opr_execute);
238
239 /* Emit is_test flag */
240 sieve_binary_emit_byte(cgenv->sblock,
241 (uint8_t)(cmd->ast_node->type == SAT_TEST ?
242 1 : 0));
243
244 /* Generate arguments */
245 if (!sieve_generate_arguments(cgenv, cmd, NULL))
246 return FALSE;
247
248 /* Emit a placeholder when the <arguments> argument is missing */
249 if (sieve_ast_argument_next(cmd->first_positional) == NULL)
250 sieve_opr_omitted_emit(cgenv->sblock);
251
252 return TRUE;
253 }
254
255 /*
256 * Code dump
257 */
258
259 static bool
cmd_execute_operation_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)260 cmd_execute_operation_dump(const struct sieve_dumptime_env *denv,
261 sieve_size_t *address)
262 {
263 int opt_code = 0;
264 unsigned int is_test = 0;
265
266 /* Read is_test flag */
267 if (!sieve_binary_read_byte(denv->sblock, address, &is_test))
268 return FALSE;
269
270 sieve_code_dumpf(denv, "EXECUTE (%s)",
271 (is_test > 0 ? "test" : "command"));
272 sieve_code_descend(denv);
273
274 /* Dump optional operands */
275 for (;;) {
276 int opt;
277 bool opok = TRUE;
278
279 if ((opt = sieve_action_opr_optional_dump(denv, address,
280 &opt_code)) < 0)
281 return FALSE;
282
283 if (opt == 0)
284 break;
285
286 switch (opt_code) {
287 case OPT_INPUT:
288 opok = sieve_opr_string_dump_ex(denv, address,
289 "input", "PIPE");
290 break;
291 case OPT_OUTPUT:
292 opok = sieve_opr_string_dump(denv, address, "output");
293 break;
294 default:
295 return FALSE;
296 }
297
298 if (!opok)
299 return FALSE;
300 }
301
302 if (!sieve_opr_string_dump(denv, address, "program-name"))
303 return FALSE;
304
305 return sieve_opr_stringlist_dump_ex(denv, address, "arguments", "");
306 }
307
308 /*
309 * Code execution
310 */
311
312 static int
cmd_execute_operation_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)313 cmd_execute_operation_execute(const struct sieve_runtime_env *renv,
314 sieve_size_t *address)
315 {
316 const struct sieve_execute_env *eenv = renv->exec_env;
317 const struct sieve_extension *this_ext = renv->oprtn->ext;
318 struct sieve_side_effects_list *slist = NULL;
319 int opt_code = 0;
320 unsigned int is_test = 0;
321 struct sieve_stringlist *args_list = NULL;
322 string_t *pname = NULL, *input = NULL;
323 struct sieve_variable_storage *var_storage = NULL;
324 unsigned int var_index;
325 bool have_input = FALSE;
326 const char *program_name = NULL;
327 const char *const *args = NULL;
328 enum sieve_error error = SIEVE_ERROR_NONE;
329 buffer_t *outbuf = NULL;
330 struct sieve_extprogram *sprog = NULL;
331 int ret;
332
333 /*
334 * Read operands
335 */
336
337 /* The is_test flag */
338 if (!sieve_binary_read_byte(renv->sblock, address, &is_test)) {
339 sieve_runtime_trace_error(renv, "invalid is_test flag");
340 return SIEVE_EXEC_BIN_CORRUPT;
341 }
342
343 /* Optional operands */
344
345 for (;;) {
346 int opt;
347
348 if ((opt = sieve_action_opr_optional_read(
349 renv, address, &opt_code, &ret, &slist)) < 0)
350 return ret;
351
352 if (opt == 0)
353 break;
354
355 switch (opt_code) {
356 case OPT_INPUT:
357 ret = sieve_opr_string_read_ex(renv, address, "input",
358 TRUE, &input, NULL);
359 have_input = TRUE;
360 break;
361 case OPT_OUTPUT:
362 ret = sieve_variable_operand_read(
363 renv, address, "output", &var_storage,
364 &var_index);
365 break;
366 default:
367 sieve_runtime_trace_error(
368 renv, "unknown optional operand");
369 return SIEVE_EXEC_BIN_CORRUPT;
370 }
371
372 if (ret <= 0)
373 return ret;
374 }
375
376 /* Fixed operands */
377
378 if ((ret = sieve_extprogram_command_read_operands(
379 renv, address, &pname, &args_list)) <= 0)
380 return ret;
381
382 program_name = str_c(pname);
383 if (args_list != NULL &&
384 sieve_stringlist_read_all(args_list, pool_datastack_create(),
385 &args) < 0) {
386 sieve_runtime_trace_error(renv, "failed to read args operand");
387 return args_list->exec_status;
388 }
389
390 /*
391 * Perform operation
392 */
393
394 /* Trace */
395
396 sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "execute action");
397 sieve_runtime_trace_descend(renv);
398 sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
399 "execute program `%s'",
400 str_sanitize(program_name, 128));
401
402 sprog = sieve_extprogram_create(this_ext, eenv->scriptenv,
403 eenv->msgdata, "execute",
404 program_name, args, &error);
405 if (sprog != NULL) {
406 if (var_storage != NULL) {
407 // FIXME: limit output size
408 struct ostream *outdata;
409
410 outbuf = buffer_create_dynamic(default_pool, 1024);
411 outdata = o_stream_create_buffer(outbuf);
412 sieve_extprogram_set_output(sprog, outdata);
413 o_stream_unref(&outdata);
414 }
415
416 if (input == NULL && have_input) {
417 struct mail *mail = sieve_message_get_mail(renv->msgctx);
418
419 if (sieve_extprogram_set_input_mail(sprog, mail) < 0) {
420 sieve_extprogram_destroy(&sprog);
421 if (outbuf != NULL)
422 buffer_free(&outbuf);
423 return sieve_runtime_mail_error(
424 renv, mail, "execute action: "
425 "failed to read input message");
426 }
427 ret = 1;
428 } else if (input != NULL) {
429 struct istream *indata =
430 i_stream_create_from_data(str_data(input),
431 str_len(input));
432 sieve_extprogram_set_input(sprog, indata);
433 i_stream_unref(&indata);
434 ret = 1;
435 }
436
437 if (ret >= 0)
438 ret = sieve_extprogram_run(sprog);
439 sieve_extprogram_destroy(&sprog);
440 } else {
441 ret = -1;
442 }
443
444 if (ret > 0) {
445 sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
446 "executed program successfully");
447
448 if (var_storage != NULL) {
449 string_t *var;
450
451 if (sieve_variable_get_modifiable(var_storage,
452 var_index, &var)) {
453 str_truncate(var, 0);
454 str_append_str(var, outbuf);
455
456 sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
457 "assigned output variable");
458 } // FIXME: handle failure
459 }
460
461 } else if (ret < 0) {
462 if (error == SIEVE_ERROR_NOT_FOUND) {
463 sieve_runtime_error(
464 renv, NULL,
465 "execute action: program `%s' not found",
466 str_sanitize(program_name, 80));
467 } else {
468 sieve_extprogram_exec_error(
469 renv->ehandler,
470 sieve_runtime_get_full_command_location(renv),
471 "execute action: failed to execute to program `%s'",
472 str_sanitize(program_name, 80));
473 }
474 } else {
475 sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
476 "execute action: "
477 "program indicated false result");
478 }
479
480 if (outbuf != NULL)
481 buffer_free(&outbuf);
482
483 if (is_test > 0) {
484 sieve_interpreter_set_test_result(renv->interp, (ret > 0));
485 return SIEVE_EXEC_OK;
486 }
487 return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE);
488 }
489