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", &section) == 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", &section) == 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", &section) == 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