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