Coverage Report

Created: 2026-04-12 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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