/* This file is part of Mailfromd. Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 #include "mailfromd.h" #include "callout.h" #include "srvman.h" #include "srvcfg.h" #include "filenames.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 */ 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"; char *callout_server_url; mu_stream_t mf_strecho; /* Output stream for 'echo' statements */ /* 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 = mu_realloc(ext_pp_options, len + 1); } else { ext_pp_options = mu_alloc(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 */ static mu_stream_t mf_trace_stream; static void open_trace_stream() { int rc = mu_filter_create(&mf_trace_stream, mf_strecho, "C-escape", MU_FILTER_ENCODE, MU_STREAM_WRITE); if (rc) { mu_error(_("cannot create trace stream, " "using standard log: %s"), mu_strerror(rc)); mf_trace_stream = mf_strecho; } } void trace(const char *fmt, ...) { if (do_trace) { int bval = 0; va_list ap; if (!mf_trace_stream) open_trace_stream(); va_start(ap, fmt); mu_stream_vprintf(mf_trace_stream, fmt, ap); bval = 1; mu_stream_ioctl(mf_trace_stream, MU_IOCTL_FILTER, MU_IOCTL_FILTER_SET_DISABLED, &bval); mu_stream_write(mf_trace_stream, "\n", 1, NULL); bval = 0; mu_stream_ioctl(mf_trace_stream, MU_IOCTL_FILTER, MU_IOCTL_FILTER_SET_DISABLED, &bval); va_end(ap); } } void log_status(sfsistat status, SMFICTX *ctx) { mu_debug_level_t lev; mu_debug_category_level(NULL, 0, &lev); if ((lev & ~MU_DEBUG_LEVEL_MASK(MU_DEBUG_ERROR)) && status != SMFIS_CONTINUE) { const char *str = sfsistat_str(status); if (str) logmsg(MU_LOG_INFO, "%s%s", mailfromd_msgid(ctx), str); else logmsg(MU_LOG_INFO, "%sstatus %d", mailfromd_msgid(ctx), status); } } /* Sendmail class file support */ static mu_list_t domain_list; /* Read domains from sendmail-style domain file NAME and store them in DOMAIN_LIST */ int read_domain_file(const char *name) { FILE *fp; char buf[256]; char *p; fp = fopen(name, "r"); if (!fp) { mu_error(_("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, mf_list_compare_string); } while (p = fgets(buf, sizeof buf, fp)) { char *q; while (*p && mu_isspace(*p)) p++; if (*p == '#') continue; for (q = p; *q && !mu_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) { mu_debug(MF_SOURCE_MAIN, MU_DEBUG_TRACE5, ("%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(union mf_option_value *val) { if (smfi_settimeout(val->ov_time) == MI_FAILURE) { mu_error(_("invalid milter timeout: %lu"), (unsigned long)val->ov_time); exit(EX_USAGE); } } static int load_relay_file(void *item, void *data) { read_domain_file(item); return 0; } static void set_relay(union mf_option_value *val) { mu_list_foreach(val->ov_list, load_relay_file, NULL); mu_list_destroy(&val->ov_list); } void set_stack_trace(union mf_option_value *val) { stack_trace_option = val->ov_bool; } static int option_relay(char const *opt, union mf_option_value *val, char const *newval) { if (!val->ov_list) mu_list_create(&val->ov_list); mu_list_append(val->ov_list, strdup(newval)); return 0; } struct mf_option_cache option_cache[] = { { "stack-trace", mf_option_boolean, set_stack_trace }, { "milter-timeout", mf_option_timeout, set_milter_timeout }, { "relay", 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_foreach(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 'd': mf_optcache_set_option("debug", 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': mf_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; struct locus locus = { "", 1, 0 }; p = strchr(arg, '='); if (!p) argp_error(state, _("expected assignment, but found `%s'"), arg); *p++ = 0; defer_initialize_variable(arg, p, &locus); break; } case OPTION_DAEMON: mode = MAILFROMD_DAEMON; need_script = 1; break; case OPTION_DOMAIN_FILE: mf_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: mf_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: mf_optcache_set_option("stack-trace", "yes"); break; case OPTION_TIMEOUT: mf_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, mu_strdup(arg ? arg : "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", "locking", "mailer", NULL }; static struct argp argp = { options, parse_opt, args_doc, doc, NULL, NULL, NULL }; /* Mailutils-2.0 configuration */ static int cb_milter_timeout(void *data, mu_config_value_t *arg) { struct timeval tv; int rc = config_cb_timeout (&tv, arg); if (rc) return 1; if (smfi_settimeout(tv.tv_sec) == MI_FAILURE) { mu_error(_("Invalid milter timeout: %lu"), (unsigned long) tv.tv_sec); exit(EX_USAGE); } return 0; } static int cb_set_variable(void *data, mu_config_value_t *arg) { const char *value; char *alloc_str = NULL; struct mu_locus mloc; struct locus locus; if (mu_cfg_assert_value_type(arg, MU_CFG_ARRAY)) return 1; if (arg->v.arg.c < 2) { mu_error(_("not enough arguments")); return 1; } else if (arg->v.arg.c > 2) { mu_error(_("too many arguments")); return 1; } if (mu_cfg_assert_value_type(&arg->v.arg.v[0], MU_CFG_STRING)) 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]); value = alloc_str; break; case MU_CFG_LIST: mu_error(_("unexpected list")); return 1; default: mu_error (_("INTERNAL ERROR at %s:%d: please report"), __FILE__, __LINE__); abort(); } locus.leng = 0; if (mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_GET_LOCUS, &mloc)) { locus.file = ""; locus.line = 0; locus.point = 0; } else { if (mloc.mu_file) { struct literal *lit = string_alloc(mloc.mu_file, strlen(mloc.mu_file)); free(mloc.mu_file); locus.file = lit->text; } else locus.file = ""; locus.line = mloc.mu_line; locus.point = mloc.mu_col; } defer_initialize_variable(arg->v.arg.v[0].v.string, value, &locus); free(alloc_str); return 0; } static int cb_include_path(void *data, mu_config_value_t *val) { int i, rc = 0; struct mu_wordsplit ws; mu_iterator_t itr; switch (val->type) { case MU_CFG_STRING: ws.ws_delim = ":"; if (mu_wordsplit(val->v.string, &ws, MU_WRDSF_NOVAR | MU_WRDSF_NOCMD | MU_WRDSF_DELIM)) { mu_error("mu_wordsplit: %s", mu_wordsplit_strerror(&ws)); return 1; } for (i = 0; i < ws.ws_wordc; i++) add_include_dir(ws.ws_wordv[i]); mu_wordsplit_free(&ws); 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)) 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); if (rc) break; add_include_dir(pval->v.string); } mu_iterator_destroy(&itr); } return rc; } static int cb_trace_program(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)) return 1; enable_prog_trace(val->v.arg.v[i].v.string); } break; case MU_CFG_LIST: mu_list_foreach(val->v.list, flush_trace_module, NULL); break; } return rc; } static int cb_relayed_domain_file(void *data, mu_config_value_t *val) { switch (val->type) { case MU_CFG_STRING: read_domain_file(val->v.string); break; case MU_CFG_LIST: mu_list_foreach(val->v.list, load_relay_file, NULL); break; default: mu_error (_("expected string or list of strings")); } return 0; } struct mu_cfg_param mf_cfg_param[] = { { ".mfd: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") }, { "callout-url", mu_cfg_string, &callout_server_url, 0, NULL, N_("Sets the URL of the default callout server."), N_("url") }, { "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 }, { "lock-retry-count", mu_cfg_callback, NULL, 0, config_cb_lock_retry_count, N_("Retry acquiring DBM file lock this number of times.") }, { "lock-retry-timeout", mu_cfg_callback, NULL, 0, config_cb_lock_retry_timeout, N_("Set the time span between the two DBM locking attempts."), N_("time") }, { "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 = mu_2nrealloc(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; } static void list_db_formats(const char *pfx) { mu_iterator_t itr; int rc; const char *defdb = DEFAULT_DB_TYPE; printf("%s", pfx); rc = mu_dbm_impl_iterator(&itr); if (rc) { printf("%s\n", _("unknown")); mu_error("%s", mu_strerror(rc)); } else { int i; for (mu_iterator_first(itr), i = 0; !mu_iterator_is_done(itr); mu_iterator_next(itr), i++) { struct mu_dbm_impl *impl; mu_iterator_current(itr, (void**)&impl); if (i) printf(", "); else if (!defdb) defdb = impl->_dbm_name; printf("%s", impl->_dbm_name); } putchar('\n'); mu_iterator_destroy(&itr); } printf("default database type: %s\n", defdb); } 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 list_db_formats("supported databases: "); printf("Optional features: "); #if defined WITH_GEOIP printf(" GeoIP"); #endif #if defined WITH_DSPAM printf(" DSPAM"); #endif printf("\n"); db_format_enumerate(db_format_enumerator, NULL); } 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); mf_srvcfg_add("milter", DEFAULT_SOCKET); } } static void provide_default_callout_server() { struct variable *var; if (provide_callout && !callout_server_url && (!(var = variable_lookup("callout_server_url")) || (var->sym.flags & SYM_REFERENCED) && !(var->sym.flags & SYM_INITIALIZED))) { mf_srvcfg_add("callout", DEFAULT_CALLOUT_SOCKET); } } static char *modnames[] = { #define __DBGMOD_C_ARRAY # include "mfd-dbgmod.h" #undef __DBGMOD_C_ARRAY NULL }; mu_debug_handle_t mfd_debug_handle; static void debug_init() { int i; mfd_debug_handle = mu_debug_register_category (modnames[0]); for (i = 1; modnames[i]; i++) mu_debug_register_category (modnames[i]); } int mf_server_function(const char *key, struct mf_srvcfg *cfg) { if (!key || strcmp(key, "default") == 0 || strcmp(key, "milter") == 0) cfg->server = milter_session_server; else if (strcmp(key, "callout") == 0) { cfg->server = mfd_callout_session_server; if (cfg->defopt || mu_list_locate(cfg->options, "default", NULL) == 0) callout_server_url = mu_strdup(mu_url_to_string(cfg->url)); } else return 1; return 0; } static void open_strecho (int daemon_mode) { int rc; if (daemon_mode) { rc = mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_CLONE, &mf_strecho); if (rc == 0) { int s = MU_LOG_INFO; mu_stream_ioctl(mf_strecho, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_SET_SEVERITY, &s); } } else { #if 0 mf_strecho = mu_strout; mu_stream_ref(mf_strecho); rc = 0; #else rc = mu_stdio_stream_create(&mf_strecho, MU_STDERR_FD, 0); #endif } if (rc) { mu_diag_output(MU_LOG_CRIT, _("cannot create echo output stream: %s"), mu_strerror(rc)); exit(EX_UNAVAILABLE); } } void mailfromd_alloc_die() { parse_error("not enough memory"); abort(); } extern char **environ; extern char *program_invocation_short_name;//FIXME int main(int argc, char **argv) { int rc; int index; prog_counter_t entry_point; struct arguments args; int stderr_is_closed = stderr_closed_p(); mf_init_nls(); mf_proctitle_init(argc, argv, environ); mu_alloc_die_hook = mailfromd_alloc_die; 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_set_program_name(argv[0]); mu_log_tag = (char*)mu_program_name; mu_log_facility = DEFAULT_LOG_FACILITY; mu_stdstream_setup(MU_STDSTREAM_RESET_NONE); mf_srvcfg_log_setup(stderr_is_closed ? "syslog" : "stderr"); debug_init(modnames); libcallout_init(); init_string_space(); init_symbols(); builtin_setup(); mf_runtime_param_finish(); db_format_setup(); include_path_setup(); pragma_setup(); mf_init_lock_options(); mf_optcache_add(option_cache, 0, MF_OCF_NULL|MF_OCF_STATIC); mf_server_save_cmdline(argc, argv); dnsbase_init(); mu_acl_cfg_init(); database_cfg_init(); srvman_init(); mf_srvcfg_init(argv[0], N_("(milter | callout)")); mu_argp_init(program_version, "<" PACKAGE_BUGREPORT ">"); mu_site_rcfile = DEFAULT_CONFIG_FILE; init_arguments(&args); rc = mu_app_init(&argp, capa, mf_cfg_param, argc, argv, argpflag(argc, argv), &index, &args); if (rc) exit(EX_CONFIG); flush_arguments(&args); mf_srvcfg_flush(); 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); } 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: mf_server_log_setup(); 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_exceptions(); free_symbols(); free_string_space(); free_parser_data(); mf_namefixup_run(mailfromd_state_dir); mf_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; } open_strecho(1); mf_server_lint_option = "--lint"; mf_server_start("mailfromd", mailfromd_state_dir, pidfile, server_flags); break; case MAILFROMD_TEST: open_strecho(0); mailfromd_test(argc, argv); break; case MAILFROMD_SHOW_DEFAULTS: mailfromd_show_defaults(); break; case MAILFROMD_RUN: open_strecho(0); mailfromd_run(entry_point, argc, argv); } exit(EX_OK); }