1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "array.h"
6 #include "str.h"
7 #include "str-sanitize.h"
8 
9 #include "sieve-common.h"
10 #include "sieve-stringlist.h"
11 #include "sieve-code.h"
12 #include "sieve-message.h"
13 #include "sieve-extensions.h"
14 #include "sieve-commands.h"
15 #include "sieve-actions.h"
16 #include "sieve-validator.h"
17 #include "sieve-generator.h"
18 #include "sieve-interpreter.h"
19 #include "sieve-dump.h"
20 #include "sieve-result.h"
21 
22 #include "sieve-extprograms-common.h"
23 
24 /* Pipe command
25  *
26  * Syntax:
27  *   pipe [":copy"] [":try"] <program-name: string> [<arguments: string-list>]
28  *
29  */
30 
31 static bool
32 cmd_pipe_registered(struct sieve_validator *valdtr,
33 		    const struct sieve_extension *ext,
34 		    struct sieve_command_registration *cmd_reg);
35 static bool
36 cmd_pipe_generate(const struct sieve_codegen_env *cgenv,
37 		  struct sieve_command *ctx);
38 
39 const struct sieve_command_def sieve_cmd_pipe = {
40 	.identifier = "pipe",
41 	.type = SCT_COMMAND,
42 	.positional_args = -1, /* We check positional arguments ourselves */
43 	.subtests = 0,
44 	.block_allowed = FALSE,
45 	.block_required = FALSE,
46 	.registered = cmd_pipe_registered,
47 	.validate = sieve_extprogram_command_validate,
48 	.generate = cmd_pipe_generate,
49 };
50 
51 /*
52  * Tagged arguments
53  */
54 
55 static const struct sieve_argument_def pipe_try_tag = {
56 	.identifier = "try",
57 };
58 
59 /*
60  * Pipe operation
61  */
62 
63 static bool
64 cmd_pipe_operation_dump(const struct sieve_dumptime_env *denv,
65 			sieve_size_t *address);
66 static int
67 cmd_pipe_operation_execute(const struct sieve_runtime_env *renv,
68 			   sieve_size_t *address);
69 
70 const struct sieve_operation_def sieve_opr_pipe = {
71 	.mnemonic = "PIPE",
72 	.ext_def = &sieve_ext_vnd_pipe,
73 	.dump = cmd_pipe_operation_dump,
74 	.execute = cmd_pipe_operation_execute,
75 };
76 
77 /* Codes for optional operands */
78 
79 enum cmd_pipe_optional {
80 	OPT_END,
81 	OPT_TRY,
82 };
83 
84 /*
85  * Pipe action
86  */
87 
88 /* Forward declarations */
89 
90 static int
91 act_pipe_check_duplicate(const struct sieve_runtime_env *renv,
92 			 const struct sieve_action *act,
93 			 const struct sieve_action *act_other);
94 static void
95 act_pipe_print(const struct sieve_action *action,
96 	       const struct sieve_result_print_env *rpenv,
97 	       bool *keep);
98 static int
99 act_pipe_start(const struct sieve_action_exec_env *aenv, void **tr_context);
100 static int
101 act_pipe_execute(const struct sieve_action_exec_env *aenv,
102 		void *tr_context, bool *keep);
103 static int
104 act_pipe_commit(const struct sieve_action_exec_env *aenv,
105 		void *tr_context);
106 static void
107 act_pipe_rollback(const struct sieve_action_exec_env *aenv,
108 		  void *tr_context, bool success);
109 
110 /* Action object */
111 
112 const struct sieve_action_def act_pipe = {
113 	.name = "pipe",
114 	.flags = SIEVE_ACTFLAG_TRIES_DELIVER,
115 	.check_duplicate = act_pipe_check_duplicate,
116 	.print = act_pipe_print,
117 	.start = act_pipe_start,
118 	.execute = act_pipe_execute,
119 	.commit = act_pipe_commit,
120 	.rollback = act_pipe_rollback,
121 };
122 
123 /* Action context information */
124 
125 struct ext_pipe_action {
126 	const char *program_name;
127 	const char * const *args;
128 	bool try;
129 };
130 
131 /*
132  * Command registration
133  */
134 
135 static bool
cmd_pipe_registered(struct sieve_validator * valdtr,const struct sieve_extension * ext,struct sieve_command_registration * cmd_reg)136 cmd_pipe_registered(struct sieve_validator *valdtr,
137 		    const struct sieve_extension *ext,
138 		    struct sieve_command_registration *cmd_reg)
139 {
140 	sieve_validator_register_tag(valdtr, cmd_reg, ext,
141 				     &pipe_try_tag, OPT_TRY);
142 	return TRUE;
143 }
144 
145 /*
146  * Code generation
147  */
148 
149 static bool
cmd_pipe_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)150 cmd_pipe_generate(const struct sieve_codegen_env *cgenv,
151 		  struct sieve_command *cmd)
152 {
153 	sieve_operation_emit(cgenv->sblock, cmd->ext, &sieve_opr_pipe);
154 
155 	/* Generate arguments */
156 	if (!sieve_generate_arguments(cgenv, cmd, NULL))
157 		return FALSE;
158 
159 	/* Emit a placeholder when the <arguments> argument is missing */
160 	if (sieve_ast_argument_next(cmd->first_positional) == NULL)
161 		sieve_opr_omitted_emit(cgenv->sblock);
162 	return TRUE;
163 }
164 
165 /*
166  * Code dump
167  */
168 
169 static bool
cmd_pipe_operation_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)170 cmd_pipe_operation_dump(const struct sieve_dumptime_env *denv,
171 			sieve_size_t *address)
172 {
173 	int opt_code = 0;
174 
175 	sieve_code_dumpf(denv, "PIPE");
176 	sieve_code_descend(denv);
177 
178 	/* Dump optional operands */
179 	for (;;) {
180 		int opt;
181 
182 		if ((opt = sieve_action_opr_optional_dump(denv, address,
183 							  &opt_code)) < 0)
184 			return FALSE;
185 
186 		if (opt == 0)
187 			break;
188 
189 		switch (opt_code) {
190 		case OPT_TRY:
191 			sieve_code_dumpf(denv, "try");
192 			break;
193 		default:
194 			return FALSE;
195 		}
196 	}
197 
198 	if (!sieve_opr_string_dump(denv, address, "program-name"))
199 		return FALSE;
200 
201 	return sieve_opr_stringlist_dump_ex(denv, address, "arguments", "");
202 }
203 
204 /*
205  * Code execution
206  */
207 
208 static int
cmd_pipe_operation_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)209 cmd_pipe_operation_execute(const struct sieve_runtime_env *renv,
210 			   sieve_size_t *address)
211 {
212 	const struct sieve_extension *this_ext = renv->oprtn->ext;
213 	struct sieve_side_effects_list *slist = NULL;
214 	struct ext_pipe_action *act;
215 	pool_t pool;
216 	int opt_code = 0;
217 	struct sieve_stringlist *args_list = NULL;
218 	string_t *pname = NULL;
219 	bool try = FALSE;
220 	int ret;
221 
222 	/*
223 	 * Read operands
224 	 */
225 
226 	/* Optional operands */
227 
228 	for (;;) {
229 		int opt;
230 
231 		if ((opt = sieve_action_opr_optional_read(renv, address,
232 							  &opt_code, &ret,
233 							  &slist)) < 0)
234 			return ret;
235 
236 		if (opt == 0)
237 			break;
238 
239 		switch (opt_code) {
240 		case OPT_TRY:
241 			try = TRUE;
242 			break;
243 		default:
244 			sieve_runtime_trace_error(
245 				renv, "unknown optional operand");
246 			return SIEVE_EXEC_BIN_CORRUPT;
247 		}
248 	}
249 
250 	/* Fixed operands */
251 
252 	if ((ret = sieve_extprogram_command_read_operands(renv, address, &pname,
253 							  &args_list)) <= 0)
254 		return ret;
255 
256 	/*
257 	 * Perform operation
258 	 */
259 
260 	/* Trace */
261 
262 	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "pipe action");
263 
264 	/* Compose action */
265 
266 	pool = sieve_result_pool(renv->result);
267 	act = p_new(pool, struct ext_pipe_action, 1);
268 
269 	if (args_list != NULL &&
270 	    sieve_stringlist_read_all(args_list, pool, &act->args) < 0) {
271 		sieve_runtime_trace_error(renv, "failed to read args operand");
272 		return args_list->exec_status;
273 	}
274 
275 	act->program_name = p_strdup(pool, str_c(pname));
276 	act->try = try;
277 
278 	if (sieve_result_add_action(renv, this_ext, "pipe", &act_pipe, slist,
279 				    (void *)act, 0, TRUE) < 0)
280 		return SIEVE_EXEC_FAILURE;
281 	return SIEVE_EXEC_OK;
282 }
283 
284 /*
285  * Action
286  */
287 
288 /* Runtime verification */
289 
290 static int
act_pipe_check_duplicate(const struct sieve_runtime_env * renv ATTR_UNUSED,const struct sieve_action * act,const struct sieve_action * act_other)291 act_pipe_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED,
292 			 const struct sieve_action *act,
293 			 const struct sieve_action *act_other)
294 {
295 	struct ext_pipe_action *new_act, *old_act;
296 
297 	if (act->context == NULL || act_other->context == NULL)
298 		return 0;
299 
300 	new_act = (struct ext_pipe_action *) act->context;
301 	old_act = (struct ext_pipe_action *) act_other->context;
302 
303 	if (strcmp(new_act->program_name, old_act->program_name) == 0) {
304 		sieve_runtime_error(renv, act->location,
305 				    "duplicate pipe \"%s\" action not allowed "
306 				    "(previously triggered one was here: %s)",
307 				    new_act->program_name, act_other->location);
308 		return -1;
309 	}
310 
311 	return 0;
312 }
313 
314 /* Result printing */
315 
316 static void
act_pipe_print(const struct sieve_action * action,const struct sieve_result_print_env * rpenv,bool * keep ATTR_UNUSED)317 act_pipe_print(const struct sieve_action *action,
318 	       const struct sieve_result_print_env *rpenv,
319 	       bool *keep ATTR_UNUSED)
320 {
321 	const struct ext_pipe_action *act =
322 		(const struct ext_pipe_action *)action->context;
323 
324 	sieve_result_action_printf(
325 		rpenv, "pipe message to external program '%s':",
326 		act->program_name);
327 
328 	/* Print main method parameters */
329 
330 	sieve_result_printf(
331 		rpenv, "    => try           : %s\n",
332 		(act->try ? "yes" : "no"));
333 
334 	/* FIXME: print args */
335 
336 	/* Finish output with an empty line */
337 	sieve_result_printf(rpenv, "\n");
338 }
339 
340 /* Result execution */
341 
342 struct act_pipe_transaction {
343 	struct sieve_extprogram *sprog;
344 };
345 
346 static int
act_pipe_start(const struct sieve_action_exec_env * aenv,void ** tr_context)347 act_pipe_start(const struct sieve_action_exec_env *aenv, void **tr_context)
348 {
349 	struct act_pipe_transaction *trans;
350 	pool_t pool = sieve_result_pool(aenv->result);
351 
352 	/* Create transaction context */
353 	trans = p_new(pool, struct act_pipe_transaction, 1);
354 	*tr_context = (void *)trans;
355 
356 	return SIEVE_EXEC_OK;
357 }
358 
359 static int
act_pipe_execute(const struct sieve_action_exec_env * aenv,void * tr_context,bool * keep)360 act_pipe_execute(const struct sieve_action_exec_env *aenv,
361 		 void *tr_context, bool *keep)
362 {
363 	const struct sieve_action *action = aenv->action;
364 	const struct sieve_execute_env *eenv = aenv->exec_env;
365 	const struct ext_pipe_action *act =
366 		(const struct ext_pipe_action *)action->context;
367 	struct act_pipe_transaction *trans = tr_context;
368 	struct mail *mail = (action->mail != NULL ?
369 		     action->mail :
370 		     sieve_message_get_mail(aenv->msgctx));
371 	enum sieve_error error = SIEVE_ERROR_NONE;
372 
373 	trans->sprog = sieve_extprogram_create(action->ext, eenv->scriptenv,
374 					       eenv->msgdata, "pipe",
375 					       act->program_name, act->args,
376 					       &error);
377 	if (trans->sprog != NULL) {
378 		if (sieve_extprogram_set_input_mail(trans->sprog, mail) < 0) {
379 			sieve_extprogram_destroy(&trans->sprog);
380 			return sieve_result_mail_error(
381 				aenv, mail, "failed to read input message");
382 		}
383 	}
384 
385 	*keep = FALSE;
386 	return SIEVE_EXEC_OK;
387 }
388 
389 static int
act_pipe_commit(const struct sieve_action_exec_env * aenv,void * tr_context ATTR_UNUSED)390 act_pipe_commit(const struct sieve_action_exec_env *aenv,
391 		void *tr_context ATTR_UNUSED)
392 {
393 	const struct sieve_action *action = aenv->action;
394 	const struct sieve_execute_env *eenv = aenv->exec_env;
395 	const struct ext_pipe_action *act =
396 		(const struct ext_pipe_action *)action->context;
397 	struct act_pipe_transaction *trans = tr_context;
398 	enum sieve_error error = SIEVE_ERROR_NONE;
399 	int ret;
400 
401 	if (trans->sprog != NULL) {
402 		ret = sieve_extprogram_run(trans->sprog);
403 		sieve_extprogram_destroy(&trans->sprog);
404 	} else {
405 		ret = -1;
406 	}
407 
408 	if (ret > 0) {
409 		struct event_passthrough *e =
410 			sieve_action_create_finish_event(aenv)->
411 			add_str("pipe_program",
412 				str_sanitize(act->program_name, 256));
413 
414 		sieve_result_event_log(aenv, e->event(),
415 				       "piped message to program `%s'",
416 				       str_sanitize(act->program_name, 128));
417 
418 		/* Indicate that message was successfully 'forwarded' */
419 		eenv->exec_status->message_forwarded = TRUE;
420 	} else {
421 		if (ret < 0) {
422 			if (error == SIEVE_ERROR_NOT_FOUND) {
423 				sieve_result_error(
424 					aenv,
425 					"failed to pipe message to program: "
426 					"program `%s' not found",
427 					str_sanitize(act->program_name, 80));
428 			} else {
429 				sieve_extprogram_exec_error(
430 					aenv->ehandler, NULL,
431 					"failed to pipe message to program `%s'",
432 					str_sanitize(act->program_name, 80));
433 			}
434 		} else {
435 			sieve_extprogram_exec_error(
436 				aenv->ehandler, NULL,
437 				"failed to execute to program `%s'",
438 				str_sanitize(act->program_name, 80));
439 		}
440 
441 		if (act->try)
442 			return SIEVE_EXEC_OK;
443 		return SIEVE_EXEC_FAILURE;
444 	}
445 
446 	return SIEVE_EXEC_OK;
447 }
448 
449 static void
act_pipe_rollback(const struct sieve_action_exec_env * aenv ATTR_UNUSED,void * tr_context,bool success ATTR_UNUSED)450 act_pipe_rollback(const struct sieve_action_exec_env *aenv ATTR_UNUSED,
451 		  void *tr_context, bool success ATTR_UNUSED)
452 {
453 	struct act_pipe_transaction *trans = tr_context;
454 
455 	if (trans->sprog != NULL)
456 		sieve_extprogram_destroy(&trans->sprog);
457 }
458