Coverage Report

Created: 2026-08-31 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "istream.h"
6
#include "ostream.h"
7
#include "buffer.h"
8
#include "time-util.h"
9
#include "eacces-error.h"
10
#include "home-expand.h"
11
#include "hostpid.h"
12
#include "settings.h"
13
#include "message-address.h"
14
#include "mail-user.h"
15
16
#include "sieve-extensions.h"
17
#include "sieve-plugins.h"
18
19
#include "sieve-address.h"
20
#include "sieve-script.h"
21
#include "sieve-storage-private.h"
22
#include "sieve-ast.h"
23
#include "sieve-binary.h"
24
#include "sieve-actions.h"
25
#include "sieve-result.h"
26
27
#include "sieve-parser.h"
28
#include "sieve-validator.h"
29
#include "sieve-generator.h"
30
#include "sieve-interpreter.h"
31
#include "sieve-binary-dumper.h"
32
#include "sieve-rusage.h"
33
34
#include "sieve.h"
35
#include "sieve-common.h"
36
#include "sieve-limits.h"
37
#include "sieve-error-private.h"
38
39
#include <sys/types.h>
40
#include <sys/stat.h>
41
#include <fcntl.h>
42
#include <unistd.h>
43
#include <stdio.h>
44
#include <dirent.h>
45
46
struct event_category event_category_sieve = {
47
  .name = "sieve",
48
};
49
50
/*
51
 * Main Sieve library interface
52
 */
53
54
int sieve_init(const struct sieve_environment *env,
55
         const struct sieve_callbacks *callbacks, void *context,
56
         bool debug, struct sieve_instance **svinst_r)
57
0
{
58
0
  struct event *event;
59
0
  struct sieve_instance *svinst;
60
0
  const char *error;
61
0
  struct sieve_settings *set;
62
0
  const char *lfilter, *domain;
63
0
  pool_t pool;
64
65
0
  *svinst_r = NULL;
66
67
0
  settings_info_register(&sieve_setting_parser_info);
68
69
0
  lfilter = NULL;
70
0
  switch (env->location) {
71
0
  case SIEVE_ENV_LOCATION_MDA:
72
0
    lfilter = "sieve_env_location_mda";
73
0
    break;
74
0
  case SIEVE_ENV_LOCATION_MTA:
75
0
    lfilter = "sieve_env_location_mta";
76
0
    break;
77
0
  case SIEVE_ENV_LOCATION_MS:
78
0
    lfilter = "sieve_env_location_ms";
79
0
    break;
80
0
  default:
81
0
    break;
82
0
  }
83
84
0
  event = event_create(env->event_parent);
85
0
  event_add_category(event, &event_category_sieve);
86
0
  event_set_forced_debug(event, debug);
87
0
  event_set_append_log_prefix(event, "sieve: ");
88
0
  event_add_str(event, "user", env->username);
89
0
  if (lfilter != NULL) {
90
0
    event_set_ptr(event, SETTINGS_EVENT_FILTER_NAME,
91
0
            (void*)lfilter);
92
0
  }
93
94
0
  enum settings_get_flags set_flags = 0;
95
0
  if ((env->flags & SIEVE_FLAG_DUMP_CAPABILITIES) != 0)
96
0
    set_flags = SETTINGS_GET_FLAG_FAKE_EXPAND;
97
98
0
  if (settings_get(event, &sieve_setting_parser_info,
99
0
       set_flags, &set, &error) < 0) {
100
0
    e_error(event, "%s", error);
101
0
    event_unref(&event);
102
0
    return -1;
103
0
  }
104
105
  /* Create Sieve engine instance */
106
0
  pool = pool_alloconly_create("sieve", 8192);
107
0
  svinst = p_new(pool, struct sieve_instance, 1);
108
0
  svinst->pool = pool;
109
0
  svinst->callbacks = callbacks;
110
0
  svinst->context = context;
111
0
  svinst->debug = debug;
112
0
  svinst->base_dir = p_strdup_empty(pool, env->base_dir);
113
0
  svinst->username = p_strdup_empty(pool, env->username);
114
0
  svinst->home_dir = p_strdup_empty(pool, env->home_dir);
115
0
  svinst->temp_dir = p_strdup_empty(pool, env->temp_dir);
116
0
  svinst->flags = env->flags;
117
0
  svinst->env_location = env->location;
118
0
  svinst->delivery_phase = env->delivery_phase;
119
0
  svinst->event = event;
120
0
  svinst->set = set;
121
122
  /* Determine domain */
123
0
  if (env->domainname != NULL && *(env->domainname) != '\0')
124
0
    domain = env->domainname;
125
0
  else {
126
    /* Fall back to parsing username localpart@domain */
127
0
    domain = svinst->username == NULL ? NULL :
128
0
      strchr(svinst->username, '@');
129
0
    if (domain == NULL || *(domain+1) == '\0') {
130
      /* Fall back to parsing hostname host.domain */
131
0
      domain = (env->hostname != NULL ?
132
0
          strchr(env->hostname, '.') : NULL);
133
0
      if (domain == NULL || *(domain+1) == '\0' ||
134
0
          strchr(domain+1, '.') == NULL) {
135
        /* Fall back to bare hostname */
136
0
        domain = env->hostname;
137
0
      } else {
138
0
        domain++;
139
0
      }
140
0
    } else {
141
0
      domain++;
142
0
    }
143
0
  }
144
0
  svinst->hostname = p_strdup_empty(pool, env->hostname);
145
0
  svinst->domainname = p_strdup(pool, domain);
146
147
0
  sieve_errors_init(svinst);
148
149
0
  e_debug(event, "%s version %s initializing",
150
0
    PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL);
151
152
  /* Initialize extensions */
153
0
  if (sieve_extensions_init(svinst) < 0) {
154
0
    sieve_deinit(&svinst);
155
0
    return -1;
156
0
  }
157
158
  /* Initialize storage classes */
159
0
  sieve_storages_init(svinst);
160
161
  /* Load plugins */
162
0
  if (sieve_plugins_load(svinst, NULL, NULL) < 0) {
163
0
    sieve_deinit(&svinst);
164
0
    return -1;
165
0
  }
166
167
  /* Load extensions */
168
0
  if (sieve_extensions_load(svinst) < 0) {
169
0
    sieve_deinit(&svinst);
170
0
    return -1;
171
0
  }
172
173
0
  *svinst_r = svinst;
174
0
  return 0;
175
0
}
176
177
void sieve_deinit(struct sieve_instance **_svinst)
178
0
{
179
0
  struct sieve_instance *svinst = *_svinst;
180
181
0
  if (svinst == NULL)
182
0
    return;
183
0
  *_svinst = NULL;
184
185
0
  sieve_plugins_unload(svinst);
186
0
  sieve_storages_deinit(svinst);
187
0
  sieve_extensions_deinit(svinst);
188
0
  sieve_errors_deinit(svinst);
189
190
0
  settings_free(svinst->set);
191
0
  event_unref(&svinst->event);
192
193
0
  pool_unref(&(svinst)->pool);
194
0
}
195
196
int sieve_settings_reload(struct sieve_instance *svinst)
197
0
{
198
0
  struct sieve_settings *set;
199
0
  const char *error;
200
201
0
  if (settings_get(svinst->event, &sieve_setting_parser_info, 0,
202
0
       &set, &error) < 0) {
203
0
    e_error(svinst->event, "%s", error);
204
0
    return -1;
205
0
  }
206
207
0
  settings_free(svinst->set);
208
0
  svinst->set = set;
209
0
  return 0;
210
0
}
211
212
void sieve_set_extensions(struct sieve_instance *svinst, const char *extensions)
213
0
{
214
0
  (void)sieve_extensions_set_string(svinst, extensions, FALSE, FALSE);
215
0
}
216
217
const char *
218
sieve_get_capabilities(struct sieve_instance *svinst, const char *name)
219
0
{
220
0
  if (name == NULL || *name == '\0')
221
0
    return sieve_extensions_get_string(svinst);
222
223
0
  return sieve_extension_capabilities_get_string(svinst, name);
224
0
}
225
226
struct event *sieve_get_event(struct sieve_instance *svinst)
227
0
{
228
0
  return svinst->event;
229
0
}
230
231
/*
232
 * Low-level compiler functions
233
 */
234
235
struct sieve_ast *
236
sieve_parse(struct sieve_script *script, struct sieve_error_handler *ehandler,
237
      enum sieve_error *error_code_r)
238
0
{
239
0
  struct sieve_parser *parser;
240
0
  struct sieve_ast *ast = NULL;
241
242
0
  sieve_error_args_init(&error_code_r, NULL);
243
244
  /* Parse */
245
0
  parser = sieve_parser_create(script, ehandler, error_code_r);
246
0
  if (parser == NULL)
247
0
    return NULL;
248
249
0
  if (!sieve_parser_run(parser, &ast))
250
0
    ast = NULL;
251
0
  else
252
0
    sieve_ast_ref(ast);
253
254
0
  sieve_parser_free(&parser);
255
256
0
  if (ast == NULL)
257
0
    *error_code_r = SIEVE_ERROR_NOT_VALID;
258
0
  return ast;
259
0
}
260
261
bool sieve_validate(struct sieve_ast *ast, struct sieve_error_handler *ehandler,
262
        enum sieve_compile_flags flags,
263
        enum sieve_error *error_code_r)
264
0
{
265
0
  bool result = TRUE;
266
0
  struct sieve_validator *validator;
267
268
0
  sieve_error_args_init(&error_code_r, NULL);
269
270
0
  validator = sieve_validator_create(ast, ehandler, flags);
271
0
  if (!sieve_validator_run(validator))
272
0
    result = FALSE;
273
274
0
  sieve_validator_free(&validator);
275
276
0
  if (!result)
277
0
    *error_code_r = SIEVE_ERROR_NOT_VALID;
278
0
  return result;
279
0
}
280
281
static struct sieve_binary *
282
sieve_generate(struct sieve_ast *ast, struct sieve_error_handler *ehandler,
283
         enum sieve_compile_flags flags, enum sieve_error *error_code_r)
284
0
{
285
0
  struct sieve_generator *generator;
286
0
  struct sieve_binary *sbin = NULL;
287
288
0
  sieve_error_args_init(&error_code_r, NULL);
289
290
0
  generator = sieve_generator_create(ast, ehandler, flags);
291
0
  sbin = sieve_generator_run(generator, NULL);
292
293
0
  sieve_generator_free(&generator);
294
295
0
  if (sbin == NULL)
296
0
    *error_code_r = SIEVE_ERROR_NOT_VALID;
297
0
  return sbin;
298
0
}
299
300
/*
301
 * Sieve compilation
302
 */
303
304
int sieve_compile_script(struct sieve_script *script,
305
       struct sieve_error_handler *ehandler,
306
       enum sieve_compile_flags flags,
307
       struct sieve_binary **sbin_r,
308
       enum sieve_error *error_code_r)
309
0
{
310
0
  struct sieve_ast *ast;
311
0
  struct sieve_binary *sbin;
312
0
  bool no_error_result = (error_code_r == NULL);
313
314
0
  *sbin_r = NULL;
315
0
  sieve_error_args_init(&error_code_r, NULL);
316
317
  /* Parse */
318
0
  ast = sieve_parse(script, ehandler, error_code_r);
319
0
  if (ast == NULL) {
320
0
    switch (*error_code_r) {
321
0
    case SIEVE_ERROR_NOT_FOUND:
322
0
      if (no_error_result) {
323
0
        sieve_error(ehandler, sieve_script_name(script),
324
0
              "script not found");
325
0
      }
326
0
      break;
327
0
    default:
328
0
      sieve_error(ehandler, sieve_script_name(script),
329
0
            "parse failed");
330
0
    }
331
0
    return -1;
332
0
  }
333
334
  /* Validate */
335
0
  if (!sieve_validate(ast, ehandler, flags, error_code_r)) {
336
0
    sieve_error(ehandler, sieve_script_name(script),
337
0
          "validation failed");
338
339
0
    sieve_ast_unref(&ast);
340
0
    return -1;
341
0
  }
342
343
  /* Generate */
344
0
  sbin = sieve_generate(ast, ehandler, flags, error_code_r);
345
0
  if (sbin == NULL) {
346
0
    sieve_error(ehandler, sieve_script_name(script),
347
0
          "code generation failed");
348
0
    sieve_ast_unref(&ast);
349
0
    return -1;
350
0
  }
351
352
  /* Cleanup */
353
0
  sieve_ast_unref(&ast);
354
0
  *sbin_r = sbin;
355
0
  return 0;
356
0
}
357
358
int sieve_compile(struct sieve_instance *svinst, const char *script_cause,
359
      const char *storage_name, const char *script_name,
360
      struct sieve_error_handler *ehandler,
361
      enum sieve_compile_flags flags, struct sieve_binary **sbin_r,
362
      enum sieve_error *error_code_r)
363
0
{
364
0
  struct sieve_script *script;
365
0
  bool no_error_result = (error_code_r == NULL);
366
367
0
  *sbin_r = NULL;
368
0
  sieve_error_args_init(&error_code_r, NULL);
369
370
0
  if (sieve_script_create_open_in(svinst, script_cause,
371
0
          storage_name, script_name,
372
0
          &script, error_code_r, NULL) < 0) {
373
0
    switch (*error_code_r) {
374
0
    case SIEVE_ERROR_NOT_FOUND:
375
0
      if (no_error_result) {
376
0
        sieve_error(ehandler, script_name,
377
0
              "script not found");
378
0
      }
379
0
      break;
380
0
    default:
381
0
      sieve_internal_error(ehandler, script_name,
382
0
               "failed to open script");
383
0
    }
384
0
    return -1;
385
0
  }
386
387
0
  if (sieve_compile_script(script, ehandler, flags,
388
0
         sbin_r, error_code_r) < 0) {
389
0
    sieve_script_unref(&script);
390
0
    return -1;
391
0
  }
392
393
0
  e_debug(svinst->event, "Script '%s' successfully compiled",
394
0
    sieve_script_label(script));
395
396
0
  sieve_script_unref(&script);
397
0
  return 0;
398
0
}
399
400
/*
401
 * Sieve runtime
402
 */
403
404
static int
405
sieve_run(struct sieve_binary *sbin, struct sieve_result *result,
406
    struct sieve_execute_env *eenv, struct sieve_error_handler *ehandler)
407
0
{
408
0
  struct sieve_interpreter *interp;
409
0
  int ret = 0;
410
411
  /* Create the interpreter */
412
0
  interp = sieve_interpreter_create(sbin, NULL, eenv, ehandler);
413
0
  if (interp == NULL)
414
0
    return SIEVE_EXEC_BIN_CORRUPT;
415
416
  /* Run the interpreter */
417
0
  ret = sieve_interpreter_run(interp, result);
418
419
  /* Free the interpreter */
420
0
  sieve_interpreter_free(&interp);
421
422
0
  return ret;
423
0
}
424
425
/*
426
 * Reading/writing sieve binaries
427
 */
428
429
int sieve_load(struct sieve_instance *svinst, const char *bin_path,
430
         struct sieve_binary **sbin_r, enum sieve_error *error_code_r)
431
0
{
432
0
  return sieve_binary_open(svinst, bin_path, NULL, sbin_r, error_code_r);
433
0
}
434
435
static int
436
sieve_open_script_real(struct sieve_script *script,
437
           struct sieve_error_handler *ehandler,
438
           enum sieve_compile_flags flags,
439
           struct sieve_binary **sbin_r,
440
           enum sieve_error *error_code_r)
441
0
{
442
0
  struct sieve_instance *svinst = sieve_script_svinst(script);
443
0
  struct sieve_resource_usage rusage;
444
0
  struct sieve_binary *sbin;
445
0
  const char *error = NULL;
446
0
  int ret;
447
448
0
  sieve_resource_usage_init(&rusage);
449
450
  /* Try to open the matching binary */
451
0
  if (sieve_script_binary_load(script, &sbin, error_code_r) == 0) {
452
    /* Per-user rusage file (if any) overrides binary header */
453
0
    sieve_binary_apply_persisted_rusage(sbin);
454
0
    sieve_binary_get_resource_usage(sbin, &rusage);
455
456
    /* Ok, it exists; now let's see if it is up to date */
457
0
    if (!sieve_resource_usage_is_excessive(svinst, &rusage) &&
458
0
        !sieve_binary_up_to_date(sbin, flags)) {
459
      /* Not up to date */
460
0
      e_debug(svinst->event,
461
0
        "Script binary %s is not up-to-date",
462
0
        sieve_binary_path(sbin));
463
0
      sieve_binary_close(&sbin);
464
0
    }
465
0
  }
466
467
  /* If the binary does not exist or is not up-to-date, we need
468
   * to (re-)compile.
469
   */
470
0
  if (sbin != NULL) {
471
0
    e_debug(svinst->event,
472
0
      "Script binary %s successfully loaded",
473
0
      sieve_binary_path(sbin));
474
0
  } else {
475
0
    if (sieve_compile_script(script, ehandler, flags,
476
0
           &sbin, error_code_r) < 0)
477
0
      return -1;
478
479
0
    e_debug(svinst->event,
480
0
      "Script '%s' successfully compiled",
481
0
      sieve_script_label(script));
482
483
0
    sieve_binary_set_resource_usage(sbin, &rusage);
484
    /* Per-user rusage file (if any) overrides any carry-forward */
485
0
    sieve_binary_apply_persisted_rusage(sbin);
486
0
  }
487
488
  /* Check whether binary can be executed. */
489
0
  ret = sieve_binary_check_executable(sbin, error_code_r, &error);
490
0
  if (ret <= 0) {
491
0
    const char *path = sieve_binary_path(sbin);
492
493
0
    i_assert(error != NULL);
494
0
    if (path != NULL) {
495
0
      e_debug(svinst->event,
496
0
        "Script binary %s cannot be executed",
497
0
        path);
498
0
    } else {
499
0
      e_debug(svinst->event,
500
0
        "Script binary from %s cannot be executed",
501
0
        sieve_binary_source(sbin));
502
0
    }
503
0
    if (ret < 0) {
504
0
      sieve_internal_error(ehandler,
505
0
               sieve_script_name(script),
506
0
               "failed to open script");
507
0
    } else {
508
0
      sieve_error(ehandler, sieve_script_name(script),
509
0
            "%s", error);
510
0
    }
511
0
    sieve_binary_close(&sbin);
512
0
    return -1;
513
0
  }
514
515
0
  *sbin_r = sbin;
516
0
  return 0;
517
0
}
518
519
int sieve_open_script(struct sieve_script *script,
520
          struct sieve_error_handler *ehandler,
521
          enum sieve_compile_flags flags,
522
          struct sieve_binary **sbin_r,
523
          enum sieve_error *error_code_r)
524
0
{
525
0
  int ret;
526
527
0
  *sbin_r = NULL;
528
0
  sieve_error_args_init(&error_code_r, NULL);
529
530
0
  T_BEGIN {
531
0
    ret = sieve_open_script_real(script, ehandler, flags,
532
0
               sbin_r, error_code_r);
533
0
  } T_END;
534
535
0
  return ret;
536
0
}
537
538
int sieve_open(struct sieve_instance *svinst, const char *script_cause,
539
         const char *storage_name, const char *script_name,
540
         struct sieve_error_handler *ehandler,
541
         enum sieve_compile_flags flags, struct sieve_binary **sbin_r,
542
         enum sieve_error *error_code_r)
543
0
{
544
0
  struct sieve_script *script;
545
0
  bool no_error_result = (error_code_r == NULL);
546
0
  int ret;
547
548
0
  *sbin_r = NULL;
549
0
  sieve_error_args_init(&error_code_r, NULL);
550
551
  /* First open the scriptfile itself */
552
0
  if (sieve_script_create_open_in(svinst, script_cause,
553
0
          storage_name, script_name,
554
0
          &script, error_code_r, NULL) < 0) {
555
    /* Failed */
556
0
    switch (*error_code_r) {
557
0
    case SIEVE_ERROR_NOT_FOUND:
558
0
      if (no_error_result) {
559
0
        sieve_error(ehandler, script_name,
560
0
              "script not found");
561
0
      }
562
0
      break;
563
0
    default:
564
0
      sieve_internal_error(ehandler, script_name,
565
0
               "failed to open script");
566
0
    }
567
0
    return -1;
568
0
  }
569
570
  /* Drop script reference, if sbin != NULL it holds a reference of its
571
     own. Otherwise the script object is freed here.
572
   */
573
0
  ret = sieve_open_script(script, ehandler, flags,
574
0
        sbin_r, error_code_r);
575
0
  sieve_script_unref(&script);
576
0
  return ret;
577
0
}
578
579
const char *sieve_get_source(struct sieve_binary *sbin)
580
0
{
581
0
  return sieve_binary_source(sbin);
582
0
}
583
584
bool sieve_is_loaded(struct sieve_binary *sbin)
585
0
{
586
0
  return sieve_binary_loaded(sbin);
587
0
}
588
589
int sieve_save_as(struct sieve_binary *sbin, const char *bin_path, bool update,
590
      mode_t save_mode, enum sieve_error *error_code_r)
591
0
{
592
0
  if (bin_path == NULL)
593
0
    return sieve_save(sbin, update, error_code_r);
594
595
0
  return sieve_binary_save(sbin, bin_path, update, save_mode,
596
0
         error_code_r);
597
0
}
598
599
int sieve_save(struct sieve_binary *sbin, bool update,
600
         enum sieve_error *error_code_r)
601
0
{
602
0
  struct sieve_script *script = sieve_binary_script(sbin);
603
604
0
  if (script == NULL) {
605
0
    return sieve_binary_save(sbin, NULL, update, 0600,
606
0
           error_code_r);
607
0
  }
608
609
0
  return sieve_script_binary_save(script, sbin, update, error_code_r);
610
0
}
611
612
bool sieve_record_resource_usage(struct sieve_binary *sbin,
613
         const struct sieve_resource_usage *rusage)
614
0
{
615
0
  return sieve_binary_record_resource_usage(sbin, rusage);
616
0
}
617
618
int sieve_check_executable(struct sieve_binary *sbin,
619
         enum sieve_error *error_code_r,
620
         const char **client_error_r)
621
0
{
622
0
  return sieve_binary_check_executable(sbin, error_code_r,
623
0
               client_error_r);
624
0
}
625
626
void sieve_close(struct sieve_binary **_sbin)
627
0
{
628
0
  sieve_binary_close(_sbin);
629
0
}
630
631
/*
632
 * Debugging
633
 */
634
635
void sieve_dump(struct sieve_binary *sbin, struct ostream *stream, bool verbose)
636
0
{
637
0
  struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);
638
639
0
  sieve_binary_dumper_run(dumpr, stream, verbose);
640
641
0
  sieve_binary_dumper_free(&dumpr);
642
0
}
643
644
void sieve_hexdump(struct sieve_binary *sbin, struct ostream *stream)
645
0
{
646
0
  struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);
647
648
0
  sieve_binary_dumper_hexdump(dumpr, stream);
649
650
0
  sieve_binary_dumper_free(&dumpr);
651
0
}
652
653
int sieve_test(struct sieve_binary *sbin,
654
         const struct sieve_message_data *msgdata,
655
         const struct sieve_script_env *senv,
656
         struct sieve_error_handler *ehandler, struct ostream *stream,
657
         enum sieve_execute_flags flags)
658
0
{
659
0
  struct sieve_instance *svinst = sieve_binary_svinst(sbin);
660
0
  struct sieve_result *result;
661
0
  struct sieve_execute_env eenv;
662
0
  pool_t pool;
663
0
  int ret;
664
665
0
  pool = pool_alloconly_create("sieve execution", 4096);
666
0
  sieve_execute_init(&eenv, svinst, pool, msgdata, senv, flags);
667
668
  /* Create result object */
669
0
  result = sieve_result_create(svinst, pool, &eenv);
670
671
  /* Run the script */
672
0
  ret = sieve_run(sbin, result, &eenv, ehandler);
673
674
  /* Print result if successful */
675
0
  if (ret > 0) {
676
0
    ret = (sieve_result_print(result, senv, stream, NULL) ?
677
0
           SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE);
678
0
  }
679
680
  /* Cleanup */
681
0
  sieve_result_unref(&result);
682
0
  sieve_execute_deinit(&eenv);
683
0
  pool_unref(&pool);
684
685
0
  return ret;
686
0
}
687
688
/*
689
 * Script execution
690
 */
691
692
int sieve_script_env_init(struct sieve_script_env *senv, struct mail_user *user,
693
        const char **error_r)
694
0
{
695
0
  const struct message_address *postmaster;
696
0
  const char *error;
697
698
0
  if (!mail_user_get_postmaster_address(user, &postmaster, &error)) {
699
0
    *error_r = t_strdup_printf(
700
0
      "Invalid postmaster_address: %s", error);
701
0
    return -1;
702
0
  }
703
704
0
  i_zero(senv);
705
0
  senv->user = user;
706
0
  senv->postmaster_address = postmaster;
707
0
  return 0;
708
0
}
709
710
int sieve_execute(struct sieve_binary *sbin,
711
      const struct sieve_message_data *msgdata,
712
      const struct sieve_script_env *senv,
713
      struct sieve_error_handler *exec_ehandler,
714
      struct sieve_error_handler *action_ehandler,
715
      enum sieve_execute_flags flags)
716
0
{
717
0
  struct sieve_instance *svinst = sieve_binary_svinst(sbin);
718
0
  struct sieve_result *result = NULL;
719
0
  struct sieve_result_execution *rexec;
720
0
  struct sieve_execute_env eenv;
721
0
  pool_t pool;
722
0
  int ret;
723
724
0
  pool = pool_alloconly_create("sieve execution", 4096);
725
0
  sieve_execute_init(&eenv, svinst, pool, msgdata, senv, flags);
726
727
  /* Create result object */
728
0
  result = sieve_result_create(svinst, pool, &eenv);
729
730
  /* Run the script */
731
0
  ret = sieve_run(sbin, result, &eenv, exec_ehandler);
732
733
0
  rexec = sieve_result_execution_create(result, pool);
734
735
  /* Evaluate status and execute the result:
736
     Strange situations, e.g. currupt binaries, must be handled by the
737
     caller. In that case no implicit keep is attempted, because the
738
     situation may be resolved.
739
   */
740
0
  ret = sieve_result_execute(rexec, ret, TRUE, action_ehandler, NULL);
741
742
0
  sieve_result_execution_destroy(&rexec);
743
744
  /* Cleanup */
745
0
  sieve_result_unref(&result);
746
0
  sieve_execute_finish(&eenv, ret);
747
0
  sieve_execute_deinit(&eenv);
748
0
  pool_unref(&pool);
749
750
0
  return ret;
751
0
}
752
753
/*
754
 * Multiscript support
755
 */
756
757
struct sieve_multiscript {
758
  pool_t pool;
759
  struct sieve_execute_env exec_env;
760
  struct sieve_result *result;
761
  struct sieve_result_execution *rexec;
762
  struct event *event;
763
764
  int status;
765
  bool keep;
766
767
  struct ostream *teststream;
768
769
  bool active:1;
770
  bool discard_handled:1;
771
};
772
773
struct sieve_multiscript *
774
sieve_multiscript_start_execute(struct sieve_instance *svinst,
775
        const struct sieve_message_data *msgdata,
776
        const struct sieve_script_env *senv)
777
0
{
778
0
  pool_t pool;
779
0
  struct sieve_result *result;
780
0
  struct sieve_multiscript *mscript;
781
782
0
  pool = pool_alloconly_create("sieve execution", 4096);
783
0
  mscript = p_new(pool, struct sieve_multiscript, 1);
784
0
  mscript->pool = pool;
785
0
  sieve_execute_init(&mscript->exec_env, svinst, pool, msgdata, senv, 0);
786
787
0
  mscript->event = event_create(mscript->exec_env.event);
788
0
  event_set_append_log_prefix(mscript->event, "multi-script: ");
789
790
0
  result = sieve_result_create(svinst, pool, &mscript->exec_env);
791
0
  sieve_result_set_keep_action(result, NULL, NULL);
792
0
  mscript->result = result;
793
794
0
  mscript->rexec = sieve_result_execution_create(result, pool);
795
796
0
  mscript->status = SIEVE_EXEC_OK;
797
0
  mscript->active = TRUE;
798
0
  mscript->keep = TRUE;
799
800
0
  e_debug(mscript->event, "Start execute sequence");
801
802
0
  return mscript;
803
0
}
804
805
static void sieve_multiscript_destroy(struct sieve_multiscript **_mscript)
806
0
{
807
0
  struct sieve_multiscript *mscript = *_mscript;
808
809
0
  if (mscript == NULL)
810
0
    return;
811
0
  *_mscript = NULL;
812
813
0
  e_debug(mscript->event, "Destroy");
814
815
0
  event_unref(&mscript->event);
816
817
0
  sieve_result_execution_destroy(&mscript->rexec);
818
0
  sieve_result_unref(&mscript->result);
819
0
  sieve_execute_deinit(&mscript->exec_env);
820
0
  pool_unref(&mscript->pool);
821
0
}
822
823
struct sieve_multiscript *
824
sieve_multiscript_start_test(struct sieve_instance *svinst,
825
           const struct sieve_message_data *msgdata,
826
           const struct sieve_script_env *senv,
827
           struct ostream *stream)
828
0
{
829
0
  struct sieve_multiscript *mscript =
830
0
    sieve_multiscript_start_execute(svinst, msgdata, senv);
831
832
0
  mscript->teststream = stream;
833
834
0
  return mscript;
835
0
}
836
837
static void
838
sieve_multiscript_test(struct sieve_multiscript *mscript)
839
0
{
840
0
  const struct sieve_script_env *senv = mscript->exec_env.scriptenv;
841
842
0
  e_debug(mscript->event, "Test result");
843
844
0
  if (mscript->status > 0) {
845
0
    mscript->status =
846
0
      (sieve_result_print(mscript->result, senv,
847
0
              mscript->teststream,
848
0
              &mscript->keep) ?
849
0
       SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE);
850
0
  } else {
851
0
    mscript->keep = TRUE;
852
0
  }
853
854
0
  sieve_result_mark_executed(mscript->result);
855
0
}
856
857
static void
858
sieve_multiscript_execute(struct sieve_multiscript *mscript,
859
        struct sieve_error_handler *ehandler,
860
        enum sieve_execute_flags flags)
861
0
{
862
0
  e_debug(mscript->event, "Execute result");
863
864
0
  mscript->exec_env.flags = flags;
865
866
0
  if (mscript->status > 0) {
867
0
    mscript->status = sieve_result_execute(mscript->rexec,
868
0
                   SIEVE_EXEC_OK, FALSE,
869
0
                   ehandler,
870
0
                   &mscript->keep);
871
0
  }
872
0
}
873
874
bool sieve_multiscript_run(struct sieve_multiscript *mscript,
875
         struct sieve_binary *sbin,
876
         struct sieve_error_handler *exec_ehandler,
877
         struct sieve_error_handler *action_ehandler,
878
         enum sieve_execute_flags flags)
879
0
{
880
0
  if (!mscript->active) {
881
0
    e_debug(mscript->event, "Sequence ended");
882
0
    return FALSE;
883
0
  }
884
885
0
  e_debug(mscript->event, "Run script '%s'", sieve_binary_source(sbin));
886
887
  /* Run the script */
888
0
  mscript->exec_env.flags = flags;
889
0
  mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env,
890
0
            exec_ehandler);
891
892
0
  if (mscript->status >= 0) {
893
0
    mscript->keep = FALSE;
894
895
0
    if (mscript->teststream != NULL)
896
0
      sieve_multiscript_test(mscript);
897
0
    else {
898
0
      sieve_multiscript_execute(mscript, action_ehandler,
899
0
              flags);
900
0
    }
901
0
    if (!mscript->keep)
902
0
      mscript->active = FALSE;
903
0
  }
904
905
0
  if (!mscript->active || mscript->status <= 0) {
906
0
    e_debug(mscript->event, "Sequence ended");
907
0
    mscript->active = FALSE;
908
0
    return FALSE;
909
0
  }
910
911
0
  e_debug(mscript->event, "Sequence active");
912
0
  return TRUE;
913
0
}
914
915
bool sieve_multiscript_will_discard(struct sieve_multiscript *mscript)
916
0
{
917
0
  return (!mscript->active && mscript->status == SIEVE_EXEC_OK &&
918
0
    !sieve_result_executed_delivery(mscript->rexec));
919
0
}
920
921
void sieve_multiscript_run_discard(struct sieve_multiscript *mscript,
922
           struct sieve_binary *sbin,
923
           struct sieve_error_handler *exec_ehandler,
924
           struct sieve_error_handler *action_ehandler,
925
           enum sieve_execute_flags flags)
926
0
{
927
0
  if (!sieve_multiscript_will_discard(mscript)) {
928
0
    e_debug(mscript->event, "Not running discard script");
929
0
    return;
930
0
  }
931
0
  i_assert(!mscript->discard_handled);
932
933
0
  e_debug(mscript->event, "Run discard script '%s'",
934
0
    sieve_binary_source(sbin));
935
936
0
  sieve_result_set_keep_action(mscript->result, NULL, &act_store);
937
938
  /* Run the discard script */
939
0
  flags |= SIEVE_EXECUTE_FLAG_DEFER_KEEP;
940
0
  mscript->exec_env.flags = flags;
941
0
  mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env,
942
0
            exec_ehandler);
943
944
0
  if (mscript->status >= 0) {
945
0
    mscript->keep = FALSE;
946
947
0
    if (mscript->teststream != NULL)
948
0
      sieve_multiscript_test(mscript);
949
0
    else {
950
0
      sieve_multiscript_execute(mscript, action_ehandler,
951
0
              flags);
952
0
    }
953
0
    if (mscript->status == SIEVE_EXEC_FAILURE)
954
0
      mscript->status = SIEVE_EXEC_KEEP_FAILED;
955
0
    mscript->active = FALSE;
956
0
  }
957
958
0
  mscript->discard_handled = TRUE;
959
0
}
960
961
int sieve_multiscript_status(struct sieve_multiscript *mscript)
962
0
{
963
0
  return mscript->status;
964
0
}
965
966
int sieve_multiscript_finish(struct sieve_multiscript **_mscript,
967
           struct sieve_error_handler *action_ehandler,
968
           enum sieve_execute_flags flags, int status)
969
0
{
970
0
  struct sieve_multiscript *mscript = *_mscript;
971
972
0
  if (mscript == NULL)
973
0
    return SIEVE_EXEC_OK;
974
0
  *_mscript = NULL;
975
976
0
  switch (status) {
977
0
  case SIEVE_EXEC_OK:
978
0
    status = mscript->status;
979
0
    break;
980
0
  case SIEVE_EXEC_TEMP_FAILURE:
981
0
    break;
982
0
  case SIEVE_EXEC_BIN_CORRUPT:
983
0
  case SIEVE_EXEC_FAILURE:
984
0
  case SIEVE_EXEC_KEEP_FAILED:
985
0
  case SIEVE_EXEC_RESOURCE_LIMIT:
986
0
    if (mscript->status == SIEVE_EXEC_TEMP_FAILURE)
987
0
      status = mscript->status;
988
0
    break;
989
0
  }
990
991
0
  e_debug(mscript->event, "Finishing sequence (status=%s)",
992
0
    sieve_execution_exitcode_to_str(status));
993
994
0
  mscript->exec_env.flags = flags;
995
0
  sieve_result_set_keep_action(mscript->result, NULL, &act_store);
996
997
0
  mscript->keep = FALSE;
998
0
  if (mscript->teststream != NULL)
999
0
    mscript->keep = TRUE;
1000
0
  else {
1001
0
    status = sieve_result_execute(
1002
0
      mscript->rexec, status, TRUE, action_ehandler,
1003
0
      &mscript->keep);
1004
0
  }
1005
1006
0
  e_debug(mscript->event, "Sequence finished (status=%s, keep=%s)",
1007
0
    sieve_execution_exitcode_to_str(status),
1008
0
    (mscript->keep ? "yes" : "no"));
1009
1010
0
  sieve_execute_finish(&mscript->exec_env, status);
1011
1012
  /* Cleanup */
1013
0
  sieve_multiscript_destroy(&mscript);
1014
1015
0
  return status;
1016
0
}
1017
1018
/*
1019
 * Configured Limits
1020
 */
1021
1022
unsigned int sieve_max_redirects(struct sieve_instance *svinst)
1023
0
{
1024
0
  return svinst->set->max_redirects;
1025
0
}
1026
1027
unsigned int sieve_max_actions(struct sieve_instance *svinst)
1028
0
{
1029
0
  return svinst->set->max_actions;
1030
0
}
1031
1032
size_t sieve_max_script_size(struct sieve_instance *svinst)
1033
0
{
1034
0
  return svinst->set->max_script_size;
1035
0
}
1036
1037
/*
1038
 * Errors
1039
 */
1040
1041
#define CRITICAL_MSG \
1042
0
  "Internal error occurred. Refer to server log for more information."
1043
0
#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
1044
1045
void sieve_error_args_init(enum sieve_error **error_code_r,
1046
         const char ***error_r)
1047
0
{
1048
  /* Dummies */
1049
0
  static enum sieve_error dummy_error_code = SIEVE_ERROR_NONE;
1050
0
  static const char *dummy_error = NULL;
1051
1052
0
  if (error_code_r != NULL) {
1053
0
    if (*error_code_r == NULL)
1054
0
      *error_code_r = &dummy_error_code;
1055
0
    **error_code_r = SIEVE_ERROR_NONE;
1056
0
  }
1057
0
  if (error_r != NULL) {
1058
0
    if (*error_r == NULL)
1059
0
      *error_r = &dummy_error;
1060
0
    **error_r = NULL;
1061
0
  }
1062
0
}
1063
1064
void sieve_error_create_internal(enum sieve_error *error_code_r,
1065
         const char **error_r)
1066
0
{
1067
0
  struct tm *tm;
1068
0
  char buf[256];
1069
1070
  /* Critical errors may contain sensitive data, so let user see only
1071
     "Internal error" with a timestamp to make it easier to look from log
1072
     files the actual error message. */
1073
0
  tm = localtime(&ioloop_time);
1074
1075
0
  if (strftime(buf, sizeof(buf), CRITICAL_MSG_STAMP, tm) > 0)
1076
0
    *error_r = t_strdup(buf);
1077
0
  else
1078
0
    *error_r = CRITICAL_MSG;
1079
0
  *error_code_r = SIEVE_ERROR_TEMP_FAILURE;
1080
0
}
1081
1082
void sieve_error_create_script_not_found(const char *script_name,
1083
           enum sieve_error *error_code_r,
1084
           const char **error_r)
1085
0
{
1086
0
  *error_code_r = SIEVE_ERROR_NOT_FOUND;
1087
0
  if (script_name == NULL)
1088
0
    *error_r = "Sieve script not found";
1089
0
  else
1090
0
    *error_r = t_strdup_printf("Sieve script '%s' not found",
1091
0
             script_name);
1092
0
}
1093
1094
/*
1095
 * User log
1096
 */
1097
1098
const char *
1099
sieve_user_get_log_path(struct sieve_instance *svinst,
1100
      struct sieve_script *user_script)
1101
0
{
1102
0
  const char *log_path = (*svinst->set->user_log_path == '\0' ?
1103
0
        NULL : svinst->set->user_log_path);
1104
1105
  /* Determine user log file path */
1106
0
  if (log_path == NULL) {
1107
0
    const char *path;
1108
1109
0
    if (user_script == NULL ||
1110
0
        (path = sieve_file_script_get_path(user_script)) == NULL) {
1111
      /* Default */
1112
0
      if (svinst->home_dir != NULL) {
1113
0
        log_path = t_strconcat(
1114
0
          svinst->home_dir, "/.dovecot.sieve.log",
1115
0
          NULL);
1116
0
      }
1117
0
    } else {
1118
      /* Use script file as a base (legacy behavior) */
1119
0
      log_path = t_strconcat(path, ".log", NULL);
1120
0
    }
1121
0
  } else if (svinst->home_dir != NULL) {
1122
    /* Expand home dir if necessary */
1123
0
    if (log_path[0] == '~') {
1124
0
      log_path = home_expand_tilde(log_path,
1125
0
                 svinst->home_dir);
1126
0
    } else if (log_path[0] != '/') {
1127
0
      log_path = t_strconcat(svinst->home_dir, "/",
1128
0
                 log_path, NULL);
1129
0
    }
1130
0
  }
1131
0
  return log_path;
1132
0
}
1133
1134
/*
1135
 * Script trace log
1136
 */
1137
1138
struct sieve_trace_log {
1139
  struct sieve_instance *svinst;
1140
  struct ostream *output;
1141
};
1142
1143
int sieve_trace_log_create(struct sieve_instance *svinst, const char *path,
1144
         struct sieve_trace_log **trace_log_r)
1145
0
{
1146
0
  struct sieve_trace_log *trace_log;
1147
0
  struct ostream *output;
1148
0
  int fd;
1149
1150
0
  *trace_log_r = NULL;
1151
1152
0
  if (path == NULL)
1153
0
    output = o_stream_create_fd(1, 0);
1154
0
  else {
1155
0
    fd = open(path, O_CREAT | O_APPEND | O_WRONLY | O_NOFOLLOW, 0600);
1156
0
    if (fd == -1) {
1157
0
      e_error(svinst->event, "trace: "
1158
0
        "creat(%s) failed: %m", path);
1159
0
      return -1;
1160
0
    }
1161
0
    output = o_stream_create_fd_autoclose(&fd, 0);
1162
0
    o_stream_set_name(output, path);
1163
0
  }
1164
1165
0
  trace_log = i_new(struct sieve_trace_log, 1);
1166
0
  trace_log->svinst = svinst;
1167
0
  trace_log->output = output;
1168
1169
0
  *trace_log_r = trace_log;
1170
0
  return 0;
1171
0
}
1172
1173
int sieve_trace_log_create_dir(struct sieve_instance *svinst, const char *dir,
1174
             struct sieve_trace_log **trace_log_r)
1175
0
{
1176
0
  static unsigned int counter = 0;
1177
0
  const char *timestamp, *prefix;
1178
0
  struct stat st;
1179
1180
0
  *trace_log_r = NULL;
1181
1182
0
  if (stat(dir, &st) < 0) {
1183
0
    if (errno != ENOENT && errno != EACCES) {
1184
0
      e_error(svinst->event, "trace: "
1185
0
        "stat(%s) failed: %m", dir);
1186
0
    }
1187
0
    return -1;
1188
0
  }
1189
1190
0
  timestamp = t_strflocaltime("%Y%m%d-%H%M%S", ioloop_time);
1191
1192
0
  counter++;
1193
1194
0
  prefix = t_strdup_printf("%s/%s.%s.%u.trace",
1195
0
         dir, timestamp, my_pid, counter);
1196
0
  return sieve_trace_log_create(svinst, prefix, trace_log_r);
1197
0
}
1198
1199
int sieve_trace_log_open(struct sieve_instance *svinst,
1200
       struct sieve_trace_log **trace_log_r)
1201
0
{
1202
0
  const char *trace_dir = svinst->set->trace_dir;
1203
1204
0
  *trace_log_r = NULL;
1205
0
  if (*trace_dir == '\0')
1206
0
    return -1;
1207
1208
0
  if (svinst->home_dir != NULL) {
1209
    /* Expand home dir if necessary */
1210
0
    if (trace_dir[0] == '~') {
1211
0
      trace_dir = home_expand_tilde(trace_dir,
1212
0
                  svinst->home_dir);
1213
0
    } else if (trace_dir[0] != '/') {
1214
0
      trace_dir = t_strconcat(svinst->home_dir, "/",
1215
0
            trace_dir, NULL);
1216
0
    }
1217
0
  }
1218
1219
0
  return sieve_trace_log_create_dir(svinst, trace_dir, trace_log_r);
1220
0
}
1221
1222
void sieve_trace_log_write_line(struct sieve_trace_log *trace_log,
1223
        const string_t *line)
1224
0
{
1225
0
  struct const_iovec iov[2];
1226
1227
0
  if (line == NULL) {
1228
0
    o_stream_nsend_str(trace_log->output, "\n");
1229
0
    return;
1230
0
  }
1231
1232
0
  memset(iov, 0, sizeof(iov));
1233
0
  iov[0].iov_base = str_data(line);
1234
0
  iov[0].iov_len = str_len(line);
1235
0
  iov[1].iov_base = "\n";
1236
0
  iov[1].iov_len = 1;
1237
0
  o_stream_nsendv(trace_log->output, iov, 2);
1238
0
}
1239
1240
void sieve_trace_log_printf(struct sieve_trace_log *trace_log,
1241
          const char *fmt, ...)
1242
0
{
1243
0
  va_list args;
1244
1245
0
  va_start(args, fmt);
1246
0
  T_BEGIN {
1247
0
    o_stream_nsend_str(trace_log->output,
1248
0
           t_strdup_vprintf(fmt, args));
1249
0
  } T_END;
1250
0
  va_end(args);
1251
0
}
1252
1253
void sieve_trace_log_free(struct sieve_trace_log **_trace_log)
1254
0
{
1255
0
  struct sieve_trace_log *trace_log = *_trace_log;
1256
1257
0
  *_trace_log = NULL;
1258
1259
0
  if (o_stream_finish(trace_log->output) < 0) {
1260
0
    e_error(trace_log->svinst->event, "write(%s) failed: %s",
1261
0
      o_stream_get_name(trace_log->output),
1262
0
      o_stream_get_error(trace_log->output));
1263
0
  }
1264
0
  o_stream_destroy(&trace_log->output);
1265
0
  i_free(trace_log);
1266
0
}
1267
1268
int sieve_trace_config_get(struct sieve_instance *svinst,
1269
         struct sieve_trace_config *tr_config)
1270
0
{
1271
0
  const char *tr_level = svinst->set->trace_level;
1272
1273
0
  i_zero(tr_config);
1274
1275
0
  if (*tr_level == '\0' || strcasecmp(tr_level, "none") == 0)
1276
0
    return -1;
1277
1278
0
  if (strcasecmp(tr_level, "actions") == 0)
1279
0
    tr_config->level = SIEVE_TRLVL_ACTIONS;
1280
0
  else if (strcasecmp(tr_level, "commands") == 0)
1281
0
    tr_config->level = SIEVE_TRLVL_COMMANDS;
1282
0
  else if (strcasecmp(tr_level, "tests") == 0)
1283
0
    tr_config->level = SIEVE_TRLVL_TESTS;
1284
0
  else if (strcasecmp(tr_level, "matching") == 0)
1285
0
    tr_config->level = SIEVE_TRLVL_MATCHING;
1286
0
  else {
1287
0
    e_error(svinst->event, "Unknown trace level: %s", tr_level);
1288
0
    return -1;
1289
0
  }
1290
1291
0
  if (svinst->set->trace_debug)
1292
0
    tr_config->flags |= SIEVE_TRFLG_DEBUG;
1293
0
  if (svinst->set->trace_addresses)
1294
0
    tr_config->flags |= SIEVE_TRFLG_ADDRESSES;
1295
0
  return 0;
1296
0
}
1297
1298
/*
1299
 * Execution exit codes
1300
 */
1301
1302
const char *sieve_execution_exitcode_to_str(int code)
1303
0
{
1304
0
  switch (code) {
1305
0
  case SIEVE_EXEC_OK:
1306
0
    return "ok";
1307
0
  case SIEVE_EXEC_FAILURE:
1308
0
    return "failure";
1309
0
  case SIEVE_EXEC_TEMP_FAILURE:
1310
0
    return "temporary_failure";
1311
0
  case SIEVE_EXEC_BIN_CORRUPT:
1312
0
    return "binary_corrupt";
1313
0
  case SIEVE_EXEC_KEEP_FAILED:
1314
0
    return "keep_failed";
1315
0
  case SIEVE_EXEC_RESOURCE_LIMIT:
1316
0
    return "resource_limit";
1317
0
  }
1318
0
  i_unreached();
1319
0
}
1320
1321
/*
1322
 * User e-mail address
1323
 */
1324
1325
const struct smtp_address *sieve_get_user_email(struct sieve_instance *svinst)
1326
0
{
1327
0
  struct smtp_address *address;
1328
0
  const char *username = svinst->username;
1329
1330
0
  if (svinst->user_email_implicit != NULL)
1331
0
    return svinst->user_email_implicit;
1332
0
  if (svinst->set->parsed.user_email != NULL)
1333
0
    return svinst->set->parsed.user_email;
1334
1335
0
  if (smtp_address_parse_mailbox(svinst->pool, username, 0,
1336
0
               &address, NULL) >= 0) {
1337
0
    svinst->user_email_implicit = address;
1338
0
    return svinst->user_email_implicit;
1339
0
  }
1340
1341
0
  if (svinst->domainname != NULL) {
1342
0
    svinst->user_email_implicit = smtp_address_create(
1343
0
      svinst->pool, username, svinst->domainname);
1344
0
    return svinst->user_email_implicit;
1345
0
  }
1346
0
  return NULL;
1347
0
}
1348
1349
/*
1350
 * Postmaster address
1351
 */
1352
1353
const struct message_address *
1354
sieve_get_postmaster(const struct sieve_script_env *senv)
1355
0
{
1356
0
  i_assert(senv->postmaster_address != NULL);
1357
0
  return senv->postmaster_address;
1358
0
}
1359
1360
const struct smtp_address *
1361
sieve_get_postmaster_smtp(const struct sieve_script_env *senv)
1362
0
{
1363
0
  struct smtp_address *addr;
1364
0
  int ret;
1365
1366
0
  ret = smtp_address_create_from_msg_temp(
1367
0
    sieve_get_postmaster(senv), &addr);
1368
0
  i_assert(ret >= 0);
1369
0
  return addr;
1370
0
}
1371
1372
const char *sieve_get_postmaster_address(const struct sieve_script_env *senv)
1373
0
{
1374
0
  const struct message_address *postmaster =
1375
0
    sieve_get_postmaster(senv);
1376
0
  string_t *addr = t_str_new(256);
1377
1378
0
  message_address_write(addr, postmaster);
1379
0
  return str_c(addr);
1380
0
}
1381
1382
/*
1383
 * Resource usage
1384
 */
1385
1386
void sieve_resource_usage_init(struct sieve_resource_usage *rusage_r)
1387
0
{
1388
0
  i_zero(rusage_r);
1389
0
}
1390
1391
void sieve_resource_usage_add(struct sieve_resource_usage *dst,
1392
            const struct sieve_resource_usage *src)
1393
0
{
1394
0
  if ((UINT_MAX - dst->cpu_time_msecs) < src->cpu_time_msecs)
1395
0
    dst->cpu_time_msecs = UINT_MAX;
1396
0
  else
1397
0
    dst->cpu_time_msecs += src->cpu_time_msecs;
1398
0
}
1399
1400
bool sieve_resource_usage_is_high(struct sieve_instance *svinst ATTR_UNUSED,
1401
          const struct sieve_resource_usage *rusage)
1402
0
{
1403
0
  return (rusage->cpu_time_msecs > SIEVE_HIGH_CPU_TIME_MSECS);
1404
0
}
1405
1406
bool sieve_resource_usage_is_excessive(
1407
  struct sieve_instance *svinst,
1408
  const struct sieve_resource_usage *rusage)
1409
0
{
1410
0
  i_assert(svinst->set->max_cpu_time <= (UINT_MAX / 1000));
1411
0
  if (svinst->set->max_cpu_time == 0)
1412
0
    return FALSE;
1413
0
  return (rusage->cpu_time_msecs >
1414
0
    (svinst->set->max_cpu_time * 1000));
1415
0
}
1416
1417
const char *
1418
sieve_resource_usage_get_summary(const struct sieve_resource_usage *rusage)
1419
0
{
1420
0
  if (rusage->cpu_time_msecs == 0)
1421
0
    return "no usage recorded";
1422
1423
0
  return t_strdup_printf("cpu time = %u ms", rusage->cpu_time_msecs);
1424
0
}