/* 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 "mailfromd.h"
#include "callout.h"
#include "srvman.h"
#include "inttostr.h"
#include "srvcfg.h"
#include "filenames.h"
#include "mf-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";
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 = 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
trace(const char *fmt, ...)
{
if (do_trace) {
va_list ap;
va_start(ap, fmt);
vlogmsg(MU_LOG_INFO, fmt, ap);
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 && 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) {
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(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(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 mf_option_cache option_cache[] = {
{ "stack-trace", NULL, mf_option_boolean, set_stack_trace },
{ "milter-timeout", NULL, mf_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':
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;
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:
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, 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(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_timeout(void *data, mu_config_value_t *arg)
{
struct timeval tv;
int rc = config_cb_timeout (&tv, arg);
if (rc == 0)
*(time_t*) data = tv.tv_sec;
return rc;
}
static int
cb_set_variable(void *data, mu_config_value_t *arg)
{
const char *value;
char *alloc_str = NULL;
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();
}
defer_initialize_variable(arg->v.arg.v[0].v.string, value);
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_do(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_do(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[] = {
{ ".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") },
{ "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 },
{ "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
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 =
xstrdup(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);
}
}
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_set_program_name (argv[0]);
mu_log_tag = (char*)mu_program_name;
mu_log_facility = DEFAULT_LOG_FACILITY;
mu_stdstream_setup ();
mf_srvcfg_log_setup(stderr_closed_p() ? "syslog" : "stderr");
debug_init(modnames);
libcallout_init();
init_string_space();
init_symbols();
builtin_setup();
mf_runtime_param_finish();
libdbm_init();
db_format_setup();
include_path_setup();
pragma_setup();
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();
mf_server_log_setup();
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);
}
fixup_create_script();
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_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_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);
}
void
xalloc_die()
{
parse_error("not enough memory");
abort();
}