From a1718144344d2743713164adc1790ec1ac399d8c Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sat, 21 Aug 2010 11:46:57 +0300 Subject: Rename mfd -> src --- src/main.c | 1383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1383 insertions(+) create mode 100644 src/main.c (limited to 'src/main.c') 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 to ."), + N_("var: string> ."), + 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", §ion) == 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_CONFIG); + } + pm_srvcfg_flush(); + + fixup_create_script(); + + /* Make sure syslog_tag is properly set */ + /* FIXME: Move this to logger.c? */ + if (!syslog_tag) + syslog_tag = (char*) MU_LOG_TAG(); + + if (script_dump_tree) + print_syntax_tree(); + if (script_dump_code) + print_code(); + + if (script_dump_xref) + print_xref(); + + if (script_dump_macros) + print_used_macros(); + + fixup_code(); + + if (script_check || script_dump_macros + || script_dump_code || script_dump_tree || script_dump_xref + || yy_flex_debug || script_ydebug) + exit(EX_OK); + + switch (mode) { + case MAILFROMD_DAEMON: + provide_default_milter_server(); + provide_default_callout_server(); + break; + + case MAILFROMD_RUN: { + struct function *fun = function_lookup(main_function_name); + if (!fun) { + mu_error(_("function %s is not defined"), + main_function_name); + exit(EX_CONFIG); + } + + if (fun->parmcount || !fun->varargs) { + mu_error(_("function %s must take variable number of " + "arguments"), + main_function_name); + exit(EX_CONFIG); + } + if (fun->rettype != dtype_number) { + mu_error(_("function %s must return number"), + main_function_name); + exit(EX_CONFIG); + } + entry_point = fun->entry; + } + } + + free_symbols(); + free_string_space(); + free_parser_data(); + + pm_namefixup_run(mailfromd_state_dir); + pm_namefixup_free(); + + switch (mode) { + case MAILFROMD_DAEMON: + if (argc > 0) { + mu_error(_("too many arguments")); + exit(EX_USAGE); + } + if (script_file[0] != '/') { + mu_diag_output(MU_DIAG_WARNING, + _("script file is given " + "without full file name")); + server_flags |= MF_SERVER_NORESTART; + } + mf_server_start("mailfromd", mailfromd_state_dir, pidfile, + server_flags); + break; + + case MAILFROMD_TEST: + mailfromd_test(argc, argv); + break; + + case MAILFROMD_SHOW_DEFAULTS: + mailfromd_show_defaults(); + break; + + case MAILFROMD_RUN: + mailfromd_run(entry_point, argc, argv); + } + + exit(EX_OK); +} + +void +xalloc_die() +{ + parse_error("not enough memory"); + abort(); +} -- cgit v1.2.1