summaryrefslogtreecommitdiffstats
path: root/src/lmtp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lmtp/Makefile.am55
-rw-r--r--src/lmtp/Makefile.in916
-rw-r--r--src/lmtp/lmtp-client.c439
-rw-r--r--src/lmtp/lmtp-client.h124
-rw-r--r--src/lmtp/lmtp-commands.c340
-rw-r--r--src/lmtp/lmtp-commands.h45
-rw-r--r--src/lmtp/lmtp-common.h35
-rw-r--r--src/lmtp/lmtp-local.c766
-rw-r--r--src/lmtp/lmtp-local.h34
-rw-r--r--src/lmtp/lmtp-proxy.c860
-rw-r--r--src/lmtp/lmtp-proxy.h29
-rw-r--r--src/lmtp/lmtp-recipient.c57
-rw-r--r--src/lmtp/lmtp-recipient.h48
-rw-r--r--src/lmtp/lmtp-settings.c205
-rw-r--r--src/lmtp/lmtp-settings.h53
-rw-r--r--src/lmtp/main.c173
16 files changed, 4179 insertions, 0 deletions
diff --git a/src/lmtp/Makefile.am b/src/lmtp/Makefile.am
new file mode 100644
index 0000000..316e5cc
--- /dev/null
+++ b/src/lmtp/Makefile.am
@@ -0,0 +1,55 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = lmtp
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-auth \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-smtp \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-master \
+ -I$(top_srcdir)/src/lib-lda \
+ -I$(top_srcdir)/src/lib-ssl-iostream \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index \
+ -I$(top_srcdir)/src/lib-storage/index/raw \
+ -DMODULEDIR=\""$(moduledir)"\" \
+ $(BINARY_CFLAGS)
+
+lmtp_LDFLAGS = -export-dynamic \
+ $(BINARY_LDFLAGS)
+
+lmtp_LDADD = \
+ $(LIBDOVECOT_LDA) \
+ $(LIBDOVECOT_STORAGE) \
+ $(LIBDOVECOT)
+lmtp_DEPENDENCIES = \
+ $(LIBDOVECOT_LDA) \
+ $(LIBDOVECOT_STORAGE_DEPS) \
+ $(LIBDOVECOT_DEPS)
+
+lmtp_SOURCES = \
+ main.c \
+ lmtp-client.c \
+ lmtp-commands.c \
+ lmtp-recipient.c \
+ lmtp-local.c \
+ lmtp-proxy.c \
+ lmtp-settings.c
+
+noinst_HEADERS = \
+ lmtp-local.h \
+ lmtp-proxy.h
+
+headers = \
+ lmtp-common.h \
+ lmtp-commands.h \
+ lmtp-recipient.h \
+ lmtp-client.h \
+ lmtp-settings.h
+
+pkginc_libdir=$(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
diff --git a/src/lmtp/Makefile.in b/src/lmtp/Makefile.in
new file mode 100644
index 0000000..7cd9eba
--- /dev/null
+++ b/src/lmtp/Makefile.in
@@ -0,0 +1,916 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+pkglibexec_PROGRAMS = lmtp$(EXEEXT)
+subdir = src/lmtp
+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) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(pkglibexecdir)" \
+ "$(DESTDIR)$(pkginc_libdir)"
+PROGRAMS = $(pkglibexec_PROGRAMS)
+am_lmtp_OBJECTS = main.$(OBJEXT) lmtp-client.$(OBJEXT) \
+ lmtp-commands.$(OBJEXT) lmtp-recipient.$(OBJEXT) \
+ lmtp-local.$(OBJEXT) lmtp-proxy.$(OBJEXT) \
+ lmtp-settings.$(OBJEXT)
+lmtp_OBJECTS = $(am_lmtp_OBJECTS)
+am__DEPENDENCIES_1 =
+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 =
+lmtp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(lmtp_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)/lmtp-client.Po \
+ ./$(DEPDIR)/lmtp-commands.Po ./$(DEPDIR)/lmtp-local.Po \
+ ./$(DEPDIR)/lmtp-proxy.Po ./$(DEPDIR)/lmtp-recipient.Po \
+ ./$(DEPDIR)/lmtp-settings.Po ./$(DEPDIR)/main.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 = $(lmtp_SOURCES)
+DIST_SOURCES = $(lmtp_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) $(pkginc_lib_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@
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-auth \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-smtp \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-master \
+ -I$(top_srcdir)/src/lib-lda \
+ -I$(top_srcdir)/src/lib-ssl-iostream \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index \
+ -I$(top_srcdir)/src/lib-storage/index/raw \
+ -DMODULEDIR=\""$(moduledir)"\" \
+ $(BINARY_CFLAGS)
+
+lmtp_LDFLAGS = -export-dynamic \
+ $(BINARY_LDFLAGS)
+
+lmtp_LDADD = \
+ $(LIBDOVECOT_LDA) \
+ $(LIBDOVECOT_STORAGE) \
+ $(LIBDOVECOT)
+
+lmtp_DEPENDENCIES = \
+ $(LIBDOVECOT_LDA) \
+ $(LIBDOVECOT_STORAGE_DEPS) \
+ $(LIBDOVECOT_DEPS)
+
+lmtp_SOURCES = \
+ main.c \
+ lmtp-client.c \
+ lmtp-commands.c \
+ lmtp-recipient.c \
+ lmtp-local.c \
+ lmtp-proxy.c \
+ lmtp-settings.c
+
+noinst_HEADERS = \
+ lmtp-local.h \
+ lmtp-proxy.h
+
+headers = \
+ lmtp-common.h \
+ lmtp-commands.h \
+ lmtp-recipient.h \
+ lmtp-client.h \
+ lmtp-settings.h
+
+pkginc_libdir = $(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
+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/lmtp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lmtp/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-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
+
+lmtp$(EXEEXT): $(lmtp_OBJECTS) $(lmtp_DEPENDENCIES) $(EXTRA_lmtp_DEPENDENCIES)
+ @rm -f lmtp$(EXEEXT)
+ $(AM_V_CCLD)$(lmtp_LINK) $(lmtp_OBJECTS) $(lmtp_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-client.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-commands.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-local.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-proxy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-recipient.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmtp-settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || 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)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(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
+check: check-am
+all-am: Makefile $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(pkginc_libdir)"; 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-generic clean-libtool clean-pkglibexecPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/lmtp-client.Po
+ -rm -f ./$(DEPDIR)/lmtp-commands.Po
+ -rm -f ./$(DEPDIR)/lmtp-local.Po
+ -rm -f ./$(DEPDIR)/lmtp-proxy.Po
+ -rm -f ./$(DEPDIR)/lmtp-recipient.Po
+ -rm -f ./$(DEPDIR)/lmtp-settings.Po
+ -rm -f ./$(DEPDIR)/main.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-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: 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)/lmtp-client.Po
+ -rm -f ./$(DEPDIR)/lmtp-commands.Po
+ -rm -f ./$(DEPDIR)/lmtp-local.Po
+ -rm -f ./$(DEPDIR)/lmtp-proxy.Po
+ -rm -f ./$(DEPDIR)/lmtp-recipient.Po
+ -rm -f ./$(DEPDIR)/lmtp-settings.Po
+ -rm -f ./$(DEPDIR)/main.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-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool 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-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-pkginc_libHEADERS \
+ 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-pkginc_libHEADERS uninstall-pkglibexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# 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/lmtp/lmtp-client.c b/src/lmtp/lmtp-client.c
new file mode 100644
index 0000000..2757c80
--- /dev/null
+++ b/src/lmtp/lmtp-client.c
@@ -0,0 +1,439 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lmtp-common.h"
+#include "base64.h"
+#include "str.h"
+#include "llist.h"
+#include "iostream.h"
+#include "istream.h"
+#include "ostream.h"
+#include "hostpid.h"
+#include "process-title.h"
+#include "var-expand.h"
+#include "module-dir.h"
+#include "master-service-ssl.h"
+#include "master-service-settings.h"
+#include "iostream-ssl.h"
+#include "mail-namespace.h"
+#include "mail-storage.h"
+#include "mail-storage-service.h"
+#include "raw-storage.h"
+#include "lda-settings.h"
+#include "lmtp-local.h"
+#include "lmtp-proxy.h"
+#include "lmtp-commands.h"
+
+#include <unistd.h>
+
+#define CLIENT_IDLE_TIMEOUT_MSECS (1000*60*5)
+
+static const struct smtp_server_callbacks lmtp_callbacks;
+static const struct lmtp_client_vfuncs lmtp_client_vfuncs;
+
+struct lmtp_module_register lmtp_module_register = { 0 };
+
+static struct client *clients = NULL;
+static unsigned int clients_count = 0;
+
+static bool verbose_proctitle = FALSE;
+
+static const char *client_remote_id(struct client *client)
+{
+ const char *addr;
+
+ addr = net_ip2addr(&client->remote_ip);
+ if (addr[0] == '\0')
+ addr = "local";
+ return addr;
+}
+
+static void refresh_proctitle(void)
+{
+ struct client *client;
+ string_t *title;
+
+ if (!verbose_proctitle)
+ return;
+
+ title = t_str_new(128);
+ str_append_c(title, '[');
+ switch (clients_count) {
+ case 0:
+ str_append(title, "idling");
+ break;
+ case 1:
+ client = clients;
+ str_append(title, client_remote_id(client));
+ str_append_c(title, ' ');
+ str_append(title, smtp_server_state_names[client->state.state]);
+ if (client->state.args != NULL && *client->state.args != '\0') {
+ str_append_c(title, ' ');
+ str_append(title, client->state.args);
+ }
+ break;
+ default:
+ str_printfa(title, "%u connections", clients_count);
+ break;
+ }
+ str_append_c(title, ']');
+ process_title_set(str_c(title));
+}
+
+static void client_load_modules(struct client *client)
+{
+ struct module_dir_load_settings mod_set;
+
+ i_zero(&mod_set);
+ mod_set.abi_version = DOVECOT_ABI_VERSION;
+ mod_set.require_init_funcs = TRUE;
+ mod_set.binary_name = "lmtp";
+
+ /* pre-load all configured mail plugins */
+ mail_storage_service_modules =
+ module_dir_load_missing(mail_storage_service_modules,
+ client->lmtp_set->mail_plugin_dir,
+ client->lmtp_set->mail_plugins,
+ &mod_set);
+ module_dir_init(mail_storage_service_modules);
+}
+
+static void client_raw_user_create(struct client *client)
+{
+ void **sets;
+
+ sets = master_service_settings_get_others(master_service);
+ client->raw_mail_user =
+ raw_storage_create_from_set(client->user_set_info, sets[0]);
+}
+
+static void client_read_settings(struct client *client, bool ssl)
+{
+ struct mail_storage_service_input input;
+ const struct setting_parser_context *set_parser;
+ struct mail_user_settings *user_set;
+ struct lmtp_settings *lmtp_set;
+ struct lda_settings *lda_set;
+ const char *error;
+
+ i_zero(&input);
+ input.module = input.service = "lmtp";
+ input.local_ip = client->local_ip;
+ input.remote_ip = client->remote_ip;
+ input.local_port = client->local_port;
+ input.remote_port = client->remote_port;
+ input.conn_secured = ssl;
+ input.conn_ssl_secured = ssl;
+ input.username = "";
+
+ if (mail_storage_service_read_settings(storage_service, &input,
+ client->pool,
+ &client->user_set_info,
+ &set_parser, &error) < 0)
+ i_fatal("%s", error);
+
+ lmtp_settings_dup(set_parser, client->pool,
+ &user_set, &lmtp_set, &lda_set);
+ const struct var_expand_table *tab =
+ mail_storage_service_get_var_expand_table(storage_service, &input);
+ if (settings_var_expand(&lmtp_setting_parser_info, lmtp_set,
+ client->pool, tab, &error) <= 0)
+ i_fatal("Failed to expand settings: %s", error);
+ client->service_set = master_service_settings_get(master_service);
+ client->user_set = user_set;
+ client->lmtp_set = lmtp_set;
+ client->unexpanded_lda_set = lda_set;
+}
+
+struct client *client_create(int fd_in, int fd_out,
+ const struct master_service_connection *conn)
+{
+ static const char *rcpt_param_extensions[] = {
+ LMTP_RCPT_FORWARD_PARAMETER, NULL };
+ static const struct smtp_capability_extra cap_rcpt_forward = {
+ .name = LMTP_RCPT_FORWARD_CAPABILITY };
+ enum lmtp_client_workarounds workarounds;
+ struct smtp_server_settings lmtp_set;
+ struct client *client;
+ pool_t pool;
+
+ pool = pool_alloconly_create("lmtp client", 2048);
+ client = p_new(pool, struct client, 1);
+ client->pool = pool;
+ client->v = lmtp_client_vfuncs;
+ client->remote_ip = conn->remote_ip;
+ client->remote_port = conn->remote_port;
+ client->local_ip = conn->local_ip;
+ client->local_port = conn->local_port;
+ client->real_local_ip = conn->real_local_ip;
+ client->real_local_port = conn->real_local_port;
+ client->real_remote_ip = conn->real_remote_ip;
+ client->real_remote_port = conn->real_remote_port;
+ client->state_pool = pool_alloconly_create("client state", 4096);
+
+ client->event = event_create(NULL);
+ event_add_category(client->event, &event_category_lmtp);
+
+ client_read_settings(client, conn->ssl);
+ client_raw_user_create(client);
+ client_load_modules(client);
+ client->my_domain = client->unexpanded_lda_set->hostname;
+
+ if (client->service_set->verbose_proctitle)
+ verbose_proctitle = TRUE;
+
+ p_array_init(&client->module_contexts, client->pool, 5);
+
+ i_zero(&lmtp_set);
+ lmtp_set.capabilities =
+ SMTP_CAPABILITY_PIPELINING |
+ SMTP_CAPABILITY_ENHANCEDSTATUSCODES |
+ SMTP_CAPABILITY_8BITMIME |
+ SMTP_CAPABILITY_CHUNKING |
+ SMTP_CAPABILITY_XCLIENT |
+ SMTP_CAPABILITY__ORCPT;
+ if (!conn->ssl && master_service_ssl_is_enabled(master_service))
+ lmtp_set.capabilities |= SMTP_CAPABILITY_STARTTLS;
+ lmtp_set.hostname = client->unexpanded_lda_set->hostname;
+ lmtp_set.login_greeting = client->lmtp_set->login_greeting;
+ lmtp_set.max_message_size = UOFF_T_MAX;
+ lmtp_set.rcpt_param_extensions = rcpt_param_extensions;
+ lmtp_set.rcpt_domain_optional = TRUE;
+ lmtp_set.max_client_idle_time_msecs = CLIENT_IDLE_TIMEOUT_MSECS;
+ lmtp_set.rawlog_dir = client->lmtp_set->lmtp_rawlog_dir;
+ lmtp_set.event_parent = client->event;
+
+ workarounds = client->lmtp_set->parsed_workarounds;
+ if ((workarounds & LMTP_WORKAROUND_WHITESPACE_BEFORE_PATH) != 0) {
+ lmtp_set.workarounds |=
+ SMTP_SERVER_WORKAROUND_WHITESPACE_BEFORE_PATH;
+ }
+ if ((workarounds & LMTP_WORKAROUND_MAILBOX_FOR_PATH) != 0) {
+ lmtp_set.workarounds |=
+ SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH;
+ }
+
+ client->conn = smtp_server_connection_create(
+ lmtp_server, fd_in, fd_out,
+ &conn->remote_ip, conn->remote_port, conn->ssl,
+ &lmtp_set, &lmtp_callbacks, client);
+ if (smtp_server_connection_is_trusted(client->conn)) {
+ smtp_server_connection_add_extra_capability(
+ client->conn, &cap_rcpt_forward);
+ }
+
+ DLLIST_PREPEND(&clients, client);
+ clients_count++;
+
+ e_info(client->event, "Connect from %s", client_remote_id(client));
+
+ if (hook_client_created != NULL)
+ hook_client_created(&client);
+
+ smtp_server_connection_start(client->conn);
+
+ refresh_proctitle();
+ return client;
+}
+
+void client_state_reset(struct client *client)
+{
+ i_free(client->state.args);
+
+ if (client->local != NULL)
+ lmtp_local_deinit(&client->local);
+ if (client->proxy != NULL)
+ lmtp_proxy_deinit(&client->proxy);
+
+ o_stream_unref(&client->state.mail_data_output);
+
+ i_zero(&client->state);
+ p_clear(client->state_pool);
+}
+
+void client_destroy(struct client **_client, const char *enh_code,
+ const char *reason)
+{
+ struct client *client = *_client;
+
+ *_client = NULL;
+
+ smtp_server_connection_terminate(&client->conn,
+ (enh_code == NULL ? "4.0.0" : enh_code), reason);
+}
+
+static void
+client_default_destroy(struct client *client)
+{
+ if (client->destroyed)
+ return;
+ client->destroyed = TRUE;
+
+ clients_count--;
+ DLLIST_REMOVE(&clients, client);
+
+ if (client->raw_mail_user != NULL)
+ mail_user_deinit(&client->raw_mail_user);
+
+ client_state_reset(client);
+ event_unref(&client->event);
+ pool_unref(&client->state_pool);
+ pool_unref(&client->pool);
+
+ master_service_client_connection_destroyed(master_service);
+}
+
+static void
+client_connection_trans_start(void *context,
+ struct smtp_server_transaction *trans)
+{
+ struct client *client = context;
+
+ client->v.trans_start(client, trans);
+}
+
+static void
+client_default_trans_start(struct client *client ATTR_UNUSED,
+ struct smtp_server_transaction *trans ATTR_UNUSED)
+{
+ /* nothing */
+}
+
+static void
+client_connection_trans_free(void *context,
+ struct smtp_server_transaction *trans)
+{
+ struct client *client = (struct client *)context;
+
+ client->v.trans_free(client, trans);
+}
+
+static void
+client_default_trans_free(struct client *client,
+ struct smtp_server_transaction *trans ATTR_UNUSED)
+{
+ client_state_reset(client);
+}
+
+static void
+client_connection_state_changed(void *context,
+ enum smtp_server_state new_state,
+ const char *new_args)
+{
+ struct client *client = (struct client *)context;
+
+ i_free(client->state.args);
+
+ client->state.state = new_state;
+ client->state.args = i_strdup(new_args);
+
+ if (clients_count == 1)
+ refresh_proctitle();
+}
+
+void client_update_data_state(struct client *client, const char *new_args)
+{
+ i_assert(client->state.state == SMTP_SERVER_STATE_DATA);
+ i_free(client->state.args);
+ client->state.args = i_strdup(new_args);
+
+ if (clients_count == 1)
+ refresh_proctitle();
+}
+
+static void
+client_connection_proxy_data_updated(void *context,
+ const struct smtp_proxy_data *data)
+{
+ struct client *client = (struct client *)context;
+
+ client->remote_ip = data->source_ip;
+ client->remote_port = data->source_port;
+
+ if (clients_count == 1)
+ refresh_proctitle();
+}
+
+static void client_connection_disconnect(void *context, const char *reason)
+{
+ struct client *client = (struct client *)context;
+
+ if (client->disconnected)
+ return;
+ client->disconnected = TRUE;
+
+ if (reason == NULL)
+ reason = "Connection closed";
+ e_info(client->event, "Disconnect from %s: %s",
+ client_remote_id(client), reason);
+}
+
+static void client_connection_free(void *context)
+{
+ struct client *client = (struct client *)context;
+
+ client->v.destroy(client);
+}
+
+static bool client_connection_is_trusted(void *context)
+{
+ struct client *client = (struct client *)context;
+ const char *const *net;
+ struct ip_addr net_ip;
+ unsigned int bits;
+
+ if (client->lmtp_set->login_trusted_networks == NULL)
+ return FALSE;
+
+ net = t_strsplit_spaces(client->lmtp_set->login_trusted_networks, ", ");
+ for (; *net != NULL; net++) {
+ if (net_parse_range(*net, &net_ip, &bits) < 0) {
+ e_error(client->event, "login_trusted_networks: "
+ "Invalid network '%s'", *net);
+ break;
+ }
+
+ if (net_is_in_network(&client->real_remote_ip, &net_ip, bits))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void clients_destroy(void)
+{
+ while (clients != NULL) {
+ struct client *client = clients;
+ client_destroy(&client, "4.3.2", "Shutting down");
+ }
+}
+
+static const struct smtp_server_callbacks lmtp_callbacks = {
+ .conn_cmd_mail = cmd_mail,
+ .conn_cmd_rcpt = cmd_rcpt,
+ .conn_cmd_data_begin = cmd_data_begin,
+ .conn_cmd_data_continue = cmd_data_continue,
+
+ .conn_trans_start = client_connection_trans_start,
+ .conn_trans_free = client_connection_trans_free,
+
+ .conn_state_changed = client_connection_state_changed,
+
+ .conn_proxy_data_updated = client_connection_proxy_data_updated,
+
+ .conn_disconnect = client_connection_disconnect,
+ .conn_free = client_connection_free,
+
+ .conn_is_trusted = client_connection_is_trusted
+};
+
+static const struct lmtp_client_vfuncs lmtp_client_vfuncs = {
+ .destroy = client_default_destroy,
+
+ .trans_start = client_default_trans_start,
+ .trans_free = client_default_trans_free,
+
+ .cmd_mail = client_default_cmd_mail,
+ .cmd_rcpt = client_default_cmd_rcpt,
+ .cmd_data = client_default_cmd_data,
+
+ .local_deliver = lmtp_local_default_deliver,
+};
diff --git a/src/lmtp/lmtp-client.h b/src/lmtp/lmtp-client.h
new file mode 100644
index 0000000..0e7d963
--- /dev/null
+++ b/src/lmtp/lmtp-client.h
@@ -0,0 +1,124 @@
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "net.h"
+#include "smtp-server.h"
+
+#define CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE (1024*128)
+
+struct mail_storage;
+struct mail_deliver_context;
+union lmtp_module_context;
+struct lmtp_recipient;
+struct client;
+
+struct lmtp_local_deliver_context {
+ struct mail *src_mail;
+ const char *session_id;
+ struct timeval delivery_time_started;
+
+ struct mail_user *rcpt_user;
+ const char *rcpt_default_mailbox;
+
+ const struct mail_storage_settings *mail_set;
+ const struct smtp_submit_settings *smtp_set;
+ const struct lda_settings *lda_set;
+
+ struct mail_deliver_session *session;
+};
+
+struct client_state {
+ enum smtp_server_state state;
+ char *args;
+ unsigned int session_id_seq;
+
+ struct istream *data_input;
+ uoff_t data_size;
+
+ struct timeval data_end_timeval;
+
+ struct ostream *mail_data_output;
+
+ const char *added_headers_local;
+ const char *added_headers_proxy;
+};
+
+struct lmtp_client_vfuncs {
+ void (*destroy)(struct client *client);
+
+ void (*trans_start)(struct client *client,
+ struct smtp_server_transaction *trans);
+ void (*trans_free)(struct client *client,
+ struct smtp_server_transaction *trans);
+
+ int (*cmd_mail)(struct client *client, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_mail *data);
+ int (*cmd_rcpt)(struct client *client, struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *lrcpt);
+ int (*cmd_data)(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *data_input, uoff_t data_size);
+
+ int (*local_deliver)(struct client *client,
+ struct lmtp_recipient *lrcpt,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct lmtp_local_deliver_context *lldctx);
+};
+
+struct client {
+ struct client *prev, *next;
+ pool_t pool;
+
+ struct lmtp_client_vfuncs v;
+ struct event *event;
+
+ const struct setting_parser_info *user_set_info;
+ const struct mail_user_settings *user_set;
+ const struct lda_settings *unexpanded_lda_set;
+ const struct lmtp_settings *lmtp_set;
+ const struct master_service_settings *service_set;
+
+ struct smtp_server_connection *conn;
+
+ struct ip_addr remote_ip, local_ip, real_local_ip, real_remote_ip;
+ in_port_t remote_port, local_port, real_local_port, real_remote_port;
+
+ struct mail_user *raw_mail_user;
+ const char *my_domain;
+
+ pool_t state_pool;
+ struct client_state state;
+ struct istream *dot_input;
+ struct lmtp_local *local;
+ struct lmtp_proxy *proxy;
+
+ /* Module-specific contexts. */
+ ARRAY(union lmtp_module_context *) module_contexts;
+
+ bool disconnected:1;
+ bool destroyed:1;
+};
+
+struct lmtp_module_register {
+ unsigned int id;
+};
+
+union lmtp_module_context {
+ struct lmtp_client_vfuncs super;
+ struct lmtp_module_register *reg;
+};
+extern struct lmtp_module_register lmtp_module_register;
+
+struct client *client_create(int fd_in, int fd_out,
+ const struct master_service_connection *conn);
+void client_destroy(struct client **client, const char *enh_code,
+ const char *reason) ATTR_NULL(2, 3);
+
+void client_state_reset(struct client *client);
+void client_update_data_state(struct client *client, const char *new_args);
+
+void clients_destroy(void);
+
+#endif
diff --git a/src/lmtp/lmtp-commands.c b/src/lmtp/lmtp-commands.c
new file mode 100644
index 0000000..dd8b791
--- /dev/null
+++ b/src/lmtp/lmtp-commands.c
@@ -0,0 +1,340 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lmtp-common.h"
+#include "str.h"
+#include "istream.h"
+#include "istream-concat.h"
+#include "ostream.h"
+#include "iostream-temp.h"
+#include "master-service.h"
+#include "settings-parser.h"
+#include "lda-settings.h"
+#include "mail-user.h"
+#include "smtp-address.h"
+#include "mail-deliver.h"
+#include "mail-error.h"
+#include "lmtp-recipient.h"
+#include "lmtp-proxy.h"
+#include "lmtp-local.h"
+#include "lmtp-commands.h"
+
+/*
+ * MAIL command
+ */
+
+int cmd_mail(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_mail *data)
+{
+ struct client *client = (struct client *)conn_ctx;
+
+ return client->v.cmd_mail(client, cmd, data);
+}
+
+int client_default_cmd_mail(struct client *client,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_cmd_mail *data ATTR_UNUSED)
+{
+ if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
+ /* Connect to anvil before dropping privileges */
+ lmtp_anvil_init();
+ }
+ return 1;
+}
+
+/*
+ * RCPT command
+ */
+
+static int
+cmd_rcpt_handle_forward_fields(struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *lrcpt)
+{
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ string_t *xforward;
+ const char *error;
+ int ret;
+
+ ret = smtp_params_rcpt_decode_extra(&rcpt->params,
+ LMTP_RCPT_FORWARD_PARAMETER,
+ &xforward, FALSE, &error);
+ if (ret < 0) {
+ smtp_server_reply(cmd, 501, "5.5.4",
+ "Invalid "LMTP_RCPT_FORWARD_PARAMETER"= "
+ "parameter: %s", error);
+ return -1;
+ }
+ if (ret == 0)
+ return 0;
+
+ /* Drop the parameter */
+ (void)smtp_params_rcpt_drop_extra(&rcpt->params,
+ LMTP_RCPT_FORWARD_PARAMETER, NULL);
+
+ /* Check the real IP rather than the proxied client IP, since XCLIENT
+ command will update that, thereby making it untrusted. Unlike the
+ XCLIENT command, the RCPT forward parameter needs to be used after
+ the XCLIENT is first issued. */
+ if (!smtp_server_connection_is_trusted(rcpt->conn)) {
+ smtp_server_reply(cmd, 550, "5.7.14",
+ "Unacceptable "LMTP_RCPT_FORWARD_PARAMETER"= "
+ "parameter: You are not from trusted IP");
+ return -1;
+ }
+
+ lrcpt->forward_fields = p_strdup(rcpt->pool, str_c(xforward));
+ return 0;
+}
+
+int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_recipient *rcpt)
+{
+ struct client *client = (struct client *)conn_ctx;
+ struct smtp_server_transaction *trans;
+ struct lmtp_recipient *lrcpt;
+
+ trans = smtp_server_connection_get_transaction(rcpt->conn);
+ i_assert(trans != NULL); /* MAIL command is synchronous */
+
+ lrcpt = lmtp_recipient_create(client, trans, rcpt);
+
+ if (cmd_rcpt_handle_forward_fields(cmd, lrcpt) < 0)
+ return -1;
+
+ return client->v.cmd_rcpt(client, cmd, lrcpt);
+}
+
+int client_default_cmd_rcpt(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *lrcpt)
+{
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ const char *username, *detail;
+ char delim = '\0';
+ int ret;
+
+ i_assert(!smtp_address_isnull(rcpt->path));
+ if (*rcpt->path->localpart == '\0' && rcpt->path->domain == NULL) {
+ smtp_server_recipient_reply(
+ rcpt, 550, "5.1.1",
+ "Unacceptable TO: Empty path not allowed");
+ return -1;
+ }
+
+ smtp_address_detail_parse_temp(
+ client->unexpanded_lda_set->recipient_delimiter,
+ rcpt->path, &username, &delim, &detail);
+ i_assert(*username != '\0');
+
+ /* Make user name and detail available in the recipient event. The
+ mail_user event (for local delivery) also adds the user field, but
+ adding it here makes it available to the recipient event in general.
+ Additionally, the auth lookups performed for local and proxy delivery
+ can further override the "user" recipient event when the auth service
+ returns a different user name. In any case, we provide the initial
+ value here.
+ */
+ event_add_str(rcpt->event, "user", username);
+ if (detail[0] != '\0')
+ event_add_str(rcpt->event, "detail", detail);
+
+ if (client->lmtp_set->lmtp_proxy) {
+ /* proxied? */
+ if ((ret=lmtp_proxy_rcpt(client, cmd, lrcpt,
+ username, detail, delim)) != 0)
+ return (ret < 0 ? -1 : 0);
+ /* no */
+ }
+
+ /* local delivery */
+ return lmtp_local_rcpt(client, cmd, lrcpt, username, detail);
+}
+
+/*
+ * DATA command
+ */
+
+static void
+cmd_data_create_added_headers(struct client *client,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans)
+{
+ size_t proxy_offset = 0;
+ string_t *str;
+
+ str = t_str_new(512);
+
+ /* Headers for local deliveries only */
+ if (client->local != NULL)
+ lmtp_local_add_headers(client->local, trans, str);
+
+ /* Headers for local and proxied messages */
+ proxy_offset = str_len(str);
+ if (client->lmtp_set->lmtp_add_received_header) {
+ const struct lmtp_settings *lmtp_set = client->lmtp_set;
+ enum smtp_server_trace_rcpt_to_address rcpt_to_address =
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_FINAL;
+
+ switch (lmtp_set->parsed_lmtp_hdr_delivery_address) {
+ case LMTP_HDR_DELIVERY_ADDRESS_NONE:
+ rcpt_to_address =
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_NONE;
+ break;
+ case LMTP_HDR_DELIVERY_ADDRESS_FINAL:
+ rcpt_to_address =
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_FINAL;
+ break;
+ case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL:
+ rcpt_to_address =
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_ORIGINAL;
+ break;
+ }
+
+ smtp_server_transaction_write_trace_record(
+ str, trans, rcpt_to_address);
+ }
+
+ client->state.added_headers_local =
+ p_strdup(client->state_pool, str_c(str));
+ client->state.added_headers_proxy =
+ client->state.added_headers_local + proxy_offset;
+}
+
+static int
+cmd_data_finish(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans)
+{
+ struct client_state *state = &client->state;
+ struct istream *input_msg;
+ int ret;
+
+ i_assert(HAS_ALL_BITS(trans->flags,
+ SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT));
+
+ client->state.data_end_timeval = ioloop_timeval;
+
+ /* finish the message */
+ input_msg = iostream_temp_finish(&state->mail_data_output,
+ IO_BLOCK_SIZE);
+
+ ret = client->v.cmd_data(client, cmd, trans,
+ input_msg, client->state.data_size);
+ i_stream_unref(&input_msg);
+
+ return ret;
+}
+
+int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans)
+{
+ struct client *client = (struct client *)conn_ctx;
+ struct client_state *state = &client->state;
+ struct istream *data_input = state->data_input;
+ const unsigned char *data;
+ size_t size;
+ ssize_t ret;
+
+ i_assert(state->mail_data_output != NULL);
+
+ while ((ret = i_stream_read(data_input)) > 0 || ret == -2) {
+ data = i_stream_get_data(data_input, &size);
+ if (o_stream_send(state->mail_data_output,
+ data, size) != (ssize_t)size) {
+ e_error(client->event, "write(%s) failed: %s",
+ o_stream_get_name(state->mail_data_output),
+ o_stream_get_error(state->mail_data_output));
+ smtp_server_reply(cmd, 451, "4.3.0",
+ "Temporary internal failure");
+ return -1;
+ }
+
+ i_stream_skip(data_input, size);
+
+ if (!smtp_server_cmd_data_check_size(cmd))
+ return -1;
+ }
+
+ if (ret == 0)
+ return 0;
+ if (ret < 0 && data_input->stream_errno != 0) {
+ /* Client probably disconnected */
+ return -1;
+ }
+
+ /* Current data stream position is the data size */
+ client->state.data_size = data_input->v_offset;
+
+ /* The ending "." line was seen. finish delivery. */
+ return cmd_data_finish(client, cmd, trans);
+}
+
+int cmd_data_begin(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct istream *data_input)
+{
+ struct client *client = (struct client *)conn_ctx;
+ string_t *path;
+
+ i_assert(client->state.mail_data_output == NULL);
+
+ path = t_str_new(256);
+ mail_user_set_get_temp_prefix(path, client->raw_mail_user->set);
+ client->state.mail_data_output =
+ iostream_temp_create_named(str_c(path), 0, "(lmtp data)");
+
+ client->state.data_input = data_input;
+ return 0;
+}
+
+int client_default_cmd_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *data_input,
+ uoff_t data_size ATTR_UNUSED)
+{
+ struct client_state *state = &client->state;
+ struct istream *input_local, *input_proxy;
+ struct istream *inputs[3];
+
+ /* Formulate prepended headers for both local and proxy delivery */
+ cmd_data_create_added_headers(client, cmd, trans);
+
+ /* Construct message streams for local and proxy delivery */
+ input_local = input_proxy = NULL;
+ if (client->local != NULL) {
+ inputs[0] = i_stream_create_from_data(
+ state->added_headers_local,
+ strlen(state->added_headers_local));
+ inputs[1] = data_input;
+ inputs[2] = NULL;
+
+ input_local = i_stream_create_concat(inputs);
+ i_stream_set_name(input_local, "<lmtp DATA local>");
+ i_stream_unref(&inputs[0]);
+ }
+ if (client->proxy != NULL) {
+ inputs[0] = i_stream_create_from_data(
+ state->added_headers_proxy,
+ strlen(state->added_headers_proxy));
+ inputs[1] = data_input;
+ inputs[2] = NULL;
+
+ input_proxy = i_stream_create_concat(inputs);
+ i_stream_set_name(input_proxy, "<lmtp DATA proxy>");
+ i_stream_unref(&inputs[0]);
+ }
+
+ /* local delivery */
+ if (client->local != NULL) {
+ lmtp_local_data(client, cmd, trans, input_local);
+ i_stream_unref(&input_local);
+ }
+ /* proxy delivery */
+ if (client->proxy != NULL) {
+ lmtp_proxy_data(client, cmd, trans, input_proxy);
+ i_stream_unref(&input_proxy);
+ }
+ return 0;
+}
diff --git a/src/lmtp/lmtp-commands.h b/src/lmtp/lmtp-commands.h
new file mode 100644
index 0000000..f580c86
--- /dev/null
+++ b/src/lmtp/lmtp-commands.h
@@ -0,0 +1,45 @@
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+struct client;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_helo;
+
+/*
+ * MAIL command
+ */
+
+int cmd_mail(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_mail *data);
+
+int client_default_cmd_mail(struct client *client,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_cmd_mail *data ATTR_UNUSED);
+
+/*
+ * RCPT command
+ */
+
+int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_recipient *rcpt);
+
+int client_default_cmd_rcpt(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *lrcpt);
+
+/*
+ * DATA command
+ */
+
+int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans);
+int cmd_data_begin(void *conn_ctx, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans,
+ struct istream *data_input);
+
+int client_default_cmd_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *data_input, uoff_t data_size);
+
+#endif
diff --git a/src/lmtp/lmtp-common.h b/src/lmtp/lmtp-common.h
new file mode 100644
index 0000000..8e1729c
--- /dev/null
+++ b/src/lmtp/lmtp-common.h
@@ -0,0 +1,35 @@
+#ifndef LMTP_COMMON_H
+#define LMTP_COMMON_H
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "settings-parser.h"
+#include "master-service.h"
+#include "smtp-reply.h"
+#include "smtp-server.h"
+#include "lmtp-client.h"
+#include "lmtp-settings.h"
+
+#define LMTP_RCPT_FORWARD_CAPABILITY "XRCPTFORWARD"
+#define LMTP_RCPT_FORWARD_PARAMETER "XRCPTFORWARD"
+
+typedef void lmtp_client_created_func_t(struct client **client);
+
+extern lmtp_client_created_func_t *hook_client_created;
+extern struct event_category event_category_lmtp;
+
+extern char *dns_client_socket_path, *base_dir;
+extern struct mail_storage_service_ctx *storage_service;
+extern struct anvil_client *anvil;
+
+extern struct smtp_server *lmtp_server;
+
+/* Sets the hook_client_created and returns the previous hook,
+ which the new_hook should call if it's non-NULL. */
+lmtp_client_created_func_t *
+lmtp_client_created_hook_set(lmtp_client_created_func_t *new_hook);
+
+void lmtp_anvil_init(void);
+
+#endif
diff --git a/src/lmtp/lmtp-local.c b/src/lmtp/lmtp-local.c
new file mode 100644
index 0000000..c20b619
--- /dev/null
+++ b/src/lmtp/lmtp-local.c
@@ -0,0 +1,766 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lmtp-common.h"
+#include "str.h"
+#include "istream.h"
+#include "strescape.h"
+#include "time-util.h"
+#include "hostpid.h"
+#include "var-expand.h"
+#include "restrict-access.h"
+#include "anvil-client.h"
+#include "settings-parser.h"
+#include "mail-storage.h"
+#include "mail-storage-service.h"
+#include "mail-namespace.h"
+#include "mail-deliver.h"
+#include "mail-autoexpunge.h"
+#include "index/raw/raw-storage.h"
+#include "smtp-common.h"
+#include "smtp-params.h"
+#include "smtp-address.h"
+#include "smtp-submit-settings.h"
+#include "lda-settings.h"
+#include "lmtp-settings.h"
+#include "lmtp-recipient.h"
+#include "lmtp-local.h"
+
+struct lmtp_local_recipient {
+ struct lmtp_recipient *rcpt;
+
+ char *detail;
+
+ struct mail_storage_service_user *service_user;
+ struct anvil_query *anvil_query;
+
+ struct lmtp_local_recipient *duplicate;
+
+ bool anvil_connect_sent:1;
+};
+
+struct lmtp_local {
+ struct client *client;
+
+ ARRAY(struct lmtp_local_recipient *) rcpt_to;
+
+ struct mail *raw_mail, *first_saved_mail;
+ struct mail_user *rcpt_user;
+};
+
+/*
+ * LMTP local
+ */
+
+static struct lmtp_local *
+lmtp_local_init(struct client *client)
+{
+ struct lmtp_local *local;
+
+ local = i_new(struct lmtp_local, 1);
+ local->client = client;
+ i_array_init(&local->rcpt_to, 8);
+
+ return local;
+}
+
+void lmtp_local_deinit(struct lmtp_local **_local)
+{
+ struct lmtp_local *local = *_local;
+
+ *_local = NULL;
+
+ if (array_is_created(&local->rcpt_to))
+ array_free(&local->rcpt_to);
+
+ if (local->raw_mail != NULL) {
+ struct mailbox_transaction_context *raw_trans =
+ local->raw_mail->transaction;
+ struct mailbox *raw_box = local->raw_mail->box;
+
+ mail_free(&local->raw_mail);
+ mailbox_transaction_rollback(&raw_trans);
+ mailbox_free(&raw_box);
+ }
+
+ i_free(local);
+}
+
+/*
+ * Recipient
+ */
+
+static void
+lmtp_local_rcpt_anvil_disconnect(struct lmtp_local_recipient *llrcpt)
+{
+ const struct mail_storage_service_input *input;
+
+ if (!llrcpt->anvil_connect_sent)
+ return;
+ llrcpt->anvil_connect_sent = FALSE;
+
+ input = mail_storage_service_user_get_input(llrcpt->service_user);
+ master_service_anvil_send(master_service, t_strconcat(
+ "DISCONNECT\t", my_pid, "\t", master_service_get_name(master_service),
+ "/", input->username, "\n", NULL));
+}
+
+static void
+lmtp_local_rcpt_destroy(struct smtp_server_recipient *rcpt ATTR_UNUSED,
+ struct lmtp_local_recipient *llrcpt)
+{
+ if (llrcpt->anvil_query != NULL)
+ anvil_client_query_abort(anvil, &llrcpt->anvil_query);
+ lmtp_local_rcpt_anvil_disconnect(llrcpt);
+ mail_storage_service_user_unref(&llrcpt->service_user);
+}
+
+static void
+lmtp_local_rcpt_reply_overquota(struct lmtp_local_recipient *llrcpt,
+ const char *error)
+{
+ struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt;
+ struct lda_settings *lda_set =
+ mail_storage_service_user_get_set(llrcpt->service_user)[2];
+
+ if (lda_set->quota_full_tempfail)
+ smtp_server_recipient_reply(rcpt, 452, "4.2.2", "%s", error);
+ else
+ smtp_server_recipient_reply(rcpt, 552, "5.2.2", "%s", error);
+}
+
+static void ATTR_FORMAT(4,5)
+lmtp_local_rcpt_fail_all(struct lmtp_local *local,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...)
+{
+ struct lmtp_local_recipient *const *llrcpts;
+ const char *msg;
+ unsigned int count, i;
+ va_list args;
+
+ va_start(args, fmt);
+ msg = t_strdup_vprintf(fmt, args);
+ va_end(args);
+
+ llrcpts = array_get(&local->rcpt_to, &count);
+ for (i = 0; i < count; i++) {
+ struct smtp_server_recipient *rcpt = llrcpts[i]->rcpt->rcpt;
+
+ smtp_server_recipient_reply(rcpt, status, enh_code, "%s", msg);
+ }
+}
+
+/*
+ * RCPT command
+ */
+
+static int
+lmtp_local_rcpt_check_quota(struct lmtp_local_recipient *llrcpt)
+{
+ struct client *client = llrcpt->rcpt->client;
+ struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt;
+ struct smtp_address *address = rcpt->path;
+ struct mail_user *user;
+ struct mail_namespace *ns;
+ struct mailbox *box;
+ struct mailbox_status status;
+ enum mail_error mail_error;
+ const char *error;
+ int ret;
+
+ if (!client->lmtp_set->lmtp_rcpt_check_quota)
+ return 0;
+
+ /* mail user will be created second time when mail is saved,
+ so it's session_id needs to be different,
+ but second time session_id needs to be the same as rcpt session_id and
+ mail user session id for the first rcpt should not overlap with session id
+ of the second recipient, so add custom ":quota" suffix to the session_id without
+ session_id counter increment, so next time mail user will get
+ the same session id as rcpt */
+ ret = mail_storage_service_next_with_session_suffix(storage_service,
+ llrcpt->service_user,
+ "quota",
+ &user, &error);
+
+ if (ret < 0) {
+ e_error(rcpt->event, "Failed to initialize user %s: %s",
+ smtp_address_encode(address), error);
+ ret = -1;
+ } else {
+ /* Set the log prefix for the user. The default log prefix is
+ automatically restored later when user context gets
+ deactivated. */
+ i_set_failure_prefix("%s",
+ mail_storage_service_user_get_log_prefix(llrcpt->service_user));
+ ns = mail_namespace_find_inbox(user->namespaces);
+ box = mailbox_alloc(ns->list, "INBOX", 0);
+ ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status);
+ if (ret < 0) {
+ error = mailbox_get_last_error(box, &mail_error);
+ if (mail_error == MAIL_ERROR_NOQUOTA) {
+ lmtp_local_rcpt_reply_overquota(llrcpt, error);
+ } else {
+ e_error(rcpt->event,
+ "mailbox_get_status(%s, STATUS_CHECK_OVER_QUOTA) "
+ "failed: %s",
+ mailbox_get_vname(box),
+ mailbox_get_last_internal_error(box, NULL));
+ }
+ ret = -1;
+ }
+ mailbox_free(&box);
+ mail_user_deinit(&user);
+ mail_storage_service_io_deactivate_user(llrcpt->service_user);
+ }
+
+ if (ret < 0 && !smtp_server_recipient_is_replied(rcpt)) {
+ smtp_server_recipient_reply(rcpt, 451, "4.3.0",
+ "Temporary internal error");
+ }
+ return ret;
+}
+
+static void
+lmtp_local_rcpt_approved(struct smtp_server_recipient *rcpt,
+ struct lmtp_local_recipient *llrcpt)
+{
+ struct client *client = llrcpt->rcpt->client;
+ struct lmtp_recipient *drcpt;
+
+ /* resolve duplicate recipient */
+ drcpt = lmtp_recipient_find_duplicate(llrcpt->rcpt, rcpt->trans);
+ if (drcpt != NULL) {
+ i_assert(drcpt->type == LMTP_RECIPIENT_TYPE_LOCAL);
+ llrcpt->duplicate = drcpt->backend_context;
+ i_assert(llrcpt->duplicate->duplicate == NULL);
+ }
+
+ /* add to local recipients */
+ array_push_back(&client->local->rcpt_to, &llrcpt);
+}
+
+static bool
+lmtp_local_rcpt_anvil_finish(struct lmtp_local_recipient *llrcpt)
+{
+ struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt;
+ struct smtp_server_cmd_ctx *cmd = rcpt->cmd;
+
+ if (lmtp_local_rcpt_check_quota(llrcpt) < 0)
+ return FALSE;
+
+ smtp_server_cmd_rcpt_reply_success(cmd);
+ return TRUE;
+}
+
+static void
+lmtp_local_rcpt_anvil_cb(const char *reply, void *context)
+{
+ struct lmtp_local_recipient *llrcpt =
+ (struct lmtp_local_recipient *)context;
+ struct client *client = llrcpt->rcpt->client;
+ struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt;
+ const struct mail_storage_service_input *input;
+ unsigned int parallel_count = 0;
+
+ llrcpt->anvil_query = NULL;
+ if (reply == NULL) {
+ /* lookup failed */
+ } else if (str_to_uint(reply, &parallel_count) < 0) {
+ e_error(rcpt->event, "Invalid reply from anvil: %s", reply);
+ }
+
+ if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
+ smtp_server_recipient_reply(
+ rcpt, 451, "4.3.0",
+ "Too many concurrent deliveries for user");
+ } else if (lmtp_local_rcpt_anvil_finish(llrcpt)) {
+ llrcpt->anvil_connect_sent = TRUE;
+ input = mail_storage_service_user_get_input(llrcpt->service_user);
+ master_service_anvil_send(master_service, t_strconcat(
+ "CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
+ "/", input->username, "\n", NULL));
+ }
+}
+
+int lmtp_local_rcpt(struct client *client,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct lmtp_recipient *lrcpt, const char *username,
+ const char *detail)
+{
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ struct lmtp_local_recipient *llrcpt;
+ struct mail_storage_service_input input;
+ struct mail_storage_service_user *service_user;
+ const char *error = NULL;
+ int ret = 0;
+
+ i_zero(&input);
+ input.module = input.service = "lmtp";
+ input.username = username;
+ input.local_ip = client->local_ip;
+ input.remote_ip = client->remote_ip;
+ input.local_port = client->local_port;
+ input.remote_port = client->remote_port;
+ input.session_id = lrcpt->session_id;
+ input.conn_ssl_secured =
+ smtp_server_connection_is_ssl_secured(client->conn);
+ input.conn_secured = input.conn_ssl_secured ||
+ smtp_server_connection_is_trusted(client->conn);
+ input.forward_fields = lrcpt->forward_fields;
+ input.event_parent = rcpt->event;
+
+ ret = mail_storage_service_lookup(storage_service, &input,
+ &service_user, &error);
+ if (ret < 0) {
+ e_error(rcpt->event, "Failed to lookup user %s: %s",
+ username, error);
+ smtp_server_recipient_reply(rcpt, 451, "4.3.0",
+ "Temporary internal error");
+ return -1;
+ }
+ if (ret == 0) {
+ smtp_server_recipient_reply(rcpt, 550, "5.1.1",
+ "User doesn't exist: %s",
+ username);
+ return -1;
+ }
+
+ if (client->local == NULL)
+ client->local = lmtp_local_init(client);
+
+ llrcpt = p_new(rcpt->pool, struct lmtp_local_recipient, 1);
+ llrcpt->rcpt = lrcpt;
+ llrcpt->detail = p_strdup(rcpt->pool, detail);
+ llrcpt->service_user = service_user;
+
+ lrcpt->type = LMTP_RECIPIENT_TYPE_LOCAL;
+ lrcpt->backend_context = llrcpt;
+
+ smtp_server_recipient_add_hook(
+ rcpt, SMTP_SERVER_RECIPIENT_HOOK_DESTROY,
+ lmtp_local_rcpt_destroy, llrcpt);
+ smtp_server_recipient_add_hook(
+ rcpt, SMTP_SERVER_RECIPIENT_HOOK_APPROVED,
+ lmtp_local_rcpt_approved, llrcpt);
+
+ if (client->lmtp_set->lmtp_user_concurrency_limit == 0) {
+ (void)lmtp_local_rcpt_anvil_finish(llrcpt);
+ } else {
+ /* NOTE: username may change as the result of the userdb
+ lookup. Look up the new one via service_user. */
+ const struct mail_storage_service_input *input =
+ mail_storage_service_user_get_input(llrcpt->service_user);
+ const char *query = t_strconcat("LOOKUP\t",
+ master_service_get_name(master_service),
+ "/", str_tabescape(input->username), NULL);
+ llrcpt->anvil_query = anvil_client_query(anvil, query,
+ lmtp_local_rcpt_anvil_cb, llrcpt);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * DATA command
+ */
+
+void lmtp_local_add_headers(struct lmtp_local *local,
+ struct smtp_server_transaction *trans,
+ string_t *headers)
+{
+ struct client *client = local->client;
+ const struct lmtp_settings *lmtp_set = client->lmtp_set;
+ struct lmtp_local_recipient *const *llrcpts;
+ const struct smtp_address *rcpt_to = NULL;
+ unsigned int count;
+
+ str_printfa(headers, "Return-Path: <%s>\r\n",
+ smtp_address_encode(trans->mail_from));
+
+ llrcpts = array_get(&local->rcpt_to, &count);
+ if (count == 1) {
+ struct smtp_server_recipient *rcpt = llrcpts[0]->rcpt->rcpt;
+
+ switch (lmtp_set->parsed_lmtp_hdr_delivery_address) {
+ case LMTP_HDR_DELIVERY_ADDRESS_NONE:
+ break;
+ case LMTP_HDR_DELIVERY_ADDRESS_FINAL:
+ rcpt_to = rcpt->path;
+ break;
+ case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL:
+ rcpt_to = rcpt->params.orcpt.addr;
+ break;
+ }
+ }
+ if (rcpt_to != NULL) {
+ str_printfa(headers, "Delivered-To: %s\r\n",
+ smtp_address_encode(rcpt_to));
+ }
+}
+
+static int
+lmtp_local_deliver(struct lmtp_local *local,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans,
+ struct lmtp_local_recipient *llrcpt,
+ struct mail *src_mail,
+ struct mail_deliver_session *session)
+{
+ struct client *client = local->client;
+ struct lmtp_recipient *lrcpt = llrcpt->rcpt;
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ struct mail_storage_service_user *service_user = llrcpt->service_user;
+ struct lmtp_local_deliver_context lldctx;
+ struct mail_user *rcpt_user;
+ const struct mail_storage_service_input *input;
+ const struct mail_storage_settings *mail_set;
+ struct smtp_submit_settings *smtp_set;
+ struct smtp_proxy_data proxy_data;
+ struct lda_settings *lda_set;
+ struct mail_namespace *ns;
+ struct setting_parser_context *set_parser;
+ const struct var_expand_table *var_table;
+ void **sets;
+ const char *line, *error, *username;
+ int ret;
+
+ input = mail_storage_service_user_get_input(service_user);
+ username = t_strdup(input->username);
+
+ mail_set = mail_storage_service_user_get_mail_set(service_user);
+ set_parser = mail_storage_service_user_get_settings_parser(service_user);
+
+ smtp_server_connection_get_proxy_data
+ (client->conn, &proxy_data);
+ if (proxy_data.timeout_secs > 0 &&
+ (mail_set->mail_max_lock_timeout == 0 ||
+ mail_set->mail_max_lock_timeout > proxy_data.timeout_secs)) {
+ /* set lock timeout waits to be less than when proxy has
+ advertised that it's going to timeout the connection.
+ this avoids duplicate deliveries in case the delivery
+ succeeds after the proxy has already disconnected from us. */
+ line = t_strdup_printf("mail_max_lock_timeout=%us",
+ proxy_data.timeout_secs <= 1 ? 1 :
+ proxy_data.timeout_secs-1);
+ if (settings_parse_line(set_parser, line) < 0)
+ i_unreached();
+ }
+
+ i_zero(&lldctx);
+ lldctx.session_id = lrcpt->session_id;
+ lldctx.src_mail = src_mail;
+ lldctx.session = session;
+
+ /* get the timestamp before user is created, since it starts the I/O */
+ io_loop_time_refresh();
+ lldctx.delivery_time_started = ioloop_timeval;
+
+ client_update_data_state(client, username);
+ if (mail_storage_service_next(storage_service, service_user,
+ &rcpt_user, &error) < 0) {
+ e_error(rcpt->event, "Failed to initialize user: %s", error);
+ smtp_server_recipient_reply(rcpt, 451, "4.3.0",
+ "Temporary internal error");
+ return -1;
+ }
+ local->rcpt_user = rcpt_user;
+
+ sets = mail_storage_service_user_get_set(service_user);
+ var_table = mail_user_var_expand_table(rcpt_user);
+ smtp_set = sets[1];
+ lda_set = sets[2];
+ ret = settings_var_expand(
+ &smtp_submit_setting_parser_info,
+ smtp_set, client->pool, var_table,
+ &error);
+ if (ret > 0) {
+ ret = settings_var_expand(
+ &lda_setting_parser_info,
+ lda_set, client->pool, var_table,
+ &error);
+ }
+ if (ret <= 0) {
+ e_error(rcpt->event, "Failed to expand settings: %s", error);
+ smtp_server_recipient_reply(rcpt, 451, "4.3.0",
+ "Temporary internal error");
+ return -1;
+ }
+
+ /* Set the log prefix for the user. The default log prefix is
+ automatically restored later when user context gets deactivated. */
+ i_set_failure_prefix("%s",
+ mail_storage_service_user_get_log_prefix(service_user));
+
+ lldctx.rcpt_user = rcpt_user;
+ lldctx.smtp_set = smtp_set;
+ lldctx.lda_set = lda_set;
+
+ if (*llrcpt->detail == '\0' ||
+ !client->lmtp_set->lmtp_save_to_detail_mailbox)
+ lldctx.rcpt_default_mailbox = "INBOX";
+ else {
+ ns = mail_namespace_find_inbox(rcpt_user->namespaces);
+ lldctx.rcpt_default_mailbox =
+ t_strconcat(ns->prefix, llrcpt->detail, NULL);
+ }
+
+ ret = client->v.local_deliver(client, lrcpt, cmd, trans, &lldctx);
+
+ lmtp_local_rcpt_anvil_disconnect(llrcpt);
+ return ret;
+}
+
+static int
+lmtp_local_default_do_deliver(struct lmtp_local *local,
+ struct lmtp_local_recipient *llrcpt,
+ struct lmtp_local_deliver_context *lldctx,
+ struct mail_deliver_context *dctx)
+{
+ struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt;
+ enum mail_deliver_error error_code;
+ const char *error;
+
+ if (mail_deliver(dctx, &error_code, &error) == 0) {
+ if (dctx->dest_mail != NULL) {
+ i_assert(local->first_saved_mail == NULL);
+ local->first_saved_mail = dctx->dest_mail;
+ }
+ smtp_server_recipient_reply(rcpt, 250, "2.0.0", "%s Saved",
+ lldctx->session_id);
+ return 0;
+ }
+
+ switch (error_code) {
+ case MAIL_DELIVER_ERROR_NONE:
+ i_unreached();
+ case MAIL_DELIVER_ERROR_TEMPORARY:
+ smtp_server_recipient_reply(rcpt, 451, "4.2.0", "%s", error);
+ break;
+ case MAIL_DELIVER_ERROR_REJECTED:
+ smtp_server_recipient_reply(rcpt, 552, "5.2.0", "%s", error);
+ break;
+ case MAIL_DELIVER_ERROR_NOQUOTA:
+ lmtp_local_rcpt_reply_overquota(llrcpt, error);
+ break;
+ case MAIL_DELIVER_ERROR_INTERNAL:
+ /* This shouldn't happen */
+ smtp_server_recipient_reply(rcpt, 451, "4.3.0", "%s", error);
+ break;
+ }
+
+ return -1;
+}
+
+int lmtp_local_default_deliver(struct client *client,
+ struct lmtp_recipient *lrcpt,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans,
+ struct lmtp_local_deliver_context *lldctx)
+{
+ struct lmtp_local *local = client->local;
+ struct lmtp_local_recipient *llrcpt = lrcpt->backend_context;
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ struct smtp_address *rcpt_to = rcpt->path;
+ struct mail_deliver_input dinput;
+ struct mail_deliver_context dctx;
+ struct event *event;
+ int ret;
+
+ event = event_create(rcpt->event);
+ event_drop_parent_log_prefixes(event, 3);
+
+ i_zero(&dinput);
+ dinput.session = lldctx->session;
+ dinput.set = lldctx->lda_set;
+ dinput.smtp_set = lldctx->smtp_set;
+ dinput.session_id = lldctx->session_id;
+ dinput.event_parent = event;
+ dinput.src_mail = lldctx->src_mail;
+
+ /* MAIL FROM */
+ dinput.mail_from = trans->mail_from;
+ dinput.mail_params = trans->params;
+
+ /* RCPT TO */
+ dinput.rcpt_user = lldctx->rcpt_user;
+ dinput.rcpt_params = rcpt->params;
+ if (dinput.rcpt_params.orcpt.addr == NULL &&
+ *dinput.set->lda_original_recipient_header != '\0') {
+ dinput.rcpt_params.orcpt.addr =
+ mail_deliver_get_address(
+ lldctx->src_mail,
+ dinput.set->lda_original_recipient_header);
+ }
+ if (dinput.rcpt_params.orcpt.addr == NULL)
+ dinput.rcpt_params.orcpt.addr = rcpt_to;
+ dinput.rcpt_to = rcpt_to;
+ dinput.rcpt_default_mailbox = lldctx->rcpt_default_mailbox;
+
+ dinput.save_dest_mail = array_count(&trans->rcpt_to) > 1 &&
+ local->first_saved_mail == NULL;
+
+ dinput.session_time_msecs =
+ timeval_diff_msecs(&client->state.data_end_timeval,
+ &trans->timestamp);
+ dinput.delivery_time_started = lldctx->delivery_time_started;
+
+ mail_deliver_init(&dctx, &dinput);
+ ret = lmtp_local_default_do_deliver(local, llrcpt, lldctx, &dctx);
+ mail_deliver_deinit(&dctx);
+ event_unref(&event);
+
+ return ret;
+}
+
+static uid_t
+lmtp_local_deliver_to_rcpts(struct lmtp_local *local,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct mail_deliver_session *session)
+{
+ struct client *client = local->client;
+ uid_t first_uid = (uid_t)-1;
+ struct mail *src_mail;
+ struct lmtp_local_recipient *const *llrcpts;
+ unsigned int count, i;
+ int ret;
+
+ src_mail = local->raw_mail;
+ llrcpts = array_get(&local->rcpt_to, &count);
+ for (i = 0; i < count; i++) {
+ struct lmtp_local_recipient *llrcpt = llrcpts[i];
+ struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt;
+
+ if (llrcpt->duplicate != NULL) {
+ struct smtp_server_recipient *drcpt =
+ llrcpt->duplicate->rcpt->rcpt;
+ /* don't deliver more than once to the same recipient */
+ smtp_server_reply_submit_duplicate(cmd, rcpt->index,
+ drcpt->index);
+ continue;
+ }
+
+ ret = lmtp_local_deliver(local, cmd,
+ trans, llrcpt, src_mail, session);
+ client_update_data_state(client, NULL);
+
+ /* succeeded and mail_user is not saved in first_saved_mail */
+ if ((ret == 0 &&
+ (local->first_saved_mail == NULL ||
+ local->first_saved_mail == src_mail)) ||
+ /* failed. try the next one. */
+ (ret != 0 && local->rcpt_user != NULL)) {
+ if (i == (count - 1))
+ mail_user_autoexpunge(local->rcpt_user);
+ mail_storage_service_io_deactivate_user(local->rcpt_user->_service_user);
+ mail_user_deinit(&local->rcpt_user);
+ } else if (ret == 0) {
+ /* use the first saved message to save it elsewhere too.
+ this might allow hard linking the files.
+ mail_user is saved in first_saved_mail,
+ will be unreferenced later on */
+ mail_storage_service_io_deactivate_user(local->rcpt_user->_service_user);
+ local->rcpt_user = NULL;
+ src_mail = local->first_saved_mail;
+ first_uid = geteuid();
+ i_assert(first_uid != 0);
+ } else if (local->rcpt_user != NULL) {
+ mail_storage_service_io_deactivate_user(local->rcpt_user->_service_user);
+ }
+ }
+ return first_uid;
+}
+
+static int
+lmtp_local_open_raw_mail(struct lmtp_local *local,
+ struct smtp_server_transaction *trans,
+ struct istream *input)
+{
+ static const char *wanted_headers[] = {
+ "From", "To", "Message-ID", "Subject", "Return-Path",
+ NULL
+ };
+ struct client *client = local->client;
+ struct mailbox *box;
+ struct mailbox_transaction_context *mtrans;
+ struct mailbox_header_lookup_ctx *headers_ctx;
+ enum mail_error error;
+
+ if (raw_mailbox_alloc_stream(client->raw_mail_user, input,
+ (time_t)-1, smtp_address_encode(trans->mail_from),
+ &box) < 0) {
+ e_error(client->event, "Can't open delivery mail as raw: %s",
+ mailbox_get_last_internal_error(box, &error));
+ mailbox_free(&box);
+ lmtp_local_rcpt_fail_all(local, 451, "4.3.0",
+ "Temporary internal error");
+ return -1;
+ }
+
+ mtrans = mailbox_transaction_begin(box, 0, __func__);
+
+ headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
+ local->raw_mail = mail_alloc(mtrans, 0, headers_ctx);
+ mailbox_header_lookup_unref(&headers_ctx);
+ mail_set_seq(local->raw_mail, 1);
+ return 0;
+}
+
+void lmtp_local_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *input)
+{
+ struct lmtp_local *local = client->local;
+ struct mail_deliver_session *session;
+ uid_t old_uid, first_uid;
+
+ if (lmtp_local_open_raw_mail(local, trans, input) < 0)
+ return;
+
+ session = mail_deliver_session_init();
+ old_uid = geteuid();
+ first_uid = lmtp_local_deliver_to_rcpts(local, cmd, trans, session);
+ mail_deliver_session_deinit(&session);
+
+ if (local->first_saved_mail != NULL) {
+ struct mail *mail = local->first_saved_mail;
+ struct mailbox_transaction_context *trans = mail->transaction;
+ struct mailbox *box = trans->box;
+ struct mail_user *user = box->storage->user;
+
+ /* just in case these functions are going to write anything,
+ change uid back to user's own one */
+ if (first_uid != old_uid) {
+ if (seteuid(0) < 0)
+ i_fatal("seteuid(0) failed: %m");
+ if (seteuid(first_uid) < 0)
+ i_fatal("seteuid() failed: %m");
+ }
+
+ mail_storage_service_io_activate_user(user->_service_user);
+ mail_free(&mail);
+ mailbox_transaction_rollback(&trans);
+ mailbox_free(&box);
+ mail_user_autoexpunge(user);
+ mail_storage_service_io_deactivate_user(user->_service_user);
+ mail_user_deinit(&user);
+ }
+
+ if (old_uid == 0) {
+ /* switch back to running as root, since that's what we're
+ practically doing anyway. it's also important in case we
+ lose e.g. config connection and need to reconnect to it. */
+ if (seteuid(0) < 0)
+ i_fatal("seteuid(0) failed: %m");
+ /* enable core dumping again. we need to chdir also to
+ root-owned directory to get core dumps. */
+ restrict_access_allow_coredumps(TRUE);
+ if (chdir(base_dir) < 0) {
+ e_error(client->event,
+ "chdir(%s) failed: %m", base_dir);
+ }
+ }
+}
diff --git a/src/lmtp/lmtp-local.h b/src/lmtp/lmtp-local.h
new file mode 100644
index 0000000..8ef942f
--- /dev/null
+++ b/src/lmtp/lmtp-local.h
@@ -0,0 +1,34 @@
+#ifndef LMTP_LOCAL_H
+#define LMTP_LOCAL_H
+
+#include "net.h"
+
+struct mail_deliver_session;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
+struct lmtp_local;
+struct client;
+
+void lmtp_local_deinit(struct lmtp_local **_local);
+
+int lmtp_local_rcpt(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *lrcpt, const char *username,
+ const char *detail);
+
+void lmtp_local_add_headers(struct lmtp_local *local,
+ struct smtp_server_transaction *trans,
+ string_t *headers);
+
+int lmtp_local_default_deliver(struct client *client,
+ struct lmtp_recipient *lrcpt,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct lmtp_local_deliver_context *lldctx);
+
+void lmtp_local_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *input);
+
+#endif
diff --git a/src/lmtp/lmtp-proxy.c b/src/lmtp/lmtp-proxy.c
new file mode 100644
index 0000000..bf03c75
--- /dev/null
+++ b/src/lmtp/lmtp-proxy.c
@@ -0,0 +1,860 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lmtp-common.h"
+#include "istream.h"
+#include "istream-sized.h"
+#include "ostream.h"
+#include "iostream-ssl.h"
+#include "str.h"
+#include "strescape.h"
+#include "time-util.h"
+#include "smtp-common.h"
+#include "smtp-params.h"
+#include "smtp-address.h"
+#include "smtp-client.h"
+#include "smtp-client-connection.h"
+#include "smtp-client-transaction.h"
+#include "auth-master.h"
+#include "master-service-ssl-settings.h"
+#include "mail-storage-service.h"
+#include "lda-settings.h"
+#include "lmtp-recipient.h"
+#include "lmtp-proxy.h"
+
+#define LMTP_MAX_REPLY_SIZE 4096
+#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125)
+
+enum lmtp_proxy_ssl_flags {
+ /* Use SSL/TLS enabled */
+ PROXY_SSL_FLAG_YES = 0x01,
+ /* Don't do SSL handshake immediately after connected */
+ PROXY_SSL_FLAG_STARTTLS = 0x02,
+ /* Don't require that the received certificate is valid */
+ PROXY_SSL_FLAG_ANY_CERT = 0x04
+};
+
+struct lmtp_proxy_rcpt_settings {
+ enum smtp_protocol protocol;
+ const char *host;
+ struct ip_addr hostip, source_ip;
+ in_port_t port;
+ enum lmtp_proxy_ssl_flags ssl_flags;
+ unsigned int timeout_msecs;
+ struct smtp_params_rcpt params;
+
+ bool proxy_not_trusted:1;
+};
+
+struct lmtp_proxy_recipient {
+ struct lmtp_recipient *rcpt;
+ struct lmtp_proxy_connection *conn;
+
+ struct smtp_address *address;
+
+ const unsigned char *forward_fields;
+ size_t forward_fields_size;
+
+ bool rcpt_to_failed:1;
+ bool data_reply_received:1;
+};
+
+struct lmtp_proxy_connection {
+ struct lmtp_proxy *proxy;
+ struct lmtp_proxy_rcpt_settings set;
+ char *host;
+
+ struct smtp_client_connection *lmtp_conn;
+ struct smtp_client_transaction *lmtp_trans;
+ struct istream *data_input;
+ struct timeout *to;
+
+ bool finished:1;
+ bool failed:1;
+};
+
+struct lmtp_proxy {
+ struct client *client;
+
+ struct smtp_server_transaction *trans;
+
+ struct smtp_client *lmtp_client;
+
+ ARRAY(struct lmtp_proxy_connection *) connections;
+ ARRAY(struct lmtp_proxy_recipient *) rcpt_to;
+ unsigned int next_data_reply_idx;
+
+ struct timeout *to_finish;
+ struct istream *data_input;
+
+ unsigned int max_timeout_msecs;
+ unsigned int proxy_session_seq;
+
+ bool finished:1;
+};
+
+static void
+lmtp_proxy_data_cb(const struct smtp_reply *reply,
+ struct lmtp_proxy_recipient *lprcpt);
+
+/*
+ * LMTP proxy
+ */
+
+static struct lmtp_proxy *
+lmtp_proxy_init(struct client *client,
+ struct smtp_server_transaction *trans)
+{
+ const char *extra_capabilities[] = {
+ LMTP_RCPT_FORWARD_CAPABILITY,
+ NULL };
+ struct smtp_client_settings lmtp_set;
+ struct lmtp_proxy *proxy;
+
+ proxy = i_new(struct lmtp_proxy, 1);
+ proxy->client = client;
+ proxy->trans = trans;
+ i_array_init(&proxy->rcpt_to, 32);
+ i_array_init(&proxy->connections, 32);
+
+ i_zero(&lmtp_set);
+ lmtp_set.my_hostname = client->my_domain;
+ lmtp_set.extra_capabilities = extra_capabilities;
+ lmtp_set.dns_client_socket_path = dns_client_socket_path;
+ lmtp_set.max_reply_size = LMTP_MAX_REPLY_SIZE;
+ lmtp_set.rawlog_dir = client->lmtp_set->lmtp_proxy_rawlog_dir;
+
+ smtp_server_connection_get_proxy_data(client->conn,
+ &lmtp_set.proxy_data);
+ lmtp_set.proxy_data.source_ip = client->remote_ip;
+ lmtp_set.proxy_data.source_port = client->remote_port;
+ /* This initial session_id is used only locally by lib-smtp. Each LMTP
+ proxy connection gets a more specific updated session_id. */
+ lmtp_set.proxy_data.session = trans->id;
+ if (lmtp_set.proxy_data.ttl_plus_1 == 0)
+ lmtp_set.proxy_data.ttl_plus_1 = LMTP_PROXY_DEFAULT_TTL + 1;
+ else
+ lmtp_set.proxy_data.ttl_plus_1--;
+ lmtp_set.event_parent = client->event;
+
+ proxy->lmtp_client = smtp_client_init(&lmtp_set);
+
+ return proxy;
+}
+
+static void lmtp_proxy_connection_deinit(struct lmtp_proxy_connection *conn)
+{
+ if (conn->lmtp_trans != NULL)
+ smtp_client_transaction_destroy(&conn->lmtp_trans);
+ if (conn->lmtp_conn != NULL)
+ smtp_client_connection_close(&conn->lmtp_conn);
+ timeout_remove(&conn->to);
+ i_stream_unref(&conn->data_input);
+ i_free(conn->host);
+ i_free(conn);
+}
+
+void lmtp_proxy_deinit(struct lmtp_proxy **_proxy)
+{
+ struct lmtp_proxy *proxy = *_proxy;
+ struct lmtp_proxy_connection *conn;
+
+ *_proxy = NULL;
+
+ array_foreach_elem(&proxy->connections, conn)
+ lmtp_proxy_connection_deinit(conn);
+
+ smtp_client_deinit(&proxy->lmtp_client);
+ i_stream_unref(&proxy->data_input);
+ timeout_remove(&proxy->to_finish);
+ array_free(&proxy->rcpt_to);
+ array_free(&proxy->connections);
+ i_free(proxy);
+}
+
+static void
+lmtp_proxy_mail_cb(const struct smtp_reply *proxy_reply ATTR_UNUSED,
+ struct lmtp_proxy_connection *conn ATTR_UNUSED)
+{
+ /* nothing */
+}
+
+static void lmtp_proxy_connection_finish(struct lmtp_proxy_connection *conn)
+{
+ conn->finished = TRUE;
+ conn->lmtp_trans = NULL;
+}
+
+static void
+lmtp_proxy_connection_init_ssl(struct lmtp_proxy_connection *conn,
+ struct ssl_iostream_settings *ssl_set_r,
+ enum smtp_client_connection_ssl_mode *ssl_mode_r)
+{
+ const struct master_service_ssl_settings *master_ssl_set;
+
+ *ssl_mode_r = SMTP_CLIENT_SSL_MODE_NONE;
+
+ if ((conn->set.ssl_flags & PROXY_SSL_FLAG_YES) == 0) {
+ i_zero(ssl_set_r);
+ return;
+ }
+
+ master_ssl_set = master_service_ssl_settings_get(master_service);
+ master_service_ssl_client_settings_to_iostream_set(
+ master_ssl_set, pool_datastack_create(), ssl_set_r);
+ if ((conn->set.ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0)
+ ssl_set_r->allow_invalid_cert = TRUE;
+
+ if ((conn->set.ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0)
+ *ssl_mode_r = SMTP_CLIENT_SSL_MODE_IMMEDIATE;
+ else
+ *ssl_mode_r = SMTP_CLIENT_SSL_MODE_STARTTLS;
+}
+
+static bool
+lmtp_proxy_connection_has_rcpt_forward(struct lmtp_proxy_connection *conn)
+{
+ const struct smtp_capability_extra *cap_extra =
+ smtp_client_connection_get_extra_capability(
+ conn->lmtp_conn, LMTP_RCPT_FORWARD_CAPABILITY);
+
+ return (cap_extra != NULL);
+}
+
+static struct lmtp_proxy_connection *
+lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
+ const struct lmtp_proxy_rcpt_settings *set)
+{
+ static const char *rcpt_param_extensions[] =
+ { LMTP_RCPT_FORWARD_PARAMETER, NULL };
+ static const struct smtp_client_capability_extra cap_rcpt_forward = {
+ .name = LMTP_RCPT_FORWARD_CAPABILITY,
+ .rcpt_param_extensions = rcpt_param_extensions,
+ };
+ struct smtp_client_settings lmtp_set;
+ struct smtp_server_transaction *trans = proxy->trans;
+ struct client *client = proxy->client;
+ struct lmtp_proxy_connection *conn;
+ enum smtp_client_connection_ssl_mode ssl_mode;
+ struct ssl_iostream_settings ssl_set;
+
+ i_assert(set->timeout_msecs > 0);
+
+ array_foreach_elem(&proxy->connections, conn) {
+ if (conn->set.protocol == set->protocol &&
+ conn->set.port == set->port &&
+ strcmp(conn->set.host, set->host) == 0 &&
+ net_ip_compare(&conn->set.hostip, &set->hostip) &&
+ net_ip_compare(&conn->set.source_ip, &set->source_ip) &&
+ conn->set.ssl_flags == set->ssl_flags)
+ return conn;
+ }
+
+ conn = i_new(struct lmtp_proxy_connection, 1);
+ conn->proxy = proxy;
+ conn->set.protocol = set->protocol;
+ conn->set.hostip = set->hostip;
+ conn->host = i_strdup(set->host);
+ conn->set.host = conn->host;
+ conn->set.source_ip = set->source_ip;
+ conn->set.port = set->port;
+ conn->set.ssl_flags = set->ssl_flags;
+ conn->set.timeout_msecs = set->timeout_msecs;
+ array_push_back(&proxy->connections, &conn);
+
+ lmtp_proxy_connection_init_ssl(conn, &ssl_set, &ssl_mode);
+
+ i_zero(&lmtp_set);
+ lmtp_set.my_ip = conn->set.source_ip;
+ lmtp_set.ssl = &ssl_set;
+ lmtp_set.peer_trusted = !conn->set.proxy_not_trusted;
+ lmtp_set.forced_capabilities = SMTP_CAPABILITY__ORCPT;
+ lmtp_set.mail_send_broken_path = TRUE;
+ lmtp_set.verbose_user_errors = client->lmtp_set->lmtp_verbose_replies;
+
+ if (conn->set.hostip.family != 0) {
+ conn->lmtp_conn = smtp_client_connection_create_ip(
+ proxy->lmtp_client, set->protocol,
+ &conn->set.hostip, conn->set.port,
+ conn->set.host, ssl_mode, &lmtp_set);
+ } else {
+ conn->lmtp_conn = smtp_client_connection_create(
+ proxy->lmtp_client, set->protocol,
+ conn->set.host, conn->set.port,
+ ssl_mode, &lmtp_set);
+ }
+ struct smtp_proxy_data proxy_data = {
+ .session = t_strdup_printf("%s:P%u", proxy->trans->id,
+ ++proxy->proxy_session_seq),
+ };
+ smtp_client_connection_update_proxy_data(conn->lmtp_conn, &proxy_data);
+ smtp_client_connection_accept_extra_capability(conn->lmtp_conn,
+ &cap_rcpt_forward);
+ smtp_client_connection_connect(conn->lmtp_conn, NULL, NULL);
+
+ conn->lmtp_trans = smtp_client_transaction_create(
+ conn->lmtp_conn, trans->mail_from, &trans->params, 0,
+ lmtp_proxy_connection_finish, conn);
+
+ smtp_client_transaction_start(conn->lmtp_trans,
+ lmtp_proxy_mail_cb, conn);
+
+ if (proxy->max_timeout_msecs < set->timeout_msecs)
+ proxy->max_timeout_msecs = set->timeout_msecs;
+ return conn;
+}
+
+static void
+lmtp_proxy_handle_connection_error(struct lmtp_proxy_recipient *lprcpt,
+ const struct smtp_reply *reply)
+{
+ struct lmtp_recipient *lrcpt = lprcpt->rcpt;
+ struct client *client = lrcpt->client;
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ const char *detail = "";
+
+ if (client->lmtp_set->lmtp_verbose_replies) {
+ smtp_server_command_fail(rcpt->cmd->cmd, 451, "4.4.0",
+ "Proxy failed: %s (session=%s)",
+ smtp_reply_log(reply),
+ lrcpt->session_id);
+ return;
+ }
+
+ switch (reply->status) {
+ case SMTP_CLIENT_COMMAND_ERROR_ABORTED:
+ break;
+ case SMTP_CLIENT_COMMAND_ERROR_HOST_LOOKUP_FAILED:
+ detail = "DNS lookup, ";
+ break;
+ case SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED:
+ case SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED:
+ detail = "connect, ";
+ break;
+ case SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST:
+ case SMTP_CLIENT_COMMAND_ERROR_CONNECTION_CLOSED:
+ detail = "connection lost, ";
+ break;
+ case SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY:
+ detail = "bad reply, ";
+ break;
+ case SMTP_CLIENT_COMMAND_ERROR_TIMED_OUT:
+ detail = "timed out, ";
+ break;
+ default:
+ break;
+ }
+
+ smtp_server_command_fail(rcpt->cmd->cmd, 451, "4.4.0",
+ "Proxy failed (%ssession=%s)",
+ detail, lrcpt->session_id);
+}
+
+static bool
+lmtp_proxy_handle_reply(struct lmtp_proxy_recipient *lprcpt,
+ const struct smtp_reply *reply,
+ struct smtp_reply *reply_r)
+{
+ *reply_r = *reply;
+
+ if (!smtp_reply_is_remote(reply) ||
+ reply->status == SMTP_CLIENT_COMMAND_ERROR_CONNECTION_CLOSED) {
+ lmtp_proxy_handle_connection_error(lprcpt, reply);
+ return FALSE;
+ }
+
+ if (!smtp_reply_has_enhanced_code(reply)) {
+ reply_r->enhanced_code =
+ SMTP_REPLY_ENH_CODE(reply->status / 100, 0, 0);
+ }
+ return TRUE;
+}
+
+/*
+ * RCPT command
+ */
+
+static bool
+lmtp_proxy_rcpt_parse_fields(struct lmtp_recipient *lrcpt,
+ struct lmtp_proxy_rcpt_settings *set,
+ const char *const *args, const char **address)
+{
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ const char *p, *key, *value;
+ bool proxying = FALSE, port_set = FALSE;
+
+ for (; *args != NULL; args++) {
+ p = strchr(*args, '=');
+ if (p == NULL) {
+ key = *args;
+ value = "";
+ } else {
+ key = t_strdup_until(*args, p);
+ value = p + 1;
+ }
+
+ if (strcmp(key, "proxy") == 0)
+ proxying = TRUE;
+ else if (strcmp(key, "host") == 0)
+ set->host = value;
+ else if (strcmp(key, "hostip") == 0) {
+ if (net_addr2ip(value, &set->hostip) < 0) {
+ e_error(rcpt->event,
+ "proxy: Invalid hostip %s", value);
+ return FALSE;
+ }
+ } else if (strcmp(key, "source_ip") == 0) {
+ if (net_addr2ip(value, &set->source_ip) < 0) {
+ e_error(rcpt->event,
+ "proxy: Invalid source_ip %s", value);
+ return FALSE;
+ }
+ } else if (strcmp(key, "port") == 0) {
+ if (net_str2port(value, &set->port) < 0) {
+ e_error(rcpt->event,
+ "proxy: Invalid port number %s", value);
+ return FALSE;
+ }
+ port_set = TRUE;
+ } else if (strcmp(key, "proxy_timeout") == 0) {
+ if (str_to_uint(value, &set->timeout_msecs) < 0) {
+ e_error(rcpt->event,"proxy: "
+ "Invalid proxy_timeout value %s", value);
+ return FALSE;
+ }
+ set->timeout_msecs *= 1000;
+ } else if (strcmp(key, "proxy_not_trusted") == 0) {
+ set->proxy_not_trusted = TRUE;
+ } else if (strcmp(key, "protocol") == 0) {
+ if (strcmp(value, "lmtp") == 0) {
+ set->protocol = SMTP_PROTOCOL_LMTP;
+ if (!port_set)
+ set->port = 24;
+ } else if (strcmp(value, "smtp") == 0) {
+ set->protocol = SMTP_PROTOCOL_SMTP;
+ if (!port_set)
+ set->port = 25;
+ } else {
+ e_error(rcpt->event,
+ "proxy: Unknown protocol %s", value);
+ return FALSE;
+ }
+ } else if (strcmp(key, "ssl") == 0) {
+ set->ssl_flags |= PROXY_SSL_FLAG_YES;
+ if (strcmp(value, "any-cert") == 0)
+ set->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
+ } else if (strcmp(key, "starttls") == 0) {
+ set->ssl_flags |= PROXY_SSL_FLAG_YES |
+ PROXY_SSL_FLAG_STARTTLS;
+ if (strcmp(value, "any-cert") == 0)
+ set->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
+ } else if (strcmp(key, "user") == 0 ||
+ strcmp(key, "destuser") == 0) {
+ /* Changing the username */
+ *address = value;
+ } else {
+ /* Just ignore it */
+ }
+ }
+ if (proxying && set->host == NULL) {
+ e_error(rcpt->event, "proxy: host not given");
+ return FALSE;
+ }
+ return proxying;
+}
+
+static bool
+lmtp_proxy_is_ourself(const struct client *client,
+ const struct lmtp_proxy_rcpt_settings *set)
+{
+ struct ip_addr ip;
+
+ if (set->port != client->local_port)
+ return FALSE;
+
+ if (set->hostip.family != 0)
+ ip = set->hostip;
+ else {
+ if (net_addr2ip(set->host, &ip) < 0)
+ return FALSE;
+ }
+ if (!net_ip_compare(&ip, &client->local_ip))
+ return FALSE;
+ return TRUE;
+}
+
+static void
+lmtp_proxy_rcpt_approved(struct smtp_server_recipient *rcpt ATTR_UNUSED,
+ struct lmtp_proxy_recipient *lprcpt)
+{
+ struct client *client = lprcpt->rcpt->client;
+
+ /* Add to proxy recipients */
+ array_push_back(&client->proxy->rcpt_to, &lprcpt);
+}
+
+static void
+lmtp_proxy_rcpt_cb(const struct smtp_reply *proxy_reply,
+ struct lmtp_proxy_recipient *lprcpt)
+{
+ struct smtp_server_recipient *rcpt = lprcpt->rcpt->rcpt;
+ struct smtp_reply reply;
+
+ if (!lmtp_proxy_handle_reply(lprcpt, proxy_reply, &reply))
+ return;
+
+ if (smtp_reply_is_success(proxy_reply)) {
+ /* If backend accepts it, we accept it too */
+
+ /* The default 2.0.0 code won't do */
+ if (!smtp_reply_has_enhanced_code(proxy_reply))
+ reply.enhanced_code = SMTP_REPLY_ENH_CODE(2, 1, 0);
+ }
+
+ /* Forward reply */
+ smtp_server_recipient_reply_forward(rcpt, &reply);
+}
+
+static void
+lmtp_proxy_rcpt_login_cb(const struct smtp_reply *proxy_reply, void *context)
+{
+ struct lmtp_proxy_recipient *lprcpt = context;
+ struct lmtp_recipient *lrcpt = lprcpt->rcpt;
+ struct lmtp_proxy_connection *conn = lprcpt->conn;
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ struct smtp_reply reply;
+ struct smtp_client_transaction_rcpt *relay_rcpt;
+ struct smtp_params_rcpt *rcpt_params = &rcpt->params;
+ bool add_orcpt_param = FALSE, add_xrcptforward_param = FALSE;
+ pool_t param_pool;
+
+ if (!lmtp_proxy_handle_reply(lprcpt, proxy_reply, &reply))
+ return;
+ if (!smtp_reply_is_success(proxy_reply)) {
+ smtp_server_recipient_reply_forward(rcpt, &reply);
+ return;
+ }
+
+ /* Add an ORCPT parameter when passdb changed the username (and
+ therefore the RCPT address changed) and there is no ORCPT parameter
+ yet. */
+ if (!smtp_params_rcpt_has_orcpt(rcpt_params) &&
+ !smtp_address_equals(lprcpt->address, rcpt->path))
+ add_orcpt_param = TRUE;
+
+ /* Add forward fields parameter when passdb returned forward_* fields */
+ if (lprcpt->forward_fields != NULL &&
+ lmtp_proxy_connection_has_rcpt_forward(conn))
+ add_xrcptforward_param = TRUE;
+
+ /* Copy params when changes are pending */
+ param_pool = NULL;
+ if (add_orcpt_param || add_xrcptforward_param) {
+ param_pool = pool_datastack_create();
+ rcpt_params = p_new(param_pool, struct smtp_params_rcpt, 1);
+ smtp_params_rcpt_copy(param_pool, rcpt_params, &rcpt->params);
+ }
+
+ /* Add ORCPT */
+ if (add_orcpt_param) {
+ smtp_params_rcpt_set_orcpt(rcpt_params, param_pool,
+ rcpt->path);
+ }
+ /* Add forward fields parameter */
+ if (add_xrcptforward_param) {
+ smtp_params_rcpt_encode_extra(
+ rcpt_params, param_pool, LMTP_RCPT_FORWARD_PARAMETER,
+ lprcpt->forward_fields, lprcpt->forward_fields_size);
+ }
+
+ smtp_server_recipient_add_hook(
+ rcpt, SMTP_SERVER_RECIPIENT_HOOK_APPROVED,
+ lmtp_proxy_rcpt_approved, lprcpt);
+
+ relay_rcpt = smtp_client_transaction_add_pool_rcpt(
+ conn->lmtp_trans, rcpt->pool, lprcpt->address, rcpt_params,
+ lmtp_proxy_rcpt_cb, lprcpt);
+ smtp_client_transaction_rcpt_set_data_callback(
+ relay_rcpt, lmtp_proxy_data_cb, lprcpt);
+}
+
+int lmtp_proxy_rcpt(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *lrcpt,
+ const char *username, const char *detail,
+ char delim)
+{
+ struct auth_master_connection *auth_conn;
+ struct lmtp_proxy_rcpt_settings set;
+ struct lmtp_proxy_connection *conn;
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ struct lmtp_proxy_recipient *lprcpt;
+ struct smtp_server_transaction *trans;
+ struct smtp_address *address = rcpt->path;
+ struct auth_user_info info;
+ struct mail_storage_service_input input;
+ const char *const *fields, *errstr, *orig_username = username;
+ struct smtp_proxy_data proxy_data;
+ struct smtp_address *user;
+ string_t *fwfields;
+ pool_t auth_pool;
+ int ret;
+
+ trans = smtp_server_connection_get_transaction(cmd->conn);
+ i_assert(trans != NULL); /* MAIL command is synchronous */
+
+ i_zero(&input);
+ input.module = input.service = "lmtp";
+ mail_storage_service_init_settings(storage_service, &input);
+
+ i_zero(&info);
+ info.service = master_service_get_name(master_service);
+ info.local_ip = client->local_ip;
+ info.real_local_ip = client->real_local_ip;
+ info.remote_ip = client->remote_ip;
+ info.real_remote_ip = client->real_remote_ip;
+ info.local_port = client->local_port;
+ info.real_local_port = client->real_local_port;
+ info.remote_port = client->remote_port;
+ info.real_remote_port = client->real_remote_port;
+ info.forward_fields = lrcpt->forward_fields;
+
+ // FIXME: make this async
+ auth_pool = pool_alloconly_create("auth lookup", 1024);
+ auth_conn = mail_storage_service_get_auth_conn(storage_service);
+ ret = auth_master_pass_lookup(auth_conn, username, &info,
+ auth_pool, &fields);
+ if (ret <= 0) {
+ errstr = (ret < 0 && fields[0] != NULL ?
+ t_strdup(fields[0]) :
+ "Temporary user lookup failure");
+ pool_unref(&auth_pool);
+ if (ret < 0) {
+ smtp_server_recipient_reply(rcpt, 451, "4.3.0", "%s",
+ errstr);
+ return -1;
+ } else {
+ /* User not found from passdb: revert to local delivery.
+ */
+ return 0;
+ }
+ }
+
+ i_zero(&set);
+ set.port = client->local_port;
+ set.protocol = SMTP_PROTOCOL_LMTP;
+ set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
+
+ if (!lmtp_proxy_rcpt_parse_fields(lrcpt, &set, fields, &username)) {
+ /* Not proxying this user */
+ pool_unref(&auth_pool);
+ return 0;
+ }
+ if (strcmp(username, orig_username) != 0) {
+ /* The existing "user" event field is overridden with the new
+ user name, while old username is available as "orig_user" */
+ event_add_str(rcpt->event, "user", username);
+ event_add_str(rcpt->event, "original_user", orig_username);
+
+ if (smtp_address_parse_username(pool_datastack_create(),
+ username, &user, &errstr) < 0) {
+ e_error(rcpt->event, "%s: "
+ "Username `%s' returned by passdb lookup is not a valid SMTP address",
+ orig_username, username);
+ smtp_server_recipient_reply(
+ rcpt, 550, "5.3.5",
+ "Internal user lookup failure");
+ pool_unref(&auth_pool);
+ return -1;
+ }
+ /* Username changed. change the address as well */
+ if (*detail == '\0') {
+ address = user;
+ } else {
+ address = smtp_address_add_detail_temp(
+ user, detail, delim);
+ }
+ } else if (lmtp_proxy_is_ourself(client, &set)) {
+ e_error(rcpt->event, "Proxying to <%s> loops to itself",
+ username);
+ smtp_server_recipient_reply(rcpt, 554, "5.4.6",
+ "Proxying loops to itself");
+ pool_unref(&auth_pool);
+ return -1;
+ }
+
+ smtp_server_connection_get_proxy_data(cmd->conn, &proxy_data);
+ if (proxy_data.ttl_plus_1 == 1) {
+ e_error(rcpt->event,
+ "Proxying to <%s> appears to be looping (TTL=0)",
+ username);
+ smtp_server_recipient_reply(rcpt, 554, "5.4.6",
+ "Proxying appears to be looping "
+ "(TTL=0)");
+ pool_unref(&auth_pool);
+ return -1;
+ }
+
+ if (client->proxy == NULL)
+ client->proxy = lmtp_proxy_init(client, trans);
+
+ conn = lmtp_proxy_get_connection(client->proxy, &set);
+
+ lprcpt = p_new(rcpt->pool, struct lmtp_proxy_recipient, 1);
+ lprcpt->rcpt = lrcpt;
+ lprcpt->address = smtp_address_clone(rcpt->pool, address);
+ lprcpt->conn = conn;
+
+ lrcpt->type = LMTP_RECIPIENT_TYPE_PROXY;
+ lrcpt->backend_context = lprcpt;
+
+ /* Copy forward fields returned from passdb */
+ fwfields = NULL;
+ for (const char *const *ptr = fields; *ptr != NULL; ptr++) {
+ if (strncasecmp(*ptr, "forward_", 8) != 0)
+ continue;
+
+ if (fwfields == NULL)
+ fwfields = t_str_new(128);
+ else
+ str_append_c(fwfields, '\t');
+
+ str_append_tabescaped(fwfields, (*ptr) + 8);
+ }
+ if (fwfields != NULL) {
+ lprcpt->forward_fields = p_memdup(
+ rcpt->pool, str_data(fwfields), str_len(fwfields));
+ lprcpt->forward_fields_size = str_len(fwfields);
+ }
+
+ pool_unref(&auth_pool);
+
+ smtp_client_connection_connect(conn->lmtp_conn,
+ lmtp_proxy_rcpt_login_cb, lprcpt);
+ return 1;
+}
+
+/*
+ * DATA command
+ */
+
+static void
+lmtp_proxy_data_cb(const struct smtp_reply *proxy_reply,
+ struct lmtp_proxy_recipient *lprcpt)
+{
+ struct lmtp_proxy_connection *conn = lprcpt->conn;
+ struct lmtp_recipient *lrcpt = lprcpt->rcpt;
+ struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+ struct lmtp_proxy *proxy = conn->proxy;
+ struct smtp_server_transaction *trans = proxy->trans;
+ struct smtp_address *address = lprcpt->address;
+ const struct smtp_client_transaction_times *times =
+ smtp_client_transaction_get_times(conn->lmtp_trans);
+ unsigned int rcpt_index = rcpt->index;
+ struct smtp_reply reply;
+ string_t *msg;
+
+ /* Compose log message */
+ msg = t_str_new(128);
+ str_printfa(msg, "<%s>: ", lrcpt->session_id);
+ if (smtp_reply_is_success(proxy_reply))
+ str_append(msg, "Sent message to");
+ else
+ str_append(msg, "Failed to send message to");
+ str_printfa(msg, " <%s> at %s:%u: %s (%u/%u at %u ms)",
+ smtp_address_encode(address),
+ conn->set.host, conn->set.port,
+ smtp_reply_log(proxy_reply),
+ rcpt_index + 1, array_count(&trans->rcpt_to),
+ timeval_diff_msecs(&ioloop_timeval, &times->started));
+
+ /* Handle reply */
+ if (smtp_reply_is_success(proxy_reply)) {
+ /* If backend accepts it, we accept it too */
+ e_info(rcpt->event, "%s", str_c(msg));
+
+ /* Substitute our own success message */
+ smtp_reply_printf(&reply, 250, "%s Saved", lrcpt->session_id);
+ /* Do let the enhanced code through */
+ if (!smtp_reply_has_enhanced_code(proxy_reply))
+ reply.enhanced_code = SMTP_REPLY_ENH_CODE(2, 0, 0);
+ else
+ reply.enhanced_code = proxy_reply->enhanced_code;
+
+ } else {
+ if (smtp_reply_is_remote(proxy_reply)) {
+ /* The problem isn't with the proxy, it's with the
+ remote side. so the remote side will log an error,
+ while for us this is just an info event */
+ e_info(rcpt->event, "%s", str_c(msg));
+ } else {
+ e_error(rcpt->event, "%s", str_c(msg));
+ }
+
+ if (!lmtp_proxy_handle_reply(lprcpt, proxy_reply, &reply))
+ return;
+ }
+
+ /* Forward reply */
+ smtp_server_recipient_reply_forward(rcpt, &reply);
+}
+
+static void
+lmtp_proxy_data_dummy_cb(const struct smtp_reply *proxy_reply ATTR_UNUSED,
+ struct lmtp_proxy_connection *conn ATTR_UNUSED)
+{
+ /* nothing */
+}
+
+void lmtp_proxy_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct istream *data_input)
+{
+ struct lmtp_proxy *proxy = client->proxy;
+ struct lmtp_proxy_connection *conn;
+ uoff_t size;
+
+ i_assert(data_input->seekable);
+ i_assert(proxy->data_input == NULL);
+
+ client_update_data_state(client, "proxying");
+
+ proxy->data_input = data_input;
+ i_stream_ref(proxy->data_input);
+ if (i_stream_get_size(proxy->data_input, TRUE, &size) < 0) {
+ e_error(client->event,
+ "i_stream_get_size(data_input) failed: %s",
+ i_stream_get_error(proxy->data_input));
+ size = UOFF_T_MAX;
+ }
+
+ /* Create the data_input streams first */
+ array_foreach_elem(&proxy->connections, conn) {
+ if (conn->finished) {
+ /* This connection had already failed */
+ continue;
+ }
+
+ if (size == UOFF_T_MAX) {
+ conn->data_input =
+ i_stream_create_limit(data_input, UOFF_T_MAX);
+ } else {
+ conn->data_input =
+ i_stream_create_sized(data_input, size);
+ }
+ }
+ /* Now that all the streams are created, start reading them
+ (reading them earlier could have caused the data_input parent's
+ offset to change) */
+ array_foreach_elem(&proxy->connections, conn) {
+ if (conn->finished) {
+ /* This connection had already failed */
+ continue;
+ }
+
+ smtp_client_transaction_set_timeout(conn->lmtp_trans,
+ proxy->max_timeout_msecs);
+ smtp_client_transaction_send(conn->lmtp_trans, conn->data_input,
+ lmtp_proxy_data_dummy_cb, conn);
+ }
+}
diff --git a/src/lmtp/lmtp-proxy.h b/src/lmtp/lmtp-proxy.h
new file mode 100644
index 0000000..18d27b7
--- /dev/null
+++ b/src/lmtp/lmtp-proxy.h
@@ -0,0 +1,29 @@
+#ifndef LMTP_PROXY_H
+#define LMTP_PROXY_H
+
+#include "net.h"
+
+#include "smtp-common.h"
+#include "smtp-params.h"
+#include "smtp-client.h"
+
+#define LMTP_PROXY_DEFAULT_TTL 5
+
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
+struct lmtp_proxy;
+struct client;
+
+void lmtp_proxy_deinit(struct lmtp_proxy **proxy);
+
+int lmtp_proxy_rcpt(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct lmtp_recipient *rcpt, const char *username,
+ const char *detail, char delim);
+
+void lmtp_proxy_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct istream *data_input);
+
+#endif
diff --git a/src/lmtp/lmtp-recipient.c b/src/lmtp/lmtp-recipient.c
new file mode 100644
index 0000000..aaad29b
--- /dev/null
+++ b/src/lmtp/lmtp-recipient.c
@@ -0,0 +1,57 @@
+/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
+
+#include "lmtp-common.h"
+#include "array.h"
+#include "smtp-server.h"
+#include "lmtp-recipient.h"
+
+struct lmtp_recipient_module_register
+lmtp_recipient_module_register = { 0 };
+
+struct lmtp_recipient *
+lmtp_recipient_create(struct client *client,
+ struct smtp_server_transaction *trans,
+ struct smtp_server_recipient *rcpt)
+{
+ struct lmtp_recipient *lrcpt;
+
+ lrcpt = p_new(rcpt->pool, struct lmtp_recipient, 1);
+ lrcpt->rcpt = rcpt;
+ lrcpt->client = client;
+
+ rcpt->context = lrcpt;
+
+ p_array_init(&lrcpt->module_contexts, rcpt->pool, 5);
+
+ /* Use a unique session_id for each mail delivery. This is especially
+ important for stats process to not see duplicate sessions. */
+ if (client->state.session_id_seq++ == 0)
+ lrcpt->session_id = trans->id;
+ else {
+ lrcpt->session_id = p_strdup_printf(rcpt->pool, "%s:R%u",
+ trans->id, client->state.session_id_seq);
+ }
+ event_add_str(rcpt->event, "session", lrcpt->session_id);
+
+ return lrcpt;
+}
+
+struct lmtp_recipient *
+lmtp_recipient_find_duplicate(struct lmtp_recipient *lrcpt,
+ struct smtp_server_transaction *trans)
+{
+ struct smtp_server_recipient *drcpt;
+ struct lmtp_recipient *dup_lrcpt;
+
+ i_assert(lrcpt->rcpt != NULL);
+ drcpt = smtp_server_transaction_find_rcpt_duplicate(trans, lrcpt->rcpt);
+ if (drcpt == NULL)
+ return NULL;
+
+ dup_lrcpt = drcpt->context;
+ i_assert(dup_lrcpt->rcpt == drcpt);
+ i_assert(dup_lrcpt->type == lrcpt->type);
+
+ return dup_lrcpt;
+}
+
diff --git a/src/lmtp/lmtp-recipient.h b/src/lmtp/lmtp-recipient.h
new file mode 100644
index 0000000..d760440
--- /dev/null
+++ b/src/lmtp/lmtp-recipient.h
@@ -0,0 +1,48 @@
+#ifndef LMTP_RECIPIENT_H
+#define LMTP_RECIPIENT_H
+
+struct smtp_address;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
+struct smtp_server_recipient;
+union lmtp_recipient_module_context;
+struct client;
+
+enum lmtp_recipient_type {
+ LMTP_RECIPIENT_TYPE_LOCAL,
+ LMTP_RECIPIENT_TYPE_PROXY,
+};
+
+struct lmtp_recipient {
+ struct client *client;
+ struct smtp_server_recipient *rcpt;
+
+ enum lmtp_recipient_type type;
+ void *backend_context;
+
+ const char *session_id;
+ const char *forward_fields;
+
+ /* Module-specific contexts. */
+ ARRAY(union lmtp_recipient_module_context *) module_contexts;
+};
+
+struct lmtp_recipient_module_register {
+ unsigned int id;
+};
+
+union lmtp_recipient_module_context {
+ struct lmtp_recipient_module_register *reg;
+};
+extern struct lmtp_recipient_module_register lmtp_recipient_module_register;
+
+struct lmtp_recipient *
+lmtp_recipient_create(struct client *client,
+ struct smtp_server_transaction *trans,
+ struct smtp_server_recipient *rcpt);
+
+struct lmtp_recipient *
+lmtp_recipient_find_duplicate(struct lmtp_recipient *lrcpt,
+ struct smtp_server_transaction *trans);
+
+#endif
diff --git a/src/lmtp/lmtp-settings.c b/src/lmtp/lmtp-settings.c
new file mode 100644
index 0000000..1d9ecb2
--- /dev/null
+++ b/src/lmtp/lmtp-settings.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "var-expand.h"
+#include "settings-parser.h"
+#include "service-settings.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "lda-settings.h"
+#include "lmtp-settings.h"
+#include "mail-storage-settings.h"
+
+#include <stddef.h>
+#include <unistd.h>
+
+static bool lmtp_settings_check(void *_set, pool_t pool, const char **error_r);
+
+/* <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> */
+
+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
+};
+
+/* <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> */
+
+void lmtp_settings_dup(const struct setting_parser_context *set_parser,
+ pool_t pool,
+ struct mail_user_settings **user_set_r,
+ struct lmtp_settings **lmtp_set_r,
+ struct lda_settings **lda_set_r)
+{
+ const char *error;
+ void **sets;
+
+ sets = master_service_settings_parser_get_others(master_service,
+ set_parser);
+ *user_set_r = settings_dup(&mail_user_setting_parser_info, sets[0], pool);
+ *lda_set_r = settings_dup(&lda_setting_parser_info, sets[2], pool);
+ *lmtp_set_r = settings_dup(&lmtp_setting_parser_info, sets[3], pool);
+ if (!lmtp_settings_check(*lmtp_set_r, pool, &error))
+ i_unreached();
+}
diff --git a/src/lmtp/lmtp-settings.h b/src/lmtp/lmtp-settings.h
new file mode 100644
index 0000000..3b2eaa3
--- /dev/null
+++ b/src/lmtp/lmtp-settings.h
@@ -0,0 +1,53 @@
+#ifndef LMTP_SETTINGS_H
+#define LMTP_SETTINGS_H
+
+struct mail_user_settings;
+struct lda_settings;
+struct lmtp_settings;
+
+/* <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;
+};
+
+extern const struct setting_parser_info lmtp_setting_parser_info;
+
+void lmtp_settings_dup(const struct setting_parser_context *set_parser,
+ pool_t pool,
+ struct mail_user_settings **user_set_r,
+ struct lmtp_settings **lmtp_set_r,
+ struct lda_settings **lda_set_r);
+
+#endif
diff --git a/src/lmtp/main.c b/src/lmtp/main.c
new file mode 100644
index 0000000..fb9ada8
--- /dev/null
+++ b/src/lmtp/main.c
@@ -0,0 +1,173 @@
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
+
+#include "lmtp-common.h"
+#include "ioloop.h"
+#include "path-util.h"
+#include "restrict-access.h"
+#include "anvil-client.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "master-interface.h"
+#include "mail-deliver.h"
+#include "mail-storage-service.h"
+#include "smtp-submit-settings.h"
+#include "lda-settings.h"
+
+#include <unistd.h>
+
+#define DNS_CLIENT_SOCKET_PATH "dns-client"
+#define LMTP_MASTER_FIRST_LISTEN_FD 3
+
+#define IS_STANDALONE() \
+ (getenv(MASTER_IS_PARENT_ENV) == NULL)
+
+struct smtp_server *lmtp_server = NULL;
+
+char *dns_client_socket_path, *base_dir;
+struct mail_storage_service_ctx *storage_service;
+struct anvil_client *anvil;
+
+lmtp_client_created_func_t *hook_client_created = NULL;
+
+struct event_category event_category_lmtp = {
+ .name = "lmtp",
+};
+
+lmtp_client_created_func_t *
+lmtp_client_created_hook_set(lmtp_client_created_func_t *new_hook)
+{
+ lmtp_client_created_func_t *old_hook = hook_client_created;
+
+ hook_client_created = new_hook;
+ return old_hook;
+}
+
+void lmtp_anvil_init(void)
+{
+ if (anvil == NULL) {
+ const char *path = t_strdup_printf("%s/anvil", base_dir);
+ anvil = anvil_client_init(path, NULL, 0);
+ }
+}
+
+static void client_connected(struct master_service_connection *conn)
+{
+ master_service_client_connection_accept(conn);
+ (void)client_create(conn->fd, conn->fd, conn);
+}
+
+static void drop_privileges(void)
+{
+ struct restrict_access_settings set;
+ const char *error;
+
+ /* by default we don't drop any privileges, but keep running as root. */
+ restrict_access_get_env(&set);
+ /* open config connection before dropping privileges */
+ struct master_service_settings_input input;
+ struct master_service_settings_output output;
+
+ i_zero(&input);
+ input.module = "lmtp";
+ input.service = "lmtp";
+ if (master_service_settings_read(master_service,
+ &input, &output, &error) < 0)
+ i_fatal("Error reading configuration: %s", error);
+ restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL);
+}
+
+static void main_init(void)
+{
+ struct master_service_connection conn;
+ struct smtp_server_settings lmtp_set;
+
+ i_zero(&lmtp_set);
+ lmtp_set.protocol = SMTP_PROTOCOL_LMTP;
+ lmtp_set.auth_optional = TRUE;
+ lmtp_set.rcpt_domain_optional = TRUE;
+ lmtp_set.mail_path_allow_broken = TRUE;
+ lmtp_set.reason_code_module = "lmtp";
+
+ lmtp_server = smtp_server_init(&lmtp_set);
+
+ if (IS_STANDALONE()) {
+ i_zero(&conn);
+ (void)client_create(STDIN_FILENO, STDOUT_FILENO, &conn);
+ }
+
+ const char *error, *tmp_socket_path;
+ if (t_abspath(DNS_CLIENT_SOCKET_PATH, &tmp_socket_path, &error) < 0) {
+ i_fatal("t_abspath(%s) failed: %s", DNS_CLIENT_SOCKET_PATH, error);
+ }
+ dns_client_socket_path = i_strdup(tmp_socket_path);
+ mail_deliver_hooks_init();
+}
+
+static void main_deinit(void)
+{
+ clients_destroy();
+ if (anvil != NULL)
+ anvil_client_deinit(&anvil);
+ i_free(dns_client_socket_path);
+ i_free(base_dir);
+ smtp_server_deinit(&lmtp_server);
+}
+
+int main(int argc, char *argv[])
+{
+ const struct setting_parser_info *set_roots[] = {
+ &smtp_submit_setting_parser_info,
+ &lda_setting_parser_info,
+ &lmtp_setting_parser_info,
+ NULL
+ };
+ enum master_service_flags service_flags =
+ MASTER_SERVICE_FLAG_HAVE_STARTTLS;
+ enum mail_storage_service_flags storage_service_flags =
+ MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP |
+ MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP |
+ MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT;
+ const char *tmp_base_dir;
+ int c;
+
+ if (IS_STANDALONE()) {
+ service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+ MASTER_SERVICE_FLAG_STD_CLIENT;
+ } else {
+ service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN ;
+ }
+
+ master_service = master_service_init("lmtp", service_flags,
+ &argc, &argv, "D");
+ while ((c = master_getopt(master_service)) > 0) {
+ switch (c) {
+ case 'D':
+ storage_service_flags |=
+ MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS;
+ break;
+ default:
+ return FATAL_DEFAULT;
+ }
+ }
+
+ const char *error;
+ if (t_get_working_dir(&tmp_base_dir, &error) < 0)
+ i_fatal("Could not get working directory: %s", error);
+ base_dir = i_strdup(tmp_base_dir);
+
+ drop_privileges();
+ master_service_init_log_with_pid(master_service);
+
+ storage_service = mail_storage_service_init(master_service, set_roots,
+ storage_service_flags);
+ restrict_access_allow_coredumps(TRUE);
+
+ main_init();
+ master_service_init_finish(master_service);
+ master_service_run(master_service, client_connected);
+
+ main_deinit();
+ mail_storage_service_deinit(&storage_service);
+ master_service_deinit(&master_service);
+ return 0;
+}