Coverage Report

Created: 2026-10-28 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-error.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 "array.h"
6
#include "ostream.h"
7
#include "var-expand.h"
8
#include "eacces-error.h"
9
10
#include "sieve-common.h"
11
#include "sieve-script.h"
12
#include "sieve-error-private.h"
13
14
#include <sys/types.h>
15
#include <sys/stat.h>
16
#include <fcntl.h>
17
#include <unistd.h>
18
#include <stdio.h>
19
#include <ctype.h>
20
21
/*
22
 * Definitions
23
 */
24
25
#define CRITICAL_MSG \
26
0
  "internal error occurred: refer to server log for more information."
27
0
#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
28
29
/* Logfile error handler will rotate log when it exceeds 10k bytes */
30
0
#define LOGFILE_MAX_SIZE (10 * 1024)
31
32
/*
33
 * Utility
34
 */
35
36
const char *
37
sieve_error_script_location(const struct sieve_script *script,
38
          unsigned int source_line)
39
0
{
40
0
  const char *sname;
41
42
0
  sname = (script == NULL ? NULL : sieve_script_name(script));
43
44
0
  if (sname == NULL || *sname == '\0') {
45
0
    if (source_line == 0)
46
0
      return NULL;
47
48
0
    return t_strdup_printf("line %d", source_line);
49
0
  }
50
51
0
  if (source_line == 0)
52
0
    return sname;
53
54
0
  return t_strdup_printf("%s: line %d", sname, source_line);
55
0
}
56
57
const char *sieve_error_from_external(const char *msg)
58
0
{
59
0
  char *new_msg;
60
61
0
  if (msg == NULL || *msg == '\0')
62
0
    return msg;
63
64
0
  new_msg = t_strdup_noconst(msg);
65
0
  new_msg[0] = i_tolower(new_msg[0]);
66
67
0
  return new_msg;
68
0
}
69
70
/*
71
 * Initialization
72
 */
73
74
void sieve_errors_init(struct sieve_instance *svinst ATTR_UNUSED)
75
0
{
76
  /* nothing */
77
0
}
78
79
void sieve_errors_deinit(struct sieve_instance *svinst ATTR_UNUSED)
80
0
{
81
  /* nothing */
82
0
}
83
84
/*
85
 * Direct handler calls
86
 */
87
88
static void
89
sieve_direct_master_log(struct sieve_instance *svinst,
90
      const struct sieve_error_params *params,
91
      const char *message)
92
0
{
93
0
  struct event_log_params event_params = {
94
0
    .log_type = params->log_type,
95
0
    .source_filename = params->csrc.filename,
96
0
    .source_linenum = params->csrc.linenum,
97
98
0
    .base_event = svinst->event,
99
0
  };
100
0
  struct event *event = (params->event != NULL ?
101
0
             params->event : svinst->event);
102
103
0
  if (params->location != NULL && *params->location != '\0') {
104
0
    event_params.base_send_prefix =
105
0
       t_strconcat(params->location, ": ", NULL);
106
0
  }
107
108
0
  event_log(event, &event_params, "%s", message);
109
0
}
110
111
void sieve_direct_logv(struct sieve_instance *svinst,
112
           struct sieve_error_handler *ehandler,
113
           const struct sieve_error_params *params,
114
           enum sieve_error_flags flags,
115
           const char *fmt, va_list args)
116
0
{
117
0
  struct event_log_params event_params = {
118
0
    .log_type = params->log_type,
119
0
    .source_filename = params->csrc.filename,
120
0
    .source_linenum = params->csrc.linenum,
121
0
    .base_event = svinst->event,
122
0
    .base_str_out = NULL,
123
0
    .no_send = TRUE,
124
0
  };
125
0
  struct event *event = (params->event != NULL ?
126
0
             params->event : svinst->event);
127
0
  bool event_log = FALSE, ehandler_log = FALSE;
128
129
0
  if (ehandler != NULL) {
130
0
    switch (params->log_type) {
131
0
    case LOG_TYPE_ERROR:
132
0
      ehandler_log = sieve_errors_more_allowed(ehandler);
133
0
      break;
134
0
    case LOG_TYPE_WARNING:
135
0
      ehandler_log = TRUE;
136
0
      break;
137
0
    case LOG_TYPE_INFO:
138
0
      ehandler_log = ehandler->log_info;
139
0
      break;
140
0
    case LOG_TYPE_DEBUG:
141
0
      ehandler_log = ehandler->log_debug;
142
0
      break;
143
0
    case LOG_TYPE_FATAL:
144
0
    case LOG_TYPE_PANIC:
145
0
    case LOG_TYPE_COUNT:
146
0
    case LOG_TYPE_OPTION:
147
0
      i_unreached();
148
0
    }
149
0
  }
150
151
0
  if (ehandler != NULL && ehandler->master_log) {
152
0
    event_log = ehandler_log;
153
0
    ehandler_log = FALSE;
154
0
  }
155
0
  if ((flags & SIEVE_ERROR_FLAG_GLOBAL) != 0) {
156
0
    event_log = TRUE;
157
0
    if ((flags & SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO) != 0 &&
158
0
        params->log_type > LOG_TYPE_INFO)
159
0
      event_params.log_type = LOG_TYPE_INFO;
160
0
  }
161
162
0
  if (event_log) {
163
0
    event_params.no_send = FALSE;
164
0
    if (params->location != NULL && *params->location != '\0') {
165
0
      event_params.base_send_prefix =
166
0
        t_strconcat(params->location, ": ", NULL);
167
0
    }
168
0
  }
169
0
  if (ehandler_log) {
170
0
    if (ehandler->log == NULL)
171
0
      ehandler_log = FALSE;
172
0
    else
173
0
      event_params.base_str_out = t_str_new(128);
174
0
  }
175
176
0
  if (event_log || ehandler_log)
177
0
    event_logv(event, &event_params, fmt, args);
178
179
0
  if (ehandler_log) {
180
0
    ehandler->log(ehandler, params, flags,
181
0
            str_c(event_params.base_str_out));
182
0
  }
183
184
0
  if (ehandler != NULL && ehandler->pool != NULL) {
185
0
    switch (params->log_type) {
186
0
    case LOG_TYPE_ERROR:
187
0
      ehandler->errors++;
188
0
      break;
189
0
    case LOG_TYPE_WARNING:
190
0
      ehandler->warnings++;
191
0
      break;
192
0
    default:
193
0
      break;
194
0
    }
195
0
  }
196
0
}
197
198
/*
199
 * User errors
200
 */
201
202
void sieve_global_logv(struct sieve_instance *svinst,
203
           struct sieve_error_handler *ehandler,
204
           const struct sieve_error_params *params,
205
           const char *fmt, va_list args)
206
0
{
207
0
  sieve_direct_logv(svinst, ehandler, params,
208
0
        SIEVE_ERROR_FLAG_GLOBAL, fmt, args);
209
0
}
210
211
void sieve_global_info_logv(struct sieve_instance *svinst,
212
          struct sieve_error_handler *ehandler,
213
          const struct sieve_error_params *params,
214
          const char *fmt, va_list args)
215
0
{
216
0
  sieve_direct_logv(svinst, ehandler, params,
217
0
        (SIEVE_ERROR_FLAG_GLOBAL |
218
0
         SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO), fmt, args);
219
0
}
220
221
#undef sieve_global_error
222
void sieve_global_error(struct sieve_instance *svinst,
223
      struct sieve_error_handler *ehandler,
224
      const char *csrc_filename, unsigned int csrc_linenum,
225
      const char *location, const char *fmt, ...)
226
0
{
227
0
  struct sieve_error_params params = {
228
0
    .log_type = LOG_TYPE_ERROR,
229
0
    .csrc = {
230
0
      .filename = csrc_filename,
231
0
      .linenum = csrc_linenum,
232
0
    },
233
0
    .location = location,
234
0
  };
235
0
  va_list args;
236
0
  va_start(args, fmt);
237
238
0
  T_BEGIN {
239
0
    sieve_global_logv(svinst, ehandler, &params, fmt, args);
240
0
  } T_END;
241
242
0
  va_end(args);
243
0
}
244
245
#undef sieve_global_warning
246
void sieve_global_warning(struct sieve_instance *svinst,
247
        struct sieve_error_handler *ehandler,
248
        const char *csrc_filename, unsigned int csrc_linenum,
249
        const char *location, const char *fmt, ...)
250
0
{
251
0
  struct sieve_error_params params = {
252
0
    .log_type = LOG_TYPE_WARNING,
253
0
    .csrc = {
254
0
      .filename = csrc_filename,
255
0
      .linenum = csrc_linenum,
256
0
    },
257
0
    .location = location,
258
0
  };
259
0
  va_list args;
260
0
  va_start(args, fmt);
261
262
0
  T_BEGIN {
263
0
    sieve_global_logv(svinst, ehandler, &params, fmt, args);
264
0
  } T_END;
265
266
0
  va_end(args);
267
0
}
268
269
#undef sieve_global_info
270
void sieve_global_info(struct sieve_instance *svinst,
271
           struct sieve_error_handler *ehandler,
272
           const char *csrc_filename, unsigned int csrc_linenum,
273
           const char *location, const char *fmt, ...)
274
0
{
275
0
  struct sieve_error_params params = {
276
0
    .log_type = LOG_TYPE_INFO,
277
0
    .csrc = {
278
0
      .filename = csrc_filename,
279
0
      .linenum = csrc_linenum,
280
0
    },
281
0
    .location = location,
282
0
  };
283
0
  va_list args;
284
0
  va_start(args, fmt);
285
286
0
  T_BEGIN {
287
0
    sieve_global_logv(svinst, ehandler, &params, fmt, args);
288
0
  } T_END;
289
290
0
  va_end(args);
291
0
}
292
293
#undef sieve_global_info_error
294
void sieve_global_info_error(struct sieve_instance *svinst,
295
           struct sieve_error_handler *ehandler,
296
           const char *csrc_filename,
297
           unsigned int csrc_linenum,
298
           const char *location, const char *fmt, ...)
299
0
{
300
0
  struct sieve_error_params params = {
301
0
    .log_type = LOG_TYPE_ERROR,
302
0
    .csrc = {
303
0
      .filename = csrc_filename,
304
0
      .linenum = csrc_linenum,
305
0
    },
306
0
    .location = location,
307
0
  };
308
0
  va_list args;
309
0
  va_start(args, fmt);
310
311
0
  T_BEGIN {
312
0
    sieve_global_info_logv(svinst, ehandler, &params, fmt, args);
313
0
  } T_END;
314
315
0
  va_end(args);
316
0
}
317
318
#undef sieve_global_info_warning
319
void sieve_global_info_warning(struct sieve_instance *svinst,
320
             struct sieve_error_handler *ehandler,
321
             const char *csrc_filename,
322
             unsigned int csrc_linenum,
323
             const char *location, const char *fmt, ...)
324
0
{
325
0
  struct sieve_error_params params = {
326
0
    .log_type = LOG_TYPE_WARNING,
327
0
    .csrc = {
328
0
      .filename = csrc_filename,
329
0
      .linenum = csrc_linenum,
330
0
    },
331
0
    .location = location,
332
0
  };
333
0
  va_list args;
334
0
  va_start(args, fmt);
335
336
0
  T_BEGIN {
337
0
    sieve_global_info_logv(svinst, ehandler, &params, fmt, args);
338
0
  } T_END;
339
340
0
  va_end(args);
341
0
}
342
343
/*
344
 * Default (user) error functions
345
 */
346
347
void sieve_internal_error_params(struct sieve_error_handler *ehandler,
348
         const struct sieve_error_params *params,
349
         const char *user_prefix)
350
0
{
351
0
  char str[256];
352
0
  const char *msg;
353
0
  struct tm *tm;
354
355
0
  if (ehandler == NULL || ehandler->master_log)
356
0
    return;
357
358
0
  tm = localtime(&ioloop_time);
359
0
  msg = (strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
360
0
         str : CRITICAL_MSG);
361
362
0
  if (user_prefix == NULL || *user_prefix == '\0') {
363
0
    sieve_direct_log(ehandler->svinst, ehandler, params, 0,
364
0
         "%s", msg);
365
0
  } else {
366
0
    sieve_direct_log(ehandler->svinst, ehandler, params, 0,
367
0
         "%s: %s", user_prefix, msg);
368
0
  }
369
0
}
370
371
#undef sieve_internal_error
372
void sieve_internal_error(struct sieve_error_handler *ehandler,
373
        const char *csrc_filename, unsigned int csrc_linenum,
374
        const char *location, const char *user_prefix)
375
376
0
{
377
0
  struct sieve_error_params params = {
378
0
    .log_type = LOG_TYPE_ERROR,
379
0
    .csrc = {
380
0
      .filename = csrc_filename,
381
0
      .linenum = csrc_linenum,
382
0
    },
383
0
    .location = location,
384
0
  };
385
386
0
  sieve_internal_error_params(ehandler, &params, user_prefix);
387
0
}
388
389
void sieve_logv(struct sieve_error_handler *ehandler,
390
    const struct sieve_error_params *params,
391
    const char *fmt, va_list args)
392
0
{
393
0
  if (ehandler == NULL) return;
394
395
0
  sieve_direct_logv(ehandler->svinst, ehandler, params, 0, fmt, args);
396
0
}
397
398
void sieve_event_logv(struct sieve_instance *svinst,
399
          struct sieve_error_handler *ehandler,
400
          struct event *event,  enum log_type log_type,
401
          const char *csrc_filename, unsigned int csrc_linenum,
402
          const char *location, enum sieve_error_flags flags,
403
          const char *fmt, va_list args)
404
0
{
405
0
  struct sieve_error_params params = {
406
0
    .log_type = log_type,
407
0
    .csrc = {
408
0
      .filename = csrc_filename,
409
0
      .linenum = csrc_linenum,
410
0
    },
411
0
    .event = event,
412
0
    .location = location,
413
0
  };
414
415
0
  T_BEGIN {
416
0
    sieve_direct_logv(svinst, ehandler, &params, flags, fmt, args);
417
0
  } T_END;
418
0
}
419
420
421
#undef sieve_event_log
422
void sieve_event_log(struct sieve_instance *svinst,
423
         struct sieve_error_handler *ehandler,
424
         struct event *event,  enum log_type log_type,
425
         const char *csrc_filename, unsigned int csrc_linenum,
426
         const char *location, enum sieve_error_flags flags,
427
         const char *fmt, ...)
428
0
{
429
0
  va_list args;
430
0
  va_start(args, fmt);
431
432
0
  sieve_event_logv(svinst, ehandler, event, log_type, csrc_filename,
433
0
       csrc_linenum, location, flags, fmt, args);
434
435
0
  va_end(args);
436
0
}
437
438
void sieve_criticalv(struct sieve_instance *svinst,
439
         struct sieve_error_handler *ehandler,
440
         const struct sieve_error_params *params,
441
         const char *user_prefix, const char *fmt, va_list args)
442
0
{
443
0
  struct sieve_error_params new_params = *params;
444
445
0
  new_params.log_type = LOG_TYPE_ERROR;
446
447
0
  sieve_direct_master_log(svinst, &new_params,
448
0
        t_strdup_vprintf(fmt, args));
449
0
  sieve_internal_error_params(ehandler, &new_params, user_prefix);
450
0
}
451
452
#undef sieve_error
453
void sieve_error(struct sieve_error_handler *ehandler,
454
     const char *csrc_filename, unsigned int csrc_linenum,
455
     const char *location, const char *fmt, ...)
456
0
{
457
0
  struct sieve_error_params params = {
458
0
    .log_type = LOG_TYPE_ERROR,
459
0
    .csrc = {
460
0
      .filename = csrc_filename,
461
0
      .linenum = csrc_linenum,
462
0
    },
463
0
    .location = location,
464
0
  };
465
0
  va_list args;
466
0
  va_start(args, fmt);
467
468
0
  T_BEGIN {
469
0
    sieve_logv(ehandler, &params, fmt, args);
470
0
  } T_END;
471
472
0
  va_end(args);
473
0
}
474
475
#undef sieve_warning
476
void sieve_warning(struct sieve_error_handler *ehandler,
477
       const char *csrc_filename, unsigned int csrc_linenum,
478
       const char *location, const char *fmt, ...)
479
0
{
480
0
  struct sieve_error_params params = {
481
0
    .log_type = LOG_TYPE_WARNING,
482
0
    .csrc = {
483
0
      .filename = csrc_filename,
484
0
      .linenum = csrc_linenum,
485
0
    },
486
0
    .location = location,
487
0
  };
488
0
  va_list args;
489
0
  va_start(args, fmt);
490
491
0
  T_BEGIN {
492
0
    sieve_logv(ehandler, &params, fmt, args);
493
0
  } T_END;
494
495
0
  va_end(args);
496
0
}
497
498
#undef sieve_info
499
void sieve_info(struct sieve_error_handler *ehandler,
500
    const char *csrc_filename, unsigned int csrc_linenum,
501
    const char *location, const char *fmt, ...)
502
0
{
503
0
  struct sieve_error_params params = {
504
0
    .log_type = LOG_TYPE_INFO,
505
0
    .csrc = {
506
0
      .filename = csrc_filename,
507
0
      .linenum = csrc_linenum,
508
0
    },
509
0
    .location = location,
510
0
  };
511
0
  va_list args;
512
0
  va_start(args, fmt);
513
514
0
  T_BEGIN {
515
0
    sieve_logv(ehandler, &params, fmt, args);
516
0
  } T_END;
517
518
0
  va_end(args);
519
0
}
520
521
#undef sieve_debug
522
void sieve_debug(struct sieve_error_handler *ehandler,
523
     const char *csrc_filename, unsigned int csrc_linenum,
524
     const char *location, const char *fmt, ...)
525
0
{
526
0
  struct sieve_error_params params = {
527
0
    .log_type = LOG_TYPE_DEBUG,
528
0
    .csrc = {
529
0
      .filename = csrc_filename,
530
0
      .linenum = csrc_linenum,
531
0
    },
532
0
    .location = location,
533
0
  };
534
0
  va_list args;
535
0
  va_start(args, fmt);
536
537
0
  T_BEGIN {
538
0
    sieve_logv(ehandler, &params, fmt, args);
539
0
  } T_END;
540
541
0
  va_end(args);
542
0
}
543
544
#undef sieve_critical
545
void sieve_critical(struct sieve_instance *svinst,
546
        struct sieve_error_handler *ehandler,
547
        const char *csrc_filename, unsigned int csrc_linenum,
548
        const char *location, const char *user_prefix,
549
        const char *fmt, ...)
550
0
{
551
0
  struct sieve_error_params params = {
552
0
    .log_type = LOG_TYPE_ERROR,
553
0
    .csrc = {
554
0
      .filename = csrc_filename,
555
0
      .linenum = csrc_linenum,
556
0
    },
557
0
    .location = location,
558
0
  };
559
0
  va_list args;
560
561
0
  va_start(args, fmt);
562
563
0
  T_BEGIN {
564
0
    sieve_criticalv(svinst, ehandler, &params, user_prefix,
565
0
        fmt, args);
566
0
  } T_END;
567
568
0
  va_end(args);
569
0
}
570
571
/*
572
 * Error statistics
573
 */
574
575
unsigned int sieve_get_errors(struct sieve_error_handler *ehandler)
576
0
{
577
0
  if (ehandler == NULL || ehandler->pool == NULL)
578
0
    return 0;
579
580
0
  return ehandler->errors;
581
0
}
582
583
unsigned int sieve_get_warnings(struct sieve_error_handler *ehandler)
584
0
{
585
0
  if (ehandler == NULL || ehandler->pool == NULL)
586
0
    return 0;
587
588
0
  return ehandler->warnings;
589
0
}
590
591
bool sieve_errors_more_allowed(struct sieve_error_handler *ehandler)
592
0
{
593
0
  if (ehandler == NULL || ehandler->pool == NULL)
594
0
    return TRUE;
595
596
0
  return (ehandler->max_errors == 0 ||
597
0
    ehandler->errors < ehandler->max_errors);
598
0
}
599
600
/*
601
 * Error handler configuration
602
 */
603
604
void sieve_error_handler_accept_infolog(struct sieve_error_handler *ehandler,
605
          bool enable)
606
0
{
607
0
  ehandler->log_info = enable;
608
0
}
609
610
void sieve_error_handler_accept_debuglog(struct sieve_error_handler *ehandler,
611
           bool enable)
612
0
{
613
0
  ehandler->log_debug = enable;
614
0
}
615
616
/*
617
 * Error handler init
618
 */
619
620
void sieve_error_handler_init(struct sieve_error_handler *ehandler,
621
            struct sieve_instance *svinst,
622
            pool_t pool, unsigned int max_errors)
623
0
{
624
0
  ehandler->pool = pool;
625
0
  ehandler->svinst = svinst;
626
0
  ehandler->refcount = 1;
627
0
  ehandler->max_errors = max_errors;
628
629
0
  ehandler->errors = 0;
630
0
  ehandler->warnings = 0;
631
0
}
632
633
void sieve_error_handler_ref(struct sieve_error_handler *ehandler)
634
0
{
635
0
  if (ehandler == NULL || ehandler->pool == NULL)
636
0
    return;
637
638
0
  ehandler->refcount++;
639
0
}
640
641
void sieve_error_handler_unref(struct sieve_error_handler **ehandler)
642
0
{
643
0
  if (*ehandler == NULL || (*ehandler)->pool == NULL)
644
0
    return;
645
646
0
  i_assert((*ehandler)->refcount > 0);
647
648
0
  if (--(*ehandler)->refcount != 0)
649
0
          return;
650
651
0
  if ((*ehandler)->free != NULL)
652
0
    (*ehandler)->free(*ehandler);
653
654
0
  pool_unref(&((*ehandler)->pool));
655
0
  *ehandler = NULL;
656
0
}
657
658
void sieve_error_handler_reset(struct sieve_error_handler *ehandler)
659
0
{
660
0
  if (ehandler == NULL || ehandler->pool == NULL)
661
0
    return;
662
663
0
  ehandler->errors = 0;
664
0
  ehandler->warnings = 0;
665
0
}
666
667
/*
668
 * Error params utility
669
 */
670
671
static void
672
sieve_error_params_add_prefix(struct sieve_error_handler *ehandler ATTR_UNUSED,
673
            const struct sieve_error_params *params,
674
            string_t *prefix)
675
0
{
676
0
  if (params->location != NULL && *params->location != '\0') {
677
0
    str_append(prefix, params->location);
678
0
    str_append(prefix, ": ");
679
0
  }
680
681
0
  switch (params->log_type) {
682
0
  case LOG_TYPE_ERROR:
683
0
    str_append(prefix, "error: ");
684
0
    break;
685
0
  case LOG_TYPE_WARNING:
686
0
    str_append(prefix, "warning: ");
687
0
    break;
688
0
  case LOG_TYPE_INFO:
689
0
    str_append(prefix, "info: ");
690
0
    break;
691
0
  case LOG_TYPE_DEBUG:
692
0
    str_append(prefix, "debug: ");
693
0
    break;
694
0
  default:
695
0
    i_unreached();
696
0
  }
697
0
}
698
699
/*
700
 * Master/System error handler
701
 *
702
 * - Output errors directly to Dovecot master log
703
 */
704
705
struct sieve_error_handler *
706
sieve_master_ehandler_create(struct sieve_instance *svinst,
707
           unsigned int max_errors)
708
0
{
709
0
  struct sieve_error_handler *ehandler;
710
0
  pool_t pool;
711
712
0
  pool = pool_alloconly_create("master_error_handler", 256);
713
0
  ehandler = p_new(pool, struct sieve_error_handler, 1);
714
0
  sieve_error_handler_init(ehandler, svinst, pool, max_errors);
715
0
  ehandler->master_log = TRUE;
716
0
  ehandler->log_debug = svinst->debug;
717
718
0
  return ehandler;
719
0
}
720
721
/*
722
 * STDERR error handler
723
 *
724
 * - Output errors directly to stderror
725
 */
726
727
static void
728
sieve_stderr_log(struct sieve_error_handler *ehandler,
729
     const struct sieve_error_params *params,
730
     enum sieve_error_flags flags ATTR_UNUSED,
731
     const char *message)
732
0
{
733
0
  string_t *prefix = t_str_new(64);
734
735
0
  sieve_error_params_add_prefix(ehandler, params, prefix);
736
737
0
  fprintf(stderr, "%s%s.\n", str_c(prefix), message);
738
0
}
739
740
struct sieve_error_handler *
741
sieve_stderr_ehandler_create(struct sieve_instance *svinst,
742
           unsigned int max_errors)
743
0
{
744
0
  pool_t pool;
745
0
  struct sieve_error_handler *ehandler;
746
747
  /* Pool is not strictly necessary, but other handler types will need
748
   * a pool, so this one will have one too.
749
   */
750
0
  pool = pool_alloconly_create("stderr_error_handler",
751
0
             sizeof(struct sieve_error_handler));
752
0
  ehandler = p_new(pool, struct sieve_error_handler, 1);
753
0
  sieve_error_handler_init(ehandler, svinst, pool, max_errors);
754
755
0
  ehandler->log = sieve_stderr_log;
756
757
0
  return ehandler;
758
0
}
759
760
/* String buffer error handler
761
 *
762
 * - Output errors to a string buffer
763
 */
764
765
struct sieve_strbuf_ehandler {
766
  struct sieve_error_handler handler;
767
768
  string_t *errors;
769
  bool crlf;
770
};
771
772
static void
773
sieve_strbuf_log(struct sieve_error_handler *ehandler,
774
     const struct sieve_error_params *params,
775
     enum sieve_error_flags flags ATTR_UNUSED, const char *message)
776
0
{
777
0
  struct sieve_strbuf_ehandler *handler =
778
0
    (struct sieve_strbuf_ehandler *) ehandler;
779
780
0
  sieve_error_params_add_prefix(ehandler, params, handler->errors);
781
0
  str_append(handler->errors, message);
782
783
0
  if (!handler->crlf)
784
0
    str_append(handler->errors, ".\n");
785
0
  else
786
0
    str_append(handler->errors, ".\r\n");
787
0
}
788
789
struct sieve_error_handler *
790
sieve_strbuf_ehandler_create(struct sieve_instance *svinst, string_t *strbuf,
791
           bool crlf, unsigned int max_errors)
792
0
{
793
0
  pool_t pool;
794
0
  struct sieve_strbuf_ehandler *ehandler;
795
796
0
  pool = pool_alloconly_create("strbuf_error_handler", 256);
797
0
  ehandler = p_new(pool, struct sieve_strbuf_ehandler, 1);
798
0
  ehandler->errors = strbuf;
799
800
0
  sieve_error_handler_init(&ehandler->handler, svinst, pool, max_errors);
801
802
0
  ehandler->handler.log = sieve_strbuf_log;
803
0
  ehandler->crlf = crlf;
804
805
0
  return &(ehandler->handler);
806
0
}
807
808
/*
809
 * Logfile error handler
810
 *
811
 * - Output errors to a log file
812
 */
813
814
struct sieve_logfile_ehandler {
815
  struct sieve_error_handler handler;
816
817
  const char *logfile;
818
  bool started;
819
  int fd;
820
  struct ostream *stream;
821
};
822
823
static void
824
sieve_logfile_write(struct sieve_logfile_ehandler *ehandler,
825
        const struct sieve_error_params *params,
826
        const char *message)
827
0
{
828
0
  string_t *outbuf;
829
0
  ssize_t ret = 0, remain;
830
0
  const char *data;
831
832
0
  if (ehandler->stream == NULL)
833
0
    return;
834
835
0
  T_BEGIN {
836
0
    outbuf = t_str_new(256);
837
0
    sieve_error_params_add_prefix(&ehandler->handler,
838
0
                params, outbuf);
839
0
    str_append(outbuf, message);
840
0
    str_append(outbuf, ".\n");
841
842
0
    remain = str_len(outbuf);
843
0
    data = (const char *) str_data(outbuf);
844
845
0
    while (remain > 0) {
846
0
      if ((ret = o_stream_send(ehandler->stream,
847
0
             data, remain)) < 0)
848
0
        break;
849
850
0
      remain -= ret;
851
0
      data += ret;
852
0
    }
853
0
  } T_END;
854
855
0
  if (ret < 0) {
856
0
    e_error(ehandler->handler.svinst->event,
857
0
      "o_stream_send() failed on logfile %s: %m",
858
0
      ehandler->logfile);
859
0
  }
860
0
}
861
862
inline static void ATTR_FORMAT(5, 6)
863
sieve_logfile_printf(struct sieve_logfile_ehandler *ehandler,
864
         const char *csrc_filename, unsigned int csrc_linenum,
865
         const char *location, const char *fmt, ...)
866
0
{
867
0
  struct sieve_error_params params = {
868
0
    .log_type = LOG_TYPE_INFO,
869
0
    .csrc = {
870
0
      .filename = csrc_filename,
871
0
      .linenum = csrc_linenum,
872
0
    },
873
0
    .location = location,
874
0
  };
875
0
  va_list args;
876
0
  va_start(args, fmt);
877
878
0
  sieve_logfile_write(ehandler, &params, t_strdup_vprintf(fmt, args));
879
880
0
  va_end(args);
881
0
}
882
883
static void sieve_logfile_start(struct sieve_logfile_ehandler *ehandler)
884
0
{
885
0
  struct sieve_instance *svinst = ehandler->handler.svinst;
886
0
  struct ostream *ostream = NULL;
887
0
  struct stat st;
888
0
  struct tm *tm;
889
0
  char buf[256];
890
0
  time_t now;
891
0
  int fd;
892
893
  /* Open the logfile */
894
895
0
  fd = open(ehandler->logfile, O_CREAT | O_APPEND | O_WRONLY | O_NOFOLLOW, 0600);
896
0
  if (fd == -1) {
897
0
    if (errno == EACCES) {
898
0
      e_error(svinst->event,
899
0
        "failed to open logfile "
900
0
        "(LOGGING TO STDERR): %s",
901
0
        eacces_error_get_creating("open",
902
0
                ehandler->logfile));
903
0
    } else {
904
0
      e_error(svinst->event, "failed to open logfile "
905
0
        "(LOGGING TO STDERR): "
906
0
        "open(%s) failed: %m", ehandler->logfile);
907
0
    }
908
0
    fd = STDERR_FILENO;
909
0
  } else {
910
    /* fd_close_on_exec(fd, TRUE); Necessary? */
911
912
    /* Stat the log file to obtain size information */
913
0
    if (fstat(fd, &st) != 0) {
914
0
      e_error(svinst->event, "failed to stat logfile "
915
0
        "(logging to STDERR): "
916
0
        "fstat(fd=%s) failed: %m", ehandler->logfile);
917
918
0
      if (close(fd) < 0) {
919
0
        e_error(svinst->event,
920
0
          "failed to close logfile after error: "
921
0
          "close(fd=%s) failed: %m",
922
0
          ehandler->logfile);
923
0
      }
924
925
0
      fd = STDERR_FILENO;
926
0
    }
927
928
    /* Rotate log when it has grown too large */
929
0
    if (st.st_size >= LOGFILE_MAX_SIZE) {
930
0
      const char *rotated;
931
932
      /* Close open file */
933
0
      if (close(fd) < 0) {
934
0
        e_error(svinst->event,
935
0
          "failed to close logfile: "
936
0
          "close(fd=%s) failed: %m",
937
0
          ehandler->logfile);
938
0
      }
939
940
      /* Rotate logfile */
941
0
      rotated = t_strconcat(ehandler->logfile, ".0", NULL);
942
0
      if (rename(ehandler->logfile, rotated) < 0 &&
943
0
          errno != ENOENT) {
944
0
        if (errno == EACCES) {
945
0
          const char *target =
946
0
            t_strconcat(ehandler->logfile,
947
0
                  ", ", rotated, NULL);
948
0
          e_error(svinst->event,
949
0
            "failed to rotate logfile: %s",
950
0
            eacces_error_get_creating(
951
0
              "rename", target));
952
0
        } else {
953
0
          e_error(svinst->event,
954
0
            "failed to rotate logfile: "
955
0
            "rename(%s, %s) failed: %m",
956
0
            ehandler->logfile, rotated);
957
0
        }
958
0
      }
959
960
      /* Open clean logfile (overwrites existing if rename() failed earlier) */
961
0
      fd = open(ehandler->logfile,
962
0
        O_CREAT | O_APPEND | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0600);
963
0
      if (fd == -1) {
964
0
        if (errno == EACCES) {
965
0
          e_error(svinst->event,
966
0
            "failed to open logfile "
967
0
            "(LOGGING TO STDERR): %s",
968
0
            eacces_error_get_creating(
969
0
              "open", ehandler->logfile));
970
0
        } else {
971
0
          e_error(svinst->event,
972
0
            "failed to open logfile "
973
0
            "(LOGGING TO STDERR): "
974
0
            "open(%s) failed: %m",
975
0
            ehandler->logfile);
976
0
        }
977
0
        fd = STDERR_FILENO;
978
0
      }
979
0
    }
980
0
  }
981
982
0
  ostream = o_stream_create_fd(fd, 0);
983
0
  if (ostream == NULL) {
984
    /* Can't we do anything else in this most awkward situation? */
985
0
    e_error(svinst->event,
986
0
      "failed to open log stream on open file: "
987
0
      "o_stream_create_fd(fd=%s) failed "
988
0
      "(non-critical messages are not logged!)",
989
0
      ehandler->logfile);
990
0
  }
991
992
0
  ehandler->fd = fd;
993
0
  ehandler->stream = ostream;
994
0
  ehandler->started = TRUE;
995
996
0
  if (ostream != NULL) {
997
0
    now = time(NULL);
998
0
    tm = localtime(&now);
999
1000
0
    if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z", tm) > 0) {
1001
0
      sieve_logfile_printf(ehandler, __FILE__, __LINE__,
1002
0
               "sieve", "started log at %s", buf);
1003
0
    }
1004
0
  }
1005
0
}
1006
1007
static void
1008
sieve_logfile_log(struct sieve_error_handler *ehandler,
1009
      const struct sieve_error_params *params,
1010
      enum sieve_error_flags flags ATTR_UNUSED,
1011
      const char *message)
1012
0
{
1013
0
  struct sieve_logfile_ehandler *handler =
1014
0
    (struct sieve_logfile_ehandler *) ehandler;
1015
1016
0
  if (!handler->started)
1017
0
    sieve_logfile_start(handler);
1018
1019
0
  sieve_logfile_write(handler, params, message);
1020
0
}
1021
1022
static void sieve_logfile_free(struct sieve_error_handler *ehandler)
1023
0
{
1024
0
  struct sieve_logfile_ehandler *handler =
1025
0
    (struct sieve_logfile_ehandler *) ehandler;
1026
1027
0
  if (handler->stream != NULL) {
1028
0
    o_stream_destroy(&(handler->stream));
1029
0
    if (handler->fd != STDERR_FILENO) {
1030
0
      if (close(handler->fd) < 0) {
1031
0
        e_error(ehandler->svinst->event,
1032
0
          "failed to close logfile: "
1033
0
          "close(fd=%s) failed: %m",
1034
0
          handler->logfile);
1035
0
      }
1036
0
    }
1037
0
  }
1038
0
}
1039
1040
struct sieve_error_handler *
1041
sieve_logfile_ehandler_create(struct sieve_instance *svinst,
1042
            const char *logfile, unsigned int max_errors)
1043
0
{
1044
0
  pool_t pool;
1045
0
  struct sieve_logfile_ehandler *ehandler;
1046
1047
0
  pool = pool_alloconly_create("logfile_error_handler", 512);
1048
0
  ehandler = p_new(pool, struct sieve_logfile_ehandler, 1);
1049
0
  sieve_error_handler_init(&ehandler->handler, svinst, pool, max_errors);
1050
1051
0
  ehandler->handler.log = sieve_logfile_log;
1052
0
  ehandler->handler.free = sieve_logfile_free;
1053
1054
  /* Don't open logfile until something is actually logged.
1055
   * Let's not pullute the sieve directory with useless logfiles.
1056
   */
1057
0
  ehandler->logfile = p_strdup(pool, logfile);
1058
0
  ehandler->started = FALSE;
1059
0
  ehandler->stream = NULL;
1060
0
  ehandler->fd = -1;
1061
1062
0
  return &(ehandler->handler);
1063
0
}