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