#include <sys_defs.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pwd.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#ifdef USE_PATHS_H
#include <paths.h>
#endif
#include <msg.h>
#include <vstream.h>
#include <msg_vstream.h>
#include <get_hostname.h>
#include <stringops.h>
#include <htable.h>
#include <dict.h>
#include <safe.h>
#include <mymalloc.h>
#include <argv.h>
#include <split_at.h>
#include <vstring_vstream.h>
#include <myflock.h>
#include <inet_proto.h>
#include <argv.h>
#include <edit_file.h>
#include <mynetworks.h>
#include <mail_conf.h>
#include <mail_dict.h>
#include <mail_proto.h>
#include <mail_version.h>
#include <mail_params.h>
#include <mail_addr.h>
#include <mbox_conf.h>
#include <mail_run.h>
#include <xsasl.h>
#define SHOW_NONDEF (1<<0)
#define SHOW_DEFS (1<<1)
#define SHOW_NAME (1<<2)
#define SHOW_MAPS (1<<3)
#define EDIT_MAIN (1<<4)
#define SHOW_LOCKS (1<<5)
#define SHOW_EVAL (1<<6)
#define SHOW_SASL_SERV (1<<7)
#define SHOW_SASL_CLNT (1<<8)
#define COMMENT_OUT (1<<9)
HTABLE *param_table;
DICT *text_table;
#include "time_vars.h"
#include "bool_vars.h"
#include "int_vars.h"
#include "str_vars.h"
#include "raw_vars.h"
#include "nint_vars.h"
#include "nbool_vars.h"
#include "auto_vars.h"
#include "install_vars.h"
static const CONFIG_TIME_TABLE time_table[] = {
#include "time_table.h"
0,
};
static const CONFIG_BOOL_TABLE bool_table[] = {
#include "bool_table.h"
0,
};
static const CONFIG_INT_TABLE int_table[] = {
#include "int_table.h"
0,
};
static const CONFIG_STR_TABLE str_table[] = {
#include "str_table.h"
#include "auto_table.h"
#include "install_table.h"
0,
};
static const CONFIG_RAW_TABLE raw_table[] = {
#include "raw_table.h"
0,
};
static const CONFIG_NINT_TABLE nint_table[] = {
#include "nint_table.h"
0,
};
static const CONFIG_NBOOL_TABLE nbool_table[] = {
#include "nbool_table.h"
0,
};
char *var_myhostname;
char *var_mydomain;
char *var_mynetworks;
static const char *check_myhostname(void);
static const char *check_mydomainname(void);
static const char *check_mynetworks(void);
static const CONFIG_STR_FN_TABLE str_fn_table[] = {
VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
0,
};
static const CONFIG_STR_FN_TABLE str_fn_table_2[] = {
VAR_MYNETWORKS, check_mynetworks, &var_mynetworks, 1, 0,
0,
};
#define DEF_MODE SHOW_NAME
static int cmd_mode = DEF_MODE;
static const char *check_myhostname(void)
{
static const char *name;
const char *dot;
const char *domain;
if (name)
return (name);
name = get_hostname();
if ((dot = strchr(name, '.')) == 0) {
if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0)
domain = DEF_MYDOMAIN;
name = concatenate(name, ".", domain, (char *) 0);
}
return (name);
}
static void get_myhostname(void)
{
const char *name;
if ((name = mail_conf_lookup_eval(VAR_MYHOSTNAME)) == 0)
name = check_myhostname();
var_myhostname = mystrdup(name);
}
static const char *check_mydomainname(void)
{
char *dot;
if (var_myhostname == 0)
get_myhostname();
if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0)
return (DEF_MYDOMAIN);
return (dot + 1);
}
static const char *check_mynetworks(void)
{
INET_PROTO_INFO *proto_info;
const char *junk;
if (var_inet_interfaces == 0) {
if ((cmd_mode & SHOW_DEFS)
|| (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0)
junk = DEF_INET_INTERFACES;
var_inet_interfaces = mystrdup(junk);
}
if (var_mynetworks_style == 0) {
if ((cmd_mode & SHOW_DEFS)
|| (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0)
junk = DEF_MYNETWORKS_STYLE;
var_mynetworks_style = mystrdup(junk);
}
if (var_inet_protocols == 0) {
if ((cmd_mode & SHOW_DEFS)
|| (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0)
junk = DEF_INET_PROTOCOLS;
var_inet_protocols = mystrdup(junk);
proto_info = inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);
}
return (mynetworks());
}
static void edit_parameters(int cmd_mode, int argc, char **argv)
{
char *config_dir;
char *path;
EDIT_FILE *ep;
VSTREAM *src;
VSTREAM *dst;
VSTRING *buf = vstring_alloc(100);
VSTRING *key = vstring_alloc(10);
char *cp;
char *edit_key;
char *edit_val;
HTABLE *table;
struct cvalue {
char *value;
int found;
};
struct cvalue *cvalue;
HTABLE_INFO **ht_info;
HTABLE_INFO **ht;
int interesting;
const char *err;
table = htable_create(argc);
while ((cp = *argv++) != 0) {
if (strchr(cp, '\n') != 0)
msg_fatal("-e or -# accepts no multi-line input");
while (ISSPACE(*cp))
cp++;
if (*cp == '#')
msg_fatal("-e or -# accepts no comment input");
if (cmd_mode & EDIT_MAIN) {
if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
msg_fatal("%s: \"%s\"", err, cp);
} else if (cmd_mode & COMMENT_OUT) {
if (*cp == 0)
msg_fatal("-# requires non-blank parameter names");
if (strchr(cp, '=') != 0)
msg_fatal("-# requires parameter names only");
edit_key = mystrdup(cp);
trimblanks(edit_key, 0);
edit_val = 0;
} else {
msg_panic("edit_parameters: unknown mode %d", cmd_mode);
}
cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
cvalue->value = edit_val;
cvalue->found = 0;
htable_enter(table, edit_key, (char *) cvalue);
}
if (var_config_dir)
myfree(var_config_dir);
var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
config_dir : DEF_CONFIG_DIR);
set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
dst = ep->tmp_fp;
if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
(void) unlink(ep->tmp_path);
msg_fatal("open %s for reading: %m", path);
}
#define STR(x) vstring_str(x)
interesting = 0;
while (vstring_get(buf, src) != VSTREAM_EOF) {
for (cp = STR(buf); ISSPACE(*cp) ; cp++)
;
if (*cp == '#' || *cp == 0) {
vstream_fputs(STR(buf), dst);
}
else if (cp > STR(buf)) {
if (interesting == 0)
vstream_fputs(STR(buf), dst);
else if (cmd_mode & COMMENT_OUT)
vstream_fprintf(dst, "#%s", STR(buf));
}
else {
vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
cvalue = (struct cvalue *) htable_find(table, STR(key));
if ((interesting = !!cvalue) != 0) {
if (cvalue->found++ == 1)
msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
if (cmd_mode & EDIT_MAIN)
vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
else if (cmd_mode & COMMENT_OUT)
vstream_fprintf(dst, "#%s", cp);
else
msg_panic("edit_parameters: unknown mode %d", cmd_mode);
} else {
vstream_fputs(STR(buf), dst);
}
}
}
if (cmd_mode & EDIT_MAIN) {
for (ht_info = ht = htable_list(table); *ht; ht++) {
cvalue = (struct cvalue *) ht[0]->value;
if (cvalue->found == 0)
vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
}
myfree((char *) ht_info);
}
if (vstream_fclose(src))
msg_fatal("read %s: %m", path);
if (edit_file_close(ep) != 0)
msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);
myfree(path);
vstring_free(buf);
vstring_free(key);
htable_free(table, myfree);
}
static void read_parameters(void)
{
char *config_dir;
char *path;
dict_unknown_allowed = 1;
if (var_config_dir)
myfree(var_config_dir);
var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
config_dir : DEF_CONFIG_DIR);
set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
dict_load_file(CONFIG_DICT, path);
myfree(path);
}
static void set_parameters(void)
{
}
static void hash_parameters(void)
{
const CONFIG_TIME_TABLE *ctt;
const CONFIG_BOOL_TABLE *cbt;
const CONFIG_INT_TABLE *cit;
const CONFIG_STR_TABLE *cst;
const CONFIG_STR_FN_TABLE *csft;
const CONFIG_RAW_TABLE *rst;
const CONFIG_NINT_TABLE *nst;
const CONFIG_NBOOL_TABLE *bst;
param_table = htable_create(100);
for (ctt = time_table; ctt->name; ctt++)
htable_enter(param_table, ctt->name, (char *) ctt);
for (cbt = bool_table; cbt->name; cbt++)
htable_enter(param_table, cbt->name, (char *) cbt);
for (cit = int_table; cit->name; cit++)
htable_enter(param_table, cit->name, (char *) cit);
for (cst = str_table; cst->name; cst++)
htable_enter(param_table, cst->name, (char *) cst);
for (csft = str_fn_table; csft->name; csft++)
htable_enter(param_table, csft->name, (char *) csft);
for (csft = str_fn_table_2; csft->name; csft++)
htable_enter(param_table, csft->name, (char *) csft);
for (rst = raw_table; rst->name; rst++)
htable_enter(param_table, rst->name, (char *) rst);
for (nst = nint_table; nst->name; nst++)
htable_enter(param_table, nst->name, (char *) nst);
for (bst = nbool_table; bst->name; bst++)
htable_enter(param_table, bst->name, (char *) bst);
}
static void show_strval(int mode, const char *name, const char *value)
{
if (mode & SHOW_EVAL)
value = mail_conf_eval(value);
if (mode & SHOW_NAME) {
vstream_printf("%s = %s\n", name, value);
} else {
vstream_printf("%s\n", value);
}
}
static void show_intval(int mode, const char *name, int value)
{
if (mode & SHOW_NAME) {
vstream_printf("%s = %d\n", name, value);
} else {
vstream_printf("%d\n", value);
}
}
static void print_bool(int mode, CONFIG_BOOL_TABLE *cbt)
{
const char *value;
if (mode & SHOW_DEFS) {
show_strval(mode, cbt->name, cbt->defval ? "yes" : "no");
} else {
value = dict_lookup(CONFIG_DICT, cbt->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, cbt->name, cbt->defval ? "yes" : "no");
} else {
show_strval(mode, cbt->name, value);
}
} else {
if (value != 0)
show_strval(mode, cbt->name, value);
}
}
}
static void print_time(int mode, CONFIG_TIME_TABLE *ctt)
{
const char *value;
if (mode & SHOW_DEFS) {
show_strval(mode, ctt->name, ctt->defval);
} else {
value = dict_lookup(CONFIG_DICT, ctt->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, ctt->name, ctt->defval);
} else {
show_strval(mode, ctt->name, value);
}
} else {
if (value != 0)
show_strval(mode, ctt->name, value);
}
}
}
static void print_int(int mode, CONFIG_INT_TABLE *cit)
{
const char *value;
if (mode & SHOW_DEFS) {
show_intval(mode, cit->name, cit->defval);
} else {
value = dict_lookup(CONFIG_DICT, cit->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_intval(mode, cit->name, cit->defval);
} else {
show_strval(mode, cit->name, value);
}
} else {
if (value != 0)
show_strval(mode, cit->name, value);
}
}
}
static void print_str(int mode, CONFIG_STR_TABLE *cst)
{
const char *value;
if (mode & SHOW_DEFS) {
show_strval(mode, cst->name, cst->defval);
} else {
value = dict_lookup(CONFIG_DICT, cst->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, cst->name, cst->defval);
} else {
show_strval(mode, cst->name, value);
}
} else {
if (value != 0)
show_strval(mode, cst->name, value);
}
}
}
static void print_str_fn(int mode, CONFIG_STR_FN_TABLE *csft)
{
const char *value;
if (mode & SHOW_DEFS) {
show_strval(mode, csft->name, csft->defval());
} else {
value = dict_lookup(CONFIG_DICT, csft->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, csft->name, csft->defval());
} else {
show_strval(mode, csft->name, value);
}
} else {
if (value != 0)
show_strval(mode, csft->name, value);
}
}
}
static void print_str_fn_2(int mode, CONFIG_STR_FN_TABLE *csft)
{
const char *value;
if (mode & SHOW_DEFS) {
show_strval(mode, csft->name, csft->defval());
} else {
value = dict_lookup(CONFIG_DICT, csft->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, csft->name, csft->defval());
} else {
show_strval(mode, csft->name, value);
}
} else {
if (value != 0)
show_strval(mode, csft->name, value);
}
}
}
static void print_raw(int mode, CONFIG_RAW_TABLE * rst)
{
const char *value;
if (mode & SHOW_EVAL)
msg_warn("parameter %s expands at run-time", rst->name);
mode &= ~SHOW_EVAL;
if (mode & SHOW_DEFS) {
show_strval(mode, rst->name, rst->defval);
} else {
value = dict_lookup(CONFIG_DICT, rst->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, rst->name, rst->defval);
} else {
show_strval(mode, rst->name, value);
}
} else {
if (value != 0)
show_strval(mode, rst->name, value);
}
}
}
static void print_nint(int mode, CONFIG_NINT_TABLE * rst)
{
const char *value;
if (mode & SHOW_EVAL)
msg_warn("parameter %s expands at run-time", rst->name);
mode &= ~SHOW_EVAL;
if (mode & SHOW_DEFS) {
show_strval(mode, rst->name, rst->defval);
} else {
value = dict_lookup(CONFIG_DICT, rst->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, rst->name, rst->defval);
} else {
show_strval(mode, rst->name, value);
}
} else {
if (value != 0)
show_strval(mode, rst->name, value);
}
}
}
static void print_nbool(int mode, CONFIG_NBOOL_TABLE * bst)
{
const char *value;
if (mode & SHOW_EVAL)
msg_warn("parameter %s expands at run-time", bst->name);
mode &= ~SHOW_EVAL;
if (mode & SHOW_DEFS) {
show_strval(mode, bst->name, bst->defval);
} else {
value = dict_lookup(CONFIG_DICT, bst->name);
if ((mode & SHOW_NONDEF) == 0) {
if (value == 0) {
show_strval(mode, bst->name, bst->defval);
} else {
show_strval(mode, bst->name, value);
}
} else {
if (value != 0)
show_strval(mode, bst->name, value);
}
}
}
static void print_parameter(int mode, char *ptr)
{
#define INSIDE(p,t) (ptr >= (char *) t && ptr < ((char *) t) + sizeof(t))
if (INSIDE(ptr, time_table))
print_time(mode, (CONFIG_TIME_TABLE *) ptr);
if (INSIDE(ptr, bool_table))
print_bool(mode, (CONFIG_BOOL_TABLE *) ptr);
if (INSIDE(ptr, int_table))
print_int(mode, (CONFIG_INT_TABLE *) ptr);
if (INSIDE(ptr, str_table))
print_str(mode, (CONFIG_STR_TABLE *) ptr);
if (INSIDE(ptr, str_fn_table))
print_str_fn(mode, (CONFIG_STR_FN_TABLE *) ptr);
if (INSIDE(ptr, str_fn_table_2))
print_str_fn_2(mode, (CONFIG_STR_FN_TABLE *) ptr);
if (INSIDE(ptr, raw_table))
print_raw(mode, (CONFIG_RAW_TABLE *) ptr);
if (INSIDE(ptr, nint_table))
print_nint(mode, (CONFIG_NINT_TABLE *) ptr);
if (INSIDE(ptr, nbool_table))
print_nbool(mode, (CONFIG_NBOOL_TABLE *) ptr);
if (msg_verbose)
vstream_fflush(VSTREAM_OUT);
}
static int comp_names(const void *a, const void *b)
{
HTABLE_INFO **ap = (HTABLE_INFO **) a;
HTABLE_INFO **bp = (HTABLE_INFO **) b;
return (strcmp(ap[0]->key, bp[0]->key));
}
static void show_maps(void)
{
ARGV *maps_argv;
int i;
maps_argv = dict_mapnames();
for (i = 0; i < maps_argv->argc; i++)
vstream_printf("%s\n", maps_argv->argv[i]);
argv_free(maps_argv);
}
static void show_locks(void)
{
ARGV *locks_argv;
int i;
locks_argv = mbox_lock_names();
for (i = 0; i < locks_argv->argc; i++)
vstream_printf("%s\n", locks_argv->argv[i]);
argv_free(locks_argv);
}
static void show_sasl(int what)
{
ARGV *sasl_argv;
int i;
sasl_argv = (what & SHOW_SASL_SERV) ? xsasl_server_types() :
xsasl_client_types();
for (i = 0; i < sasl_argv->argc; i++)
vstream_printf("%s\n", sasl_argv->argv[i]);
argv_free(sasl_argv);
}
static void show_parameters(int mode, char **names)
{
HTABLE_INFO **list;
HTABLE_INFO **ht;
char **namep;
char *value;
if (*names == 0) {
list = htable_list(param_table);
qsort((char *) list, param_table->used, sizeof(*list), comp_names);
for (ht = list; *ht; ht++)
print_parameter(mode, ht[0]->value);
myfree((char *) list);
return;
}
for (namep = names; *namep; namep++) {
if ((value = htable_find(param_table, *namep)) == 0) {
msg_warn("%s: unknown parameter", *namep);
} else {
print_parameter(mode, value);
}
}
}
MAIL_VERSION_STAMP_DECLARE;
int main(int argc, char **argv)
{
int ch;
int fd;
struct stat st;
int junk;
ARGV *ext_argv = 0;
MAIL_VERSION_STAMP_ALLOCATE;
umask(022);
for (fd = 0; fd < 3; fd++)
if (fstat(fd, &st) == -1
&& (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
msg_fatal("open /dev/null: %m");
msg_vstream_init(argv[0], VSTREAM_ERR);
while ((ch = GETOPT(argc, argv, "aAbc:deE#hmlntv")) > 0) {
switch (ch) {
case 'a':
cmd_mode |= SHOW_SASL_SERV;
break;
case 'A':
cmd_mode |= SHOW_SASL_CLNT;
break;
case 'b':
if (ext_argv)
msg_fatal("specify one of -b and -t");
ext_argv = argv_alloc(2);
argv_add(ext_argv, "bounce", "-SVnexpand_templates", (char *) 0);
break;
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory");
break;
case 'd':
cmd_mode |= SHOW_DEFS;
break;
case 'e':
cmd_mode |= EDIT_MAIN;
break;
#if 0
case 'E':
cmd_mode |= SHOW_EVAL;
break;
#endif
case '#':
cmd_mode = COMMENT_OUT;
break;
case 'h':
cmd_mode &= ~SHOW_NAME;
break;
case 'l':
cmd_mode |= SHOW_LOCKS;
break;
case 'm':
cmd_mode |= SHOW_MAPS;
break;
case 'n':
cmd_mode |= SHOW_NONDEF;
break;
case 't':
if (ext_argv)
msg_fatal("specify one of -b and -t");
ext_argv = argv_alloc(2);
argv_add(ext_argv, "bounce", "-SVndump_templates", (char *) 0);
break;
case 'v':
msg_verbose++;
break;
default:
msg_fatal("usage: %s [-a (server SASL types)] [-A (client SASL types)] [-b (bounce templates)] [-c config_dir] [-d (defaults)] [-e (edit)] [-# (comment-out)] [-h (no names)] [-l (lock types)] [-m (map types)] [-n (non-defaults)] [-v] [name...]", argv[0]);
}
}
junk = (cmd_mode & (SHOW_DEFS | SHOW_NONDEF | SHOW_MAPS | SHOW_LOCKS | EDIT_MAIN | SHOW_SASL_SERV | SHOW_SASL_CLNT | COMMENT_OUT));
if (junk != 0 && ((junk != SHOW_DEFS && junk != SHOW_NONDEF
&& junk != SHOW_MAPS && junk != SHOW_LOCKS && junk != EDIT_MAIN
&& junk != SHOW_SASL_SERV && junk != SHOW_SASL_CLNT
&& junk != COMMENT_OUT)
|| ext_argv != 0))
msg_fatal("specify one of -a, -A, -b, -d, -e, -#, -m, -l and -n");
if (ext_argv) {
if (argv[optind]) {
if (argv[optind + 1])
msg_fatal("options -b and -t require at most one template file");
argv_add(ext_argv, "-o",
concatenate(VAR_BOUNCE_TMPL, "=",
argv[optind], (char *) 0),
(char *) 0);
}
argv_add(ext_argv, "-o",
concatenate(VAR_QUEUE_DIR, "=", ".", (char *) 0),
(char *) 0);
mail_conf_read();
mail_run_replace(var_daemon_dir, ext_argv->argv);
}
if (cmd_mode & SHOW_MAPS) {
mail_dict_init();
show_maps();
}
else if (cmd_mode & SHOW_LOCKS) {
show_locks();
}
else if (cmd_mode & SHOW_SASL_SERV) {
show_sasl(SHOW_SASL_SERV);
} else if (cmd_mode & SHOW_SASL_CLNT) {
show_sasl(SHOW_SASL_CLNT);
}
else if (cmd_mode & (EDIT_MAIN | COMMENT_OUT)) {
edit_parameters(cmd_mode, argc - optind, argv + optind);
} else if (cmd_mode == DEF_MODE
&& argv[optind] && strchr(argv[optind], '=')) {
edit_parameters(cmd_mode | EDIT_MAIN, argc - optind, argv + optind);
}
else {
if ((cmd_mode & SHOW_DEFS) == 0) {
read_parameters();
set_parameters();
}
hash_parameters();
show_parameters(cmd_mode, argv + optind);
}
vstream_fflush(VSTREAM_OUT);
exit(0);
}