diff options
Diffstat (limited to 'src/lmtp')
-rw-r--r-- | src/lmtp/Makefile.am | 55 | ||||
-rw-r--r-- | src/lmtp/Makefile.in | 916 | ||||
-rw-r--r-- | src/lmtp/lmtp-client.c | 439 | ||||
-rw-r--r-- | src/lmtp/lmtp-client.h | 124 | ||||
-rw-r--r-- | src/lmtp/lmtp-commands.c | 340 | ||||
-rw-r--r-- | src/lmtp/lmtp-commands.h | 45 | ||||
-rw-r--r-- | src/lmtp/lmtp-common.h | 35 | ||||
-rw-r--r-- | src/lmtp/lmtp-local.c | 766 | ||||
-rw-r--r-- | src/lmtp/lmtp-local.h | 34 | ||||
-rw-r--r-- | src/lmtp/lmtp-proxy.c | 860 | ||||
-rw-r--r-- | src/lmtp/lmtp-proxy.h | 29 | ||||
-rw-r--r-- | src/lmtp/lmtp-recipient.c | 57 | ||||
-rw-r--r-- | src/lmtp/lmtp-recipient.h | 48 | ||||
-rw-r--r-- | src/lmtp/lmtp-settings.c | 205 | ||||
-rw-r--r-- | src/lmtp/lmtp-settings.h | 53 | ||||
-rw-r--r-- | src/lmtp/main.c | 173 |
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, ¶llel_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, ×->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; +} |