summaryrefslogtreecommitdiffstats
path: root/src/config
diff options
context:
space:
mode:
Diffstat (limited to 'src/config')
-rw-r--r--src/config/Makefile.am98
-rw-r--r--src/config/Makefile.in1079
-rw-r--r--src/config/all-settings.c6110
-rw-r--r--src/config/all-settings.h8
-rw-r--r--src/config/config-connection.c267
-rw-r--r--src/config/config-connection.h9
-rw-r--r--src/config/config-filter.c401
-rw-r--r--src/config/config-filter.h58
-rw-r--r--src/config/config-parser-private.h75
-rw-r--r--src/config/config-parser.c1212
-rw-r--r--src/config/config-parser.h33
-rw-r--r--src/config/config-request.c524
-rw-r--r--src/config/config-request.h61
-rw-r--r--src/config/config-settings.c46
-rw-r--r--src/config/doveconf.c1072
-rw-r--r--src/config/main.c53
-rw-r--r--src/config/old-set-parser.c824
-rw-r--r--src/config/old-set-parser.h16
-rwxr-xr-xsrc/config/settings-get.pl162
-rw-r--r--src/config/sysinfo-get.c127
-rw-r--r--src/config/sysinfo-get.h6
-rw-r--r--src/config/test-config-parser.c170
22 files changed, 12411 insertions, 0 deletions
diff --git a/src/config/Makefile.am b/src/config/Makefile.am
new file mode 100644
index 0000000..084ec0e
--- /dev/null
+++ b/src/config/Makefile.am
@@ -0,0 +1,98 @@
+pkgsysconfdir = $(sysconfdir)/dovecot
+pkglibexecdir = $(libexecdir)/dovecot
+exampledir = $(docdir)/example-config
+
+bin_PROGRAMS = doveconf
+pkglibexec_PROGRAMS = config
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-test \
+ -I$(top_srcdir)/src/lib-dns \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-master \
+ -DPKG_RUNDIR=\""$(rundir)"\" \
+ -DPKG_STATEDIR=\""$(statedir)"\" \
+ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
+ -DEXAMPLE_CONFIG_DIR=\""$(exampledir)"\" \
+ -DMODULEDIR=\""$(moduledir)"\" \
+ -DSSLDIR=\""$(ssldir)\"" \
+ -DSYSCONFDIR=\""$(pkgsysconfdir)"\" \
+ $(BINARY_CFLAGS)
+
+noinst_LTLIBRARIES = libconfig.la
+
+config_LDADD = \
+ $(noinst_LTLIBRARIES) \
+ $(LIBDOVECOT) \
+ $(RAND_LIBS) \
+ $(BINARY_LDFLAGS) \
+ -lm
+
+config_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(noinst_LTLIBRARIES)
+
+doveconf_LDADD = \
+ $(noinst_LTLIBRARIES) \
+ $(LIBDOVECOT) \
+ $(RAND_LIBS) \
+ $(BINARY_LDFLAGS) \
+ -lm
+
+doveconf_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(noinst_LTLIBRARIES)
+
+common = \
+ config-connection.c \
+ config-filter.c \
+ config-parser.c \
+ config-request.c \
+ old-set-parser.c \
+ sysinfo-get.c
+
+libconfig_la_SOURCES = $(common)
+
+config_SOURCES = \
+ all-settings.c \
+ main.c
+
+doveconf_SOURCES = \
+ all-settings.c \
+ doveconf.c
+
+noinst_HEADERS = \
+ all-settings.h \
+ config-connection.h \
+ old-set-parser.h \
+ sysinfo-get.h
+
+pkginclude_HEADERS = \
+ config-filter.h \
+ config-parser.h \
+ config-parser-private.h \
+ config-request.h
+
+all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl
+ $(top_srcdir)/src/config/settings-get.pl $(SETTING_FILES) > all-settings.c || rm -f all-settings.c
+
+EXTRA_DIST = \
+ config-settings.c \
+ settings-get.pl
+
+test_programs = \
+ test-config-parser
+
+noinst_PROGRAMS = $(test_programs)
+
+test_libs = \
+ $(noinst_LTLIBRARIES) \
+ $(LIBDOVECOT)
+
+test_config_parser_CFLAGS = $(AM_CPPFLAGS)
+test_config_parser_SOURCES = test-config-parser.c
+test_config_parser_LDADD = $(test_libs)
+test_config_parser_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(noinst_LTLIBRARIES)
+
+check-local:
+ for bin in $(test_programs); do \
+ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
+ done
diff --git a/src/config/Makefile.in b/src/config/Makefile.in
new file mode 100644
index 0000000..d26133c
--- /dev/null
+++ b/src/config/Makefile.in
@@ -0,0 +1,1079 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = doveconf$(EXEEXT)
+pkglibexec_PROGRAMS = config$(EXEEXT)
+noinst_PROGRAMS = $(am__EXEEXT_1)
+subdir = src/config
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \
+ $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \
+ $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \
+ $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \
+ $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \
+ $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \
+ $(top_srcdir)/m4/flexible_array_member.m4 \
+ $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \
+ $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \
+ $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \
+ $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \
+ $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \
+ $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \
+ $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \
+ $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/pr_set_dumpable.m4 \
+ $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \
+ $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \
+ $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \
+ $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \
+ $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \
+ $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \
+ $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \
+ $(top_srcdir)/m4/typeof_dev_t.m4 \
+ $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \
+ $(top_srcdir)/m4/want_apparmor.m4 \
+ $(top_srcdir)/m4/want_bsdauth.m4 \
+ $(top_srcdir)/m4/want_bzlib.m4 \
+ $(top_srcdir)/m4/want_cassandra.m4 \
+ $(top_srcdir)/m4/want_cdb.m4 \
+ $(top_srcdir)/m4/want_checkpassword.m4 \
+ $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \
+ $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \
+ $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \
+ $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \
+ $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \
+ $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \
+ $(top_srcdir)/m4/want_prefetch.m4 \
+ $(top_srcdir)/m4/want_shadow.m4 \
+ $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \
+ $(top_srcdir)/m4/want_sqlite.m4 \
+ $(top_srcdir)/m4/want_stemmer.m4 \
+ $(top_srcdir)/m4/want_systemd.m4 \
+ $(top_srcdir)/m4/want_textcat.m4 \
+ $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \
+ $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginclude_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" \
+ "$(DESTDIR)$(pkgincludedir)"
+am__EXEEXT_1 = test-config-parser$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS)
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libconfig_la_LIBADD =
+am__objects_1 = config-connection.lo config-filter.lo config-parser.lo \
+ config-request.lo old-set-parser.lo sysinfo-get.lo
+am_libconfig_la_OBJECTS = $(am__objects_1)
+libconfig_la_OBJECTS = $(am_libconfig_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_config_OBJECTS = all-settings.$(OBJEXT) main.$(OBJEXT)
+config_OBJECTS = $(am_config_OBJECTS)
+am__DEPENDENCIES_1 =
+am_doveconf_OBJECTS = all-settings.$(OBJEXT) doveconf.$(OBJEXT)
+doveconf_OBJECTS = $(am_doveconf_OBJECTS)
+am_test_config_parser_OBJECTS = \
+ test_config_parser-test-config-parser.$(OBJEXT)
+test_config_parser_OBJECTS = $(am_test_config_parser_OBJECTS)
+am__DEPENDENCIES_2 = $(noinst_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_config_parser_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(test_config_parser_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/all-settings.Po \
+ ./$(DEPDIR)/config-connection.Plo \
+ ./$(DEPDIR)/config-filter.Plo ./$(DEPDIR)/config-parser.Plo \
+ ./$(DEPDIR)/config-request.Plo ./$(DEPDIR)/doveconf.Po \
+ ./$(DEPDIR)/main.Po ./$(DEPDIR)/old-set-parser.Plo \
+ ./$(DEPDIR)/sysinfo-get.Plo \
+ ./$(DEPDIR)/test_config_parser-test-config-parser.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libconfig_la_SOURCES) $(config_SOURCES) \
+ $(doveconf_SOURCES) $(test_config_parser_SOURCES)
+DIST_SOURCES = $(libconfig_la_SOURCES) $(config_SOURCES) \
+ $(doveconf_SOURCES) $(test_config_parser_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+HEADERS = $(noinst_HEADERS) $(pkginclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+pkglibexecdir = $(libexecdir)/dovecot
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPARMOR_LIBS = @APPARMOR_LIBS@
+AR = @AR@
+AUTH_CFLAGS = @AUTH_CFLAGS@
+AUTH_LIBS = @AUTH_LIBS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+BISON = @BISON@
+CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@
+CASSANDRA_LIBS = @CASSANDRA_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CDB_LIBS = @CDB_LIBS@
+CFLAGS = @CFLAGS@
+CLUCENE_CFLAGS = @CLUCENE_CFLAGS@
+CLUCENE_LIBS = @CLUCENE_LIBS@
+COMPRESS_LIBS = @COMPRESS_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPT_LIBS = @CRYPT_LIBS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DICT_LIBS = @DICT_LIBS@
+DLLIB = @DLLIB@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FLEX = @FLEX@
+FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@
+FUZZER_LDFLAGS = @FUZZER_LDFLAGS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KRB5CONFIG = @KRB5CONFIG@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_LIBS = @KRB5_LIBS@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@
+LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@
+LIBCAP = @LIBCAP@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@
+LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@
+LIBICONV = @LIBICONV@
+LIBICU_CFLAGS = @LIBICU_CFLAGS@
+LIBICU_LIBS = @LIBICU_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@
+LIBSODIUM_LIBS = @LIBSODIUM_LIBS@
+LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@
+LIBTIRPC_LIBS = @LIBTIRPC_LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBWRAP_LIBS = @LIBWRAP_LIBS@
+LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LUA_CFLAGS = @LUA_CFLAGS@
+LUA_LIBS = @LUA_LIBS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MODULE_LIBS = @MODULE_LIBS@
+MODULE_SUFFIX = @MODULE_SUFFIX@
+MYSQL_CFLAGS = @MYSQL_CFLAGS@
+MYSQL_CONFIG = @MYSQL_CONFIG@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANDOC = @PANDOC@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PGSQL_CFLAGS = @PGSQL_CFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PG_CONFIG = @PG_CONFIG@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+QUOTA_LIBS = @QUOTA_LIBS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RPCGEN = @RPCGEN@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SETTING_FILES = @SETTING_FILES@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CFLAGS = @SQLITE_CFLAGS@
+SQLITE_LIBS = @SQLITE_LIBS@
+SQL_CFLAGS = @SQL_CFLAGS@
+SQL_LIBS = @SQL_LIBS@
+SSL_CFLAGS = @SSL_CFLAGS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+ZSTD_CFLAGS = @ZSTD_CFLAGS@
+ZSTD_LIBS = @ZSTD_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dict_drivers = @dict_drivers@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rundir = @rundir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sql_drivers = @sql_drivers@
+srcdir = @srcdir@
+ssldir = @ssldir@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+systemdservicetype = @systemdservicetype@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+pkgsysconfdir = $(sysconfdir)/dovecot
+exampledir = $(docdir)/example-config
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-test \
+ -I$(top_srcdir)/src/lib-dns \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-master \
+ -DPKG_RUNDIR=\""$(rundir)"\" \
+ -DPKG_STATEDIR=\""$(statedir)"\" \
+ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
+ -DEXAMPLE_CONFIG_DIR=\""$(exampledir)"\" \
+ -DMODULEDIR=\""$(moduledir)"\" \
+ -DSSLDIR=\""$(ssldir)\"" \
+ -DSYSCONFDIR=\""$(pkgsysconfdir)"\" \
+ $(BINARY_CFLAGS)
+
+noinst_LTLIBRARIES = libconfig.la
+config_LDADD = \
+ $(noinst_LTLIBRARIES) \
+ $(LIBDOVECOT) \
+ $(RAND_LIBS) \
+ $(BINARY_LDFLAGS) \
+ -lm
+
+config_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(noinst_LTLIBRARIES)
+doveconf_LDADD = \
+ $(noinst_LTLIBRARIES) \
+ $(LIBDOVECOT) \
+ $(RAND_LIBS) \
+ $(BINARY_LDFLAGS) \
+ -lm
+
+doveconf_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(noinst_LTLIBRARIES)
+common = \
+ config-connection.c \
+ config-filter.c \
+ config-parser.c \
+ config-request.c \
+ old-set-parser.c \
+ sysinfo-get.c
+
+libconfig_la_SOURCES = $(common)
+config_SOURCES = \
+ all-settings.c \
+ main.c
+
+doveconf_SOURCES = \
+ all-settings.c \
+ doveconf.c
+
+noinst_HEADERS = \
+ all-settings.h \
+ config-connection.h \
+ old-set-parser.h \
+ sysinfo-get.h
+
+pkginclude_HEADERS = \
+ config-filter.h \
+ config-parser.h \
+ config-parser-private.h \
+ config-request.h
+
+EXTRA_DIST = \
+ config-settings.c \
+ settings-get.pl
+
+test_programs = \
+ test-config-parser
+
+test_libs = \
+ $(noinst_LTLIBRARIES) \
+ $(LIBDOVECOT)
+
+test_config_parser_CFLAGS = $(AM_CPPFLAGS)
+test_config_parser_SOURCES = test-config-parser.c
+test_config_parser_LDADD = $(test_libs)
+test_config_parser_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(noinst_LTLIBRARIES)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/config/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/config/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-pkglibexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files
+
+clean-pkglibexecPROGRAMS:
+ @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libconfig.la: $(libconfig_la_OBJECTS) $(libconfig_la_DEPENDENCIES) $(EXTRA_libconfig_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libconfig_la_OBJECTS) $(libconfig_la_LIBADD) $(LIBS)
+
+config$(EXEEXT): $(config_OBJECTS) $(config_DEPENDENCIES) $(EXTRA_config_DEPENDENCIES)
+ @rm -f config$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(config_OBJECTS) $(config_LDADD) $(LIBS)
+
+doveconf$(EXEEXT): $(doveconf_OBJECTS) $(doveconf_DEPENDENCIES) $(EXTRA_doveconf_DEPENDENCIES)
+ @rm -f doveconf$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(doveconf_OBJECTS) $(doveconf_LDADD) $(LIBS)
+
+test-config-parser$(EXEEXT): $(test_config_parser_OBJECTS) $(test_config_parser_DEPENDENCIES) $(EXTRA_test_config_parser_DEPENDENCIES)
+ @rm -f test-config-parser$(EXEEXT)
+ $(AM_V_CCLD)$(test_config_parser_LINK) $(test_config_parser_OBJECTS) $(test_config_parser_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/all-settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-connection.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-filter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-request.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveconf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/old-set-parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysinfo-get.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_config_parser-test-config-parser.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+test_config_parser-test-config-parser.o: test-config-parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -MT test_config_parser-test-config-parser.o -MD -MP -MF $(DEPDIR)/test_config_parser-test-config-parser.Tpo -c -o test_config_parser-test-config-parser.o `test -f 'test-config-parser.c' || echo '$(srcdir)/'`test-config-parser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_config_parser-test-config-parser.Tpo $(DEPDIR)/test_config_parser-test-config-parser.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-config-parser.c' object='test_config_parser-test-config-parser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -c -o test_config_parser-test-config-parser.o `test -f 'test-config-parser.c' || echo '$(srcdir)/'`test-config-parser.c
+
+test_config_parser-test-config-parser.obj: test-config-parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -MT test_config_parser-test-config-parser.obj -MD -MP -MF $(DEPDIR)/test_config_parser-test-config-parser.Tpo -c -o test_config_parser-test-config-parser.obj `if test -f 'test-config-parser.c'; then $(CYGPATH_W) 'test-config-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-config-parser.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_config_parser-test-config-parser.Tpo $(DEPDIR)/test_config_parser-test-config-parser.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-config-parser.c' object='test_config_parser-test-config-parser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -c -o test_config_parser-test-config-parser.obj `if test -f 'test-config-parser.c'; then $(CYGPATH_W) 'test-config-parser.c'; else $(CYGPATH_W) '$(srcdir)/test-config-parser.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkgincludeHEADERS: $(pkginclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \
+ done
+
+uninstall-pkgincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: check-am
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkgincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ clean-pkglibexecPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/all-settings.Po
+ -rm -f ./$(DEPDIR)/config-connection.Plo
+ -rm -f ./$(DEPDIR)/config-filter.Plo
+ -rm -f ./$(DEPDIR)/config-parser.Plo
+ -rm -f ./$(DEPDIR)/config-request.Plo
+ -rm -f ./$(DEPDIR)/doveconf.Po
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/old-set-parser.Plo
+ -rm -f ./$(DEPDIR)/sysinfo-get.Plo
+ -rm -f ./$(DEPDIR)/test_config_parser-test-config-parser.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkgincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-pkglibexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/all-settings.Po
+ -rm -f ./$(DEPDIR)/config-connection.Plo
+ -rm -f ./$(DEPDIR)/config-filter.Plo
+ -rm -f ./$(DEPDIR)/config-parser.Plo
+ -rm -f ./$(DEPDIR)/config-request.Plo
+ -rm -f ./$(DEPDIR)/doveconf.Po
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/old-set-parser.Plo
+ -rm -f ./$(DEPDIR)/sysinfo-get.Plo
+ -rm -f ./$(DEPDIR)/test_config_parser-test-config-parser.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-pkgincludeHEADERS \
+ uninstall-pkglibexecPROGRAMS
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am \
+ check-local clean clean-binPROGRAMS clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ clean-pkglibexecPROGRAMS cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkgincludeHEADERS \
+ install-pkglibexecPROGRAMS install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-pkgincludeHEADERS \
+ uninstall-pkglibexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl
+ $(top_srcdir)/src/config/settings-get.pl $(SETTING_FILES) > all-settings.c || rm -f all-settings.c
+
+check-local:
+ for bin in $(test_programs); do \
+ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
+ done
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/config/all-settings.c b/src/config/all-settings.c
new file mode 100644
index 0000000..268a321
--- /dev/null
+++ b/src/config/all-settings.c
@@ -0,0 +1,6110 @@
+/* WARNING: THIS FILE IS GENERATED - DO NOT PATCH!
+ It's not enough alone in any case, because the defaults may be
+ coming from the individual *-settings.c in some situations. If you
+ wish to modify defaults, change the other *-settings.c files and
+ just delete this file. This file will be automatically regenerated
+ by make. (This file is distributed in the tarball only because some
+ systems might not have Perl installed.) */
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ipwd.h"
+#include "var-expand.h"
+#include "file-lock.h"
+#include "fsync-mode.h"
+#include "hash-format.h"
+#include "net.h"
+#include "unichar.h"
+#include "hash-method.h"
+#include "settings-parser.h"
+#include "message-header-parser.h"
+#include "all-settings.h"
+#include <stddef.h>
+#include <unistd.h>
+#define CONFIG_BINARY
+extern buffer_t config_all_services_buf;/* ../../src/lib-storage/mail-storage-settings.h */
+extern const struct setting_parser_info mail_user_setting_parser_info;
+extern const struct setting_parser_info mail_namespace_setting_parser_info;
+extern const struct setting_parser_info mail_storage_setting_parser_info;
+/* <settings checks> */
+#define MAILBOX_SET_AUTO_NO "no"
+#define MAILBOX_SET_AUTO_CREATE "create"
+#define MAILBOX_SET_AUTO_SUBSCRIBE "subscribe"
+/* </settings checks> */
+struct mail_storage_settings {
+ const char *mail_location;
+ const char *mail_attachment_fs;
+ const char *mail_attachment_dir;
+ const char *mail_attachment_hash;
+ uoff_t mail_attachment_min_size;
+ const char *mail_attribute_dict;
+ unsigned int mail_prefetch_count;
+ const char *mail_cache_fields;
+ const char *mail_always_cache_fields;
+ const char *mail_never_cache_fields;
+ const char *mail_server_comment;
+ const char *mail_server_admin;
+ unsigned int mail_cache_min_mail_count;
+ unsigned int mail_cache_unaccessed_field_drop;
+ uoff_t mail_cache_record_max_size;
+ uoff_t mail_cache_max_size;
+ uoff_t mail_cache_purge_min_size;
+ unsigned int mail_cache_purge_delete_percentage;
+ unsigned int mail_cache_purge_continued_percentage;
+ unsigned int mail_cache_purge_header_continue_count;
+ uoff_t mail_index_rewrite_min_log_bytes;
+ uoff_t mail_index_rewrite_max_log_bytes;
+ uoff_t mail_index_log_rotate_min_size;
+ uoff_t mail_index_log_rotate_max_size;
+ unsigned int mail_index_log_rotate_min_age;
+ unsigned int mail_index_log2_max_age;
+ unsigned int mailbox_idle_check_interval;
+ unsigned int mail_max_keyword_length;
+ unsigned int mail_max_lock_timeout;
+ unsigned int mail_temp_scan_interval;
+ unsigned int mail_vsize_bg_after_count;
+ unsigned int mail_sort_max_read_count;
+ bool mail_save_crlf;
+ const char *mail_fsync;
+ bool mmap_disable;
+ bool dotlock_use_excl;
+ bool mail_nfs_storage;
+ bool mail_nfs_index;
+ bool mailbox_list_index;
+ bool mailbox_list_index_very_dirty_syncs;
+ bool mailbox_list_index_include_inbox;
+ bool mail_debug;
+ bool mail_full_filesystem_access;
+ bool maildir_stat_dirs;
+ bool mail_shared_explicit_inbox;
+ const char *lock_method;
+ const char *pop3_uidl_format;
+
+ const char *hostname;
+ const char *recipient_delimiter;
+
+ const char *mail_attachment_detection_options;
+
+ enum file_lock_method parsed_lock_method;
+ enum fsync_mode parsed_fsync_mode;
+
+ const char *const *parsed_mail_attachment_content_type_filter;
+ bool parsed_mail_attachment_exclude_inlined;
+ bool parsed_mail_attachment_detection_add_flags;
+ bool parsed_mail_attachment_detection_no_flags_on_fetch;
+};
+struct mail_namespace_settings {
+ const char *name;
+ const char *type;
+ const char *separator;
+ const char *prefix;
+ const char *location;
+ const char *alias_for;
+
+ bool inbox;
+ bool hidden;
+ const char *list;
+ bool subscriptions;
+ bool ignore_on_failure;
+ bool disabled;
+ unsigned int order;
+
+ ARRAY(struct mailbox_settings *) mailboxes;
+ struct mail_user_settings *user_set;
+};
+struct mailbox_settings {
+ const char *name;
+ const char *autocreate;
+ const char *special_use;
+ const char *driver;
+ const char *comment;
+ unsigned int autoexpunge;
+ unsigned int autoexpunge_max_mails;
+};
+struct mail_user_settings {
+ const char *base_dir;
+ const char *auth_socket_path;
+ const char *mail_temp_dir;
+
+ const char *mail_uid;
+ const char *mail_gid;
+ const char *mail_home;
+ const char *mail_chroot;
+ const char *mail_access_groups;
+ const char *mail_privileged_group;
+ const char *valid_chroot_dirs;
+
+ unsigned int first_valid_uid, last_valid_uid;
+ unsigned int first_valid_gid, last_valid_gid;
+
+ const char *mail_plugins;
+ const char *mail_plugin_dir;
+
+ const char *mail_log_prefix;
+
+ const char *hostname;
+ const char *postmaster_address;
+
+ ARRAY(struct mail_namespace_settings *) namespaces;
+ ARRAY(const char *) plugin_envs;
+
+ /* May be NULL - use mail_storage_get_postmaster_address() instead of
+ directly accessing this. */
+ const struct message_address *_parsed_postmaster_address;
+ const struct smtp_address *_parsed_postmaster_address_smtp;
+};
+/* ../../src/lib-storage/index/pop3c/pop3c-settings.h */
+/* <settings checks> */
+enum pop3c_features {
+ POP3C_FEATURE_NO_PIPELINING = 0x1,
+};
+/* </settings checks> */
+struct pop3c_settings {
+ const char *pop3c_host;
+ in_port_t pop3c_port;
+
+ const char *pop3c_user;
+ const char *pop3c_master_user;
+ const char *pop3c_password;
+
+ const char *pop3c_ssl;
+ bool pop3c_ssl_verify;
+
+ const char *pop3c_rawlog_dir;
+ bool pop3c_quick_received_date;
+
+ const char *pop3c_features;
+ enum pop3c_features parsed_features;
+};
+/* ../../src/lib-storage/index/mbox/mbox-settings.h */
+struct mbox_settings {
+ const char *mbox_read_locks;
+ const char *mbox_write_locks;
+ unsigned int mbox_lock_timeout;
+ unsigned int mbox_dotlock_change_timeout;
+ uoff_t mbox_min_index_size;
+ bool mbox_dirty_syncs;
+ bool mbox_very_dirty_syncs;
+ bool mbox_lazy_writes;
+ const char *mbox_md5;
+};
+/* ../../src/lib-storage/index/maildir/maildir-settings.h */
+struct maildir_settings {
+ bool maildir_copy_with_hardlinks;
+ bool maildir_very_dirty_syncs;
+ bool maildir_broken_filename_sizes;
+ bool maildir_empty_new;
+};
+/* ../../src/lib-storage/index/imapc/imapc-settings.h */
+/* <settings checks> */
+enum imapc_features {
+ IMAPC_FEATURE_RFC822_SIZE = 0x01,
+ IMAPC_FEATURE_GUID_FORCED = 0x02,
+ IMAPC_FEATURE_FETCH_HEADERS = 0x04,
+ IMAPC_FEATURE_GMAIL_MIGRATION = 0x08,
+ IMAPC_FEATURE_SEARCH = 0x10,
+ IMAPC_FEATURE_ZIMBRA_WORKAROUNDS = 0x20,
+ IMAPC_FEATURE_NO_EXAMINE = 0x40,
+ IMAPC_FEATURE_PROXYAUTH = 0x80,
+ IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS = 0x100,
+ IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS = 0x200,
+ IMAPC_FEATURE_MODSEQ = 0x400,
+ IMAPC_FEATURE_DELAY_LOGIN = 0x800,
+ IMAPC_FEATURE_FETCH_BODYSTRUCTURE = 0x1000,
+ IMAPC_FEATURE_SEND_ID = 0x2000,
+ IMAPC_FEATURE_FETCH_EMPTY_IS_EXPUNGED = 0x4000,
+ IMAPC_FEATURE_NO_MSN_UPDATES = 0x8000,
+ IMAPC_FEATURE_ACL = 0x10000,
+};
+/* </settings checks> */
+struct imapc_settings {
+ const char *imapc_host;
+ in_port_t imapc_port;
+
+ const char *imapc_user;
+ const char *imapc_master_user;
+ const char *imapc_password;
+ const char *imapc_sasl_mechanisms;
+
+ const char *imapc_ssl;
+ bool imapc_ssl_verify;
+
+ const char *imapc_features;
+ const char *imapc_rawlog_dir;
+ const char *imapc_list_prefix;
+ unsigned int imapc_cmd_timeout;
+ unsigned int imapc_max_idle_time;
+ unsigned int imapc_connection_retry_count;
+ unsigned int imapc_connection_retry_interval;
+ uoff_t imapc_max_line_length;
+
+ const char *pop3_deleted_flag;
+
+ enum imapc_features parsed_features;
+ unsigned int throttle_init_msecs;
+ unsigned int throttle_max_msecs;
+ unsigned int throttle_shrink_min_msecs;
+};
+/* ../../src/lib-storage/index/dbox-multi/mdbox-settings.h */
+struct mdbox_settings {
+ bool mdbox_preallocate_space;
+ uoff_t mdbox_rotate_size;
+ unsigned int mdbox_rotate_interval;
+};
+/* ../../src/lib-smtp/smtp-submit-settings.h */
+extern const struct setting_parser_info smtp_submit_setting_parser_info;
+struct smtp_submit_settings {
+ const char *hostname;
+ bool mail_debug;
+
+ const char *submission_host;
+ const char *sendmail_path;
+ unsigned int submission_timeout;
+
+ const char *submission_ssl;
+};
+/* ../../src/lib-settings/settings.h */
+#define DEF_STRUCT_STR(name, struct_name) \
+ { SET_STR + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
+ ((struct struct_name *)0)->name, const char *), \
+ #name, offsetof(struct struct_name, name) }
+#define DEF_STRUCT_INT(name, struct_name) \
+ { SET_INT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
+ ((struct struct_name *)0)->name, unsigned int), \
+ #name, offsetof(struct struct_name, name) }
+#define DEF_STRUCT_BOOL(name, struct_name) \
+ { SET_BOOL + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
+ ((struct struct_name *)0)->name, bool), \
+ #name, offsetof(struct struct_name, name) }
+/* ../../src/lib-master/service-settings.h */
+/* <settings checks> */
+enum service_user_default {
+ SERVICE_USER_DEFAULT_NONE = 0,
+ SERVICE_USER_DEFAULT_INTERNAL,
+ SERVICE_USER_DEFAULT_LOGIN
+};
+
+enum service_type {
+ SERVICE_TYPE_UNKNOWN,
+ SERVICE_TYPE_LOG,
+ SERVICE_TYPE_ANVIL,
+ SERVICE_TYPE_CONFIG,
+ SERVICE_TYPE_LOGIN,
+ SERVICE_TYPE_STARTUP,
+ /* Worker processes are intentionally limited to their process_limit,
+ and they can regularly reach it. There shouldn't be unnecessary
+ warnings about temporarily reaching the limit. */
+ SERVICE_TYPE_WORKER,
+};
+/* </settings checks> */
+struct file_listener_settings {
+ const char *path;
+ unsigned int mode;
+ const char *user;
+ const char *group;
+};
+ARRAY_DEFINE_TYPE(file_listener_settings, struct file_listener_settings *);
+struct inet_listener_settings {
+ const char *name;
+ const char *address;
+ in_port_t port;
+ bool ssl;
+ bool reuse_port;
+ bool haproxy;
+};
+ARRAY_DEFINE_TYPE(inet_listener_settings, struct inet_listener_settings *);
+struct service_settings {
+ const char *name;
+ const char *protocol;
+ const char *type;
+ const char *executable;
+ const char *user;
+ const char *group;
+ const char *privileged_group;
+ const char *extra_groups;
+ const char *chroot;
+
+ bool drop_priv_before_exec;
+
+ unsigned int process_min_avail;
+ unsigned int process_limit;
+ unsigned int client_limit;
+ unsigned int service_count;
+ unsigned int idle_kill;
+ uoff_t vsz_limit;
+
+ ARRAY_TYPE(file_listener_settings) unix_listeners;
+ ARRAY_TYPE(file_listener_settings) fifo_listeners;
+ ARRAY_TYPE(inet_listener_settings) inet_listeners;
+
+ /* internal to master: */
+ struct master_settings *master_set;
+ enum service_type parsed_type;
+ enum service_user_default user_default;
+ bool login_dump_core:1;
+
+ /* -- flags that can be set internally -- */
+
+ /* process_limit must not be higher than 1 */
+ bool process_limit_1:1;
+};
+ARRAY_DEFINE_TYPE(service_settings, struct service_settings *);
+/* ../../src/lib-master/master-service-ssl-settings.h */
+extern const struct setting_parser_info master_service_ssl_setting_parser_info;
+extern const struct setting_parser_info master_service_ssl_server_setting_parser_info;
+struct master_service_ssl_settings {
+ const char *ssl;
+ const char *ssl_ca;
+ const char *ssl_client_ca_file;
+ const char *ssl_client_ca_dir;
+ const char *ssl_client_cert;
+ const char *ssl_client_key;
+ const char *ssl_cipher_list;
+ const char *ssl_cipher_suites;
+ const char *ssl_curve_list;
+ const char *ssl_min_protocol;
+ const char *ssl_cert_username_field;
+ const char *ssl_crypto_device;
+ const char *ssl_options;
+
+ bool ssl_verify_client_cert;
+ bool ssl_client_require_valid_cert;
+ bool ssl_require_crl;
+ bool verbose_ssl;
+ bool ssl_prefer_server_ciphers;
+
+ /* These are derived from ssl_options, not set directly */
+ struct {
+ bool compression;
+ bool tickets;
+ } parsed_opts;
+};
+struct master_service_ssl_server_settings {
+ const char *ssl_cert;
+ const char *ssl_alt_cert;
+ const char *ssl_key;
+ const char *ssl_alt_key;
+ const char *ssl_key_password;
+ const char *ssl_dh;
+};
+/* ../../src/lib-master/master-service-settings.h */
+extern const struct setting_parser_info master_service_setting_parser_info;
+struct master_service_settings {
+ /* NOTE: log process won't see any new settings unless they're
+ explicitly sent via environment variables by master process. */
+ const char *base_dir;
+ const char *state_dir;
+ const char *instance_name;
+ const char *log_path;
+ const char *info_log_path;
+ const char *debug_log_path;
+ const char *log_timestamp;
+ const char *log_debug;
+ const char *log_core_filter;
+ const char *process_shutdown_filter;
+ const char *syslog_facility;
+ const char *import_environment;
+ const char *stats_writer_socket_path;
+ uoff_t config_cache_size;
+ bool version_ignore;
+ bool shutdown_clients;
+ bool verbose_proctitle;
+
+ const char *haproxy_trusted_networks;
+ unsigned int haproxy_timeout;
+};
+/* ../../src/lib-lda/lda-settings.h */
+extern const struct setting_parser_info lda_setting_parser_info;
+struct lda_settings {
+ const char *hostname;
+ const char *rejection_subject;
+ const char *rejection_reason;
+ const char *deliver_log_format;
+ const char *recipient_delimiter;
+ const char *lda_original_recipient_header;
+
+ bool quota_full_tempfail;
+ bool lda_mailbox_autocreate;
+ bool lda_mailbox_autosubscribe;
+};
+/* ../../src/lib-dict-backend/dict-sql-settings.h */
+struct dict_sql_settings {
+ const char *connect;
+
+ unsigned int max_pattern_fields_count;
+ ARRAY(struct dict_sql_map) maps;
+};
+/* ../../src/lib-dict-backend/dict-ldap-settings.h */
+struct dict_ldap_settings {
+ const char *uri;
+ const char *bind_dn;
+ const char *password;
+ unsigned int timeout;
+ unsigned int max_idle_time;
+ unsigned int debug;
+ unsigned int max_attribute_count;
+ bool require_ssl;
+ bool start_tls;
+ ARRAY(struct dict_ldap_map) maps;
+};
+/* ../../src/lib-storage/mail-storage-settings.c */
+extern const struct setting_parser_info mailbox_setting_parser_info;
+extern const struct setting_parser_info mail_namespace_setting_parser_info;
+/* <settings checks> */
+static bool mail_cache_fields_parse(const char *key, const char *value,
+ const char **error_r)
+{
+ const char *const *arr;
+
+ for (arr = t_strsplit_spaces(value, " ,"); *arr != NULL; arr++) {
+ const char *name = *arr;
+
+ if (strncasecmp(name, "hdr.", 4) == 0 &&
+ !message_header_name_is_valid(name+4)) {
+ *error_r = t_strdup_printf(
+ "Invalid %s: %s is not a valid header name",
+ key, name);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static bool mail_storage_settings_check(void *_set, pool_t pool,
+ const char **error_r)
+{
+ struct mail_storage_settings *set = _set;
+ struct hash_format *format;
+ const char *p, *error;
+ bool uidl_format_ok;
+ char c;
+
+ if (set->mailbox_idle_check_interval == 0) {
+ *error_r = "mailbox_idle_check_interval must not be 0";
+ return FALSE;
+ }
+
+ if (strcmp(set->mail_fsync, "optimized") == 0)
+ set->parsed_fsync_mode = FSYNC_MODE_OPTIMIZED;
+ else if (strcmp(set->mail_fsync, "never") == 0)
+ set->parsed_fsync_mode = FSYNC_MODE_NEVER;
+ else if (strcmp(set->mail_fsync, "always") == 0)
+ set->parsed_fsync_mode = FSYNC_MODE_ALWAYS;
+ else {
+ *error_r = t_strdup_printf("Unknown mail_fsync: %s",
+ set->mail_fsync);
+ return FALSE;
+ }
+
+ if (set->mail_nfs_index && !set->mmap_disable) {
+ *error_r = "mail_nfs_index=yes requires mmap_disable=yes";
+ return FALSE;
+ }
+ if (set->mail_nfs_index &&
+ set->parsed_fsync_mode != FSYNC_MODE_ALWAYS) {
+ *error_r = "mail_nfs_index=yes requires mail_fsync=always";
+ return FALSE;
+ }
+
+ if (!file_lock_method_parse(set->lock_method,
+ &set->parsed_lock_method)) {
+ *error_r = t_strdup_printf("Unknown lock_method: %s",
+ set->lock_method);
+ return FALSE;
+ }
+
+ if (set->mail_cache_max_size > 1024 * 1024 * 1024) {
+ *error_r = "mail_cache_max_size can't be over 1 GB";
+ return FALSE;
+ }
+ if (set->mail_cache_purge_delete_percentage > 100) {
+ *error_r = "mail_cache_purge_delete_percentage can't be over 100";
+ return FALSE;
+ }
+
+ uidl_format_ok = FALSE;
+ for (p = set->pop3_uidl_format; *p != '\0'; p++) {
+ if (p[0] != '%' || p[1] == '\0')
+ continue;
+
+ c = var_get_key(++p);
+ switch (c) {
+ case 'v':
+ case 'u':
+ case 'm':
+ case 'f':
+ case 'g':
+ uidl_format_ok = TRUE;
+ break;
+ case '%':
+ break;
+ default:
+ *error_r = t_strdup_printf(
+ "Unknown pop3_uidl_format variable: %%%c", c);
+ return FALSE;
+ }
+ }
+ if (!uidl_format_ok) {
+ *error_r = "pop3_uidl_format setting doesn't contain any "
+ "%% variables.";
+ return FALSE;
+ }
+
+ if (strchr(set->mail_attachment_hash, '/') != NULL) {
+ *error_r = "mail_attachment_hash setting "
+ "must not contain '/' characters";
+ return FALSE;
+ }
+ if (hash_format_init(set->mail_attachment_hash, &format, &error) < 0) {
+ *error_r = t_strconcat("Invalid mail_attachment_hash setting: ",
+ error, NULL);
+ return FALSE;
+ }
+ if (strchr(set->mail_attachment_hash, '-') != NULL) {
+ *error_r = "mail_attachment_hash setting "
+ "must not contain '-' characters";
+ return FALSE;
+ }
+ hash_format_deinit_free(&format);
+
+ // FIXME: check set->mail_server_admin syntax (RFC 5464, Section 6.2.2)
+
+#ifndef CONFIG_BINARY
+ if (*set->hostname == '\0')
+ set->hostname = p_strdup(pool, my_hostdomain());
+#endif
+
+ /* parse mail_attachment_indicator_options */
+ if (*set->mail_attachment_detection_options != '\0') {
+ ARRAY_TYPE(const_string) content_types;
+ p_array_init(&content_types, pool, 2);
+
+ const char *const *options =
+ t_strsplit_spaces(set->mail_attachment_detection_options, " ");
+
+ while(*options != NULL) {
+ const char *opt = *options;
+
+ if (strcmp(opt, "add-flags") == 0 ||
+ strcmp(opt, "add-flags-on-save") == 0) {
+ set->parsed_mail_attachment_detection_add_flags = TRUE;
+ } else if (strcmp(opt, "no-flags-on-fetch") == 0) {
+ set->parsed_mail_attachment_detection_no_flags_on_fetch = TRUE;
+ } else if (strcmp(opt, "exclude-inlined") == 0) {
+ set->parsed_mail_attachment_exclude_inlined = TRUE;
+ } else if (str_begins(opt, "content-type=")) {
+ const char *value = p_strdup(pool, opt+13);
+ array_push_back(&content_types, &value);
+ } else {
+ *error_r = t_strdup_printf("mail_attachment_detection_options: "
+ "Unknown option: %s", opt);
+ return FALSE;
+ }
+ options++;
+ }
+
+ array_append_zero(&content_types);
+ set->parsed_mail_attachment_content_type_filter = array_front(&content_types);
+ }
+
+ if (!mail_cache_fields_parse("mail_cache_fields",
+ set->mail_cache_fields, error_r))
+ return FALSE;
+ if (!mail_cache_fields_parse("mail_always_cache_fields",
+ set->mail_always_cache_fields, error_r))
+ return FALSE;
+ if (!mail_cache_fields_parse("mail_never_cache_fields",
+ set->mail_never_cache_fields, error_r))
+ return FALSE;
+ return TRUE;
+}
+
+static bool namespace_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct mail_namespace_settings *ns = _set;
+ struct mail_namespace_settings *const *namespaces;
+ const char *name;
+ unsigned int i, count;
+
+ name = ns->prefix != NULL ? ns->prefix : "";
+
+ if (ns->separator[0] != '\0' && ns->separator[1] != '\0') {
+ *error_r = t_strdup_printf("Namespace '%s': "
+ "Hierarchy separator must be only one character long",
+ name);
+ return FALSE;
+ }
+ if (!uni_utf8_str_is_valid(name)) {
+ *error_r = t_strdup_printf("Namespace prefix not valid UTF8: %s",
+ name);
+ return FALSE;
+ }
+
+ if (ns->alias_for != NULL && !ns->disabled) {
+ if (array_is_created(&ns->user_set->namespaces)) {
+ namespaces = array_get(&ns->user_set->namespaces,
+ &count);
+ } else {
+ namespaces = NULL;
+ count = 0;
+ }
+ for (i = 0; i < count; i++) {
+ if (strcmp(namespaces[i]->prefix, ns->alias_for) == 0)
+ break;
+ }
+ if (i == count) {
+ *error_r = t_strdup_printf(
+ "Namespace '%s': alias_for points to "
+ "unknown namespace: %s", name, ns->alias_for);
+ return FALSE;
+ }
+ if (namespaces[i]->alias_for != NULL) {
+ *error_r = t_strdup_printf(
+ "Namespace '%s': alias_for chaining isn't "
+ "allowed: %s -> %s", name, ns->alias_for,
+ namespaces[i]->alias_for);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static bool mailbox_special_use_exists(const char *name)
+{
+ if (name[0] != '\\')
+ return FALSE;
+ name++;
+
+ if (strcasecmp(name, "All") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Archive") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Drafts") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Flagged") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Important") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Junk") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Sent") == 0)
+ return TRUE;
+ if (strcasecmp(name, "Trash") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static bool
+mailbox_special_use_check(struct mailbox_settings *set, pool_t pool,
+ const char **error_r)
+{
+ const char *const *uses, *str;
+ unsigned int i;
+
+ uses = t_strsplit_spaces(set->special_use, " ");
+ for (i = 0; uses[i] != NULL; i++) {
+ if (!mailbox_special_use_exists(uses[i])) {
+ *error_r = t_strdup_printf(
+ "mailbox %s: unknown special_use: %s",
+ set->name, uses[i]);
+ return FALSE;
+ }
+ }
+ /* make sure there are no extra spaces */
+ str = t_strarray_join(uses, " ");
+ if (strcmp(str, set->special_use) != 0)
+ set->special_use = p_strdup(pool, str);
+ return TRUE;
+}
+
+static bool mailbox_settings_check(void *_set, pool_t pool,
+ const char **error_r)
+{
+ struct mailbox_settings *set = _set;
+
+ if (!uni_utf8_str_is_valid(set->name)) {
+ *error_r = t_strdup_printf("mailbox %s: name isn't valid UTF-8",
+ set->name);
+ return FALSE;
+ }
+ if (*set->special_use != '\0') {
+ if (!mailbox_special_use_check(set, pool, error_r))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool mail_user_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r ATTR_UNUSED)
+{
+ struct mail_user_settings *set = _set;
+
+#ifndef CONFIG_BINARY
+ fix_base_path(set, pool, &set->auth_socket_path);
+
+ if (*set->hostname == '\0')
+ set->hostname = p_strdup(pool, my_hostdomain());
+ if (set->postmaster_address[0] == SETTING_STRVAR_UNEXPANDED[0] &&
+ set->postmaster_address[1] == '\0') {
+ /* check for valid looking fqdn in hostname */
+ if (strchr(set->hostname, '.') == NULL) {
+ *error_r = "postmaster_address setting not given";
+ return FALSE;
+ }
+ set->postmaster_address =
+ p_strconcat(pool, SETTING_STRVAR_UNEXPANDED,
+ "postmaster@", set->hostname, NULL);
+ }
+#else
+ if (*set->mail_plugins != '\0' &&
+ access(set->mail_plugin_dir, R_OK | X_OK) < 0) {
+ *error_r = t_strdup_printf(
+ "mail_plugin_dir: access(%s) failed: %m",
+ set->mail_plugin_dir);
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+#ifndef CONFIG_BINARY
+static bool parse_postmaster_address(const char *address, pool_t pool,
+ struct mail_user_settings *set,
+ const char **error_r) ATTR_NULL(3)
+{
+ struct message_address *addr;
+ struct smtp_address *smtp_addr;
+
+ addr = message_address_parse(pool,
+ (const unsigned char *)address,
+ strlen(address), 2, 0);
+ if (addr == NULL || addr->domain == NULL || addr->invalid_syntax ||
+ smtp_address_create_from_msg(pool, addr, &smtp_addr) < 0) {
+ *error_r = t_strdup_printf(
+ "invalid address `%s' specified for the "
+ "postmaster_address setting", address);
+ return FALSE;
+ }
+ if (addr->next != NULL) {
+ *error_r = "more than one address specified for the "
+ "postmaster_address setting";
+ return FALSE;
+ }
+ if (addr->name == NULL || *addr->name == '\0')
+ addr->name = "Postmaster";
+ if (set != NULL) {
+ set->_parsed_postmaster_address = addr;
+ set->_parsed_postmaster_address_smtp = smtp_addr;
+ }
+ return TRUE;
+}
+
+static bool
+mail_user_settings_expand_check(void *_set, pool_t pool,
+ const char **error_r ATTR_UNUSED)
+{
+ struct mail_user_settings *set = _set;
+ const char *error;
+
+ /* Parse if possible. Perform error handling later. */
+ (void)parse_postmaster_address(set->postmaster_address, pool,
+ set, &error);
+ return TRUE;
+}
+#endif
+
+/* </settings checks> */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct mail_storage_settings)
+static const struct setting_define mail_storage_setting_defines[] = {
+ DEF(STR_VARS, mail_location),
+ { .type = SET_ALIAS, .key = "mail" },
+ DEF(STR_VARS, mail_attachment_fs),
+ DEF(STR_VARS, mail_attachment_dir),
+ DEF(STR, mail_attachment_hash),
+ DEF(SIZE, mail_attachment_min_size),
+ DEF(STR, mail_attachment_detection_options),
+ DEF(STR_VARS, mail_attribute_dict),
+ DEF(UINT, mail_prefetch_count),
+ DEF(STR, mail_cache_fields),
+ DEF(STR, mail_always_cache_fields),
+ DEF(STR, mail_never_cache_fields),
+ DEF(STR, mail_server_comment),
+ DEF(STR, mail_server_admin),
+ DEF(TIME_HIDDEN, mail_cache_unaccessed_field_drop),
+ DEF(SIZE_HIDDEN, mail_cache_record_max_size),
+ DEF(SIZE_HIDDEN, mail_cache_max_size),
+ DEF(UINT_HIDDEN, mail_cache_min_mail_count),
+ DEF(SIZE_HIDDEN, mail_cache_purge_min_size),
+ DEF(UINT_HIDDEN, mail_cache_purge_delete_percentage),
+ DEF(UINT_HIDDEN, mail_cache_purge_continued_percentage),
+ DEF(UINT_HIDDEN, mail_cache_purge_header_continue_count),
+ DEF(SIZE_HIDDEN, mail_index_rewrite_min_log_bytes),
+ DEF(SIZE_HIDDEN, mail_index_rewrite_max_log_bytes),
+ DEF(SIZE_HIDDEN, mail_index_log_rotate_min_size),
+ DEF(SIZE_HIDDEN, mail_index_log_rotate_max_size),
+ DEF(TIME_HIDDEN, mail_index_log_rotate_min_age),
+ DEF(TIME_HIDDEN, mail_index_log2_max_age),
+ DEF(TIME, mailbox_idle_check_interval),
+ DEF(UINT, mail_max_keyword_length),
+ DEF(TIME, mail_max_lock_timeout),
+ DEF(TIME, mail_temp_scan_interval),
+ DEF(UINT, mail_vsize_bg_after_count),
+ DEF(UINT, mail_sort_max_read_count),
+ DEF(BOOL, mail_save_crlf),
+ DEF(ENUM, mail_fsync),
+ DEF(BOOL, mmap_disable),
+ DEF(BOOL, dotlock_use_excl),
+ DEF(BOOL, mail_nfs_storage),
+ DEF(BOOL, mail_nfs_index),
+ DEF(BOOL, mailbox_list_index),
+ DEF(BOOL, mailbox_list_index_very_dirty_syncs),
+ DEF(BOOL, mailbox_list_index_include_inbox),
+ DEF(BOOL, mail_debug),
+ DEF(BOOL, mail_full_filesystem_access),
+ DEF(BOOL, maildir_stat_dirs),
+ DEF(BOOL, mail_shared_explicit_inbox),
+ DEF(ENUM, lock_method),
+ DEF(STR, pop3_uidl_format),
+
+ DEF(STR, hostname),
+ DEF(STR, recipient_delimiter),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct mail_storage_settings mail_storage_default_settings = {
+ .mail_location = "",
+ .mail_attachment_fs = "sis posix",
+ .mail_attachment_dir = "",
+ .mail_attachment_hash = "%{sha1}",
+ .mail_attachment_min_size = 1024*128,
+ .mail_attachment_detection_options = "",
+ .mail_attribute_dict = "",
+ .mail_prefetch_count = 0,
+ .mail_cache_fields = "flags",
+ .mail_always_cache_fields = "",
+ .mail_never_cache_fields = "imap.envelope",
+ .mail_server_comment = "",
+ .mail_server_admin = "",
+ .mail_cache_min_mail_count = 0,
+ .mail_cache_unaccessed_field_drop = 60*60*24*30,
+ .mail_cache_record_max_size = 64 * 1024,
+ .mail_cache_max_size = 1024 * 1024 * 1024,
+ .mail_cache_purge_min_size = 32 * 1024,
+ .mail_cache_purge_delete_percentage = 20,
+ .mail_cache_purge_continued_percentage = 200,
+ .mail_cache_purge_header_continue_count = 4,
+ .mail_index_rewrite_min_log_bytes = 8 * 1024,
+ .mail_index_rewrite_max_log_bytes = 128 * 1024,
+ .mail_index_log_rotate_min_size = 32 * 1024,
+ .mail_index_log_rotate_max_size = 1024 * 1024,
+ .mail_index_log_rotate_min_age = 5 * 60,
+ .mail_index_log2_max_age = 3600 * 24 * 2,
+ .mailbox_idle_check_interval = 30,
+ .mail_max_keyword_length = 50,
+ .mail_max_lock_timeout = 0,
+ .mail_temp_scan_interval = 7*24*60*60,
+ .mail_vsize_bg_after_count = 0,
+ .mail_sort_max_read_count = 0,
+ .mail_save_crlf = FALSE,
+ .mail_fsync = "optimized:never:always",
+ .mmap_disable = FALSE,
+ .dotlock_use_excl = TRUE,
+ .mail_nfs_storage = FALSE,
+ .mail_nfs_index = FALSE,
+ .mailbox_list_index = TRUE,
+ .mailbox_list_index_very_dirty_syncs = FALSE,
+ .mailbox_list_index_include_inbox = FALSE,
+ .mail_debug = FALSE,
+ .mail_full_filesystem_access = FALSE,
+ .maildir_stat_dirs = FALSE,
+ .mail_shared_explicit_inbox = FALSE,
+ .lock_method = "fcntl:flock:dotlock",
+ .pop3_uidl_format = "%08Xu%08Xv",
+
+ .hostname = "",
+ .recipient_delimiter = "+",
+};
+const struct setting_parser_info mail_storage_setting_parser_info = {
+ .module_name = "mail",
+ .defines = mail_storage_setting_defines,
+ .defaults = &mail_storage_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct mail_storage_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info,
+
+ .check_func = mail_storage_settings_check,
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct mailbox_settings)
+static const struct setting_define mailbox_setting_defines[] = {
+ DEF(STR, name),
+ { .type = SET_ENUM, .key = "auto",
+ .offset = offsetof(struct mailbox_settings, autocreate) } ,
+ DEF(STR, special_use),
+ DEF(STR, driver),
+ DEF(STR, comment),
+ DEF(TIME, autoexpunge),
+ DEF(UINT, autoexpunge_max_mails),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct mailbox_settings mailbox_default_settings = {
+ .name = "",
+ .autocreate = MAILBOX_SET_AUTO_NO":"
+ MAILBOX_SET_AUTO_CREATE":"
+ MAILBOX_SET_AUTO_SUBSCRIBE,
+ .special_use = "",
+ .driver = "",
+ .comment = "",
+ .autoexpunge = 0,
+ .autoexpunge_max_mails = 0
+};
+const struct setting_parser_info mailbox_setting_parser_info = {
+ .defines = mailbox_setting_defines,
+ .defaults = &mailbox_default_settings,
+
+ .type_offset = offsetof(struct mailbox_settings, name),
+ .struct_size = sizeof(struct mailbox_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info,
+
+ .check_func = mailbox_settings_check
+};
+#undef DEF
+#undef DEFLIST_UNIQUE
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct mail_namespace_settings)
+#define DEFLIST_UNIQUE(field, name, defines) \
+ { .type = SET_DEFLIST_UNIQUE, .key = name, \
+ .offset = offsetof(struct mail_namespace_settings, field), \
+ .list_info = defines }
+static const struct setting_define mail_namespace_setting_defines[] = {
+ DEF(STR, name),
+ DEF(ENUM, type),
+ DEF(STR, separator),
+ DEF(STR_VARS, prefix),
+ DEF(STR_VARS, location),
+ { .type = SET_ALIAS, .key = "mail" },
+ { .type = SET_ALIAS, .key = "mail_location" },
+ DEF(STR_VARS, alias_for),
+
+ DEF(BOOL, inbox),
+ DEF(BOOL, hidden),
+ DEF(ENUM, list),
+ DEF(BOOL, subscriptions),
+ DEF(BOOL, ignore_on_failure),
+ DEF(BOOL, disabled),
+ DEF(UINT, order),
+
+ DEFLIST_UNIQUE(mailboxes, "mailbox", &mailbox_setting_parser_info),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct mail_namespace_settings mail_namespace_default_settings = {
+ .name = "",
+ .type = "private:shared:public",
+ .separator = "",
+ .prefix = "",
+ .location = "",
+ .alias_for = NULL,
+
+ .inbox = FALSE,
+ .hidden = FALSE,
+ .list = "yes:no:children",
+ .subscriptions = TRUE,
+ .ignore_on_failure = FALSE,
+ .disabled = FALSE,
+ .order = 0,
+
+ .mailboxes = ARRAY_INIT
+};
+const struct setting_parser_info mail_namespace_setting_parser_info = {
+ .defines = mail_namespace_setting_defines,
+ .defaults = &mail_namespace_default_settings,
+
+ .type_offset = offsetof(struct mail_namespace_settings, name),
+ .struct_size = sizeof(struct mail_namespace_settings),
+
+ .parent_offset = offsetof(struct mail_namespace_settings, user_set),
+ .parent = &mail_user_setting_parser_info,
+
+ .check_func = namespace_settings_check
+};
+#undef DEF
+#undef DEFLIST_UNIQUE
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct mail_user_settings)
+#define DEFLIST_UNIQUE(field, name, defines) \
+ { .type = SET_DEFLIST_UNIQUE, .key = name, \
+ .offset = offsetof(struct mail_user_settings, field), \
+ .list_info = defines }
+static const struct setting_define mail_user_setting_defines[] = {
+ DEF(STR, base_dir),
+ DEF(STR, auth_socket_path),
+ DEF(STR_VARS, mail_temp_dir),
+
+ DEF(STR, mail_uid),
+ DEF(STR, mail_gid),
+ DEF(STR_VARS, mail_home),
+ DEF(STR_VARS, mail_chroot),
+ DEF(STR, mail_access_groups),
+ DEF(STR, mail_privileged_group),
+ DEF(STR, valid_chroot_dirs),
+
+ DEF(UINT, first_valid_uid),
+ DEF(UINT, last_valid_uid),
+ DEF(UINT, first_valid_gid),
+ DEF(UINT, last_valid_gid),
+
+ DEF(STR, mail_plugins),
+ DEF(STR, mail_plugin_dir),
+
+ DEF(STR, mail_log_prefix),
+
+ DEF(STR, hostname),
+ DEF(STR_VARS, postmaster_address),
+
+ DEFLIST_UNIQUE(namespaces, "namespace", &mail_namespace_setting_parser_info),
+ { .type = SET_STRLIST, .key = "plugin",
+ .offset = offsetof(struct mail_user_settings, plugin_envs) },
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct mail_user_settings mail_user_default_settings = {
+ .base_dir = PKG_RUNDIR,
+ .auth_socket_path = "auth-userdb",
+ .mail_temp_dir = "/tmp",
+
+ .mail_uid = "",
+ .mail_gid = "",
+ .mail_home = "",
+ .mail_chroot = "",
+ .mail_access_groups = "",
+ .mail_privileged_group = "",
+ .valid_chroot_dirs = "",
+
+ .first_valid_uid = 500,
+ .last_valid_uid = 0,
+ .first_valid_gid = 1,
+ .last_valid_gid = 0,
+
+ .mail_plugins = "",
+ .mail_plugin_dir = MODULEDIR,
+
+ .mail_log_prefix = "%s(%u)<%{pid}><%{session}>: ",
+
+ .hostname = "",
+ .postmaster_address = "postmaster@%{if;%d;ne;;%d;%{hostname}}",
+
+ .namespaces = ARRAY_INIT,
+ .plugin_envs = ARRAY_INIT
+};
+const struct setting_parser_info mail_user_setting_parser_info = {
+ .module_name = "mail",
+ .defines = mail_user_setting_defines,
+ .defaults = &mail_user_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct mail_user_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = mail_user_settings_check,
+#ifndef CONFIG_BINARY
+ .expand_check_func = mail_user_settings_expand_check,
+#endif
+};
+/* ../../src/lib-storage/index/pop3c/pop3c-settings.c */
+/* <settings checks> */
+struct pop3c_feature_list {
+ const char *name;
+ enum pop3c_features num;
+};
+
+static const struct pop3c_feature_list pop3c_feature_list[] = {
+ { "no-pipelining", POP3C_FEATURE_NO_PIPELINING },
+ { NULL, 0 }
+};
+
+static int
+pop3c_settings_parse_features(struct pop3c_settings *set,
+ const char **error_r)
+{
+ enum pop3c_features features = 0;
+ const struct pop3c_feature_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->pop3c_features, " ,");
+ for (; *str != NULL; str++) {
+ list = pop3c_feature_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ features |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf("pop3c_features: "
+ "Unknown feature: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_features = features;
+ return 0;
+}
+
+static bool pop3c_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct pop3c_settings *set = _set;
+
+ if (pop3c_settings_parse_features(set, error_r) < 0)
+ return FALSE;
+ return TRUE;
+}
+/* </settings checks> */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct pop3c_settings)
+static const struct setting_define pop3c_setting_defines[] = {
+ DEF(STR, pop3c_host),
+ DEF(IN_PORT, pop3c_port),
+
+ DEF(STR_VARS, pop3c_user),
+ DEF(STR_VARS, pop3c_master_user),
+ DEF(STR, pop3c_password),
+
+ DEF(ENUM, pop3c_ssl),
+ DEF(BOOL, pop3c_ssl_verify),
+
+ DEF(STR, pop3c_rawlog_dir),
+ DEF(BOOL, pop3c_quick_received_date),
+
+ DEF(STR, pop3c_features),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct pop3c_settings pop3c_default_settings = {
+ .pop3c_host = "",
+ .pop3c_port = 110,
+
+ .pop3c_user = "%u",
+ .pop3c_master_user = "",
+ .pop3c_password = "",
+
+ .pop3c_ssl = "no:pop3s:starttls",
+ .pop3c_ssl_verify = TRUE,
+
+ .pop3c_rawlog_dir = "",
+ .pop3c_quick_received_date = FALSE,
+
+ .pop3c_features = ""
+};
+static const struct setting_parser_info pop3c_setting_parser_info = {
+ .module_name = "pop3c",
+ .defines = pop3c_setting_defines,
+ .defaults = &pop3c_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct pop3c_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info,
+
+ .check_func = pop3c_settings_check
+};
+/* ../../src/lib-storage/index/mbox/mbox-settings.c */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct mbox_settings)
+static const struct setting_define mbox_setting_defines[] = {
+ DEF(STR, mbox_read_locks),
+ DEF(STR, mbox_write_locks),
+ DEF(TIME, mbox_lock_timeout),
+ DEF(TIME, mbox_dotlock_change_timeout),
+ DEF(SIZE, mbox_min_index_size),
+ DEF(BOOL, mbox_dirty_syncs),
+ DEF(BOOL, mbox_very_dirty_syncs),
+ DEF(BOOL, mbox_lazy_writes),
+ DEF(ENUM, mbox_md5),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct mbox_settings mbox_default_settings = {
+ .mbox_read_locks = "fcntl",
+ .mbox_write_locks = "dotlock fcntl",
+ .mbox_lock_timeout = 5*60,
+ .mbox_dotlock_change_timeout = 2*60,
+ .mbox_min_index_size = 0,
+ .mbox_dirty_syncs = TRUE,
+ .mbox_very_dirty_syncs = FALSE,
+ .mbox_lazy_writes = TRUE,
+ .mbox_md5 = "apop3d:all"
+};
+static const struct setting_parser_info mbox_setting_parser_info = {
+ .module_name = "mbox",
+ .defines = mbox_setting_defines,
+ .defaults = &mbox_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct mbox_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info
+};
+/* ../../src/lib-storage/index/maildir/maildir-settings.c */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct maildir_settings)
+static const struct setting_define maildir_setting_defines[] = {
+ DEF(BOOL, maildir_copy_with_hardlinks),
+ DEF(BOOL, maildir_very_dirty_syncs),
+ DEF(BOOL, maildir_broken_filename_sizes),
+ DEF(BOOL, maildir_empty_new),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct maildir_settings maildir_default_settings = {
+ .maildir_copy_with_hardlinks = TRUE,
+ .maildir_very_dirty_syncs = FALSE,
+ .maildir_broken_filename_sizes = FALSE,
+ .maildir_empty_new = FALSE
+};
+static const struct setting_parser_info maildir_setting_parser_info = {
+ .module_name = "maildir",
+ .defines = maildir_setting_defines,
+ .defaults = &maildir_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct maildir_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info
+};
+/* ../../src/lib-storage/index/imapc/imapc-settings.c */
+/* <settings checks> */
+struct imapc_feature_list {
+ const char *name;
+ enum imapc_features num;
+};
+
+static const struct imapc_feature_list imapc_feature_list[] = {
+ { "rfc822.size", IMAPC_FEATURE_RFC822_SIZE },
+ { "guid-forced", IMAPC_FEATURE_GUID_FORCED },
+ { "fetch-headers", IMAPC_FEATURE_FETCH_HEADERS },
+ { "gmail-migration", IMAPC_FEATURE_GMAIL_MIGRATION },
+ { "search", IMAPC_FEATURE_SEARCH },
+ { "zimbra-workarounds", IMAPC_FEATURE_ZIMBRA_WORKAROUNDS },
+ { "no-examine", IMAPC_FEATURE_NO_EXAMINE },
+ { "proxyauth", IMAPC_FEATURE_PROXYAUTH },
+ { "fetch-msn-workarounds", IMAPC_FEATURE_FETCH_MSN_WORKAROUNDS },
+ { "fetch-fix-broken-mails", IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS },
+ { "modseq", IMAPC_FEATURE_MODSEQ },
+ { "delay-login", IMAPC_FEATURE_DELAY_LOGIN },
+ { "fetch-bodystructure", IMAPC_FEATURE_FETCH_BODYSTRUCTURE },
+ { "send-id", IMAPC_FEATURE_SEND_ID },
+ { "fetch-empty-is-expunged", IMAPC_FEATURE_FETCH_EMPTY_IS_EXPUNGED },
+ { "no-msn-updates", IMAPC_FEATURE_NO_MSN_UPDATES },
+ { "acl", IMAPC_FEATURE_ACL },
+ { NULL, 0 }
+};
+
+static int
+imapc_settings_parse_throttle(struct imapc_settings *set,
+ const char *throttle_str, const char **error_r)
+{
+ const char *const *tmp;
+
+ tmp = t_strsplit(throttle_str, ":");
+ if (str_array_length(tmp) != 3 ||
+ str_to_uint(tmp[0], &set->throttle_init_msecs) < 0 ||
+ str_to_uint(tmp[1], &set->throttle_max_msecs) < 0 ||
+ str_to_uint(tmp[2], &set->throttle_shrink_min_msecs) < 0) {
+ *error_r = "imapc_features: Invalid throttle settings";
+ return -1;
+ }
+ return 0;
+}
+
+static int
+imapc_settings_parse_features(struct imapc_settings *set,
+ const char **error_r)
+{
+ enum imapc_features features = 0;
+ const struct imapc_feature_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->imapc_features, " ,");
+ for (; *str != NULL; str++) {
+ list = imapc_feature_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ features |= list->num;
+ break;
+ }
+ }
+ if (strncasecmp(*str, "throttle:", 9) == 0) {
+ if (imapc_settings_parse_throttle(set, *str + 9, error_r) < 0)
+ return -1;
+ continue;
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf("imapc_features: "
+ "Unknown feature: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_features = features;
+ return 0;
+}
+
+static bool imapc_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct imapc_settings *set = _set;
+
+ if (set->imapc_max_idle_time == 0) {
+ *error_r = "imapc_max_idle_time must not be 0";
+ return FALSE;
+ }
+ if (imapc_settings_parse_features(set, error_r) < 0)
+ return FALSE;
+ return TRUE;
+}
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct imapc_settings)
+static const struct setting_define imapc_setting_defines[] = {
+ DEF(STR, imapc_host),
+ DEF(IN_PORT, imapc_port),
+
+ DEF(STR_VARS, imapc_user),
+ DEF(STR_VARS, imapc_master_user),
+ DEF(STR, imapc_password),
+ DEF(STR, imapc_sasl_mechanisms),
+
+ DEF(ENUM, imapc_ssl),
+ DEF(BOOL, imapc_ssl_verify),
+
+ DEF(STR, imapc_features),
+ DEF(STR, imapc_rawlog_dir),
+ DEF(STR, imapc_list_prefix),
+ DEF(TIME, imapc_cmd_timeout),
+ DEF(TIME, imapc_max_idle_time),
+ DEF(UINT, imapc_connection_retry_count),
+ DEF(TIME_MSECS, imapc_connection_retry_interval),
+ DEF(SIZE, imapc_max_line_length),
+
+ DEF(STR, pop3_deleted_flag),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct imapc_settings imapc_default_settings = {
+ .imapc_host = "",
+ .imapc_port = 143,
+
+ .imapc_user = "",
+ .imapc_master_user = "",
+ .imapc_password = "",
+ .imapc_sasl_mechanisms = "",
+
+ .imapc_ssl = "no:imaps:starttls",
+ .imapc_ssl_verify = TRUE,
+
+ .imapc_features = "",
+ .imapc_rawlog_dir = "",
+ .imapc_list_prefix = "",
+ .imapc_cmd_timeout = 5*60,
+ .imapc_max_idle_time = 60*29,
+ .imapc_connection_retry_count = 1,
+ .imapc_connection_retry_interval = 1000,
+ .imapc_max_line_length = 0,
+
+ .pop3_deleted_flag = ""
+};
+static const struct setting_parser_info imapc_setting_parser_info = {
+ .module_name = "imapc",
+ .defines = imapc_setting_defines,
+ .defaults = &imapc_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct imapc_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info,
+
+ .check_func = imapc_settings_check
+};
+/* ../../src/lib-storage/index/dbox-multi/mdbox-settings.c */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct mdbox_settings)
+static const struct setting_define mdbox_setting_defines[] = {
+ DEF(BOOL, mdbox_preallocate_space),
+ DEF(SIZE, mdbox_rotate_size),
+ DEF(TIME, mdbox_rotate_interval),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct mdbox_settings mdbox_default_settings = {
+ .mdbox_preallocate_space = FALSE,
+ .mdbox_rotate_size = 10*1024*1024,
+ .mdbox_rotate_interval = 0
+};
+static const struct setting_parser_info mdbox_setting_parser_info = {
+ .module_name = "mdbox",
+ .defines = mdbox_setting_defines,
+ .defaults = &mdbox_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct mdbox_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &mail_user_setting_parser_info
+};
+/* ../../src/lib-lda/lda-settings.c */
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct lda_settings)
+#define DEFLIST(field, name, defines) \
+ { .type = SET_DEFLIST, .key = name, \
+ .offset = offsetof(struct lda_settings, field), \
+ .list_info = defines }
+static const struct setting_define lda_setting_defines[] = {
+ DEF(STR, hostname),
+ DEF(STR, rejection_subject),
+ DEF(STR, rejection_reason),
+ DEF(STR, deliver_log_format),
+ DEF(STR, recipient_delimiter),
+ DEF(STR, lda_original_recipient_header),
+ DEF(BOOL, quota_full_tempfail),
+ DEF(BOOL, lda_mailbox_autocreate),
+ DEF(BOOL, lda_mailbox_autosubscribe),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct lda_settings lda_default_settings = {
+ .hostname = "",
+ .rejection_subject = "Rejected: %s",
+ .rejection_reason =
+ "Your message to <%t> was automatically rejected:%n%r",
+ .deliver_log_format = "msgid=%m: %$",
+ .recipient_delimiter = "+",
+ .lda_original_recipient_header = "",
+ .quota_full_tempfail = FALSE,
+ .lda_mailbox_autocreate = FALSE,
+ .lda_mailbox_autosubscribe = FALSE
+};
+static const struct setting_parser_info *lda_setting_dependencies[] = {
+ &mail_user_setting_parser_info,
+ &smtp_submit_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info lda_setting_parser_info = {
+ .module_name = "lda",
+ .defines = lda_setting_defines,
+ .defaults = &lda_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct lda_settings),
+
+ .parent_offset = SIZE_MAX,
+
+#ifndef CONFIG_BINARY
+ .check_func = lda_settings_check,
+#endif
+ .dependencies = lda_setting_dependencies
+};
+/* ../../src/lib-dict-backend/dict-sql-settings.c */
+#define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map)
+#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_sql_map)
+/* ../../src/lib-dict-backend/dict-ldap-settings.c */
+#undef DEF_STR
+#undef DEF_BOOL
+#undef DEF_UINT
+#define DEF_STR(name) DEF_STRUCT_STR(name, dict_ldap_map)
+#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_ldap_map)
+#define DEF_UINT(name) DEF_STRUCT_UINT(name ,dict_ldap_map)
+/* ../../src/submission/submission-settings.h */
+extern const struct setting_parser_info submission_setting_parser_info;
+/* <settings checks> */
+enum submission_client_workarounds {
+ SUBMISSION_WORKAROUND_WHITESPACE_BEFORE_PATH = BIT(0),
+ SUBMISSION_WORKAROUND_MAILBOX_FOR_PATH = BIT(1),
+};
+/* </settings checks> */
+struct submission_settings {
+ bool verbose_proctitle;
+ const char *rawlog_dir;
+
+ const char *hostname;
+
+ const char *login_greeting;
+ const char *login_trusted_networks;
+
+ const char *recipient_delimiter;
+
+ /* submission: */
+ uoff_t submission_max_mail_size;
+ unsigned int submission_max_recipients;
+ const char *submission_client_workarounds;
+ const char *submission_logout_format;
+
+ /* submission backend: */
+ const char *submission_backend_capabilities;
+
+ /* submission relay: */
+ const char *submission_relay_host;
+ in_port_t submission_relay_port;
+ bool submission_relay_trusted;
+
+ const char *submission_relay_user;
+ const char *submission_relay_master_user;
+ const char *submission_relay_password;
+
+ const char *submission_relay_ssl;
+ bool submission_relay_ssl_verify;
+
+ const char *submission_relay_rawlog_dir;
+ unsigned int submission_relay_max_idle_time;
+
+ unsigned int submission_relay_connect_timeout;
+ unsigned int submission_relay_command_timeout;
+
+ /* imap urlauth: */
+ const char *imap_urlauth_host;
+ in_port_t imap_urlauth_port;
+
+ enum submission_client_workarounds parsed_workarounds;
+};
+/* ../../src/submission-login/submission-login-settings.h */
+extern const struct setting_parser_info *submission_login_setting_roots[];
+/* <settings checks> */
+enum submission_login_client_workarounds {
+ SUBMISSION_LOGIN_WORKAROUND_IMPLICIT_AUTH_EXTERNAL = BIT(0),
+ SUBMISSION_LOGIN_WORKAROUND_EXOTIC_BACKEND = BIT(1),
+};
+/* </settings checks> */
+struct submission_login_settings {
+ const char *hostname;
+
+ /* submission: */
+ uoff_t submission_max_mail_size;
+ const char *submission_client_workarounds;
+ const char *submission_backend_capabilities;
+
+ enum submission_login_client_workarounds parsed_workarounds;
+};
+/* ../../src/stats/stats-settings.h */
+extern const struct setting_parser_info stats_setting_parser_info;
+extern const struct setting_parser_info stats_metric_setting_parser_info;
+/* <settings checks> */
+/*
+ * We allow a selection of a timestamp format.
+ *
+ * The 'time-unix' format generates a number with the number of seconds
+ * since 1970-01-01 00:00 UTC.
+ *
+ * The 'time-rfc3339' format uses the YYYY-MM-DDTHH:MM:SS.uuuuuuZ format as
+ * defined by RFC 3339.
+ *
+ * The special native format (not explicitly selectable in the config, but
+ * default if no time-* token is used) uses the format's native timestamp
+ * format. Note that not all formats have a timestamp data format.
+ *
+ * The native format and the rules below try to address the question: can a
+ * parser that doesn't have any knowledge of fields' values' types losslessly
+ * reconstruct the fields?
+ *
+ * For example, JSON only has strings and numbers, so it cannot represent a
+ * timestamp in a "context-free lossless" way. Therefore, when making a
+ * JSON blob, we need to decide which way to serialize timestamps. No
+ * matter how we do it, we incur some loss. If a decoder sees 1557232304 in
+ * a field, it cannot be certain if the field is an integer that just
+ * happens to be a reasonable timestamp, or if it actually is a timestamp.
+ * Same goes with RFC3339 - it could just be that the user supplied a string
+ * that looks like a timestamp, and that string made it into an event field.
+ *
+ * Other common serialization formats, such as CBOR, have a lossless way of
+ * encoding timestamps.
+ *
+ * Note that there are two concepts at play: native and default.
+ *
+ * The rules for how the format's timestamp formats are used:
+ *
+ * 1. The default time format is the native format.
+ * 2. The native time format may or may not exist for a given format (e.g.,
+ * in JSON)
+ * 3. If the native format doesn't exist and no time format was specified in
+ * the config, it is a config error.
+ *
+ * We went with these rules because:
+ *
+ * 1. It prevents type information loss by default.
+ * 2. It completely isolates the policy from the algorithm.
+ * 3. It defers the decision whether each format without a native timestamp
+ * type should have a default acting as native until after we've had some
+ * operational experience.
+ * 4. A future decision to add a default (via 3. point) will be 100% compatible.
+ */
+enum event_exporter_time_fmt {
+ EVENT_EXPORTER_TIME_FMT_NATIVE = 0,
+ EVENT_EXPORTER_TIME_FMT_UNIX,
+ EVENT_EXPORTER_TIME_FMT_RFC3339,
+};
+/* </settings checks> */
+/* <settings checks> */
+enum stats_metric_group_by_func {
+ STATS_METRIC_GROUPBY_DISCRETE = 0,
+ STATS_METRIC_GROUPBY_QUANTIZED,
+};
+
+/*
+ * A range covering a stats bucket. The the interval is half closed - the
+ * minimum is excluded and the maximum is included. In other words: (min, max].
+ * Because we don't have a +Inf and -Inf, we use INTMAX_MIN and INTMAX_MAX
+ * respectively.
+ */
+struct stats_metric_settings_bucket_range {
+ intmax_t min;
+ intmax_t max;
+};
+
+struct stats_metric_settings_group_by {
+ const char *field;
+ enum stats_metric_group_by_func func;
+ unsigned int num_ranges;
+ struct stats_metric_settings_bucket_range *ranges;
+};
+/* </settings checks> */
+#define STATS_METRIC_SETTINGS_DEFAULT_EXPORTER_INCLUDE \
+ "name hostname timestamps categories fields"
+struct stats_exporter_settings {
+ const char *name;
+ const char *transport;
+ const char *transport_args;
+ unsigned int transport_timeout;
+ const char *format;
+ const char *format_args;
+
+ /* parsed values */
+ enum event_exporter_time_fmt parsed_time_format;
+};
+struct stats_metric_settings {
+ const char *metric_name;
+ const char *description;
+ const char *fields;
+ const char *group_by;
+ const char *filter;
+
+ ARRAY(struct stats_metric_settings_group_by) parsed_group_by;
+ struct event_filter *parsed_filter;
+
+ /* exporter related fields */
+ const char *exporter;
+ const char *exporter_include;
+};
+struct stats_settings {
+ const char *stats_http_rawlog_dir;
+
+ ARRAY(struct stats_exporter_settings *) exporters;
+ ARRAY(struct stats_metric_settings *) metrics;
+};
+/* ../../src/replication/replicator/replicator-settings.h */
+extern const struct setting_parser_info replicator_setting_parser_info;
+struct replicator_settings {
+ const char *auth_socket_path;
+ const char *doveadm_socket_path;
+ const char *replication_dsync_parameters;
+
+ unsigned int replication_full_sync_interval;
+ unsigned int replication_max_conns;
+};
+/* ../../src/replication/aggregator/aggregator-settings.h */
+extern const struct setting_parser_info aggregator_setting_parser_info;
+struct aggregator_settings {
+ const char *replicator_host;
+ in_port_t replicator_port;
+};
+/* ../../src/pop3/pop3-settings.h */
+extern const struct setting_parser_info pop3_setting_parser_info;
+/* <settings checks> */
+enum pop3_client_workarounds {
+ WORKAROUND_OUTLOOK_NO_NULS = 0x01,
+ WORKAROUND_OE_NS_EOH = 0x02
+};
+enum pop3_delete_type {
+ POP3_DELETE_TYPE_EXPUNGE = 0,
+ POP3_DELETE_TYPE_FLAG,
+};
+/* </settings checks> */
+struct pop3_settings {
+ bool verbose_proctitle;
+ const char *rawlog_dir;
+
+ /* pop3: */
+ bool pop3_no_flag_updates;
+ bool pop3_enable_last;
+ bool pop3_reuse_xuidl;
+ bool pop3_save_uidl;
+ bool pop3_lock_session;
+ bool pop3_fast_size_lookups;
+ const char *pop3_client_workarounds;
+ const char *pop3_logout_format;
+ const char *pop3_uidl_duplicates;
+ const char *pop3_deleted_flag;
+ const char *pop3_delete_type;
+
+ enum pop3_client_workarounds parsed_workarounds;
+ enum pop3_delete_type parsed_delete_type;
+};
+/* ../../src/pop3-login/pop3-login-settings.h */
+extern const struct setting_parser_info *pop3_login_setting_roots[];
+/* ../../src/plugins/quota/quota-status-settings.h */
+extern const struct setting_parser_info quota_status_setting_parser_info;
+struct quota_status_settings {
+ const char *recipient_delimiter;
+};
+/* ../../src/plugins/mail-crypt/fs-crypt-settings.h */
+extern const struct setting_parser_info fs_crypt_setting_parser_info;
+struct fs_crypt_settings {
+ ARRAY(const char *) plugin_envs;
+};
+/* ../../src/old-stats/stats-settings.h */
+extern const struct setting_parser_info old_stats_setting_parser_info;
+struct old_stats_settings {
+ uoff_t memory_limit;
+
+ unsigned int command_min_time;
+ unsigned int session_min_time;
+ unsigned int user_min_time;
+ unsigned int domain_min_time;
+ unsigned int ip_min_time;
+
+ unsigned int carbon_interval;
+ const char *carbon_server;
+ const char *carbon_name;
+};
+/* ../../src/master/master-settings.h */
+extern const struct setting_parser_info master_setting_parser_info;
+struct master_settings {
+ const char *base_dir;
+ const char *state_dir;
+ const char *libexec_dir;
+ const char *instance_name;
+ const char *protocols;
+ const char *listen;
+ const char *ssl;
+ const char *default_internal_user;
+ const char *default_internal_group;
+ const char *default_login_user;
+ unsigned int default_process_limit;
+ unsigned int default_client_limit;
+ unsigned int default_idle_kill;
+ uoff_t default_vsz_limit;
+
+ bool version_ignore;
+
+ unsigned int first_valid_uid, last_valid_uid;
+ unsigned int first_valid_gid, last_valid_gid;
+
+ ARRAY_TYPE(service_settings) services;
+ char **protocols_split;
+};
+/* ../../src/login-common/login-settings.h */
+extern const struct setting_parser_info **login_set_roots;
+extern const struct setting_parser_info login_setting_parser_info;
+struct login_settings {
+ const char *login_trusted_networks;
+ const char *login_source_ips;
+ const char *login_greeting;
+ const char *login_log_format_elements, *login_log_format;
+ const char *login_access_sockets;
+ const char *login_proxy_notify_path;
+ const char *login_plugin_dir;
+ const char *login_plugins;
+ unsigned int login_proxy_timeout;
+ unsigned int login_proxy_max_reconnects;
+ unsigned int login_proxy_max_disconnect_delay;
+ const char *login_proxy_rawlog_dir;
+ const char *director_username_hash;
+
+ bool auth_ssl_require_client_cert;
+ bool auth_ssl_username_from_cert;
+
+ bool disable_plaintext_auth;
+ bool auth_verbose;
+ bool auth_debug;
+ bool auth_debug_passwords;
+ bool verbose_proctitle;
+
+ unsigned int mail_max_userip_connections;
+
+ /* generated: */
+ char *const *log_format_elements_split;
+};
+/* ../../src/lmtp/lmtp-settings.h */
+extern const struct setting_parser_info lmtp_setting_parser_info;
+/* <settings checks> */
+enum lmtp_hdr_delivery_address {
+ LMTP_HDR_DELIVERY_ADDRESS_NONE,
+ LMTP_HDR_DELIVERY_ADDRESS_FINAL,
+ LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL
+};
+
+enum lmtp_client_workarounds {
+ LMTP_WORKAROUND_WHITESPACE_BEFORE_PATH = BIT(0),
+ LMTP_WORKAROUND_MAILBOX_FOR_PATH = BIT(1),
+};
+/* </settings checks> */
+struct lmtp_settings {
+ bool lmtp_proxy;
+ bool lmtp_save_to_detail_mailbox;
+ bool lmtp_rcpt_check_quota;
+ bool lmtp_add_received_header;
+ bool lmtp_verbose_replies;
+ unsigned int lmtp_user_concurrency_limit;
+ const char *lmtp_hdr_delivery_address;
+ const char *lmtp_rawlog_dir;
+ const char *lmtp_proxy_rawlog_dir;
+
+ const char *lmtp_client_workarounds;
+
+ const char *login_greeting;
+ const char *login_trusted_networks;
+
+ const char *mail_plugins;
+ const char *mail_plugin_dir;
+
+ enum lmtp_hdr_delivery_address parsed_lmtp_hdr_delivery_address;
+
+ enum lmtp_client_workarounds parsed_workarounds;
+};
+/* ../../src/imap/imap-settings.h */
+extern const struct setting_parser_info imap_setting_parser_info;
+/* <settings checks> */
+enum imap_client_workarounds {
+ WORKAROUND_DELAY_NEWMAIL = 0x01,
+ WORKAROUND_TB_EXTRA_MAILBOX_SEP = 0x08,
+ WORKAROUND_TB_LSUB_FLAGS = 0x10
+};
+
+enum imap_client_fetch_failure {
+ IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY,
+ IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_AFTER,
+ IMAP_CLIENT_FETCH_FAILURE_NO_AFTER,
+};
+/* </settings checks> */
+struct imap_settings {
+ bool verbose_proctitle;
+ const char *rawlog_dir;
+
+ /* imap: */
+ uoff_t imap_max_line_length;
+ unsigned int imap_idle_notify_interval;
+ const char *imap_capability;
+ const char *imap_client_workarounds;
+ const char *imap_logout_format;
+ const char *imap_id_send;
+ const char *imap_id_log;
+ const char *imap_fetch_failure;
+ bool imap_metadata;
+ bool imap_literal_minus;
+ unsigned int imap_hibernate_timeout;
+
+ /* imap urlauth: */
+ const char *imap_urlauth_host;
+ in_port_t imap_urlauth_port;
+
+ enum imap_client_workarounds parsed_workarounds;
+ enum imap_client_fetch_failure parsed_fetch_failure;
+};
+/* ../../src/imap-urlauth/imap-urlauth-worker-settings.h */
+extern const struct setting_parser_info imap_urlauth_worker_setting_parser_info;
+struct imap_urlauth_worker_settings {
+ bool verbose_proctitle;
+
+ /* imap_urlauth: */
+ const char *imap_urlauth_host;
+ in_port_t imap_urlauth_port;
+};
+/* ../../src/imap-urlauth/imap-urlauth-settings.h */
+extern const struct setting_parser_info imap_urlauth_setting_parser_info;
+struct imap_urlauth_settings {
+ const char *base_dir;
+
+ bool mail_debug;
+
+ bool verbose_proctitle;
+
+ /* imap_urlauth: */
+ const char *imap_urlauth_logout_format;
+
+ const char *imap_urlauth_submit_user;
+ const char *imap_urlauth_stream_user;
+};
+/* ../../src/imap-urlauth/imap-urlauth-login-settings.h */
+extern const struct setting_parser_info *imap_urlauth_login_setting_roots[];
+/* ../../src/imap-login/imap-login-settings.h */
+extern const struct setting_parser_info *imap_login_setting_roots[];
+struct imap_login_settings {
+ const char *imap_capability;
+ const char *imap_id_send;
+ const char *imap_id_log;
+ bool imap_literal_minus;
+ bool imap_id_retain;
+};
+/* ../../src/doveadm/doveadm-settings.h */
+extern const struct setting_parser_info doveadm_setting_parser_info;
+/* <settings checks> */
+enum dsync_features {
+ DSYNC_FEATURE_EMPTY_HDR_WORKAROUND = 0x1,
+};
+/* </settings checks> */
+struct doveadm_settings {
+ const char *base_dir;
+ const char *libexec_dir;
+ const char *mail_plugins;
+ const char *mail_plugin_dir;
+ const char *mail_temp_dir;
+ bool auth_debug;
+ const char *auth_socket_path;
+ const char *doveadm_socket_path;
+ unsigned int doveadm_worker_count;
+ in_port_t doveadm_port;
+ const char *doveadm_ssl;
+ const char *doveadm_username;
+ const char *doveadm_password;
+ const char *doveadm_allowed_commands;
+ const char *dsync_alt_char;
+ const char *dsync_remote_cmd;
+ const char *director_username_hash;
+ const char *doveadm_api_key;
+ const char *dsync_features;
+ const char *dsync_hashed_headers;
+ unsigned int dsync_commit_msgs_interval;
+ const char *doveadm_http_rawlog_dir;
+ enum dsync_features parsed_features;
+ ARRAY(const char *) plugin_envs;
+};
+/* ../../src/director/director-settings.h */
+extern const struct setting_parser_info director_setting_parser_info;
+struct director_settings {
+ const char *master_user_separator;
+
+ const char *director_servers;
+ const char *director_mail_servers;
+ const char *director_username_hash;
+ const char *director_flush_socket;
+
+ unsigned int director_ping_idle_timeout;
+ unsigned int director_ping_max_timeout;
+ unsigned int director_user_expire;
+ unsigned int director_user_kick_delay;
+ unsigned int director_max_parallel_moves;
+ unsigned int director_max_parallel_kicks;
+ uoff_t director_output_buffer_size;
+};
+/* ../../src/dict/dict-settings.h */
+extern const struct setting_parser_info dict_setting_parser_info;
+struct dict_server_settings {
+ const char *base_dir;
+ bool verbose_proctitle;
+
+ const char *dict_db_config;
+ ARRAY(const char *) dicts;
+};
+/* ../../src/auth/auth-settings.h */
+extern const struct setting_parser_info auth_setting_parser_info;
+struct auth_passdb_settings {
+ const char *name;
+ const char *driver;
+ const char *args;
+ const char *default_fields;
+ const char *override_fields;
+ const char *mechanisms;
+ const char *username_filter;
+
+ const char *skip;
+ const char *result_success;
+ const char *result_failure;
+ const char *result_internalfail;
+ bool deny;
+ bool pass; /* deprecated, use result_success=continue instead */
+ bool master;
+ const char *auth_verbose;
+};
+struct auth_userdb_settings {
+ const char *name;
+ const char *driver;
+ const char *args;
+ const char *default_fields;
+ const char *override_fields;
+
+ const char *skip;
+ const char *result_success;
+ const char *result_failure;
+ const char *result_internalfail;
+ const char *auth_verbose;
+};
+struct auth_settings {
+ const char *mechanisms;
+ const char *realms;
+ const char *default_realm;
+ uoff_t cache_size;
+ unsigned int cache_ttl;
+ unsigned int cache_negative_ttl;
+ bool cache_verify_password_with_worker;
+ const char *username_chars;
+ const char *username_translation;
+ const char *username_format;
+ const char *master_user_separator;
+ const char *anonymous_username;
+ const char *krb5_keytab;
+ const char *gssapi_hostname;
+ const char *winbind_helper_path;
+ const char *proxy_self;
+ unsigned int failure_delay;
+
+ const char *policy_server_url;
+ const char *policy_server_api_header;
+ unsigned int policy_server_timeout_msecs;
+ const char *policy_hash_mech;
+ const char *policy_hash_nonce;
+ const char *policy_request_attributes;
+ bool policy_reject_on_fail;
+ bool policy_check_before_auth;
+ bool policy_check_after_auth;
+ bool policy_report_after_auth;
+ bool policy_log_only;
+ unsigned int policy_hash_truncate;
+
+ bool stats;
+ bool verbose, debug, debug_passwords;
+ const char *verbose_passwords;
+ bool ssl_require_client_cert;
+ bool ssl_username_from_cert;
+ bool use_winbind;
+
+ unsigned int worker_max_count;
+
+ /* settings that don't have auth_ prefix: */
+ ARRAY(struct auth_passdb_settings *) passdbs;
+ ARRAY(struct auth_userdb_settings *) userdbs;
+
+ const char *base_dir;
+ const char *ssl_client_ca_dir;
+ const char *ssl_client_ca_file;
+
+ bool verbose_proctitle;
+ unsigned int first_valid_uid;
+ unsigned int last_valid_uid;
+ unsigned int first_valid_gid;
+ unsigned int last_valid_gid;
+
+ /* generated: */
+ char username_chars_map[256];
+ char username_translation_map[256];
+ const char *const *realms_arr;
+ const struct ip_addr *proxy_self_ips;
+};
+/* ../../src/util/tcpwrap-settings.c */
+#ifdef HAVE_LIBWRAP
+struct service_settings tcpwrap_service_settings = {
+ .name = "tcpwrap",
+ .protocol = "",
+ .type = "",
+ .executable = "tcpwrap",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#endif
+/* ../../src/util/health-check-settings.c */
+struct service_settings health_check_service_settings = {
+ .name = "health-check",
+ .protocol = "",
+ .type = "",
+ .executable = "script -p health-check.sh",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = TRUE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+/* ../../src/submission/submission-settings.c */
+/* <settings checks> */
+static struct file_listener_settings submission_unix_listeners_array[] = {
+ { "login/submission", 0666, "", "" }
+};
+static struct file_listener_settings *submission_unix_listeners[] = {
+ &submission_unix_listeners_array[0]
+};
+static buffer_t submission_unix_listeners_buf = {
+ { { submission_unix_listeners, sizeof(submission_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+struct submission_client_workaround_list {
+ const char *name;
+ enum submission_client_workarounds num;
+};
+
+/* These definitions need to be kept in sync with equivalent definitions present
+ in src/submission-login/submission-login-settings.c. Workarounds that are not
+ relevant to the submission service are defined as 0 here to prevent "Unknown
+ workaround" errors below. */
+static const struct submission_client_workaround_list
+submission_client_workaround_list[] = {
+ { "whitespace-before-path",
+ SUBMISSION_WORKAROUND_WHITESPACE_BEFORE_PATH },
+ { "mailbox-for-path",
+ SUBMISSION_WORKAROUND_MAILBOX_FOR_PATH },
+ { "implicit-auth-external", 0 },
+ { "exotic-backend", 0 },
+ { NULL, 0 }
+};
+
+static int
+submission_settings_parse_workarounds(struct submission_settings *set,
+ const char **error_r)
+{
+ enum submission_client_workarounds client_workarounds = 0;
+ const struct submission_client_workaround_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->submission_client_workarounds, " ,");
+ for (; *str != NULL; str++) {
+ list = submission_client_workaround_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ client_workarounds |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf(
+ "submission_client_workarounds: "
+ "Unknown workaround: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_workarounds = client_workarounds;
+ return 0;
+}
+
+static bool
+submission_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r)
+{
+ struct submission_settings *set = _set;
+
+ if (submission_settings_parse_workarounds(set, error_r) < 0)
+ return FALSE;
+
+#ifndef CONFIG_BINARY
+ if (set->submission_relay_max_idle_time == 0) {
+ *error_r = "submission_relay_max_idle_time must not be 0";
+ return FALSE;
+ }
+ if (*set->hostname == '\0')
+ set->hostname = p_strdup(pool, my_hostdomain());
+#endif
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings submission_service_settings = {
+ .name = "submission",
+ .protocol = "submission",
+ .type = "",
+ .executable = "submission",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1024,
+ .client_limit = 1,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &submission_unix_listeners_buf,
+ sizeof(submission_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct submission_settings)
+static const struct setting_define submission_setting_defines[] = {
+ DEF(BOOL, verbose_proctitle),
+ DEF(STR_VARS, rawlog_dir),
+
+ DEF(STR, hostname),
+
+ DEF(STR, login_greeting),
+ DEF(STR, login_trusted_networks),
+
+ DEF(STR, recipient_delimiter),
+
+ DEF(SIZE, submission_max_mail_size),
+ DEF(UINT, submission_max_recipients),
+ DEF(STR, submission_client_workarounds),
+ DEF(STR, submission_logout_format),
+
+ DEF(STR, submission_backend_capabilities),
+
+ DEF(STR, submission_relay_host),
+ DEF(IN_PORT, submission_relay_port),
+ DEF(BOOL, submission_relay_trusted),
+
+ DEF(STR, submission_relay_user),
+ DEF(STR, submission_relay_master_user),
+ DEF(STR, submission_relay_password),
+
+ DEF(ENUM, submission_relay_ssl),
+ DEF(BOOL, submission_relay_ssl_verify),
+
+ DEF(STR_VARS, submission_relay_rawlog_dir),
+ DEF(TIME, submission_relay_max_idle_time),
+
+ DEF(TIME_MSECS, submission_relay_connect_timeout),
+ DEF(TIME_MSECS, submission_relay_command_timeout),
+
+ DEF(STR, imap_urlauth_host),
+ DEF(IN_PORT, imap_urlauth_port),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct submission_settings submission_default_settings = {
+ .verbose_proctitle = FALSE,
+ .rawlog_dir = "",
+
+ .hostname = "",
+
+ .login_greeting = PACKAGE_NAME" ready.",
+ .login_trusted_networks = "",
+
+ .recipient_delimiter = "+",
+
+ .submission_max_mail_size = 40*1024*1024,
+ .submission_max_recipients = 0,
+ .submission_client_workarounds = "",
+ .submission_logout_format = "in=%i out=%o",
+
+ .submission_backend_capabilities = NULL,
+
+ .submission_relay_host = "",
+ .submission_relay_port = 25,
+ .submission_relay_trusted = FALSE,
+
+ .submission_relay_user = "",
+ .submission_relay_master_user = "",
+ .submission_relay_password = "",
+
+ .submission_relay_ssl = "no:smtps:starttls",
+ .submission_relay_ssl_verify = TRUE,
+
+ .submission_relay_rawlog_dir = "",
+ .submission_relay_max_idle_time = 60*29,
+
+ .submission_relay_connect_timeout = 30*1000,
+ .submission_relay_command_timeout = 60*5*1000,
+
+ .imap_urlauth_host = "",
+ .imap_urlauth_port = 143,
+};
+static const struct setting_parser_info *submission_setting_dependencies[] = {
+ &mail_user_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info submission_setting_parser_info = {
+ .module_name = "submission",
+ .defines = submission_setting_defines,
+ .defaults = &submission_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct submission_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = submission_settings_verify,
+ .dependencies = submission_setting_dependencies
+};
+/* ../../src/submission-login/submission-login-settings.c */
+/* <settings checks> */
+static struct inet_listener_settings submission_login_inet_listeners_array[] = {
+ { .name = "submission", .address = "", .port = 587 },
+ { .name = "submissions", .address = "", .port = 465, .ssl = TRUE }
+};
+static struct inet_listener_settings *submission_login_inet_listeners[] = {
+ &submission_login_inet_listeners_array[0]
+};
+static buffer_t submission_login_inet_listeners_buf = {
+ { { submission_login_inet_listeners,
+ sizeof(submission_login_inet_listeners) } }
+};
+
+/* </settings checks> */
+/* <settings checks> */
+struct submission_login_client_workaround_list {
+ const char *name;
+ enum submission_login_client_workarounds num;
+};
+
+/* These definitions need to be kept in sync with equivalent definitions present
+ in src/submission/submission-settings.c. Workarounds that are not relevant
+ to the submission-login service are defined as 0 here to prevent "Unknown
+ workaround" errors below. */
+static const struct submission_login_client_workaround_list
+submission_login_client_workaround_list[] = {
+ { "whitespace-before-path", 0},
+ { "mailbox-for-path", 0 },
+ { "implicit-auth-external",
+ SUBMISSION_LOGIN_WORKAROUND_IMPLICIT_AUTH_EXTERNAL },
+ { "exotic-backend",
+ SUBMISSION_LOGIN_WORKAROUND_EXOTIC_BACKEND },
+ { NULL, 0 }
+};
+
+static int
+submission_login_settings_parse_workarounds(
+ struct submission_login_settings *set, const char **error_r)
+{
+ enum submission_login_client_workarounds client_workarounds = 0;
+ const struct submission_login_client_workaround_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->submission_client_workarounds, " ,");
+ for (; *str != NULL; str++) {
+ list = submission_login_client_workaround_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ client_workarounds |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf(
+ "submission_client_workarounds: "
+ "Unknown workaround: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_workarounds = client_workarounds;
+ return 0;
+}
+
+static bool
+submission_login_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct submission_login_settings *set = _set;
+
+ if (submission_login_settings_parse_workarounds(set, error_r) < 0)
+ return FALSE;
+
+#ifndef CONFIG_BINARY
+ if (*set->hostname == '\0')
+ set->hostname = p_strdup(pool, my_hostdomain());
+#endif
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings submission_login_service_settings = {
+ .name = "submission-login",
+ .protocol = "submission",
+ .type = "login",
+ .executable = "submission-login",
+ .user = "$default_login_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "login",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = { { &submission_login_inet_listeners_buf,
+ sizeof(submission_login_inet_listeners[0]) } }
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct submission_login_settings)
+static const struct setting_define submission_login_setting_defines[] = {
+ DEF(STR, hostname),
+
+ DEF(SIZE, submission_max_mail_size),
+ DEF(STR, submission_client_workarounds),
+ DEF(STR, submission_backend_capabilities),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct submission_login_settings submission_login_default_settings = {
+ .hostname = "",
+
+ .submission_max_mail_size = 0,
+ .submission_client_workarounds = "",
+ .submission_backend_capabilities = NULL
+};
+static const struct setting_parser_info *submission_login_setting_dependencies[] = {
+ &login_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info submission_login_setting_parser_info = {
+ .module_name = "submission-login",
+ .defines = submission_login_setting_defines,
+ .defaults = &submission_login_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct submission_login_settings),
+ .parent_offset = SIZE_MAX,
+
+ .check_func = submission_login_settings_check,
+ .dependencies = submission_login_setting_dependencies
+};
+const struct setting_parser_info *submission_login_setting_roots[] = {
+ &login_setting_parser_info,
+ &submission_login_setting_parser_info,
+ NULL
+};
+/* ../../src/stats/stats-settings.c */
+extern const struct setting_parser_info stats_metric_setting_parser_info;
+extern const struct setting_parser_info stats_exporter_setting_parser_info;
+/* <settings checks> */
+#include "event-filter.h"
+#include <math.h>
+/* </settings checks> */
+/* <settings checks> */
+static struct file_listener_settings stats_unix_listeners_array[] = {
+ { "stats-reader", 0600, "", "" },
+ { "stats-writer", 0660, "", "$default_internal_group" },
+ { "login/stats-writer", 0600, "$default_login_user", "" },
+};
+static struct file_listener_settings *stats_unix_listeners[] = {
+ &stats_unix_listeners_array[0],
+ &stats_unix_listeners_array[1],
+ &stats_unix_listeners_array[2],
+};
+static buffer_t stats_unix_listeners_buf = {
+ { { stats_unix_listeners, sizeof(stats_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+static bool parse_format_args_set_time(struct stats_exporter_settings *set,
+ enum event_exporter_time_fmt fmt,
+ const char **error_r)
+{
+ if ((set->parsed_time_format != EVENT_EXPORTER_TIME_FMT_NATIVE) &&
+ (set->parsed_time_format != fmt)) {
+ *error_r = t_strdup_printf("Exporter '%s' specifies multiple "
+ "time format args", set->name);
+ return FALSE;
+ }
+
+ set->parsed_time_format = fmt;
+
+ return TRUE;
+}
+
+static bool parse_format_args(struct stats_exporter_settings *set,
+ const char **error_r)
+{
+ const char *const *tmp;
+
+ /* Defaults */
+ set->parsed_time_format = EVENT_EXPORTER_TIME_FMT_NATIVE;
+
+ tmp = t_strsplit_spaces(set->format_args, " ");
+
+ /*
+ * If the config contains multiple types of the same type (e.g.,
+ * both time-rfc3339 and time-unix) we fail the config check.
+ *
+ * Note: At the moment, we have only time-* tokens. In the future
+ * when we have other tokens, they should be parsed here.
+ */
+ for (; *tmp != NULL; tmp++) {
+ enum event_exporter_time_fmt fmt;
+
+ if (strcmp(*tmp, "time-rfc3339") == 0) {
+ fmt = EVENT_EXPORTER_TIME_FMT_RFC3339;
+ } else if (strcmp(*tmp, "time-unix") == 0) {
+ fmt = EVENT_EXPORTER_TIME_FMT_UNIX;
+ } else {
+ *error_r = t_strdup_printf("Unknown exporter format "
+ "arg: %s", *tmp);
+ return FALSE;
+ }
+
+ if (!parse_format_args_set_time(set, fmt, error_r))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool stats_exporter_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct stats_exporter_settings *set = _set;
+ bool time_fmt_required;
+
+ if (set->name[0] == '\0') {
+ *error_r = "Exporter name can't be empty";
+ return FALSE;
+ }
+
+ /* TODO: The following should be plugable.
+ *
+ * Note: Make sure to mirror any changes to the below code in
+ * stats_exporters_add_set().
+ */
+ if (set->format[0] == '\0') {
+ *error_r = "Exporter format name can't be empty";
+ return FALSE;
+ } else if (strcmp(set->format, "none") == 0) {
+ time_fmt_required = FALSE;
+ } else if (strcmp(set->format, "json") == 0) {
+ time_fmt_required = TRUE;
+ } else if (strcmp(set->format, "tab-text") == 0) {
+ time_fmt_required = TRUE;
+ } else {
+ *error_r = t_strdup_printf("Unknown exporter format '%s'",
+ set->format);
+ return FALSE;
+ }
+
+ /* TODO: The following should be plugable.
+ *
+ * Note: Make sure to mirror any changes to the below code in
+ * stats_exporters_add_set().
+ */
+ if (set->transport[0] == '\0') {
+ *error_r = "Exporter transport name can't be empty";
+ return FALSE;
+ } else if (strcmp(set->transport, "drop") == 0 ||
+ strcmp(set->transport, "http-post") == 0 ||
+ strcmp(set->transport, "log") == 0) {
+ /* no-op */
+ } else {
+ *error_r = t_strdup_printf("Unknown transport type '%s'",
+ set->transport);
+ return FALSE;
+ }
+
+ if (!parse_format_args(set, error_r))
+ return FALSE;
+
+ /* Some formats don't have a native way of serializing time stamps */
+ if (time_fmt_required &&
+ set->parsed_time_format == EVENT_EXPORTER_TIME_FMT_NATIVE) {
+ *error_r = t_strdup_printf("%s exporter format requires a "
+ "time-* argument", set->format);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool parse_metric_group_by_common(const char *func,
+ const char *const *params,
+ intmax_t *min_r,
+ intmax_t *max_r,
+ intmax_t *other_r,
+ const char **error_r)
+{
+ intmax_t min, max, other;
+
+ if ((str_array_length(params) != 3) ||
+ (str_to_intmax(params[0], &min) < 0) ||
+ (str_to_intmax(params[1], &max) < 0) ||
+ (str_to_intmax(params[2], &other) < 0)) {
+ *error_r = t_strdup_printf("group_by '%s' aggregate function takes "
+ "3 int args", func);
+ return FALSE;
+ }
+
+ if ((min < 0) || (max < 0) || (other < 0)) {
+ *error_r = t_strdup_printf("group_by '%s' aggregate function "
+ "arguments must be >= 0", func);
+ return FALSE;
+ }
+
+ if (min >= max) {
+ *error_r = t_strdup_printf("group_by '%s' aggregate function "
+ "min must be < max (%ju must be < %ju)",
+ func, min, max);
+ return FALSE;
+ }
+
+ *min_r = min;
+ *max_r = max;
+ *other_r = other;
+
+ return TRUE;
+}
+
+static bool parse_metric_group_by_exp(pool_t pool, struct stats_metric_settings_group_by *group_by,
+ const char *const *params, const char **error_r)
+{
+ intmax_t min, max, base;
+
+ if (!parse_metric_group_by_common("exponential", params, &min, &max, &base, error_r))
+ return FALSE;
+
+ if ((base != 2) && (base != 10)) {
+ *error_r = t_strdup_printf("group_by 'exponential' aggregate function "
+ "base must be one of: 2, 10 (base=%ju)",
+ base);
+ return FALSE;
+ }
+
+ group_by->func = STATS_METRIC_GROUPBY_QUANTIZED;
+
+ /*
+ * Allocate the bucket range array and fill it in
+ *
+ * The first bucket is special - it contains everything less than or
+ * equal to 'base^min'. The last bucket is also special - it
+ * contains everything greater than 'base^max'.
+ *
+ * The second bucket begins at 'base^min + 1', the third bucket
+ * begins at 'base^(min + 1) + 1', and so on.
+ */
+ group_by->num_ranges = max - min + 2;
+ group_by->ranges = p_new(pool, struct stats_metric_settings_bucket_range,
+ group_by->num_ranges);
+
+ /* set up min & max buckets */
+ group_by->ranges[0].min = INTMAX_MIN;
+ group_by->ranges[0].max = pow(base, min);
+ group_by->ranges[group_by->num_ranges - 1].min = pow(base, max);
+ group_by->ranges[group_by->num_ranges - 1].max = INTMAX_MAX;
+
+ /* remaining buckets */
+ for (unsigned int i = 1; i < group_by->num_ranges - 1; i++) {
+ group_by->ranges[i].min = pow(base, min + (i - 1));
+ group_by->ranges[i].max = pow(base, min + i);
+ }
+
+ return TRUE;
+}
+
+static bool parse_metric_group_by_lin(pool_t pool, struct stats_metric_settings_group_by *group_by,
+ const char *const *params, const char **error_r)
+{
+ intmax_t min, max, step;
+
+ if (!parse_metric_group_by_common("linear", params, &min, &max, &step, error_r))
+ return FALSE;
+
+ if ((min + step) > max) {
+ *error_r = t_strdup_printf("group_by 'linear' aggregate function "
+ "min+step must be <= max (%ju must be <= %ju)",
+ min + step, max);
+ return FALSE;
+ }
+
+ group_by->func = STATS_METRIC_GROUPBY_QUANTIZED;
+
+ /*
+ * Allocate the bucket range array and fill it in
+ *
+ * The first bucket is special - it contains everything less than or
+ * equal to 'min'. The last bucket is also special - it contains
+ * everything greater than 'max'.
+ *
+ * The second bucket begins at 'min + 1', the third bucket begins at
+ * 'min + 1 * step + 1', the fourth at 'min + 2 * step + 1', and so on.
+ */
+ group_by->num_ranges = (max - min) / step + 2;
+ group_by->ranges = p_new(pool, struct stats_metric_settings_bucket_range,
+ group_by->num_ranges);
+
+ /* set up min & max buckets */
+ group_by->ranges[0].min = INTMAX_MIN;
+ group_by->ranges[0].max = min;
+ group_by->ranges[group_by->num_ranges - 1].min = max;
+ group_by->ranges[group_by->num_ranges - 1].max = INTMAX_MAX;
+
+ /* remaining buckets */
+ for (unsigned int i = 1; i < group_by->num_ranges - 1; i++) {
+ group_by->ranges[i].min = min + (i - 1) * step;
+ group_by->ranges[i].max = min + i * step;
+ }
+
+ return TRUE;
+}
+
+static bool parse_metric_group_by(struct stats_metric_settings *set,
+ pool_t pool, const char **error_r)
+{
+ const char *const *tmp = t_strsplit_spaces(set->group_by, " ");
+
+ if (tmp[0] == NULL)
+ return TRUE;
+
+ p_array_init(&set->parsed_group_by, pool, str_array_length(tmp));
+
+ /* For each group_by field */
+ for (; *tmp != NULL; tmp++) {
+ struct stats_metric_settings_group_by group_by;
+ const char *const *params;
+
+ i_zero(&group_by);
+
+ /* <field name>:<aggregation func>... */
+ params = t_strsplit(*tmp, ":");
+
+ if (params[1] == NULL) {
+ /* <field name> - alias for <field>:discrete */
+ group_by.func = STATS_METRIC_GROUPBY_DISCRETE;
+ } else if (strcmp(params[1], "discrete") == 0) {
+ /* <field>:discrete */
+ group_by.func = STATS_METRIC_GROUPBY_DISCRETE;
+ if (params[2] != NULL) {
+ *error_r = "group_by 'discrete' aggregate function "
+ "does not take any args";
+ return FALSE;
+ }
+ } else if (strcmp(params[1], "exponential") == 0) {
+ /* <field>:exponential:<min mag>:<max mag>:<base> */
+ if (!parse_metric_group_by_exp(pool, &group_by, &params[2], error_r))
+ return FALSE;
+ } else if (strcmp(params[1], "linear") == 0) {
+ /* <field>:linear:<min val>:<max val>:<step> */
+ if (!parse_metric_group_by_lin(pool, &group_by, &params[2], error_r))
+ return FALSE;
+ } else {
+ *error_r = t_strdup_printf("unknown aggregation function "
+ "'%s' on field '%s'", params[1], params[0]);
+ return FALSE;
+ }
+
+ group_by.field = p_strdup(pool, params[0]);
+
+ array_push_back(&set->parsed_group_by, &group_by);
+ }
+
+ return TRUE;
+}
+
+static bool stats_metric_settings_check(void *_set, pool_t pool, const char **error_r)
+{
+ struct stats_metric_settings *set = _set;
+
+ if (set->metric_name[0] == '\0') {
+ *error_r = "Metric name can't be empty";
+ return FALSE;
+ }
+
+ if (set->filter[0] == '\0') {
+ *error_r = t_strdup_printf("metric %s { filter } is empty - "
+ "will not match anything", set->metric_name);
+ return FALSE;
+ }
+
+ set->parsed_filter = event_filter_create_fragment(pool);
+ if (event_filter_parse(set->filter, set->parsed_filter, error_r) < 0)
+ return FALSE;
+
+ if (!parse_metric_group_by(set, pool, error_r))
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool stats_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct stats_settings *set = _set;
+ struct stats_exporter_settings *exporter;
+ struct stats_metric_settings *metric;
+
+ if (!array_is_created(&set->metrics) || !array_is_created(&set->exporters))
+ return TRUE;
+
+ /* check that all metrics refer to exporters that exist */
+ array_foreach_elem(&set->metrics, metric) {
+ bool found = FALSE;
+
+ if (metric->exporter[0] == '\0')
+ continue; /* metric not exported */
+
+ array_foreach_elem(&set->exporters, exporter) {
+ if (strcmp(metric->exporter, exporter->name) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ *error_r = t_strdup_printf("metric %s refers to "
+ "non-existent exporter '%s'",
+ metric->metric_name,
+ metric->exporter);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings stats_service_settings = {
+ .name = "stats",
+ .protocol = "",
+ .type = "",
+ .executable = "stats",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &stats_unix_listeners_buf,
+ sizeof(stats_unix_listeners[0]) } },
+ .inet_listeners = ARRAY_INIT,
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct stats_exporter_settings)
+static const struct setting_define stats_exporter_setting_defines[] = {
+ DEF(STR, name),
+ DEF(STR, transport),
+ DEF(STR, transport_args),
+ DEF(TIME_MSECS, transport_timeout),
+ DEF(STR, format),
+ DEF(STR, format_args),
+ SETTING_DEFINE_LIST_END
+};
+static const struct stats_exporter_settings stats_exporter_default_settings = {
+ .name = "",
+ .transport = "",
+ .transport_args = "",
+ .transport_timeout = 250, /* ms */
+ .format = "",
+ .format_args = "",
+};
+const struct setting_parser_info stats_exporter_setting_parser_info = {
+ .defines = stats_exporter_setting_defines,
+ .defaults = &stats_exporter_default_settings,
+
+ .type_offset = offsetof(struct stats_exporter_settings, name),
+ .struct_size = sizeof(struct stats_exporter_settings),
+
+ .parent_offset = SIZE_MAX,
+ .check_func = stats_exporter_settings_check,
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct stats_metric_settings)
+static const struct setting_define stats_metric_setting_defines[] = {
+ DEF(STR, metric_name),
+ DEF(STR, fields),
+ DEF(STR, group_by),
+ DEF(STR, filter),
+ DEF(STR, exporter),
+ DEF(STR, exporter_include),
+ DEF(STR, description),
+ SETTING_DEFINE_LIST_END
+};
+static const struct stats_metric_settings stats_metric_default_settings = {
+ .metric_name = "",
+ .fields = "",
+ .filter = "",
+ .exporter = "",
+ .group_by = "",
+ .exporter_include = STATS_METRIC_SETTINGS_DEFAULT_EXPORTER_INCLUDE,
+ .description = "",
+};
+const struct setting_parser_info stats_metric_setting_parser_info = {
+ .defines = stats_metric_setting_defines,
+ .defaults = &stats_metric_default_settings,
+
+ .type_offset = offsetof(struct stats_metric_settings, metric_name),
+ .struct_size = sizeof(struct stats_metric_settings),
+
+ .parent_offset = SIZE_MAX,
+ .check_func = stats_metric_settings_check,
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct stats_settings)
+#undef DEFLIST_UNIQUE
+#define DEFLIST_UNIQUE(field, name, defines) \
+ { .type = SET_DEFLIST_UNIQUE, .key = name, \
+ .offset = offsetof(struct stats_settings, field), \
+ .list_info = defines }
+static const struct setting_define stats_setting_defines[] = {
+ DEF(STR, stats_http_rawlog_dir),
+
+ DEFLIST_UNIQUE(metrics, "metric", &stats_metric_setting_parser_info),
+ DEFLIST_UNIQUE(exporters, "event_exporter", &stats_exporter_setting_parser_info),
+ SETTING_DEFINE_LIST_END
+};
+const struct stats_settings stats_default_settings = {
+ .stats_http_rawlog_dir = "",
+
+ .metrics = ARRAY_INIT,
+ .exporters = ARRAY_INIT,
+};
+const struct setting_parser_info stats_setting_parser_info = {
+ .module_name = "stats",
+ .defines = stats_setting_defines,
+ .defaults = &stats_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct stats_settings),
+
+ .parent_offset = SIZE_MAX,
+ .check_func = stats_settings_check,
+};
+/* ../../src/replication/replicator/replicator-settings.c */
+/* <settings checks> */
+static struct file_listener_settings replicator_unix_listeners_array[] = {
+ { "replicator", 0600, "$default_internal_user", "" },
+ { "replicator-doveadm", 0, "$default_internal_user", "" }
+};
+static struct file_listener_settings *replicator_unix_listeners[] = {
+ &replicator_unix_listeners_array[0],
+ &replicator_unix_listeners_array[1]
+};
+static buffer_t replicator_unix_listeners_buf = {
+ { { replicator_unix_listeners, sizeof(replicator_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings replicator_service_settings = {
+ .name = "replicator",
+ .protocol = "",
+ .type = "",
+ .executable = "replicator",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &replicator_unix_listeners_buf,
+ sizeof(replicator_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct replicator_settings)
+static const struct setting_define replicator_setting_defines[] = {
+ DEF(STR, auth_socket_path),
+ DEF(STR, doveadm_socket_path),
+ DEF(STR, replication_dsync_parameters),
+
+ DEF(TIME, replication_full_sync_interval),
+ DEF(UINT, replication_max_conns),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct replicator_settings replicator_default_settings = {
+ .auth_socket_path = "auth-userdb",
+ .doveadm_socket_path = "doveadm-server",
+ .replication_dsync_parameters = "-d -N -l 30 -U",
+
+ .replication_full_sync_interval = 60*60*24,
+ .replication_max_conns = 10
+};
+const struct setting_parser_info replicator_setting_parser_info = {
+ .module_name = "replicator",
+ .defines = replicator_setting_defines,
+ .defaults = &replicator_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct replicator_settings),
+
+ .parent_offset = SIZE_MAX
+};
+/* ../../src/replication/aggregator/aggregator-settings.c */
+/* <settings checks> */
+static struct file_listener_settings aggregator_unix_listeners_array[] = {
+ { "replication-notify", 0600, "", "" }
+};
+static struct file_listener_settings *aggregator_unix_listeners[] = {
+ &aggregator_unix_listeners_array[0]
+};
+static buffer_t aggregator_unix_listeners_buf = {
+ { { aggregator_unix_listeners, sizeof(aggregator_unix_listeners) } }
+};
+
+static struct file_listener_settings aggregator_fifo_listeners_array[] = {
+ { "replication-notify-fifo", 0600, "", "" }
+};
+static struct file_listener_settings *aggregator_fifo_listeners[] = {
+ &aggregator_fifo_listeners_array[0]
+};
+static buffer_t aggregator_fifo_listeners_buf = {
+ { { aggregator_fifo_listeners, sizeof(aggregator_fifo_listeners) } }
+};
+/* </settings checks> */
+struct service_settings aggregator_service_settings = {
+ .name = "aggregator",
+ .protocol = "",
+ .type = "",
+ .executable = "aggregator",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = ".",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &aggregator_unix_listeners_buf,
+ sizeof(aggregator_unix_listeners[0]) } },
+ .fifo_listeners = { { &aggregator_fifo_listeners_buf,
+ sizeof(aggregator_fifo_listeners[0]) } },
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct aggregator_settings)
+static const struct setting_define aggregator_setting_defines[] = {
+ DEF(STR, replicator_host),
+ DEF(IN_PORT, replicator_port),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct aggregator_settings aggregator_default_settings = {
+ .replicator_host = "replicator",
+ .replicator_port = 0
+};
+const struct setting_parser_info aggregator_setting_parser_info = {
+ .module_name = "aggregator",
+ .defines = aggregator_setting_defines,
+ .defaults = &aggregator_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct aggregator_settings),
+
+ .parent_offset = SIZE_MAX
+};
+/* ../../src/pop3/pop3-settings.c */
+/* <settings checks> */
+static struct file_listener_settings pop3_unix_listeners_array[] = {
+ { "login/pop3", 0666, "", "" }
+};
+static struct file_listener_settings *pop3_unix_listeners[] = {
+ &pop3_unix_listeners_array[0]
+};
+static buffer_t pop3_unix_listeners_buf = {
+ { { pop3_unix_listeners, sizeof(pop3_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+struct pop3_client_workaround_list {
+ const char *name;
+ enum pop3_client_workarounds num;
+};
+
+static const struct pop3_client_workaround_list pop3_client_workaround_list[] = {
+ { "outlook-no-nuls", WORKAROUND_OUTLOOK_NO_NULS },
+ { "oe-ns-eoh", WORKAROUND_OE_NS_EOH },
+ { NULL, 0 }
+};
+
+static int
+pop3_settings_parse_workarounds(struct pop3_settings *set,
+ const char **error_r)
+{
+ enum pop3_client_workarounds client_workarounds = 0;
+ const struct pop3_client_workaround_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->pop3_client_workarounds, " ,");
+ for (; *str != NULL; str++) {
+ list = pop3_client_workaround_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ client_workarounds |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf("pop3_client_workarounds: "
+ "Unknown workaround: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_workarounds = client_workarounds;
+ return 0;
+}
+
+static bool
+pop3_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r)
+{
+ struct pop3_settings *set = _set;
+
+ if (pop3_settings_parse_workarounds(set, error_r) < 0)
+ return FALSE;
+ if (strcmp(set->pop3_delete_type, "default") == 0) {
+ if (set->pop3_deleted_flag[0] == '\0')
+ set->parsed_delete_type = POP3_DELETE_TYPE_EXPUNGE;
+ else
+ set->parsed_delete_type = POP3_DELETE_TYPE_FLAG;
+ } else if (strcmp(set->pop3_delete_type, "expunge") == 0) {
+ set->parsed_delete_type = POP3_DELETE_TYPE_EXPUNGE;
+ } else if (strcmp(set->pop3_delete_type, "flag") == 0) {
+ if (set->pop3_deleted_flag[0] == '\0') {
+ *error_r = "pop3_delete_type=flag, but pop3_deleted_flag not set";
+ return FALSE;
+ }
+ set->parsed_delete_type = POP3_DELETE_TYPE_FLAG;
+ } else {
+ *error_r = t_strdup_printf("pop3_delete_type: Unknown value '%s'",
+ set->pop3_delete_type);
+ return FALSE;
+ }
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings pop3_service_settings = {
+ .name = "pop3",
+ .protocol = "pop3",
+ .type = "",
+ .executable = "pop3",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1024,
+ .client_limit = 1,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &pop3_unix_listeners_buf,
+ sizeof(pop3_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct pop3_settings)
+#define DEFLIST(field, name, defines) \
+ { .type = SET_DEFLIST, .key = name, \
+ .offset = offsetof(struct pop3_settings, field), \
+ .list_info = defines }
+static const struct setting_define pop3_setting_defines[] = {
+ DEF(BOOL, verbose_proctitle),
+ DEF(STR_VARS, rawlog_dir),
+
+ DEF(BOOL, pop3_no_flag_updates),
+ DEF(BOOL, pop3_enable_last),
+ DEF(BOOL, pop3_reuse_xuidl),
+ DEF(BOOL, pop3_save_uidl),
+ DEF(BOOL, pop3_lock_session),
+ DEF(BOOL, pop3_fast_size_lookups),
+ DEF(STR, pop3_client_workarounds),
+ DEF(STR, pop3_logout_format),
+ DEF(ENUM, pop3_uidl_duplicates),
+ DEF(STR, pop3_deleted_flag),
+ DEF(ENUM, pop3_delete_type),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct pop3_settings pop3_default_settings = {
+ .verbose_proctitle = FALSE,
+ .rawlog_dir = "",
+
+ .pop3_no_flag_updates = FALSE,
+ .pop3_enable_last = FALSE,
+ .pop3_reuse_xuidl = FALSE,
+ .pop3_save_uidl = FALSE,
+ .pop3_lock_session = FALSE,
+ .pop3_fast_size_lookups = FALSE,
+ .pop3_client_workarounds = "",
+ .pop3_logout_format = "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s",
+ .pop3_uidl_duplicates = "allow:rename",
+ .pop3_deleted_flag = "",
+ .pop3_delete_type = "default:expunge:flag"
+};
+static const struct setting_parser_info *pop3_setting_dependencies[] = {
+ &mail_user_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info pop3_setting_parser_info = {
+ .module_name = "pop3",
+ .defines = pop3_setting_defines,
+ .defaults = &pop3_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct pop3_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = pop3_settings_verify,
+ .dependencies = pop3_setting_dependencies
+};
+/* ../../src/pop3-login/pop3-login-settings.c */
+/* <settings checks> */
+static struct inet_listener_settings pop3_login_inet_listeners_array[] = {
+ { .name = "pop3", .address = "", .port = 110 },
+ { .name = "pop3s", .address = "", .port = 995, .ssl = TRUE }
+};
+static struct inet_listener_settings *pop3_login_inet_listeners[] = {
+ &pop3_login_inet_listeners_array[0],
+ &pop3_login_inet_listeners_array[1]
+};
+static buffer_t pop3_login_inet_listeners_buf = {
+ { { pop3_login_inet_listeners, sizeof(pop3_login_inet_listeners) } }
+};
+
+/* </settings checks> */
+struct service_settings pop3_login_service_settings = {
+ .name = "pop3-login",
+ .protocol = "pop3",
+ .type = "login",
+ .executable = "pop3-login",
+ .user = "$default_login_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "login",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = { { &pop3_login_inet_listeners_buf,
+ sizeof(pop3_login_inet_listeners[0]) } }
+};
+static const struct setting_define pop3_login_setting_defines[] = {
+ SETTING_DEFINE_LIST_END
+};
+static const struct setting_parser_info *pop3_login_setting_dependencies[] = {
+ &login_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info pop3_login_setting_parser_info = {
+ .module_name = "pop3-login",
+ .defines = pop3_login_setting_defines,
+
+ .type_offset = SIZE_MAX,
+ .parent_offset = SIZE_MAX,
+
+ .dependencies = pop3_login_setting_dependencies
+};
+const struct setting_parser_info *pop3_login_setting_roots[] = {
+ &login_setting_parser_info,
+ &pop3_login_setting_parser_info,
+ NULL
+};
+/* ../../src/plugins/quota/quota-status-settings.c */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct quota_status_settings)
+static const struct setting_define quota_status_setting_defines[] = {
+ DEF(STR, recipient_delimiter),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct quota_status_settings quota_status_default_settings = {
+ .recipient_delimiter = "+",
+};
+static const struct setting_parser_info *quota_status_setting_dependencies[] = {
+ NULL
+};
+const struct setting_parser_info quota_status_setting_parser_info = {
+ .module_name = "mail",
+ .defines = quota_status_setting_defines,
+ .defaults = &quota_status_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct quota_status_settings),
+
+ .parent_offset = SIZE_MAX,
+ .dependencies = quota_status_setting_dependencies
+};
+/* ../../src/plugins/mail-crypt/fs-crypt-settings.c */
+static const struct setting_define fs_crypt_setting_defines[] = {
+ { .type = SET_STRLIST, .key = "plugin",
+ .offset = offsetof(struct fs_crypt_settings, plugin_envs) },
+
+ SETTING_DEFINE_LIST_END
+};
+const struct fs_crypt_settings fs_crypt_default_settings = {
+ .plugin_envs = ARRAY_INIT
+};
+static const struct setting_parser_info *fs_crypt_setting_dependencies[] = {
+ NULL
+};
+const struct setting_parser_info fs_crypt_setting_parser_info = {
+ .module_name = "fs-crypt",
+ .defines = fs_crypt_setting_defines,
+ .defaults = &fs_crypt_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct fs_crypt_settings),
+
+ .parent_offset = SIZE_MAX,
+ .dependencies = fs_crypt_setting_dependencies
+};
+/* ../../src/old-stats/stats-settings.c */
+/* <settings checks> */
+static struct file_listener_settings old_stats_unix_listeners_array[] = {
+ { "old-stats", 0600, "", "" }
+};
+static struct file_listener_settings *old_stats_unix_listeners[] = {
+ &old_stats_unix_listeners_array[0]
+};
+static buffer_t old_stats_unix_listeners_buf = {
+ { { old_stats_unix_listeners, sizeof(old_stats_unix_listeners) } }
+};
+static struct file_listener_settings old_stats_fifo_listeners_array[] = {
+ { "old-stats-mail", 0600, "", "" },
+ { "old-stats-user", 0600, "", "" }
+};
+static struct file_listener_settings *old_stats_fifo_listeners[] = {
+ &old_stats_fifo_listeners_array[0],
+ &old_stats_fifo_listeners_array[1]
+};
+static buffer_t old_stats_fifo_listeners_buf = {
+ { { old_stats_fifo_listeners, sizeof(old_stats_fifo_listeners) } }
+};
+/* </settings checks> */
+struct service_settings old_stats_service_settings = {
+ .name = "old-stats",
+ .protocol = "",
+ .type = "",
+ .executable = "old-stats",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "empty",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &old_stats_unix_listeners_buf,
+ sizeof(old_stats_unix_listeners[0]) } },
+ .fifo_listeners = { { &old_stats_fifo_listeners_buf,
+ sizeof(old_stats_fifo_listeners[0]) } },
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type("old_stats_"#name, name, struct old_stats_settings)
+static const struct setting_define old_stats_setting_defines[] = {
+ DEF(SIZE, memory_limit),
+ DEF(TIME, command_min_time),
+ DEF(TIME, session_min_time),
+ DEF(TIME, user_min_time),
+ DEF(TIME, domain_min_time),
+ DEF(TIME, ip_min_time),
+ DEF(STR, carbon_server),
+ DEF(TIME, carbon_interval),
+ DEF(STR, carbon_name),
+ SETTING_DEFINE_LIST_END
+};
+const struct old_stats_settings old_stats_default_settings = {
+ .memory_limit = 1024*1024*16,
+
+ .command_min_time = 60,
+ .session_min_time = 60*15,
+ .user_min_time = 60*60,
+ .domain_min_time = 60*60*12,
+ .ip_min_time = 60*60*12,
+
+ .carbon_interval = 30,
+ .carbon_server = "",
+ .carbon_name = ""
+};
+const struct setting_parser_info old_stats_setting_parser_info = {
+ .module_name = "stats",
+ .defines = old_stats_setting_defines,
+ .defaults = &old_stats_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct old_stats_settings),
+
+ .parent_offset = SIZE_MAX
+};
+/* ../../src/master/master-settings.c */
+extern const struct setting_parser_info service_setting_parser_info;
+extern const struct setting_parser_info service_setting_parser_info;
+/* <settings checks> */
+static void
+expand_user(const char **user, enum service_user_default *default_r,
+ const struct master_settings *set)
+{
+ /* $variable expansion is typically done by doveconf, but these
+ variables can come from built-in settings, so we need to expand
+ them here */
+ if (strcmp(*user, "$default_internal_user") == 0) {
+ *user = set->default_internal_user;
+ *default_r = SERVICE_USER_DEFAULT_INTERNAL;
+ } else if (strcmp(*user, "$default_login_user") == 0) {
+ *user = set->default_login_user;
+ *default_r = SERVICE_USER_DEFAULT_LOGIN;
+ } else {
+ *default_r = SERVICE_USER_DEFAULT_NONE;
+ }
+}
+
+static void
+expand_group(const char **group, const struct master_settings *set)
+{
+ /* $variable expansion is typically done by doveconf, but these
+ variables can come from built-in settings, so we need to expand
+ them here */
+ if (strcmp(*group, "$default_internal_group") == 0)
+ *group = set->default_internal_group;
+}
+
+static bool
+fix_file_listener_paths(ARRAY_TYPE(file_listener_settings) *l,
+ pool_t pool, const struct master_settings *master_set,
+ ARRAY_TYPE(const_string) *all_listeners,
+ const char **error_r)
+{
+ struct file_listener_settings *set;
+ size_t base_dir_len = strlen(master_set->base_dir);
+ enum service_user_default user_default;
+
+ if (!array_is_created(l))
+ return TRUE;
+
+ array_foreach_elem(l, set) {
+ if (set->path[0] == '\0') {
+ *error_r = "path must not be empty";
+ return FALSE;
+ }
+
+ expand_user(&set->user, &user_default, master_set);
+ expand_group(&set->group, master_set);
+ if (*set->path != '/') {
+ set->path = p_strconcat(pool, master_set->base_dir, "/",
+ set->path, NULL);
+ } else if (strncmp(set->path, master_set->base_dir,
+ base_dir_len) == 0 &&
+ set->path[base_dir_len] == '/') {
+ i_warning("You should remove base_dir prefix from "
+ "unix_listener: %s", set->path);
+ }
+ if (set->mode != 0)
+ array_push_back(all_listeners, &set->path);
+ }
+ return TRUE;
+}
+
+static void add_inet_listeners(ARRAY_TYPE(inet_listener_settings) *l,
+ ARRAY_TYPE(const_string) *all_listeners)
+{
+ struct inet_listener_settings *set;
+ const char *str;
+
+ if (!array_is_created(l))
+ return;
+
+ array_foreach_elem(l, set) {
+ if (set->port != 0) {
+ str = t_strdup_printf("%u:%s", set->port, set->address);
+ array_push_back(all_listeners, &str);
+ }
+ }
+}
+
+static bool master_settings_parse_type(struct service_settings *set,
+ const char **error_r)
+{
+ if (*set->type == '\0')
+ set->parsed_type = SERVICE_TYPE_UNKNOWN;
+ else if (strcmp(set->type, "log") == 0)
+ set->parsed_type = SERVICE_TYPE_LOG;
+ else if (strcmp(set->type, "config") == 0)
+ set->parsed_type = SERVICE_TYPE_CONFIG;
+ else if (strcmp(set->type, "anvil") == 0)
+ set->parsed_type = SERVICE_TYPE_ANVIL;
+ else if (strcmp(set->type, "login") == 0)
+ set->parsed_type = SERVICE_TYPE_LOGIN;
+ else if (strcmp(set->type, "startup") == 0)
+ set->parsed_type = SERVICE_TYPE_STARTUP;
+ else if (strcmp(set->type, "worker") == 0)
+ set->parsed_type = SERVICE_TYPE_WORKER;
+ else {
+ *error_r = t_strconcat("Unknown service type: ",
+ set->type, NULL);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void service_set_login_dump_core(struct service_settings *set)
+{
+ const char *p;
+
+ if (set->parsed_type != SERVICE_TYPE_LOGIN)
+ return;
+
+ p = strstr(set->executable, " -D");
+ if (p != NULL && (p[3] == '\0' || p[3] == ' '))
+ set->login_dump_core = TRUE;
+}
+
+static bool
+services_have_protocol(struct master_settings *set, const char *name)
+{
+ struct service_settings *service;
+
+ array_foreach_elem(&set->services, service) {
+ if (strcmp(service->protocol, name) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifdef CONFIG_BINARY
+static const struct service_settings *
+master_default_settings_get_service(const char *name)
+{
+ extern struct master_settings master_default_settings;
+ struct service_settings *set;
+
+ array_foreach_elem(&master_default_settings.services, set) {
+ if (strcmp(set->name, name) == 0)
+ return set;
+ }
+ return NULL;
+}
+#endif
+
+static unsigned int
+service_get_client_limit(struct master_settings *set, const char *name)
+{
+ struct service_settings *service;
+
+ array_foreach_elem(&set->services, service) {
+ if (strcmp(service->name, name) == 0) {
+ if (service->client_limit != 0)
+ return service->client_limit;
+ else
+ return set->default_client_limit;
+ }
+ }
+ return set->default_client_limit;
+}
+
+static bool
+master_settings_verify(void *_set, pool_t pool, const char **error_r)
+{
+ static bool warned_auth = FALSE, warned_anvil = FALSE;
+ struct master_settings *set = _set;
+ struct service_settings *const *services;
+ const char *const *strings;
+ ARRAY_TYPE(const_string) all_listeners;
+ struct passwd pw;
+ unsigned int i, j, count, client_limit, process_limit;
+ unsigned int max_auth_client_processes, max_anvil_client_processes;
+ string_t *max_auth_client_processes_reason = t_str_new(64);
+ string_t *max_anvil_client_processes_reason = t_str_new(64);
+ size_t len;
+#ifdef CONFIG_BINARY
+ const struct service_settings *default_service;
+#else
+ rlim_t fd_limit;
+ const char *max_client_limit_source = "default_client_limit";
+ unsigned int max_client_limit = set->default_client_limit;
+#endif
+
+ if (*set->listen == '\0') {
+ *error_r = "listen can't be set empty";
+ return FALSE;
+ }
+
+ len = strlen(set->base_dir);
+ if (len > 0 && set->base_dir[len-1] == '/') {
+ /* drop trailing '/' */
+ set->base_dir = p_strndup(pool, set->base_dir, len - 1);
+ }
+
+ if (set->last_valid_uid != 0 &&
+ set->first_valid_uid > set->last_valid_uid) {
+ *error_r = "first_valid_uid can't be larger than last_valid_uid";
+ return FALSE;
+ }
+ if (set->last_valid_gid != 0 &&
+ set->first_valid_gid > set->last_valid_gid) {
+ *error_r = "first_valid_gid can't be larger than last_valid_gid";
+ return FALSE;
+ }
+
+ if (i_getpwnam(set->default_login_user, &pw) == 0) {
+ *error_r = t_strdup_printf("default_login_user doesn't exist: %s",
+ set->default_login_user);
+ return FALSE;
+ }
+ if (i_getpwnam(set->default_internal_user, &pw) == 0) {
+ *error_r = t_strdup_printf("default_internal_user doesn't exist: %s",
+ set->default_internal_user);
+ return FALSE;
+ }
+
+ /* check that we have at least one service. the actual service
+ structure validity is checked later while creating them. */
+ if (!array_is_created(&set->services) ||
+ array_count(&set->services) == 0) {
+ *error_r = "No services defined";
+ return FALSE;
+ }
+ services = array_get(&set->services, &count);
+ for (i = 0; i < count; i++) {
+ struct service_settings *service = services[i];
+
+ if (*service->name == '\0') {
+ *error_r = t_strdup_printf(
+ "Service #%d is missing name", i);
+ return FALSE;
+ }
+ if (!master_settings_parse_type(service, error_r))
+ return FALSE;
+ for (j = 0; j < i; j++) {
+ if (strcmp(service->name, services[j]->name) == 0) {
+ *error_r = t_strdup_printf(
+ "Duplicate service name: %s",
+ service->name);
+ return FALSE;
+ }
+ }
+ expand_user(&service->user, &service->user_default, set);
+ expand_group(&service->extra_groups, set);
+ service_set_login_dump_core(service);
+ }
+ set->protocols_split = p_strsplit_spaces(pool, set->protocols, " ");
+ if (set->protocols_split[0] != NULL &&
+ strcmp(set->protocols_split[0], "none") == 0 &&
+ set->protocols_split[1] == NULL)
+ set->protocols_split[0] = NULL;
+
+ for (i = 0; set->protocols_split[i] != NULL; i++) {
+ if (!services_have_protocol(set, set->protocols_split[i])) {
+ *error_r = t_strdup_printf("protocols: "
+ "Unknown protocol: %s",
+ set->protocols_split[i]);
+ return FALSE;
+ }
+ }
+ t_array_init(&all_listeners, 64);
+ max_auth_client_processes = 0;
+ max_anvil_client_processes = 2; /* blocking, nonblocking pipes */
+ for (i = 0; i < count; i++) {
+ struct service_settings *service = services[i];
+
+ if (*service->protocol != '\0' &&
+ !str_array_find((const char **)set->protocols_split,
+ service->protocol)) {
+ /* protocol not enabled, ignore its settings */
+ continue;
+ }
+
+ if (*service->executable != '/' &&
+ *service->executable != '\0') {
+ service->executable =
+ p_strconcat(pool, set->libexec_dir, "/",
+ service->executable, NULL);
+ }
+ if (*service->chroot != '/' && *service->chroot != '\0') {
+ service->chroot =
+ p_strconcat(pool, set->base_dir, "/",
+ service->chroot, NULL);
+ }
+ if (service->drop_priv_before_exec &&
+ *service->chroot != '\0') {
+ *error_r = t_strdup_printf("service(%s): "
+ "drop_priv_before_exec=yes can't be "
+ "used with chroot", service->name);
+ return FALSE;
+ }
+ process_limit = service->process_limit;
+ if (process_limit == 0)
+ process_limit = set->default_process_limit;
+ if (service->process_min_avail > process_limit) {
+ *error_r = t_strdup_printf("service(%s): "
+ "process_min_avail is higher than process_limit",
+ service->name);
+ return FALSE;
+ }
+ if (service->vsz_limit < 1024*1024 && service->vsz_limit != 0) {
+ *error_r = t_strdup_printf("service(%s): "
+ "vsz_limit is too low", service->name);
+ return FALSE;
+ }
+
+#ifdef CONFIG_BINARY
+ default_service =
+ master_default_settings_get_service(service->name);
+ if (default_service != NULL &&
+ default_service->process_limit_1 && process_limit > 1) {
+ *error_r = t_strdup_printf("service(%s): "
+ "process_limit must be 1", service->name);
+ return FALSE;
+ }
+#else
+ if (max_client_limit < service->client_limit) {
+ max_client_limit = service->client_limit;
+ max_client_limit_source = t_strdup_printf(
+ "service %s { client_limit }", service->name);
+ }
+#endif
+
+ if (*service->protocol != '\0') {
+ /* each imap/pop3/lmtp process can use up a connection,
+ although if service_count=1 it's only temporary.
+ imap-hibernate doesn't do any auth lookups. */
+ if ((service->service_count != 1 ||
+ strcmp(service->type, "login") == 0) &&
+ strcmp(service->name, "imap-hibernate") != 0) {
+ str_printfa(max_auth_client_processes_reason,
+ " + service %s { process_limit=%u }",
+ service->name, process_limit);
+ max_auth_client_processes += process_limit;
+ }
+ }
+ if (strcmp(service->type, "login") == 0 ||
+ strcmp(service->name, "auth") == 0) {
+ max_anvil_client_processes += process_limit;
+ str_printfa(max_anvil_client_processes_reason,
+ " + service %s { process_limit=%u }",
+ service->name, process_limit);
+ }
+
+ if (!fix_file_listener_paths(&service->unix_listeners, pool,
+ set, &all_listeners, error_r)) {
+ *error_r = t_strdup_printf("service(%s): unix_listener: %s",
+ service->name, *error_r);
+ return FALSE;
+ }
+ if (!fix_file_listener_paths(&service->fifo_listeners, pool,
+ set, &all_listeners, error_r)) {
+ *error_r = t_strdup_printf("service(%s): fifo_listener: %s",
+ service->name, *error_r);
+ return FALSE;
+ }
+ add_inet_listeners(&service->inet_listeners, &all_listeners);
+ }
+
+ client_limit = service_get_client_limit(set, "auth");
+ if (client_limit < max_auth_client_processes && !warned_auth) {
+ warned_auth = TRUE;
+ str_delete(max_auth_client_processes_reason, 0, 3);
+ i_warning("service auth { client_limit=%u } is lower than "
+ "required under max. load (%u). "
+ "Counted for protocol services with service_count != 1: %s",
+ client_limit, max_auth_client_processes,
+ str_c(max_auth_client_processes_reason));
+ }
+
+ client_limit = service_get_client_limit(set, "anvil");
+ if (client_limit < max_anvil_client_processes && !warned_anvil) {
+ warned_anvil = TRUE;
+ str_delete(max_anvil_client_processes_reason, 0, 3);
+ i_warning("service anvil { client_limit=%u } is lower than "
+ "required under max. load (%u). Counted with: %s",
+ client_limit, max_anvil_client_processes,
+ str_c(max_anvil_client_processes_reason));
+ }
+#ifndef CONFIG_BINARY
+ if (restrict_get_fd_limit(&fd_limit) == 0 &&
+ fd_limit < (rlim_t)max_client_limit) {
+ i_warning("fd limit (ulimit -n) is lower than required "
+ "under max. load (%u < %u), because of %s",
+ (unsigned int)fd_limit, max_client_limit,
+ max_client_limit_source);
+ }
+#endif
+
+ /* check for duplicate listeners */
+ array_sort(&all_listeners, i_strcmp_p);
+ strings = array_get(&all_listeners, &count);
+ for (i = 1; i < count; i++) {
+ if (strcmp(strings[i-1], strings[i]) == 0) {
+ *error_r = t_strdup_printf("duplicate listener: %s",
+ strings[i]);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+/* </settings checks> */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct file_listener_settings)
+static const struct setting_define file_listener_setting_defines[] = {
+ DEF(STR, path),
+ DEF(UINT_OCT, mode),
+ DEF(STR, user),
+ DEF(STR, group),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct file_listener_settings file_listener_default_settings = {
+ .path = "",
+ .mode = 0600,
+ .user = "",
+ .group = "",
+};
+static const struct setting_parser_info file_listener_setting_parser_info = {
+ .defines = file_listener_setting_defines,
+ .defaults = &file_listener_default_settings,
+
+ .type_offset = offsetof(struct file_listener_settings, path),
+ .struct_size = sizeof(struct file_listener_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &service_setting_parser_info
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct inet_listener_settings)
+static const struct setting_define inet_listener_setting_defines[] = {
+ DEF(STR, name),
+ DEF(STR, address),
+ DEF(IN_PORT, port),
+ DEF(BOOL, ssl),
+ DEF(BOOL, reuse_port),
+ DEF(BOOL, haproxy),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct inet_listener_settings inet_listener_default_settings = {
+ .name = "",
+ .address = "",
+ .port = 0,
+ .ssl = FALSE,
+ .reuse_port = FALSE,
+ .haproxy = FALSE
+};
+static const struct setting_parser_info inet_listener_setting_parser_info = {
+ .defines = inet_listener_setting_defines,
+ .defaults = &inet_listener_default_settings,
+
+ .type_offset = offsetof(struct inet_listener_settings, name),
+ .struct_size = sizeof(struct inet_listener_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &service_setting_parser_info
+};
+#undef DEF
+#undef DEFLIST_UNIQUE
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct service_settings)
+#define DEFLIST_UNIQUE(field, name, defines) \
+ { .type = SET_DEFLIST_UNIQUE, .key = name, \
+ .offset = offsetof(struct service_settings, field), \
+ .list_info = defines }
+static const struct setting_define service_setting_defines[] = {
+ DEF(STR, name),
+ DEF(STR, protocol),
+ DEF(STR, type),
+ DEF(STR, executable),
+ DEF(STR, user),
+ DEF(STR, group),
+ DEF(STR, privileged_group),
+ DEF(STR, extra_groups),
+ DEF(STR, chroot),
+
+ DEF(BOOL, drop_priv_before_exec),
+
+ DEF(UINT, process_min_avail),
+ DEF(UINT, process_limit),
+ DEF(UINT, client_limit),
+ DEF(UINT, service_count),
+ DEF(TIME, idle_kill),
+ DEF(SIZE, vsz_limit),
+
+ DEFLIST_UNIQUE(unix_listeners, "unix_listener",
+ &file_listener_setting_parser_info),
+ DEFLIST_UNIQUE(fifo_listeners, "fifo_listener",
+ &file_listener_setting_parser_info),
+ DEFLIST_UNIQUE(inet_listeners, "inet_listener",
+ &inet_listener_setting_parser_info),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct service_settings service_default_settings = {
+ .name = "",
+ .protocol = "",
+ .type = "",
+ .executable = "",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+const struct setting_parser_info service_setting_parser_info = {
+ .defines = service_setting_defines,
+ .defaults = &service_default_settings,
+
+ .type_offset = offsetof(struct service_settings, name),
+ .struct_size = sizeof(struct service_settings),
+
+ .parent_offset = offsetof(struct service_settings, master_set),
+ .parent = &master_setting_parser_info
+};
+#undef DEF
+#undef DEFLIST_UNIQUE
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct master_settings)
+#define DEFLIST_UNIQUE(field, name, defines) \
+ { .type = SET_DEFLIST_UNIQUE, .key = name, \
+ .offset = offsetof(struct master_settings, field), \
+ .list_info = defines }
+static const struct setting_define master_setting_defines[] = {
+ DEF(STR, base_dir),
+ DEF(STR, state_dir),
+ DEF(STR, libexec_dir),
+ DEF(STR, instance_name),
+ DEF(STR, protocols),
+ DEF(STR, listen),
+ DEF(ENUM, ssl),
+ DEF(STR, default_internal_user),
+ DEF(STR, default_internal_group),
+ DEF(STR, default_login_user),
+ DEF(UINT, default_process_limit),
+ DEF(UINT, default_client_limit),
+ DEF(TIME, default_idle_kill),
+ DEF(SIZE, default_vsz_limit),
+
+ DEF(BOOL, version_ignore),
+
+ DEF(UINT, first_valid_uid),
+ DEF(UINT, last_valid_uid),
+ DEF(UINT, first_valid_gid),
+ DEF(UINT, last_valid_gid),
+
+ DEFLIST_UNIQUE(services, "service", &service_setting_parser_info),
+
+ SETTING_DEFINE_LIST_END
+};
+struct master_settings master_default_settings = {
+ .base_dir = PKG_RUNDIR,
+ .state_dir = PKG_STATEDIR,
+ .libexec_dir = PKG_LIBEXECDIR,
+ .instance_name = PACKAGE,
+ .protocols = "imap pop3 lmtp",
+ .listen = "*, ::",
+ .ssl = "yes:no:required",
+ .default_internal_user = "dovecot",
+ .default_internal_group = "dovecot",
+ .default_login_user = "dovenull",
+ .default_process_limit = 100,
+ .default_client_limit = 1000,
+ .default_idle_kill = 60,
+ .default_vsz_limit = 256*1024*1024,
+
+ .version_ignore = FALSE,
+
+ .first_valid_uid = 500,
+ .last_valid_uid = 0,
+ .first_valid_gid = 1,
+ .last_valid_gid = 0,
+
+#ifndef CONFIG_BINARY
+ .services = ARRAY_INIT
+#else
+ .services = { { &config_all_services_buf,
+ sizeof(struct service_settings *) } },
+#endif
+};
+const struct setting_parser_info master_setting_parser_info = {
+ .module_name = "master",
+ .defines = master_setting_defines,
+ .defaults = &master_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct master_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = master_settings_verify
+};
+/* ../../src/login-common/login-settings.c */
+/* <settings checks> */
+static bool login_settings_check(void *_set, pool_t pool,
+ const char **error_r ATTR_UNUSED)
+{
+ struct login_settings *set = _set;
+
+ set->log_format_elements_split =
+ p_strsplit(pool, set->login_log_format_elements, " ");
+
+ if (set->auth_debug_passwords)
+ set->auth_debug = TRUE;
+ if (set->auth_debug)
+ set->auth_verbose = TRUE;
+ return TRUE;
+}
+/* </settings checks> */
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct login_settings)
+static const struct setting_define login_setting_defines[] = {
+ DEF(STR, login_trusted_networks),
+ DEF(STR, login_source_ips),
+ DEF(STR_VARS, login_greeting),
+ DEF(STR, login_log_format_elements),
+ DEF(STR, login_log_format),
+ DEF(STR, login_access_sockets),
+ DEF(STR_VARS, login_proxy_notify_path),
+ DEF(STR, login_plugin_dir),
+ DEF(STR, login_plugins),
+ DEF(TIME_MSECS, login_proxy_timeout),
+ DEF(UINT, login_proxy_max_reconnects),
+ DEF(TIME, login_proxy_max_disconnect_delay),
+ DEF(STR, login_proxy_rawlog_dir),
+ DEF(STR, director_username_hash),
+
+ DEF(BOOL, auth_ssl_require_client_cert),
+ DEF(BOOL, auth_ssl_username_from_cert),
+
+ DEF(BOOL, disable_plaintext_auth),
+ DEF(BOOL, auth_verbose),
+ DEF(BOOL, auth_debug),
+ DEF(BOOL, verbose_proctitle),
+
+ DEF(UINT, mail_max_userip_connections),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct login_settings login_default_settings = {
+ .login_trusted_networks = "",
+ .login_source_ips = "",
+ .login_greeting = PACKAGE_NAME" ready.",
+ .login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c session=<%{session}>",
+ .login_log_format = "%$: %s",
+ .login_access_sockets = "",
+ .login_proxy_notify_path = "proxy-notify",
+ .login_plugin_dir = MODULEDIR"/login",
+ .login_plugins = "",
+ .login_proxy_timeout = 30*1000,
+ .login_proxy_max_reconnects = 3,
+ .login_proxy_max_disconnect_delay = 0,
+ .login_proxy_rawlog_dir = "",
+ .director_username_hash = "%Lu",
+
+ .auth_ssl_require_client_cert = FALSE,
+ .auth_ssl_username_from_cert = FALSE,
+
+ .disable_plaintext_auth = TRUE,
+ .auth_verbose = FALSE,
+ .auth_debug = FALSE,
+ .verbose_proctitle = FALSE,
+
+ .mail_max_userip_connections = 10
+};
+const struct setting_parser_info login_setting_parser_info = {
+ .module_name = "login",
+ .defines = login_setting_defines,
+ .defaults = &login_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct login_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = login_settings_check
+};
+/* ../../src/log/log-settings.c */
+/* <settings checks> */
+static struct file_listener_settings log_unix_listeners_array[] = {
+ { "log-errors", 0600, "", "" }
+};
+static struct file_listener_settings *log_unix_listeners[] = {
+ &log_unix_listeners_array[0]
+};
+static buffer_t log_unix_listeners_buf = {
+ { { log_unix_listeners, sizeof(log_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings log_service_settings = {
+ .name = "log",
+ .protocol = "",
+ .type = "log",
+ .executable = "log",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &log_unix_listeners_buf,
+ sizeof(log_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+/* ../../src/lmtp/lmtp-settings.c */
+/* <settings checks> */
+static struct file_listener_settings lmtp_unix_listeners_array[] = {
+ { "lmtp", 0666, "", "" }
+};
+static struct file_listener_settings *lmtp_unix_listeners[] = {
+ &lmtp_unix_listeners_array[0]
+};
+static buffer_t lmtp_unix_listeners_buf = {
+ { { lmtp_unix_listeners, sizeof(lmtp_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+struct lmtp_client_workaround_list {
+ const char *name;
+ enum lmtp_client_workarounds num;
+};
+
+static const struct lmtp_client_workaround_list
+lmtp_client_workaround_list[] = {
+ { "whitespace-before-path", LMTP_WORKAROUND_WHITESPACE_BEFORE_PATH },
+ { "mailbox-for-path", LMTP_WORKAROUND_MAILBOX_FOR_PATH },
+ { NULL, 0 }
+};
+
+static int
+lmtp_settings_parse_workarounds(struct lmtp_settings *set,
+ const char **error_r)
+{
+ enum lmtp_client_workarounds client_workarounds = 0;
+ const struct lmtp_client_workaround_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->lmtp_client_workarounds, " ,");
+ for (; *str != NULL; str++) {
+ list = lmtp_client_workaround_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ client_workarounds |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf(
+ "lmtp_client_workarounds: "
+ "Unknown workaround: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_workarounds = client_workarounds;
+ return 0;
+}
+
+static bool lmtp_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct lmtp_settings *set = _set;
+
+ if (lmtp_settings_parse_workarounds(set, error_r) < 0)
+ return FALSE;
+
+ if (strcmp(set->lmtp_hdr_delivery_address, "none") == 0) {
+ set->parsed_lmtp_hdr_delivery_address =
+ LMTP_HDR_DELIVERY_ADDRESS_NONE;
+ } else if (strcmp(set->lmtp_hdr_delivery_address, "final") == 0) {
+ set->parsed_lmtp_hdr_delivery_address =
+ LMTP_HDR_DELIVERY_ADDRESS_FINAL;
+ } else if (strcmp(set->lmtp_hdr_delivery_address, "original") == 0) {
+ set->parsed_lmtp_hdr_delivery_address =
+ LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL;
+ } else {
+ *error_r = t_strdup_printf("Unknown lmtp_hdr_delivery_address: %s",
+ set->lmtp_hdr_delivery_address);
+ return FALSE;
+ }
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings lmtp_service_settings = {
+ .name = "lmtp",
+ .protocol = "lmtp",
+ .type = "",
+ .executable = "lmtp",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &lmtp_unix_listeners_buf,
+ sizeof(lmtp_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct lmtp_settings)
+static const struct setting_define lmtp_setting_defines[] = {
+ DEF(BOOL, lmtp_proxy),
+ DEF(BOOL, lmtp_save_to_detail_mailbox),
+ DEF(BOOL, lmtp_rcpt_check_quota),
+ DEF(BOOL, lmtp_add_received_header),
+ DEF(BOOL, lmtp_verbose_replies),
+ DEF(UINT, lmtp_user_concurrency_limit),
+ DEF(ENUM, lmtp_hdr_delivery_address),
+ DEF(STR_VARS, lmtp_rawlog_dir),
+ DEF(STR_VARS, lmtp_proxy_rawlog_dir),
+
+ DEF(STR, lmtp_client_workarounds),
+
+ DEF(STR_VARS, login_greeting),
+ DEF(STR, login_trusted_networks),
+
+ DEF(STR, mail_plugins),
+ DEF(STR, mail_plugin_dir),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct lmtp_settings lmtp_default_settings = {
+ .lmtp_proxy = FALSE,
+ .lmtp_save_to_detail_mailbox = FALSE,
+ .lmtp_rcpt_check_quota = FALSE,
+ .lmtp_add_received_header = TRUE,
+ .lmtp_verbose_replies = FALSE,
+ .lmtp_user_concurrency_limit = 0,
+ .lmtp_hdr_delivery_address = "final:none:original",
+ .lmtp_rawlog_dir = "",
+ .lmtp_proxy_rawlog_dir = "",
+
+ .lmtp_client_workarounds = "",
+
+ .login_greeting = PACKAGE_NAME" ready.",
+ .login_trusted_networks = "",
+
+ .mail_plugins = "",
+ .mail_plugin_dir = MODULEDIR,
+};
+static const struct setting_parser_info *lmtp_setting_dependencies[] = {
+ &lda_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info lmtp_setting_parser_info = {
+ .module_name = "lmtp",
+ .defines = lmtp_setting_defines,
+ .defaults = &lmtp_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct lmtp_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = lmtp_settings_check,
+ .dependencies = lmtp_setting_dependencies
+};
+/* ../../src/ipc/ipc-settings.c */
+/* <settings checks> */
+static struct file_listener_settings ipc_unix_listeners_array[] = {
+ { "ipc", 0600, "$default_internal_user", "" },
+ { "login/ipc-proxy", 0600, "$default_login_user", "" }
+};
+static struct file_listener_settings *ipc_unix_listeners[] = {
+ &ipc_unix_listeners_array[0],
+ &ipc_unix_listeners_array[1]
+};
+static buffer_t ipc_unix_listeners_buf = {
+ { { ipc_unix_listeners, sizeof(ipc_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings ipc_service_settings = {
+ .name = "ipc",
+ .protocol = "",
+ .type = "",
+ .executable = "ipc",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "empty",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &ipc_unix_listeners_buf,
+ sizeof(ipc_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+/* ../../src/indexer/indexer-worker-settings.c */
+/* <settings checks> */
+static struct file_listener_settings indexer_worker_unix_listeners_array[] = {
+ { "indexer-worker", 0600, "$default_internal_user", "" }
+};
+static struct file_listener_settings *indexer_worker_unix_listeners[] = {
+ &indexer_worker_unix_listeners_array[0]
+};
+static buffer_t indexer_worker_unix_listeners_buf = {
+ { { indexer_worker_unix_listeners, sizeof(indexer_worker_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings indexer_worker_service_settings = {
+ .name = "indexer-worker",
+ .protocol = "",
+ .type = "worker",
+ .executable = "indexer-worker",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 10,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &indexer_worker_unix_listeners_buf,
+ sizeof(indexer_worker_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+/* ../../src/indexer/indexer-settings.c */
+extern const struct setting_parser_info service_setting_parser_info;
+/* <settings checks> */
+static struct file_listener_settings indexer_unix_listeners_array[] = {
+ { "indexer", 0666, "", "" }
+};
+static struct file_listener_settings *indexer_unix_listeners[] = {
+ &indexer_unix_listeners_array[0]
+};
+static buffer_t indexer_unix_listeners_buf = {
+ { { indexer_unix_listeners, sizeof(indexer_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings indexer_service_settings = {
+ .name = "indexer",
+ .protocol = "",
+ .type = "",
+ .executable = "indexer",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &indexer_unix_listeners_buf,
+ sizeof(indexer_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+/* ../../src/imap/imap-settings.c */
+/* <settings checks> */
+static struct file_listener_settings imap_unix_listeners_array[] = {
+ { "login/imap", 0666, "", "" },
+ { "imap-master", 0600, "", "" }
+};
+static struct file_listener_settings *imap_unix_listeners[] = {
+ &imap_unix_listeners_array[0],
+ &imap_unix_listeners_array[1]
+};
+static buffer_t imap_unix_listeners_buf = {
+ { { imap_unix_listeners, sizeof(imap_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+struct imap_client_workaround_list {
+ const char *name;
+ enum imap_client_workarounds num;
+};
+
+static const struct imap_client_workaround_list imap_client_workaround_list[] = {
+ { "delay-newmail", WORKAROUND_DELAY_NEWMAIL },
+ { "tb-extra-mailbox-sep", WORKAROUND_TB_EXTRA_MAILBOX_SEP },
+ { "tb-lsub-flags", WORKAROUND_TB_LSUB_FLAGS },
+ { NULL, 0 }
+};
+
+static int
+imap_settings_parse_workarounds(struct imap_settings *set,
+ const char **error_r)
+{
+ enum imap_client_workarounds client_workarounds = 0;
+ const struct imap_client_workaround_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->imap_client_workarounds, " ,");
+ for (; *str != NULL; str++) {
+ list = imap_client_workaround_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ client_workarounds |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf("imap_client_workarounds: "
+ "Unknown workaround: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_workarounds = client_workarounds;
+ return 0;
+}
+
+
+static bool
+imap_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r)
+{
+ struct imap_settings *set = _set;
+
+ if (imap_settings_parse_workarounds(set, error_r) < 0)
+ return FALSE;
+
+ if (strcmp(set->imap_fetch_failure, "disconnect-immediately") == 0)
+ set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_IMMEDIATELY;
+ else if (strcmp(set->imap_fetch_failure, "disconnect-after") == 0)
+ set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_DISCONNECT_AFTER;
+ else if (strcmp(set->imap_fetch_failure, "no-after") == 0)
+ set->parsed_fetch_failure = IMAP_CLIENT_FETCH_FAILURE_NO_AFTER;
+ else {
+ *error_r = t_strdup_printf("Unknown imap_fetch_failure: %s",
+ set->imap_fetch_failure);
+ return FALSE;
+ }
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings imap_service_settings = {
+ .name = "imap",
+ .protocol = "imap",
+ .type = "",
+ .executable = "imap",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1024,
+ .client_limit = 1,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &imap_unix_listeners_buf,
+ sizeof(imap_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct imap_settings)
+#define DEFLIST(field, name, defines) \
+ { .type = SET_DEFLIST, .key = name, \
+ .offset = offsetof(struct imap_settings, field), \
+ .list_info = defines }
+static const struct setting_define imap_setting_defines[] = {
+ DEF(BOOL, verbose_proctitle),
+ DEF(STR_VARS, rawlog_dir),
+
+ DEF(SIZE, imap_max_line_length),
+ DEF(TIME, imap_idle_notify_interval),
+ DEF(STR, imap_capability),
+ DEF(STR, imap_client_workarounds),
+ DEF(STR, imap_logout_format),
+ DEF(STR, imap_id_send),
+ DEF(STR, imap_id_log),
+ DEF(ENUM, imap_fetch_failure),
+ DEF(BOOL, imap_metadata),
+ DEF(BOOL, imap_literal_minus),
+ DEF(TIME, imap_hibernate_timeout),
+
+ DEF(STR, imap_urlauth_host),
+ DEF(IN_PORT, imap_urlauth_port),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct imap_settings imap_default_settings = {
+ .verbose_proctitle = FALSE,
+ .rawlog_dir = "",
+
+ /* RFC-2683 recommends at least 8000 bytes. Some clients however don't
+ break large message sets to multiple commands, so we're pretty
+ liberal by default. */
+ .imap_max_line_length = 64*1024,
+ .imap_idle_notify_interval = 2*60,
+ .imap_capability = "",
+ .imap_client_workarounds = "",
+ .imap_logout_format = "in=%i out=%o deleted=%{deleted} "
+ "expunged=%{expunged} trashed=%{trashed} "
+ "hdr_count=%{fetch_hdr_count} hdr_bytes=%{fetch_hdr_bytes} "
+ "body_count=%{fetch_body_count} body_bytes=%{fetch_body_bytes}",
+ .imap_id_send = "name *",
+ .imap_id_log = "",
+ .imap_fetch_failure = "disconnect-immediately:disconnect-after:no-after",
+ .imap_metadata = FALSE,
+ .imap_literal_minus = FALSE,
+ .imap_hibernate_timeout = 0,
+
+ .imap_urlauth_host = "",
+ .imap_urlauth_port = 143
+};
+static const struct setting_parser_info *imap_setting_dependencies[] = {
+ &mail_user_setting_parser_info,
+ &smtp_submit_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info imap_setting_parser_info = {
+ .module_name = "imap",
+ .defines = imap_setting_defines,
+ .defaults = &imap_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct imap_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = imap_settings_verify,
+ .dependencies = imap_setting_dependencies
+};
+/* ../../src/imap-urlauth/imap-urlauth-worker-settings.c */
+/* <settings checks> */
+static struct file_listener_settings imap_urlauth_worker_unix_listeners_array[] = {
+ { "imap-urlauth-worker", 0600, "$default_internal_user", "" }
+};
+static struct file_listener_settings *imap_urlauth_worker_unix_listeners[] = {
+ &imap_urlauth_worker_unix_listeners_array[0]
+};
+static buffer_t imap_urlauth_worker_unix_listeners_buf = {
+ { { imap_urlauth_worker_unix_listeners,
+ sizeof(imap_urlauth_worker_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings imap_urlauth_worker_service_settings = {
+ .name = "imap-urlauth-worker",
+ .protocol = "imap",
+ .type = "",
+ .executable = "imap-urlauth-worker",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1024,
+ .client_limit = 1,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &imap_urlauth_worker_unix_listeners_buf,
+ sizeof(imap_urlauth_worker_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct imap_urlauth_worker_settings)
+static const struct setting_define imap_urlauth_worker_setting_defines[] = {
+ DEF(BOOL, verbose_proctitle),
+
+ DEF(STR, imap_urlauth_host),
+ DEF(IN_PORT, imap_urlauth_port),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct imap_urlauth_worker_settings imap_urlauth_worker_default_settings = {
+ .verbose_proctitle = FALSE,
+
+ .imap_urlauth_host = "",
+ .imap_urlauth_port = 143
+};
+static const struct setting_parser_info *imap_urlauth_worker_setting_dependencies[] = {
+ &mail_user_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info imap_urlauth_worker_setting_parser_info = {
+ .module_name = "imap-urlauth-worker",
+ .defines = imap_urlauth_worker_setting_defines,
+ .defaults = &imap_urlauth_worker_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct imap_urlauth_worker_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .dependencies = imap_urlauth_worker_setting_dependencies
+};
+/* ../../src/imap-urlauth/imap-urlauth-settings.c */
+/* <settings checks> */
+static struct file_listener_settings imap_urlauth_unix_listeners_array[] = {
+ { "token-login/imap-urlauth", 0666, "", "" }
+};
+static struct file_listener_settings *imap_urlauth_unix_listeners[] = {
+ &imap_urlauth_unix_listeners_array[0]
+};
+static buffer_t imap_urlauth_unix_listeners_buf = {
+ { { imap_urlauth_unix_listeners, sizeof(imap_urlauth_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings imap_urlauth_service_settings = {
+ .name = "imap-urlauth",
+ .protocol = "imap",
+ .type = "",
+ .executable = "imap-urlauth",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1024,
+ .client_limit = 1,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &imap_urlauth_unix_listeners_buf,
+ sizeof(imap_urlauth_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct imap_urlauth_settings)
+static const struct setting_define imap_urlauth_setting_defines[] = {
+ DEF(STR, base_dir),
+
+ DEF(BOOL, mail_debug),
+
+ DEF(BOOL, verbose_proctitle),
+
+ DEF(STR, imap_urlauth_logout_format),
+ DEF(STR, imap_urlauth_submit_user),
+ DEF(STR, imap_urlauth_stream_user),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct imap_urlauth_settings imap_urlauth_default_settings = {
+ .base_dir = PKG_RUNDIR,
+ .mail_debug = FALSE,
+
+ .verbose_proctitle = FALSE,
+
+ .imap_urlauth_logout_format = "in=%i out=%o",
+ .imap_urlauth_submit_user = NULL,
+ .imap_urlauth_stream_user = NULL
+};
+static const struct setting_parser_info *imap_urlauth_setting_dependencies[] = {
+ NULL
+};
+const struct setting_parser_info imap_urlauth_setting_parser_info = {
+ .module_name = "imap-urlauth",
+ .defines = imap_urlauth_setting_defines,
+ .defaults = &imap_urlauth_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct imap_urlauth_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .dependencies = imap_urlauth_setting_dependencies
+};
+/* ../../src/imap-urlauth/imap-urlauth-login-settings.c */
+/* <settings checks> */
+static struct file_listener_settings
+imap_urlauth_login_unix_listeners_array[] = {
+ { "imap-urlauth", 0666, "", "" }
+};
+static struct file_listener_settings *imap_urlauth_login_unix_listeners[] = {
+ &imap_urlauth_login_unix_listeners_array[0]
+};
+static buffer_t imap_urlauth_login_unix_listeners_buf = {
+ { { imap_urlauth_login_unix_listeners,
+ sizeof(imap_urlauth_login_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings imap_urlauth_login_service_settings = {
+ .name = "imap-urlauth-login",
+ .protocol = "imap",
+ .type = "login",
+ .executable = "imap-urlauth-login",
+ .user = "$default_login_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "token-login",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &imap_urlauth_login_unix_listeners_buf,
+ sizeof(imap_urlauth_login_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+static const struct setting_define imap_urlauth_login_setting_defines[] = {
+ SETTING_DEFINE_LIST_END
+};
+static const struct setting_parser_info *imap_urlauth_login_setting_dependencies[] = {
+ &login_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info imap_urlauth_login_setting_parser_info = {
+ .module_name = "imap-urlauth-login",
+ .defines = imap_urlauth_login_setting_defines,
+
+ .type_offset = SIZE_MAX,
+ .parent_offset = SIZE_MAX,
+
+ .dependencies = imap_urlauth_login_setting_dependencies
+};
+const struct setting_parser_info *imap_urlauth_login_setting_roots[] = {
+ &login_setting_parser_info,
+ &imap_urlauth_login_setting_parser_info,
+ NULL
+};
+/* ../../src/imap-login/imap-login-settings.c */
+/* <settings checks> */
+static struct inet_listener_settings imap_login_inet_listeners_array[] = {
+ { .name = "imap", .address = "", .port = 143 },
+ { .name = "imaps", .address = "", .port = 993, .ssl = TRUE }
+};
+static struct inet_listener_settings *imap_login_inet_listeners[] = {
+ &imap_login_inet_listeners_array[0],
+ &imap_login_inet_listeners_array[1]
+};
+static buffer_t imap_login_inet_listeners_buf = {
+ { { imap_login_inet_listeners, sizeof(imap_login_inet_listeners) } }
+};
+/* </settings checks> */
+struct service_settings imap_login_service_settings = {
+ .name = "imap-login",
+ .protocol = "imap",
+ .type = "login",
+ .executable = "imap-login",
+ .user = "$default_login_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "login",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = ARRAY_INIT,
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = { { &imap_login_inet_listeners_buf,
+ sizeof(imap_login_inet_listeners[0]) } }
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct imap_login_settings)
+static const struct setting_define imap_login_setting_defines[] = {
+ DEF(STR, imap_capability),
+ DEF(STR, imap_id_send),
+ DEF(STR, imap_id_log),
+ DEF(BOOL, imap_literal_minus),
+ DEF(BOOL, imap_id_retain),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct imap_login_settings imap_login_default_settings = {
+ .imap_capability = "",
+ .imap_id_send = "name *",
+ .imap_id_log = "",
+ .imap_literal_minus = FALSE,
+ .imap_id_retain = FALSE,
+};
+static const struct setting_parser_info *imap_login_setting_dependencies[] = {
+ &login_setting_parser_info,
+ NULL
+};
+static const struct setting_parser_info imap_login_setting_parser_info = {
+ .module_name = "imap-login",
+ .defines = imap_login_setting_defines,
+ .defaults = &imap_login_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct imap_login_settings),
+
+ .parent_offset = SIZE_MAX,
+ .dependencies = imap_login_setting_dependencies
+};
+const struct setting_parser_info *imap_login_setting_roots[] = {
+ &login_setting_parser_info,
+ &imap_login_setting_parser_info,
+ NULL
+};
+/* ../../src/imap-hibernate/imap-hibernate-settings.c */
+/* <settings checks> */
+static struct file_listener_settings imap_hibernate_unix_listeners_array[] = {
+ { "imap-hibernate", 0660, "", "$default_internal_group" }
+};
+static struct file_listener_settings *imap_hibernate_unix_listeners[] = {
+ &imap_hibernate_unix_listeners_array[0]
+};
+static buffer_t imap_hibernate_unix_listeners_buf = {
+ { { imap_hibernate_unix_listeners, sizeof(imap_hibernate_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings imap_hibernate_service_settings = {
+ .name = "imap-hibernate",
+ .protocol = "imap",
+ .type = "",
+ .executable = "imap-hibernate",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &imap_hibernate_unix_listeners_buf,
+ sizeof(imap_hibernate_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+/* ../../src/doveadm/doveadm-settings.c */
+/* <settings checks> */
+static struct file_listener_settings doveadm_unix_listeners_array[] = {
+ { "doveadm-server", 0600, "", "" }
+};
+static struct file_listener_settings *doveadm_unix_listeners[] = {
+ &doveadm_unix_listeners_array[0]
+};
+static buffer_t doveadm_unix_listeners_buf = {
+ { { doveadm_unix_listeners, sizeof(doveadm_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+struct dsync_feature_list {
+ const char *name;
+ enum dsync_features num;
+};
+
+static const struct dsync_feature_list dsync_feature_list[] = {
+ { "empty-header-workaround", DSYNC_FEATURE_EMPTY_HDR_WORKAROUND },
+ { NULL, 0 }
+};
+
+static int
+dsync_settings_parse_features(struct doveadm_settings *set,
+ const char **error_r)
+{
+ enum dsync_features features = 0;
+ const struct dsync_feature_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->dsync_features, " ,");
+ for (; *str != NULL; str++) {
+ list = dsync_feature_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ features |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf("dsync_features: "
+ "Unknown feature: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_features = features;
+ return 0;
+}
+
+static bool doveadm_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct doveadm_settings *set = _set;
+
+#ifndef CONFIG_BINARY
+ fix_base_path(set, pool, &set->auth_socket_path);
+ fix_base_path(set, pool, &set->doveadm_socket_path);
+#endif
+ if (*set->dsync_hashed_headers == '\0') {
+ *error_r = "dsync_hashed_headers must not be empty";
+ return FALSE;
+ }
+ if (*set->dsync_alt_char == '\0') {
+ *error_r = "dsync_alt_char must not be empty";
+ return FALSE;
+ }
+ if (dsync_settings_parse_features(set, error_r) != 0)
+ return FALSE;
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings doveadm_service_settings = {
+ .name = "doveadm",
+ .protocol = "",
+ .type = "",
+ .executable = "doveadm-server",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "$default_internal_group",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 1,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &doveadm_unix_listeners_buf,
+ sizeof(doveadm_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct doveadm_settings)
+static const struct setting_define doveadm_setting_defines[] = {
+ DEF(STR, base_dir),
+ DEF(STR, libexec_dir),
+ DEF(STR, mail_plugins),
+ DEF(STR, mail_plugin_dir),
+ DEF(STR_VARS, mail_temp_dir),
+ DEF(BOOL, auth_debug),
+ DEF(STR, auth_socket_path),
+ DEF(STR, doveadm_socket_path),
+ DEF(UINT, doveadm_worker_count),
+ DEF(IN_PORT, doveadm_port),
+ { .type = SET_ALIAS, .key = "doveadm_proxy_port" },
+ DEF(ENUM, doveadm_ssl),
+ DEF(STR, doveadm_username),
+ DEF(STR, doveadm_password),
+ DEF(STR, doveadm_allowed_commands),
+ DEF(STR, dsync_alt_char),
+ DEF(STR, dsync_remote_cmd),
+ DEF(STR, director_username_hash),
+ DEF(STR, doveadm_api_key),
+ DEF(STR, dsync_features),
+ DEF(UINT, dsync_commit_msgs_interval),
+ DEF(STR, doveadm_http_rawlog_dir),
+ DEF(STR, dsync_hashed_headers),
+
+ { .type = SET_STRLIST, .key = "plugin",
+ .offset = offsetof(struct doveadm_settings, plugin_envs) },
+
+ SETTING_DEFINE_LIST_END
+};
+const struct doveadm_settings doveadm_default_settings = {
+ .base_dir = PKG_RUNDIR,
+ .libexec_dir = PKG_LIBEXECDIR,
+ .mail_plugins = "",
+ .mail_plugin_dir = MODULEDIR,
+ .mail_temp_dir = "/tmp",
+ .auth_debug = FALSE,
+ .auth_socket_path = "auth-userdb",
+ .doveadm_socket_path = "doveadm-server",
+ .doveadm_worker_count = 0,
+ .doveadm_port = 0,
+ .doveadm_ssl = "no:ssl:starttls",
+ .doveadm_username = "doveadm",
+ .doveadm_password = "",
+ .doveadm_allowed_commands = "",
+ .dsync_alt_char = "_",
+ .dsync_remote_cmd = "ssh -l%{login} %{host} doveadm dsync-server -u%u -U",
+ .dsync_features = "",
+ .dsync_hashed_headers = "Date Message-ID",
+ .dsync_commit_msgs_interval = 100,
+ .director_username_hash = "%Lu",
+ .doveadm_api_key = "",
+ .doveadm_http_rawlog_dir = "",
+
+ .plugin_envs = ARRAY_INIT
+};
+static const struct setting_parser_info *doveadm_setting_dependencies[] = {
+ &mail_user_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info doveadm_setting_parser_info = {
+ .module_name = "doveadm",
+ .defines = doveadm_setting_defines,
+ .defaults = &doveadm_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct doveadm_settings),
+
+ .parent_offset = SIZE_MAX,
+ .check_func = doveadm_settings_check,
+ .dependencies = doveadm_setting_dependencies
+};
+/* ../../src/dns/dns-client-settings.c */
+/* <settings checks> */
+static struct file_listener_settings dns_client_unix_listeners_array[] = {
+ { "dns-client", 0666, "", "" },
+ { "login/dns-client", 0666, "", "" },
+};
+static struct file_listener_settings *dns_client_unix_listeners[] = {
+ &dns_client_unix_listeners_array[0],
+ &dns_client_unix_listeners_array[1],
+};
+static buffer_t dns_client_unix_listeners_buf = {
+ { { dns_client_unix_listeners, sizeof(dns_client_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings dns_client_service_settings = {
+ .name = "dns-client",
+ .protocol = "",
+ .type = "",
+ .executable = "dns-client",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &dns_client_unix_listeners_buf,
+ sizeof(dns_client_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+/* ../../src/director/director-settings.c */
+/* <settings checks> */
+static bool director_settings_verify(void *_set, pool_t pool, const char **error_r);
+
+static struct file_listener_settings director_unix_listeners_array[] = {
+ { "login/director", 0, "", "" },
+ { "director-admin", 0600, "", "" }
+};
+static struct file_listener_settings *director_unix_listeners[] = {
+ &director_unix_listeners_array[0],
+ &director_unix_listeners_array[1]
+};
+static buffer_t director_unix_listeners_buf = {
+ { { director_unix_listeners, sizeof(director_unix_listeners) } }
+};
+static struct file_listener_settings director_fifo_listeners_array[] = {
+ { "login/proxy-notify", 0, "", "" }
+};
+static struct file_listener_settings *director_fifo_listeners[] = {
+ &director_fifo_listeners_array[0]
+};
+static buffer_t director_fifo_listeners_buf = {
+ { { director_fifo_listeners, sizeof(director_fifo_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+static bool
+director_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **error_r)
+{
+ struct director_settings *set = _set;
+
+ if (set->director_user_expire < 10) {
+ *error_r = "director_user_expire is too low";
+ return FALSE;
+ }
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings director_service_settings = {
+ .name = "director",
+ .protocol = "",
+ .type = "",
+ .executable = "director",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = ".",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &director_unix_listeners_buf,
+ sizeof(director_unix_listeners[0]) } },
+ .fifo_listeners = { { &director_fifo_listeners_buf,
+ sizeof(director_fifo_listeners[0]) } },
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct director_settings)
+static const struct setting_define director_setting_defines[] = {
+ DEF(STR, master_user_separator),
+
+ DEF(STR, director_servers),
+ DEF(STR, director_mail_servers),
+ DEF(STR, director_username_hash),
+ DEF(STR, director_flush_socket),
+ DEF(TIME, director_ping_idle_timeout),
+ DEF(TIME, director_ping_max_timeout),
+ DEF(TIME, director_user_expire),
+ DEF(TIME, director_user_kick_delay),
+ DEF(UINT, director_max_parallel_moves),
+ DEF(UINT, director_max_parallel_kicks),
+ DEF(SIZE, director_output_buffer_size),
+
+ SETTING_DEFINE_LIST_END
+};
+const struct director_settings director_default_settings = {
+ .master_user_separator = "",
+
+ .director_servers = "",
+ .director_mail_servers = "",
+ .director_username_hash = "%Lu",
+ .director_flush_socket = "",
+ .director_ping_idle_timeout = 30,
+ .director_ping_max_timeout = 60,
+ .director_user_expire = 60*15,
+ .director_user_kick_delay = 2,
+ .director_max_parallel_moves = 100,
+ .director_max_parallel_kicks = 100,
+ .director_output_buffer_size = 10 * 1024 * 1024,
+};
+const struct setting_parser_info director_setting_parser_info = {
+ .module_name = "director",
+ .defines = director_setting_defines,
+ .defaults = &director_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct director_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = director_settings_verify
+};
+/* ../../src/dict/dict-settings.c */
+/* <settings checks> */
+static struct file_listener_settings dict_unix_listeners_array[] = {
+ { "dict", 0660, "", "$default_internal_group" }
+};
+static struct file_listener_settings *dict_unix_listeners[] = {
+ &dict_unix_listeners_array[0]
+};
+static buffer_t dict_unix_listeners_buf = {
+ { { dict_unix_listeners, sizeof(dict_unix_listeners) } }
+};
+
+static struct file_listener_settings dict_async_unix_listeners_array[] = {
+ { "dict-async", 0660, "", "$default_internal_group" }
+};
+static struct file_listener_settings *dict_async_unix_listeners[] = {
+ &dict_async_unix_listeners_array[0]
+};
+static buffer_t dict_async_unix_listeners_buf = {
+ { { dict_async_unix_listeners, sizeof(dict_async_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings dict_service_settings = {
+ .name = "dict",
+ .protocol = "",
+ .type = "",
+ .executable = "dict",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &dict_unix_listeners_buf,
+ sizeof(dict_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+struct service_settings dict_async_service_settings = {
+ .name = "dict-async",
+ .protocol = "",
+ .type = "",
+ .executable = "dict",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &dict_async_unix_listeners_buf,
+ sizeof(dict_async_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct dict_server_settings)
+static const struct setting_define dict_setting_defines[] = {
+ DEF(STR, base_dir),
+ DEF(BOOL, verbose_proctitle),
+
+ DEF(STR, dict_db_config),
+ { .type = SET_STRLIST, .key = "dict",
+ .offset = offsetof(struct dict_server_settings, dicts) },
+
+ SETTING_DEFINE_LIST_END
+};
+const struct dict_server_settings dict_default_settings = {
+ .base_dir = PKG_RUNDIR,
+ .verbose_proctitle = FALSE,
+
+ .dict_db_config = "",
+ .dicts = ARRAY_INIT
+};
+const struct setting_parser_info dict_setting_parser_info = {
+ .module_name = "dict",
+ .defines = dict_setting_defines,
+ .defaults = &dict_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct dict_server_settings),
+
+ .parent_offset = SIZE_MAX
+};
+/* ../../src/config/config-settings.c */
+/* <settings checks> */
+static struct file_listener_settings config_unix_listeners_array[] = {
+ { "config", 0600, "", "" }
+};
+static struct file_listener_settings *config_unix_listeners[] = {
+ &config_unix_listeners_array[0]
+};
+static buffer_t config_unix_listeners_buf = {
+ { { config_unix_listeners, sizeof(config_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings config_service_settings = {
+ .name = "config",
+ .protocol = "",
+ .type = "config",
+ .executable = "config",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &config_unix_listeners_buf,
+ sizeof(config_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+/* ../../src/auth/auth-settings.c */
+extern const struct setting_parser_info auth_passdb_setting_parser_info;
+extern const struct setting_parser_info auth_userdb_setting_parser_info;
+/* <settings checks> */
+static struct file_listener_settings auth_unix_listeners_array[] = {
+ { "login/login", 0666, "", "" },
+ { "token-login/tokenlogin", 0666, "", "" },
+ { "auth-login", 0600, "$default_internal_user", "" },
+ { "auth-client", 0600, "$default_internal_user", "" },
+ { "auth-userdb", 0666, "$default_internal_user", "" },
+ { "auth-master", 0600, "", "" }
+};
+static struct file_listener_settings *auth_unix_listeners[] = {
+ &auth_unix_listeners_array[0],
+ &auth_unix_listeners_array[1],
+ &auth_unix_listeners_array[2],
+ &auth_unix_listeners_array[3],
+ &auth_unix_listeners_array[4],
+ &auth_unix_listeners_array[5]
+};
+static buffer_t auth_unix_listeners_buf = {
+ { { auth_unix_listeners, sizeof(auth_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+static struct file_listener_settings auth_worker_unix_listeners_array[] = {
+ { "auth-worker", 0600, "$default_internal_user", "" }
+};
+static struct file_listener_settings *auth_worker_unix_listeners[] = {
+ &auth_worker_unix_listeners_array[0]
+};
+static buffer_t auth_worker_unix_listeners_buf = {
+ { { auth_worker_unix_listeners, sizeof(auth_worker_unix_listeners) } }
+};
+/* </settings checks> */
+/* <settings checks> */
+static bool
+auth_settings_set_self_ips(struct auth_settings *set, pool_t pool,
+ const char **error_r)
+{
+ const char *const *tmp;
+ ARRAY(struct ip_addr) ips_array;
+ struct ip_addr *ips;
+ unsigned int ips_count;
+ int ret;
+
+ if (*set->proxy_self == '\0') {
+ set->proxy_self_ips = p_new(pool, struct ip_addr, 1);
+ return TRUE;
+ }
+
+ p_array_init(&ips_array, pool, 4);
+ tmp = t_strsplit_spaces(set->proxy_self, " ");
+ for (; *tmp != NULL; tmp++) {
+ ret = net_gethostbyname(*tmp, &ips, &ips_count);
+ if (ret != 0) {
+ *error_r = t_strdup_printf("auth_proxy_self_ips: "
+ "gethostbyname(%s) failed: %s",
+ *tmp, net_gethosterror(ret));
+ }
+ array_append(&ips_array, ips, ips_count);
+ }
+ array_append_zero(&ips_array);
+ set->proxy_self_ips = array_front(&ips_array);
+ return TRUE;
+}
+
+static bool
+auth_verify_verbose_password(struct auth_settings *set,
+ const char **error_r)
+{
+ const char *p, *value = set->verbose_passwords;
+ unsigned int num;
+
+ p = strchr(value, ':');
+ if (p != NULL) {
+ if (str_to_uint(p+1, &num) < 0 || num == 0) {
+ *error_r = t_strdup_printf("auth_verbose_passwords: "
+ "Invalid truncation number: '%s'", p+1);
+ return FALSE;
+ }
+ value = t_strdup_until(value, p);
+ }
+ if (strcmp(value, "no") == 0)
+ return TRUE;
+ else if (strcmp(value, "plain") == 0)
+ return TRUE;
+ else if (strcmp(value, "sha1") == 0)
+ return TRUE;
+ else if (strcmp(value, "yes") == 0) {
+ /* just use it as alias for "plain" */
+ set->verbose_passwords = "plain";
+ return TRUE;
+ } else {
+ *error_r = "auth_verbose_passwords: Invalid value";
+ return FALSE;
+ }
+}
+
+static bool auth_settings_check(void *_set, pool_t pool,
+ const char **error_r)
+{
+ struct auth_settings *set = _set;
+ const char *p;
+
+ if (set->debug_passwords)
+ set->debug = TRUE;
+ if (set->debug)
+ set->verbose = TRUE;
+
+ if (set->worker_max_count == 0) {
+ *error_r = "auth_worker_max_count must be above zero";
+ return FALSE;
+ }
+
+ if (set->cache_size > 0 && set->cache_size < 1024) {
+ /* probably a configuration error.
+ older versions used megabyte numbers */
+ *error_r = t_strdup_printf("auth_cache_size value is too small "
+ "(%"PRIuUOFF_T" bytes)",
+ set->cache_size);
+ return FALSE;
+ }
+
+ if (!auth_verify_verbose_password(set, error_r))
+ return FALSE;
+
+ if (*set->username_chars == '\0') {
+ /* all chars are allowed */
+ memset(set->username_chars_map, 1,
+ sizeof(set->username_chars_map));
+ } else {
+ for (p = set->username_chars; *p != '\0'; p++)
+ set->username_chars_map[(int)(uint8_t)*p] = 1;
+ }
+
+ if (*set->username_translation != '\0') {
+ p = set->username_translation;
+ for (; *p != '\0' && p[1] != '\0'; p += 2)
+ set->username_translation_map[(int)(uint8_t)*p] = p[1];
+ }
+ set->realms_arr =
+ (const char *const *)p_strsplit_spaces(pool, set->realms, " ");
+
+ if (*set->policy_server_url != '\0') {
+ if (*set->policy_hash_nonce == '\0') {
+
+ *error_r = "auth_policy_hash_nonce must be set when policy server is used";
+ return FALSE;
+ }
+ const struct hash_method *digest = hash_method_lookup(set->policy_hash_mech);
+ if (digest == NULL) {
+ *error_r = "invalid auth_policy_hash_mech given";
+ return FALSE;
+ }
+ if (set->policy_hash_truncate > 0 && set->policy_hash_truncate >= digest->digest_size*8) {
+ *error_r = t_strdup_printf("policy_hash_truncate is not smaller than digest size (%u >= %u)",
+ set->policy_hash_truncate,
+ digest->digest_size*8);
+ return FALSE;
+ }
+ }
+
+ if (!auth_settings_set_self_ips(set, pool, error_r))
+ return FALSE;
+ return TRUE;
+}
+
+static bool
+auth_passdb_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct auth_passdb_settings *set = _set;
+
+ if (set->driver == NULL || *set->driver == '\0') {
+ *error_r = "passdb is missing driver";
+ return FALSE;
+ }
+ if (set->pass && strcmp(set->result_success, "return-ok") != 0) {
+ *error_r = "Obsolete pass=yes setting mixed with non-default result_success";
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool
+auth_userdb_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct auth_userdb_settings *set = _set;
+
+ if (set->driver == NULL || *set->driver == '\0') {
+ *error_r = "userdb is missing driver";
+ return FALSE;
+ }
+ return TRUE;
+}
+/* </settings checks> */
+struct service_settings auth_service_settings = {
+ .name = "auth",
+ .protocol = "",
+ .type = "",
+ .executable = "auth",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &auth_unix_listeners_buf,
+ sizeof(auth_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+struct service_settings auth_worker_service_settings = {
+ .name = "auth-worker",
+ .protocol = "",
+ .type = "worker",
+ .executable = "auth -w",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 1,
+ .service_count = 0,
+ .idle_kill = 0,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &auth_worker_unix_listeners_buf,
+ sizeof(auth_worker_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct auth_passdb_settings)
+static const struct setting_define auth_passdb_setting_defines[] = {
+ DEF(STR, name),
+ DEF(STR, driver),
+ DEF(STR, args),
+ DEF(STR, default_fields),
+ DEF(STR, override_fields),
+ DEF(STR, mechanisms),
+ DEF(STR, username_filter),
+
+ DEF(ENUM, skip),
+ DEF(ENUM, result_success),
+ DEF(ENUM, result_failure),
+ DEF(ENUM, result_internalfail),
+
+ DEF(BOOL, deny),
+ DEF(BOOL, pass),
+ DEF(BOOL, master),
+ DEF(ENUM, auth_verbose),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct auth_passdb_settings auth_passdb_default_settings = {
+ .name = "",
+ .driver = "",
+ .args = "",
+ .default_fields = "",
+ .override_fields = "",
+ .mechanisms = "",
+ .username_filter = "",
+
+ .skip = "never:authenticated:unauthenticated",
+ .result_success = "return-ok:return:return-fail:continue:continue-ok:continue-fail",
+ .result_failure = "continue:return:return-ok:return-fail:continue-ok:continue-fail",
+ .result_internalfail = "continue:return:return-ok:return-fail:continue-ok:continue-fail",
+
+ .deny = FALSE,
+ .pass = FALSE,
+ .master = FALSE,
+ .auth_verbose = "default:yes:no"
+};
+const struct setting_parser_info auth_passdb_setting_parser_info = {
+ .defines = auth_passdb_setting_defines,
+ .defaults = &auth_passdb_default_settings,
+
+ .type_offset = offsetof(struct auth_passdb_settings, name),
+ .struct_size = sizeof(struct auth_passdb_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &auth_setting_parser_info,
+
+ .check_func = auth_passdb_settings_check
+};
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct auth_userdb_settings)
+static const struct setting_define auth_userdb_setting_defines[] = {
+ DEF(STR, name),
+ DEF(STR, driver),
+ DEF(STR, args),
+ DEF(STR, default_fields),
+ DEF(STR, override_fields),
+
+ DEF(ENUM, skip),
+ DEF(ENUM, result_success),
+ DEF(ENUM, result_failure),
+ DEF(ENUM, result_internalfail),
+
+ DEF(ENUM, auth_verbose),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct auth_userdb_settings auth_userdb_default_settings = {
+ /* NOTE: when adding fields, update also auth.c:userdb_dummy_set */
+ .name = "",
+ .driver = "",
+ .args = "",
+ .default_fields = "",
+ .override_fields = "",
+
+ .skip = "never:found:notfound",
+ .result_success = "return-ok:return:return-fail:continue:continue-ok:continue-fail",
+ .result_failure = "continue:return:return-ok:return-fail:continue-ok:continue-fail",
+ .result_internalfail = "continue:return:return-ok:return-fail:continue-ok:continue-fail",
+
+ .auth_verbose = "default:yes:no"
+};
+const struct setting_parser_info auth_userdb_setting_parser_info = {
+ .defines = auth_userdb_setting_defines,
+ .defaults = &auth_userdb_default_settings,
+
+ .type_offset = offsetof(struct auth_userdb_settings, name),
+ .struct_size = sizeof(struct auth_userdb_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = &auth_setting_parser_info,
+
+ .check_func = auth_userdb_settings_check
+};
+#undef DEF
+#undef DEF_NOPREFIX
+#undef DEFLIST
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type("auth_"#name, name, struct auth_settings)
+#define DEF_NOPREFIX(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct auth_settings)
+#define DEFLIST(field, name, defines) \
+ { .type = SET_DEFLIST, .key = name, \
+ .offset = offsetof(struct auth_settings, field), \
+ .list_info = defines }
+static const struct setting_define auth_setting_defines[] = {
+ DEF(STR, mechanisms),
+ DEF(STR, realms),
+ DEF(STR, default_realm),
+ DEF(SIZE, cache_size),
+ DEF(TIME, cache_ttl),
+ DEF(TIME, cache_negative_ttl),
+ DEF(BOOL, cache_verify_password_with_worker),
+ DEF(STR, username_chars),
+ DEF(STR, username_translation),
+ DEF(STR, username_format),
+ DEF(STR, master_user_separator),
+ DEF(STR, anonymous_username),
+ DEF(STR, krb5_keytab),
+ DEF(STR, gssapi_hostname),
+ DEF(STR, winbind_helper_path),
+ DEF(STR, proxy_self),
+ DEF(TIME, failure_delay),
+
+ DEF(STR, policy_server_url),
+ DEF(STR, policy_server_api_header),
+ DEF(UINT, policy_server_timeout_msecs),
+ DEF(STR, policy_hash_mech),
+ DEF(STR, policy_hash_nonce),
+ DEF(STR, policy_request_attributes),
+ DEF(BOOL, policy_reject_on_fail),
+ DEF(BOOL, policy_check_before_auth),
+ DEF(BOOL, policy_check_after_auth),
+ DEF(BOOL, policy_report_after_auth),
+ DEF(BOOL, policy_log_only),
+ DEF(UINT, policy_hash_truncate),
+
+ DEF(BOOL, stats),
+ DEF(BOOL, verbose),
+ DEF(BOOL, debug),
+ DEF(BOOL, debug_passwords),
+ DEF(STR, verbose_passwords),
+ DEF(BOOL, ssl_require_client_cert),
+ DEF(BOOL, ssl_username_from_cert),
+ DEF(BOOL, use_winbind),
+
+ DEF(UINT, worker_max_count),
+
+ DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info),
+ DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info),
+
+ DEF_NOPREFIX(STR, base_dir),
+ DEF_NOPREFIX(BOOL, verbose_proctitle),
+ DEF_NOPREFIX(UINT, first_valid_uid),
+ DEF_NOPREFIX(UINT, last_valid_uid),
+ DEF_NOPREFIX(UINT, first_valid_gid),
+ DEF_NOPREFIX(UINT, last_valid_gid),
+
+ DEF_NOPREFIX(STR, ssl_client_ca_dir),
+ DEF_NOPREFIX(STR, ssl_client_ca_file),
+
+ SETTING_DEFINE_LIST_END
+};
+static const struct auth_settings auth_default_settings = {
+ .mechanisms = "plain",
+ .realms = "",
+ .default_realm = "",
+ .cache_size = 0,
+ .cache_ttl = 60*60,
+ .cache_negative_ttl = 60*60,
+ .cache_verify_password_with_worker = FALSE,
+ .username_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@",
+ .username_translation = "",
+ .username_format = "%Lu",
+ .master_user_separator = "",
+ .anonymous_username = "anonymous",
+ .krb5_keytab = "",
+ .gssapi_hostname = "",
+ .winbind_helper_path = "/usr/bin/ntlm_auth",
+ .proxy_self = "",
+ .failure_delay = 2,
+
+ .policy_server_url = "",
+ .policy_server_api_header = "",
+ .policy_server_timeout_msecs = 2000,
+ .policy_hash_mech = "sha256",
+ .policy_hash_nonce = "",
+ .policy_request_attributes = "login=%{requested_username} pwhash=%{hashed_password} remote=%{rip} device_id=%{client_id} protocol=%s session_id=%{session}",
+ .policy_reject_on_fail = FALSE,
+ .policy_check_before_auth = TRUE,
+ .policy_check_after_auth = TRUE,
+ .policy_report_after_auth = TRUE,
+ .policy_log_only = FALSE,
+ .policy_hash_truncate = 12,
+
+ .stats = FALSE,
+ .verbose = FALSE,
+ .debug = FALSE,
+ .debug_passwords = FALSE,
+ .verbose_passwords = "no",
+ .ssl_require_client_cert = FALSE,
+ .ssl_username_from_cert = FALSE,
+ .ssl_client_ca_dir = "",
+ .ssl_client_ca_file = "",
+
+ .use_winbind = FALSE,
+
+ .worker_max_count = 30,
+
+ .passdbs = ARRAY_INIT,
+ .userdbs = ARRAY_INIT,
+
+ .base_dir = PKG_RUNDIR,
+ .verbose_proctitle = FALSE,
+ .first_valid_uid = 500,
+ .last_valid_uid = 0,
+ .first_valid_gid = 1,
+ .last_valid_gid = 0,
+};
+const struct setting_parser_info auth_setting_parser_info = {
+ .module_name = "auth",
+ .defines = auth_setting_defines,
+ .defaults = &auth_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct auth_settings),
+
+ .parent_offset = SIZE_MAX,
+
+ .check_func = auth_settings_check
+};
+/* ../../src/anvil/anvil-settings.c */
+/* <settings checks> */
+static struct file_listener_settings anvil_unix_listeners_array[] = {
+ { "anvil", 0600, "", "" },
+ { "anvil-auth-penalty", 0600, "", "" }
+};
+static struct file_listener_settings *anvil_unix_listeners[] = {
+ &anvil_unix_listeners_array[0],
+ &anvil_unix_listeners_array[1]
+};
+static buffer_t anvil_unix_listeners_buf = {
+ { { anvil_unix_listeners, sizeof(anvil_unix_listeners) } }
+};
+/* </settings checks> */
+struct service_settings anvil_service_settings = {
+ .name = "anvil",
+ .protocol = "",
+ .type = "anvil",
+ .executable = "anvil",
+ .user = "$default_internal_user",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "empty",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 1,
+ .process_limit = 1,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &anvil_unix_listeners_buf,
+ sizeof(anvil_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT,
+
+ .process_limit_1 = TRUE
+};
+static struct service_settings *config_all_services[] = {
+#ifdef HAVE_LIBWRAP
+ &tcpwrap_service_settings,
+#endif
+ &health_check_service_settings,
+ &submission_service_settings,
+ &submission_login_service_settings,
+ &stats_service_settings,
+ &replicator_service_settings,
+ &aggregator_service_settings,
+ &pop3_service_settings,
+ &pop3_login_service_settings,
+ &old_stats_service_settings,
+ &log_service_settings,
+ &lmtp_service_settings,
+ &ipc_service_settings,
+ &indexer_worker_service_settings,
+ &indexer_service_settings,
+ &imap_service_settings,
+ &imap_urlauth_worker_service_settings,
+ &imap_urlauth_service_settings,
+ &imap_urlauth_login_service_settings,
+ &imap_login_service_settings,
+ &imap_hibernate_service_settings,
+ &doveadm_service_settings,
+ &dns_client_service_settings,
+ &director_service_settings,
+ &dict_service_settings,
+ &dict_async_service_settings,
+ &config_service_settings,
+ &auth_service_settings,
+ &auth_worker_service_settings,
+ &anvil_service_settings,
+};
+buffer_t config_all_services_buf = {
+ { { config_all_services, sizeof(config_all_services) } }
+};
+const struct setting_parser_info *all_default_roots[] = {
+ &master_service_setting_parser_info,
+ &master_service_ssl_setting_parser_info,
+ &master_service_ssl_server_setting_parser_info,
+ &smtp_submit_setting_parser_info,
+ &aggregator_setting_parser_info,
+ &auth_setting_parser_info,
+ &dict_setting_parser_info,
+ &director_setting_parser_info,
+ &doveadm_setting_parser_info,
+ &fs_crypt_setting_parser_info,
+ &imap_login_setting_parser_info,
+ &imap_setting_parser_info,
+ &imap_urlauth_login_setting_parser_info,
+ &imap_urlauth_setting_parser_info,
+ &imap_urlauth_worker_setting_parser_info,
+ &imapc_setting_parser_info,
+ &lda_setting_parser_info,
+ &lmtp_setting_parser_info,
+ &login_setting_parser_info,
+ &mail_storage_setting_parser_info,
+ &mail_user_setting_parser_info,
+ &maildir_setting_parser_info,
+ &master_setting_parser_info,
+ &mbox_setting_parser_info,
+ &mdbox_setting_parser_info,
+ &old_stats_setting_parser_info,
+ &pop3_login_setting_parser_info,
+ &pop3_setting_parser_info,
+ &pop3c_setting_parser_info,
+ &quota_status_setting_parser_info,
+ &replicator_setting_parser_info,
+ &stats_setting_parser_info,
+ &submission_login_setting_parser_info,
+ &submission_setting_parser_info,
+ NULL
+};
+const struct setting_parser_info *const *all_roots = all_default_roots;
+ARRAY_TYPE(service_settings) *default_services = &master_default_settings.services;
diff --git a/src/config/all-settings.h b/src/config/all-settings.h
new file mode 100644
index 0000000..96ac7bd
--- /dev/null
+++ b/src/config/all-settings.h
@@ -0,0 +1,8 @@
+#ifndef ALL_SETTINGS_H
+#define ALL_SETTINGS_H
+
+extern const struct setting_parser_info *const *all_roots;
+extern const struct setting_parser_info *all_default_roots[];
+extern ARRAY_TYPE(service_settings) *default_services;
+
+#endif
diff --git a/src/config/config-connection.c b/src/config/config-connection.c
new file mode 100644
index 0000000..bd3db86
--- /dev/null
+++ b/src/config/config-connection.c
@@ -0,0 +1,267 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "llist.h"
+#include "istream.h"
+#include "ostream.h"
+#include "strescape.h"
+#include "settings-parser.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "config-request.h"
+#include "config-parser.h"
+#include "config-connection.h"
+
+#include <unistd.h>
+
+#define MAX_INBUF_SIZE 1024
+
+#define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 2
+#define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0
+
+struct config_connection {
+ struct config_connection *prev, *next;
+
+ int fd;
+ struct istream *input;
+ struct ostream *output;
+ struct io *io;
+
+ bool version_received:1;
+ bool handshaked:1;
+};
+
+static struct config_connection *config_connections = NULL;
+
+static const char *const *
+config_connection_next_line(struct config_connection *conn)
+{
+ const char *line;
+
+ line = i_stream_next_line(conn->input);
+ if (line == NULL)
+ return NULL;
+
+ return t_strsplit_tabescaped(line);
+}
+
+static void
+config_request_output(const char *key, const char *value,
+ enum config_key_type type ATTR_UNUSED, void *context)
+{
+ struct ostream *output = context;
+ const char *p;
+
+ o_stream_nsend_str(output, key);
+ o_stream_nsend_str(output, "=");
+ while ((p = strchr(value, '\n')) != NULL) {
+ o_stream_nsend(output, value, p-value);
+ o_stream_nsend(output, SETTING_STREAM_LF_CHAR, 1);
+ value = p+1;
+ }
+ o_stream_nsend_str(output, value);
+ o_stream_nsend_str(output, "\n");
+}
+
+static int config_connection_request(struct config_connection *conn,
+ const char *const *args)
+{
+ struct config_export_context *ctx;
+ struct master_service_settings_output output;
+ struct config_filter filter;
+ const char *path, *error, *module, *const *wanted_modules;
+ ARRAY(const char *) modules;
+ ARRAY(const char *) exclude_settings;
+ bool is_master = FALSE;
+
+ /* [<args>] */
+ t_array_init(&modules, 4);
+ t_array_init(&exclude_settings, 4);
+ i_zero(&filter);
+ for (; *args != NULL; args++) {
+ if (str_begins(*args, "service="))
+ filter.service = *args + 8;
+ else if (str_begins(*args, "module=")) {
+ module = *args + 7;
+ if (strcmp(module, "master") == 0)
+ is_master = TRUE;
+ array_push_back(&modules, &module);
+ } else if (str_begins(*args, "exclude=")) {
+ const char *value = *args + 8;
+ array_push_back(&exclude_settings, &value);
+ } else if (str_begins(*args, "lname="))
+ filter.local_name = *args + 6;
+ else if (str_begins(*args, "lip=")) {
+ if (net_addr2ip(*args + 4, &filter.local_net) == 0) {
+ filter.local_bits =
+ IPADDR_IS_V4(&filter.local_net) ?
+ 32 : 128;
+ }
+ } else if (str_begins(*args, "rip=")) {
+ if (net_addr2ip(*args + 4, &filter.remote_net) == 0) {
+ filter.remote_bits =
+ IPADDR_IS_V4(&filter.remote_net) ?
+ 32 : 128;
+ }
+ }
+ }
+ array_append_zero(&modules);
+ wanted_modules = array_count(&modules) == 1 ? NULL :
+ array_front(&modules);
+ array_append_zero(&exclude_settings);
+
+ if (is_master) {
+ /* master reads configuration only when reloading settings */
+ path = master_service_get_config_path(master_service);
+ if (config_parse_file(path, TRUE, NULL, &error) <= 0) {
+ o_stream_nsend_str(conn->output,
+ t_strconcat("\nERROR ", error, "\n", NULL));
+ config_connection_destroy(conn);
+ return -1;
+ }
+ }
+
+ o_stream_cork(conn->output);
+
+ ctx = config_export_init(wanted_modules,
+ array_count(&exclude_settings) == 1 ? NULL :
+ array_front(&exclude_settings),
+ CONFIG_DUMP_SCOPE_SET, 0,
+ config_request_output, conn->output);
+ config_export_by_filter(ctx, &filter);
+ config_export_get_output(ctx, &output);
+
+ if (output.specific_services != NULL) {
+ const char *const *s;
+
+ for (s = output.specific_services; *s != NULL; s++) {
+ o_stream_nsend_str(conn->output,
+ t_strdup_printf("service=%s\t", *s));
+ }
+ }
+ if (output.service_uses_local)
+ o_stream_nsend_str(conn->output, "service-uses-local\t");
+ if (output.service_uses_remote)
+ o_stream_nsend_str(conn->output, "service-uses-remote\t");
+ if (output.used_local)
+ o_stream_nsend_str(conn->output, "used-local\t");
+ if (output.used_remote)
+ o_stream_nsend_str(conn->output, "used-remote\t");
+ o_stream_nsend_str(conn->output, "\n");
+
+ if (config_export_finish(&ctx) < 0) {
+ config_connection_destroy(conn);
+ return -1;
+ }
+ o_stream_nsend_str(conn->output, "\n");
+ o_stream_uncork(conn->output);
+ return 0;
+}
+
+static int config_filters_request(struct config_connection *conn)
+{
+ struct config_filter_parser *const *filters = config_filter_get_all(config_filter);
+ o_stream_cork(conn->output);
+ while(*filters != NULL) {
+ const struct config_filter *filter = &(*filters)->filter;
+ o_stream_nsend_str(conn->output, "FILTER");
+ if (filter->service != NULL)
+ o_stream_nsend_str(conn->output, t_strdup_printf("\tservice=%s",
+ str_tabescape(filter->service)));
+ if (filter->local_name != NULL)
+ o_stream_nsend_str(conn->output, t_strdup_printf("\tlocal-name=%s",
+ str_tabescape(filter->local_name)));
+ if (filter->local_bits > 0)
+ o_stream_nsend_str(conn->output, t_strdup_printf("\tlocal-net=%s/%u",
+ net_ip2addr(&filter->local_net),
+ filter->local_bits));
+ if (filter->remote_bits > 0)
+ o_stream_nsend_str(conn->output, t_strdup_printf("\tremote-net=%s/%u",
+ net_ip2addr(&filter->remote_net),
+ filter->remote_bits));
+ o_stream_nsend_str(conn->output, "\n");
+ filters++;
+ }
+ o_stream_nsend_str(conn->output, "\n");
+ o_stream_uncork(conn->output);
+ return 0;
+}
+
+
+static void config_connection_input(struct config_connection *conn)
+{
+ const char *const *args, *line;
+
+ switch (i_stream_read(conn->input)) {
+ case -2:
+ i_error("BUG: Config client connection sent too much data");
+ config_connection_destroy(conn);
+ return;
+ case -1:
+ config_connection_destroy(conn);
+ return;
+ }
+
+ if (!conn->version_received) {
+ line = i_stream_next_line(conn->input);
+ if (line == NULL)
+ return;
+
+ if (!version_string_verify(line, "config",
+ CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION)) {
+ i_error("Config client not compatible with this server "
+ "(mixed old and new binaries?)");
+ config_connection_destroy(conn);
+ return;
+ }
+ conn->version_received = TRUE;
+ }
+
+ while ((args = config_connection_next_line(conn)) != NULL) {
+ if (args[0] == NULL)
+ continue;
+ if (strcmp(args[0], "REQ") == 0) {
+ if (config_connection_request(conn, args + 1) < 0)
+ break;
+ }
+ if (strcmp(args[0], "FILTERS") == 0) {
+ if (config_filters_request(conn) < 0)
+ break;
+ }
+ }
+}
+
+struct config_connection *config_connection_create(int fd)
+{
+ struct config_connection *conn;
+
+ conn = i_new(struct config_connection, 1);
+ conn->fd = fd;
+ conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE);
+ conn->output = o_stream_create_fd(fd, SIZE_MAX);
+ o_stream_set_no_error_handling(conn->output, TRUE);
+ conn->io = io_add(fd, IO_READ, config_connection_input, conn);
+ DLLIST_PREPEND(&config_connections, conn);
+ return conn;
+}
+
+void config_connection_destroy(struct config_connection *conn)
+{
+ DLLIST_REMOVE(&config_connections, conn);
+
+ io_remove(&conn->io);
+ i_stream_destroy(&conn->input);
+ o_stream_destroy(&conn->output);
+ if (close(conn->fd) < 0)
+ i_error("close(config conn) failed: %m");
+ i_free(conn);
+
+ master_service_client_connection_destroyed(master_service);
+}
+
+void config_connections_destroy_all(void)
+{
+ while (config_connections != NULL)
+ config_connection_destroy(config_connections);
+}
diff --git a/src/config/config-connection.h b/src/config/config-connection.h
new file mode 100644
index 0000000..7983fff
--- /dev/null
+++ b/src/config/config-connection.h
@@ -0,0 +1,9 @@
+#ifndef CONFIG_CONNECTION_H
+#define CONFIG_CONNECTION_H
+
+struct config_connection *config_connection_create(int fd);
+void config_connection_destroy(struct config_connection *conn);
+
+void config_connections_destroy_all(void);
+
+#endif
diff --git a/src/config/config-filter.c b/src/config/config-filter.c
new file mode 100644
index 0000000..336380d
--- /dev/null
+++ b/src/config/config-filter.c
@@ -0,0 +1,401 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "settings-parser.h"
+#include "master-service-settings.h"
+#include "config-parser.h"
+#include "config-filter.h"
+#include "dns-util.h"
+
+struct config_filter_context {
+ pool_t pool;
+ struct config_filter_parser *const *parsers;
+};
+
+static bool config_filter_match_service(const struct config_filter *mask,
+ const struct config_filter *filter)
+{
+ if (mask->service != NULL) {
+ if (filter->service == NULL)
+ return FALSE;
+ if (mask->service[0] == '!') {
+ /* not service */
+ if (strcmp(filter->service, mask->service + 1) == 0)
+ return FALSE;
+ } else {
+ if (strcmp(filter->service, mask->service) != 0)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static bool
+config_filter_match_local_name(const struct config_filter *mask,
+ const char *filter_local_name)
+{
+ /* Handle multiple names separated by spaces in local_name
+ * Ex: local_name "mail.domain.tld domain.tld mx.domain.tld" { ... } */
+ const char *ptr, *local_name = mask->local_name;
+ while((ptr = strchr(local_name, ' ')) != NULL) {
+ if (dns_match_wildcard(filter_local_name,
+ t_strdup_until(local_name, ptr)) == 0)
+ return TRUE;
+ local_name = ptr+1;
+ }
+ return dns_match_wildcard(filter_local_name, local_name) == 0;
+}
+
+static bool config_filter_match_rest(const struct config_filter *mask,
+ const struct config_filter *filter)
+{
+ bool matched;
+
+ if (mask->local_name != NULL) {
+ if (filter->local_name == NULL)
+ return FALSE;
+ T_BEGIN {
+ matched = config_filter_match_local_name(mask, filter->local_name);
+ } T_END;
+ if (!matched)
+ return FALSE;
+ }
+ /* FIXME: it's not comparing full masks */
+ if (mask->remote_bits != 0) {
+ if (filter->remote_bits == 0)
+ return FALSE;
+ if (!net_is_in_network(&filter->remote_net, &mask->remote_net,
+ mask->remote_bits))
+ return FALSE;
+ }
+ if (mask->local_bits != 0) {
+ if (filter->local_bits == 0)
+ return FALSE;
+ if (!net_is_in_network(&filter->local_net, &mask->local_net,
+ mask->local_bits))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool config_filter_match(const struct config_filter *mask,
+ const struct config_filter *filter)
+{
+ if (!config_filter_match_service(mask, filter))
+ return FALSE;
+
+ return config_filter_match_rest(mask, filter);
+}
+
+bool config_filters_equal(const struct config_filter *f1,
+ const struct config_filter *f2)
+{
+ if (null_strcmp(f1->service, f2->service) != 0)
+ return FALSE;
+
+ if (f1->remote_bits != f2->remote_bits)
+ return FALSE;
+ if (!net_ip_compare(&f1->remote_net, &f2->remote_net))
+ return FALSE;
+
+ if (f1->local_bits != f2->local_bits)
+ return FALSE;
+ if (!net_ip_compare(&f1->local_net, &f2->local_net))
+ return FALSE;
+
+ if (null_strcasecmp(f1->local_name, f2->local_name) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct config_filter_context *config_filter_init(pool_t pool)
+{
+ struct config_filter_context *ctx;
+
+ ctx = p_new(pool, struct config_filter_context, 1);
+ ctx->pool = pool;
+ return ctx;
+}
+
+void config_filter_deinit(struct config_filter_context **_ctx)
+{
+ struct config_filter_context *ctx = *_ctx;
+ unsigned int i;
+
+ *_ctx = NULL;
+
+ for (i = 0; ctx->parsers[i] != NULL; i++)
+ config_filter_parsers_free(ctx->parsers[i]->parsers);
+ pool_unref(&ctx->pool);
+}
+
+void config_filter_add_all(struct config_filter_context *ctx,
+ struct config_filter_parser *const *parsers)
+{
+ ctx->parsers = parsers;
+}
+
+static int
+config_filter_parser_cmp(struct config_filter_parser *const *p1,
+ struct config_filter_parser *const *p2)
+{
+ const struct config_filter *f1 = &(*p1)->filter, *f2 = &(*p2)->filter;
+
+ /* remote and locals are first, although it doesn't really
+ matter which one comes first */
+ if (f1->local_name != NULL && f2->local_name == NULL)
+ return -1;
+ if (f1->local_name == NULL && f2->local_name != NULL)
+ return 1;
+
+ if (f1->local_bits > f2->local_bits)
+ return -1;
+ if (f1->local_bits < f2->local_bits)
+ return 1;
+
+ if (f1->remote_bits > f2->remote_bits)
+ return -1;
+ if (f1->remote_bits < f2->remote_bits)
+ return 1;
+
+ if (f1->service != NULL && f2->service == NULL)
+ return -1;
+ if (f1->service == NULL && f2->service != NULL)
+ return 1;
+ return 0;
+}
+
+static int
+config_filter_parser_cmp_rev(struct config_filter_parser *const *p1,
+ struct config_filter_parser *const *p2)
+{
+ return -config_filter_parser_cmp(p1, p2);
+}
+
+static bool str_array_contains(ARRAY_TYPE(const_string) *arr, const char *str)
+{
+ const char *p;
+
+ array_foreach_elem(arr, p) {
+ if (strcmp(p, str) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool have_changed_settings(const struct config_filter_parser *parser,
+ const char *const *modules)
+{
+ const unsigned char *changes;
+ unsigned int i, j, size;
+
+ for (i = 0; parser->parsers[i].root != NULL; i++) {
+ if (!config_module_want_parser(config_module_parsers,
+ modules, parser->parsers[i].root))
+ continue;
+
+ changes = settings_parser_get_changes(parser->parsers[i].parser);
+ size = parser->parsers[i].root->struct_size;
+ for (j = 0; j < size; j++) {
+ if (changes[j] != 0)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static struct config_filter_parser *const *
+config_filter_find_all(struct config_filter_context *ctx, pool_t pool,
+ const char *const *modules,
+ const struct config_filter *filter,
+ struct master_service_settings_output *output_r)
+{
+ ARRAY_TYPE(config_filter_parsers) matches;
+ ARRAY_TYPE(const_string) service_names;
+ unsigned int i;
+
+ i_zero(output_r);
+
+ p_array_init(&matches, pool, 8);
+ p_array_init(&service_names, pool, 8);
+ for (i = 0; ctx->parsers[i] != NULL; i++) {
+ const struct config_filter *mask = &ctx->parsers[i]->filter;
+
+ if (!config_filter_match_service(mask, filter)) {
+ if (!str_array_contains(&service_names, mask->service) &&
+ have_changed_settings(ctx->parsers[i], modules))
+ array_push_back(&service_names,
+ &mask->service);
+ continue;
+ }
+
+ if (mask->local_bits > 0 || mask->local_name != NULL)
+ output_r->service_uses_local = TRUE;
+ if (mask->remote_bits > 0)
+ output_r->service_uses_remote = TRUE;
+ if (config_filter_match_rest(mask, filter)) {
+ if (mask->local_bits > 0 || mask->local_name != NULL)
+ output_r->used_local = TRUE;
+ if (mask->remote_bits > 0)
+ output_r->used_remote = TRUE;
+ array_push_back(&matches, &ctx->parsers[i]);
+ }
+ }
+ if (filter->service == NULL) {
+ array_append_zero(&service_names);
+ output_r->specific_services = array_front(&service_names);
+ }
+
+ array_sort(&matches, config_filter_parser_cmp);
+ array_append_zero(&matches);
+ return array_front(&matches);
+}
+
+struct config_filter_parser *const *
+config_filter_get_all(struct config_filter_context *ctx)
+{
+ ARRAY_TYPE(config_filter_parsers) filters;
+ unsigned int i;
+
+ t_array_init(&filters, 8);
+ for (i = 0; ctx->parsers[i] != NULL; i++) {
+ array_push_back(&filters, &ctx->parsers[i]);
+ }
+ array_sort(&filters, config_filter_parser_cmp_rev);
+ array_append_zero(&filters);
+ return array_front(&filters);
+}
+
+struct config_filter_parser *const *
+config_filter_find_subset(struct config_filter_context *ctx,
+ const struct config_filter *filter)
+{
+ ARRAY_TYPE(config_filter_parsers) matches;
+ struct config_filter tmp_mask;
+ unsigned int i;
+
+ t_array_init(&matches, 8);
+ for (i = 0; ctx->parsers[i] != NULL; i++) {
+ const struct config_filter *mask = &ctx->parsers[i]->filter;
+
+ if (filter->service != NULL) {
+ if (!config_filter_match_service(mask, filter))
+ continue;
+ }
+
+ tmp_mask = *mask;
+ if (filter->local_name == NULL)
+ tmp_mask.local_name = NULL;
+ if (filter->local_bits == 0)
+ tmp_mask.local_bits = 0;
+ if (filter->remote_bits == 0)
+ tmp_mask.remote_bits = 0;
+
+ if (config_filter_match_rest(&tmp_mask, filter))
+ array_push_back(&matches, &ctx->parsers[i]);
+ }
+ array_sort(&matches, config_filter_parser_cmp_rev);
+ array_append_zero(&matches);
+ return array_front(&matches);
+}
+
+static bool
+config_filter_is_superset(const struct config_filter *sup,
+ const struct config_filter *filter)
+{
+ /* assume that both of the filters match the same subset, so we don't
+ need to compare IPs and service name. */
+ if (sup->local_bits > filter->local_bits)
+ return FALSE;
+ if (sup->remote_bits > filter->remote_bits)
+ return FALSE;
+ if (sup->local_name != NULL && filter->local_name == NULL) {
+ i_warning("%s", sup->local_name);
+ return FALSE;
+ }
+ if (sup->service != NULL && filter->service == NULL)
+ return FALSE;
+ return TRUE;
+}
+
+static int
+config_module_parser_apply_changes(struct config_module_parser *dest,
+ const struct config_filter_parser *src,
+ pool_t pool, const char **error_r)
+{
+ const char *conflict_key;
+ unsigned int i;
+
+ for (i = 0; dest[i].root != NULL; i++) {
+ if (settings_parser_apply_changes(dest[i].parser,
+ src->parsers[i].parser, pool,
+ error_r == NULL ? NULL :
+ &conflict_key) < 0) {
+ i_assert(error_r != NULL);
+ *error_r = t_strdup_printf("Conflict in setting %s "
+ "found from filter at %s", conflict_key,
+ src->file_and_line);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
+ const char *const *modules,
+ const struct config_filter *filter,
+ struct config_module_parser **parsers_r,
+ struct master_service_settings_output *output_r,
+ const char **error_r)
+{
+ struct config_filter_parser *const *src;
+ struct config_module_parser *dest;
+ const char *error = NULL, **error_p;
+ unsigned int i, count;
+
+ /* get the matching filters. the most specific ones are handled first,
+ so that if more generic filters try to override settings we'll fail
+ with an error. Merging SET_STRLIST types requires
+ settings_parser_apply_changes() to work a bit unintuitively by
+ letting the destination settings override the source settings. */
+ src = config_filter_find_all(ctx, pool, modules, filter, output_r);
+
+ /* all of them should have the same number of parsers.
+ duplicate our initial parsers from the first match */
+ for (count = 0; src[0]->parsers[count].root != NULL; count++) ;
+ dest = p_new(pool, struct config_module_parser, count + 1);
+ for (i = 0; i < count; i++) {
+ dest[i] = src[0]->parsers[i];
+ dest[i].parser =
+ settings_parser_dup(src[0]->parsers[i].parser, pool);
+ }
+
+ /* apply the changes from rest of the matches */
+ for (i = 1; src[i] != NULL; i++) {
+ if (config_filter_is_superset(&src[i]->filter,
+ &src[i-1]->filter))
+ error_p = NULL;
+ else
+ error_p = &error;
+
+ if (config_module_parser_apply_changes(dest, src[i], pool,
+ error_p) < 0) {
+ i_assert(error != NULL);
+ config_filter_parsers_free(dest);
+ *error_r = error;
+ return -1;
+ }
+ }
+ *parsers_r = dest;
+ return 0;
+}
+
+void config_filter_parsers_free(struct config_module_parser *parsers)
+{
+ unsigned int i;
+
+ for (i = 0; parsers[i].root != NULL; i++)
+ settings_parser_deinit(&parsers[i].parser);
+}
diff --git a/src/config/config-filter.h b/src/config/config-filter.h
new file mode 100644
index 0000000..fda3182
--- /dev/null
+++ b/src/config/config-filter.h
@@ -0,0 +1,58 @@
+#ifndef CONFIG_FILTER_H
+#define CONFIG_FILTER_H
+
+#include "net.h"
+
+struct master_service_settings_output;
+
+struct config_filter {
+ const char *service;
+ /* local_name is for TLS SNI requests.
+ both local_name and local_bits can't be set at the same time. */
+ const char *local_name;
+ /* the hosts are used only in doveconf output */
+ const char *local_host, *remote_host;
+ struct ip_addr local_net, remote_net;
+ unsigned int local_bits, remote_bits;
+};
+
+struct config_filter_parser {
+ struct config_filter filter;
+ const char *file_and_line;
+ /* NULL-terminated array of parsers */
+ struct config_module_parser *parsers;
+};
+ARRAY_DEFINE_TYPE(config_filter_parsers, struct config_filter_parser *);
+
+struct config_filter_context *config_filter_init(pool_t pool);
+void config_filter_deinit(struct config_filter_context **ctx);
+
+/* Replace filter's parsers with given parser list. */
+void config_filter_add_all(struct config_filter_context *ctx,
+ struct config_filter_parser *const *parsers);
+
+/* Build new parsers from all existing ones matching the given filter. */
+int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
+ const char *const *modules,
+ const struct config_filter *filter,
+ struct config_module_parser **parsers_r,
+ struct master_service_settings_output *output_r,
+ const char **error_r) ATTR_NULL(3);
+void config_filter_parsers_free(struct config_module_parser *parsers);
+
+/* Return a list of filters that are a subset of the given filter. */
+struct config_filter_parser *const *
+config_filter_find_subset(struct config_filter_context *ctx,
+ const struct config_filter *filter);
+
+struct config_filter_parser *const *
+config_filter_get_all(struct config_filter_context *ctx);
+
+/* Returns TRUE if filter matches mask. */
+bool config_filter_match(const struct config_filter *mask,
+ const struct config_filter *filter);
+/* Returns TRUE if two filters are fully equal. */
+bool config_filters_equal(const struct config_filter *f1,
+ const struct config_filter *f2);
+
+#endif
diff --git a/src/config/config-parser-private.h b/src/config/config-parser-private.h
new file mode 100644
index 0000000..9fe98ce
--- /dev/null
+++ b/src/config/config-parser-private.h
@@ -0,0 +1,75 @@
+#ifndef CONFIG_PARSER_PRIVATE_H
+#define CONFIG_PARSER_PRIVATE_H
+
+#include "config-parser.h"
+#include "config-filter.h"
+
+enum config_line_type {
+ CONFIG_LINE_TYPE_SKIP,
+ CONFIG_LINE_TYPE_CONTINUE,
+ CONFIG_LINE_TYPE_ERROR,
+ CONFIG_LINE_TYPE_KEYVALUE,
+ CONFIG_LINE_TYPE_KEYFILE,
+ CONFIG_LINE_TYPE_KEYVARIABLE,
+ CONFIG_LINE_TYPE_SECTION_BEGIN,
+ CONFIG_LINE_TYPE_SECTION_END,
+ CONFIG_LINE_TYPE_INCLUDE,
+ CONFIG_LINE_TYPE_INCLUDE_TRY
+};
+
+struct config_section_stack {
+ struct config_section_stack *prev;
+ const char *key;
+
+ struct config_filter filter;
+ /* root=NULL-terminated list of parsers */
+ struct config_module_parser *parsers;
+ size_t pathlen;
+
+ const char *open_path;
+ unsigned int open_linenum;
+ bool is_filter;
+};
+
+struct input_stack {
+ struct input_stack *prev;
+
+ struct istream *input;
+ const char *path;
+ unsigned int linenum;
+};
+
+struct config_parser_context {
+ pool_t pool;
+ const char *path;
+ const char *const *modules;
+
+ ARRAY(struct config_filter_parser *) all_parsers;
+ struct config_module_parser *root_parsers;
+ struct config_section_stack *cur_section;
+ struct input_stack *cur_input;
+
+ string_t *str;
+ size_t pathlen;
+ unsigned int section_counter;
+ const char *error;
+
+ struct old_set_parser *old;
+
+ HASH_TABLE(const char *, const char *) seen_settings;
+ struct config_filter_context *filter;
+ bool expand_values:1;
+ bool hide_errors:1;
+};
+
+extern void (*hook_config_parser_begin)(struct config_parser_context *ctx);
+extern int (*hook_config_parser_end)(struct config_parser_context *ctx,
+ const char **error_r);
+
+int config_apply_line(struct config_parser_context *ctx, const char *key,
+ const char *line, const char *section_name) ATTR_NULL(4);
+void config_parser_apply_line(struct config_parser_context *ctx,
+ enum config_line_type type,
+ const char *key, const char *value);
+
+#endif
diff --git a/src/config/config-parser.c b/src/config/config-parser.c
new file mode 100644
index 0000000..9d36796
--- /dev/null
+++ b/src/config/config-parser.c
@@ -0,0 +1,1212 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "hash.h"
+#include "strescape.h"
+#include "istream.h"
+#include "module-dir.h"
+#include "settings-parser.h"
+#include "service-settings.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "master-service-ssl-settings.h"
+#include "all-settings.h"
+#include "old-set-parser.h"
+#include "config-request.h"
+#include "config-parser-private.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_GLOB_H
+# include <glob.h>
+#endif
+
+#ifndef GLOB_BRACE
+# define GLOB_BRACE 0
+#endif
+
+#define DNS_LOOKUP_TIMEOUT_SECS 30
+#define DNS_LOOKUP_WARN_SECS 5
+
+ARRAY_DEFINE_TYPE(setting_parser_info_p, const struct setting_parser_info *);
+
+static const enum settings_parser_flags settings_parser_flags =
+ SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS |
+ SETTINGS_PARSER_FLAG_TRACK_CHANGES;
+
+struct config_module_parser *config_module_parsers;
+struct config_filter_context *config_filter;
+struct module *modules;
+void (*hook_config_parser_begin)(struct config_parser_context *ctx);
+int (*hook_config_parser_end)(struct config_parser_context *ctx,
+ const char **error_r);
+
+static ARRAY_TYPE(service_settings) services_free_at_deinit = ARRAY_INIT;
+static ARRAY_TYPE(setting_parser_info_p) roots_free_at_deinit = ARRAY_INIT;
+
+static const char *info_type_name_find(const struct setting_parser_info *info)
+{
+ unsigned int i;
+
+ for (i = 0; info->defines[i].key != NULL; i++) {
+ if (info->defines[i].offset == info->type_offset)
+ return info->defines[i].key;
+ }
+ i_panic("setting parser: Invalid type_offset value");
+ return NULL;
+}
+
+static int config_add_type(struct setting_parser_context *parser,
+ const char *line, const char *section_name)
+{
+ const struct setting_parser_info *info;
+ const char *p;
+ string_t *str;
+ int ret;
+
+ info = settings_parse_get_prev_info(parser);
+ if (info == NULL) {
+ /* section inside strlist */
+ return -1;
+ }
+ if (info->type_offset == SIZE_MAX)
+ return 0;
+
+ str = t_str_new(256);
+ p = strchr(line, '=');
+ str_append_data(str, line, p-line);
+ str_append_c(str, SETTINGS_SEPARATOR);
+ str_append(str, p+1);
+ if (info != NULL) {
+ str_append_c(str, SETTINGS_SEPARATOR);
+ str_append(str, info_type_name_find(info));
+ }
+
+ ret = settings_parse_keyvalue(parser, str_c(str), section_name);
+ i_assert(ret > 0);
+ return 0;
+}
+
+static bool
+config_parser_is_in_localremote(struct config_section_stack *section)
+{
+ const struct config_filter *filter = &section->filter;
+
+ return filter->local_name != NULL || filter->local_bits > 0 ||
+ filter->remote_bits > 0;
+}
+
+static void
+section_stack_write(string_t *str, struct config_section_stack *section)
+{
+ if (section == NULL)
+ return;
+
+ section_stack_write(str, section->prev);
+ if (!section->is_filter && section->key != NULL)
+ str_printfa(str, "%s { ", section->key);
+}
+
+static const char *
+get_setting_full_path(struct config_parser_context *ctx, const char *key)
+{
+ string_t *str = t_str_new(128);
+
+ section_stack_write(str, ctx->cur_section);
+ str_append(str, key);
+ return str_c(str);
+}
+
+int config_apply_line(struct config_parser_context *ctx, const char *key,
+ const char *line, const char *section_name)
+{
+ struct config_module_parser *l;
+ bool found = FALSE;
+ int ret;
+
+ for (l = ctx->cur_section->parsers; l->root != NULL; l++) {
+ ret = settings_parse_line(l->parser, line);
+ if (ret > 0) {
+ found = TRUE;
+ /* FIXME: remove once auth does support these. */
+ if (strcmp(l->root->module_name, "auth") == 0 &&
+ config_parser_is_in_localremote(ctx->cur_section)) {
+ ctx->error = p_strconcat(ctx->pool,
+ "Auth settings not supported inside local/remote blocks: ",
+ key, NULL);
+ return -1;
+ }
+ if (section_name != NULL) {
+ if (config_add_type(l->parser, line, section_name) < 0) {
+ ctx->error = "Section not allowed here";
+ return -1;
+ }
+ }
+ } else if (ret < 0) {
+ ctx->error = settings_parser_get_error(l->parser);
+ return -1;
+ }
+ }
+ if (!found) {
+ ctx->error = p_strconcat(ctx->pool, "Unknown setting: ",
+ get_setting_full_path(ctx, key), NULL);
+ return -1;
+ }
+ return 0;
+}
+
+static const char *
+fix_relative_path(const char *path, struct input_stack *input)
+{
+ const char *p;
+
+ if (*path == '/')
+ return path;
+
+ p = strrchr(input->path, '/');
+ if (p == NULL)
+ return path;
+
+ return t_strconcat(t_strdup_until(input->path, p+1), path, NULL);
+}
+
+static struct config_module_parser *
+config_module_parsers_init(pool_t pool)
+{
+ struct config_module_parser *dest;
+ unsigned int i, count;
+
+ for (count = 0; all_roots[count] != NULL; count++) ;
+
+ dest = p_new(pool, struct config_module_parser, count + 1);
+ for (i = 0; i < count; i++) {
+ dest[i].root = all_roots[i];
+ dest[i].parser = settings_parser_init(pool, all_roots[i],
+ settings_parser_flags);
+ }
+ return dest;
+}
+
+static void
+config_add_new_parser(struct config_parser_context *ctx)
+{
+ struct config_section_stack *cur_section = ctx->cur_section;
+ struct config_filter_parser *parser;
+
+ parser = p_new(ctx->pool, struct config_filter_parser, 1);
+ parser->filter = cur_section->filter;
+ if (ctx->cur_input->linenum == 0) {
+ parser->file_and_line =
+ p_strdup(ctx->pool, ctx->cur_input->path);
+ } else {
+ parser->file_and_line =
+ p_strdup_printf(ctx->pool, "%s:%d",
+ ctx->cur_input->path,
+ ctx->cur_input->linenum);
+ }
+ parser->parsers = cur_section->prev == NULL ? ctx->root_parsers :
+ config_module_parsers_init(ctx->pool);
+ array_push_back(&ctx->all_parsers, &parser);
+
+ cur_section->parsers = parser->parsers;
+}
+
+static struct config_section_stack *
+config_add_new_section(struct config_parser_context *ctx)
+{
+ struct config_section_stack *section;
+
+ section = p_new(ctx->pool, struct config_section_stack, 1);
+ section->prev = ctx->cur_section;
+ section->filter = ctx->cur_section->filter;
+ section->parsers = ctx->cur_section->parsers;
+
+ section->open_path = p_strdup(ctx->pool, ctx->cur_input->path);
+ section->open_linenum = ctx->cur_input->linenum;
+ return section;
+}
+
+static struct config_filter_parser *
+config_filter_parser_find(struct config_parser_context *ctx,
+ const struct config_filter *filter)
+{
+ struct config_filter_parser *parser;
+
+ array_foreach_elem(&ctx->all_parsers, parser) {
+ if (config_filters_equal(&parser->filter, filter))
+ return parser;
+ }
+ return NULL;
+}
+
+int config_parse_net(const char *value, struct ip_addr *ip_r,
+ unsigned int *bits_r, const char **error_r)
+{
+ struct ip_addr *ips;
+ const char *p;
+ unsigned int ip_count, bits, max_bits;
+ time_t t1, t2;
+ int ret;
+
+ if (net_parse_range(value, ip_r, bits_r) == 0)
+ return 0;
+
+ p = strchr(value, '/');
+ if (p != NULL) {
+ value = t_strdup_until(value, p);
+ p++;
+ }
+
+ t1 = time(NULL);
+ alarm(DNS_LOOKUP_TIMEOUT_SECS);
+ ret = net_gethostbyname(value, &ips, &ip_count);
+ alarm(0);
+ t2 = time(NULL);
+ if (ret != 0) {
+ *error_r = t_strdup_printf("gethostbyname(%s) failed: %s",
+ value, net_gethosterror(ret));
+ return -1;
+ }
+ *ip_r = ips[0];
+
+ if (t2 - t1 >= DNS_LOOKUP_WARN_SECS) {
+ i_warning("gethostbyname(%s) took %d seconds",
+ value, (int)(t2-t1));
+ }
+
+ max_bits = IPADDR_IS_V4(&ips[0]) ? 32 : 128;
+ if (p == NULL)
+ *bits_r = max_bits;
+ else if (str_to_uint(p, &bits) == 0 && bits <= max_bits)
+ *bits_r = bits;
+ else {
+ *error_r = t_strdup_printf("Invalid network mask: %s", p);
+ return -1;
+ }
+ return 0;
+}
+
+static bool
+config_filter_add_new_filter(struct config_parser_context *ctx,
+ const char *key, const char *value)
+{
+ struct config_filter *filter = &ctx->cur_section->filter;
+ struct config_filter *parent = &ctx->cur_section->prev->filter;
+ struct config_filter_parser *parser;
+ const char *error;
+
+ if (strcmp(key, "protocol") == 0) {
+ if (parent->service != NULL)
+ ctx->error = "Nested protocol { protocol { .. } } block not allowed";
+ else
+ filter->service = p_strdup(ctx->pool, value);
+ } else if (strcmp(key, "local") == 0) {
+ if (parent->remote_bits > 0)
+ ctx->error = "remote { local { .. } } not allowed (use local { remote { .. } } instead)";
+ else if (parent->service != NULL)
+ ctx->error = "protocol { local { .. } } not allowed (use local { protocol { .. } } instead)";
+ else if (parent->local_name != NULL)
+ ctx->error = "local_name { local { .. } } not allowed (use local { local_name { .. } } instead)";
+ else if (config_parse_net(value, &filter->local_net,
+ &filter->local_bits, &error) < 0)
+ ctx->error = p_strdup(ctx->pool, error);
+ else if (parent->local_bits > filter->local_bits ||
+ (parent->local_bits > 0 &&
+ !net_is_in_network(&filter->local_net,
+ &parent->local_net,
+ parent->local_bits)))
+ ctx->error = "local net1 { local net2 { .. } } requires net2 to be inside net1";
+ else
+ filter->local_host = p_strdup(ctx->pool, value);
+ } else if (strcmp(key, "local_name") == 0) {
+ if (parent->remote_bits > 0)
+ ctx->error = "remote { local_name { .. } } not allowed (use local_name { remote { .. } } instead)";
+ else if (parent->service != NULL)
+ ctx->error = "protocol { local_name { .. } } not allowed (use local_name { protocol { .. } } instead)";
+ else
+ filter->local_name = p_strdup(ctx->pool, value);
+ } else if (strcmp(key, "remote") == 0) {
+ if (parent->service != NULL)
+ ctx->error = "protocol { remote { .. } } not allowed (use remote { protocol { .. } } instead)";
+ else if (config_parse_net(value, &filter->remote_net,
+ &filter->remote_bits, &error) < 0)
+ ctx->error = p_strdup(ctx->pool, error);
+ else if (parent->remote_bits > filter->remote_bits ||
+ (parent->remote_bits > 0 &&
+ !net_is_in_network(&filter->remote_net,
+ &parent->remote_net,
+ parent->remote_bits)))
+ ctx->error = "remote net1 { remote net2 { .. } } requires net2 to be inside net1";
+ else
+ filter->remote_host = p_strdup(ctx->pool, value);
+ } else {
+ return FALSE;
+ }
+
+ parser = config_filter_parser_find(ctx, filter);
+ if (parser != NULL)
+ ctx->cur_section->parsers = parser->parsers;
+ else
+ config_add_new_parser(ctx);
+ ctx->cur_section->is_filter = TRUE;
+ return TRUE;
+}
+
+static int
+config_filter_parser_check(struct config_parser_context *ctx,
+ const struct config_module_parser *p,
+ const char **error_r)
+{
+ const char *error = NULL;
+ bool ok;
+
+ for (; p->root != NULL; p++) {
+ /* skip checking settings we don't care about */
+ if (!config_module_want_parser(ctx->root_parsers,
+ ctx->modules, p->root))
+ continue;
+
+ settings_parse_var_skip(p->parser);
+ T_BEGIN {
+ ok = settings_parser_check(p->parser, ctx->pool, &error);
+ } T_END_PASS_STR_IF(!ok, &error);
+ if (!ok) {
+ /* be sure to assert-crash early if error is missing */
+ i_assert(error != NULL);
+ *error_r = error;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static const char *
+get_str_setting(struct config_filter_parser *parser, const char *key,
+ const char *default_value)
+{
+ struct config_module_parser *module_parser;
+ const char *const *set_value;
+ enum setting_type set_type;
+
+ module_parser = parser->parsers;
+ for (; module_parser->parser != NULL; module_parser++) {
+ set_value = settings_parse_get_value(module_parser->parser,
+ key, &set_type);
+ if (set_value != NULL &&
+ settings_parse_is_changed(module_parser->parser, key)) {
+ i_assert(set_type == SET_STR || set_type == SET_ENUM);
+ return *set_value;
+ }
+ }
+ return default_value;
+}
+
+static int
+config_all_parsers_check(struct config_parser_context *ctx,
+ struct config_filter_context *new_filter,
+ const char **error_r)
+{
+ struct config_filter_parser *const *parsers;
+ struct config_module_parser *tmp_parsers;
+ struct master_service_settings_output output;
+ unsigned int i, count;
+ const char *ssl_set, *global_ssl_set;
+ pool_t tmp_pool;
+ bool ssl_warned = FALSE;
+ int ret = 0;
+
+ if (ctx->cur_section->prev != NULL) {
+ *error_r = t_strdup_printf(
+ "Missing '}' (section started at %s:%u)",
+ ctx->cur_section->open_path,
+ ctx->cur_section->open_linenum);
+ return -1;
+ }
+
+ tmp_pool = pool_alloconly_create(MEMPOOL_GROWING"config parsers check", 1024*64);
+ parsers = array_get(&ctx->all_parsers, &count);
+ i_assert(count > 0 && parsers[count-1] == NULL);
+ count--;
+
+ global_ssl_set = get_str_setting(parsers[0], "ssl", "");
+ for (i = 0; i < count && ret == 0; i++) {
+ if (config_filter_parsers_get(new_filter, tmp_pool, NULL,
+ &parsers[i]->filter,
+ &tmp_parsers, &output,
+ error_r) < 0) {
+ ret = -1;
+ break;
+ }
+
+ ssl_set = get_str_setting(parsers[i], "ssl", global_ssl_set);
+ if (strcmp(ssl_set, "no") != 0 &&
+ strcmp(global_ssl_set, "no") == 0 && !ssl_warned) {
+ i_warning("SSL is disabled because global ssl=no, "
+ "ignoring ssl=%s for subsection", ssl_set);
+ ssl_warned = TRUE;
+ }
+
+ ret = config_filter_parser_check(ctx, tmp_parsers, error_r);
+ config_filter_parsers_free(tmp_parsers);
+ p_clear(tmp_pool);
+ }
+ pool_unref(&tmp_pool);
+ return ret;
+}
+
+static int
+str_append_file(string_t *str, const char *key, const char *path,
+ const char **error_r)
+{
+ unsigned char buf[1024];
+ int fd;
+ ssize_t ret;
+
+ *error_r = NULL;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ *error_r = t_strdup_printf("%s: Can't open file %s: %m",
+ key, path);
+ return -1;
+ }
+ while ((ret = read(fd, buf, sizeof(buf))) > 0)
+ str_append_data(str, buf, ret);
+ if (ret < 0) {
+ *error_r = t_strdup_printf("%s: read(%s) failed: %m",
+ key, path);
+ }
+ i_close_fd(&fd);
+ return ret < 0 ? -1 : 0;
+}
+
+static int settings_add_include(struct config_parser_context *ctx, const char *path,
+ bool ignore_errors, const char **error_r)
+{
+ struct input_stack *tmp, *new_input;
+ int fd;
+
+ for (tmp = ctx->cur_input; tmp != NULL; tmp = tmp->prev) {
+ if (strcmp(tmp->path, path) == 0)
+ break;
+ }
+ if (tmp != NULL) {
+ *error_r = t_strdup_printf("Recursive include file: %s", path);
+ return -1;
+ }
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ if (ignore_errors)
+ return 0;
+
+ *error_r = t_strdup_printf("Couldn't open include file %s: %m",
+ path);
+ return -1;
+ }
+
+ new_input = p_new(ctx->pool, struct input_stack, 1);
+ new_input->prev = ctx->cur_input;
+ new_input->path = p_strdup(ctx->pool, path);
+ new_input->input = i_stream_create_fd_autoclose(&fd, SIZE_MAX);
+ i_stream_set_return_partial_line(new_input->input, TRUE);
+ ctx->cur_input = new_input;
+ return 0;
+}
+
+static int
+settings_include(struct config_parser_context *ctx, const char *pattern,
+ bool ignore_errors)
+{
+ const char *error;
+#ifdef HAVE_GLOB
+ glob_t globbers;
+ unsigned int i;
+
+ switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) {
+ case 0:
+ break;
+ case GLOB_NOSPACE:
+ ctx->error = "glob() failed: Not enough memory";
+ return -1;
+ case GLOB_ABORTED:
+ ctx->error = "glob() failed: Read error";
+ return -1;
+ case GLOB_NOMATCH:
+ if (ignore_errors)
+ return 0;
+ ctx->error = "No matches";
+ return -1;
+ default:
+ ctx->error = "glob() failed: Unknown error";
+ return -1;
+ }
+
+ /* iterate through the different files matching the globbing */
+ for (i = globbers.gl_pathc; i > 0; i--) {
+ if (settings_add_include(ctx, globbers.gl_pathv[i-1],
+ ignore_errors, &error) < 0) {
+ ctx->error = p_strdup(ctx->pool, error);
+ return -1;
+ }
+ }
+ globfree(&globbers);
+ return 0;
+#else
+ if (settings_add_include(ctx, pattern, ignore_errors, &error) < 0) {
+ ctx->error = p_strdup(ctx->pool, error);
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+static enum config_line_type
+config_parse_line(struct config_parser_context *ctx,
+ char *line, string_t *full_line,
+ const char **key_r, const char **value_r)
+{
+ const char *key;
+ size_t len;
+ char *p;
+
+ *key_r = NULL;
+ *value_r = NULL;
+
+ /* @UNSAFE: line is modified */
+
+ /* skip whitespace */
+ while (IS_WHITE(*line))
+ line++;
+
+ /* ignore comments or empty lines */
+ if (*line == '#' || *line == '\0')
+ return CONFIG_LINE_TYPE_SKIP;
+
+ /* strip away comments. pretty kludgy way really.. */
+ for (p = line; *p != '\0'; p++) {
+ if (*p == '\'' || *p == '"') {
+ char quote = *p;
+ for (p++; *p != quote && *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0')
+ p++;
+ }
+ if (*p == '\0')
+ break;
+ } else if (*p == '#') {
+ if (!IS_WHITE(p[-1])) {
+ i_warning("Configuration file %s line %u: "
+ "Ambiguous '#' character in line, treating it as comment. "
+ "Add a space before it to remove this warning.",
+ ctx->cur_input->path,
+ ctx->cur_input->linenum);
+ }
+ *p = '\0';
+ break;
+ }
+ }
+
+ /* remove whitespace from end of line */
+ len = strlen(line);
+ while (len >= 1) {
+ if(!IS_WHITE(line[len-1]))
+ break;
+ len--;
+ }
+ line[len] = '\0';
+
+ if (len >= 1 && line[len-1] == '\\') {
+ /* continues in next line */
+ len--;
+ while (len >= 1) {
+ if(!IS_WHITE(line[len-1]))
+ break;
+ len--;
+ }
+ if(len >= 1) {
+ str_append_data(full_line, line, len);
+ str_append_c(full_line, ' ');
+ }
+ return CONFIG_LINE_TYPE_CONTINUE;
+ }
+ if (str_len(full_line) > 0) {
+ str_append(full_line, line);
+ line = str_c_modifiable(full_line);
+ }
+
+ /* a) key = value
+ b) section_type [section_name] {
+ c) } */
+ key = line;
+ while (!IS_WHITE(*line) && *line != '\0' && *line != '=')
+ line++;
+ if (IS_WHITE(*line)) {
+ *line++ = '\0';
+ while (IS_WHITE(*line)) line++;
+ }
+ *key_r = key;
+ *value_r = line;
+
+ if (strcmp(key, "!include") == 0)
+ return CONFIG_LINE_TYPE_INCLUDE;
+ if (strcmp(key, "!include_try") == 0)
+ return CONFIG_LINE_TYPE_INCLUDE_TRY;
+
+ if (*line == '=') {
+ /* a) */
+ *line++ = '\0';
+ while (IS_WHITE(*line)) line++;
+
+ if (*line == '<') {
+ while (IS_WHITE(line[1])) line++;
+ *value_r = line + 1;
+ return CONFIG_LINE_TYPE_KEYFILE;
+ }
+ if (*line != '\'' && *line != '"' && strchr(line, '$') != NULL) {
+ *value_r = line;
+ return CONFIG_LINE_TYPE_KEYVARIABLE;
+ }
+
+ len = strlen(line);
+ if (len > 0 &&
+ ((*line == '"' && line[len-1] == '"') ||
+ (*line == '\'' && line[len-1] == '\''))) {
+ line[len-1] = '\0';
+ line = str_unescape(line+1);
+ }
+ *value_r = line;
+ return CONFIG_LINE_TYPE_KEYVALUE;
+ }
+
+ if (strcmp(key, "}") == 0 && *line == '\0')
+ return CONFIG_LINE_TYPE_SECTION_END;
+
+ /* b) + errors */
+ line[-1] = '\0';
+
+ if (*line == '{')
+ *value_r = "";
+ else {
+ /* get section name */
+ if (*line != '"') {
+ *value_r = line;
+ while (!IS_WHITE(*line) && *line != '\0')
+ line++;
+ if (*line != '\0') {
+ *line++ = '\0';
+ while (IS_WHITE(*line))
+ line++;
+ }
+ } else {
+ char *value = ++line;
+ while (*line != '"' && *line != '\0')
+ line++;
+ if (*line == '"') {
+ *line++ = '\0';
+ while (IS_WHITE(*line))
+ line++;
+ *value_r = str_unescape(value);
+ }
+ }
+ if (*line != '{') {
+ *value_r = "Expecting '{'";
+ return CONFIG_LINE_TYPE_ERROR;
+ }
+ }
+ if (line[1] != '\0') {
+ *value_r = "Garbage after '{'";
+ return CONFIG_LINE_TYPE_ERROR;
+ }
+ return CONFIG_LINE_TYPE_SECTION_BEGIN;
+}
+
+static int config_parse_finish(struct config_parser_context *ctx, const char **error_r)
+{
+ struct config_filter_context *new_filter;
+ const char *error;
+ int ret = 0;
+
+ if (hook_config_parser_end != NULL)
+ ret = hook_config_parser_end(ctx, error_r);
+
+ new_filter = config_filter_init(ctx->pool);
+ array_append_zero(&ctx->all_parsers);
+ config_filter_add_all(new_filter, array_front(&ctx->all_parsers));
+
+ if (ret < 0)
+ ;
+ else if (ctx->hide_errors)
+ ret = 0;
+ else if ((ret = config_all_parsers_check(ctx, new_filter, &error)) < 0) {
+ *error_r = t_strdup_printf("Error in configuration file %s: %s",
+ ctx->path, error);
+ }
+
+ if (config_filter != NULL)
+ config_filter_deinit(&config_filter);
+ config_module_parsers = ctx->root_parsers;
+ config_filter = new_filter;
+ return ret;
+}
+
+static const void *
+config_get_value(struct config_section_stack *section, const char *key,
+ bool expand_parent, enum setting_type *type_r)
+{
+ struct config_module_parser *l;
+ const void *value;
+
+ for (l = section->parsers; l->root != NULL; l++) {
+ value = settings_parse_get_value(l->parser, key, type_r);
+ if (value != NULL) {
+ if (!expand_parent || section->prev == NULL ||
+ settings_parse_is_changed(l->parser, key))
+ return value;
+
+ /* not changed by this parser. maybe parent has. */
+ return config_get_value(section->prev,
+ key, TRUE, type_r);
+ }
+ }
+ return NULL;
+}
+
+static bool
+config_require_key(struct config_parser_context *ctx, const char *key)
+{
+ struct config_module_parser *l;
+
+ if (ctx->modules == NULL)
+ return TRUE;
+
+ for (l = ctx->cur_section->parsers; l->root != NULL; l++) {
+ if (config_module_want_parser(ctx->root_parsers,
+ ctx->modules, l->root) &&
+ settings_parse_is_valid_key(l->parser, key))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int config_write_keyvariable(struct config_parser_context *ctx,
+ const char *key, const char *value,
+ string_t *str)
+{
+ const char *var_end, *p_start = value;
+ bool dump;
+ while (value != NULL) {
+ const char *var_name;
+ bool expand_parent;
+ var_end = strchr(value, ' ');
+
+ /* expand_parent=TRUE for "key = $key stuff".
+ we'll always expand it so that doveconf -n can give
+ usable output */
+ if (var_end == NULL)
+ var_name = value;
+ else
+ var_name = t_strdup_until(value, var_end);
+ expand_parent = strcmp(key, var_name +
+ (*var_name == '$' ? 1 : 0)) == 0;
+
+ if (!str_begins(var_name, "$") ||
+ (value > p_start && !IS_WHITE(value[-1]))) {
+ str_append(str, var_name);
+ } else if (!ctx->expand_values && !expand_parent) {
+ str_append(str, var_name);
+ } else if (str_begins(var_name, "$ENV:")) {
+ /* use environment variable */
+ const char *envval = getenv(var_name+5);
+ if (envval != NULL)
+ str_append(str, envval);
+ } else {
+ const char *var_value;
+ enum setting_type var_type;
+
+ i_assert(var_name[0] == '$');
+ var_name++;
+
+ var_value = config_get_value(ctx->cur_section, var_name,
+ expand_parent, &var_type);
+ if (var_value == NULL) {
+ ctx->error = p_strconcat(ctx->pool,
+ "Unknown variable: $",
+ var_name, NULL);
+ return -1;
+ }
+ if (!config_export_type(str, var_value, NULL,
+ var_type, TRUE, &dump)) {
+ ctx->error = p_strconcat(ctx->pool,
+ "Invalid variable: $",
+ var_name, NULL);
+ return -1;
+ }
+ }
+
+ if (var_end == NULL)
+ break;
+
+ str_append_c(str, ' ');
+
+ /* find next token */
+ while (*var_end != '\0' && IS_WHITE(*var_end)) var_end++;
+ value = var_end;
+ while (*var_end != '\0' && !IS_WHITE(*var_end)) var_end++;
+ }
+
+ return 0;
+}
+
+static int config_write_value(struct config_parser_context *ctx,
+ enum config_line_type type,
+ const char *key, const char *value)
+{
+ string_t *str = ctx->str;
+ const char *error, *path, *full_key;
+
+ switch (type) {
+ case CONFIG_LINE_TYPE_KEYVALUE:
+ str_append(str, value);
+ break;
+ case CONFIG_LINE_TYPE_KEYFILE:
+ full_key = t_strndup(str_data(ctx->str), str_len(str)-1);
+ if (!ctx->expand_values) {
+ str_append_c(str, '<');
+ str_append(str, value);
+ } else {
+ if (!config_require_key(ctx, full_key)) {
+ /* don't even try to open the file */
+ } else {
+ path = fix_relative_path(value, ctx->cur_input);
+ if (str_append_file(str, full_key, path, &error) < 0) {
+ /* file reading failed */
+ ctx->error = p_strdup(ctx->pool, error);
+ return -1;
+ }
+ }
+ }
+ break;
+ case CONFIG_LINE_TYPE_KEYVARIABLE:
+ if (config_write_keyvariable(ctx, key, value, str) < 0)
+ return -1;
+ break;
+ default:
+ i_unreached();
+ }
+ return 0;
+}
+
+static void
+config_parser_check_warnings(struct config_parser_context *ctx, const char *key)
+{
+ const char *path, *first_pos;
+
+ first_pos = hash_table_lookup(ctx->seen_settings, str_c(ctx->str));
+ if (ctx->cur_section->prev == NULL) {
+ /* changing a root setting. if we've already seen it inside
+ filters, log a warning. */
+ if (first_pos == NULL)
+ return;
+ i_warning("%s line %u: Global setting %s won't change the setting inside an earlier filter at %s "
+ "(if this is intentional, avoid this warning by moving the global setting before %s)",
+ ctx->cur_input->path, ctx->cur_input->linenum,
+ key, first_pos, first_pos);
+ return;
+ }
+ if (first_pos != NULL)
+ return;
+ first_pos = p_strdup_printf(ctx->pool, "%s line %u",
+ ctx->cur_input->path, ctx->cur_input->linenum);
+ path = p_strdup(ctx->pool, str_c(ctx->str));
+ hash_table_insert(ctx->seen_settings, path, first_pos);
+}
+
+void config_parser_apply_line(struct config_parser_context *ctx,
+ enum config_line_type type,
+ const char *key, const char *value)
+{
+ const char *section_name;
+
+ str_truncate(ctx->str, ctx->pathlen);
+ switch (type) {
+ case CONFIG_LINE_TYPE_SKIP:
+ break;
+ case CONFIG_LINE_TYPE_CONTINUE:
+ i_unreached();
+ case CONFIG_LINE_TYPE_ERROR:
+ ctx->error = p_strdup(ctx->pool, value);
+ break;
+ case CONFIG_LINE_TYPE_KEYVALUE:
+ case CONFIG_LINE_TYPE_KEYFILE:
+ case CONFIG_LINE_TYPE_KEYVARIABLE:
+ str_append(ctx->str, key);
+ config_parser_check_warnings(ctx, key);
+ str_append_c(ctx->str, '=');
+
+ if (config_write_value(ctx, type, key, value) < 0)
+ break;
+ (void)config_apply_line(ctx, key, str_c(ctx->str), NULL);
+ break;
+ case CONFIG_LINE_TYPE_SECTION_BEGIN:
+ ctx->cur_section = config_add_new_section(ctx);
+ ctx->cur_section->pathlen = ctx->pathlen;
+ ctx->cur_section->key = p_strdup(ctx->pool, key);
+
+ if (config_filter_add_new_filter(ctx, key, value)) {
+ /* new filter */
+ break;
+ }
+
+ /* new config section */
+ if (*value == '\0') {
+ /* no section name, use a counter */
+ section_name = dec2str(ctx->section_counter++);
+ } else {
+ section_name = settings_section_escape(value);
+ }
+ str_append(ctx->str, key);
+ ctx->pathlen = str_len(ctx->str);
+
+ str_append_c(ctx->str, '=');
+ str_append(ctx->str, section_name);
+
+ if (config_apply_line(ctx, key, str_c(ctx->str), value) < 0)
+ break;
+
+ str_truncate(ctx->str, ctx->pathlen);
+ str_append_c(ctx->str, SETTINGS_SEPARATOR);
+ str_append(ctx->str, section_name);
+ str_append_c(ctx->str, SETTINGS_SEPARATOR);
+ ctx->pathlen = str_len(ctx->str);
+ break;
+ case CONFIG_LINE_TYPE_SECTION_END:
+ if (ctx->cur_section->prev == NULL)
+ ctx->error = "Unexpected '}'";
+ else {
+ ctx->pathlen = ctx->cur_section->pathlen;
+ ctx->cur_section = ctx->cur_section->prev;
+ }
+ break;
+ case CONFIG_LINE_TYPE_INCLUDE:
+ case CONFIG_LINE_TYPE_INCLUDE_TRY:
+ (void)settings_include(ctx, fix_relative_path(value, ctx->cur_input),
+ type == CONFIG_LINE_TYPE_INCLUDE_TRY);
+ break;
+ }
+}
+
+int config_parse_file(const char *path, bool expand_values,
+ const char *const *modules, const char **error_r)
+{
+ struct input_stack root;
+ struct config_parser_context ctx;
+ unsigned int i, count;
+ const char *key, *value;
+ string_t *full_line;
+ enum config_line_type type;
+ char *line;
+ int fd, ret = 0;
+ bool handled;
+
+ if (path == NULL) {
+ path = "<defaults>";
+ fd = -1;
+ } else {
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ *error_r = t_strdup_printf("open(%s) failed: %m", path);
+ return 0;
+ }
+ }
+
+ i_zero(&ctx);
+ ctx.pool = pool_alloconly_create(MEMPOOL_GROWING"config file parser", 1024*256);
+ ctx.path = path;
+ ctx.hide_errors = fd == -1;
+
+ for (count = 0; all_roots[count] != NULL; count++) ;
+ ctx.root_parsers =
+ p_new(ctx.pool, struct config_module_parser, count+1);
+ for (i = 0; i < count; i++) {
+ ctx.root_parsers[i].root = all_roots[i];
+ ctx.root_parsers[i].parser =
+ settings_parser_init(ctx.pool, all_roots[i],
+ settings_parser_flags);
+ }
+
+ i_zero(&root);
+ root.path = path;
+ ctx.cur_input = &root;
+ ctx.expand_values = expand_values;
+ ctx.modules = modules;
+ hash_table_create(&ctx.seen_settings, ctx.pool, 0, str_hash, strcmp);
+
+ p_array_init(&ctx.all_parsers, ctx.pool, 128);
+ ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1);
+ config_add_new_parser(&ctx);
+
+ ctx.str = str_new(ctx.pool, 256);
+ full_line = str_new(default_pool, 512);
+ ctx.cur_input->input = fd != -1 ?
+ i_stream_create_fd_autoclose(&fd, SIZE_MAX) :
+ i_stream_create_from_data("", 0);
+ i_stream_set_return_partial_line(ctx.cur_input->input, TRUE);
+ old_settings_init(&ctx);
+ if (hook_config_parser_begin != NULL) T_BEGIN {
+ hook_config_parser_begin(&ctx);
+ } T_END;
+
+prevfile:
+ while ((line = i_stream_read_next_line(ctx.cur_input->input)) != NULL) {
+ ctx.cur_input->linenum++;
+ type = config_parse_line(&ctx, line, full_line,
+ &key, &value);
+ str_truncate(ctx.str, ctx.pathlen);
+ if (type == CONFIG_LINE_TYPE_CONTINUE)
+ continue;
+
+ T_BEGIN {
+ handled = old_settings_handle(&ctx, type, key, value);
+ if (!handled)
+ config_parser_apply_line(&ctx, type, key, value);
+ } T_END;
+
+ if (ctx.error != NULL) {
+ *error_r = t_strdup_printf(
+ "Error in configuration file %s line %d: %s",
+ ctx.cur_input->path, ctx.cur_input->linenum,
+ ctx.error);
+ ret = -2;
+ break;
+ }
+ str_truncate(full_line, 0);
+ }
+
+ i_stream_destroy(&ctx.cur_input->input);
+ ctx.cur_input = ctx.cur_input->prev;
+ if (line == NULL && ctx.cur_input != NULL)
+ goto prevfile;
+
+ hash_table_destroy(&ctx.seen_settings);
+ str_free(&full_line);
+ if (ret == 0)
+ ret = config_parse_finish(&ctx, error_r);
+ return ret < 0 ? ret : 1;
+}
+
+void config_parse_load_modules(void)
+{
+ struct module_dir_load_settings mod_set;
+ struct module *m;
+ const struct setting_parser_info **roots;
+ ARRAY_TYPE(setting_parser_info_p) new_roots;
+ ARRAY_TYPE(service_settings) new_services;
+ struct service_settings *const *services, *service_set;
+ unsigned int i, count;
+
+ i_zero(&mod_set);
+ mod_set.abi_version = DOVECOT_ABI_VERSION;
+ modules = module_dir_load(CONFIG_MODULE_DIR, NULL, &mod_set);
+ module_dir_init(modules);
+
+ i_array_init(&new_roots, 64);
+ i_array_init(&new_services, 64);
+ for (m = modules; m != NULL; m = m->next) {
+ roots = module_get_symbol_quiet(m,
+ t_strdup_printf("%s_set_roots", m->name));
+ if (roots != NULL) {
+ for (i = 0; roots[i] != NULL; i++)
+ array_push_back(&new_roots, &roots[i]);
+ }
+
+ services = module_get_symbol_quiet(m,
+ t_strdup_printf("%s_service_settings_array", m->name));
+ if (services != NULL) {
+ for (count = 0; services[count] != NULL; count++) ;
+ array_append(&new_services, services, count);
+ } else {
+ service_set = module_get_symbol_quiet(m,
+ t_strdup_printf("%s_service_settings", m->name));
+ if (service_set != NULL)
+ array_push_back(&new_services, &service_set);
+ }
+ }
+ if (array_count(&new_roots) > 0) {
+ /* modules added new settings. add the defaults and start
+ using the new list. */
+ for (i = 0; all_roots[i] != NULL; i++)
+ array_push_back(&new_roots, &all_roots[i]);
+ array_append_zero(&new_roots);
+ all_roots = array_front(&new_roots);
+ roots_free_at_deinit = new_roots;
+ } else {
+ array_free(&new_roots);
+ }
+ if (array_count(&new_services) > 0) {
+ /* module added new services. update the defaults. */
+ services = array_get(default_services, &count);
+ for (i = 0; i < count; i++)
+ array_push_back(&new_services, &services[i]);
+ *default_services = new_services;
+ services_free_at_deinit = new_services;
+ } else {
+ array_free(&new_services);
+ }
+}
+
+static bool parsers_are_connected(const struct setting_parser_info *root,
+ const struct setting_parser_info *info)
+{
+ const struct setting_parser_info *p;
+ const struct setting_parser_info *const *dep;
+
+ /* we're trying to find info or its parents from root's dependencies. */
+
+ for (p = info; p != NULL; p = p->parent) {
+ if (p == root)
+ return TRUE;
+ }
+
+ if (root->dependencies != NULL) {
+ for (dep = root->dependencies; *dep != NULL; dep++) {
+ if (parsers_are_connected(*dep, info))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+bool config_module_want_parser(struct config_module_parser *parsers,
+ const char *const *modules,
+ const struct setting_parser_info *root)
+{
+ struct config_module_parser *l;
+
+ if (modules == NULL)
+ return TRUE;
+ if (root == &master_service_setting_parser_info) {
+ /* everyone wants master service settings */
+ return TRUE;
+ }
+
+ for (l = parsers; l->root != NULL; l++) {
+ if (!str_array_find(modules, l->root->module_name))
+ continue;
+
+ /* see if we can find a way to get from the original parser
+ to this parser */
+ if (parsers_are_connected(l->root, root))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void config_parser_deinit(void)
+{
+ if (array_is_created(&services_free_at_deinit))
+ array_free(&services_free_at_deinit);
+ if (array_is_created(&roots_free_at_deinit))
+ array_free(&roots_free_at_deinit);
+}
diff --git a/src/config/config-parser.h b/src/config/config-parser.h
new file mode 100644
index 0000000..e0a0a5b
--- /dev/null
+++ b/src/config/config-parser.h
@@ -0,0 +1,33 @@
+#ifndef CONFIG_PARSER_H
+#define CONFIG_PARSER_H
+
+#define CONFIG_MODULE_DIR MODULEDIR"/settings"
+
+#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
+
+struct config_module_parser {
+ const struct setting_parser_info *root;
+ struct setting_parser_context *parser;
+ void *settings;
+};
+ARRAY_DEFINE_TYPE(config_module_parsers, struct config_module_parser *);
+
+extern struct config_module_parser *config_module_parsers;
+extern struct config_filter_context *config_filter;
+extern struct module *modules;
+
+int config_parse_net(const char *value, struct ip_addr *ip_r,
+ unsigned int *bits_r, const char **error_r);
+int config_parse_file(const char *path, bool expand_values,
+ const char *const *modules, const char **error_r)
+ ATTR_NULL(3);
+
+void config_parse_load_modules(void);
+
+bool config_module_want_parser(struct config_module_parser *parsers,
+ const char *const *modules,
+ const struct setting_parser_info *root)
+ ATTR_NULL(2);
+void config_parser_deinit(void);
+
+#endif
diff --git a/src/config/config-request.c b/src/config/config-request.c
new file mode 100644
index 0000000..7428367
--- /dev/null
+++ b/src/config/config-request.c
@@ -0,0 +1,524 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "hash.h"
+#include "ostream.h"
+#include "settings-parser.h"
+#include "master-service-settings.h"
+#include "all-settings.h"
+#include "config-parser.h"
+#include "config-request.h"
+#include "old-set-parser.h"
+
+struct config_export_context {
+ pool_t pool;
+ string_t *value;
+ string_t *prefix;
+ HASH_TABLE(char *, char *) keys;
+ enum config_dump_scope scope;
+
+ config_request_callback_t *callback;
+ void *context;
+
+ const char *const *modules;
+ const char *const *exclude_settings;
+ enum config_dump_flags flags;
+ const struct config_module_parser *parsers;
+ struct config_module_parser *dup_parsers;
+ struct master_service_settings_output output;
+
+ bool failed;
+};
+
+static void config_export_size(string_t *str, uoff_t size)
+{
+ static const char suffixes[] = { 'B', 'k', 'M', 'G', 'T' };
+ char suffix = suffixes[0];
+ unsigned int i;
+
+ if (size == 0) {
+ str_append_c(str, '0');
+ return;
+ }
+ for (i = 1; i < N_ELEMENTS(suffixes) && (size % 1024) == 0; i++) {
+ suffix = suffixes[i];
+ size /= 1024;
+ }
+ str_printfa(str, "%"PRIuUOFF_T" %c", size, suffix);
+}
+
+static void config_export_time(string_t *str, unsigned int stamp)
+{
+ const char *suffix = "secs";
+
+ if (stamp == 0) {
+ str_append_c(str, '0');
+ return;
+ }
+
+ if (stamp % 60 == 0) {
+ stamp /= 60;
+ suffix = "mins";
+ if (stamp % 60 == 0) {
+ stamp /= 60;
+ suffix = "hours";
+ if (stamp % 24 == 0) {
+ stamp /= 24;
+ suffix = "days";
+ if (stamp % 7 == 0) {
+ stamp /= 7;
+ suffix = "weeks";
+ }
+ }
+ }
+ }
+
+ str_printfa(str, "%u %s", stamp, suffix);
+}
+
+static void config_export_time_msecs(string_t *str, unsigned int stamp_msecs)
+{
+ if ((stamp_msecs % 1000) == 0)
+ config_export_time(str, stamp_msecs/1000);
+ else
+ str_printfa(str, "%u ms", stamp_msecs);
+}
+
+bool config_export_type(string_t *str, const void *value,
+ const void *default_value,
+ enum setting_type type, bool dump_default,
+ bool *dump_r)
+{
+ switch (type) {
+ case SET_BOOL: {
+ const bool *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval)
+ str_append(str, *val ? "yes" : "no");
+ break;
+ }
+ case SET_SIZE: {
+ const uoff_t *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval)
+ config_export_size(str, *val);
+ break;
+ }
+ case SET_UINT:
+ case SET_UINT_OCT:
+ case SET_TIME:
+ case SET_TIME_MSECS: {
+ const unsigned int *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval) {
+ switch (type) {
+ case SET_UINT_OCT:
+ str_printfa(str, "0%o", *val);
+ break;
+ case SET_TIME:
+ config_export_time(str, *val);
+ break;
+ case SET_TIME_MSECS:
+ config_export_time_msecs(str, *val);
+ break;
+ default:
+ str_printfa(str, "%u", *val);
+ break;
+ }
+ }
+ break;
+ }
+ case SET_IN_PORT: {
+ const in_port_t *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval)
+ str_printfa(str, "%u", *val);
+ break;
+ }
+ case SET_STR_VARS: {
+ const char *const *val = value, *sval;
+ const char *const *_dval = default_value;
+ const char *dval = _dval == NULL ? NULL : *_dval;
+
+ i_assert(*val == NULL ||
+ **val == SETTING_STRVAR_UNEXPANDED[0]);
+
+ sval = *val == NULL ? NULL : (*val + 1);
+ if ((dump_default || null_strcmp(sval, dval) != 0) &&
+ sval != NULL) {
+ str_append(str, sval);
+ *dump_r = TRUE;
+ }
+ break;
+ }
+ case SET_STR: {
+ const char *const *val = value;
+ const char *const *_dval = default_value;
+ const char *dval = _dval == NULL ? NULL : *_dval;
+
+ if ((dump_default || null_strcmp(*val, dval) != 0) &&
+ *val != NULL) {
+ str_append(str, *val);
+ *dump_r = TRUE;
+ }
+ break;
+ }
+ case SET_ENUM: {
+ const char *const *val = value;
+ size_t len = strlen(*val);
+
+ if (dump_default)
+ str_append(str, *val);
+ else {
+ const char *const *_dval = default_value;
+ const char *dval = _dval == NULL ? NULL : *_dval;
+
+ i_assert(dval != NULL);
+ if (strncmp(*val, dval, len) != 0 ||
+ ((*val)[len] != ':' && (*val)[len] != '\0'))
+ str_append(str, *val);
+ }
+ break;
+ }
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+setting_export_section_name(string_t *str, const struct setting_define *def,
+ const void *set, unsigned int idx)
+{
+ const char *const *name;
+ size_t name_offset;
+
+ if (def->type != SET_DEFLIST_UNIQUE) {
+ /* not unique, use the index */
+ str_printfa(str, "%u", idx);
+ return;
+ }
+ name_offset = def->list_info->type_offset;
+ i_assert(name_offset != SIZE_MAX);
+
+ name = CONST_PTR_OFFSET(set, name_offset);
+ if (*name == NULL || **name == '\0') {
+ /* no name, this one isn't unique. use the index. */
+ str_printfa(str, "%u", idx);
+ } else T_BEGIN {
+ str_append(str, settings_section_escape(*name));
+ } T_END;
+}
+
+static void
+settings_export(struct config_export_context *ctx,
+ const struct setting_parser_info *info,
+ bool parent_unique_deflist,
+ const void *set, const void *change_set)
+{
+ const struct setting_define *def;
+ const void *value, *default_value, *change_value;
+ void *const *children, *const *change_children = NULL;
+ unsigned int i, count, count2;
+ size_t prefix_len;
+ const char *str;
+ char *key;
+ bool dump, dump_default = FALSE;
+
+ for (def = info->defines; def->key != NULL; def++) {
+ if (ctx->exclude_settings != NULL &&
+ str_array_find(ctx->exclude_settings, def->key))
+ continue;
+
+ value = CONST_PTR_OFFSET(set, def->offset);
+ default_value = info->defaults == NULL ? NULL :
+ CONST_PTR_OFFSET(info->defaults, def->offset);
+ change_value = CONST_PTR_OFFSET(change_set, def->offset);
+ switch (ctx->scope) {
+ case CONFIG_DUMP_SCOPE_ALL_WITH_HIDDEN:
+ dump_default = TRUE;
+ break;
+ case CONFIG_DUMP_SCOPE_ALL_WITHOUT_HIDDEN:
+ if ((def->flags & SET_FLAG_HIDDEN) == 0) {
+ /* not hidden - dump it */
+ dump_default = TRUE;
+ break;
+ }
+ /* hidden - dump default only if it's explicitly set */
+ /* fall through */
+ case CONFIG_DUMP_SCOPE_SET:
+ dump_default = *((const char *)change_value) != 0;
+ break;
+ case CONFIG_DUMP_SCOPE_CHANGED:
+ dump_default = FALSE;
+ break;
+ }
+ if (!parent_unique_deflist ||
+ (ctx->flags & CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS) == 0) {
+ /* .. */
+ } else if (*((const char *)change_value) == 0 &&
+ def->offset != info->type_offset) {
+ /* this is mainly for service {} blocks. if value
+ hasn't changed, it's the default. even if
+ info->defaults has a different value. */
+ default_value = value;
+ } else {
+ /* value is set explicitly, but we don't know the
+ default here. assume it's not the default. */
+ dump_default = TRUE;
+ }
+
+ dump = FALSE;
+ count = 0; children = NULL;
+ str_truncate(ctx->value, 0);
+ switch (def->type) {
+ case SET_BOOL:
+ case SET_SIZE:
+ case SET_UINT:
+ case SET_UINT_OCT:
+ case SET_TIME:
+ case SET_TIME_MSECS:
+ case SET_IN_PORT:
+ case SET_STR_VARS:
+ case SET_STR:
+ case SET_ENUM:
+ if (!config_export_type(ctx->value, value,
+ default_value, def->type,
+ dump_default, &dump))
+ i_unreached();
+ break;
+ case SET_DEFLIST:
+ case SET_DEFLIST_UNIQUE: {
+ const ARRAY_TYPE(void_array) *val = value;
+ const ARRAY_TYPE(void_array) *change_val = change_value;
+
+ if (!array_is_created(val))
+ break;
+
+ children = array_get(val, &count);
+ for (i = 0; i < count; i++) {
+ if (i > 0)
+ str_append_c(ctx->value, ' ');
+ setting_export_section_name(ctx->value, def, children[i], i);
+ }
+ change_children = array_get(change_val, &count2);
+ i_assert(count == count2);
+ break;
+ }
+ case SET_STRLIST: {
+ const ARRAY_TYPE(const_string) *val = value;
+ const char *const *strings;
+
+ if (!array_is_created(val))
+ break;
+
+ key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+ def->key, NULL);
+
+ if (hash_table_lookup(ctx->keys, key) != NULL) {
+ /* already added all of these */
+ break;
+ }
+ hash_table_insert(ctx->keys, key, key);
+ /* for doveconf -n to see this KEY_LIST */
+ ctx->callback(key, "", CONFIG_KEY_LIST, ctx->context);
+
+ strings = array_get(val, &count);
+ i_assert(count % 2 == 0);
+ for (i = 0; i < count; i += 2) {
+ str = p_strdup_printf(ctx->pool, "%s%s%c%s",
+ str_c(ctx->prefix),
+ def->key,
+ SETTINGS_SEPARATOR,
+ strings[i]);
+ ctx->callback(str, strings[i+1],
+ CONFIG_KEY_NORMAL, ctx->context);
+ }
+ count = 0;
+ break;
+ }
+ case SET_ALIAS:
+ break;
+ }
+ if (str_len(ctx->value) > 0 || dump) {
+ key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+ def->key, NULL);
+ if (hash_table_lookup(ctx->keys, key) == NULL) {
+ enum config_key_type type;
+
+ if (def->offset == info->type_offset &&
+ parent_unique_deflist)
+ type = CONFIG_KEY_UNIQUE_KEY;
+ else if (SETTING_TYPE_IS_DEFLIST(def->type))
+ type = CONFIG_KEY_LIST;
+ else
+ type = CONFIG_KEY_NORMAL;
+ ctx->callback(key, str_c(ctx->value), type,
+ ctx->context);
+ hash_table_insert(ctx->keys, key, key);
+ }
+ }
+
+ i_assert(count == 0 || children != NULL);
+ prefix_len = str_len(ctx->prefix);
+ for (i = 0; i < count; i++) {
+ str_append(ctx->prefix, def->key);
+ str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
+ setting_export_section_name(ctx->prefix, def, children[i], i);
+ str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
+ settings_export(ctx, def->list_info,
+ def->type == SET_DEFLIST_UNIQUE,
+ children[i], change_children[i]);
+
+ str_truncate(ctx->prefix, prefix_len);
+ }
+ }
+}
+
+struct config_export_context *
+config_export_init(const char *const *modules,
+ const char *const *exclude_settings,
+ enum config_dump_scope scope,
+ enum config_dump_flags flags,
+ config_request_callback_t *callback, void *context)
+{
+ struct config_export_context *ctx;
+ pool_t pool;
+
+ pool = pool_alloconly_create(MEMPOOL_GROWING"config export", 1024*64);
+ ctx = p_new(pool, struct config_export_context, 1);
+ ctx->pool = pool;
+
+ ctx->modules = modules == NULL ? NULL : p_strarray_dup(pool, modules);
+ ctx->exclude_settings = exclude_settings == NULL ? NULL :
+ p_strarray_dup(pool, exclude_settings);
+ ctx->flags = flags;
+ ctx->callback = callback;
+ ctx->context = context;
+ ctx->scope = scope;
+ ctx->value = str_new(pool, 256);
+ ctx->prefix = str_new(pool, 64);
+ hash_table_create(&ctx->keys, ctx->pool, 0, str_hash, strcmp);
+ return ctx;
+}
+
+void config_export_by_filter(struct config_export_context *ctx,
+ const struct config_filter *filter)
+{
+ const char *error;
+
+ if (config_filter_parsers_get(config_filter, ctx->pool,
+ ctx->modules, filter,
+ &ctx->dup_parsers, &ctx->output,
+ &error) < 0) {
+ i_error("%s", error);
+ ctx->failed = TRUE;
+ }
+ ctx->parsers = ctx->dup_parsers;
+}
+
+void config_export_parsers(struct config_export_context *ctx,
+ const struct config_module_parser *parsers)
+{
+ ctx->parsers = parsers;
+}
+
+void config_export_get_output(struct config_export_context *ctx,
+ struct master_service_settings_output *output_r)
+{
+ *output_r = ctx->output;
+}
+
+const char *
+config_export_get_import_environment(struct config_export_context *ctx)
+{
+ enum setting_type stype;
+ unsigned int i;
+
+ for (i = 0; ctx->parsers[i].root != NULL; i++) {
+ if (ctx->parsers[i].root == &master_service_setting_parser_info) {
+ const char *const *value =
+ settings_parse_get_value(ctx->parsers[i].parser,
+ "import_environment", &stype);
+ i_assert(value != NULL);
+ return *value;
+ }
+ }
+ i_unreached();
+}
+
+static void config_export_free(struct config_export_context *ctx)
+{
+ if (ctx->dup_parsers != NULL)
+ config_filter_parsers_free(ctx->dup_parsers);
+ hash_table_destroy(&ctx->keys);
+ pool_unref(&ctx->pool);
+}
+
+int config_export_finish(struct config_export_context **_ctx)
+{
+ struct config_export_context *ctx = *_ctx;
+ const struct config_module_parser *parser;
+ const char *error;
+ unsigned int i;
+ int ret = 0;
+
+ *_ctx = NULL;
+
+ if (ctx->failed) {
+ config_export_free(ctx);
+ return -1;
+ }
+
+ for (i = 0; ctx->parsers[i].root != NULL; i++) {
+ parser = &ctx->parsers[i];
+ if (!config_module_want_parser(config_module_parsers,
+ ctx->modules, parser->root))
+ continue;
+
+ T_BEGIN {
+ enum setting_type stype;
+ const char *const *value = settings_parse_get_value(parser->parser, "ssl", &stype);
+
+ if ((ctx->flags & CONFIG_DUMP_FLAG_IN_SECTION) == 0 &&
+ value != NULL && strcmp(*value, "no") != 0 &&
+ settings_parse_is_valid_key(parser->parser, "ssl_dh")) {
+ value = settings_parse_get_value(parser->parser,
+ "ssl_dh", &stype);
+
+ if (value == NULL || **value == '\0') {
+ const char *newval;
+ if (old_settings_ssl_dh_load(&newval, &error)) {
+ if (newval != NULL)
+ settings_parse_line(parser->parser, t_strdup_printf("%s=%s", "ssl_dh", newval));
+ } else {
+ i_error("%s", error);
+ ret = -1;
+ }
+ }
+ }
+ settings_export(ctx, parser->root, FALSE,
+ settings_parser_get(parser->parser),
+ settings_parser_get_changes(parser->parser));
+ } T_END;
+
+ if ((ctx->flags & CONFIG_DUMP_FLAG_CHECK_SETTINGS) != 0) {
+ settings_parse_var_skip(parser->parser);
+ if (!settings_parser_check(parser->parser, ctx->pool,
+ &error)) {
+ if ((ctx->flags & CONFIG_DUMP_FLAG_CALLBACK_ERRORS) != 0) {
+ ctx->callback(NULL, error, CONFIG_KEY_ERROR,
+ ctx->context);
+ } else {
+ i_error("%s", error);
+ ret = -1;
+ break;
+ }
+ }
+ }
+ }
+ config_export_free(ctx);
+ return ret;
+}
diff --git a/src/config/config-request.h b/src/config/config-request.h
new file mode 100644
index 0000000..c91e836
--- /dev/null
+++ b/src/config/config-request.h
@@ -0,0 +1,61 @@
+#ifndef CONFIG_REQUEST_H
+#define CONFIG_REQUEST_H
+
+#include "settings-parser.h"
+#include "config-filter.h"
+
+struct master_service_settings_output;
+
+enum config_dump_scope {
+ /* Dump all settings, including hidden settings */
+ CONFIG_DUMP_SCOPE_ALL_WITH_HIDDEN,
+ /* Dump all non-hidden settings */
+ CONFIG_DUMP_SCOPE_ALL_WITHOUT_HIDDEN,
+ /* Dump all that have explicitly been set */
+ CONFIG_DUMP_SCOPE_SET,
+ /* Dump only settings that differ from defaults */
+ CONFIG_DUMP_SCOPE_CHANGED
+};
+
+enum config_dump_flags {
+ CONFIG_DUMP_FLAG_CHECK_SETTINGS = 0x01,
+ CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS = 0x02,
+ /* Errors are reported using callback and they don't stop handling */
+ CONFIG_DUMP_FLAG_CALLBACK_ERRORS = 0x04,
+ /* Set if dumping a section and not top level config */
+ CONFIG_DUMP_FLAG_IN_SECTION = 0x08,
+};
+
+enum config_key_type {
+ CONFIG_KEY_NORMAL,
+ CONFIG_KEY_LIST,
+ CONFIG_KEY_UNIQUE_KEY,
+ /* error message is in value */
+ CONFIG_KEY_ERROR
+};
+
+typedef void config_request_callback_t(const char *key, const char *value,
+ enum config_key_type type, void *context);
+
+bool config_export_type(string_t *str, const void *value,
+ const void *default_value,
+ enum setting_type type, bool dump_default,
+ bool *dump_r) ATTR_NULL(3);
+struct config_export_context *
+config_export_init(const char *const *modules,
+ const char *const *exclude_settings,
+ enum config_dump_scope scope,
+ enum config_dump_flags flags,
+ config_request_callback_t *callback, void *context)
+ ATTR_NULL(1, 5);
+void config_export_by_filter(struct config_export_context *ctx,
+ const struct config_filter *filter);
+void config_export_parsers(struct config_export_context *ctx,
+ const struct config_module_parser *parsers);
+void config_export_get_output(struct config_export_context *ctx,
+ struct master_service_settings_output *output_r);
+const char *
+config_export_get_import_environment(struct config_export_context *ctx);
+int config_export_finish(struct config_export_context **ctx);
+
+#endif
diff --git a/src/config/config-settings.c b/src/config/config-settings.c
new file mode 100644
index 0000000..d19fe4e
--- /dev/null
+++ b/src/config/config-settings.c
@@ -0,0 +1,46 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "settings-parser.h"
+#include "service-settings.h"
+
+#include <stddef.h>
+
+/* <settings checks> */
+static struct file_listener_settings config_unix_listeners_array[] = {
+ { "config", 0600, "", "" }
+};
+static struct file_listener_settings *config_unix_listeners[] = {
+ &config_unix_listeners_array[0]
+};
+static buffer_t config_unix_listeners_buf = {
+ { { config_unix_listeners, sizeof(config_unix_listeners) } }
+};
+/* </settings checks> */
+
+struct service_settings config_service_settings = {
+ .name = "config",
+ .protocol = "",
+ .type = "config",
+ .executable = "config",
+ .user = "",
+ .group = "",
+ .privileged_group = "",
+ .extra_groups = "",
+ .chroot = "",
+
+ .drop_priv_before_exec = FALSE,
+
+ .process_min_avail = 0,
+ .process_limit = 0,
+ .client_limit = 0,
+ .service_count = 0,
+ .idle_kill = UINT_MAX,
+ .vsz_limit = UOFF_T_MAX,
+
+ .unix_listeners = { { &config_unix_listeners_buf,
+ sizeof(config_unix_listeners[0]) } },
+ .fifo_listeners = ARRAY_INIT,
+ .inet_listeners = ARRAY_INIT
+};
diff --git a/src/config/doveconf.c b/src/config/doveconf.c
new file mode 100644
index 0000000..79ea9e8
--- /dev/null
+++ b/src/config/doveconf.c
@@ -0,0 +1,1072 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "path-util.h"
+#include "module-dir.h"
+#include "env-util.h"
+#include "guid.h"
+#include "hash.h"
+#include "hostpid.h"
+#include "ostream.h"
+#include "str.h"
+#include "strescape.h"
+#include "settings-parser.h"
+#include "master-interface.h"
+#include "master-service.h"
+#include "all-settings.h"
+#include "sysinfo-get.h"
+#include "old-set-parser.h"
+#include "config-connection.h"
+#include "config-parser.h"
+#include "config-request.h"
+#include "dovecot-version.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+struct prefix_stack {
+ unsigned int prefix_idx;
+ unsigned int str_pos;
+};
+ARRAY_DEFINE_TYPE(prefix_stack, struct prefix_stack);
+
+struct config_dump_human_context {
+ pool_t pool;
+ string_t *list_prefix;
+ ARRAY_TYPE(const_string) strings;
+ ARRAY_TYPE(const_string) errors;
+ struct config_export_context *export_ctx;
+
+ bool list_prefix_sent:1;
+};
+
+#define LIST_KEY_PREFIX "\001"
+#define UNIQUE_KEY_SUFFIX "\xff"
+
+static const char *indent_str = " !!!!";
+
+static const char *const secrets[] = {
+ "key",
+ "secret",
+ "pass",
+ "http://",
+ "https://",
+ "ftp://",
+ NULL
+};
+
+
+static void
+config_request_get_strings(const char *key, const char *value,
+ enum config_key_type type, void *context)
+{
+ struct config_dump_human_context *ctx = context;
+ const char *p;
+
+ switch (type) {
+ case CONFIG_KEY_NORMAL:
+ value = p_strdup_printf(ctx->pool, "%s=%s", key, value);
+ break;
+ case CONFIG_KEY_LIST:
+ value = p_strdup_printf(ctx->pool, LIST_KEY_PREFIX"%s=%s",
+ key, value);
+ break;
+ case CONFIG_KEY_UNIQUE_KEY:
+ p = strrchr(key, '/');
+ i_assert(p != NULL);
+ value = p_strdup_printf(ctx->pool, "%.*s/"UNIQUE_KEY_SUFFIX"%s=%s",
+ (int)(p - key), key, p + 1, value);
+ break;
+ case CONFIG_KEY_ERROR:
+ value = p_strdup(ctx->pool, value);
+ array_push_back(&ctx->errors, &value);
+ return;
+ }
+ array_push_back(&ctx->strings, &value);
+}
+
+static int config_string_cmp(const char *const *p1, const char *const *p2)
+{
+ const char *s1 = *p1, *s2 = *p2;
+ unsigned int i = 0;
+
+ while (s1[i] == s2[i]) {
+ if (s1[i] == '\0' || s1[i] == '=')
+ return 0;
+ i++;
+ }
+
+ if (s1[i] == '=')
+ return -1;
+ if (s2[i] == '=')
+ return 1;
+
+ return s1[i] - s2[i];
+}
+
+static struct prefix_stack prefix_stack_pop(ARRAY_TYPE(prefix_stack) *stack)
+{
+ const struct prefix_stack *s;
+ struct prefix_stack sc;
+ unsigned int count;
+
+ s = array_get(stack, &count);
+ i_assert(count > 0);
+ if (count == 1) {
+ sc.prefix_idx = UINT_MAX;
+ } else {
+ sc.prefix_idx = s[count-2].prefix_idx;
+ }
+ sc.str_pos = s[count-1].str_pos;
+ array_delete(stack, count-1, 1);
+ return sc;
+}
+
+static void prefix_stack_reset_str(ARRAY_TYPE(prefix_stack) *stack)
+{
+ struct prefix_stack *s;
+
+ array_foreach_modifiable(stack, s)
+ s->str_pos = UINT_MAX;
+}
+
+static struct config_dump_human_context *
+config_dump_human_init(const char *const *modules, enum config_dump_scope scope,
+ bool check_settings, bool in_section)
+{
+ struct config_dump_human_context *ctx;
+ enum config_dump_flags flags;
+ pool_t pool;
+
+ pool = pool_alloconly_create(MEMPOOL_GROWING"config human strings", 1024*32);
+ ctx = p_new(pool, struct config_dump_human_context, 1);
+ ctx->pool = pool;
+ ctx->list_prefix = str_new(ctx->pool, 128);
+ i_array_init(&ctx->strings, 256);
+ i_array_init(&ctx->errors, 256);
+
+ flags = CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS |
+ CONFIG_DUMP_FLAG_CALLBACK_ERRORS;
+ if (check_settings)
+ flags |= CONFIG_DUMP_FLAG_CHECK_SETTINGS;
+ if (in_section)
+ flags |= CONFIG_DUMP_FLAG_IN_SECTION;
+ ctx->export_ctx = config_export_init(modules, NULL, scope, flags,
+ config_request_get_strings, ctx);
+ return ctx;
+}
+
+static void config_dump_human_deinit(struct config_dump_human_context *ctx)
+{
+ array_free(&ctx->strings);
+ array_free(&ctx->errors);
+ pool_unref(&ctx->pool);
+}
+
+static bool value_need_quote(const char *value)
+{
+ size_t len = strlen(value);
+
+ if (len == 0)
+ return FALSE;
+
+ if (strchr(value, '#') != NULL)
+ return TRUE;
+ if (IS_WHITE(value[0]) || IS_WHITE(value[len-1]))
+ return TRUE;
+ return FALSE;
+}
+
+static const char *find_next_secret(const char *input, const char **secret_r)
+{
+ const char *const *secret;
+ const char *ptr = NULL;
+ *secret_r = NULL;
+ for(secret = secrets; *secret != NULL; secret++) {
+ const char *cptr;
+ if ((cptr = strstr(input, *secret)) != NULL) {
+ if (ptr == NULL || cptr < ptr) {
+ *secret_r = *secret;
+ ptr = cptr;
+ }
+ }
+ }
+ i_assert(*secret_r != NULL || ptr == NULL);
+ return ptr;
+}
+
+static bool
+hide_url_userpart_from_value(struct ostream *output, const char **_ptr,
+ const char **optr, bool quote)
+{
+ const char *ptr = *_ptr;
+ const char *start_of_user = ptr;
+ const char *start_of_host = NULL;
+ string_t *quoted = NULL;
+
+ if (quote)
+ quoted = t_str_new(256);
+
+ /* it's a URL, see if there is a userpart */
+ while(*ptr != '\0' && !i_isspace(*ptr) && *ptr != '/') {
+ if (*ptr == '@') {
+ start_of_host = ptr;
+ break;
+ }
+ ptr++;
+ }
+
+ if (quote) {
+ str_truncate(quoted, 0);
+ str_append_escaped(quoted, *optr, start_of_user - (*optr));
+ o_stream_nsend(output, quoted->data, quoted->used);
+ } else {
+ o_stream_nsend(output, *optr, start_of_user - (*optr));
+ }
+
+ if (start_of_host != NULL && start_of_host != start_of_user) {
+ o_stream_nsend_str(output, "#hidden_use-P_to_show#");
+ } else if (quote) {
+ str_truncate(quoted, 0);
+ str_append_escaped(quoted, start_of_user, ptr - start_of_user);
+ o_stream_nsend(output, quoted->data, quoted->used);
+ } else {
+ o_stream_nsend(output, start_of_user, ptr - start_of_user);
+ }
+
+ *optr = ptr;
+ *_ptr = ptr;
+ return TRUE;
+}
+
+static inline bool key_ends_with(const char *key, const char *eptr,
+ const char *suffix)
+{
+ /* take = into account */
+ size_t n = strlen(suffix)+1;
+ return (eptr-key > (ptrdiff_t)n && str_begins(eptr-n, suffix));
+}
+
+static bool
+hide_secrets_from_value(struct ostream *output, const char *key,
+ const char *value)
+{
+ bool ret = FALSE, quote = value_need_quote(value);
+ const char *ptr, *optr, *secret;
+ if (*value != '\0' &&
+ (key_ends_with(key, value, "_password") ||
+ key_ends_with(key, value, "_key") ||
+ key_ends_with(key, value, "_nonce") ||
+ str_begins(key, "ssl_dh"))) {
+ o_stream_nsend_str(output, "# hidden, use -P to show it");
+ return TRUE;
+ }
+
+ /* Check if we can find anything that has prefix of any of the
+ secrets. It should match things like secret_api_key or pass or password,
+ etc. but not something like nonsecret. */
+ optr = ptr = value;
+ while((ptr = find_next_secret(ptr, &secret)) != NULL) {
+ if (strstr(secret, "://") != NULL) {
+ ptr += strlen(secret);
+ if ((ret = hide_url_userpart_from_value(output, &ptr, &optr, quote)))
+ continue;
+ }
+ /* we have found something that we hide, and will deal with output
+ here. */
+ ret = TRUE;
+ if (ptr == value ||
+ (ptr > value && !i_isalnum(ptr[-1]))) {
+ size_t len;
+ while(*ptr != '\0') {
+ if (*ptr == '=' || i_isspace(*ptr))
+ break;
+ ptr++;
+ }
+ while(i_isspace(*ptr))
+ ptr++;
+ len = (size_t)(ptr-optr);
+ if (quote) {
+ string_t *quoted = t_str_new(len*2);
+ str_append_escaped(quoted, optr, len);
+ o_stream_nsend(output,
+ quoted->data, quoted->used);
+ } else {
+ o_stream_nsend(output, optr, len);
+ }
+ if (*ptr == '=') {
+ o_stream_nsend(output, ptr, 1);
+ o_stream_nsend_str(output, "#hidden_use-P_to_show#");
+ while(*ptr != '\0' && !i_isspace(*ptr) &&
+ *ptr != ';' && *ptr != ':')
+ ptr++;
+ }
+ optr = ptr;
+ } else {
+ /* "secret" is prefixed with alphanumeric character,
+ e.g. "nopassword". So it's not really a secret.
+ Skip forward to avoid infinite loop. */
+ ptr++;
+ }
+ };
+ /* if we are dealing with output, send rest here */
+ if (ret) {
+ if (quote)
+ o_stream_nsend_str(output, str_escape(optr));
+ else
+ o_stream_nsend_str(output, optr);
+ }
+ return ret;
+}
+
+static int ATTR_NULL(4)
+config_dump_human_output(struct config_dump_human_context *ctx,
+ struct ostream *output, unsigned int indent,
+ const char *setting_name_filter, bool hide_passwords)
+{
+ ARRAY_TYPE(const_string) prefixes_arr;
+ ARRAY_TYPE(prefix_stack) prefix_stack;
+ struct prefix_stack prefix;
+ const char *const *strings, *const *args, *p, *str, *const *prefixes;
+ const char *key, *key2, *value;
+ unsigned int i, j, count, prefix_count;
+ unsigned int prefix_idx = UINT_MAX;
+ size_t len, skip_len, setting_name_filter_len;
+ bool unique_key;
+ int ret = 0;
+
+ setting_name_filter_len = setting_name_filter == NULL ? 0 :
+ strlen(setting_name_filter);
+ if (config_export_finish(&ctx->export_ctx) < 0)
+ return -1;
+
+ array_sort(&ctx->strings, config_string_cmp);
+ strings = array_get(&ctx->strings, &count);
+
+ /* strings are sorted so that all lists come first */
+ p_array_init(&prefixes_arr, ctx->pool, 32);
+ for (i = 0; i < count && strings[i][0] == LIST_KEY_PREFIX[0]; i++) T_BEGIN {
+ p = strchr(strings[i], '=');
+ i_assert(p != NULL);
+ if (p[1] == '\0') {
+ /* "strlist=" */
+ str = p_strdup_printf(ctx->pool, "%s/",
+ t_strcut(strings[i]+1, '='));
+ array_push_back(&prefixes_arr, &str);
+ } else {
+ /* string is in format: "list=0 1 2" */
+ for (args = t_strsplit(p + 1, " "); *args != NULL; args++) {
+ str = p_strdup_printf(ctx->pool, "%s/%s/",
+ t_strcut(strings[i]+1, '='),
+ *args);
+ array_push_back(&prefixes_arr, &str);
+ }
+ }
+ } T_END;
+ prefixes = array_get(&prefixes_arr, &prefix_count);
+
+ p_array_init(&prefix_stack, ctx->pool, 8);
+ for (; i < count; i++) T_BEGIN {
+ value = strchr(strings[i], '=');
+ i_assert(value != NULL);
+
+ key = t_strdup_until(strings[i], value++);
+ unique_key = FALSE;
+
+ p = strrchr(key, '/');
+ if (p != NULL && p[1] == UNIQUE_KEY_SUFFIX[0]) {
+ key = t_strconcat(t_strdup_until(key, p + 1),
+ p + 2, NULL);
+ unique_key = TRUE;
+ }
+ if (setting_name_filter_len > 0) {
+ /* see if this setting matches the name filter */
+ if (!(strncmp(setting_name_filter, key,
+ setting_name_filter_len) == 0 &&
+ (key[setting_name_filter_len] == '/' ||
+ key[setting_name_filter_len] == '\0')))
+ goto end;
+ }
+ again:
+ j = 0;
+ /* if there are open sections and this key isn't in it,
+ close the sections */
+ while (prefix_idx != UINT_MAX) {
+ len = strlen(prefixes[prefix_idx]);
+ if (strncmp(prefixes[prefix_idx], key, len) != 0) {
+ prefix = prefix_stack_pop(&prefix_stack);
+ indent--;
+ if (prefix.str_pos != UINT_MAX)
+ str_truncate(ctx->list_prefix, prefix.str_pos);
+ else {
+ o_stream_nsend(output, indent_str, indent*2);
+ o_stream_nsend_str(output, "}\n");
+ }
+ prefix_idx = prefix.prefix_idx;
+ } else {
+ /* keep the prefix */
+ j = prefix_idx + 1;
+ break;
+ }
+ }
+ /* see if this key is in some section */
+ for (; j < prefix_count; j++) {
+ len = strlen(prefixes[j]);
+ if (strncmp(prefixes[j], key, len) == 0) {
+ key2 = key + (prefix_idx == UINT_MAX ? 0 :
+ strlen(prefixes[prefix_idx]));
+ prefix.str_pos = !unique_key ? UINT_MAX :
+ str_len(ctx->list_prefix);
+ prefix_idx = j;
+ prefix.prefix_idx = prefix_idx;
+ array_push_back(&prefix_stack, &prefix);
+
+ str_append_max(ctx->list_prefix, indent_str, indent*2);
+ p = strchr(key2, '/');
+ if (p != NULL)
+ str_append_data(ctx->list_prefix, key2, p - key2);
+ else
+ str_append(ctx->list_prefix, key2);
+ if (unique_key && *value != '\0') {
+ if (strchr(value, ' ') == NULL)
+ str_printfa(ctx->list_prefix, " %s", value);
+ else
+ str_printfa(ctx->list_prefix, " \"%s\"", str_escape(value));
+ }
+ str_append(ctx->list_prefix, " {\n");
+ indent++;
+
+ if (unique_key)
+ goto end;
+ else
+ goto again;
+ }
+ }
+ o_stream_nsend(output, str_data(ctx->list_prefix), str_len(ctx->list_prefix));
+ str_truncate(ctx->list_prefix, 0);
+ prefix_stack_reset_str(&prefix_stack);
+ ctx->list_prefix_sent = TRUE;
+
+ skip_len = prefix_idx == UINT_MAX ? 0 : strlen(prefixes[prefix_idx]);
+ i_assert(skip_len == 0 ||
+ strncmp(prefixes[prefix_idx], strings[i], skip_len) == 0);
+ o_stream_nsend(output, indent_str, indent*2);
+ key = strings[i] + skip_len;
+ if (unique_key) key++;
+ value = strchr(key, '=');
+ i_assert(value != NULL);
+ o_stream_nsend(output, key, value-key);
+ o_stream_nsend_str(output, " = ");
+ if (hide_passwords &&
+ hide_secrets_from_value(output, key, value+1))
+ /* sent */
+ ;
+ else if (!value_need_quote(value+1))
+ o_stream_nsend_str(output, value+1);
+ else {
+ o_stream_nsend(output, "\"", 1);
+ o_stream_nsend_str(output, str_escape(value+1));
+ o_stream_nsend(output, "\"", 1);
+ }
+ o_stream_nsend(output, "\n", 1);
+ end: ;
+ } T_END;
+
+ while (prefix_idx != UINT_MAX) {
+ prefix = prefix_stack_pop(&prefix_stack);
+ if (prefix.str_pos != UINT_MAX)
+ break;
+ prefix_idx = prefix.prefix_idx;
+ indent--;
+ o_stream_nsend(output, indent_str, indent*2);
+ o_stream_nsend_str(output, "}\n");
+ }
+
+ /* flush output before writing errors */
+ o_stream_uncork(output);
+ array_foreach_elem(&ctx->errors, str) {
+ i_error("%s", str);
+ ret = -1;
+ }
+ return ret;
+}
+
+static unsigned int
+config_dump_filter_begin(string_t *str,
+ const struct config_filter *filter)
+{
+ unsigned int indent = 0;
+
+ if (filter->local_bits > 0) {
+ str_printfa(str, "local %s", net_ip2addr(&filter->local_net));
+
+ if (IPADDR_IS_V4(&filter->local_net)) {
+ if (filter->local_bits != 32)
+ str_printfa(str, "/%u", filter->local_bits);
+ } else {
+ if (filter->local_bits != 128)
+ str_printfa(str, "/%u", filter->local_bits);
+ }
+ str_append(str, " {\n");
+ indent++;
+ }
+
+ if (filter->local_name != NULL) {
+ str_append_max(str, indent_str, indent*2);
+ str_printfa(str, "local_name %s {\n", filter->local_name);
+ indent++;
+ }
+
+ if (filter->remote_bits > 0) {
+ str_append_max(str, indent_str, indent*2);
+ str_printfa(str, "remote %s", net_ip2addr(&filter->remote_net));
+
+ if (IPADDR_IS_V4(&filter->remote_net)) {
+ if (filter->remote_bits != 32)
+ str_printfa(str, "/%u", filter->remote_bits);
+ } else {
+ if (filter->remote_bits != 128)
+ str_printfa(str, "/%u", filter->remote_bits);
+ }
+ str_append(str, " {\n");
+ indent++;
+ }
+ if (filter->service != NULL) {
+ str_append_max(str, indent_str, indent*2);
+ str_printfa(str, "protocol %s {\n", filter->service);
+ indent++;
+ }
+ return indent;
+}
+
+static void
+config_dump_filter_end(struct ostream *output, unsigned int indent)
+{
+ while (indent > 0) {
+ indent--;
+ o_stream_nsend(output, indent_str, indent*2);
+ o_stream_nsend(output, "}\n", 2);
+ }
+}
+
+static int
+config_dump_human_sections(struct ostream *output,
+ const struct config_filter *filter,
+ const char *const *modules, bool hide_passwords)
+{
+ struct config_filter_parser *const *filters;
+ static struct config_dump_human_context *ctx;
+ unsigned int indent;
+ int ret = 0;
+
+ filters = config_filter_find_subset(config_filter, filter);
+
+ /* first filter should be the global one */
+ i_assert(filters[0] != NULL && filters[0]->filter.service == NULL);
+ filters++;
+
+ for (; *filters != NULL; filters++) {
+ ctx = config_dump_human_init(modules, CONFIG_DUMP_SCOPE_SET,
+ FALSE, TRUE);
+ indent = config_dump_filter_begin(ctx->list_prefix,
+ &(*filters)->filter);
+ config_export_parsers(ctx->export_ctx, (*filters)->parsers);
+ if (config_dump_human_output(ctx, output, indent, NULL, hide_passwords) < 0)
+ ret = -1;
+ if (ctx->list_prefix_sent)
+ config_dump_filter_end(output, indent);
+ config_dump_human_deinit(ctx);
+ }
+ return ret;
+}
+
+static int ATTR_NULL(4)
+config_dump_human(const struct config_filter *filter, const char *const *modules,
+ enum config_dump_scope scope, const char *setting_name_filter,
+ bool hide_passwords)
+{
+ static struct config_dump_human_context *ctx;
+ struct ostream *output;
+ int ret;
+
+ output = o_stream_create_fd(STDOUT_FILENO, 0);
+ o_stream_set_no_error_handling(output, TRUE);
+ o_stream_cork(output);
+
+ ctx = config_dump_human_init(modules, scope, TRUE, FALSE);
+ config_export_by_filter(ctx->export_ctx, filter);
+ ret = config_dump_human_output(ctx, output, 0, setting_name_filter, hide_passwords);
+ config_dump_human_deinit(ctx);
+
+ if (setting_name_filter == NULL)
+ ret = config_dump_human_sections(output, filter, modules, hide_passwords);
+
+ o_stream_uncork(output);
+ o_stream_destroy(&output);
+ return ret;
+}
+
+static int
+config_dump_one(const struct config_filter *filter, bool hide_key,
+ enum config_dump_scope scope, const char *setting_name_filter,
+ bool hide_passwords)
+{
+ static struct config_dump_human_context *ctx;
+ const char *str;
+ size_t len;
+ bool dump_section = FALSE;
+
+ ctx = config_dump_human_init(NULL, scope, FALSE, FALSE);
+ config_export_by_filter(ctx->export_ctx, filter);
+ if (config_export_finish(&ctx->export_ctx) < 0)
+ return -1;
+
+ len = strlen(setting_name_filter);
+ array_foreach_elem(&ctx->strings, str) {
+ if (strncmp(str, setting_name_filter, len) != 0)
+ continue;
+
+ if (str[len] == '=') {
+ if (hide_key)
+ printf("%s\n", str + len+1);
+ else {
+ printf("%s = %s\n", setting_name_filter,
+ str + len+1);
+ }
+ dump_section = FALSE;
+ break;
+ } else if (str[len] == '/') {
+ dump_section = TRUE;
+ }
+ }
+ config_dump_human_deinit(ctx);
+
+ if (dump_section)
+ (void)config_dump_human(filter, NULL, scope, setting_name_filter, hide_passwords);
+ return 0;
+}
+
+static void config_request_simple_stdout(const char *key, const char *value,
+ enum config_key_type type ATTR_UNUSED,
+ void *context)
+{
+ char **setting_name_filters = context;
+ unsigned int i;
+ size_t filter_len;
+
+ if (setting_name_filters == NULL) {
+ printf("%s=%s\n", key, value);
+ return;
+ }
+
+ for (i = 0; setting_name_filters[i] != NULL; i++) {
+ filter_len = strlen(setting_name_filters[i]);
+ if (strncmp(setting_name_filters[i], key, filter_len) == 0 &&
+ (key[filter_len] == '\0' || key[filter_len] == '/'))
+ printf("%s=%s\n", key, value);
+ }
+}
+
+static void config_request_putenv(const char *key, const char *value,
+ enum config_key_type type ATTR_UNUSED,
+ void *context ATTR_UNUSED)
+{
+ T_BEGIN {
+ env_put(t_str_ucase(key), value);
+ } T_END;
+}
+
+static const char *get_setting(const char *module, const char *name)
+{
+ struct config_module_parser *l;
+ const struct setting_define *def;
+ const char *const *value;
+ const void *set;
+
+ for (l = config_module_parsers; l->root != NULL; l++) {
+ if (strcmp(l->root->module_name, module) != 0)
+ continue;
+
+ set = settings_parser_get(l->parser);
+ for (def = l->root->defines; def->key != NULL; def++) {
+ if (strcmp(def->key, name) == 0) {
+ value = CONST_PTR_OFFSET(set, def->offset);
+ return *value;
+ }
+ }
+ }
+ return "";
+}
+
+static void filter_parse_arg(struct config_filter *filter, const char *arg)
+{
+ const char *key, *value, *error;
+
+ value = strchr(arg, '=');
+ if (value != NULL)
+ key = t_strdup_until(arg, value++);
+ else {
+ key = arg;
+ value = "";
+ }
+
+ if (strcmp(key, "service") == 0)
+ filter->service = value;
+ else if (strcmp(key, "protocol") == 0)
+ filter->service = value;
+ else if (strcmp(key, "lname") == 0)
+ filter->local_name = value;
+ else if (strcmp(key, "local") == 0) {
+ if (config_parse_net(value, &filter->local_net,
+ &filter->local_bits, &error) < 0)
+ i_fatal("local filter: %s", error);
+ } else if (strcmp(key, "remote") == 0) {
+ if (config_parse_net(value, &filter->remote_net,
+ &filter->remote_bits, &error) < 0)
+ i_fatal("remote filter: %s", error);
+ } else {
+ i_fatal("Unknown filter argument: %s", arg);
+ }
+}
+
+struct hostname_format {
+ const char *prefix, *suffix;
+ unsigned int numcount;
+ bool zeropadding;
+};
+
+static void
+hostname_format_write(string_t *str, const struct hostname_format *fmt,
+ unsigned int num)
+{
+ str_truncate(str, 0);
+ str_append(str, fmt->prefix);
+ if (!fmt->zeropadding)
+ str_printfa(str, "%d", num);
+ else
+ str_printfa(str, "%0*d", fmt->numcount, num);
+ str_append(str, fmt->suffix);
+}
+
+static void hostname_verify_format(const char *arg)
+{
+ struct hostname_format fmt;
+ const char *p;
+ unsigned char hash[GUID_128_HOST_HASH_SIZE];
+ unsigned int n, limit;
+ HASH_TABLE(void *, void *) hosts;
+ void *key, *value;
+ string_t *host;
+ const char *host2;
+ bool duplicates = FALSE;
+
+ i_zero(&fmt);
+ if (arg != NULL) {
+ /* host%d, host%2d, host%02d */
+ p = strchr(arg, '%');
+ if (p == NULL)
+ i_fatal("Host parameter missing %%d");
+ fmt.prefix = t_strdup_until(arg, p++);
+ if (*p == '0') {
+ fmt.zeropadding = TRUE;
+ p++;
+ }
+ if (!i_isdigit(*p))
+ fmt.numcount = 1;
+ else
+ fmt.numcount = *p++ - '0';
+ if (*p++ != 'd')
+ i_fatal("Host parameter missing %%d");
+ fmt.suffix = p;
+ } else {
+ /* detect host1[suffix] vs host01[suffix] */
+ size_t len = strlen(my_hostname);
+ while (len > 0 && !i_isdigit(my_hostname[len-1]))
+ len--;
+ fmt.suffix = my_hostname + len;
+ fmt.numcount = 0;
+ while (len > 0 && i_isdigit(my_hostname[len-1])) {
+ len--;
+ fmt.numcount++;
+ }
+ if (my_hostname[len] == '0')
+ fmt.zeropadding = TRUE;
+ fmt.prefix = t_strndup(my_hostname, len);
+ if (fmt.numcount == 0) {
+ i_fatal("Hostname '%s' has no digits, can't verify",
+ my_hostname);
+ }
+ }
+ for (n = 0, limit = 1; n < fmt.numcount; n++)
+ limit *= 10;
+ host = t_str_new(128);
+ hash_table_create_direct(&hosts, default_pool, limit);
+ for (n = 0; n < limit; n++) {
+ hostname_format_write(host, &fmt, n);
+
+ guid_128_host_hash_get(str_c(host), hash);
+ i_assert(sizeof(key) >= sizeof(hash));
+ key = NULL; memcpy(&key, hash, sizeof(hash));
+
+ value = hash_table_lookup(hosts, key);
+ if (value != NULL) {
+ host2 = t_strdup(str_c(host));
+ hostname_format_write(host, &fmt,
+ POINTER_CAST_TO(value, unsigned int)-1);
+ i_error("Duplicate host hashes: %s and %s",
+ str_c(host), host2);
+ duplicates = TRUE;
+ } else {
+ hash_table_insert(hosts, key, POINTER_CAST(n+1));
+ }
+ }
+ hash_table_destroy(&hosts);
+
+ if (duplicates)
+ lib_exit(EX_CONFIG);
+ else {
+ host2 = t_strdup(str_c(host));
+ hostname_format_write(host, &fmt, 0);
+ printf("No duplicate host hashes in %s .. %s\n",
+ str_c(host), host2);
+ lib_exit(0);
+ }
+}
+
+static void check_wrong_config(const char *config_path)
+{
+ const char *base_dir, *symlink_path, *prev_path, *error;
+
+ base_dir = get_setting("master", "base_dir");
+ symlink_path = t_strconcat(base_dir, "/"PACKAGE".conf", NULL);
+ if (t_readlink(symlink_path, &prev_path, &error) < 0) {
+ if (errno != ENOENT)
+ i_error("t_readlink(%s) failed: %s", symlink_path, error);
+ return;
+ }
+
+ if (strcmp(prev_path, config_path) != 0) {
+ i_warning("Dovecot was last started using %s, "
+ "but this config is %s", prev_path, config_path);
+ }
+}
+
+static void failure_exit_callback(int *status)
+{
+ /* don't use EX_CONFIG, because it often causes MTAs to bounce
+ the mails back. */
+ *status = EX_TEMPFAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ enum master_service_flags master_service_flags =
+ MASTER_SERVICE_FLAG_DONT_SEND_STATS |
+ MASTER_SERVICE_FLAG_STANDALONE |
+ MASTER_SERVICE_FLAG_NO_INIT_DATASTACK_FRAME;
+ enum config_dump_scope scope = CONFIG_DUMP_SCOPE_ALL_WITHOUT_HIDDEN;
+ const char *orig_config_path, *config_path, *module;
+ ARRAY(const char *) module_names;
+ struct config_filter filter;
+ const char *const *wanted_modules, *error;
+ char **exec_args = NULL, **setting_name_filters = NULL;
+ unsigned int i;
+ int c, ret, ret2;
+ bool config_path_specified, expand_vars = FALSE, hide_key = FALSE;
+ bool parse_full_config = FALSE, simple_output = FALSE;
+ bool dump_defaults = FALSE, host_verify = FALSE;
+ bool print_plugin_banner = FALSE, hide_passwords = TRUE;
+
+ if (getenv("USE_SYSEXITS") != NULL) {
+ /* we're coming from (e.g.) LDA */
+ i_set_failure_exit_callback(failure_exit_callback);
+ }
+
+ i_zero(&filter);
+ master_service = master_service_init("config", master_service_flags,
+ &argc, &argv, "adf:hHm:nNpPexsS");
+ orig_config_path = t_strdup(master_service_get_config_path(master_service));
+
+ i_set_failure_prefix("doveconf: ");
+ t_array_init(&module_names, 4);
+ while ((c = master_getopt(master_service)) > 0) {
+ if (c == 'e') {
+ expand_vars = TRUE;
+ break;
+ }
+ switch (c) {
+ case 'a':
+ break;
+ case 'd':
+ dump_defaults = TRUE;
+ break;
+ case 'f':
+ filter_parse_arg(&filter, optarg);
+ break;
+ case 'h':
+ hide_key = TRUE;
+ break;
+ case 'H':
+ host_verify = TRUE;
+ break;
+ case 'm':
+ module = t_strdup(optarg);
+ array_push_back(&module_names, &module);
+ break;
+ case 'n':
+ scope = CONFIG_DUMP_SCOPE_CHANGED;
+ break;
+ case 'N':
+ scope = CONFIG_DUMP_SCOPE_SET;
+ break;
+ case 'p':
+ parse_full_config = TRUE;
+ break;
+ case 'P':
+ hide_passwords = FALSE;
+ break;
+ case 's':
+ scope = CONFIG_DUMP_SCOPE_ALL_WITH_HIDDEN;
+ break;
+ case 'S':
+ simple_output = TRUE;
+ break;
+ case 'x':
+ expand_vars = TRUE;
+ break;
+ default:
+ return FATAL_DEFAULT;
+ }
+ }
+ array_append_zero(&module_names);
+ wanted_modules = array_count(&module_names) == 1 ? NULL :
+ array_front(&module_names);
+
+ config_path = master_service_get_config_path(master_service);
+ /* use strcmp() instead of !=, because dovecot -n always gives us
+ -c parameter */
+ config_path_specified = strcmp(config_path, orig_config_path) != 0;
+
+ if (host_verify)
+ hostname_verify_format(argv[optind]);
+
+ if (c == 'e') {
+ if (argv[optind] == NULL)
+ i_fatal("Missing command for -e");
+ exec_args = &argv[optind];
+ } else if (argv[optind] != NULL) {
+ /* print only a single config setting */
+ setting_name_filters = argv+optind;
+ if (scope == CONFIG_DUMP_SCOPE_ALL_WITHOUT_HIDDEN)
+ scope = CONFIG_DUMP_SCOPE_ALL_WITH_HIDDEN;
+ } else if (!simple_output) {
+ /* print the config file path before parsing it, so in case
+ of errors it's still shown */
+ printf("# "DOVECOT_VERSION_FULL": %s\n", config_path);
+ print_plugin_banner = TRUE;
+ fflush(stdout);
+ }
+ master_service_init_finish(master_service);
+ config_parse_load_modules();
+
+ if (print_plugin_banner) {
+ struct module *m;
+
+ for (m = modules; m != NULL; m = m->next) {
+ const char **str = module_get_symbol_quiet(m,
+ t_strdup_printf("%s_doveconf_banner", m->name));
+ if (str != NULL)
+ printf("# %s\n", *str);
+ }
+ }
+
+ if ((ret = config_parse_file(dump_defaults ? NULL : config_path,
+ expand_vars,
+ parse_full_config ? NULL : wanted_modules,
+ &error)) == 0 &&
+ access(EXAMPLE_CONFIG_DIR, X_OK) == 0) {
+ i_fatal("%s (copy example configs from "EXAMPLE_CONFIG_DIR"/)",
+ error);
+ }
+
+ if ((ret == -1 && exec_args != NULL) || ret == 0 || ret == -2)
+ i_fatal("%s", error);
+
+ if (simple_output) {
+ struct config_export_context *ctx;
+
+ ctx = config_export_init(wanted_modules, NULL, scope,
+ CONFIG_DUMP_FLAG_CHECK_SETTINGS,
+ config_request_simple_stdout,
+ setting_name_filters);
+ config_export_by_filter(ctx, &filter);
+ ret2 = config_export_finish(&ctx);
+ } else if (setting_name_filters != NULL) {
+ ret2 = 0;
+ /* ignore settings-check failures in configuration. this allows
+ using doveconf to lookup settings for things like install or
+ uninstall scripts where the configuration might
+ (temporarily) not be fully usable */
+ ret = 0;
+ for (i = 0; setting_name_filters[i] != NULL; i++) {
+ if (config_dump_one(&filter, hide_key, scope,
+ setting_name_filters[i], hide_passwords) < 0)
+ ret2 = -1;
+ }
+ } else if (exec_args == NULL) {
+ const char *info;
+
+ info = sysinfo_get(get_setting("mail", "mail_location"));
+ if (*info != '\0')
+ printf("# %s\n", info);
+ printf("# Hostname: %s\n", my_hostdomain());
+ if (!config_path_specified)
+ check_wrong_config(config_path);
+ if (scope == CONFIG_DUMP_SCOPE_ALL_WITHOUT_HIDDEN)
+ printf("# NOTE: Send doveconf -n output instead when asking for help.\n");
+ fflush(stdout);
+ ret2 = config_dump_human(&filter, wanted_modules, scope, NULL, hide_passwords);
+ } else {
+ struct config_export_context *ctx;
+
+ ctx = config_export_init(wanted_modules, NULL, CONFIG_DUMP_SCOPE_SET,
+ CONFIG_DUMP_FLAG_CHECK_SETTINGS,
+ config_request_putenv, NULL);
+ config_export_by_filter(ctx, &filter);
+
+ if (getenv(DOVECOT_PRESERVE_ENVS_ENV) != NULL) {
+ /* Standalone binary is getting its configuration via
+ doveconf. Clean the environment before calling it.
+ Do this only if the environment exists, because
+ lib-master doesn't set it if it doesn't want the
+ environment to be cleaned (e.g. -k parameter). */
+ const char *import_environment =
+ config_export_get_import_environment(ctx);
+ master_service_import_environment(import_environment);
+ master_service_env_clean();
+ }
+
+ env_put("DOVECONF_ENV", "1");
+ if (config_export_finish(&ctx) < 0)
+ i_fatal("Invalid configuration");
+ execvp(exec_args[0], exec_args);
+ i_fatal("execvp(%s) failed: %m", exec_args[0]);
+ }
+
+ if (ret < 0) {
+ /* delayed error */
+ i_fatal("%s", error);
+ }
+ if (ret2 < 0)
+ i_fatal("Errors in configuration");
+
+ config_filter_deinit(&config_filter);
+ old_settings_deinit_global();
+ module_dir_unload(&modules);
+ config_parser_deinit();
+ master_service_deinit(&master_service);
+ return 0;
+}
diff --git a/src/config/main.c b/src/config/main.c
new file mode 100644
index 0000000..01a80ba
--- /dev/null
+++ b/src/config/main.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "module-dir.h"
+#include "restrict-access.h"
+#include "master-service.h"
+#include "old-set-parser.h"
+#include "config-connection.h"
+#include "config-parser.h"
+#include "config-request.h"
+
+static void client_connected(struct master_service_connection *conn)
+{
+ master_service_client_connection_accept(conn);
+ (void)config_connection_create(conn->fd);
+}
+
+int main(int argc, char *argv[])
+{
+ const enum master_service_flags service_flags =
+ MASTER_SERVICE_FLAG_DONT_SEND_STATS;
+ const char *path, *error;
+
+ master_service = master_service_init("config", service_flags,
+ &argc, &argv, "");
+ if (master_getopt(master_service) > 0)
+ return FATAL_DEFAULT;
+ master_service_init_log(master_service);
+
+ restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL);
+ restrict_access_allow_coredumps(TRUE);
+
+ config_parse_load_modules();
+
+ path = master_service_get_config_path(master_service);
+ if (config_parse_file(path, TRUE, NULL, &error) <= 0)
+ i_fatal("%s", error);
+
+ /* notify about our success only after successfully parsing the
+ config file, so if the parsing fails, master won't immediately
+ just recreate this process (and fail again and so on). */
+ master_service_init_finish(master_service);
+
+ master_service_run(master_service, client_connected);
+ config_connections_destroy_all();
+
+ config_filter_deinit(&config_filter);
+ old_settings_deinit_global();
+ module_dir_unload(&modules);
+ config_parser_deinit();
+ master_service_deinit(&master_service);
+ return 0;
+}
diff --git a/src/config/old-set-parser.c b/src/config/old-set-parser.c
new file mode 100644
index 0000000..d5302f8
--- /dev/null
+++ b/src/config/old-set-parser.c
@@ -0,0 +1,824 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "settings-parser.h"
+#include "config-parser-private.h"
+#include "old-set-parser.h"
+#include "istream.h"
+#include "base64.h"
+
+static bool seen_ssl_parameters_dat;
+static const char *ssl_dh_parameters;
+
+#define config_apply_line (void)config_apply_line
+
+struct socket_set {
+ const char *path, *mode, *user, *group;
+ bool master;
+};
+
+struct old_set_parser {
+ const char *base_dir;
+ /* 1 when in auth {} section, >1 when inside auth { .. { .. } } */
+ unsigned int auth_section;
+ /* 1 when in socket listen {}, >1 when inside more of its sections */
+ unsigned int socket_listen_section;
+ bool seen_auth_section;
+ struct socket_set socket_set;
+};
+
+static const struct config_filter any_filter = {
+ .service = NULL
+};
+
+static const struct config_filter imap_filter = {
+ .service = "imap"
+};
+static const struct config_filter pop3_filter = {
+ .service = "pop3"
+};
+static const struct config_filter managesieve_filter = {
+ .service = "sieve"
+};
+
+static char *ssl_dh_value = NULL;
+static bool ssl_dh_loaded = FALSE;
+
+static void ATTR_FORMAT(2, 3)
+obsolete(struct config_parser_context *ctx, const char *str, ...)
+{
+ static bool seen_obsoletes = FALSE;
+ va_list args;
+
+ if (!seen_obsoletes) {
+ i_warning("NOTE: You can get a new clean config file with: "
+ "doveconf -Pn > dovecot-new.conf");
+ seen_obsoletes = TRUE;
+ }
+
+ va_start(args, str);
+ i_warning("Obsolete setting in %s:%u: %s",
+ ctx->cur_input->path, ctx->cur_input->linenum,
+ t_strdup_vprintf(str, args));
+ va_end(args);
+}
+
+static void set_rename(struct config_parser_context *ctx,
+ const char *old_key, const char *key, const char *value)
+{
+ obsolete(ctx, "%s has been renamed to %s", old_key, key);
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, key, value);
+}
+
+static bool old_settings_ssl_dh_read(const char **value, const char **error_r)
+{
+
+ if (ssl_dh_parameters != NULL) *value = ssl_dh_parameters;
+
+ const char *fn = t_strconcat(PKG_STATEDIR, "/ssl-parameters.dat", NULL);
+ buffer_t *data = t_buffer_create(300);
+ string_t *b64_data = t_str_new(500);
+ size_t siz;
+ unsigned short keysize;
+ unsigned int off=0;
+
+ /* try read it */
+ struct istream *is = i_stream_create_file(fn, IO_BLOCK_SIZE);
+
+ if (is->stream_errno == ENOENT) {
+ /* this is given because the ssl-parameters.dat file is no more there
+ and we don't want to to make go searching for the file
+ this code is only ever reached if ssl_dh_parameters is empty anyways
+ */
+ /* check moved to correct place from here */
+ *value = NULL;
+ i_stream_unref(&is);
+ return TRUE;
+ } else if (is->stream_errno != 0) {
+ *error_r = t_strdup(i_stream_get_error(is));
+ i_stream_unref(&is);
+ return FALSE;
+ }
+
+ /* then try to read the rest of the data */
+ while(i_stream_read(is) > 0) {
+ const unsigned char *buf = i_stream_get_data(is, &siz);
+ if (siz < 88) break;
+ memcpy(&keysize, buf, 2);
+ if (keysize == 512) {
+ memcpy(&off, buf+4, 4);
+ off += 16; // skip headers
+ } else {
+ off = 8; // skip header
+ }
+ if (off > siz) break;
+ buffer_append(data, buf+off, siz);
+ break;
+ }
+
+ const void *tmp = buffer_get_data(data, &siz);
+
+ if (siz > 4) {
+ str_append(b64_data, "-----BEGIN DH PARAMETERS-----\n");
+ base64_encode(tmp, siz-4, b64_data);
+ /* need to wrap the string nicely */
+ for(size_t i = 29+65; i < str_len(b64_data); i+=64) /* start at header + first 64 */
+ {
+ str_insert(b64_data, i++, "\n");
+ }
+ str_append_c(b64_data,'\n');
+ str_append(b64_data, "-----END DH PARAMETERS-----");
+ ssl_dh_parameters = i_strdup(str_c(b64_data));
+ *value = ssl_dh_parameters;
+
+ if (!seen_ssl_parameters_dat) {
+ i_warning("please set ssl_dh=<%s", SYSCONFDIR"/dh.pem");
+ i_warning("You can generate it with: dd if=%s bs=1 skip=%u | openssl dhparam -inform der > %s", fn, off, SYSCONFDIR"/dh.pem");
+ seen_ssl_parameters_dat = TRUE;
+ }
+ } else if (is->stream_errno == ENOENT) {
+ /* check for empty ssl_dh elsewhere */
+ *value = NULL;
+ i_stream_unref(&is);
+ return TRUE;
+ } else {
+ *error_r = "ssl enabled, but ssl_dh not set";
+ i_stream_unref(&is);
+ return FALSE;
+ }
+ i_stream_unref(&is);
+
+ return TRUE;
+}
+
+bool old_settings_ssl_dh_load(const char **value, const char **error_r)
+{
+ if (ssl_dh_loaded) {
+ *value = ssl_dh_value;
+ return TRUE;
+ }
+ if (!old_settings_ssl_dh_read(value, error_r))
+ return FALSE;
+ ssl_dh_value = i_strdup(*value);
+ ssl_dh_loaded = TRUE;
+ return TRUE;
+}
+
+/* FIXME: Remove ssl_protocols_to_min_protocol() in v2.4 */
+static int ssl_protocols_to_min_protocol(const char *ssl_protocols,
+ const char **min_protocol_r,
+ const char **error_r)
+{
+ static const char *protocol_versions[] = {
+ "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2",
+ };
+ /* Array where -1 = disable, 0 = not found, 1 = enable */
+ int protos[N_ELEMENTS(protocol_versions)];
+ memset(protos, 0, sizeof(protos));
+ bool explicit_enable = FALSE;
+
+ const char *const *tmp = t_strsplit_spaces(ssl_protocols, ", ");
+ for (; *tmp != NULL; tmp++) {
+ const char *p = *tmp;
+ bool enable = TRUE;
+ if (p[0] == '!') {
+ enable = FALSE;
+ ++p;
+ }
+ for (unsigned i = 0; i < N_ELEMENTS(protocol_versions); i++) {
+ if (strcmp(p, protocol_versions[i]) == 0) {
+ if (enable) {
+ protos[i] = 1;
+ explicit_enable = TRUE;
+ } else {
+ protos[i] = -1;
+ }
+ goto found;
+ }
+ }
+ *error_r = t_strdup_printf("Unrecognized protocol '%s'", p);
+ return -1;
+
+ found:;
+ }
+
+ unsigned min = N_ELEMENTS(protocol_versions);
+ for (unsigned i = 0; i < N_ELEMENTS(protocol_versions); i++) {
+ if (explicit_enable) {
+ if (protos[i] > 0)
+ min = I_MIN(min, i);
+ } else if (protos[i] == 0)
+ min = I_MIN(min, i);
+ }
+ if (min == N_ELEMENTS(protocol_versions)) {
+ *error_r = "All protocols disabled";
+ return -1;
+ }
+
+ *min_protocol_r = protocol_versions[min];
+ return 0;
+}
+
+static bool
+old_settings_handle_root(struct config_parser_context *ctx,
+ const char *key, const char *value)
+{
+ const char *p;
+ size_t len;
+
+ if (strcmp(key, "base_dir") == 0) {
+ len = strlen(value);
+ if (len > 0 && value[len-1] == '/')
+ value = t_strndup(value, len-1);
+ ctx->old->base_dir = p_strdup(ctx->pool, value);
+ }
+ if (strcmp(key, "protocols") == 0) {
+ char **protos, **s;
+ bool have_imap = FALSE, have_imaps = FALSE;
+ bool have_pop3 = FALSE, have_pop3s = FALSE;
+
+ protos = p_strsplit_spaces(pool_datastack_create(), value, " ");
+ for (s = protos; *s != NULL; s++) {
+ if (strcmp(*s, "imap") == 0)
+ have_imap = TRUE;
+ else if (strcmp(*s, "imaps") == 0) {
+ *s = "";
+ have_imaps = TRUE;
+ } else if (strcmp(*s, "pop3") == 0)
+ have_pop3 = TRUE;
+ else if (strcmp(*s, "pop3s") == 0) {
+ *s = "";
+ have_pop3s = TRUE;
+ } else if (strcmp(*s, "managesieve") == 0) {
+ *s = "sieve";
+ obsolete(ctx, "protocols=managesieve has been renamed to protocols=sieve");
+ }
+ }
+ value = t_strarray_join((const char *const *)protos, " ");
+ /* ugly way to drop extra spaces.. */
+ protos = p_strsplit_spaces(pool_datastack_create(), value, " ");
+ value = t_strarray_join((const char *const *)protos, " ");
+
+ if (have_imaps && !have_imap) {
+ obsolete(ctx, "'imaps' protocol can no longer be specified (use protocols=imap). to disable non-ssl imap, use service imap-login { inet_listener imap { port=0 } }");
+ value = t_strconcat(value, " imap", NULL);
+ config_apply_line(ctx, "port",
+ "service/imap-login/inet_listener/imap/port=0", NULL);
+ } else if (have_imaps)
+ obsolete(ctx, "'imaps' protocol is no longer necessary, remove it");
+ if (have_pop3s && !have_pop3) {
+ obsolete(ctx, "'pop3s' protocol can no longer be specified (use protocols=pop3). to disable non-ssl pop3, use service pop3-login { inet_listener pop3 { port=0 } }");
+ value = t_strconcat(value, " pop3", NULL);
+ config_apply_line(ctx, "port",
+ "service/pop3-login/inet_listener/pop3/port=0", NULL);
+ } else if (have_pop3s)
+ obsolete(ctx, "'pop3s' protocol is no longer necessary, remove it");
+
+ if (*value == ' ') value++;
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+ key, value);
+ return TRUE;
+ }
+ if (strcmp(key, "ssl_cert_file") == 0 ||
+ strcmp(key, "ssl_key_file") == 0 ||
+ strcmp(key, "ssl_ca_file") == 0) {
+ if (*value == '\0')
+ return TRUE;
+ p = t_strdup_until(key, strrchr(key, '_'));
+ obsolete(ctx, "%s has been replaced by %s = <file", key, p);
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYFILE,
+ p, value);
+ return TRUE;
+ }
+ if (strcmp(key, "ssl_disable") == 0) {
+ if (strcasecmp(value, "yes") == 0)
+ value = "no";
+ else if (strcasecmp(value, "no") == 0)
+ value = "yes";
+ set_rename(ctx, key, "ssl", value);
+ return TRUE;
+ }
+ if (strcmp(key, "ssl_parameters_regenerate") == 0 ||
+ strcmp(key, "ssl_dh_parameters_length") == 0) {
+ obsolete(ctx, "%s is no longer needed", key);
+ return TRUE;
+ }
+ if (strcmp(key, "ssl_protocols") == 0) {
+ obsolete(ctx, "%s has been replaced by ssl_min_protocol", key);
+ const char *min_protocol, *error;
+ if (ssl_protocols_to_min_protocol(value, &min_protocol, &error) < 0) {
+ i_error("Could not find a minimum ssl_min_protocol "
+ "setting from ssl_protocols = %s: %s",
+ value, error);
+ return TRUE;
+ }
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+ "ssl_min_protocol", min_protocol);
+ return TRUE;
+ }
+ if (strcmp(key, "sieve") == 0 ||
+ strcmp(key, "sieve_storage") == 0) {
+ if (strcmp(key, "sieve_storage") == 0)
+ obsolete(ctx, "sieve_storage has been moved into plugin { sieve_dir }");
+ else
+ obsolete(ctx, "%s has been moved into plugin {} section", key);
+
+ config_apply_line(ctx, "", "plugin=", NULL);
+ config_apply_line(ctx, key,
+ t_strdup_printf("plugin/%s=%s", key, value), NULL);
+ return TRUE;
+ }
+ if (strcmp(key, "fsync_disable") == 0) {
+ if (strcasecmp(value, "yes") == 0)
+ value = "never";
+ else if (strcasecmp(value, "no") == 0)
+ value = "optimized";
+ set_rename(ctx, key, "mail_fsync", value);
+ return TRUE;
+ }
+ if (strcmp(key, "dbox_rotate_size") == 0) {
+ set_rename(ctx, key, "mdbox_rotate_size", value);
+ return TRUE;
+ }
+ if (str_begins(key, "mail_cache_compress_")) {
+ const char *new_key = t_strconcat("mail_cache_purge_", key+20, NULL);
+ set_rename(ctx, key, new_key, value);
+ return TRUE;
+ }
+ if (strcmp(key, "imap_client_workarounds") == 0) {
+ char **args, **arg;
+
+ args = p_strsplit_spaces(pool_datastack_create(), value, " ,");
+ for (arg = args; *arg != NULL; arg++) {
+ if (strcmp(*arg, "outlook-idle") == 0) {
+ *arg = "";
+ obsolete(ctx, "imap_client_workarounds=outlook-idle is no longer necessary");
+ } else if (strcmp(*arg, "netscape-eoh") == 0) {
+ *arg = "";
+ obsolete(ctx, "imap_client_workarounds=netscape-eoh is no longer supported");
+ }
+ }
+ value = t_strarray_join((void *)args, " ");
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+ key, value);
+ return TRUE;
+ }
+
+ if (strcmp(key, "login_dir") == 0 ||
+ strcmp(key, "dbox_rotate_min_size") == 0 ||
+ strcmp(key, "dbox_rotate_days") == 0 ||
+ strcmp(key, "director_consistent_hashing") == 0 ||
+ strcmp(key, "mail_log_max_lines_per_sec") == 0 ||
+ strcmp(key, "maildir_copy_preserve_filename") == 0) {
+ obsolete(ctx, "%s has been removed", key);
+ return TRUE;
+ }
+ if (ctx->old->auth_section == 1) {
+ if (!str_begins(key, "auth_"))
+ key = t_strconcat("auth_", key, NULL);
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+ key, value);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+config_apply_login_set(struct config_parser_context *ctx,
+ struct config_section_stack *old_section,
+ const char *old_key, const char *key, const char *value)
+{
+ obsolete(ctx, "%s has been replaced by service { %s }", old_key, key);
+
+ if (config_filter_match(&old_section->filter, &imap_filter)) {
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/imap-login/%s=%s", key, value), NULL);
+ }
+ if (config_filter_match(&old_section->filter, &pop3_filter)) {
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/pop3-login/%s=%s", key, value), NULL);
+ }
+ if (config_filter_match(&old_section->filter, &managesieve_filter)) {
+ /* if pigeonhole isn't installed, this fails.
+ just ignore it then.. */
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/managesieve-login/%s=%s", key, value), NULL);
+ ctx->error = NULL;
+ }
+}
+
+static void
+config_apply_mail_set(struct config_parser_context *ctx,
+ struct config_section_stack *old_section,
+ const char *old_key, const char *key, const char *value)
+{
+ obsolete(ctx, "%s has been replaced by service { %s }", old_key, key);
+
+ if (config_filter_match(&old_section->filter, &imap_filter)) {
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/imap/%s=%s", key,value), NULL);
+ }
+ if (config_filter_match(&old_section->filter, &pop3_filter)) {
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/pop3/%s=%s", key,value), NULL);
+ }
+ if (config_filter_match(&old_section->filter, &managesieve_filter)) {
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/managesieve/%s=%s", key,value), NULL);
+ ctx->error = NULL;
+ }
+}
+
+static void
+config_apply_auth_set(struct config_parser_context *ctx,
+ const char *old_key, const char *key, const char *value)
+{
+ obsolete(ctx, "%s has been replaced by service auth { %s }", old_key, key);
+ config_apply_line(ctx, key,
+ t_strdup_printf("service/auth/%s=%s", key,value), NULL);
+}
+
+static bool listen_has_port(const char *str)
+{
+ const char *const *addrs;
+
+ if (strchr(str, ':') == NULL)
+ return FALSE;
+
+ addrs = t_strsplit_spaces(str, ", ");
+ for (; *addrs != NULL; addrs++) {
+ if (strcmp(*addrs, "*") != 0 &&
+ strcmp(*addrs, "::") != 0 &&
+ strcmp(*addrs, "[::]") != 0 &&
+ !is_ipv4_address(*addrs) &&
+ !is_ipv6_address(*addrs))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool
+old_settings_handle_proto(struct config_parser_context *ctx,
+ const char *key, const char *value)
+{
+ struct config_section_stack *old_section = ctx->cur_section;
+ const char *p;
+ uoff_t size;
+ bool root;
+
+ while (ctx->cur_section->prev != NULL)
+ ctx->cur_section = ctx->cur_section->prev;
+
+ root = config_filter_match(&old_section->filter, &any_filter);
+
+ if (strcmp(key, "ssl_listen") == 0 ||
+ (strcmp(key, "listen") == 0 &&
+ (listen_has_port(value) || !root))) {
+ const char *ssl = strcmp(key, "ssl_listen") == 0 ? "s" : "";
+
+ if (*value == '\0') {
+ /* default */
+ return TRUE;
+ }
+ p = strrchr(value, ':');
+ if (p != NULL && listen_has_port(value)) {
+ obsolete(ctx, "%s=..:port has been replaced by service { inet_listener { port } }", key);
+ value = t_strdup_until(value, p++);
+ if (config_filter_match(&old_section->filter, &imap_filter)) {
+ config_apply_line(ctx, "port",
+ t_strdup_printf("service/imap-login/inet_listener/imap%s/port=%s", ssl, p), NULL);
+ }
+ if (config_filter_match(&old_section->filter, &pop3_filter)) {
+ config_apply_line(ctx, "port",
+ t_strdup_printf("service/pop3-login/inet_listener/pop3%s/port=%s", ssl, p), NULL);
+ }
+ if (*ssl == '\0' &&
+ config_filter_match(&old_section->filter, &managesieve_filter)) {
+ config_apply_line(ctx, "port",
+ t_strdup_printf("service/managesieve-login/inet_listener/managesieve/port=%s", p), NULL);
+ ctx->error = NULL;
+ }
+ }
+ if (root && *ssl == '\0') {
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+ key, value);
+ } else {
+ obsolete(ctx, "protocol { %s } has been replaced by service { inet_listener { address } }", key);
+ if (config_filter_match(&old_section->filter, &imap_filter)) {
+ config_apply_line(ctx, "address",
+ t_strdup_printf("service/imap-login/inet_listener/imap%s/address=%s", ssl, value), NULL);
+ }
+ if (config_filter_match(&old_section->filter, &pop3_filter)) {
+ config_apply_line(ctx, "address",
+ t_strdup_printf("service/pop3-login/inet_listener/pop3%s/address=%s", ssl, value), NULL);
+ }
+ if (*ssl == '\0' &&
+ config_filter_match(&old_section->filter, &managesieve_filter)) {
+ config_apply_line(ctx, "address",
+ t_strdup_printf("service/managesieve-login/inet_listener/managesieve/address=%s", value), NULL);
+ ctx->error = NULL;
+ }
+ }
+ return TRUE;
+ }
+ if (strcmp(key, "login_chroot") == 0) {
+ if (strcmp(value, "no") == 0)
+ value = "";
+ else
+ value = "login";
+
+ config_apply_login_set(ctx, old_section, key, "chroot", value);
+ return TRUE;
+ }
+ if (strcmp(key, "login_user") == 0) {
+ config_apply_login_set(ctx, old_section, key, "user", value);
+ return TRUE;
+ }
+ if (strcmp(key, "login_executable") == 0) {
+ config_apply_login_set(ctx, old_section, key, "executable", value);
+ return TRUE;
+ }
+ if (strcmp(key, "login_process_size") == 0) {
+ config_apply_login_set(ctx, old_section, key, "vsz_limit",
+ t_strconcat(value, " M", NULL));
+ return TRUE;
+ }
+ if (strcmp(key, "login_process_per_connection") == 0) {
+ config_apply_login_set(ctx, old_section, key, "service_count",
+ strcmp(value, "no") == 0 ? "0" : "1");
+ return TRUE;
+ }
+ if (strcmp(key, "login_processes_count") == 0) {
+ config_apply_login_set(ctx, old_section, key, "process_min_avail", value);
+ return TRUE;
+ }
+ if (strcmp(key, "login_max_processes_count") == 0) {
+ config_apply_login_set(ctx, old_section, key, "process_limit", value);
+ return TRUE;
+ }
+ if (strcmp(key, "login_max_connections") == 0) {
+ config_apply_login_set(ctx, old_section, key, "client_limit", value);
+ return TRUE;
+ }
+ if (strcmp(key, "login_process_size") == 0) {
+ config_apply_login_set(ctx, old_section, key, "vsz_limit",
+ t_strconcat(value, " M", NULL));
+ return TRUE;
+ }
+
+ if (strcmp(key, "max_mail_processes") == 0) {
+ config_apply_mail_set(ctx, old_section, key, "process_limit", value);
+ return TRUE;
+ }
+ if (strcmp(key, "mail_executable") == 0) {
+ config_apply_mail_set(ctx, old_section, key, "executable", value);
+ return TRUE;
+ }
+ if (strcmp(key, "mail_process_size") == 0) {
+ config_apply_mail_set(ctx, old_section, key, "vsz_limit",
+ t_strconcat(value, " M", NULL));
+ return TRUE;
+ }
+ if (strcmp(key, "mail_drop_priv_before_exec") == 0) {
+ config_apply_mail_set(ctx, old_section, key, "drop_priv_before_exec", value);
+ return TRUE;
+ }
+
+ if (ctx->old->auth_section == 1) {
+ if (!str_begins(key, "auth_"))
+ key = t_strconcat("auth_", key, NULL);
+ }
+
+ if (strcmp(key, "auth_executable") == 0) {
+ config_apply_auth_set(ctx, key, "executable", value);
+ return TRUE;
+ }
+ if (strcmp(key, "auth_process_size") == 0) {
+ config_apply_auth_set(ctx, key, "vsz_limit",
+ t_strconcat(value, " M", NULL));
+ return TRUE;
+ }
+ if (strcmp(key, "auth_user") == 0) {
+ config_apply_auth_set(ctx, key, "user", value);
+ return TRUE;
+ }
+ if (strcmp(key, "auth_chroot") == 0) {
+ config_apply_auth_set(ctx, key, "chroot", value);
+ return TRUE;
+ }
+ if (strcmp(key, "auth_cache_size") == 0 &&
+ str_to_uoff(value, &size) == 0 && size > 0 && size < 1024) {
+ obsolete(ctx, "auth_cache_size value no longer defaults to "
+ "megabytes. Use %sM", value);
+ config_apply_line(ctx, key,
+ t_strdup_printf("%s=%sM", key, value), NULL);
+ return TRUE;
+ }
+ if (strcmp(key, "auth_count") == 0) {
+ if (strcmp(value, "count") == 0)
+ obsolete(ctx, "auth_count has been removed");
+ else
+ obsolete(ctx, "auth_count has been removed, and its value must be 1");
+ return TRUE;
+ }
+ if (ctx->old->socket_listen_section == 2) {
+ const char **p = NULL;
+
+ if (strcmp(key, "path") == 0)
+ p = &ctx->old->socket_set.path;
+ else if (strcmp(key, "mode") == 0)
+ p = &ctx->old->socket_set.mode;
+ else if (strcmp(key, "user") == 0)
+ p = &ctx->old->socket_set.user;
+ else if (strcmp(key, "group") == 0)
+ p = &ctx->old->socket_set.group;
+
+ if (p != NULL) {
+ *p = p_strdup(ctx->pool, value);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool old_auth_section(struct config_parser_context *ctx,
+ const char *key, const char *value)
+{
+ if (ctx->old->auth_section == 0 && ctx->old->seen_auth_section) {
+ obsolete(ctx, "Multiple auth sections are no longer supported");
+ return FALSE;
+ }
+ ctx->old->seen_auth_section = TRUE;
+ i_zero(&ctx->old->socket_set);
+
+ ctx->old->auth_section++;
+ if ((strcmp(key, "passdb") == 0 || strcmp(key, "userdb") == 0) &&
+ ctx->old->auth_section == 2) {
+ obsolete(ctx, "%s %s {} has been replaced by %s { driver=%s }",
+ key, value, key, value);
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN, key, "");
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+ "driver", value);
+ return TRUE;
+ }
+ if (strcmp(key, "socket") == 0 && ctx->old->auth_section == 2) {
+ if (strcmp(value, "connect") == 0) {
+ obsolete(ctx, "socket connect {} is no longer supported, configure external auth server separately");
+ return FALSE;
+ }
+ if (strcmp(value, "listen") != 0)
+ return FALSE;
+
+ /* socket listen { .. } */
+ ctx->old->socket_listen_section++;
+ return TRUE;
+ }
+
+ if (ctx->old->socket_listen_section > 0)
+ ctx->old->socket_listen_section++;
+ if ((strcmp(key, "master") == 0 || strcmp(key, "client") == 0) &&
+ ctx->old->socket_listen_section == 2) {
+ ctx->old->socket_set.master = strcmp(key, "master") == 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void socket_apply(struct config_parser_context *ctx)
+{
+ const struct socket_set *set = &ctx->old->socket_set;
+ const char *path, *prefix;
+ size_t len;
+ bool master_suffix;
+
+ if (set->path == NULL) {
+ ctx->error = "socket listen {} is missing path";
+ return;
+ }
+ path = set->path;
+ len = strlen(ctx->old->base_dir);
+ if (str_begins(path, ctx->old->base_dir) &&
+ path[len] == '/')
+ path += len + 1;
+
+ len = strlen(path);
+ master_suffix = len >= 7 &&
+ (strcmp(path + len - 7, "-master") == 0 ||
+ strcmp(path + len - 7, "-userdb") == 0);
+
+ if (set->master && !master_suffix) {
+ ctx->error = "socket listen { master { path=.. } } must end with -master (or -userdb) suffix";
+ return;
+ } else if (!set->master && master_suffix) {
+ ctx->error = "socket listen { client { path=.. } } must end not with -master or -userdb suffix";
+ return;
+ }
+
+ config_apply_line(ctx, "unix_listener",
+ t_strdup_printf("service/auth/unix_listener=%s", settings_section_escape(path)), path);
+ prefix = t_strdup_printf("service/auth/unix_listener/%s", settings_section_escape(path));
+ if (set->mode != NULL) {
+ config_apply_line(ctx, "mode",
+ t_strdup_printf("%s/mode=%s", prefix, set->mode), NULL);
+ }
+ if (set->user != NULL) {
+ config_apply_line(ctx, "user",
+ t_strdup_printf("%s/user=%s", prefix, set->user), NULL);
+ }
+ if (set->group != NULL) {
+ config_apply_line(ctx, "group",
+ t_strdup_printf("%s/group=%s", prefix, set->group), NULL);
+ }
+ i_zero(&ctx->old->socket_set);
+}
+
+static bool
+old_settings_handle_path(struct config_parser_context *ctx,
+ const char *key, const char *value)
+{
+ if (strcmp(str_c(ctx->str), "plugin/0/") == 0) {
+ if (strcmp(key, "imap_zlib_compress_level") == 0) {
+ obsolete(ctx, "%s has been replaced by imap_compress_deflate_level", key);
+ config_apply_line(ctx, key,
+ t_strdup_printf("plugin/0/imap_compress_deflate_level=%s", value), NULL);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+bool old_settings_handle(struct config_parser_context *ctx,
+ enum config_line_type type,
+ const char *key, const char *value)
+{
+ switch (type) {
+ case CONFIG_LINE_TYPE_SKIP:
+ case CONFIG_LINE_TYPE_CONTINUE:
+ case CONFIG_LINE_TYPE_ERROR:
+ case CONFIG_LINE_TYPE_INCLUDE:
+ case CONFIG_LINE_TYPE_INCLUDE_TRY:
+ case CONFIG_LINE_TYPE_KEYFILE:
+ case CONFIG_LINE_TYPE_KEYVARIABLE:
+ break;
+ case CONFIG_LINE_TYPE_KEYVALUE:
+ if (ctx->pathlen == 0) {
+ struct config_section_stack *old_section =
+ ctx->cur_section;
+ bool ret;
+
+ ret = old_settings_handle_proto(ctx, key, value);
+ ctx->cur_section = old_section;
+ if (ret)
+ return TRUE;
+
+ return old_settings_handle_root(ctx, key, value);
+ }
+ return old_settings_handle_path(ctx, key, value);
+ case CONFIG_LINE_TYPE_SECTION_BEGIN:
+ if (ctx->old->auth_section > 0)
+ return old_auth_section(ctx, key, value);
+ else if (ctx->pathlen == 0 && strcmp(key, "auth") == 0) {
+ obsolete(ctx, "add auth_ prefix to all settings inside auth {} and remove the auth {} section completely");
+ ctx->old->auth_section = 1;
+ return TRUE;
+ } else if (ctx->pathlen == 0 && strcmp(key, "protocol") == 0 &&
+ strcmp(value, "managesieve") == 0) {
+ obsolete(ctx, "protocol managesieve {} has been replaced by protocol sieve { }");
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN,
+ "protocol", "sieve");
+ return TRUE;
+ } else if (ctx->pathlen == 0 && strcmp(key, "service") == 0 &&
+ strcmp(value, "dns_client") == 0) {
+ obsolete(ctx, "service dns_client {} has been replaced by service dns-client { }");
+ config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN,
+ "service", "dns-client");
+ return TRUE;
+ }
+ break;
+ case CONFIG_LINE_TYPE_SECTION_END:
+ if (ctx->old->auth_section > 0) {
+ if (--ctx->old->auth_section == 0)
+ return TRUE;
+ }
+ if (ctx->old->socket_listen_section > 0) {
+ if (ctx->old->socket_listen_section == 2)
+ socket_apply(ctx);
+ ctx->old->socket_listen_section--;
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+void old_settings_init(struct config_parser_context *ctx)
+{
+ ctx->old = p_new(ctx->pool, struct old_set_parser, 1);
+ ctx->old->base_dir = PKG_RUNDIR;
+}
+
+void old_settings_deinit_global(void)
+{
+ i_free(ssl_dh_value);
+}
diff --git a/src/config/old-set-parser.h b/src/config/old-set-parser.h
new file mode 100644
index 0000000..e95b3e1
--- /dev/null
+++ b/src/config/old-set-parser.h
@@ -0,0 +1,16 @@
+#ifndef OLD_SET_PARSER_H
+#define OLD_SET_PARSER_H
+
+#include "config-parser-private.h"
+
+struct config_parser_context;
+
+bool old_settings_ssl_dh_load(const char **value, const char **error_r);
+
+bool old_settings_handle(struct config_parser_context *ctx,
+ enum config_line_type type,
+ const char *key, const char *value);
+void old_settings_init(struct config_parser_context *ctx);
+void old_settings_deinit_global(void);
+
+#endif
diff --git a/src/config/settings-get.pl b/src/config/settings-get.pl
new file mode 100755
index 0000000..ca124eb
--- /dev/null
+++ b/src/config/settings-get.pl
@@ -0,0 +1,162 @@
+#!/usr/bin/env perl
+use strict;
+
+print "/* WARNING: THIS FILE IS GENERATED - DO NOT PATCH!\n";
+print " It's not enough alone in any case, because the defaults may be\n";
+print " coming from the individual *-settings.c in some situations. If you\n";
+print " wish to modify defaults, change the other *-settings.c files and\n";
+print " just delete this file. This file will be automatically regenerated\n";
+print " by make. (This file is distributed in the tarball only because some\n";
+print " systems might not have Perl installed.) */\n";
+print '#include "lib.h"'."\n";
+print '#include "array.h"'."\n";
+print '#include "str.h"'."\n";
+print '#include "ipwd.h"'."\n";
+print '#include "var-expand.h"'."\n";
+print '#include "file-lock.h"'."\n";
+print '#include "fsync-mode.h"'."\n";
+print '#include "hash-format.h"'."\n";
+print '#include "net.h"'."\n";
+print '#include "unichar.h"'."\n";
+print '#include "hash-method.h"'."\n";
+print '#include "settings-parser.h"'."\n";
+print '#include "message-header-parser.h"'."\n";
+print '#include "all-settings.h"'."\n";
+print '#include <stddef.h>'."\n";
+print '#include <unistd.h>'."\n";
+print '#define CONFIG_BINARY'."\n";
+print 'extern buffer_t config_all_services_buf;';
+
+my @services = ();
+my @service_ifdefs = ();
+my %parsers = {};
+
+foreach my $file (@ARGV) {
+ my $f;
+ open($f, $file) || die "Can't open $file: $@";
+
+ my $state = 0;
+ my $file_contents = "";
+ my $externs = "";
+ my $code = "";
+ my %funcs;
+ my $cur_name = "";
+ my $ifdef = "";
+ my $state_ifdef = 0;
+
+ while (<$f>) {
+ my $write = 0;
+ if ($state == 0) {
+ if (/struct .*_settings \{/ ||
+ /struct setting_define.*\{/ ||
+ /struct .*_default_settings = \{/) {
+ $state++;
+ } elsif (/^struct service_settings (.*) = \{/) {
+ $state++;
+ if ($ifdef eq "") {
+ $state_ifdef = 0;
+ } else {
+ $_ = $ifdef."\n".$_;
+ $state_ifdef = 1;
+ }
+ push @services, $1;
+ push @service_ifdefs, $ifdef;
+ } elsif (/^(static )?const struct setting_parser_info (.*) = \{/) {
+ $cur_name = $2;
+ $state++ if ($cur_name !~ /^\*default_/);
+ } elsif (/^extern const struct setting_parser_info (.*);/) {
+ $externs .= "extern const struct setting_parser_info $1;\n";
+ } elsif (/\/\* <settings checks> \*\//) {
+ $state = 4;
+ $code .= $_;
+ }
+
+ if (/(^#ifdef .*)$/ || /^(#if .*)$/) {
+ $ifdef = $1;
+ } else {
+ $ifdef = "";
+ }
+
+ if (/#define.*DEF/ || /^#undef.*DEF/ || /ARRAY_DEFINE_TYPE.*_settings/) {
+ $write = 1;
+ $state = 2 if (/\\$/);
+ }
+ } elsif ($state == 2) {
+ $write = 1;
+ $state = 0 if (!/\\$/);
+ } elsif ($state == 4) {
+ $code .= $_;
+ $state = 0 if (/\/\* <\/settings checks> \*\//);
+ }
+
+ if ($state == 1 || $state == 3) {
+ if ($state == 1) {
+ if (/\.module_name = "(.*)"/) {
+ $parsers{$cur_name} = $1;
+ }
+ if (/DEFLIST.*".*",(.*)$/) {
+ my $value = $1;
+ if ($value =~ /.*&(.*)\)/) {
+ $parsers{$1} = 0;
+ $externs .= "extern const struct setting_parser_info $1;\n";
+ } else {
+ $state = 3;
+ }
+ }
+ } elsif ($state == 3) {
+ if (/.*&(.*)\)/) {
+ $parsers{$1} = 0;
+ }
+ }
+
+ s/^static const (struct master_settings master_default_settings)/$1/;
+
+ $write = 1;
+ if (/};/) {
+ $state = 0;
+ if ($state_ifdef) {
+ $_ .= "#endif\n";
+ $state_ifdef = 0;
+ }
+ }
+ }
+
+ $file_contents .= $_ if ($write);
+ }
+
+ print "/* $file */\n";
+ print $externs;
+ print $code;
+ print $file_contents;
+
+ close $f;
+}
+
+print "static struct service_settings *config_all_services[] = {\n";
+
+for (my $i = 0; $i < scalar(@services); $i++) {
+ my $ifdef = $service_ifdefs[$i];
+ print "$ifdef\n" if ($ifdef ne "");
+ print "\t&".$services[$i].",\n";
+ print "#endif\n" if ($ifdef ne "");
+}
+print "};\n";
+print "buffer_t config_all_services_buf = {\n";
+print "\t{ { config_all_services, sizeof(config_all_services) } }\n";
+print "};\n";
+
+print "const struct setting_parser_info *all_default_roots[] = {\n";
+print "\t&master_service_setting_parser_info,\n";
+print "\t&master_service_ssl_setting_parser_info,\n";
+print "\t&master_service_ssl_server_setting_parser_info,\n";
+print "\t&smtp_submit_setting_parser_info,\n";
+foreach my $name (sort(keys %parsers)) {
+ my $module = $parsers{$name};
+ next if (!$module);
+
+ print "\t&".$name.", \n";
+}
+print "\tNULL\n";
+print "};\n";
+print "const struct setting_parser_info *const *all_roots = all_default_roots;\n";
+print "ARRAY_TYPE(service_settings) *default_services = &master_default_settings.services;\n";
diff --git a/src/config/sysinfo-get.c b/src/config/sysinfo-get.c
new file mode 100644
index 0000000..dffebe6
--- /dev/null
+++ b/src/config/sysinfo-get.c
@@ -0,0 +1,127 @@
+/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mountpoint.h"
+#include "strescape.h"
+#include "sysinfo-get.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif
+
+static bool readfile(const char *path, const char **data_r)
+{
+ char buf[1024];
+ int fd, ret;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return FALSE;
+ ret = read(fd, buf, sizeof(buf));
+ i_close_fd(&fd);
+ if (ret <= 0)
+ return FALSE;
+
+ *data_r = t_strndup(buf, ret);
+ return TRUE;
+}
+
+static bool lsb_distro_get(const char *path, const char **name_r)
+{
+ const char *data, *const *p, *str, *end;
+
+ if (!readfile(path, &data))
+ return FALSE;
+
+ for (p = t_strsplit(data, "\n"); *p != NULL; p++) {
+ if (str_begins(*p, "DISTRIB_DESCRIPTION="))
+ break;
+ }
+ if (*p == NULL)
+ return FALSE;
+
+ str = t_strcut(*p + 20, '\n');
+ if (*str != '"')
+ *name_r = str;
+ else {
+ end = strrchr(++str, '"');
+ *name_r = str_unescape(p_strdup_until(unsafe_data_stack_pool,
+ str, end));
+ }
+ return TRUE;
+}
+
+static const char *distro_get(void)
+{
+ static const char *files[] = {
+ "", "/etc/redhat-release",
+ "", "/etc/SuSE-release",
+ "", "/etc/mandriva-release",
+ "", "/etc/fedora-release",
+ "", "/etc/sourcemage-release",
+ "", "/etc/slackware-version",
+ "", "/etc/gentoo-release",
+ "Debian ", "/etc/debian_version",
+ NULL
+ };
+ const char *name;
+ unsigned int i;
+
+ if (lsb_distro_get("/etc/lsb-release", &name))
+ return name;
+ for (i = 0; files[i] != NULL; i += 2) {
+ if (readfile(files[i+1], &name)) {
+ return t_strconcat(files[i], t_strcut(name, '\n'),
+ NULL);
+ }
+ }
+ return "";
+}
+
+static const char *filesystem_get(const char *mail_location)
+{
+ struct mountpoint mp;
+ const char *path;
+
+ path = strchr(mail_location, ':');
+ if (path == NULL)
+ path = mail_location;
+ else
+ path = t_strcut(path + 1, ':');
+ if (*path == '~') {
+ /* we don't know where users' home dirs are */
+ return "";
+ }
+ path = t_strcut(path, '%');
+ if (strlen(path) <= 1)
+ return "";
+
+ /* all in all it seems we can support only /<path>/%u style location */
+ if (mountpoint_get(path, pool_datastack_create(), &mp) < 0)
+ return "";
+ return mp.type == NULL ? "" : mp.type;
+}
+
+const char *sysinfo_get(const char *mail_location)
+{
+ const char *distro = "", *fs, *uname_info = "";
+#ifdef HAVE_SYS_UTSNAME_H
+ struct utsname u;
+
+ if (uname(&u) < 0)
+ i_error("uname() failed: %m");
+ else {
+ uname_info = t_strdup_printf("%s %s %s",
+ u.sysname, u.release, u.machine);
+ }
+ if (strcmp(u.sysname, "Linux") == 0)
+ distro = distro_get();
+#endif
+ fs = filesystem_get(mail_location);
+ if (*uname_info == '\0' && *distro == '\0' && *fs == '\0')
+ return "";
+ return t_strdup_printf("OS: %s %s %s %s %s", u.sysname, u.release, u.machine, distro, fs);
+}
diff --git a/src/config/sysinfo-get.h b/src/config/sysinfo-get.h
new file mode 100644
index 0000000..0f319cb
--- /dev/null
+++ b/src/config/sysinfo-get.h
@@ -0,0 +1,6 @@
+#ifndef SYSINFO_GET_H
+#define SYSINFO_GET_H
+
+const char *sysinfo_get(const char *mail_location);
+
+#endif
diff --git a/src/config/test-config-parser.c b/src/config/test-config-parser.c
new file mode 100644
index 0000000..baf91a1
--- /dev/null
+++ b/src/config/test-config-parser.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ostream.h"
+#include "service-settings.h"
+#include "settings-parser.h"
+#include "config-filter.h"
+#include "test-common.h"
+#include "all-settings.h"
+#include "config-parser.h"
+
+#define TEST_CONFIG_FILE ".test-config"
+
+static ARRAY_TYPE(service_settings) services = ARRAY_INIT;
+ARRAY_TYPE(service_settings) *default_services = &services;
+
+struct test_settings {
+ const char *key;
+ const char *key2;
+ const char *key3;
+ const char *key4;
+ const char *key5;
+ const char *pop3_deleted_flag;
+ const char *env_key;
+ const char *env_key2;
+ const char *env_key3;
+ const char *env_key4;
+ const char *env_key5;
+ const char *protocols;
+};
+
+static const struct setting_define test_settings_defs[] = {
+ SETTING_DEFINE_STRUCT_STR("key", key, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("key2", key2, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("key3", key3, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("key4", key4, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("key5", key5, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("pop3_deleted_flag", pop3_deleted_flag, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("env_key", env_key, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("env_key2", env_key2, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("env_key3", env_key3, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("env_key4", env_key4, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("env_key5", env_key5, struct test_settings),
+ SETTING_DEFINE_STRUCT_STR("protocols", protocols, struct test_settings),
+ SETTING_DEFINE_LIST_END
+};
+
+static const struct test_settings test_settings_defaults = {
+ .key = "",
+ .key2 = "",
+ .key3 = "",
+ .key4 = "",
+ .key5 = "",
+ .pop3_deleted_flag = "",
+ .env_key = "",
+ .env_key2 = "",
+ .env_key3 = "",
+ .env_key4 = "",
+ .env_key5 = "",
+ .protocols = "pop3",
+};
+
+const struct setting_parser_info test_settings_root = {
+ .module_name = "test",
+ .defines = test_settings_defs,
+ .defaults = &test_settings_defaults,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct test_settings),
+
+ .parent_offset = SIZE_MAX,
+ .parent = NULL,
+};
+
+static const struct setting_parser_info *const roots[] = {
+ &test_settings_root,
+ NULL
+};
+
+const struct setting_parser_info *const *all_roots = roots;
+
+static void write_config_file(const char *contents)
+{
+ struct ostream *os = o_stream_create_file(TEST_CONFIG_FILE, 0, 0600, 0);
+ o_stream_nsend_str(os, contents);
+ test_assert(o_stream_finish(os) == 1);
+ o_stream_unref(&os);
+}
+
+static void test_config_parser(void)
+{
+ const char *error = NULL;
+
+ test_begin("config_parse_file");
+
+ write_config_file(
+"# comment\n"
+"key=value\n"
+"key2 = \\$escape \\escape \\\"escape\\\"\n"
+"key3 = value\n"
+"key3 = $key3 nothervalue\n"
+"key3 = yetanother value $key3 right here\n"
+"key4 = \" $key3 \"\n"
+"key5 = ' $key4 '\n"
+"pop3_deleted_flag = \"$Deleted\"\n"
+"env_key=$ENV:foo\n"
+"env_key=$env_key $ENV:bar\n"
+"env_key=$env_key \"$env_key\"\n"
+"env_key2 = foo$ENV:FOO bar\n"
+"env_key3 = $ENV:FOO$ENV:FOO bar\n"
+"env_key4 = $ENV:foo $ENV:bar $key\n"
+"env_key5 = $ENV:foo $ENV:foo\n"
+"protocols = $protocols imap\n"
+ );
+
+ putenv("foo=test1");
+ putenv("bar=test2");
+ putenv("FOO$ENV:FOO=works");
+
+ test_assert(config_parse_file(TEST_CONFIG_FILE, TRUE, NULL, &error) == 1);
+ if (error != NULL)
+ i_error("config_parse_file(): %s", error);
+
+ /* get the parsed output */
+ const struct test_settings *set =
+ settings_parser_get(config_module_parsers[0].parser);
+ test_assert_strcmp(set->key, "value");
+ test_assert_strcmp(set->key2, "\\$escape \\escape \\\"escape\\\"");
+ test_assert_strcmp(set->key3, "yetanother value value nothervalue right here");
+ test_assert_strcmp(set->key4, " $key3 ");
+ test_assert_strcmp(set->key5, " $key4 ");
+ test_assert_strcmp(set->pop3_deleted_flag, "$Deleted");
+ test_assert_strcmp(set->env_key, "test1 test2 \"$env_key\"");
+ test_assert_strcmp(set->env_key2, "foo$ENV:FOO bar");
+ test_assert_strcmp(set->env_key3, "works bar");
+ test_assert_strcmp(set->env_key4, "test1 test2 value");
+ test_assert_strcmp(set->env_key5, "test1 test1");
+ test_assert_strcmp(set->protocols, "pop3 imap");
+
+ /* try again unexpanded */
+ test_assert(config_parse_file(TEST_CONFIG_FILE, FALSE, NULL, &error) == 1);
+ set = settings_parser_get(config_module_parsers[0].parser);
+
+ test_assert_strcmp(set->key, "value");
+ test_assert_strcmp(set->key2, "\\$escape \\escape \\\"escape\\\"");
+ test_assert_strcmp(set->key3, "yetanother value value nothervalue right here");
+ test_assert_strcmp(set->key4, " $key3 ");
+ test_assert_strcmp(set->key5, " $key4 ");
+ test_assert_strcmp(set->pop3_deleted_flag, "$Deleted");
+ test_assert_strcmp(set->env_key, "$ENV:foo $ENV:bar \"$env_key\"");
+ test_assert_strcmp(set->env_key2, "foo$ENV:FOO bar");
+ test_assert_strcmp(set->env_key3, "$ENV:FOO$ENV:FOO bar");
+ test_assert_strcmp(set->env_key4, "$ENV:foo $ENV:bar $key");
+ test_assert_strcmp(set->env_key5, "$ENV:foo $ENV:foo");
+ test_assert_strcmp(set->protocols, "pop3 imap");
+
+ config_filter_deinit(&config_filter);
+ config_parser_deinit();
+ i_unlink_if_exists(TEST_CONFIG_FILE);
+ test_end();
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_config_parser,
+ NULL
+ };
+ return test_run(test_functions);
+}