#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 STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#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 <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>
#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)
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 "smtp_vars.h"
#include "install_vars.h"
static CONFIG_TIME_TABLE time_table[] = {
#include "time_table.h"
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
#include "bool_table.h"
0,
};
static CONFIG_INT_TABLE int_table[] = {
#include "int_table.h"
0,
};
static CONFIG_STR_TABLE str_table[] = {
#include "str_table.h"
#include "smtp_table.h"
#include "install_table.h"
0,
};
static CONFIG_RAW_TABLE raw_table[] = {
#include "raw_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 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 CONFIG_STR_FN_TABLE str_fn_table_2[] = {
VAR_MYNETWORKS, check_mynetworks, &var_mynetworks, 1, 0,
0,
};
static int mode = SHOW_NAME;
static const char *check_myhostname(void)
{
static const char *name;
const char *dot;
const char *domain;
if (name)
return (name);
name = get_hostname();
if ((mode & SHOW_DEFS) == 0 && (dot = strchr(name, '.')) == 0) {
if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0) {
msg_warn("My hostname %s is not a fully qualified name - set %s or %s in %s/main.cf",
name, VAR_MYHOSTNAME, VAR_MYDOMAIN, var_config_dir);
} else {
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 (var_myhostname);
return (dot + 1);
}
static const char *check_mynetworks(void)
{
const char *junk;
if (var_inet_interfaces == 0) {
if ((mode & SHOW_DEFS)
|| !(junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)))
junk = DEF_INET_INTERFACES;
var_inet_interfaces = mystrdup(junk);
}
if (var_mynetworks_style == 0) {
if ((mode & SHOW_DEFS)
|| !(junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)))
junk = DEF_MYNETWORKS_STYLE;
var_mynetworks_style = mystrdup(junk);
}
return (mynetworks());
}
static void edit_parameters(int argc, char **argv)
{
char *config_dir;
char *path;
char *temp;
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("edit accepts no multi-line input");
while (ISSPACE(*cp))
cp++;
if (*cp == '#')
msg_fatal("edit accepts no comment input");
if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
msg_fatal("%s: \"%s\"", err, cp);
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 ((src = vstream_fopen(path, O_RDONLY, 0)) == 0)
msg_fatal("open %s for reading: %m", path);
temp = concatenate(path, ".tmp", (char *) 0);
if ((dst = vstream_fopen(temp, O_CREAT | O_WRONLY, 0644)) == 0)
msg_fatal("open %s: %m", temp);
if (myflock(vstream_fileno(dst), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
msg_fatal("lock %s: %m", temp);
if (ftruncate(vstream_fileno(dst), 0) < 0)
msg_fatal("truncate %s: %m", temp);
#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 {
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));
vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
} else {
vstream_fputs(STR(buf), dst);
}
}
}
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 (vstream_fclose(dst))
msg_fatal("write %s: %m", temp);
if (rename(temp, path) < 0)
msg_fatal("rename %s to %s: %m", temp, path);
myfree(path);
myfree(temp);
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)
{
CONFIG_TIME_TABLE *ctt;
CONFIG_BOOL_TABLE *cbt;
CONFIG_INT_TABLE *cit;
CONFIG_STR_TABLE *cst;
CONFIG_STR_FN_TABLE *csft;
CONFIG_RAW_TABLE *rst;
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);
}
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_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 (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 *maps_argv;
int i;
maps_argv = mbox_lock_names();
for (i = 0; i < maps_argv->argc; i++)
vstream_printf("%s\n", maps_argv->argv[i]);
argv_free(maps_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);
}
}
}
int main(int argc, char **argv)
{
int ch;
int fd;
struct stat st;
int junk;
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, "c:deEhmlnv")) > 0) {
switch (ch) {
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory");
break;
case 'd':
mode |= SHOW_DEFS;
break;
case 'e':
mode |= EDIT_MAIN;
break;
#if 0
case 'E':
mode |= SHOW_EVAL;
break;
#endif
case 'h':
mode &= ~SHOW_NAME;
break;
case 'l':
mode |= SHOW_LOCKS;
break;
case 'm':
mode |= SHOW_MAPS;
break;
case 'n':
mode |= SHOW_NONDEF;
break;
case 'v':
msg_verbose++;
break;
default:
msg_fatal("usage: %s [-c config_dir] [-d (defaults)] [-e (edit)] [-h (no names)] [-l (lock types)] [-m (map types)] [-n (non-defaults)] [-v] [name...]", argv[0]);
}
}
junk = (mode & (SHOW_DEFS | SHOW_NONDEF | SHOW_MAPS | SHOW_LOCKS | EDIT_MAIN));
if (junk != 0 && junk != SHOW_DEFS && junk != SHOW_NONDEF
&& junk != SHOW_MAPS && junk != SHOW_LOCKS && junk != EDIT_MAIN)
msg_fatal("specify one of -d, -e, -m, -l and -n");
if (mode & SHOW_MAPS) {
mail_dict_init();
show_maps();
}
else if (mode & SHOW_LOCKS) {
show_locks();
}
else if (mode & EDIT_MAIN) {
edit_parameters(argc - optind, argv + optind);
}
else {
if ((mode & SHOW_DEFS) == 0) {
read_parameters();
set_parameters();
}
hash_parameters();
show_parameters(mode, argv + optind);
}
vstream_fflush(VSTREAM_OUT);
exit(0);
}