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