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