summaryrefslogtreecommitdiffstats
path: root/src/postconf
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:46:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:46:30 +0000
commitb5896ba9f6047e7031e2bdee0622d543e11a6734 (patch)
treefd7b460593a2fee1be579bec5697e6d887ea3421 /src/postconf
parentInitial commit. (diff)
downloadpostfix-upstream.tar.xz
postfix-upstream.zip
Adding upstream version 3.4.23.upstream/3.4.23upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
l---------src/postconf/.indent.pro1
-rw-r--r--src/postconf/.printfck25
-rw-r--r--src/postconf/Makefile.in1286
-rw-r--r--src/postconf/extract.awk190
-rw-r--r--src/postconf/extract_cfg.sh92
-rw-r--r--src/postconf/install_table.h2
-rw-r--r--src/postconf/install_vars.h2
-rw-r--r--src/postconf/postconf.c1096
-rw-r--r--src/postconf/postconf.h327
-rw-r--r--src/postconf/postconf_builtin.c461
-rw-r--r--src/postconf/postconf_dbms.c325
-rw-r--r--src/postconf/postconf_edit.c578
-rw-r--r--src/postconf/postconf_lookup.c198
-rw-r--r--src/postconf/postconf_main.c216
-rw-r--r--src/postconf/postconf_master.c1108
-rw-r--r--src/postconf/postconf_match.c188
-rw-r--r--src/postconf/postconf_misc.c60
-rw-r--r--src/postconf/postconf_node.c185
-rw-r--r--src/postconf/postconf_other.c141
-rw-r--r--src/postconf/postconf_print.c114
-rw-r--r--src/postconf/postconf_service.c199
-rw-r--r--src/postconf/postconf_unused.c129
-rw-r--r--src/postconf/postconf_user.c422
-rw-r--r--src/postconf/test1.ref4
-rw-r--r--src/postconf/test10.ref2
-rw-r--r--src/postconf/test11.ref2
-rw-r--r--src/postconf/test12.ref2
-rw-r--r--src/postconf/test13.ref3
-rw-r--r--src/postconf/test14.ref3
-rw-r--r--src/postconf/test15.ref3
-rw-r--r--src/postconf/test16.ref2
-rw-r--r--src/postconf/test17.ref1
-rw-r--r--src/postconf/test18.ref3
-rw-r--r--src/postconf/test19.ref3
-rw-r--r--src/postconf/test2.ref3
-rw-r--r--src/postconf/test20.ref4
-rw-r--r--src/postconf/test21.ref3
-rw-r--r--src/postconf/test22.ref16
-rw-r--r--src/postconf/test23.ref2
-rw-r--r--src/postconf/test24.ref1
-rw-r--r--src/postconf/test25.ref16
-rw-r--r--src/postconf/test26.ref3
-rw-r--r--src/postconf/test27.ref16
-rw-r--r--src/postconf/test28.ref10
-rw-r--r--src/postconf/test29.ref16
-rw-r--r--src/postconf/test3.ref4
-rw-r--r--src/postconf/test30.ref7
-rw-r--r--src/postconf/test31.ref3
-rw-r--r--src/postconf/test32.ref1
-rw-r--r--src/postconf/test33.ref1
-rw-r--r--src/postconf/test34.ref4
-rw-r--r--src/postconf/test35.ref3
-rw-r--r--src/postconf/test36.ref4
-rw-r--r--src/postconf/test37.ref4
-rw-r--r--src/postconf/test38.ref1
-rw-r--r--src/postconf/test39.ref2
-rw-r--r--src/postconf/test4.ref3
-rw-r--r--src/postconf/test40.ref7
-rw-r--r--src/postconf/test41.ref18
-rw-r--r--src/postconf/test42.ref14
-rw-r--r--src/postconf/test43.ref6
-rw-r--r--src/postconf/test44.ref6
-rw-r--r--src/postconf/test45.ref1
-rw-r--r--src/postconf/test46.ref1
-rw-r--r--src/postconf/test47.ref1
-rw-r--r--src/postconf/test48.ref1
-rw-r--r--src/postconf/test49.ref1
-rw-r--r--src/postconf/test4b.ref5
-rw-r--r--src/postconf/test5.ref1
-rw-r--r--src/postconf/test50.ref1
-rw-r--r--src/postconf/test51.ref1
-rw-r--r--src/postconf/test52.ref1
-rw-r--r--src/postconf/test53.ref3
-rw-r--r--src/postconf/test54.ref3
-rw-r--r--src/postconf/test55.ref3
-rw-r--r--src/postconf/test56.ref5
-rw-r--r--src/postconf/test57.ref10
-rw-r--r--src/postconf/test58.ref8
-rw-r--r--src/postconf/test59.ref10
-rw-r--r--src/postconf/test6.ref17
-rw-r--r--src/postconf/test60.ref8
-rw-r--r--src/postconf/test61.ref1
-rw-r--r--src/postconf/test62.ref8
-rw-r--r--src/postconf/test63.ref1
-rw-r--r--src/postconf/test64.ref1
-rw-r--r--src/postconf/test65.ref1
-rw-r--r--src/postconf/test66.ref5
-rw-r--r--src/postconf/test67.ref10
-rw-r--r--src/postconf/test68.ref5
-rw-r--r--src/postconf/test69.ref2
-rw-r--r--src/postconf/test7.ref1
-rw-r--r--src/postconf/test8.ref1
-rw-r--r--src/postconf/test9.ref1
93 files changed, 7669 insertions, 0 deletions
diff --git a/src/postconf/.indent.pro b/src/postconf/.indent.pro
new file mode 120000
index 0000000..5c837ec
--- /dev/null
+++ b/src/postconf/.indent.pro
@@ -0,0 +1 @@
+../../.indent.pro \ No newline at end of file
diff --git a/src/postconf/.printfck b/src/postconf/.printfck
new file mode 100644
index 0000000..66016ed
--- /dev/null
+++ b/src/postconf/.printfck
@@ -0,0 +1,25 @@
+been_here_xt 2 0
+bounce_append 5 0
+cleanup_out_format 1 0
+defer_append 5 0
+mail_command 1 0
+mail_print 1 0
+msg_error 0 0
+msg_fatal 0 0
+msg_info 0 0
+msg_panic 0 0
+msg_warn 0 0
+opened 4 0
+post_mail_fprintf 1 0
+qmgr_message_bounce 2 0
+rec_fprintf 2 0
+sent 4 0
+smtp_cmd 1 0
+smtp_mesg_fail 2 0
+smtp_printf 1 0
+smtp_rcpt_fail 3 0
+smtp_site_fail 2 0
+udp_syslog 1 0
+vstream_fprintf 1 0
+vstream_printf 0 0
+vstring_sprintf 1 0
diff --git a/src/postconf/Makefile.in b/src/postconf/Makefile.in
new file mode 100644
index 0000000..b19b230
--- /dev/null
+++ b/src/postconf/Makefile.in
@@ -0,0 +1,1286 @@
+SHELL = /bin/sh
+SRCS = postconf.c postconf_builtin.c postconf_edit.c postconf_main.c \
+ postconf_master.c postconf_misc.c postconf_node.c postconf_other.c \
+ postconf_service.c postconf_unused.c postconf_user.c postconf_dbms.c \
+ postconf_lookup.c postconf_match.c postconf_print.c
+OBJS = postconf.o postconf_builtin.o postconf_edit.o postconf_main.o \
+ postconf_master.o postconf_misc.o postconf_node.o postconf_other.o \
+ postconf_service.o postconf_unused.o postconf_user.o postconf_dbms.o \
+ postconf_lookup.o postconf_match.o postconf_print.o
+HDRS = postconf.h
+TESTSRC =
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS) -DLEGACY_DBMS_SUPPORT
+TESTPROG=
+MAKES = bool_table.h bool_vars.h int_table.h int_vars.h str_table.h \
+ str_vars.h time_table.h time_vars.h raw_table.h raw_vars.h \
+ nint_table.h nint_vars.h nbool_table.h nbool_vars.h long_table.h \
+ long_vars.h str_fn_table.h str_fn_vars.h
+DB_MAKES= pcf_ldap_suffixes.h pcf_memcache_suffixes.h pcf_mysql_suffixes.h \
+ pcf_pgsql_suffixes.h pcf_sqlite_suffixes.h
+TEST_TMP= main.cf master.cf test*.tmp
+DUMMIES = makes_dummy # for "make -j"
+PROG = postconf
+SAMPLES = ../../conf/main.cf.default
+INC_DIR = ../../include
+LIBS = ../../lib/libxsasl.a \
+ ../../lib/lib$(LIB_PREFIX)tls$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PREFIX)dns$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+../../conf/main.cf.default: $(PROG) Makefile
+ rm -f $@
+ (echo "# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE"; \
+ echo "# TEXT HERE JUST SHOWS DEFAULT SETTINGS BUILT INTO POSTFIX."; \
+ echo "#"; $(SHLIB_ENV) ./$(PROG) -d -c ../../conf) | \
+ egrep -v '^(myhostname|mydomain|mynetworks|process_name|process_id) ' >$@
+
+$(OBJS): ../../conf/makedefs.out
+
+Makefile: Makefile.in
+ cat ../../conf/makedefs.out $? >$@
+
+test: $(TESTPROG)
+
+tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \
+ test12 test13 test14 test15 test16 test17 test18 test19 test20 test21 \
+ test22 test23 test24 test25 test26 test27 test28 test29 test30 test4b \
+ test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \
+ test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
+ test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \
+ test62 test63 test64 test65 test66 test67 test68 test69
+
+root_tests:
+
+update: ../../bin/$(PROG) $(SAMPLES)
+
+../../bin/$(PROG): $(PROG)
+ cp $(PROG) ../../bin
+
+$(MAKES): makes_dummy
+
+makes_dummy: $(INC_DIR)/mail_params.h ../global/mail_params.c extract.awk Makefile.in
+ $(AWK) -f extract.awk ../*/*.c | $(SHELL)
+ touch makes_dummy
+
+$(DB_MAKES): extract_cfg.sh Makefile.in
+
+pcf_ldap_suffixes.h: ../global/dict_ldap.c
+ sh extract_cfg.sh -d ../global/dict_ldap.c > $@
+
+pcf_memcache_suffixes.h: ../global/dict_memcache.c
+ sh extract_cfg.sh -d ../global/dict_memcache.c > $@
+
+pcf_mysql_suffixes.h: ../global/dict_mysql.c
+ sh extract_cfg.sh -d -s ../global/dict_mysql.c > $@
+
+pcf_pgsql_suffixes.h: ../global/dict_pgsql.c
+ sh extract_cfg.sh -d -s ../global/dict_pgsql.c > $@
+
+pcf_sqlite_suffixes.h: ../global/dict_sqlite.c
+ sh extract_cfg.sh -d -s ../global/dict_sqlite.c > $@
+
+# Define two parameters with smtpd_restriction_classes. One will be ignored.
+
+test1: $(PROG) test1.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo smtpd_restriction_classes = foo bar >> main.cf
+ echo foo = yes >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test1.tmp 2>&1
+ diff test1.ref test1.tmp
+ rm -f main.cf master.cf test1.tmp
+
+# Define two unused parameters. Expect two warnings.
+
+test2: $(PROG) test2.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo restriction_classes = foo bar >> main.cf
+ echo foo = yes >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test2.tmp 2>&1
+ diff test2.ref test2.tmp
+ rm -f main.cf master.cf test2.tmp
+
+# Define one parameter in main.cf, validate it with main.cf.
+
+test3: $(PROG) test3.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo = yes >> main.cf
+ echo 'bar = $$foo' >> main.cf
+ echo 'always_bcc = $$bar' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test3.tmp 2>&1
+ diff test3.ref test3.tmp
+ rm -f main.cf master.cf test3.tmp
+
+# Define one parameter in main.cf, validate it with master.cf.
+
+test4: $(PROG) test4.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo = yes >> main.cf
+ echo 'bar = $$foo' >> main.cf
+ echo smtpd unix - n n - 0 smtpd >> master.cf
+ echo ' -o always_bcc=$$bar' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test4.tmp 2>&1
+ diff test4.ref test4.tmp
+ rm -f main.cf master.cf test4.tmp
+
+# Define one parameter in master.cf, validate it with main.cf.
+
+test4b: $(PROG) test4b.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'always_bcc = $$foo' >> main.cf
+ echo 'biff = $$bar' >> main.cf
+ echo 'bar = aaa' >> main.cf
+ echo smtpd1 unix - n n - 0 smtpd >> master.cf
+ echo ' -o foo=xxx -o bar=yyy -o baz=zzz' >> master.cf
+ echo '#smtpd2 unix - n n - 0 smtpd' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test4b.tmp 2>&1
+ diff test4b.ref test4b.tmp
+ rm -f main.cf master.cf test4b.tmp
+
+# Define one user-defined parameter with name=value in master.cf,
+# validate it with known_parameter=$$name in master.cf.
+
+test5: $(PROG) test5.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo smtpd unix - n n - 0 smtpd >> master.cf
+ echo ' -o bar=yes -o always_bcc=$$bar -o' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test5.tmp 2>&1
+ diff test5.ref test5.tmp
+ rm -f main.cf master.cf test5.tmp
+
+# Basic functionality test: service parameters for delivery agents.
+
+test6: $(PROG) test6.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo whatevershebrings unix - n n - 0 pipe >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test6.tmp
+ diff test6.ref test6.tmp
+ rm -f main.cf master.cf test6.tmp
+
+# Basic functionality test: service parameters for spawn programs.
+
+test7: $(PROG) test7.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo whatevershebrings unix - n n - 0 spawn >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test7.tmp
+ diff test7.ref test7.tmp
+ rm -f main.cf master.cf test7.tmp
+
+test8: $(PROG) test8.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo whatevershebrings inet - n n - 0 spawn >> master.cf
+ echo whatevershebrings_time_limit=1 >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test8.tmp
+ diff test8.ref test8.tmp
+ rm -f main.cf master.cf test8.tmp
+
+test9: $(PROG) test9.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo bar unix - n n - 0 spawn >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -M '*'/inet >test9.tmp 2>&1
+ diff test9.ref test9.tmp
+ rm -f main.cf master.cf test9.tmp
+
+test10: $(PROG) test10.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo bar unix - n n - 0 spawn >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -M bar/inet foo/unix >test10.tmp 2>&1
+ diff test10.ref test10.tmp
+ rm -f main.cf master.cf test10.tmp
+
+test11: $(PROG) test11.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo bar unix - n n - 0 spawn >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -M >test11.tmp 2>&1
+ diff test11.ref test11.tmp
+ rm -f main.cf master.cf test11.tmp
+
+# Duplicate service entry.
+
+test12: $(PROG) test12.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo bar=yes >> main.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo ' -o always_bcc=$$bar -o' >> master.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo ' -o always_bcc=$$bar -o' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -M >test12.tmp 2>&1
+ diff test12.ref test12.tmp
+ rm -f main.cf master.cf test12.tmp
+
+# Define parameter with restriction_classes in master.cf, validate in main.cf.
+
+test13: $(PROG) test13.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo bar=yes >> main.cf
+ echo baz=xx >> main.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo ' -o smtpd_restriction_classes=bar' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test13.tmp 2>&1
+ diff test13.ref test13.tmp
+ rm -f main.cf master.cf test13.tmp
+
+# Define parameter with restriction_classes in main.cf, validate in master.cf.
+
+test14: $(PROG) test14.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo smtpd_restriction_classes=bar >> main.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo ' -o bar=yes -o baz=xx' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test14.tmp 2>&1
+ diff test14.ref test14.tmp
+ rm -f main.cf master.cf test14.tmp
+
+# Define two parameters, one is hidden by master.cf.
+
+test15: $(PROG) test15.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo bar=xx >> main.cf
+ echo baz=yy >> main.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo ' -o bar=yes -o always_bcc=$$bar$$baz' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test15.tmp 2>&1
+ diff test15.ref test15.tmp
+ rm -f main.cf master.cf test15.tmp
+
+# Test graceful degradation if master.cf is unavailable.
+
+test16: $(PROG) test16.ref
+ rm -f main.cf master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test16.tmp 2>&1
+ diff test16.ref test16.tmp
+ rm -f main.cf master.cf test16.tmp
+
+test17: $(PROG) test17.ref
+ rm -f main.cf master.cf
+ touch -t 197101010000 main.cf
+ -$(SHLIB_ENV) ./$(PROG) -Mc . >test17.tmp 2>&1; exit 0
+ diff test17.ref test17.tmp
+ rm -f main.cf master.cf test17.tmp
+
+# Test legacy $name in built-in defaults.
+
+test18: $(PROG) test18.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo virtual_maps=xxx >> main.cf
+ echo smtpd_client_connection_limit_exceptions=yyy >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test18.tmp 2>&1
+ diff test18.ref test18.tmp
+ rm -f main.cf master.cf test18.tmp
+
+# Test $name in "raw" parameters.
+
+test19: $(PROG) test19.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo forward_path='$$'aaaa >> main.cf
+ echo default_rbl_reply='$$'bbbb >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test19.tmp 2>&1
+ diff test19.ref test19.tmp
+ rm -f main.cf master.cf test19.tmp
+
+# Test master.cf line folding.
+
+test20: $(PROG) test20.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo inet - n n - 0 spawn >> master.cf
+ echo ' -o always_bcc=$$bar$$baz' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc . >test20.tmp 2>&1
+ diff test20.ref test20.tmp
+ rm -f main.cf master.cf test20.tmp
+
+# Test main.cf line folding.
+
+test21: $(PROG) test21.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo forward_path = xxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx \
+ xxxxxxxxxxxxx xxxxxxxxxxxxxx >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nfc . >test21.tmp 2>&1
+ diff test21.ref test21.tmp
+ rm -f main.cf master.cf test21.tmp
+
+# Like test6, but using a delivery agent that has no _time_limit magic.
+
+test22: $(PROG) test22.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo whatevershebrings unix - n n - 0 smtp >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test22.tmp
+ diff test22.ref test22.tmp
+ rm -f main.cf master.cf test22.tmp
+
+# Test the -C flag for each category.
+
+test23: $(PROG) test23.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo always_bcc = yes >> main.cf
+ echo name = value >> main.cf
+ echo whatevershebrings unix - n n - 0 smtp >> master.cf
+ echo ' -o always_bcc=$$name' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -nC builtin >test23.tmp 2>&1
+ diff test23.ref test23.tmp
+ rm -f main.cf master.cf test23.tmp
+
+test24: $(PROG) test24.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo always_bcc = yes >> main.cf
+ echo name = value >> main.cf
+ echo whatevershebrings unix - n n - 0 smtp >> master.cf
+ echo ' -o always_bcc=$$name' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -nC user >test24.tmp 2>&1
+ diff test24.ref test24.tmp
+ rm -f main.cf master.cf test24.tmp
+
+test25: $(PROG) test25.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo always_bcc = yes >> main.cf
+ echo name = value >> main.cf
+ echo whatevershebrings unix - n n - 0 smtp >> master.cf
+ echo ' -o always_bcc=$$name' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -C service 2>&1 | grep whatevershebrings >test25.tmp
+ diff test25.ref test25.tmp
+ rm -f main.cf master.cf test25.tmp
+
+# Test completeness of "-C all".
+
+test26: $(PROG) test26.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo always_bcc = yes >> main.cf
+ echo name = value >> main.cf
+ echo whatevershebrings unix - n n - 0 smtp >> master.cf
+ echo ' -o always_bcc=$$name' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . -C all >test26.tmp 2>&1
+ diff test26.ref test26.tmp
+ rm -f main.cf master.cf test26.tmp
+
+test27: $(PROG) test27.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo always_bcc = yes >> main.cf
+ echo name = value >> main.cf
+ echo whatevershebrings unix - n n - 0 smtp >> master.cf
+ echo ' -o always_bcc=$$name' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c . -C all 2>&1 | grep whatevershebrings >test27.tmp
+ diff test27.ref test27.tmp
+ rm -f main.cf master.cf test27.tmp
+
+# Test macro expansion, type:table parsing and scoping.
+
+test28: $(PROG) test28.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'xx = proxy:ldap:foo' >> main.cf
+ echo 'foo_domain = bar' >> main.cf
+ echo 'header_checks = ldap:hh' >> main.cf
+ echo 'hh_domain = whatever' >> main.cf
+ echo 'zz = $$yy' >> main.cf
+ echo 'yy = aap' >> main.cf
+ echo 'db = memcache' >> main.cf
+ echo whatevershebrings unix - n n - 0 other >> master.cf
+ echo ' -o body_checks=$$db:$$zz' >> master.cf
+ echo 'aap_domain = whatever' >> main.cf
+ echo 'aa_domain = whatever' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test28.tmp 2>&1
+ diff test28.ref test28.tmp
+ rm -f main.cf master.cf test28.tmp
+
+# Test the handling of known and unknown database-defined suffixes.
+
+test29: $(PROG) test29.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'ldapxx = proxy:ldap:ldapfoo' >> main.cf
+ echo 'ldapfoo_domain = bar' >> main.cf
+ echo 'ldapfoo_domainx = bar' >> main.cf
+ echo 'mysqlxx = proxy:mysql:mysqlfoo' >> main.cf
+ echo 'mysqlfoo_domain = bar' >> main.cf
+ echo 'mysqlfoo_domainx = bar' >> main.cf
+ echo 'pgsqlxx = proxy:pgsql:pgsqlfoo' >> main.cf
+ echo 'pgsqlfoo_domain = bar' >> main.cf
+ echo 'pgsqlfoo_domainx = bar' >> main.cf
+ echo 'sqlitexx = proxy:sqlite:sqlitefoo' >> main.cf
+ echo 'sqlitefoo_domain = bar' >> main.cf
+ echo 'sqlitefoo_domainx = bar' >> main.cf
+ echo 'memcachexx = proxy:memcache:memcachefoo' >> main.cf
+ echo 'memcachefoo_domain = bar' >> main.cf
+ echo 'memcachefoo_domainx = bar' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test29.tmp 2>&1
+ diff test29.ref test29.tmp
+ rm -f main.cf master.cf test29.tmp
+
+test30: $(PROG) test30.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo p1=xx >> main.cf
+ echo p2=xx >> main.cf
+ echo p3=xx >> main.cf
+ echo p4=xx >> main.cf
+ echo whatevershebrings unix - n n - 0 other >> master.cf
+ echo ' -o body_checks=$$p1' >> master.cf
+ echo ' -o bodyx_checks=$$p2' >> master.cf
+ echo ' -oheader_checks=$$p3' >> master.cf
+ echo ' -oheaderx_checks=$$p4' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test30.tmp 2>&1
+ diff test30.ref test30.tmp
+ rm -f main.cf master.cf test30.tmp
+
+# Does a non-default setting propagate to a non-default value?
+
+test31: $(PROG) test31.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'smtpd_helo_restrictions=whatever' >> main.cf
+ echo 'smtpd_sender_restrictions=$$smtpd_helo_restrictions' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nxc . >test31.tmp 2>&1
+ diff test31.ref test31.tmp
+ rm -f main.cf master.cf test31.tmp
+
+# Does a non-default setting propagate to a default value?
+
+test32: $(PROG) test32.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'relay_domains=whatever' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -xc . fast_flush_domains >test32.tmp 2>&1
+ diff test32.ref test32.tmp
+ rm -f main.cf master.cf test32.tmp
+
+# Does a default setting propagate to a non-default value?
+
+test33: $(PROG) test33.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=whatever' >> main.cf
+ echo 'always_bcc=$$relay_domains' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -xc . always_bcc >test33.tmp 2>&1
+ diff test33.ref test33.tmp
+ rm -f main.cf master.cf test33.tmp
+
+test34: $(PROG) test34.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=whatever' >> main.cf
+ echo 'process_name=xxx' >> main.cf
+ echo 'process_id=yyy' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -xc . mydestination process_name >test34.tmp 2>&1
+ diff test34.ref test34.tmp
+ rm -f main.cf master.cf test34.tmp
+
+test35: $(PROG) test35.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo whatevershebrings unix - n n - 0 other >> master.cf
+ echo ' -o body_checks=whatever' >> master.cf
+ echo ' -o process_name=aaa' >> master.cf
+ echo ' -o process_id=bbb' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -xc . process_name >test35.tmp 2>&1
+ diff test35.ref test35.tmp
+ rm -f main.cf master.cf test35.tmp
+
+test36: $(PROG) test36.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination=$$virtual_mapx' >> main.cf
+ echo 'virtual_alias_maps=$$virtual_maps' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nxc . >test36.tmp 2>&1
+ diff test36.ref test36.tmp
+ rm -f main.cf master.cf test36.tmp
+
+test37: $(PROG) test37.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'xxx=yyy' >> main.cf
+ echo 'aaa=bbb' >> main.cf
+ echo whatever unix - n n - 0 other >> master.cf
+ echo ' -o mydestination=$$xxx' >> master.cf
+ echo ' -o always_bcc=$$aaa' >> master.cf
+ echo ' -o aaa=ccc' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfxc . >test37.tmp 2>&1
+ diff test37.ref test37.tmp
+ rm -f main.cf master.cf test37.tmp
+
+test39: $(PROG) test39.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc . '*'/unix >test39.tmp 2>&1
+ diff test39.ref test39.tmp
+ rm -f main.cf master.cf test39.tmp
+
+test40: $(PROG) test40.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -voaaa=bbb' >> master.cf
+ echo ' -vo ccc=$$aaa' >> master.cf
+ echo ' -v -oddd=$$ccc' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfxc . '*'/unix >test40.tmp 2>&1
+ diff test40.ref test40.tmp
+ rm -f main.cf master.cf test40.tmp
+
+test41: $(PROG) test41.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar unix - n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test41.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >>test41.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Pc . bar/unix/xxx=YYY bar/unix/aaa=BBB >>test41.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >>test41.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Pc . >>test41.tmp 2>&1
+ diff test41.ref test41.tmp
+ rm -f main.cf master.cf test41.tmp
+
+test42: $(PROG) test42.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar unix - n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test42.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >>test42.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Pc . >>test42.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -PXc. bar/unix/xxx bar/unix/aaa >>test42.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >>test42.tmp 2>&1
+ diff test42.ref test42.tmp
+ rm -f main.cf master.cf test42.tmp
+
+test43: $(PROG) test43.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar unix - n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Fc . bar/unix/chroot=y bar/unix/command='aa -stuffobb=cc dd' >test43.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >>test43.tmp 2>&1
+ diff test43.ref test43.tmp
+ rm -f main.cf master.cf test43.tmp
+
+test44: $(PROG) test44.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar unix - n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mc . bar/unix='xx inet - n n - 0 aa -stuffobb=cc dd' >test44.tmp 2>&1
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >>test44.tmp 2>&1
+ diff test44.ref test44.tmp
+ rm -f main.cf master.cf test44.tmp
+
+test45: $(PROG) test45.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar xxxx - n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test45.tmp 2>&1 || true
+ diff test45.ref test45.tmp
+ rm -f main.cf master.cf test45.tmp
+
+test46: $(PROG) test46.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet X n n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test46.tmp 2>&1 || true
+ diff test46.ref test46.tmp
+ rm -f main.cf master.cf test46.tmp
+
+test47: $(PROG) test47.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - X n - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test47.tmp 2>&1 || true
+ diff test47.ref test47.tmp
+ rm -f main.cf master.cf test47.tmp
+
+test48: $(PROG) test48.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n X - 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test48.tmp 2>&1 || true
+ diff test48.ref test48.tmp
+ rm -f main.cf master.cf test48.tmp
+
+test49: $(PROG) test49.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n X 0 other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test49.tmp 2>&1 || true
+ diff test49.ref test49.tmp
+ rm -f main.cf master.cf test49.tmp
+
+test50: $(PROG) test50.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n - X other >> master.cf
+ echo baz unix - n n - 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test50.tmp 2>&1 || true
+ diff test50.ref test50.tmp
+ rm -f main.cf master.cf test50.tmp
+
+test51: $(PROG) test51.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n -? 0 other >> master.cf
+ echo bar inet - n n X? 0 other >> master.cf
+ echo baz unix - n n 0? 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test51.tmp 2>&1 || true
+ diff test51.ref test51.tmp
+ rm -f main.cf master.cf test51.tmp
+
+test52: $(PROG) test52.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n 0 0 other >> master.cf
+ echo baz unix - n n 0 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -MXc. bar/inet foo/unix xxx/yyy
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test52.tmp 2>&1 || true
+ diff test52.ref test52.tmp
+ rm -f main.cf master.cf test52.tmp
+
+test53: $(PROG) test53.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n 0 0 other >> master.cf
+ echo baz unix - n n 0 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet xxx/yyy
+ diff test53.ref master.cf
+ rm -f main.cf master.cf test53.tmp
+
+test54: $(PROG) test54.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n 0 0 other >> master.cf
+ echo baz unix - n n 0 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet foo/unix
+ diff test54.ref master.cf
+ rm -f main.cf master.cf test54.tmp
+
+test55: $(PROG) test55.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n 0 0 other >> master.cf
+ echo baz unix - n n 0 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet baz/unix
+ diff test55.ref master.cf
+ rm -f main.cf master.cf test55.tmp
+
+test56: $(PROG) test56.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n 0 0 other >> master.cf
+ echo " -o first" >> master.cf
+ echo " -o second" >> master.cf
+ echo baz unix - n n 0 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet xxx/yyy
+ diff test56.ref master.cf
+ rm -f main.cf master.cf test56.tmp
+
+# Many more tests in util/mac_expand.in.
+
+test57: $(PROG) test57.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'x = $${{1} == {2}?{error}:x-value}' >> main.cf
+ echo 'y = y-value' >> main.cf
+ echo 'bar = $${x?{$$y}:$$z}' >> main.cf
+ echo 'baz = $${x?{$$z}:$$y}' >> main.cf
+ echo 'foo = $$bar$$baz' >> main.cf
+ echo 't1 = Postfix 2.11 $${{$${x?bug:x}} == {bug}?in}compatible' >> main.cf
+ echo 't2 = $$t1' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nxc. >test57.tmp 2>&1
+ diff test57.ref test57.tmp
+ rm -f main.cf master.cf test57.tmp
+
+test58: $(PROG) test58.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'mydestination = foo bar pipemap:{ldap:xxx, memcache:yy}x randmap:{xx' >> main.cf
+ echo 'xxx_domain = foo' >> main.cf
+ echo 'xxx_bogus = foo' >> main.cf
+ echo 'yy_backup = bbb' >> main.cf
+ echo 'yy_bogus = bbb' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./postconf -nc. >test58.tmp 2>&1 || true
+ diff test58.ref test58.tmp
+ rm -f main.cf master.cf test58.tmp
+
+test59: $(PROG) test59.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo bar inet - n n 0 0 other >> master.cf
+ echo " -o name1=value1" >> master.cf
+ echo " -o { name2 = value2a value2b }" >> master.cf
+ echo " { arg1a arg1b }" >> master.cf
+ echo " { arg2a arg2b }x" >> master.cf
+ echo " { arg3a arg3b " >> master.cf
+ echo baz unix - n n 0 0 other >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Mfc. >test59.tmp 2>&1 || true
+ diff test59.ref test59.tmp
+ rm -f main.cf master.cf test59.tmp
+
+test60: $(PROG) test60.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o always_bcc=bar' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Fhc. >test60.tmp 2>&1 || true
+ diff test60.ref test60.tmp
+ rm -f main.cf master.cf test60.tmp
+
+test61: $(PROG) test61.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o always_bcc=bar' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -Phc. >test61.tmp 2>&1 || true
+ diff test61.ref test61.tmp
+ rm -f main.cf master.cf test61.tmp
+
+test62: $(PROG) test62.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o always_bcc=bar' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -FHc. >test62.tmp 2>&1 || true
+ diff test62.ref test62.tmp
+ rm -f main.cf master.cf test62.tmp
+
+test63: $(PROG) test63.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo foo unix - n n - 0 other >> master.cf
+ echo ' -o always_bcc=bar' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -PHc. >test63.tmp 2>&1 || true
+ diff test63.ref test63.tmp
+ rm -f main.cf master.cf test63.tmp
+
+# main.cf overrides built-in default.
+
+test64: $(PROG) test64.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'relayhost = relay-from-main.cf' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c. relayhost >test64.tmp 2>&1
+ diff test64.ref test64.tmp
+ rm -f main.cf master.cf test64.tmp
+
+# '-o name=value' overrides main.cf.
+
+test65: $(PROG) test65.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo 'relayhost = relay-from-main.cf' >> main.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c. -o relayhost=relay-from-cmd-line relayhost >test65.tmp 2>&1
+ diff test65.ref test65.tmp
+ rm -f main.cf master.cf test65.tmp
+
+# unknown parameters in database configuration file (absolute pathname).
+
+test66: $(PROG) test66.ref
+ rm -f main.cf master.cf
+ touch master.cf
+ echo alias_maps = ldap:`pwd`/test66.cf >> main.cf
+ echo " " mysql:`pwd`/test66.cf >> main.cf
+ echo " " pgsql:`pwd`/test66.cf >> main.cf
+ echo " " sqlite:`pwd`/test66.cf >> main.cf
+ echo " " memcache:`pwd`/test66.cf >> main.cf
+ echo junk = junk >> test66.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c. 2>test66.tmp >/dev/null
+ sed "s;PWD;`pwd`;" test66.ref | diff - test66.tmp
+ rm -f main.cf master.cf test66.tmp test66.cf
+
+# expand process name and service name in master.cf.
+
+test67: $(PROG) test67.ref
+ rm -f main.cf master.cf
+ touch master.cf
+ echo 'smtp inet n - n - - smtpd' >>master.cf
+ echo ' -o test1_process_name=$$process_name' >> master.cf
+ echo ' -o test1_service_name=$$service_name' >> master.cf
+ echo 'smtp unix n - n - - smtp' >>master.cf
+ echo ' -o test2_process_name=$$process_name' >> master.cf
+ echo ' -o test2_service_name=$$service_name' >> master.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -xMfc. >test67.tmp 2>&1
+ diff test67.ref test67.tmp
+ rm -f main.cf master.cf test67.tmp
+
+test68: $(PROG) test68.ref
+ rm -f main.cf master.cf
+ touch master.cf
+ echo foo = ldap:`pwd` >> main.cf
+ echo 'alias_maps = $$foo/test68.cf' >> main.cf
+ echo " " mysql:`pwd`/test68.cf >> main.cf
+ echo " " pgsql:`pwd`/test68.cf >> main.cf
+ echo " " sqlite:`pwd`/test68.cf >> main.cf
+ echo " " memcache:`pwd`/test68.cf >> main.cf
+ echo junk = junk >> test68.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c. 2>test68.tmp >/dev/null
+ sed "s;PWD;`pwd`;" test68.ref | diff - test68.tmp
+ rm -f main.cf master.cf test68.tmp test68.cf
+
+# See also test28 for user-defined parameters defined in main.cf.
+
+test69: $(PROG) test69.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo whatevershebrings unix - n n - 0 other >> master.cf
+ echo " -o ldap=ldap:`pwd`" >> master.cf
+ echo ' -o body_checks=$$ldap/test69.cf' >> master.cf
+ echo junk = junk >> test69.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -nc . >test69.tmp 2>&1
+ sed "s;PWD;`pwd`;" test69.ref | diff - test69.tmp
+ rm -f main.cf master.cf test69.tmp test69.cf
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ cp *.h printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk $(MAKES) $(AUTOS) $(DUMMIES) \
+ $(TEST_TMP) $(DB_MAKES)
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \
+ -e 's/o: \.\//o: /' -e p -e '}' ; \
+ done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+postconf.o: ../../include/argv.h
+postconf.o: ../../include/check_arg.h
+postconf.o: ../../include/dict.h
+postconf.o: ../../include/htable.h
+postconf.o: ../../include/mail_conf.h
+postconf.o: ../../include/mail_dict.h
+postconf.o: ../../include/mail_params.h
+postconf.o: ../../include/mail_run.h
+postconf.o: ../../include/mail_version.h
+postconf.o: ../../include/msg.h
+postconf.o: ../../include/msg_vstream.h
+postconf.o: ../../include/myflock.h
+postconf.o: ../../include/mymalloc.h
+postconf.o: ../../include/name_code.h
+postconf.o: ../../include/name_mask.h
+postconf.o: ../../include/stringops.h
+postconf.o: ../../include/sys_defs.h
+postconf.o: ../../include/vbuf.h
+postconf.o: ../../include/vstream.h
+postconf.o: ../../include/vstring.h
+postconf.o: ../../include/warn_stat.h
+postconf.o: postconf.c
+postconf.o: postconf.h
+postconf_builtin.o: ../../include/argv.h
+postconf_builtin.o: ../../include/attr.h
+postconf_builtin.o: ../../include/check_arg.h
+postconf_builtin.o: ../../include/dict.h
+postconf_builtin.o: ../../include/get_hostname.h
+postconf_builtin.o: ../../include/htable.h
+postconf_builtin.o: ../../include/inet_proto.h
+postconf_builtin.o: ../../include/iostuff.h
+postconf_builtin.o: ../../include/mail_addr.h
+postconf_builtin.o: ../../include/mail_conf.h
+postconf_builtin.o: ../../include/mail_params.h
+postconf_builtin.o: ../../include/mail_proto.h
+postconf_builtin.o: ../../include/mail_version.h
+postconf_builtin.o: ../../include/msg.h
+postconf_builtin.o: ../../include/myflock.h
+postconf_builtin.o: ../../include/mymalloc.h
+postconf_builtin.o: ../../include/mynetworks.h
+postconf_builtin.o: ../../include/name_code.h
+postconf_builtin.o: ../../include/nvtable.h
+postconf_builtin.o: ../../include/server_acl.h
+postconf_builtin.o: ../../include/stringops.h
+postconf_builtin.o: ../../include/sys_defs.h
+postconf_builtin.o: ../../include/vbuf.h
+postconf_builtin.o: ../../include/vstream.h
+postconf_builtin.o: ../../include/vstring.h
+postconf_builtin.o: bool_table.h
+postconf_builtin.o: bool_vars.h
+postconf_builtin.o: install_table.h
+postconf_builtin.o: install_vars.h
+postconf_builtin.o: int_table.h
+postconf_builtin.o: int_vars.h
+postconf_builtin.o: long_table.h
+postconf_builtin.o: long_vars.h
+postconf_builtin.o: nbool_table.h
+postconf_builtin.o: nbool_vars.h
+postconf_builtin.o: nint_table.h
+postconf_builtin.o: nint_vars.h
+postconf_builtin.o: postconf.h
+postconf_builtin.o: postconf_builtin.c
+postconf_builtin.o: raw_table.h
+postconf_builtin.o: raw_vars.h
+postconf_builtin.o: str_fn_table.h
+postconf_builtin.o: str_fn_vars.h
+postconf_builtin.o: str_table.h
+postconf_builtin.o: str_vars.h
+postconf_builtin.o: time_table.h
+postconf_builtin.o: time_vars.h
+postconf_dbms.o: ../../include/argv.h
+postconf_dbms.o: ../../include/check_arg.h
+postconf_dbms.o: ../../include/dict.h
+postconf_dbms.o: ../../include/dict_ht.h
+postconf_dbms.o: ../../include/dict_ldap.h
+postconf_dbms.o: ../../include/dict_memcache.h
+postconf_dbms.o: ../../include/dict_mysql.h
+postconf_dbms.o: ../../include/dict_pgsql.h
+postconf_dbms.o: ../../include/dict_proxy.h
+postconf_dbms.o: ../../include/dict_sqlite.h
+postconf_dbms.o: ../../include/htable.h
+postconf_dbms.o: ../../include/mac_expand.h
+postconf_dbms.o: ../../include/mac_parse.h
+postconf_dbms.o: ../../include/mail_conf.h
+postconf_dbms.o: ../../include/mail_params.h
+postconf_dbms.o: ../../include/msg.h
+postconf_dbms.o: ../../include/myflock.h
+postconf_dbms.o: ../../include/mymalloc.h
+postconf_dbms.o: ../../include/name_code.h
+postconf_dbms.o: ../../include/split_at.h
+postconf_dbms.o: ../../include/stringops.h
+postconf_dbms.o: ../../include/sys_defs.h
+postconf_dbms.o: ../../include/vbuf.h
+postconf_dbms.o: ../../include/vstream.h
+postconf_dbms.o: ../../include/vstring.h
+postconf_dbms.o: pcf_ldap_suffixes.h
+postconf_dbms.o: pcf_memcache_suffixes.h
+postconf_dbms.o: pcf_mysql_suffixes.h
+postconf_dbms.o: pcf_pgsql_suffixes.h
+postconf_dbms.o: pcf_sqlite_suffixes.h
+postconf_dbms.o: postconf.h
+postconf_dbms.o: postconf_dbms.c
+postconf_edit.o: ../../include/argv.h
+postconf_edit.o: ../../include/check_arg.h
+postconf_edit.o: ../../include/dict.h
+postconf_edit.o: ../../include/edit_file.h
+postconf_edit.o: ../../include/htable.h
+postconf_edit.o: ../../include/mail_params.h
+postconf_edit.o: ../../include/msg.h
+postconf_edit.o: ../../include/myflock.h
+postconf_edit.o: ../../include/mymalloc.h
+postconf_edit.o: ../../include/name_code.h
+postconf_edit.o: ../../include/readlline.h
+postconf_edit.o: ../../include/split_at.h
+postconf_edit.o: ../../include/stringops.h
+postconf_edit.o: ../../include/sys_defs.h
+postconf_edit.o: ../../include/vbuf.h
+postconf_edit.o: ../../include/vstream.h
+postconf_edit.o: ../../include/vstring.h
+postconf_edit.o: ../../include/vstring_vstream.h
+postconf_edit.o: postconf.h
+postconf_edit.o: postconf_edit.c
+postconf_lookup.o: ../../include/argv.h
+postconf_lookup.o: ../../include/check_arg.h
+postconf_lookup.o: ../../include/dict.h
+postconf_lookup.o: ../../include/htable.h
+postconf_lookup.o: ../../include/mac_expand.h
+postconf_lookup.o: ../../include/mac_parse.h
+postconf_lookup.o: ../../include/mail_conf.h
+postconf_lookup.o: ../../include/msg.h
+postconf_lookup.o: ../../include/myflock.h
+postconf_lookup.o: ../../include/mymalloc.h
+postconf_lookup.o: ../../include/name_code.h
+postconf_lookup.o: ../../include/stringops.h
+postconf_lookup.o: ../../include/sys_defs.h
+postconf_lookup.o: ../../include/vbuf.h
+postconf_lookup.o: ../../include/vstream.h
+postconf_lookup.o: ../../include/vstring.h
+postconf_lookup.o: postconf.h
+postconf_lookup.o: postconf_lookup.c
+postconf_main.o: ../../include/argv.h
+postconf_main.o: ../../include/check_arg.h
+postconf_main.o: ../../include/dict.h
+postconf_main.o: ../../include/htable.h
+postconf_main.o: ../../include/mac_expand.h
+postconf_main.o: ../../include/mac_parse.h
+postconf_main.o: ../../include/mail_conf.h
+postconf_main.o: ../../include/mail_params.h
+postconf_main.o: ../../include/msg.h
+postconf_main.o: ../../include/myflock.h
+postconf_main.o: ../../include/mymalloc.h
+postconf_main.o: ../../include/name_code.h
+postconf_main.o: ../../include/readlline.h
+postconf_main.o: ../../include/stringops.h
+postconf_main.o: ../../include/sys_defs.h
+postconf_main.o: ../../include/vbuf.h
+postconf_main.o: ../../include/vstream.h
+postconf_main.o: ../../include/vstring.h
+postconf_main.o: postconf.h
+postconf_main.o: postconf_main.c
+postconf_master.o: ../../include/argv.h
+postconf_master.o: ../../include/check_arg.h
+postconf_master.o: ../../include/dict.h
+postconf_master.o: ../../include/htable.h
+postconf_master.o: ../../include/mail_params.h
+postconf_master.o: ../../include/master_proto.h
+postconf_master.o: ../../include/msg.h
+postconf_master.o: ../../include/myflock.h
+postconf_master.o: ../../include/mymalloc.h
+postconf_master.o: ../../include/name_code.h
+postconf_master.o: ../../include/readlline.h
+postconf_master.o: ../../include/split_at.h
+postconf_master.o: ../../include/stringops.h
+postconf_master.o: ../../include/sys_defs.h
+postconf_master.o: ../../include/vbuf.h
+postconf_master.o: ../../include/vstream.h
+postconf_master.o: ../../include/vstring.h
+postconf_master.o: postconf.h
+postconf_master.o: postconf_master.c
+postconf_match.o: ../../include/argv.h
+postconf_match.o: ../../include/check_arg.h
+postconf_match.o: ../../include/dict.h
+postconf_match.o: ../../include/htable.h
+postconf_match.o: ../../include/msg.h
+postconf_match.o: ../../include/myflock.h
+postconf_match.o: ../../include/mymalloc.h
+postconf_match.o: ../../include/name_code.h
+postconf_match.o: ../../include/split_at.h
+postconf_match.o: ../../include/sys_defs.h
+postconf_match.o: ../../include/vbuf.h
+postconf_match.o: ../../include/vstream.h
+postconf_match.o: ../../include/vstring.h
+postconf_match.o: postconf.h
+postconf_match.o: postconf_match.c
+postconf_misc.o: ../../include/argv.h
+postconf_misc.o: ../../include/check_arg.h
+postconf_misc.o: ../../include/dict.h
+postconf_misc.o: ../../include/htable.h
+postconf_misc.o: ../../include/mail_conf.h
+postconf_misc.o: ../../include/mail_params.h
+postconf_misc.o: ../../include/myflock.h
+postconf_misc.o: ../../include/mymalloc.h
+postconf_misc.o: ../../include/name_code.h
+postconf_misc.o: ../../include/safe.h
+postconf_misc.o: ../../include/sys_defs.h
+postconf_misc.o: ../../include/vbuf.h
+postconf_misc.o: ../../include/vstream.h
+postconf_misc.o: ../../include/vstring.h
+postconf_misc.o: postconf.h
+postconf_misc.o: postconf_misc.c
+postconf_node.o: ../../include/argv.h
+postconf_node.o: ../../include/check_arg.h
+postconf_node.o: ../../include/dict.h
+postconf_node.o: ../../include/htable.h
+postconf_node.o: ../../include/msg.h
+postconf_node.o: ../../include/myflock.h
+postconf_node.o: ../../include/mymalloc.h
+postconf_node.o: ../../include/name_code.h
+postconf_node.o: ../../include/sys_defs.h
+postconf_node.o: ../../include/vbuf.h
+postconf_node.o: ../../include/vstream.h
+postconf_node.o: ../../include/vstring.h
+postconf_node.o: postconf.h
+postconf_node.o: postconf_node.c
+postconf_other.o: ../../include/argv.h
+postconf_other.o: ../../include/check_arg.h
+postconf_other.o: ../../include/dict.h
+postconf_other.o: ../../include/dns.h
+postconf_other.o: ../../include/htable.h
+postconf_other.o: ../../include/mbox_conf.h
+postconf_other.o: ../../include/msg.h
+postconf_other.o: ../../include/myaddrinfo.h
+postconf_other.o: ../../include/myflock.h
+postconf_other.o: ../../include/name_code.h
+postconf_other.o: ../../include/name_mask.h
+postconf_other.o: ../../include/sock_addr.h
+postconf_other.o: ../../include/sys_defs.h
+postconf_other.o: ../../include/tls.h
+postconf_other.o: ../../include/vbuf.h
+postconf_other.o: ../../include/vstream.h
+postconf_other.o: ../../include/vstring.h
+postconf_other.o: ../../include/xsasl.h
+postconf_other.o: postconf.h
+postconf_other.o: postconf_other.c
+postconf_print.o: ../../include/argv.h
+postconf_print.o: ../../include/check_arg.h
+postconf_print.o: ../../include/dict.h
+postconf_print.o: ../../include/htable.h
+postconf_print.o: ../../include/msg.h
+postconf_print.o: ../../include/myflock.h
+postconf_print.o: ../../include/name_code.h
+postconf_print.o: ../../include/sys_defs.h
+postconf_print.o: ../../include/vbuf.h
+postconf_print.o: ../../include/vstream.h
+postconf_print.o: ../../include/vstring.h
+postconf_print.o: postconf.h
+postconf_print.o: postconf_print.c
+postconf_service.o: ../../include/argv.h
+postconf_service.o: ../../include/check_arg.h
+postconf_service.o: ../../include/dict.h
+postconf_service.o: ../../include/htable.h
+postconf_service.o: ../../include/mail_params.h
+postconf_service.o: ../../include/msg.h
+postconf_service.o: ../../include/myflock.h
+postconf_service.o: ../../include/mymalloc.h
+postconf_service.o: ../../include/name_code.h
+postconf_service.o: ../../include/stringops.h
+postconf_service.o: ../../include/sys_defs.h
+postconf_service.o: ../../include/vbuf.h
+postconf_service.o: ../../include/vstream.h
+postconf_service.o: ../../include/vstring.h
+postconf_service.o: postconf.h
+postconf_service.o: postconf_service.c
+postconf_unused.o: ../../include/argv.h
+postconf_unused.o: ../../include/check_arg.h
+postconf_unused.o: ../../include/dict.h
+postconf_unused.o: ../../include/htable.h
+postconf_unused.o: ../../include/mail_conf.h
+postconf_unused.o: ../../include/mail_params.h
+postconf_unused.o: ../../include/msg.h
+postconf_unused.o: ../../include/myflock.h
+postconf_unused.o: ../../include/name_code.h
+postconf_unused.o: ../../include/sys_defs.h
+postconf_unused.o: ../../include/vbuf.h
+postconf_unused.o: ../../include/vstream.h
+postconf_unused.o: ../../include/vstring.h
+postconf_unused.o: postconf.h
+postconf_unused.o: postconf_unused.c
+postconf_user.o: ../../include/argv.h
+postconf_user.o: ../../include/check_arg.h
+postconf_user.o: ../../include/dict.h
+postconf_user.o: ../../include/htable.h
+postconf_user.o: ../../include/mac_expand.h
+postconf_user.o: ../../include/mac_parse.h
+postconf_user.o: ../../include/mail_conf.h
+postconf_user.o: ../../include/mail_params.h
+postconf_user.o: ../../include/msg.h
+postconf_user.o: ../../include/myflock.h
+postconf_user.o: ../../include/mymalloc.h
+postconf_user.o: ../../include/name_code.h
+postconf_user.o: ../../include/stringops.h
+postconf_user.o: ../../include/sys_defs.h
+postconf_user.o: ../../include/vbuf.h
+postconf_user.o: ../../include/vstream.h
+postconf_user.o: ../../include/vstring.h
+postconf_user.o: postconf.h
+postconf_user.o: postconf_user.c
diff --git a/src/postconf/extract.awk b/src/postconf/extract.awk
new file mode 100644
index 0000000..13b1f91
--- /dev/null
+++ b/src/postconf/extract.awk
@@ -0,0 +1,190 @@
+# Extract initialization tables from actual source code.
+
+# XXX: Associated variable aliasing:
+#
+# Some parameters bind to different variables in different contexts,
+# And other parameters map to associated variables in a many-to-1
+# fashion. This is mostly the result of the SMTP+LMTP integration
+# and the overloading of parameters that have identical semantics,
+# for the corresponding context.
+#
+# The "++table[...]" below ignores the associated variable name
+# when doing duplicate elimination. Differences in the default value
+# or lower/upper bounds still result in "postconf -d" duplicates,
+# which are a sign of an error somewhere...
+#
+# XXX Work around ancient AWK implementations with a 10 file limit
+# and no working close() operator (e.g. Solaris). Some systems
+# have a more modern implementation that is XPG4-compatible, but it
+# is too much bother to find out where each system keeps these.
+
+/^(static| )*(const +)?CONFIG_INT_TABLE .*\{/,/\};/ {
+ if ($1 ~ /VAR/) {
+ int_vars["int " substr($3,2,length($3)-2) ";"] = 1
+ if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ int_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_STR_TABLE .*\{/,/\};/ {
+ if ($1 ~ /^VAR/) {
+ str_vars["char *" substr($3,2,length($3)-2) ";"] = 1
+ if (++stab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ str_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_STR_FN_TABLE .*\{/,/\};/ {
+ if ($1 ~ /^VAR/) {
+ str_fn_vars["char *" substr($3,2,length($3)-2) ";"] = 1
+ $2 = "pcf_" $2
+ if (++stab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ str_fn_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_RAW_TABLE .*\{/,/\};/ {
+ if ($1 ~ /^VAR/) {
+ raw_vars["char *" substr($3,2,length($3)-2) ";"] = 1
+ if (++rtab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ raw_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_BOOL_TABLE .*\{/,/\};/ {
+ if ($1 ~ /^VAR/) {
+ bool_vars["int " substr($3,2,length($3)-2) ";"] = 1
+ if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ bool_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_TIME_TABLE .*\{/,/\};/ {
+ if ($1 ~ /^VAR/) {
+ time_vars["int " substr($3,2,length($3)-2) ";"] = 1
+ if (++ttab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ time_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_NINT_TABLE .*\{/,/\};/ {
+ if ($1 ~ /VAR/) {
+ nint_vars["int " substr($3,2,length($3)-2) ";"] = 1
+ if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ nint_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_NBOOL_TABLE .*\{/,/\};/ {
+ if ($1 ~ /^VAR/) {
+ nbool_vars["int " substr($3,2,length($3)-2) ";"] = 1
+ if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ nbool_table[$0] = 1
+ }
+ }
+}
+/^(static| )*(const +)?CONFIG_LONG_TABLE .*\{/,/\};/ {
+ if ($1 ~ /VAR/) {
+ long_vars["long " substr($3,2,length($3)-2) ";"] = 1
+ if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+ long_table[$0] = 1
+ }
+ }
+}
+
+END {
+ # Print parameter declarations without busting old AWK's file limit.
+ print "cat >int_vars.h <<'EOF'"
+ for (key in int_vars)
+ print key
+ print "EOF"
+
+ print "cat >str_vars.h <<'EOF'"
+ for (key in str_vars)
+ print key
+ print "EOF"
+
+ print "cat >str_fn_vars.h <<'EOF'"
+ for (key in str_fn_vars)
+ print key
+ print "EOF"
+
+ print "cat >raw_vars.h <<'EOF'"
+ for (key in raw_vars)
+ print key
+ print "EOF"
+
+ print "cat >bool_vars.h <<'EOF'"
+ for (key in bool_vars)
+ print key
+ print "EOF"
+
+ print "cat >time_vars.h <<'EOF'"
+ for (key in time_vars)
+ print key
+ print "EOF"
+
+ print "cat >nint_vars.h <<'EOF'"
+ for (key in nint_vars)
+ print key
+ print "EOF"
+
+ print "cat >nbool_vars.h <<'EOF'"
+ for (key in nbool_vars)
+ print key
+ print "EOF"
+
+ print "cat >long_vars.h <<'EOF'"
+ for (key in long_vars)
+ print key
+ print "EOF"
+
+ # Print parameter initializations without busting old AWK's file limit.
+ print "sed 's/[ ][ ]*/ /g' >int_table.h <<'EOF'"
+ for (key in int_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >str_table.h <<'EOF'"
+ for (key in str_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >str_fn_table.h <<'EOF'"
+ for (key in str_fn_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >raw_table.h <<'EOF'"
+ for (key in raw_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >bool_table.h <<'EOF'"
+ for (key in bool_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >time_table.h <<'EOF'"
+ for (key in time_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >nint_table.h <<'EOF'"
+ for (key in nint_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >nbool_table.h <<'EOF'"
+ for (key in nbool_table)
+ print key
+ print "EOF"
+
+ print "sed 's/[ ][ ]*/ /g' >long_table.h <<'EOF'"
+ for (key in long_table)
+ print key
+ print "EOF"
+
+ # Flush output nicely.
+ exit(0);
+}
diff --git a/src/postconf/extract_cfg.sh b/src/postconf/extract_cfg.sh
new file mode 100644
index 0000000..5901e95
--- /dev/null
+++ b/src/postconf/extract_cfg.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+# To view the formatted manual page of this file, type:
+# POSTFIXSOURCE/mantools/srctoman - extract_cfg.sh | nroff -man
+
+#++
+# NAME
+# extract_cfg 1
+# SUMMARY
+# extract database parameter names from cfg_get_xxx() calls
+# SYNOPSIS
+# \fBextract_cfg [-d|-s] [\fIfile...\fB]\fR
+# DESCRIPTION
+# The \fBextract_cfg\fR command extracts the parameter names
+# from cfg_get_{str,int,bool}() calls in dict_xxx.c files. The
+# output is one parameter name per line, formatted as a C string
+# followed by comma.
+#
+# Options:
+# .IP \fB-d\fR
+# Add the "domain" parameter to the output. This is used by
+# the LDAP, memcache, and *SQL* tables.
+# .IP \fB-s\fR
+# Add the legacy SQL query parameters: "select_field", "table",
+# "where_field", and "additional_conditions".
+# LICENSE
+# .ad
+# .fi
+# The Secure Mailer license must be distributed with this software.
+# HISTORY
+# .ad
+# .fi
+# This command was introduced with Postfix 3.3.
+# AUTHOR(S)
+# Wietse Venema
+# Google, Inc.
+# 111 8th Avenue
+# New York, NY 10011, USA
+#--
+
+# In case not installed.
+m4 </dev/null || exit 1
+
+# Flags to add db_common parameter names.
+add_legacy_sql_query_params=
+add_domain_param=
+
+# Parse JCL.
+
+while :
+do
+ case "$1" in
+ -d) add_domain_param=1;;
+ -s) add_legacy_sql_query_params=1;;
+ -*) echo Bad option: $1 1>&2; exit 1;;
+ *) break;;
+ esac
+ shift
+done
+
+# We use m4 macros to extract arguments from cfg_get_xxx() calls that
+# may span multiple lines. We sandwich information of interest between
+# control-A characters. Multiple cfg_get_xxx() calls on the same line
+# should be OK, as long as the calls don't nest.
+
+(
+cat <<'EOF'
+define(`cfg_get_str',`$2
+')dnl
+define(`cfg_get_int',`$2
+')dnl
+define(`cfg_get_bool',`$2
+')dnl
+EOF
+# Convert selected C macro definitions into m4 macro definitions.
+sed 's/^#define[ ]*\([DICT_MC_NAME_A-Za-z0-9_]*\)[ ]*\("[^"]*"\)/define(`\1'"'"',`\2'"'"')/' "$@"
+) | m4 | awk -F '// { print $2 }' | (
+test -n "$add_domain_param" && {
+cat <<EOF
+"domain"
+EOF
+}
+test -n "$add_legacy_sql_query_params" && {
+cat <<EOF
+"table"
+"select_field"
+"where_field"
+"additional_conditions"
+EOF
+}
+cat -
+) | sort -u | sed 's/$/,/'
diff --git a/src/postconf/install_table.h b/src/postconf/install_table.h
new file mode 100644
index 0000000..0e0ea0a
--- /dev/null
+++ b/src/postconf/install_table.h
@@ -0,0 +1,2 @@
+ VAR_CONFIG_DIR, DEF_CONFIG_DIR, &var_config_dir, 1, 0,
+ VAR_DEBUG_COMMAND, "", &var_debug_command, 1, 0,
diff --git a/src/postconf/install_vars.h b/src/postconf/install_vars.h
new file mode 100644
index 0000000..c46882b
--- /dev/null
+++ b/src/postconf/install_vars.h
@@ -0,0 +1,2 @@
+char *var_config_dir;
+char *var_debug_command;
diff --git a/src/postconf/postconf.c b/src/postconf/postconf.c
new file mode 100644
index 0000000..026639e
--- /dev/null
+++ b/src/postconf/postconf.c
@@ -0,0 +1,1096 @@
+/*++
+/* NAME
+/* postconf 1
+/* SUMMARY
+/* Postfix configuration utility
+/* SYNOPSIS
+/* .fi
+/* .ti -4
+/* \fBManaging main.cf:\fR
+/*
+/* \fBpostconf\fR [\fB-dfhHnopvx\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fB-C \fIclass,...\fR] [\fIparameter ...\fR]
+/*
+/* \fBpostconf\fR [\fB-epv\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIparameter\fB=\fIvalue ...\fR
+/*
+/* \fBpostconf\fR \fB-#\fR [\fB-pv\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIparameter ...\fR
+/*
+/* \fBpostconf\fR \fB-X\fR [\fB-pv\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIparameter ...\fR
+/*
+/* .ti -4
+/* \fBManaging master.cf service entries:\fR
+/*
+/* \fBpostconf\fR \fB-M\fR [\fB-fovx\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fIservice\fR[\fB/\fItype\fR]\fI ...\fR]
+/*
+/* \fBpostconf\fR \fB-M\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIservice\fB/\fItype\fB=\fIvalue ...\fR
+/*
+/* \fBpostconf\fR \fB-M#\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIservice\fB/\fItype ...\fR
+/*
+/* \fBpostconf\fR \fB-MX\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIservice\fB/\fItype ...\fR
+/*
+/* .ti -4
+/* \fBManaging master.cf service fields:\fR
+/*
+/* \fBpostconf\fR \fB-F\fR [\fB-fhHovx\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fIservice\fR[\fB/\fItype\fR[\fB/\fIfield\fR]]\fI ...\fR]
+/*
+/* \fBpostconf\fR \fB-F\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIservice\fB/\fItype\fB/\fIfield\fB=\fIvalue ...\fR
+/*
+/* .ti -4
+/* \fBManaging master.cf service parameters:\fR
+/*
+/* \fBpostconf\fR \fB-P\fR [\fB-fhHovx\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fIservice\fR[\fB/\fItype\fR[\fB/\fIparameter\fR]]\fI ...\fR]
+/*
+/* \fBpostconf\fR \fB-P\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIservice\fB/\fItype\fB/\fIparameter\fB=\fIvalue ...\fR
+/*
+/* \fBpostconf\fR \fB-PX\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR]
+/* \fIservice\fB/\fItype\fB/\fIparameter ...\fR
+/*
+/* .ti -4
+/* \fBManaging bounce message templates:\fR
+/*
+/* \fBpostconf\fR \fB-b\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fItemplate_file\fR]
+/*
+/* \fBpostconf\fR \fB-t\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fItemplate_file\fR]
+/*
+/* .ti -4
+/* \fBManaging TLS features:\fR
+/*
+/* \fBpostconf\fR \fB-T \fImode\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR]
+/*
+/* .ti -4
+/* \fBManaging other configuration:\fR
+/*
+/* \fBpostconf\fR \fB-a\fR|\fB-A\fR|\fB-l\fR|\fB-m\fR [\fB-v\fR]
+/* [\fB-c \fIconfig_dir\fR]
+/* DESCRIPTION
+/* By default, the \fBpostconf\fR(1) command displays the
+/* values of \fBmain.cf\fR configuration parameters, and warns
+/* about possible mis-typed parameter names (Postfix 2.9 and later).
+/* The command can also change \fBmain.cf\fR configuration
+/* parameter values, or display other configuration information
+/* about the Postfix mail system.
+/*
+/* Options:
+/* .IP \fB-a\fR
+/* List the available SASL plug-in types for the Postfix SMTP
+/* server. The plug-in type is selected with the \fBsmtpd_sasl_type\fR
+/* configuration parameter by specifying one of the names
+/* listed below.
+/* .RS
+/* .IP \fBcyrus\fR
+/* This server plug-in is available when Postfix is built with
+/* Cyrus SASL support.
+/* .IP \fBdovecot\fR
+/* This server plug-in uses the Dovecot authentication server,
+/* and is available when Postfix is built with any form of SASL
+/* support.
+/* .RE
+/* .IP
+/* This feature is available with Postfix 2.3 and later.
+/* .IP \fB-A\fR
+/* List the available SASL plug-in types for the Postfix SMTP
+/* client. The plug-in type is selected with the \fBsmtp_sasl_type\fR
+/* or \fBlmtp_sasl_type\fR configuration parameters by specifying
+/* one of the names listed below.
+/* .RS
+/* .IP \fBcyrus\fR
+/* This client plug-in is available when Postfix is built with
+/* Cyrus SASL support.
+/* .RE
+/* .IP
+/* This feature is available with Postfix 2.3 and later.
+/* .IP "\fB-b\fR [\fItemplate_file\fR]"
+/* Display the message text that appears at the beginning of
+/* delivery status notification (DSN) messages, expanding
+/* $\fBname\fR expressions with actual values as described in
+/* \fBbounce\fR(5).
+/*
+/* To override the \fBbounce_template_file\fR parameter setting,
+/* specify a template file name at the end of the "\fBpostconf
+/* -b\fR" command line. Specify an empty file name to display
+/* built-in templates (in shell language: "").
+/*
+/* This feature is available with Postfix 2.3 and later.
+/* .IP "\fB-c \fIconfig_dir\fR"
+/* The \fBmain.cf\fR configuration file is in the named directory
+/* instead of the default configuration directory.
+/* .IP "\fB-C \fIclass,...\fR"
+/* When displaying \fBmain.cf\fR parameters, select only
+/* parameters from the specified class(es):
+/* .RS
+/* .IP \fBbuiltin\fR
+/* Parameters with built-in names.
+/* .IP \fBservice\fR
+/* Parameters with service-defined names (the first field of
+/* a \fBmaster.cf\fR entry plus a Postfix-defined suffix).
+/* .IP \fBuser\fR
+/* Parameters with user-defined names.
+/* .IP \fBall\fR
+/* All the above classes.
+/* .RE
+/* .IP
+/* The default is as if "\fB-C all\fR" is
+/* specified.
+/*
+/* This feature is available with Postfix 2.9 and later.
+/* .IP \fB-d\fR
+/* Print \fBmain.cf\fR default parameter settings instead of
+/* actual settings.
+/* Specify \fB-df\fR to fold long lines for human readability
+/* (Postfix 2.9 and later).
+/* .IP \fB-e\fR
+/* Edit the \fBmain.cf\fR configuration file, and update
+/* parameter settings with the "\fIname=value\fR" pairs on the
+/* \fBpostconf\fR(1) command line.
+/*
+/* With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+/* and replace one or more service entries with new values as
+/* specified with "\fIservice/type=value\fR" on the \fBpostconf\fR(1)
+/* command line.
+/*
+/* With \fB-F\fR, edit the \fBmaster.cf\fR configuration file,
+/* and replace one or more service fields with new values as
+/* specified with "\fIservice/type/field=value\fR" on the
+/* \fBpostconf\fR(1) command line. Currently, the "command"
+/* field contains the command name and command arguments. this
+/* may change in the near future, so that the "command" field
+/* contains only the command name, and a new "arguments"
+/* pseudofield contains the command arguments.
+/*
+/* With \fB-P\fR, edit the \fBmaster.cf\fR configuration file,
+/* and add or update one or more service parameter settings
+/* (-o parameter=value settings) with new values as specified
+/* with "\fIservice/type/parameter=value\fR" on the \fBpostconf\fR(1)
+/* command line.
+/*
+/* In all cases the file is copied to a temporary file then
+/* renamed into place. Specify quotes to protect special
+/* characters and whitespace on the \fBpostconf\fR(1) command
+/* line.
+/*
+/* The \fB-e\fR option is no longer needed with Postfix version
+/* 2.8 and later.
+/* .IP \fB-f\fR
+/* Fold long lines when printing \fBmain.cf\fR or \fBmaster.cf\fR
+/* configuration file entries, for human readability.
+/*
+/* This feature is available with Postfix 2.9 and later.
+/* .IP \fB-F\fR
+/* Show \fBmaster.cf\fR per-entry field settings (by default
+/* all services and all fields), formatted as
+/* "\fIservice/type/field=value\fR", one per line. Specify
+/* \fB-Ff\fR to fold long lines.
+/*
+/* Specify one or more "\fIservice/type/field\fR" instances
+/* on the \fBpostconf\fR(1) command line to limit the output
+/* to fields of interest. Trailing parameter name or service
+/* type fields that are omitted will be handled as "*" wildcard
+/* fields.
+/*
+/* This feature is available with Postfix 2.11 and later.
+/* .IP \fB-h\fR
+/* Show parameter or attribute values without the "\fIname\fR
+/* = " label that normally precedes the value.
+/* .IP \fB-H\fR
+/* Show parameter or attribute names without the " = \fIvalue\fR"
+/* that normally follows the name.
+/*
+/* This feature is available with Postfix 3.1 and later.
+/* .IP \fB-l\fR
+/* List the names of all supported mailbox locking methods.
+/* Postfix supports the following methods:
+/* .RS
+/* .IP \fBflock\fR
+/* A kernel-based advisory locking method for local files only.
+/* This locking method is available on systems with a BSD
+/* compatible library.
+/* .IP \fBfcntl\fR
+/* A kernel-based advisory locking method for local and remote
+/* files.
+/* .IP \fBdotlock\fR
+/* An application-level locking method. An application locks
+/* a file named \fIfilename\fR by creating a file named
+/* \fIfilename\fB.lock\fR. The application is expected to
+/* remove its own lock file, as well as stale lock files that
+/* were left behind after abnormal program termination.
+/* .RE
+/* .IP \fB-m\fR
+/* List the names of all supported lookup table types. In
+/* Postfix configuration files, lookup tables are specified
+/* as \fItype\fB:\fIname\fR, where \fItype\fR is one of the
+/* types listed below. The table \fIname\fR syntax depends on
+/* the lookup table type as described in the DATABASE_README
+/* document.
+/* .RS
+/* .IP \fBbtree\fR
+/* A sorted, balanced tree structure. Available on systems
+/* with support for Berkeley DB databases.
+/* .IP \fBcdb\fR
+/* A read-optimized structure with no support for incremental
+/* updates. Available on systems with support for CDB databases.
+/*
+/* This feature is available with Postfix 2.2 and later.
+/* .IP \fBcidr\fR
+/* A table that associates values with Classless Inter-Domain
+/* Routing (CIDR) patterns. This is described in \fBcidr_table\fR(5).
+/*
+/* This feature is available with Postfix 2.2 and later.
+/* .IP \fBdbm\fR
+/* An indexed file type based on hashing. Available on systems
+/* with support for DBM databases.
+/* .IP \fBenviron\fR
+/* The UNIX process environment array. The lookup key is the
+/* environment variable name; the table name is ignored. Originally
+/* implemented for testing, someone may find this useful someday.
+/* .IP \fBfail\fR
+/* A table that reliably fails all requests. The lookup table
+/* name is used for logging. This table exists to simplify
+/* Postfix error tests.
+/*
+/* This feature is available with Postfix 2.9 and later.
+/* .IP \fBhash\fR
+/* An indexed file type based on hashing. Available on systems
+/* with support for Berkeley DB databases.
+/* .IP "\fBinline\fR (read-only)"
+/* A non-shared, in-memory lookup table. Example: "\fBinline:{
+/* \fIkey\fB=\fIvalue\fB, { \fIkey\fB = \fItext with whitespace
+/* or comma\fB }}\fR". Key-value pairs are separated by
+/* whitespace or comma; with a key-value pair inside "\fB{}\fR",
+/* whitespace is ignored after the opening "\fB{\fR", around
+/* the "\fB=\fR" between key and value, and before the closing
+/* "\fB}\fR". Inline tables eliminate the need to create a
+/* database file for just a few fixed elements. See also the
+/* \fIstatic:\fR map type.
+/*
+/* This feature is available with Postfix 3.0 and later.
+/* .IP \fBinternal\fR
+/* A non-shared, in-memory hash table. Its content are lost
+/* when a process terminates.
+/* .IP "\fBlmdb\fR"
+/* OpenLDAP LMDB database (a memory-mapped, persistent file).
+/* Available on systems with support for LMDB databases. This
+/* is described in \fBlmdb_table\fR(5).
+/*
+/* This feature is available with Postfix 2.11 and later.
+/* .IP "\fBldap\fR (read-only)"
+/* LDAP database client. This is described in \fBldap_table\fR(5).
+/* .IP "\fBmemcache\fR"
+/* Memcache database client. This is described in
+/* \fBmemcache_table\fR(5).
+/*
+/* This feature is available with Postfix 2.9 and later.
+/* .IP "\fBmysql\fR (read-only)"
+/* MySQL database client. Available on systems with support
+/* for MySQL databases. This is described in \fBmysql_table\fR(5).
+/* .IP "\fBpcre\fR (read-only)"
+/* A lookup table based on Perl Compatible Regular Expressions.
+/* The file format is described in \fBpcre_table\fR(5).
+/* .IP "\fBpgsql\fR (read-only)"
+/* PostgreSQL database client. This is described in
+/* \fBpgsql_table\fR(5).
+/*
+/* This feature is available with Postfix 2.1 and later.
+/* .IP "\fBpipemap\fR (read-only)"
+/* A lookup table that constructs a pipeline of tables. Example:
+/* "\fBpipemap:{\fItype_1:name_1, ..., type_n:name_n\fB}\fR".
+/* Each "pipemap:" query is given to the first table. Each
+/* lookup result becomes the query for the next table in the
+/* pipeline, and the last table produces the final result.
+/* When any table lookup produces no result, the pipeline
+/* produces no result. The first and last characters of the
+/* "pipemap:" table name must be "\fB{\fR" and "\fB}\fR".
+/* Within these, individual maps are separated with comma or
+/* whitespace.
+/*
+/* This feature is available with Postfix 3.0 and later.
+/* .IP "\fBproxy\fR"
+/* Postfix \fBproxymap\fR(8) client for shared access to Postfix
+/* databases. The table name syntax is \fItype\fB:\fIname\fR.
+/*
+/* This feature is available with Postfix 2.0 and later.
+/* .IP "\fBrandmap\fR (read-only)"
+/* An in-memory table that performs random selection. Example:
+/* "\fBrandmap:{\fIresult_1, ..., result_n\fB}\fR". Each table query
+/* returns a random choice from the specified results. The first
+/* and last characters of the "randmap:" table name must be
+/* "\fB{\fR" and "\fB}\fR". Within these, individual results
+/* are separated with comma or whitespace. To give a specific
+/* result more weight, specify it multiple times.
+/*
+/* This feature is available with Postfix 3.0 and later.
+/* .IP "\fBregexp\fR (read-only)"
+/* A lookup table based on regular expressions. The file format
+/* is described in \fBregexp_table\fR(5).
+/* .IP \fBsdbm\fR
+/* An indexed file type based on hashing. Available on systems
+/* with support for SDBM databases.
+/*
+/* This feature is available with Postfix 2.2 and later.
+/* .IP "\fBsocketmap\fR (read-only)"
+/* Sendmail-style socketmap client. The table name is
+/* \fBinet\fR:\fIhost\fR:\fIport\fR:\fIname\fR for a TCP/IP
+/* server, or \fBunix\fR:\fIpathname\fR:\fIname\fR for a
+/* UNIX-domain server. This is described in \fBsocketmap_table\fR(5).
+/*
+/* This feature is available with Postfix 2.10 and later.
+/* .IP "\fBsqlite\fR (read-only)"
+/* SQLite database. This is described in \fBsqlite_table\fR(5).
+/*
+/* This feature is available with Postfix 2.8 and later.
+/* .IP "\fBstatic\fR (read-only)"
+/* A table that always returns its name as lookup result. For
+/* example, \fBstatic:foobar\fR always returns the string
+/* \fBfoobar\fR as lookup result. Specify "\fBstatic:{ \fItext
+/* with whitespace\fB }\fR" when the result contains whitespace;
+/* this form ignores whitespace after the opening "\fB{\fR"
+/* and before the closing
+/* "\fB}\fR". See also the \fIinline:\fR map.
+/*
+/* The form "\fBstatic:{\fItext\fB}\fR is available with Postfix
+/* 3.0 and later.
+/* .IP "\fBtcp\fR (read-only)"
+/* TCP/IP client. The protocol is described in \fBtcp_table\fR(5).
+/* .IP "\fBtexthash\fR (read-only)"
+/* Produces similar results as hash: files, except that you
+/* don't need to run the \fBpostmap\fR(1) command before you
+/* can use the file, and that it does not detect changes after
+/* the file is read.
+/*
+/* This feature is available with Postfix 2.8 and later.
+/* .IP "\fBunionmap\fR (read-only)"
+/* A table that sends each query to multiple lookup tables and
+/* that concatenates all found results, separated by comma.
+/* The table name syntax is the same as for \fBpipemap\fR.
+/*
+/* This feature is available with Postfix 3.0 and later.
+/* .IP "\fBunix\fR (read-only)"
+/* A limited view of the UNIX authentication database. The
+/* following tables are implemented:
+/* .RS
+/*. IP \fBunix:passwd.byname\fR
+/* The table is the UNIX password database. The key is a login
+/* name. The result is a password file entry in \fBpasswd\fR(5)
+/* format.
+/* .IP \fBunix:group.byname\fR
+/* The table is the UNIX group database. The key is a group
+/* name. The result is a group file entry in \fBgroup\fR(5)
+/* format.
+/* .RE
+/* .RE
+/* .IP
+/* Other table types may exist depending on how Postfix was
+/* built.
+/* .IP \fB-M\fR
+/* Show \fBmaster.cf\fR file contents instead of \fBmain.cf\fR
+/* file contents. Specify \fB-Mf\fR to fold long lines for
+/* human readability.
+/*
+/* Specify zero or more arguments, each with a \fIservice-name\fR
+/* or \fIservice-name/service-type\fR pair, where \fIservice-name\fR
+/* is the first field of a master.cf entry and \fIservice-type\fR
+/* is one of (\fBinet\fR, \fBunix\fR, \fBfifo\fR, or \fBpass\fR).
+/*
+/* If \fIservice-name\fR or \fIservice-name/service-type\fR
+/* is specified, only the matching master.cf entries will be
+/* output. For example, "\fBpostconf -Mf smtp\fR" will output
+/* all services named "smtp", and "\fBpostconf -Mf smtp/inet\fR"
+/* will output only the smtp service that listens on the
+/* network. Trailing service type fields that are omitted
+/* will be handled as "*" wildcard fields.
+/*
+/* This feature is available with Postfix 2.9 and later. The
+/* syntax was changed from "\fIname.type\fR" to "\fIname/type\fR",
+/* and "*" wildcard support was added with Postfix 2.11.
+/* .IP \fB-n\fR
+/* Show only configuration parameters that have explicit
+/* \fIname=value\fR settings in \fBmain.cf\fR. Specify \fB-nf\fR
+/* to fold long lines for human readability (Postfix 2.9 and
+/* later). To show settings that differ from built-in defaults
+/* only, use the following bash syntax:
+/* .nf
+/* comm -23 <(postconf -n) <(postconf -d)
+/* .fi
+/* Replace "-23" with "-12" to show settings that duplicate
+/* built-in defaults.
+/* .IP "\fB-o \fIname=value\fR"
+/* Override \fBmain.cf\fR parameter settings.
+/*
+/* This feature is available with Postfix 2.10 and later.
+/* .IP \fB-p\fR
+/* Show \fBmain.cf\fR parameter settings. This is the default.
+/*
+/* This feature is available with Postfix 2.11 and later.
+/* .IP \fB-P\fR
+/* Show \fBmaster.cf\fR service parameter settings (by default
+/* all services and all parameters), formatted as
+/* "\fIservice/type/parameter=value\fR", one per line. Specify
+/* \fB-Pf\fR to fold long lines.
+/*
+/* Specify one or more "\fIservice/type/parameter\fR" instances
+/* on the \fBpostconf\fR(1) command line to limit the output
+/* to parameters of interest. Trailing parameter name or
+/* service type fields that are omitted will be handled as "*"
+/* wildcard fields.
+/*
+/* This feature is available with Postfix 2.11 and later.
+/* .IP "\fB-t\fR [\fItemplate_file\fR]"
+/* Display the templates for text that appears at the beginning
+/* of delivery status notification (DSN) messages, without
+/* expanding $\fBname\fR expressions.
+/*
+/* To override the \fBbounce_template_file\fR parameter setting,
+/* specify a template file name at the end of the "\fBpostconf
+/* -t\fR" command line. Specify an empty file name to display
+/* built-in templates (in shell language: "").
+/*
+/* This feature is available with Postfix 2.3 and later.
+/* .IP "\fB-T \fImode\fR"
+/* If Postfix is compiled without TLS support, the \fB-T\fR option
+/* produces no output. Otherwise, if an invalid \fImode\fR is specified,
+/* the \fB-T\fR option reports an error and exits with a non-zero status
+/* code. The valid modes are:
+/* .RS
+/* .IP \fBcompile-version\fR
+/* Output the OpenSSL version that Postfix was compiled with
+/* (i.e. the OpenSSL version in a header file). The output
+/* format is the same as with the command "\fBopenssl version\fR".
+/* .IP \fBrun-version\fR
+/* Output the OpenSSL version that Postfix is linked with at
+/* runtime (i.e. the OpenSSL version in a shared library).
+/* .IP \fBpublic-key-algorithms\fR
+/* Output the lower-case names of the supported public-key
+/* algorithms, one per-line.
+/* .RE
+/* .IP
+/* This feature is available with Postfix 3.1 and later.
+/* .IP \fB-v\fR
+/* Enable verbose logging for debugging purposes. Multiple
+/* \fB-v\fR options make the software increasingly verbose.
+/* .IP \fB-x\fR
+/* Expand \fI$name\fR in \fBmain.cf\fR or \fBmaster.cf\fR
+/* parameter values. The expansion is recursive.
+/*
+/* This feature is available with Postfix 2.10 and later.
+/* .IP \fB-X\fR
+/* Edit the \fBmain.cf\fR configuration file, and remove the
+/* parameters named on the \fBpostconf\fR(1) command line.
+/* Specify a list of parameter names, not "\fIname=value\fR"
+/* pairs.
+/*
+/* With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+/* and remove one or more service entries as specified with
+/* "\fIservice/type\fR" on the \fBpostconf\fR(1) command line.
+/*
+/* With \fB-P\fR, edit the \fBmaster.cf\fR configuration file,
+/* and remove one or more service parameter settings (-o
+/* parameter=value settings) as specified with
+/* "\fIservice/type/parameter\fR" on the \fBpostconf\fR(1)
+/* command line.
+/*
+/* In all cases the file is copied to a temporary file then
+/* renamed into place. Specify quotes to protect special
+/* characters on the \fBpostconf\fR(1) command line.
+/*
+/* There is no \fBpostconf\fR(1) command to perform the reverse
+/* operation.
+/*
+/* This feature is available with Postfix 2.10 and later.
+/* Support for -M and -P was added with Postfix 2.11.
+/* .IP \fB-#\fR
+/* Edit the \fBmain.cf\fR configuration file, and comment out
+/* the parameters named on the \fBpostconf\fR(1) command line,
+/* so that those parameters revert to their default values.
+/* Specify a list of parameter names, not "\fIname=value\fR"
+/* pairs.
+/*
+/* With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+/* and comment out one or more service entries as specified
+/* with "\fIservice/type\fR" on the \fBpostconf\fR(1) command
+/* line.
+/*
+/* In all cases the file is copied to a temporary file then
+/* renamed into place. Specify quotes to protect special
+/* characters on the \fBpostconf\fR(1) command line.
+/*
+/* There is no \fBpostconf\fR(1) command to perform the reverse
+/* operation.
+/*
+/* This feature is available with Postfix 2.6 and later. Support
+/* for -M was added with Postfix 2.11.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* ENVIRONMENT
+/* .ad
+/* .fi
+/* .IP \fBMAIL_CONFIG\fR
+/* Directory with Postfix configuration files.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* The following \fBmain.cf\fR parameters are especially
+/* relevant to this program.
+/*
+/* The text below provides only a parameter summary. See
+/* \fBpostconf\fR(5) for more details including examples.
+/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
+/* The default location of the Postfix main.cf and master.cf
+/* configuration files.
+/* .IP "\fBbounce_template_file (empty)\fR"
+/* Pathname of a configuration file with bounce message templates.
+/* FILES
+/* /etc/postfix/main.cf, Postfix configuration parameters
+/* /etc/postfix/master.cf, Postfix master daemon configuration
+/* SEE ALSO
+/* bounce(5), bounce template file format
+/* master(5), master.cf configuration file syntax
+/* postconf(5), main.cf configuration file syntax
+/* README FILES
+/* .ad
+/* .fi
+/* Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+/* html_directory\fR" to locate this information.
+/* .na
+/* .nf
+/* DATABASE_README, Postfix lookup table overview
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this
+/* software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_vstream.h>
+#include <dict.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+#include <name_mask.h>
+#include <warn_stat.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <mail_version.h>
+#include <mail_run.h>
+#include <mail_dict.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+ * Global storage. See postconf.h for description.
+ */
+PCF_PARAM_TABLE *pcf_param_table;
+PCF_MASTER_ENT *pcf_master_table;
+int pcf_cmd_mode = PCF_DEF_MODE;
+
+ /*
+ * Application fingerprinting.
+ */
+MAIL_VERSION_STAMP_DECLARE;
+
+ /*
+ * This program has so many command-line options that we have to implement a
+ * compatibility matrix to weed out the conflicting option combinations, and
+ * to alert the user about option combinations that have no effect.
+ */
+
+ /*
+ * Options that are mutually-exclusive. First entry must specify the major
+ * modes. Other entries specify conflicts between option modifiers.
+ */
+static const int pcf_incompat_options[] = {
+ /* Major modes. */
+ PCF_SHOW_SASL_SERV | PCF_SHOW_SASL_CLNT | PCF_EXP_DSN_TEMPL \
+ |PCF_SHOW_LOCKS | PCF_SHOW_MAPS | PCF_DUMP_DSN_TEMPL | PCF_MAIN_PARAM \
+ |PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM | PCF_SHOW_TLS,
+ /* Modifiers. */
+ PCF_SHOW_DEFS | PCF_EDIT_CONF | PCF_SHOW_NONDEF | PCF_COMMENT_OUT \
+ |PCF_EDIT_EXCL,
+ PCF_FOLD_LINE | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL,
+ PCF_SHOW_EVAL | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL,
+ PCF_MAIN_OVER | PCF_SHOW_DEFS | PCF_EDIT_CONF | PCF_COMMENT_OUT \
+ |PCF_EDIT_EXCL,
+ PCF_HIDE_NAME | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL \
+ |PCF_HIDE_VALUE,
+ 0,
+};
+
+ /*
+ * Options, and the only options that they are compatible with. There must
+ * be one entry for each major mode. Other entries specify compatibility
+ * between option modifiers.
+ */
+static const int pcf_compat_options[][2] = {
+ /* Major modes. */
+ {PCF_SHOW_SASL_SERV, 0},
+ {PCF_SHOW_SASL_CLNT, 0},
+ {PCF_EXP_DSN_TEMPL, 0},
+ {PCF_SHOW_LOCKS, 0},
+ {PCF_SHOW_MAPS, 0,},
+ {PCF_SHOW_TLS, 0,},
+ {PCF_DUMP_DSN_TEMPL, 0},
+ {PCF_MAIN_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT \
+ |PCF_FOLD_LINE | PCF_HIDE_NAME | PCF_PARAM_CLASS \
+ |PCF_SHOW_EVAL | PCF_SHOW_DEFS | PCF_SHOW_NONDEF \
+ |PCF_MAIN_OVER | PCF_HIDE_VALUE)},
+ {PCF_MASTER_ENTRY, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT \
+ |PCF_FOLD_LINE | PCF_MAIN_OVER | PCF_SHOW_EVAL)},
+ {PCF_MASTER_FLD, (PCF_EDIT_CONF | PCF_FOLD_LINE | PCF_HIDE_NAME \
+ |PCF_MAIN_OVER | PCF_SHOW_EVAL | PCF_HIDE_VALUE)},
+ {PCF_MASTER_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_FOLD_LINE \
+ |PCF_HIDE_NAME | PCF_MAIN_OVER | PCF_SHOW_EVAL \
+ |PCF_HIDE_VALUE)},
+ /* Modifiers. */
+ {PCF_PARAM_CLASS, (PCF_MAIN_PARAM | PCF_SHOW_DEFS | PCF_SHOW_NONDEF)},
+ 0,
+};
+
+ /*
+ * Compatibility to string conversion support.
+ */
+static const NAME_MASK pcf_compat_names[] = {
+ "-a", PCF_SHOW_SASL_SERV,
+ "-A", PCF_SHOW_SASL_CLNT,
+ "-b", PCF_EXP_DSN_TEMPL,
+ "-C", PCF_PARAM_CLASS,
+ "-d", PCF_SHOW_DEFS,
+ "-e", PCF_EDIT_CONF,
+ "-f", PCF_FOLD_LINE,
+ "-F", PCF_MASTER_FLD,
+ "-h", PCF_HIDE_NAME,
+ "-H", PCF_HIDE_VALUE,
+ "-l", PCF_SHOW_LOCKS,
+ "-m", PCF_SHOW_MAPS,
+ "-M", PCF_MASTER_ENTRY,
+ "-n", PCF_SHOW_NONDEF,
+ "-o", PCF_MAIN_OVER,
+ "-p", PCF_MAIN_PARAM,
+ "-P", PCF_MASTER_PARAM,
+ "-t", PCF_DUMP_DSN_TEMPL,
+ "-T", PCF_SHOW_TLS,
+ "-x", PCF_SHOW_EVAL,
+ "-X", PCF_EDIT_EXCL,
+ "-#", PCF_COMMENT_OUT,
+ 0,
+};
+
+/* usage - enumerate parameters without compatibility info */
+
+static void usage(const char *progname)
+{
+ msg_fatal("usage: %s"
+ " [-a (server SASL types)]"
+ " [-A (client SASL types)]"
+ " [-b (bounce templates)]"
+ " [-c config_dir]"
+ " [-c param_class]"
+ " [-d (parameter defaults)]"
+ " [-e (edit configuration)]"
+ " [-f (fold lines)]"
+ " [-F (master.cf fields)]"
+ " [-h (no names)]"
+ " [-H (no values)]"
+ " [-l (lock types)]"
+ " [-m (map types)]"
+ " [-M (master.cf)]"
+ " [-n (non-default parameters)]"
+ " [-o name=value (override parameter value)]"
+ " [-p (main.cf, default)]"
+ " [-P (master.cf parameters)]"
+ " [-t (bounce templates)]"
+ " [-T compile-version|run-version|public-key-algorithms]"
+ " [-v (verbose)]"
+ " [-x (expand parameter values)]"
+ " [-X (exclude)]"
+ " [-# (comment-out)]"
+ " [name...]", progname);
+}
+
+/* pcf_check_exclusive_options - complain about mutually-exclusive options */
+
+static void pcf_check_exclusive_options(int optval)
+{
+ const char *myname = "pcf_check_exclusive_options";
+ const int *op;
+ int oval;
+ unsigned mask;
+
+ for (op = pcf_incompat_options; (oval = *op) != 0; op++) {
+ oval &= optval;
+ for (mask = ~0U; (mask & oval) != 0; mask >>= 1) {
+ if ((mask & oval) != oval)
+ msg_fatal("specify one of %s",
+ str_name_mask(myname, pcf_compat_names, oval));
+ }
+ }
+}
+
+/* pcf_check_compat_options - complain about incompatible options */
+
+static void pcf_check_compat_options(int optval)
+{
+ const char *myname = "pcf_check_compat_options";
+ VSTRING *buf1 = vstring_alloc(10);
+ VSTRING *buf2 = vstring_alloc(10);
+ const int (*op)[2];
+ int excess;
+
+ for (op = pcf_compat_options; op[0][0] != 0; op++) {
+ if ((optval & *op[0]) != 0
+ && (excess = (optval & ~((*op)[0] | (*op)[1]))) != 0)
+ msg_fatal("with option %s, do not specify %s",
+ str_name_mask_opt(buf1, myname, pcf_compat_names,
+ (*op)[0], NAME_MASK_NUMBER),
+ str_name_mask_opt(buf2, myname, pcf_compat_names,
+ excess, NAME_MASK_NUMBER));
+ }
+ vstring_free(buf1);
+ vstring_free(buf2);
+}
+
+/* main */
+
+int main(int argc, char **argv)
+{
+ int ch;
+ int fd;
+ struct stat st;
+ ARGV *ext_argv = 0;
+ int param_class = PCF_PARAM_MASK_CLASS;
+ static const NAME_MASK param_class_table[] = {
+ "builtin", PCF_PARAM_FLAG_BUILTIN,
+ "service", PCF_PARAM_FLAG_SERVICE,
+ "user", PCF_PARAM_FLAG_USER,
+ "all", PCF_PARAM_MASK_CLASS,
+ 0,
+ };
+ ARGV *override_params = 0;
+ const char *pcf_tls_arg = 0;
+
+ /*
+ * Fingerprint executables and core dumps.
+ */
+ MAIL_VERSION_STAMP_ALLOCATE;
+
+ /*
+ * Be consistent with file permissions.
+ */
+ umask(022);
+
+ /*
+ * To minimize confusion, make sure that the standard file descriptors
+ * are open before opening anything else. XXX Work around for 44BSD where
+ * fstat can return EBADF on an open file descriptor.
+ */
+ 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");
+
+ /*
+ * Set up logging.
+ */
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+
+ /*
+ * Parse JCL.
+ */
+ while ((ch = GETOPT(argc, argv, "aAbc:C:deEfFhHlmMno:pPtT:vxX#")) > 0) {
+ switch (ch) {
+ case 'a':
+ pcf_cmd_mode |= PCF_SHOW_SASL_SERV;
+ break;
+ case 'A':
+ pcf_cmd_mode |= PCF_SHOW_SASL_CLNT;
+ break;
+ case 'b':
+ pcf_cmd_mode |= PCF_EXP_DSN_TEMPL;
+ 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 'C':
+ param_class = name_mask_opt("-C option", param_class_table,
+ optarg, NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
+ break;
+ case 'd':
+ pcf_cmd_mode |= PCF_SHOW_DEFS;
+ break;
+ case 'e':
+ pcf_cmd_mode |= PCF_EDIT_CONF;
+ break;
+ case 'f':
+ pcf_cmd_mode |= PCF_FOLD_LINE;
+ break;
+ case 'F':
+ pcf_cmd_mode |= PCF_MASTER_FLD;
+ break;
+ case '#':
+ pcf_cmd_mode |= PCF_COMMENT_OUT;
+ break;
+ case 'h':
+ pcf_cmd_mode |= PCF_HIDE_NAME;
+ break;
+ case 'H':
+ pcf_cmd_mode |= PCF_HIDE_VALUE;
+ break;
+ case 'l':
+ pcf_cmd_mode |= PCF_SHOW_LOCKS;
+ break;
+ case 'm':
+ pcf_cmd_mode |= PCF_SHOW_MAPS;
+ break;
+ case 'M':
+ pcf_cmd_mode |= PCF_MASTER_ENTRY;
+ break;
+ case 'n':
+ pcf_cmd_mode |= PCF_SHOW_NONDEF;
+ break;
+ case 'o':
+ pcf_cmd_mode |= PCF_MAIN_OVER;
+ if (override_params == 0)
+ override_params = argv_alloc(2);
+ argv_add(override_params, optarg, (char *) 0);
+ break;
+ case 'p':
+ pcf_cmd_mode |= PCF_MAIN_PARAM;
+ break;
+ case 'P':
+ pcf_cmd_mode |= PCF_MASTER_PARAM;
+ break;
+ case 't':
+ pcf_cmd_mode |= PCF_DUMP_DSN_TEMPL;
+ 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 'T':
+ if (pcf_cmd_mode & PCF_SHOW_TLS)
+ msg_fatal("At most one -T <mode> option may be specified");
+ pcf_cmd_mode |= PCF_SHOW_TLS;
+ pcf_tls_arg = optarg;
+ break;
+ case 'x':
+ pcf_cmd_mode |= PCF_SHOW_EVAL;
+ break;
+ case 'X':
+ /* This is irreversible, therefore require two-finger action. */
+ pcf_cmd_mode |= PCF_EDIT_EXCL;
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ /*
+ * We don't enforce import_environment consistency in this program.
+ *
+ * We don't extract import_environment from main.cf, because the postconf
+ * command must be able to extract parameter settings from main.cf before
+ * all installation parameters such as mail_owner or setgid_group have a
+ * legitimate value.
+ *
+ * We would need the functionality of mail_params_init() including all the
+ * side effects of populating the CONFIG_DICT with default values so that
+ * $name expansion works correctly, but excluding all the parameter value
+ * sanity checks so that it would not abort at installation time.
+ */
+
+ /*
+ * Make all options explicit, before checking their compatibility.
+ */
+#define PCF_MAIN_OR_MASTER \
+ (PCF_MAIN_PARAM | PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)
+
+ if ((pcf_cmd_mode & pcf_incompat_options[0]) == 0)
+ pcf_cmd_mode |= PCF_MAIN_PARAM;
+ if ((pcf_cmd_mode & PCF_MAIN_OR_MASTER)
+ && argv[optind] && strchr(argv[optind], '='))
+ pcf_cmd_mode |= PCF_EDIT_CONF;
+
+ /*
+ * Sanity check.
+ */
+ pcf_check_exclusive_options(pcf_cmd_mode);
+ pcf_check_compat_options(pcf_cmd_mode);
+
+ if ((pcf_cmd_mode & PCF_EDIT_CONF) && argc == optind)
+ msg_fatal("-e requires name=value argument");
+
+ /*
+ * Display bounce template information and exit.
+ */
+ 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);
+ }
+ /* Grr... */
+ 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);
+ /* NOTREACHED */
+ }
+
+ /*
+ * If showing map types, show them and exit
+ */
+ if (pcf_cmd_mode & PCF_SHOW_MAPS) {
+ mail_conf_read();
+ mail_dict_init();
+ pcf_show_maps();
+ }
+
+ /*
+ * If showing locking methods, show them and exit
+ */
+ else if (pcf_cmd_mode & PCF_SHOW_LOCKS) {
+ pcf_show_locks();
+ }
+
+ /*
+ * If showing master.cf entries, show them and exit
+ */
+ else if ((pcf_cmd_mode & (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM))
+ && !(pcf_cmd_mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT))) {
+ pcf_read_master(PCF_FAIL_ON_OPEN_ERROR);
+ pcf_read_parameters();
+ if (override_params)
+ pcf_set_parameters(override_params->argv);
+ pcf_register_builtin_parameters(basename(argv[0]), getpid());
+ pcf_register_service_parameters();
+ pcf_register_user_parameters();
+ if (pcf_cmd_mode & PCF_MASTER_FLD)
+ pcf_show_master_fields(VSTREAM_OUT, pcf_cmd_mode, argc - optind,
+ argv + optind);
+ else if (pcf_cmd_mode & PCF_MASTER_PARAM)
+ pcf_show_master_params(VSTREAM_OUT, pcf_cmd_mode, argc - optind,
+ argv + optind);
+ else
+ pcf_show_master_entries(VSTREAM_OUT, pcf_cmd_mode, argc - optind,
+ argv + optind);
+ pcf_flag_unused_master_parameters();
+ }
+
+ /*
+ * If showing SASL plug-in types, show them and exit
+ */
+ else if (pcf_cmd_mode & PCF_SHOW_SASL_SERV) {
+ pcf_show_sasl(PCF_SHOW_SASL_SERV);
+ } else if (pcf_cmd_mode & PCF_SHOW_SASL_CLNT) {
+ pcf_show_sasl(PCF_SHOW_SASL_CLNT);
+ }
+
+ /*
+ * Show TLS info and exit.
+ */
+ else if (pcf_cmd_mode & PCF_SHOW_TLS) {
+ pcf_show_tls(pcf_tls_arg);
+ }
+
+ /*
+ * Edit main.cf or master.cf.
+ */
+ else if (pcf_cmd_mode & (PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL)) {
+ if (optind == argc)
+ msg_fatal("missing service argument");
+ if (pcf_cmd_mode & (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)) {
+ pcf_edit_master(pcf_cmd_mode, argc - optind, argv + optind);
+ } else {
+ pcf_edit_main(pcf_cmd_mode, argc - optind, argv + optind);
+ }
+ }
+
+ /*
+ * If showing non-default values, read main.cf.
+ */
+ else {
+ if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) {
+ pcf_read_parameters();
+ if (override_params)
+ pcf_set_parameters(override_params->argv);
+ }
+ pcf_register_builtin_parameters(basename(argv[0]), getpid());
+
+ /*
+ * Add service-dependent parameters (service names from master.cf)
+ * and user-defined parameters ($name macros in parameter values in
+ * main.cf and master.cf, but only if those names have a name=value
+ * in main.cf or master.cf).
+ */
+ pcf_read_master(PCF_WARN_ON_OPEN_ERROR);
+ pcf_register_service_parameters();
+ if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0)
+ pcf_register_user_parameters();
+
+ /*
+ * Show the requested values.
+ */
+ pcf_show_parameters(VSTREAM_OUT, pcf_cmd_mode, param_class,
+ argv + optind);
+
+ /*
+ * Flag unused parameters. This makes no sense with "postconf -d",
+ * because that ignores all the user-specified parameters and
+ * user-specified macro expansions in main.cf.
+ */
+ if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) {
+ pcf_flag_unused_main_parameters();
+ pcf_flag_unused_master_parameters();
+ }
+ }
+ vstream_fflush(VSTREAM_OUT);
+ exit(0);
+}
diff --git a/src/postconf/postconf.h b/src/postconf/postconf.h
new file mode 100644
index 0000000..0f47648
--- /dev/null
+++ b/src/postconf/postconf.h
@@ -0,0 +1,327 @@
+/*++
+/* NAME
+/* postconf 3h
+/* SUMMARY
+/* module interfaces
+/* SYNOPSIS
+/* #include <postconf.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <unistd.h>
+
+ /*
+ * Utility library.
+ */
+#include <htable.h>
+#include <argv.h>
+#include <dict.h>
+#include <name_code.h>
+
+ /*
+ * What we're supposed to be doing.
+ */
+#define PCF_SHOW_NONDEF (1<<0) /* show main.cf non-default settings */
+#define PCF_SHOW_DEFS (1<<1) /* show main.cf default setting */
+#define PCF_HIDE_NAME (1<<2) /* hide main.cf parameter name */
+#define PCF_SHOW_MAPS (1<<3) /* show map types */
+#define PCF_EDIT_CONF (1<<4) /* edit main.cf or master.cf */
+#define PCF_SHOW_LOCKS (1<<5) /* show mailbox lock methods */
+#define PCF_SHOW_EVAL (1<<6) /* expand main.cf right-hand sides */
+#define PCF_SHOW_SASL_SERV (1<<7) /* show server auth plugin types */
+#define PCF_SHOW_SASL_CLNT (1<<8) /* show client auth plugin types */
+#define PCF_COMMENT_OUT (1<<9) /* #-out selected main.cf entries */
+#define PCF_MASTER_ENTRY (1<<10) /* manage master.cf entries */
+#define PCF_FOLD_LINE (1<<11) /* fold long *.cf entries */
+#define PCF_EDIT_EXCL (1<<12) /* exclude main.cf entries */
+#define PCF_MASTER_FLD (1<<13) /* hierarchical pathname */
+#define PCF_MAIN_PARAM (1<<14) /* manage main.cf entries */
+#define PCF_EXP_DSN_TEMPL (1<<15) /* expand bounce templates */
+#define PCF_PARAM_CLASS (1<<16) /* select parameter class */
+#define PCF_MAIN_OVER (1<<17) /* override parameter values */
+#define PCF_DUMP_DSN_TEMPL (1<<18) /* show bounce templates */
+#define PCF_MASTER_PARAM (1<<19) /* manage master.cf -o name=value */
+#define PCF_HIDE_VALUE (1<<20) /* hide main.cf/master.cf =value */
+#define PCF_SHOW_TLS (1<<21) /* TLS support introspection */
+
+#define PCF_DEF_MODE 0
+
+ /*
+ * Structure for one "valid parameter" (built-in, service-defined or valid
+ * user-defined). See the postconf_builtin, postconf_service and
+ * postconf_user modules for narrative text.
+ */
+typedef struct {
+ int flags; /* see below */
+ void *param_data; /* mostly, the default value */
+ const char *(*convert_fn) (void *); /* value to string */
+} PCF_PARAM_NODE;
+
+ /* Values for flags. See the postconf_node module for narrative text. */
+#define PCF_PARAM_FLAG_RAW (1<<0) /* raw parameter value */
+#define PCF_PARAM_FLAG_BUILTIN (1<<1) /* built-in parameter name */
+#define PCF_PARAM_FLAG_SERVICE (1<<2) /* service-defined parameter name */
+#define PCF_PARAM_FLAG_USER (1<<3) /* user-defined parameter name */
+#define PCF_PARAM_FLAG_LEGACY (1<<4) /* legacy parameter name */
+#define PCF_PARAM_FLAG_READONLY (1<<5) /* legacy parameter name */
+#define PCF_PARAM_FLAG_DBMS (1<<6) /* dbms-defined parameter name */
+
+#define PCF_PARAM_MASK_CLASS \
+ (PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_SERVICE | PCF_PARAM_FLAG_USER)
+#define PCF_PARAM_CLASS_OVERRIDE(node, class) \
+ ((node)->flags = (((node)->flags & ~PCF_PARAM_MASK_CLASS) | (class)))
+
+#define PCF_RAW_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_RAW)
+#define PCF_BUILTIN_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_BUILTIN)
+#define PCF_SERVICE_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_SERVICE)
+#define PCF_USER_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_USER)
+#define PCF_LEGACY_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_LEGACY)
+#define PCF_READONLY_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_READONLY)
+#define PCF_DBMS_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_DBMS)
+
+ /* Values for param_data. See postconf_node module for narrative text. */
+#define PCF_PARAM_NO_DATA ((char *) 0)
+
+ /*
+ * Lookup table for global "valid parameter" information.
+ */
+#define PCF_PARAM_TABLE HTABLE
+#define PCF_PARAM_INFO HTABLE_INFO
+
+extern PCF_PARAM_TABLE *pcf_param_table;
+
+ /*
+ * postconf_node.c.
+ */
+#define PCF_PARAM_TABLE_CREATE(size) htable_create(size);
+#define PCF_PARAM_NODE_CAST(ptr) ((PCF_PARAM_NODE *) (ptr))
+
+#define PCF_PARAM_TABLE_LIST(table) htable_list(table)
+#define PCF_PARAM_INFO_NAME(ht) ((const char *) (ht)->key)
+#define PCF_PARAM_INFO_NODE(ht) PCF_PARAM_NODE_CAST((ht)->value)
+
+#define PCF_PARAM_TABLE_FIND(table, name) \
+ PCF_PARAM_NODE_CAST(htable_find((table), (name)))
+#define PCF_PARAM_TABLE_LOCATE(table, name) htable_locate((table), (name))
+#define PCF_PARAM_TABLE_ENTER(table, name, flags, data, func) \
+ htable_enter((table), (name), (char *) pcf_make_param_node((flags), \
+ (data), (func)))
+
+extern PCF_PARAM_NODE *pcf_make_param_node(int, void *, const char *(*) (void *));
+extern const char *pcf_convert_param_node(int, const char *, PCF_PARAM_NODE *);
+extern VSTRING *pcf_param_string_buf;
+
+ /*
+ * Structure of one master.cf entry.
+ */
+typedef struct {
+ char *name_space; /* service/type, parameter name space */
+ ARGV *argv; /* null, or master.cf fields */
+ DICT *all_params; /* null, or all name=value entries */
+ DICT *ro_params; /* read-only name=value entries */
+ HTABLE *valid_names; /* null, or "valid" parameter names */
+} PCF_MASTER_ENT;
+
+#define PCF_MASTER_MIN_FIELDS 8 /* mandatory field minimum */
+
+#define PCF_MASTER_NAME_SERVICE "service"
+#define PCF_MASTER_NAME_TYPE "type"
+#define PCF_MASTER_NAME_PRIVATE "private"
+#define PCF_MASTER_NAME_UNPRIV "unprivileged"
+#define PCF_MASTER_NAME_CHROOT "chroot"
+#define PCF_MASTER_NAME_WAKEUP "wakeup"
+#define PCF_MASTER_NAME_MAXPROC "process_limit"
+#define PCF_MASTER_NAME_CMD "command"
+
+#define PCF_MASTER_FLD_SERVICE 0 /* service name */
+#define PCF_MASTER_FLD_TYPE 1 /* service type */
+#define PCF_MASTER_FLD_PRIVATE 2 /* private service */
+#define PCF_MASTER_FLD_UNPRIV 3 /* unprivileged service */
+#define PCF_MASTER_FLD_CHROOT 4 /* chrooted service */
+#define PCF_MASTER_FLD_WAKEUP 5 /* wakeup timer */
+#define PCF_MASTER_FLD_MAXPROC 6 /* process limit */
+#define PCF_MASTER_FLD_CMD 7 /* command */
+
+#define PCF_MASTER_FLD_WILDC -1 /* wild-card */
+#define PCF_MASTER_FLD_NONE -2 /* not available */
+
+ /*
+ * Lookup table for master.cf entries. The table is terminated with an entry
+ * that has a null argv member.
+ */
+PCF_MASTER_ENT *pcf_master_table;
+
+ /*
+ * Line-wrapping support.
+ */
+#define PCF_LINE_LIMIT 80 /* try to fold longer lines */
+#define PCF_SEPARATORS " \t\r\n"
+#define PCF_INDENT_LEN 4 /* indent long text by 4 */
+#define PCF_INDENT_TEXT " "
+
+ /*
+ * XXX Global so that postconf_builtin.c call-backs can see it.
+ */
+extern int pcf_cmd_mode;
+
+ /*
+ * postconf_misc.c.
+ */
+extern void pcf_set_config_dir(void);
+
+ /*
+ * postconf_main.c
+ */
+extern void pcf_read_parameters(void);
+extern void pcf_set_parameters(char **);
+extern void pcf_show_parameters(VSTREAM *, int, int, char **);
+
+ /*
+ * postconf_edit.c
+ */
+extern void pcf_edit_main(int, int, char **);
+extern void pcf_edit_master(int, int, char **);
+
+ /*
+ * postconf_master.c.
+ */
+extern const char pcf_daemon_options_expecting_value[];
+extern void pcf_read_master(int);
+extern void pcf_show_master_entries(VSTREAM *, int, int, char **);
+extern const char *pcf_parse_master_entry(PCF_MASTER_ENT *, const char *);
+extern void pcf_print_master_entry(VSTREAM *, int, PCF_MASTER_ENT *);
+extern void pcf_free_master_entry(PCF_MASTER_ENT *);
+extern void pcf_show_master_fields(VSTREAM *, int, int, char **);
+extern void pcf_edit_master_field(PCF_MASTER_ENT *, int, const char *);
+extern void pcf_show_master_params(VSTREAM *, int, int, char **);
+extern void pcf_edit_master_param(PCF_MASTER_ENT *, int, const char *, const char *);
+
+#define PCF_WARN_ON_OPEN_ERROR 0
+#define PCF_FAIL_ON_OPEN_ERROR 1
+
+#define PCF_MASTER_BLANKS " \t\r\n" /* XXX */
+
+ /*
+ * Master.cf parameter namespace management. The idea is to manage master.cf
+ * "-o name=value" settings with other tools than text editors.
+ *
+ * The natural choice is to use "service-name.service-type.parameter-name", but
+ * unfortunately the '.' may appear in service and parameter names.
+ *
+ * For example, a spawn(8) listener can have a service name 127.0.0.1:10028.
+ * This service name becomes part of a service-dependent parameter name
+ * "127.0.0.1:10028_time_limit". All those '.' characters mean we can't use
+ * '.' as the parameter namespace delimiter.
+ *
+ * (We could require that such service names are specified as $foo:port with
+ * the value of "foo" defined in main.cf or at the top of master.cf.)
+ *
+ * But it is easier if we use '/' instead.
+ */
+#define PCF_NAMESP_SEP_CH '/'
+#define PCF_NAMESP_SEP_STR "/"
+
+#define PCF_LEGACY_SEP_CH '.'
+
+ /*
+ * postconf_match.c.
+ */
+#define PCF_MATCH_WILDC_STR "*"
+#define PCF_MATCH_ANY(p) ((p)[0] == PCF_MATCH_WILDC_STR[0] && (p)[1] == 0)
+#define PCF_MATCH_STRING(p, s) (PCF_MATCH_ANY(p) || strcmp((p), (s)) == 0)
+
+extern ARGV *pcf_parse_service_pattern(const char *, int, int);
+extern int pcf_parse_field_pattern(const char *);
+
+#define PCF_IS_MAGIC_SERVICE_PATTERN(pat) \
+ (PCF_MATCH_ANY((pat)->argv[0]) || PCF_MATCH_ANY((pat)->argv[1]))
+#define PCF_MATCH_SERVICE_PATTERN(pat, name, type) \
+ (PCF_MATCH_STRING((pat)->argv[0], (name)) \
+ && PCF_MATCH_STRING((pat)->argv[1], (type)))
+
+#define pcf_is_magic_field_pattern(pat) ((pat) == PCF_MASTER_FLD_WILDC)
+#define pcf_str_field_pattern(pat) ((const char *) (pcf_field_name_offset[pat].name))
+
+#define PCF_IS_MAGIC_PARAM_PATTERN(pat) PCF_MATCH_ANY(pat)
+#define PCF_MATCH_PARAM_PATTERN(pat, name) PCF_MATCH_STRING((pat), (name))
+
+/* The following is not part of the postconf_match API. */
+extern NAME_CODE pcf_field_name_offset[];
+
+ /*
+ * postconf_builtin.c.
+ */
+extern void pcf_register_builtin_parameters(const char *, pid_t);
+
+ /*
+ * postconf_service.c.
+ */
+extern void pcf_register_service_parameters(void);
+
+ /*
+ * Parameter context structure.
+ */
+typedef struct {
+ PCF_MASTER_ENT *local_scope;
+ int param_class;
+} PCF_PARAM_CTX;
+
+ /*
+ * postconf_user.c.
+ */
+extern void pcf_register_user_parameters(void);
+
+ /*
+ * postconf_dbms.c
+ */
+extern void pcf_register_dbms_parameters(const char *,
+ const char *(*) (const char *, int, PCF_MASTER_ENT *),
+ PCF_MASTER_ENT *);
+
+ /*
+ * postconf_lookup.c.
+ */
+extern const char *pcf_lookup_parameter_value(int, const char *,
+ PCF_MASTER_ENT *,
+ PCF_PARAM_NODE *);
+
+extern char *pcf_expand_parameter_value(VSTRING *, int, const char *,
+ PCF_MASTER_ENT *);
+
+ /*
+ * postconf_print.c.
+ */
+extern void PRINTFLIKE(3, 4) pcf_print_line(VSTREAM *, int, const char *,...);
+
+ /*
+ * postconf_unused.c.
+ */
+extern void pcf_flag_unused_main_parameters(void);
+extern void pcf_flag_unused_master_parameters(void);
+
+ /*
+ * postconf_other.c.
+ */
+extern void pcf_show_maps(void);
+extern void pcf_show_locks(void);
+extern void pcf_show_sasl(int);
+extern void pcf_show_tls(const char *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
diff --git a/src/postconf/postconf_builtin.c b/src/postconf/postconf_builtin.c
new file mode 100644
index 0000000..e1a55fa
--- /dev/null
+++ b/src/postconf/postconf_builtin.c
@@ -0,0 +1,461 @@
+/*++
+/* NAME
+/* postconf_builtin 3
+/* SUMMARY
+/* built-in main.cf parameter support
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_register_builtin_parameters(procname, pid)
+/* const char *procname;
+/* pid_t pid;
+/* DESCRIPTION
+/* pcf_register_builtin_parameters() initializes the global
+/* main.cf parameter name space and adds all built-in parameter
+/* information.
+/*
+/* Arguments:
+/*.IP procname
+/* Provides the default value for the "process_name" parameter.
+/*.IP pid
+/* Provides the default value for the "process_id" parameter.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <get_hostname.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mynetworks.h>
+#include <mail_conf.h>
+#include <mail_params.h>
+#include <mail_version.h>
+#include <mail_proto.h>
+#include <mail_addr.h>
+#include <inet_proto.h>
+#include <server_acl.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+ * Support for built-in parameters: declarations generated by scanning
+ * actual C source files.
+ */
+#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 "long_vars.h"
+
+ /*
+ * Support for built-in parameters: manually extracted.
+ */
+#include "install_vars.h"
+
+ /*
+ * Support for built-in parameters: lookup tables generated by scanning
+ * actual C source files.
+ */
+static const CONFIG_TIME_TABLE pcf_time_table[] = {
+#include "time_table.h"
+ 0,
+};
+
+static const CONFIG_BOOL_TABLE pcf_bool_table[] = {
+#include "bool_table.h"
+ 0,
+};
+
+static const CONFIG_INT_TABLE pcf_int_table[] = {
+#include "int_table.h"
+ 0,
+};
+
+static const CONFIG_STR_TABLE pcf_str_table[] = {
+#include "str_table.h"
+#include "install_table.h"
+ 0,
+};
+
+static const CONFIG_RAW_TABLE pcf_raw_table[] = {
+#include "raw_table.h"
+ 0,
+};
+
+static const CONFIG_NINT_TABLE pcf_nint_table[] = {
+#include "nint_table.h"
+ 0,
+};
+
+static const CONFIG_NBOOL_TABLE pcf_nbool_table[] = {
+#include "nbool_table.h"
+ 0,
+};
+
+static const CONFIG_LONG_TABLE pcf_long_table[] = {
+#include "long_table.h"
+ 0,
+};
+
+ /*
+ * Legacy parameters for backwards compatibility.
+ */
+static const CONFIG_STR_TABLE pcf_legacy_str_table[] = {
+ {"virtual_maps", ""},
+ {"fallback_relay", ""},
+ {"authorized_verp_clients", ""},
+ {"smtpd_client_connection_limit_exceptions", ""},
+ {"postscreen_dnsbl_ttl", ""},
+ 0,
+};
+
+ /*
+ * Parameters whose default values are normally initialized by calling a
+ * function. We direct the calls to our own versions of those functions
+ * because the run-time conditions are slightly different.
+ *
+ * Important: if the evaluation of a parameter default value has any side
+ * effects, then those side effects must happen only once.
+ */
+static const char *pcf_check_myhostname(void);
+static const char *pcf_check_mydomainname(void);
+static const char *pcf_mynetworks(void);
+
+#include "str_fn_vars.h"
+
+static const CONFIG_STR_FN_TABLE pcf_str_fn_table[] = {
+#include "str_fn_table.h"
+ 0,
+};
+
+ /*
+ * Parameters whose default values are normally initialized by ad-hoc code.
+ * The AWK script cannot identify these parameters or values, so we provide
+ * our own.
+ *
+ * Important: if the evaluation of a parameter default value has any side
+ * effects, then those side effects must happen only once.
+ */
+static CONFIG_STR_TABLE pcf_adhoc_procname = {VAR_PROCNAME};
+static CONFIG_STR_TABLE pcf_adhoc_servname = {VAR_SERVNAME};
+static CONFIG_INT_TABLE pcf_adhoc_pid = {VAR_PID};
+
+#define STR(x) vstring_str(x)
+
+/* pcf_check_myhostname - lookup hostname and validate */
+
+static const char *pcf_check_myhostname(void)
+{
+ static const char *name;
+ const char *dot;
+ const char *domain;
+
+ /*
+ * Use cached result.
+ */
+ if (name)
+ return (name);
+
+ /*
+ * If the local machine name is not in FQDN form, try to append the
+ * contents of $mydomain.
+ */
+ 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);
+}
+
+/* pcf_get_myhostname - look up and store my hostname */
+
+static void pcf_get_myhostname(void)
+{
+ const char *name;
+
+ if ((name = mail_conf_lookup_eval(VAR_MYHOSTNAME)) == 0)
+ name = pcf_check_myhostname();
+ var_myhostname = mystrdup(name);
+}
+
+/* pcf_check_mydomainname - lookup domain name and validate */
+
+static const char *pcf_check_mydomainname(void)
+{
+ static const char *domain;
+ char *dot;
+
+ /*
+ * Use cached result.
+ */
+ if (domain)
+ return (domain);
+
+ /*
+ * Use a default domain when the hostname is not a FQDN ("foo").
+ */
+ if (var_myhostname == 0)
+ pcf_get_myhostname();
+ if ((dot = strchr(var_myhostname, '.')) == 0)
+ return (domain = DEF_MYDOMAIN);
+ return (domain = mystrdup(dot + 1));
+}
+
+/* pcf_mynetworks - lookup network address list */
+
+static const char *pcf_mynetworks(void)
+{
+ static const char *networks;
+ VSTRING *exp_buf;
+ const char *junk;
+
+ /*
+ * Use cached result.
+ */
+ if (networks)
+ return (networks);
+
+ exp_buf = vstring_alloc(100);
+
+ if (var_inet_interfaces == 0) {
+ if ((pcf_cmd_mode & PCF_SHOW_DEFS)
+ || (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0)
+ junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
+ DEF_INET_INTERFACES,
+ (PCF_MASTER_ENT *) 0);
+ var_inet_interfaces = mystrdup(junk);
+ }
+ if (var_mynetworks_style == 0) {
+ if ((pcf_cmd_mode & PCF_SHOW_DEFS)
+ || (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0)
+ junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
+ DEF_MYNETWORKS_STYLE,
+ (PCF_MASTER_ENT *) 0);
+ var_mynetworks_style = mystrdup(junk);
+ }
+ if (var_inet_protocols == 0) {
+ if ((pcf_cmd_mode & PCF_SHOW_DEFS)
+ || (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0)
+ junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
+ DEF_INET_PROTOCOLS,
+ (PCF_MASTER_ENT *) 0);
+ var_inet_protocols = mystrdup(junk);
+ (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);
+ }
+ vstring_free(exp_buf);
+ return (networks = mystrdup(mynetworks()));
+}
+
+/* pcf_conv_bool_parameter - get boolean parameter string value */
+
+static const char *pcf_conv_bool_parameter(void *ptr)
+{
+ CONFIG_BOOL_TABLE *cbt = (CONFIG_BOOL_TABLE *) ptr;
+
+ return (cbt->defval ? "yes" : "no");
+}
+
+/* pcf_conv_time_parameter - get relative time parameter string value */
+
+static const char *pcf_conv_time_parameter(void *ptr)
+{
+ CONFIG_TIME_TABLE *ctt = (CONFIG_TIME_TABLE *) ptr;
+
+ return (ctt->defval);
+}
+
+/* pcf_conv_int_parameter - get integer parameter string value */
+
+static const char *pcf_conv_int_parameter(void *ptr)
+{
+ CONFIG_INT_TABLE *cit = (CONFIG_INT_TABLE *) ptr;
+
+ return (STR(vstring_sprintf(pcf_param_string_buf, "%d", cit->defval)));
+}
+
+/* pcf_conv_str_parameter - get string parameter string value */
+
+static const char *pcf_conv_str_parameter(void *ptr)
+{
+ CONFIG_STR_TABLE *cst = (CONFIG_STR_TABLE *) ptr;
+
+ return (cst->defval);
+}
+
+/* pcf_conv_str_fn_parameter - get string-function parameter string value */
+
+static const char *pcf_conv_str_fn_parameter(void *ptr)
+{
+ CONFIG_STR_FN_TABLE *cft = (CONFIG_STR_FN_TABLE *) ptr;
+
+ return (cft->defval());
+}
+
+/* pcf_conv_raw_parameter - get raw string parameter string value */
+
+static const char *pcf_conv_raw_parameter(void *ptr)
+{
+ CONFIG_RAW_TABLE *rst = (CONFIG_RAW_TABLE *) ptr;
+
+ return (rst->defval);
+}
+
+/* pcf_conv_nint_parameter - get new integer parameter string value */
+
+static const char *pcf_conv_nint_parameter(void *ptr)
+{
+ CONFIG_NINT_TABLE *rst = (CONFIG_NINT_TABLE *) ptr;
+
+ return (rst->defval);
+}
+
+/* pcf_conv_nbool_parameter - get new boolean parameter string value */
+
+static const char *pcf_conv_nbool_parameter(void *ptr)
+{
+ CONFIG_NBOOL_TABLE *bst = (CONFIG_NBOOL_TABLE *) ptr;
+
+ return (bst->defval);
+}
+
+/* pcf_conv_long_parameter - get long parameter string value */
+
+static const char *pcf_conv_long_parameter(void *ptr)
+{
+ CONFIG_LONG_TABLE *clt = (CONFIG_LONG_TABLE *) ptr;
+
+ return (STR(vstring_sprintf(pcf_param_string_buf, "%ld", clt->defval)));
+}
+
+/* pcf_register_builtin_parameters - add built-ins to the global name space */
+
+void pcf_register_builtin_parameters(const char *procname, pid_t pid)
+{
+ const char *myname = "pcf_register_builtin_parameters";
+ 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 *cft;
+ const CONFIG_RAW_TABLE *rst;
+ const CONFIG_NINT_TABLE *nst;
+ const CONFIG_NBOOL_TABLE *bst;
+ const CONFIG_LONG_TABLE *lst;
+
+ /*
+ * Sanity checks.
+ */
+ if (pcf_param_table != 0)
+ msg_panic("%s: global parameter table is already initialized", myname);
+
+ /*
+ * Initialize the global parameter table.
+ */
+ pcf_param_table = PCF_PARAM_TABLE_CREATE(1000);
+
+ /*
+ * Add the built-in parameters to the global name space. The class
+ * (built-in) is tentative; some parameters are actually service-defined,
+ * but they have their own default value.
+ */
+ for (ctt = pcf_time_table; ctt->name; ctt++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, ctt->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) ctt,
+ pcf_conv_time_parameter);
+ for (cbt = pcf_bool_table; cbt->name; cbt++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, cbt->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) cbt,
+ pcf_conv_bool_parameter);
+ for (cit = pcf_int_table; cit->name; cit++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, cit->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) cit,
+ pcf_conv_int_parameter);
+ for (cst = pcf_str_table; cst->name; cst++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, cst->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) cst,
+ pcf_conv_str_parameter);
+ for (cft = pcf_str_fn_table; cft->name; cft++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, cft->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) cft,
+ pcf_conv_str_fn_parameter);
+ for (rst = pcf_raw_table; rst->name; rst++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, rst->name,
+ PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_RAW,
+ (void *) rst, pcf_conv_raw_parameter);
+ for (nst = pcf_nint_table; nst->name; nst++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, nst->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) nst,
+ pcf_conv_nint_parameter);
+ for (bst = pcf_nbool_table; bst->name; bst++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, bst->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) bst,
+ pcf_conv_nbool_parameter);
+ for (lst = pcf_long_table; lst->name; lst++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, lst->name,
+ PCF_PARAM_FLAG_BUILTIN, (void *) lst,
+ pcf_conv_long_parameter);
+
+ /*
+ * Register legacy parameters (used as a backwards-compatible migration
+ * aid).
+ */
+ for (cst = pcf_legacy_str_table; cst->name; cst++)
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, cst->name,
+ PCF_PARAM_FLAG_LEGACY, (void *) cst,
+ pcf_conv_str_parameter);
+
+ /*
+ * Register parameters whose default value is normally initialized by
+ * ad-hoc code.
+ */
+ pcf_adhoc_procname.defval = mystrdup(procname);
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_procname.name,
+ PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY,
+ (void *) &pcf_adhoc_procname, pcf_conv_str_parameter);
+ pcf_adhoc_servname.defval = mystrdup("");
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_servname.name,
+ PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY,
+ (void *) &pcf_adhoc_servname, pcf_conv_str_parameter);
+ pcf_adhoc_pid.defval = pid;
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_pid.name,
+ PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY,
+ (void *) &pcf_adhoc_pid, pcf_conv_int_parameter);
+}
diff --git a/src/postconf/postconf_dbms.c b/src/postconf/postconf_dbms.c
new file mode 100644
index 0000000..eddeab0
--- /dev/null
+++ b/src/postconf/postconf_dbms.c
@@ -0,0 +1,325 @@
+/*++
+/* NAME
+/* postconf_dbms 3
+/* SUMMARY
+/* legacy support for database-defined main.cf parameter names
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_register_dbms_parameters(param_value, flag_parameter,
+/* local_scope)
+/* const char *param_value;
+/* const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *);
+/* PCF_MASTER_ENT *local_scope;
+/* DESCRIPTION
+/* This module implements legacy support for database configuration
+/* where main.cf parameter names are generated by prepending
+/* the database name to a database-defined suffix.
+/*
+/* Arguments:
+/* .IP param_value
+/* A parameter value to be searched for "type:table" strings.
+/* When a database type is found that supports legacy-style
+/* configuration, the table name is combined with each of the
+/* database-defined suffixes to generate candidate parameter
+/* names for that database type; if the table name specifies
+/* a client configuration file, that file is scanned for unused
+/* parameter settings.
+/* .IP flag_parameter
+/* A function that takes as arguments a candidate parameter
+/* name, parameter flags, and a PCF_MASTER_ENT pointer. The
+/* function will flag the parameter as "used" if it has a
+/* "name=value" entry in the local or global namespace.
+/* .IP local_scope
+/* The local namespace.
+/* DIAGNOSTICS
+/* No explicit diagnostics.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <stringops.h>
+#include <split_at.h>
+#include <mac_expand.h>
+#include <dict.h>
+#include <msg.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_conf.h>
+#include <mail_params.h>
+#include <dict_ht.h>
+#include <dict_proxy.h>
+#include <dict_ldap.h>
+#include <dict_mysql.h>
+#include <dict_pgsql.h>
+#include <dict_sqlite.h>
+#include <dict_memcache.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+ * SLMs.
+ */
+#define STR(x) vstring_str(x)
+
+#ifdef LEGACY_DBMS_SUPPORT
+
+ /*
+ * The legacy database interface automagically instantiates a list of
+ * parameters by prepending the table name to database-specific suffixes.
+ */
+
+/* See ldap_table(5). */
+
+static const char *pcf_ldap_suffixes[] = {
+#include "pcf_ldap_suffixes.h"
+ 0,
+};
+
+/* See mysql_table(5). */
+
+static const char *pcf_mysql_suffixes[] = {
+#include "pcf_mysql_suffixes.h"
+ 0,
+};
+
+/* See pgsql_table(5). */
+
+static const char *pcf_pgsql_suffixes[] = {
+#include "pcf_pgsql_suffixes.h"
+ 0,
+};
+
+/* See sqlite_table(5). */
+
+static const char *pcf_sqlite_suffixes[] = {
+#include "pcf_sqlite_suffixes.h"
+ 0,
+};
+
+/* See memcache_table(5). */
+
+static const char *pcf_memcache_suffixes[] = {
+#include "pcf_memcache_suffixes.h"
+ 0,
+};
+
+ /*
+ * Bundle up the database types and their suffix lists.
+ */
+typedef struct {
+ const char *db_type;
+ const char **db_suffixes;
+} PCF_DBMS_INFO;
+
+static const PCF_DBMS_INFO pcf_dbms_info[] = {
+ DICT_TYPE_LDAP, pcf_ldap_suffixes,
+ DICT_TYPE_MYSQL, pcf_mysql_suffixes,
+ DICT_TYPE_PGSQL, pcf_pgsql_suffixes,
+ DICT_TYPE_SQLITE, pcf_sqlite_suffixes,
+ DICT_TYPE_MEMCACHE, pcf_memcache_suffixes,
+ 0,
+};
+
+/* pcf_check_dbms_client - look for unused names in client configuration */
+
+static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file)
+{
+ DICT *dict;
+ VSTREAM *fp;
+ const char **cpp;
+ const char *name;
+ const char *value;
+ char *dict_spec;
+ int dir;
+
+ /*
+ * We read each database client configuration file into its own
+ * dictionary, and nag only the first time that a file is visited.
+ */
+ dict_spec = concatenate(dp->db_type, ":", cf_file, (char *) 0);
+ if ((dict = dict_handle(dict_spec)) == 0) {
+ struct stat st;
+
+ /*
+ * Populate the dictionary with settings in this database client
+ * configuration file. Don't die if a file can't be opened - some
+ * files may contain passwords and should not be world-readable.
+ * Note: dict_load_fp() nags about duplicate pameter settings.
+ */
+ dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0);
+ dict_register(dict_spec, dict);
+ if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0) {
+ if (errno != EACCES)
+ msg_warn("open \"%s\" configuration \"%s\": %m",
+ dp->db_type, cf_file);
+ myfree(dict_spec);
+ return;
+ }
+ if (fstat(vstream_fileno(fp), &st) == 0 && !S_ISREG(st.st_mode)) {
+ msg_warn("open \"%s\" configuration \"%s\": not a regular file",
+ dp->db_type, cf_file);
+ myfree(dict_spec);
+ (void) vstream_fclose(fp);
+ return;
+ }
+ dict_load_fp(dict_spec, fp);
+ if (vstream_fclose(fp)) {
+ msg_warn("read \"%s\" configuration \"%s\": %m",
+ dp->db_type, cf_file);
+ myfree(dict_spec);
+ return;
+ }
+
+ /*
+ * Remove all known database client parameters from this dictionary,
+ * then report the remaining ones as "unused". We use ad-hoc logging
+ * code, because a database client parameter namespace is unlike the
+ * parameter namespaces in main.cf or master.cf.
+ */
+ for (cpp = dp->db_suffixes; *cpp; cpp++)
+ (void) dict_del(dict, *cpp);
+ for (dir = DICT_SEQ_FUN_FIRST;
+ dict->sequence(dict, dir, &name, &value) == DICT_STAT_SUCCESS;
+ dir = DICT_SEQ_FUN_NEXT)
+ msg_warn("%s: unused parameter: %s=%s", dict_spec, name, value);
+ }
+ myfree(dict_spec);
+}
+
+/* pcf_register_dbms_helper - parse one possible database type:name */
+
+static void pcf_register_dbms_helper(char *str_value,
+ const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
+ PCF_MASTER_ENT *local_scope)
+{
+ const PCF_DBMS_INFO *dp;
+ char *db_type;
+ char *prefix;
+ static VSTRING *candidate = 0;
+ const char **cpp;
+ char *err;
+
+ /*
+ * Naive parsing. We don't really know if this substring specifies a
+ * database or some other text.
+ */
+ while ((db_type = mystrtokq(&str_value, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
+
+ /*
+ * Skip over "proxy:" maptypes, to emulate the proxymap(8) server's
+ * behavior when opening a local database configuration file.
+ */
+ while ((prefix = split_at(db_type, ':')) != 0
+ && strcmp(db_type, DICT_TYPE_PROXY) == 0)
+ db_type = prefix;
+
+ if (prefix == 0)
+ continue;
+
+ /*
+ * Look for database:prefix where the prefix is an absolute pathname.
+ * Then, report unknown database client configuration parameters.
+ *
+ * XXX What about a pathname beginning with '.'? This supposedly is
+ * relative to the queue directory, which is the default directory
+ * for all Postfix daemon processes. This would also have to handle
+ * the case that the queue is not yet created.
+ */
+ if (*prefix == '/') {
+ for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
+ if (strcmp(db_type, dp->db_type) == 0) {
+ pcf_check_dbms_client(dp, prefix);
+ break;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * Look for database:prefix where the prefix is not a pathname and
+ * the database is a known type. Synthesize candidate parameter names
+ * from the user-defined prefix and from the database-defined suffix
+ * list, and see if those parameters have a "name=value" entry in the
+ * local or global namespace.
+ */
+ if (*prefix != '.') {
+ if (*prefix == CHARS_BRACE[0]) {
+ if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) {
+ /* XXX Encapsulate this in pcf_warn() function. */
+ if (local_scope)
+ msg_warn("%s:%s: %s",
+ MASTER_CONF_FILE, local_scope->name_space,
+ err);
+ else
+ msg_warn("%s: %s", MAIN_CONF_FILE, err);
+ myfree(err);
+ }
+ pcf_register_dbms_helper(prefix, flag_parameter,
+ local_scope);
+ } else {
+ for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
+ if (strcmp(db_type, dp->db_type) == 0) {
+ for (cpp = dp->db_suffixes; *cpp; cpp++) {
+ vstring_sprintf(candidate ? candidate :
+ (candidate = vstring_alloc(30)),
+ "%s_%s", prefix, *cpp);
+ flag_parameter(STR(candidate),
+ PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER,
+ local_scope);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* pcf_register_dbms_parameters - look for database_type:prefix_name */
+
+void pcf_register_dbms_parameters(const char *param_value,
+ const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
+ PCF_MASTER_ENT *local_scope)
+{
+ char *bufp;
+ static VSTRING *buffer = 0;
+
+ /*
+ * XXX This does not examine both sides of conditional macro expansion,
+ * and may expand the "wrong" conditional macros. This is the best we can
+ * do for legacy database configuration support.
+ */
+ if (buffer == 0)
+ buffer = vstring_alloc(100);
+ bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value,
+ local_scope);
+ pcf_register_dbms_helper(bufp, flag_parameter, local_scope);
+}
+
+#endif
diff --git a/src/postconf/postconf_edit.c b/src/postconf/postconf_edit.c
new file mode 100644
index 0000000..c60cb06
--- /dev/null
+++ b/src/postconf/postconf_edit.c
@@ -0,0 +1,578 @@
+/*++
+/* NAME
+/* postconf_edit 3
+/* SUMMARY
+/* edit main.cf or master.cf
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_edit_main(mode, argc, argv)
+/* int mode;
+/* int argc;
+/* char **argv;
+/*
+/* void pcf_edit_master(mode, argc, argv)
+/* int mode;
+/* int argc;
+/* char **argv;
+/* DESCRIPTION
+/* pcf_edit_main() edits the \fBmain.cf\fR configuration file.
+/* It replaces or adds parameter settings given as "\fIname=value\fR"
+/* pairs given on the command line, or removes parameter
+/* settings given as "\fIname\fR" on the command line. The
+/* file is copied to a temporary file then renamed into place.
+/*
+/* pcf_edit_master() edits the \fBmaster.cf\fR configuration
+/* file. The file is copied to a temporary file then renamed
+/* into place. Depending on the flags in \fBmode\fR:
+/* .IP PCF_MASTER_ENTRY
+/* With PCF_EDIT_CONF, pcf_edit_master() replaces or adds
+/* entire master.cf entries, specified on the command line as
+/* "\fIname/type = name type private unprivileged chroot wakeup
+/* process_limit command...\fR".
+/*
+/* With PCF_EDIT_EXCL or PCF_COMMENT_OUT, pcf_edit_master()
+/* removes or comments out entries specified on the command
+/* line as "\fIname/type\fR.
+/* .IP PCF_MASTER_FLD
+/* With PCF_EDIT_CONF, pcf_edit_master() replaces the value
+/* of specific service attributes, specified on the command
+/* line as "\fIname/type/attribute = value\fR".
+/* .IP PCF_MASTER_PARAM
+/* With PCF_EDIT_CONF, pcf_edit_master() replaces or adds the
+/* value of service parameters, specified on the command line
+/* as "\fIname/type/parameter = value\fR".
+/*
+/* With PCF_EDIT_EXCL, pcf_edit_master() removes service
+/* parameters specified on the command line as "\fIparametername\fR".
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* FILES
+/* /etc/postfix/main.cf, Postfix configuration parameters
+/* /etc/postfix/main.cf.tmp, temporary name
+/* /etc/postfix/master.cf, Postfix configuration parameters
+/* /etc/postfix/master.cf.tmp, temporary name
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <edit_file.h>
+#include <readlline.h>
+#include <stringops.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+#define STR(x) vstring_str(x)
+
+/* pcf_find_cf_info - pass-through non-content line, return content or null */
+
+static char *pcf_find_cf_info(VSTRING *buf, VSTREAM *dst)
+{
+ char *cp;
+
+ for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
+ /* void */ ;
+ /* Pass-through comment, all-whitespace, or empty line. */
+ if (*cp == '#' || *cp == 0) {
+ vstream_fputs(STR(buf), dst);
+ return (0);
+ } else {
+ return (cp);
+ }
+}
+
+/* pcf_next_cf_line - return next content line, pass non-content */
+
+static char *pcf_next_cf_line(VSTRING *buf, VSTREAM *src, VSTREAM *dst, int *lineno)
+{
+ char *cp;
+
+ while (vstring_get(buf, src) != VSTREAM_EOF) {
+ if (lineno)
+ *lineno += 1;
+ if ((cp = pcf_find_cf_info(buf, dst)) != 0)
+ return (cp);
+ }
+ return (0);
+}
+
+/* pcf_gobble_cf_line - accumulate multi-line content, pass non-content */
+
+static void pcf_gobble_cf_line(VSTRING *full_entry_buf, VSTRING *line_buf,
+ VSTREAM *src, VSTREAM *dst, int *lineno)
+{
+ int ch;
+
+ vstring_strcpy(full_entry_buf, STR(line_buf));
+ for (;;) {
+ if ((ch = VSTREAM_GETC(src)) != VSTREAM_EOF)
+ vstream_ungetc(src, ch);
+ if ((ch != '#' && !ISSPACE(ch))
+ || vstring_get(line_buf, src) == VSTREAM_EOF)
+ break;
+ lineno += 1;
+ if (pcf_find_cf_info(line_buf, dst))
+ vstring_strcat(full_entry_buf, STR(line_buf));
+ }
+}
+
+/* pcf_edit_main - edit main.cf file */
+
+void pcf_edit_main(int mode, int argc, char **argv)
+{
+ char *path;
+ EDIT_FILE *ep;
+ VSTREAM *src;
+ VSTREAM *dst;
+ VSTRING *buf = vstring_alloc(100);
+ VSTRING *key = vstring_alloc(10);
+ char *cp;
+ char *pattern;
+ char *edit_value;
+ HTABLE *table;
+ struct cvalue {
+ char *value;
+ int found;
+ };
+ struct cvalue *cvalue;
+ HTABLE_INFO **ht_info;
+ HTABLE_INFO **ht;
+ int interesting;
+ const char *err;
+
+ /*
+ * Store command-line parameters for quick lookup.
+ */
+ table = htable_create(argc);
+ while ((cp = *argv++) != 0) {
+ if (strchr(cp, '\n') != 0)
+ msg_fatal("-e, -X, or -# accepts no multi-line input");
+ while (ISSPACE(*cp))
+ cp++;
+ if (*cp == '#')
+ msg_fatal("-e, -X, or -# accepts no comment input");
+ if (mode & PCF_EDIT_CONF) {
+ if ((err = split_nameval(cp, &pattern, &edit_value)) != 0)
+ msg_fatal("%s: \"%s\"", err, cp);
+ } else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) {
+ if (*cp == 0)
+ msg_fatal("-X or -# requires non-blank parameter names");
+ if (strchr(cp, '=') != 0)
+ msg_fatal("-X or -# requires parameter names without value");
+ pattern = cp;
+ trimblanks(pattern, 0);
+ edit_value = 0;
+ } else {
+ msg_panic("pcf_edit_main: unknown mode %d", mode);
+ }
+ cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
+ cvalue->value = edit_value;
+ cvalue->found = 0;
+ htable_enter(table, pattern, (void *) cvalue);
+ }
+
+ /*
+ * Open a temp file for the result. This uses a deterministic name so we
+ * don't leave behind thrash with random names.
+ */
+ pcf_set_config_dir();
+ path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (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;
+
+ /*
+ * Open the original file for input.
+ */
+ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
+ /* OK to delete, since we control the temp file name exclusively. */
+ (void) unlink(ep->tmp_path);
+ msg_fatal("open %s for reading: %m", path);
+ }
+
+ /*
+ * Copy original file to temp file, while replacing parameters on the
+ * fly. Issue warnings for names found multiple times.
+ */
+#define STR(x) vstring_str(x)
+
+ interesting = 0;
+ while ((cp = pcf_next_cf_line(buf, src, dst, (int *) 0)) != 0) {
+ /* Copy, skip or replace continued text. */
+ if (cp > STR(buf)) {
+ if (interesting == 0)
+ vstream_fputs(STR(buf), dst);
+ else if (mode & PCF_COMMENT_OUT)
+ vstream_fprintf(dst, "#%s", STR(buf));
+ }
+ /* Copy or replace start of logical line. */
+ else {
+ vstring_strncpy(key, cp, strcspn(cp, CHARS_SPACE "="));
+ 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 (mode & PCF_EDIT_CONF)
+ vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
+ else if (mode & PCF_COMMENT_OUT)
+ vstream_fprintf(dst, "#%s", cp);
+ } else {
+ vstream_fputs(STR(buf), dst);
+ }
+ }
+ }
+
+ /*
+ * Generate new entries for parameters that were not found.
+ */
+ if (mode & PCF_EDIT_CONF) {
+ 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((void *) ht_info);
+ }
+
+ /*
+ * When all is well, rename the temp file to the original one.
+ */
+ 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);
+
+ /*
+ * Cleanup.
+ */
+ myfree(path);
+ vstring_free(buf);
+ vstring_free(key);
+ htable_free(table, myfree);
+}
+
+ /*
+ * Data structure to hold a master.cf edit request.
+ */
+typedef struct {
+ int match_count; /* hit count */
+ const char *raw_text; /* unparsed command-line argument */
+ char *parsed_text; /* destructive parse */
+ ARGV *service_pattern; /* service name, type, ... */
+ int field_number; /* attribute field number */
+ const char *param_pattern; /* parameter name */
+ char *edit_value; /* value substring */
+} PCF_MASTER_EDIT_REQ;
+
+/* pcf_edit_master - edit master.cf file */
+
+void pcf_edit_master(int mode, int argc, char **argv)
+{
+ const char *myname = "pcf_edit_master";
+ char *path;
+ EDIT_FILE *ep;
+ VSTREAM *src;
+ VSTREAM *dst;
+ VSTRING *line_buf = vstring_alloc(100);
+ VSTRING *parse_buf = vstring_alloc(100);
+ int lineno;
+ PCF_MASTER_ENT *new_entry;
+ VSTRING *full_entry_buf = vstring_alloc(100);
+ char *cp;
+ char *pattern;
+ int service_name_type_matched;
+ const char *err;
+ PCF_MASTER_EDIT_REQ *edit_reqs;
+ PCF_MASTER_EDIT_REQ *req;
+ int num_reqs = argc;
+ const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#";
+ char *service_name;
+ char *service_type;
+
+ /*
+ * Sanity check.
+ */
+ if (num_reqs <= 0)
+ msg_panic("%s: empty argument list", myname);
+
+ /*
+ * Preprocessing: split pattern=value, then split the pattern components.
+ */
+ edit_reqs = (PCF_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs);
+ for (req = edit_reqs; *argv != 0; req++, argv++) {
+ req->match_count = 0;
+ req->raw_text = *argv;
+ cp = req->parsed_text = mystrdup(req->raw_text);
+ if (strchr(cp, '\n') != 0)
+ msg_fatal("%s accept no multi-line input", edit_opts);
+ while (ISSPACE(*cp))
+ cp++;
+ if (*cp == '#')
+ msg_fatal("%s accept no comment input", edit_opts);
+ /* Separate the pattern from the value. */
+ if (mode & PCF_EDIT_CONF) {
+ if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0)
+ msg_fatal("%s: \"%s\"", err, req->raw_text);
+#if 0
+ if ((mode & PCF_MASTER_PARAM)
+ && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
+ msg_fatal("whitespace in parameter value: \"%s\"",
+ req->raw_text);
+#endif
+ } else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) {
+ if (strchr(cp, '=') != 0)
+ msg_fatal("-X or -# requires names without value");
+ pattern = cp;
+ trimblanks(pattern, 0);
+ req->edit_value = 0;
+ } else {
+ msg_panic("%s: unknown mode %d", myname, mode);
+ }
+
+#define PCF_MASTER_MASK (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)
+
+ /*
+ * Split name/type or name/type/whatever pattern into components.
+ */
+ switch (mode & PCF_MASTER_MASK) {
+ case PCF_MASTER_ENTRY:
+ if ((req->service_pattern =
+ pcf_parse_service_pattern(pattern, 2, 2)) == 0)
+ msg_fatal("-Me, -MX or -M# requires service_name/type");
+ break;
+ case PCF_MASTER_FLD:
+ if ((req->service_pattern =
+ pcf_parse_service_pattern(pattern, 3, 3)) == 0)
+ msg_fatal("-Fe or -FX requires service_name/type/field_name");
+ req->field_number =
+ pcf_parse_field_pattern(req->service_pattern->argv[2]);
+ if (pcf_is_magic_field_pattern(req->field_number))
+ msg_fatal("-Fe does not accept wild-card field name");
+ if ((mode & PCF_EDIT_CONF)
+ && req->field_number < PCF_MASTER_FLD_CMD
+ && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
+ msg_fatal("-Fe does not accept whitespace in non-command field");
+ break;
+ case PCF_MASTER_PARAM:
+ if ((req->service_pattern =
+ pcf_parse_service_pattern(pattern, 3, 3)) == 0)
+ msg_fatal("-Pe or -PX requires service_name/type/parameter");
+ req->param_pattern = req->service_pattern->argv[2];
+ if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern))
+ msg_fatal("-Pe does not accept wild-card parameter name");
+ if ((mode & PCF_EDIT_CONF)
+ && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
+ msg_fatal("-Pe does not accept whitespace in parameter value");
+ break;
+ default:
+ msg_panic("%s: unknown edit mode %d", myname, mode);
+ }
+ }
+
+ /*
+ * Open a temp file for the result. This uses a deterministic name so we
+ * don't leave behind thrash with random names.
+ */
+ pcf_set_config_dir();
+ path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (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;
+
+ /*
+ * Open the original file for input.
+ */
+ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
+ /* OK to delete, since we control the temp file name exclusively. */
+ (void) unlink(ep->tmp_path);
+ msg_fatal("open %s for reading: %m", path);
+ }
+
+ /*
+ * Copy original file to temp file, while replacing service entries on
+ * the fly.
+ */
+ service_name_type_matched = 0;
+ new_entry = 0;
+ lineno = 0;
+ while ((cp = pcf_next_cf_line(parse_buf, src, dst, &lineno)) != 0) {
+ vstring_strcpy(line_buf, STR(parse_buf));
+
+ /*
+ * Copy, skip or replace continued text.
+ */
+ if (cp > STR(parse_buf)) {
+ if (service_name_type_matched == 0)
+ vstream_fputs(STR(line_buf), dst);
+ else if (mode & PCF_COMMENT_OUT)
+ vstream_fprintf(dst, "#%s", STR(line_buf));
+ }
+
+ /*
+ * Copy or replace (start of) logical line.
+ */
+ else {
+ service_name_type_matched = 0;
+
+ /*
+ * Parse out the service name and type.
+ */
+ if ((service_name = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0
+ || (service_type = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0)
+ msg_fatal("file %s: line %d: specify service name and type "
+ "on the same line", path, lineno);
+ if (strchr(service_name, '='))
+ msg_fatal("file %s: line %d: service name syntax \"%s\" is "
+ "unsupported with %s", path, lineno, service_name,
+ edit_opts);
+ if (service_type[strcspn(service_type, "=/")] != 0)
+ msg_fatal("file %s: line %d: "
+ "service type syntax \"%s\" is unsupported with %s",
+ path, lineno, service_type, edit_opts);
+
+ /*
+ * Match each service pattern.
+ */
+ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
+ if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
+ service_name,
+ service_type)) {
+ service_name_type_matched = 1; /* Sticky flag */
+ req->match_count += 1;
+
+ /*
+ * Generate replacement master.cf entries.
+ */
+ if ((mode & PCF_EDIT_CONF)
+ || ((mode & PCF_MASTER_PARAM) && (mode & PCF_EDIT_EXCL))) {
+ switch (mode & PCF_MASTER_MASK) {
+
+ /*
+ * Replace master.cf entry field or parameter
+ * value.
+ */
+ case PCF_MASTER_FLD:
+ case PCF_MASTER_PARAM:
+ if (new_entry == 0) {
+ /* Gobble up any continuation lines. */
+ pcf_gobble_cf_line(full_entry_buf, line_buf,
+ src, dst, &lineno);
+ new_entry = (PCF_MASTER_ENT *)
+ mymalloc(sizeof(*new_entry));
+ if ((err = pcf_parse_master_entry(new_entry,
+ STR(full_entry_buf))) != 0)
+ msg_fatal("file %s: line %d: %s",
+ path, lineno, err);
+ }
+ if (mode & PCF_MASTER_FLD) {
+ pcf_edit_master_field(new_entry,
+ req->field_number,
+ req->edit_value);
+ } else {
+ pcf_edit_master_param(new_entry, mode,
+ req->param_pattern,
+ req->edit_value);
+ }
+ break;
+
+ /*
+ * Replace entire master.cf entry.
+ */
+ case PCF_MASTER_ENTRY:
+ if (new_entry != 0)
+ pcf_free_master_entry(new_entry);
+ new_entry = (PCF_MASTER_ENT *)
+ mymalloc(sizeof(*new_entry));
+ if ((err = pcf_parse_master_entry(new_entry,
+ req->edit_value)) != 0)
+ msg_fatal("%s: \"%s\"", err, req->raw_text);
+ break;
+ default:
+ msg_panic("%s: unknown edit mode %d", myname, mode);
+ }
+ }
+ }
+ }
+
+ /*
+ * Pass through or replace the current input line.
+ */
+ if (new_entry) {
+ pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry);
+ pcf_free_master_entry(new_entry);
+ new_entry = 0;
+ } else if (service_name_type_matched == 0) {
+ vstream_fputs(STR(line_buf), dst);
+ } else if (mode & PCF_COMMENT_OUT) {
+ vstream_fprintf(dst, "#%s", STR(line_buf));
+ }
+ }
+ }
+
+ /*
+ * Postprocessing: when editing entire service entries, generate new
+ * entries for services not found. Otherwise (editing fields or
+ * parameters), "service not found" is a fatal error.
+ */
+ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
+ if (req->match_count == 0) {
+ if ((mode & PCF_MASTER_ENTRY) && (mode & PCF_EDIT_CONF)) {
+ new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry));
+ if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0)
+ msg_fatal("%s: \"%s\"", err, req->raw_text);
+ pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry);
+ pcf_free_master_entry(new_entry);
+ } else if ((mode & PCF_MASTER_ENTRY) == 0) {
+ msg_warn("unmatched service_name/type: \"%s\"", req->raw_text);
+ }
+ }
+ }
+
+ /*
+ * When all is well, rename the temp file to the original one.
+ */
+ 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);
+
+ /*
+ * Cleanup.
+ */
+ myfree(path);
+ vstring_free(line_buf);
+ vstring_free(parse_buf);
+ vstring_free(full_entry_buf);
+ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
+ argv_free(req->service_pattern);
+ myfree(req->parsed_text);
+ }
+ myfree((void *) edit_reqs);
+}
diff --git a/src/postconf/postconf_lookup.c b/src/postconf/postconf_lookup.c
new file mode 100644
index 0000000..3cfa9f3
--- /dev/null
+++ b/src/postconf/postconf_lookup.c
@@ -0,0 +1,198 @@
+/*++
+/* NAME
+/* postconf_lookup 3
+/* SUMMARY
+/* parameter lookup routines
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* const char *pcf_lookup_parameter_value(mode, name, local_scope, node)
+/* int mode;
+/* const char *name;
+/* PCF_MASTER_ENT *local_scope;
+/* PCF_PARAM_NODE *node;
+/*
+/* char *pcf_expand_parameter_value(buf, mode, value, local_scope)
+/* VSTRING *buf;
+/* int mode;
+/* const char *value;
+/* PCF_MASTER_ENT *local_scope;
+/* DESCRIPTION
+/* These functions perform parameter value lookups. The order
+/* of decreasing precedence is:
+/* .IP \(bu
+/* Search name=value parameter settings in master.cf. These
+/* lookups are disabled with the PCF_SHOW_DEFS flag.
+/* .IP \(bu
+/* Search name=value parameter settings in main.cf. These
+/* lookups are disabled with the PCF_SHOW_DEFS flag.
+/* .IP \(bu
+/* Search built-in default parameter settings. These lookups
+/* are disabled with the PCF_SHOW_NONDEF flag.
+/* .PP
+/* pcf_lookup_parameter_value() looks up the value for the
+/* named parameter, and returns null if the name was not found.
+/*
+/* pcf_expand_parameter_value() expands $name in the specified
+/* parameter value. This function ignores the PCF_SHOW_NONDEF
+/* flag. The result value is a pointer to storage in a
+/* user-supplied buffer, or in a buffer that is overwritten
+/* with each call.
+/*
+/* Arguments:
+/* .IP buf
+/* Null buffer pointer, or pointer to user-supplied buffer.
+/* .IP mode
+/* Bit-wise OR of zero or one of the following (other flags
+/* are ignored):
+/* .RS
+/* .IP PCF_SHOW_DEFS
+/* Search built-in default parameter settings only.
+/* .IP PCF_SHOW_NONDEF
+/* Search local (master.cf) and global (main.cf) name=value
+/* parameter settings only.
+/* .RE
+/* .IP name
+/* The name of a parameter to be looked up.
+/* .IP value
+/* The parameter value where $name should be expanded.
+/* .IP local_scope
+/* Pointer to master.cf entry with local name=value settings,
+/* or a null pointer (i.e. no local parameter lookup).
+/* .IP node
+/* Global default value for the named parameter, or a null
+/* pointer (i.e. do the global default lookup anyway).
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <dict.h>
+#include <stringops.h>
+#include <mac_expand.h>
+
+/* Global library. */
+
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+#define STR(x) vstring_str(x)
+
+/* pcf_lookup_parameter_value - look up specific parameter value */
+
+const char *pcf_lookup_parameter_value(int mode, const char *name,
+ PCF_MASTER_ENT *local_scope,
+ PCF_PARAM_NODE *node)
+{
+ const char *value = 0;
+
+#define LOOKUP(dict, name) ((dict) ? dict_get((dict), (name)) : 0)
+
+ /*
+ * Local name=value entries in master.cf take precedence over global
+ * name=value entries in main.cf. Built-in defaults have the lowest
+ * precedence.
+ */
+ if ((mode & PCF_SHOW_DEFS) != 0
+ || ((local_scope == 0
+ || ((value = LOOKUP(local_scope->ro_params, name)) == 0
+ && (value = LOOKUP(local_scope->all_params, name)) == 0))
+ && (value = dict_lookup(CONFIG_DICT, name)) == 0
+ && (mode & PCF_SHOW_NONDEF) == 0)) {
+ if (node != 0 || (node = PCF_PARAM_TABLE_FIND(pcf_param_table, name)) != 0)
+ value = pcf_convert_param_node(PCF_SHOW_DEFS, name, node);
+ }
+ return (value);
+}
+
+ /*
+ * Data structure to pass private state while recursively expanding $name in
+ * parameter values.
+ */
+typedef struct {
+ int mode;
+ PCF_MASTER_ENT *local_scope;
+} PCF_EVAL_CTX;
+
+/* pcf_lookup_parameter_value_wrapper - macro parser call-back routine */
+
+static const char *pcf_lookup_parameter_value_wrapper(const char *key,
+ int unused_type,
+ void *context)
+{
+ PCF_EVAL_CTX *cp = (PCF_EVAL_CTX *) context;
+
+ return (pcf_lookup_parameter_value(cp->mode, key, cp->local_scope,
+ (PCF_PARAM_NODE *) 0));
+}
+
+/* pcf_expand_parameter_value - expand $name in parameter value */
+
+char *pcf_expand_parameter_value(VSTRING *buf, int mode, const char *value,
+ PCF_MASTER_ENT *local_scope)
+{
+ const char *myname = "pcf_expand_parameter_value";
+ static VSTRING *local_buf;
+ int status;
+ PCF_EVAL_CTX eval_ctx;
+
+ /*
+ * Initialize.
+ */
+ if (buf == 0) {
+ if (local_buf == 0)
+ local_buf = vstring_alloc(10);
+ buf = local_buf;
+ }
+
+ /*
+ * Expand macros recursively.
+ *
+ * When expanding $name in "postconf -n" parameter values, don't limit the
+ * search to only non-default parameter values.
+ *
+ * When expanding $name in "postconf -d" parameter values, do limit the
+ * search to only default parameter values.
+ */
+#define DONT_FILTER (char *) 0
+
+ eval_ctx.mode = (mode & ~PCF_SHOW_NONDEF);
+ eval_ctx.local_scope = local_scope;
+ status = mac_expand(buf, value, MAC_EXP_FLAG_RECURSE, DONT_FILTER,
+ pcf_lookup_parameter_value_wrapper, (void *) &eval_ctx);
+ if (status & MAC_PARSE_ERROR)
+ msg_fatal("macro processing error");
+ if (msg_verbose > 1) {
+ if (strcmp(value, STR(buf)) != 0)
+ msg_info("%s: expand %s -> %s", myname, value, STR(buf));
+ else
+ msg_info("%s: const %s", myname, value);
+ }
+ return (STR(buf));
+}
diff --git a/src/postconf/postconf_main.c b/src/postconf/postconf_main.c
new file mode 100644
index 0000000..07485f1
--- /dev/null
+++ b/src/postconf/postconf_main.c
@@ -0,0 +1,216 @@
+/*++
+/* NAME
+/* postconf_main 3
+/* SUMMARY
+/* basic support for main.cf
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_read_parameters()
+/*
+/* void pcf_show_parameters(fp, mode, param_class, names)
+/* VSTREAM *fp;
+/* int mode;
+/* int param_class;
+/* char **names;
+/* DESCRIPTION
+/* pcf_read_parameters() reads parameters from main.cf.
+/*
+/* pcf_set_parameters() takes an array of \fIname=value\fR
+/* pairs and overrides settings read with pcf_read_parameters().
+/*
+/* pcf_show_parameters() writes main.cf parameters to the
+/* specified output stream.
+/*
+/* Arguments:
+/* .IP fp
+/* Output stream.
+/* .IP mode
+/* Bit-wise OR of zero or more of the following:
+/* .RS
+/* .IP PCF_FOLD_LINE
+/* Fold long lines.
+/* .IP PCF_SHOW_DEFS
+/* Output default parameter values.
+/* .IP PCF_SHOW_NONDEF
+/* Output explicit settings only.
+/* .IP PCF_HIDE_NAME
+/* Output parameter values without the "name =" prefix.
+/* .IP PCF_SHOW_EVAL
+/* Expand $name in parameter values.
+/* .RE
+/* .IP param_class
+/* Bit-wise OR of one or more of the following:
+/* .RS
+/* .IP PCF_PARAM_FLAG_BUILTIN
+/* Show built-in parameters.
+/* .IP PCF_PARAM_FLAG_SERVICE
+/* Show service-defined parameters.
+/* .IP PCF_PARAM_FLAG_USER
+/* Show user-defined parameters.
+/* .RE
+/* .IP names
+/* List of zero or more parameter names. If the list is empty,
+/* output all parameters.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <readlline.h>
+#include <dict.h>
+#include <stringops.h>
+#include <htable.h>
+#include <mac_expand.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+#define STR(x) vstring_str(x)
+
+/* pcf_read_parameters - read parameter info from file */
+
+void pcf_read_parameters(void)
+{
+ char *path;
+
+ /*
+ * A direct rip-off of mail_conf_read(). XXX Avoid code duplication by
+ * better code decomposition.
+ */
+ pcf_set_config_dir();
+ path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
+ if (dict_load_file_xt(CONFIG_DICT, path) == 0)
+ msg_fatal("open %s: %m", path);
+ myfree(path);
+}
+
+/* pcf_set_parameters - add or override name=value pairs */
+
+void pcf_set_parameters(char **name_val_array)
+{
+ char *name, *value, *junk;
+ const char *err;
+ char **cpp;
+
+ for (cpp = name_val_array; *cpp; cpp++) {
+ junk = mystrdup(*cpp);
+ if ((err = split_nameval(junk, &name, &value)) != 0)
+ msg_fatal("invalid parameter override: %s: %s", *cpp, err);
+ mail_conf_update(name, value);
+ myfree(junk);
+ }
+}
+
+/* pcf_print_parameter - show specific parameter */
+
+static void pcf_print_parameter(VSTREAM *fp, int mode, const char *name,
+ PCF_PARAM_NODE *node)
+{
+ const char *value;
+
+ /*
+ * Use the default or actual value.
+ */
+ value = pcf_lookup_parameter_value(mode, name, (PCF_MASTER_ENT *) 0, node);
+
+ /*
+ * Optionally expand $name in the parameter value. Print the result with
+ * or without the name= prefix.
+ */
+ if (value != 0) {
+ if (mode & PCF_HIDE_VALUE) {
+ pcf_print_line(fp, mode, "%s\n", name);
+ } else {
+ if ((mode & PCF_SHOW_EVAL) != 0 && PCF_RAW_PARAMETER(node) == 0)
+ value = pcf_expand_parameter_value((VSTRING *) 0, mode, value,
+ (PCF_MASTER_ENT *) 0);
+ if ((mode & PCF_HIDE_NAME) == 0) {
+ pcf_print_line(fp, mode, "%s = %s\n", name, value);
+ } else {
+ pcf_print_line(fp, mode, "%s\n", value);
+ }
+ }
+ if (msg_verbose)
+ vstream_fflush(fp);
+ }
+}
+
+/* pcf_comp_names - qsort helper */
+
+static int pcf_comp_names(const void *a, const void *b)
+{
+ PCF_PARAM_INFO **ap = (PCF_PARAM_INFO **) a;
+ PCF_PARAM_INFO **bp = (PCF_PARAM_INFO **) b;
+
+ return (strcmp(PCF_PARAM_INFO_NAME(ap[0]),
+ PCF_PARAM_INFO_NAME(bp[0])));
+}
+
+/* pcf_show_parameters - show parameter info */
+
+void pcf_show_parameters(VSTREAM *fp, int mode, int param_class, char **names)
+{
+ PCF_PARAM_INFO **list;
+ PCF_PARAM_INFO **ht;
+ char **namep;
+ PCF_PARAM_NODE *node;
+
+ /*
+ * Show all parameters.
+ */
+ if (*names == 0) {
+ list = PCF_PARAM_TABLE_LIST(pcf_param_table);
+ qsort((void *) list, pcf_param_table->used, sizeof(*list),
+ pcf_comp_names);
+ for (ht = list; *ht; ht++)
+ if (param_class & PCF_PARAM_INFO_NODE(*ht)->flags)
+ pcf_print_parameter(fp, mode, PCF_PARAM_INFO_NAME(*ht),
+ PCF_PARAM_INFO_NODE(*ht));
+ myfree((void *) list);
+ return;
+ }
+
+ /*
+ * Show named parameters.
+ */
+ for (namep = names; *namep; namep++) {
+ if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, *namep)) == 0) {
+ msg_warn("%s: unknown parameter", *namep);
+ } else {
+ pcf_print_parameter(fp, mode, *namep, node);
+ }
+ }
+}
diff --git a/src/postconf/postconf_master.c b/src/postconf/postconf_master.c
new file mode 100644
index 0000000..3c169f7
--- /dev/null
+++ b/src/postconf/postconf_master.c
@@ -0,0 +1,1108 @@
+/*++
+/* NAME
+/* postconf_master 3
+/* SUMMARY
+/* support for master.cf
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* const char pcf_daemon_options_expecting_value[];
+/*
+/* void pcf_read_master(fail_on_open)
+/* int fail_on_open;
+/*
+/* void pcf_show_master_entries(fp, mode, service_filters)
+/* VSTREAM *fp;
+/* int mode;
+/* char **service_filters;
+/*
+/* void pcf_show_master_fields(fp, mode, n_filters, field_filters)
+/* VSTREAM *fp;
+/* int mode;
+/* int n_filters;
+/* char **field_filters;
+/*
+/* void pcf_edit_master_field(masterp, field, new_value)
+/* PCF_MASTER_ENT *masterp;
+/* int field;
+/* const char *new_value;
+/*
+/* void pcf_show_master_params(fp, mode, argc, **param_filters)
+/* VSTREAM *fp;
+/* int mode;
+/* int argc;
+/* char **param_filters;
+/*
+/* void pcf_edit_master_param(masterp, mode, param_name, param_value)
+/* PCF_MASTER_ENT *masterp;
+/* int mode;
+/* const char *param_name;
+/* const char *param_value;
+/* AUXILIARY FUNCTIONS
+/* const char *pcf_parse_master_entry(masterp, buf)
+/* PCF_MASTER_ENT *masterp;
+/* const char *buf;
+/*
+/* void pcf_print_master_entry(fp, mode, masterp)
+/* VSTREAM *fp;
+/* int mode;
+/* PCF_MASTER_ENT *masterp;
+/*
+/* void pcf_free_master_entry(masterp)
+/* PCF_MASTER_ENT *masterp;
+/* DESCRIPTION
+/* pcf_read_master() reads entries from master.cf into memory.
+/*
+/* pcf_show_master_entries() writes the entries in the master.cf
+/* file to the specified stream.
+/*
+/* pcf_show_master_fields() writes name/type/field=value records
+/* to the specified stream.
+/*
+/* pcf_edit_master_field() updates the value of a single-column
+/* or multi-column attribute.
+/*
+/* pcf_show_master_params() writes name/type/parameter=value
+/* records to the specified stream.
+/*
+/* pcf_edit_master_param() updates, removes or adds the named
+/* parameter in a master.cf entry (the remove request ignores
+/* the parameter value).
+/*
+/* pcf_daemon_options_expecting_value[] is an array of master.cf
+/* daemon command-line options that expect an option value.
+/*
+/* pcf_parse_master_entry() parses a (perhaps multi-line)
+/* string that contains a complete master.cf entry, and
+/* normalizes daemon command-line options to simplify further
+/* handling.
+/*
+/* pcf_print_master_entry() prints a parsed master.cf entry.
+/*
+/* pcf_free_master_entry() returns storage to the heap that
+/* was allocated by pcf_parse_master_entry().
+/*
+/* Arguments
+/* .IP fail_on_open
+/* Specify FAIL_ON_OPEN if open failure is a fatal error,
+/* WARN_ON_OPEN if a warning should be logged instead.
+/* .IP fp
+/* Output stream.
+/* .IP mode
+/* Bit-wise OR of flags. Flags other than the following are
+/* ignored.
+/* .RS
+/* .IP PCF_FOLD_LINE
+/* Wrap long output lines.
+/* .IP PCF_SHOW_EVAL
+/* Expand $name in parameter values.
+/* .IP PCF_EDIT_EXCL
+/* Request that pcf_edit_master_param() removes the parameter.
+/* .RE
+/* .IP n_filters
+/* The number of command-line filters.
+/* .IP field_filters
+/* A list of zero or more service field patterns (name/type/field).
+/* The output is formatted as "name/type/field = value". If
+/* no filters are specified, pcf_show_master_fields() outputs
+/* the fields of all master.cf entries in the specified order.
+/* .IP param_filters
+/* A list of zero or more service parameter patterns
+/* (name/type/parameter). The output is formatted as
+/* "name/type/parameter = value". If no filters are specified,
+/* pcf_show_master_params() outputs the parameters of all
+/* master.cf entries in sorted order.
+/* .IP service_filters
+/* A list of zero or more service patterns (name or name/type).
+/* If no filters are specified, pcf_show_master_entries()
+/* outputs all master.cf entries in the specified order.
+/* .IP field
+/* Index into parsed master.cf entry.
+/* .IP new_value
+/* Replacement value for the specified field. It is split in
+/* whitespace in case of a multi-field attribute.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <argv.h>
+#include <vstream.h>
+#include <readlline.h>
+#include <stringops.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Master library. */
+
+#include <master_proto.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+const char pcf_daemon_options_expecting_value[] = "o";
+
+ /*
+ * Data structure to capture a command-line service field filter.
+ */
+typedef struct {
+ int match_count; /* hit count */
+ const char *raw_text; /* full pattern text */
+ ARGV *service_pattern; /* parsed service name, type, ... */
+ int field_pattern; /* parsed field pattern */
+ const char *param_pattern; /* parameter pattern */
+} PCF_MASTER_FLD_REQ;
+
+ /*
+ * Valid inputs.
+ */
+static const char *pcf_valid_master_types[] = {
+ MASTER_XPORT_NAME_UNIX,
+ MASTER_XPORT_NAME_FIFO,
+ MASTER_XPORT_NAME_INET,
+ MASTER_XPORT_NAME_PASS,
+ MASTER_XPORT_NAME_UXDG,
+ 0,
+};
+
+static const char pcf_valid_bool_types[] = "yn-";
+
+#define STR(x) vstring_str(x)
+
+/* pcf_extract_field - extract text from {}, trim leading/trailing blanks */
+
+static void pcf_extract_field(ARGV *argv, int field, const char *parens)
+{
+ char *arg = argv->argv[field];
+ char *err;
+
+ if ((err = extpar(&arg, parens, EXTPAR_FLAG_STRIP)) != 0) {
+ msg_warn("%s: %s", MASTER_CONF_FILE, err);
+ myfree(err);
+ }
+ argv_replace_one(argv, field, arg);
+}
+
+/* pcf_normalize_nameval - normalize name = value from inside {} */
+
+static void pcf_normalize_nameval(ARGV *argv, int field)
+{
+ char *arg = argv->argv[field];
+ char *name;
+ char *value;
+ const char *err;
+ char *normalized;
+
+ if ((err = split_nameval(arg, &name, &value)) != 0) {
+ msg_warn("%s: %s: \"%s\"", MASTER_CONF_FILE, err, arg);
+ } else {
+ normalized = concatenate(name, "=", value, (char *) 0);
+ argv_replace_one(argv, field, normalized);
+ myfree(normalized);
+ }
+}
+
+/* pcf_normalize_daemon_args - bring daemon arguments into canonical form */
+
+static void pcf_normalize_daemon_args(ARGV *argv)
+{
+ int field;
+ char *arg;
+ char *cp;
+ char *junk;
+ int extract_field;
+
+ /*
+ * Normalize options to simplify later processing.
+ */
+ for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
+ arg = argv->argv[field];
+ if (arg[0] != '-' || strcmp(arg, "--") == 0)
+ break;
+ for (cp = arg + 1; *cp; cp++) {
+ if (strchr(pcf_daemon_options_expecting_value, *cp) != 0
+ && cp > arg + 1) {
+ /* Split "-stuffozz" into "-stuff" and "-ozz". */
+ junk = concatenate("-", cp, (char *) 0);
+ argv_insert_one(argv, field + 1, junk);
+ myfree(junk);
+ *cp = 0; /* XXX argv_replace_one() */
+ break;
+ }
+ }
+ if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0)
+ /* Option requires no value. */
+ continue;
+ if (arg[2] != 0) {
+ /* Split "-oname=value" into "-o" "name=value". */
+ argv_insert_one(argv, field + 1, arg + 2);
+ arg[2] = 0; /* XXX argv_replace_one() */
+ field += 1;
+ extract_field = (argv->argv[field][0] == CHARS_BRACE[0]);
+ } else if (argv->argv[field + 1] != 0) {
+ /* Already in "-o" "name=value" form. */
+ field += 1;
+ extract_field = (argv->argv[field][0] == CHARS_BRACE[0]);
+ } else
+ extract_field = 0;
+ /* Extract text inside {}, optionally convert to name=value. */
+ if (extract_field) {
+ pcf_extract_field(argv, field, CHARS_BRACE);
+ if (argv->argv[field - 1][1] == 'o')
+ pcf_normalize_nameval(argv, field);
+ }
+ }
+ /* Normalize non-option arguments. */
+ for ( /* void */ ; argv->argv[field] != 0; field++)
+ /* Extract text inside {}. */
+ if (argv->argv[field][0] == CHARS_BRACE[0])
+ pcf_extract_field(argv, field, CHARS_BRACE);
+}
+
+/* pcf_fix_fatal - fix multiline text before release */
+
+static NORETURN PRINTFLIKE(1, 2) pcf_fix_fatal(const char *fmt,...)
+{
+ VSTRING *buf = vstring_alloc(100);
+ va_list ap;
+
+ /*
+ * Replace newline with whitespace.
+ */
+ va_start(ap, fmt);
+ vstring_vsprintf(buf, fmt, ap);
+ va_end(ap);
+ translit(STR(buf), "\n", " ");
+ msg_fatal("%s", STR(buf));
+ /* NOTREACHED */
+}
+
+/* pcf_check_master_entry - sanity check master.cf entry */
+
+static void pcf_check_master_entry(ARGV *argv, const char *raw_text)
+{
+ const char **cpp;
+ char *cp;
+ int len;
+ int field;
+
+ cp = argv->argv[PCF_MASTER_FLD_TYPE];
+ for (cpp = pcf_valid_master_types; /* see below */ ; cpp++) {
+ if (*cpp == 0)
+ pcf_fix_fatal("invalid " PCF_MASTER_NAME_TYPE " field \"%s\" in \"%s\"",
+ cp, raw_text);
+ if (strcmp(*cpp, cp) == 0)
+ break;
+ }
+
+ for (field = PCF_MASTER_FLD_PRIVATE; field <= PCF_MASTER_FLD_CHROOT; field++) {
+ cp = argv->argv[field];
+ if (cp[1] != 0 || strchr(pcf_valid_bool_types, *cp) == 0)
+ pcf_fix_fatal("invalid %s field \"%s\" in \"%s\"",
+ pcf_str_field_pattern(field), cp, raw_text);
+ }
+
+ cp = argv->argv[PCF_MASTER_FLD_WAKEUP];
+ len = strlen(cp);
+ if (len > 0 && cp[len - 1] == '?')
+ len--;
+ if (!(cp[0] == '-' && len == 1) && strspn(cp, "0123456789") != len)
+ pcf_fix_fatal("invalid " PCF_MASTER_NAME_WAKEUP " field \"%s\" in \"%s\"",
+ cp, raw_text);
+
+ cp = argv->argv[PCF_MASTER_FLD_MAXPROC];
+ if (strcmp("-", cp) != 0 && cp[strspn(cp, "0123456789")] != 0)
+ pcf_fix_fatal("invalid " PCF_MASTER_NAME_MAXPROC " field \"%s\" in \"%s\"",
+ cp, raw_text);
+}
+
+/* pcf_free_master_entry - destroy parsed entry */
+
+void pcf_free_master_entry(PCF_MASTER_ENT *masterp)
+{
+ /* XX Fixme: allocation/deallocation asymmetry. */
+ myfree(masterp->name_space);
+ argv_free(masterp->argv);
+ if (masterp->valid_names)
+ htable_free(masterp->valid_names, myfree);
+ if (masterp->ro_params)
+ dict_free(masterp->ro_params);
+ if (masterp->all_params)
+ dict_free(masterp->all_params);
+ myfree((void *) masterp);
+}
+
+/* pcf_parse_master_entry - parse one master line */
+
+const char *pcf_parse_master_entry(PCF_MASTER_ENT *masterp, const char *buf)
+{
+ ARGV *argv;
+ char *ro_name_space;
+ char *process_name;
+
+ /*
+ * We can't use the master daemon's master_ent routines in their current
+ * form. They convert everything to internal form, and they skip disabled
+ * services.
+ *
+ * The postconf command needs to show default fields as "-", and needs to
+ * know about all service names so that it can generate service-dependent
+ * parameter names (transport-dependent etc.).
+ *
+ * XXX Do per-field sanity checks.
+ */
+ argv = argv_splitq(buf, PCF_MASTER_BLANKS, CHARS_BRACE);
+ if (argv->argc < PCF_MASTER_MIN_FIELDS) {
+ argv_free(argv); /* Coverity 201311 */
+ return ("bad field count");
+ }
+ pcf_check_master_entry(argv, buf);
+ pcf_normalize_daemon_args(argv);
+ masterp->name_space =
+ concatenate(argv->argv[0], PCF_NAMESP_SEP_STR, argv->argv[1], (char *) 0);
+ ro_name_space =
+ concatenate("ro", PCF_NAMESP_SEP_STR, masterp->name_space, (char *) 0);
+ masterp->argv = argv;
+ masterp->valid_names = 0;
+ process_name = basename(argv->argv[PCF_MASTER_FLD_CMD]);
+ dict_update(ro_name_space, VAR_PROCNAME, process_name);
+ dict_update(ro_name_space, VAR_SERVNAME,
+ strcmp(process_name, argv->argv[0]) != 0 ?
+ argv->argv[0] : process_name);
+ masterp->ro_params = dict_handle(ro_name_space);
+ myfree(ro_name_space);
+ masterp->all_params = 0;
+ return (0);
+}
+
+/* pcf_read_master - read and digest the master.cf file */
+
+void pcf_read_master(int fail_on_open_error)
+{
+ const char *myname = "pcf_read_master";
+ char *path;
+ VSTRING *buf;
+ VSTREAM *fp;
+ const char *err;
+ int entry_count = 0;
+ int line_count;
+ int last_line = 0;
+
+ /*
+ * Sanity check.
+ */
+ if (pcf_master_table != 0)
+ msg_panic("%s: master table is already initialized", myname);
+
+ /*
+ * Get the location of master.cf.
+ */
+ if (var_config_dir == 0)
+ pcf_set_config_dir();
+ path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
+
+ /*
+ * Initialize the in-memory master table.
+ */
+ pcf_master_table = (PCF_MASTER_ENT *) mymalloc(sizeof(*pcf_master_table));
+
+ /*
+ * Skip blank lines and comment lines. Degrade gracefully if master.cf is
+ * not available, and master.cf is not the primary target.
+ */
+ if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) {
+ if (fail_on_open_error)
+ msg_fatal("open %s: %m", path);
+ msg_warn("open %s: %m", path);
+ } else {
+ buf = vstring_alloc(100);
+ while (readllines(buf, fp, &last_line, &line_count) != 0) {
+ pcf_master_table = (PCF_MASTER_ENT *) myrealloc((void *) pcf_master_table,
+ (entry_count + 2) * sizeof(*pcf_master_table));
+ if ((err = pcf_parse_master_entry(pcf_master_table + entry_count,
+ STR(buf))) != 0)
+ msg_fatal("file %s: line %d: %s", path, line_count, err);
+ entry_count += 1;
+ }
+ vstream_fclose(fp);
+ vstring_free(buf);
+ }
+
+ /*
+ * Null-terminate the master table and clean up.
+ */
+ pcf_master_table[entry_count].argv = 0;
+ myfree(path);
+}
+
+/* pcf_print_master_entry - print one master line */
+
+void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp)
+{
+ char **argv = masterp->argv->argv;
+ const char *arg;
+ const char *aval;
+ int arg_len;
+ int line_len;
+ int field;
+ int in_daemon_options;
+ int need_parens;
+ static int column_goal[] = {
+ 0, /* service */
+ 11, /* type */
+ 17, /* private */
+ 25, /* unpriv */
+ 33, /* chroot */
+ 41, /* wakeup */
+ 49, /* maxproc */
+ 57, /* command */
+ };
+
+#define ADD_TEXT(text, len) do { \
+ vstream_fputs(text, fp); line_len += len; } \
+ while (0)
+#define ADD_SPACE ADD_TEXT(" ", 1)
+
+ /*
+ * Show the standard fields at their preferred column position. Use at
+ * least one-space column separation.
+ */
+ for (line_len = 0, field = 0; field < PCF_MASTER_MIN_FIELDS; field++) {
+ arg = argv[field];
+ if (line_len > 0) {
+ do {
+ ADD_SPACE;
+ } while (line_len < column_goal[field]);
+ }
+ ADD_TEXT(arg, strlen(arg));
+ }
+
+ /*
+ * Format the daemon command-line options and non-option arguments. Here,
+ * we have no data-dependent preference for column positions, but we do
+ * have argument grouping preferences.
+ */
+ in_daemon_options = 1;
+ for ( /* void */ ; (arg = argv[field]) != 0; field++) {
+ arg_len = strlen(arg);
+ aval = 0;
+ need_parens = 0;
+ if (in_daemon_options) {
+
+ /*
+ * Try to show the generic options (-v -D) on the first line, and
+ * non-options on a later line.
+ */
+ if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+ in_daemon_options = 0;
+#if 0
+ if (mode & PCF_FOLD_LINE)
+ /* Force line wrap. */
+ line_len = PCF_LINE_LIMIT;
+#endif
+ }
+
+ /*
+ * Special processing for options that require a value.
+ */
+ else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
+ && (aval = argv[field + 1]) != 0) {
+
+ /* Force line wrap before option with value. */
+ line_len = PCF_LINE_LIMIT;
+
+ /*
+ * Optionally, expand $name in parameter value.
+ */
+ if (strcmp(arg, "-o") == 0
+ && (mode & PCF_SHOW_EVAL) != 0)
+ aval = pcf_expand_parameter_value((VSTRING *) 0, mode,
+ aval, masterp);
+
+ /*
+ * Keep option and value on the same line.
+ */
+ arg_len += strlen(aval) + 3;
+ if ((need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)]) != 0)
+ arg_len += 2;
+ }
+ } else {
+ need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
+ }
+
+ /*
+ * Insert a line break when the next item won't fit.
+ */
+ if (line_len > PCF_INDENT_LEN) {
+ if ((mode & PCF_FOLD_LINE) == 0
+ || line_len + 1 + arg_len < PCF_LINE_LIMIT) {
+ ADD_SPACE;
+ } else {
+ vstream_fputs("\n" PCF_INDENT_TEXT, fp);
+ line_len = PCF_INDENT_LEN;
+ }
+ }
+ if (in_daemon_options == 0 && need_parens)
+ ADD_TEXT("{", 1);
+ ADD_TEXT(arg, strlen(arg));
+ if (in_daemon_options == 0 && need_parens)
+ ADD_TEXT("}", 1);
+ if (aval) {
+ ADD_TEXT(" ", 1);
+ if (need_parens)
+ ADD_TEXT("{", 1);
+ ADD_TEXT(aval, strlen(aval));
+ if (need_parens)
+ ADD_TEXT("}", 1);
+ field += 1;
+
+ /* Force line wrap after option with value. */
+ line_len = PCF_LINE_LIMIT;
+
+ }
+ }
+ vstream_fputs("\n", fp);
+
+ if (msg_verbose)
+ vstream_fflush(fp);
+}
+
+/* pcf_show_master_entries - show master.cf entries */
+
+void pcf_show_master_entries(VSTREAM *fp, int mode, int argc, char **argv)
+{
+ PCF_MASTER_ENT *masterp;
+ PCF_MASTER_FLD_REQ *field_reqs;
+ PCF_MASTER_FLD_REQ *req;
+
+ /*
+ * Parse the filter expressions.
+ */
+ if (argc > 0) {
+ field_reqs = (PCF_MASTER_FLD_REQ *)
+ mymalloc(sizeof(*field_reqs) * argc);
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ req->match_count = 0;
+ req->raw_text = *argv++;
+ req->service_pattern =
+ pcf_parse_service_pattern(req->raw_text, 1, 2);
+ if (req->service_pattern == 0)
+ msg_fatal("-M option requires service_name[/type]");
+ }
+ }
+
+ /*
+ * Iterate over the master table.
+ */
+ for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
+ if (argc > 0) {
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
+ masterp->argv->argv[0],
+ masterp->argv->argv[1])) {
+ req->match_count++;
+ pcf_print_master_entry(fp, mode, masterp);
+ }
+ }
+ } else {
+ pcf_print_master_entry(fp, mode, masterp);
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (argc > 0) {
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ if (req->match_count == 0)
+ msg_warn("unmatched request: \"%s\"", req->raw_text);
+ argv_free(req->service_pattern);
+ }
+ myfree((void *) field_reqs);
+ }
+}
+
+/* pcf_print_master_field - scaffolding */
+
+static void pcf_print_master_field(VSTREAM *fp, int mode,
+ PCF_MASTER_ENT *masterp,
+ int field)
+{
+ char **argv = masterp->argv->argv;
+ const char *arg;
+ const char *aval;
+ int arg_len;
+ int line_len;
+ int in_daemon_options;
+ int need_parens;
+
+ /*
+ * Show the field value, or the first value in the case of a multi-column
+ * field.
+ */
+#define ADD_CHAR(ch) ADD_TEXT((ch), 1)
+
+ line_len = 0;
+ if ((mode & PCF_HIDE_NAME) == 0) {
+ ADD_TEXT(argv[0], strlen(argv[0]));
+ ADD_CHAR(PCF_NAMESP_SEP_STR);
+ ADD_TEXT(argv[1], strlen(argv[1]));
+ ADD_CHAR(PCF_NAMESP_SEP_STR);
+ ADD_TEXT(pcf_str_field_pattern(field), strlen(pcf_str_field_pattern(field)));
+ }
+ if ((mode & (PCF_HIDE_NAME | PCF_HIDE_VALUE)) == 0) {
+ ADD_TEXT(" = ", 3);
+ }
+ if ((mode & PCF_HIDE_VALUE) == 0) {
+ if (line_len > 0 && line_len + strlen(argv[field]) > PCF_LINE_LIMIT) {
+ vstream_fputs("\n" PCF_INDENT_TEXT, fp);
+ line_len = PCF_INDENT_LEN;
+ }
+ ADD_TEXT(argv[field], strlen(argv[field]));
+ }
+
+ /*
+ * Format the daemon command-line options and non-option arguments. Here,
+ * we have no data-dependent preference for column positions, but we do
+ * have argument grouping preferences.
+ */
+ if (field == PCF_MASTER_FLD_CMD && (mode & PCF_HIDE_VALUE) == 0) {
+ in_daemon_options = 1;
+ for (field += 1; (arg = argv[field]) != 0; field++) {
+ arg_len = strlen(arg);
+ aval = 0;
+ need_parens = 0;
+ if (in_daemon_options) {
+
+ /*
+ * We make no special case for generic options (-v -D)
+ * options.
+ */
+ if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+ in_daemon_options = 0;
+ } else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
+ && (aval = argv[field + 1]) != 0) {
+
+ /* Force line break before option with value. */
+ line_len = PCF_LINE_LIMIT;
+
+ /*
+ * Optionally, expand $name in parameter value.
+ */
+ if (strcmp(arg, "-o") == 0
+ && (mode & PCF_SHOW_EVAL) != 0)
+ aval = pcf_expand_parameter_value((VSTRING *) 0, mode,
+ aval, masterp);
+
+ /*
+ * Keep option and value on the same line.
+ */
+ arg_len += strlen(aval) + 1;
+ if ((need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)]) != 0)
+ arg_len += 2;
+ }
+ } else {
+ need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
+ }
+
+ /*
+ * Insert a line break when the next item won't fit.
+ */
+ if (line_len > PCF_INDENT_LEN) {
+ if ((mode & PCF_FOLD_LINE) == 0
+ || line_len + 1 + arg_len < PCF_LINE_LIMIT) {
+ ADD_SPACE;
+ } else {
+ vstream_fputs("\n" PCF_INDENT_TEXT, fp);
+ line_len = PCF_INDENT_LEN;
+ }
+ }
+ if (in_daemon_options == 0 && need_parens)
+ ADD_TEXT("{", 1);
+ ADD_TEXT(arg, strlen(arg));
+ if (in_daemon_options == 0 && need_parens)
+ ADD_TEXT("}", 1);
+ if (aval) {
+ ADD_SPACE;
+ if (need_parens)
+ ADD_TEXT("{", 1);
+ ADD_TEXT(aval, strlen(aval));
+ if (need_parens)
+ ADD_TEXT("}", 1);
+ field += 1;
+
+ /* Force line break after option with value. */
+ line_len = PCF_LINE_LIMIT;
+ }
+ }
+ }
+ vstream_fputs("\n", fp);
+
+ if (msg_verbose)
+ vstream_fflush(fp);
+}
+
+/* pcf_show_master_fields - show master.cf fields */
+
+void pcf_show_master_fields(VSTREAM *fp, int mode, int argc, char **argv)
+{
+ const char *myname = "pcf_show_master_fields";
+ PCF_MASTER_ENT *masterp;
+ PCF_MASTER_FLD_REQ *field_reqs;
+ PCF_MASTER_FLD_REQ *req;
+ int field;
+
+ /*
+ * Parse the filter expressions.
+ */
+ if (argc > 0) {
+ field_reqs = (PCF_MASTER_FLD_REQ *)
+ mymalloc(sizeof(*field_reqs) * argc);
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ req->match_count = 0;
+ req->raw_text = *argv++;
+ req->service_pattern =
+ pcf_parse_service_pattern(req->raw_text, 1, 3);
+ if (req->service_pattern == 0)
+ msg_fatal("-F option requires service_name[/type[/field]]");
+ field = req->field_pattern =
+ pcf_parse_field_pattern(req->service_pattern->argv[2]);
+ if (pcf_is_magic_field_pattern(field) == 0
+ && (field < 0 || field > PCF_MASTER_FLD_CMD))
+ msg_panic("%s: bad attribute field index: %d",
+ myname, field);
+ }
+ }
+
+ /*
+ * Iterate over the master table.
+ */
+ for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
+ if (argc > 0) {
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
+ masterp->argv->argv[0],
+ masterp->argv->argv[1])) {
+ req->match_count++;
+ field = req->field_pattern;
+ if (pcf_is_magic_field_pattern(field)) {
+ for (field = 0; field <= PCF_MASTER_FLD_CMD; field++)
+ pcf_print_master_field(fp, mode, masterp, field);
+ } else {
+ pcf_print_master_field(fp, mode, masterp, field);
+ }
+ }
+ }
+ } else {
+ for (field = 0; field <= PCF_MASTER_FLD_CMD; field++)
+ pcf_print_master_field(fp, mode, masterp, field);
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (argc > 0) {
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ if (req->match_count == 0)
+ msg_warn("unmatched request: \"%s\"", req->raw_text);
+ argv_free(req->service_pattern);
+ }
+ myfree((void *) field_reqs);
+ }
+}
+
+/* pcf_edit_master_field - replace master.cf field value. */
+
+void pcf_edit_master_field(PCF_MASTER_ENT *masterp, int field,
+ const char *new_value)
+{
+
+ /*
+ * Replace multi-column attribute.
+ */
+ if (field == PCF_MASTER_FLD_CMD) {
+ argv_truncate(masterp->argv, PCF_MASTER_FLD_CMD);
+ argv_splitq_append(masterp->argv, new_value, PCF_MASTER_BLANKS, CHARS_BRACE);
+ pcf_normalize_daemon_args(masterp->argv);
+ }
+
+ /*
+ * Replace single-column attribute.
+ */
+ else {
+ argv_replace_one(masterp->argv, field, new_value);
+ }
+
+ /*
+ * Do per-field sanity checks.
+ */
+ pcf_check_master_entry(masterp->argv, new_value);
+}
+
+/* pcf_print_master_param - scaffolding */
+
+static void pcf_print_master_param(VSTREAM *fp, int mode,
+ PCF_MASTER_ENT *masterp,
+ const char *param_name,
+ const char *param_value)
+{
+ if (mode & PCF_HIDE_VALUE) {
+ pcf_print_line(fp, mode, "%s%c%s\n",
+ masterp->name_space, PCF_NAMESP_SEP_CH,
+ param_name);
+ } else {
+ if ((mode & PCF_SHOW_EVAL) != 0)
+ param_value = pcf_expand_parameter_value((VSTRING *) 0, mode,
+ param_value, masterp);
+ if ((mode & PCF_HIDE_NAME) == 0) {
+ pcf_print_line(fp, mode, "%s%c%s = %s\n",
+ masterp->name_space, PCF_NAMESP_SEP_CH,
+ param_name, param_value);
+ } else {
+ pcf_print_line(fp, mode, "%s\n", param_value);
+ }
+ }
+ if (msg_verbose)
+ vstream_fflush(fp);
+}
+
+/* pcf_sort_argv_cb - sort argv call-back */
+
+static int pcf_sort_argv_cb(const void *a, const void *b)
+{
+ return (strcmp(*(char **) a, *(char **) b));
+}
+
+/* pcf_show_master_any_param - show any parameter in master.cf service entry */
+
+static void pcf_show_master_any_param(VSTREAM *fp, int mode,
+ PCF_MASTER_ENT *masterp)
+{
+ const char *myname = "pcf_show_master_any_param";
+ ARGV *argv = argv_alloc(10);
+ DICT *dict = masterp->all_params;
+ const char *param_name;
+ const char *param_value;
+ int param_count = 0;
+ int how;
+ char **cpp;
+
+ /*
+ * Print parameters in sorted order. The number of parameters per
+ * master.cf entry is small, so we optmiize for code simplicity and don't
+ * worry about the cost of double lookup.
+ */
+
+ /* Look up the parameter names and ignore the values. */
+
+ for (how = DICT_SEQ_FUN_FIRST;
+ dict->sequence(dict, how, &param_name, &param_value) == 0;
+ how = DICT_SEQ_FUN_NEXT) {
+ argv_add(argv, param_name, ARGV_END);
+ param_count++;
+ }
+
+ /* Print the parameters in sorted order. */
+
+ qsort(argv->argv, param_count, sizeof(argv->argv[0]), pcf_sort_argv_cb);
+ for (cpp = argv->argv; (param_name = *cpp) != 0; cpp++) {
+ if ((param_value = dict_get(dict, param_name)) == 0)
+ msg_panic("%s: parameter name not found: %s", myname, param_name);
+ pcf_print_master_param(fp, mode, masterp, param_name, param_value);
+ }
+
+ /*
+ * Clean up.
+ */
+ argv_free(argv);
+}
+
+/* pcf_show_master_params - show master.cf params */
+
+void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv)
+{
+ PCF_MASTER_ENT *masterp;
+ PCF_MASTER_FLD_REQ *field_reqs;
+ PCF_MASTER_FLD_REQ *req;
+ DICT *dict;
+ const char *param_value;
+
+ /*
+ * Parse the filter expressions.
+ */
+ if (argc > 0) {
+ field_reqs = (PCF_MASTER_FLD_REQ *)
+ mymalloc(sizeof(*field_reqs) * argc);
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ req->match_count = 0;
+ req->raw_text = *argv++;
+ req->service_pattern =
+ pcf_parse_service_pattern(req->raw_text, 1, 3);
+ if (req->service_pattern == 0)
+ msg_fatal("-P option requires service_name[/type[/parameter]]");
+ req->param_pattern = req->service_pattern->argv[2];
+ }
+ }
+
+ /*
+ * Iterate over the master table.
+ */
+ for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
+ if ((dict = masterp->all_params) != 0) {
+ if (argc > 0) {
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
+ masterp->argv->argv[0],
+ masterp->argv->argv[1])) {
+ if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) {
+ pcf_show_master_any_param(fp, mode, masterp);
+ req->match_count += 1;
+ } else if ((param_value = dict_get(dict,
+ req->param_pattern)) != 0) {
+ pcf_print_master_param(fp, mode, masterp,
+ req->param_pattern,
+ param_value);
+ req->match_count += 1;
+ }
+ }
+ }
+ } else {
+ pcf_show_master_any_param(fp, mode, masterp);
+ }
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (argc > 0) {
+ for (req = field_reqs; req < field_reqs + argc; req++) {
+ if (req->match_count == 0)
+ msg_warn("unmatched request: \"%s\"", req->raw_text);
+ argv_free(req->service_pattern);
+ }
+ myfree((void *) field_reqs);
+ }
+}
+
+/* pcf_edit_master_param - update, add or remove -o parameter=value */
+
+void pcf_edit_master_param(PCF_MASTER_ENT *masterp, int mode,
+ const char *param_name,
+ const char *param_value)
+{
+ const char *myname = "pcf_edit_master_param";
+ ARGV *argv = masterp->argv;
+ const char *arg;
+ const char *aval;
+ int param_match = 0;
+ int name_len = strlen(param_name);
+ int field;
+
+ for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
+ arg = argv->argv[field];
+
+ /*
+ * Stop at the first non-option argument or end-of-list.
+ */
+ if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+ break;
+ }
+
+ /*
+ * Zoom in on command-line options with a value.
+ */
+ else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
+ && (aval = argv->argv[field + 1]) != 0) {
+
+ /*
+ * Zoom in on "-o parameter=value".
+ */
+ if (strcmp(arg, "-o") == 0) {
+ if (strncmp(aval, param_name, name_len) == 0
+ && aval[name_len] == '=') {
+ param_match = 1;
+ switch (mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL)) {
+
+ /*
+ * Update parameter=value.
+ */
+ case PCF_EDIT_CONF:
+ aval = concatenate(param_name, "=",
+ param_value, (char *) 0);
+ argv_replace_one(argv, field + 1, aval);
+ myfree((void *) aval);
+ if (masterp->all_params)
+ dict_put(masterp->all_params, param_name, param_value);
+ /* XXX Update parameter "used/defined" status. */
+ break;
+
+ /*
+ * Delete parameter=value.
+ */
+ case PCF_EDIT_EXCL:
+ argv_delete(argv, field, 2);
+ if (masterp->all_params)
+ dict_del(masterp->all_params, param_name);
+ /* XXX Update parameter "used/defined" status. */
+ field -= 2;
+ break;
+ default:
+ msg_panic("%s: unexpected mode: %d", myname, mode);
+ }
+ }
+ }
+
+ /*
+ * Skip over the command-line option value.
+ */
+ field += 1;
+ }
+ }
+
+ /*
+ * Add unmatched parameter.
+ */
+ if ((mode & PCF_EDIT_CONF) && param_match == 0) {
+ /* XXX Generalize: argv_insert(argv, where, list...) */
+ argv_insert_one(argv, field, "-o");
+ aval = concatenate(param_name, "=",
+ param_value, (char *) 0);
+ argv_insert_one(argv, field + 1, aval);
+ if (masterp->all_params)
+ dict_put(masterp->all_params, param_name, param_value);
+ /* XXX May affect parameter "used/defined" status. */
+ myfree((void *) aval);
+ param_match = 1;
+ }
+}
diff --git a/src/postconf/postconf_match.c b/src/postconf/postconf_match.c
new file mode 100644
index 0000000..53709a4
--- /dev/null
+++ b/src/postconf/postconf_match.c
@@ -0,0 +1,188 @@
+/*++
+/* NAME
+/* postconf_match 3
+/* SUMMARY
+/* pattern-matching support
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* int pcf_parse_field_pattern(field_expr)
+/* char *field_expr;
+/*
+/* const char *pcf_str_field_pattern(field_pattern)
+/* int field_pattern;
+/*
+/* int pcf_is_magic_field_pattern(field_pattern)
+/* int field_pattern;
+/*
+/* ARGV *pcf_parse_service_pattern(service_expr, min_expr, max_expr)
+/* const char *service_expr;
+/* int min_expr;
+/* int max_expr;
+/*
+/* int PCF_IS_MAGIC_SERVICE_PATTERN(service_pattern)
+/* const ARGV *service_pattern;
+/*
+/* int PCF_MATCH_SERVICE_PATTERN(service_pattern, service_name,
+/* service_type)
+/* const ARGV *service_pattern;
+/* const char *service_name;
+/* const char *service_type;
+/*
+/* const char *pcf_str_field_pattern(field_pattern)
+/* int field_pattern;
+/*
+/* int PCF_IS_MAGIC_PARAM_PATTERN(param_pattern)
+/* const char *param_pattern;
+/*
+/* int PCF_MATCH_PARAM_PATTERN(param_pattern, param_name)
+/* const char *param_pattern;
+/* const char *param_name;
+/* DESCRIPTION
+/* pcf_parse_service_pattern() takes an expression and splits
+/* it up on '/' into an array of sub-expressions, This function
+/* returns null if the input does fewer than "min" or more
+/* than "max" sub-expressions.
+/*
+/* PCF_IS_MAGIC_SERVICE_PATTERN() returns non-zero if any of
+/* the service name or service type sub-expressions contains
+/* a matching operator (as opposed to string literals that
+/* only match themselves). This is an unsafe macro that evaluates
+/* its arguments more than once.
+/*
+/* PCF_MATCH_SERVICE_PATTERN() matches a service name and type
+/* from master.cf against the parsed pattern. This is an unsafe
+/* macro that evaluates its arguments more than once.
+/*
+/* pcf_parse_field_pattern() converts a field sub-expression,
+/* and returns the conversion result.
+/*
+/* pcf_str_field_pattern() converts a result from
+/* pcf_parse_field_pattern() into string form.
+/*
+/* pcf_is_magic_field_pattern() returns non-zero if the field
+/* pattern sub-expression contained a matching operator (as
+/* opposed to a string literal that only matches itself).
+/*
+/* PCF_IS_MAGIC_PARAM_PATTERN() returns non-zero if the parameter
+/* sub-expression contains a matching operator (as opposed to
+/* a string literal that only matches itself). This is an
+/* unsafe macro that evaluates its arguments more than once.
+/*
+/* PCF_MATCH_PARAM_PATTERN() matches a parameter name from
+/* master.cf against the parsed pattern. This is an unsafe
+/* macro that evaluates its arguments more than once.
+/*
+/* Arguments
+/* .IP field_expr
+/* A field expression.
+/* .IP service_expr
+/* This argument is split on '/' into its constituent
+/* sub-expressions.
+/* .IP min_expr
+/* The minimum number of sub-expressions in service_expr.
+/* .IP max_expr
+/* The maximum number of sub-expressions in service_expr.
+/* .IP service_name
+/* Service name from master.cf.
+/* .IP service_type
+/* Service type from master.cf.
+/* .IP param_pattern
+/* A parameter name expression.
+/* DIAGNOSTICS
+/* Fatal errors: invalid syntax.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <split_at.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+ * Conversion table. Each PCF_MASTER_NAME_XXX name entry must be stored at
+ * table offset PCF_MASTER_FLD_XXX. So don't mess it up.
+ */
+NAME_CODE pcf_field_name_offset[] = {
+ PCF_MASTER_NAME_SERVICE, PCF_MASTER_FLD_SERVICE,
+ PCF_MASTER_NAME_TYPE, PCF_MASTER_FLD_TYPE,
+ PCF_MASTER_NAME_PRIVATE, PCF_MASTER_FLD_PRIVATE,
+ PCF_MASTER_NAME_UNPRIV, PCF_MASTER_FLD_UNPRIV,
+ PCF_MASTER_NAME_CHROOT, PCF_MASTER_FLD_CHROOT,
+ PCF_MASTER_NAME_WAKEUP, PCF_MASTER_FLD_WAKEUP,
+ PCF_MASTER_NAME_MAXPROC, PCF_MASTER_FLD_MAXPROC,
+ PCF_MASTER_NAME_CMD, PCF_MASTER_FLD_CMD,
+ "*", PCF_MASTER_FLD_WILDC,
+ 0, PCF_MASTER_FLD_NONE,
+};
+
+/* pcf_parse_field_pattern - parse service attribute pattern */
+
+int pcf_parse_field_pattern(const char *field_name)
+{
+ int field_pattern;
+
+ if ((field_pattern = name_code(pcf_field_name_offset,
+ NAME_CODE_FLAG_STRICT_CASE,
+ field_name)) == PCF_MASTER_FLD_NONE)
+ msg_fatal("invalid service attribute name: \"%s\"", field_name);
+ return (field_pattern);
+}
+
+/* pcf_parse_service_pattern - parse service pattern */
+
+ARGV *pcf_parse_service_pattern(const char *pattern, int min_expr, int max_expr)
+{
+ ARGV *argv;
+ char **cpp;
+
+ /*
+ * Work around argv_split() lameness.
+ */
+ if (*pattern == '/')
+ return (0);
+ argv = argv_split(pattern, PCF_NAMESP_SEP_STR);
+ if (argv->argc < min_expr || argv->argc > max_expr) {
+ argv_free(argv);
+ return (0);
+ }
+
+ /*
+ * Allow '*' only all by itself.
+ */
+ for (cpp = argv->argv; *cpp; cpp++) {
+ if (!PCF_MATCH_ANY(*cpp) && strchr(*cpp, PCF_MATCH_WILDC_STR[0]) != 0) {
+ argv_free(argv);
+ return (0);
+ }
+ }
+
+ /*
+ * Provide defaults for missing fields.
+ */
+ while (argv->argc < max_expr)
+ argv_add(argv, PCF_MATCH_WILDC_STR, ARGV_END);
+ return (argv);
+}
diff --git a/src/postconf/postconf_misc.c b/src/postconf/postconf_misc.c
new file mode 100644
index 0000000..0107651
--- /dev/null
+++ b/src/postconf/postconf_misc.c
@@ -0,0 +1,60 @@
+/*++
+/* NAME
+/* postconf_misc 3
+/* SUMMARY
+/* miscellaneous low-level code
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_set_config_dir()
+/* DESCRIPTION
+/* pcf_set_config_dir() forcibly overrides the var_config_dir
+/* parameter setting with the value from the environment or
+/* with the default pathname, and updates the mail parameter
+/* dictionary.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <safe.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* pcf_set_config_dir - forcibly override var_config_dir */
+
+void pcf_set_config_dir(void)
+{
+ char *config_dir;
+
+ if (var_config_dir)
+ myfree(var_config_dir);
+ if ((config_dir = safe_getenv(CONF_ENV_PATH)) != 0) {
+ var_config_dir = mystrdup(config_dir);
+ set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
+ } else {
+ var_config_dir = mystrdup(DEF_CONFIG_DIR);
+ }
+}
diff --git a/src/postconf/postconf_node.c b/src/postconf/postconf_node.c
new file mode 100644
index 0000000..dca3594
--- /dev/null
+++ b/src/postconf/postconf_node.c
@@ -0,0 +1,185 @@
+/*++
+/* NAME
+/* postconf_node 3
+/* SUMMARY
+/* low-level parameter node support
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* PCF_PARAM_TABLE *PCF_PARAM_TABLE_CREATE(size)
+/* ssize_t size;
+/*
+/* PCF_PARAM_INFO **PCF_PARAM_TABLE_LIST(table)
+/* PCF_PARAM_TABLE *table;
+/*
+/* const char *PCF_PARAM_INFO_NAME(info)
+/* PCF_PARAM_INFO *info;
+/*
+/* PCF_PARAM_NODE *PCF_PARAM_INFO_NODE(info)
+/* PCF_PARAM_INFO *info;
+/*
+/* PCF_PARAM_NODE *PCF_PARAM_TABLE_FIND(table, name)
+/* PCF_PARAM_TABLE *table;
+/* const char *name;
+/*
+/* PCF_PARAM_INFO *PCF_PARAM_TABLE_LOCATE(table, name)
+/* PCF_PARAM_TABLE *table;
+/* const char *name;
+/*
+/* PCF_PARAM_INFO *PCF_PARAM_TABLE_ENTER(table, name, flags,
+/* param_data, convert_fn)
+/* PCF_PARAM_TABLE *table;
+/* const char *name;
+/* int flags;
+/* void *param_data;
+/* const char *(*convert_fn)(void *);
+/*
+/* PCF_PARAM_NODE *pcf_make_param_node(flags, param_data, convert_fn)
+/* int flags;
+/* void *param_data;
+/* const char *(*convert_fn) (void *);
+/*
+/* const char *pcf_convert_param_node(mode, name, node)
+/* int mode;
+/* const char *name;
+/* PCF_PARAM_NODE *node;
+/*
+/* VSTRING *pcf_param_string_buf;
+/*
+/* PCF_RAW_PARAMETER(node)
+/* const PCF_PARAM_NODE *node;
+/* DESCRIPTION
+/* This module maintains data structures (PCF_PARAM_NODE) with
+/* information about known-legitimate parameters. These data
+/* structures are stored in a hash table.
+/*
+/* The PCF_PARAM_MUMBLE() macros are wrappers around the
+/* htable(3) module. Their sole purpose is to encapsulate all
+/* the pointer casting from and to (PCF_PARAM_NODE *). Apart
+/* from that, the macros have no features worth discussing.
+/*
+/* pcf_make_param_node() creates a node for the global parameter
+/* table. This node provides a parameter default value, and a
+/* function that converts the default value to string.
+/*
+/* pcf_convert_param_node() produces a string representation
+/* for a global parameter default value.
+/*
+/* PCF_RAW_PARAMETER() returns non-zero if the specified
+/* parameter node represents a "raw parameter". The value of
+/* such parameters must not be scanned for macro names. Some
+/* "raw parameter" values contain "$" without macros, such as
+/* the smtpd_expansion_filter "safe character" set; and some
+/* contain $name from a private name space, such as forward_path.
+/* Some "raw parameter" values in postscreen(8) are safe to
+/* expand by one level. Support for that may be added later.
+/*
+/* pcf_param_string_buf is a buffer that is initialized on the
+/* fly and that parameter-to-string conversion functions may
+/* use for temporary result storage.
+/*
+/* Arguments:
+/* .IP size
+/* The initial size of the hash table.
+/* .IP table
+/* A hash table for storage of "valid parameter" information.
+/* .IP info
+/* A data structure with a name component and a PCF_PARAM_NODE
+/* component. Use PCF_PARAM_INFO_NAME() and PCF_PARAM_INFO_NODE()
+/* to access these components.
+/* .IP name
+/* The name of a "valid parameter".
+/* .IP flags
+/* PCF_PARAM_FLAG_RAW for a "raw parameter", PCF_PARAM_FLAG_NONE
+/* otherwise. See the PCF_RAW_PARAMETER() discussion above for
+/* discussion of "raw parameter" values.
+/* .IP param_data
+/* Information about the parameter value. Specify PCF_PARAM_NO_DATA
+/* if this is not applicable.
+/* .IP convert_fn
+/* The function that will be invoked to produce a string
+/* representation of the information in param_data. The function
+/* receives the param_data value as argument.
+/* .IP mode
+/* For now, the PCF_SHOW_DEFS flag is required.
+/* .IP name
+/* The name of the parameter whose value is requested. This
+/* is used for diagnostics.
+/* .IP node
+/* The (flags, param_data, convert_fn) information that needs
+/* to be converted to a string representation of the default
+/* value.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+VSTRING *pcf_param_string_buf;
+
+/* pcf_make_param_node - make node for global parameter table */
+
+PCF_PARAM_NODE *pcf_make_param_node(int flags, void *param_data,
+ const char *(*convert_fn) (void *))
+{
+ PCF_PARAM_NODE *node;
+
+ node = (PCF_PARAM_NODE *) mymalloc(sizeof(*node));
+ node->flags = flags;
+ node->param_data = param_data;
+ node->convert_fn = convert_fn;
+ return (node);
+}
+
+/* pcf_convert_param_node - get default parameter value */
+
+const char *pcf_convert_param_node(int mode, const char *name, PCF_PARAM_NODE *node)
+{
+ const char *myname = "pcf_convert_param_node";
+ const char *value;
+
+ /*
+ * One-off initialization.
+ */
+ if (pcf_param_string_buf == 0)
+ pcf_param_string_buf = vstring_alloc(100);
+
+ /*
+ * Sanity check. A null value indicates that a parameter does not have
+ * the requested value. At this time, the only requested value can be the
+ * default value, and a null pointer value makes no sense here.
+ */
+ if ((mode & PCF_SHOW_DEFS) == 0)
+ msg_panic("%s: request for non-default value of parameter %s",
+ myname, name);
+ if ((value = node->convert_fn(node->param_data)) == 0)
+ msg_panic("%s: parameter %s has null pointer default value",
+ myname, name);
+
+ /*
+ * Return the parameter default value.
+ */
+ return (value);
+}
diff --git a/src/postconf/postconf_other.c b/src/postconf/postconf_other.c
new file mode 100644
index 0000000..0c4c0c4
--- /dev/null
+++ b/src/postconf/postconf_other.c
@@ -0,0 +1,141 @@
+/*++
+/* NAME
+/* postconf_other 3
+/* SUMMARY
+/* support for miscellaneous information categories
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_show_maps()
+/*
+/* void pcf_show_locks()
+/*
+/* void pcf_show_sasl(mode)
+/* int mode;
+/*
+/* void pcf_show_tls(what)
+/* const char *what;
+/* DESCRIPTION
+/* pcf_show_maps() lists the available map (lookup table)
+/* types.
+/*
+/* pcf_show_locks() lists the available mailbox lock types.
+/*
+/* pcf_show_sasl() shows the available SASL authentication
+/* plugin types.
+/*
+/* pcf_show_tls() reports the "compile-version" or "run-version"
+/* of the TLS library, or the supported public-key algorithms.
+/*
+/* Arguments:
+/* .IP mode
+/* Show server information if the PCF_SHOW_SASL_SERV flag is
+/* set, otherwise show client information.
+/* .IP what
+/* One of the literals "compile-version", "run-version" or
+/* "public-key-algorithms".
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstream.h>
+#include <argv.h>
+#include <dict.h>
+#include <msg.h>
+
+/* Global library. */
+
+#include <mbox_conf.h>
+
+/* XSASL library. */
+
+#include <xsasl.h>
+
+/* TLS library. */
+
+#include <tls.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* pcf_show_maps - show available maps */
+
+void pcf_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);
+}
+
+/* pcf_show_locks - show available mailbox locking methods */
+
+void pcf_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);
+}
+
+/* pcf_show_sasl - show SASL plug-in types */
+
+void pcf_show_sasl(int what)
+{
+ ARGV *sasl_argv;
+ int i;
+
+ sasl_argv = (what & PCF_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);
+}
+
+/* pcf_show_tls - show TLS support */
+
+void pcf_show_tls(const char *what)
+{
+#ifdef USE_TLS
+ if (strcmp(what, "compile-version") == 0)
+ vstream_printf("%s\n", tls_compile_version());
+ else if (strcmp(what, "run-version") == 0)
+ vstream_printf("%s\n", tls_run_version());
+ else if (strcmp(what, "public-key-algorithms") == 0) {
+ const char **cpp;
+
+ for (cpp = tls_pkey_algorithms(); *cpp; cpp++)
+ vstream_printf("%s\n", *cpp);
+ } else {
+ msg_warn("unknown 'postconf -T' mode: %s", what);
+ exit(1);
+ }
+#endif /* USE_TLS */
+}
diff --git a/src/postconf/postconf_print.c b/src/postconf/postconf_print.c
new file mode 100644
index 0000000..32c4015
--- /dev/null
+++ b/src/postconf/postconf_print.c
@@ -0,0 +1,114 @@
+/*++
+/* NAME
+/* postconf_print 3
+/* SUMMARY
+/* basic line printing support
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_print_line(fp, mode, const char *fmt, ...)
+/* VSTREAM *fp;
+/* int mode;
+/* const char *fmt;
+/* DESCRIPTION
+/* pcf_print_line() formats text, normalized whitespace, and
+/* optionally folds long lines.
+/*
+/* Arguments:
+/* .IP fp
+/* Output stream.
+/* .IP mode
+/* Bit-wise OR of zero or more of the following (other flags
+/* are ignored):
+/* .RS
+/* .IP PCF_FOLD_LINE
+/* Fold long lines.
+/* .RE
+/* .IP fmt
+/* Format string.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* SLMs. */
+
+#define STR(x) vstring_str(x)
+
+/* pcf_print_line - show line possibly folded, and with normalized whitespace */
+
+void pcf_print_line(VSTREAM *fp, int mode, const char *fmt,...)
+{
+ va_list ap;
+ static VSTRING *buf = 0;
+ char *start;
+ char *next;
+ int line_len = 0;
+ int word_len;
+
+ /*
+ * One-off initialization.
+ */
+ if (buf == 0)
+ buf = vstring_alloc(100);
+
+ /*
+ * Format the text.
+ */
+ va_start(ap, fmt);
+ vstring_vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ /*
+ * Normalize the whitespace. We don't use the line_wrap() routine because
+ * 1) that function does not normalize whitespace between words and 2) we
+ * want to normalize whitespace even when not wrapping lines.
+ *
+ * XXX Some parameters preserve whitespace: for example, smtpd_banner and
+ * smtpd_reject_footer. If we have to preserve whitespace between words,
+ * then perhaps readlline() can be changed to canonicalize whitespace
+ * that follows a newline.
+ */
+ for (start = STR(buf); *(start += strspn(start, PCF_SEPARATORS)) != 0; start = next) {
+ word_len = strcspn(start, PCF_SEPARATORS);
+ if (*(next = start + word_len) != 0)
+ *next++ = 0;
+ if (word_len > 0 && line_len > 0) {
+ if ((mode & PCF_FOLD_LINE) == 0
+ || line_len + word_len < PCF_LINE_LIMIT) {
+ vstream_fputs(" ", fp);
+ line_len += 1;
+ } else {
+ vstream_fputs("\n" PCF_INDENT_TEXT, fp);
+ line_len = PCF_INDENT_LEN;
+ }
+ }
+ vstream_fputs(start, fp);
+ line_len += word_len;
+ }
+ vstream_fputs("\n", fp);
+}
diff --git a/src/postconf/postconf_service.c b/src/postconf/postconf_service.c
new file mode 100644
index 0000000..38a4cce
--- /dev/null
+++ b/src/postconf/postconf_service.c
@@ -0,0 +1,199 @@
+/*++
+/* NAME
+/* postconf_service 3
+/* SUMMARY
+/* service-defined main.cf parameter name support
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_register_service_parameters()
+/* DESCRIPTION
+/* Service-defined parameter names are created by appending
+/* postfix-defined suffixes to master.cf service names. All
+/* service-defined parameters have default values that are
+/* defined by a built-in parameter.
+/*
+/* pcf_register_service_parameters() adds the service-defined
+/* parameters to the global name space. This function must be
+/* called after the built-in parameters are added to the global
+/* name space, and after the master.cf file is read.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <argv.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+ * Basename of programs in $daemon_directory. XXX These belong in a header
+ * file, or they should be made configurable.
+ */
+#ifndef MAIL_PROGRAM_LOCAL
+#define MAIL_PROGRAM_LOCAL "local"
+#define MAIL_PROGRAM_ERROR "error"
+#define MAIL_PROGRAM_VIRTUAL "virtual"
+#define MAIL_PROGRAM_SMTP "smtp"
+#define MAIL_PROGRAM_LMTP "lmtp"
+#define MAIL_PROGRAM_PIPE "pipe"
+#define MAIL_PROGRAM_SPAWN "spawn"
+#endif
+
+ /*
+ * Ad-hoc name-value string pair.
+ */
+typedef struct {
+ const char *name;
+ const char *value;
+} PCF_STRING_NV;
+
+#define STR(x) vstring_str(x)
+
+/* pcf_convert_service_parameter - get service parameter string value */
+
+static const char *pcf_convert_service_parameter(void *ptr)
+{
+ return (STR(vstring_sprintf(pcf_param_string_buf, "$%s", (char *) ptr)));
+}
+
+/* pcf_register_service_parameter - add service parameter name and default */
+
+static void pcf_register_service_parameter(const char *service,
+ const char *suffix,
+ const char *defparam)
+{
+ char *name = concatenate(service, suffix, (char *) 0);
+ PCF_PARAM_NODE *node;
+
+ /*
+ * Skip service parameter names that have built-in definitions. This
+ * happens with message delivery transports that have a non-default
+ * per-destination concurrency or recipient limit, such as local(8).
+ *
+ * Some parameters were tentatively flagged as built-in, but they are
+ * service parameters with their own default value. We don't change the
+ * default but we correct the parameter class.
+ */
+ if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, name)) != 0) {
+ PCF_PARAM_CLASS_OVERRIDE(node, PCF_PARAM_FLAG_SERVICE);
+ } else {
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, name, PCF_PARAM_FLAG_SERVICE,
+ (void *) defparam, pcf_convert_service_parameter);
+ }
+ myfree(name);
+}
+
+/* pcf_register_service_parameters - add all service parameters with defaults */
+
+void pcf_register_service_parameters(void)
+{
+ const char *myname = "pcf_register_service_parameters";
+ static const PCF_STRING_NV pipe_params[] = {
+ /* suffix, default parameter name */
+ _MAXTIME, VAR_COMMAND_MAXTIME,
+#define service_params (pipe_params + 1)
+ _XPORT_RCPT_LIMIT, VAR_XPORT_RCPT_LIMIT,
+ _STACK_RCPT_LIMIT, VAR_STACK_RCPT_LIMIT,
+ _XPORT_REFILL_LIMIT, VAR_XPORT_REFILL_LIMIT,
+ _XPORT_REFILL_DELAY, VAR_XPORT_REFILL_DELAY,
+ _DELIVERY_SLOT_COST, VAR_DELIVERY_SLOT_COST,
+ _DELIVERY_SLOT_LOAN, VAR_DELIVERY_SLOT_LOAN,
+ _DELIVERY_SLOT_DISCOUNT, VAR_DELIVERY_SLOT_DISCOUNT,
+ _MIN_DELIVERY_SLOTS, VAR_MIN_DELIVERY_SLOTS,
+ _INIT_DEST_CON, VAR_INIT_DEST_CON,
+ _DEST_CON_LIMIT, VAR_DEST_CON_LIMIT,
+ _DEST_RCPT_LIMIT, VAR_DEST_RCPT_LIMIT,
+ _CONC_POS_FDBACK, VAR_CONC_POS_FDBACK,
+ _CONC_NEG_FDBACK, VAR_CONC_NEG_FDBACK,
+ _CONC_COHORT_LIM, VAR_CONC_COHORT_LIM,
+ _DEST_RATE_DELAY, VAR_DEST_RATE_DELAY,
+ _XPORT_RATE_DELAY, VAR_XPORT_RATE_DELAY,
+ 0,
+ };
+ static const PCF_STRING_NV spawn_params[] = {
+ /* suffix, default parameter name */
+ _MAXTIME, VAR_COMMAND_MAXTIME,
+ 0,
+ };
+ typedef struct {
+ const char *progname;
+ const PCF_STRING_NV *params;
+ } PCF_SERVICE_DEF;
+ static const PCF_SERVICE_DEF service_defs[] = {
+ MAIL_PROGRAM_LOCAL, service_params,
+ MAIL_PROGRAM_ERROR, service_params,
+ MAIL_PROGRAM_VIRTUAL, service_params,
+ MAIL_PROGRAM_SMTP, service_params,
+ MAIL_PROGRAM_LMTP, service_params,
+ MAIL_PROGRAM_PIPE, pipe_params,
+ MAIL_PROGRAM_SPAWN, spawn_params,
+ 0,
+ };
+ const PCF_STRING_NV *sp;
+ const char *progname;
+ const char *service;
+ PCF_MASTER_ENT *masterp;
+ ARGV *argv;
+ const PCF_SERVICE_DEF *sd;
+
+ /*
+ * Sanity checks.
+ */
+ if (pcf_param_table == 0)
+ msg_panic("%s: global parameter table is not initialized", myname);
+ if (pcf_master_table == 0)
+ msg_panic("%s: master table is not initialized", myname);
+
+ /*
+ * Extract service names from master.cf and generate service parameter
+ * information.
+ */
+ for (masterp = pcf_master_table; (argv = masterp->argv) != 0; masterp++) {
+
+ /*
+ * Add service parameters for message delivery transports or spawn
+ * programs.
+ */
+ progname = argv->argv[7];
+ for (sd = service_defs; sd->progname; sd++) {
+ if (strcmp(sd->progname, progname) == 0) {
+ service = argv->argv[0];
+ for (sp = sd->params; sp->name; sp++)
+ pcf_register_service_parameter(service, sp->name, sp->value);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/postconf/postconf_unused.c b/src/postconf/postconf_unused.c
new file mode 100644
index 0000000..d4416f8
--- /dev/null
+++ b/src/postconf/postconf_unused.c
@@ -0,0 +1,129 @@
+/*++
+/* NAME
+/* postconf_unused 3
+/* SUMMARY
+/* report unused parameters
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_flag_unused_main_parameters()
+/*
+/* void pcf_flag_unused_master_parameters()
+/* DESCRIPTION
+/* These functions must be called after all parameter information
+/* is initialized: built-ins, service-defined and user-defined.
+/* In other words, don't call these functions with "postconf
+/* -d" which ignores user-defined main.cf settings.
+/*
+/* pcf_flag_unused_main_parameters() reports unused "name=value"
+/* entries in main.cf.
+/*
+/* pcf_flag_unused_master_parameters() reports unused "-o
+/* name=value" entries in master.cf.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <dict.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* pcf_flag_unused_parameters - warn about unused parameters */
+
+static void pcf_flag_unused_parameters(DICT *dict, const char *conf_name,
+ PCF_MASTER_ENT *local_scope)
+{
+ const char *myname = "pcf_flag_unused_parameters";
+ const char *param_name;
+ const char *param_value;
+ int how;
+
+ /*
+ * Sanity checks.
+ */
+ if (pcf_param_table == 0)
+ msg_panic("%s: global parameter table is not initialized", myname);
+
+ /*
+ * Iterate over all entries, and flag parameter names that aren't used
+ * anywhere. Show the warning message at the end of the output.
+ */
+ if (dict->sequence == 0)
+ msg_panic("%s: parameter dictionary %s has no iterator",
+ myname, conf_name);
+ for (how = DICT_SEQ_FUN_FIRST;
+ dict->sequence(dict, how, &param_name, &param_value) == 0;
+ how = DICT_SEQ_FUN_NEXT) {
+ if (PCF_PARAM_TABLE_LOCATE(pcf_param_table, param_name) == 0
+ && (local_scope == 0
+ || PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, param_name) == 0)) {
+ vstream_fflush(VSTREAM_OUT);
+ msg_warn("%s/%s: unused parameter: %s=%s",
+ var_config_dir, conf_name, param_name, param_value);
+ }
+ }
+}
+
+/* pcf_flag_unused_main_parameters - warn about unused parameters */
+
+void pcf_flag_unused_main_parameters(void)
+{
+ const char *myname = "pcf_flag_unused_main_parameters";
+ DICT *dict;
+
+ /*
+ * Iterate over all main.cf entries, and flag parameter names that aren't
+ * used anywhere.
+ */
+ if ((dict = dict_handle(CONFIG_DICT)) == 0)
+ msg_panic("%s: parameter dictionary %s not found",
+ myname, CONFIG_DICT);
+ pcf_flag_unused_parameters(dict, MAIN_CONF_FILE, (PCF_MASTER_ENT *) 0);
+}
+
+/* pcf_flag_unused_master_parameters - warn about unused parameters */
+
+void pcf_flag_unused_master_parameters(void)
+{
+ const char *myname = "pcf_flag_unused_master_parameters";
+ PCF_MASTER_ENT *masterp;
+ DICT *dict;
+
+ /*
+ * Sanity checks.
+ */
+ if (pcf_master_table == 0)
+ msg_panic("%s: master table is not initialized", myname);
+
+ /*
+ * Iterate over all master.cf entries, and flag parameter names that
+ * aren't used anywhere.
+ */
+ for (masterp = pcf_master_table; masterp->argv != 0; masterp++)
+ if ((dict = masterp->all_params) != 0)
+ pcf_flag_unused_parameters(dict, MASTER_CONF_FILE, masterp);
+}
diff --git a/src/postconf/postconf_user.c b/src/postconf/postconf_user.c
new file mode 100644
index 0000000..8d0e726
--- /dev/null
+++ b/src/postconf/postconf_user.c
@@ -0,0 +1,422 @@
+/*++
+/* NAME
+/* postconf_user 3
+/* SUMMARY
+/* support for user-defined main.cf parameter names
+/* SYNOPSIS
+/* #include <postconf.h>
+/*
+/* void pcf_register_user_parameters()
+/* DESCRIPTION
+/* Postfix has multiple parameter name spaces: the global
+/* main.cf parameter name space, and the local parameter name
+/* space of each master.cf entry. Parameters in local name
+/* spaces take precedence over global parameters.
+/*
+/* There are three categories of known parameter names: built-in,
+/* service-defined (see postconf_service.c), and valid
+/* user-defined.
+/*
+/* There are two categories of valid user-defined parameter
+/* names:
+/*
+/* - Parameters whose user-defined-name appears in the value
+/* of smtpd_restriction_classes in main.cf or master.cf.
+/*
+/* - Parameters whose $user-defined-name appear in the value
+/* of "name=value" entries in main.cf or master.cf.
+/*
+/* - In both cases the parameters must have a
+/* "user-defined-name=value" entry in main.cf or master.cf.
+/*
+/* Other user-defined parameter names are flagged as "unused".
+/*
+/* pcf_register_user_parameters() scans the global and per-service
+/* name spaces for user-defined parameters and flags parameters
+/* as "valid" in the global name space (pcf_param_table) or
+/* in the per-service name space (valid_params).
+/*
+/* This function also invokes pcf_register_dbms_parameters() to
+/* to instantiate legacy per-dbms parameters, and to examine
+/* per-dbms configuration files. This is limited to the content
+/* of global and local, built-in and per-service, parameters.
+/* DIAGNOSTICS
+/* Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <mac_expand.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_conf.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+ * Hash with all user-defined names in the global smtpd_restriction_classes
+ * value. This is used when validating "-o user-defined-name=value" entries
+ * in master.cf.
+ */
+static HTABLE *pcf_rest_class_table;
+
+ /*
+ * SLMs.
+ */
+#define STR(x) vstring_str(x)
+
+ /*
+ * Macros to make code with obscure constants more readable.
+ */
+#define NO_SCAN_RESULT ((VSTRING *) 0)
+#define NO_SCAN_FILTER ((char *) 0)
+
+/* SCAN_USER_PARAMETER_VALUE - examine macro names in parameter value */
+
+#define SCAN_USER_PARAMETER_VALUE(value, class, scope) do { \
+ PCF_PARAM_CTX _ctx; \
+ _ctx.local_scope = (scope); \
+ _ctx.param_class = (class); \
+ (void) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \
+ NO_SCAN_FILTER, pcf_flag_user_parameter_wrapper, (void *) &_ctx); \
+} while (0)
+
+/* pcf_convert_user_parameter - get user-defined parameter string value */
+
+static const char *pcf_convert_user_parameter(void *unused_ptr)
+{
+ return (""); /* can't happen */
+}
+
+/* pcf_flag_user_parameter - flag user-defined name "valid" if it has name=value */
+
+static const char *pcf_flag_user_parameter(const char *mac_name,
+ int param_class,
+ PCF_MASTER_ENT *local_scope)
+{
+ const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE;
+ int user_supplied = 0;
+
+ /*
+ * If the name=value exists in the local (or global) name space, update
+ * the local (or global) "valid" parameter name table.
+ *
+ * Do not "validate" user-defined parameters whose name appears only as
+ * macro expansion; this is how Postfix historically implements backwards
+ * compatibility after a feature name change.
+ */
+ if (local_scope && dict_get(local_scope->all_params, mac_name)) {
+ user_supplied = 1;
+ /* $name in master.cf references name=value in master.cf. */
+ if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0) {
+ PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name,
+ param_class, PCF_PARAM_NO_DATA,
+ pcf_convert_user_parameter);
+ if (msg_verbose)
+ msg_info("$%s in %s:%s validates %s=value in %s:%s",
+ mac_name, MASTER_CONF_FILE,
+ local_scope->name_space,
+ mac_name, MASTER_CONF_FILE,
+ local_scope->name_space);
+ }
+ } else if (mail_conf_lookup(mac_name) != 0) {
+ user_supplied = 1;
+ /* $name in main/master.cf references name=value in main.cf. */
+ if (PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0) {
+ PCF_PARAM_TABLE_ENTER(pcf_param_table, mac_name, param_class,
+ PCF_PARAM_NO_DATA, pcf_convert_user_parameter);
+ if (msg_verbose) {
+ if (local_scope)
+ msg_info("$%s in %s:%s validates %s=value in %s",
+ mac_name, MASTER_CONF_FILE,
+ local_scope->name_space,
+ mac_name, MAIN_CONF_FILE);
+ else
+ msg_info("$%s in %s validates %s=value in %s",
+ mac_name, MAIN_CONF_FILE,
+ mac_name, MAIN_CONF_FILE);
+ }
+ }
+ }
+ if (local_scope == 0) {
+ for (local_scope = pcf_master_table; local_scope->argv; local_scope++) {
+ if (local_scope->all_params != 0
+ && dict_get(local_scope->all_params, mac_name) != 0) {
+ user_supplied = 1;
+ /* $name in main.cf references name=value in master.cf. */
+ if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0) {
+ PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name,
+ param_class, PCF_PARAM_NO_DATA,
+ pcf_convert_user_parameter);
+ if (msg_verbose)
+ msg_info("$%s in %s validates %s=value in %s:%s",
+ mac_name, MAIN_CONF_FILE,
+ mac_name, MASTER_CONF_FILE,
+ local_scope->name_space);
+ }
+ }
+ }
+ }
+
+ /*
+ * Warn about a $name that has no user-supplied explicit value or
+ * Postfix-supplied default value. We don't enforce this for legacy DBMS
+ * parameters because they exist only for backwards compatibility, so we
+ * don't bother to figure out which parameters come without defaults.
+ */
+ if (user_supplied == 0 && (param_class & PCF_PARAM_FLAG_DBMS) == 0
+ && PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0)
+ msg_warn("%s/%s: undefined parameter: %s",
+ var_config_dir, source, mac_name);
+ return (0);
+}
+
+/* pcf_flag_user_parameter_wrapper - mac_expand call-back helper */
+
+static const char *pcf_flag_user_parameter_wrapper(const char *mac_name,
+ int unused_mode,
+ void *context)
+{
+ PCF_PARAM_CTX *ctx = (PCF_PARAM_CTX *) context;
+
+ return (pcf_flag_user_parameter(mac_name, ctx->param_class, ctx->local_scope));
+}
+
+/* pcf_lookup_eval - generalized mail_conf_lookup_eval */
+
+static const char *pcf_lookup_eval(const char *dict_name, const char *name)
+{
+ const char *value;
+
+#define RECURSIVE 1
+
+ if ((value = dict_lookup(dict_name, name)) != 0)
+ value = dict_eval(dict_name, value, RECURSIVE);
+ return (value);
+}
+
+/* pcf_scan_user_parameter_namespace - scan parameters in name space */
+
+static void pcf_scan_user_parameter_namespace(const char *dict_name,
+ PCF_MASTER_ENT *local_scope)
+{
+ const char *myname = "pcf_scan_user_parameter_namespace";
+ const char *class_list;
+ char *saved_class_list;
+ char *cp;
+ DICT *dict;
+ char *param_name;
+ int how;
+ const char *cparam_name;
+ const char *cparam_value;
+ PCF_PARAM_NODE *node;
+ const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE;
+
+ /*
+ * Flag parameter names in smtpd_restriction_classes as "valid", but only
+ * if they have a "name=value" entry. If we are in not in a local name
+ * space, update the global restriction class name table, so that we can
+ * query the global table from within a local master.cf name space.
+ */
+ if ((class_list = pcf_lookup_eval(dict_name, VAR_REST_CLASSES)) != 0) {
+ cp = saved_class_list = mystrdup(class_list);
+ while ((param_name = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
+ if (local_scope == 0
+ && htable_locate(pcf_rest_class_table, param_name) == 0)
+ htable_enter(pcf_rest_class_table, param_name, "");
+ pcf_flag_user_parameter(param_name, PCF_PARAM_FLAG_USER, local_scope);
+ }
+ myfree(saved_class_list);
+ }
+
+ /*
+ * For all "name=value" instances: a) if the name space is local and the
+ * name appears in the global restriction class table, flag the name as
+ * "valid" in the local name space; b) scan the value for macro
+ * expansions of unknown parameter names, and flag those parameter names
+ * as "valid" if they have a "name=value" entry.
+ *
+ * We delete name=value entries for read-only parameters, to maintain
+ * compatibility with Postfix programs that ignore such settings.
+ */
+ if ((dict = dict_handle(dict_name)) == 0)
+ msg_panic("%s: parameter dictionary %s not found",
+ myname, dict_name);
+ if (dict->sequence == 0)
+ msg_panic("%s: parameter dictionary %s has no iterator",
+ myname, dict_name);
+ for (how = DICT_SEQ_FUN_FIRST;
+ dict->sequence(dict, how, &cparam_name, &cparam_value) == 0;
+ how = DICT_SEQ_FUN_NEXT) {
+ if (local_scope != 0
+ && PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, cparam_name) == 0
+ && htable_locate(pcf_rest_class_table, cparam_name) != 0)
+ PCF_PARAM_TABLE_ENTER(local_scope->valid_names, cparam_name,
+ PCF_PARAM_FLAG_USER, PCF_PARAM_NO_DATA,
+ pcf_convert_user_parameter);
+ if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, cparam_name)) != 0) {
+ if (PCF_READONLY_PARAMETER(node)) {
+ msg_warn("%s/%s: read-only parameter assignment: %s=%s",
+ var_config_dir, source, cparam_name, cparam_value);
+ /* Can't use dict_del() with Postfix<2.10 htable_sequence(). */
+ if (dict_del(dict, cparam_name) != 0)
+ msg_panic("%s: can't delete %s/%s parameter entry for %s",
+ myname, var_config_dir, source, cparam_name);
+ continue;
+ }
+ /* Re-label legacy parameter as user-defined, so it's printed. */
+ if (PCF_LEGACY_PARAMETER(node))
+ PCF_PARAM_CLASS_OVERRIDE(node, PCF_PARAM_FLAG_USER);
+ /* Skip "do not expand" parameters. */
+ if (PCF_RAW_PARAMETER(node))
+ continue;
+ }
+ SCAN_USER_PARAMETER_VALUE(cparam_value, PCF_PARAM_FLAG_USER, local_scope);
+#ifdef LEGACY_DBMS_SUPPORT
+
+ /*
+ * Scan global or local parameters that are built-in or per-service
+ * (when node == 0, the parameter doesn't exist in the global
+ * namespace and therefore it can't be built-in or per-service).
+ */
+ if (node != 0
+ && (PCF_BUILTIN_PARAMETER(node) || PCF_SERVICE_PARAMETER(node)))
+ pcf_register_dbms_parameters(cparam_value, pcf_flag_user_parameter,
+ local_scope);
+#endif
+ }
+}
+
+/* pcf_scan_default_parameter_values - scan parameters at implicit defaults */
+
+static void pcf_scan_default_parameter_values(HTABLE *valid_params,
+ const char *dict_name,
+ PCF_MASTER_ENT *local_scope)
+{
+ const char *myname = "pcf_scan_default_parameter_values";
+ PCF_PARAM_INFO **list;
+ PCF_PARAM_INFO **ht;
+ const char *param_value;
+
+ list = PCF_PARAM_TABLE_LIST(valid_params);
+ for (ht = list; *ht; ht++) {
+ /* Skip "do not expand" parameters. */
+ if (PCF_RAW_PARAMETER(PCF_PARAM_INFO_NODE(*ht)))
+ continue;
+ /* Skip parameters with a non-default value. */
+ if (dict_lookup(dict_name, PCF_PARAM_INFO_NAME(*ht)))
+ continue;
+ if ((param_value = pcf_convert_param_node(PCF_SHOW_DEFS, PCF_PARAM_INFO_NAME(*ht),
+ PCF_PARAM_INFO_NODE(*ht))) == 0)
+ msg_panic("%s: parameter %s has no default value",
+ myname, PCF_PARAM_INFO_NAME(*ht));
+ SCAN_USER_PARAMETER_VALUE(param_value, PCF_PARAM_FLAG_USER, local_scope);
+ /* No need to scan default values for legacy DBMS configuration. */
+ }
+ myfree((void *) list);
+}
+
+/* pcf_register_user_parameters - add parameters with user-defined names */
+
+void pcf_register_user_parameters(void)
+{
+ const char *myname = "pcf_register_user_parameters";
+ PCF_MASTER_ENT *masterp;
+ ARGV *argv;
+ char *arg;
+ char *aval;
+ int field;
+ char *saved_arg;
+ char *param_name;
+ char *param_value;
+ DICT *dict;
+
+ /*
+ * Sanity checks.
+ */
+ if (pcf_param_table == 0)
+ msg_panic("%s: global parameter table is not initialized", myname);
+ if (pcf_master_table == 0)
+ msg_panic("%s: master table is not initialized", myname);
+ if (pcf_rest_class_table != 0)
+ msg_panic("%s: restriction class table is already initialized", myname);
+
+ /*
+ * Initialize the table with global restriction class names.
+ */
+ pcf_rest_class_table = htable_create(1);
+
+ /*
+ * Initialize the per-service parameter name spaces.
+ */
+ for (masterp = pcf_master_table; (argv = masterp->argv) != 0; masterp++) {
+ for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
+ arg = argv->argv[field];
+ if (arg[0] != '-' || strcmp(arg, "--") == 0)
+ break;
+ if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0
+ || (aval = argv->argv[field + 1]) == 0)
+ continue;
+ if (strcmp(arg, "-o") == 0) {
+ saved_arg = mystrdup(aval);
+ if (split_nameval(saved_arg, &param_name, &param_value) == 0)
+ dict_update(masterp->name_space, param_name, param_value);
+ myfree(saved_arg);
+ }
+ field += 1;
+ }
+ if ((dict = dict_handle(masterp->name_space)) != 0) {
+ masterp->all_params = dict;
+ masterp->valid_names = htable_create(1);
+ }
+ }
+
+ /*
+ * Scan the "-o parameter=value" instances in each master.cf name space.
+ */
+ for (masterp = pcf_master_table; masterp->argv != 0; masterp++)
+ if (masterp->all_params != 0)
+ pcf_scan_user_parameter_namespace(masterp->name_space, masterp);
+
+ /*
+ * Scan parameter values that are left at their defaults in the global
+ * name space. Some defaults contain the $name of an obsolete parameter
+ * for backwards compatilility purposes. We might warn that an explicit
+ * name=value is obsolete, but we must not warn that the parameter is
+ * unused.
+ */
+ pcf_scan_default_parameter_values(pcf_param_table, CONFIG_DICT,
+ (PCF_MASTER_ENT *) 0);
+
+ /*
+ * Scan the explicit name=value entries in the global name space.
+ */
+ pcf_scan_user_parameter_namespace(CONFIG_DICT, (PCF_MASTER_ENT *) 0);
+}
diff --git a/src/postconf/test1.ref b/src/postconf/test1.ref
new file mode 100644
index 0000000..2aa9903
--- /dev/null
+++ b/src/postconf/test1.ref
@@ -0,0 +1,4 @@
+./postconf: warning: ./main.cf: undefined parameter: bar
+config_directory = .
+foo = yes
+smtpd_restriction_classes = foo bar
diff --git a/src/postconf/test10.ref b/src/postconf/test10.ref
new file mode 100644
index 0000000..5bb6891
--- /dev/null
+++ b/src/postconf/test10.ref
@@ -0,0 +1,2 @@
+./postconf: warning: unmatched request: "bar/inet"
+./postconf: warning: unmatched request: "foo/unix"
diff --git a/src/postconf/test11.ref b/src/postconf/test11.ref
new file mode 100644
index 0000000..c4cc727
--- /dev/null
+++ b/src/postconf/test11.ref
@@ -0,0 +1,2 @@
+foo inet - n n - 0 spawn
+bar unix - n n - 0 spawn
diff --git a/src/postconf/test12.ref b/src/postconf/test12.ref
new file mode 100644
index 0000000..a9f5eb4
--- /dev/null
+++ b/src/postconf/test12.ref
@@ -0,0 +1,2 @@
+foo inet - n n - 0 spawn -o always_bcc=$bar -o
+foo inet - n n - 0 spawn -o always_bcc=$bar -o
diff --git a/src/postconf/test13.ref b/src/postconf/test13.ref
new file mode 100644
index 0000000..09787fa
--- /dev/null
+++ b/src/postconf/test13.ref
@@ -0,0 +1,3 @@
+bar = yes
+config_directory = .
+./postconf: warning: ./main.cf: unused parameter: baz=xx
diff --git a/src/postconf/test14.ref b/src/postconf/test14.ref
new file mode 100644
index 0000000..98884f3
--- /dev/null
+++ b/src/postconf/test14.ref
@@ -0,0 +1,3 @@
+config_directory = .
+smtpd_restriction_classes = bar
+./postconf: warning: ./master.cf: unused parameter: baz=xx
diff --git a/src/postconf/test15.ref b/src/postconf/test15.ref
new file mode 100644
index 0000000..2a15ca8
--- /dev/null
+++ b/src/postconf/test15.ref
@@ -0,0 +1,3 @@
+baz = yy
+config_directory = .
+./postconf: warning: ./main.cf: unused parameter: bar=xx
diff --git a/src/postconf/test16.ref b/src/postconf/test16.ref
new file mode 100644
index 0000000..a1c2e06
--- /dev/null
+++ b/src/postconf/test16.ref
@@ -0,0 +1,2 @@
+./postconf: warning: open ./master.cf: No such file or directory
+config_directory = .
diff --git a/src/postconf/test17.ref b/src/postconf/test17.ref
new file mode 100644
index 0000000..bcfb716
--- /dev/null
+++ b/src/postconf/test17.ref
@@ -0,0 +1 @@
+./postconf: fatal: open ./master.cf: No such file or directory
diff --git a/src/postconf/test18.ref b/src/postconf/test18.ref
new file mode 100644
index 0000000..09224a6
--- /dev/null
+++ b/src/postconf/test18.ref
@@ -0,0 +1,3 @@
+config_directory = .
+smtpd_client_connection_limit_exceptions = yyy
+virtual_maps = xxx
diff --git a/src/postconf/test19.ref b/src/postconf/test19.ref
new file mode 100644
index 0000000..5a286c6
--- /dev/null
+++ b/src/postconf/test19.ref
@@ -0,0 +1,3 @@
+config_directory = .
+default_rbl_reply = $bbbb
+forward_path = $aaaa
diff --git a/src/postconf/test2.ref b/src/postconf/test2.ref
new file mode 100644
index 0000000..49af249
--- /dev/null
+++ b/src/postconf/test2.ref
@@ -0,0 +1,3 @@
+config_directory = .
+./postconf: warning: ./main.cf: unused parameter: restriction_classes=foo bar
+./postconf: warning: ./main.cf: unused parameter: foo=yes
diff --git a/src/postconf/test20.ref b/src/postconf/test20.ref
new file mode 100644
index 0000000..0a430d2
--- /dev/null
+++ b/src/postconf/test20.ref
@@ -0,0 +1,4 @@
+./postconf: warning: ./master.cf: undefined parameter: bar
+./postconf: warning: ./master.cf: undefined parameter: baz
+foo inet - n n - 0 spawn
+ -o always_bcc=$bar$baz
diff --git a/src/postconf/test21.ref b/src/postconf/test21.ref
new file mode 100644
index 0000000..423197e
--- /dev/null
+++ b/src/postconf/test21.ref
@@ -0,0 +1,3 @@
+config_directory = .
+forward_path = xxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxxx
+ xxxxxxxxxxxxxx
diff --git a/src/postconf/test22.ref b/src/postconf/test22.ref
new file mode 100644
index 0000000..1e4629f
--- /dev/null
+++ b/src/postconf/test22.ref
@@ -0,0 +1,16 @@
+whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost
+whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount
+whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan
+whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit
+whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit
+whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback
+whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback
+whatevershebrings_destination_rate_delay = $default_destination_rate_delay
+whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit
+whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit
+whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency
+whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots
+whatevershebrings_recipient_limit = $default_recipient_limit
+whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay
+whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit
+whatevershebrings_transport_rate_delay = $default_transport_rate_delay
diff --git a/src/postconf/test23.ref b/src/postconf/test23.ref
new file mode 100644
index 0000000..d1f9a90
--- /dev/null
+++ b/src/postconf/test23.ref
@@ -0,0 +1,2 @@
+always_bcc = yes
+config_directory = .
diff --git a/src/postconf/test24.ref b/src/postconf/test24.ref
new file mode 100644
index 0000000..2292793
--- /dev/null
+++ b/src/postconf/test24.ref
@@ -0,0 +1 @@
+name = value
diff --git a/src/postconf/test25.ref b/src/postconf/test25.ref
new file mode 100644
index 0000000..1e4629f
--- /dev/null
+++ b/src/postconf/test25.ref
@@ -0,0 +1,16 @@
+whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost
+whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount
+whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan
+whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit
+whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit
+whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback
+whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback
+whatevershebrings_destination_rate_delay = $default_destination_rate_delay
+whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit
+whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit
+whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency
+whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots
+whatevershebrings_recipient_limit = $default_recipient_limit
+whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay
+whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit
+whatevershebrings_transport_rate_delay = $default_transport_rate_delay
diff --git a/src/postconf/test26.ref b/src/postconf/test26.ref
new file mode 100644
index 0000000..0b33025
--- /dev/null
+++ b/src/postconf/test26.ref
@@ -0,0 +1,3 @@
+always_bcc = yes
+config_directory = .
+name = value
diff --git a/src/postconf/test27.ref b/src/postconf/test27.ref
new file mode 100644
index 0000000..1e4629f
--- /dev/null
+++ b/src/postconf/test27.ref
@@ -0,0 +1,16 @@
+whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost
+whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount
+whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan
+whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit
+whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit
+whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback
+whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback
+whatevershebrings_destination_rate_delay = $default_destination_rate_delay
+whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit
+whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit
+whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency
+whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots
+whatevershebrings_recipient_limit = $default_recipient_limit
+whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay
+whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit
+whatevershebrings_transport_rate_delay = $default_transport_rate_delay
diff --git a/src/postconf/test28.ref b/src/postconf/test28.ref
new file mode 100644
index 0000000..cb117b0
--- /dev/null
+++ b/src/postconf/test28.ref
@@ -0,0 +1,10 @@
+aap_domain = whatever
+config_directory = .
+db = memcache
+header_checks = ldap:hh
+hh_domain = whatever
+yy = aap
+zz = $yy
+./postconf: warning: ./main.cf: unused parameter: foo_domain=bar
+./postconf: warning: ./main.cf: unused parameter: aa_domain=whatever
+./postconf: warning: ./main.cf: unused parameter: xx=proxy:ldap:foo
diff --git a/src/postconf/test29.ref b/src/postconf/test29.ref
new file mode 100644
index 0000000..d44e38d
--- /dev/null
+++ b/src/postconf/test29.ref
@@ -0,0 +1,16 @@
+config_directory = .
+./postconf: warning: ./main.cf: unused parameter: sqlitexx=proxy:sqlite:sqlitefoo
+./postconf: warning: ./main.cf: unused parameter: pgsqlxx=proxy:pgsql:pgsqlfoo
+./postconf: warning: ./main.cf: unused parameter: ldapfoo_domain=bar
+./postconf: warning: ./main.cf: unused parameter: memcachefoo_domainx=bar
+./postconf: warning: ./main.cf: unused parameter: sqlitefoo_domainx=bar
+./postconf: warning: ./main.cf: unused parameter: sqlitefoo_domain=bar
+./postconf: warning: ./main.cf: unused parameter: memcachexx=proxy:memcache:memcachefoo
+./postconf: warning: ./main.cf: unused parameter: mysqlxx=proxy:mysql:mysqlfoo
+./postconf: warning: ./main.cf: unused parameter: ldapxx=proxy:ldap:ldapfoo
+./postconf: warning: ./main.cf: unused parameter: ldapfoo_domainx=bar
+./postconf: warning: ./main.cf: unused parameter: memcachefoo_domain=bar
+./postconf: warning: ./main.cf: unused parameter: pgsqlfoo_domainx=bar
+./postconf: warning: ./main.cf: unused parameter: mysqlfoo_domainx=bar
+./postconf: warning: ./main.cf: unused parameter: mysqlfoo_domain=bar
+./postconf: warning: ./main.cf: unused parameter: pgsqlfoo_domain=bar
diff --git a/src/postconf/test3.ref b/src/postconf/test3.ref
new file mode 100644
index 0000000..bdeff73
--- /dev/null
+++ b/src/postconf/test3.ref
@@ -0,0 +1,4 @@
+always_bcc = $bar
+bar = $foo
+config_directory = .
+foo = yes
diff --git a/src/postconf/test30.ref b/src/postconf/test30.ref
new file mode 100644
index 0000000..706a429
--- /dev/null
+++ b/src/postconf/test30.ref
@@ -0,0 +1,7 @@
+config_directory = .
+p1 = xx
+p2 = xx
+p3 = xx
+p4 = xx
+./postconf: warning: ./master.cf: unused parameter: bodyx_checks=$p2
+./postconf: warning: ./master.cf: unused parameter: headerx_checks=$p4
diff --git a/src/postconf/test31.ref b/src/postconf/test31.ref
new file mode 100644
index 0000000..6fda09c
--- /dev/null
+++ b/src/postconf/test31.ref
@@ -0,0 +1,3 @@
+config_directory = .
+smtpd_helo_restrictions = whatever
+smtpd_sender_restrictions = whatever
diff --git a/src/postconf/test32.ref b/src/postconf/test32.ref
new file mode 100644
index 0000000..43e2b7b
--- /dev/null
+++ b/src/postconf/test32.ref
@@ -0,0 +1 @@
+fast_flush_domains = whatever
diff --git a/src/postconf/test33.ref b/src/postconf/test33.ref
new file mode 100644
index 0000000..7b3f52d
--- /dev/null
+++ b/src/postconf/test33.ref
@@ -0,0 +1 @@
+always_bcc = whatever
diff --git a/src/postconf/test34.ref b/src/postconf/test34.ref
new file mode 100644
index 0000000..e771293
--- /dev/null
+++ b/src/postconf/test34.ref
@@ -0,0 +1,4 @@
+./postconf: warning: ./main.cf: read-only parameter assignment: process_id=yyy
+./postconf: warning: ./main.cf: read-only parameter assignment: process_name=xxx
+mydestination = whatever
+process_name = postconf
diff --git a/src/postconf/test35.ref b/src/postconf/test35.ref
new file mode 100644
index 0000000..dc27f39
--- /dev/null
+++ b/src/postconf/test35.ref
@@ -0,0 +1,3 @@
+./postconf: warning: ./master.cf: read-only parameter assignment: process_id=bbb
+./postconf: warning: ./master.cf: read-only parameter assignment: process_name=aaa
+process_name = postconf
diff --git a/src/postconf/test36.ref b/src/postconf/test36.ref
new file mode 100644
index 0000000..bf09921
--- /dev/null
+++ b/src/postconf/test36.ref
@@ -0,0 +1,4 @@
+./postconf: warning: ./main.cf: undefined parameter: virtual_mapx
+config_directory = .
+mydestination =
+virtual_alias_maps =
diff --git a/src/postconf/test37.ref b/src/postconf/test37.ref
new file mode 100644
index 0000000..8ef6d6d
--- /dev/null
+++ b/src/postconf/test37.ref
@@ -0,0 +1,4 @@
+whatever unix - n n - 0 other
+ -o mydestination=yyy
+ -o always_bcc=ccc
+ -o aaa=ccc
diff --git a/src/postconf/test38.ref b/src/postconf/test38.ref
new file mode 100644
index 0000000..186ffc3
--- /dev/null
+++ b/src/postconf/test38.ref
@@ -0,0 +1 @@
+bar unix - n n - 0 other -o aaa=ccc
diff --git a/src/postconf/test39.ref b/src/postconf/test39.ref
new file mode 100644
index 0000000..35b078d
--- /dev/null
+++ b/src/postconf/test39.ref
@@ -0,0 +1,2 @@
+foo unix - n n - 0 other
+baz unix - n n - 0 other
diff --git a/src/postconf/test4.ref b/src/postconf/test4.ref
new file mode 100644
index 0000000..6ec1913
--- /dev/null
+++ b/src/postconf/test4.ref
@@ -0,0 +1,3 @@
+bar = $foo
+config_directory = .
+foo = yes
diff --git a/src/postconf/test40.ref b/src/postconf/test40.ref
new file mode 100644
index 0000000..fa3a5d7
--- /dev/null
+++ b/src/postconf/test40.ref
@@ -0,0 +1,7 @@
+foo unix - n n - 0 other -v
+ -o aaa=bbb
+ -v
+ -o ccc=bbb
+ -v
+ -o ddd=bbb
+./postconf: warning: ./master.cf: unused parameter: ddd=$ccc
diff --git a/src/postconf/test41.ref b/src/postconf/test41.ref
new file mode 100644
index 0000000..f8200d4
--- /dev/null
+++ b/src/postconf/test41.ref
@@ -0,0 +1,18 @@
+foo unix - n n - 0 other
+bar unix - n n - 0 other
+ -o xxx=yyy
+ -o aaa=bbb
+baz unix - n n - 0 other
+./postconf: warning: ./master.cf: unused parameter: aaa=bbb
+./postconf: warning: ./master.cf: unused parameter: xxx=yyy
+foo unix - n n - 0 other
+bar unix - n n - 0 other
+ -o xxx=YYY
+ -o aaa=BBB
+baz unix - n n - 0 other
+./postconf: warning: ./master.cf: unused parameter: aaa=BBB
+./postconf: warning: ./master.cf: unused parameter: xxx=YYY
+bar/unix/aaa = BBB
+bar/unix/xxx = YYY
+./postconf: warning: ./master.cf: unused parameter: aaa=BBB
+./postconf: warning: ./master.cf: unused parameter: xxx=YYY
diff --git a/src/postconf/test42.ref b/src/postconf/test42.ref
new file mode 100644
index 0000000..80676f3
--- /dev/null
+++ b/src/postconf/test42.ref
@@ -0,0 +1,14 @@
+foo unix - n n - 0 other
+bar unix - n n - 0 other
+ -o xxx=yyy
+ -o aaa=bbb
+baz unix - n n - 0 other
+./postconf: warning: ./master.cf: unused parameter: aaa=bbb
+./postconf: warning: ./master.cf: unused parameter: xxx=yyy
+bar/unix/aaa = bbb
+bar/unix/xxx = yyy
+./postconf: warning: ./master.cf: unused parameter: aaa=bbb
+./postconf: warning: ./master.cf: unused parameter: xxx=yyy
+foo unix - n n - 0 other
+bar unix - n n - 0 other
+baz unix - n n - 0 other
diff --git a/src/postconf/test43.ref b/src/postconf/test43.ref
new file mode 100644
index 0000000..2bfea0e
--- /dev/null
+++ b/src/postconf/test43.ref
@@ -0,0 +1,6 @@
+foo unix - n n - 0 other
+bar unix - n y - 0 aa -stuff
+ -o bb=cc
+ dd
+baz unix - n n - 0 other
+./postconf: warning: ./master.cf: unused parameter: bb=cc
diff --git a/src/postconf/test44.ref b/src/postconf/test44.ref
new file mode 100644
index 0000000..b8cf23d
--- /dev/null
+++ b/src/postconf/test44.ref
@@ -0,0 +1,6 @@
+foo unix - n n - 0 other
+xx inet - n n - 0 aa -stuff
+ -o bb=cc
+ dd
+baz unix - n n - 0 other
+./postconf: warning: ./master.cf: unused parameter: bb=cc
diff --git a/src/postconf/test45.ref b/src/postconf/test45.ref
new file mode 100644
index 0000000..8ae54a0
--- /dev/null
+++ b/src/postconf/test45.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid type field "xxxx" in "bar xxxx - n n - 0 other"
diff --git a/src/postconf/test46.ref b/src/postconf/test46.ref
new file mode 100644
index 0000000..aac7c1f
--- /dev/null
+++ b/src/postconf/test46.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid private field "X" in "bar inet X n n - 0 other"
diff --git a/src/postconf/test47.ref b/src/postconf/test47.ref
new file mode 100644
index 0000000..e8d46ed
--- /dev/null
+++ b/src/postconf/test47.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid unprivileged field "X" in "bar inet - X n - 0 other"
diff --git a/src/postconf/test48.ref b/src/postconf/test48.ref
new file mode 100644
index 0000000..9403117
--- /dev/null
+++ b/src/postconf/test48.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid chroot field "X" in "bar inet - n X - 0 other"
diff --git a/src/postconf/test49.ref b/src/postconf/test49.ref
new file mode 100644
index 0000000..f52b171
--- /dev/null
+++ b/src/postconf/test49.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid wakeup field "X" in "bar inet - n n X 0 other"
diff --git a/src/postconf/test4b.ref b/src/postconf/test4b.ref
new file mode 100644
index 0000000..5dda033
--- /dev/null
+++ b/src/postconf/test4b.ref
@@ -0,0 +1,5 @@
+always_bcc = $foo
+bar = aaa
+biff = $bar
+config_directory = .
+./postconf: warning: ./master.cf: unused parameter: baz=zzz
diff --git a/src/postconf/test5.ref b/src/postconf/test5.ref
new file mode 100644
index 0000000..41bee93
--- /dev/null
+++ b/src/postconf/test5.ref
@@ -0,0 +1 @@
+config_directory = .
diff --git a/src/postconf/test50.ref b/src/postconf/test50.ref
new file mode 100644
index 0000000..41cdc44
--- /dev/null
+++ b/src/postconf/test50.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid process_limit field "X" in "bar inet - n n - X other"
diff --git a/src/postconf/test51.ref b/src/postconf/test51.ref
new file mode 100644
index 0000000..a37d749
--- /dev/null
+++ b/src/postconf/test51.ref
@@ -0,0 +1 @@
+./postconf: fatal: invalid wakeup field "X?" in "bar inet - n n X? 0 other"
diff --git a/src/postconf/test52.ref b/src/postconf/test52.ref
new file mode 100644
index 0000000..8902db9
--- /dev/null
+++ b/src/postconf/test52.ref
@@ -0,0 +1 @@
+baz unix - n n 0 0 other
diff --git a/src/postconf/test53.ref b/src/postconf/test53.ref
new file mode 100644
index 0000000..c81cf65
--- /dev/null
+++ b/src/postconf/test53.ref
@@ -0,0 +1,3 @@
+foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+baz unix - n n 0 0 other
diff --git a/src/postconf/test54.ref b/src/postconf/test54.ref
new file mode 100644
index 0000000..045a9f6
--- /dev/null
+++ b/src/postconf/test54.ref
@@ -0,0 +1,3 @@
+#foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+baz unix - n n 0 0 other
diff --git a/src/postconf/test55.ref b/src/postconf/test55.ref
new file mode 100644
index 0000000..96c11b0
--- /dev/null
+++ b/src/postconf/test55.ref
@@ -0,0 +1,3 @@
+foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+#baz unix - n n 0 0 other
diff --git a/src/postconf/test56.ref b/src/postconf/test56.ref
new file mode 100644
index 0000000..ac845f6
--- /dev/null
+++ b/src/postconf/test56.ref
@@ -0,0 +1,5 @@
+foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+# -o first
+# -o second
+baz unix - n n 0 0 other
diff --git a/src/postconf/test57.ref b/src/postconf/test57.ref
new file mode 100644
index 0000000..362fd16
--- /dev/null
+++ b/src/postconf/test57.ref
@@ -0,0 +1,10 @@
+./postconf: warning: ./main.cf: undefined parameter: z
+./postconf: warning: ./main.cf: undefined parameter: z
+bar = y-value
+baz =
+config_directory = .
+t1 = Postfix 2.11 compatible
+x = x-value
+y = y-value
+./postconf: warning: ./main.cf: unused parameter: t2=$t1
+./postconf: warning: ./main.cf: unused parameter: foo=$bar$baz
diff --git a/src/postconf/test58.ref b/src/postconf/test58.ref
new file mode 100644
index 0000000..1e74c4a
--- /dev/null
+++ b/src/postconf/test58.ref
@@ -0,0 +1,8 @@
+./postconf: warning: main.cf: syntax error after '}' in "{ldap:xxx, memcache:yy}x"
+./postconf: warning: main.cf: missing '}' in "{xx"
+config_directory = .
+mydestination = foo bar pipemap:{ldap:xxx, memcache:yy}x randmap:{xx
+xxx_domain = foo
+yy_backup = bbb
+./postconf: warning: ./main.cf: unused parameter: yy_bogus=bbb
+./postconf: warning: ./main.cf: unused parameter: xxx_bogus=foo
diff --git a/src/postconf/test59.ref b/src/postconf/test59.ref
new file mode 100644
index 0000000..3a7e57f
--- /dev/null
+++ b/src/postconf/test59.ref
@@ -0,0 +1,10 @@
+./postconf: warning: master.cf: syntax error after '}' in "{ arg2a arg2b }x"
+./postconf: warning: master.cf: missing '}' in "{ arg3a arg3b "
+foo unix - n n - 0 other
+bar inet - n n 0 0 other
+ -o name1=value1
+ -o {name2=value2a value2b}
+ arg1a arg1b {arg2a arg2b} {arg3a arg3b}
+baz unix - n n 0 0 other
+./postconf: warning: ./master.cf: unused parameter: name1=value1
+./postconf: warning: ./master.cf: unused parameter: name2=value2a value2b
diff --git a/src/postconf/test6.ref b/src/postconf/test6.ref
new file mode 100644
index 0000000..55e47f2
--- /dev/null
+++ b/src/postconf/test6.ref
@@ -0,0 +1,17 @@
+whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost
+whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount
+whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan
+whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit
+whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit
+whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback
+whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback
+whatevershebrings_destination_rate_delay = $default_destination_rate_delay
+whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit
+whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit
+whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency
+whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots
+whatevershebrings_recipient_limit = $default_recipient_limit
+whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay
+whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit
+whatevershebrings_time_limit = $command_time_limit
+whatevershebrings_transport_rate_delay = $default_transport_rate_delay
diff --git a/src/postconf/test60.ref b/src/postconf/test60.ref
new file mode 100644
index 0000000..5a6f3ca
--- /dev/null
+++ b/src/postconf/test60.ref
@@ -0,0 +1,8 @@
+foo
+unix
+-
+n
+n
+-
+0
+other -o always_bcc=bar
diff --git a/src/postconf/test61.ref b/src/postconf/test61.ref
new file mode 100644
index 0000000..5716ca5
--- /dev/null
+++ b/src/postconf/test61.ref
@@ -0,0 +1 @@
+bar
diff --git a/src/postconf/test62.ref b/src/postconf/test62.ref
new file mode 100644
index 0000000..ffc4913
--- /dev/null
+++ b/src/postconf/test62.ref
@@ -0,0 +1,8 @@
+foo/unix/service
+foo/unix/type
+foo/unix/private
+foo/unix/unprivileged
+foo/unix/chroot
+foo/unix/wakeup
+foo/unix/process_limit
+foo/unix/command
diff --git a/src/postconf/test63.ref b/src/postconf/test63.ref
new file mode 100644
index 0000000..b609d62
--- /dev/null
+++ b/src/postconf/test63.ref
@@ -0,0 +1 @@
+foo/unix/always_bcc
diff --git a/src/postconf/test64.ref b/src/postconf/test64.ref
new file mode 100644
index 0000000..595620c
--- /dev/null
+++ b/src/postconf/test64.ref
@@ -0,0 +1 @@
+relayhost = relay-from-main.cf
diff --git a/src/postconf/test65.ref b/src/postconf/test65.ref
new file mode 100644
index 0000000..6bc7fd8
--- /dev/null
+++ b/src/postconf/test65.ref
@@ -0,0 +1 @@
+relayhost = relay-from-cmd-line
diff --git a/src/postconf/test66.ref b/src/postconf/test66.ref
new file mode 100644
index 0000000..bd35822
--- /dev/null
+++ b/src/postconf/test66.ref
@@ -0,0 +1,5 @@
+./postconf: warning: ldap:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: mysql:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: pgsql:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: sqlite:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: memcache:PWD/test66.cf: unused parameter: junk=junk
diff --git a/src/postconf/test67.ref b/src/postconf/test67.ref
new file mode 100644
index 0000000..03def6f
--- /dev/null
+++ b/src/postconf/test67.ref
@@ -0,0 +1,10 @@
+smtp inet n - n - - smtpd
+ -o test1_process_name=smtpd
+ -o test1_service_name=smtp
+smtp unix n - n - - smtp
+ -o test2_process_name=smtp
+ -o test2_service_name=smtp
+./postconf: warning: ./master.cf: unused parameter: test1_process_name=$process_name
+./postconf: warning: ./master.cf: unused parameter: test1_service_name=$service_name
+./postconf: warning: ./master.cf: unused parameter: test2_process_name=$process_name
+./postconf: warning: ./master.cf: unused parameter: test2_service_name=$service_name
diff --git a/src/postconf/test68.ref b/src/postconf/test68.ref
new file mode 100644
index 0000000..e2d7c7d
--- /dev/null
+++ b/src/postconf/test68.ref
@@ -0,0 +1,5 @@
+./postconf: warning: ldap:PWD/test68.cf: unused parameter: junk=junk
+./postconf: warning: mysql:PWD/test68.cf: unused parameter: junk=junk
+./postconf: warning: pgsql:PWD/test68.cf: unused parameter: junk=junk
+./postconf: warning: sqlite:PWD/test68.cf: unused parameter: junk=junk
+./postconf: warning: memcache:PWD/test68.cf: unused parameter: junk=junk
diff --git a/src/postconf/test69.ref b/src/postconf/test69.ref
new file mode 100644
index 0000000..465a91f
--- /dev/null
+++ b/src/postconf/test69.ref
@@ -0,0 +1,2 @@
+./postconf: warning: ldap:PWD/test69.cf: unused parameter: junk=junk
+config_directory = .
diff --git a/src/postconf/test7.ref b/src/postconf/test7.ref
new file mode 100644
index 0000000..1bd731b
--- /dev/null
+++ b/src/postconf/test7.ref
@@ -0,0 +1 @@
+whatevershebrings_time_limit = $command_time_limit
diff --git a/src/postconf/test8.ref b/src/postconf/test8.ref
new file mode 100644
index 0000000..af47b44
--- /dev/null
+++ b/src/postconf/test8.ref
@@ -0,0 +1 @@
+whatevershebrings_time_limit = 1
diff --git a/src/postconf/test9.ref b/src/postconf/test9.ref
new file mode 100644
index 0000000..b62303f
--- /dev/null
+++ b/src/postconf/test9.ref
@@ -0,0 +1 @@
+foo inet - n n - 0 spawn