Coverage Report

Created: 2026-06-09 06:49

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