1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "str.h"
6 #include "array.h"
7 #include "home-expand.h"
8 #include "var-expand.h"
9 #include "eacces-error.h"
10 #include "smtp-address.h"
11 #include "smtp-submit.h"
12 #include "mail-storage.h"
13 #include "mail-deliver.h"
14 #include "mail-user.h"
15 #include "mail-duplicate.h"
16 #include "smtp-submit.h"
17 #include "mail-send.h"
18 #include "iostream-ssl.h"
19 #include "lda-settings.h"
20
21 #include "sieve.h"
22 #include "sieve-script.h"
23 #include "sieve-storage.h"
24
25 #include "lda-sieve-plugin.h"
26
27 #include <sys/stat.h>
28 #include <dirent.h>
29
30 /*
31 * Configuration
32 */
33
34 #define LDA_SIEVE_DEFAULT_LOCATION "~/.dovecot.sieve"
35
36 #define LDA_SIEVE_MAX_USER_ERRORS 30
37
38 /*
39 * Global variables
40 */
41
42 static deliver_mail_func_t *next_deliver_mail;
43
44 /*
45 * Settings handling
46 */
47
48 static const char *
lda_sieve_get_setting(void * context,const char * identifier)49 lda_sieve_get_setting(void *context, const char *identifier)
50 {
51 struct mail_deliver_context *mdctx =
52 (struct mail_deliver_context *)context;
53 const char *value = NULL;
54
55 if (mdctx == NULL)
56 return NULL;
57
58 if (mdctx->rcpt_user == NULL ||
59 (value = mail_user_plugin_getenv(
60 mdctx->rcpt_user, identifier)) == NULL) {
61 if (strcmp(identifier, "recipient_delimiter") == 0)
62 value = mdctx->set->recipient_delimiter;
63 }
64
65 return value;
66 }
67
68 static const struct sieve_callbacks lda_sieve_callbacks = {
69 NULL,
70 lda_sieve_get_setting
71 };
72
73 /*
74 * Mail transmission
75 */
76
77 static void *
lda_sieve_smtp_start(const struct sieve_script_env * senv,const struct smtp_address * mail_from)78 lda_sieve_smtp_start(const struct sieve_script_env *senv,
79 const struct smtp_address *mail_from)
80 {
81 struct mail_deliver_context *dctx =
82 (struct mail_deliver_context *)senv->script_context;
83 struct mail_user *user = dctx->rcpt_user;
84 struct ssl_iostream_settings ssl_set;
85 struct smtp_submit_input submit_input;
86
87 i_zero(&ssl_set);
88 mail_user_init_ssl_client_settings(user, &ssl_set);
89
90 i_zero(&submit_input);
91 submit_input.ssl = &ssl_set;
92
93 return (void *)smtp_submit_init_simple(&submit_input, dctx->smtp_set,
94 mail_from);
95 }
96
97 static void
lda_sieve_smtp_add_rcpt(const struct sieve_script_env * senv ATTR_UNUSED,void * handle,const struct smtp_address * rcpt_to)98 lda_sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED,
99 void *handle, const struct smtp_address *rcpt_to)
100 {
101 struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
102
103 smtp_submit_add_rcpt(smtp_submit, rcpt_to);
104 }
105
106 static struct ostream *
lda_sieve_smtp_send(const struct sieve_script_env * senv ATTR_UNUSED,void * handle)107 lda_sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED,
108 void *handle)
109 {
110 struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
111
112 return smtp_submit_send(smtp_submit);
113 }
114
115 static void
lda_sieve_smtp_abort(const struct sieve_script_env * senv ATTR_UNUSED,void * handle)116 lda_sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED,
117 void *handle)
118 {
119 struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
120
121 smtp_submit_deinit(&smtp_submit);
122 }
123
124 static int
lda_sieve_smtp_finish(const struct sieve_script_env * senv ATTR_UNUSED,void * handle,const char ** error_r)125 lda_sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED,
126 void *handle, const char **error_r)
127 {
128 struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
129 int ret;
130
131 ret = smtp_submit_run(smtp_submit, error_r);
132 smtp_submit_deinit(&smtp_submit);
133 return ret;
134 }
135
136 static int
lda_sieve_reject_mail(const struct sieve_script_env * senv,const struct smtp_address * recipient,const char * reason)137 lda_sieve_reject_mail(const struct sieve_script_env *senv,
138 const struct smtp_address *recipient,
139 const char *reason)
140 {
141 struct mail_deliver_context *dctx =
142 (struct mail_deliver_context *)senv->script_context;
143
144 return mail_send_rejection(dctx, recipient, reason);
145 }
146
147 /*
148 * Duplicate checking
149 */
150
151 static void *
lda_sieve_duplicate_transaction_begin(const struct sieve_script_env * senv)152 lda_sieve_duplicate_transaction_begin(const struct sieve_script_env *senv)
153 {
154 struct mail_deliver_context *dctx =
155 (struct mail_deliver_context *)senv->script_context;
156
157 return mail_duplicate_transaction_begin(dctx->dup_db);
158 }
159
lda_sieve_duplicate_transaction_commit(void ** _dup_trans)160 static void lda_sieve_duplicate_transaction_commit(void **_dup_trans)
161 {
162 struct mail_duplicate_transaction *dup_trans = *_dup_trans;
163
164 *_dup_trans = NULL;
165 mail_duplicate_transaction_commit(&dup_trans);
166 }
167
lda_sieve_duplicate_transaction_rollback(void ** _dup_trans)168 static void lda_sieve_duplicate_transaction_rollback(void **_dup_trans)
169 {
170 struct mail_duplicate_transaction *dup_trans = *_dup_trans;
171
172 *_dup_trans = NULL;
173 mail_duplicate_transaction_rollback(&dup_trans);
174 }
175
176 static enum sieve_duplicate_check_result
lda_sieve_duplicate_check(void * _dup_trans,const struct sieve_script_env * senv,const void * id,size_t id_size)177 lda_sieve_duplicate_check(void *_dup_trans, const struct sieve_script_env *senv,
178 const void *id, size_t id_size)
179 {
180 struct mail_duplicate_transaction *dup_trans = _dup_trans;
181
182 switch (mail_duplicate_check(dup_trans, id, id_size,
183 senv->user->username)) {
184 case MAIL_DUPLICATE_CHECK_RESULT_EXISTS:
185 return SIEVE_DUPLICATE_CHECK_RESULT_EXISTS;
186 case MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND:
187 return SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND;
188 case MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK:
189 case MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT:
190 return SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE;
191 case MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR:
192 case MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS:
193 break;
194 }
195 return SIEVE_DUPLICATE_CHECK_RESULT_FAILURE;
196 }
197
198 static void
lda_sieve_duplicate_mark(void * _dup_trans,const struct sieve_script_env * senv,const void * id,size_t id_size,time_t time)199 lda_sieve_duplicate_mark(void *_dup_trans, const struct sieve_script_env *senv,
200 const void *id, size_t id_size, time_t time)
201 {
202 struct mail_duplicate_transaction *dup_trans = _dup_trans;
203
204 mail_duplicate_mark(dup_trans, id, id_size, senv->user->username, time);
205 }
206
207 /*
208 * Result logging
209 */
210
211 static const char *
lda_sieve_result_amend_log_message(const struct sieve_script_env * senv,enum log_type log_type ATTR_UNUSED,const char * message)212 lda_sieve_result_amend_log_message(const struct sieve_script_env *senv,
213 enum log_type log_type ATTR_UNUSED,
214 const char *message)
215 {
216 struct mail_deliver_context *mdctx = senv->script_context;
217 const struct var_expand_table *table;
218 string_t *str;
219 const char *error;
220
221 table = mail_deliver_ctx_get_log_var_expand_table(mdctx, message);
222
223 str = t_str_new(256);
224 if (var_expand(str, mdctx->set->deliver_log_format,
225 table, &error) <= 0) {
226 e_error(mdctx->event,
227 "Failed to expand deliver_log_format=%s: %s",
228 mdctx->set->deliver_log_format, error);
229 }
230 return str_c(str);
231 }
232
233 /*
234 * Plugin implementation
235 */
236
237 struct lda_sieve_run_context {
238 struct sieve_instance *svinst;
239
240 struct mail_deliver_context *mdctx;
241 const char *home_dir;
242
243 struct sieve_script **scripts;
244 unsigned int script_count;
245
246 struct sieve_script *user_script;
247 struct sieve_script *main_script;
248 struct sieve_script *discard_script;
249
250 const struct sieve_message_data *msgdata;
251 const struct sieve_script_env *scriptenv;
252
253 struct sieve_error_handler *user_ehandler;
254 struct sieve_error_handler *master_ehandler;
255 struct sieve_error_handler *action_ehandler;
256 const char *userlog;
257 };
258
259 static int
lda_sieve_get_personal_storage(struct sieve_instance * svinst,struct mail_user * user,struct sieve_storage ** storage_r,enum sieve_error * error_r)260 lda_sieve_get_personal_storage(struct sieve_instance *svinst,
261 struct mail_user *user,
262 struct sieve_storage **storage_r,
263 enum sieve_error *error_r)
264 {
265 *storage_r = sieve_storage_create_main(svinst, user, 0, error_r);
266 if (*storage_r == NULL) {
267 switch (*error_r) {
268 case SIEVE_ERROR_NOT_POSSIBLE:
269 case SIEVE_ERROR_NOT_FOUND:
270 break;
271 case SIEVE_ERROR_TEMP_FAILURE:
272 e_error(sieve_get_event(svinst),
273 "Failed to access user's personal storage "
274 "(temporary failure)");
275 return -1;
276 default:
277 e_error(sieve_get_event(svinst),
278 "Failed to access user's personal storage");
279 break;
280 }
281 return 0;
282 }
283 return 1;
284 }
285
286 static int
lda_sieve_multiscript_get_scripts(struct sieve_instance * svinst,const char * label,const char * location,ARRAY_TYPE (sieve_script)* scripts,enum sieve_error * error_r)287 lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst,
288 const char *label, const char *location,
289 ARRAY_TYPE(sieve_script) *scripts,
290 enum sieve_error *error_r)
291 {
292 struct sieve_script_sequence *seq;
293 struct sieve_script *script;
294 bool finished = FALSE;
295 int ret = 1;
296
297 seq = sieve_script_sequence_create(svinst, location, error_r);
298 if (seq == NULL)
299 return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1);
300
301 while (ret > 0 && !finished) {
302 script = sieve_script_sequence_next(seq, error_r);
303 if (script == NULL) {
304 switch (*error_r) {
305 case SIEVE_ERROR_NONE:
306 finished = TRUE;
307 break;
308 case SIEVE_ERROR_TEMP_FAILURE:
309 e_error(sieve_get_event(svinst),
310 "Failed to access %s script from `%s' "
311 "(temporary failure)",
312 label, location);
313 ret = -1;
314 default:
315 break;
316 }
317 continue;
318 }
319
320 array_append(scripts, &script, 1);
321 }
322
323 sieve_script_sequence_free(&seq);
324 return ret;
325 }
326
327 static void
lda_sieve_binary_save(struct lda_sieve_run_context * srctx,struct sieve_binary * sbin,struct sieve_script * script)328 lda_sieve_binary_save(struct lda_sieve_run_context *srctx,
329 struct sieve_binary *sbin, struct sieve_script *script)
330 {
331 enum sieve_error error;
332
333 /* Save binary when compiled */
334 if (sieve_save(sbin, FALSE, &error) < 0 &&
335 error == SIEVE_ERROR_NO_PERMISSION &&
336 script != srctx->user_script) {
337 /* Cannot save binary for global script */
338 e_error(sieve_get_event(srctx->svinst),
339 "The LDA Sieve plugin does not have permission "
340 "to save global Sieve script binaries; "
341 "global Sieve scripts like `%s' need to be "
342 "pre-compiled using the sievec tool",
343 sieve_script_location(script));
344 }
345 }
346
347 static struct
lda_sieve_open(struct lda_sieve_run_context * srctx,struct sieve_script * script,enum sieve_compile_flags cpflags,bool recompile,enum sieve_error * error_r)348 sieve_binary *lda_sieve_open(struct lda_sieve_run_context *srctx,
349 struct sieve_script *script,
350 enum sieve_compile_flags cpflags, bool recompile,
351 enum sieve_error *error_r)
352 {
353 struct sieve_instance *svinst = srctx->svinst;
354 struct sieve_error_handler *ehandler;
355 struct sieve_binary *sbin;
356 const char *compile_name = "compile";
357
358 if (recompile) {
359 /* Warn */
360 e_warning(sieve_get_event(svinst),
361 "Encountered corrupt binary: re-compiling script %s",
362 sieve_script_location(script));
363 compile_name = "re-compile";
364 } else {
365 e_debug(sieve_get_event(svinst),
366 "Loading script %s", sieve_script_location(script));
367 }
368
369 if (script == srctx->user_script)
370 ehandler = srctx->user_ehandler;
371 else
372 ehandler = srctx->master_ehandler;
373
374 sieve_error_handler_reset(ehandler);
375
376 if (recompile)
377 sbin = sieve_compile_script(script, ehandler, cpflags, error_r);
378 else
379 sbin = sieve_open_script(script, ehandler, cpflags, error_r);
380
381 /* Load or compile the sieve script */
382 if (sbin == NULL) {
383 switch (*error_r) {
384 /* Script not found */
385 case SIEVE_ERROR_NOT_FOUND:
386 e_debug(sieve_get_event(svinst),
387 "Script `%s' is missing for %s",
388 sieve_script_location(script),
389 compile_name);
390 break;
391 /* Temporary failure */
392 case SIEVE_ERROR_TEMP_FAILURE:
393 e_error(sieve_get_event(svinst),
394 "Failed to open script `%s' for %s "
395 "(temporary failure)",
396 sieve_script_location(script), compile_name);
397 break;
398 /* Compile failed */
399 case SIEVE_ERROR_NOT_VALID:
400 if (script == srctx->user_script &&
401 srctx->userlog != NULL ) {
402 e_info(sieve_get_event(svinst),
403 "Failed to %s script `%s' "
404 "(view user logfile `%s' for more information)",
405 compile_name,
406 sieve_script_location(script),
407 srctx->userlog);
408 break;
409 }
410 e_error(sieve_get_event(svinst),
411 "Failed to %s script `%s'",
412 compile_name, sieve_script_location(script));
413 break;
414 /* Cumulative resource limit exceeded */
415 case SIEVE_ERROR_RESOURCE_LIMIT:
416 e_error(sieve_get_event(svinst),
417 "Failed to open script `%s' for %s "
418 "(cumulative resource limit exceeded)",
419 sieve_script_location(script), compile_name);
420 break;
421 /* Something else */
422 default:
423 e_error(sieve_get_event(svinst),
424 "Failed to open script `%s' for %s",
425 sieve_script_location(script), compile_name);
426 break;
427 }
428
429 return NULL;
430 }
431
432 if (!recompile)
433 lda_sieve_binary_save(srctx, sbin, script);
434 return sbin;
435 }
436
437 static int
lda_sieve_handle_exec_status(struct lda_sieve_run_context * srctx,struct sieve_script * script,int status)438 lda_sieve_handle_exec_status(struct lda_sieve_run_context *srctx,
439 struct sieve_script *script, int status)
440 {
441 struct sieve_instance *svinst = srctx->svinst;
442 struct mail_deliver_context *mdctx = srctx->mdctx;
443 struct sieve_exec_status *estatus = srctx->scriptenv->exec_status;
444 const char *userlog_notice = "";
445 enum log_type log_level, user_log_level;
446 enum mail_error mail_error = MAIL_ERROR_NONE;
447 int ret;
448
449 log_level = user_log_level = LOG_TYPE_ERROR;
450
451 if (estatus != NULL && estatus->last_storage != NULL &&
452 estatus->store_failed) {
453 mail_storage_get_last_error(estatus->last_storage, &mail_error);
454
455 /* Don't bother administrator too much with benign errors */
456 if (mail_error == MAIL_ERROR_NOQUOTA) {
457 log_level = LOG_TYPE_INFO;
458 user_log_level = LOG_TYPE_INFO;
459 }
460 }
461
462 if (script == srctx->user_script && srctx->userlog != NULL) {
463 userlog_notice = t_strdup_printf(
464 " (user logfile %s may reveal additional details)",
465 srctx->userlog);
466 user_log_level = LOG_TYPE_INFO;
467 }
468
469 switch (status) {
470 case SIEVE_EXEC_FAILURE:
471 e_log(sieve_get_event(svinst), user_log_level,
472 "Execution of script %s failed, "
473 "but implicit keep was successful%s",
474 sieve_script_location(script), userlog_notice);
475 ret = 1;
476 break;
477 case SIEVE_EXEC_TEMP_FAILURE:
478 e_log(sieve_get_event(svinst), log_level,
479 "Execution of script %s was aborted due to temporary failure%s",
480 sieve_script_location(script), userlog_notice);
481 if (mail_error != MAIL_ERROR_TEMP &&
482 mdctx->tempfail_error == NULL) {
483 mdctx->tempfail_error =
484 "Execution of Sieve filters was aborted due to temporary failure";
485 }
486 ret = -1;
487 break;
488 case SIEVE_EXEC_BIN_CORRUPT:
489 e_error(sieve_get_event(svinst),
490 "!!BUG!!: Binary compiled from %s is still corrupt; "
491 "bailing out and reverting to default delivery",
492 sieve_script_location(script));
493 ret = -1;
494 break;
495 case SIEVE_EXEC_RESOURCE_LIMIT:
496 e_error(sieve_get_event(svinst),
497 "Execution of script %s was aborted "
498 "due to excessive resource usage",
499 sieve_script_location(script));
500 ret = -1;
501 break;
502 case SIEVE_EXEC_KEEP_FAILED:
503 e_log(sieve_get_event(svinst), log_level,
504 "Execution of script %s failed with unsuccessful implicit keep%s",
505 sieve_script_location(script), userlog_notice);
506 ret = -1;
507 break;
508 default:
509 ret = status > 0 ? 1 : -1;
510 break;
511 }
512
513 return ret;
514 }
515
516 static int
lda_sieve_execute_script(struct lda_sieve_run_context * srctx,struct sieve_multiscript * mscript,struct sieve_script * script,unsigned int index,bool discard_script,enum sieve_error * error_r)517 lda_sieve_execute_script(struct lda_sieve_run_context *srctx,
518 struct sieve_multiscript *mscript,
519 struct sieve_script *script,
520 unsigned int index, bool discard_script,
521 enum sieve_error *error_r)
522 {
523 struct sieve_instance *svinst = srctx->svinst;
524 struct sieve_error_handler *exec_ehandler;
525 struct sieve_binary *sbin = NULL;
526 enum sieve_compile_flags cpflags = 0;
527 enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT;
528 struct sieve_resource_usage *rusage =
529 &srctx->scriptenv->exec_status->resource_usage;
530 bool user_script;
531 int mstatus, ret;
532
533 *error_r = SIEVE_ERROR_NONE;
534
535 user_script = (script == srctx->user_script);
536
537 sieve_resource_usage_init(rusage);
538 if (user_script) {
539 cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL;
540 exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
541 exec_ehandler = srctx->user_ehandler;
542 } else {
543 exec_ehandler = srctx->master_ehandler;
544 }
545
546 /* Open */
547
548 if (!discard_script) {
549 e_debug(sieve_get_event(svinst),
550 "Opening script %d of %d from `%s'",
551 index, srctx->script_count,
552 sieve_script_location(script));
553 } else {
554 e_debug(sieve_get_event(svinst),
555 "Opening discard script from `%s'",
556 sieve_script_location(script));
557 }
558
559 sbin = lda_sieve_open(srctx, script, cpflags, FALSE, error_r);
560 if (sbin == NULL)
561 return 0;
562
563 /* Execute */
564
565 e_debug(sieve_get_event(svinst),
566 "Executing script from `%s'",
567 sieve_get_source(sbin));
568
569 if (!discard_script) {
570 ret = (sieve_multiscript_run(mscript, sbin, exec_ehandler,
571 exec_ehandler, exflags) ? 1 : 0);
572 } else {
573 sieve_multiscript_run_discard(mscript, sbin, exec_ehandler,
574 exec_ehandler, exflags);
575 ret = 0;
576 }
577
578 mstatus = sieve_multiscript_status(mscript);
579 if (ret == 0 && mstatus == SIEVE_EXEC_BIN_CORRUPT &&
580 sieve_is_loaded(sbin)) {
581 /* Close corrupt script */
582
583 sieve_close(&sbin);
584
585 /* Recompile */
586
587 sbin = lda_sieve_open(srctx, script, cpflags, TRUE,
588 error_r);
589 if (sbin == NULL)
590 return 0;
591
592 /* Execute again */
593
594 if (!discard_script) {
595 ret = (sieve_multiscript_run(
596 mscript, sbin, exec_ehandler,
597 exec_ehandler, exflags) ? 1 : 0);
598 } else {
599 sieve_multiscript_run_discard(
600 mscript, sbin, exec_ehandler,
601 exec_ehandler, exflags);
602 }
603
604 /* Save new version */
605
606 mstatus = sieve_multiscript_status(mscript);
607 if (mstatus != SIEVE_EXEC_BIN_CORRUPT)
608 lda_sieve_binary_save(srctx, sbin, script);
609 }
610 if (ret == 0 && mstatus == SIEVE_EXEC_RESOURCE_LIMIT)
611 ret = -1;
612
613 if (user_script)
614 (void)sieve_record_resource_usage(sbin, rusage);
615
616 sieve_close(&sbin);
617
618 return ret;
619 }
620
lda_sieve_execute_scripts(struct lda_sieve_run_context * srctx)621 static int lda_sieve_execute_scripts(struct lda_sieve_run_context *srctx)
622 {
623 struct sieve_instance *svinst = srctx->svinst;
624 struct sieve_multiscript *mscript;
625 struct sieve_error_handler *exec_ehandler;
626 struct sieve_script *script, *last_script = NULL;
627 enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT;
628 bool discard_script;
629 enum sieve_error error;
630 unsigned int i;
631 int ret;
632
633 i_assert(srctx->script_count > 0);
634
635 /* Start execution */
636
637 mscript = sieve_multiscript_start_execute(svinst, srctx->msgdata,
638 srctx->scriptenv);
639
640 /* Execute scripts */
641
642 i = 0;
643 discard_script = FALSE;
644 error = SIEVE_ERROR_NONE;
645 for (;;) {
646 if (!discard_script) {
647 /* normal script sequence */
648 i_assert(i < srctx->script_count);
649 script = srctx->scripts[i];
650 i++;
651 } else {
652 /* discard script */
653 script = srctx->discard_script;
654 }
655
656 i_assert(script != NULL);
657 last_script = script;
658
659 ret = lda_sieve_execute_script(srctx, mscript, script, i,
660 discard_script, &error);
661 if (ret < 0)
662 break;
663 if (error == SIEVE_ERROR_NOT_FOUND) {
664 /* skip scripts which finally turn out not to exist */
665 ret = 1;
666 }
667
668 if (discard_script) {
669 /* Executed discard script, which is always final */
670 break;
671 } else if (ret > 0) {
672 /* The "keep" action is applied; execute next script */
673 i_assert(i <= srctx->script_count);
674 if (i == srctx->script_count) {
675 /* End of normal script sequence */
676 break;
677 }
678 } else if (error != SIEVE_ERROR_NONE) {
679 break;
680 } else if (sieve_multiscript_will_discard(mscript) &&
681 srctx->discard_script != NULL) {
682 /* Mail is set to be discarded, but we have a discard script. */
683 discard_script = TRUE;
684 } else {
685 break;
686 }
687 }
688
689 /* Finish execution */
690 exec_ehandler = (srctx->user_ehandler != NULL ?
691 srctx->user_ehandler : srctx->master_ehandler);
692 ret = sieve_multiscript_finish(&mscript, exec_ehandler, exflags,
693 (error == SIEVE_ERROR_TEMP_FAILURE ?
694 SIEVE_EXEC_TEMP_FAILURE :
695 SIEVE_EXEC_OK));
696
697 /* Don't log additional messages about compile failure */
698 if (error != SIEVE_ERROR_NONE && ret == SIEVE_EXEC_FAILURE) {
699 e_info(sieve_get_event(svinst),
700 "Aborted script execution sequence with successful implicit keep");
701 return 1;
702 }
703
704 return lda_sieve_handle_exec_status(srctx, last_script, ret);
705 }
706
lda_sieve_find_scripts(struct lda_sieve_run_context * srctx)707 static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
708 {
709 struct mail_deliver_context *mdctx = srctx->mdctx;
710 struct sieve_instance *svinst = srctx->svinst;
711 struct sieve_storage *main_storage;
712 const char *sieve_before, *sieve_after, *sieve_discard;
713 const char *setting_name;
714 enum sieve_error error;
715 ARRAY_TYPE(sieve_script) script_sequence;
716 struct sieve_script *const *scripts;
717 unsigned int after_index, count, i;
718 int ret = 1;
719
720 /* Find the personal script to execute */
721
722 ret = lda_sieve_get_personal_storage(svinst, mdctx->rcpt_user,
723 &main_storage, &error);
724 if (ret == 0 && error == SIEVE_ERROR_NOT_POSSIBLE)
725 return 0;
726 if (ret > 0) {
727 srctx->main_script =
728 sieve_storage_active_script_open(main_storage, &error);
729
730 if (srctx->main_script == NULL) {
731 switch (error) {
732 case SIEVE_ERROR_NOT_FOUND:
733 e_debug(sieve_get_event(svinst),
734 "User has no active script in storage `%s'",
735 sieve_storage_location(main_storage));
736 break;
737 case SIEVE_ERROR_TEMP_FAILURE:
738 e_error(sieve_get_event(svinst),
739 "Failed to access active Sieve script in user storage `%s' "
740 "(temporary failure)",
741 sieve_storage_location(main_storage));
742 ret = -1;
743 break;
744 default:
745 e_error(sieve_get_event(svinst),
746 "Failed to access active Sieve script in user storage `%s'",
747 sieve_storage_location(main_storage));
748 break;
749 }
750 } else if (!sieve_script_is_default(srctx->main_script)) {
751 srctx->user_script = srctx->main_script;
752 }
753 sieve_storage_unref(&main_storage);
754 }
755
756 if (ret >= 0 && srctx->main_script == NULL) {
757 e_debug(sieve_get_event(svinst),
758 "User has no personal script");
759 }
760
761 /* Compose script array */
762
763 t_array_init(&script_sequence, 16);
764
765 /* before */
766 if (ret >= 0) {
767 i = 2;
768 setting_name = "sieve_before";
769 sieve_before = mail_user_plugin_getenv(
770 mdctx->rcpt_user, setting_name);
771 while (ret >= 0 &&
772 sieve_before != NULL && *sieve_before != '\0') {
773 ret = lda_sieve_multiscript_get_scripts(
774 svinst, setting_name, sieve_before,
775 &script_sequence, &error);
776 if (ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE) {
777 ret = -1;
778 break;
779 } else if (ret == 0) {
780 e_debug(sieve_get_event(svinst),
781 "Location for %s not found: %s",
782 setting_name, sieve_before);
783 }
784 ret = 0;
785 setting_name = t_strdup_printf("sieve_before%u", i++);
786 sieve_before = mail_user_plugin_getenv(
787 mdctx->rcpt_user, setting_name);
788 }
789
790 if (ret >= 0) {
791 scripts = array_get(&script_sequence, &count);
792 for (i = 0; i < count; i ++) {
793 e_debug(sieve_get_event(svinst),
794 "Executed before user's personal Sieve script(%d): %s",
795 i+1, sieve_script_location(scripts[i]));
796 }
797 }
798 }
799
800 /* main */
801 if (srctx->main_script != NULL) {
802 array_append(&script_sequence, &srctx->main_script, 1);
803
804 if (ret >= 0) {
805 e_debug(sieve_get_event(svinst),
806 "Using the following location for user's Sieve script: %s",
807 sieve_script_location(srctx->main_script));
808 }
809 }
810
811 after_index = array_count(&script_sequence);
812
813 /* after */
814 if (ret >= 0) {
815 i = 2;
816 setting_name = "sieve_after";
817 sieve_after = mail_user_plugin_getenv(mdctx->rcpt_user, setting_name);
818 while (sieve_after != NULL && *sieve_after != '\0') {
819 ret = lda_sieve_multiscript_get_scripts(
820 svinst, setting_name, sieve_after,
821 &script_sequence, &error);
822 if (ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE) {
823 ret = -1;
824 break;
825 } else if (ret == 0) {
826 e_debug(sieve_get_event(svinst),
827 "Location for %s not found: %s",
828 setting_name, sieve_after);
829 }
830 ret = 0;
831 setting_name = t_strdup_printf("sieve_after%u", i++);
832 sieve_after = mail_user_plugin_getenv(
833 mdctx->rcpt_user, setting_name);
834 }
835
836 if (ret >= 0) {
837 scripts = array_get(&script_sequence, &count);
838 for ( i = after_index; i < count; i ++ ) {
839 e_debug(sieve_get_event(svinst),
840 "executed after user's Sieve script(%d): %s",
841 i+1, sieve_script_location(scripts[i]));
842 }
843 }
844 }
845
846 /* discard */
847 sieve_discard = mail_user_plugin_getenv(
848 mdctx->rcpt_user, "sieve_discard");
849 if (sieve_discard != NULL && *sieve_discard != '\0') {
850 srctx->discard_script = sieve_script_create_open(
851 svinst, sieve_discard, NULL, &error);
852 if (srctx->discard_script == NULL) {
853 switch (error) {
854 case SIEVE_ERROR_NOT_FOUND:
855 e_debug(sieve_get_event(svinst),
856 "Location for sieve_discard not found: %s",
857 sieve_discard);
858 break;
859 case SIEVE_ERROR_TEMP_FAILURE:
860 ret = -1;
861 break;
862 default:
863 break;
864 }
865 }
866 }
867
868 if (ret < 0) {
869 mdctx->tempfail_error =
870 "Temporarily unable to access necessary Sieve scripts";
871 }
872 srctx->scripts =
873 array_get_modifiable(&script_sequence, &srctx->script_count);
874 return ret;
875 }
876
877 static void
lda_sieve_free_scripts(struct lda_sieve_run_context * srctx)878 lda_sieve_free_scripts(struct lda_sieve_run_context *srctx)
879 {
880 unsigned int i;
881
882 for (i = 0; i < srctx->script_count; i++)
883 sieve_script_unref(&srctx->scripts[i]);
884 if (srctx->discard_script != NULL)
885 sieve_script_unref(&srctx->discard_script);
886 }
887
888 static void
lda_sieve_init_trace_log(struct lda_sieve_run_context * srctx,const struct smtp_address * mail_from,struct sieve_trace_config * trace_config_r,struct sieve_trace_log ** trace_log_r)889 lda_sieve_init_trace_log(struct lda_sieve_run_context *srctx,
890 const struct smtp_address *mail_from,
891 struct sieve_trace_config *trace_config_r,
892 struct sieve_trace_log **trace_log_r)
893 {
894 struct mail_deliver_context *mdctx = srctx->mdctx;
895 struct sieve_instance *svinst = srctx->svinst;
896 struct sieve_trace_log *trace_log = NULL;
897
898 if (sieve_trace_config_get(svinst, trace_config_r) < 0 ||
899 sieve_trace_log_open(svinst, &trace_log) < 0) {
900 i_zero(trace_config_r);
901 *trace_log_r = NULL;
902 return;
903 }
904
905 /* Write header for trace file */
906 sieve_trace_log_printf(trace_log,
907 "Sieve trace log for message delivery:\n"
908 "\n"
909 " Username: %s\n", mdctx->rcpt_user->username);
910 if (mdctx->rcpt_user->session_id != NULL) {
911 sieve_trace_log_printf(trace_log,
912 " Session ID: %s\n",
913 mdctx->rcpt_user->session_id);
914 }
915 sieve_trace_log_printf(trace_log,
916 " Sender: %s\n"
917 " Final recipient: %s\n"
918 " Default mailbox: %s\n\n",
919 smtp_address_encode_path(mail_from),
920 smtp_address_encode_path(mdctx->rcpt_to),
921 (mdctx->rcpt_default_mailbox != NULL ?
922 mdctx->rcpt_default_mailbox : "INBOX"));
923
924 *trace_log_r = trace_log;
925 }
926
927 static int
lda_sieve_execute(struct lda_sieve_run_context * srctx,struct mail_storage ** storage_r)928 lda_sieve_execute(struct lda_sieve_run_context *srctx,
929 struct mail_storage **storage_r)
930 {
931 struct mail_deliver_context *mdctx = srctx->mdctx;
932 struct sieve_instance *svinst = srctx->svinst;
933 struct sieve_message_data msgdata;
934 struct sieve_script_env scriptenv;
935 struct sieve_exec_status estatus;
936 struct sieve_trace_config trace_config;
937 const struct smtp_address *mail_from;
938 struct sieve_trace_log *trace_log;
939 const char *error;
940 int ret;
941
942 /* Check whether there are any scripts to execute at all */
943
944 if (srctx->script_count == 0) {
945 e_debug(sieve_get_event(svinst),
946 "No scripts to execute: "
947 "reverting to default delivery.");
948
949 /* No error, but no delivery by this plugin either. A return
950 value of <= 0 for a deliver plugin is is considered a
951 failure. In deliver itself, saved_mail and tried_default_save
952 remain unset, meaning that deliver will then attempt the
953 default delivery. We return 0 to signify the lack of a real
954 error.
955 */
956 return 0;
957 }
958
959 /* Initialize user error handler */
960
961 if (srctx->user_script != NULL) {
962 const char *log_path =
963 sieve_user_get_log_path(svinst, srctx->user_script);
964
965 if (log_path != NULL) {
966 srctx->userlog = log_path;
967 srctx->user_ehandler = sieve_logfile_ehandler_create(
968 svinst, srctx->userlog,
969 LDA_SIEVE_MAX_USER_ERRORS);
970 }
971 }
972
973 /* Determine return address */
974
975 mail_from = mail_deliver_get_return_address(mdctx);
976
977 /* Initialize trace logging */
978
979 lda_sieve_init_trace_log(srctx, mail_from, &trace_config, &trace_log);
980
981 /* Collect necessary message data */
982
983 i_zero(&msgdata);
984
985 msgdata.mail = mdctx->src_mail;
986 msgdata.auth_user = mdctx->rcpt_user->username;
987 msgdata.envelope.mail_from = mail_from;
988 msgdata.envelope.mail_params = &mdctx->mail_params;
989 msgdata.envelope.rcpt_to = mdctx->rcpt_to;
990 msgdata.envelope.rcpt_params = &mdctx->rcpt_params;
991 (void)mail_get_message_id(msgdata.mail, &msgdata.id);
992
993 srctx->msgdata = &msgdata;
994
995 /* Compose script execution environment */
996
997 if (sieve_script_env_init(&scriptenv, mdctx->rcpt_user, &error) < 0) {
998 e_error(sieve_get_event(svinst),
999 "Failed to initialize script execution: %s", error);
1000 if (trace_log != NULL)
1001 sieve_trace_log_free(&trace_log);
1002 return -1;
1003 }
1004
1005 scriptenv.default_mailbox = mdctx->rcpt_default_mailbox;
1006 scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate;
1007 scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe;
1008 scriptenv.smtp_start = lda_sieve_smtp_start;
1009 scriptenv.smtp_add_rcpt = lda_sieve_smtp_add_rcpt;
1010 scriptenv.smtp_send = lda_sieve_smtp_send;
1011 scriptenv.smtp_abort = lda_sieve_smtp_abort;
1012 scriptenv.smtp_finish = lda_sieve_smtp_finish;
1013 scriptenv.duplicate_transaction_begin =
1014 lda_sieve_duplicate_transaction_begin;
1015 scriptenv.duplicate_transaction_commit =
1016 lda_sieve_duplicate_transaction_commit;
1017 scriptenv.duplicate_transaction_rollback =
1018 lda_sieve_duplicate_transaction_rollback;
1019 scriptenv.duplicate_mark = lda_sieve_duplicate_mark;
1020 scriptenv.duplicate_check = lda_sieve_duplicate_check;
1021 scriptenv.reject_mail = lda_sieve_reject_mail;
1022 scriptenv.result_amend_log_message = lda_sieve_result_amend_log_message;
1023 scriptenv.script_context = (void *) mdctx;
1024 scriptenv.trace_log = trace_log;
1025 scriptenv.trace_config = trace_config;
1026
1027 i_zero(&estatus);
1028 scriptenv.exec_status = &estatus;
1029
1030 srctx->scriptenv = &scriptenv;
1031
1032 /* Execute script(s) */
1033
1034 ret = lda_sieve_execute_scripts(srctx);
1035
1036 /* Record status */
1037
1038 mdctx->tried_default_save = estatus.tried_default_save;
1039 *storage_r = estatus.last_storage;
1040
1041 if (trace_log != NULL)
1042 sieve_trace_log_free(&trace_log);
1043
1044 return ret;
1045 }
1046
1047 static int
lda_sieve_deliver_mail(struct mail_deliver_context * mdctx,struct mail_storage ** storage_r)1048 lda_sieve_deliver_mail(struct mail_deliver_context *mdctx,
1049 struct mail_storage **storage_r)
1050 {
1051 struct lda_sieve_run_context srctx;
1052 const struct mail_storage_settings *mail_set =
1053 mail_user_set_get_storage_set(mdctx->rcpt_user);
1054 bool debug = mdctx->rcpt_user->mail_debug;
1055 struct sieve_environment svenv;
1056 int ret = 0;
1057
1058 /* Initialize run context */
1059
1060 i_zero(&srctx);
1061 srctx.mdctx = mdctx;
1062 (void)mail_user_get_home(mdctx->rcpt_user, &srctx.home_dir);
1063
1064 /* Initialize Sieve engine */
1065
1066 memset((void*)&svenv, 0, sizeof(svenv));
1067 svenv.username = mdctx->rcpt_user->username;
1068 svenv.home_dir = srctx.home_dir;
1069 svenv.hostname = mail_set->hostname;
1070 svenv.base_dir = mdctx->rcpt_user->set->base_dir;
1071 svenv.temp_dir = mdctx->rcpt_user->set->mail_temp_dir;
1072 svenv.event_parent = mdctx->event;
1073 svenv.flags = SIEVE_FLAG_HOME_RELATIVE;
1074 svenv.location = SIEVE_ENV_LOCATION_MDA;
1075 svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING;
1076
1077 srctx.svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug);
1078
1079 /* Initialize master error handler */
1080
1081 srctx.master_ehandler = sieve_master_ehandler_create(srctx.svinst, 0);
1082
1083 sieve_error_handler_accept_infolog(srctx.master_ehandler, TRUE);
1084 sieve_error_handler_accept_debuglog(srctx.master_ehandler, debug);
1085
1086 *storage_r = NULL;
1087
1088 /* Find Sieve scripts and run them */
1089
1090 T_BEGIN {
1091 if (lda_sieve_find_scripts(&srctx) < 0)
1092 ret = -1;
1093 else if (srctx.scripts == NULL)
1094 ret = 0;
1095 else
1096 ret = lda_sieve_execute(&srctx, storage_r);
1097
1098 lda_sieve_free_scripts(&srctx);
1099 } T_END;
1100
1101 /* Clean up */
1102
1103 if (srctx.user_ehandler != NULL)
1104 sieve_error_handler_unref(&srctx.user_ehandler);
1105 sieve_error_handler_unref(&srctx.master_ehandler);
1106 sieve_deinit(&srctx.svinst);
1107
1108 return ret;
1109 }
1110
1111 /*
1112 * Plugin interface
1113 */
1114
1115 const char *sieve_plugin_version = DOVECOT_ABI_VERSION;
1116 const char sieve_plugin_binary_dependency[] = "lda lmtp";
1117
sieve_plugin_init(void)1118 void sieve_plugin_init(void)
1119 {
1120 /* Hook into the delivery process */
1121 next_deliver_mail = mail_deliver_hook_set(lda_sieve_deliver_mail);
1122 }
1123
sieve_plugin_deinit(void)1124 void sieve_plugin_deinit(void)
1125 {
1126 /* Remove hook */
1127 mail_deliver_hook_set(next_deliver_mail);
1128 }
1129