aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c1383
1 files changed, 1383 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..8ba58cb5
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,1383 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <mailutils/mailutils.h>
+#include <mailutils/server.h>
+#include <mailutils/syslog.h>
+#include <mailutils/libargp.h>
+
+#include "mailfromd.h"
+#include "callout.h"
+#include "srvman.h"
+#include "inttostr.h"
+#include "srvcfg.h"
+#include "filenames.h"
+#include "mu_dbm.h"
+#include "builtin.h"
+#include "prog.h"
+
+
+/* Configurable options */
+
+int mode = MAILFROMD_DAEMON; /* Default operation mode */
+enum smtp_state test_state = smtp_state_envfrom; /* State for --test mode */
+int need_script = 1; /* Set if the current mode requires reading the
+ script file */
+char *script_file = DEFAULT_SCRIPT_FILE;
+int script_check; /* Check config file syntax and exit */
+extern int yy_flex_debug; /* Enable tracing the lexical analyzer */
+int script_ydebug; /* Enable tracing the parser */
+int script_dump_tree; /* Dump created config tree to stdout */
+int script_dump_code; /* Dump disassembled code to stdout */
+int script_dump_macros; /* Dump used Sendmail macros */
+int script_dump_xref; /* Dump cross-reference */
+int preprocess_option; /* Only preprocess the sources */
+int location_column_option; /* Print column numbers in error locations */
+
+char *ext_pp = DEF_EXT_PP; /* External preprocessor to use */
+char *ext_pp_options;
+int ext_pp_options_given;
+
+int do_trace; /* Enable tracing configuration */
+int mtasim_option; /* mtasim compatibility mode */
+unsigned optimization_level = 1; /* Optimization level */
+
+int stack_trace_option; /* Print stack traces on runtime errors */
+
+size_t max_match_mx = MAXMXCOUNT;
+
+char *main_function_name = "main";
+
+
+/* Preprocessor helper function */
+static void
+add_pp_option(const char *opt, const char *arg)
+{
+ size_t len;
+
+ len = 1 + strlen(opt) + (arg ? strlen(arg) : 0);
+ if (ext_pp_options) {
+ len += strlen(ext_pp_options);
+ ext_pp_options = xrealloc(ext_pp_options, len + 1);
+ } else {
+ ext_pp_options = xmalloc(len + 1);
+ ext_pp_options[0] = 0;
+ }
+ strcat(ext_pp_options, " ");
+ strcat(ext_pp_options, opt);
+ strcat(ext_pp_options, arg);
+}
+
+void
+pp_define(const char *symbol)
+{
+ add_pp_option("-D", symbol);
+}
+
+
+/* Logging & debugging */
+
+void
+mfd_gacopyz_log_printer(int level, char *fmt, va_list ap)
+{
+ switch (level) {
+ case SMI_LOG_DEBUG:
+ level = LOG_DEBUG;
+ break;
+ case SMI_LOG_INFO:
+ level = LOG_INFO;
+ break;
+ case SMI_LOG_WARN:
+ level = LOG_WARNING;
+ break;
+ case SMI_LOG_ERR:
+ level = LOG_ERR;
+ break;
+
+ case SMI_LOG_FATAL:
+ default:
+ level = LOG_EMERG;
+ }
+ vlogmsg(level, fmt, ap);
+}
+
+int
+mf_diag_syslog_printer (void *data, mu_log_level_t level, const char *buf)
+{
+ int len = strlen (buf);
+ if (len > 0 && buf[len-1] == '\n') {
+ len--;
+ if (len > 0 && buf[len-1] == '\r')
+ len--;
+ }
+ logmsg(mu_diag_level_to_syslog(level), "%-.*s", len, buf);
+ return 0;
+}
+
+void
+trace(const char *fmt, ...)
+{
+ if (do_trace) {
+ va_list ap;
+ va_start(ap, fmt);
+ vlogmsg(LOG_INFO, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+log_status(sfsistat status, SMFICTX *ctx)
+{
+ int lev;
+ debug_module_level(NULL, &lev);
+ if (lev >= 1 && status != SMFIS_CONTINUE) {
+ const char *str = sfsistat_str(status);
+ if (str)
+ logmsg(LOG_INFO,
+ "%s%s", mailfromd_msgid(ctx), str);
+ else
+ logmsg(LOG_INFO,
+ "%sstatus %d",
+ mailfromd_msgid(ctx), status);
+ }
+}
+
+
+/* Sendmail class file support */
+
+static mu_list_t domain_list;
+
+int
+compare_string(const void *item, const void *value)
+{
+ return strcmp(item, value);
+}
+
+/* Read domains from sendmail-style domain file NAME and store them in
+ DOMAIN_LIST */
+int
+read_domain_file(mu_debug_t err, const char *name)
+{
+ FILE *fp;
+ char buf[256];
+ char *p;
+
+ fp = fopen(name, "r");
+ if (!fp) {
+ mf_error_on_locus(err, _("cannot open file `%s': %s"),
+ name, mu_strerror(errno));
+ return 1;
+ }
+
+ if (!domain_list) {
+ mu_list_create(&domain_list);
+ mu_list_set_comparator(domain_list, compare_string);
+ }
+
+ while (p = fgets(buf, sizeof buf, fp)) {
+ char *q;
+
+ while (*p && c_isspace(*p))
+ p++;
+ if (*p == '#')
+ continue;
+
+ for (q = p; *q && !c_isspace(*q); q++)
+ ;
+ *q = 0;
+
+ if (*p == 0)
+ continue;
+
+ mu_list_append(domain_list, strdup(p));
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+/* Return true if we relay domain NAME */
+int
+relayed_domain_p(char *name)
+{
+ char *p = name;
+
+ while (p) {
+ if (mu_list_locate(domain_list, p, NULL) == 0) {
+ debug(MF_SOURCE_MAIN, 10,
+ ("%s is in relayed domain %s", name, p));
+ return 1;
+ }
+ p = strchr(p, '.');
+ if (p)
+ p++;
+ }
+ return 0;
+}
+
+/* Return true if CLIENT represents a host we relay. CLIENT is a dotted-quad
+ IP address. */
+int
+host_in_relayed_domain_p(char *client)
+{
+ int rc;
+ char *hbuf;
+
+ if (mu_list_is_empty(domain_list))
+ return 0;
+
+ if (resolve_ipstr(client, &hbuf))
+ return 0;
+ rc = relayed_domain_p(hbuf);
+ free(hbuf);
+ return rc;
+}
+
+
+static void
+set_milter_timeout(void *value)
+{
+ time_t to = *(time_t*) value;
+ free(value);
+ if (smfi_settimeout(to) == MI_FAILURE) {
+ mu_error(_("invalid milter timeout: %lu"), (unsigned long) to);
+ exit(EX_USAGE);
+ }
+}
+
+static int
+load_relay_file(void *item, void *data)
+{
+ read_domain_file((mu_debug_t) data, item);
+ return 0;
+}
+
+static void
+set_relay(void *value)
+{
+ mu_list_do(value, load_relay_file, NULL);
+}
+
+void
+set_stack_trace(void *value)
+{
+ stack_trace_option = (int) value;
+}
+
+static int
+option_relay(char *opt, void **pval, char *newval)
+{
+ if (!*pval)
+ mu_list_create((mu_list_t*)pval);
+ mu_list_append(*pval, strdup(newval));
+ return 0;
+}
+
+struct pm_option_cache option_cache[] = {
+ { "stack-trace", NULL, pm_option_boolean, set_stack_trace },
+ { "milter-timeout", NULL, pm_option_time, set_milter_timeout },
+ { "relay", NULL, option_relay, set_relay },
+ { NULL }
+};
+
+
+/* Command line parsing */
+
+const char *program_version = "mailfromd (" PACKAGE_STRING ")";
+static char doc[] = N_("mailfromd -- a general purpose milter daemon");
+static char args_doc[] = "[var=value...][SCRIPT]";
+
+enum mailfromd_option {
+ OPTION_DAEMON = 256,
+ OPTION_DOMAIN_FILE,
+ OPTION_DUMP_CODE,
+ OPTION_DUMP_GRAMMAR_TRACE,
+ OPTION_DUMP_LEX_TRACE,
+ OPTION_DUMP_MACROS,
+ OPTION_DUMP_TREE,
+ OPTION_DUMP_XREF,
+ OPTION_LOCATION_COLUMN,
+ OPTION_GACOPYZ_LOG,
+ OPTION_LINT,
+ OPTION_MILTER_TIMEOUT,
+ OPTION_MTASIM,
+ OPTION_NO_PREPROCESSOR,
+ OPTION_PREPROCESSOR,
+ OPTION_RUN,
+ OPTION_SHOW_DEFAULTS,
+ OPTION_STACK_TRACE,
+ OPTION_TIMEOUT,
+ OPTION_TRACE,
+ OPTION_TRACE_PROGRAM,
+};
+
+static struct argp_option options[] = {
+#define GRP 0
+ { NULL, 0, NULL, 0,
+ N_("Operation modifiers"), GRP },
+ { "test", 't', N_("HANDLER"), OPTION_ARG_OPTIONAL,
+ N_("run in test mode"), GRP+1 },
+ { "run", OPTION_RUN, N_("FUNC"), OPTION_ARG_OPTIONAL,
+ N_("run script and execute function FUNC (\"main\" if not given)"),
+ GRP+1 },
+ { "lint", OPTION_LINT, NULL, 0,
+ N_("check syntax and exit"), GRP+1 },
+ { "syntax-check", 0, NULL, OPTION_ALIAS, NULL, GRP+1 },
+ { "show-defaults", OPTION_SHOW_DEFAULTS, NULL, 0,
+ N_("show compilation defaults"), GRP+1 },
+ { "daemon", OPTION_DAEMON, NULL, 0,
+ N_("run in daemon mode (default)"), GRP+1 },
+
+ { NULL, 'E', NULL, 0,
+ N_("preprocess source files and exit"), GRP+1 },
+ /* Reserved for future use: */
+ { "compile", 'c', NULL, OPTION_HIDDEN,
+ N_("compile files"), GRP+1 },
+ { "load", 'l', N_("FILE"), OPTION_HIDDEN,
+ N_("load library"), GRP+1 },
+ { "load-dir", 'L', N_("DIR"), OPTION_HIDDEN,
+ N_("add DIR to the load path"), GRP+1 },
+
+#undef GRP
+#define GRP 20
+ { NULL, 0, NULL, 0,
+ N_("General options"), GRP },
+ { "include", 'I', N_("DIR"), 0,
+ N_("add the directory DIR to the list of directories to be "
+ "searched for header files"), GRP+1 },
+ { "port", 'p', N_("STRING"), 0,
+ N_("set communication socket"), GRP+1 },
+ { "mtasim", OPTION_MTASIM, NULL, 0,
+ N_("run in mtasim compatibility mode"), GRP+1 },
+ { "optimize", 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL,
+ N_("set code optimization level"), GRP+1 },
+ { "variable", 'v', N_("VAR=VALUE"), 0,
+ N_("assign VALUE to VAR"), GRP+1 },
+ { "relayed-domain-file", OPTION_DOMAIN_FILE, N_("FILE"), 0,
+ N_("read relayed domains from FILE"), GRP+1 },
+
+#undef GRP
+#define GRP 25
+ { NULL, 0, NULL, 0,
+ N_("Preprocessor options"), GRP },
+ { "preprocessor", OPTION_PREPROCESSOR, N_("COMMAND"), 0,
+ N_("use command as external preprocessor"), GRP+1 },
+ { "no-preprocessor", OPTION_NO_PREPROCESSOR, NULL, 0,
+ N_("disable the use of external preprocessor"), GRP+1 },
+ { "define", 'D', N_("NAME[=VALUE]"), 0,
+ N_("define a preprocessor symbol NAME as having VALUE, or empty"),
+ GRP+1 },
+ { "undefine", 'U', N_("NAME"), 0,
+ N_("undefine a preprocessor symbol NAME"),
+ GRP+1 },
+
+#undef GRP
+#define GRP 30
+ { NULL, 0, NULL, 0,
+ N_("Timeout control"), GRP },
+ { "milter-timeout", OPTION_MILTER_TIMEOUT, N_("TIME"), 0,
+ N_("set MTA connection timeout"), GRP+1 },
+ { "timeout", OPTION_TIMEOUT, N_("TIME"), 0,
+ N_("set I/O operation timeout"), GRP+1 },
+
+#undef GRP
+#define GRP 40
+ { NULL, 0, NULL, 0,
+ N_("Informational and debugging options"), GRP },
+ { "location-column", OPTION_LOCATION_COLUMN, NULL, 0,
+ N_("print location column numbers in compiler diagnostics messages"),
+ GRP+1 },
+ { "trace", OPTION_TRACE, NULL, 0,
+ N_("trace executed actions"), GRP+1 },
+ { "trace-program", OPTION_TRACE_PROGRAM, N_("MODULES"),
+ OPTION_ARG_OPTIONAL,
+ N_("enable filter program tracing"), GRP+1 },
+ { "dump-code", OPTION_DUMP_CODE, NULL, 0,
+ N_("dump disassembled code"), GRP+1 },
+ { "dump-grammar-trace", OPTION_DUMP_GRAMMAR_TRACE, NULL, 0,
+ N_("dump grammar traces"), GRP+1 },
+ { "dump-lex-trace", OPTION_DUMP_LEX_TRACE, NULL, 0,
+ N_("dump lexical analyzer traces"), GRP+1 },
+ { "dump-tree", OPTION_DUMP_TREE, NULL, 0,
+ N_("dump parser tree"), GRP+1 },
+ { "dump-macros", OPTION_DUMP_MACROS, NULL, 0,
+ N_("show used Sendmail macros"), GRP+1 },
+ { "xref", OPTION_DUMP_XREF, NULL, 0,
+ N_("produce a cross-reference listing"), GRP+1 },
+ { "dump-xref", 0, NULL, OPTION_ALIAS, NULL, GRP+1 },
+ { "gacopyz-log", OPTION_GACOPYZ_LOG, N_("LEVEL"), 0,
+ N_("set Gacopyz log level"), GRP+1 },
+ { "stack-trace", OPTION_STACK_TRACE, NULL, 0,
+ N_("enable stack traces on runtime errors"), GRP+1 },
+#undef GRP
+
+#if 0
+/* This entry is to pacify `make check-docs'. The options below
+ are defined in libmailutils.
+ */
+ { "log-facility", }
+ { "mailer", }
+#endif
+ { NULL }
+};
+
+static int
+validate_options()
+{
+ /* FIXME */
+ return 0;
+}
+
+struct arguments
+{
+ int trace;
+ mu_list_t trace_modules;
+};
+
+#define ARG_UNSET (-1)
+
+static void
+init_arguments(struct arguments *args)
+{
+ args->trace = ARG_UNSET;
+ args->trace_modules = NULL;
+}
+
+static int
+flush_trace_module(void *item, void *data)
+{
+ enable_prog_trace((const char *) item);
+ return 0;
+}
+
+static void
+flush_arguments(struct arguments *args)
+{
+ if (args->trace != ARG_UNSET)
+ do_trace = args->trace;
+ if (args->trace_modules) {
+ mu_list_do(args->trace_modules, flush_trace_module, NULL);
+ mu_list_destroy(&args->trace_modules);
+ }
+}
+
+static void
+destroy_trace_item(void *ptr)
+{
+ free(ptr);
+}
+
+static error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
+ struct arguments *args = state->input;
+ switch (key) {
+ case 'D':
+ ext_pp_options_given = 1;
+ add_pp_option("-D", arg);
+ break;
+
+ case 'U':
+ ext_pp_options_given = 1;
+ add_pp_option("-U", arg);
+ break;
+
+ case 'E':
+ preprocess_option = 1;
+ break;
+
+ case 'I':
+ add_include_dir(arg);
+ break;
+
+ case OPTION_LOCATION_COLUMN:
+ location_column_option = 1;
+ break;
+
+ case OPTION_PREPROCESSOR:
+ ext_pp = arg;
+ break;
+
+ case OPTION_NO_PREPROCESSOR:
+ ext_pp = NULL;
+ break;
+
+ case 'c':
+ case 'l':
+ case 'L':
+ argp_error(state,
+ _("the option `-%c' is not yet implemented"),
+ key);
+ break;
+
+ case OPTION_LINT:
+ log_stream = "stderr";
+ script_check = 1;
+ need_script = 1;
+ break;
+
+ case 'p':
+ pm_optcache_set_option("port", arg);
+ break;
+
+ case OPTION_RUN:
+ mode = MAILFROMD_RUN;
+ if (arg)
+ main_function_name = arg;
+ log_stream = "stderr";
+ need_script = 1;
+ break;
+
+ case 'O':
+ if (!arg)
+ optimization_level = 1;
+ else {
+ char *p;
+ optimization_level = strtoul(arg, &p, 0);
+ if (*p)
+ argp_error(state,
+ _("-O level must be a non-negative integer"));
+ }
+ break;
+
+ case 't':
+ mode = MAILFROMD_TEST;
+ if (arg) {
+ test_state = string_to_state(arg);
+ if (test_state == smtp_state_none)
+ argp_error(state,
+ _("unknown smtp state tag: %s"),
+ arg);
+ }
+ log_stream = "stderr";
+ need_script = 1;
+ break;
+
+ case 'v':
+ {
+ char *p;
+
+ p = strchr(arg, '=');
+ if (!p)
+ argp_error(state,
+ _("expected assignment, but found `%s'"),
+ arg);
+ *p++ = 0;
+ defer_initialize_variable(arg, p);
+ break;
+ }
+
+ case OPTION_DAEMON:
+ mode = MAILFROMD_DAEMON;
+ need_script = 1;
+ break;
+
+ case OPTION_DOMAIN_FILE:
+ pm_optcache_set_option("relay", arg);
+ break;
+
+ case OPTION_DUMP_CODE:
+ script_dump_code = 1;
+ log_stream = "stderr";
+ break;
+
+ case OPTION_DUMP_GRAMMAR_TRACE:
+ script_ydebug = 1;
+ log_stream = "stderr";
+ break;
+
+ case OPTION_DUMP_LEX_TRACE:
+ yy_flex_debug = 1;
+ log_stream = "stderr";
+ break;
+
+ case OPTION_DUMP_MACROS:
+ script_dump_macros = 1;
+ log_stream = "stderr";
+ break;
+
+ case OPTION_DUMP_TREE:
+ script_dump_tree = 1;
+ log_stream = "stderr";
+ break;
+
+ case OPTION_DUMP_XREF:
+ script_dump_xref = 1;
+ log_stream = "stderr";
+ break;
+
+ case OPTION_GACOPYZ_LOG:
+ {
+ int lev = gacopyz_string_to_log_level(arg);
+ if (lev == -1)
+ argp_error(state,
+ _("%s: invalid log level"),
+ arg);
+ milter_setlogmask(SMI_LOG_FROM(lev));
+ break;
+ }
+
+ case OPTION_MILTER_TIMEOUT:
+ pm_optcache_set_option("milter-timeout", arg);
+ break;
+
+ case OPTION_MTASIM:
+ mtasim_option = 1;
+ server_flags |= MF_SERVER_FOREGROUND;
+ break;
+
+ case OPTION_SHOW_DEFAULTS:
+ mode = MAILFROMD_SHOW_DEFAULTS;
+ need_script = 0;
+ break;
+
+ case OPTION_STACK_TRACE:
+ pm_optcache_set_option("stack-trace", "yes");
+ break;
+
+ case OPTION_TIMEOUT:
+ pm_optcache_set_option("timeout", arg);
+ break;
+
+ case OPTION_TRACE:
+ args->trace = 1;
+ break;
+
+ case OPTION_TRACE_PROGRAM:
+ if (!args->trace_modules) {
+ mu_list_create(&args->trace_modules);
+ mu_list_set_destroy_item(args->trace_modules,
+ destroy_trace_item);
+ }
+ mu_list_append(args->trace_modules, arg ? arg: xstrdup("all"));
+ break;
+
+ case ARGP_KEY_INIT:
+ milter_setlogmask(SMI_LOG_FROM(SMI_LOG_WARN));
+ milter_settimeout(7200);
+ break;
+
+ case ARGP_KEY_FINI:
+ if (validate_options())
+ exit(EX_USAGE);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const char *capa[] = {
+ "auth",
+ "common",
+ "debug",
+ "logging",
+ "mailer",
+ NULL
+};
+
+static struct argp argp = {
+ options,
+ parse_opt,
+ args_doc,
+ doc,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/* Mailutils-2.0 configuration */
+
+static int
+cb_milter_timeout(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ struct timeval tv;
+ int rc = config_cb_timeout (&tv, err, arg);
+
+ if (rc)
+ return 1;
+ if (smfi_settimeout(tv.tv_sec) == MI_FAILURE) {
+ mf_error_on_locus(err,
+ _("Invalid milter timeout: %lu"),
+ (unsigned long) tv.tv_sec);
+ exit(EX_USAGE);
+ }
+
+ return 0;
+}
+
+static int
+cb_timeout(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ struct timeval tv;
+ int rc = config_cb_timeout (&tv, err, arg);
+ if (rc == 0)
+ *(time_t*) data = tv.tv_sec;
+ return rc;
+}
+
+static int
+cb_set_variable(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ const char *value;
+ char *alloc_str = NULL;
+
+ if (mu_cfg_assert_value_type(arg, MU_CFG_ARRAY, err))
+ return 1;
+ if (arg->v.arg.c < 2) {
+ mf_error_on_locus(err, _("not enough arguments"));
+ return 1;
+ } else if (arg->v.arg.c > 2) {
+ mf_error_on_locus(err, _("too many arguments"));
+ return 1;
+ }
+
+ if (mu_cfg_assert_value_type(&arg->v.arg.v[0], MU_CFG_ARRAY, err))
+ return 1;
+
+ switch (arg->v.arg.v[1].type) {
+ case MU_CFG_STRING:
+ value = arg->v.arg.v[1].v.string;
+ break;
+
+ case MU_CFG_ARRAY:
+ alloc_str = config_array_to_string(&arg->v.arg.v[1], err);
+ value = alloc_str;
+ break;
+
+ case MU_CFG_LIST:
+ mu_cfg_format_error(err, MU_DEBUG_ERROR, _("unexpected list"));
+ return 1;
+
+ default:
+ mu_cfg_format_error (err, MU_DEBUG_ERROR,
+ _("INTERNAL ERROR at %s:%d: please report"),
+ __FILE__, __LINE__);
+ abort();
+ }
+
+ defer_initialize_variable(arg->v.arg.v[0].v.string, value);
+ free(alloc_str);
+ return 0;
+}
+
+static int
+cb_ehlo_domain(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ if (mu_cfg_assert_value_type(arg, MU_CFG_STRING, err))
+ return 1;
+ defer_initialize_variable("ehlo_domain", arg->v.string);
+ return 0;
+}
+
+static int
+mf_option_mailfrom(mu_debug_t err, const char *arg)
+{
+ int rc;
+ mu_address_t addr;
+
+ rc = mu_address_create(&addr, arg);
+ if (rc) {
+ mf_error_on_locus(err, _("cannot create address `%s': %s"),
+ arg, mu_strerror(rc));
+ return 1;
+ }
+ mu_address_destroy(&addr);
+ defer_initialize_variable("mailfrom_address", arg);
+ return 0;
+}
+
+static int
+cb_mail_from_address(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ if (mu_cfg_assert_value_type(arg, MU_CFG_STRING, err))
+ return 1;
+ return mf_option_mailfrom(err, arg->v.string);
+}
+
+static int
+cb_include_path(mu_debug_t err, void *data, mu_config_value_t *val)
+{
+ int i, rc = 0;
+ int argc;
+ char **argv;
+ mu_iterator_t itr;
+
+ switch (val->type) {
+ case MU_CFG_STRING:
+ rc = mu_argcv_get_np(val->v.string, strlen(val->v.string),
+ ":", NULL, 0, &argc, &argv, NULL);
+ if (rc) {
+ mu_cfg_format_error(err, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s",
+ mu_strerror(rc));
+ return 1;
+ }
+ for (i = 0; i < argc; i++)
+ add_include_dir(argv[i]);
+ mu_argcv_free(argc, argv);
+ break;
+
+ case MU_CFG_ARRAY:
+ for (i = 0; i < val->v.arg.c; i++) {
+ if (mu_cfg_assert_value_type(&val->v.arg.v[i],
+ MU_CFG_STRING,
+ err))
+ return 1;
+ add_include_dir(val->v.arg.v[i].v.string);
+ }
+ break;
+
+ case MU_CFG_LIST:
+ mu_list_get_iterator(val->v.list, &itr);
+ for (mu_iterator_first(itr);
+ !mu_iterator_is_done(itr); mu_iterator_next(itr)) {
+ mu_config_value_t *pval;
+ mu_iterator_current(itr, (void*) &pval);
+ rc = mu_cfg_assert_value_type(pval, MU_CFG_STRING,
+ err);
+ if (rc)
+ break;
+ add_include_dir(pval->v.string);
+ }
+ mu_iterator_destroy(&itr);
+ }
+ return rc;
+}
+
+static int
+cb_trace_program(mu_debug_t err, void *data, mu_config_value_t *val)
+{
+ int i, rc = 0;
+
+ switch (val->type) {
+ case MU_CFG_STRING:
+ enable_prog_trace(val->v.string);
+ break;
+
+ case MU_CFG_ARRAY:
+ for (i = 0; i < val->v.arg.c; i++) {
+ if (mu_cfg_assert_value_type(&val->v.arg.v[i],
+ MU_CFG_STRING,
+ err))
+ return 1;
+ enable_prog_trace(val->v.arg.v[i].v.string);
+ }
+ break;
+
+ case MU_CFG_LIST:
+ mu_list_do(val->v.list, flush_trace_module, NULL);
+ break;
+ }
+ return rc;
+}
+
+static int
+cb_relayed_domain_file(mu_debug_t err, void *data, mu_config_value_t *val)
+{
+ switch (val->type) {
+ case MU_CFG_STRING:
+ read_domain_file(err, val->v.string);
+ break;
+
+ case MU_CFG_LIST:
+ mu_list_do(val->v.list, load_relay_file, err);
+ break;
+
+ default:
+ mu_cfg_format_error (err, MU_DEBUG_ERROR,
+ _("expected string or list of strings"));
+ }
+ return 0;
+}
+
+
+struct mu_cfg_param mf_cfg_param[] = {
+ { ".pm:server", mu_cfg_section, NULL, 0, NULL, NULL },
+ { "stack-trace", mu_cfg_bool, &stack_trace_option, 0, NULL,
+ N_("Dump stack traces on runtime errors.") },
+
+ { "milter-timeout", mu_cfg_callback, NULL, 0, cb_milter_timeout,
+ N_("Set milter timeout."),
+ N_("time") },
+#if 0
+ /* FIXME */
+ { "io-timeout", mu_cfg_callback, &io_timeout, 0, cb_timeout,
+ N_("Timeout for all SMTP I/O operations."),
+ N_("time") },
+#endif
+
+ { "ehlo-domain", mu_cfg_callback, NULL, 0, cb_ehlo_domain,
+ N_("Set the domain name for EHLO command.") },
+ { "mail-from-address", mu_cfg_callback, NULL, 0, cb_mail_from_address,
+ N_("Set email address for use in SMTP `MAIL FROM' command. "
+ "Argument is an email address or a comma-separated list of "
+ "addresses. Use <> for null address. Other addresses can "
+ "be given without angle brackets."),
+ N_("addr") },
+ { "include-path", mu_cfg_callback, NULL, 0, cb_include_path,
+ N_("Add directories to the list of directories to be searched for "
+ "header files. Argument is a list of directory names "
+ "separated by colons."),
+ N_("path") },
+ { "setvar", mu_cfg_callback, NULL, 0, cb_set_variable,
+ N_("Initialize a mailfromd variable <var> to <value>."),
+ N_("var: string> <value: string-or-number") },
+ { "script-file", mu_cfg_string, &script_file, 0, NULL,
+ N_("Read filter script from <file>."),
+ N_("file") },
+ { "trace-actions", mu_cfg_bool, &do_trace, 0, NULL,
+ N_("Trace executed actions.") },
+ { "trace-program", mu_cfg_callback, NULL, 0, cb_trace_program,
+ N_("Enable filter program tracing."),
+ N_("modules: list") },
+
+ { "relayed-domain-file", mu_cfg_callback, NULL, 0,
+ cb_relayed_domain_file,
+ N_("Read relayed domain names from the file"),
+ N_("file: string") },
+
+ { "database", mu_cfg_section, NULL },
+
+ { "max-match-mx", mu_cfg_size, &max_match_mx, 0, NULL,
+ N_("Maximum number of MXs used by MFL \"mx match\" operation.") },
+
+ { "runtime", mu_cfg_section, NULL },
+
+ { NULL }
+};
+
+
+static struct mu_cfg_param *runtime_param;
+static size_t runtime_param_cnt;
+static size_t runtime_param_max;
+
+static void
+_add_runtime_param_entry(struct mu_cfg_param *p)
+{
+ if (runtime_param_cnt == runtime_param_max) {
+ if (runtime_param_max == 0)
+ runtime_param_max = 16;
+ runtime_param = x2nrealloc(runtime_param,
+ &runtime_param_max,
+ sizeof(runtime_param[0]));
+ }
+ runtime_param[runtime_param_cnt++] = *p;
+}
+
+void
+mf_add_runtime_params(struct mu_cfg_param *p)
+{
+ for (; p->ident; p++)
+ _add_runtime_param_entry(p);
+}
+
+void
+mf_runtime_param_finish()
+{
+ if (runtime_param_cnt) {
+ static struct mu_cfg_param term = { NULL };
+ struct mu_cfg_section *section;
+
+ _add_runtime_param_entry(&term);
+
+ if (mu_create_canned_section ("runtime", &section) == 0) {
+ section->parser = NULL;
+ section->docstring = N_("Configure MFL runtime values.");
+ section->label = NULL;
+ mu_cfg_section_add_params(section, runtime_param);
+ }
+ } else {
+ struct mu_cfg_param *p;
+ for (p = mf_cfg_param; p->ident; p++) {
+ if (strcmp (p->ident, "runtime") == 0) {
+ memset (p, 0, sizeof (*p));
+ break;
+ }
+ }
+ }
+}
+
+
+
+/* Auxiliary functions */
+
+static int
+db_format_enumerator(struct db_format *fmt, void *data)
+{
+ printf("%s database: %s\n", fmt->name, fmt->dbname);
+ if (strcmp(fmt->name, "cache") == 0) {
+ printf("%s positive expiration: %lu\n", fmt->name,
+ fmt->expire_interval);
+ printf("%s negative expiration: %lu\n", fmt->name,
+ negative_expire_interval);
+ } else
+ printf("%s expiration: %lu\n", fmt->name,
+ fmt->expire_interval);
+ return 0;
+}
+
+void
+mailfromd_show_defaults()
+{
+ printf("version: %s\n", VERSION);
+ printf("script file: %s\n", script_file);
+ printf("preprocessor: %s\n", ext_pp ? ext_pp : "none");
+ printf("user: %s\n", mf_server_user);
+ printf("statedir: %s\n", mailfromd_state_dir);
+ printf("socket: %s\n", DEFAULT_SOCKET);
+ printf("pidfile: %s\n", pidfile);
+#ifdef USE_SYSLOG_ASYNC
+#if DEFAULT_SYSLOG_ASYNC == 1
+ printf("default syslog: non-blocking\n");
+#else
+ printf("default syslog: blocking\n");
+#endif
+#endif
+ printf("database format: ");
+#if defined WITH_GDBM
+ printf("GDBM");
+#elif defined WITH_BDB
+ printf("Berkeley DB %d.x", WITH_BDB);
+#endif
+ printf("\n");
+
+ printf("Optional features: ");
+#if defined WITH_GEOIP
+ printf("GeoIP");
+#endif
+ printf("\n");
+
+ db_format_enumerate(db_format_enumerator, NULL);
+}
+
+static void
+_log_setup(const char *tag, char *stream)
+{
+ mu_debug_t debug;
+
+ mu_diag_get_debug (&debug);
+
+ if (logger_select(stream)) {
+ mu_error(_("unsupported logger stream: %s"), stream);
+ exit(EX_USAGE);
+ }
+ logger_open(tag, LOG_PID, mu_log_facility);
+ gacopyz_set_logger(mfd_gacopyz_log_printer);
+
+ /* FIXME */
+ if (strcmp(stream, "stderr") == 0) {
+ mu_debug_default_printer = mu_debug_stderr_printer;
+ } else {
+ mu_debug_set_print (debug, mf_diag_syslog_printer, NULL);
+ mu_debug_default_printer = mu_debug_syslog_printer;
+ }
+}
+
+void
+mf_server_log_setup(void)
+{
+ _log_setup(syslog_tag, log_stream);
+}
+
+static void
+version(FILE *stream, struct argp_state *state)
+{
+ mailfromd_version("mailfromd", stream);
+}
+
+static int
+argpflag(int argc, char **argv)
+{
+ int i;
+ int flag = 0;
+ for (i = 0; i < argc; i++) {
+ size_t len = strcspn(argv[i], "=");
+ if (len > 3
+ && (memcmp(argv[i], "--ru", 4) == 0
+ || memcmp(argv[i], "--run", 5) == 0)) {
+ flag = ARGP_IN_ORDER;
+ break;
+ }
+ }
+ return flag;
+}
+
+
+static void
+provide_default_milter_server()
+{
+ if (mfd_srvman_count_servers() == 0) {
+ mu_diag_output(MU_DIAG_WARNING,
+ _("no servers defined; will listen on %s"),
+ DEFAULT_SOCKET);
+ pm_srvcfg_add("milter", DEFAULT_SOCKET);
+ }
+}
+
+static void
+provide_default_callout_server()
+{
+ struct variable *var;
+
+ if (provide_callout &&
+ (!(var = variable_lookup("callout_server_url")) ||
+ (var->sym.flags & SYM_REFERENCED) &&
+ !(var->sym.flags & SYM_INITIALIZED))) {
+ struct value val;
+
+ val.type = dtype_string;
+ val.v.literal = string_alloc(DEFAULT_CALLOUT_SOCKET,
+ sizeof(DEFAULT_CALLOUT_SOCKET)-1);
+ ensure_initialized_variable("callout_server_url", &val);
+ pm_srvcfg_add("callout", val.v.literal->text);
+ }
+}
+
+
+static char *modnames[] = {
+#define __DBGMOD_C_ARRAY
+# include "mfd-dbgmod.h"
+#undef __DBGMOD_C_ARRAY
+ NULL
+};
+
+
+int
+pm_server_function(const char *key, mfd_server_func_t *pret)
+{
+ if (!key || strcmp(key, "default") == 0 || strcmp(key, "milter") == 0)
+ *pret = milter_session_server;
+ else if (strcmp(key, "callout") == 0)
+ *pret = callout_session_server;
+ return 0;
+}
+
+
+extern char **environ;
+
+int
+main(int argc, char **argv)
+{
+ int rc;
+ int index;
+ prog_counter_t entry_point;
+ struct arguments args;
+
+ mf_init_nls();
+ mf_proctitle_init(argc, argv, environ);
+
+ MU_AUTH_REGISTER_ALL_MODULES();
+ mu_register_all_formats();
+ mu_register_all_mailer_formats();
+ if (!program_invocation_short_name)
+ program_invocation_short_name = argv[0];
+ argp_program_version_hook = version;
+ yy_flex_debug = 0;
+
+ /* Set default logging */
+ mu_log_facility = DEFAULT_LOG_FACILITY;
+ _log_setup(MU_LOG_TAG(), stderr_closed_p() ? "syslog" : "stderr");
+
+ debug_init(modnames);
+ init_string_space();
+ init_symbols();
+ builtin_setup();
+ mf_runtime_param_finish();
+ libdbm_init();
+ db_format_setup();
+ include_path_setup();
+ pragma_setup();
+ pm_optcache_add(option_cache, 0, PM_OCF_NULL|PM_OCF_STATIC);
+ mf_server_save_cmdline(argc, argv);
+
+ mu_acl_cfg_init();
+ database_cfg_init();
+ srvman_init();
+ pm_srvcfg_init(N_("(milter | server)"));
+
+ mu_argp_init(program_version, "<" PACKAGE_BUGREPORT ">");
+ mu_app_rcfile = DEFAULT_CONFIG_FILE;
+ init_arguments(&args);
+ rc = mfd_app_init(&argp, capa, mf_cfg_param, argc, argv,
+ argpflag(argc, argv), &index, &args, NULL);
+ if (rc)
+ exit(EX_CONFIG);
+ flush_arguments(&args);
+
+ alloc_ext_pp();
+
+ argv += index;
+ argc -= index;
+
+ if (need_script) {
+ char *new_script = NULL;
+ if (argc) {
+ int i, n = -1;
+ for (i = 0; i < argc; i++) {
+ if (strchr(argv[i], '=') == 0) {
+ if (n == -1) {
+ n = i;
+ if (mode == MAILFROMD_RUN)
+ break;
+ } else {
+ mu_error(_("script file "
+ "specified twice "
+ "(%s and %s)"),
+ argv[n], argv[i]);
+ exit(EX_USAGE);
+ }
+ }
+ }
+ if (n >= 0) {
+ new_script = argv[n];
+ memmove(argv + n, argv + n + 1,
+ (argc - n + 1) * sizeof argv[0]);
+ argc--;
+ }
+ }
+
+ if (new_script)
+ script_file = new_script;
+
+ if (script_file[0] != '/')
+ /* Clear saved command line */
+ mf_server_save_cmdline(0, NULL);
+ if (preprocess_option)
+ exit(preprocess_input());
+ if (parse_program(script_file, script_ydebug))
+ exit(EX