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