/src/pigeonhole/src/lib-sieve/sieve-actions.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file |
2 | | */ |
3 | | |
4 | | #include "lib.h" |
5 | | #include "str.h" |
6 | | #include "strfuncs.h" |
7 | | #include "ioloop.h" |
8 | | #include "hostpid.h" |
9 | | #include "str-sanitize.h" |
10 | | #include "unichar.h" |
11 | | #include "istream.h" |
12 | | #include "istream-header-filter.h" |
13 | | #include "ostream.h" |
14 | | #include "smtp-params.h" |
15 | | #include "mail-storage.h" |
16 | | #include "message-date.h" |
17 | | #include "message-size.h" |
18 | | |
19 | | #include "rfc2822.h" |
20 | | |
21 | | #include "sieve-code.h" |
22 | | #include "sieve-extensions.h" |
23 | | #include "sieve-binary.h" |
24 | | #include "sieve-interpreter.h" |
25 | | #include "sieve-dump.h" |
26 | | #include "sieve-result.h" |
27 | | #include "sieve-actions.h" |
28 | | #include "sieve-message.h" |
29 | | #include "sieve-smtp.h" |
30 | | |
31 | | /* |
32 | | * Action execution environment |
33 | | */ |
34 | | |
35 | | struct event_passthrough * |
36 | | sieve_action_create_finish_event(const struct sieve_action_exec_env *aenv) |
37 | 0 | { |
38 | 0 | struct event_passthrough *e = |
39 | 0 | event_create_passthrough(aenv->event)-> |
40 | 0 | set_name("sieve_action_finished"); |
41 | |
|
42 | 0 | return e; |
43 | 0 | } |
44 | | |
45 | | /* |
46 | | * Action instance |
47 | | */ |
48 | | |
49 | | bool sieve_action_is_executed(const struct sieve_action *act, |
50 | | struct sieve_result *result) |
51 | 0 | { |
52 | 0 | unsigned int cur_exec_seq = sieve_result_get_exec_seq(result); |
53 | |
|
54 | 0 | i_assert(act->exec_seq <= cur_exec_seq); |
55 | 0 | return (act->exec_seq < cur_exec_seq); |
56 | 0 | } |
57 | | |
58 | | /* |
59 | | * Side-effect operand |
60 | | */ |
61 | | |
62 | | const struct sieve_operand_class |
63 | | sieve_side_effect_operand_class = { "SIDE-EFFECT" }; |
64 | | |
65 | | bool sieve_opr_side_effect_dump(const struct sieve_dumptime_env *denv, |
66 | | sieve_size_t *address) |
67 | 0 | { |
68 | 0 | struct sieve_side_effect seffect; |
69 | 0 | const struct sieve_side_effect_def *sdef; |
70 | |
|
71 | 0 | if (!sieve_opr_object_dump(denv, &sieve_side_effect_operand_class, |
72 | 0 | address, &seffect.object)) |
73 | 0 | return FALSE; |
74 | | |
75 | 0 | sdef = seffect.def = |
76 | 0 | (const struct sieve_side_effect_def *)seffect.object.def; |
77 | |
|
78 | 0 | if (sdef->dump_context != NULL) { |
79 | 0 | sieve_code_descend(denv); |
80 | 0 | if (!sdef->dump_context(&seffect, denv, address)) |
81 | 0 | return FALSE; |
82 | 0 | sieve_code_ascend(denv); |
83 | 0 | } |
84 | 0 | return TRUE; |
85 | 0 | } |
86 | | |
87 | | int sieve_opr_side_effect_read(const struct sieve_runtime_env *renv, |
88 | | sieve_size_t *address, |
89 | | struct sieve_side_effect *seffect) |
90 | 0 | { |
91 | 0 | const struct sieve_side_effect_def *sdef; |
92 | 0 | int ret; |
93 | |
|
94 | 0 | seffect->context = NULL; |
95 | |
|
96 | 0 | if (!sieve_opr_object_read(renv, &sieve_side_effect_operand_class, |
97 | 0 | address, &seffect->object)) |
98 | 0 | return SIEVE_EXEC_BIN_CORRUPT; |
99 | | |
100 | 0 | sdef = seffect->def = |
101 | 0 | (const struct sieve_side_effect_def *)seffect->object.def; |
102 | |
|
103 | 0 | if (sdef->read_context != NULL && |
104 | 0 | (ret = sdef->read_context(seffect, renv, address, |
105 | 0 | &seffect->context)) <= 0) |
106 | 0 | return ret; |
107 | 0 | return SIEVE_EXEC_OK; |
108 | 0 | } |
109 | | |
110 | | /* |
111 | | * Optional operands |
112 | | */ |
113 | | |
114 | | int sieve_action_opr_optional_dump(const struct sieve_dumptime_env *denv, |
115 | | sieve_size_t *address, signed int *opt_code) |
116 | 0 | { |
117 | 0 | signed int _opt_code = 0; |
118 | 0 | bool final = FALSE, opok = TRUE; |
119 | |
|
120 | 0 | if (opt_code == NULL) { |
121 | 0 | opt_code = &_opt_code; |
122 | 0 | final = TRUE; |
123 | 0 | } |
124 | |
|
125 | 0 | while (opok) { |
126 | 0 | int opt; |
127 | |
|
128 | 0 | opt = sieve_opr_optional_dump(denv, address, opt_code); |
129 | 0 | if (opt <= 0) |
130 | 0 | return opt; |
131 | | |
132 | 0 | if (*opt_code == SIEVE_OPT_SIDE_EFFECT) |
133 | 0 | opok = sieve_opr_side_effect_dump(denv, address); |
134 | 0 | else |
135 | 0 | return (final ? -1 : 1); |
136 | 0 | } |
137 | | |
138 | 0 | return -1; |
139 | 0 | } |
140 | | |
141 | | int sieve_action_opr_optional_read(const struct sieve_runtime_env *renv, |
142 | | sieve_size_t *address, |
143 | | signed int *opt_code, int *exec_status, |
144 | | struct sieve_side_effects_list **list) |
145 | 0 | { |
146 | 0 | signed int _opt_code = 0; |
147 | 0 | bool final = FALSE; |
148 | 0 | int ret; |
149 | |
|
150 | 0 | if (opt_code == NULL) { |
151 | 0 | opt_code = &_opt_code; |
152 | 0 | final = TRUE; |
153 | 0 | } |
154 | |
|
155 | 0 | if (exec_status != NULL) |
156 | 0 | *exec_status = SIEVE_EXEC_OK; |
157 | |
|
158 | 0 | for (;;) { |
159 | 0 | int opt; |
160 | |
|
161 | 0 | opt = sieve_opr_optional_read(renv, address, opt_code); |
162 | 0 | if (opt <= 0) { |
163 | 0 | if (opt < 0 && exec_status != NULL) |
164 | 0 | *exec_status = SIEVE_EXEC_BIN_CORRUPT; |
165 | 0 | return opt; |
166 | 0 | } |
167 | | |
168 | 0 | if (*opt_code == SIEVE_OPT_SIDE_EFFECT) { |
169 | 0 | struct sieve_side_effect seffect; |
170 | |
|
171 | 0 | i_assert(list != NULL); |
172 | | |
173 | 0 | ret = sieve_opr_side_effect_read(renv, address, |
174 | 0 | &seffect); |
175 | 0 | if (ret <= 0) { |
176 | 0 | if (exec_status != NULL) |
177 | 0 | *exec_status = ret; |
178 | 0 | return -1; |
179 | 0 | } |
180 | | |
181 | 0 | if (*list == NULL) { |
182 | 0 | *list = sieve_side_effects_list_create( |
183 | 0 | renv->result); |
184 | 0 | } |
185 | |
|
186 | 0 | sieve_side_effects_list_add(*list, &seffect); |
187 | 0 | } else { |
188 | 0 | if (final) { |
189 | 0 | sieve_runtime_trace_error( |
190 | 0 | renv, "invalid optional operand"); |
191 | 0 | if (exec_status != NULL) |
192 | 0 | *exec_status = SIEVE_EXEC_BIN_CORRUPT; |
193 | 0 | return -1; |
194 | 0 | } |
195 | 0 | return 1; |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | 0 | i_unreached(); |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * Store action |
204 | | */ |
205 | | |
206 | | /* Forward declarations */ |
207 | | |
208 | | static bool |
209 | | act_store_equals(const struct sieve_script_env *senv, |
210 | | const struct sieve_action *act1, |
211 | | const struct sieve_action *act2); |
212 | | |
213 | | static int |
214 | | act_store_check_duplicate(const struct sieve_runtime_env *renv, |
215 | | const struct sieve_action *act, |
216 | | const struct sieve_action *act_other); |
217 | | static void |
218 | | act_store_print(const struct sieve_action *action, |
219 | | const struct sieve_result_print_env *rpenv, bool *keep); |
220 | | |
221 | | static int |
222 | | act_store_start(const struct sieve_action_exec_env *aenv, void **tr_context); |
223 | | static int |
224 | | act_store_execute(const struct sieve_action_exec_env *aenv, void *tr_context, |
225 | | bool *keep); |
226 | | static int |
227 | | act_store_commit(const struct sieve_action_exec_env *aenv, void *tr_context); |
228 | | static void |
229 | | act_store_rollback(const struct sieve_action_exec_env *aenv, void *tr_context, |
230 | | bool success); |
231 | | |
232 | | /* Action object */ |
233 | | |
234 | | const struct sieve_action_def act_store = { |
235 | | .name = "store", |
236 | | .flags = |
237 | | SIEVE_ACTFLAG_TRIES_DELIVER | |
238 | | SIEVE_ACTFLAG_MAIL_STORAGE, |
239 | | .equals = act_store_equals, |
240 | | .check_duplicate = act_store_check_duplicate, |
241 | | .print = act_store_print, |
242 | | .start = act_store_start, |
243 | | .execute = act_store_execute, |
244 | | .commit = act_store_commit, |
245 | | .rollback = act_store_rollback, |
246 | | }; |
247 | | |
248 | | /* API */ |
249 | | |
250 | | int sieve_act_store_add_to_result(const struct sieve_runtime_env *renv, |
251 | | const char *name, |
252 | | struct sieve_side_effects_list *seffects, |
253 | | const char *mailbox) |
254 | 0 | { |
255 | 0 | pool_t pool; |
256 | 0 | struct act_store_context *act; |
257 | | |
258 | | /* Add redirect action to the result */ |
259 | 0 | pool = sieve_result_pool(renv->result); |
260 | 0 | act = p_new(pool, struct act_store_context, 1); |
261 | 0 | act->mailbox = p_strdup(pool, mailbox); |
262 | |
|
263 | 0 | return sieve_result_add_action(renv, NULL, name, &act_store, seffects, |
264 | 0 | act, 0, TRUE); |
265 | 0 | } |
266 | | |
267 | | void sieve_act_store_add_flags(const struct sieve_action_exec_env *aenv, |
268 | | void *tr_context, const char *const *keywords, |
269 | | enum mail_flags flags) |
270 | 0 | { |
271 | 0 | struct act_store_transaction *trans = |
272 | 0 | (struct act_store_transaction *)tr_context; |
273 | |
|
274 | 0 | i_assert(trans != NULL); |
275 | | |
276 | | /* Assign mail keywords for subsequent mailbox_copy() */ |
277 | 0 | if (*keywords != NULL) { |
278 | 0 | const char *const *kw; |
279 | |
|
280 | 0 | if (!array_is_created(&trans->keywords)) { |
281 | 0 | pool_t pool = sieve_result_pool(aenv->result); |
282 | 0 | p_array_init(&trans->keywords, pool, 2); |
283 | 0 | } |
284 | |
|
285 | 0 | kw = keywords; |
286 | 0 | while (*kw != NULL) { |
287 | 0 | array_append(&trans->keywords, kw, 1); |
288 | 0 | kw++; |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | | /* Assign mail flags for subsequent mailbox_copy() */ |
293 | 0 | trans->flags |= flags; |
294 | |
|
295 | 0 | trans->flags_altered = TRUE; |
296 | 0 | } |
297 | | |
298 | | /* Equality */ |
299 | | |
300 | | static bool |
301 | | act_store_equals(const struct sieve_script_env *senv, |
302 | | const struct sieve_action *act1, |
303 | | const struct sieve_action *act2) |
304 | 0 | { |
305 | 0 | struct act_store_context *st_ctx1 = |
306 | 0 | (act1 == NULL ? |
307 | 0 | NULL : (struct act_store_context *)act1->context); |
308 | 0 | struct act_store_context *st_ctx2 = |
309 | 0 | (act2 == NULL ? |
310 | 0 | NULL : (struct act_store_context *)act2->context); |
311 | 0 | const char *mailbox1, *mailbox2; |
312 | | |
313 | | /* FIXME: consider namespace aliases */ |
314 | |
|
315 | 0 | if (st_ctx1 == NULL && st_ctx2 == NULL) |
316 | 0 | return TRUE; |
317 | | |
318 | 0 | mailbox1 = (st_ctx1 == NULL ? |
319 | 0 | SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx1->mailbox); |
320 | 0 | mailbox2 = (st_ctx2 == NULL ? |
321 | 0 | SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx2->mailbox); |
322 | |
|
323 | 0 | if (strcmp(mailbox1, mailbox2) == 0) |
324 | 0 | return TRUE; |
325 | | |
326 | 0 | return (strcasecmp(mailbox1, "INBOX") == 0 && |
327 | 0 | strcasecmp(mailbox2, "INBOX") == 0); |
328 | 0 | } |
329 | | |
330 | | /* Result verification */ |
331 | | |
332 | | static int |
333 | | act_store_check_duplicate(const struct sieve_runtime_env *renv, |
334 | | const struct sieve_action *act, |
335 | | const struct sieve_action *act_other) |
336 | 0 | { |
337 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
338 | |
|
339 | 0 | return (act_store_equals(eenv->scriptenv, act, act_other) ? 1 : 0); |
340 | 0 | } |
341 | | |
342 | | /* Result printing */ |
343 | | |
344 | | static void |
345 | | act_store_print(const struct sieve_action *action, |
346 | | const struct sieve_result_print_env *rpenv, bool *keep) |
347 | 0 | { |
348 | 0 | struct act_store_context *ctx = |
349 | 0 | (struct act_store_context *)action->context; |
350 | 0 | const char *mailbox; |
351 | |
|
352 | 0 | mailbox = (ctx == NULL ? |
353 | 0 | SIEVE_SCRIPT_DEFAULT_MAILBOX(rpenv->scriptenv) : |
354 | 0 | ctx->mailbox); |
355 | |
|
356 | 0 | sieve_result_action_printf(rpenv, "store message in folder: %s", |
357 | 0 | str_sanitize(mailbox, 128)); |
358 | |
|
359 | 0 | *keep = FALSE; |
360 | 0 | } |
361 | | |
362 | | /* Action implementation */ |
363 | | |
364 | | void sieve_act_store_get_storage_error(const struct sieve_action_exec_env *aenv, |
365 | | struct act_store_transaction *trans) |
366 | 0 | { |
367 | 0 | pool_t pool = sieve_result_pool(aenv->result); |
368 | |
|
369 | 0 | trans->error = p_strdup(pool, |
370 | 0 | mailbox_get_last_internal_error(trans->box, |
371 | 0 | &trans->error_code)); |
372 | 0 | } |
373 | | |
374 | | static bool |
375 | | act_store_mailbox_alloc(const struct sieve_action_exec_env *aenv, |
376 | | const char *mailbox, struct mailbox **box_r, |
377 | | enum mail_error *error_code_r, const char **error_r) |
378 | 0 | { |
379 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
380 | 0 | struct mailbox *box; |
381 | 0 | struct mail_storage **storage = &(eenv->exec_status->last_storage); |
382 | 0 | enum mailbox_flags flags = MAILBOX_FLAG_POST_SESSION; |
383 | |
|
384 | 0 | *box_r = NULL; |
385 | 0 | *error_code_r = MAIL_ERROR_NONE; |
386 | 0 | *error_r = NULL; |
387 | |
|
388 | 0 | if (!uni_utf8_str_is_valid(mailbox)) { |
389 | | /* Just a precaution; already (supposed to be) checked at |
390 | | compiletime/runtime. |
391 | | */ |
392 | 0 | *error_r = t_strdup_printf("mailbox name not utf-8: %s", |
393 | 0 | mailbox); |
394 | 0 | *error_code_r = MAIL_ERROR_PARAMS; |
395 | 0 | return FALSE; |
396 | 0 | } |
397 | | |
398 | 0 | if (eenv->scriptenv->mailbox_autocreate) |
399 | 0 | flags |= MAILBOX_FLAG_AUTO_CREATE; |
400 | 0 | if (eenv->scriptenv->mailbox_autosubscribe) |
401 | 0 | flags |= MAILBOX_FLAG_AUTO_SUBSCRIBE; |
402 | 0 | *box_r = box = mailbox_alloc_for_user(eenv->scriptenv->user, mailbox, |
403 | 0 | flags); |
404 | 0 | *storage = mailbox_get_storage(box); |
405 | |
|
406 | 0 | return TRUE; |
407 | 0 | } |
408 | | |
409 | | static int |
410 | | act_store_start(const struct sieve_action_exec_env *aenv, void **tr_context) |
411 | 0 | { |
412 | 0 | const struct sieve_action *action = aenv->action; |
413 | 0 | struct act_store_context *ctx = |
414 | 0 | (struct act_store_context *)action->context; |
415 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
416 | 0 | const struct sieve_script_env *senv = eenv->scriptenv; |
417 | 0 | struct act_store_transaction *trans; |
418 | 0 | struct mailbox *box = NULL; |
419 | 0 | pool_t pool = sieve_result_pool(aenv->result); |
420 | 0 | const char *error = NULL; |
421 | 0 | enum mail_error error_code = MAIL_ERROR_NONE; |
422 | 0 | bool disabled = FALSE, alloc_failed = FALSE; |
423 | | |
424 | | /* If context is NULL, the store action is the result of (implicit) |
425 | | keep. |
426 | | */ |
427 | 0 | if (ctx == NULL) { |
428 | 0 | ctx = p_new(pool, struct act_store_context, 1); |
429 | 0 | ctx->mailbox = |
430 | 0 | p_strdup(pool, SIEVE_SCRIPT_DEFAULT_MAILBOX(senv)); |
431 | 0 | } |
432 | |
|
433 | 0 | e_debug(aenv->event, "Start storing into mailbox %s", ctx->mailbox); |
434 | | |
435 | | /* Open the requested mailbox */ |
436 | | |
437 | | /* NOTE: The caller of the sieve library is allowed to leave user set |
438 | | to NULL. This implementation will then skip actually storing the |
439 | | message. |
440 | | */ |
441 | 0 | if (senv->user != NULL) { |
442 | 0 | if (!act_store_mailbox_alloc(aenv, ctx->mailbox, &box, |
443 | 0 | &error_code, &error)) |
444 | 0 | alloc_failed = TRUE; |
445 | 0 | } else { |
446 | 0 | disabled = TRUE; |
447 | 0 | } |
448 | | |
449 | | /* Create transaction context */ |
450 | 0 | trans = p_new(pool, struct act_store_transaction, 1); |
451 | |
|
452 | 0 | trans->context = ctx; |
453 | 0 | trans->box = box; |
454 | 0 | trans->flags = 0; |
455 | |
|
456 | 0 | trans->mailbox_name = ctx->mailbox; |
457 | 0 | trans->mailbox_identifier = |
458 | 0 | p_strdup_printf(pool, "'%s'", str_sanitize(ctx->mailbox, 256)); |
459 | |
|
460 | 0 | trans->disabled = disabled; |
461 | |
|
462 | 0 | if (alloc_failed) { |
463 | 0 | trans->error = p_strdup(pool, error); |
464 | 0 | trans->error_code = error_code; |
465 | 0 | e_debug(aenv->event, "Failed to open mailbox %s: %s", |
466 | 0 | trans->mailbox_identifier, trans->error); |
467 | 0 | } else { |
468 | 0 | trans->error_code = MAIL_ERROR_NONE; |
469 | 0 | } |
470 | |
|
471 | 0 | *tr_context = trans; |
472 | |
|
473 | 0 | switch (trans->error_code) { |
474 | 0 | case MAIL_ERROR_NONE: |
475 | 0 | case MAIL_ERROR_NOTFOUND: |
476 | 0 | return SIEVE_EXEC_OK; |
477 | 0 | case MAIL_ERROR_TEMP: |
478 | 0 | return SIEVE_EXEC_TEMP_FAILURE; |
479 | 0 | default: |
480 | 0 | break; |
481 | 0 | } |
482 | 0 | return SIEVE_EXEC_FAILURE; |
483 | 0 | } |
484 | | |
485 | | static struct mail_keywords * |
486 | | act_store_keywords_create(const struct sieve_action_exec_env *aenv, |
487 | | ARRAY_TYPE(const_string) *keywords, |
488 | | struct mailbox *box, bool create_empty) |
489 | 0 | { |
490 | 0 | struct mail_keywords *box_keywords = NULL; |
491 | 0 | const char *const *kwds = NULL; |
492 | |
|
493 | 0 | if (!array_is_created(keywords) || array_count(keywords) == 0) { |
494 | 0 | if (!create_empty) |
495 | 0 | return NULL; |
496 | 0 | } else { |
497 | 0 | ARRAY_TYPE(const_string) valid_keywords; |
498 | 0 | const char *error; |
499 | 0 | unsigned int count, i; |
500 | |
|
501 | 0 | kwds = array_get(keywords, &count); |
502 | 0 | t_array_init(&valid_keywords, count); |
503 | |
|
504 | 0 | for (i = 0; i < count; i++) { |
505 | 0 | if (mailbox_keyword_is_valid(box, kwds[i], &error)) { |
506 | 0 | array_append(&valid_keywords, &kwds[i], 1); |
507 | 0 | continue; |
508 | 0 | } |
509 | | |
510 | 0 | sieve_result_warning(aenv, |
511 | 0 | "specified IMAP keyword '%s' is invalid " |
512 | 0 | "(ignored): %s", str_sanitize(kwds[i], 64), |
513 | 0 | sieve_error_from_external(error)); |
514 | 0 | } |
515 | |
|
516 | 0 | array_append_zero(keywords); |
517 | 0 | kwds = array_idx(keywords, 0); |
518 | 0 | } |
519 | | |
520 | 0 | if (mailbox_keywords_create(box, kwds, &box_keywords) < 0) { |
521 | 0 | sieve_result_error( |
522 | 0 | aenv, "invalid keywords set for stored message"); |
523 | 0 | return NULL; |
524 | 0 | } |
525 | | |
526 | 0 | return box_keywords; |
527 | 0 | } |
528 | | |
529 | | static bool have_equal_keywords(struct mail *mail, struct mail_keywords *new_kw) |
530 | 0 | { |
531 | 0 | const ARRAY_TYPE(keyword_indexes) *old_kw_arr = |
532 | 0 | mail_get_keyword_indexes(mail); |
533 | 0 | const unsigned int *old_kw; |
534 | 0 | unsigned int i, j; |
535 | |
|
536 | 0 | if (array_count(old_kw_arr) != new_kw->count) |
537 | 0 | return FALSE; |
538 | 0 | if (new_kw->count == 0) |
539 | 0 | return TRUE; |
540 | | |
541 | 0 | old_kw = array_front(old_kw_arr); |
542 | 0 | for (i = 0; i < new_kw->count; i++) { |
543 | | /* new_kw->count equals old_kw's count and it's easier to use */ |
544 | 0 | for (j = 0; j < new_kw->count; j++) { |
545 | 0 | if (old_kw[j] == new_kw->idx[i]) |
546 | 0 | break; |
547 | 0 | } |
548 | 0 | if (j == new_kw->count) |
549 | 0 | return FALSE; |
550 | 0 | } |
551 | 0 | return TRUE; |
552 | 0 | } |
553 | | |
554 | | static int |
555 | | act_store_execute(const struct sieve_action_exec_env *aenv, void *tr_context, |
556 | | bool *keep) |
557 | 0 | { |
558 | 0 | const struct sieve_action *action = aenv->action; |
559 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
560 | 0 | struct act_store_transaction *trans = |
561 | 0 | (struct act_store_transaction *)tr_context; |
562 | 0 | struct mail *mail = (action->mail != NULL ? |
563 | 0 | action->mail : eenv->msgdata->mail); |
564 | 0 | struct mail_save_context *save_ctx; |
565 | 0 | struct mail_keywords *keywords = NULL; |
566 | 0 | struct mailbox *box; |
567 | 0 | bool backends_equal = FALSE; |
568 | 0 | int status = SIEVE_EXEC_OK; |
569 | | |
570 | | /* Verify transaction */ |
571 | 0 | if (trans == NULL) |
572 | 0 | return SIEVE_EXEC_FAILURE; |
573 | 0 | box = trans->box; |
574 | | |
575 | | /* Check whether we need to do anything */ |
576 | 0 | if (trans->disabled) { |
577 | 0 | e_debug(aenv->event, "Skip storing into mailbox %s", |
578 | 0 | trans->mailbox_identifier); |
579 | 0 | *keep = FALSE; |
580 | 0 | return SIEVE_EXEC_OK; |
581 | 0 | } |
582 | | |
583 | | /* Exit early if mailbox is not available */ |
584 | 0 | if (box == NULL) |
585 | 0 | return SIEVE_EXEC_FAILURE; |
586 | | |
587 | 0 | e_debug(aenv->event, "Execute storing into mailbox %s", |
588 | 0 | trans->mailbox_identifier); |
589 | | |
590 | | /* Mark attempt to use storage. Can only get here when all previous |
591 | | actions succeeded. |
592 | | */ |
593 | 0 | eenv->exec_status->last_storage = mailbox_get_storage(box); |
594 | | |
595 | | /* Open the mailbox (may already be open) */ |
596 | 0 | if (trans->error_code == MAIL_ERROR_NONE) { |
597 | 0 | if (mailbox_open(box) < 0) { |
598 | 0 | sieve_act_store_get_storage_error(aenv, trans); |
599 | 0 | e_debug(aenv->event, "Failed to open mailbox %s: %s", |
600 | 0 | trans->mailbox_identifier, trans->error); |
601 | 0 | } |
602 | 0 | } |
603 | | |
604 | | /* Exit early if transaction already failed */ |
605 | 0 | switch (trans->error_code) { |
606 | 0 | case MAIL_ERROR_NONE: |
607 | 0 | break; |
608 | 0 | case MAIL_ERROR_TEMP: |
609 | 0 | return SIEVE_EXEC_TEMP_FAILURE; |
610 | 0 | default: |
611 | 0 | return SIEVE_EXEC_FAILURE; |
612 | 0 | } |
613 | | |
614 | | /* If the message originates from the target mailbox, only update the |
615 | | flags and keywords (if not read-only) |
616 | | */ |
617 | 0 | if (mailbox_backends_equal(box, mail->box)) { |
618 | 0 | backends_equal = TRUE; |
619 | 0 | } else { |
620 | 0 | struct mail *real_mail; |
621 | |
|
622 | 0 | if (mail_get_backend_mail(mail, &real_mail) < 0) |
623 | 0 | return SIEVE_EXEC_FAILURE; |
624 | 0 | if (real_mail != mail && |
625 | 0 | mailbox_backends_equal(box, real_mail->box)) |
626 | 0 | backends_equal = TRUE; |
627 | 0 | } |
628 | 0 | if (backends_equal) { |
629 | 0 | trans->redundant = TRUE; |
630 | |
|
631 | 0 | if (trans->flags_altered && !mailbox_is_readonly(mail->box)) { |
632 | 0 | keywords = act_store_keywords_create( |
633 | 0 | aenv, &trans->keywords, mail->box, TRUE); |
634 | |
|
635 | 0 | if (keywords != NULL) { |
636 | 0 | if (!have_equal_keywords(mail, keywords)) { |
637 | 0 | eenv->exec_status->significant_action_executed = TRUE; |
638 | 0 | mail_update_keywords(mail, MODIFY_REPLACE, keywords); |
639 | 0 | } |
640 | 0 | mailbox_keywords_unref(&keywords); |
641 | 0 | } |
642 | |
|
643 | 0 | if ((mail_get_flags(mail) & MAIL_FLAGS_NONRECENT) != trans->flags) { |
644 | 0 | eenv->exec_status->significant_action_executed = TRUE; |
645 | 0 | mail_update_flags(mail, MODIFY_REPLACE, trans->flags); |
646 | 0 | } |
647 | 0 | } |
648 | 0 | e_debug(aenv->event, "Updated existing mail in mailbox %s", |
649 | 0 | trans->mailbox_identifier); |
650 | 0 | return SIEVE_EXEC_OK; |
651 | | |
652 | | /* If the message is modified, only store it in the source mailbox when |
653 | | it is not opened read-only. Mail structs of modified messages have |
654 | | their own mailbox, unrelated to the orignal mail, so this case needs |
655 | | to be handled separately. |
656 | | */ |
657 | 0 | } else if (mail != eenv->msgdata->mail && |
658 | 0 | mailbox_is_readonly(eenv->msgdata->mail->box) && |
659 | 0 | (mailbox_backends_equal(box, eenv->msgdata->mail->box))) { |
660 | 0 | e_debug(aenv->event, |
661 | 0 | "Not modifying exsiting mail in read-only mailbox %s", |
662 | 0 | trans->mailbox_identifier); |
663 | 0 | trans->redundant = TRUE; |
664 | 0 | return SIEVE_EXEC_OK; |
665 | 0 | } |
666 | | |
667 | | /* Mark attempt to store in default mailbox */ |
668 | 0 | if (strcmp(trans->context->mailbox, |
669 | 0 | SIEVE_SCRIPT_DEFAULT_MAILBOX(eenv->scriptenv)) == 0) |
670 | 0 | eenv->exec_status->tried_default_save = TRUE; |
671 | | |
672 | | /* Start mail transaction */ |
673 | 0 | trans->mail_trans = mailbox_transaction_begin( |
674 | 0 | box, MAILBOX_TRANSACTION_FLAG_EXTERNAL, __func__); |
675 | | |
676 | | /* Store the message */ |
677 | 0 | save_ctx = mailbox_save_alloc(trans->mail_trans); |
678 | | |
679 | | /* Apply keywords and flags that side-effects may have added */ |
680 | 0 | if (trans->flags_altered) { |
681 | 0 | keywords = act_store_keywords_create(aenv, &trans->keywords, |
682 | 0 | box, FALSE); |
683 | |
|
684 | 0 | if (trans->flags != 0 || keywords != NULL) { |
685 | 0 | eenv->exec_status->significant_action_executed = TRUE; |
686 | 0 | mailbox_save_set_flags(save_ctx, trans->flags, keywords); |
687 | 0 | } |
688 | 0 | } else { |
689 | 0 | mailbox_save_copy_flags(save_ctx, mail); |
690 | 0 | } |
691 | |
|
692 | 0 | if (mailbox_save_using_mail(&save_ctx, mail) < 0) { |
693 | 0 | sieve_act_store_get_storage_error(aenv, trans); |
694 | 0 | e_debug(aenv->event, "Failed to save to mailbox %s: %s", |
695 | 0 | trans->mailbox_identifier, trans->error); |
696 | |
|
697 | 0 | status = (trans->error_code == MAIL_ERROR_TEMP ? |
698 | 0 | SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); |
699 | 0 | } else { |
700 | 0 | e_debug(aenv->event, "Saving to mailbox %s successful so far", |
701 | 0 | trans->mailbox_identifier); |
702 | 0 | eenv->exec_status->significant_action_executed = TRUE; |
703 | 0 | } |
704 | | |
705 | | /* Deallocate keywords */ |
706 | 0 | if (keywords != NULL) |
707 | 0 | mailbox_keywords_unref(&keywords); |
708 | | |
709 | | /* Cancel implicit keep if all went well so far */ |
710 | 0 | *keep = (status < SIEVE_EXEC_OK); |
711 | |
|
712 | 0 | return status; |
713 | 0 | } |
714 | | |
715 | | static void |
716 | | act_store_log_status(struct act_store_transaction *trans, |
717 | | const struct sieve_action_exec_env *aenv, |
718 | | bool rolled_back, bool status) |
719 | 0 | { |
720 | 0 | const char *mailbox_name = trans->mailbox_name; |
721 | 0 | const char *mailbox_identifier = trans->mailbox_identifier; |
722 | |
|
723 | 0 | if (trans->box != NULL) { |
724 | 0 | const char *mailbox_vname = str_sanitize(mailbox_get_vname(trans->box), 128); |
725 | |
|
726 | 0 | if (strcmp(trans->mailbox_name, mailbox_vname) != 0) { |
727 | 0 | mailbox_identifier = t_strdup_printf( |
728 | 0 | "%s (%s)", mailbox_identifier, |
729 | 0 | str_sanitize(mailbox_vname, 256)); |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | /* Store disabled? */ |
734 | 0 | if (trans->disabled) { |
735 | 0 | sieve_result_global_log(aenv, "store into mailbox %s skipped", |
736 | 0 | mailbox_identifier); |
737 | | /* Store redundant? */ |
738 | 0 | } else if (trans->redundant) { |
739 | 0 | sieve_result_global_log(aenv, "left message in mailbox %s", |
740 | 0 | mailbox_identifier); |
741 | | /* Store failed? */ |
742 | 0 | } else if (!status) { |
743 | 0 | const char *errstr; |
744 | 0 | enum mail_error error_code; |
745 | |
|
746 | 0 | if (trans->error == NULL) |
747 | 0 | sieve_act_store_get_storage_error(aenv, trans); |
748 | |
|
749 | 0 | errstr = trans->error; |
750 | 0 | error_code = trans->error_code; |
751 | |
|
752 | 0 | if (error_code == MAIL_ERROR_NOQUOTA) { |
753 | | /* Never log quota problems as error in global log */ |
754 | 0 | sieve_result_global_log_error( |
755 | 0 | aenv, "failed to store into mailbox %s: %s", |
756 | 0 | mailbox_identifier, errstr); |
757 | 0 | } else if (error_code == MAIL_ERROR_NOTFOUND || |
758 | 0 | error_code == MAIL_ERROR_PARAMS || |
759 | 0 | error_code == MAIL_ERROR_PERM) { |
760 | 0 | sieve_result_error( |
761 | 0 | aenv, "failed to store into mailbox %s: %s", |
762 | 0 | mailbox_identifier, errstr); |
763 | 0 | } else { |
764 | 0 | sieve_result_global_error( |
765 | 0 | aenv, "failed to store into mailbox %s: %s", |
766 | 0 | mailbox_identifier, errstr); |
767 | 0 | } |
768 | | /* Store aborted? */ |
769 | 0 | } else if (rolled_back) { |
770 | 0 | if (!aenv->action->keep) { |
771 | 0 | sieve_result_global_log( |
772 | 0 | aenv, "store into mailbox %s aborted", |
773 | 0 | mailbox_identifier); |
774 | 0 | } else { |
775 | 0 | e_debug(aenv->event, "Store into mailbox %s aborted", |
776 | 0 | mailbox_identifier); |
777 | 0 | } |
778 | | /* Succeeded */ |
779 | 0 | } else { |
780 | 0 | struct event_passthrough *e = |
781 | 0 | sieve_action_create_finish_event(aenv)-> |
782 | 0 | add_str("fileinto_mailbox_name", mailbox_name)-> |
783 | 0 | add_str("fileinto_mailbox", mailbox_identifier); |
784 | 0 | sieve_result_event_log(aenv, e->event(), |
785 | 0 | "stored mail into mailbox %s", |
786 | 0 | mailbox_identifier); |
787 | 0 | } |
788 | 0 | } |
789 | | |
790 | | static void act_store_cleanup(struct act_store_transaction *trans) |
791 | 0 | { |
792 | 0 | if (trans->mail_trans != NULL) |
793 | 0 | mailbox_transaction_rollback(&trans->mail_trans); |
794 | 0 | if (trans->box != NULL) |
795 | 0 | mailbox_free(&trans->box); |
796 | 0 | } |
797 | | |
798 | | static int |
799 | | act_store_commit(const struct sieve_action_exec_env *aenv, void *tr_context) |
800 | 0 | { |
801 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
802 | 0 | struct act_store_transaction *trans = |
803 | 0 | (struct act_store_transaction *)tr_context; |
804 | 0 | bool bail_out = FALSE, status = TRUE; |
805 | 0 | int ret = SIEVE_EXEC_OK; |
806 | | |
807 | | /* Verify transaction */ |
808 | 0 | if (trans == NULL) |
809 | 0 | return SIEVE_EXEC_FAILURE; |
810 | | |
811 | 0 | e_debug(aenv->event, "Commit storing into mailbox %s", |
812 | 0 | trans->mailbox_identifier); |
813 | | |
814 | | /* Check whether we can commit this transaction */ |
815 | 0 | if (trans->error_code != MAIL_ERROR_NONE) { |
816 | | /* Transaction already failed */ |
817 | 0 | bail_out = TRUE; |
818 | 0 | status = FALSE; |
819 | 0 | if (trans->error_code == MAIL_ERROR_TEMP) |
820 | 0 | ret = SIEVE_EXEC_TEMP_FAILURE; |
821 | 0 | else |
822 | 0 | ret = SIEVE_EXEC_FAILURE; |
823 | | /* Check whether we need to do anything */ |
824 | 0 | } else if (trans->disabled) { |
825 | | /* Nothing to do */ |
826 | 0 | bail_out = TRUE; |
827 | 0 | } else if (trans->redundant) { |
828 | | /* This transaction is redundant */ |
829 | 0 | bail_out = TRUE; |
830 | 0 | eenv->exec_status->keep_original = TRUE; |
831 | 0 | eenv->exec_status->message_saved = TRUE; |
832 | 0 | } |
833 | 0 | if (bail_out) { |
834 | 0 | act_store_log_status(trans, aenv, FALSE, status); |
835 | 0 | act_store_cleanup(trans); |
836 | 0 | return ret; |
837 | 0 | } |
838 | | |
839 | 0 | i_assert(trans->box != NULL); |
840 | 0 | i_assert(trans->mail_trans != NULL); |
841 | | |
842 | | /* Mark attempt to use storage. Can only get here when all previous |
843 | | actions succeeded. |
844 | | */ |
845 | 0 | eenv->exec_status->last_storage = mailbox_get_storage(trans->box); |
846 | | |
847 | | /* Commit mailbox transaction */ |
848 | 0 | status = (mailbox_transaction_commit(&trans->mail_trans) == 0); |
849 | | |
850 | | /* Note the fact that the message was stored at least once */ |
851 | 0 | if (status) |
852 | 0 | eenv->exec_status->message_saved = TRUE; |
853 | 0 | else |
854 | 0 | eenv->exec_status->store_failed = TRUE; |
855 | | |
856 | | /* Log our status */ |
857 | 0 | act_store_log_status(trans, aenv, FALSE, status); |
858 | | |
859 | | /* Clean up */ |
860 | 0 | act_store_cleanup(trans); |
861 | |
|
862 | 0 | if (status) |
863 | 0 | return SIEVE_EXEC_OK; |
864 | | |
865 | 0 | return (trans->error_code == MAIL_ERROR_TEMP ? |
866 | 0 | SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); |
867 | 0 | } |
868 | | |
869 | | static void |
870 | | act_store_rollback(const struct sieve_action_exec_env *aenv, void *tr_context, |
871 | | bool success) |
872 | 0 | { |
873 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
874 | 0 | struct act_store_transaction *trans = |
875 | 0 | (struct act_store_transaction *)tr_context; |
876 | |
|
877 | 0 | if (trans == NULL) |
878 | 0 | return; |
879 | | |
880 | 0 | e_debug(aenv->event, "Roll back storing into mailbox %s", |
881 | 0 | trans->mailbox_identifier); |
882 | |
|
883 | 0 | i_assert(trans->box != NULL); |
884 | | |
885 | 0 | if (!success) { |
886 | 0 | eenv->exec_status->last_storage = |
887 | 0 | mailbox_get_storage(trans->box); |
888 | 0 | eenv->exec_status->store_failed = TRUE; |
889 | 0 | } |
890 | | |
891 | | /* Log status */ |
892 | 0 | act_store_log_status(trans, aenv, TRUE, success); |
893 | | |
894 | | /* Rollback mailbox transaction and clean up */ |
895 | 0 | act_store_cleanup(trans); |
896 | 0 | } |
897 | | |
898 | | /* |
899 | | * Redirect action |
900 | | */ |
901 | | |
902 | | int sieve_act_redirect_add_to_result(const struct sieve_runtime_env *renv, |
903 | | const char *name, |
904 | | struct sieve_side_effects_list *seffects, |
905 | | const struct smtp_address *to_address) |
906 | 0 | { |
907 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
908 | 0 | struct sieve_instance *svinst = eenv->svinst; |
909 | 0 | struct act_redirect_context *act; |
910 | 0 | pool_t pool; |
911 | |
|
912 | 0 | pool = sieve_result_pool(renv->result); |
913 | 0 | act = p_new(pool, struct act_redirect_context, 1); |
914 | 0 | act->to_address = smtp_address_clone(pool, to_address); |
915 | |
|
916 | 0 | if (sieve_result_add_action(renv, NULL, name, &act_redirect, seffects, |
917 | 0 | act, svinst->set->max_redirects, |
918 | 0 | TRUE) < 0) |
919 | 0 | return SIEVE_EXEC_FAILURE; |
920 | | |
921 | 0 | return SIEVE_EXEC_OK; |
922 | 0 | } |
923 | | |
924 | | /* |
925 | | * Action utility functions |
926 | | */ |
927 | | |
928 | | /* Rejecting the mail */ |
929 | | |
930 | | static int |
931 | | sieve_action_do_reject_mail(const struct sieve_action_exec_env *aenv, |
932 | | const struct smtp_address *recipient, |
933 | | const char *reason) |
934 | 0 | { |
935 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
936 | 0 | struct sieve_instance *svinst = eenv->svinst; |
937 | 0 | const struct sieve_script_env *senv = eenv->scriptenv; |
938 | 0 | const struct sieve_message_data *msgdata = eenv->msgdata; |
939 | 0 | const struct smtp_address *sender, *orig_recipient; |
940 | 0 | struct istream *input; |
941 | 0 | struct ostream *output; |
942 | 0 | struct sieve_smtp_context *sctx; |
943 | 0 | const char *new_msgid, *boundary, *error; |
944 | 0 | string_t *hdr; |
945 | 0 | int ret; |
946 | |
|
947 | 0 | sender = sieve_message_get_sender(aenv->msgctx); |
948 | 0 | orig_recipient = msgdata->envelope.rcpt_params->orcpt.addr; |
949 | |
|
950 | 0 | sctx = sieve_smtp_start_single(senv, sender, NULL, &output); |
951 | | |
952 | | /* Just to be sure */ |
953 | 0 | if (sctx == NULL) { |
954 | 0 | sieve_result_global_warning( |
955 | 0 | aenv, "reject action has no means to send mail"); |
956 | 0 | return SIEVE_EXEC_OK; |
957 | 0 | } |
958 | | |
959 | 0 | new_msgid = sieve_message_get_new_id(svinst); |
960 | 0 | boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname); |
961 | |
|
962 | 0 | hdr = t_str_new(512); |
963 | 0 | rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); |
964 | 0 | rfc2822_header_write(hdr, "Message-ID", new_msgid); |
965 | 0 | rfc2822_header_write(hdr, "Date", message_date_create(ioloop_time)); |
966 | 0 | rfc2822_header_write(hdr, "From", sieve_get_postmaster_address(senv)); |
967 | 0 | rfc2822_header_printf(hdr, "To", "<%s>", smtp_address_encode(sender)); |
968 | 0 | rfc2822_header_write(hdr, "Subject", "Automatically rejected mail"); |
969 | 0 | rfc2822_header_write(hdr, "Auto-Submitted", "auto-replied (rejected)"); |
970 | 0 | rfc2822_header_write(hdr, "Precedence", "bulk"); |
971 | |
|
972 | 0 | rfc2822_header_write(hdr, "MIME-Version", "1.0"); |
973 | 0 | rfc2822_header_printf(hdr, "Content-Type", |
974 | 0 | "multipart/report; " |
975 | 0 | "report-type=disposition-notification;\r\n" |
976 | 0 | "boundary=\"%s\"", boundary); |
977 | |
|
978 | 0 | str_append(hdr, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); |
979 | | |
980 | | /* Human readable status report */ |
981 | 0 | str_printfa(hdr, "--%s\r\n", boundary); |
982 | 0 | rfc2822_header_write(hdr, "Content-Type", "text/plain; charset=utf-8"); |
983 | 0 | rfc2822_header_write(hdr, "Content-Disposition", "inline"); |
984 | 0 | rfc2822_header_write(hdr, "Content-Transfer-Encoding", "8bit"); |
985 | |
|
986 | 0 | str_printfa(hdr, "\r\nYour message to <%s> was automatically rejected:\r\n" |
987 | 0 | "%s\r\n", smtp_address_encode(recipient), reason); |
988 | | |
989 | | /* MDN status report */ |
990 | 0 | str_printfa(hdr, "--%s\r\n", boundary); |
991 | 0 | rfc2822_header_write(hdr, "Content-Type", |
992 | 0 | "message/disposition-notification"); |
993 | 0 | str_append(hdr, "\r\n"); |
994 | 0 | rfc2822_header_write(hdr, |
995 | 0 | "Reporting-UA: %s; Dovecot Mail Delivery Agent", |
996 | 0 | svinst->hostname); |
997 | 0 | if (orig_recipient != NULL) { |
998 | 0 | rfc2822_header_printf(hdr, "Original-Recipient", "rfc822; %s", |
999 | 0 | smtp_address_encode(orig_recipient)); |
1000 | 0 | } |
1001 | 0 | rfc2822_header_printf(hdr, "Final-Recipient", "rfc822; %s", |
1002 | 0 | smtp_address_encode(recipient)); |
1003 | |
|
1004 | 0 | if (msgdata->id != NULL) |
1005 | 0 | rfc2822_header_write(hdr, "Original-Message-ID", msgdata->id); |
1006 | 0 | rfc2822_header_write(hdr, "Disposition", |
1007 | 0 | "automatic-action/MDN-sent-automatically; deleted"); |
1008 | 0 | str_append(hdr, "\r\n"); |
1009 | | |
1010 | | /* original message's headers */ |
1011 | 0 | str_printfa(hdr, "--%s\r\n", boundary); |
1012 | 0 | rfc2822_header_write(hdr, "Content-Type", "message/rfc822"); |
1013 | 0 | str_append(hdr, "\r\n"); |
1014 | 0 | o_stream_nsend(output, str_data(hdr), str_len(hdr)); |
1015 | |
|
1016 | 0 | if (mail_get_hdr_stream(msgdata->mail, NULL, &input) == 0) { |
1017 | | /* NOTE: If you add more headers, they need to be sorted. We'll |
1018 | | drop Content-Type because we're not including the message |
1019 | | body, and having a multipart Content-Type may confuse some |
1020 | | MIME parsers when they don't see the message boundaries. |
1021 | | */ |
1022 | 0 | static const char *const exclude_headers[] = { |
1023 | 0 | "Content-Type" |
1024 | 0 | }; |
1025 | |
|
1026 | 0 | input = i_stream_create_header_filter( |
1027 | 0 | input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | |
1028 | 0 | HEADER_FILTER_HIDE_BODY, |
1029 | 0 | exclude_headers, N_ELEMENTS(exclude_headers), |
1030 | 0 | *null_header_filter_callback, NULL); |
1031 | 0 | o_stream_nsend_istream(output, input); |
1032 | 0 | i_stream_unref(&input); |
1033 | 0 | } |
1034 | |
|
1035 | 0 | str_truncate(hdr, 0); |
1036 | 0 | str_printfa(hdr, "\r\n\r\n--%s--\r\n", boundary); |
1037 | 0 | o_stream_nsend(output, str_data(hdr), str_len(hdr)); |
1038 | |
|
1039 | 0 | if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { |
1040 | 0 | if (ret < 0) { |
1041 | 0 | sieve_result_global_error(aenv, |
1042 | 0 | "failed to send rejection message to <%s>: %s " |
1043 | 0 | "(temporary failure)", |
1044 | 0 | smtp_address_encode(sender), |
1045 | 0 | str_sanitize(error, 512)); |
1046 | 0 | } else { |
1047 | 0 | sieve_result_global_log_error(aenv, |
1048 | 0 | "failed to send rejection message to <%s>: %s " |
1049 | 0 | "(permanent failure)", |
1050 | 0 | smtp_address_encode(sender), |
1051 | 0 | str_sanitize(error, 512)); |
1052 | 0 | } |
1053 | 0 | return SIEVE_EXEC_FAILURE; |
1054 | 0 | } |
1055 | | |
1056 | 0 | return SIEVE_EXEC_OK; |
1057 | 0 | } |
1058 | | |
1059 | | int sieve_action_reject_mail(const struct sieve_action_exec_env *aenv, |
1060 | | const struct smtp_address *recipient, |
1061 | | const char *reason) |
1062 | 0 | { |
1063 | 0 | const struct sieve_execute_env *eenv = aenv->exec_env; |
1064 | 0 | const struct sieve_script_env *senv = eenv->scriptenv; |
1065 | 0 | int result; |
1066 | |
|
1067 | 0 | T_BEGIN { |
1068 | 0 | if (senv->reject_mail != NULL) { |
1069 | 0 | result = (senv->reject_mail(senv, recipient, |
1070 | 0 | reason) >= 0 ? |
1071 | 0 | SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); |
1072 | 0 | } else { |
1073 | 0 | result = sieve_action_do_reject_mail(aenv, recipient, |
1074 | 0 | reason); |
1075 | 0 | } |
1076 | 0 | } T_END; |
1077 | | |
1078 | 0 | return result; |
1079 | 0 | } |
1080 | | |
1081 | | /* |
1082 | | * Mailbox |
1083 | | */ |
1084 | | |
1085 | | bool sieve_mailbox_check_name(const char *mailbox, const char **error_r) |
1086 | 0 | { |
1087 | 0 | if (!uni_utf8_str_is_valid(mailbox)) { |
1088 | 0 | *error_r = "invalid utf-8"; |
1089 | 0 | return FALSE; |
1090 | 0 | } |
1091 | 0 | return TRUE; |
1092 | 0 | } |
1093 | | |
1094 | | |