/src/pigeonhole/src/lib-sieve-tool/sieve-tool.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "lib-signals.h" |
5 | | #include "array.h" |
6 | | #include "ioloop.h" |
7 | | #include "ostream.h" |
8 | | #include "hostpid.h" |
9 | | #include "settings.h" |
10 | | #include "dict.h" |
11 | | #include "mail-namespace.h" |
12 | | #include "mail-storage.h" |
13 | | #include "mail-user.h" |
14 | | #include "message-address.h" |
15 | | #include "smtp-params.h" |
16 | | #include "master-service.h" |
17 | | #include "master-service-settings.h" |
18 | | #include "mail-storage-service.h" |
19 | | |
20 | | #include "sieve.h" |
21 | | #include "sieve-plugins.h" |
22 | | #include "sieve-extensions.h" |
23 | | #include "sieve-storage.h" |
24 | | |
25 | | #include "mail-raw.h" |
26 | | |
27 | | #include "sieve-tool.h" |
28 | | |
29 | | #include <stdio.h> |
30 | | #include <unistd.h> |
31 | | #include <fcntl.h> |
32 | | #include <pwd.h> |
33 | | #include <sysexits.h> |
34 | | |
35 | | /* |
36 | | * Global state |
37 | | */ |
38 | | |
39 | | struct sieve_tool { |
40 | | pool_t pool; |
41 | | char *name; |
42 | | |
43 | | bool no_config; |
44 | | |
45 | | char *username; |
46 | | char *homedir; |
47 | | |
48 | | char *sieve_extensions; |
49 | | ARRAY_TYPE(const_string) sieve_plugins; |
50 | | |
51 | | sieve_tool_setting_callback_t setting_callback; |
52 | | void *setting_callback_context; |
53 | | |
54 | | struct sieve_instance *svinst; |
55 | | |
56 | | struct mail_storage_service_ctx *storage_service; |
57 | | struct mail_user *mail_user_dovecot; |
58 | | struct mail_user *mail_user; |
59 | | |
60 | | struct mail_user *mail_raw_user; |
61 | | struct mail_raw *mail_raw; |
62 | | |
63 | | bool fuzzer:1; |
64 | | bool debug:1; |
65 | | }; |
66 | | |
67 | | struct sieve_tool *sieve_tool; |
68 | | |
69 | | /* |
70 | | * Settings management |
71 | | */ |
72 | | |
73 | | static const char * |
74 | | sieve_tool_sieve_get_homedir(struct sieve_instance *svinst ATTR_UNUSED, |
75 | | void *context) |
76 | 0 | { |
77 | 0 | struct sieve_tool *tool = (struct sieve_tool *)context; |
78 | |
|
79 | 0 | return sieve_tool_get_homedir(tool); |
80 | 0 | } |
81 | | |
82 | | const struct sieve_callbacks sieve_tool_callbacks = { |
83 | | sieve_tool_sieve_get_homedir, |
84 | | }; |
85 | | |
86 | | /* |
87 | | * Initialization |
88 | | */ |
89 | | |
90 | | static void |
91 | | sieve_tool_get_user_data(const char **username_r, const char **homedir_r) |
92 | 0 | { |
93 | 0 | uid_t process_euid = geteuid(); |
94 | 0 | struct passwd *pw; |
95 | 0 | const char *user = NULL, *home = NULL; |
96 | |
|
97 | 0 | user = getenv("USER"); |
98 | 0 | home = getenv("HOME"); |
99 | |
|
100 | 0 | if (user == NULL || *user == '\0' || home == NULL || *home == '\0') { |
101 | 0 | pw = getpwuid(process_euid); |
102 | 0 | if (pw != NULL) { |
103 | 0 | user = pw->pw_name; |
104 | 0 | home = pw->pw_dir; |
105 | 0 | } |
106 | 0 | } |
107 | |
|
108 | 0 | if (username_r != NULL) { |
109 | 0 | if (user == NULL || *user == '\0') { |
110 | 0 | i_fatal("couldn't lookup our username (uid=%s)", |
111 | 0 | dec2str(process_euid)); |
112 | 0 | } |
113 | | |
114 | 0 | *username_r = t_strdup(user); |
115 | 0 | } |
116 | | |
117 | 0 | if (homedir_r != NULL) |
118 | 0 | *homedir_r = t_strdup(home); |
119 | 0 | } |
120 | | |
121 | | struct sieve_tool * |
122 | | sieve_tool_init(const char *name, int *argc, char **argv[], |
123 | | const char *getopt_str, bool no_config) |
124 | 0 | { |
125 | 0 | struct sieve_tool *tool; |
126 | 0 | enum master_service_flags service_flags = |
127 | 0 | MASTER_SERVICE_FLAG_STANDALONE | |
128 | 0 | MASTER_SERVICE_FLAG_DONT_SEND_STATS | |
129 | 0 | MASTER_SERVICE_FLAG_NO_INIT_DATASTACK_FRAME; |
130 | 0 | pool_t pool; |
131 | |
|
132 | 0 | if (no_config) |
133 | 0 | service_flags |= MASTER_SERVICE_FLAG_CONFIG_DEFAULTS; |
134 | |
|
135 | 0 | getopt_str = t_strconcat(getopt_str, "DP:x:", NULL); |
136 | 0 | master_service = master_service_init(name, service_flags, |
137 | 0 | argc, argv, getopt_str); |
138 | |
|
139 | 0 | pool = pool_alloconly_create("sieve tool", 8192); |
140 | 0 | tool = p_new(pool, struct sieve_tool, 1); |
141 | 0 | tool->pool = pool; |
142 | 0 | tool->name = p_strdup(tool->pool, name); |
143 | 0 | tool->no_config = no_config; |
144 | |
|
145 | 0 | p_array_init(&tool->sieve_plugins, pool, 16); |
146 | 0 | return tool; |
147 | 0 | } |
148 | | |
149 | | |
150 | | struct sieve_tool *sieve_tool_init_fuzzer(const char *name) |
151 | 0 | { |
152 | 0 | struct sieve_tool *tool; |
153 | 0 | enum master_service_flags service_flags = |
154 | 0 | MASTER_SERVICE_FLAG_STANDALONE | |
155 | 0 | MASTER_SERVICE_FLAG_DONT_SEND_STATS | |
156 | 0 | MASTER_SERVICE_FLAG_NO_INIT_DATASTACK_FRAME | |
157 | 0 | MASTER_SERVICE_FLAG_CONFIG_BUILTIN; |
158 | 0 | pool_t pool; |
159 | |
|
160 | 0 | pool = pool_alloconly_create("sieve tool", 8192); |
161 | 0 | char *arg = p_strdup(pool, name); |
162 | 0 | char **argv = p_new(pool, char *, 2); |
163 | 0 | argv[0] = arg; |
164 | 0 | int argc = 1; |
165 | 0 | master_service = master_service_init(name, service_flags, |
166 | 0 | &argc, &argv, ""); |
167 | |
|
168 | 0 | tool = p_new(pool, struct sieve_tool, 1); |
169 | 0 | tool->pool = pool; |
170 | 0 | tool->name = arg; |
171 | 0 | tool->fuzzer = TRUE; |
172 | 0 | tool->no_config = TRUE; |
173 | |
|
174 | 0 | p_array_init(&tool->sieve_plugins, pool, 16); |
175 | 0 | return tool; |
176 | 0 | } |
177 | | |
178 | | int sieve_tool_getopt(struct sieve_tool *tool) |
179 | 0 | { |
180 | 0 | int c; |
181 | |
|
182 | 0 | i_assert(!tool->fuzzer); |
183 | 0 | while ((c = master_getopt(master_service)) > 0) { |
184 | 0 | switch (c) { |
185 | 0 | case 'x': |
186 | | /* Extensions */ |
187 | 0 | if (tool->sieve_extensions != NULL) { |
188 | 0 | i_fatal_status( |
189 | 0 | EX_USAGE, |
190 | 0 | "duplicate -x option specified, " |
191 | 0 | "but only one allowed."); |
192 | 0 | } |
193 | 0 | tool->sieve_extensions = p_strdup(tool->pool, optarg); |
194 | 0 | break; |
195 | 0 | case 'u': |
196 | 0 | if (tool->username == NULL) |
197 | 0 | tool->username = p_strdup(tool->pool, optarg); |
198 | 0 | break; |
199 | 0 | case 'P': { |
200 | | /* Plugin */ |
201 | 0 | const char *plugin; |
202 | |
|
203 | 0 | plugin = p_strdup(tool->pool, optarg); |
204 | 0 | array_append(&tool->sieve_plugins, &plugin, 1); |
205 | 0 | break; |
206 | 0 | } |
207 | 0 | case 'D': |
208 | 0 | tool->debug = TRUE; |
209 | 0 | break; |
210 | 0 | default: |
211 | 0 | return c; |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | 0 | return c; |
216 | 0 | } |
217 | | |
218 | | static void sieve_tool_load_plugins(struct sieve_tool *tool) |
219 | 0 | { |
220 | 0 | unsigned int i, count; |
221 | 0 | const char *const *plugins; |
222 | |
|
223 | 0 | plugins = array_get(&tool->sieve_plugins, &count); |
224 | 0 | for (i = 0; i < count; i++) { |
225 | 0 | const char *path, *file = strrchr(plugins[i], '/'); |
226 | |
|
227 | 0 | if (file != NULL) { |
228 | 0 | path = t_strdup_until(plugins[i], file); |
229 | 0 | file = file+1; |
230 | 0 | } else { |
231 | 0 | path = NULL; |
232 | 0 | file = plugins[i]; |
233 | 0 | } |
234 | |
|
235 | 0 | sieve_plugins_load(tool->svinst, path, file); |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | struct sieve_instance * |
240 | | sieve_tool_init_finish(struct sieve_tool *tool, bool init_mailstore, |
241 | | bool preserve_root) |
242 | 0 | { |
243 | 0 | enum mail_storage_service_flags storage_service_flags = |
244 | 0 | MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR | |
245 | 0 | MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; |
246 | 0 | struct mail_storage_service_input service_input; |
247 | 0 | struct sieve_environment svenv; |
248 | 0 | const char *username = tool->username; |
249 | 0 | const char *homedir = tool->homedir; |
250 | 0 | const char *errstr; |
251 | |
|
252 | 0 | if (master_service_settings_read_simple(master_service, &errstr) < 0) |
253 | 0 | i_fatal("%s", errstr); |
254 | | |
255 | 0 | master_service_init_finish(master_service); |
256 | |
|
257 | 0 | if (tool->fuzzer) { |
258 | 0 | username = tool->username = "fuzzer"; |
259 | 0 | tool->homedir = "/tmp"; |
260 | 0 | } else if (username == NULL) { |
261 | 0 | sieve_tool_get_user_data(&username, &homedir); |
262 | |
|
263 | 0 | username = tool->username = p_strdup(tool->pool, username); |
264 | |
|
265 | 0 | tool->homedir = p_strdup(tool->pool, homedir); |
266 | |
|
267 | 0 | if (!tool->fuzzer && preserve_root) { |
268 | 0 | storage_service_flags |= |
269 | 0 | MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS; |
270 | 0 | } |
271 | 0 | } else { |
272 | 0 | storage_service_flags |= |
273 | 0 | MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; |
274 | 0 | } |
275 | |
|
276 | 0 | if (tool->fuzzer || !init_mailstore) |
277 | 0 | storage_service_flags |= |
278 | 0 | MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES; |
279 | |
|
280 | 0 | const char *const code_override_fields[] = { |
281 | 0 | (tool->homedir == NULL ? NULL : |
282 | 0 | t_strconcat("mail_home=", tool->homedir, NULL)), |
283 | 0 | NULL |
284 | 0 | }; |
285 | |
|
286 | 0 | i_zero(&service_input); |
287 | 0 | service_input.service = tool->name; |
288 | 0 | service_input.username = username; |
289 | 0 | service_input.code_override_fields = code_override_fields; |
290 | |
|
291 | 0 | tool->storage_service = mail_storage_service_init( |
292 | 0 | master_service, storage_service_flags); |
293 | 0 | if (mail_storage_service_lookup_next( |
294 | 0 | tool->storage_service, &service_input, |
295 | 0 | &tool->mail_user_dovecot, &errstr) <= 0) |
296 | 0 | i_fatal("%s", errstr); |
297 | | |
298 | 0 | i_zero(&svenv); |
299 | 0 | svenv.username = username; |
300 | 0 | (void)mail_user_get_home(tool->mail_user_dovecot, &svenv.home_dir); |
301 | 0 | svenv.hostname = my_hostdomain(); |
302 | 0 | svenv.base_dir = tool->mail_user_dovecot->set->base_dir; |
303 | 0 | svenv.temp_dir = tool->mail_user_dovecot->set->mail_temp_dir; |
304 | 0 | svenv.event_parent = tool->mail_user_dovecot->event; |
305 | 0 | svenv.flags = SIEVE_FLAG_COMMAND_LINE; |
306 | 0 | svenv.location = SIEVE_ENV_LOCATION_MS; |
307 | 0 | svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; |
308 | | |
309 | | /* Initialize Sieve Engine */ |
310 | 0 | if (sieve_init(&svenv, &sieve_tool_callbacks, tool, tool->debug, |
311 | 0 | &tool->svinst) < 0) |
312 | 0 | i_fatal("Failed to initialize Sieve"); |
313 | | |
314 | | /* Load Sieve plugins */ |
315 | 0 | if (array_count(&tool->sieve_plugins) > 0) |
316 | 0 | sieve_tool_load_plugins(tool); |
317 | | |
318 | | /* Set active Sieve extensions */ |
319 | 0 | if (tool->sieve_extensions != NULL) { |
320 | 0 | sieve_set_extensions(tool->svinst, tool->sieve_extensions); |
321 | 0 | } else if (tool->no_config) { |
322 | 0 | sieve_set_extensions(tool->svinst, NULL); |
323 | 0 | } |
324 | |
|
325 | 0 | return tool->svinst; |
326 | 0 | } |
327 | | |
328 | | void sieve_tool_deinit(struct sieve_tool **_tool) |
329 | 0 | { |
330 | 0 | struct sieve_tool *tool = *_tool; |
331 | |
|
332 | 0 | *_tool = NULL; |
333 | | |
334 | | /* Deinitialize Sieve engine */ |
335 | 0 | sieve_deinit(&tool->svinst); |
336 | | |
337 | | /* Free raw mail */ |
338 | |
|
339 | 0 | if (tool->mail_raw != NULL) |
340 | 0 | mail_raw_close(&tool->mail_raw); |
341 | |
|
342 | 0 | if (tool->mail_raw_user != NULL) |
343 | 0 | mail_user_unref(&tool->mail_raw_user); |
344 | | |
345 | | /* Free mail service */ |
346 | |
|
347 | 0 | if (tool->mail_user != NULL) |
348 | 0 | mail_user_unref(&tool->mail_user); |
349 | 0 | if (tool->mail_user_dovecot != NULL) |
350 | 0 | mail_user_unref(&tool->mail_user_dovecot); |
351 | |
|
352 | 0 | mail_storage_service_deinit(&tool->storage_service); |
353 | | |
354 | | /* Free sieve tool object */ |
355 | |
|
356 | 0 | pool_unref(&tool->pool); |
357 | | |
358 | | /* Deinitialize service */ |
359 | 0 | master_service_deinit(&master_service); |
360 | 0 | } |
361 | | |
362 | | /* |
363 | | * Mail environment |
364 | | */ |
365 | | |
366 | | void sieve_tool_init_mail_user(struct sieve_tool *tool) |
367 | 0 | { |
368 | 0 | struct mail_user *mail_user_dovecot = tool->mail_user_dovecot; |
369 | 0 | const char *username = tool->username; |
370 | 0 | struct mail_namespace *ns = NULL; |
371 | 0 | const char *home = NULL, *errstr = NULL; |
372 | |
|
373 | 0 | struct settings_instance *set_instance = |
374 | 0 | mail_storage_service_user_get_settings_instance( |
375 | 0 | mail_user_dovecot->service_user); |
376 | 0 | struct mail_storage_service_input input = { |
377 | 0 | .username = username, |
378 | 0 | .set_instance = set_instance, |
379 | 0 | .no_userdb_lookup = TRUE, |
380 | 0 | }; |
381 | 0 | if (mail_storage_service_lookup_next(tool->storage_service, &input, |
382 | 0 | &tool->mail_user, &errstr) < 0) |
383 | 0 | i_fatal("Test user lookup failed: %s", errstr); |
384 | | |
385 | 0 | home = sieve_tool_get_homedir(sieve_tool); |
386 | 0 | if (home != NULL) |
387 | 0 | mail_user_set_home(tool->mail_user, home); |
388 | |
|
389 | 0 | if (mail_user_init(tool->mail_user, &errstr) < 0) |
390 | 0 | i_fatal("Test user initialization failed: %s", errstr); |
391 | | |
392 | 0 | if (mail_namespaces_init_location(tool->mail_user, |
393 | 0 | tool->mail_user->event, &errstr) < 0) |
394 | 0 | i_fatal("Test storage creation failed: %s", errstr); |
395 | | |
396 | 0 | ns = tool->mail_user->namespaces; |
397 | 0 | ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL; |
398 | 0 | } |
399 | | |
400 | | static void sieve_tool_init_mail_raw_user(struct sieve_tool *tool) |
401 | 0 | { |
402 | 0 | if (tool->mail_raw_user == NULL) { |
403 | 0 | tool->mail_raw_user = mail_raw_user_create( |
404 | 0 | tool->mail_user_dovecot); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | struct mail * |
409 | | sieve_tool_open_file_as_mail(struct sieve_tool *tool, const char *path) |
410 | 0 | { |
411 | 0 | sieve_tool_init_mail_raw_user(tool); |
412 | |
|
413 | 0 | if (tool->mail_raw != NULL) |
414 | 0 | mail_raw_close(&tool->mail_raw); |
415 | |
|
416 | 0 | tool->mail_raw = mail_raw_open_file(tool->mail_raw_user, path); |
417 | |
|
418 | 0 | return tool->mail_raw->mail; |
419 | 0 | } |
420 | | |
421 | | struct mail * |
422 | | sieve_tool_open_data_as_mail(struct sieve_tool *tool, string_t *mail_data) |
423 | 0 | { |
424 | 0 | sieve_tool_init_mail_raw_user(tool); |
425 | |
|
426 | 0 | if (tool->mail_raw != NULL) |
427 | 0 | mail_raw_close(&tool->mail_raw); |
428 | |
|
429 | 0 | tool->mail_raw = mail_raw_open_data(tool->mail_raw_user, mail_data); |
430 | |
|
431 | 0 | return tool->mail_raw->mail; |
432 | 0 | } |
433 | | |
434 | | /* |
435 | | * Configuration |
436 | | */ |
437 | | |
438 | | void sieve_tool_set_homedir(struct sieve_tool *tool, const char *homedir) |
439 | 0 | { |
440 | 0 | i_assert(!tool->fuzzer); |
441 | | |
442 | 0 | if (tool->homedir != NULL && strcmp(homedir, tool->homedir) == 0) |
443 | 0 | return; |
444 | | |
445 | 0 | tool->homedir = p_strdup(tool->pool, homedir); |
446 | |
|
447 | 0 | if (tool->mail_user_dovecot != NULL) |
448 | 0 | mail_user_set_home(tool->mail_user_dovecot, tool->homedir); |
449 | 0 | if (tool->mail_user != NULL) |
450 | 0 | mail_user_set_home(tool->mail_user, tool->homedir); |
451 | 0 | } |
452 | | |
453 | | void sieve_tool_set_setting_callback(struct sieve_tool *tool, |
454 | | sieve_tool_setting_callback_t callback, |
455 | | void *context) |
456 | 0 | { |
457 | 0 | tool->setting_callback = callback; |
458 | 0 | tool->setting_callback_context = context; |
459 | 0 | } |
460 | | |
461 | | /* |
462 | | * Accessors |
463 | | */ |
464 | | |
465 | | const char *sieve_tool_get_username(struct sieve_tool *tool) |
466 | 0 | { |
467 | 0 | const char *username; |
468 | |
|
469 | 0 | if (tool->username == NULL) { |
470 | 0 | sieve_tool_get_user_data(&username, NULL); |
471 | 0 | return username; |
472 | 0 | } |
473 | | |
474 | 0 | return tool->username; |
475 | 0 | } |
476 | | |
477 | | const char *sieve_tool_get_homedir(struct sieve_tool *tool) |
478 | 0 | { |
479 | 0 | const char *homedir = NULL; |
480 | |
|
481 | 0 | if (tool->homedir != NULL) |
482 | 0 | return tool->homedir; |
483 | | |
484 | 0 | if (tool->mail_user_dovecot != NULL && |
485 | 0 | mail_user_get_home(tool->mail_user_dovecot, &homedir) > 0) |
486 | 0 | return tool->homedir; |
487 | | |
488 | 0 | sieve_tool_get_user_data(NULL, &homedir); |
489 | 0 | return homedir; |
490 | 0 | } |
491 | | |
492 | | struct mail_user *sieve_tool_get_mail_user(struct sieve_tool *tool) |
493 | 0 | { |
494 | 0 | return (tool->mail_user == NULL ? |
495 | 0 | tool->mail_user_dovecot : tool->mail_user); |
496 | 0 | } |
497 | | |
498 | | struct mail_user *sieve_tool_get_mail_raw_user(struct sieve_tool *tool) |
499 | 0 | { |
500 | 0 | sieve_tool_init_mail_raw_user(tool); |
501 | 0 | return tool->mail_raw_user; |
502 | 0 | } |
503 | | |
504 | | struct mail_storage_service_ctx * |
505 | | sieve_tool_get_mail_storage_service(struct sieve_tool *tool) |
506 | 0 | { |
507 | 0 | sieve_tool_init_mail_raw_user(tool); |
508 | 0 | return tool->storage_service; |
509 | 0 | } |
510 | | |
511 | | /* |
512 | | * Commonly needed functionality |
513 | | */ |
514 | | |
515 | | static const struct smtp_address * |
516 | | sieve_tool_get_address(struct mail *mail, const char *header) |
517 | 0 | { |
518 | 0 | struct message_address *addr; |
519 | 0 | struct smtp_address *smtp_addr; |
520 | 0 | const char *str; |
521 | |
|
522 | 0 | if (mail_get_first_header(mail, header, &str) <= 0) |
523 | 0 | return NULL; |
524 | 0 | addr = message_address_parse(pool_datastack_create(), |
525 | 0 | (const unsigned char *)str, |
526 | 0 | strlen(str), 1, 0); |
527 | 0 | if (addr == NULL || addr->mailbox == NULL || |
528 | 0 | addr->domain == NULL || *addr->mailbox == '\0' || |
529 | 0 | *addr->domain == '\0') |
530 | 0 | return NULL; |
531 | 0 | if (smtp_address_create_from_msg_temp(addr, &smtp_addr) < 0) |
532 | 0 | return NULL; |
533 | 0 | return smtp_addr; |
534 | 0 | } |
535 | | |
536 | | void sieve_tool_get_envelope_data(struct sieve_message_data *msgdata, |
537 | | struct mail *mail, |
538 | | const struct smtp_address *sender, |
539 | | const struct smtp_address *rcpt_orig, |
540 | | const struct smtp_address *rcpt_final) |
541 | 0 | { |
542 | 0 | struct smtp_params_rcpt *rcpt_params; |
543 | | |
544 | | /* Get sender address */ |
545 | 0 | if (sender == NULL) |
546 | 0 | sender = sieve_tool_get_address(mail, "Return-path"); |
547 | 0 | if (sender == NULL) |
548 | 0 | sender = sieve_tool_get_address(mail, "Sender"); |
549 | 0 | if (sender == NULL) |
550 | 0 | sender = sieve_tool_get_address(mail, "From"); |
551 | 0 | if (sender == NULL) |
552 | 0 | sender = smtp_address_create_temp("sender", "example.com"); |
553 | | |
554 | | /* Get recipient address */ |
555 | 0 | if (rcpt_final == NULL) |
556 | 0 | rcpt_final = sieve_tool_get_address(mail, "Envelope-To"); |
557 | 0 | if (rcpt_final == NULL) |
558 | 0 | rcpt_final = sieve_tool_get_address(mail, "To"); |
559 | 0 | if (rcpt_final == NULL) { |
560 | 0 | rcpt_final = smtp_address_create_temp("recipient", |
561 | 0 | "example.com"); |
562 | 0 | } |
563 | 0 | if (rcpt_orig == NULL) |
564 | 0 | rcpt_orig = rcpt_final; |
565 | |
|
566 | 0 | msgdata->envelope.mail_from = sender; |
567 | 0 | msgdata->envelope.rcpt_to = rcpt_final; |
568 | |
|
569 | 0 | rcpt_params = t_new(struct smtp_params_rcpt, 1); |
570 | 0 | rcpt_params->orcpt.addr = rcpt_orig; |
571 | |
|
572 | 0 | msgdata->envelope.rcpt_params = rcpt_params; |
573 | 0 | } |
574 | | |
575 | | /* |
576 | | * File I/O |
577 | | */ |
578 | | |
579 | | struct ostream *sieve_tool_open_output_stream(const char *filename) |
580 | 0 | { |
581 | 0 | struct ostream *outstream; |
582 | 0 | int fd; |
583 | |
|
584 | 0 | if (strcmp(filename, "-") == 0) |
585 | 0 | outstream = o_stream_create_fd(1, 0); |
586 | 0 | else { |
587 | 0 | fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_NOFOLLOW, 0600); |
588 | 0 | if (fd < 0) |
589 | 0 | i_fatal("failed to open file for writing: %m"); |
590 | | |
591 | 0 | outstream = o_stream_create_fd_autoclose(&fd, 0); |
592 | 0 | } |
593 | | |
594 | 0 | return outstream; |
595 | 0 | } |
596 | | |
597 | | /* |
598 | | * Sieve script handling |
599 | | */ |
600 | | |
601 | | static void |
602 | | sieve_tool_script_parse_location(struct sieve_tool *tool, const char *location, |
603 | | const char **storage_name_r) |
604 | 0 | { |
605 | 0 | struct sieve_instance *svinst = tool->svinst; |
606 | 0 | const char *data = strchr(location, ':'); |
607 | 0 | const char *script_driver = "file"; |
608 | 0 | const char *script_path = NULL; |
609 | 0 | const char *storage_name = "_file"; |
610 | |
|
611 | 0 | if (data != NULL) { |
612 | 0 | script_driver = t_strdup_until(location, data++); |
613 | 0 | if (strcmp(script_driver, "file") == 0) |
614 | 0 | script_path = data; |
615 | 0 | else |
616 | 0 | storage_name = data; |
617 | 0 | } else { |
618 | 0 | script_path = location; |
619 | 0 | } |
620 | |
|
621 | 0 | struct settings_instance *set_instance = |
622 | 0 | settings_instance_find(svinst->event); |
623 | 0 | const char *prefix = t_strdup_printf("sieve_script/%s", storage_name); |
624 | |
|
625 | 0 | settings_override(set_instance, "sieve_script+", storage_name, |
626 | 0 | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
627 | 0 | settings_override(set_instance, |
628 | 0 | t_strdup_printf("%s/sieve_script_storage", prefix), |
629 | 0 | storage_name, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
630 | 0 | settings_override(set_instance, |
631 | 0 | t_strdup_printf("%s/sieve_script_type", prefix), |
632 | 0 | "command-line", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
633 | 0 | settings_override(set_instance, |
634 | 0 | t_strdup_printf("%s/sieve_script_driver", prefix), |
635 | 0 | script_driver, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
636 | 0 | if (script_path != NULL) { |
637 | 0 | settings_override( |
638 | 0 | set_instance, t_strdup_printf("%s/sieve_script_path", |
639 | 0 | prefix), |
640 | 0 | script_path, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
641 | 0 | } |
642 | |
|
643 | 0 | *storage_name_r = storage_name; |
644 | 0 | } |
645 | | |
646 | | struct sieve_binary * |
647 | | sieve_tool_script_compile(struct sieve_tool *tool, const char *location) |
648 | 0 | { |
649 | 0 | struct sieve_instance *svinst = tool->svinst; |
650 | 0 | struct sieve_error_handler *ehandler; |
651 | 0 | enum sieve_error error_code; |
652 | 0 | struct sieve_binary *sbin = NULL; |
653 | |
|
654 | 0 | ehandler = sieve_stderr_ehandler_create(svinst, 0); |
655 | 0 | sieve_error_handler_accept_infolog(ehandler, TRUE); |
656 | 0 | sieve_error_handler_accept_debuglog(ehandler, svinst->debug); |
657 | |
|
658 | 0 | if (sieve_storage_name_is_valid(location) && |
659 | 0 | sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, location, NULL, |
660 | 0 | ehandler, 0, &sbin, &error_code) < 0 && |
661 | 0 | error_code != SIEVE_ERROR_NOT_FOUND) |
662 | 0 | i_fatal("failed to compile sieve script storage"); |
663 | | |
664 | 0 | if (sbin == NULL) { |
665 | 0 | const char *storage_name; |
666 | |
|
667 | 0 | sieve_tool_script_parse_location(tool, location, &storage_name); |
668 | 0 | if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, storage_name, |
669 | 0 | NULL, ehandler, 0, &sbin, NULL) < 0) |
670 | 0 | i_fatal("failed to compile sieve script"); |
671 | 0 | } |
672 | 0 | i_assert(sbin != NULL); |
673 | | |
674 | 0 | sieve_error_handler_unref(&ehandler); |
675 | 0 | return sbin; |
676 | 0 | } |
677 | | |
678 | | struct sieve_binary * |
679 | | sieve_tool_script_open(struct sieve_tool *tool, const char *location) |
680 | 0 | { |
681 | 0 | struct sieve_instance *svinst = tool->svinst; |
682 | 0 | struct sieve_error_handler *ehandler; |
683 | 0 | enum sieve_error error_code; |
684 | 0 | struct sieve_binary *sbin = NULL; |
685 | |
|
686 | 0 | ehandler = sieve_stderr_ehandler_create(svinst, 0); |
687 | 0 | sieve_error_handler_accept_infolog(ehandler, TRUE); |
688 | 0 | sieve_error_handler_accept_debuglog(ehandler, svinst->debug); |
689 | |
|
690 | 0 | if (sieve_storage_name_is_valid(location) && |
691 | 0 | sieve_open(svinst, SIEVE_SCRIPT_CAUSE_ANY, location, NULL, |
692 | 0 | ehandler, 0, &sbin, &error_code) < 0 && |
693 | 0 | error_code != SIEVE_ERROR_NOT_FOUND) |
694 | 0 | i_fatal("failed to open sieve script storage"); |
695 | | |
696 | 0 | if (sbin == NULL) { |
697 | 0 | const char *storage_name; |
698 | |
|
699 | 0 | sieve_tool_script_parse_location(tool, location, &storage_name); |
700 | 0 | if (sieve_open(svinst, SIEVE_SCRIPT_CAUSE_ANY, storage_name, |
701 | 0 | NULL, ehandler, 0, &sbin, NULL) < 0) |
702 | 0 | i_fatal("failed to open sieve script"); |
703 | 0 | } |
704 | 0 | i_assert(sbin != NULL); |
705 | | |
706 | 0 | sieve_error_handler_unref(&ehandler); |
707 | |
|
708 | 0 | sieve_save(sbin, FALSE, NULL); |
709 | 0 | return sbin; |
710 | 0 | } |
711 | | |
712 | | void sieve_tool_dump_binary_to(struct sieve_binary *sbin, |
713 | | const char *filename, bool hexdump) |
714 | 0 | { |
715 | 0 | struct ostream *dumpstream; |
716 | |
|
717 | 0 | if (filename == NULL) |
718 | 0 | return; |
719 | | |
720 | 0 | dumpstream = sieve_tool_open_output_stream(filename); |
721 | 0 | if (dumpstream != NULL) { |
722 | 0 | if (hexdump) |
723 | 0 | (void)sieve_hexdump(sbin, dumpstream); |
724 | 0 | else |
725 | 0 | (void)sieve_dump(sbin, dumpstream, FALSE); |
726 | 0 | if (o_stream_finish(dumpstream) < 0) { |
727 | 0 | i_fatal("write(%s) failed: %s", filename, |
728 | 0 | o_stream_get_error(dumpstream)); |
729 | 0 | } |
730 | 0 | o_stream_destroy(&dumpstream); |
731 | 0 | } else { |
732 | 0 | i_fatal("Failed to create stream for sieve code dump."); |
733 | 0 | } |
734 | 0 | } |
735 | | |
736 | | /* |
737 | | * Commandline option parsing |
738 | | */ |
739 | | |
740 | | void sieve_tool_parse_trace_option(struct sieve_trace_config *tr_config, |
741 | | const char *tr_option) |
742 | 0 | { |
743 | 0 | const char *lvl; |
744 | |
|
745 | 0 | if (str_begins(tr_option, "level=", &lvl)) { |
746 | 0 | if (strcmp(lvl, "none") == 0) { |
747 | 0 | tr_config->level = SIEVE_TRLVL_NONE; |
748 | 0 | } else if (strcmp(lvl, "actions") == 0) { |
749 | 0 | tr_config->level = SIEVE_TRLVL_ACTIONS; |
750 | 0 | } else if (strcmp(lvl, "commands") == 0) { |
751 | 0 | tr_config->level = SIEVE_TRLVL_COMMANDS; |
752 | 0 | } else if (strcmp(lvl, "tests") == 0) { |
753 | 0 | tr_config->level = SIEVE_TRLVL_TESTS; |
754 | 0 | } else if (strcmp(lvl, "matching") == 0) { |
755 | 0 | tr_config->level = SIEVE_TRLVL_MATCHING; |
756 | 0 | } else { |
757 | 0 | i_fatal_status(EX_USAGE, |
758 | 0 | "Unknown -tlevel= trace level: %s", lvl); |
759 | 0 | } |
760 | 0 | } else if (strcmp(tr_option, "debug") == 0) { |
761 | 0 | tr_config->flags |= SIEVE_TRFLG_DEBUG; |
762 | 0 | } else if (strcmp(tr_option, "addresses") == 0) { |
763 | 0 | tr_config->flags |= SIEVE_TRFLG_ADDRESSES; |
764 | 0 | } else { |
765 | | i_fatal_status(EX_USAGE, "Unknown -t trace option value: %s", |
766 | 0 | tr_option); |
767 | 0 | } |
768 | 0 | } |