/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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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 | } |