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