1 /* This file is part of Mailfromd.
2 Copyright (C) 2005-2021 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <syslog.h>
27 #include <signal.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34
35 #include <mailutils/mailutils.h>
36 #include <mailutils/server.h>
37 #include <mailutils/syslog.h>
38 #include <mailutils/cli.h>
39 #include <mailutils/dbm.h>
40
41 #include "mailfromd.h"
42 #include "callout.h"
43 #include "srvman.h"
44 #include "srvcfg.h"
45 #include "filenames.h"
46 #include "builtin.h"
47 #include "prog.h"
48
49
50 /* Configurable options */
51
52 int mode = MAILFROMD_DAEMON; /* Default operation mode */
53 enum smtp_state test_state = smtp_state_envfrom; /* State for --test mode */
54 int need_script = 1; /* Set if the current mode requires reading the
55 script file */
56 char *script_file = DEFAULT_SCRIPT_FILE;
57 int script_check; /* Check config file syntax and exit */
58 extern int yy_flex_debug; /* Enable tracing the lexical analyzer */
59 int script_ydebug; /* Enable tracing the parser */
60 int script_dump_tree; /* Dump created config tree to stdout */
61 int script_dump_code; /* Dump disassembled code to stdout */
62 int script_dump_macros; /* Dump used Sendmail macros */
63 int script_dump_xref; /* Dump cross-reference */
64 int preprocess_option; /* Only preprocess the sources */
65 int location_column_option; /* Print column numbers in error locations */
66
67 char *ext_pp = DEF_EXT_PP; /* External preprocessor to use */
68 char *ext_pp_options;
69 int ext_pp_options_given;
70
71 int do_trace; /* Enable tracing configuration */
72 unsigned optimization_level = 1; /* Optimization level */
73
74 int stack_trace_option; /* Print stack traces on runtime errors */
75
76 char *main_function_name = "main";
77 char *callout_server_url;
78
79 mu_stream_t mf_strecho; /* Output stream for 'echo' statements */
80
81 #define ARG_UNSET (-1)
82
83 static int trace_option = ARG_UNSET;
84 static mu_list_t trace_modules;
85
86 static char *resolv_conf_file;
87
88 /* Preprocessor helper function */
89 static void
add_pp_option(const char * opt,const char * arg)90 add_pp_option(const char *opt, const char *arg)
91 {
92 size_t len;
93
94 len = 1 + strlen(opt) + (arg ? strlen(arg) : 0);
95 if (ext_pp_options) {
96 len += strlen(ext_pp_options);
97 ext_pp_options = mu_realloc(ext_pp_options, len + 1);
98 } else {
99 ext_pp_options = mu_alloc(len + 1);
100 ext_pp_options[0] = 0;
101 }
102 strcat(ext_pp_options, " ");
103 strcat(ext_pp_options, opt);
104 strcat(ext_pp_options, arg);
105 }
106
107 void
pp_define(const char * symbol)108 pp_define(const char *symbol)
109 {
110 add_pp_option("-D", symbol);
111 }
112
113
114 /* Logging & debugging */
115
116 static mu_stream_t mf_trace_stream;
117
118 static void
open_trace_stream()119 open_trace_stream()
120 {
121 int rc = mu_filter_create(&mf_trace_stream,
122 mf_strecho,
123 "C-escape",
124 MU_FILTER_ENCODE,
125 MU_STREAM_WRITE);
126 if (rc) {
127 mu_error(_("cannot create trace stream, "
128 "using standard log: %s"),
129 mu_strerror(rc));
130 mf_trace_stream = mf_strecho;
131 }
132 }
133
134 void
trace(const char * fmt,...)135 trace(const char *fmt, ...)
136 {
137 if (do_trace) {
138 int bval = 0;
139 va_list ap;
140
141 if (!mf_trace_stream)
142 open_trace_stream();
143
144 va_start(ap, fmt);
145 mu_stream_vprintf(mf_trace_stream, fmt, ap);
146 bval = 1;
147 mu_stream_ioctl(mf_trace_stream, MU_IOCTL_FILTER,
148 MU_IOCTL_FILTER_SET_DISABLED,
149 &bval);
150 mu_stream_write(mf_trace_stream, "\n", 1, NULL);
151 bval = 0;
152 mu_stream_ioctl(mf_trace_stream, MU_IOCTL_FILTER,
153 MU_IOCTL_FILTER_SET_DISABLED,
154 &bval);
155 va_end(ap);
156 }
157 }
158
159 void
log_status(sfsistat status,SMFICTX * ctx)160 log_status(sfsistat status, SMFICTX *ctx)
161 {
162 mu_debug_level_t lev;
163
164 mu_debug_category_level(NULL, 0, &lev);
165 if ((lev & ~MU_DEBUG_LEVEL_MASK(MU_DEBUG_ERROR)) &&
166 status != SMFIS_CONTINUE) {
167 const char *str = sfsistat_str(status);
168 if (str)
169 logmsg(MU_LOG_INFO,
170 "%s%s", mailfromd_msgid(ctx), str);
171 else
172 logmsg(MU_LOG_INFO,
173 "%sstatus %d",
174 mailfromd_msgid(ctx), status);
175 }
176 }
177
178
179 /* Sendmail class file support */
180
181 static mu_list_t domain_list;
182
183 /* Read domains from sendmail-style domain file NAME and store them in
184 DOMAIN_LIST */
185 int
read_domain_file(const char * name)186 read_domain_file(const char *name)
187 {
188 FILE *fp;
189 char buf[256];
190 char *p;
191
192 fp = fopen(name, "r");
193 if (!fp) {
194 mu_error(_("cannot open file `%s': %s"),
195 name, mu_strerror(errno));
196 return 1;
197 }
198
199 if (!domain_list) {
200 mu_list_create(&domain_list);
201 mu_list_set_comparator(domain_list, mf_list_compare_string);
202 }
203
204 while (p = fgets(buf, sizeof buf, fp)) {
205 char *q;
206
207 while (*p && mu_isspace(*p))
208 p++;
209 if (*p == '#')
210 continue;
211
212 for (q = p; *q && !mu_isspace(*q); q++)
213 ;
214 *q = 0;
215
216 if (*p == 0)
217 continue;
218
219 mu_list_append(domain_list, strdup(p));
220 }
221
222 fclose(fp);
223 return 0;
224 }
225
226 /* Return true if we relay domain NAME */
227 int
relayed_domain_p(char * name)228 relayed_domain_p(char *name)
229 {
230 char *p = name;
231
232 while (p) {
233 if (mu_list_locate(domain_list, p, NULL) == 0) {
234 mu_debug(MF_SOURCE_MAIN, MU_DEBUG_TRACE5,
235 ("%s is in relayed domain %s", name, p));
236 return 1;
237 }
238 p = strchr(p, '.');
239 if (p)
240 p++;
241 }
242 return 0;
243 }
244
245 /* Return true if CLIENT represents a host we relay. CLIENT is a dotted-quad
246 IP address. */
247 int
host_in_relayed_domain_p(char * client)248 host_in_relayed_domain_p(char *client)
249 {
250 int rc;
251 char *hbuf;
252
253 if (mu_list_is_empty(domain_list))
254 return 0;
255
256 if (resolve_ipstr(client, &hbuf))
257 return 0;
258 rc = relayed_domain_p(hbuf);
259 free(hbuf);
260 return rc;
261 }
262
263 static mu_list_t relayed_domain_files;
264
265 static int
load_relay_file(void * item,void * data)266 load_relay_file(void *item, void *data)
267 {
268 read_domain_file(item);
269 return 0;
270 }
271
272 static void
init_relayed_domains(void)273 init_relayed_domains(void)
274 {
275 mu_list_foreach(relayed_domain_files, load_relay_file, NULL);
276 mu_list_destroy(&relayed_domain_files);
277 }
278
279 /* Command line parsing */
280
281 const char *program_version = "mailfromd (" PACKAGE_STRING ")";
282 static char prog_doc[] = N_("mailfromd -- a general purpose milter daemon");
283 static char args_doc[] = "[var=value...][SCRIPT]";
284
285 static void
opt_testmode(struct mu_parseopt * po,struct mu_option * op,char const * arg)286 opt_testmode(struct mu_parseopt *po, struct mu_option *op, char const *arg)
287 {
288 mode = MAILFROMD_TEST;
289 if (arg) {
290 test_state = string_to_state(arg);
291 if (test_state == smtp_state_none) {
292 mu_parseopt_error(po,
293 _("unknown smtp state tag: %s"),
294 arg);
295 exit(po->po_exit_error);
296 }
297 log_stream = "stderr";
298 need_script = 1;
299 }
300 }
301
302 static void
opt_runmode(struct mu_parseopt * po,struct mu_option * op,char const * arg)303 opt_runmode(struct mu_parseopt *po, struct mu_option *op, char const *arg)
304 {
305 mode = MAILFROMD_RUN;
306 if (arg)
307 main_function_name = mu_strdup (arg);
308 log_stream = "stderr";
309 need_script = 1;
310 }
311
312 static void
opt_lint(struct mu_parseopt * po,struct mu_option * op,char const * arg)313 opt_lint(struct mu_parseopt *po, struct mu_option *op, char const *arg)
314 {
315 log_stream = "stderr";
316 script_check = 1;
317 need_script = 1;
318 }
319
320 static void
opt_show_defaults(struct mu_parseopt * po,struct mu_option * op,char const * arg)321 opt_show_defaults(struct mu_parseopt *po, struct mu_option *op,
322 char const *arg)
323 {
324 mode = MAILFROMD_SHOW_DEFAULTS;
325 need_script = 0;
326 }
327
328 static void
opt_daemon(struct mu_parseopt * po,struct mu_option * op,char const * arg)329 opt_daemon(struct mu_parseopt *po, struct mu_option *op,
330 char const *arg)
331 {
332 mode = MAILFROMD_DAEMON;
333 need_script = 1;
334 }
335
336 static void
opt_include_dir(struct mu_parseopt * po,struct mu_option * op,char const * arg)337 opt_include_dir(struct mu_parseopt *po, struct mu_option *op,
338 char const *arg)
339 {
340 add_include_dir(arg);
341 }
342
343 static void
opt_milter_socket(struct mu_parseopt * po,struct mu_option * op,char const * arg)344 opt_milter_socket(struct mu_parseopt *po, struct mu_option *op,
345 char const *arg)
346 {
347 mf_srvcfg_add("default", arg);
348 }
349
350 static void
opt_callout_socket(struct mu_parseopt * po,struct mu_option * op,char const * arg)351 opt_callout_socket(struct mu_parseopt *po, struct mu_option *op,
352 char const *arg)
353 {
354 mf_srvcfg_add("callout", arg);
355 free(callout_server_url);
356 callout_server_url = mu_strdup(arg);
357 }
358
359 static void
opt_mtasim(struct mu_parseopt * po,struct mu_option * op,char const * arg)360 opt_mtasim(struct mu_parseopt *po, struct mu_option *op,
361 char const *arg)
362 {
363 mtasim_option = 1;
364 server_flags |= MF_SERVER_FOREGROUND;
365 }
366
367 static void
opt_optimize(struct mu_parseopt * po,struct mu_option * op,char const * arg)368 opt_optimize(struct mu_parseopt *po, struct mu_option *op,
369 char const *arg)
370 {
371 if (!arg)
372 optimization_level = 1;
373 else {
374 char *p;
375 optimization_level = strtoul(arg, &p, 0);
376 if (*p)
377 mu_parseopt_error(po,
378 _("-O level must be a non-negative integer"));
379 }
380 }
381
382 static void
opt_variable(struct mu_parseopt * po,struct mu_option * op,char const * arg)383 opt_variable(struct mu_parseopt *po, struct mu_option *op,
384 char const *arg)
385 {
386 char *p;
387 struct mu_locus_range locus = {{ "<command line>", 1, 0 }};
388
389 p = strchr(arg, '=');
390 if (!p) {
391 mu_parseopt_error(po,
392 _("expected assignment, but found `%s'"),
393 arg);
394 exit(po->po_exit_error);
395 }
396 *p++ = 0;
397 defer_initialize_variable(arg, p, &locus);
398 }
399
400 static void
opt_relayed_domain_file(struct mu_parseopt * po,struct mu_option * op,char const * arg)401 opt_relayed_domain_file(struct mu_parseopt *po, struct mu_option *op,
402 char const *arg)
403 {
404 if (!relayed_domain_files)
405 mu_list_create(&relayed_domain_files);
406 mu_list_append(relayed_domain_files, mu_strdup(arg));
407 }
408
409 static void
opt_clear_ext_pp(struct mu_parseopt * po,struct mu_option * op,char const * arg)410 opt_clear_ext_pp(struct mu_parseopt *po, struct mu_option *op, char const *arg)
411 {
412 ext_pp = NULL;
413 }
414
415 static void
opt_define(struct mu_parseopt * po,struct mu_option * op,char const * arg)416 opt_define(struct mu_parseopt *po, struct mu_option *op, char const *arg)
417 {
418 ext_pp_options_given = 1;
419 add_pp_option("-D", arg);
420 }
421
422 static void
opt_undefine(struct mu_parseopt * po,struct mu_option * op,char const * arg)423 opt_undefine(struct mu_parseopt *po, struct mu_option *op, char const *arg)
424 {
425 ext_pp_options_given = 1;
426 add_pp_option("-U", arg);
427 }
428
429 static void
opt_set_milter_timeout(struct mu_parseopt * po,struct mu_option * op,char const * arg)430 opt_set_milter_timeout(struct mu_parseopt *po, struct mu_option *op,
431 char const *arg)
432 {
433 time_t v;
434
435 if (mu_str_to_c(arg, mu_c_time, &v, NULL)) {
436 mu_parseopt_error(po, _("%s: not a valid interval"), arg);
437 exit(po->po_exit_error);
438 }
439
440 if (smfi_settimeout(v) == MI_FAILURE) {
441 mu_parseopt_error(po, _("invalid milter timeout: %s"), arg);
442 exit(po->po_exit_error);
443 }
444 }
445
446 static void
destroy_trace_item(void * ptr)447 destroy_trace_item(void *ptr)
448 {
449 free(ptr);
450 }
451
452 static void
opt_trace_program(struct mu_parseopt * po,struct mu_option * op,char const * arg)453 opt_trace_program(struct mu_parseopt *po, struct mu_option *op,
454 char const *arg)
455 {
456 if (!trace_modules) {
457 mu_list_create(&trace_modules);
458 mu_list_set_destroy_item(trace_modules, destroy_trace_item);
459 }
460 mu_list_append(trace_modules, mu_strdup(arg ? arg : "all"));
461 }
462
463 static void
opt_dump_code(struct mu_parseopt * po,struct mu_option * op,char const * arg)464 opt_dump_code(struct mu_parseopt *po, struct mu_option *op,
465 char const *arg)
466 {
467 script_dump_code = 1;
468 log_stream = "stderr";
469 }
470
471 static void
opt_dump_grammar_trace(struct mu_parseopt * po,struct mu_option * op,char const * arg)472 opt_dump_grammar_trace(struct mu_parseopt *po, struct mu_option *op,
473 char const *arg)
474 {
475 script_ydebug = 1;
476 log_stream = "stderr";
477 }
478
479 static void
opt_dump_lex_trace(struct mu_parseopt * po,struct mu_option * op,char const * arg)480 opt_dump_lex_trace(struct mu_parseopt *po, struct mu_option *op,
481 char const *arg)
482 {
483 yy_flex_debug = 1;
484 log_stream = "stderr";
485 }
486
487 static void
opt_dump_macros(struct mu_parseopt * po,struct mu_option * op,char const * arg)488 opt_dump_macros(struct mu_parseopt *po, struct mu_option *op,
489 char const *arg)
490 {
491 script_dump_macros = 1;
492 log_stream = "stderr";
493 }
494
495 static void
opt_dump_tree(struct mu_parseopt * po,struct mu_option * op,char const * arg)496 opt_dump_tree(struct mu_parseopt *po, struct mu_option *op,
497 char const *arg)
498 {
499 script_dump_tree = 1;
500 log_stream = "stderr";
501 }
502
503 static void
opt_gacopyz_log(struct mu_parseopt * po,struct mu_option * op,char const * arg)504 opt_gacopyz_log(struct mu_parseopt *po, struct mu_option *op,
505 char const *arg)
506 {
507 int lev = gacopyz_string_to_log_level(arg);
508 if (lev == -1) {
509 mu_parseopt_error(po, _("%s: invalid log level"), arg);
510 exit(po->po_exit_error);
511 }
512 milter_setlogmask(SMI_LOG_FROM(lev));
513 }
514
515 static void
opt_dump_xref(struct mu_parseopt * po,struct mu_option * op,char const * arg)516 opt_dump_xref(struct mu_parseopt *po, struct mu_option *op,
517 char const *arg)
518 {
519 script_dump_xref = 1;
520 log_stream = "stderr";
521 }
522
523 static struct mu_option mailfromd_options[] = {
524 MU_OPTION_GROUP(N_("Operation modifiers")),
525 { "test", 't', N_("HANDLER"), MU_OPTION_ARG_OPTIONAL,
526 N_("run in test mode"),
527 mu_c_string, NULL, opt_testmode },
528 { "run", 0, N_("FUNC"), MU_OPTION_ARG_OPTIONAL,
529 N_("run script and execute function FUNC (\"main\" if not given)"),
530 mu_c_string, NULL, opt_runmode },
531 { "lint", 0, NULL, MU_OPTION_DEFAULT,
532 N_("check syntax and exit"),
533 mu_c_string, NULL, opt_lint },
534 { "syntax-check", 0, NULL, MU_OPTION_ALIAS, },
535 { "show-defaults", 0, NULL, MU_OPTION_DEFAULT,
536 N_("show compilation defaults"),
537 mu_c_string, NULL, opt_show_defaults },
538
539 { "daemon", 0, NULL, MU_OPTION_DEFAULT,
540 N_("run in daemon mode (default)"),
541 mu_c_string, NULL, opt_daemon },
542
543 { NULL, 'E', NULL, MU_OPTION_DEFAULT,
544 N_("preprocess source files and exit"),
545 mu_c_bool, &preprocess_option },
546 /* Reserved for future use: */
547 { "compile", 'c', NULL, MU_OPTION_HIDDEN,
548 N_("compile files"),
549 mu_c_void },
550 { "load", 'l', N_("FILE"), MU_OPTION_HIDDEN,
551 N_("load library"),
552 mu_c_void },
553 { "load-dir", 'L', N_("DIR"), MU_OPTION_HIDDEN,
554 N_("add DIR to the load path"),
555 mu_c_void },
556
557 MU_OPTION_GROUP(N_("General options")),
558 { "include", 'I', N_("DIR"), MU_OPTION_DEFAULT,
559 N_("add the directory DIR to the list of directories to be "
560 "searched for header files"),
561 mu_c_string, NULL, opt_include_dir },
562 { "port", 'p', N_("STRING"), MU_OPTION_DEFAULT,
563 N_("set communication socket"),
564 mu_c_string, NULL, opt_milter_socket },
565 { "milter-socket", 0, NULL, MU_OPTION_ALIAS },
566 { "callout-socket", 0, N_("STRING"), MU_OPTION_DEFAULT,
567 N_("set callout socket"),
568 mu_c_string, NULL, opt_callout_socket },
569 { "mtasim", 0, NULL, MU_OPTION_IMMEDIATE,
570 N_("run in mtasim compatibility mode"),
571 mu_c_string, NULL, opt_mtasim },
572 { "optimize", 'O', N_("LEVEL"), MU_OPTION_ARG_OPTIONAL,
573 N_("set code optimization level"),
574 mu_c_string, NULL, opt_optimize },
575 { "variable", 'v', N_("VAR=VALUE"), MU_OPTION_DEFAULT,
576 N_("assign VALUE to VAR"),
577 mu_c_string, NULL, opt_variable },
578 { "relayed-domain-file", 0, N_("FILE"), MU_OPTION_DEFAULT,
579 N_("read relayed domains from FILE"),
580 mu_c_string, NULL, opt_relayed_domain_file },
581 { "resolv-conf-file", 0, N_("FILE"), MU_OPTION_DEFAULT,
582 N_("read resolver configuration from FILE"),
583 mu_c_string, &resolv_conf_file },
584
585 MU_OPTION_GROUP(N_("Preprocessor options")),
586 { "preprocessor", 0, N_("COMMAND"), MU_OPTION_DEFAULT,
587 N_("use command as external preprocessor"),
588 mu_c_string, &ext_pp },
589 { "no-preprocessor", 0, NULL, MU_OPTION_DEFAULT,
590 N_("disable the use of external preprocessor"),
591 mu_c_string, NULL, opt_clear_ext_pp },
592 { "define", 'D', N_("NAME[=VALUE]"), MU_OPTION_DEFAULT,
593 N_("define a preprocessor symbol NAME as having VALUE, or empty"),
594 mu_c_string, NULL, opt_define },
595 { "undefine", 'U', N_("NAME"), MU_OPTION_DEFAULT,
596 N_("undefine a preprocessor symbol NAME"),
597 mu_c_string, NULL, opt_undefine },
598
599 MU_OPTION_GROUP(N_("Timeout control")),
600 { "milter-timeout", 0, N_("TIME"), MU_OPTION_DEFAULT,
601 N_("set MTA connection timeout"),
602 mu_c_string, NULL, opt_set_milter_timeout },
603
604 MU_OPTION_GROUP (N_("Informational and debugging options")),
605 { "location-column", 0, NULL, MU_OPTION_DEFAULT,
606 N_("print location column numbers in compiler diagnostics messages"),
607 mu_c_bool, &location_column_option },
608 { "trace", 0, NULL, MU_OPTION_DEFAULT,
609 N_("trace executed actions"),
610 mu_c_bool, &trace_option },
611 { "trace-program", 0, N_("MODULES"), MU_OPTION_ARG_OPTIONAL,
612 N_("enable filter program tracing"),
613 mu_c_string, NULL, opt_trace_program },
614
615 { "dump-code", 0, NULL, MU_OPTION_DEFAULT,
616 N_("dump disassembled code"),
617 mu_c_string, NULL, opt_dump_code },
618
619 { "dump-grammar-trace", 0, NULL, MU_OPTION_DEFAULT,
620 N_("dump grammar traces"),
621 mu_c_string, NULL, opt_dump_grammar_trace },
622 { "dump-lex-trace", 0, NULL, MU_OPTION_DEFAULT,
623 N_("dump lexical analyzer traces"),
624 mu_c_string, NULL, opt_dump_lex_trace },
625 { "dump-tree", 0, NULL, MU_OPTION_DEFAULT,
626 N_("dump parser tree"),
627 mu_c_string, NULL, opt_dump_tree },
628 { "dump-macros", 0, NULL, MU_OPTION_DEFAULT,
629 N_("show used Sendmail macros"),
630 mu_c_string, NULL, opt_dump_macros },
631 { "xref", 0, NULL, MU_OPTION_DEFAULT,
632 N_("produce a cross-reference listing"),
633 mu_c_string, NULL, opt_dump_xref },
634 { "dump-xref", 0, NULL, MU_OPTION_ALIAS },
635 { "gacopyz-log", 0, N_("LEVEL"), MU_OPTION_DEFAULT,
636 N_("set Gacopyz log level"),
637 mu_c_string, NULL, opt_gacopyz_log },
638 { "stack-trace", 0, NULL, MU_OPTION_DEFAULT,
639 N_("enable stack traces on runtime errors"),
640 mu_c_bool, &stack_trace_option },
641
642 MU_OPTION_END
643 }, *options[] = { mailfromd_options, NULL };
644
645 static int
validate_options()646 validate_options()
647 {
648 /* FIXME */
649 return 0;
650 }
651
652 static int
flush_trace_module(void * item,void * data)653 flush_trace_module(void *item, void *data)
654 {
655 enable_prog_trace((const char *) item);
656 return 0;
657 }
658
659 static char *capa[] = {
660 "auth",
661 "debug",
662 "logging",
663 "locking",
664 "mailer",
665 ".mfd:server",
666 NULL
667 };
668
669
670 /* Mailutils configuration */
671
672 static int
cb_milter_timeout(void * data,mu_config_value_t * arg)673 cb_milter_timeout(void *data, mu_config_value_t *arg)
674 {
675 struct timeval tv;
676 int rc = config_cb_timeout (&tv, arg);
677
678 if (rc)
679 return 1;
680 if (smfi_settimeout(tv.tv_sec) == MI_FAILURE) {
681 mu_error(_("Invalid milter timeout: %lu"),
682 (unsigned long) tv.tv_sec);
683 exit(EX_USAGE);
684 }
685
686 return 0;
687 }
688
689 static int
cb_set_variable(void * data,mu_config_value_t * arg)690 cb_set_variable(void *data, mu_config_value_t *arg)
691 {
692 const char *value;
693 char *alloc_str = NULL;
694 struct mu_locus_range locus = MU_LOCUS_RANGE_INITIALIZER;
695
696 if (mu_cfg_assert_value_type(arg, MU_CFG_ARRAY))
697 return 1;
698 if (arg->v.arg.c < 2) {
699 mu_error(_("not enough arguments"));
700 return 1;
701 } else if (arg->v.arg.c > 2) {
702 mu_error(_("too many arguments"));
703 return 1;
704 }
705
706 if (mu_cfg_assert_value_type(&arg->v.arg.v[0], MU_CFG_STRING))
707 return 1;
708
709 switch (arg->v.arg.v[1].type) {
710 case MU_CFG_STRING:
711 value = arg->v.arg.v[1].v.string;
712 break;
713
714 case MU_CFG_ARRAY:
715 alloc_str = config_array_to_string(&arg->v.arg.v[1]);
716 value = alloc_str;
717 break;
718
719 case MU_CFG_LIST:
720 mu_error(_("unexpected list"));
721 return 1;
722
723 default:
724 mu_error (_("INTERNAL ERROR at %s:%d: please report"),
725 __FILE__, __LINE__);
726 abort();
727 }
728
729 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
730 MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE, &locus);
731 defer_initialize_variable(arg->v.arg.v[0].v.string, value, &locus);
732 mu_locus_range_deinit(&locus);
733 free(alloc_str);
734 return 0;
735 }
736
737 static int
cb_include_path(void * data,mu_config_value_t * val)738 cb_include_path(void *data, mu_config_value_t *val)
739 {
740 int i, rc = 0;
741 struct mu_wordsplit ws;
742 mu_iterator_t itr;
743
744 switch (val->type) {
745 case MU_CFG_STRING:
746 ws.ws_delim = ":";
747 if (mu_wordsplit(val->v.string, &ws,
748 MU_WRDSF_NOVAR |
749 MU_WRDSF_NOCMD | MU_WRDSF_DELIM)) {
750 mu_error("mu_wordsplit: %s",
751 mu_wordsplit_strerror(&ws));
752 return 1;
753 }
754 for (i = 0; i < ws.ws_wordc; i++)
755 add_include_dir(ws.ws_wordv[i]);
756 mu_wordsplit_free(&ws);
757 break;
758
759 case MU_CFG_ARRAY:
760 for (i = 0; i < val->v.arg.c; i++) {
761 if (mu_cfg_assert_value_type(&val->v.arg.v[i],
762 MU_CFG_STRING))
763 return 1;
764 add_include_dir(val->v.arg.v[i].v.string);
765 }
766 break;
767
768 case MU_CFG_LIST:
769 mu_list_get_iterator(val->v.list, &itr);
770 for (mu_iterator_first(itr);
771 !mu_iterator_is_done(itr); mu_iterator_next(itr)) {
772 mu_config_value_t *pval;
773 mu_iterator_current(itr, (void*) &pval);
774 rc = mu_cfg_assert_value_type(pval, MU_CFG_STRING);
775 if (rc)
776 break;
777 add_include_dir(pval->v.string);
778 }
779 mu_iterator_destroy(&itr);
780 }
781 return rc;
782 }
783
784 static int
cb_trace_program(void * data,mu_config_value_t * val)785 cb_trace_program(void *data, mu_config_value_t *val)
786 {
787 int i, rc = 0;
788
789 switch (val->type) {
790 case MU_CFG_STRING:
791 enable_prog_trace(val->v.string);
792 break;
793
794 case MU_CFG_ARRAY:
795 for (i = 0; i < val->v.arg.c; i++) {
796 if (mu_cfg_assert_value_type(&val->v.arg.v[i],
797 MU_CFG_STRING))
798 return 1;
799 enable_prog_trace(val->v.arg.v[i].v.string);
800 }
801 break;
802
803 case MU_CFG_LIST:
804 mu_list_foreach(val->v.list, flush_trace_module, NULL);
805 break;
806 }
807 return rc;
808 }
809
810 static int
cb_relayed_domain_file(void * data,mu_config_value_t * val)811 cb_relayed_domain_file(void *data, mu_config_value_t *val)
812 {
813 switch (val->type) {
814 case MU_CFG_STRING:
815 read_domain_file(val->v.string);
816 break;
817
818 case MU_CFG_LIST:
819 mu_list_foreach(val->v.list, load_relay_file, NULL);
820 break;
821
822 default:
823 mu_error (_("expected string or list of strings"));
824 }
825 return 0;
826 }
827
828
829 struct mu_cfg_param mf_cfg_param[] = {
830 { ".mfd:server", mu_cfg_section, NULL, 0, NULL, NULL },
831 { "stack-trace", mu_c_bool, &stack_trace_option, 0, NULL,
832 N_("Dump stack traces on runtime errors.") },
833
834 { "milter-timeout", mu_cfg_callback, NULL, 0, cb_milter_timeout,
835 N_("Set milter timeout."),
836 N_("time: interval") },
837 { "callout-url", mu_c_string, &callout_server_url, 0, NULL,
838 N_("Sets the URL of the default callout server."),
839 N_("url") },
840 { "include-path", mu_cfg_callback, NULL, 0, cb_include_path,
841 N_("Add directories to the list of directories to be searched for "
842 "header files. Argument is a list of directory names "
843 "separated by colons."),
844 N_("path: string") },
845 { "setvar", mu_cfg_callback, NULL, 0, cb_set_variable,
846 N_("Initialize a mailfromd variable <var> to <value>."),
847 N_("var: string> <value: string-or-number") },
848 { "script-file", mu_c_string, &script_file, 0, NULL,
849 N_("Read filter script from <file>."),
850 N_("file") },
851 { "trace-actions", mu_c_bool, &do_trace, 0, NULL,
852 N_("Trace executed actions.") },
853 { "trace-program", mu_cfg_callback, NULL, 0, cb_trace_program,
854 N_("Enable filter program tracing."),
855 N_("modules: list") },
856
857 { "relayed-domain-file", mu_cfg_callback, NULL, 0,
858 cb_relayed_domain_file,
859 N_("Read relayed domain names from the file"),
860 N_("file: string") },
861
862 { "lock-retry-count", mu_cfg_callback, NULL, 0,
863 config_cb_lock_retry_count,
864 N_("Retry acquiring DBM file lock this number of times.") },
865 { "lock-retry-timeout", mu_cfg_callback, NULL, 0,
866 config_cb_lock_retry_timeout,
867 N_("Set the time span between the two DBM locking attempts."),
868 N_("time: interval") },
869
870 { "runtime", mu_cfg_section, NULL },
871
872 { NULL }
873 };
874
875
876 static struct mu_cfg_param *runtime_param;
877 static size_t runtime_param_cnt;
878 static size_t runtime_param_max;
879
880 static void
_add_runtime_param_entry(struct mu_cfg_param * p)881 _add_runtime_param_entry(struct mu_cfg_param *p)
882 {
883 if (runtime_param_cnt == runtime_param_max) {
884 if (runtime_param_max == 0)
885 runtime_param_max = 16;
886 runtime_param = mu_2nrealloc(runtime_param,
887 &runtime_param_max,
888 sizeof(runtime_param[0]));
889 }
890 runtime_param[runtime_param_cnt++] = *p;
891 }
892
893 void
mf_add_runtime_params(struct mu_cfg_param * p)894 mf_add_runtime_params(struct mu_cfg_param *p)
895 {
896 for (; p->ident; p++)
897 _add_runtime_param_entry(p);
898 }
899
900 void
mf_runtime_param_finish()901 mf_runtime_param_finish()
902 {
903 if (runtime_param_cnt) {
904 static struct mu_cfg_param term = { NULL };
905 struct mu_cfg_section *section;
906
907 _add_runtime_param_entry(&term);
908
909 if (mu_create_canned_section ("runtime", §ion) == 0) {
910 section->parser = NULL;
911 section->docstring = N_("Configure MFL runtime values.");
912 section->label = NULL;
913 mu_cfg_section_add_params(section, runtime_param);
914 }
915 } else {
916 struct mu_cfg_param *p;
917 for (p = mf_cfg_param; p->ident; p++) {
918 if (strcmp (p->ident, "runtime") == 0) {
919 memset (p, 0, sizeof (*p));
920 break;
921 }
922 }
923 }
924 }
925
926
927
928 /* Auxiliary functions */
929 unsigned keyword_column = 0;
930 unsigned header_column = 2;
931 unsigned value_column = 32;
932 unsigned right_margin = 79;
933
934 static void
set_column(mu_stream_t str,unsigned margin)935 set_column(mu_stream_t str, unsigned margin)
936 {
937 mu_stream_ioctl(str, MU_IOCTL_WORDWRAPSTREAM,
938 MU_IOCTL_WORDWRAP_SET_MARGIN,
939 &margin);
940 }
941
942 static int
db_format_enumerator(struct db_format * fmt,void * data)943 db_format_enumerator(struct db_format *fmt, void *data)
944 {
945 mu_stream_t str = data;
946
947 set_column(str, keyword_column);
948 mu_stream_printf(str, "%s database:", fmt->name);
949 set_column(str, value_column);
950 mu_stream_printf(str, "%s\n", fmt->dbname);
951
952 set_column(str, keyword_column);
953 if (strcmp(fmt->name, "cache") == 0) {
954 mu_stream_printf(str, "%s positive expiration:", fmt->name);
955 set_column(str, value_column);
956 mu_stream_printf(str, "%lu\n", fmt->expire_interval);
957
958 set_column(str, keyword_column);
959 mu_stream_printf(str, "%s negative expiration:", fmt->name);
960 set_column(str, value_column);
961 mu_stream_printf(str, "%lu\n", negative_expire_interval);
962 } else {
963 mu_stream_printf(str, "%s expiration:", fmt->name);
964 set_column(str, value_column);
965 mu_stream_printf(str, "%lu\n", fmt->expire_interval);
966 }
967 return 0;
968 }
969
970 static void
list_db_formats(mu_stream_t str,const char * pfx)971 list_db_formats(mu_stream_t str, const char *pfx)
972 {
973 mu_iterator_t itr;
974 int rc;
975 const char *defdb = DEFAULT_DB_TYPE;
976
977 set_column(str, keyword_column);
978 mu_stream_printf(str, "%s:", pfx);
979
980 set_column(str, value_column);
981
982 rc = mu_dbm_impl_iterator(&itr);
983 if (rc) {
984 mu_stream_printf(str, "%s\n", _("unknown"));
985 mu_error("%s", mu_strerror(rc));
986 } else {
987 int i;
988 for (mu_iterator_first(itr), i = 0; !mu_iterator_is_done(itr);
989 mu_iterator_next(itr), i++) {
990 struct mu_dbm_impl *impl;
991
992 mu_iterator_current(itr, (void**)&impl);
993 if (i)
994 mu_stream_printf(str, ", ");
995 else if (!defdb)
996 defdb = impl->_dbm_name;
997 mu_stream_printf(str, "%s", impl->_dbm_name);
998 }
999 mu_stream_write(str, "\n", 1, NULL);
1000 mu_iterator_destroy(&itr);
1001 }
1002
1003 set_column(str, keyword_column);
1004 mu_stream_printf(str, "%s:", "default database type");
1005 set_column(str, value_column);
1006 mu_stream_printf(str, "%s\n", defdb);
1007 }
1008
1009 struct string_value {
1010 char const *kw;
1011 int type;
1012 union {
1013 char *s_const;
1014 char **s_var;
1015 char *(*s_func) (void);
1016 } data;
1017 };
1018
1019 static char *
string_preprocessor(void)1020 string_preprocessor (void)
1021 {
1022 return ext_pp ? ext_pp : "none";
1023 }
1024
1025 #ifdef USE_SYSLOG_ASYNC
1026 # if DEFAULT_SYSLOG_ASYNC == 1
1027 # define DEFAULT_SYSLOG "non-blocking"
1028 # else
1029 # define DEFAULT_SYSLOG "blocking"
1030 # endif
1031 #else
1032 # define DEFAULT_SYSLOG "blocking"
1033 #endif
1034
1035 enum {
1036 STRING_CONSTANT,
1037 STRING_VARIABLE,
1038 STRING_FUNCTION
1039 };
1040
1041 static struct string_value string_values[] = {
1042 { "version", STRING_CONSTANT, { .s_const = VERSION } },
1043 { "script file", STRING_VARIABLE, { .s_var = &script_file } },
1044 { "preprocessor", STRING_FUNCTION, { .s_func = string_preprocessor } },
1045 { "user", STRING_VARIABLE, { .s_var = &mf_server_user } },
1046 { "statedir", STRING_VARIABLE, { .s_var = &mailfromd_state_dir } },
1047 { "socket", STRING_CONSTANT, { .s_const = DEFAULT_SOCKET } },
1048 { "pidfile", STRING_VARIABLE, { .s_var = &pidfile } },
1049 { "default syslog", STRING_CONSTANT, { .s_const = DEFAULT_SYSLOG } },
1050 { NULL }
1051 };
1052
1053 static void
print_string_values(mu_stream_t str)1054 print_string_values(mu_stream_t str)
1055 {
1056 struct string_value *p;
1057 char const *val;
1058
1059 for (p = string_values; p->kw; p++) {
1060 set_column(str, keyword_column);
1061 mu_stream_printf(str, "%s:", p->kw);
1062
1063 switch (p->type) {
1064 case STRING_CONSTANT:
1065 val = p->data.s_const;
1066 break;
1067
1068 case STRING_VARIABLE:
1069 val = *p->data.s_var;
1070 break;
1071
1072 case STRING_FUNCTION:
1073 val = p->data.s_func ();
1074 }
1075
1076 set_column(str, value_column);
1077 mu_stream_printf(str, "%s\n", val);
1078 }
1079 }
1080
1081 void
mailfromd_show_defaults(void)1082 mailfromd_show_defaults(void)
1083 {
1084 int rc;
1085 mu_stream_t str;
1086
1087 rc = mu_wordwrap_stream_create (&str, mu_strout, 0, right_margin);
1088 if (rc) {
1089 str = mu_strout;
1090 mu_stream_ref(str);
1091 }
1092
1093 print_string_values(str);
1094
1095 list_db_formats(str, "supported databases");
1096
1097 set_column(str, keyword_column);
1098 mu_stream_printf(str, "%s:", "optional features");
1099 set_column(str, value_column);
1100 #if defined WITH_DKIM
1101 mu_stream_printf(str, " %s", "DKIM");
1102 #endif
1103 #if defined WITH_GEOIP
1104 mu_stream_printf(str, " %s", "GeoIP");
1105 #endif
1106 #if defined WITH_GEOIP2
1107 mu_stream_printf(str, " %s", "GeoIP2");
1108 #endif
1109 #if defined WITH_DSPAM
1110 mu_stream_printf(str, " %s", "DSPAM");
1111 #endif
1112 mu_stream_write(str, "\n", 1, NULL);
1113
1114 db_format_enumerate(db_format_enumerator, str);
1115
1116 mu_stream_destroy (&str);
1117 }
1118
1119 static int
args_in_order(int argc,char ** argv)1120 args_in_order(int argc, char **argv)
1121 {
1122 int i;
1123 for (i = 0; i < argc; i++) {
1124 size_t len = strcspn(argv[i], "=");
1125 if (len > 3
1126 && (memcmp(argv[i], "--ru", 4) == 0
1127 || memcmp(argv[i], "--run", 5) == 0)) {
1128 return MF_GETOPT_IN_ORDER;
1129 }
1130 }
1131 return MF_GETOPT_DEFAULT;
1132 }
1133
1134
1135 static void
provide_default_milter_server(void)1136 provide_default_milter_server(void)
1137 {
1138 if (mfd_srvman_count_servers() == 0) {
1139 mu_diag_output(MU_DIAG_WARNING,
1140 _("no servers defined; will listen on %s"),
1141 DEFAULT_SOCKET);
1142 mf_srvcfg_add("milter", DEFAULT_SOCKET);
1143 }
1144 }
1145
1146 static void
provide_default_callout_server(void)1147 provide_default_callout_server(void)
1148 {
1149 struct variable *var;
1150
1151 if (provide_callout &&
1152 !callout_server_url &&
1153 (!(var = variable_lookup("callout_server_url")) ||
1154 (var->sym.flags & SYM_REFERENCED) &&
1155 !(var->sym.flags & SYM_INITIALIZED))) {
1156 mf_srvcfg_add("callout", DEFAULT_CALLOUT_SOCKET);
1157 }
1158 }
1159
1160
1161 static char *modnames[] = {
1162 #define __DBGMOD_C_ARRAY
1163 # include "mfd-dbgmod.h"
1164 #undef __DBGMOD_C_ARRAY
1165 NULL
1166 };
1167
1168 mu_debug_handle_t mfd_debug_handle;
1169
1170 static void
debug_init(void)1171 debug_init(void)
1172 {
1173 int i;
1174
1175 mfd_debug_handle = mu_debug_register_category (modnames[0]);
1176 for (i = 1; modnames[i]; i++)
1177 mu_debug_register_category (modnames[i]);
1178 }
1179
1180
1181 int
mf_server_function(const char * key,struct mf_srvcfg * cfg)1182 mf_server_function(const char *key, struct mf_srvcfg *cfg)
1183 {
1184 if (!key || strcmp(key, "default") == 0 || strcmp(key, "milter") == 0)
1185 cfg->server = milter_session_server;
1186 else if (strcmp(key, "callout") == 0) {
1187 cfg->server = mfd_callout_session_server;
1188 if (cfg->defopt ||
1189 mu_list_locate(cfg->options, "default", NULL) == 0)
1190 callout_server_url =
1191 mu_strdup(mu_url_to_string(cfg->url));
1192 } else
1193 return 1;
1194 return 0;
1195 }
1196
1197 static void
open_strecho(int daemon_mode)1198 open_strecho (int daemon_mode)
1199 {
1200 int rc;
1201 if (daemon_mode) {
1202 rc = mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
1203 MU_IOCTL_LOGSTREAM_CLONE,
1204 &mf_strecho);
1205 if (rc == 0) {
1206 int s = MU_LOG_INFO;
1207 mu_stream_ioctl(mf_strecho, MU_IOCTL_LOGSTREAM,
1208 MU_IOCTL_LOGSTREAM_SET_SEVERITY,
1209 &s);
1210 }
1211 } else {
1212 #if 0
1213 mf_strecho = mu_strout;
1214 mu_stream_ref(mf_strecho);
1215 rc = 0;
1216 #else
1217 rc = mu_stdio_stream_create(&mf_strecho, MU_STDERR_FD, 0);
1218 #endif
1219 }
1220 if (rc) {
1221 mu_diag_output(MU_LOG_CRIT,
1222 _("cannot create echo output stream: %s"),
1223 mu_strerror(rc));
1224 exit(EX_UNAVAILABLE);
1225 }
1226 }
1227
1228 void
mailfromd_alloc_die()1229 mailfromd_alloc_die()
1230 {
1231 parse_error("not enough memory");
1232 abort();
1233 }
1234
1235 extern char **environ;
1236
1237 struct mu_cli_setup cli = {
1238 .optv = options,
1239 .cfg = mf_cfg_param,
1240 .prog_doc = prog_doc,
1241 .prog_args = args_doc
1242 };
1243
1244 int
main(int argc,char ** argv)1245 main(int argc, char **argv)
1246 {
1247 prog_counter_t entry_point;
1248 int stderr_is_closed = stderr_closed_p();
1249
1250 mf_init_nls();
1251 mf_proctitle_init(argc, argv, environ);
1252 mu_alloc_die_hook = mailfromd_alloc_die;
1253
1254 MU_AUTH_REGISTER_ALL_MODULES();
1255 mu_register_all_formats();
1256 mu_register_all_mailer_formats();
1257 yy_flex_debug = 0;
1258
1259 /* Initialize milter */
1260 milter_setlogmask(SMI_LOG_FROM(SMI_LOG_WARN));
1261 milter_settimeout(7200);
1262
1263 /* Set default logging */
1264 mu_set_program_name(argv[0]);
1265 mu_log_tag = mu_strdup(mu_program_name);
1266 mu_log_facility = DEFAULT_LOG_FACILITY;
1267 mu_stdstream_setup(MU_STDSTREAM_RESET_NONE);
1268 mf_srvcfg_log_setup(stderr_is_closed ? "syslog" : "stderr");
1269
1270 debug_init();
1271 libcallout_init();
1272 init_string_space();
1273 init_symbols();
1274 builtin_setup();
1275 mf_runtime_param_finish();
1276 db_format_setup();
1277 include_path_setup();
1278 pragma_setup();
1279
1280 mf_server_save_cmdline(argc, argv);
1281
1282 dnsbase_init();
1283 mu_acl_cfg_init();
1284 database_cfg_init();
1285 srvman_init();
1286 mf_srvcfg_init(argv[0], "(milter | callout)");
1287
1288 mf_getopt(&cli, &argc, &argv, capa, args_in_order(argc, argv));
1289
1290 if (validate_options())
1291 exit(EX_USAGE);
1292
1293 init_relayed_domains();
1294 if (resolv_conf_file)
1295 dnsbase_file_init(resolv_conf_file);
1296
1297 if (trace_option != ARG_UNSET)
1298 do_trace = trace_option;
1299 if (trace_modules) {
1300 mu_list_foreach(trace_modules, flush_trace_module, NULL);
1301 mu_list_destroy(&trace_modules);
1302 }
1303
1304 mf_srvcfg_flush();
1305
1306 alloc_ext_pp();
1307
1308 if (need_script) {
1309 char *new_script = NULL;
1310 if (argc) {
1311 int i, n = -1;
1312 for (i = 0; i < argc; i++) {
1313 if (strchr(argv[i], '=') == 0) {
1314 if (n == -1) {
1315 n = i;
1316 if (mode == MAILFROMD_RUN)
1317 break;
1318 } else {
1319 mu_error(_("script file "
1320 "specified twice "
1321 "(%s and %s)"),
1322 argv[n], argv[i]);
1323 exit(EX_USAGE);
1324 }
1325 }
1326 }
1327 if (n >= 0) {
1328 new_script = argv[n];
1329 memmove(argv + n, argv + n + 1,
1330 (argc - n + 1) * sizeof argv[0]);
1331 argc--;
1332 }
1333 }
1334
1335 if (new_script)
1336 script_file = new_script;
1337
1338 if (script_file[0] != '/')
1339 /* Clear saved command line */
1340 mf_server_save_cmdline(0, NULL);
1341 if (preprocess_option)
1342 exit(preprocess_input());
1343 if (parse_program(script_file, script_ydebug))
1344 exit(EX_CONFIG);
1345 }
1346
1347 if (script_dump_tree)
1348 print_syntax_tree();
1349 if (script_dump_code)
1350 print_code();
1351
1352 if (script_dump_xref)
1353 print_xref();
1354
1355 if (script_dump_macros)
1356 print_used_macros();
1357
1358 fixup_code();
1359
1360 if (script_check || script_dump_macros
1361 || script_dump_code || script_dump_tree || script_dump_xref
1362 || yy_flex_debug || script_ydebug)
1363 exit(EX_OK);
1364
1365 switch (mode) {
1366 case MAILFROMD_DAEMON:
1367 mf_server_log_setup();
1368 provide_default_milter_server();
1369 provide_default_callout_server();
1370 break;
1371
1372 case MAILFROMD_RUN: {
1373 struct function *fun = function_lookup(main_function_name);
1374 if (!fun) {
1375 mu_error(_("function %s is not defined"),
1376 main_function_name);
1377 exit(EX_CONFIG);
1378 }
1379
1380 if (fun->parmcount || !fun->varargs) {
1381 mu_error(_("function %s must take variable number of "
1382 "arguments"),
1383 main_function_name);
1384 exit(EX_CONFIG);
1385 }
1386 if (fun->rettype != dtype_number) {
1387 mu_error(_("function %s must return number"),
1388 main_function_name);
1389 exit(EX_CONFIG);
1390 }
1391 entry_point = fun->entry;
1392 }
1393 }
1394
1395 free_exceptions();
1396 free_symbols();
1397 free_string_space();
1398 free_parser_data();
1399
1400 mf_namefixup_run(mailfromd_state_dir);
1401 mf_namefixup_free();
1402
1403 switch (mode) {
1404 case MAILFROMD_DAEMON:
1405 if (argc > 0) {
1406 mu_error(_("too many arguments"));
1407 exit(EX_USAGE);
1408 }
1409 if (script_file[0] != '/') {
1410 mu_diag_output(MU_DIAG_WARNING,
1411 _("script file is given "
1412 "without full file name"));
1413 server_flags |= MF_SERVER_NORESTART;
1414 }
1415 open_strecho(1);
1416 mf_server_lint_option = "--lint";
1417 mf_server_start("mailfromd", mailfromd_state_dir, pidfile,
1418 server_flags);
1419 break;
1420
1421 case MAILFROMD_TEST:
1422 open_strecho(0);
1423 mailfromd_test(argc, argv);
1424 break;
1425
1426 case MAILFROMD_SHOW_DEFAULTS:
1427 mailfromd_show_defaults();
1428 break;
1429
1430 case MAILFROMD_RUN:
1431 open_strecho(0);
1432 mailfromd_run(entry_point, argc, argv);
1433 }
1434
1435 exit(EX_OK);
1436 }
1437
1438