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