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
21 #include <mailutils/mailutils.h>
22 #include <mailutils/cli.h>
23 #include <mailutils/dbm.h>
24 #include <mailutils/alloc.h>
25 #include <sysexits.h>
26
27 #include "libmf.h"
28 #include "filenames.h"
29 #include "callout.h"
30 #include "srvman.h"
31 #include "gacopyz.h"
32 #include "srvcfg.h"
33 #include "mfdb.h"
34
35 char *mailfromd_state_dir;
36 int server_flags = 0;
37 char *log_stream = DEFAULT_LOG_STREAM;
38 char *pidfile;
39 int smtp_transcript;
40 static int transcript_option;
41 struct mu_sockaddr *source_address; /* Source address for TCP connections */
42 int mtasim_option; /* mtasim compatibility mode */
43 char *db_type_str = DEFAULT_DB_TYPE;
44
45 /* Timeouts */
46 time_t smtp_timeout_soft[SMTP_NUM_TIMEOUT] = {
47 10,
48 30,
49 0,
50 0,
51 0,
52 0,
53 0,
54 };
55
56 /* Hard timeouts comply to RFC 2821 */
57 time_t smtp_timeout_hard[SMTP_NUM_TIMEOUT] = {
58 5*60, /* smtp_timeout_connect */
59 5*60, /* smtp_timeout_initial */
60 5*60, /* smtp_timeout_helo */
61 10*60, /* smtp_timeout_mail */
62 5*60, /* smtp_timeout_rcpt */
63 5*60, /* smtp_timeout_rset */
64 2*60, /* smtp_timeout_quit */
65 };
66
67 /* I/O timeout. Overrides unset smtp_timeouts */
68 time_t io_timeout = 3;
69
70 static const char *
next_server_id()71 next_server_id()
72 {
73 static unsigned long count;
74 static char nbuf[INT_BUFSIZE_BOUND(unsigned long)];
75 count++;
76 snprintf(nbuf, sizeof(nbuf), "%lu", count);
77 return nbuf;
78 }
79
80 static mu_url_t
_parse_url(const char * str)81 _parse_url(const char *str)
82 {
83 mu_url_t url;
84 int rc;
85 const char *s;
86
87 rc = mu_url_create(&url, str);
88 if (rc) {
89 mu_error(_("cannot create URL from `%s': %s"),
90 str, mu_strerror(rc));
91 return NULL;
92 }
93 if (mu_url_sget_scheme(url, &s) == 0 && strcmp(s, "file") == 0)
94 mu_url_set_scheme(url, "unix");
95
96 return url;
97 }
98
99 mu_url_t
parse_milter_url(const char * str)100 parse_milter_url(const char *str)
101 {
102 mu_url_t url;
103
104 char *tmp;
105 char *proto, *port, *path;
106
107 /* FIXME: This is awkward. */
108 if (gacopyz_parse_connection(str,
109 &proto,
110 &port, &path) != MI_SUCCESS) {
111 mu_error(_("%s: error parsing URL"), str);
112 }
113 if (port) {
114 if (!proto)
115 mu_asprintf(&tmp, "unix://%s:%s", path, port);
116 else if (strcmp(proto, "inet6") == 0)
117 mu_asprintf(&tmp, "inet6://[%s]:%s", path, port);
118 else
119 mu_asprintf(&tmp, "%s://%s:%s", proto, path, port);
120 } else
121 mu_asprintf(&tmp, "%s://%s",
122 proto ? proto : "unix", path);
123 free(proto);
124 free(path);
125 free(port);
126 url = _parse_url(tmp);
127 free(tmp);
128 return url;
129 }
130
131 static int
mf_option_group(const char * arg)132 mf_option_group(const char *arg)
133 {
134 struct group *group = getgrnam(arg);
135 if (group) {
136 if (!mf_server_retain_groups)
137 mf_server_retain_groups = mf_gid_list_alloc();
138 mf_gid_list_add(mf_server_retain_groups, group->gr_gid);
139 } else {
140 mu_error(_("unknown group: %s"), arg);
141 return 1;
142 }
143 return 0;
144 }
145
146 static int
mf_option_state_directory(const char * arg)147 mf_option_state_directory(const char *arg)
148 {
149 struct stat st;
150 if (stat(arg, &st)) {
151 mu_error(_("cannot stat file `%s': %s"),
152 arg,
153 mu_strerror(errno));
154 return 1;
155 }
156 if (!S_ISDIR(st.st_mode)) {
157 mu_error(_("`%s' is not a directory"), arg);
158 return 1;
159 }
160 if (arg[0] != '/') {
161 mu_error(_("state directory `%s' is not an absolute "
162 "file name"), arg);
163 return 1;
164 }
165 mailfromd_state_dir = mu_strdup(arg);
166 return 0;
167 }
168
169 static int
set_source_ip(char const * arg)170 set_source_ip(char const *arg)
171 {
172 int rc;
173 struct mu_sockaddr_hints hints;
174
175 memset(&hints, 0, sizeof hints);
176 hints.family = AF_INET;
177 hints.socktype = SOCK_STREAM;
178 hints.protocol = IPPROTO_TCP;
179 rc = mu_sockaddr_from_node(&source_address, arg, NULL, &hints);
180 if (rc) {
181 mu_error(_("cannot convert %s to sockaddr: %s"),
182 arg, mu_strerror(rc));
183 return 1;
184 }
185 return 0;
186 }
187
188 void
mf_srvcfg_add(const char * type,const char * urlstr)189 mf_srvcfg_add(const char *type, const char *urlstr)
190 {
191 struct mf_srvcfg cfg;
192
193 memset(&cfg, 0, sizeof(cfg));
194 cfg.id = next_server_id();
195 cfg.url = parse_milter_url(urlstr);
196 if (cfg.url) {
197 mfd_server_t srv;
198
199 if (mf_server_function(type, &cfg)) {
200 mu_error(_("INTERNAL ERROR: no such server type: %s"),
201 type);
202 exit(EX_SOFTWARE);
203 } else if (!cfg.server) {
204 mu_error(_("INTERNAL ERROR at %s:%d: "
205 "server function not defined"),
206 __FILE__, __LINE__);
207 exit(EX_SOFTWARE);
208 }
209 srv = mfd_server_new(cfg.id, cfg.url, cfg.server, 0);
210 if (srv)
211 mfd_srvman_attach_server(srv);
212 }
213 }
214
215 /* FIXME: This duplicates the debug.level statement */
216 static int
cb_debug(void * data,mu_config_value_t * arg)217 cb_debug(void *data, mu_config_value_t *arg)
218 {
219 if (mu_cfg_assert_value_type(arg, MU_CFG_STRING))
220 return 1;
221 mu_debug_parse_spec(arg->v.string);
222 return 0;
223 }
224
225 /* See also option_group. */
226 static int
cb_group(void * data,mu_config_value_t * arg)227 cb_group(void *data, mu_config_value_t *arg)
228 {
229 if (mu_cfg_assert_value_type(arg, MU_CFG_STRING))
230 return 1;
231 return mf_option_group(arg->v.string);
232 }
233
234 static int
cb_source_ip(void * data,mu_config_value_t * arg)235 cb_source_ip(void *data, mu_config_value_t *arg)
236 {
237 if (mu_cfg_assert_value_type(arg, MU_CFG_STRING))
238 return 1;
239 set_source_ip(arg->v.string);
240 return 0;
241 }
242
243 static struct mf_srvcfg server_config_stmt;
244
245 static int
cb_server_stmt_listen(void * data,mu_config_value_t * arg)246 cb_server_stmt_listen(void *data, mu_config_value_t *arg)
247 {
248 if (mu_cfg_assert_value_type(arg, MU_CFG_STRING))
249 return 1;
250 *(mu_url_t*)data = parse_milter_url(arg->v.string);
251 return 0;
252 }
253
254 struct mu_cfg_param server_section_param[] = {
255 { "id", mu_c_string,
256 &server_config_stmt.id, 0,
257 NULL,
258 N_("Server ID.") },
259 { "listen", mu_cfg_callback,
260 &server_config_stmt.url, 0,
261 cb_server_stmt_listen,
262 N_("Listen on this URL."),
263 N_("url: string") },
264 { "max-instances", mu_c_size,
265 &server_config_stmt.max_children, 0,
266 NULL,
267 N_("Maximum number of instances allowed for this server.") },
268 { "single-process", mu_c_bool,
269 &server_config_stmt.single_process, 0, NULL,
270 N_("Single-process mode.") },
271 { "reuseaddr", mu_c_bool,
272 &server_config_stmt.reuseaddr, 0, NULL,
273 N_("Reuse existing socket (default).") },
274 { "acl", mu_cfg_section,
275 &server_config_stmt.acl },
276 { "option", MU_CFG_LIST_OF(mu_c_string),
277 &server_config_stmt.options, 0, NULL,
278 N_("Server-dependent options") },
279 { "default", mu_c_bool,
280 &server_config_stmt.defopt, 0, NULL,
281 N_("Deprecated. It is equivalent to `option \"default\"', "
282 "i.e. it marks this callout server as the default one.") },
283 { "backlog", mu_c_int,
284 &server_config_stmt.backlog, 0, NULL,
285 N_("Size of queue of pending connections.") },
286 { NULL }
287 };
288
289 static int
server_section_parser(enum mu_cfg_section_stage stage,const mu_cfg_node_t * node,const char * section_label,void ** section_data,void * call_data,mu_cfg_tree_t * tree)290 server_section_parser(enum mu_cfg_section_stage stage,
291 const mu_cfg_node_t *node,
292 const char *section_label, void **section_data,
293 void *call_data,
294 mu_cfg_tree_t *tree)
295 {
296 switch (stage) {
297 case mu_cfg_section_start:
298 memset(&server_config_stmt, 0, sizeof(server_config_stmt));
299 server_config_stmt.reuseaddr = 1;
300 if (node->label &&
301 mu_cfg_assert_value_type(node->label, MU_CFG_STRING))
302 return 1;
303 break;
304
305 case mu_cfg_section_end:
306 if (mtasim_option)
307 mu_url_destroy(&server_config_stmt.url);
308 else {
309 if (server_config_stmt.options)
310 mu_list_set_comparator(
311 server_config_stmt.options,
312 mf_list_compare_string);
313 if (mf_server_function(node->label ?
314 node->label->v.string : NULL,
315 &server_config_stmt)) {
316 mu_error(_("unknown server type"));
317 return 1;
318 } else if (!server_config_stmt.server) {
319 mu_error(_("INTERNAL ERROR at %s:%d: "
320 "server function not defined"),
321 __FILE__, __LINE__);
322 return 1;
323 }
324 if (!server_config_stmt.id)
325 server_config_stmt.id = next_server_id();
326 if (server_config_stmt.url
327 && server_config_stmt.server) {
328 int flags = 0;
329 mfd_server_t srv;
330
331 if (server_config_stmt.single_process)
332 flags |= SRV_SINGLE_PROCESS;
333 if (!server_config_stmt.reuseaddr)
334 flags |= SRV_KEEP_EXISTING;
335 srv = mfd_server_new(server_config_stmt.id,
336 server_config_stmt.url,
337 server_config_stmt.server,
338 flags);
339 if (srv) {
340 mfd_server_set_max_children(srv,
341 server_config_stmt.max_children);
342 mfd_server_set_acl(srv,
343 server_config_stmt.acl);
344 if (server_config_stmt.backlog)
345 mfd_server_set_backlog(srv, server_config_stmt.backlog);
346 mfd_srvman_attach_server(srv);
347 }
348 }
349 }
350 mu_list_destroy(&server_config_stmt.options);
351 break;
352 }
353 return 0;
354 }
355
356 static void
server_stmt_init(const char * label)357 server_stmt_init(const char *label)
358 {
359 struct mu_cfg_section *section;
360
361 if (mu_create_canned_section ("server", §ion) == 0) {
362 section->parser = server_section_parser;
363 section->docstring = N_("Configure server.");
364 section->label = (char*) (label ? label : _("label"));
365 mu_cfg_section_add_params(section, server_section_param);
366 }
367 }
368
369
370 static int
smtp_timeout_section_parser(enum mu_cfg_section_stage stage,const mu_cfg_node_t * node,const char * section_label,void ** section_data,void * call_data,mu_cfg_tree_t * tree)371 smtp_timeout_section_parser (enum mu_cfg_section_stage stage,
372 const mu_cfg_node_t *node,
373 const char *section_label, void **section_data,
374 void *call_data,
375 mu_cfg_tree_t *tree)
376 {
377 switch (stage) {
378 case mu_cfg_section_start:
379 if (!node->label)
380 *section_data = smtp_timeout_soft;
381 else {
382 if (mu_cfg_assert_value_type(node->label,
383 MU_CFG_STRING))
384 return 0;
385 if (strcmp(node->label->v.string, "soft") == 0)
386 *section_data = smtp_timeout_soft;
387 else if (strcmp(node->label->v.string, "hard") == 0)
388 *section_data = smtp_timeout_hard;
389 else
390 mu_error (_("unknown timeout class: %s"),
391 node->label->v.string);
392 }
393 break;
394
395 case mu_cfg_section_end:
396 break;
397 }
398 return 0;
399 }
400
401 static int
cb_timeout(void * data,mu_config_value_t * arg)402 cb_timeout(void *data, mu_config_value_t *arg)
403 {
404 struct timeval tv;
405 int rc = config_cb_timeout (&tv, arg);
406 if (rc == 0)
407 *(time_t*) data = tv.tv_sec;
408 return rc;
409 }
410
411 struct mu_cfg_param smtp_timeout_section_param[] = {
412 { "connection", mu_cfg_callback,
413 NULL, smtp_timeout_connect * sizeof(time_t), cb_timeout,
414 N_("Initial SMTP connection timeout."),
415 N_("time: interval") },
416 { "initial-response",
417 mu_cfg_callback,
418 NULL, smtp_timeout_initial * sizeof(time_t), cb_timeout,
419 N_("Timeout for initial SMTP response."),
420 N_("time: interval") },
421 { "helo",
422 mu_cfg_callback,
423 NULL, smtp_timeout_helo * sizeof(time_t), cb_timeout,
424 N_("Timeout for HELO response."),
425 N_("time: interval") },
426 { "mail",
427 mu_cfg_callback,
428 NULL, smtp_timeout_mail * sizeof(time_t), cb_timeout,
429 N_("Timeout for MAIL response."),
430 N_("time: interval") },
431 { "rcpt",
432 mu_cfg_callback,
433 NULL, smtp_timeout_rcpt * sizeof(time_t),
434 cb_timeout,
435 N_("Timeout for RCPT response."),
436 N_("time: interval") },
437 { "rset",
438 mu_cfg_callback,
439 NULL, smtp_timeout_rset * sizeof(time_t),
440 cb_timeout,
441 N_("Timeout for RSET response."),
442 N_("time: interval") },
443 { "quit",
444 mu_cfg_callback,
445 NULL, smtp_timeout_quit * sizeof(time_t),
446 cb_timeout,
447 N_("Timeout for QUIT response."),
448 N_("time: interval") },
449 { NULL }
450 };
451
452 static void
smtp_timeout_cfg_init()453 smtp_timeout_cfg_init()
454 {
455 struct mu_cfg_section *section;
456
457 if (mu_create_canned_section ("smtp-timeout", §ion) == 0) {
458 section->parser = smtp_timeout_section_parser;
459 section->docstring = N_("Set SMTP timeouts.");
460 /* TRANSLATORS: soft and hard are keywords, do not translate
461 them */
462 section->label = N_("[soft | hard]");
463 mu_cfg_section_add_params (section, smtp_timeout_section_param);
464 }
465 }
466
467 static int
cb_state_directory(void * data,mu_config_value_t * arg)468 cb_state_directory(void *data, mu_config_value_t *arg)
469 {
470 if (mu_cfg_assert_value_type(arg, MU_CFG_STRING))
471 return 1;
472 return mf_option_state_directory(arg->v.string);
473 }
474
475 /* FIXME: Umask not configurable */
476
477 static struct mu_cfg_param srv_cfg_param[] = {
478 { "debug", mu_cfg_callback, NULL, 0, cb_debug,
479 N_("Set mailfromd debugging level. Argument is a semicolon-separated list "
480 "of debugging specifications. A simplified specification syntax is:\n"
481 " [!]<category: string>[.<level: string>,<level: string>...]\n"
482 "For details, please see the Mailfromd manual, section 3.18 \"Logging\n"
483 "and Debugging\", GNU Mailutils manual, section 3.3 \"Debugging\",\n"
484 "or visit <http://mailutils.org/wiki/Debug_level>."),
485 N_("spec: list") },
486 { "transcript", mu_c_bool, &smtp_transcript, 0, NULL,
487 N_("Enable transcript of call-out SMTP sessions.") },
488
489 { "smtp-timeout", mu_cfg_section, },
490 { "io-timeout", mu_cfg_callback, &io_timeout, 0, cb_timeout,
491 N_("Timeout for all SMTP I/O operations."),
492 N_("time: seconds") },
493 /* FIXME: Could have used mu_cfg_ipv4 here, but... */
494 { "source-ip", mu_cfg_callback, NULL, 0, cb_source_ip,
495 N_("Set source address for TCP connections."),
496 N_("ip: ipaddr") },
497 { "group", mu_cfg_callback, NULL, 0, cb_group,
498 N_("Retain the supplementary group <name> when switching to user "
499 "privileges"),
500 N_("group: string") },
501 { "user", mu_c_string, &mf_server_user, 0, NULL,
502 N_("Switch to this user privileges after startup.") },
503 { "pidfile", mu_c_string, &pidfile, 0, NULL,
504 N_("Set file to store PID value in."),
505 N_("file") },
506 { "server", mu_cfg_section },
507 { "acl", mu_cfg_section, &srvman_param.acl },
508 { "logger", mu_c_string, &log_stream, 0, NULL,
509 N_("Set logger stream.") },
510 { "state-directory", mu_cfg_callback, NULL, 0, cb_state_directory,
511 N_("Set program state directory."),
512 N_("dir: string") },
513 { "database-type", mu_c_string, &db_type_str, 0, NULL,
514 N_("Default database type"),
515 N_("type") },
516 { "database", mu_cfg_section, NULL, 0, NULL, NULL },
517 { "database-mode", mu_cfg_callback,
518 &mf_database_mode, 0, cb_database_mode,
519 N_("Configure file mode for database files"),
520 N_("mode: octal") },
521 { "ehlo-domain", mu_c_string, &ehlo_domain, 0, NULL,
522 N_("Set the domain name for EHLO command.") },
523 { "mail-from-address", mu_c_string, &mailfrom_address, 0, NULL,
524 N_("Set email address for use in SMTP `MAIL FROM' command. "
525 "Argument is an email address or a comma-separated list of "
526 "addresses. Use <> for null address. Other addresses can "
527 "be given without angle brackets."),
528 N_("addr") },
529 { "enable-vrfy", mu_c_bool, &enable_vrfy, 0, NULL,
530 N_("Use the SMTP VRFY command, when available.") },
531 { NULL }
532 };
533
534 static void
opt_foreground(struct mu_parseopt * po,struct mu_option * op,char const * arg)535 opt_foreground (struct mu_parseopt *po, struct mu_option *op, char const *arg)
536 {
537 server_flags |= MF_SERVER_FOREGROUND;
538 }
539
540 static void
opt_single_process(struct mu_parseopt * po,struct mu_option * op,char const * arg)541 opt_single_process (struct mu_parseopt *po, struct mu_option *op,
542 char const *arg)
543 {
544 srvman_param.flags |= SRV_SINGLE_PROCESS;
545 }
546
547 static void
opt_group(struct mu_parseopt * po,struct mu_option * op,char const * arg)548 opt_group (struct mu_parseopt *po, struct mu_option *op, char const *arg)
549 {
550 mf_option_group(arg);
551 }
552
553 static void
opt_source_ip(struct mu_parseopt * po,struct mu_option * op,char const * arg)554 opt_source_ip (struct mu_parseopt *po, struct mu_option *op, char const *arg)
555 {
556 if (set_source_ip(arg))
557 exit (po->po_exit_error);
558 }
559
560 static void
opt_state_directory(struct mu_parseopt * po,struct mu_option * op,char const * arg)561 opt_state_directory (struct mu_parseopt *po, struct mu_option *op,
562 char const *arg)
563 {
564 struct stat st;
565 if (stat(arg, &st)) {
566 mu_parseopt_error(po, _("cannot stat file `%s': %s"),
567 arg,
568 mu_strerror(errno));
569 exit(po->po_exit_error);
570 }
571 if (!S_ISDIR(st.st_mode)) {
572 mu_parseopt_error(po, _("`%s' is not a directory"), arg);
573 exit(po->po_exit_error);
574 }
575 if (arg[0] != '/') {
576 mu_parseopt_error(po,
577 _("state directory `%s' is not an absolute "
578 "file name"), arg);
579 exit(po->po_exit_error);
580 }
581 mailfromd_state_dir = mu_strdup(arg);
582 }
583
584 static void
opt_debug(struct mu_parseopt * po,struct mu_option * op,char const * arg)585 opt_debug(struct mu_parseopt *po, struct mu_option *op, char const *arg)
586 {
587 mu_debug_parse_spec(arg);
588 }
589
590 static void
opt_logger_stream(struct mu_parseopt * po,struct mu_option * op,char const * arg)591 opt_logger_stream(struct mu_parseopt *po, struct mu_option *op,
592 char const *arg)
593 {
594 //FIXME: Check arg: either syslog or stderr */
595 /* This option is handled twice. First, it must take
596 effect immediately, so that any eventual startup
597 errors end up being reported to syslog: */
598 mf_srvcfg_log_setup(arg);
599 /* Second, it overrides any logging settings read from the
600 configuration file. */
601 log_stream = mu_strdup(arg);
602 }
603
604 static void
opt_pidfile(struct mu_parseopt * po,struct mu_option * op,char const * arg)605 opt_pidfile(struct mu_parseopt *po, struct mu_option *op, char const *arg)
606 {
607 if (arg[0] != '/') {
608 mu_parseopt_error(po,
609 _("invalid pidfile name: must be absolute"));
610 exit(po->po_exit_error);
611 }
612 pidfile = mu_strdup(arg);
613 }
614
615 static struct mu_option srv_options[] = {
616 MU_OPTION_GROUP(N_("Server configuration modifiers")),
617 { "foreground", 0, NULL, MU_OPTION_DEFAULT,
618 N_("stay in foreground"),
619 mu_c_string, NULL, opt_foreground },
620 { "single-process", 0, NULL, MU_OPTION_DEFAULT,
621 N_("run in single-process mode"),
622 mu_c_string, NULL, opt_single_process },
623 { "pidfile", 0, N_("FILE"), MU_OPTION_DEFAULT,
624 N_("set pidfile name"),
625 mu_c_string, &pidfile, opt_pidfile },
626 { "user", 'u', N_("NAME"), MU_OPTION_DEFAULT,
627 N_("switch to this user privileges after startup"),
628 mu_c_string, &mf_server_user },
629 { "group", 'g', N_("NAME"), MU_OPTION_DEFAULT,
630 N_("retain the supplementary group NAME when switching to user "
631 "privileges"),
632 mu_c_string, NULL, opt_group },
633 { "source-ip", 'S', N_("ADDRESS"), MU_OPTION_DEFAULT,
634 N_("set source address for TCP connections"),
635 mu_c_string, NULL, opt_source_ip },
636 { "state-directory", 0, N_("DIR"), MU_OPTION_DEFAULT,
637 N_("set new program state directory"),
638 mu_c_string, NULL, opt_state_directory },
639
640 MU_OPTION_GROUP(N_("Logging and debugging options")),
641 { "transcript", 'X', NULL, MU_OPTION_DEFAULT,
642 N_("enable transcript of SMTP sessions"),
643 mu_c_bool, &transcript_option },
644 { "debug", 'd', N_("LEVEL"), MU_OPTION_DEFAULT,
645 N_("set debugging level"),
646 mu_c_string, NULL, opt_debug },
647
648 /* In the contrast to --syslog, this option takes effect
649 only after the configuration file has been parsed.
650 This is so because --stderr is the default behavior,
651 so that any configuration file errors are reported to
652 stderr anyway (unless it is closed, in which case they
653 are redirected to syslog). */
654 { "stderr", 0, NULL, MU_OPTION_DEFAULT,
655 N_("log to stderr"),
656 mu_c_string, NULL, opt_logger_stream, "stderr" },
657 { "syslog", 0, NULL, MU_OPTION_DEFAULT,
658 N_("log to syslog (default)"),
659 mu_c_string, NULL, opt_logger_stream, "syslog" },
660 { "logger", 0, N_("STREAM"), MU_OPTION_DEFAULT,
661 N_("select logger stream"),
662 mu_c_string, NULL, opt_logger_stream },
663 { "log-tag", 0, N_("STRING"), MU_OPTION_DEFAULT,
664 N_("set the identifier used in syslog messages to STRING"),
665 mu_c_string, &mu_log_tag },
666 { "source-info", 0, NULL, MU_OPTION_DEFAULT,
667 N_("debug messages include source information"),
668 mu_c_bool, &mu_debug_line_info },
669
670 MU_OPTION_END
671 };
672
673 #define PIDSUF ".pid"
674
675 static void
setdefpidfilename(const char * progname)676 setdefpidfilename(const char *progname)
677 {
678 size_t len;
679 const char *base = strrchr(progname, '/');
680 if (base)
681 base++;
682 else
683 base = progname;
684 len = strlen(base);
685 pidfile = mu_alloc(len + sizeof(PIDSUF));
686 memcpy(pidfile, base, len);
687 strcpy(pidfile + len, PIDSUF);
688 }
689
690 static struct mu_cli_capa mfd_capa_server = {
691 ".mfd:server",
692 srv_options,
693 };
694
695 void
mf_srvcfg_init(const char * progname,const char * label)696 mf_srvcfg_init(const char *progname, const char *label)
697 {
698 struct mu_cfg_section *section;
699
700 smtp_timeout_cfg_init();
701 server_stmt_init(label);
702
703 mailfromd_state_dir = DEFAULT_STATE_DIR;
704 if (!pidfile)
705 setdefpidfilename(progname);
706
707 mu_cli_capa_register (&mfd_capa_server);
708
709 if (mu_create_canned_section (".mfd:server", §ion) == 0) {
710 mu_cfg_section_add_params (section, srv_cfg_param);
711 }
712 }
713
714 static void
init_ehlo_domain(void)715 init_ehlo_domain(void)
716 {
717 char *smtp_hostname;
718 char *smtp_domain;
719 mu_get_host_name(&smtp_hostname);
720
721 smtp_domain = strchr(smtp_hostname, '.');
722 if (smtp_domain)
723 smtp_domain++;
724 else
725 smtp_domain = smtp_hostname;
726 ehlo_domain = mu_strdup(smtp_domain);
727 }
728
729 void
mf_srvcfg_flush()730 mf_srvcfg_flush()
731 {
732 int i;
733
734 if (transcript_option)
735 smtp_transcript = transcript_option;
736
737 /* Fix unspecified timeouts */
738 for (i = 0; i < SMTP_NUM_TIMEOUT; i++) {
739 if (smtp_timeout_soft[i] == 0)
740 smtp_timeout_soft[i] = io_timeout;
741 if (smtp_timeout_hard[i] == 0)
742 smtp_timeout_hard[i] = io_timeout;
743 }
744
745 if (!ehlo_domain)
746 init_ehlo_domain();
747
748 if (db_type_str) {
749 mu_url_t dbhint;
750 int rc;
751 static char const *defparam[] = { "linkwrdir", "awrdir" };
752
753 if ((rc = mu_url_create_null(&dbhint)) ||
754 (rc = mu_url_set_scheme(dbhint, db_type_str)) ||
755 (rc = mu_url_add_param(dbhint,
756 MU_ARRAY_SIZE(defparam),
757 defparam))) {
758 mu_error(_("cannot initialize DBM hint: %s"),
759 mu_strerror(rc));
760 exit(EX_SOFTWARE);
761 }
762
763 mu_url_destroy(&mu_dbm_hint);
764 mu_dbm_hint = dbhint;
765 }
766 }
767
768 void
mf_srvcfg_log_setup(char const * stream)769 mf_srvcfg_log_setup(char const *stream)
770 {
771 if (logger_select(stream)) {
772 mu_error(_("unsupported logger stream: %s"), stream);
773 exit(EX_USAGE);
774 }
775 logger_open();
776
777 FD_ZERO(&srvman_param.keepfds);
778 logger_fdset(&srvman_param.keepfds);
779 if (logger_flags(LOGF_STDERR))
780 /* Keep also stdout.
781 FIXME: Is it still needed? */
782 FD_SET(1, &srvman_param.keepfds);
783 }
784
785 void
mf_server_log_setup(void)786 mf_server_log_setup(void)
787 {
788 mf_srvcfg_log_setup(log_stream);
789 }
790
791