summaryrefslogtreecommitdiffstats
path: root/src/local
diff options
context:
space:
mode:
Diffstat (limited to 'src/local')
l---------src/local/.indent.pro1
-rw-r--r--src/local/.printfck25
-rw-r--r--src/local/Makefile.in686
-rw-r--r--src/local/Musings39
-rw-r--r--src/local/alias.c386
-rw-r--r--src/local/biff_notify.c98
-rw-r--r--src/local/biff_notify.h30
-rw-r--r--src/local/bounce_workaround.c159
-rw-r--r--src/local/command.c251
-rw-r--r--src/local/deliver_attr.c105
-rw-r--r--src/local/dotforward.c304
-rw-r--r--src/local/file.c195
-rw-r--r--src/local/forward.c393
-rw-r--r--src/local/include.c223
-rw-r--r--src/local/indirect.c94
-rw-r--r--src/local/local.c988
-rw-r--r--src/local/local.h251
-rw-r--r--src/local/local_expand.c180
-rw-r--r--src/local/mailbox.c373
-rw-r--r--src/local/maildir.c257
-rw-r--r--src/local/recipient.c307
-rw-r--r--src/local/resolve.c170
-rw-r--r--src/local/token.c222
-rw-r--r--src/local/unknown.c187
24 files changed, 5924 insertions, 0 deletions
diff --git a/src/local/.indent.pro b/src/local/.indent.pro
new file mode 120000
index 0000000..5c837ec
--- /dev/null
+++ b/src/local/.indent.pro
@@ -0,0 +1 @@
+../../.indent.pro \ No newline at end of file
diff --git a/src/local/.printfck b/src/local/.printfck
new file mode 100644
index 0000000..66016ed
--- /dev/null
+++ b/src/local/.printfck
@@ -0,0 +1,25 @@
+been_here_xt 2 0
+bounce_append 5 0
+cleanup_out_format 1 0
+defer_append 5 0
+mail_command 1 0
+mail_print 1 0
+msg_error 0 0
+msg_fatal 0 0
+msg_info 0 0
+msg_panic 0 0
+msg_warn 0 0
+opened 4 0
+post_mail_fprintf 1 0
+qmgr_message_bounce 2 0
+rec_fprintf 2 0
+sent 4 0
+smtp_cmd 1 0
+smtp_mesg_fail 2 0
+smtp_printf 1 0
+smtp_rcpt_fail 3 0
+smtp_site_fail 2 0
+udp_syslog 1 0
+vstream_fprintf 1 0
+vstream_printf 0 0
+vstring_sprintf 1 0
diff --git a/src/local/Makefile.in b/src/local/Makefile.in
new file mode 100644
index 0000000..648ad51
--- /dev/null
+++ b/src/local/Makefile.in
@@ -0,0 +1,686 @@
+SHELL = /bin/sh
+SRCS = alias.c command.c dotforward.c file.c forward.c \
+ include.c indirect.c local.c mailbox.c recipient.c resolve.c token.c \
+ deliver_attr.c maildir.c biff_notify.c unknown.c \
+ local_expand.c bounce_workaround.c
+OBJS = alias.o command.o dotforward.o file.o forward.o \
+ include.o indirect.o local.o mailbox.o recipient.o resolve.o token.o \
+ deliver_attr.o maildir.o biff_notify.o unknown.o \
+ local_expand.o bounce_workaround.c
+HDRS = local.h
+TESTSRC =
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+PROG = local
+TESTPROG=
+INC_DIR = ../../include
+LIBS = ../../lib/lib$(LIB_PREFIX)master$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+$(OBJS): ../../conf/makedefs.out
+
+Makefile: Makefile.in
+ cat ../../conf/makedefs.out $? >$@
+
+test: $(TESTPROG)
+
+tests:
+
+root_tests:
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+ cp $(PROG) ../../libexec
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ cp *.h printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \
+ -e 's/o: \.\//o: /' -e p -e '}' ; \
+ done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+alias.o: ../../include/argv.h
+alias.o: ../../include/attr.h
+alias.o: ../../include/been_here.h
+alias.o: ../../include/bounce.h
+alias.o: ../../include/canon_addr.h
+alias.o: ../../include/check_arg.h
+alias.o: ../../include/defer.h
+alias.o: ../../include/deliver_request.h
+alias.o: ../../include/delivered_hdr.h
+alias.o: ../../include/dict.h
+alias.o: ../../include/dsn.h
+alias.o: ../../include/dsn_buf.h
+alias.o: ../../include/dsn_mask.h
+alias.o: ../../include/fold_addr.h
+alias.o: ../../include/htable.h
+alias.o: ../../include/mail_params.h
+alias.o: ../../include/maps.h
+alias.o: ../../include/mbox_conf.h
+alias.o: ../../include/msg.h
+alias.o: ../../include/msg_stats.h
+alias.o: ../../include/myflock.h
+alias.o: ../../include/mymalloc.h
+alias.o: ../../include/mypwd.h
+alias.o: ../../include/nvtable.h
+alias.o: ../../include/recipient_list.h
+alias.o: ../../include/resolve_clnt.h
+alias.o: ../../include/sent.h
+alias.o: ../../include/stringops.h
+alias.o: ../../include/sys_defs.h
+alias.o: ../../include/tok822.h
+alias.o: ../../include/trace.h
+alias.o: ../../include/vbuf.h
+alias.o: ../../include/vstream.h
+alias.o: ../../include/vstring.h
+alias.o: alias.c
+alias.o: local.h
+biff_notify.o: ../../include/iostuff.h
+biff_notify.o: ../../include/msg.h
+biff_notify.o: ../../include/sys_defs.h
+biff_notify.o: biff_notify.c
+biff_notify.o: biff_notify.h
+bounce_workaround.o: ../../include/argv.h
+bounce_workaround.o: ../../include/attr.h
+bounce_workaround.o: ../../include/been_here.h
+bounce_workaround.o: ../../include/bounce.h
+bounce_workaround.o: ../../include/canon_addr.h
+bounce_workaround.o: ../../include/check_arg.h
+bounce_workaround.o: ../../include/defer.h
+bounce_workaround.o: ../../include/deliver_request.h
+bounce_workaround.o: ../../include/delivered_hdr.h
+bounce_workaround.o: ../../include/dict.h
+bounce_workaround.o: ../../include/dsn.h
+bounce_workaround.o: ../../include/dsn_buf.h
+bounce_workaround.o: ../../include/fold_addr.h
+bounce_workaround.o: ../../include/htable.h
+bounce_workaround.o: ../../include/mail_params.h
+bounce_workaround.o: ../../include/maps.h
+bounce_workaround.o: ../../include/mbox_conf.h
+bounce_workaround.o: ../../include/msg.h
+bounce_workaround.o: ../../include/msg_stats.h
+bounce_workaround.o: ../../include/myflock.h
+bounce_workaround.o: ../../include/mymalloc.h
+bounce_workaround.o: ../../include/nvtable.h
+bounce_workaround.o: ../../include/recipient_list.h
+bounce_workaround.o: ../../include/resolve_clnt.h
+bounce_workaround.o: ../../include/split_addr.h
+bounce_workaround.o: ../../include/split_at.h
+bounce_workaround.o: ../../include/stringops.h
+bounce_workaround.o: ../../include/strip_addr.h
+bounce_workaround.o: ../../include/sys_defs.h
+bounce_workaround.o: ../../include/tok822.h
+bounce_workaround.o: ../../include/vbuf.h
+bounce_workaround.o: ../../include/vstream.h
+bounce_workaround.o: ../../include/vstring.h
+bounce_workaround.o: bounce_workaround.c
+bounce_workaround.o: local.h
+command.o: ../../include/argv.h
+command.o: ../../include/attr.h
+command.o: ../../include/been_here.h
+command.o: ../../include/bounce.h
+command.o: ../../include/check_arg.h
+command.o: ../../include/defer.h
+command.o: ../../include/deliver_request.h
+command.o: ../../include/delivered_hdr.h
+command.o: ../../include/dict.h
+command.o: ../../include/dsn.h
+command.o: ../../include/dsn_buf.h
+command.o: ../../include/dsn_util.h
+command.o: ../../include/fold_addr.h
+command.o: ../../include/htable.h
+command.o: ../../include/mac_parse.h
+command.o: ../../include/mail_copy.h
+command.o: ../../include/mail_params.h
+command.o: ../../include/mail_parm_split.h
+command.o: ../../include/maps.h
+command.o: ../../include/mbox_conf.h
+command.o: ../../include/msg.h
+command.o: ../../include/msg_stats.h
+command.o: ../../include/myflock.h
+command.o: ../../include/mymalloc.h
+command.o: ../../include/nvtable.h
+command.o: ../../include/pipe_command.h
+command.o: ../../include/recipient_list.h
+command.o: ../../include/resolve_clnt.h
+command.o: ../../include/sent.h
+command.o: ../../include/sys_defs.h
+command.o: ../../include/tok822.h
+command.o: ../../include/vbuf.h
+command.o: ../../include/vstream.h
+command.o: ../../include/vstring.h
+command.o: command.c
+command.o: local.h
+deliver_attr.o: ../../include/argv.h
+deliver_attr.o: ../../include/attr.h
+deliver_attr.o: ../../include/been_here.h
+deliver_attr.o: ../../include/check_arg.h
+deliver_attr.o: ../../include/deliver_request.h
+deliver_attr.o: ../../include/delivered_hdr.h
+deliver_attr.o: ../../include/dict.h
+deliver_attr.o: ../../include/dsn.h
+deliver_attr.o: ../../include/dsn_buf.h
+deliver_attr.o: ../../include/fold_addr.h
+deliver_attr.o: ../../include/htable.h
+deliver_attr.o: ../../include/maps.h
+deliver_attr.o: ../../include/mbox_conf.h
+deliver_attr.o: ../../include/msg.h
+deliver_attr.o: ../../include/msg_stats.h
+deliver_attr.o: ../../include/myflock.h
+deliver_attr.o: ../../include/mymalloc.h
+deliver_attr.o: ../../include/nvtable.h
+deliver_attr.o: ../../include/recipient_list.h
+deliver_attr.o: ../../include/resolve_clnt.h
+deliver_attr.o: ../../include/sys_defs.h
+deliver_attr.o: ../../include/tok822.h
+deliver_attr.o: ../../include/vbuf.h
+deliver_attr.o: ../../include/vstream.h
+deliver_attr.o: ../../include/vstring.h
+deliver_attr.o: deliver_attr.c
+deliver_attr.o: local.h
+dotforward.o: ../../include/argv.h
+dotforward.o: ../../include/attr.h
+dotforward.o: ../../include/been_here.h
+dotforward.o: ../../include/bounce.h
+dotforward.o: ../../include/check_arg.h
+dotforward.o: ../../include/defer.h
+dotforward.o: ../../include/deliver_request.h
+dotforward.o: ../../include/delivered_hdr.h
+dotforward.o: ../../include/dict.h
+dotforward.o: ../../include/dsn.h
+dotforward.o: ../../include/dsn_buf.h
+dotforward.o: ../../include/dsn_mask.h
+dotforward.o: ../../include/ext_prop.h
+dotforward.o: ../../include/fold_addr.h
+dotforward.o: ../../include/htable.h
+dotforward.o: ../../include/iostuff.h
+dotforward.o: ../../include/lstat_as.h
+dotforward.o: ../../include/mac_expand.h
+dotforward.o: ../../include/mac_parse.h
+dotforward.o: ../../include/mail_conf.h
+dotforward.o: ../../include/mail_params.h
+dotforward.o: ../../include/maps.h
+dotforward.o: ../../include/mbox_conf.h
+dotforward.o: ../../include/msg.h
+dotforward.o: ../../include/msg_stats.h
+dotforward.o: ../../include/myflock.h
+dotforward.o: ../../include/mymalloc.h
+dotforward.o: ../../include/mypwd.h
+dotforward.o: ../../include/nvtable.h
+dotforward.o: ../../include/open_as.h
+dotforward.o: ../../include/recipient_list.h
+dotforward.o: ../../include/resolve_clnt.h
+dotforward.o: ../../include/sent.h
+dotforward.o: ../../include/stringops.h
+dotforward.o: ../../include/sys_defs.h
+dotforward.o: ../../include/tok822.h
+dotforward.o: ../../include/trace.h
+dotforward.o: ../../include/vbuf.h
+dotforward.o: ../../include/vstream.h
+dotforward.o: ../../include/vstring.h
+dotforward.o: dotforward.c
+dotforward.o: local.h
+file.o: ../../include/argv.h
+file.o: ../../include/attr.h
+file.o: ../../include/been_here.h
+file.o: ../../include/bounce.h
+file.o: ../../include/check_arg.h
+file.o: ../../include/defer.h
+file.o: ../../include/deliver_flock.h
+file.o: ../../include/deliver_request.h
+file.o: ../../include/delivered_hdr.h
+file.o: ../../include/dict.h
+file.o: ../../include/dsn.h
+file.o: ../../include/dsn_buf.h
+file.o: ../../include/dsn_util.h
+file.o: ../../include/fold_addr.h
+file.o: ../../include/htable.h
+file.o: ../../include/mail_copy.h
+file.o: ../../include/mail_params.h
+file.o: ../../include/maps.h
+file.o: ../../include/mbox_conf.h
+file.o: ../../include/mbox_open.h
+file.o: ../../include/msg.h
+file.o: ../../include/msg_stats.h
+file.o: ../../include/myflock.h
+file.o: ../../include/mymalloc.h
+file.o: ../../include/nvtable.h
+file.o: ../../include/recipient_list.h
+file.o: ../../include/resolve_clnt.h
+file.o: ../../include/safe_open.h
+file.o: ../../include/sent.h
+file.o: ../../include/set_eugid.h
+file.o: ../../include/sys_defs.h
+file.o: ../../include/tok822.h
+file.o: ../../include/vbuf.h
+file.o: ../../include/vstream.h
+file.o: ../../include/vstring.h
+file.o: file.c
+file.o: local.h
+forward.o: ../../include/argv.h
+forward.o: ../../include/attr.h
+forward.o: ../../include/been_here.h
+forward.o: ../../include/bounce.h
+forward.o: ../../include/check_arg.h
+forward.o: ../../include/cleanup_user.h
+forward.o: ../../include/deliver_request.h
+forward.o: ../../include/delivered_hdr.h
+forward.o: ../../include/dict.h
+forward.o: ../../include/dsn.h
+forward.o: ../../include/dsn_buf.h
+forward.o: ../../include/dsn_mask.h
+forward.o: ../../include/fold_addr.h
+forward.o: ../../include/htable.h
+forward.o: ../../include/iostuff.h
+forward.o: ../../include/mail_date.h
+forward.o: ../../include/mail_params.h
+forward.o: ../../include/mail_proto.h
+forward.o: ../../include/maps.h
+forward.o: ../../include/mark_corrupt.h
+forward.o: ../../include/mbox_conf.h
+forward.o: ../../include/msg.h
+forward.o: ../../include/msg_stats.h
+forward.o: ../../include/myflock.h
+forward.o: ../../include/mymalloc.h
+forward.o: ../../include/nvtable.h
+forward.o: ../../include/rec_type.h
+forward.o: ../../include/recipient_list.h
+forward.o: ../../include/record.h
+forward.o: ../../include/resolve_clnt.h
+forward.o: ../../include/sent.h
+forward.o: ../../include/smtputf8.h
+forward.o: ../../include/stringops.h
+forward.o: ../../include/sys_defs.h
+forward.o: ../../include/tok822.h
+forward.o: ../../include/vbuf.h
+forward.o: ../../include/vstream.h
+forward.o: ../../include/vstring.h
+forward.o: ../../include/vstring_vstream.h
+forward.o: forward.c
+forward.o: local.h
+include.o: ../../include/argv.h
+include.o: ../../include/attr.h
+include.o: ../../include/been_here.h
+include.o: ../../include/bounce.h
+include.o: ../../include/check_arg.h
+include.o: ../../include/defer.h
+include.o: ../../include/deliver_request.h
+include.o: ../../include/delivered_hdr.h
+include.o: ../../include/dict.h
+include.o: ../../include/dsn.h
+include.o: ../../include/dsn_buf.h
+include.o: ../../include/ext_prop.h
+include.o: ../../include/fold_addr.h
+include.o: ../../include/htable.h
+include.o: ../../include/iostuff.h
+include.o: ../../include/mail_params.h
+include.o: ../../include/maps.h
+include.o: ../../include/mbox_conf.h
+include.o: ../../include/msg.h
+include.o: ../../include/msg_stats.h
+include.o: ../../include/myflock.h
+include.o: ../../include/mymalloc.h
+include.o: ../../include/mypwd.h
+include.o: ../../include/nvtable.h
+include.o: ../../include/open_as.h
+include.o: ../../include/recipient_list.h
+include.o: ../../include/resolve_clnt.h
+include.o: ../../include/sent.h
+include.o: ../../include/stat_as.h
+include.o: ../../include/sys_defs.h
+include.o: ../../include/tok822.h
+include.o: ../../include/vbuf.h
+include.o: ../../include/vstream.h
+include.o: ../../include/vstring.h
+include.o: include.c
+include.o: local.h
+indirect.o: ../../include/argv.h
+indirect.o: ../../include/attr.h
+indirect.o: ../../include/been_here.h
+indirect.o: ../../include/bounce.h
+indirect.o: ../../include/check_arg.h
+indirect.o: ../../include/defer.h
+indirect.o: ../../include/deliver_request.h
+indirect.o: ../../include/delivered_hdr.h
+indirect.o: ../../include/dict.h
+indirect.o: ../../include/dsn.h
+indirect.o: ../../include/dsn_buf.h
+indirect.o: ../../include/fold_addr.h
+indirect.o: ../../include/htable.h
+indirect.o: ../../include/mail_params.h
+indirect.o: ../../include/maps.h
+indirect.o: ../../include/mbox_conf.h
+indirect.o: ../../include/msg.h
+indirect.o: ../../include/msg_stats.h
+indirect.o: ../../include/myflock.h
+indirect.o: ../../include/mymalloc.h
+indirect.o: ../../include/nvtable.h
+indirect.o: ../../include/recipient_list.h
+indirect.o: ../../include/resolve_clnt.h
+indirect.o: ../../include/sent.h
+indirect.o: ../../include/sys_defs.h
+indirect.o: ../../include/tok822.h
+indirect.o: ../../include/vbuf.h
+indirect.o: ../../include/vstream.h
+indirect.o: ../../include/vstring.h
+indirect.o: indirect.c
+indirect.o: local.h
+local.o: ../../include/argv.h
+local.o: ../../include/attr.h
+local.o: ../../include/been_here.h
+local.o: ../../include/check_arg.h
+local.o: ../../include/deliver_completed.h
+local.o: ../../include/deliver_request.h
+local.o: ../../include/delivered_hdr.h
+local.o: ../../include/dict.h
+local.o: ../../include/dsn.h
+local.o: ../../include/dsn_buf.h
+local.o: ../../include/ext_prop.h
+local.o: ../../include/flush_clnt.h
+local.o: ../../include/fold_addr.h
+local.o: ../../include/htable.h
+local.o: ../../include/iostuff.h
+local.o: ../../include/mail_addr.h
+local.o: ../../include/mail_conf.h
+local.o: ../../include/mail_params.h
+local.o: ../../include/mail_server.h
+local.o: ../../include/mail_version.h
+local.o: ../../include/maps.h
+local.o: ../../include/mbox_conf.h
+local.o: ../../include/msg.h
+local.o: ../../include/msg_stats.h
+local.o: ../../include/myflock.h
+local.o: ../../include/mymalloc.h
+local.o: ../../include/name_mask.h
+local.o: ../../include/nvtable.h
+local.o: ../../include/recipient_list.h
+local.o: ../../include/resolve_clnt.h
+local.o: ../../include/set_eugid.h
+local.o: ../../include/sys_defs.h
+local.o: ../../include/tok822.h
+local.o: ../../include/vbuf.h
+local.o: ../../include/vstream.h
+local.o: ../../include/vstring.h
+local.o: local.c
+local.o: local.h
+local_expand.o: ../../include/argv.h
+local_expand.o: ../../include/attr.h
+local_expand.o: ../../include/been_here.h
+local_expand.o: ../../include/check_arg.h
+local_expand.o: ../../include/deliver_request.h
+local_expand.o: ../../include/delivered_hdr.h
+local_expand.o: ../../include/dict.h
+local_expand.o: ../../include/dsn.h
+local_expand.o: ../../include/dsn_buf.h
+local_expand.o: ../../include/fold_addr.h
+local_expand.o: ../../include/htable.h
+local_expand.o: ../../include/mac_expand.h
+local_expand.o: ../../include/mac_parse.h
+local_expand.o: ../../include/mail_params.h
+local_expand.o: ../../include/maps.h
+local_expand.o: ../../include/mbox_conf.h
+local_expand.o: ../../include/msg_stats.h
+local_expand.o: ../../include/myflock.h
+local_expand.o: ../../include/mymalloc.h
+local_expand.o: ../../include/nvtable.h
+local_expand.o: ../../include/recipient_list.h
+local_expand.o: ../../include/resolve_clnt.h
+local_expand.o: ../../include/sys_defs.h
+local_expand.o: ../../include/tok822.h
+local_expand.o: ../../include/vbuf.h
+local_expand.o: ../../include/vstream.h
+local_expand.o: ../../include/vstring.h
+local_expand.o: local.h
+local_expand.o: local_expand.c
+mailbox.o: ../../include/argv.h
+mailbox.o: ../../include/attr.h
+mailbox.o: ../../include/been_here.h
+mailbox.o: ../../include/bounce.h
+mailbox.o: ../../include/check_arg.h
+mailbox.o: ../../include/defer.h
+mailbox.o: ../../include/deliver_pass.h
+mailbox.o: ../../include/deliver_request.h
+mailbox.o: ../../include/delivered_hdr.h
+mailbox.o: ../../include/dict.h
+mailbox.o: ../../include/dsn.h
+mailbox.o: ../../include/dsn_buf.h
+mailbox.o: ../../include/dsn_util.h
+mailbox.o: ../../include/fold_addr.h
+mailbox.o: ../../include/htable.h
+mailbox.o: ../../include/iostuff.h
+mailbox.o: ../../include/mail_copy.h
+mailbox.o: ../../include/mail_params.h
+mailbox.o: ../../include/mail_proto.h
+mailbox.o: ../../include/maps.h
+mailbox.o: ../../include/mbox_conf.h
+mailbox.o: ../../include/mbox_open.h
+mailbox.o: ../../include/msg.h
+mailbox.o: ../../include/msg_stats.h
+mailbox.o: ../../include/myflock.h
+mailbox.o: ../../include/mymalloc.h
+mailbox.o: ../../include/mypwd.h
+mailbox.o: ../../include/nvtable.h
+mailbox.o: ../../include/recipient_list.h
+mailbox.o: ../../include/resolve_clnt.h
+mailbox.o: ../../include/safe_open.h
+mailbox.o: ../../include/sent.h
+mailbox.o: ../../include/set_eugid.h
+mailbox.o: ../../include/stringops.h
+mailbox.o: ../../include/sys_defs.h
+mailbox.o: ../../include/tok822.h
+mailbox.o: ../../include/vbuf.h
+mailbox.o: ../../include/vstream.h
+mailbox.o: ../../include/vstring.h
+mailbox.o: ../../include/warn_stat.h
+mailbox.o: biff_notify.h
+mailbox.o: local.h
+mailbox.o: mailbox.c
+maildir.o: ../../include/argv.h
+maildir.o: ../../include/attr.h
+maildir.o: ../../include/been_here.h
+maildir.o: ../../include/bounce.h
+maildir.o: ../../include/check_arg.h
+maildir.o: ../../include/defer.h
+maildir.o: ../../include/deliver_request.h
+maildir.o: ../../include/delivered_hdr.h
+maildir.o: ../../include/dict.h
+maildir.o: ../../include/dsn.h
+maildir.o: ../../include/dsn_buf.h
+maildir.o: ../../include/dsn_util.h
+maildir.o: ../../include/fold_addr.h
+maildir.o: ../../include/get_hostname.h
+maildir.o: ../../include/htable.h
+maildir.o: ../../include/mail_copy.h
+maildir.o: ../../include/mail_params.h
+maildir.o: ../../include/make_dirs.h
+maildir.o: ../../include/maps.h
+maildir.o: ../../include/mbox_conf.h
+maildir.o: ../../include/mbox_open.h
+maildir.o: ../../include/msg.h
+maildir.o: ../../include/msg_stats.h
+maildir.o: ../../include/myflock.h
+maildir.o: ../../include/mymalloc.h
+maildir.o: ../../include/nvtable.h
+maildir.o: ../../include/recipient_list.h
+maildir.o: ../../include/resolve_clnt.h
+maildir.o: ../../include/safe_open.h
+maildir.o: ../../include/sane_fsops.h
+maildir.o: ../../include/sent.h
+maildir.o: ../../include/set_eugid.h
+maildir.o: ../../include/stringops.h
+maildir.o: ../../include/sys_defs.h
+maildir.o: ../../include/tok822.h
+maildir.o: ../../include/vbuf.h
+maildir.o: ../../include/vstream.h
+maildir.o: ../../include/vstring.h
+maildir.o: ../../include/warn_stat.h
+maildir.o: local.h
+maildir.o: maildir.c
+recipient.o: ../../include/argv.h
+recipient.o: ../../include/attr.h
+recipient.o: ../../include/been_here.h
+recipient.o: ../../include/bounce.h
+recipient.o: ../../include/canon_addr.h
+recipient.o: ../../include/check_arg.h
+recipient.o: ../../include/defer.h
+recipient.o: ../../include/deliver_request.h
+recipient.o: ../../include/delivered_hdr.h
+recipient.o: ../../include/dict.h
+recipient.o: ../../include/dsn.h
+recipient.o: ../../include/dsn_buf.h
+recipient.o: ../../include/ext_prop.h
+recipient.o: ../../include/fold_addr.h
+recipient.o: ../../include/htable.h
+recipient.o: ../../include/mail_params.h
+recipient.o: ../../include/maps.h
+recipient.o: ../../include/mbox_conf.h
+recipient.o: ../../include/msg.h
+recipient.o: ../../include/msg_stats.h
+recipient.o: ../../include/myflock.h
+recipient.o: ../../include/mymalloc.h
+recipient.o: ../../include/mypwd.h
+recipient.o: ../../include/nvtable.h
+recipient.o: ../../include/recipient_list.h
+recipient.o: ../../include/resolve_clnt.h
+recipient.o: ../../include/split_addr.h
+recipient.o: ../../include/split_at.h
+recipient.o: ../../include/stat_as.h
+recipient.o: ../../include/stringops.h
+recipient.o: ../../include/strip_addr.h
+recipient.o: ../../include/sys_defs.h
+recipient.o: ../../include/tok822.h
+recipient.o: ../../include/vbuf.h
+recipient.o: ../../include/vstream.h
+recipient.o: ../../include/vstring.h
+recipient.o: local.h
+recipient.o: recipient.c
+resolve.o: ../../include/argv.h
+resolve.o: ../../include/attr.h
+resolve.o: ../../include/been_here.h
+resolve.o: ../../include/bounce.h
+resolve.o: ../../include/check_arg.h
+resolve.o: ../../include/defer.h
+resolve.o: ../../include/deliver_request.h
+resolve.o: ../../include/delivered_hdr.h
+resolve.o: ../../include/dict.h
+resolve.o: ../../include/dsn.h
+resolve.o: ../../include/dsn_buf.h
+resolve.o: ../../include/fold_addr.h
+resolve.o: ../../include/htable.h
+resolve.o: ../../include/iostuff.h
+resolve.o: ../../include/mail_params.h
+resolve.o: ../../include/mail_proto.h
+resolve.o: ../../include/maps.h
+resolve.o: ../../include/mbox_conf.h
+resolve.o: ../../include/msg.h
+resolve.o: ../../include/msg_stats.h
+resolve.o: ../../include/myflock.h
+resolve.o: ../../include/mymalloc.h
+resolve.o: ../../include/nvtable.h
+resolve.o: ../../include/recipient_list.h
+resolve.o: ../../include/resolve_clnt.h
+resolve.o: ../../include/rewrite_clnt.h
+resolve.o: ../../include/sys_defs.h
+resolve.o: ../../include/tok822.h
+resolve.o: ../../include/vbuf.h
+resolve.o: ../../include/vstream.h
+resolve.o: ../../include/vstring.h
+resolve.o: local.h
+resolve.o: resolve.c
+token.o: ../../include/argv.h
+token.o: ../../include/attr.h
+token.o: ../../include/been_here.h
+token.o: ../../include/bounce.h
+token.o: ../../include/check_arg.h
+token.o: ../../include/defer.h
+token.o: ../../include/deliver_request.h
+token.o: ../../include/delivered_hdr.h
+token.o: ../../include/dict.h
+token.o: ../../include/dsn.h
+token.o: ../../include/dsn_buf.h
+token.o: ../../include/fold_addr.h
+token.o: ../../include/htable.h
+token.o: ../../include/mail_params.h
+token.o: ../../include/maps.h
+token.o: ../../include/mbox_conf.h
+token.o: ../../include/msg.h
+token.o: ../../include/msg_stats.h
+token.o: ../../include/myflock.h
+token.o: ../../include/mymalloc.h
+token.o: ../../include/nvtable.h
+token.o: ../../include/readlline.h
+token.o: ../../include/recipient_list.h
+token.o: ../../include/resolve_clnt.h
+token.o: ../../include/stringops.h
+token.o: ../../include/sys_defs.h
+token.o: ../../include/tok822.h
+token.o: ../../include/vbuf.h
+token.o: ../../include/vstream.h
+token.o: ../../include/vstring.h
+token.o: ../../include/vstring_vstream.h
+token.o: local.h
+token.o: token.c
+unknown.o: ../../include/argv.h
+unknown.o: ../../include/attr.h
+unknown.o: ../../include/been_here.h
+unknown.o: ../../include/bounce.h
+unknown.o: ../../include/canon_addr.h
+unknown.o: ../../include/check_arg.h
+unknown.o: ../../include/defer.h
+unknown.o: ../../include/deliver_pass.h
+unknown.o: ../../include/deliver_request.h
+unknown.o: ../../include/delivered_hdr.h
+unknown.o: ../../include/dict.h
+unknown.o: ../../include/dsn.h
+unknown.o: ../../include/dsn_buf.h
+unknown.o: ../../include/fold_addr.h
+unknown.o: ../../include/htable.h
+unknown.o: ../../include/iostuff.h
+unknown.o: ../../include/mail_addr.h
+unknown.o: ../../include/mail_params.h
+unknown.o: ../../include/mail_proto.h
+unknown.o: ../../include/maps.h
+unknown.o: ../../include/mbox_conf.h
+unknown.o: ../../include/msg.h
+unknown.o: ../../include/msg_stats.h
+unknown.o: ../../include/myflock.h
+unknown.o: ../../include/mymalloc.h
+unknown.o: ../../include/nvtable.h
+unknown.o: ../../include/recipient_list.h
+unknown.o: ../../include/resolve_clnt.h
+unknown.o: ../../include/sent.h
+unknown.o: ../../include/stringops.h
+unknown.o: ../../include/sys_defs.h
+unknown.o: ../../include/tok822.h
+unknown.o: ../../include/vbuf.h
+unknown.o: ../../include/vstream.h
+unknown.o: ../../include/vstring.h
+unknown.o: local.h
+unknown.o: unknown.c
diff --git a/src/local/Musings b/src/local/Musings
new file mode 100644
index 0000000..6149a2e
--- /dev/null
+++ b/src/local/Musings
@@ -0,0 +1,39 @@
+Local delivery models
+
+The "monolithic" model: recursively expand the complete initial
+recipient list (via aliases, mailing lists, .forward files) to one
+expanded recipient list (mail addresses, shell commands, files,
+mailboxes). Sort/uniq the expanded recipient list, and deliver.
+
+The "forward as if sent by recipient" model: each level of recursion
+(aliases, mailing lists, forward files) takes one entire iteration
+through the mail system. Non-recursively expand one local recipient
+(via alias, mailing list, the recipient's .forward file) to a list
+of expanded recipients. Sort/uniq the list and deliver by re-injecting
+messages into the mail system. Since recipient expansion uses a
+non-recursive algorithm, the mailer might loop indefinitely,
+re-injecting messages into itself. These local forwarding loops
+must be broken by stamping a message when it reaches the local
+delivery stage (e.g., by adding a Delivered-To: message header).
+
+The Postfix system uses a hybrid approach. It does recursive alias
+expansion, but only one initial recipient at a time. It delivers
+to expanded recipients by re-submitting the message into the mail
+system, so it can keep track of the delivery status for each expanded
+recipient. Because alias expansion does not look in .forward files,
+it cannot prevent local forwarding loops. The Postfix system adds
+Delivered: message headers to break local and external forwarding
+loops.
+
+Delivery status management
+
+The "exact" model: maintain on file the delivery status of each
+expanded recipient: remote recipients, shell commands and files,
+including the privileges for delivery to shell commands and files.
+
+The "safe" model: maintain on file only the delivery status of
+non-sensitive destinations (local or remote addresses). Deliver to
+sensitive destinations first (commands, files), but do not keep a
+record of their status on file (including privileges). This means
+that the mail system will occasionally deliver the same message
+more than once to a file or command.
diff --git a/src/local/alias.c b/src/local/alias.c
new file mode 100644
index 0000000..99e3dd6
--- /dev/null
+++ b/src/local/alias.c
@@ -0,0 +1,386 @@
+/*++
+/* NAME
+/* alias 3
+/* SUMMARY
+/* alias data base lookups
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_alias(state, usr_attr, name, statusp)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *name;
+/* int *statusp;
+/* DESCRIPTION
+/* deliver_alias() looks up the expansion of the recipient in
+/* the global alias database and delivers the message to the
+/* listed destinations. The result is zero when no alias was found
+/* or when the message should be delivered to the user instead.
+/*
+/* deliver_alias() has wired-in knowledge about a few reserved
+/* recipient names.
+/* .IP \(bu
+/* When no alias is found for the local \fIpostmaster\fR or
+/* \fImailer-daemon\fR a warning is issued and the message
+/* is discarded.
+/* .IP \(bu
+/* When an alias exists for recipient \fIname\fR, and an alias
+/* exists for \fIowner-name\fR, the sender address is changed
+/* to \fIowner-name\fR, and the owner delivery attribute is
+/* set accordingly. This feature is disabled with
+/* "owner_request_special = no".
+/* .PP
+/* Arguments:
+/* .IP state
+/* Attributes that specify the message, recipient and more.
+/* Expansion type (alias, include, .forward).
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* User attributes (rights, environment).
+/* .IP name
+/* The alias to be looked up.
+/* .IP statusp
+/* Delivery status. See below.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. The delivery status is non-zero
+/* when delivery should be tried again.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <dict.h>
+#include <argv.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <defer.h>
+#include <maps.h>
+#include <bounce.h>
+#include <mypwd.h>
+#include <canon_addr.h>
+#include <sent.h>
+#include <trace.h>
+#include <dsn_mask.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* Application-specific. */
+
+#define NO 0
+#define YES 1
+
+/* deliver_alias - expand alias file entry */
+
+int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
+ char *name, int *statusp)
+{
+ const char *myname = "deliver_alias";
+ const char *alias_result;
+ char *saved_alias_result;
+ char *owner;
+ char **cpp;
+ struct mypasswd *alias_pwd;
+ VSTRING *canon_owner;
+ DICT *dict;
+ const char *owner_rhs; /* owner alias, RHS */
+ int alias_count;
+ int dsn_notify;
+ char *dsn_envid;
+ int dsn_ret;
+ const char *dsn_orcpt;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE/LOOP ELIMINATION
+ *
+ * We cannot do duplicate elimination here. Sendmail compatibility requires
+ * that we allow multiple deliveries to the same alias, even recursively!
+ * For example, we must deliver to mailbox any messages that are addressed
+ * to the alias of a user that lists that same alias in her own .forward
+ * file. Yuck! This is just an example of some really perverse semantics
+ * that people will expect Postfix to implement just like sendmail.
+ *
+ * We can recognize one special case: when an alias includes its own name,
+ * deliver to the user instead, just like sendmail. Otherwise, we just
+ * bail out when nesting reaches some unreasonable depth, and blame it on
+ * a possible alias loop.
+ */
+ if (state.msg_attr.exp_from != 0
+ && strcasecmp_utf8(state.msg_attr.exp_from, name) == 0)
+ return (NO);
+ if (state.level > 100) {
+ msg_warn("alias database loop for %s", name);
+ dsb_simple(state.msg_attr.why, "5.4.6",
+ "alias database loop for %s", name);
+ *statusp = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ state.msg_attr.exp_from = name;
+
+ /*
+ * There are a bunch of roles that we're trying to keep track of.
+ *
+ * First, there's the issue of whose rights should be used when delivering
+ * to "|command" or to /file/name. With alias databases, the rights are
+ * those of who owns the alias, i.e. the database owner. With aliases
+ * owned by root, a default user is used instead. When an alias with
+ * default rights references an include file owned by an ordinary user,
+ * we must use the rights of the include file owner, otherwise the
+ * include file owner could take control of the default account.
+ *
+ * Secondly, there's the question of who to notify of delivery problems.
+ * With aliases that have an owner- alias, the latter is used to set the
+ * sender and owner attributes. Otherwise, the owner attribute is reset
+ * (the alias is globally visible and could be sent to by anyone).
+ */
+ for (cpp = alias_maps->argv->argv; *cpp; cpp++) {
+ if ((dict = dict_handle(*cpp)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, *cpp);
+ if ((alias_result = dict_get(dict, name)) != 0) {
+ if (msg_verbose)
+ msg_info("%s: %s: %s = %s", myname, *cpp, name, alias_result);
+
+ /*
+ * Don't expand a verify-only request.
+ */
+ if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
+ dsb_simple(state.msg_attr.why, "2.0.0",
+ "aliased to %s", alias_result);
+ *statusp = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ return (YES);
+ }
+
+ /*
+ * DELIVERY POLICY
+ *
+ * Update the expansion type attribute, so we can decide if
+ * deliveries to |command and /file/name are allowed at all.
+ */
+ state.msg_attr.exp_type = EXPAND_TYPE_ALIAS;
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * What rights to use for |command and /file/name deliveries? The
+ * command and file code will use default rights when the alias
+ * database is owned by root, otherwise it will use the rights of
+ * the alias database owner.
+ */
+ if (dict->owner.status == DICT_OWNER_TRUSTED) {
+ alias_pwd = 0;
+ RESET_USER_ATTR(usr_attr, state.level);
+ } else {
+ if (dict->owner.status == DICT_OWNER_UNKNOWN) {
+ msg_warn("%s: no owner UID for alias database %s",
+ myname, *cpp);
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ if ((errno = mypwuid_err(dict->owner.uid, &alias_pwd)) != 0
+ || alias_pwd == 0) {
+ msg_warn(errno ?
+ "cannot find alias database owner for %s: %m" :
+ "cannot find alias database owner for %s", *cpp);
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "cannot find alias database owner");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ SET_USER_ATTR(usr_attr, alias_pwd, state.level);
+ }
+
+ /*
+ * WHERE TO REPORT DELIVERY PROBLEMS.
+ *
+ * Use the owner- alias if one is specified, otherwise reset the
+ * owner attribute and use the include file ownership if we can.
+ * Save the dict_lookup() result before something clobbers it.
+ *
+ * Don't match aliases that are based on regexps.
+ */
+#define OWNER_ASSIGN(own) \
+ (own = (var_ownreq_special == 0 ? 0 : \
+ concatenate("owner-", name, (char *) 0)))
+
+ saved_alias_result = mystrdup(alias_result);
+ if (OWNER_ASSIGN(owner) != 0
+ && (owner_rhs = maps_find(alias_maps, owner, DICT_FLAG_NONE)) != 0) {
+ canon_owner = canon_addr_internal(vstring_alloc(10),
+ var_exp_own_alias ? owner_rhs : owner);
+ /* Set envelope sender and owner attribute. */
+ SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
+ } else {
+ canon_owner = 0;
+ /* Note: this does not reset the envelope sender. */
+ if (var_reset_owner_attr)
+ RESET_OWNER_ATTR(state.msg_attr, state.level);
+ }
+
+ /*
+ * EXTERNAL LOOP CONTROL
+ *
+ * Set the delivered message attribute to the recipient, so that
+ * this message will list the correct forwarding address.
+ */
+ if (var_frozen_delivered == 0)
+ state.msg_attr.delivered = state.msg_attr.rcpt.address;
+
+ /*
+ * Deliver.
+ */
+ alias_count = 0;
+ if (owner != 0 && alias_maps->error != 0) {
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "alias database unavailable");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+
+ /*
+ * XXX DSN
+ *
+ * When delivering to a mailing list (i.e. the envelope sender
+ * is replaced) the ENVID, NOTIFY, RET, and ORCPT parameters
+ * which accompany the redistributed message MUST NOT be
+ * derived from those of the original message.
+ *
+ * When delivering to an alias (i.e. the envelope sender is not
+ * replaced) any ENVID, RET, or ORCPT parameters are
+ * propagated to all forwarding addresses associated with
+ * that alias. The NOTIFY parameter is propagated to the
+ * forwarding addresses, except that any SUCCESS keyword is
+ * removed.
+ */
+#define DSN_SAVE_UPDATE(saved, old, new) do { \
+ saved = old; \
+ old = new; \
+ } while (0)
+
+ DSN_SAVE_UPDATE(dsn_notify, state.msg_attr.rcpt.dsn_notify,
+ dsn_notify == DSN_NOTIFY_SUCCESS ?
+ DSN_NOTIFY_NEVER :
+ dsn_notify & ~DSN_NOTIFY_SUCCESS);
+ if (canon_owner != 0) {
+ DSN_SAVE_UPDATE(dsn_envid, state.msg_attr.dsn_envid, "");
+ DSN_SAVE_UPDATE(dsn_ret, state.msg_attr.dsn_ret, 0);
+ DSN_SAVE_UPDATE(dsn_orcpt, state.msg_attr.rcpt.dsn_orcpt, "");
+ state.msg_attr.rcpt.orig_addr = "";
+ }
+ *statusp =
+ deliver_token_string(state, usr_attr, saved_alias_result,
+ &alias_count);
+#if 0
+ if (var_ownreq_special
+ && strncmp("owner-", state.msg_attr.sender, 6) != 0
+ && alias_count > 10)
+ msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias",
+ name, name);
+#endif
+ if (alias_count < 1) {
+ msg_warn("no recipient in alias lookup result for %s", name);
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "alias database unavailable");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+
+ /*
+ * XXX DSN
+ *
+ * When delivering to a mailing list (i.e. the envelope
+ * sender address is replaced) and NOTIFY=SUCCESS was
+ * specified, report a DSN of "delivered".
+ *
+ * When delivering to an alias (i.e. the envelope sender
+ * address is not replaced) and NOTIFY=SUCCESS was
+ * specified, report a DSN of "expanded".
+ */
+ if (dsn_notify & DSN_NOTIFY_SUCCESS) {
+ state.msg_attr.rcpt.dsn_notify = dsn_notify;
+ if (canon_owner != 0) {
+ state.msg_attr.dsn_envid = dsn_envid;
+ state.msg_attr.dsn_ret = dsn_ret;
+ state.msg_attr.rcpt.dsn_orcpt = dsn_orcpt;
+ }
+ dsb_update(state.msg_attr.why, "2.0.0", canon_owner ?
+ "delivered" : "expanded",
+ DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+ "alias expanded");
+ (void) trace_append(BOUNCE_FLAG_NONE,
+ SENT_ATTR(state.msg_attr));
+ }
+ }
+ }
+ myfree(saved_alias_result);
+ if (owner)
+ myfree(owner);
+ if (canon_owner)
+ vstring_free(canon_owner);
+ if (alias_pwd)
+ mypwfree(alias_pwd);
+ return (YES);
+ }
+
+ /*
+ * If the alias database was inaccessible for some reason, defer
+ * further delivery for the current top-level recipient.
+ */
+ if (alias_result == 0 && dict->error != 0) {
+ msg_warn("%s:%s: lookup of '%s' failed",
+ dict->type, dict->name, name);
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "alias database unavailable");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ } else {
+ if (msg_verbose)
+ msg_info("%s: %s: %s not found", myname, *cpp, name);
+ }
+ }
+
+ /*
+ * Try delivery to a local user instead.
+ */
+ return (NO);
+}
diff --git a/src/local/biff_notify.c b/src/local/biff_notify.c
new file mode 100644
index 0000000..a6a4925
--- /dev/null
+++ b/src/local/biff_notify.c
@@ -0,0 +1,98 @@
+/*++
+/* NAME
+/* biff_notify 3
+/* SUMMARY
+/* send biff notification
+/* SYNOPSIS
+/* #include <biff_notify.h>
+/*
+/* void biff_notify(text, len)
+/* const char *text;
+/* ssize_t len;
+/* DESCRIPTION
+/* biff_notify() sends a \fBBIFF\fR notification request to the
+/* \fBcomsat\fR daemon.
+/*
+/* Arguments:
+/* .IP text
+/* Null-terminated text (username@mailbox-offset).
+/* .IP len
+/* Length of text, including null terminator.
+/* BUGS
+/* The \fBBIFF\fR "service" can be a noticeable load for
+/* systems that have many logged-in users.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* Application-specific. */
+
+#include <biff_notify.h>
+
+/* biff_notify - notify recipient via the biff "protocol" */
+
+void biff_notify(const char *text, ssize_t len)
+{
+ static struct sockaddr_in sin;
+ static int sock = -1;
+ struct hostent *hp;
+ struct servent *sp;
+
+ /*
+ * Initialize a socket address structure, or re-use an existing one.
+ */
+ if (sin.sin_family == 0) {
+ if ((sp = getservbyname("biff", "udp")) == 0) {
+ msg_warn("service not found: biff/udp");
+ return;
+ }
+ if ((hp = gethostbyname("localhost")) == 0) {
+ msg_warn("host not found: localhost");
+ return;
+ }
+ if ((int) hp->h_length > (int) sizeof(sin.sin_addr)) {
+ msg_warn("bad address size %d for localhost", hp->h_length);
+ return;
+ }
+ sin.sin_family = hp->h_addrtype;
+ sin.sin_port = sp->s_port;
+ memcpy((void *) &sin.sin_addr, hp->h_addr_list[0], hp->h_length);
+ }
+
+ /*
+ * Open a socket, or re-use an existing one.
+ */
+ if (sock < 0) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ msg_warn("socket: %m");
+ return;
+ }
+ close_on_exec(sock, CLOSE_ON_EXEC);
+ }
+
+ /*
+ * Biff!
+ */
+ if (sendto(sock, text, len, 0, (struct sockaddr *) &sin, sizeof(sin)) != len)
+ msg_warn("biff_notify: %m");
+}
diff --git a/src/local/biff_notify.h b/src/local/biff_notify.h
new file mode 100644
index 0000000..8b76f9d
--- /dev/null
+++ b/src/local/biff_notify.h
@@ -0,0 +1,30 @@
+#ifndef _BIFF_H_INCLUDED_
+#define _BIFF_H_INCLUDED_
+
+/*++
+/* NAME
+/* biff_notify 3h
+/* SUMMARY
+/* read logical line
+/* SYNOPSIS
+/* #include <biff_notify.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+extern void biff_notify(const char *, ssize_t);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/src/local/bounce_workaround.c b/src/local/bounce_workaround.c
new file mode 100644
index 0000000..7fe4aaa
--- /dev/null
+++ b/src/local/bounce_workaround.c
@@ -0,0 +1,159 @@
+/*++
+/* NAME
+/* bounce_workaround 3
+/* SUMMARY
+/* Send non-delivery notification with sender override
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int bounce_workaround(state)
+/* LOCAL_STATE state;
+/* DESCRIPTION
+/* This module works around a limitation in the bounce daemon
+/* protocol, namely, the assumption that the envelope sender
+/* address in a queue file is the delivery status notification
+/* address for all recipients in that queue file. The assumption
+/* is not valid when the local(8) delivery agent overrides the
+/* envelope sender address by an owner- alias, for one or more
+/* recipients in the queue file.
+/*
+/* Sender address override is a problem only when delivering
+/* to command or file, or when breaking a Delivered-To loop.
+/* The local(8) delivery agent saves normal recipients to a
+/* new queue file, together with the replacement envelope
+/* sender address; delivery then proceeds from that new queue
+/* file, and no workaround is needed.
+/*
+/* The workaround sends one non-delivery notification for each
+/* failed delivery that has a replacement sender address. The
+/* notifications are not aggregated, unlike notifications to
+/* non-replaced sender addresses. In practice, a local alias
+/* rarely has more than one file or command destination (if
+/* only because soft error handling is problematic).
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* The non-delivery status must be either 4.X.X or 5.X.X.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. The result is non-zero when
+/* the operation should be tried again. Warnings: malformed
+/* address.
+/* BUGS
+/* The proper fix is to record in the bounce logfile an error
+/* return address for each individual recipient. This would
+/* eliminate the need for VERP-specific bounce protocol code,
+/* and would move complexity from the bounce client side to
+/* the bounce server side where it more likely belongs.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this
+/* software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <strings.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <strip_addr.h>
+#include <stringops.h>
+#include <bounce.h>
+#include <defer.h>
+#include <split_addr.h>
+#include <canon_addr.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+int bounce_workaround(LOCAL_STATE state)
+{
+ const char *myname = "bounce_workaround";
+ VSTRING *canon_owner = 0;
+ int rcpt_stat;
+
+ /*
+ * Look up the substitute sender address.
+ */
+ if (var_ownreq_special) {
+ char *stripped_recipient;
+ char *owner_alias;
+ const char *owner_expansion;
+
+#define FIND_OWNER(lhs, rhs, addr) { \
+ lhs = concatenate("owner-", addr, (char *) 0); \
+ (void) split_at_right(lhs, '@'); \
+ rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \
+ }
+
+ FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address);
+ if (alias_maps->error == 0 && owner_expansion == 0
+ && (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
+ (char **) 0,
+ var_rcpt_delim)) != 0) {
+ myfree(owner_alias);
+ FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
+ myfree(stripped_recipient);
+ }
+ if (alias_maps->error == 0 && owner_expansion != 0) {
+ canon_owner = canon_addr_internal(vstring_alloc(10),
+ var_exp_own_alias ?
+ owner_expansion : owner_alias);
+ SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
+ }
+ myfree(owner_alias);
+ if (alias_maps->error != 0) {
+ /* At this point, canon_owner == 0. */
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "alias database unavailable");
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ }
+
+ /*
+ * Send a delivery status notification with a single recipient to the
+ * substitute sender address, before completion of the delivery request.
+ */
+ if (canon_owner) {
+ rcpt_stat =
+ (STR(state.msg_attr.why->status)[0] == '4' ?
+ defer_one : bounce_one)
+ (BOUNCE_FLAGS(state.request),
+ BOUNCE_ONE_ATTR(state.msg_attr));
+ vstring_free(canon_owner);
+ }
+
+ /*
+ * Send a regular delivery status notification, after completion of the
+ * delivery request.
+ */
+ else {
+ rcpt_stat =
+ (STR(state.msg_attr.why->status)[0] == '4' ?
+ defer_append : bounce_append)
+ (BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ }
+ return (rcpt_stat);
+}
diff --git a/src/local/command.c b/src/local/command.c
new file mode 100644
index 0000000..4781daf
--- /dev/null
+++ b/src/local/command.c
@@ -0,0 +1,251 @@
+/*++
+/* NAME
+/* command 3
+/* SUMMARY
+/* message delivery to shell command
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_command(state, usr_attr, command)
+/* LOCAL_STATE state;
+/* USER_ATTR exp_attr;
+/* const char *command;
+/* DESCRIPTION
+/* deliver_command() runs a command with a message as standard
+/* input. A limited amount of standard output and standard error
+/* output is captured for diagnostics purposes.
+/* Duplicate commands for the same recipient are suppressed.
+/* A limited amount of information is exported via the environment:
+/* HOME, SHELL, LOGNAME, USER, EXTENSION, DOMAIN, RECIPIENT (entire
+/* address) LOCAL (just the local part) and SENDER. The exported
+/* information is censored with var_cmd_filter.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing the alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP command
+/* The shell command to be executed. If possible, the command is
+/* executed without actually invoking a shell. if the command is
+/* the mailbox_command, it is subjected to $name expansion.
+/* DIAGNOSTICS
+/* deliver_command() returns non-zero when delivery should be
+/* tried again,
+/* SEE ALSO
+/* mailbox(3) deliver to mailbox
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+#include <mac_parse.h>
+
+/* Global library. */
+
+#include <defer.h>
+#include <bounce.h>
+#include <sent.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <pipe_command.h>
+#include <mail_copy.h>
+#include <dsn_util.h>
+#include <mail_parm_split.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_command - deliver to shell command */
+
+int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command)
+{
+ const char *myname = "deliver_command";
+ DSN_BUF *why = state.msg_attr.why;
+ int cmd_status;
+ int deliver_status;
+ ARGV *env;
+ int copy_flags;
+ char **cpp;
+ char *cp;
+ ARGV *export_env;
+ VSTRING *exec_dir;
+ int expand_status;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE ELIMINATION
+ *
+ * Skip this command if it was already delivered to as this user.
+ */
+ if (been_here(state.dup_filter, "command %s:%ld %s",
+ state.msg_attr.user, (long) usr_attr.uid, command))
+ return (0);
+
+ /*
+ * Don't deliver a trace-only request.
+ */
+ if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+ dsb_simple(why, "2.0.0", "delivers to command: %s", command);
+ return (sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * Choose a default uid and gid when none have been selected (i.e. values
+ * are still zero).
+ */
+ if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0)
+ msg_panic("privileged default user id");
+ if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0)
+ msg_panic("privileged default group id");
+
+ /*
+ * Deliver.
+ */
+ copy_flags = MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH
+ | MAIL_COPY_ORIG_RCPT;
+ if (local_deliver_hdr_mask & DELIVER_HDR_CMD)
+ copy_flags |= MAIL_COPY_DELIVERED;
+
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("%s: seek queue file %s: %m",
+ myname, VSTREAM_PATH(state.msg_attr.fp));
+
+ /*
+ * Pass additional environment information. XXX This should be
+ * configurable. However, passing untrusted information via environment
+ * parameters opens up a whole can of worms. Lesson from web servers:
+ * don't let any network data even near a shell. It causes trouble.
+ */
+ env = argv_alloc(1);
+ if (usr_attr.home)
+ argv_add(env, "HOME", usr_attr.home, ARGV_END);
+ argv_add(env,
+ "LOGNAME", state.msg_attr.user,
+ "USER", state.msg_attr.user,
+ "SENDER", state.msg_attr.sender,
+ "RECIPIENT", state.msg_attr.rcpt.address,
+ "LOCAL", state.msg_attr.local,
+ ARGV_END);
+ if (usr_attr.shell)
+ argv_add(env, "SHELL", usr_attr.shell, ARGV_END);
+ if (state.msg_attr.domain)
+ argv_add(env, "DOMAIN", state.msg_attr.domain, ARGV_END);
+ if (state.msg_attr.extension)
+ argv_add(env, "EXTENSION", state.msg_attr.extension, ARGV_END);
+ if (state.msg_attr.rcpt.orig_addr && state.msg_attr.rcpt.orig_addr[0])
+ argv_add(env, "ORIGINAL_RECIPIENT", state.msg_attr.rcpt.orig_addr,
+ ARGV_END);
+
+#define EXPORT_REQUEST(name, value) \
+ if ((value)[0]) argv_add(env, (name), (value), ARGV_END);
+
+ EXPORT_REQUEST("CLIENT_HOSTNAME", state.msg_attr.request->client_name);
+ EXPORT_REQUEST("CLIENT_ADDRESS", state.msg_attr.request->client_addr);
+ EXPORT_REQUEST("CLIENT_HELO", state.msg_attr.request->client_helo);
+ EXPORT_REQUEST("CLIENT_PROTOCOL", state.msg_attr.request->client_proto);
+ EXPORT_REQUEST("SASL_METHOD", state.msg_attr.request->sasl_method);
+ EXPORT_REQUEST("SASL_SENDER", state.msg_attr.request->sasl_sender);
+ EXPORT_REQUEST("SASL_USERNAME", state.msg_attr.request->sasl_username);
+
+ argv_terminate(env);
+
+ /*
+ * Censor out undesirable characters from exported data.
+ */
+ for (cpp = env->argv; *cpp; cpp += 2)
+ for (cp = cpp[1]; *(cp += strspn(cp, var_cmd_exp_filter)) != 0;)
+ *cp++ = '_';
+
+ /*
+ * Evaluate the command execution directory. Defer delivery if expansion
+ * fails.
+ */
+ export_env = mail_parm_split(VAR_EXPORT_ENVIRON, var_export_environ);
+ exec_dir = vstring_alloc(10);
+ expand_status = local_expand(exec_dir, var_exec_directory,
+ &state, &usr_attr, var_exec_exp_filter);
+
+ if (expand_status & MAC_PARSE_ERROR) {
+ cmd_status = PIPE_STAT_DEFER;
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ msg_warn("bad parameter value syntax for %s: %s",
+ VAR_EXEC_DIRECTORY, var_exec_directory);
+ } else {
+ cmd_status = pipe_command(state.msg_attr.fp, why,
+ CA_PIPE_CMD_UID(usr_attr.uid),
+ CA_PIPE_CMD_GID(usr_attr.gid),
+ CA_PIPE_CMD_COMMAND(command),
+ CA_PIPE_CMD_COPY_FLAGS(copy_flags),
+ CA_PIPE_CMD_SENDER(state.msg_attr.sender),
+ CA_PIPE_CMD_ORIG_RCPT(state.msg_attr.rcpt.orig_addr),
+ CA_PIPE_CMD_DELIVERED(state.msg_attr.delivered),
+ CA_PIPE_CMD_TIME_LIMIT(var_command_maxtime),
+ CA_PIPE_CMD_ENV(env->argv),
+ CA_PIPE_CMD_EXPORT(export_env->argv),
+ CA_PIPE_CMD_SHELL(var_local_cmd_shell),
+ CA_PIPE_CMD_CWD(*STR(exec_dir) ?
+ STR(exec_dir) : (char *) 0),
+ CA_PIPE_CMD_END);
+ }
+ vstring_free(exec_dir);
+ argv_free(export_env);
+ argv_free(env);
+
+ /*
+ * Depending on the result, bounce or defer the message.
+ */
+ switch (cmd_status) {
+ case PIPE_STAT_OK:
+ dsb_simple(why, "2.0.0", "delivered to command: %s", command);
+ deliver_status = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ break;
+ case PIPE_STAT_BOUNCE:
+ case PIPE_STAT_DEFER:
+ /* Account for possible owner- sender address override. */
+ deliver_status = bounce_workaround(state);
+ break;
+ case PIPE_STAT_CORRUPT:
+ deliver_status = DEL_STAT_DEFER;
+ break;
+ default:
+ msg_panic("%s: bad status %d", myname, cmd_status);
+ /* NOTREACHED */
+ }
+
+ return (deliver_status);
+}
diff --git a/src/local/deliver_attr.c b/src/local/deliver_attr.c
new file mode 100644
index 0000000..9ed18e2
--- /dev/null
+++ b/src/local/deliver_attr.c
@@ -0,0 +1,105 @@
+/*++
+/* NAME
+/* deliver_attr 3
+/* SUMMARY
+/* initialize message delivery attributes
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* void deliver_attr_init(attrp)
+/* DELIVER_ATTR *attrp;
+/*
+/* void deliver_attr_dump(attrp)
+/* DELIVER_ATTR *attrp;
+/*
+/* void deliver_attr_free(attrp)
+/* DELIVER_ATTR *attrp;
+/* DESCRIPTION
+/* deliver_attr_init() initializes a structure with message delivery
+/* attributes to a known initial state (all zeros).
+/*
+/* deliver_attr_dump() logs the contents of the given attribute list.
+/*
+/* deliver_attr_free() releases memory that was allocated by
+/* deliver_attr_init().
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_attr_init - set message delivery attributes to all-zero state */
+
+void deliver_attr_init(DELIVER_ATTR *attrp)
+{
+ attrp->level = 0;
+ attrp->fp = 0;
+ attrp->queue_name = 0;
+ attrp->queue_id = 0;
+ attrp->offset = 0;
+ attrp->sender = 0;
+ RECIPIENT_ASSIGN(&(attrp->rcpt), 0, 0, 0, 0, 0);
+ attrp->domain = 0;
+ attrp->local = 0;
+ attrp->user = 0;
+ attrp->extension = 0;
+ attrp->unmatched = 0;
+ attrp->owner = 0;
+ attrp->delivered = 0;
+ attrp->relay = 0;
+ attrp->exp_type = 0;
+ attrp->exp_from = 0;
+ attrp->why = dsb_create();
+}
+
+/* deliver_attr_dump - log message delivery attributes */
+
+void deliver_attr_dump(DELIVER_ATTR *attrp)
+{
+ msg_info("level: %d", attrp->level);
+ msg_info("path: %s", VSTREAM_PATH(attrp->fp));
+ msg_info("fp: 0x%lx", (long) attrp->fp);
+ msg_info("queue_name: %s", attrp->queue_name ? attrp->queue_name : "null");
+ msg_info("queue_id: %s", attrp->queue_id ? attrp->queue_id : "null");
+ msg_info("offset: %ld", attrp->rcpt.offset);
+ msg_info("sender: %s", attrp->sender ? attrp->sender : "null");
+ msg_info("recipient: %s", attrp->rcpt.address ? attrp->rcpt.address : "null");
+ msg_info("domain: %s", attrp->domain ? attrp->domain : "null");
+ msg_info("local: %s", attrp->local ? attrp->local : "null");
+ msg_info("user: %s", attrp->user ? attrp->user : "null");
+ msg_info("extension: %s", attrp->extension ? attrp->extension : "null");
+ msg_info("unmatched: %s", attrp->unmatched ? attrp->unmatched : "null");
+ msg_info("owner: %s", attrp->owner ? attrp->owner : "null");
+ msg_info("delivered: %s", attrp->delivered ? attrp->delivered : "null");
+ msg_info("relay: %s", attrp->relay ? attrp->relay : "null");
+ msg_info("exp_type: %d", attrp->exp_type);
+ msg_info("exp_from: %s", attrp->exp_from ? attrp->exp_from : "null");
+ msg_info("why: %s", attrp->why ? "buffer" : "null");
+}
+
+/* deliver_attr_free - release storage */
+
+void deliver_attr_free(DELIVER_ATTR *attrp)
+{
+ dsb_free(attrp->why);
+}
diff --git a/src/local/dotforward.c b/src/local/dotforward.c
new file mode 100644
index 0000000..3ce2cfc
--- /dev/null
+++ b/src/local/dotforward.c
@@ -0,0 +1,304 @@
+/*++
+/* NAME
+/* dotforward 3
+/* SUMMARY
+/* $HOME/.forward file expansion
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_dotforward(state, usr_attr, statusp)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* int *statusp;
+/* DESCRIPTION
+/* deliver_dotforward() delivers a message to the destinations
+/* listed in a recipient's .forward file(s) as specified through
+/* the forward_path configuration parameter. The result is
+/* zero when no acceptable .forward file was found, or when
+/* a recipient is listed in her own .forward file. Expansions
+/* are scrutinized with the forward_expansion_filter parameter.
+/*
+/* Arguments:
+/* .IP state
+/* Message delivery attributes (sender, recipient etc.).
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP statusp
+/* Message delivery status. See below.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. Warnings: bad $HOME/.forward
+/* file type, permissions or ownership. The message delivery
+/* status is non-zero when delivery should be tried again.
+/* SEE ALSO
+/* include(3) include file processor.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <htable.h>
+#include <open_as.h>
+#include <lstat_as.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <mac_expand.h>
+
+/* Global library. */
+
+#include <mypwd.h>
+#include <bounce.h>
+#include <defer.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <ext_prop.h>
+#include <sent.h>
+#include <dsn_mask.h>
+#include <trace.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+#define NO 0
+#define YES 1
+
+/* deliver_dotforward - expand contents of .forward file */
+
+int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
+{
+ const char *myname = "deliver_dotforward";
+ struct stat st;
+ VSTRING *path;
+ struct mypasswd *mypwd;
+ int fd;
+ VSTREAM *fp;
+ int status;
+ int forward_found = NO;
+ int lookup_status;
+ int addr_count;
+ char *saved_forward_path;
+ char *lhs;
+ char *next;
+ int expand_status;
+ int saved_notify;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Skip this module if per-user forwarding is disabled.
+ */
+ if (*var_forward_path == 0)
+ return (NO);
+
+ /*
+ * Skip non-existing users. The mailbox delivery routine will catch the
+ * error.
+ */
+ if ((errno = mypwnam_err(state.msg_attr.user, &mypwd)) != 0) {
+ msg_warn("error looking up passwd info for %s: %m",
+ state.msg_attr.user);
+ dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ if (mypwd == 0)
+ return (NO);
+
+ /*
+ * From here on no early returns or we have a memory leak.
+ */
+
+ /*
+ * EXTERNAL LOOP CONTROL
+ *
+ * Set the delivered message attribute to the recipient, so that this
+ * message will list the correct forwarding address.
+ */
+ if (var_frozen_delivered == 0)
+ state.msg_attr.delivered = state.msg_attr.rcpt.address;
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * Do not inherit rights from the .forward file owner. Instead, use the
+ * recipient's rights, and insist that the .forward file is owned by the
+ * recipient. This is a small but significant difference. Use the
+ * recipient's rights for all /file and |command deliveries, and pass on
+ * these rights to command/file destinations in included files. When
+ * these are the rights of root, the /file and |command delivery routines
+ * will use unprivileged default rights instead. Better safe than sorry.
+ */
+ SET_USER_ATTR(usr_attr, mypwd, state.level);
+
+ /*
+ * DELIVERY POLICY
+ *
+ * Update the expansion type attribute so that we can decide if deliveries
+ * to |command and /file/name are allowed at all.
+ */
+ state.msg_attr.exp_type = EXPAND_TYPE_FWD;
+
+ /*
+ * WHERE TO REPORT DELIVERY PROBLEMS
+ *
+ * Set the owner attribute so that 1) include files won't set the sender to
+ * be this user and 2) mail forwarded to other local users will be
+ * resubmitted as a new queue file.
+ */
+ state.msg_attr.owner = state.msg_attr.user;
+
+ /*
+ * Search the forward_path for an existing forward file.
+ *
+ * If unmatched extensions should never be propagated, or if a forward file
+ * name includes the address extension, don't propagate the extension to
+ * the recipient addresses.
+ */
+ status = 0;
+ path = vstring_alloc(100);
+ saved_forward_path = mystrdup(var_forward_path);
+ next = saved_forward_path;
+ lookup_status = -1;
+
+ while ((lhs = mystrtok(&next, CHARS_COMMA_SP)) != 0) {
+ expand_status = local_expand(path, lhs, &state, &usr_attr,
+ var_fwd_exp_filter);
+ if ((expand_status & (MAC_PARSE_ERROR | MAC_PARSE_UNDEF)) == 0) {
+ lookup_status =
+ lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid);
+ if (msg_verbose)
+ msg_info("%s: path %s expand_status %d look_status %d", myname,
+ STR(path), expand_status, lookup_status);
+ if (lookup_status >= 0) {
+ if ((expand_status & LOCAL_EXP_EXTENSION_MATCHED) != 0
+ || (local_ext_prop_mask & EXT_PROP_FORWARD) == 0)
+ state.msg_attr.unmatched = 0;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Process the forward file.
+ *
+ * Assume that usernames do not have file system meta characters. Open the
+ * .forward file as the user. Ignore files that aren't regular files,
+ * files that are owned by the wrong user, or files that have world write
+ * permission enabled.
+ *
+ * DUPLICATE/LOOP ELIMINATION
+ *
+ * If this user includes (an alias of) herself in her own .forward file,
+ * deliver to the user instead.
+ */
+ if (lookup_status >= 0) {
+
+ /*
+ * Don't expand a verify-only request.
+ */
+ if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
+ dsb_simple(state.msg_attr.why, "2.0.0",
+ "forward via file: %s", STR(path));
+ *statusp = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ forward_found = YES;
+ } else if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
+ state.msg_attr.exp_from = state.msg_attr.local;
+ if (S_ISREG(st.st_mode) == 0) {
+ msg_warn("file %s is not a regular file", STR(path));
+ } else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) {
+ msg_warn("file %s has bad owner uid %ld",
+ STR(path), (long) st.st_uid);
+ } else if (st.st_mode & 002) {
+ msg_warn("file %s is world writable", STR(path));
+ } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
+ msg_warn("cannot open file %s: %m", STR(path));
+ } else {
+
+ /*
+ * XXX DSN. When delivering to an alias (i.e. the envelope
+ * sender address is not replaced) any ENVID, RET, or ORCPT
+ * parameters are propagated to all forwarding addresses
+ * associated with that alias. The NOTIFY parameter is
+ * propagated to the forwarding addresses, except that any
+ * SUCCESS keyword is removed.
+ */
+ close_on_exec(fd, CLOSE_ON_EXEC);
+ addr_count = 0;
+ fp = vstream_fdopen(fd, O_RDONLY);
+ saved_notify = state.msg_attr.rcpt.dsn_notify;
+ state.msg_attr.rcpt.dsn_notify =
+ (saved_notify == DSN_NOTIFY_SUCCESS ?
+ DSN_NOTIFY_NEVER : saved_notify & ~DSN_NOTIFY_SUCCESS);
+ status = deliver_token_stream(state, usr_attr, fp, &addr_count);
+ if (vstream_fclose(fp))
+ msg_warn("close file %s: %m", STR(path));
+ if (addr_count > 0) {
+ forward_found = YES;
+ been_here(state.dup_filter, "forward-done %s", STR(path));
+
+ /*
+ * XXX DSN. When delivering to an alias (i.e. the
+ * envelope sender address is not replaced) and the
+ * original NOTIFY parameter for the alias contained the
+ * SUCCESS keyword, an "expanded" DSN is issued for the
+ * alias.
+ */
+ if (status == 0 && (saved_notify & DSN_NOTIFY_SUCCESS)) {
+ state.msg_attr.rcpt.dsn_notify = saved_notify;
+ dsb_update(state.msg_attr.why, "2.0.0", "expanded",
+ DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+ "alias expanded");
+ (void) trace_append(BOUNCE_FLAG_NONE,
+ SENT_ATTR(state.msg_attr));
+ }
+ }
+ }
+ } else if (been_here_check(state.dup_filter, "forward-done %s", STR(path)) != 0)
+ forward_found = YES; /* else we're recursive */
+ }
+
+ /*
+ * Clean up.
+ */
+ vstring_free(path);
+ myfree(saved_forward_path);
+ mypwfree(mypwd);
+
+ *statusp = status;
+ return (forward_found);
+}
diff --git a/src/local/file.c b/src/local/file.c
new file mode 100644
index 0000000..0cc4c18
--- /dev/null
+++ b/src/local/file.c
@@ -0,0 +1,195 @@
+/*++
+/* NAME
+/* file 3
+/* SUMMARY
+/* mail delivery to arbitrary file
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_file(state, usr_attr, path)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *path;
+/* DESCRIPTION
+/* deliver_file() appends a message to a file, UNIX mailbox format,
+/* or qmail maildir format,
+/* with duplicate suppression. It will deliver only to non-executable
+/* regular files.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* .IP usr_attr
+/* Attributes describing user rights and environment information.
+/* .IP path
+/* The file to deliver to. If the name ends in '/', delivery is done
+/* in qmail maildir format, otherwise delivery is done in UNIX mailbox
+/* format.
+/* DIAGNOSTICS
+/* deliver_file() returns non-zero when delivery should be tried again.
+/* SEE ALSO
+/* defer(3)
+/* bounce(3)
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <deliver_flock.h>
+#include <set_eugid.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <bounce.h>
+#include <defer.h>
+#include <sent.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <mbox_conf.h>
+#include <mbox_open.h>
+#include <dsn_util.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_file - deliver to file */
+
+int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
+{
+ const char *myname = "deliver_file";
+ struct stat st;
+ MBOX *mp;
+ DSN_BUF *why = state.msg_attr.why;
+ int mail_copy_status = MAIL_COPY_STAT_WRITE;
+ int deliver_status;
+ int copy_flags;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE ELIMINATION
+ *
+ * Skip this file if it was already delivered to as this user.
+ */
+ if (been_here(state.dup_filter, "file %ld %s", (long) usr_attr.uid, path))
+ return (0);
+
+ /*
+ * DELIVERY POLICY
+ *
+ * Do we allow delivery to files?
+ */
+ if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) {
+ dsb_simple(why, "5.7.1", "mail to file is restricted");
+ /* Account for possible owner- sender address override. */
+ return (bounce_workaround(state));
+ }
+
+ /*
+ * Don't deliver trace-only requests.
+ */
+ if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+ dsb_simple(why, "2.0.0", "delivers to file: %s", path);
+ return (sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * Use a default uid/gid when none are given.
+ */
+ if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0)
+ msg_panic("privileged default user id");
+ if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0)
+ msg_panic("privileged default group id");
+
+ /*
+ * If the name ends in /, use maildir-style delivery instead.
+ */
+ if (path[strlen(path) - 1] == '/')
+ return (deliver_maildir(state, usr_attr, path));
+
+ /*
+ * Deliver. From here on, no early returns or we have a memory leak.
+ */
+ if (msg_verbose)
+ msg_info("deliver_file (%ld,%ld): %s",
+ (long) usr_attr.uid, (long) usr_attr.gid, path);
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id);
+
+ /*
+ * As the specified user, open or create the file, lock it, and append
+ * the message.
+ */
+ copy_flags = MAIL_COPY_MBOX;
+ if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0)
+ copy_flags &= ~MAIL_COPY_DELIVERED;
+
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR, &st, -1, -1,
+ local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL,
+ "5.2.0", why);
+ if (mp != 0) {
+ if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ vstream_fclose(mp->fp);
+ dsb_simple(why, "5.7.1", "file is executable");
+ } else {
+ mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ S_ISREG(st.st_mode) ? copy_flags :
+ (copy_flags & ~MAIL_COPY_TOFILE),
+ "\n", why);
+ }
+ mbox_release(mp);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * As the mail system, bounce, defer delivery, or report success.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ vstring_sprintf_prepend(why->reason,
+ "cannot append message to file %s: ", path);
+ /* Account for possible owner- sender address override. */
+ deliver_status = bounce_workaround(state);
+ } else {
+ dsb_simple(why, "2.0.0", "delivered to file: %s", path);
+ deliver_status = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ }
+ return (deliver_status);
+}
diff --git a/src/local/forward.c b/src/local/forward.c
new file mode 100644
index 0000000..722dcf7
--- /dev/null
+++ b/src/local/forward.c
@@ -0,0 +1,393 @@
+/*++
+/* NAME
+/* forward 3
+/* SUMMARY
+/* message forwarding
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int forward_init()
+/*
+/* int forward_append(attr)
+/* DELIVER_ATTR attr;
+/*
+/* int forward_finish(request, attr, cancel)
+/* DELIVER_REQUEST *request;
+/* DELIVER_ATTR attr;
+/* int cancel;
+/* DESCRIPTION
+/* This module implements the client interface for message
+/* forwarding.
+/*
+/* forward_init() initializes internal data structures.
+/*
+/* forward_append() appends a recipient to the list of recipients
+/* that will receive a message with the specified message sender
+/* and delivered-to addresses.
+/*
+/* forward_finish() forwards the actual message contents and
+/* releases the memory allocated by forward_init() and by
+/* forward_append(). When the \fIcancel\fR argument is true, no
+/* messages will be forwarded. The \fIattr\fR argument specifies
+/* the original message delivery attributes as they were before
+/* alias or forward expansions.
+/* DIAGNOSTICS
+/* A non-zero result means that the requested operation should
+/* be tried again.
+/* Warnings: problems connecting to the forwarding service,
+/* corrupt message file. A corrupt message is saved to the
+/* "corrupt" queue for further inspection.
+/* Fatal: out of memory.
+/* Panic: missing forward_init() or forward_finish() call.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <argv.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <iostuff.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <cleanup_user.h>
+#include <sent.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mark_corrupt.h>
+#include <mail_date.h>
+#include <mail_params.h>
+#include <dsn_mask.h>
+#include <smtputf8.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+ /*
+ * Use one cleanup service connection for each (delivered to, sender) pair.
+ */
+static HTABLE *forward_dt;
+
+typedef struct FORWARD_INFO {
+ VSTREAM *cleanup; /* clean up service handle */
+ char *queue_id; /* forwarded message queue id */
+ struct timeval posting_time; /* posting time */
+} FORWARD_INFO;
+
+/* forward_init - prepare for forwarding */
+
+int forward_init(void)
+{
+
+ /*
+ * Sanity checks.
+ */
+ if (forward_dt != 0)
+ msg_panic("forward_init: missing forward_finish call");
+
+ forward_dt = htable_create(0);
+ return (0);
+}
+
+/* forward_open - open connection to cleanup service */
+
+static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
+{
+ VSTRING *buffer = vstring_alloc(100);
+ FORWARD_INFO *info;
+ VSTREAM *cleanup;
+
+#define FORWARD_OPEN_RETURN(res) do { \
+ vstring_free(buffer); \
+ return (res); \
+ } while (0)
+
+ /*
+ * Contact the cleanup service and save the new mail queue id. Request
+ * that the cleanup service bounces bad messages to the sender so that we
+ * can avoid the trouble of bounce management.
+ *
+ * In case you wonder what kind of bounces, examples are "too many hops",
+ * "message too large", perhaps some others. The reason not to bounce
+ * ourselves is that we don't really know who the recipients are.
+ */
+ cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING);
+ if (cleanup == 0) {
+ msg_warn("connect to %s/%s: %m",
+ MAIL_CLASS_PUBLIC, var_cleanup_service);
+ FORWARD_OPEN_RETURN(0);
+ }
+ close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC);
+ if (attr_scan(cleanup, ATTR_FLAG_STRICT,
+ RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_CLEANUP),
+ RECV_ATTR_STR(MAIL_ATTR_QUEUEID, buffer),
+ ATTR_TYPE_END) != 1) {
+ vstream_fclose(cleanup);
+ FORWARD_OPEN_RETURN(0);
+ }
+ info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO));
+ info->cleanup = cleanup;
+ info->queue_id = mystrdup(STR(buffer));
+ GETTIMEOFDAY(&info->posting_time);
+
+#define FORWARD_CLEANUP_FLAGS \
+ (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL \
+ | smtputf8_autodetect(MAIL_SRC_MASK_FORWARD) \
+ | ((request->smtputf8 & SMTPUTF8_FLAG_REQUESTED) ? \
+ CLEANUP_FLAG_SMTPUTF8 : 0))
+
+ attr_print(cleanup, ATTR_FLAG_NONE,
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, FORWARD_CLEANUP_FLAGS),
+ ATTR_TYPE_END);
+
+ /*
+ * Send initial message envelope information. For bounces, set the
+ * designated sender: mailing list owner, posting user, whatever.
+ */
+ rec_fprintf(cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
+ REC_TYPE_TIME_ARG(info->posting_time));
+ rec_fputs(cleanup, REC_TYPE_FROM, sender);
+
+ /*
+ * Don't send the original envelope ID or full/headers return mask if it
+ * was reset due to mailing list expansion.
+ */
+ if (request->dsn_ret)
+ rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%d",
+ MAIL_ATTR_DSN_RET, request->dsn_ret);
+ if (request->dsn_envid && *(request->dsn_envid))
+ rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_DSN_ENVID, request->dsn_envid);
+
+ /*
+ * Zero-length attribute values are place holders for unavailable
+ * attribute values. See qmgr_message.c. They are not meant to be
+ * propagated to queue files.
+ */
+#define PASS_ATTR(fp, name, value) do { \
+ if ((value) && *(value)) \
+ rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \
+ } while (0)
+
+ /*
+ * XXX encapsulate these as one object.
+ */
+ PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name);
+ PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr);
+ PASS_ATTR(cleanup, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto);
+ PASS_ATTR(cleanup, MAIL_ATTR_LOG_HELO_NAME, request->client_helo);
+ PASS_ATTR(cleanup, MAIL_ATTR_SASL_METHOD, request->sasl_method);
+ PASS_ATTR(cleanup, MAIL_ATTR_SASL_USERNAME, request->sasl_username);
+ PASS_ATTR(cleanup, MAIL_ATTR_SASL_SENDER, request->sasl_sender);
+ PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident);
+ PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context);
+
+ FORWARD_OPEN_RETURN(info);
+}
+
+/* forward_append - append recipient to message envelope */
+
+int forward_append(DELIVER_ATTR attr)
+{
+ FORWARD_INFO *info;
+ HTABLE *table_snd;
+
+ /*
+ * Sanity checks.
+ */
+ if (msg_verbose)
+ msg_info("forward delivered=%s sender=%s recip=%s",
+ attr.delivered, attr.sender, attr.rcpt.address);
+ if (forward_dt == 0)
+ msg_panic("forward_append: missing forward_init call");
+
+ /*
+ * In order to find the recipient list, first index a table by
+ * delivered-to header address, then by envelope sender address.
+ */
+ if ((table_snd = (HTABLE *) htable_find(forward_dt, attr.delivered)) == 0) {
+ table_snd = htable_create(0);
+ htable_enter(forward_dt, attr.delivered, (void *) table_snd);
+ }
+ if ((info = (FORWARD_INFO *) htable_find(table_snd, attr.sender)) == 0) {
+ if ((info = forward_open(attr.request, attr.sender)) == 0)
+ return (-1);
+ htable_enter(table_snd, attr.sender, (void *) info);
+ }
+
+ /*
+ * Append the recipient to the message envelope. Don't send the original
+ * recipient or notification mask if it was reset due to mailing list
+ * expansion.
+ */
+ if (*attr.rcpt.dsn_orcpt)
+ rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_DSN_ORCPT, attr.rcpt.dsn_orcpt);
+ if (attr.rcpt.dsn_notify)
+ rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%d",
+ MAIL_ATTR_DSN_NOTIFY, attr.rcpt.dsn_notify);
+ if (*attr.rcpt.orig_addr)
+ rec_fputs(info->cleanup, REC_TYPE_ORCP, attr.rcpt.orig_addr);
+ rec_fputs(info->cleanup, REC_TYPE_RCPT, attr.rcpt.address);
+
+ return (vstream_ferror(info->cleanup));
+}
+
+/* forward_send - send forwarded message */
+
+static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
+ DELIVER_ATTR attr, char *delivered)
+{
+ const char *myname = "forward_send";
+ VSTRING *buffer = vstring_alloc(100);
+ VSTRING *folded;
+ int status;
+ int rec_type = 0;
+
+ /*
+ * Start the message content segment. Prepend our Delivered-To: header to
+ * the message data. Stop at the first error. XXX Rely on the front-end
+ * services to enforce record size limits.
+ */
+ rec_fputs(info->cleanup, REC_TYPE_MESG, "");
+ vstring_strcpy(buffer, delivered);
+ rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)",
+ var_myhostname, var_mail_name);
+ rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s",
+ info->queue_id, mail_date(info->posting_time.tv_sec));
+ if (local_deliver_hdr_mask & DELIVER_HDR_FWD) {
+ folded = vstring_alloc(100);
+ rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s",
+ casefold(folded, (STR(buffer))));
+ vstring_free(folded);
+ }
+ if ((status = vstream_ferror(info->cleanup)) == 0)
+ if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
+ msg_fatal("%s: seek queue file %s: %m:",
+ myname, VSTREAM_PATH(attr.fp));
+ while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) {
+ if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM)
+ break;
+ status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type);
+ }
+ if (status == 0 && rec_type != REC_TYPE_XTRA) {
+ msg_warn("%s: bad record type: %d in message content",
+ info->queue_id, rec_type);
+ status |= mark_corrupt(attr.fp);
+ }
+
+ /*
+ * Send the end-of-data marker only when there were no errors.
+ */
+ if (status == 0) {
+ rec_fputs(info->cleanup, REC_TYPE_XTRA, "");
+ rec_fputs(info->cleanup, REC_TYPE_END, "");
+ }
+
+ /*
+ * Retrieve the cleanup service completion status only if there are no
+ * problems.
+ */
+ if (status == 0)
+ if (vstream_fflush(info->cleanup)
+ || attr_scan(info->cleanup, ATTR_FLAG_MISSING,
+ RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
+ ATTR_TYPE_END) != 1)
+ status = 1;
+
+ /*
+ * Log successful forwarding.
+ *
+ * XXX DSN alias and .forward expansion already report SUCCESS, so don't do
+ * it again here.
+ */
+ if (status == 0) {
+ attr.rcpt.dsn_notify =
+ (attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ?
+ DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS);
+ dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+ "forwarded as %s", info->queue_id);
+ status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr));
+ }
+
+ /*
+ * Cleanup.
+ */
+ vstring_free(buffer);
+ return (status);
+}
+
+/* forward_finish - complete message forwarding requests and clean up */
+
+int forward_finish(DELIVER_REQUEST *request, DELIVER_ATTR attr, int cancel)
+{
+ HTABLE_INFO **dt_list;
+ HTABLE_INFO **dt;
+ HTABLE_INFO **sn_list;
+ HTABLE_INFO **sn;
+ HTABLE *table_snd;
+ char *delivered;
+ char *sender;
+ FORWARD_INFO *info;
+ int status = cancel;
+
+ /*
+ * Sanity checks.
+ */
+ if (forward_dt == 0)
+ msg_panic("forward_finish: missing forward_init call");
+
+ /*
+ * Walk over all delivered-to header addresses and over each envelope
+ * sender address.
+ */
+ for (dt = dt_list = htable_list(forward_dt); *dt; dt++) {
+ delivered = dt[0]->key;
+ table_snd = (HTABLE *) dt[0]->value;
+ for (sn = sn_list = htable_list(table_snd); *sn; sn++) {
+ sender = sn[0]->key;
+ info = (FORWARD_INFO *) sn[0]->value;
+ if (status == 0)
+ status |= forward_send(info, request, attr, delivered);
+ if (msg_verbose)
+ msg_info("forward_finish: delivered %s sender %s status %d",
+ delivered, sender, status);
+ (void) vstream_fclose(info->cleanup);
+ myfree(info->queue_id);
+ myfree((void *) info);
+ }
+ myfree((void *) sn_list);
+ htable_free(table_snd, (void (*) (void *)) 0);
+ }
+ myfree((void *) dt_list);
+ htable_free(forward_dt, (void (*) (void *)) 0);
+ forward_dt = 0;
+ return (status);
+}
diff --git a/src/local/include.c b/src/local/include.c
new file mode 100644
index 0000000..a213d3c
--- /dev/null
+++ b/src/local/include.c
@@ -0,0 +1,223 @@
+/*++
+/* NAME
+/* deliver_include 3
+/* SUMMARY
+/* deliver to addresses listed in include file
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_include(state, usr_attr, path)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *path;
+/* DESCRIPTION
+/* deliver_include() processes the contents of the named include
+/* file and delivers to each address listed. Some sanity checks
+/* are done on the include file permissions and type.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing alias, include, or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP path
+/* Pathname of the include file.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. Warnings: bad include file type
+/* or permissions. The result is non-zero when delivery should be
+/* tried again.
+/* SEE ALSO
+/* token(3) tokenize list
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <open_as.h>
+#include <stat_as.h>
+#include <iostuff.h>
+#include <mypwd.h>
+
+/* Global library. */
+
+#include <bounce.h>
+#include <defer.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <ext_prop.h>
+#include <sent.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_include - open include file and deliver */
+
+int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
+{
+ const char *myname = "deliver_include";
+ struct stat st;
+ struct mypasswd *file_pwd = 0;
+ int status;
+ VSTREAM *fp;
+ int fd;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE ELIMINATION
+ *
+ * Don't process this include file more than once as this particular user.
+ */
+ if (been_here(state.dup_filter, "include %ld %s", (long) usr_attr.uid, path))
+ return (0);
+ state.msg_attr.exp_from = state.msg_attr.local;
+
+ /*
+ * Can of worms. Allow this include file to be symlinked, but disallow
+ * inclusion of special files or of files with world write permission
+ * enabled.
+ */
+ if (*path != '/') {
+ msg_warn(":include:%s uses a relative path", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (stat_as(path, &st, usr_attr.uid, usr_attr.gid) < 0) {
+ msg_warn("unable to lookup :include: file %s: %m", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (S_ISREG(st.st_mode) == 0) {
+ msg_warn(":include: file %s is not a regular file", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (st.st_mode & S_IWOTH) {
+ msg_warn(":include: file %s is world writable", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * DELIVERY POLICY
+ *
+ * Set the expansion type attribute so that we can decide if destinations
+ * such as /file/name and |command are allowed at all.
+ */
+ state.msg_attr.exp_type = EXPAND_TYPE_INCL;
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * When a non-root include file is listed in a root-owned alias, use the
+ * rights of the include file owner. We do not want to give the include
+ * file owner control of the default account.
+ *
+ * When an include file is listed in a user-owned alias or .forward file,
+ * leave the delivery rights alone. Users should not be able to make
+ * things happen with someone else's rights just by including some file
+ * that is owned by their victim.
+ */
+ if (usr_attr.uid == 0) {
+ if ((errno = mypwuid_err(st.st_uid, &file_pwd)) != 0 || file_pwd == 0) {
+ msg_warn(errno ? "cannot find username for uid %ld: %m" :
+ "cannot find username for uid %ld", (long) st.st_uid);
+ msg_warn("%s: cannot find :include: file owner username", path);
+ dsb_simple(state.msg_attr.why, "4.3.5",
+ "mail system configuration error");
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (file_pwd->pw_uid != 0)
+ SET_USER_ATTR(usr_attr, file_pwd, state.level);
+ }
+
+ /*
+ * MESSAGE FORWARDING
+ *
+ * When no owner attribute is set (either via an owner- alias, or as part of
+ * .forward file processing), set the owner attribute, to disable direct
+ * delivery of local recipients. By now it is clear that the owner
+ * attribute should have been called forwarder instead.
+ */
+ if (state.msg_attr.owner == 0)
+ state.msg_attr.owner = state.msg_attr.rcpt.address;
+
+ /*
+ * From here on no early returns or we have a memory leak.
+ *
+ * FILE OPEN RIGHTS
+ *
+ * Use the delivery rights to open the include file. When no delivery rights
+ * were established sofar, the file containing the :include: is owned by
+ * root, so it should be OK to open any file that is accessible to root.
+ * The command and file delivery routines are responsible for setting the
+ * proper delivery rights. These are the rights of the default user, in
+ * case the :include: is in a root-owned alias.
+ *
+ * Don't propagate unmatched extensions unless permitted to do so.
+ */
+#define FOPEN_AS(p,u,g) ((fd = open_as(p,O_RDONLY,0,u,g)) >= 0 ? \
+ vstream_fdopen(fd,O_RDONLY) : 0)
+
+ if ((fp = FOPEN_AS(path, usr_attr.uid, usr_attr.gid)) == 0) {
+ msg_warn("cannot open include file %s: %m", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+ if ((local_ext_prop_mask & EXT_PROP_INCLUDE) == 0)
+ state.msg_attr.unmatched = 0;
+ close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC);
+ status = deliver_token_stream(state, usr_attr, fp, (int *) 0);
+ if (vstream_fclose(fp))
+ msg_warn("close %s: %m", path);
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (file_pwd)
+ mypwfree(file_pwd);
+
+ return (status);
+}
diff --git a/src/local/indirect.c b/src/local/indirect.c
new file mode 100644
index 0000000..a9699a5
--- /dev/null
+++ b/src/local/indirect.c
@@ -0,0 +1,94 @@
+/*++
+/* NAME
+/* indirect 3
+/* SUMMARY
+/* indirect delivery
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* void deliver_indirect(state)
+/* LOCAL_STATE state;
+/* char *recipient;
+/* DESCRIPTION
+/* deliver_indirect() delivers a message via the message
+/* forwarding service, with duplicate filtering up to a
+/* configurable number of recipients.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, sender and more.
+/* A table with the results from expanding aliases or lists.
+/* CONFIGURATION VARIABLES
+/* duplicate_filter_limit, duplicate filter size limit
+/* DIAGNOSTICS
+/* The result is non-zero when the operation should be tried again.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <bounce.h>
+#include <defer.h>
+#include <been_here.h>
+#include <sent.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_indirect - deliver mail via forwarding service */
+
+int deliver_indirect(LOCAL_STATE state)
+{
+
+ /*
+ * Suppress duplicate expansion results. Add some sugar to the name to
+ * avoid collisions with other duplicate filters. Allow the user to
+ * specify an upper bound on the size of the duplicate filter, so that we
+ * can handle huge mailing lists with millions of recipients.
+ */
+ if (msg_verbose)
+ msg_info("deliver_indirect: %s", state.msg_attr.rcpt.address);
+ if (been_here(state.dup_filter, "indirect %s",
+ state.msg_attr.rcpt.address))
+ return (0);
+
+ /*
+ * Don't forward a trace-only request.
+ */
+ if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+ dsb_simple(state.msg_attr.why, "2.0.0", "forwards to %s",
+ state.msg_attr.rcpt.address);
+ return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * Send the address to the forwarding service. Inherit the delivered
+ * attribute from the alias or from the .forward file owner.
+ */
+ if (forward_append(state.msg_attr)) {
+ dsb_simple(state.msg_attr.why, "4.3.0", "unable to forward message");
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ return (0);
+}
diff --git a/src/local/local.c b/src/local/local.c
new file mode 100644
index 0000000..32bdea7
--- /dev/null
+++ b/src/local/local.c
@@ -0,0 +1,988 @@
+/*++
+/* NAME
+/* local 8
+/* SUMMARY
+/* Postfix local mail delivery
+/* SYNOPSIS
+/* \fBlocal\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/* The \fBlocal\fR(8) daemon processes delivery requests from the
+/* Postfix queue manager to deliver mail to local recipients.
+/* Each delivery request specifies a queue file, a sender address,
+/* a domain or host to deliver to, and one or more recipients.
+/* This program expects to be run from the \fBmaster\fR(8) process
+/* manager.
+/*
+/* The \fBlocal\fR(8) daemon updates queue files and marks recipients
+/* as finished, or it informs the queue manager that delivery should
+/* be tried again at a later time. Delivery status reports are sent
+/* to the \fBbounce\fR(8), \fBdefer\fR(8) or \fBtrace\fR(8) daemon as
+/* appropriate.
+/* CASE FOLDING
+/* .ad
+/* .fi
+/* All delivery decisions are made using the bare recipient
+/* name (i.e. the address localpart), folded to lower case.
+/* See also under ADDRESS EXTENSION below for a few exceptions.
+/* SYSTEM-WIDE AND USER-LEVEL ALIASING
+/* .ad
+/* .fi
+/* The system administrator can set up one or more system-wide
+/* \fBsendmail\fR-style alias databases.
+/* Users can have \fBsendmail\fR-style ~/.\fBforward\fR files.
+/* Mail for \fIname\fR is delivered to the alias \fIname\fR, to
+/* destinations in ~\fIname\fR/.\fBforward\fR, to the mailbox owned
+/* by the user \fIname\fR, or it is sent back as undeliverable.
+/*
+/* The system administrator can specify a comma/space separated list
+/* of ~\fR/.\fBforward\fR like files through the \fBforward_path\fR
+/* configuration parameter. Upon delivery, the local delivery agent
+/* tries each pathname in the list until a file is found.
+/*
+/* Delivery via ~/.\fBforward\fR files is done with the privileges
+/* of the recipient.
+/* Thus, ~/.\fBforward\fR like files must be readable by the
+/* recipient, and their parent directory needs to have "execute"
+/* permission for the recipient.
+/*
+/* The \fBforward_path\fR parameter is subject to interpolation of
+/* \fB$user\fR (recipient username), \fB$home\fR (recipient home
+/* directory), \fB$shell\fR (recipient shell), \fB$recipient\fR
+/* (complete recipient address), \fB$extension\fR (recipient address
+/* extension), \fB$domain\fR (recipient domain), \fB$local\fR
+/* (entire recipient address localpart) and
+/* \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR
+/* and \fI${name?{value}}\fR (Postfix 3.0 and later) expand
+/* conditionally to \fIvalue\fR when \fI$name\fR is defined,
+/* and the forms \fI${name:value}\fR \fI${name:{value}}\fR
+/* (Postfix 3.0 and later) expand conditionally to \fIvalue\fR
+/* when \fI$name\fR is not defined. The form
+/* \fI${name?{value1}:{value2}}\fR (Postfix 3.0 and later)
+/* expands conditionally to \fIvalue1\fR when \fI$name\fR is
+/* defined, or \fIvalue2\fR otherwise. Characters that may
+/* have special meaning to the shell or file system are replaced
+/* with underscores. The list of acceptable characters is
+/* specified with the \fBforward_expansion_filter\fR configuration
+/* parameter.
+/*
+/* An alias or ~/.\fBforward\fR file may list any combination of external
+/* commands, destination file names, \fB:include:\fR directives, or
+/* mail addresses.
+/* See \fBaliases\fR(5) for a precise description. Each line in a
+/* user's .\fBforward\fR file has the same syntax as the right-hand part
+/* of an alias.
+/*
+/* When an address is found in its own alias expansion, delivery is
+/* made to the user instead. When a user is listed in the user's own
+/* ~/.\fBforward\fR file, delivery is made to the user's mailbox instead.
+/* An empty ~/.\fBforward\fR file means do not forward mail.
+/*
+/* In order to prevent the mail system from using up unreasonable
+/* amounts of memory, input records read from \fB:include:\fR or from
+/* ~/.\fBforward\fR files are broken up into chunks of length
+/* \fBline_length_limit\fR.
+/*
+/* While expanding aliases, ~/.\fBforward\fR files, and so on, the
+/* program attempts to avoid duplicate deliveries. The
+/* \fBduplicate_filter_limit\fR configuration parameter limits the
+/* number of remembered recipients.
+/* MAIL FORWARDING
+/* .ad
+/* .fi
+/* For the sake of reliability, forwarded mail is re-submitted as
+/* a new message, so that each recipient has a separate on-file
+/* delivery status record.
+/*
+/* In order to stop mail forwarding loops early, the software adds an
+/* optional
+/* \fBDelivered-To:\fR header with the final envelope recipient address. If
+/* mail arrives for a recipient that is already listed in a
+/* \fBDelivered-To:\fR header, the message is bounced.
+/* MAILBOX DELIVERY
+/* .ad
+/* .fi
+/* The default per-user mailbox is a file in the UNIX mail spool
+/* directory (\fB/var/mail/\fIuser\fR or \fB/var/spool/mail/\fIuser\fR);
+/* the location can be specified with the \fBmail_spool_directory\fR
+/* configuration parameter. Specify a name ending in \fB/\fR for
+/* \fBqmail\fR-compatible \fBmaildir\fR delivery.
+/*
+/* Alternatively, the per-user mailbox can be a file in the user's home
+/* directory with a name specified via the \fBhome_mailbox\fR
+/* configuration parameter. Specify a relative path name. Specify a name
+/* ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR delivery.
+/*
+/* Mailbox delivery can be delegated to an external command specified
+/* with the \fBmailbox_command_maps\fR and \fBmailbox_command\fR
+/* configuration parameters. The command
+/* executes with the privileges of the recipient user (exceptions:
+/* secondary groups are not enabled; in case of delivery as root,
+/* the command executes with the privileges of \fBdefault_privs\fR).
+/*
+/* Mailbox delivery can be delegated to alternative message transports
+/* specified in the \fBmaster.cf\fR file.
+/* The \fBmailbox_transport_maps\fR and \fBmailbox_transport\fR
+/* configuration parameters specify an optional
+/* message transport that is to be used for all local recipients,
+/* regardless of whether they are found in the UNIX passwd database.
+/* The \fBfallback_transport_maps\fR and
+/* \fBfallback_transport\fR parameters specify an optional
+/* message transport
+/* for recipients that are not found in the aliases(5) or UNIX
+/* passwd database.
+/*
+/* In the case of UNIX-style mailbox delivery,
+/* the \fBlocal\fR(8) daemon prepends a "\fBFrom \fIsender time_stamp\fR"
+/* envelope header to each message, prepends an
+/* \fBX-Original-To:\fR header with the recipient address as given to
+/* Postfix, prepends an
+/* optional \fBDelivered-To:\fR header
+/* with the final envelope recipient address, prepends a \fBReturn-Path:\fR
+/* header with the envelope sender address, prepends a \fB>\fR character
+/* to lines beginning with "\fBFrom \fR", and appends an empty line.
+/* The mailbox is locked for exclusive access while delivery is in
+/* progress. In case of problems, an attempt is made to truncate the
+/* mailbox to its original length.
+/*
+/* In the case of \fBmaildir\fR delivery, the local daemon prepends
+/* an optional
+/* \fBDelivered-To:\fR header with the final envelope recipient address,
+/* prepends an
+/* \fBX-Original-To:\fR header with the recipient address as given to
+/* Postfix,
+/* and prepends a \fBReturn-Path:\fR header with the envelope sender
+/* address.
+/* EXTERNAL COMMAND DELIVERY
+/* .ad
+/* .fi
+/* The \fBallow_mail_to_commands\fR configuration parameter restricts
+/* delivery to external commands. The default setting (\fBalias,
+/* forward\fR) forbids command destinations in \fB:include:\fR files.
+/*
+/* Optionally, the process working directory is changed to the path
+/* specified with \fBcommand_execution_directory\fR (Postfix 2.2 and
+/* later). Failure to change directory causes mail to be deferred.
+/*
+/* The \fBcommand_execution_directory\fR parameter value is subject
+/* to interpolation of \fB$user\fR (recipient username),
+/* \fB$home\fR (recipient home directory), \fB$shell\fR
+/* (recipient shell), \fB$recipient\fR (complete recipient
+/* address), \fB$extension\fR (recipient address extension),
+/* \fB$domain\fR (recipient domain), \fB$local\fR (entire
+/* recipient address localpart) and \fB$recipient_delimiter.\fR
+/* The forms \fI${name?value}\fR and \fI${name?{value}}\fR
+/* (Postfix 3.0 and later) expand conditionally to \fIvalue\fR
+/* when \fI$name\fR is defined, and the forms \fI${name:value}\fR
+/* and \fI${name:{value}}\fR (Postfix 3.0 and later) expand
+/* conditionally to \fIvalue\fR when \fI$name\fR is not defined.
+/* The form \fI${name?{value1}:{value2}}\fR (Postfix 3.0 and
+/* later) expands conditionally to \fIvalue1\fR when \fI$name\fR
+/* is defined, or \fIvalue2\fR otherwise. Characters that may
+/* have special meaning to the shell or file system are replaced
+/* with underscores. The list of acceptable characters
+/* is specified with the \fBexecution_directory_expansion_filter\fR
+/* configuration parameter.
+/*
+/* The command is executed directly where possible. Assistance by the
+/* shell (\fB/bin/sh\fR on UNIX systems) is used only when the command
+/* contains shell magic characters, or when the command invokes a shell
+/* built-in command.
+/*
+/* A limited amount of command output (standard output and standard
+/* error) is captured for inclusion with non-delivery status reports.
+/* A command is forcibly terminated if it does not complete within
+/* \fBcommand_time_limit\fR seconds. Command exit status codes are
+/* expected to follow the conventions defined in <\fBsysexits.h\fR>.
+/* Exit status 0 means normal successful completion.
+/*
+/* Postfix version 2.3 and later support RFC 3463-style enhanced
+/* status codes. If a command terminates with a non-zero exit
+/* status, and the command output begins with an enhanced
+/* status code, this status code takes precedence over the
+/* non-zero exit status.
+/*
+/* A limited amount of message context is exported via environment
+/* variables. Characters that may have special meaning to the shell
+/* are replaced with underscores. The list of acceptable characters
+/* is specified with the \fBcommand_expansion_filter\fR configuration
+/* parameter.
+/* .IP \fBSHELL\fR
+/* The recipient user's login shell.
+/* .IP \fBHOME\fR
+/* The recipient user's home directory.
+/* .IP \fBUSER\fR
+/* The bare recipient name.
+/* .IP \fBEXTENSION\fR
+/* The optional recipient address extension.
+/* .IP \fBDOMAIN\fR
+/* The recipient address domain part.
+/* .IP \fBLOGNAME\fR
+/* The bare recipient name.
+/* .IP \fBLOCAL\fR
+/* The entire recipient address localpart (text to the left of the
+/* rightmost @ character).
+/* .IP \fBORIGINAL_RECIPIENT\fR
+/* The entire recipient address, before any address rewriting
+/* or aliasing (Postfix 2.5 and later).
+/* .IP \fBRECIPIENT\fR
+/* The entire recipient address.
+/* .IP \fBSENDER\fR
+/* The entire sender address.
+/* .PP
+/* Additional remote client information is made available via
+/* the following environment variables:
+/* .IP \fBCLIENT_ADDRESS\fR
+/* Remote client network address. Available as of Postfix 2.2.
+/* .IP \fBCLIENT_HELO\fR
+/* Remote client EHLO command parameter. Available as of Postfix 2.2.
+/* .IP \fBCLIENT_HOSTNAME\fR
+/* Remote client hostname. Available as of Postfix 2.2.
+/* .IP \fBCLIENT_PROTOCOL\fR
+/* Remote client protocol. Available as of Postfix 2.2.
+/* .IP \fBSASL_METHOD\fR
+/* SASL authentication method specified in the
+/* remote client AUTH command. Available as of Postfix 2.2.
+/* .IP \fBSASL_SENDER\fR
+/* SASL sender address specified in the remote client MAIL
+/* FROM command. Available as of Postfix 2.2.
+/* .IP \fBSASL_USERNAME\fR
+/* SASL username specified in the remote client AUTH command.
+/* Available as of Postfix 2.2.
+/* .PP
+/* The \fBPATH\fR environment variable is always reset to a
+/* system-dependent default path, and environment variables
+/* whose names are blessed by the \fBexport_environment\fR
+/* configuration parameter are exported unchanged.
+/*
+/* The current working directory is the mail queue directory.
+/*
+/* The \fBlocal\fR(8) daemon prepends a "\fBFrom \fIsender time_stamp\fR"
+/* envelope header to each message, prepends an
+/* \fBX-Original-To:\fR header with the recipient address as given to
+/* Postfix, prepends an
+/* optional \fBDelivered-To:\fR
+/* header with the final recipient envelope address, prepends a
+/* \fBReturn-Path:\fR header with the sender envelope address,
+/* and appends no empty line.
+/* EXTERNAL FILE DELIVERY
+/* .ad
+/* .fi
+/* The delivery format depends on the destination filename syntax.
+/* The default is to use UNIX-style mailbox format. Specify a name
+/* ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR delivery.
+/*
+/* The \fBallow_mail_to_files\fR configuration parameter restricts
+/* delivery to external files. The default setting (\fBalias,
+/* forward\fR) forbids file destinations in \fB:include:\fR files.
+/*
+/* In the case of UNIX-style mailbox delivery,
+/* the \fBlocal\fR(8) daemon prepends a "\fBFrom \fIsender time_stamp\fR"
+/* envelope header to each message, prepends an
+/* \fBX-Original-To:\fR header with the recipient address as given to
+/* Postfix, prepends an
+/* optional \fBDelivered-To:\fR
+/* header with the final recipient envelope address, prepends a \fB>\fR
+/* character to lines beginning with "\fBFrom \fR", and appends an
+/* empty line.
+/* The envelope sender address is available in the \fBReturn-Path:\fR
+/* header.
+/* When the destination is a regular file, it is locked for exclusive
+/* access while delivery is in progress. In case of problems, an attempt
+/* is made to truncate a regular file to its original length.
+/*
+/* In the case of \fBmaildir\fR delivery, the local daemon prepends
+/* an optional
+/* \fBDelivered-To:\fR header with the final envelope recipient address,
+/* and prepends an
+/* \fBX-Original-To:\fR header with the recipient address as given to
+/* Postfix.
+/* The envelope sender address is available in the \fBReturn-Path:\fR
+/* header.
+/* ADDRESS EXTENSION
+/* .ad
+/* .fi
+/* The optional \fBrecipient_delimiter\fR configuration parameter
+/* specifies how to separate address extensions from local recipient
+/* names.
+/*
+/* For example, with "\fBrecipient_delimiter = +\fR", mail for
+/* \fIname\fR+\fIfoo\fR is delivered to the alias \fIname\fR+\fIfoo\fR
+/* or to the alias \fIname\fR, to the destinations listed in
+/* ~\fIname\fR/.\fBforward\fR+\fIfoo\fR or in ~\fIname\fR/.\fBforward\fR,
+/* to the mailbox owned by the user \fIname\fR, or it is sent back as
+/* undeliverable.
+/* DELIVERY RIGHTS
+/* .ad
+/* .fi
+/* Deliveries to external files and external commands are made with
+/* the rights of the receiving user on whose behalf the delivery is made.
+/* In the absence of a user context, the \fBlocal\fR(8) daemon uses the
+/* owner rights of the \fB:include:\fR file or alias database.
+/* When those files are owned by the superuser, delivery is made with
+/* the rights specified with the \fBdefault_privs\fR configuration
+/* parameter.
+/* STANDARDS
+/* RFC 822 (ARPA Internet Text Messages)
+/* RFC 3463 (Enhanced status codes)
+/* DIAGNOSTICS
+/* Problems and transactions are logged to \fBsyslogd\fR(8)
+/* or \fBpostlogd\fR(8).
+/* Corrupted message files are marked so that the queue
+/* manager can move them to the \fBcorrupt\fR queue afterwards.
+/*
+/* Depending on the setting of the \fBnotify_classes\fR parameter,
+/* the postmaster is notified of bounces and of other trouble.
+/* SECURITY
+/* .ad
+/* .fi
+/* The \fBlocal\fR(8) delivery agent needs a dual personality
+/* 1) to access the private Postfix queue and IPC mechanisms,
+/* 2) to impersonate the recipient and deliver to recipient-specified
+/* files or commands. It is therefore security sensitive.
+/*
+/* The \fBlocal\fR(8) delivery agent disallows regular expression
+/* substitution of $1 etc. in \fBalias_maps\fR, because that
+/* would open a security hole.
+/*
+/* The \fBlocal\fR(8) delivery agent will silently ignore
+/* requests to use the \fBproxymap\fR(8) server within
+/* \fBalias_maps\fR. Instead it will open the table directly.
+/* Before Postfix version 2.2, the \fBlocal\fR(8) delivery
+/* agent will terminate with a fatal error.
+/* BUGS
+/* For security reasons, the message delivery status of external commands
+/* or of external files is never checkpointed to file. As a result,
+/* the program may occasionally deliver more than once to a command or
+/* external file. Better safe than sorry.
+/*
+/* Mutually-recursive aliases or ~/.\fBforward\fR files are not detected
+/* early. The resulting mail forwarding loop is broken by the use of the
+/* \fBDelivered-To:\fR message header.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* Changes to \fBmain.cf\fR are picked up automatically, as \fBlocal\fR(8)
+/* processes run for only a limited amount of time. Use the command
+/* "\fBpostfix reload\fR" to speed up a change.
+/*
+/* The text below provides only a parameter summary. See
+/* \fBpostconf\fR(5) for more details including examples.
+/* COMPATIBILITY CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBbiff (yes)\fR"
+/* Whether or not to use the local biff service.
+/* .IP "\fBexpand_owner_alias (no)\fR"
+/* When delivering to an alias "\fIaliasname\fR" that has an
+/* "owner-\fIaliasname\fR" companion alias, set the envelope sender
+/* address to the expansion of the "owner-\fIaliasname\fR" alias.
+/* .IP "\fBowner_request_special (yes)\fR"
+/* Enable special treatment for owner-\fIlistname\fR entries in the
+/* \fBaliases\fR(5) file, and don't split owner-\fIlistname\fR and
+/* \fIlistname\fR-request address localparts when the recipient_delimiter
+/* is set to "-".
+/* .IP "\fBsun_mailtool_compatibility (no)\fR"
+/* Obsolete SUN mailtool compatibility feature.
+/* .PP
+/* Available in Postfix version 2.3 and later:
+/* .IP "\fBfrozen_delivered_to (yes)\fR"
+/* Update the \fBlocal\fR(8) delivery agent's idea of the Delivered-To:
+/* address (see prepend_delivered_header) only once, at the start of
+/* a delivery attempt; do not update the Delivered-To: address while
+/* expanding aliases or .forward files.
+/* .PP
+/* Available in Postfix version 2.5.3 and later:
+/* .IP "\fBstrict_mailbox_ownership (yes)\fR"
+/* Defer delivery when a mailbox file is not owned by its recipient.
+/* .IP "\fBreset_owner_alias (no)\fR"
+/* Reset the \fBlocal\fR(8) delivery agent's idea of the owner-alias
+/* attribute, when delivering mail to a child alias that does not have
+/* its own owner alias.
+/* .PP
+/* Available in Postfix version 3.0 and later:
+/* .IP "\fBlocal_delivery_status_filter ($default_delivery_status_filter)\fR"
+/* Optional filter for the \fBlocal\fR(8) delivery agent to change the
+/* status code or explanatory text of successful or unsuccessful
+/* deliveries.
+/* DELIVERY METHOD CONTROLS
+/* .ad
+/* .fi
+/* The precedence of \fBlocal\fR(8) delivery methods from high to low is:
+/* aliases, .forward files, mailbox_transport_maps,
+/* mailbox_transport, mailbox_command_maps, mailbox_command,
+/* home_mailbox, mail_spool_directory, fallback_transport_maps,
+/* fallback_transport, and luser_relay.
+/* .IP "\fBalias_maps (see 'postconf -d' output)\fR"
+/* The alias databases that are used for \fBlocal\fR(8) delivery.
+/* .IP "\fBforward_path (see 'postconf -d' output)\fR"
+/* The \fBlocal\fR(8) delivery agent search list for finding a .forward
+/* file with user-specified delivery methods.
+/* .IP "\fBmailbox_transport_maps (empty)\fR"
+/* Optional lookup tables with per-recipient message delivery
+/* transports to use for \fBlocal\fR(8) mailbox delivery, whether or not the
+/* recipients are found in the UNIX passwd database.
+/* .IP "\fBmailbox_transport (empty)\fR"
+/* Optional message delivery transport that the \fBlocal\fR(8) delivery
+/* agent should use for mailbox delivery to all local recipients,
+/* whether or not they are found in the UNIX passwd database.
+/* .IP "\fBmailbox_command_maps (empty)\fR"
+/* Optional lookup tables with per-recipient external commands to use
+/* for \fBlocal\fR(8) mailbox delivery.
+/* .IP "\fBmailbox_command (empty)\fR"
+/* Optional external command that the \fBlocal\fR(8) delivery agent should
+/* use for mailbox delivery.
+/* .IP "\fBhome_mailbox (empty)\fR"
+/* Optional pathname of a mailbox file relative to a \fBlocal\fR(8) user's
+/* home directory.
+/* .IP "\fBmail_spool_directory (see 'postconf -d' output)\fR"
+/* The directory where \fBlocal\fR(8) UNIX-style mailboxes are kept.
+/* .IP "\fBfallback_transport_maps (empty)\fR"
+/* Optional lookup tables with per-recipient message delivery
+/* transports for recipients that the \fBlocal\fR(8) delivery agent could
+/* not find in the \fBaliases\fR(5) or UNIX password database.
+/* .IP "\fBfallback_transport (empty)\fR"
+/* Optional message delivery transport that the \fBlocal\fR(8) delivery
+/* agent should use for names that are not found in the \fBaliases\fR(5)
+/* or UNIX password database.
+/* .IP "\fBluser_relay (empty)\fR"
+/* Optional catch-all destination for unknown \fBlocal\fR(8) recipients.
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBcommand_execution_directory (empty)\fR"
+/* The \fBlocal\fR(8) delivery agent working directory for delivery to
+/* external commands.
+/* MAILBOX LOCKING CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBdeliver_lock_attempts (20)\fR"
+/* The maximal number of attempts to acquire an exclusive lock on a
+/* mailbox file or \fBbounce\fR(8) logfile.
+/* .IP "\fBdeliver_lock_delay (1s)\fR"
+/* The time between attempts to acquire an exclusive lock on a mailbox
+/* file or \fBbounce\fR(8) logfile.
+/* .IP "\fBstale_lock_time (500s)\fR"
+/* The time after which a stale exclusive mailbox lockfile is removed.
+/* .IP "\fBmailbox_delivery_lock (see 'postconf -d' output)\fR"
+/* How to lock a UNIX-style \fBlocal\fR(8) mailbox before attempting delivery.
+/* RESOURCE AND RATE CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBcommand_time_limit (1000s)\fR"
+/* Time limit for delivery to external commands.
+/* .IP "\fBduplicate_filter_limit (1000)\fR"
+/* The maximal number of addresses remembered by the address
+/* duplicate filter for \fBaliases\fR(5) or \fBvirtual\fR(5) alias expansion, or
+/* for \fBshowq\fR(8) queue displays.
+/* .IP "\fBmailbox_size_limit (51200000)\fR"
+/* The maximal size of any \fBlocal\fR(8) individual mailbox or maildir
+/* file, or zero (no limit).
+/* .PP
+/* Implemented in the qmgr(8) daemon:
+/* .IP "\fBlocal_destination_concurrency_limit (2)\fR"
+/* The maximal number of parallel deliveries via the local mail
+/* delivery transport to the same recipient (when
+/* "local_destination_recipient_limit = 1") or the maximal number of
+/* parallel deliveries to the same local domain (when
+/* "local_destination_recipient_limit > 1").
+/* .IP "\fBlocal_destination_recipient_limit (1)\fR"
+/* The maximal number of recipients per message delivery via the
+/* local mail delivery transport.
+/* SECURITY CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBallow_mail_to_commands (alias, forward)\fR"
+/* Restrict \fBlocal\fR(8) mail delivery to external commands.
+/* .IP "\fBallow_mail_to_files (alias, forward)\fR"
+/* Restrict \fBlocal\fR(8) mail delivery to external files.
+/* .IP "\fBcommand_expansion_filter (see 'postconf -d' output)\fR"
+/* Restrict the characters that the \fBlocal\fR(8) delivery agent allows in
+/* $name expansions of $mailbox_command and $command_execution_directory.
+/* .IP "\fBdefault_privs (nobody)\fR"
+/* The default rights used by the \fBlocal\fR(8) delivery agent for delivery
+/* to an external file or command.
+/* .IP "\fBforward_expansion_filter (see 'postconf -d' output)\fR"
+/* Restrict the characters that the \fBlocal\fR(8) delivery agent allows in
+/* $name expansions of $forward_path.
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBexecution_directory_expansion_filter (see 'postconf -d' output)\fR"
+/* Restrict the characters that the \fBlocal\fR(8) delivery agent allows
+/* in $name expansions of $command_execution_directory.
+/* .PP
+/* Available in Postfix version 2.5.3 and later:
+/* .IP "\fBstrict_mailbox_ownership (yes)\fR"
+/* Defer delivery when a mailbox file is not owned by its recipient.
+/* MISCELLANEOUS CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
+/* The default location of the Postfix main.cf and master.cf
+/* configuration files.
+/* .IP "\fBdaemon_timeout (18000s)\fR"
+/* How much time a Postfix daemon process may take to handle a
+/* request before it is terminated by a built-in watchdog timer.
+/* .IP "\fBdelay_logging_resolution_limit (2)\fR"
+/* The maximal number of digits after the decimal point when logging
+/* sub-second delay values.
+/* .IP "\fBexport_environment (see 'postconf -d' output)\fR"
+/* The list of environment variables that a Postfix process will export
+/* to non-Postfix processes.
+/* .IP "\fBipc_timeout (3600s)\fR"
+/* The time limit for sending or receiving information over an internal
+/* communication channel.
+/* .IP "\fBlocal_command_shell (empty)\fR"
+/* Optional shell program for \fBlocal\fR(8) delivery to non-Postfix commands.
+/* .IP "\fBmax_idle (100s)\fR"
+/* The maximum amount of time that an idle Postfix daemon process waits
+/* for an incoming connection before terminating voluntarily.
+/* .IP "\fBmax_use (100)\fR"
+/* The maximal number of incoming connections that a Postfix daemon
+/* process will service before terminating voluntarily.
+/* .IP "\fBprepend_delivered_header (command, file, forward)\fR"
+/* The message delivery contexts where the Postfix \fBlocal\fR(8) delivery
+/* agent prepends a Delivered-To: message header with the address
+/* that the mail was delivered to.
+/* .IP "\fBprocess_id (read-only)\fR"
+/* The process ID of a Postfix command or daemon process.
+/* .IP "\fBprocess_name (read-only)\fR"
+/* The process name of a Postfix command or daemon process.
+/* .IP "\fBpropagate_unmatched_extensions (canonical, virtual)\fR"
+/* What address lookup tables copy an address extension from the lookup
+/* key to the lookup result.
+/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
+/* The location of the Postfix top-level queue directory.
+/* .IP "\fBrecipient_delimiter (empty)\fR"
+/* The set of characters that can separate an email address
+/* localpart, user name, or a .forward file name from its extension.
+/* .IP "\fBrequire_home_directory (no)\fR"
+/* Require that a \fBlocal\fR(8) recipient's home directory exists
+/* before mail delivery is attempted.
+/* .IP "\fBsyslog_facility (mail)\fR"
+/* The syslog facility of Postfix logging.
+/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
+/* A prefix that is prepended to the process name in syslog
+/* records, so that, for example, "smtpd" becomes "prefix/smtpd".
+/* .PP
+/* Available in Postfix version 3.3 and later:
+/* .IP "\fBenable_original_recipient (yes)\fR"
+/* Enable support for the original recipient address after an
+/* address is rewritten to a different address (for example with
+/* aliasing or with canonical mapping).
+/* .IP "\fBservice_name (read-only)\fR"
+/* The master.cf service name of a Postfix daemon process.
+/* .PP
+/* Available in Postfix 3.5 and later:
+/* .IP "\fBinfo_log_address_format (external)\fR"
+/* The email address form that will be used in non-debug logging
+/* (info, warning, etc.).
+/* FILES
+/* The following are examples; details differ between systems.
+/* $HOME/.forward, per-user aliasing
+/* /etc/aliases, system-wide alias database
+/* /var/spool/mail, system mailboxes
+/* SEE ALSO
+/* qmgr(8), queue manager
+/* bounce(8), delivery status reports
+/* newaliases(1), create/update alias database
+/* postalias(1), create/update alias database
+/* aliases(5), format of alias database
+/* postconf(5), configuration parameters
+/* master(5), generic daemon options
+/* postlogd(8), Postfix logging
+/* syslogd(8), system logging
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* HISTORY
+/* .ad
+/* .fi
+/* The \fBDelivered-To:\fR message header appears in the \fBqmail\fR
+/* system by Daniel Bernstein.
+/*
+/* The \fImaildir\fR structure appears in the \fBqmail\fR system
+/* by Daniel Bernstein.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <iostuff.h>
+#include <name_mask.h>
+#include <set_eugid.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <mail_conf.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <mail_version.h>
+#include <ext_prop.h>
+#include <maps.h>
+#include <flush_clnt.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+ /*
+ * Tunable parameters.
+ */
+char *var_allow_commands;
+char *var_allow_files;
+char *var_alias_maps;
+int var_dup_filter_limit;
+int var_command_maxtime; /* You can now leave this here. */
+char *var_home_mailbox;
+char *var_mailbox_command;
+char *var_mailbox_cmd_maps;
+char *var_rcpt_fdelim;
+char *var_local_cmd_shell;
+char *var_luser_relay;
+int var_biff;
+char *var_mail_spool_dir;
+char *var_mailbox_transport;
+char *var_mbox_transp_maps;
+char *var_fallback_transport;
+char *var_fbck_transp_maps;
+char *var_exec_directory;
+char *var_exec_exp_filter;
+char *var_forward_path;
+char *var_cmd_exp_filter;
+char *var_fwd_exp_filter;
+char *var_prop_extension;
+int var_exp_own_alias;
+char *var_deliver_hdr;
+int var_stat_home_dir;
+int var_mailtool_compat;
+char *var_mailbox_lock;
+long var_mailbox_limit;
+bool var_frozen_delivered;
+bool var_reset_owner_attr;
+bool var_strict_mbox_owner;
+
+int local_cmd_deliver_mask;
+int local_file_deliver_mask;
+int local_ext_prop_mask;
+int local_deliver_hdr_mask;
+int local_mbox_lock_mask;
+MAPS *alias_maps;
+char *var_local_dsn_filter;
+
+/* local_deliver - deliver message with extreme prejudice */
+
+static int local_deliver(DELIVER_REQUEST *rqst, char *service)
+{
+ const char *myname = "local_deliver";
+ RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len;
+ RECIPIENT *rcpt;
+ int rcpt_stat;
+ int msg_stat;
+ LOCAL_STATE state;
+ USER_ATTR usr_attr;
+
+ if (msg_verbose)
+ msg_info("local_deliver: %s from %s", rqst->queue_id, rqst->sender);
+
+ /*
+ * Initialize the delivery attributes that are not recipient specific.
+ * While messages are being delivered and while aliases or forward files
+ * are being expanded, this attribute list is being changed constantly.
+ * For this reason, the list is passed on by value (except when it is
+ * being initialized :-), so that there is no need to undo attribute
+ * changes made by lower-level routines. The alias/include/forward
+ * expansion attribute list is part of a tree with self and parent
+ * references (see the EXPAND_ATTR definitions). The user-specific
+ * attributes are security sensitive, and are therefore kept separate.
+ * All this results in a noticeable level of clumsiness, but passing
+ * things around by value gives good protection against accidental change
+ * by subroutines.
+ */
+ state.level = 0;
+ deliver_attr_init(&state.msg_attr);
+ state.msg_attr.queue_name = rqst->queue_name;
+ state.msg_attr.queue_id = rqst->queue_id;
+ state.msg_attr.fp = rqst->fp;
+ state.msg_attr.offset = rqst->data_offset;
+ state.msg_attr.encoding = rqst->encoding;
+ state.msg_attr.smtputf8 = rqst->smtputf8;
+ state.msg_attr.sender = rqst->sender;
+ state.msg_attr.dsn_envid = rqst->dsn_envid;
+ state.msg_attr.dsn_ret = rqst->dsn_ret;
+ state.msg_attr.relay = service;
+ state.msg_attr.msg_stats = rqst->msg_stats;
+ state.msg_attr.request = rqst;
+ RESET_OWNER_ATTR(state.msg_attr, state.level);
+ RESET_USER_ATTR(usr_attr, state.level);
+ state.loop_info = delivered_hdr_init(rqst->fp, rqst->data_offset,
+ FOLD_ADDR_ALL);
+ state.request = rqst;
+
+ /*
+ * Iterate over each recipient named in the delivery request. When the
+ * mail delivery status for a given recipient is definite (i.e. bounced
+ * or delivered), update the message queue file and cross off the
+ * recipient. Update the per-message delivery status.
+ */
+ for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
+ state.dup_filter = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
+ forward_init();
+ state.msg_attr.rcpt = *rcpt;
+ rcpt_stat = deliver_recipient(state, usr_attr);
+ rcpt_stat |= forward_finish(rqst, state.msg_attr, rcpt_stat);
+ if (rcpt_stat == 0 && (rqst->flags & DEL_REQ_FLAG_SUCCESS))
+ deliver_completed(state.msg_attr.fp, rcpt->offset);
+ been_here_free(state.dup_filter);
+ msg_stat |= rcpt_stat;
+ }
+
+ /*
+ * Clean up.
+ */
+ delivered_hdr_free(state.loop_info);
+ deliver_attr_free(&state.msg_attr);
+
+ return (msg_stat);
+}
+
+/* local_service - perform service for client */
+
+static void local_service(VSTREAM *stream, char *service, char **argv)
+{
+ DELIVER_REQUEST *request;
+ int status;
+
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+ /*
+ * This routine runs whenever a client connects to the UNIX-domain socket
+ * that is dedicated to local mail delivery service. What we see below is
+ * a little protocol to (1) tell the client that we are ready, (2) read a
+ * delivery request from the client, and (3) report the completion status
+ * of that request.
+ */
+ if ((request = deliver_request_read(stream)) != 0) {
+ status = local_deliver(request, service);
+ deliver_request_done(stream, request, status);
+ }
+}
+
+/* local_mask_init - initialize delivery restrictions */
+
+static void local_mask_init(void)
+{
+ static const NAME_MASK file_mask[] = {
+ "alias", EXPAND_TYPE_ALIAS,
+ "forward", EXPAND_TYPE_FWD,
+ "include", EXPAND_TYPE_INCL,
+ 0,
+ };
+ static const NAME_MASK command_mask[] = {
+ "alias", EXPAND_TYPE_ALIAS,
+ "forward", EXPAND_TYPE_FWD,
+ "include", EXPAND_TYPE_INCL,
+ 0,
+ };
+ static const NAME_MASK deliver_mask[] = {
+ "command", DELIVER_HDR_CMD,
+ "file", DELIVER_HDR_FILE,
+ "forward", DELIVER_HDR_FWD,
+ 0,
+ };
+
+ local_file_deliver_mask = name_mask(VAR_ALLOW_FILES, file_mask,
+ var_allow_files);
+ local_cmd_deliver_mask = name_mask(VAR_ALLOW_COMMANDS, command_mask,
+ var_allow_commands);
+ local_ext_prop_mask =
+ ext_prop_mask(VAR_PROP_EXTENSION, var_prop_extension);
+ local_deliver_hdr_mask = name_mask(VAR_DELIVER_HDR, deliver_mask,
+ var_deliver_hdr);
+ local_mbox_lock_mask = mbox_lock_mask(var_mailbox_lock);
+ if (var_mailtool_compat) {
+ msg_warn("%s: deprecated parameter, use \"%s = dotlock\" instead",
+ VAR_MAILTOOL_COMPAT, VAR_MAILBOX_LOCK);
+ local_mbox_lock_mask &= MBOX_DOT_LOCK;
+ }
+ if (local_mbox_lock_mask == 0)
+ msg_fatal("parameter %s specifies no applicable mailbox locking method",
+ VAR_MAILBOX_LOCK);
+}
+
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(char *unused_name, char **unused_argv)
+{
+ const char *table;
+
+ if ((table = dict_changed_name()) != 0) {
+ msg_info("table %s has changed -- restarting", table);
+ exit(0);
+ }
+}
+
+/* post_init - post-jail initialization */
+
+static void post_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * Drop privileges most of the time, and set up delivery restrictions.
+ */
+ set_eugid(var_owner_uid, var_owner_gid);
+ local_mask_init();
+}
+
+/* pre_init - pre-jail initialization */
+
+static void pre_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * Reset the file size limit from the message size limit to the mailbox
+ * size limit. XXX This still isn't accurate because the file size limit
+ * also affects delivery to command.
+ *
+ * A file size limit protects the machine against runaway software errors.
+ * It is not suitable to enforce mail quota, because users can get around
+ * mail quota by delivering to /file/name or to |command.
+ *
+ * We can't have mailbox size limit smaller than the message size limit,
+ * because that prohibits the delivery agent from updating the queue
+ * file.
+ */
+ if (ENFORCING_SIZE_LIMIT(var_mailbox_limit)) {
+ if (!ENFORCING_SIZE_LIMIT(var_message_limit))
+ msg_fatal("configuration error: %s is limited but %s is "
+ "unlimited", VAR_MAILBOX_LIMIT, VAR_MESSAGE_LIMIT);
+ if (var_mailbox_limit < var_message_limit)
+ msg_fatal("configuration error: %s is smaller than %s",
+ VAR_MAILBOX_LIMIT, VAR_MESSAGE_LIMIT);
+ set_file_limit(var_mailbox_limit);
+ }
+ alias_maps = maps_create("aliases", var_alias_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_PARANOID
+ | DICT_FLAG_FOLD_FIX
+ | DICT_FLAG_UTF8_REQUEST);
+
+ flush_init();
+}
+
+MAIL_VERSION_STAMP_DECLARE;
+
+/* main - pass control to the single-threaded skeleton */
+
+int main(int argc, char **argv)
+{
+ static const CONFIG_TIME_TABLE time_table[] = {
+ VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
+ 0,
+ };
+ static const CONFIG_INT_TABLE int_table[] = {
+ VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
+ 0,
+ };
+ static const CONFIG_LONG_TABLE long_table[] = {
+ VAR_MAILBOX_LIMIT, DEF_MAILBOX_LIMIT, &var_mailbox_limit, 0, 0,
+ 0,
+ };
+ static const CONFIG_STR_TABLE str_table[] = {
+ VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
+ VAR_HOME_MAILBOX, DEF_HOME_MAILBOX, &var_home_mailbox, 0, 0,
+ VAR_ALLOW_COMMANDS, DEF_ALLOW_COMMANDS, &var_allow_commands, 0, 0,
+ VAR_ALLOW_FILES, DEF_ALLOW_FILES, &var_allow_files, 0, 0,
+ VAR_LOCAL_CMD_SHELL, DEF_LOCAL_CMD_SHELL, &var_local_cmd_shell, 0, 0,
+ VAR_MAIL_SPOOL_DIR, DEF_MAIL_SPOOL_DIR, &var_mail_spool_dir, 0, 0,
+ VAR_MAILBOX_TRANSP, DEF_MAILBOX_TRANSP, &var_mailbox_transport, 0, 0,
+ VAR_MBOX_TRANSP_MAPS, DEF_MBOX_TRANSP_MAPS, &var_mbox_transp_maps, 0, 0,
+ VAR_FALLBACK_TRANSP, DEF_FALLBACK_TRANSP, &var_fallback_transport, 0, 0,
+ VAR_FBCK_TRANSP_MAPS, DEF_FBCK_TRANSP_MAPS, &var_fbck_transp_maps, 0, 0,
+ VAR_CMD_EXP_FILTER, DEF_CMD_EXP_FILTER, &var_cmd_exp_filter, 1, 0,
+ VAR_FWD_EXP_FILTER, DEF_FWD_EXP_FILTER, &var_fwd_exp_filter, 1, 0,
+ VAR_EXEC_EXP_FILTER, DEF_EXEC_EXP_FILTER, &var_exec_exp_filter, 1, 0,
+ VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
+ VAR_DELIVER_HDR, DEF_DELIVER_HDR, &var_deliver_hdr, 0, 0,
+ VAR_MAILBOX_LOCK, DEF_MAILBOX_LOCK, &var_mailbox_lock, 1, 0,
+ VAR_MAILBOX_CMD_MAPS, DEF_MAILBOX_CMD_MAPS, &var_mailbox_cmd_maps, 0, 0,
+ VAR_LOCAL_DSN_FILTER, DEF_LOCAL_DSN_FILTER, &var_local_dsn_filter, 0, 0,
+ 0,
+ };
+ static const CONFIG_BOOL_TABLE bool_table[] = {
+ VAR_BIFF, DEF_BIFF, &var_biff,
+ VAR_EXP_OWN_ALIAS, DEF_EXP_OWN_ALIAS, &var_exp_own_alias,
+ VAR_STAT_HOME_DIR, DEF_STAT_HOME_DIR, &var_stat_home_dir,
+ VAR_MAILTOOL_COMPAT, DEF_MAILTOOL_COMPAT, &var_mailtool_compat,
+ VAR_FROZEN_DELIVERED, DEF_FROZEN_DELIVERED, &var_frozen_delivered,
+ VAR_RESET_OWNER_ATTR, DEF_RESET_OWNER_ATTR, &var_reset_owner_attr,
+ VAR_STRICT_MBOX_OWNER, DEF_STRICT_MBOX_OWNER, &var_strict_mbox_owner,
+ 0,
+ };
+
+ /* Suppress $name expansion upon loading. */
+ static const CONFIG_RAW_TABLE raw_table[] = {
+ VAR_EXEC_DIRECTORY, DEF_EXEC_DIRECTORY, &var_exec_directory, 0, 0,
+ VAR_FORWARD_PATH, DEF_FORWARD_PATH, &var_forward_path, 0, 0,
+ VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0,
+ VAR_LUSER_RELAY, DEF_LUSER_RELAY, &var_luser_relay, 0, 0,
+ 0,
+ };
+
+ /*
+ * Fingerprint executables and core dumps.
+ */
+ MAIL_VERSION_STAMP_ALLOCATE;
+
+ single_server_main(argc, argv, local_service,
+ CA_MAIL_SERVER_INT_TABLE(int_table),
+ CA_MAIL_SERVER_LONG_TABLE(long_table),
+ CA_MAIL_SERVER_STR_TABLE(str_table),
+ CA_MAIL_SERVER_RAW_TABLE(raw_table),
+ CA_MAIL_SERVER_BOOL_TABLE(bool_table),
+ CA_MAIL_SERVER_TIME_TABLE(time_table),
+ CA_MAIL_SERVER_PRE_INIT(pre_init),
+ CA_MAIL_SERVER_POST_INIT(post_init),
+ CA_MAIL_SERVER_PRE_ACCEPT(pre_accept),
+ CA_MAIL_SERVER_PRIVILEGED,
+ CA_MAIL_SERVER_BOUNCE_INIT(VAR_LOCAL_DSN_FILTER,
+ &var_local_dsn_filter),
+ 0);
+}
diff --git a/src/local/local.h b/src/local/local.h
new file mode 100644
index 0000000..4052000
--- /dev/null
+++ b/src/local/local.h
@@ -0,0 +1,251 @@
+/*++
+/* NAME
+/* local 3h
+/* SUMMARY
+/* local mail delivery
+/* SYNOPSIS
+/* #include "local.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <htable.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <been_here.h>
+#include <tok822.h>
+#include <deliver_request.h>
+#include <mbox_conf.h>
+#include <maps.h>
+#include <dsn_buf.h>
+#include <dsn.h>
+#include <delivered_hdr.h>
+
+ /*
+ * User attributes: these control the privileges for delivery to external
+ * commands, external files, or mailboxes, and the initial environment of
+ * external commands.
+ */
+typedef struct USER_ATTR {
+ uid_t uid; /* file/command access */
+ gid_t gid; /* file/command access */
+ char *home; /* null or home directory */
+ char *logname; /* null or login name */
+ char *shell; /* null or login shell */
+} USER_ATTR;
+
+ /*
+ * Critical macros. Not for obscurity, but to ensure consistency.
+ */
+#define RESET_USER_ATTR(usr_attr, level) { \
+ usr_attr.uid = 0; usr_attr.gid = 0; usr_attr.home = 0; \
+ usr_attr.logname = 0; usr_attr.shell = 0; \
+ if (msg_verbose) \
+ msg_info("%s[%d]: reset user_attr", myname, level); \
+ }
+
+#define SET_USER_ATTR(usr_attr, pwd, level) { \
+ usr_attr.uid = pwd->pw_uid; usr_attr.gid = pwd->pw_gid; \
+ usr_attr.home = pwd->pw_dir; usr_attr.logname = pwd->pw_name; \
+ usr_attr.shell = pwd->pw_shell; \
+ if (msg_verbose) \
+ msg_info("%s[%d]: set user_attr: %s", \
+ myname, level, pwd->pw_name); \
+ }
+
+ /*
+ * The delivery attributes are inherited from files, from aliases, and from
+ * whatnot. Some of the information is changed on the fly. DELIVER_ATTR
+ * structures are therefore passed by value, so there is no need to undo
+ * changes.
+ */
+typedef struct DELIVER_ATTR {
+ int level; /* recursion level */
+ VSTREAM *fp; /* open queue file */
+ char *queue_name; /* mail queue id */
+ char *queue_id; /* mail queue id */
+ long offset; /* data offset */
+ char *encoding; /* MIME encoding */
+ int smtputf8; /* from delivery request */
+ const char *sender; /* taken from envelope */
+ char *dsn_envid; /* DSN envelope ID */
+ int dsn_ret; /* DSN headers/full */
+ RECIPIENT rcpt; /* from delivery request */
+ char *domain; /* recipient domain */
+ char *local; /* recipient full localpart */
+ char *user; /* recipient localpart, base name */
+ char *extension; /* recipient localpart, extension */
+ char *unmatched; /* unmatched extension */
+ const char *owner; /* null or list owner */
+ const char *delivered; /* for loop detection */
+ char *relay; /* relay host */
+ MSG_STATS msg_stats; /* time profile */
+ int exp_type; /* expansion type. see below */
+ char *exp_from; /* expanded_from */
+ DELIVER_REQUEST *request; /* the kitchen sink */
+ DSN_BUF *why; /* delivery status */
+} DELIVER_ATTR;
+
+extern void deliver_attr_init(DELIVER_ATTR *);
+extern void deliver_attr_dump(DELIVER_ATTR *);
+extern void deliver_attr_free(DELIVER_ATTR *);
+
+#define EXPAND_TYPE_ALIAS (1<<0)
+#define EXPAND_TYPE_FWD (1<<1)
+#define EXPAND_TYPE_INCL (1<<2)
+
+ /*
+ * Rather than schlepping around dozens of arguments, here is one that has
+ * all. Well, almost. The user attributes are just a bit too sensitive, so
+ * they are passed around separately.
+ */
+typedef struct LOCAL_STATE {
+ int level; /* nesting level, for logging */
+ DELIVER_ATTR msg_attr; /* message attributes */
+ BH_TABLE *dup_filter; /* internal duplicate filter */
+ DELIVERED_HDR_INFO *loop_info; /* external loop filter */
+ DELIVER_REQUEST *request; /* as from queue manager */
+} LOCAL_STATE;
+
+#define RESET_OWNER_ATTR(msg_attr, level) { \
+ msg_attr.owner = 0; \
+ if (msg_verbose) \
+ msg_info("%s[%d]: reset owner attr", myname, level); \
+ }
+
+#define SET_OWNER_ATTR(msg_attr, who, level) { \
+ msg_attr.sender = msg_attr.owner = who; \
+ if (msg_verbose) \
+ msg_info("%s[%d]: set owner attr: %s", \
+ myname, level, who); \
+ }
+
+ /*
+ * Bundle up some often-user attributes.
+ */
+#define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS((request)->flags)
+
+#define BOUNCE_ATTR(attr) \
+ attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
+ DSN_FROM_DSN_BUF(attr.why)
+#define BOUNCE_ONE_ATTR(attr) \
+ attr.queue_name, attr.queue_id, attr.encoding, attr.smtputf8, \
+ attr.sender, attr.dsn_envid, attr.dsn_ret, \
+ &attr.msg_stats, &attr.rcpt, attr.relay, \
+ DSN_FROM_DSN_BUF(attr.why)
+#define SENT_ATTR(attr) \
+ attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
+ DSN_FROM_DSN_BUF(attr.why)
+#define OPENED_ATTR(attr) \
+ attr.queue_id, attr.sender
+#define COPY_ATTR(attr) \
+ attr.sender, attr.rcpt.orig_addr, attr.delivered, attr.fp
+
+#define MSG_LOG_STATE(m, p) \
+ msg_info("%s[%d]: local %s recip %s exten %s deliver %s exp_from %s", \
+ m, \
+ p.level, \
+ p.msg_attr.local ? p.msg_attr.local : "" , \
+ p.msg_attr.rcpt.address ? p.msg_attr.rcpt.address : "", \
+ p.msg_attr.extension ? p.msg_attr.extension : "", \
+ p.msg_attr.delivered ? p.msg_attr.delivered : "", \
+ p.msg_attr.exp_from ? p.msg_attr.exp_from : "")
+
+ /*
+ * "inner" nodes of the delivery graph.
+ */
+extern int deliver_recipient(LOCAL_STATE, USER_ATTR);
+extern int deliver_alias(LOCAL_STATE, USER_ATTR, char *, int *);
+extern int deliver_dotforward(LOCAL_STATE, USER_ATTR, int *);
+extern int deliver_include(LOCAL_STATE, USER_ATTR, char *);
+extern int deliver_token(LOCAL_STATE, USER_ATTR, TOK822 *);
+extern int deliver_token_string(LOCAL_STATE, USER_ATTR, char *, int *);
+extern int deliver_token_stream(LOCAL_STATE, USER_ATTR, VSTREAM *, int *);
+extern int deliver_resolve_tree(LOCAL_STATE, USER_ATTR, TOK822 *);
+extern int deliver_resolve_addr(LOCAL_STATE, USER_ATTR, char *);
+
+ /*
+ * "leaf" nodes of the delivery graph.
+ */
+extern int deliver_mailbox(LOCAL_STATE, USER_ATTR, int *);
+extern int deliver_command(LOCAL_STATE, USER_ATTR, const char *);
+extern int deliver_file(LOCAL_STATE, USER_ATTR, char *);
+extern int deliver_indirect(LOCAL_STATE);
+extern int deliver_maildir(LOCAL_STATE, USER_ATTR, char *);
+extern int deliver_unknown(LOCAL_STATE, USER_ATTR);
+
+ /*
+ * Restrictions on delivery to sensitive destinations.
+ */
+extern int local_file_deliver_mask;
+extern int local_cmd_deliver_mask;
+
+ /*
+ * Restrictions on extension propagation.
+ */
+extern int local_ext_prop_mask;
+
+ /*
+ * Mailbox lock protocol.
+ */
+extern int local_mbox_lock_mask;
+
+ /*
+ * When to prepend a Delivered-To: header upon external delivery.
+ */
+#define DELIVER_HDR_CMD (1<<0)
+#define DELIVER_HDR_FILE (1<<1)
+#define DELIVER_HDR_FWD (1<<2)
+
+extern int local_deliver_hdr_mask;
+
+ /*
+ * forward.c
+ */
+extern int forward_init(void);
+extern int forward_append(DELIVER_ATTR);
+extern int forward_finish(DELIVER_REQUEST *, DELIVER_ATTR, int);
+
+ /*
+ * feature.c
+ */
+extern int feature_control(const char *);
+
+ /*
+ * local_expand.c
+ */
+int local_expand(VSTRING *, const char *, LOCAL_STATE *, USER_ATTR *, const char *);
+
+#define LOCAL_EXP_EXTENSION_MATCHED (1<<MAC_PARSE_USER)
+
+ /*
+ * alias.c
+ */
+extern MAPS *alias_maps;
+
+ /*
+ * Silly little macros.
+ */
+#define STR(s) vstring_str(s)
+
+ /*
+ * bounce_workaround.c
+ */
+int bounce_workaround(LOCAL_STATE);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
diff --git a/src/local/local_expand.c b/src/local/local_expand.c
new file mode 100644
index 0000000..ff9c3d6
--- /dev/null
+++ b/src/local/local_expand.c
@@ -0,0 +1,180 @@
+/*++
+/* NAME
+/* local_expand 3
+/* SUMMARY
+/* set up attribute list for $name expansion
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int local_expand(result, pattern, state, usr_attr, filter)
+/* VSTRING *result;
+/* const char *pattern;
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* const char *filter;
+/* DESCRIPTION
+/* local_expand() performs conditional and unconditional $name
+/* expansion based on message delivery attributes.
+/* The result is the bitwise OR or zero or more of the following:
+/* .IP LOCAL_EXP_EXTENSION_MATCHED
+/* The result of expansion contains the $extension attribute.
+/* .IP MAC_PARSE_XXX
+/* See mac_parse(3).
+/* .PP
+/* Attributes:
+/* .IP client_address
+/* The client network address.
+/* .IP client_helo
+/* The client HELO command parameter.
+/* .IP client_hostname
+/* The client hostname.
+/* .IP client_protocol
+/* The client protocol.
+/* .IP domain
+/* The recipient address domain.
+/* .IP extension
+/* The recipient address extension.
+/* .IP home
+/* The recipient home directory.
+/* .IP local
+/* The entire recipient address localpart.
+/* .IP recipient
+/* The entire recipient address.
+/* .IP recipient_delimiter
+/* The recipient delimiter.
+/* .IP shell
+/* The recipient shell program.
+/* .IP sasl_method
+/* The SASL authentication method.
+/* .IP sasl_sender
+/* The SASL MAIL FROM address.
+/* .IP sasl_username
+/* The SASL login name.
+/* .IP user
+/* The recipient user name.
+/* .PP
+/* Arguments:
+/* .IP result
+/* Storage for the result of expansion. The buffer is truncated
+/* upon entry.
+/* .IP pattern
+/* The string with unconditional and conditional macro expansions.
+/* .IP state
+/* Message delivery attributes (sender, recipient etc.).
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP filter
+/* A null pointer, or a string of allowed characters in $name
+/* expansions. Illegal characters are replaced by underscores.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory.
+/* SEE ALSO
+/* mac_expand(3) macro expansion
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <mac_expand.h>
+
+/* Global library */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+typedef struct {
+ LOCAL_STATE *state;
+ USER_ATTR *usr_attr;
+ int status;
+} LOCAL_EXP;
+
+/* local_expand_lookup - mac_expand() lookup routine */
+
+static const char *local_expand_lookup(const char *name, int mode, void *ptr)
+{
+ LOCAL_EXP *local = (LOCAL_EXP *) ptr;
+ static char rcpt_delim[2];
+
+#define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0)
+
+ if (STREQ(name, "user")) {
+ return (local->state->msg_attr.user);
+ } else if (STREQ(name, "home")) {
+ return (local->usr_attr->home);
+ } else if (STREQ(name, "shell")) {
+ return (local->usr_attr->shell);
+ } else if (STREQ(name, "domain")) {
+ return (local->state->msg_attr.domain);
+ } else if (STREQ(name, "local")) {
+ return (local->state->msg_attr.local);
+ } else if (STREQ(name, "mailbox")) {
+ return (local->state->msg_attr.local);
+ } else if (STREQ(name, "recipient")) {
+ return (local->state->msg_attr.rcpt.address);
+ } else if (STREQ(name, "extension")) {
+ if (mode == MAC_EXP_MODE_USE)
+ local->status |= LOCAL_EXP_EXTENSION_MATCHED;
+ return (local->state->msg_attr.extension);
+ } else if (STREQ(name, "recipient_delimiter")) {
+ rcpt_delim[0] =
+ local->state->msg_attr.local[strlen(local->state->msg_attr.user)];
+ if (rcpt_delim[0] == 0)
+ rcpt_delim[0] = var_rcpt_delim[0];
+ rcpt_delim[1] = 0;
+ return (rcpt_delim[0] ? rcpt_delim : 0);
+#if 0
+ } else if (STREQ(name, "client_hostname")) {
+ return (local->state->msg_attr.request->client_name);
+ } else if (STREQ(name, "client_address")) {
+ return (local->state->msg_attr.request->client_addr);
+ } else if (STREQ(name, "client_protocol")) {
+ return (local->state->msg_attr.request->client_proto);
+ } else if (STREQ(name, "client_helo")) {
+ return (local->state->msg_attr.request->client_helo);
+ } else if (STREQ(name, "sasl_method")) {
+ return (local->state->msg_attr.request->sasl_method);
+ } else if (STREQ(name, "sasl_sender")) {
+ return (local->state->msg_attr.request->sasl_sender);
+ } else if (STREQ(name, "sasl_username")) {
+ return (local->state->msg_attr.request->sasl_username);
+#endif
+ } else {
+ return (0);
+ }
+}
+
+/* local_expand - expand message delivery attributes */
+
+int local_expand(VSTRING *result, const char *pattern,
+ LOCAL_STATE *state, USER_ATTR *usr_attr, const char *filter)
+{
+ LOCAL_EXP local;
+ int expand_status;
+
+ local.state = state;
+ local.usr_attr = usr_attr;
+ local.status = 0;
+ expand_status = mac_expand(result, pattern, MAC_EXP_FLAG_NONE,
+ filter, local_expand_lookup, (void *) &local);
+ return (local.status | expand_status);
+}
diff --git a/src/local/mailbox.c b/src/local/mailbox.c
new file mode 100644
index 0000000..ed55291
--- /dev/null
+++ b/src/local/mailbox.c
@@ -0,0 +1,373 @@
+/*++
+/* NAME
+/* mailbox 3
+/* SUMMARY
+/* mailbox delivery
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_mailbox(state, usr_attr, statusp)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* int *statusp;
+/* DESCRIPTION
+/* deliver_mailbox() delivers to mailbox, with duplicate
+/* suppression. The default is direct mailbox delivery to
+/* /var/[spool/]mail/\fIuser\fR; when a \fIhome_mailbox\fR
+/* has been configured, mail is delivered to ~/$\fIhome_mailbox\fR;
+/* and when a \fImailbox_command\fR has been configured, the message
+/* is piped into the command instead.
+/*
+/* A zero result means that the named user was not found.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP statusp
+/* Delivery status: see below.
+/* DIAGNOSTICS
+/* The message delivery status is non-zero when delivery should be tried
+/* again.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <set_eugid.h>
+#include <warn_stat.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <defer.h>
+#include <sent.h>
+#include <mypwd.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <deliver_pass.h>
+#include <mbox_open.h>
+#include <maps.h>
+#include <dsn_util.h>
+
+/* Application-specific. */
+
+#include "local.h"
+#include "biff_notify.h"
+
+#define YES 1
+#define NO 0
+
+/* deliver_mailbox_file - deliver to recipient mailbox */
+
+static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ const char *myname = "deliver_mailbox_file";
+ char *spool_dir;
+ char *mailbox;
+ DSN_BUF *why = state.msg_attr.why;
+ MBOX *mp;
+ int mail_copy_status;
+ int deliver_status;
+ int copy_flags;
+ VSTRING *biff;
+ off_t end;
+ struct stat st;
+ uid_t spool_uid;
+ gid_t spool_gid;
+ uid_t chown_uid;
+ gid_t chown_gid;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Don't deliver trace-only requests.
+ */
+ if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+ dsb_simple(why, "2.0.0", "delivers to mailbox");
+ return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * Initialize. Assume the operation will fail. Set the delivered
+ * attribute to reflect the final recipient.
+ */
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
+ if (var_frozen_delivered == 0)
+ state.msg_attr.delivered = state.msg_attr.rcpt.address;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ if (*var_home_mailbox) {
+ spool_dir = 0;
+ mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
+ } else {
+ spool_dir = var_mail_spool_dir;
+ mailbox = concatenate(spool_dir, "/", state.msg_attr.user, (char *) 0);
+ }
+
+ /*
+ * Mailbox delivery with least privilege. As long as we do not use root
+ * privileges this code may also work over NFS.
+ *
+ * If delivering to the recipient's home directory, perform all operations
+ * (including file locking) as that user (Mike Muuss, Army Research
+ * Laboratory, USA).
+ *
+ * If delivering to the mail spool directory, and the spool directory is
+ * world-writable, deliver as the recipient; if the spool directory is
+ * group-writable, use the recipient user id and the mail spool group id.
+ *
+ * Otherwise, use root privileges and chown the mailbox if we create it.
+ */
+ if (spool_dir == 0
+ || stat(spool_dir, &st) < 0
+ || (st.st_mode & S_IWOTH) != 0) {
+ spool_uid = usr_attr.uid;
+ spool_gid = usr_attr.gid;
+ } else if ((st.st_mode & S_IWGRP) != 0) {
+ spool_uid = usr_attr.uid;
+ spool_gid = st.st_gid;
+ } else {
+ spool_uid = 0;
+ spool_gid = 0;
+ }
+ if (spool_uid == usr_attr.uid) {
+ chown_uid = -1;
+ chown_gid = -1;
+ } else {
+ chown_uid = usr_attr.uid;
+ chown_gid = usr_attr.gid;
+ }
+ if (msg_verbose)
+ msg_info("spool_uid/gid %ld/%ld chown_uid/gid %ld/%ld",
+ (long) spool_uid, (long) spool_gid,
+ (long) chown_uid, (long) chown_gid);
+
+ /*
+ * Lock the mailbox and open/create the mailbox file. Depending on the
+ * type of locking used, we lock first or we open first.
+ *
+ * Write the file as the recipient, so that file quota work.
+ */
+ copy_flags = MAIL_COPY_MBOX;
+ if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0)
+ copy_flags &= ~MAIL_COPY_DELIVERED;
+
+ set_eugid(spool_uid, spool_gid);
+ mp = mbox_open(mailbox, O_APPEND | O_WRONLY | O_CREAT,
+ S_IRUSR | S_IWUSR, &st, chown_uid, chown_gid,
+ local_mbox_lock_mask, "5.2.0", why);
+ if (mp != 0) {
+ if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid)
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ if (S_ISREG(st.st_mode) == 0) {
+ vstream_fclose(mp->fp);
+ dsb_simple(why, "5.2.0",
+ "destination %s is not a regular file", mailbox);
+ } else if (var_strict_mbox_owner && st.st_uid != usr_attr.uid) {
+ vstream_fclose(mp->fp);
+ dsb_simple(why, "4.2.0",
+ "destination %s is not owned by recipient", mailbox);
+ msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch",
+ VAR_STRICT_MBOX_OWNER);
+ } else {
+ if ((end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END)) < 0)
+ msg_fatal("seek mailbox file %s: %m", mailbox);
+ mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ copy_flags, "\n", why);
+ }
+ if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid)
+ set_eugid(spool_uid, spool_gid);
+ mbox_release(mp);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * As the mail system, bounce, defer delivery, or report success.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ vstring_sprintf_prepend(why->reason,
+ "cannot update mailbox %s for user %s. ",
+ mailbox, state.msg_attr.user);
+ deliver_status =
+ (STR(why->status)[0] == '4' ?
+ defer_append : bounce_append)
+ (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ } else {
+ dsb_simple(why, "2.0.0", "delivered to mailbox");
+ deliver_status = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ if (var_biff) {
+ biff = vstring_alloc(100);
+ vstring_sprintf(biff, "%s@%ld", usr_attr.logname, (long) end);
+ biff_notify(STR(biff), VSTRING_LEN(biff) + 1);
+ vstring_free(biff);
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ myfree(mailbox);
+ return (deliver_status);
+}
+
+/* deliver_mailbox - deliver to recipient mailbox */
+
+int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
+{
+ const char *myname = "deliver_mailbox";
+ int status;
+ struct mypasswd *mbox_pwd;
+ char *path;
+ static MAPS *transp_maps;
+ const char *map_transport;
+ static MAPS *cmd_maps;
+ const char *map_command;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE ELIMINATION
+ *
+ * Don't come here more than once, whether or not the recipient exists.
+ */
+ if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local))
+ return (YES);
+
+ /*
+ * Delegate mailbox delivery to another message transport.
+ */
+ if (*var_mbox_transp_maps && transp_maps == 0)
+ transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB
+ | DICT_FLAG_UTF8_REQUEST);
+ /* The -1 is a hint for the down-stream deliver_completed() function. */
+ if (transp_maps
+ && (map_transport = maps_find(transp_maps, state.msg_attr.user,
+ DICT_FLAG_NONE)) != 0) {
+ state.msg_attr.rcpt.offset = -1L;
+ *statusp = deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
+ state.request, &state.msg_attr.rcpt);
+ return (YES);
+ } else if (transp_maps && transp_maps->error != 0) {
+ /* Details in the logfile. */
+ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ if (*var_mailbox_transport) {
+ state.msg_attr.rcpt.offset = -1L;
+ *statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport,
+ state.request, &state.msg_attr.rcpt);
+ return (YES);
+ }
+
+ /*
+ * Skip delivery when this recipient does not exist.
+ */
+ if ((errno = mypwnam_err(state.msg_attr.user, &mbox_pwd)) != 0) {
+ msg_warn("error looking up passwd info for %s: %m",
+ state.msg_attr.user);
+ dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ if (mbox_pwd == 0)
+ return (NO);
+
+ /*
+ * No early returns or we have a memory leak.
+ */
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * Use the rights of the recipient user.
+ */
+ SET_USER_ATTR(usr_attr, mbox_pwd, state.level);
+
+ /*
+ * Deliver to mailbox, maildir or to external command.
+ */
+#define LAST_CHAR(s) (s[strlen(s) - 1])
+
+ if (*var_mailbox_cmd_maps && cmd_maps == 0)
+ cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_PARANOID
+ | DICT_FLAG_UTF8_REQUEST);
+
+ if (cmd_maps && (map_command = maps_find(cmd_maps, state.msg_attr.user,
+ DICT_FLAG_NONE)) != 0) {
+ status = deliver_command(state, usr_attr, map_command);
+ } else if (cmd_maps && cmd_maps->error != 0) {
+ /* Details in the logfile. */
+ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
+ status = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else if (*var_mailbox_command) {
+ status = deliver_command(state, usr_attr, var_mailbox_command);
+ } else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') {
+ path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
+ status = deliver_maildir(state, usr_attr, path);
+ myfree(path);
+ } else if (*var_mail_spool_dir && LAST_CHAR(var_mail_spool_dir) == '/') {
+ path = concatenate(var_mail_spool_dir, state.msg_attr.user,
+ "/", (char *) 0);
+ status = deliver_maildir(state, usr_attr, path);
+ myfree(path);
+ } else
+ status = deliver_mailbox_file(state, usr_attr);
+
+ /*
+ * Cleanup.
+ */
+ mypwfree(mbox_pwd);
+ *statusp = status;
+ return (YES);
+}
diff --git a/src/local/maildir.c b/src/local/maildir.c
new file mode 100644
index 0000000..46b8641
--- /dev/null
+++ b/src/local/maildir.c
@@ -0,0 +1,257 @@
+/*++
+/* NAME
+/* maildir 3
+/* SUMMARY
+/* delivery to maildir
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_maildir(state, usr_attr, path)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *path;
+/* DESCRIPTION
+/* deliver_maildir() delivers a message to a qmail maildir.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* .IP usr_attr
+/* Attributes describing user rights and environment information.
+/* .IP path
+/* The maildir to deliver to, including trailing slash.
+/* DIAGNOSTICS
+/* deliver_maildir() always succeeds or it bounces the message.
+/* SEE ALSO
+/* bounce(3)
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <make_dirs.h>
+#include <set_eugid.h>
+#include <get_hostname.h>
+#include <sane_fsops.h>
+#include <warn_stat.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <bounce.h>
+#include <defer.h>
+#include <sent.h>
+#include <mail_params.h>
+#include <dsn_util.h>
+#include <mbox_open.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_maildir - delivery to maildir-style mailbox */
+
+int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
+{
+ const char *myname = "deliver_maildir";
+ char *newdir;
+ char *tmpdir;
+ char *curdir;
+ char *tmpfile;
+ char *newfile;
+ DSN_BUF *why = state.msg_attr.why;
+ VSTRING *buf;
+ VSTREAM *dst;
+ int mail_copy_status;
+ int deliver_status;
+ int copy_flags;
+ struct stat st;
+ struct timeval starttime;
+
+ GETTIMEOFDAY(&starttime);
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Don't deliver trace-only requests.
+ */
+ if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+ dsb_simple(why, "2.0.0", "delivers to maildir");
+ return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * Initialize. Assume the operation will fail. Set the delivered
+ * attribute to reflect the final recipient.
+ */
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
+ if (var_frozen_delivered == 0)
+ state.msg_attr.delivered = state.msg_attr.rcpt.address;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ buf = vstring_alloc(100);
+
+ copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_ORIG_RCPT;
+ if (local_deliver_hdr_mask & DELIVER_HDR_FILE)
+ copy_flags |= MAIL_COPY_DELIVERED;
+
+ newdir = concatenate(path, "new/", (char *) 0);
+ tmpdir = concatenate(path, "tmp/", (char *) 0);
+ curdir = concatenate(path, "cur/", (char *) 0);
+
+ /*
+ * Create and write the file as the recipient, so that file quota work.
+ * Create any missing directories on the fly. The file name is chosen
+ * according to ftp://koobera.math.uic.edu/www/proto/maildir.html:
+ *
+ * "A unique name has three pieces, separated by dots. On the left is the
+ * result of time(). On the right is the result of gethostname(). In the
+ * middle is something that doesn't repeat within one second on a single
+ * host. I fork a new process for each delivery, so I just use the
+ * process ID. If you're delivering several messages from one process,
+ * use starttime.pid_count.host, where starttime is the time that your
+ * process started, and count is the number of messages you've
+ * delivered."
+ *
+ * Well, that stopped working on fast machines, and on operating systems
+ * that randomize process ID values. When creating a file in tmp/ we use
+ * the process ID because it still is an exclusive resource. When moving
+ * the file to new/ we use the device number and inode number. I do not
+ * care if this breaks on a remote AFS file system, because people should
+ * know better.
+ *
+ * On January 26, 2003, http://cr.yp.to/proto/maildir.html said:
+ *
+ * A unique name has three pieces, separated by dots. On the left is the
+ * result of time() or the second counter from gettimeofday(). On the
+ * right is the result of gethostname(). (To deal with invalid host
+ * names, replace / with \057 and : with \072.) In the middle is a
+ * delivery identifier, discussed below.
+ *
+ * [...]
+ *
+ * Modern delivery identifiers are created by concatenating enough of the
+ * following strings to guarantee uniqueness:
+ *
+ * [...]
+ *
+ * In, where n is (in hexadecimal) the UNIX inode number of this file.
+ * Unfortunately, inode numbers aren't always available through NFS.
+ *
+ * Vn, where n is (in hexadecimal) the UNIX device number of this file.
+ * Unfortunately, device numbers aren't always available through NFS.
+ * (Device numbers are also not helpful with the standard UNIX
+ * filesystem: a maildir has to be within a single UNIX device for link()
+ * and rename() to work.)
+ *
+ * Mn, where n is (in decimal) the microsecond counter from the same
+ * gettimeofday() used for the left part of the unique name.
+ *
+ * Pn, where n is (in decimal) the process ID.
+ *
+ * [...]
+ */
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ vstring_sprintf(buf, "%lu.P%d.%s",
+ (unsigned long) starttime.tv_sec, var_pid, get_hostname());
+ tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
+ newfile = 0;
+ if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
+ && (errno != ENOENT
+ || make_dirs(tmpdir, 0700) < 0
+ || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
+ dsb_simple(why, mbox_dsn(errno, "5.2.0"),
+ "create maildir file %s: %m", tmpfile);
+ } else if (fstat(vstream_fileno(dst), &st) < 0) {
+
+ /*
+ * Coverity 200604: file descriptor leak in code that never executes.
+ * Code replaced by msg_fatal(), as it is not worthwhile to continue
+ * after an impossible error condition.
+ */
+ msg_fatal("fstat %s: %m", tmpfile);
+ } else {
+ vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
+ (unsigned long) starttime.tv_sec,
+ (unsigned long) st.st_dev,
+ (unsigned long) st.st_ino,
+ (unsigned long) starttime.tv_usec,
+ get_hostname());
+ newfile = concatenate(newdir, STR(buf), (char *) 0);
+ if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
+ dst, copy_flags, "\n",
+ why)) == 0) {
+ if (sane_link(tmpfile, newfile) < 0
+ && (errno != ENOENT
+ || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
+ || sane_link(tmpfile, newfile) < 0)) {
+ dsb_simple(why, mbox_dsn(errno, "5.2.0"),
+ "create maildir file %s: %m", newfile);
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ }
+ }
+ if (unlink(tmpfile) < 0)
+ msg_warn("remove %s: %m", tmpfile);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * As the mail system, bounce or defer delivery.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ if (errno == EACCES) {
+ msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
+ (long) usr_attr.uid, (long) usr_attr.gid,
+ STR(why->reason));
+ msg_warn("perhaps you need to create the maildirs in advance");
+ }
+ vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
+ deliver_status =
+ (STR(why->status)[0] == '4' ?
+ defer_append : bounce_append)
+ (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ } else {
+ dsb_simple(why, "2.0.0", "delivered to maildir");
+ deliver_status = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ }
+ vstring_free(buf);
+ myfree(newdir);
+ myfree(tmpdir);
+ myfree(curdir);
+ myfree(tmpfile);
+ if (newfile)
+ myfree(newfile);
+ return (deliver_status);
+}
diff --git a/src/local/recipient.c b/src/local/recipient.c
new file mode 100644
index 0000000..e3f4d1c
--- /dev/null
+++ b/src/local/recipient.c
@@ -0,0 +1,307 @@
+/*++
+/* NAME
+/* recipient 3
+/* SUMMARY
+/* deliver to one local recipient
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_recipient(state, usr_attr)
+/* LOCAL_STATE state;
+/* USER_ATTR *usr_attr;
+/* DESCRIPTION
+/* deliver_recipient() delivers a message to a local recipient.
+/* It is called initially when the queue manager requests
+/* delivery to a local recipient, and is called recursively
+/* when an alias or forward file expands to a local recipient.
+/*
+/* When called recursively with, for example, a result from alias
+/* or forward file expansion, aliases are expanded immediately,
+/* but mail for non-alias destinations is submitted as a new
+/* message, so that each recipient has a dedicated queue file
+/* message delivery status record (in a shared queue file).
+/*
+/* When the \fIrecipient_delimiter\fR configuration parameter
+/* is set, it is used to separate cookies off recipient names.
+/* A common setting is to have "recipient_delimiter = +"
+/* so that mail for \fIuser+foo\fR is delivered to \fIuser\fR,
+/* with a "Delivered-To: user+foo@domain" header line.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, sender, and more.
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* DIAGNOSTICS
+/* deliver_recipient() returns non-zero when delivery should be
+/* tried again.
+/* BUGS
+/* Mutually-recursive aliases or $HOME/.forward files aren't
+/* detected when they could be. The resulting mail forwarding loop
+/* is broken by the use of the Delivered-To: message header.
+/* SEE ALSO
+/* alias(3) delivery to aliases
+/* mailbox(3) delivery to mailbox
+/* dotforward(3) delivery to destinations in .forward file
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <split_at.h>
+#include <stringops.h>
+#include <dict.h>
+#include <stat_as.h>
+
+/* Global library. */
+
+#include <bounce.h>
+#include <defer.h>
+#include <mail_params.h>
+#include <split_addr.h>
+#include <strip_addr.h>
+#include <ext_prop.h>
+#include <mypwd.h>
+#include <canon_addr.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_switch - branch on recipient type */
+
+static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ const char *myname = "deliver_switch";
+ int status = 0;
+ struct stat st;
+ struct mypasswd *mypwd;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+
+ /*
+ * \user is special: it means don't do any alias or forward expansion.
+ *
+ * XXX This code currently does not work due to revision of the RFC822
+ * address parser. \user should be permitted only in locally specified
+ * aliases, includes or forward files.
+ *
+ * XXX Should test for presence of user home directory.
+ */
+ if (state.msg_attr.rcpt.address[0] == '\\') {
+ state.msg_attr.rcpt.address++, state.msg_attr.local++, state.msg_attr.user++;
+ if (deliver_mailbox(state, usr_attr, &status) == 0)
+ status = deliver_unknown(state, usr_attr);
+ return (status);
+ }
+
+ /*
+ * Otherwise, alias expansion has highest precedence. First look up the
+ * full localpart, then the bare user. Obey the address extension
+ * propagation policy.
+ */
+ state.msg_attr.unmatched = 0;
+ if (deliver_alias(state, usr_attr, state.msg_attr.local, &status))
+ return (status);
+ if (state.msg_attr.extension != 0) {
+ if (local_ext_prop_mask & EXT_PROP_ALIAS)
+ state.msg_attr.unmatched = state.msg_attr.extension;
+ if (deliver_alias(state, usr_attr, state.msg_attr.user, &status))
+ return (status);
+ state.msg_attr.unmatched = state.msg_attr.extension;
+ }
+
+ /*
+ * Special case for mail locally forwarded or aliased to a different
+ * local address. Resubmit the message via the cleanup service, so that
+ * each recipient gets a separate delivery queue file status record in
+ * the new queue file. The downside of this approach is that mutually
+ * recursive .forward files cause a mail forwarding loop. Fortunately,
+ * the loop can be broken by the use of the Delivered-To: message header.
+ *
+ * The code below must not trigger on mail sent to an alias that has no
+ * owner- companion, so that mail for an alias first.last->username is
+ * delivered directly, instead of going through username->first.last
+ * canonical mappings in the cleanup service. The downside of this
+ * approach is that recipients in the expansion of an alias without
+ * owner- won't have separate delivery queue file status records, because
+ * for them, the message won't be resubmitted as a new queue file.
+ *
+ * Do something sensible on systems that receive mail for multiple domains,
+ * such as primary.name and secondary.name. Don't resubmit the message
+ * when mail for `user@secondary.name' is delivered to a .forward file
+ * that lists `user' or `user@primary.name'. We already know that the
+ * recipient domain is local, so we only have to compare local parts.
+ */
+ if (state.msg_attr.owner != 0
+ && strcasecmp_utf8(state.msg_attr.owner, state.msg_attr.user) != 0)
+ return (deliver_indirect(state));
+
+ /*
+ * Always forward recipients in :include: files.
+ */
+ if (state.msg_attr.exp_type == EXPAND_TYPE_INCL)
+ return (deliver_indirect(state));
+
+ /*
+ * Delivery to local user. First try expansion of the recipient's
+ * $HOME/.forward file, then mailbox delivery. Back off when the user's
+ * home directory does not exist.
+ */
+ mypwd = 0;
+ if (var_stat_home_dir
+ && (errno = mypwnam_err(state.msg_attr.user, &mypwd)) != 0) {
+ msg_warn("error looking up passwd info for %s: %m",
+ state.msg_attr.user);
+ dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error");
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (mypwd != 0) {
+ if (stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0) {
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "cannot access home directory %s: %m", mypwd->pw_dir);
+ mypwfree(mypwd);
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ mypwfree(mypwd);
+ }
+ if (deliver_dotforward(state, usr_attr, &status) == 0
+ && deliver_mailbox(state, usr_attr, &status) == 0)
+ status = deliver_unknown(state, usr_attr);
+ return (status);
+}
+
+/* deliver_recipient - deliver one local recipient */
+
+int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ const char *myname = "deliver_recipient";
+ VSTRING *folded;
+ int rcpt_stat;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Duplicate filter.
+ */
+ if (been_here(state.dup_filter, "recipient %d %s",
+ state.level, state.msg_attr.rcpt.address))
+ return (0);
+
+ /*
+ * With each level of recursion, detect and break external message
+ * forwarding loops.
+ *
+ * If the looping recipient address has an owner- alias, send the error
+ * report there instead.
+ *
+ * XXX A delivery agent cannot change the envelope sender address for
+ * bouncing. As a workaround we use a one-recipient bounce procedure.
+ *
+ * The proper fix would be to record in the bounce logfile an error return
+ * address for each individual recipient. This would also eliminate the
+ * need for VERP specific bouncing code, at the cost of complicating the
+ * normal bounce sending procedure, but would simplify the code below.
+ */
+ if (delivered_hdr_find(state.loop_info, state.msg_attr.rcpt.address)) {
+ dsb_simple(state.msg_attr.why, "5.4.6", "mail forwarding loop for %s",
+ state.msg_attr.rcpt.address);
+ /* Account for possible owner- sender address override. */
+ return (bounce_workaround(state));
+ }
+
+ /*
+ * Set up the recipient-specific attributes. If this is forwarded mail,
+ * leave the delivered attribute alone, so that the forwarded message
+ * will show the correct forwarding recipient.
+ */
+ if (state.msg_attr.delivered == 0)
+ state.msg_attr.delivered = state.msg_attr.rcpt.address;
+ folded = vstring_alloc(100);
+ state.msg_attr.local = casefold(folded, state.msg_attr.rcpt.address);
+ if ((state.msg_attr.domain = split_at_right(state.msg_attr.local, '@')) == 0)
+ msg_warn("no @ in recipient address: %s", state.msg_attr.local);
+
+ /*
+ * Address extension management.
+ *
+ * XXX Fix 20100422, finalized 20100529: it is too error-prone to
+ * distinguish between "no extension" and "no valid extension", so we
+ * drop an invalid extension from the recipient address local-part.
+ */
+ state.msg_attr.user = mystrdup(state.msg_attr.local);
+ if (*var_rcpt_delim) {
+ state.msg_attr.extension =
+ split_addr(state.msg_attr.user, var_rcpt_delim);
+ if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) {
+ msg_warn("%s: address with illegal extension: %s",
+ state.msg_attr.queue_id, state.msg_attr.local);
+ state.msg_attr.extension = 0;
+ /* XXX Can't myfree + mystrdup, must truncate instead. */
+ state.msg_attr.local[strlen(state.msg_attr.user)] = 0;
+ /* Truncating is safe. The code below rejects null usernames. */
+ }
+ } else
+ state.msg_attr.extension = 0;
+ state.msg_attr.unmatched = state.msg_attr.extension;
+
+ /*
+ * Do not allow null usernames.
+ */
+ if (state.msg_attr.user[0] == 0) {
+ dsb_simple(state.msg_attr.why, "5.1.3",
+ "null username in \"%s\"", state.msg_attr.rcpt.address);
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * Run the recipient through the delivery switch.
+ */
+ if (msg_verbose)
+ deliver_attr_dump(&state.msg_attr);
+ rcpt_stat = deliver_switch(state, usr_attr);
+
+ /*
+ * Clean up.
+ */
+ vstring_free(folded);
+ myfree(state.msg_attr.user);
+
+ return (rcpt_stat);
+}
diff --git a/src/local/resolve.c b/src/local/resolve.c
new file mode 100644
index 0000000..a6aa9d0
--- /dev/null
+++ b/src/local/resolve.c
@@ -0,0 +1,170 @@
+/*++
+/* NAME
+/* resolve 3
+/* SUMMARY
+/* resolve recipient and deliver locally or remotely
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_resolve_tree(state, usr_attr, addr)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* TOK822 *addr;
+/*
+/* int deliver_resolve_addr(state, usr_attr, addr)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *addr;
+/* DESCRIPTION
+/* deliver_resolve_XXX() resolves a recipient that is the result from
+/* e.g., alias expansion, and delivers locally or via forwarding.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, sender and more.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP addr
+/* An address from, e.g., alias expansion.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. The result is non-zero when the
+/* operation should be tried again. Warnings: malformed address.
+/* SEE ALSO
+/* recipient(3) local delivery
+/* indirect(3) deliver via forwarding
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <resolve_clnt.h>
+#include <rewrite_clnt.h>
+#include <tok822.h>
+#include <mail_params.h>
+#include <defer.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_resolve_addr - resolve and deliver */
+
+int deliver_resolve_addr(LOCAL_STATE state, USER_ATTR usr_attr, char *addr)
+{
+ TOK822 *tree;
+ int result;
+
+ tree = tok822_scan_addr(addr);
+ result = deliver_resolve_tree(state, usr_attr, tree);
+ tok822_free_tree(tree);
+ return (result);
+}
+
+/* deliver_resolve_tree - resolve and deliver */
+
+int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
+{
+ const char *myname = "deliver_resolve_tree";
+ RESOLVE_REPLY reply;
+ int status;
+ ssize_t ext_len;
+ char *ratsign;
+ int rcpt_delim;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Initialize.
+ */
+ resolve_clnt_init(&reply);
+
+ /*
+ * Rewrite the address to canonical form, just like the cleanup service
+ * does. Then, resolve the address to (transport, nexhop, recipient),
+ * just like the queue manager does. The only part missing here is the
+ * virtual address substitution. Message forwarding fixes most of that.
+ */
+ tok822_rewrite(addr, REWRITE_CANON);
+ tok822_resolve(addr, &reply);
+
+ /*
+ * First, a healthy portion of error handling.
+ */
+ if (reply.flags & RESOLVE_FLAG_FAIL) {
+ dsb_simple(state.msg_attr.why, "4.3.0", "address resolver failure");
+ status = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else if (reply.flags & RESOLVE_FLAG_ERROR) {
+ dsb_simple(state.msg_attr.why, "5.1.3",
+ "bad recipient address syntax: %s", STR(reply.recipient));
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+
+ /*
+ * Splice in the optional unmatched address extension.
+ */
+ if (state.msg_attr.unmatched) {
+ rcpt_delim = state.msg_attr.local[strlen(state.msg_attr.user)];
+ if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) {
+ VSTRING_ADDCH(reply.recipient, rcpt_delim);
+ vstring_strcat(reply.recipient, state.msg_attr.unmatched);
+ } else {
+ ext_len = strlen(state.msg_attr.unmatched);
+ VSTRING_SPACE(reply.recipient, ext_len + 2);
+ if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0)
+ msg_panic("%s: recipient @ botch", myname);
+ memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1);
+ *ratsign = rcpt_delim;
+ memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len);
+ VSTRING_SKIP(reply.recipient);
+ }
+ }
+ state.msg_attr.rcpt.address = STR(reply.recipient);
+
+ /*
+ * Delivery to a local or non-local address. For a while there was
+ * some ugly code to force local recursive alias expansions on a host
+ * with no authority over the local domain, but that code was just
+ * too unclean.
+ */
+ if (strcmp(state.msg_attr.relay, STR(reply.transport)) == 0) {
+ status = deliver_recipient(state, usr_attr);
+ } else {
+ status = deliver_indirect(state);
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ resolve_clnt_free(&reply);
+
+ return (status);
+}
diff --git a/src/local/token.c b/src/local/token.c
new file mode 100644
index 0000000..2eb0c28
--- /dev/null
+++ b/src/local/token.c
@@ -0,0 +1,222 @@
+/*++
+/* NAME
+/* token 3
+/* SUMMARY
+/* tokenize alias/include/.forward entries and deliver
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_token(state, usr_attr, addr)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* TOK822 *addr;
+/*
+/* int deliver_token_string(state, usr_attr, string, addr_count)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *string;
+/* int *addr_count;
+/*
+/* int deliver_token_stream(state, usr_attr, fp, addr_count)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* VSTREAM *fp;
+/* int *addr_count;
+/* DESCRIPTION
+/* This module delivers to addresses listed in an alias database
+/* entry, in an include file, or in a .forward file.
+/*
+/* deliver_token() delivers to the address in the given token:
+/* an absolute /path/name, a ~/path/name relative to the recipient's
+/* home directory, an :include:/path/name request, an external
+/* "|command", or a mail address.
+/*
+/* deliver_token_string() delivers to all addresses listed in
+/* the specified string.
+/*
+/* deliver_token_stream() delivers to all addresses listed in
+/* the specified stream. Input records > \fIline_length_limit\fR
+/* are broken up into multiple records, to prevent the mail
+/* system from using unreasonable amounts of memory.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP addr
+/* A parsed address from an include file, alias file or .forward file.
+/* .IP string
+/* A null-terminated string.
+/* .IP fp
+/* A readable stream.
+/* .IP addr_count
+/* Null pointer, or the address of a counter that is incremented
+/* by the number of destinations found by the tokenizer.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. The result is non-zero when the
+/* operation should be tried again. Warnings: malformed address.
+/* SEE ALSO
+/* list_token(3) tokenize list
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <htable.h>
+#include <readlline.h>
+#include <mymalloc.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <tok822.h>
+#include <mail_params.h>
+#include <bounce.h>
+#include <defer.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_token_home - expand ~token */
+
+static int deliver_token_home(LOCAL_STATE state, USER_ATTR usr_attr, char *addr)
+{
+ char *full_path;
+ int status;
+
+ if (addr[1] != '/') { /* disallow ~user */
+ msg_warn("bad home directory syntax for: %s", addr);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else if (usr_attr.home == 0) { /* require user context */
+ msg_warn("unknown home directory for: %s", addr);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else if (usr_attr.home[0] == '/' && usr_attr.home[1] == 0) {
+ status = deliver_file(state, usr_attr, addr + 1);
+ } else { /* expand ~ to home */
+ full_path = concatenate(usr_attr.home, addr + 1, (char *) 0);
+ status = deliver_file(state, usr_attr, full_path);
+ myfree(full_path);
+ }
+ return (status);
+}
+
+/* deliver_token - deliver to expansion of include file or alias */
+
+int deliver_token(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
+{
+ VSTRING *addr_buf = vstring_alloc(100);
+ static char include[] = ":include:";
+ int status;
+ char *path;
+
+ tok822_internalize(addr_buf, addr->head, TOK822_STR_DEFL);
+ if (msg_verbose)
+ msg_info("deliver_token: %s", STR(addr_buf));
+
+ if (*STR(addr_buf) == '/') {
+ status = deliver_file(state, usr_attr, STR(addr_buf));
+ } else if (*STR(addr_buf) == '~') {
+ status = deliver_token_home(state, usr_attr, STR(addr_buf));
+ } else if (*STR(addr_buf) == '|') {
+ if ((local_cmd_deliver_mask & state.msg_attr.exp_type) == 0) {
+ dsb_simple(state.msg_attr.why, "5.7.1",
+ "mail to command is restricted");
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else
+ status = deliver_command(state, usr_attr, STR(addr_buf) + 1);
+ } else if (strncasecmp(STR(addr_buf), include, sizeof(include) - 1) == 0) {
+ path = STR(addr_buf) + sizeof(include) - 1;
+ status = deliver_include(state, usr_attr, path);
+ } else {
+ status = deliver_resolve_tree(state, usr_attr, addr);
+ }
+ vstring_free(addr_buf);
+
+ return (status);
+}
+
+/* deliver_token_string - tokenize string and deliver */
+
+int deliver_token_string(LOCAL_STATE state, USER_ATTR usr_attr,
+ char *string, int *addr_count)
+{
+ TOK822 *tree;
+ TOK822 *addr;
+ int status = 0;
+
+ if (msg_verbose)
+ msg_info("deliver_token_string: %s", string);
+
+ tree = tok822_parse(string);
+ for (addr = tree; addr != 0; addr = addr->next) {
+ if (addr->type == TOK822_ADDR) {
+ if (addr_count)
+ (*addr_count)++;
+ status |= deliver_token(state, usr_attr, addr);
+ }
+ }
+ tok822_free_tree(tree);
+ return (status);
+}
+
+/* deliver_token_stream - tokenize stream and deliver */
+
+int deliver_token_stream(LOCAL_STATE state, USER_ATTR usr_attr,
+ VSTREAM *fp, int *addr_count)
+{
+ VSTRING *buf = vstring_alloc(100);
+ int status = 0;
+
+ if (msg_verbose)
+ msg_info("deliver_token_stream: %s", VSTREAM_PATH(fp));
+
+ while (vstring_fgets_bound(buf, fp, var_line_limit)) {
+ if (*STR(buf) != '#') {
+ status = deliver_token_string(state, usr_attr, STR(buf), addr_count);
+ if (status != 0)
+ break;
+ }
+ }
+ if (vstream_ferror(fp)) {
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "error reading forwarding file: %m");
+ status = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ }
+ vstring_free(buf);
+ return (status);
+}
diff --git a/src/local/unknown.c b/src/local/unknown.c
new file mode 100644
index 0000000..96443e1
--- /dev/null
+++ b/src/local/unknown.c
@@ -0,0 +1,187 @@
+/*++
+/* NAME
+/* unknown 3
+/* SUMMARY
+/* delivery of unknown recipients
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_unknown(state, usr_attr)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* DESCRIPTION
+/* deliver_unknown() delivers a message for unknown recipients.
+/* .IP \(bu
+/* If an alternative message transport is specified via the
+/* fallback_transport parameter, delivery is delegated to the
+/* named transport.
+/* .IP \(bu
+/* If an alternative address is specified via the luser_relay
+/* configuration parameter, mail is forwarded to that address.
+/* .IP \(bu
+/* Otherwise the recipient is bounced.
+/* .PP
+/* The luser_relay parameter is subjected to $name expansion of
+/* the standard message attributes: $user, $home, $shell, $domain,
+/* $recipient, $mailbox, $extension, $recipient_delimiter, not
+/* all of which actually make sense.
+/*
+/* Arguments:
+/* .IP state
+/* Message delivery attributes (sender, recipient etc.).
+/* Attributes describing alias, include or forward expansion.
+/* A table with the results from expanding aliases or lists.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* DIAGNOSTICS
+/* The result status is non-zero when delivery should be tried again.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <been_here.h>
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <bounce.h>
+#include <mail_addr.h>
+#include <sent.h>
+#include <deliver_pass.h>
+#include <defer.h>
+#include <canon_addr.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+#define STREQ(x,y) (strcasecmp((x),(y)) == 0)
+
+/* deliver_unknown - delivery for unknown recipients */
+
+int deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ const char *myname = "deliver_unknown";
+ int status;
+ VSTRING *expand_luser;
+ VSTRING *canon_luser;
+ static MAPS *transp_maps;
+ const char *map_transport;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE/LOOP ELIMINATION
+ *
+ * Don't deliver the same user twice.
+ */
+ if (been_here(state.dup_filter, "%s %s", myname, state.msg_attr.local))
+ return (0);
+
+ /*
+ * The fall-back transport specifies a delivery mechanism that handles
+ * users not found in the aliases or UNIX passwd databases.
+ */
+ if (*var_fbck_transp_maps && transp_maps == 0)
+ transp_maps = maps_create(VAR_FBCK_TRANSP_MAPS, var_fbck_transp_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB
+ | DICT_FLAG_UTF8_REQUEST);
+ /* The -1 is a hint for the down-stream deliver_completed() function. */
+ if (transp_maps
+ && (map_transport = maps_find(transp_maps, state.msg_attr.user,
+ DICT_FLAG_NONE)) != 0) {
+ state.msg_attr.rcpt.offset = -1L;
+ return (deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
+ state.request, &state.msg_attr.rcpt));
+ } else if (transp_maps && transp_maps->error != 0) {
+ /* Details in the logfile. */
+ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (*var_fallback_transport) {
+ state.msg_attr.rcpt.offset = -1L;
+ return (deliver_pass(MAIL_CLASS_PRIVATE, var_fallback_transport,
+ state.request, &state.msg_attr.rcpt));
+ }
+
+ /*
+ * Subject the luser_relay address to $name expansion, disable
+ * propagation of unmatched address extension, and re-inject the address
+ * into the delivery machinery. Do not give special treatment to "|stuff"
+ * or /stuff.
+ */
+ if (*var_luser_relay) {
+ state.msg_attr.unmatched = 0;
+ expand_luser = vstring_alloc(100);
+ canon_luser = vstring_alloc(100);
+ local_expand(expand_luser, var_luser_relay, &state, &usr_attr, (void *) 0);
+ /* In case luser_relay specifies a domain-less address. */
+ canon_addr_external(canon_luser, vstring_str(expand_luser));
+ /* Assumes that the address resolver won't change the address. */
+ if (STREQ(vstring_str(canon_luser), state.msg_attr.rcpt.address)) {
+ dsb_simple(state.msg_attr.why, "5.1.1",
+ "unknown user: \"%s\"", state.msg_attr.user);
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+ status = deliver_resolve_addr(state, usr_attr, STR(expand_luser));
+ }
+ vstring_free(canon_luser);
+ vstring_free(expand_luser);
+ return (status);
+ }
+
+ /*
+ * If no alias was found for a required reserved name, toss the message
+ * into the bit bucket, and issue a warning instead.
+ */
+ if (STREQ(state.msg_attr.user, MAIL_ADDR_MAIL_DAEMON)
+ || STREQ(state.msg_attr.user, MAIL_ADDR_POSTMASTER)) {
+ msg_warn("required alias not found: %s", state.msg_attr.user);
+ dsb_simple(state.msg_attr.why, "2.0.0", "discarded");
+ return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * Bounce the message when no luser relay is specified.
+ */
+ dsb_simple(state.msg_attr.why, "5.1.1",
+ "unknown user: \"%s\"", state.msg_attr.user);
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+}